1 #define __NR_PATH_C__
3 /*
4 * Pixel buffer rendering library
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 *
9 * This code is in public domain
10 */
12 #include <glib/gmem.h>
13 #include "n-art-bpath.h"
14 #include "nr-rect.h"
15 #include "nr-path.h"
17 static void nr_curve_bbox (NR::Coord x000, NR::Coord y000, NR::Coord x001, NR::Coord y001, NR::Coord x011, NR::Coord y011, NR::Coord x111, NR::Coord y111, NRRect *bbox);
19 static void nr_curve_bbox(NR::Point const p000, NR::Point const p001,
20 NR::Point const p011, NR::Point const p111,
21 NRRect *bbox);
23 NRBPath *nr_path_duplicate_transform(NRBPath *d, NRBPath *s, NRMatrix const *transform)
24 {
25 int i;
27 if (!s->path) {
28 d->path = NULL;
29 return d;
30 }
32 i = 0;
33 while (s->path[i].code != NR_END) i += 1;
35 d->path = g_new (NArtBpath, i + 1);
37 i = 0;
38 while (s->path[i].code != NR_END) {
39 d->path[i].code = s->path[i].code;
40 if (s->path[i].code == NR_CURVETO) {
41 d->path[i].x1 = NR_MATRIX_DF_TRANSFORM_X (transform, s->path[i].x1, s->path[i].y1);
42 d->path[i].y1 = NR_MATRIX_DF_TRANSFORM_Y (transform, s->path[i].x1, s->path[i].y1);
43 d->path[i].x2 = NR_MATRIX_DF_TRANSFORM_X (transform, s->path[i].x2, s->path[i].y2);
44 d->path[i].y2 = NR_MATRIX_DF_TRANSFORM_Y (transform, s->path[i].x2, s->path[i].y2);
45 }
46 d->path[i].x3 = NR_MATRIX_DF_TRANSFORM_X (transform, s->path[i].x3, s->path[i].y3);
47 d->path[i].y3 = NR_MATRIX_DF_TRANSFORM_Y (transform, s->path[i].x3, s->path[i].y3);
48 i += 1;
49 }
50 d->path[i].code = NR_END;
52 return d;
53 }
55 NRBPath *nr_path_duplicate_transform(NRBPath *d, NRBPath *s, NR::Matrix const transform) {
56 NRMatrix tr = transform;
57 return nr_path_duplicate_transform(d, s, &tr);
58 }
60 NArtBpath* nr_artpath_affine(NArtBpath *s, NR::Matrix const &aff) {
61 NRBPath bp, abp;
62 bp.path = s;
63 nr_path_duplicate_transform(&abp, &bp, aff);
64 return abp.path;
65 }
67 static void
68 nr_line_wind_distance (NR::Coord x0, NR::Coord y0, NR::Coord x1, NR::Coord y1, NR::Point &pt, int *wind, NR::Coord *best)
69 {
70 NR::Coord Ax, Ay, Bx, By, Dx, Dy, s;
71 NR::Coord dist2;
73 /* Find distance */
74 Ax = x0;
75 Ay = y0;
76 Bx = x1;
77 By = y1;
78 Dx = x1 - x0;
79 Dy = y1 - y0;
80 const NR::Coord Px = pt[NR::X];
81 const NR::Coord Py = pt[NR::Y];
83 if (best) {
84 s = ((Px - Ax) * Dx + (Py - Ay) * Dy) / (Dx * Dx + Dy * Dy);
85 if (s <= 0.0) {
86 dist2 = (Px - Ax) * (Px - Ax) + (Py - Ay) * (Py - Ay);
87 } else if (s >= 1.0) {
88 dist2 = (Px - Bx) * (Px - Bx) + (Py - By) * (Py - By);
89 } else {
90 NR::Coord Qx, Qy;
91 Qx = Ax + s * Dx;
92 Qy = Ay + s * Dy;
93 dist2 = (Px - Qx) * (Px - Qx) + (Py - Qy) * (Py - Qy);
94 }
96 if (dist2 < (*best * *best)) *best = sqrt (dist2);
97 }
99 if (wind) {
100 /* Find wind */
101 if ((Ax >= Px) && (Bx >= Px)) return;
102 if ((Ay >= Py) && (By >= Py)) return;
103 if ((Ay < Py) && (By < Py)) return;
104 if (Ay == By) return;
105 /* Ctach upper y bound */
106 if (Ay == Py) {
107 if (Ax < Px) *wind -= 1;
108 return;
109 } else if (By == Py) {
110 if (Bx < Px) *wind += 1;
111 return;
112 } else {
113 NR::Coord Qx;
114 /* Have to calculate intersection */
115 Qx = Ax + Dx * (Py - Ay) / Dy;
116 if (Qx < Px) {
117 *wind += (Dy > 0.0) ? 1 : -1;
118 }
119 }
120 }
121 }
123 static void
124 nr_curve_bbox_wind_distance (NR::Coord x000, NR::Coord y000,
125 NR::Coord x001, NR::Coord y001,
126 NR::Coord x011, NR::Coord y011,
127 NR::Coord x111, NR::Coord y111,
128 NR::Point &pt,
129 NRRect *bbox, int *wind, NR::Coord *best,
130 NR::Coord tolerance)
131 {
132 NR::Coord x0, y0, x1, y1, len2;
133 int needdist, needwind, needline;
135 const NR::Coord Px = pt[NR::X];
136 const NR::Coord Py = pt[NR::Y];
138 needdist = 0;
139 needwind = 0;
140 needline = 0;
142 if (bbox) nr_curve_bbox (x000, y000, x001, y001, x011, y011, x111, y111, bbox);
144 x0 = MIN (x000, x001);
145 x0 = MIN (x0, x011);
146 x0 = MIN (x0, x111);
147 y0 = MIN (y000, y001);
148 y0 = MIN (y0, y011);
149 y0 = MIN (y0, y111);
150 x1 = MAX (x000, x001);
151 x1 = MAX (x1, x011);
152 x1 = MAX (x1, x111);
153 y1 = MAX (y000, y001);
154 y1 = MAX (y1, y011);
155 y1 = MAX (y1, y111);
157 if (best) {
158 /* Quicly adjust to endpoints */
159 len2 = (x000 - Px) * (x000 - Px) + (y000 - Py) * (y000 - Py);
160 if (len2 < (*best * *best)) *best = (NR::Coord) sqrt (len2);
161 len2 = (x111 - Px) * (x111 - Px) + (y111 - Py) * (y111 - Py);
162 if (len2 < (*best * *best)) *best = (NR::Coord) sqrt (len2);
164 if (((x0 - Px) < *best) && ((y0 - Py) < *best) && ((Px - x1) < *best) && ((Py - y1) < *best)) {
165 /* Point is inside sloppy bbox */
166 /* Now we have to decide, whether subdivide */
167 /* fixme: (Lauris) */
168 if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) {
169 needdist = 1;
170 } else {
171 needline = 1;
172 }
173 }
174 }
175 if (!needdist && wind) {
176 if ((y1 >= Py) && (y0 < Py) && (x0 < Px)) {
177 /* Possible intersection at the left */
178 /* Now we have to decide, whether subdivide */
179 /* fixme: (Lauris) */
180 if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) {
181 needwind = 1;
182 } else {
183 needline = 1;
184 }
185 }
186 }
188 if (needdist || needwind) {
189 NR::Coord x00t, x0tt, xttt, x1tt, x11t, x01t;
190 NR::Coord y00t, y0tt, yttt, y1tt, y11t, y01t;
191 NR::Coord s, t;
193 t = 0.5;
194 s = 1 - t;
196 x00t = s * x000 + t * x001;
197 x01t = s * x001 + t * x011;
198 x11t = s * x011 + t * x111;
199 x0tt = s * x00t + t * x01t;
200 x1tt = s * x01t + t * x11t;
201 xttt = s * x0tt + t * x1tt;
203 y00t = s * y000 + t * y001;
204 y01t = s * y001 + t * y011;
205 y11t = s * y011 + t * y111;
206 y0tt = s * y00t + t * y01t;
207 y1tt = s * y01t + t * y11t;
208 yttt = s * y0tt + t * y1tt;
210 nr_curve_bbox_wind_distance (x000, y000, x00t, y00t, x0tt, y0tt, xttt, yttt, pt, NULL, wind, best, tolerance);
211 nr_curve_bbox_wind_distance (xttt, yttt, x1tt, y1tt, x11t, y11t, x111, y111, pt, NULL, wind, best, tolerance);
212 } else if (1 || needline) {
213 nr_line_wind_distance (x000, y000, x111, y111, pt, wind, best);
214 }
215 }
217 void
218 nr_path_matrix_point_bbox_wind_distance (NRBPath *bpath, NR::Matrix const &m, NR::Point &pt,
219 NRRect *bbox, int *wind, NR::Coord *dist,
220 NR::Coord tolerance)
221 {
222 NR::Coord x0, y0, x3, y3;
223 const NArtBpath *p;
225 if (!bpath->path) {
226 if (wind) *wind = 0;
227 if (dist) *dist = NR_HUGE;
228 return;
229 }
231 x0 = y0 = 0.0;
232 x3 = y3 = 0.0;
234 for (p = bpath->path; p->code != NR_END; p+= 1) {
235 switch (p->code) {
236 case NR_MOVETO_OPEN:
237 case NR_MOVETO:
238 x0 = m[0] * p->x3 + m[2] * p->y3 + m[4];
239 y0 = m[1] * p->x3 + m[3] * p->y3 + m[5];
240 if (bbox) {
241 bbox->x0 = (NR::Coord) MIN (bbox->x0, x0);
242 bbox->y0 = (NR::Coord) MIN (bbox->y0, y0);
243 bbox->x1 = (NR::Coord) MAX (bbox->x1, x0);
244 bbox->y1 = (NR::Coord) MAX (bbox->y1, y0);
245 }
246 break;
247 case NR_LINETO:
248 x3 = m[0] * p->x3 + m[2] * p->y3 + m[4];
249 y3 = m[1] * p->x3 + m[3] * p->y3 + m[5];
250 if (bbox) {
251 bbox->x0 = (NR::Coord) MIN (bbox->x0, x3);
252 bbox->y0 = (NR::Coord) MIN (bbox->y0, y3);
253 bbox->x1 = (NR::Coord) MAX (bbox->x1, x3);
254 bbox->y1 = (NR::Coord) MAX (bbox->y1, y3);
255 }
256 if (dist || wind) {
257 nr_line_wind_distance (x0, y0, x3, y3, pt, wind, dist);
258 }
259 x0 = x3;
260 y0 = y3;
261 break;
262 case NR_CURVETO:
263 x3 = m[0] * p->x3 + m[2] * p->y3 + m[4];
264 y3 = m[1] * p->x3 + m[3] * p->y3 + m[5];
265 nr_curve_bbox_wind_distance (x0, y0,
266 m[0] * p->x1 + m[2] * p->y1 + m[4],
267 m[1] * p->x1 + m[3] * p->y1 + m[5],
268 m[0] * p->x2 + m[2] * p->y2 + m[4],
269 m[1] * p->x2 + m[3] * p->y2 + m[5],
270 x3, y3,
271 pt,
272 bbox, wind, dist, tolerance);
273 x0 = x3;
274 y0 = y3;
275 break;
276 default:
277 break;
278 }
279 }
280 }
282 static void
283 nr_curve_bbox(NR::Point const p000, NR::Point const p001,
284 NR::Point const p011, NR::Point const p111,
285 NRRect *bbox)
286 {
287 using NR::X;
288 using NR::Y;
290 nr_curve_bbox(p000[X], p000[Y],
291 p001[X], p001[Y],
292 p011[X], p011[Y],
293 p111[X], p111[Y],
294 bbox);
295 }
297 /* Fast bbox calculation */
298 /* Thanks to Nathan Hurst for suggesting it */
300 static void
301 nr_curve_bbox (NR::Coord x000, NR::Coord y000, NR::Coord x001, NR::Coord y001, NR::Coord x011, NR::Coord y011, NR::Coord x111, NR::Coord y111, NRRect *bbox)
302 {
303 NR::Coord a, b, c, D;
305 bbox->x0 = (NR::Coord) MIN (bbox->x0, x111);
306 bbox->y0 = (NR::Coord) MIN (bbox->y0, y111);
307 bbox->x1 = (NR::Coord) MAX (bbox->x1, x111);
308 bbox->y1 = (NR::Coord) MAX (bbox->y1, y111);
310 /*
311 * xttt = s * (s * (s * x000 + t * x001) + t * (s * x001 + t * x011)) + t * (s * (s * x001 + t * x011) + t * (s * x011 + t * x111))
312 * xttt = s * (s2 * x000 + s * t * x001 + t * s * x001 + t2 * x011) + t * (s2 * x001 + s * t * x011 + t * s * x011 + t2 * x111)
313 * xttt = s * (s2 * x000 + 2 * st * x001 + t2 * x011) + t * (s2 * x001 + 2 * st * x011 + t2 * x111)
314 * xttt = s3 * x000 + 2 * s2t * x001 + st2 * x011 + s2t * x001 + 2st2 * x011 + t3 * x111
315 * xttt = s3 * x000 + 3s2t * x001 + 3st2 * x011 + t3 * x111
316 * xttt = s3 * x000 + (1 - s) 3s2 * x001 + (1 - s) * (1 - s) * 3s * x011 + (1 - s) * (1 - s) * (1 - s) * x111
317 * xttt = s3 * x000 + (3s2 - 3s3) * x001 + (3s - 6s2 + 3s3) * x011 + (1 - 2s + s2 - s + 2s2 - s3) * x111
318 * xttt = (x000 - 3 * x001 + 3 * x011 - x111) * s3 +
319 * ( 3 * x001 - 6 * x011 + 3 * x111) * s2 +
320 * ( 3 * x011 - 3 * x111) * s +
321 * ( x111)
322 * xttt' = (3 * x000 - 9 * x001 + 9 * x011 - 3 * x111) * s2 +
323 * ( 6 * x001 - 12 * x011 + 6 * x111) * s +
324 * ( 3 * x011 - 3 * x111)
325 */
327 a = 3 * x000 - 9 * x001 + 9 * x011 - 3 * x111;
328 b = 6 * x001 - 12 * x011 + 6 * x111;
329 c = 3 * x011 - 3 * x111;
331 /*
332 * s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a;
333 */
334 if (fabs (a) < NR_EPSILON) {
335 /* s = -c / b */
336 if (fabs (b) > NR_EPSILON) {
337 double s, t, xttt;
338 s = -c / b;
339 if ((s > 0.0) && (s < 1.0)) {
340 t = 1.0 - s;
341 xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
342 bbox->x0 = (float) MIN (bbox->x0, xttt);
343 bbox->x1 = (float) MAX (bbox->x1, xttt);
344 }
345 }
346 } else {
347 /* s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a; */
348 D = b * b - 4 * a * c;
349 if (D >= 0.0) {
350 NR::Coord d, s, t, xttt;
351 /* Have solution */
352 d = sqrt (D);
353 s = (-b + d) / (2 * a);
354 if ((s > 0.0) && (s < 1.0)) {
355 t = 1.0 - s;
356 xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
357 bbox->x0 = (NR::Coord) MIN (bbox->x0, xttt);
358 bbox->x1 = (NR::Coord) MAX (bbox->x1, xttt);
359 }
360 s = (-b - d) / (2 * a);
361 if ((s > 0.0) && (s < 1.0)) {
362 t = 1.0 - s;
363 xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
364 bbox->x0 = (NR::Coord) MIN (bbox->x0, xttt);
365 bbox->x1 = (NR::Coord) MAX (bbox->x1, xttt);
366 }
367 }
368 }
370 a = 3 * y000 - 9 * y001 + 9 * y011 - 3 * y111;
371 b = 6 * y001 - 12 * y011 + 6 * y111;
372 c = 3 * y011 - 3 * y111;
374 if (fabs (a) < NR_EPSILON) {
375 /* s = -c / b */
376 if (fabs (b) > NR_EPSILON) {
377 double s, t, yttt;
378 s = -c / b;
379 if ((s > 0.0) && (s < 1.0)) {
380 t = 1.0 - s;
381 yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
382 bbox->y0 = (float) MIN (bbox->y0, yttt);
383 bbox->y1 = (float) MAX (bbox->y1, yttt);
384 }
385 }
386 } else {
387 /* s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a; */
388 D = b * b - 4 * a * c;
389 if (D >= 0.0) {
390 NR::Coord d, s, t, yttt;
391 /* Have solution */
392 d = sqrt (D);
393 s = (-b + d) / (2 * a);
394 if ((s > 0.0) && (s < 1.0)) {
395 t = 1.0 - s;
396 yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
397 bbox->y0 = (NR::Coord) MIN (bbox->y0, yttt);
398 bbox->y1 = (NR::Coord) MAX (bbox->y1, yttt);
399 }
400 s = (-b - d) / (2 * a);
401 if ((s > 0.0) && (s < 1.0)) {
402 t = 1.0 - s;
403 yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
404 bbox->y0 = (NR::Coord) MIN (bbox->y0, yttt);
405 bbox->y1 = (NR::Coord) MAX (bbox->y1, yttt);
406 }
407 }
408 }
409 }
411 void
412 nr_path_matrix_bbox_union(NRBPath const *bpath, NR::Matrix const &m,
413 NRRect *bbox)
414 {
415 using NR::X;
416 using NR::Y;
418 if (!bpath->path) {
419 return;
420 }
422 NR::Point prev0(0, 0);
423 for (NArtBpath const *p = bpath->path; p->code != NR_END; ++p) {
424 switch (p->code) {
425 case NR_MOVETO_OPEN:
426 case NR_MOVETO: {
427 NR::Point const c3(p->x3,
428 p->y3);
429 prev0 = c3 * m;
430 nr_rect_union_pt(bbox, prev0);
431 break;
432 }
434 case NR_LINETO: {
435 NR::Point const c3(p->x3,
436 p->y3);
437 NR::Point const endPt( c3 * m );
438 nr_rect_union_pt(bbox, endPt);
439 prev0 = endPt;
440 break;
441 }
443 case NR_CURVETO: {
444 NR::Point const c1(p->x1, p->y1);
445 NR::Point const c2(p->x2, p->y2);
446 NR::Point const c3(p->x3, p->y3);
447 NR::Point const endPt( c3 * m );
448 nr_curve_bbox(prev0,
449 c1 * m,
450 c2 * m,
451 endPt,
452 bbox);
453 prev0 = endPt;
454 break;
455 }
457 default:
458 break;
459 }
460 }
461 }