In computer graphics, the centripetal Catmull–Rom spline is a variant form of the Catmull–Rom spline, originally formulated by Edwin Catmull and Raphael Rom,[1] which can be evaluated using a recursive algorithm proposed by Barry and Goldman.[2] It is a type of interpolating spline (a curve that goes through its control points) defined by four control points
P0,P1,P2,P3
P1
P2
Let
Pi=[xi
T | |
y | |
i] |
C
P0,P1,P2,P3
t0,t1,t2,t3
C=
t2-t | |
t2-t1 |
B | ||||
|
B2
where
B1=
t2-t | |
t2-t0 |
A | ||||
|
A2
B2=
t3-t | |
t3-t1 |
A | ||||
|
A3
A1=
t1-t | |
t1-t0 |
P | ||||
|
P1
A2=
t2-t | |
t2-t1 |
P | ||||
|
P2
A3=
t3-t | |
t3-t2 |
P | ||||
|
P3
and
ti+1=\left[\sqrt{(xi+1
2+(y | |
-x | |
i+1 |
2}\right] | |
-y | |
i) |
\alpha+ti
in which
\alpha
i=0,1,2,3
t0=0
\alpha
0.5
\alpha=0
\alpha=1
t=t1
A1,A2,A3,B1,B2,
C
t1
C=P1
t=t2
C=P2
t2
\alpha
ti+1
C
t1
t2
Pi=[xi yi
T | |
z | |
i] |
Pi
ti+1=\left[\sqrt{(xi+1
2+(y | |
-x | |
i+1 |
2+(z | |
-y | |
i+1 |
2}\right] | |
-z | |
i) |
\alpha+ti
Centripetal Catmull–Rom spline has several desirable mathematical properties compared to the original and the other types of Catmull-Rom formulation.[3] First, it will not form loop or self-intersection within a curve segment. Second, cusp will never occur within a curve segment. Third, it follows the control points more tightly.[4]
In computer vision, centripetal Catmull-Rom spline has been used to formulate an active model for segmentation. The method is termed active spline model.[5] The model is devised on the basis of active shape model, but uses centripetal Catmull-Rom spline to join two successive points (active shape model uses simple straight line), so that the total number of points necessary to depict a shape is less. The use of centripetal Catmull-Rom spline makes the training of a shape model much simpler, and it enables a better way to edit a contour after segmentation.
The following is an implementation of the Catmull–Rom spline in Python that produces the plot shown beneath.
QUADRUPLE_SIZE: int = 4
def num_segments(point_chain: tuple) -> int: # There is 1 segment per 4 points, so we must subtract 3 from the number of points return len(point_chain) - (QUADRUPLE_SIZE - 1)
def flatten(list_of_lists) -> list: # E.g. mapping 1, 2, [3], [4, 5]] to [1, 2, 3, 4, 5] return [elem for lst in list_of_lists for elem in lst]
def catmull_rom_spline(P0: tuple, P1: tuple, P2: tuple, P3: tuple, num_points: int, alpha: float = 0.5,): """ Compute the points in the spline segment :param P0, P1, P2, and P3: The (x,y) point pairs that define the Catmull-Rom spline :param num_points: The number of points to include in the resulting curve segment :param alpha: 0.5 for the centripetal spline, 0.0 for the uniform spline, 1.0 for the chordal spline. :return: The points """
# Calculate t0 to t4. Then only calculate points between P1 and P2. # Reshape linspace so that we can multiply by the points P0 to P3 # and get a point for each value of t. def tj(ti: float, pi: tuple, pj: tuple) -> float: xi, yi = pi xj, yj = pj dx, dy = xj - xi, yj - yi l = (dx ** 2 + dy ** 2) ** 0.5 return ti + l ** alpha
t0: float = 0.0 t1: float = tj(t0, P0, P1) t2: float = tj(t1, P1, P2) t3: float = tj(t2, P2, P3) t = numpy.linspace(t1, t2, num_points).reshape(num_points, 1)
A1 = (t1 - t) / (t1 - t0) * P0 + (t - t0) / (t1 - t0) * P1 A2 = (t2 - t) / (t2 - t1) * P1 + (t - t1) / (t2 - t1) * P2 A3 = (t3 - t) / (t3 - t2) * P2 + (t - t2) / (t3 - t2) * P3 B1 = (t2 - t) / (t2 - t0) * A1 + (t - t0) / (t2 - t0) * A2 B2 = (t3 - t) / (t3 - t1) * A2 + (t - t1) / (t3 - t1) * A3 points = (t2 - t) / (t2 - t1) * B1 + (t - t1) / (t2 - t1) * B2 return points
def catmull_rom_chain(points: tuple, num_points: int) -> list: """ Calculate Catmull-Rom for a sequence of initial points and return the combined curve. :param points: Base points from which the quadruples for the algorithm are taken :param num_points: The number of points to include in each curve segment :return: The chain of all points (points of all segments) """ point_quadruples = (# Prepare function inputs (points[idx_segment_start + d] for d in range(QUADRUPLE_SIZE)) for idx_segment_start in range(num_segments(points))) all_splines = (catmull_rom_spline(*pq, num_points) for pq in point_quadruples) return flatten(all_splines)
if __name__
chain_points: list = catmull_rom_chain(POINTS, NUM_POINTS) assert len(chain_points)
plt.plot(*zip(*chain_points), c="blue") plt.plot(*zip(*POINTS), c="red", linestyle="none", marker="o") plt.show
// a single catmull-rom curvepublic struct CatmullRomCurve
// Draws a catmull-rom spline in the scene view,// along the child objects of the transform of this componentpublic class CatmullRomSpline : MonoBehaviour
float GetT(float t, float alpha, const FVector& p0, const FVector& p1)
FVector CatmullRom(const FVector& p0, const FVector& p1, const FVector& p2, const FVector& p3, float t /* between 0 and 1 */, float alpha=.5f /* between 0 and 1 */)