I have stumbled upon an unexpected behaviour of the Curve.GetLocalPerpPoint() method of RhinoCommon.
When the testPoint happens to be precisely at a location, which should result in a perpendicular point being precisely at the target curve’s C2 discontinuity location, the method fails (both overloads do).
I wonder if is there’s anything I should be doing differently, or whether this is just a peculiar edge case.
Attaching image and source files below for more context.
GetLocalPerpPoint splits the curve at its C2 discontinuities and looks for a sign change in the perpendicularity function within each span. A solution sitting exactly on a span boundary produces no sign change to find, so it falls back to a secondary “is this already perpendicular?” test — and that test uses a fixed tolerance against a quantity that scales with your offset distance and the curve’s parameterization. In your file that scale factor is around 1e7, so a solution that’s perpendicular to better than a billionth of a degree still gets rejected.
As a workaround, Curve.LocalClosestPoint() takes the same test point and seed parameter and returns exactly the parameter you’re expecting on your geometry. The two aren’t strictly equivalent — a closest point can land on a curve end where the curve isn’t perpendicular to the test point — so add a check if your inputs vary.
much thanks for the insights. For the time being, I will use the suggested Curve.LocalClosestPoint() with additional orthogonality check as a fallback.