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, const_NRBPath *s, NR::Matrix 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].setC(1, s->path[i].c(1) * (*transform));
42 d->path[i].setC(2, s->path[i].c(2) * (*transform));
43 }
44 d->path[i].setC(3, s->path[i].c(3) * (*transform));
45 i += 1;
46 }
47 d->path[i].code = NR_END;
49 return d;
50 }
52 NRBPath *nr_path_duplicate_transform(NRBPath *d, const_NRBPath *s, NR::Matrix const transform) {
53 NR::Matrix tr = transform;
54 return nr_path_duplicate_transform(d, s, &tr);
55 }
57 NArtBpath* nr_artpath_affine(NArtBpath const *s, NR::Matrix const &aff) {
58 const_NRBPath bp;
59 bp.path = s;
60 NRBPath abp;
61 nr_path_duplicate_transform(&abp, &bp, aff);
62 return abp.path;
63 }
65 static void
66 nr_line_wind_distance (NR::Coord x0, NR::Coord y0, NR::Coord x1, NR::Coord y1, NR::Point &pt, int *wind, NR::Coord *best)
67 {
68 NR::Coord Ax, Ay, Bx, By, Dx, Dy, s;
69 NR::Coord dist2;
71 /* Find distance */
72 Ax = x0;
73 Ay = y0;
74 Bx = x1;
75 By = y1;
76 Dx = x1 - x0;
77 Dy = y1 - y0;
78 const NR::Coord Px = pt[NR::X];
79 const NR::Coord Py = pt[NR::Y];
81 if (best) {
82 s = ((Px - Ax) * Dx + (Py - Ay) * Dy) / (Dx * Dx + Dy * Dy);
83 if (s <= 0.0) {
84 dist2 = (Px - Ax) * (Px - Ax) + (Py - Ay) * (Py - Ay);
85 } else if (s >= 1.0) {
86 dist2 = (Px - Bx) * (Px - Bx) + (Py - By) * (Py - By);
87 } else {
88 NR::Coord Qx, Qy;
89 Qx = Ax + s * Dx;
90 Qy = Ay + s * Dy;
91 dist2 = (Px - Qx) * (Px - Qx) + (Py - Qy) * (Py - Qy);
92 }
94 if (dist2 < (*best * *best)) *best = sqrt (dist2);
95 }
97 if (wind) {
98 /* Find wind */
99 if ((Ax >= Px) && (Bx >= Px)) return;
100 if ((Ay >= Py) && (By >= Py)) return;
101 if ((Ay < Py) && (By < Py)) return;
102 if (Ay == By) return;
103 /* Ctach upper y bound */
104 if (Ay == Py) {
105 if (Ax < Px) *wind -= 1;
106 return;
107 } else if (By == Py) {
108 if (Bx < Px) *wind += 1;
109 return;
110 } else {
111 NR::Coord Qx;
112 /* Have to calculate intersection */
113 Qx = Ax + Dx * (Py - Ay) / Dy;
114 if (Qx < Px) {
115 *wind += (Dy > 0.0) ? 1 : -1;
116 }
117 }
118 }
119 }
121 static void
122 nr_curve_bbox_wind_distance (NR::Coord x000, NR::Coord y000,
123 NR::Coord x001, NR::Coord y001,
124 NR::Coord x011, NR::Coord y011,
125 NR::Coord x111, NR::Coord y111,
126 NR::Point &pt,
127 NRRect *bbox, int *wind, NR::Coord *best,
128 NR::Coord tolerance)
129 {
130 NR::Coord x0, y0, x1, y1, len2;
131 int needdist, needwind, needline;
133 const NR::Coord Px = pt[NR::X];
134 const NR::Coord Py = pt[NR::Y];
136 needdist = 0;
137 needwind = 0;
138 needline = 0;
140 if (bbox) nr_curve_bbox (x000, y000, x001, y001, x011, y011, x111, y111, bbox);
142 x0 = MIN (x000, x001);
143 x0 = MIN (x0, x011);
144 x0 = MIN (x0, x111);
145 y0 = MIN (y000, y001);
146 y0 = MIN (y0, y011);
147 y0 = MIN (y0, y111);
148 x1 = MAX (x000, x001);
149 x1 = MAX (x1, x011);
150 x1 = MAX (x1, x111);
151 y1 = MAX (y000, y001);
152 y1 = MAX (y1, y011);
153 y1 = MAX (y1, y111);
155 if (best) {
156 /* Quicly adjust to endpoints */
157 len2 = (x000 - Px) * (x000 - Px) + (y000 - Py) * (y000 - Py);
158 if (len2 < (*best * *best)) *best = (NR::Coord) sqrt (len2);
159 len2 = (x111 - Px) * (x111 - Px) + (y111 - Py) * (y111 - Py);
160 if (len2 < (*best * *best)) *best = (NR::Coord) sqrt (len2);
162 if (((x0 - Px) < *best) && ((y0 - Py) < *best) && ((Px - x1) < *best) && ((Py - y1) < *best)) {
163 /* Point is inside sloppy bbox */
164 /* Now we have to decide, whether subdivide */
165 /* fixme: (Lauris) */
166 if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) {
167 needdist = 1;
168 } else {
169 needline = 1;
170 }
171 }
172 }
173 if (!needdist && wind) {
174 if ((y1 >= Py) && (y0 < Py) && (x0 < Px)) {
175 /* Possible intersection at the left */
176 /* Now we have to decide, whether subdivide */
177 /* fixme: (Lauris) */
178 if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) {
179 needwind = 1;
180 } else {
181 needline = 1;
182 }
183 }
184 }
186 if (needdist || needwind) {
187 NR::Coord x00t, x0tt, xttt, x1tt, x11t, x01t;
188 NR::Coord y00t, y0tt, yttt, y1tt, y11t, y01t;
189 NR::Coord s, t;
191 t = 0.5;
192 s = 1 - t;
194 x00t = s * x000 + t * x001;
195 x01t = s * x001 + t * x011;
196 x11t = s * x011 + t * x111;
197 x0tt = s * x00t + t * x01t;
198 x1tt = s * x01t + t * x11t;
199 xttt = s * x0tt + t * x1tt;
201 y00t = s * y000 + t * y001;
202 y01t = s * y001 + t * y011;
203 y11t = s * y011 + t * y111;
204 y0tt = s * y00t + t * y01t;
205 y1tt = s * y01t + t * y11t;
206 yttt = s * y0tt + t * y1tt;
208 nr_curve_bbox_wind_distance (x000, y000, x00t, y00t, x0tt, y0tt, xttt, yttt, pt, NULL, wind, best, tolerance);
209 nr_curve_bbox_wind_distance (xttt, yttt, x1tt, y1tt, x11t, y11t, x111, y111, pt, NULL, wind, best, tolerance);
210 } else if (1 || needline) {
211 nr_line_wind_distance (x000, y000, x111, y111, pt, wind, best);
212 }
213 }
215 void
216 nr_path_matrix_point_bbox_wind_distance (const_NRBPath const *bpath, NR::Matrix const &m, NR::Point &pt,
217 NRRect *bbox, int *wind, NR::Coord *dist,
218 NR::Coord tolerance, NR::Rect *viewbox)
219 {
220 if (!bpath->path) {
221 if (wind) *wind = 0;
222 if (dist) *dist = NR_HUGE;
223 return;
224 }
226 NR::Coord x0 = 0;
227 NR::Coord y0 = 0;
228 NR::Coord x1 = 0;
229 NR::Coord y1 = 0;
230 NR::Coord x2 = 0;
231 NR::Coord y2 = 0;
232 NR::Coord x3 = 0;
233 NR::Coord y3 = 0;
235 // remembering the start of subpath
236 NR::Coord x_start = 0, y_start = 0; bool start_set = false;
237 NR::Rect swept;
239 for (const NArtBpath *p = bpath->path; p->code != NR_END; p+= 1) {
240 switch (p->code) {
241 case NR_MOVETO_OPEN:
242 case NR_MOVETO:
243 if (start_set) { // this is a new subpath
244 if (wind && (x0 != x_start || y0 != y_start)) // for correct fill picking, each subpath must be closed
245 nr_line_wind_distance (x0, y0, x_start, y_start, pt, wind, dist);
246 }
247 x0 = m[0] * p->x3 + m[2] * p->y3 + m[4];
248 y0 = m[1] * p->x3 + m[3] * p->y3 + m[5];
249 x_start = x0; y_start = y0; start_set = true;
250 if (bbox) {
251 bbox->x0 = (NR::Coord) MIN (bbox->x0, x0);
252 bbox->y0 = (NR::Coord) MIN (bbox->y0, y0);
253 bbox->x1 = (NR::Coord) MAX (bbox->x1, x0);
254 bbox->y1 = (NR::Coord) MAX (bbox->y1, y0);
255 }
256 break;
257 case NR_LINETO:
258 x3 = m[0] * p->x3 + m[2] * p->y3 + m[4];
259 y3 = m[1] * p->x3 + m[3] * p->y3 + m[5];
260 if (bbox) {
261 bbox->x0 = (NR::Coord) MIN (bbox->x0, x3);
262 bbox->y0 = (NR::Coord) MIN (bbox->y0, y3);
263 bbox->x1 = (NR::Coord) MAX (bbox->x1, x3);
264 bbox->y1 = (NR::Coord) MAX (bbox->y1, y3);
265 }
266 if (dist || wind) {
267 if (wind) { // we need to pick fill, so do what we're told
268 nr_line_wind_distance (x0, y0, x3, y3, pt, wind, dist);
269 } else { // only stroke is being picked; skip this segment if it's totally outside the viewbox
270 swept = NR::Rect(NR::Point(x0, y0), NR::Point(x3, y3));
271 //std::cout << "swept: " << swept;
272 //std::cout << "view: " << *viewbox;
273 //std::cout << "intersects? " << (swept.intersects(*viewbox)? "YES" : "NO") << "\n";
274 if (!viewbox || swept.intersects(*viewbox))
275 nr_line_wind_distance (x0, y0, x3, y3, pt, wind, dist);
276 }
277 }
278 x0 = x3;
279 y0 = y3;
280 break;
281 case NR_CURVETO:
282 {
283 x3 = m[0] * p->x3 + m[2] * p->y3 + m[4];
284 y3 = m[1] * p->x3 + m[3] * p->y3 + m[5];
285 x1 = m[0] * p->x1 + m[2] * p->y1 + m[4];
286 y1 = m[1] * p->x1 + m[3] * p->y1 + m[5];
287 x2 = m[0] * p->x2 + m[2] * p->y2 + m[4];
288 y2 = m[1] * p->x2 + m[3] * p->y2 + m[5];
290 swept = NR::Rect(NR::Point(x0, y0), NR::Point(x3, y3));
291 swept.expandTo(NR::Point(x1, y1));
292 swept.expandTo(NR::Point(x2, y2));
294 if (!viewbox || swept.intersects(*viewbox)) { // we see this segment, so do full processing
295 nr_curve_bbox_wind_distance (
296 x0, y0,
297 x1, y1,
298 x2, y2,
299 x3, y3,
300 pt,
301 bbox, wind, dist, tolerance);
302 } else {
303 if (wind) { // if we need fill, we can just pretend it's a straight line
304 nr_line_wind_distance (x0, y0, x3, y3, pt, wind, dist);
305 } else { // otherwise, skip it completely
306 }
307 }
308 x0 = x3;
309 y0 = y3;
310 }
311 break;
312 default:
313 break;
314 }
315 }
317 if (start_set) {
318 if (wind && (x0 != x_start || y0 != y_start)) // for correct picking, each subpath must be closed
319 nr_line_wind_distance (x0, y0, x_start, y_start, pt, wind, dist);
320 }
321 }
323 static void
324 nr_curve_bbox(NR::Point const p000, NR::Point const p001,
325 NR::Point const p011, NR::Point const p111,
326 NRRect *bbox)
327 {
328 using NR::X;
329 using NR::Y;
331 nr_curve_bbox(p000[X], p000[Y],
332 p001[X], p001[Y],
333 p011[X], p011[Y],
334 p111[X], p111[Y],
335 bbox);
336 }
338 /* Fast bbox calculation */
339 /* Thanks to Nathan Hurst for suggesting it */
341 static void
342 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)
343 {
344 NR::Coord a, b, c, D;
346 bbox->x0 = (NR::Coord) MIN (bbox->x0, x111);
347 bbox->y0 = (NR::Coord) MIN (bbox->y0, y111);
348 bbox->x1 = (NR::Coord) MAX (bbox->x1, x111);
349 bbox->y1 = (NR::Coord) MAX (bbox->y1, y111);
351 /*
352 * xttt = s * (s * (s * x000 + t * x001) + t * (s * x001 + t * x011)) + t * (s * (s * x001 + t * x011) + t * (s * x011 + t * x111))
353 * xttt = s * (s2 * x000 + s * t * x001 + t * s * x001 + t2 * x011) + t * (s2 * x001 + s * t * x011 + t * s * x011 + t2 * x111)
354 * xttt = s * (s2 * x000 + 2 * st * x001 + t2 * x011) + t * (s2 * x001 + 2 * st * x011 + t2 * x111)
355 * xttt = s3 * x000 + 2 * s2t * x001 + st2 * x011 + s2t * x001 + 2st2 * x011 + t3 * x111
356 * xttt = s3 * x000 + 3s2t * x001 + 3st2 * x011 + t3 * x111
357 * xttt = s3 * x000 + (1 - s) 3s2 * x001 + (1 - s) * (1 - s) * 3s * x011 + (1 - s) * (1 - s) * (1 - s) * x111
358 * xttt = s3 * x000 + (3s2 - 3s3) * x001 + (3s - 6s2 + 3s3) * x011 + (1 - 2s + s2 - s + 2s2 - s3) * x111
359 * xttt = (x000 - 3 * x001 + 3 * x011 - x111) * s3 +
360 * ( 3 * x001 - 6 * x011 + 3 * x111) * s2 +
361 * ( 3 * x011 - 3 * x111) * s +
362 * ( x111)
363 * xttt' = (3 * x000 - 9 * x001 + 9 * x011 - 3 * x111) * s2 +
364 * ( 6 * x001 - 12 * x011 + 6 * x111) * s +
365 * ( 3 * x011 - 3 * x111)
366 */
368 a = 3 * x000 - 9 * x001 + 9 * x011 - 3 * x111;
369 b = 6 * x001 - 12 * x011 + 6 * x111;
370 c = 3 * x011 - 3 * x111;
372 /*
373 * s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a;
374 */
375 if (fabs (a) < NR_EPSILON) {
376 /* s = -c / b */
377 if (fabs (b) > NR_EPSILON) {
378 double s, t, xttt;
379 s = -c / b;
380 if ((s > 0.0) && (s < 1.0)) {
381 t = 1.0 - s;
382 xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
383 bbox->x0 = (float) MIN (bbox->x0, xttt);
384 bbox->x1 = (float) MAX (bbox->x1, xttt);
385 }
386 }
387 } else {
388 /* s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a; */
389 D = b * b - 4 * a * c;
390 if (D >= 0.0) {
391 NR::Coord d, s, t, xttt;
392 /* Have solution */
393 d = sqrt (D);
394 s = (-b + d) / (2 * a);
395 if ((s > 0.0) && (s < 1.0)) {
396 t = 1.0 - s;
397 xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
398 bbox->x0 = (NR::Coord) MIN (bbox->x0, xttt);
399 bbox->x1 = (NR::Coord) MAX (bbox->x1, xttt);
400 }
401 s = (-b - d) / (2 * a);
402 if ((s > 0.0) && (s < 1.0)) {
403 t = 1.0 - s;
404 xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
405 bbox->x0 = (NR::Coord) MIN (bbox->x0, xttt);
406 bbox->x1 = (NR::Coord) MAX (bbox->x1, xttt);
407 }
408 }
409 }
411 a = 3 * y000 - 9 * y001 + 9 * y011 - 3 * y111;
412 b = 6 * y001 - 12 * y011 + 6 * y111;
413 c = 3 * y011 - 3 * y111;
415 if (fabs (a) < NR_EPSILON) {
416 /* s = -c / b */
417 if (fabs (b) > NR_EPSILON) {
418 double s, t, yttt;
419 s = -c / b;
420 if ((s > 0.0) && (s < 1.0)) {
421 t = 1.0 - s;
422 yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
423 bbox->y0 = (float) MIN (bbox->y0, yttt);
424 bbox->y1 = (float) MAX (bbox->y1, yttt);
425 }
426 }
427 } else {
428 /* s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a; */
429 D = b * b - 4 * a * c;
430 if (D >= 0.0) {
431 NR::Coord d, s, t, yttt;
432 /* Have solution */
433 d = sqrt (D);
434 s = (-b + d) / (2 * a);
435 if ((s > 0.0) && (s < 1.0)) {
436 t = 1.0 - s;
437 yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
438 bbox->y0 = (NR::Coord) MIN (bbox->y0, yttt);
439 bbox->y1 = (NR::Coord) MAX (bbox->y1, yttt);
440 }
441 s = (-b - d) / (2 * a);
442 if ((s > 0.0) && (s < 1.0)) {
443 t = 1.0 - s;
444 yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
445 bbox->y0 = (NR::Coord) MIN (bbox->y0, yttt);
446 bbox->y1 = (NR::Coord) MAX (bbox->y1, yttt);
447 }
448 }
449 }
450 }
452 void
453 nr_path_matrix_bbox_union(const_NRBPath *bpath, NR::Matrix const &m,
454 NRRect *bbox)
455 {
456 using NR::X;
457 using NR::Y;
459 if (!bpath->path) {
460 return;
461 }
463 NR::Point prev0(0, 0);
464 for (NArtBpath const *p = bpath->path; p->code != NR_END; ++p) {
465 switch (p->code) {
466 case NR_MOVETO_OPEN:
467 case NR_MOVETO: {
468 NR::Point const c3(p->x3,
469 p->y3);
470 prev0 = c3 * m;
471 nr_rect_union_pt(bbox, prev0);
472 break;
473 }
475 case NR_LINETO: {
476 NR::Point const c3(p->x3,
477 p->y3);
478 NR::Point const endPt( c3 * m );
479 nr_rect_union_pt(bbox, endPt);
480 prev0 = endPt;
481 break;
482 }
484 case NR_CURVETO: {
485 NR::Point const c1(p->x1, p->y1);
486 NR::Point const c2(p->x2, p->y2);
487 NR::Point const c3(p->x3, p->y3);
488 NR::Point const endPt( c3 * m );
489 nr_curve_bbox(prev0,
490 c1 * m,
491 c2 * m,
492 endPt,
493 bbox);
494 prev0 = endPt;
495 break;
496 }
498 default:
499 break;
500 }
501 }
502 }