Method for computing normals
In 2D, the normal is any vector orthgonal to the tangent. For most popular splines, there is a closed form for the tangent. If you don't have that, you can compute it numerically.
Computing it numerically: For a function p(s)
which returns points on the spline as a function of the scalar s
, tangent_at_s = (p(s + e) - p(s - e)) / (2 * e)
, where e is a small but nonzero number. Say 1e-6
.
You can get a normal vector by rotating tangent_at_s
90 degrees. In 2D, that is easy: normal_at_s = [-tangent_at_s.y, tangent_at_s.x]
. Divide by the norm of n
to get a unit normal.
Code for computing normals
import numpy as np
def p(s):
'''p(s) returns an np.array of size 2. A point on the spline.
s + e is a different point for all s within the spline, and nonzero e.
'''
return a_point_on_the_spline
def get_unit_normal(p, s):
# Compute tangent by central differences. You can use a closed form tangent if you have it.
tangent_at_s = (p(s + e) - p(s - e)) / (2 * e)
normal_at_s = np.array([-tangent_at_s[1], tangent_at_s[0]])
unit_normal_at_s = normal_at_s / np.linalg.norm(normal_at_s)
my_normal = get_unit_normal(p, 0.1)
To translate a point at p(s)
along the normal vector, you can just construct a point, p'(s) = p(s) + d_along_normal * get_unit_normal(p, s)
. The distance to the original point is norm(p'(s) - p(s))
.
Distance to curve in general
The shortest segment between a point and a curve is always either perpendicular to the curve, or attached to one of the curve endpoints. You can compute a decent approximation by iterating over nearly spaced points on the curve, and finding the point on the curve that minimizes distance to your query point.
For a more accurate estimate, you might check with large spacing, and then use a Newton method to find the minimum in small segments of the curve.