|
|
|
|
CPML reference manual |
|
|---|---|---|---|---|
| Top | Description | ||||
void cpml_curve_put_pair_at_time (const CpmlPrimitive *curve,,double tCpmlPair *pair);void cpml_curve_put_vector_at_time (const CpmlPrimitive *curve,,double tCpmlVector *vector);
The following functions manipulate CPML_CURVE CpmlPrimitive.
No validation is made on the input so use the following methods
only when you are sure the primitive argument
is effectively a cubic Bézier curve.
get_length()put_extents()put_pair_at()put_vector_at()get_closest_pos()put_intersections()offset() implementation, when the equations are
inconsistent, the alternative approach performs very bad
if v0 and v3 are
opposite or staggered.
Given a cubic Bézier primitive, it must be found the approximated Bézier curve parallel to the original one at a specific distance (the so called "offset curve"). The four points needed to build the new curve must be returned.
To solve the offset problem, a custom algorithm is used. First, the resulting curve MUST have the same slope at the start and end point. These constraints are not sufficient to resolve the system, so I let the curve pass thought a given point (pm, known and got from the original curve) at a given time (m, now hardcoded to 0.5).
Firstly, some useful variables are defined:
v0 = unitvector(p[1] − p[0]) × offset; v3 = unitvector(p[3] − p[2]) × offset; p0 = p[0] + normal v0; p3 = p[3] + normal v3.
The resulting curve must have the same slopes than the original one at the start and end points. Forcing the same slopes means:
p1 = p0 + k0 v0.
where k0xy
p1.x = p0.x + k0 v0.x; p1.y = p0.y + k0 v0.y.
and doing the same for the end point:
p2.x = p3.x + k3 v3.x; p2.y = p3.y + k3 v3.y.
This does not give a resolvable system, though. The curve will be
interpolated by forcing its path to pass throught pmtime is m0 ≤ m ≤ 1.
Knowing the function of the cubic Bézier:
C(t) = (1 − t)³p0 + 3t(1 − t)²p1 + 3t²(1 − t)p2 + t³p3.
and forcing t = m and C(t) = pm:
pm = (1 − m)³p0 + 3m(1 − m)²p1 + 3m²(1 − m)p2 + m³p3. (1 − m) p1 + m p2 = (pm − (1 − m)³p0 − m³p3) / (3m (1 − m)).
gives this final system:
p1.x = p0.x + k0 v0.x; p1.y = p0.y + k0 v0.y; p2.x = p3.x + k3 v3.x; p2.y = p3.y + k3 v3.y; (1 − m) p1.x + m p2.x = (pm.x − (1 − m)³p0.x − m³p3.x) / (3m (1 − m)); (1 − m) p1.y + m p2.y = (pm.y − (1 − m)³p0.y − m³p3.y) / (3m (1 − m)).
Substituting and resolving for k0k3
(1 − m) k0 v0.x + m k3 v3.x = (pm.x − (1 − m)³p0.x − m³p3.x) / (3m (1 − m)) − (1 − m) p0.x − m p3.x; (1 − m) k0 v0.y + m k3 v3.y = (pm.y − (1 − m)³p0.y − m³p3.y) / (3m (1 − m)) − (1 − m) p0.y − m p3.y. (1 − m) k0 v0.x + m k3 v3.x = (pm.x − (1 − m)²(1+2m) p0.x − m²(3 − 2m) p3.x) / (3m (1 − m)); (1 − m) k0 v0.y + m k3 v3.y = (pm.y − (1 − m)²(1+2m) p0.y − m²(3 − 2m) p3.y) / (3m (1 − m)).
Letting:
pk = (pm − (1 − m)²(1+2m) p0 − m²(3 − 2m) p3) / (3m (1 − m)).
reduces the above to this final equations:
(1 − m) k0 v0.x + m k3 v3.x = pk.x; (1 − m) k0 v0.y + m k3 v3.y = pk.y.
If v0.x ≠ 0, the system can be resolved for k0k3
k0 = (pk.x − m k3 v3.x) / ((1 − m) v0.x); (pk.x − m k3 v3.x) v0.y / v0.x + m k3 v3.y = pk.y. k0 = (pk.x − m k3 v3.x) / ((1 − m) v0.x); k3 m (v3.y − v3.x v0.y / v0.x) = pk.y − pk.x v0.y / v0.x. k3 = (pk.y − pk.x v0.y / v0.x) / (m (v3.y − v3.x v0.y / v0.x)); k0 = (pk.x − m k3 v3.x) / ((1 − m) v0.x).
Otherwise, if v3.x ≠ 0, the system can be solved
for k3k0
k3 = (pk.x − (1 − m) k0 v0.x) / (m v3.x); (1 − m) k0 v0.y + (pk.x − (1 − m) k0 v0.x) v3.y / v3.x = pk.y. k3 = (pk.x − (1 − m) k0 v0.x) / (m v3.x); k0 (1 − m) (v0.y − k0 v0.x v3.y / v3.x) = pk.y − pk.x v3.y / v3.x. k0 = (pk.y − pk.x v3.y / v3.x) / ((1 − m) (v0.y − v0.x v3.y / v3.x)); k3 = (pk.x − (1 − m) k0 v0.x) / (m v3.x).
The whole process must be guarded against division by 0 exceptions.
If either v0.x and v3.x are 0v0.x × v3.y = v3.x × v3.y condition must
be avoided. This is the first situation to avoid, in which case
an alternative approach should be used.
void cpml_curve_put_pair_at_time (const CpmlPrimitive *curve,,double tCpmlPair *pair);
Given the curve Bézier cubic, finds the coordinates at time t
(where 0 is the start and 1 is the end) and stores the result
in pair. Keep in mind t is not homogeneous, so 0.5 does not
necessarily means the mid point.
|
|
the CpmlPrimitive curve data |
|
|
the "time" value |
|
|
the destination pair |
Since 1.0
void cpml_curve_put_vector_at_time (const CpmlPrimitive *curve,,double tCpmlVector *vector);
Given the curve Bézier cubic, finds the slope at time t
(where 0 is the start and 1 is the end) and stores the result
in vector. Keep in mind t is not homogeneous, so 0.5
does not necessarily means the mid point.
|
|
the CpmlPrimitive curve data |
|
|
the "time" value |
|
|
the destination vector |
Since 1.0