Find smallest distance from a point to a Bézier curve
g0 = Graphics[{BezierCurve[pts], Point[pts], Red, Point[pt]}, Frame -> True];
lines = MeshPrimitives[DiscretizeGraphics[g0], 1];
npt = RegionNearest[RegionUnion @@ lines][pt]
{0.0805512, 0.671604}
Graphics[{Blue,lines, Red, Point[pt], Black, Point@pts,
Green, PointSize[Large], Point@npt}, Frame -> True]
Simply using BezierFunction
is not enough. The BezierFunction
will not match the BezierCurve
because that curve is actually a composite of multiple splines - see here: BezierCurve is different from BezierFunction.
This below is adapted from the above and @J. M.'s technical difficulties solution:
You need to first chop your spline into its components and minimize over both, then find which closest point on each sub-spline is closer to your point. See here on how to produce the parts: How to construct BezierFunction for BezierCurve with npts>4 and SplineDegree -> 3?
pt = {-0.07194, 0.6342};
pts = {{-3, 0}, {-1, 3}, {1, -3}, {0, 1}, {0, 2}, {2, 2}, {-2, -2}};
bzsplinefns = BezierFunction /@ Partition[pts, 4, 3];
distance[p1_, p2_] := SquaredEuclideanDistance[p1, p2]
splineDistance[spline_, point_, t_?NumericQ] :=
distance[spline[t], point]
closest[spline_, point_] :=
NArgMin[{splineDistance[spline, point, t], 0 < t < 1}, t]
tvals = closest[#, pt] & /@ bzsplinefns;
finalNearestPoint =
MinimalBy[MapThread[#1[#2] &, {bzsplinefns, tvals}],
distance[#, pt] &][[1]]
Graphics[{Point[pt], Thick, Gray, BezierCurve[pts], Thin,
{RandomColor[], Line[Table[#[t], {t, 0, 1, 0.01}]]} & /@
bzsplinefns, PointSize[Large], Point[finalNearestPoint]}]
If you choose BSplineCurve
instead, you don't need to worry about breaking it into multiple BSplineFunctions
- you can just minimize a single BSplineFunction
that accounts for the whole curve.
pt = {-0.07194, 0.6342};
pts = {{-3, 0}, {-1, 3}, {1, -3}, {0, 1}, {0, 2}, {2, 2}, {-2, -2}};
distance[p1_, p2_] := SquaredEuclideanDistance[p1, p2]
splineDistance[spline_, point_, t_?NumericQ] :=
distance[spline[t], point]
closest[spline_, point_] :=
NArgMin[{splineDistance[spline, point, t], 0 < t < 1}, t]
bsp = BSplineFunction[pts];
result = bsp[closest[bsp, pt]]
Graphics[{BSplineCurve[pts], Point[pt], PointSize[Large],
Point[result]}]
Another way is to express the curve as a union of ParametricRegions
and then use RegionNearest
.
p1 = (List @@ Expand[(x + y)^3] /. {x -> 1 - t, y -> t}).pts[[1 ;; 4]];
p2 = (List @@ Expand[(x + y)^3] /. {x -> 1 - t, y -> t}).pts[[4 ;; 7]];
breg = RegionUnion[
ParametricRegion[p1, {{t, 0, 1}}],
ParametricRegion[p2, {{t, 0, 1}}]
];
Region[Style[breg, Thick]]
RegionNearest[breg, pt]
{0.0808892, 0.67102}