3

I need to draw arrows between two arbitrary "nodes". The arrow ends needs to enter or exit the nodes from one of the four cardinal directions: N, S, E, W.

data Dir = N | S | E | W
     deriving (Eq, Ord, Show)

cir, circles :: Diagram B
cir  = circle 0.3 # showOrigin # lw thick
circles = (cir # named "1") ||| strutX 3 ||| (cir # named "2")

ctrlPoint :: Dir -> V2 Double
ctrlPoint N = r2 (0, 1)
ctrlPoint S = r2 (0, -1)
ctrlPoint E = r2 (1, 0)
ctrlPoint W = r2 (-1, 0)

-- This function should specify an arrow shaft entering nodes from directions dir1 and dir2
shaft :: Dir -> Dir -> Trail V2 Double
shaft dir1 dir2 = trailFromSegments [bézier3 (controlPoint dir1) (controlPoint dir2) (r2 (3, 0))]

example = circles # connect' (with ... & arrowShaft .~ shaft N S ) "1" "2"

enter image description here

In the picture above, the arrow enters correctly from North in the first circle, and South in the second. However, if I setup the points vertically, everything is rotated:

circles = (cir # named "1") === strutY 3 === (cir # named "2")

enter image description here

This is not correct, because I wanted the arrow to enter from North and South, respectively. It seems the shaft of the arrow is rotated altogether... How to write my function shaft :: Dir -> Dir -> Trail V2 Double? Thanks

cdupont
  • 1,138
  • 10
  • 17
  • 1
    (1) Your code here, after removing the `...` placeholder and changing `controlPoint` to `ctrlPoint`, doesn't result in a shaft like the one in the question. Please double-check the code to make sure we are seeing the same thing that you do. (2) `circles` only sets the positioning of the circles. If there is anything to fix, it will presumably be in how you set the Bézier control points in `shaft`. – duplode Oct 31 '21 at 23:58
  • @duplode you're right, setting the control points was the solution. – cdupont Nov 01 '21 at 10:41

1 Answers1

2

I found an answer using arrowFromLocatedTrail' instead:

-- control points for bézier curves
control :: Dir -> V2 Double
control N = r2 (0, 0.5)
control S = r2 (0, -0.5)
control E = r2 (0.5, 0)
control W = r2 (-0.5, 0)

-- shaft of arrows
shaft :: (P2 Double, Dir) -> (P2 Double, Dir) ->  Located (Trail V2 Double)
shaft (p, d) (p', d') = trailFromSegments [bézier3 (control d) ((p' .-. p) - (control d')) (p' .-. p)] `at` p

-- create a single arrow
mkArrow :: (P2 Double, Dir) -> (P2 Double, Dir) -> Diagram B
mkArrow a b = arrowFromLocatedTrail' (with & arrowHead .~ dart
                                    & lengths .~ veryLarge
                                    & shaftStyle %~ lw thick) (shaft a b)

This version performs the necessary transformations:

bézier3 (control d) ((p' .-. p) + (control d')) (p' .-. p)

Here is the signature ofbézier:

bézier3 :: v n -> v n -> v n -> Segment Closed v n 

It takes 3 vectors, named here V1, V2 and V3. bézier curve are by default not located in Diagrams, they just specify how to move.

enter image description here

So, to draw the bézier curve, we set:

V1 = control d
V2 = (p' .-. p) + (control d')
V3 = p' .-. p

The resulting bézier curve will located at p.

cdupont
  • 1,138
  • 10
  • 17