在两点之间使用NSBezierPath绘制箭头

时间:2012-12-28 11:32:25

标签: objective-c macos drawing geometry

所以我试图生成一个NSBezierPath看起来像两个点之间的箭头,它可以位于视图的任何位置,因此startPoint可以大于或小于端点。

当用户在绘图应用程序中拖动鼠标时,箭头会更新。

我已经想通了,我可能不得不使用转换和一些数学来做三角函数并且已经提出了这个实现:

 +(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;
    }

当这些点是某种对角线时,此代码生成相当漂亮的箭头, 喜欢

  • (0,0)
  • ( - 2,-2)

彼此,但当点越来越接近水平或垂直线,如

  • (2,3)
  • (5,3)

结果变为没有箭头的直线。

所以我认为我在转型矩阵中做错了。

如果有人知道我犯了什么错,那就太好了。

1 个答案:

答案 0 :(得分:1)

您需要的是一种仿射转换,可将点(0, 0)转换为startPoint(1, 1)转换为endPoint。这种转换可以直接计算:

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];

说明:

NSAffineTransformStruct transformStruct = { a, b, -b, a, tx, ty };

描述了平移,缩放和旋转的一般组合,即没有剪切的仿射变换。要求(0, 0) -> startPoint(1, 1) -> endPoint给出等式

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

并解决abtxty的这些等式给出了上述解决方案。 (有关详细信息,请参阅“可可绘图指南”中的Transform Mathematics。)

原始代码的问题在于

  • atan2y作为第一个参数,因此atan2(height, width)会计算角度。
  • 对于水平或垂直线,widthheight,因此一个缩放系数为零,这会导致没有箭头的直线。