Рисование стрелки с помощью NSBezierPath между двумя точками

Поэтому я пытаюсь создать 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
nl ja de

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

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

и решение этих уравнений для a , b , tx , ty дает выше решение. (См. Transform Mathematics в «Руководстве по рисованию какао» для получения дополнительной информации.)

Проблема с вашим исходным кодом заключается в том, что

  • atan2 takes y as first argument, so atan2(height, width) would compute the angle.
  • For horizontal or vertical lines, width or height and therefore one scaling factor is zero, this causes the straight lines without arrowhead.
1
добавлено
В первой версии формулы была ошибка знака, которую я исправил сейчас.
добавлено автор Martin R, источник
Редактировать: Вы были быстрее меня здесь, еще раз спасибо!
добавлено автор miradorn, источник
macOS — русскоговорящее сообщество
macOS — русскоговорящее сообщество
693 участник(ов)

Обсуждаем всё, что связано с экосистемой macOS и продуктами Apple.

macOS Developers — русскоговорящее сообщество
macOS Developers — русскоговорящее сообщество
124 участник(ов)

По всем остальным macOS / Apple вопросам в @macOS_ru Обсуждаем всё, что связано с разработкой под macOS.