# [NTG-context] MetaFun: How to draw bent paths?

Alan Braslau braslau.list at comcast.net
Wed Sep 19 18:25:09 CEST 2018

```On Wed, 19 Sep 2018 11:18:30 +1200
Henri Menke <henrimenke at gmail.com> wrote:

> On 18/09/18 21:23, Henri Menke wrote:
> > On 9/18/18 7:57 PM, Hans Hagen wrote:
> >> On 9/16/2018 10:28 AM, Henri Menke wrote:
> >>> Dear list,
> >>>
> >>> I want to draw paths between points which are bent by a certain angle.
> >>> The code in the MWE below works well but I was thinking that the expert
> >>> here can surely improve it and suggest a nicer syntax.
> >> looks like a challenge for Alan ... isn't it also a bit 3d (2.5d)?
> >
> > I wasn't thinking about a 3D application here, just a little bend on the
> > path to go around other elements in my drawing without having to think
> > about explicit control points (like the second example in my original
> > post).  In the meantime I have improved the defintion of the macro to
> > avoid double computation of the angle:
> >
> >     vardef bend(expr a,b,phi) =
> >         save x ;
> >         x := angle(b-a) ;
> >         a{dir(x+phi)} .. {dir(x-phi)}b
> >     enddef ;
> >
> > So currently the applied syntax is
> >
> >     drawarrow bend(z1, z2, 30) ;
> >
> > However, I would prefer something like
> >
> >     drawarrow z1 bend(30) z2 ;
> >
> > I'm not sure whether this is possible though.  Actually I'm reading the
> > MetaPost manual right know because my MP skills are rather poor.
>
> I think I got it now.  Not sure whether “bend” should be primary or
> something else.
>
> \startMPdefinitions
>     primarydef a bend phi =
>         do_bend(a,phi)
>     enddef ;
>
>     vardef do_bend(expr a, phi) expr b =
>         save x ;
>         x := angle(b-a) ;
>         a{dir(x+phi)} .. {dir(x-phi)}b
>     enddef ;
> \stopMPdefinitions
>
> \starttext
>
> \startMPpage
>     z1 = (-.5cm,1cm) ;
>     z2 = (1cm,3cm) ;
>
>     for phi = 0 step 5 until 360:
>         draw z1 bend(phi) z2 withcolor (phi/360)[red,blue] ;
>     endfor ;
> \stopMPpage
>
> % Possible use case:
>
> \startMPpage[offset=1cm]
>     z1 = (-.5cm,1cm) ;
>     z2 = (1cm,3cm) ;
>     draw z1 -- z2 ;
>     dotlabel.lft("\$-\$", z1) ;
>     dotlabel.lft("\$+\$", z2) ;
>
>     path p; p := z1 bend(-30) z2 ;
>     drawdblarrow p withcolor blue ;
>     label.lrt(textext("\$\Delta\$"),point .5 along p)  withcolor blue ;
> \stopMPpage
>
> \stoptext

Henri,

1. I added a sanity test (angle(origin) is undefined);
2. it is probably more efficient to calculate the angle twice than to allocate storage;
3. primary b;

4. then I created the variant arc (as in a bow) taking a delta (the tangent of
an angle).

Alan

\startMPdefinitions
primarydef a bend phi =
do_bend(a,phi)
enddef ;

vardef do_bend(expr a, phi) primary b =
if abs(a-b)=0 :
a
else :
(a{dir(angle(b-a)+phi)} .. {dir(angle(b-a)-phi)}b)
fi
enddef ;

primarydef a arc delta =
do_arc(a,delta)
enddef ;

vardef do_arc(expr a,delta) primary b =
if abs(a-b)=0 :
a
else :
(a .. unitvector direction .5 of (a--b) rotated 90
scaled delta * arclength (a--b)
shifted .5[a,b]
.. b)
fi
enddef ;
\stopMPdefinitions

\starttext

\startMPpage
z1 = (-.5cm,1cm) ;
z2 = (1cm,3cm) ;

for phi = 0 step 5 until 360:
draw z1 bend phi z2 withcolor (phi/360)[red,blue] ;
endfor ;
\stopMPpage

\startMPpage
z1 = (-.5cm,1cm) ;
z2 = (1cm,3cm) ;

for delta = -1 step .1 until 1:
draw z1 arc delta z2 withcolor ((delta+1)/2)[red,blue] ;
endfor ;
\stopMPpage

% Possible use case:

\startMPpage[offset=1cm]
z1 = (-.5cm,1cm) ;
z2 = (1cm,3cm) ;
draw z1 -- z2 ;
dotlabel.lft("\$-\$", z1) ;
dotlabel.lft("\$+\$", z2) ;

path p; p := z1 bend(-30) z2 ;
drawdblarrow p withcolor blue ;
label.lrt(textext("\$\Delta\$"),point .5 along p)  withcolor blue ;
\stopMPpage

\stoptext
```