Important: Please read the Qt Code of Conduct -

Modify arrow head from Diagramscene Example

  • Hello,

    I am not a mathematician, so I don't know how to add another point to the arrow head.

    From "Diagram Scene Example":
    double angle = ::acos(line().dx() / line().length());
    if (line().dy() >= 0)
    angle = (Pi * 2) - angle;

    QPointF arrowP1 = line().p1() + QPointF(sin(angle + Pi / 3) * arrowSize,
    cos(angle + Pi / 3) * arrowSize);

    QPointF arrowP2 = line().p1() + QPointF(sin(angle + Pi - Pi / 3) * arrowSize,
    cos(angle + Pi - Pi / 3) * arrowSize);

    arrowHead << line().p1() << arrowP1 << arrowP2;

    Someone understand how this algorithm works?

  • Since this is very basic math I'm not sure how to break it down even more, but I'll try (and it might help if you just refresh your knowledge about trigonometric functions):

    Line 1:
    cos(alpha) = adjacent/hypotenuse
    The adjacent here is the x-vector component of the line, the hypotenuse the length of the line. Thus the arccos of dx/length equals the angle between the line and the x-Axis.
    Lines 2 and 3:
    Since the arccos returns the same angle for all line directions which have the same dx (but might actually have different signs of dy without changing the length of the line vector), we add a distinction of cases for separating positive/negative y-vectors and adjust the angle accordingly (see how this works by sketching some angles and circles).

    So now we have the angle.
    Lines 5-9:
    The next two operations create two extra points (arrowP1, arrowP2) that always have the same relative position/angle from line().p1(), no matter what orientation the line has. This is why we calculated the line angle in the first place: Now we can just add a certain angle (here Pi/3) to the angle of the line to get arrowP1 and subtract that angle to get arrowP2. The rest, again, is just trigonometry, using cos and sin to generate x- and y-offsets according to the angle and the hypotenuse (here arrowSize).

    That said, I think this method is unnecessarily copious for creating fixed shapes in rotated reference frames (the reference frame of the line), and the one who wrote the code clearly didn't think about it alot (or has no idea about linear algebra).
    Here's how I'd do it: You have the line vector (line.dx() and line.dy()), normalize (make length 1 by dividing both components by length) it and now you have a unit coordinate vector V that always points in the line direction. How do we find the second linear independent coordinate vector? Simple, we rotate the first coordinate vector by 90 degrees. How do we do that? We multiply the 2D-rotation matrix which is (cos, -sin; sin, cos). Luckily sin/cos of 90 degrees reduce to 0 and 1, so the matrix is (0, -1; 1, 0). So what does this matrix do with our old coordinate vector? Look at it! It just swaps x and y and applies a negative sign to one of them (which one doesn't matter, because multiplying the matrix by -1 corresponds to a rotation about -90° instead of 90°, which is good for our purpose, too)
    So now we have the second coordinate vector W which is orthogonal to V in our rotated reference frame which we can use to move around and find our arrowhead points.
    The arrowhead points are now easily found:
    arrowP1 = Base + V + W
    arrowP2 = Base + V - W

    That's it! No cos/sin calculation needed anywhere! (the cos/sin in the description was only to derive the 90°-rotation matrix. All you need to remember is that creating an orthogonal vector from a given one is done by swapping x and y components and adding a minus sign to one of them.)

    Here's some code to make the linear algebra method more clear:
    QVector2D V(line.dx(), line.dy());
    V.normalize(); // V vector is just normalized line vector
    QVector2D W(-V.y(), V.x()) // here we do the x-y-minus-swapping to get W

    QPointF arrowP1 = line.p1() + V.toPointF() + W.toPointF();
    QPointF arrowP2 = line.p1() + V.toPointF() - W.toPointF();
    Easy eh? And much more elegant than tinkering with angles. The symmetry of the problem is beautifully condensed in the sign of W.
    Know your math.

  • OMG! Now I am in love with you! LOL

    I was thinking that nobody would answer me, but thanks a lot!

Log in to reply