1 /* CPML - Cairo Path Manipulation Library
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
23 * @Section_Id:CpmlCurve
25 * @short_description: Bézier cubic curve primitive management
27 * The following functions manipulate %CPML_CURVE #CpmlPrimitive.
28 * No validation is made on the input so use the following methods
29 * only when you are sure the <varname>primitive</varname> argument
30 * is effectively a cubic Bézier curve.
35 * <listitem>the <function>get_length</function> method must be
36 * implemented;</listitem>
37 * <listitem>actually the <function>put_extents</function> method is
38 * implemented by computing the bounding box of the control
39 * polygon and this will likely include some empty space:
40 * there is room for improvements;</listitem>
41 * <listitem>the <function>put_pair_at</function> method must be
42 * implemented;</listitem>
43 * <listitem>the <function>put_vector_at</function> method must be
44 * implemented;</listitem>
45 * <listitem>the <function>get_closest_pos</function> method must be
46 * implemented;</listitem>
47 * <listitem>the <function>put_intersections</function> method must be
48 * implemented;</listitem>
49 * <listitem>by default, the offset curve is calculated by using the point
50 * at t=0.5 as reference: use a better candidate;</listitem>
51 * <listitem>in the <function>offset</function> implementation, when the
52 * equations are inconsistent, the alternative approach performs
53 * very bad if <varname>v0</varname> and <varname>v3</varname> are
54 * opposite or staggered.</listitem>
62 #include "cpml-internal.h"
63 #include "cpml-extents.h"
64 #include "cpml-segment.h"
65 #include "cpml-primitive.h"
66 #include "cpml-primitive-private.h"
67 #include "cpml-curve.h"
70 static void put_extents (const CpmlPrimitive
*curve
,
71 CpmlExtents
*extents
);
72 static void offset (CpmlPrimitive
*curve
,
76 const _CpmlPrimitiveClass
*
77 _cpml_curve_get_class(void)
79 static _CpmlPrimitiveClass
*p_class
= NULL
;
81 if (p_class
== NULL
) {
82 static _CpmlPrimitiveClass class_data
= {
93 p_class
= &class_data
;
101 * cpml_curve_put_pair_at_time:
102 * @curve: the #CpmlPrimitive curve data
103 * @t: the "time" value
104 * @pair: the destination pair
106 * Given the @curve Bézier cubic, finds the coordinates at time @t
107 * (where 0 is the start and 1 is the end) and stores the result
108 * in @pair. Keep in mind @t is not homogeneous, so 0.5 does not
109 * necessarily means the mid point.
114 cpml_curve_put_pair_at_time(const CpmlPrimitive
*curve
, double t
,
117 CpmlPair p1
, p2
, p3
, p4
;
118 double t_2
, t_3
, t1
, t1_2
, t1_3
;
120 cpml_primitive_put_point(curve
, 0, &p1
);
121 cpml_primitive_put_point(curve
, 1, &p2
);
122 cpml_primitive_put_point(curve
, 2, &p3
);
123 cpml_primitive_put_point(curve
, 3, &p4
);
131 pair
->x
= t1_3
* p1
.x
+ 3 * t1_2
* t
* p2
.x
132 + 3 * t1
* t_2
* p3
.x
+ t_3
* p4
.x
;
133 pair
->y
= t1_3
* p1
.y
+ 3 * t1_2
* t
* p2
.y
134 + 3 * t1
* t_2
* p3
.y
+ t_3
* p4
.y
;
138 * cpml_curve_put_vector_at_time:
139 * @curve: the #CpmlPrimitive curve data
140 * @t: the "time" value
141 * @vector: the destination vector
143 * Given the @curve Bézier cubic, finds the slope at time @t
144 * (where 0 is the start and 1 is the end) and stores the result
145 * in @vector. Keep in mind @t is not homogeneous, so 0.5
146 * does not necessarily means the mid point.
151 cpml_curve_put_vector_at_time(const CpmlPrimitive
*curve
,
152 double t
, CpmlVector
*vector
)
154 CpmlPair p1
, p2
, p3
, p4
;
155 CpmlPair p21
, p32
, p43
;
156 double t1
, t1_2
, t_2
;
158 cpml_primitive_put_point(curve
, 0, &p1
);
159 cpml_primitive_put_point(curve
, 1, &p2
);
160 cpml_primitive_put_point(curve
, 2, &p3
);
161 cpml_primitive_put_point(curve
, 3, &p4
);
174 vector
->x
= 3 * t1_2
* p21
.x
+ 6 * t1
* t
* p32
.x
+ 3 * t_2
* p43
.x
;
175 vector
->y
= 3 * t1_2
* p21
.y
+ 6 * t1
* t
* p32
.y
+ 3 * t_2
* p43
.y
;
180 put_extents(const CpmlPrimitive
*curve
, CpmlExtents
*extents
)
182 CpmlPair p1
, p2
, p3
, p4
;
184 extents
->is_defined
= 0;
186 cpml_primitive_put_point(curve
, 0, &p1
);
187 cpml_primitive_put_point(curve
, 1, &p2
);
188 cpml_primitive_put_point(curve
, 2, &p3
);
189 cpml_primitive_put_point(curve
, 3, &p4
);
191 cpml_extents_pair_add(extents
, &p1
);
192 cpml_extents_pair_add(extents
, &p2
);
193 cpml_extents_pair_add(extents
, &p3
);
194 cpml_extents_pair_add(extents
, &p4
);
198 offset(CpmlPrimitive
*curve
, double offset
)
201 CpmlVector v0
, v3
, vm
, vtmp
;
202 CpmlPair p0
, p1
, p2
, p3
, r
;
207 /* Firstly, convert the curve points from cairo format to cpml format
208 * and store them (temporary) in p0..p3 */
209 cpml_pair_from_cairo(&p0
, curve
->org
);
210 cpml_pair_from_cairo(&p1
, &curve
->data
[1]);
211 cpml_pair_from_cairo(&p2
, &curve
->data
[2]);
212 cpml_pair_from_cairo(&p3
, &curve
->data
[3]);
222 /* r = point in C(m) offseted the requested @offset distance */
223 cpml_curve_put_vector_at_time(curve
, m
, &vm
);
224 cpml_vector_set_length(&vm
, offset
);
225 cpml_vector_normal(&vm
);
226 cpml_curve_put_pair_at_time(curve
, m
, &r
);
230 /* p0 = p0 + normal of v0 of @offset magnitude (exact value) */
231 cpml_pair_copy(&vtmp
, &v0
);
232 cpml_vector_set_length(&vtmp
, offset
);
233 cpml_vector_normal(&vtmp
);
237 /* p3 = p3 + normal of v3 of @offset magnitude, as done for p0 */
238 cpml_pair_copy(&vtmp
, &v3
);
239 cpml_vector_set_length(&vtmp
, offset
);
240 cpml_vector_normal(&vtmp
);
244 if (v0
.x
*v3
.y
== v3
.x
*v0
.y
) {
245 /* Inconsistent equations: use the alternative approach */
246 p1
.x
= p0
.x
+ v0
.x
+ vm
.x
* 4/3;
247 p1
.y
= p0
.y
+ v0
.y
+ vm
.y
* 4/3;
248 p2
.x
= p3
.x
- v3
.x
+ vm
.x
* 4/3;
249 p2
.y
= p3
.y
- v3
.y
+ vm
.y
* 4/3;
254 s
.x
= (r
.x
- mm
*mm
*(1+m
+m
)*p0
.x
- m
*m
*(1+mm
+mm
)*p3
.x
) / (3*m
*(1-m
));
255 s
.y
= (r
.y
- mm
*mm
*(1+m
+m
)*p0
.y
- m
*m
*(1+mm
+mm
)*p3
.y
) / (3*m
*(1-m
));
258 k3
= (s
.y
- s
.x
*v0
.y
/ v0
.x
) / (m
*(v3
.y
- v3
.x
*v0
.y
/ v0
.x
));
259 k0
= (s
.x
- m
*k3
*v3
.x
) / (mm
*v0
.x
);
261 k0
= (s
.y
- s
.x
*v3
.y
/ v3
.x
) / (mm
*(v0
.y
- v0
.x
*v3
.y
/ v3
.x
));
262 k3
= (s
.x
- mm
*k0
*v0
.x
) / (m
*v3
.x
);
265 p1
.x
= p0
.x
+ k0
*v0
.x
;
266 p1
.y
= p0
.y
+ k0
*v0
.y
;
267 p2
.x
= p3
.x
+ k3
*v3
.x
;
268 p2
.y
= p3
.y
+ k3
*v3
.y
;
271 /* Return the new curve in the original array */
272 cpml_pair_to_cairo(&p0
, curve
->org
);
273 cpml_pair_to_cairo(&p1
, &curve
->data
[1]);
274 cpml_pair_to_cairo(&p2
, &curve
->data
[2]);
275 cpml_pair_to_cairo(&p3
, &curve
->data
[3]);