Reputation: 75
So I'm trying to generate a NSBezierPath
that looks like an arrow between two points, which can lie anywhere on the view, so the startPoint can be larger or smaller than the endpoint.
The arrow is updated while a user drags the mouse like in a drawing app.
I already figured out, that I probably have to use transformations and some math to do trigonometric and have come up with this implementation:
+(NSBezierPath *)arrowWithStart:(NSPoint)startPoint andEnd:(NSPoint)endPoint{
NSBezierPath* path = [NSBezierPath bezierPath];
CGFloat width = endPoint.x - startPoint.x;
CGFloat height = endPoint.y - startPoint.y;
CGFloat angle = atan2(width, height);
NSAffineTransform *tr = [NSAffineTransform transform];
[tr translateXBy:startPoint.x yBy:startPoint.y];
[tr scaleXBy:width yBy:height];
[tr rotateByDegrees:angle];
[path moveToPoint:CGPointZero];
[path lineToPoint:CGPointMake(0.75, 0.7)];
[path lineToPoint:CGPointMake(0.8, 0.65)];
[path lineToPoint:CGPointMake(1, 1)];
[path lineToPoint:CGPointMake(0.65, 0.8)];
[path lineToPoint:CGPointMake(0.7, 0.75)];
[path closePath];
[path transformUsingAffineTransform:tr];
return path;
}
This Code generates pretty nice arrows, when the points are some kind of diagonal, like
to each other, but when the points are getting nearer to a horizontal or vertical line, like
the result becomes a straight line without an arrowhead.
So I think I'm doing something wrong in the transformation Matrix.
If somebody knows where I'm making a mistake it would be great.
Upvotes: 1
Views: 2133
Reputation: 540075
What you need is an affine transformation that transforms the point (0, 0)
to startPoint
and (1, 1)
to endPoint
. This transformation can be computed directly:
CGFloat tx = startPoint.x;
CGFloat ty = startPoint.y;
CGFloat a = ((endPoint.x - startPoint.x) + (endPoint.y - startPoint.y))/2.;
CGFloat b = (-(endPoint.x - startPoint.x) + (endPoint.y - startPoint.y))/2.;
NSAffineTransformStruct transformStruct = { a, b, -b, a, tx, ty };
NSAffineTransform *tr = [NSAffineTransform transform];
[tr setTransformStruct:transformStruct];
Explanation:
NSAffineTransformStruct transformStruct = { a, b, -b, a, tx, ty };
describes a general combination of translation, scaling and rotation, i.e. an affine transformation without shearing. The requirements (0, 0) -> startPoint
, (1, 1) -> endPoint
give the equations
startPoint.x = 0 * a + 0 * (-b) + tx
startPoint.y = 0 * b + 0 * a + ty
endPoint.x = 1 * a + 1 * (-b) + tx
endPoint.y = 1 * b + 1 * a + tx
and solving these equations for a
, b
, tx
, ty
gives above solution. (See Transform Mathematics in the "Cocoa Drawing Guide" for more information.)
The problem with your original code is that
atan2
takes y
as first argument, so atan2(height, width)
would compute the angle.width
or height
and therefore one scaling factor is zero, this causes the straight lines without arrowhead. Upvotes: 1