# Metafont/Metapost — Find a path tangent through the external point

Assuming that I have the path `P` given, and let the `T` be a point outside the path `P`. Is there an easy way to find the point `X` on the path `P` such that the tangent on point `X` going through the point `T`?

Example in Metapost:

``````outputformat := "svg";
outputtemplate := "%j-%c.svg";
beginfig(1)
pair T,X; path P;
T:=(800,900); P:=(0,0)..(300,100)..(200,600);
draw P withpen pencircle scaled 10;
draw T withpen pencircle scaled 20 withcolor red;
endfig; end
``````

Basically, what I need is: `X = directionpoint (T-X) of P` (this is, of course, an error).

The only solution I can think of is to check an every point on the path until I found it, something like:

``````numeric v; v:=0;
forever: X:=point v of P;
exitif abs(angle(T-X) - angle(direction v of P)) < 0.001; % for example
v:=v+epsilon; endfor;
``````

Of course, this specific code will not work in the every case. Plus, it's very slow.

Is there a better solution? Of course, in a general case it's possible that there is no the solution, or more than just one solution.

• we can probably do it with the `solve` macro Commented Nov 5, 2021 at 19:02
• The arc is a general path rather than the arc of a circle? Commented Nov 5, 2021 at 19:05

Try this:

``````outputformat := "svg";
outputtemplate := "%j-%c.svg";
beginfig(1);
pair T,X; path P;
T:=(800,900); P:=(0,0)..(300,100)..(200,600);
draw P withpen pencircle scaled 10;
draw T withpen pencircle scaled 20 withcolor red;

vardef f(expr t) = angle direction t of P - angle (T - point t of P) < eps enddef;
X = point solve f (0, 2) of P;
draw X -- T withpen pencircle scaled 5 withcolor red;

endfig; end
``````

which gets me this (although I was using eps as the output format and converting it to png to get this actual pic)

The `solve` macro is explained in The Metafont Book pp.176-177. Paraphrasing, the macro uses a binary search to find a numeric (brute force) solution to a non-linear equation. You have to define a macro with a single parameter that returns either `true` or `false`. You then pass the name of your macro to `solve` as a `suffix`, followed by a pair `(a, b)` such that `f(a)` is `true` and `f(b)` is `false`. The `solve` macro returns a value between `a` and `b` that is "at the cutting edge between truth and falsity".

You can find the macro in your local copy of `plain.mp`:

``````vardef solve@#(expr true_x,false_x)=
tx_:=true_x; fx_:=false_x;
forever: x_:=.5[tx_,fx_]; exitif abs(tx_-fx_)<=tolerance;
if @#(x_): tx_ else: fx_ fi :=x_; endfor
x_ enddef; % now x_ is near where @# changes from true to false
newinternal tolerance, tx_,fx_,x_; tolerance:=.01;
``````

Notice how the passed macro is called with `@#(x_)`...

• Great solution! Of course, it's not an universal solution, but for the my purposes it's absolutely perfect!
– Урош
Commented Nov 7, 2021 at 14:32