2 |
- |
1 |
/* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
|
|
|
2 |
|
|
|
3 |
This software is provided AS-IS with no warranty, either express or
|
|
|
4 |
implied.
|
|
|
5 |
|
|
|
6 |
This software is distributed under license and may not be copied,
|
|
|
7 |
modified or distributed except as expressly authorized under the terms
|
|
|
8 |
of the license contained in the file LICENSE in this distribution.
|
|
|
9 |
|
|
|
10 |
For more information about licensing, please refer to
|
|
|
11 |
http://www.ghostscript.com/licensing/. For information on
|
|
|
12 |
commercial licensing, go to http://www.artifex.com/licensing/ or
|
|
|
13 |
contact Artifex Software, Inc., 101 Lucas Valley Road #110,
|
|
|
14 |
San Rafael, CA 94903, U.S.A., +1(415)492-9861.
|
|
|
15 |
*/
|
|
|
16 |
|
|
|
17 |
/* $Id: gspath1.c,v 1.10 2004/08/31 13:23:16 igor Exp $ */
|
|
|
18 |
/* Additional PostScript Level 1 path routines for Ghostscript library */
|
|
|
19 |
#include "math_.h"
|
|
|
20 |
#include "gx.h"
|
|
|
21 |
#include "gserrors.h"
|
|
|
22 |
#include "gsstruct.h"
|
|
|
23 |
#include "gxfixed.h"
|
|
|
24 |
#include "gxfarith.h"
|
|
|
25 |
#include "gxmatrix.h"
|
|
|
26 |
#include "gzstate.h"
|
|
|
27 |
#include "gspath.h"
|
|
|
28 |
#include "gzpath.h"
|
|
|
29 |
#include "gscoord.h" /* gs_itransform prototype */
|
|
|
30 |
|
|
|
31 |
/* ------ Arcs ------ */
|
|
|
32 |
|
|
|
33 |
/* Conversion parameters */
|
|
|
34 |
#define degrees_to_radians (M_PI / 180.0)
|
|
|
35 |
|
|
|
36 |
typedef enum {
|
|
|
37 |
arc_nothing,
|
|
|
38 |
arc_moveto,
|
|
|
39 |
arc_lineto
|
|
|
40 |
} arc_action;
|
|
|
41 |
|
|
|
42 |
typedef struct arc_curve_params_s {
|
|
|
43 |
/* The following are set once. */
|
|
|
44 |
gx_path *ppath;
|
|
|
45 |
gs_imager_state *pis;
|
|
|
46 |
gs_point center; /* (not used by arc_add) */
|
|
|
47 |
double radius;
|
|
|
48 |
/* The following may be updated dynamically. */
|
|
|
49 |
arc_action action;
|
|
|
50 |
segment_notes notes;
|
|
|
51 |
gs_point p0, p3, pt;
|
|
|
52 |
gs_sincos_t sincos; /* (not used by arc_add) */
|
|
|
53 |
fixed angle; /* (not used by arc_add) */
|
|
|
54 |
int fast_quadrant; /* 0 = not calculated, -1 = not fast, */
|
|
|
55 |
/* 1 = fast (only used for quadrants) */
|
|
|
56 |
/* The following are set once iff fast_quadrant > 0. */
|
|
|
57 |
fixed scaled_radius; /* radius * CTM scale */
|
|
|
58 |
fixed quadrant_delta; /* scaled_radius * quarter_arc_fraction */
|
|
|
59 |
} arc_curve_params_t;
|
|
|
60 |
|
|
|
61 |
/* Forward declarations */
|
|
|
62 |
private int arc_add(const arc_curve_params_t *arc, bool is_quadrant);
|
|
|
63 |
|
|
|
64 |
|
|
|
65 |
int
|
|
|
66 |
gx_setcurrentpoint_from_path(gs_imager_state *pis, gx_path *path)
|
|
|
67 |
{
|
|
|
68 |
gs_point pt;
|
|
|
69 |
|
|
|
70 |
pt.x = fixed2float(path->position.x);
|
|
|
71 |
pt.y = fixed2float(path->position.y);
|
|
|
72 |
gx_setcurrentpoint(pis, pt.x, pt.y);
|
|
|
73 |
pis->current_point_valid = true;
|
|
|
74 |
return 0;
|
|
|
75 |
}
|
|
|
76 |
|
|
|
77 |
private inline int
|
|
|
78 |
gs_arc_add_inline(gs_state *pgs, bool cw, floatp xc, floatp yc, floatp rad,
|
|
|
79 |
floatp a1, floatp a2, bool add)
|
|
|
80 |
{
|
|
|
81 |
int code = gs_imager_arc_add(pgs->path, (gs_imager_state *)pgs, cw, xc, yc, rad, a1, a2, add);
|
|
|
82 |
|
|
|
83 |
if (code < 0)
|
|
|
84 |
return code;
|
|
|
85 |
return gx_setcurrentpoint_from_path((gs_imager_state *)pgs, pgs->path);
|
|
|
86 |
}
|
|
|
87 |
|
|
|
88 |
int
|
|
|
89 |
gs_arc(gs_state * pgs,
|
|
|
90 |
floatp xc, floatp yc, floatp r, floatp ang1, floatp ang2)
|
|
|
91 |
{
|
|
|
92 |
return gs_arc_add_inline(pgs, false, xc, yc, r, ang1, ang2, true);
|
|
|
93 |
}
|
|
|
94 |
|
|
|
95 |
int
|
|
|
96 |
gs_arcn(gs_state * pgs,
|
|
|
97 |
floatp xc, floatp yc, floatp r, floatp ang1, floatp ang2)
|
|
|
98 |
{
|
|
|
99 |
return gs_arc_add_inline(pgs, true, xc, yc, r, ang1, ang2, true);
|
|
|
100 |
}
|
|
|
101 |
|
|
|
102 |
int
|
|
|
103 |
gs_arc_add(gs_state * pgs, bool clockwise, floatp axc, floatp ayc,
|
|
|
104 |
floatp arad, floatp aang1, floatp aang2, bool add_line)
|
|
|
105 |
{
|
|
|
106 |
return gs_arc_add_inline(pgs, clockwise, axc, ayc, arad,
|
|
|
107 |
aang1, aang2, add_line);
|
|
|
108 |
}
|
|
|
109 |
|
|
|
110 |
/* Compute the next curve as part of an arc. */
|
|
|
111 |
private int
|
|
|
112 |
next_arc_curve(arc_curve_params_t * arc, fixed anext)
|
|
|
113 |
{
|
|
|
114 |
double x0 = arc->p0.x = arc->p3.x;
|
|
|
115 |
double y0 = arc->p0.y = arc->p3.y;
|
|
|
116 |
double trad = arc->radius *
|
|
|
117 |
tan(fixed2float(anext - arc->angle) *
|
|
|
118 |
(degrees_to_radians / 2));
|
|
|
119 |
|
|
|
120 |
arc->pt.x = x0 - trad * arc->sincos.sin;
|
|
|
121 |
arc->pt.y = y0 + trad * arc->sincos.cos;
|
|
|
122 |
gs_sincos_degrees(fixed2float(anext), &arc->sincos);
|
|
|
123 |
arc->p3.x = arc->center.x + arc->radius * arc->sincos.cos;
|
|
|
124 |
arc->p3.y = arc->center.y + arc->radius * arc->sincos.sin;
|
|
|
125 |
arc->angle = anext;
|
|
|
126 |
return arc_add(arc, false);
|
|
|
127 |
}
|
|
|
128 |
/*
|
|
|
129 |
* Use this when both arc.angle and anext are multiples of 90 degrees,
|
|
|
130 |
* and anext = arc.angle +/- 90.
|
|
|
131 |
*/
|
|
|
132 |
private int
|
|
|
133 |
next_arc_quadrant(arc_curve_params_t * arc, fixed anext)
|
|
|
134 |
{
|
|
|
135 |
double x0 = arc->p0.x = arc->p3.x;
|
|
|
136 |
double y0 = arc->p0.y = arc->p3.y;
|
|
|
137 |
|
|
|
138 |
if (!arc->fast_quadrant) {
|
|
|
139 |
/*
|
|
|
140 |
* If the CTM is well-behaved, we can pre-calculate the delta
|
|
|
141 |
* from the arc points to the control points.
|
|
|
142 |
*/
|
|
|
143 |
const gs_imager_state *pis = arc->pis;
|
|
|
144 |
double scale;
|
|
|
145 |
|
|
|
146 |
if (is_fzero2(pis->ctm.xy, pis->ctm.yx) ?
|
|
|
147 |
(scale = fabs(pis->ctm.xx)) == fabs(pis->ctm.yy) :
|
|
|
148 |
is_fzero2(pis->ctm.xx, pis->ctm.yy) ?
|
|
|
149 |
(scale = fabs(pis->ctm.xy)) == fabs(pis->ctm.yx) :
|
|
|
150 |
|
|
|
151 |
) {
|
|
|
152 |
double scaled_radius = arc->radius * scale;
|
|
|
153 |
|
|
|
154 |
arc->scaled_radius = float2fixed(scaled_radius);
|
|
|
155 |
arc->quadrant_delta =
|
|
|
156 |
float2fixed(scaled_radius * quarter_arc_fraction);
|
|
|
157 |
arc->fast_quadrant = 1;
|
|
|
158 |
} else {
|
|
|
159 |
arc->fast_quadrant = -1;
|
|
|
160 |
}
|
|
|
161 |
}
|
|
|
162 |
/*
|
|
|
163 |
* We know that anext is a multiple of 90 (as a fixed); we want
|
|
|
164 |
* (anext / 90) & 3. The following is much faster than a division.
|
|
|
165 |
*/
|
|
|
166 |
switch ((fixed2int(anext) >> 1) & 3) {
|
|
|
167 |
case 0:
|
|
|
168 |
arc->sincos.sin = 0, arc->sincos.cos = 1;
|
|
|
169 |
arc->p3.x = x0 = arc->center.x + arc->radius;
|
|
|
170 |
arc->p3.y = arc->center.y;
|
|
|
171 |
break;
|
|
|
172 |
case 1:
|
|
|
173 |
arc->sincos.sin = 1, arc->sincos.cos = 0;
|
|
|
174 |
arc->p3.x = arc->center.x;
|
|
|
175 |
arc->p3.y = y0 = arc->center.y + arc->radius;
|
|
|
176 |
break;
|
|
|
177 |
case 2:
|
|
|
178 |
arc->sincos.sin = 0, arc->sincos.cos = -1;
|
|
|
179 |
arc->p3.x = x0 = arc->center.x - arc->radius;
|
|
|
180 |
arc->p3.y = arc->center.y;
|
|
|
181 |
break;
|
|
|
182 |
case 3:
|
|
|
183 |
arc->sincos.sin = -1, arc->sincos.cos = 0;
|
|
|
184 |
arc->p3.x = arc->center.x;
|
|
|
185 |
arc->p3.y = y0 = arc->center.y - arc->radius;
|
|
|
186 |
break;
|
|
|
187 |
}
|
|
|
188 |
arc->pt.x = x0, arc->pt.y = y0;
|
|
|
189 |
arc->angle = anext;
|
|
|
190 |
return arc_add(arc, true);
|
|
|
191 |
}
|
|
|
192 |
|
|
|
193 |
int
|
|
|
194 |
gs_imager_arc_add(gx_path * ppath, gs_imager_state * pis, bool clockwise,
|
|
|
195 |
floatp axc, floatp ayc, floatp arad, floatp aang1, floatp aang2,
|
|
|
196 |
bool add_line)
|
|
|
197 |
{
|
|
|
198 |
double ar = arad;
|
|
|
199 |
fixed ang1 = float2fixed(aang1), ang2 = float2fixed(aang2), anext;
|
|
|
200 |
double ang1r; /* reduced angle */
|
|
|
201 |
arc_curve_params_t arc;
|
|
|
202 |
int code;
|
|
|
203 |
|
|
|
204 |
arc.ppath = ppath;
|
|
|
205 |
arc.pis = pis;
|
|
|
206 |
arc.center.x = axc;
|
|
|
207 |
arc.center.y = ayc;
|
|
|
208 |
#define fixed_90 int2fixed(90)
|
|
|
209 |
#define fixed_180 int2fixed(180)
|
|
|
210 |
#define fixed_360 int2fixed(360)
|
|
|
211 |
if (ar < 0) {
|
|
|
212 |
ang1 += fixed_180;
|
|
|
213 |
ang2 += fixed_180;
|
|
|
214 |
ar = -ar;
|
|
|
215 |
}
|
|
|
216 |
arc.radius = ar;
|
|
|
217 |
arc.action = (add_line ? arc_lineto : arc_moveto);
|
|
|
218 |
arc.notes = sn_none;
|
|
|
219 |
arc.fast_quadrant = 0;
|
|
|
220 |
ang1r = fixed2float(ang1 % fixed_360);
|
|
|
221 |
gs_sincos_degrees(ang1r, &arc.sincos);
|
|
|
222 |
arc.p3.x = axc + ar * arc.sincos.cos;
|
|
|
223 |
arc.p3.y = ayc + ar * arc.sincos.sin;
|
|
|
224 |
if (clockwise) {
|
|
|
225 |
while (ang1 < ang2)
|
|
|
226 |
ang2 -= fixed_360;
|
|
|
227 |
if (ang2 < 0) {
|
|
|
228 |
fixed adjust = ROUND_UP(-ang2, fixed_360);
|
|
|
229 |
|
|
|
230 |
ang1 += adjust, ang2 += adjust;
|
|
|
231 |
}
|
|
|
232 |
arc.angle = ang1;
|
|
|
233 |
if (ang1 == ang2)
|
|
|
234 |
goto last;
|
|
|
235 |
/* Do the first part, up to a multiple of 90 degrees. */
|
|
|
236 |
if (!arc.sincos.orthogonal) {
|
|
|
237 |
anext = ROUND_DOWN(arc.angle - fixed_epsilon, fixed_90);
|
|
|
238 |
if (anext < ang2)
|
|
|
239 |
goto last;
|
|
|
240 |
code = next_arc_curve(&arc, anext);
|
|
|
241 |
if (code < 0)
|
|
|
242 |
return code;
|
|
|
243 |
arc.action = arc_nothing;
|
|
|
244 |
arc.notes = sn_not_first;
|
|
|
245 |
}
|
|
|
246 |
/* Do multiples of 90 degrees. Invariant: ang1 >= ang2 >= 0. */
|
|
|
247 |
while ((anext = arc.angle - fixed_90) >= ang2) {
|
|
|
248 |
code = next_arc_quadrant(&arc, anext);
|
|
|
249 |
if (code < 0)
|
|
|
250 |
return code;
|
|
|
251 |
arc.action = arc_nothing;
|
|
|
252 |
arc.notes = sn_not_first;
|
|
|
253 |
}
|
|
|
254 |
} else {
|
|
|
255 |
while (ang2 < ang1)
|
|
|
256 |
ang2 += fixed_360;
|
|
|
257 |
if (ang1 < 0) {
|
|
|
258 |
fixed adjust = ROUND_UP(-ang1, fixed_360);
|
|
|
259 |
|
|
|
260 |
ang1 += adjust, ang2 += adjust;
|
|
|
261 |
}
|
|
|
262 |
arc.angle = ang1;
|
|
|
263 |
if (ang1 == ang2)
|
|
|
264 |
return next_arc_curve(&arc, ang2);
|
|
|
265 |
/* Do the first part, up to a multiple of 90 degrees. */
|
|
|
266 |
if (!arc.sincos.orthogonal) {
|
|
|
267 |
anext = ROUND_UP(arc.angle + fixed_epsilon, fixed_90);
|
|
|
268 |
if (anext > ang2)
|
|
|
269 |
goto last;
|
|
|
270 |
code = next_arc_curve(&arc, anext);
|
|
|
271 |
if (code < 0)
|
|
|
272 |
return code;
|
|
|
273 |
arc.action = arc_nothing;
|
|
|
274 |
arc.notes = sn_not_first;
|
|
|
275 |
}
|
|
|
276 |
/* Do multiples of 90 degrees. Invariant: 0 <= ang1 <= ang2. */
|
|
|
277 |
while ((anext = arc.angle + fixed_90) <= ang2) {
|
|
|
278 |
code = next_arc_quadrant(&arc, anext);
|
|
|
279 |
if (code < 0)
|
|
|
280 |
return code;
|
|
|
281 |
arc.action = arc_nothing;
|
|
|
282 |
arc.notes = sn_not_first;
|
|
|
283 |
}
|
|
|
284 |
}
|
|
|
285 |
/*
|
|
|
286 |
* Do the last curve of the arc, if any.
|
|
|
287 |
*/
|
|
|
288 |
if (arc.angle == ang2)
|
|
|
289 |
return 0;
|
|
|
290 |
last:
|
|
|
291 |
return next_arc_curve(&arc, ang2);
|
|
|
292 |
}
|
|
|
293 |
|
|
|
294 |
int
|
|
|
295 |
gs_arcto(gs_state * pgs,
|
|
|
296 |
floatp ax1, floatp ay1, floatp ax2, floatp ay2, floatp arad, float retxy[4])
|
|
|
297 |
{
|
|
|
298 |
double xt0, yt0, xt2, yt2;
|
|
|
299 |
gs_point up0;
|
|
|
300 |
|
|
|
301 |
#define ax0 up0.x
|
|
|
302 |
#define ay0 up0.y
|
|
|
303 |
/* Transform the current point back into user coordinates. */
|
|
|
304 |
int code = gs_currentpoint(pgs, &up0);
|
|
|
305 |
|
|
|
306 |
if (code < 0)
|
|
|
307 |
return code;
|
|
|
308 |
{ /* Now we have to compute the tangent points. */
|
|
|
309 |
/* Basically, the idea is to compute the tangent */
|
|
|
310 |
/* of the bisector by using tan(x+y) and tan(z/2) */
|
|
|
311 |
/* formulas, without ever using any trig. */
|
|
|
312 |
double dx0 = ax0 - ax1, dy0 = ay0 - ay1;
|
|
|
313 |
double dx2 = ax2 - ax1, dy2 = ay2 - ay1;
|
|
|
314 |
|
|
|
315 |
/* Compute the squared lengths from p1 to p0 and p2. */
|
|
|
316 |
double sql0 = dx0 * dx0 + dy0 * dy0;
|
|
|
317 |
double sql2 = dx2 * dx2 + dy2 * dy2;
|
|
|
318 |
|
|
|
319 |
/* Compute the distance from p1 to the tangent points. */
|
|
|
320 |
/* This is the only messy part. */
|
|
|
321 |
double num = dy0 * dx2 - dy2 * dx0;
|
|
|
322 |
double denom = sqrt(sql0 * sql2) - (dx0 * dx2 + dy0 * dy2);
|
|
|
323 |
|
|
|
324 |
/* Check for collinear points. */
|
|
|
325 |
if (denom == 0) {
|
|
|
326 |
code = gs_lineto(pgs, ax1, ay1);
|
|
|
327 |
xt0 = xt2 = ax1;
|
|
|
328 |
yt0 = yt2 = ay1;
|
|
|
329 |
} else { /* not collinear */
|
|
|
330 |
double dist = fabs(arad * num / denom);
|
|
|
331 |
double l0 = dist / sqrt(sql0), l2 = dist / sqrt(sql2);
|
|
|
332 |
arc_curve_params_t arc;
|
|
|
333 |
|
|
|
334 |
arc.ppath = pgs->path;
|
|
|
335 |
arc.pis = (gs_imager_state *) pgs;
|
|
|
336 |
arc.radius = arad;
|
|
|
337 |
arc.action = arc_lineto;
|
|
|
338 |
arc.notes = sn_none;
|
|
|
339 |
if (arad < 0)
|
|
|
340 |
l0 = -l0, l2 = -l2;
|
|
|
341 |
arc.p0.x = xt0 = ax1 + dx0 * l0;
|
|
|
342 |
arc.p0.y = yt0 = ay1 + dy0 * l0;
|
|
|
343 |
arc.p3.x = xt2 = ax1 + dx2 * l2;
|
|
|
344 |
arc.p3.y = yt2 = ay1 + dy2 * l2;
|
|
|
345 |
arc.pt.x = ax1;
|
|
|
346 |
arc.pt.y = ay1;
|
|
|
347 |
code = arc_add(&arc, false);
|
|
|
348 |
if (code == 0)
|
|
|
349 |
code = gx_setcurrentpoint_from_path((gs_imager_state *)pgs, pgs->path);
|
|
|
350 |
}
|
|
|
351 |
}
|
|
|
352 |
if (retxy != 0) {
|
|
|
353 |
retxy[0] = xt0;
|
|
|
354 |
retxy[1] = yt0;
|
|
|
355 |
retxy[2] = xt2;
|
|
|
356 |
retxy[3] = yt2;
|
|
|
357 |
}
|
|
|
358 |
return code;
|
|
|
359 |
}
|
|
|
360 |
|
|
|
361 |
/* Internal routine for adding an arc to the path. */
|
|
|
362 |
private int
|
|
|
363 |
arc_add(const arc_curve_params_t * arc, bool is_quadrant)
|
|
|
364 |
{
|
|
|
365 |
gx_path *path = arc->ppath;
|
|
|
366 |
gs_imager_state *pis = arc->pis;
|
|
|
367 |
double x0 = arc->p0.x, y0 = arc->p0.y;
|
|
|
368 |
double xt = arc->pt.x, yt = arc->pt.y;
|
|
|
369 |
floatp fraction;
|
|
|
370 |
gs_fixed_point p0, p2, p3, pt;
|
|
|
371 |
int code;
|
|
|
372 |
|
|
|
373 |
if ((arc->action != arc_nothing &&
|
|
|
374 |
#if !PRECISE_CURRENTPOINT
|
|
|
375 |
(code = gs_point_transform2fixed(&pis->ctm, x0, y0, &p0)) < 0) ||
|
|
|
376 |
(code = gs_point_transform2fixed(&pis->ctm, xt, yt, &pt)) < 0 ||
|
|
|
377 |
(code = gs_point_transform2fixed(&pis->ctm, arc->p3.x, arc->p3.y, &p3)) < 0 ||
|
|
|
378 |
#else
|
|
|
379 |
(code = gs_point_transform2fixed_rounding(&pis->ctm, x0, y0, &p0)) < 0) ||
|
|
|
380 |
(code = gs_point_transform2fixed_rounding(&pis->ctm, xt, yt, &pt)) < 0 ||
|
|
|
381 |
(code = gs_point_transform2fixed_rounding(&pis->ctm, arc->p3.x, arc->p3.y, &p3)) < 0 ||
|
|
|
382 |
#endif
|
|
|
383 |
(code =
|
|
|
384 |
(arc->action == arc_nothing ?
|
|
|
385 |
(p0.x = path->position.x, p0.y = path->position.y, 0) :
|
|
|
386 |
arc->action == arc_lineto && path_position_valid(path) ?
|
|
|
387 |
gx_path_add_line(path, p0.x, p0.y) :
|
|
|
388 |
/* action == arc_moveto, or lineto with no current point */
|
|
|
389 |
gx_path_add_point(path, p0.x, p0.y))) < 0
|
|
|
390 |
)
|
|
|
391 |
return code;
|
|
|
392 |
/* Compute the fraction coefficient for the curve. */
|
|
|
393 |
/* See gx_path_add_partial_arc for details. */
|
|
|
394 |
if (is_quadrant) {
|
|
|
395 |
/* one of |dx| and |dy| is r, the other is zero */
|
|
|
396 |
fraction = quarter_arc_fraction;
|
|
|
397 |
if (arc->fast_quadrant > 0) {
|
|
|
398 |
/*
|
|
|
399 |
* The CTM is well-behaved, and we have pre-calculated the delta
|
|
|
400 |
* from the circumference points to the control points.
|
|
|
401 |
*/
|
|
|
402 |
fixed delta = arc->quadrant_delta;
|
|
|
403 |
|
|
|
404 |
if (pt.x != p0.x)
|
|
|
405 |
p0.x = (pt.x > p0.x ? p0.x + delta : p0.x - delta);
|
|
|
406 |
if (pt.y != p0.y)
|
|
|
407 |
p0.y = (pt.y > p0.y ? p0.y + delta : p0.y - delta);
|
|
|
408 |
p2.x = (pt.x == p3.x ? p3.x :
|
|
|
409 |
pt.x > p3.x ? p3.x + delta : p3.x - delta);
|
|
|
410 |
p2.y = (pt.y == p3.y ? p3.y :
|
|
|
411 |
pt.y > p3.y ? p3.y + delta : p3.y - delta);
|
|
|
412 |
goto add;
|
|
|
413 |
}
|
|
|
414 |
} else {
|
|
|
415 |
double r = arc->radius;
|
|
|
416 |
floatp dx = xt - x0, dy = yt - y0;
|
|
|
417 |
double dist = dx * dx + dy * dy;
|
|
|
418 |
double r2 = r * r;
|
|
|
419 |
|
|
|
420 |
if (dist >= r2 * 1.0e8) /* almost zero radius; */
|
|
|
421 |
/* the >= catches dist == r == 0 */
|
|
|
422 |
fraction = 0.0;
|
|
|
423 |
else
|
|
|
424 |
fraction = (4.0 / 3.0) / (1 + sqrt(1 + dist / r2));
|
|
|
425 |
}
|
|
|
426 |
p0.x += (fixed)((pt.x - p0.x) * fraction);
|
|
|
427 |
p0.y += (fixed)((pt.y - p0.y) * fraction);
|
|
|
428 |
p2.x = p3.x + (fixed)((pt.x - p3.x) * fraction);
|
|
|
429 |
p2.y = p3.y + (fixed)((pt.y - p3.y) * fraction);
|
|
|
430 |
add:
|
|
|
431 |
if_debug8('r',
|
|
|
432 |
"[r]Arc f=%f p0=(%f,%f) pt=(%f,%f) p3=(%f,%f) action=%d\n",
|
|
|
433 |
fraction, x0, y0, xt, yt, arc->p3.x, arc->p3.y,
|
|
|
434 |
(int)arc->action);
|
|
|
435 |
/* Open-code gx_path_add_partial_arc_notes */
|
|
|
436 |
return gx_path_add_curve_notes(path, p0.x, p0.y, p2.x, p2.y, p3.x, p3.y,
|
|
|
437 |
arc->notes | sn_from_arc);
|
|
|
438 |
}
|
|
|
439 |
|
|
|
440 |
void
|
|
|
441 |
make_quadrant_arc(gs_point *p, const gs_point *c,
|
|
|
442 |
const gs_point *p0, const gs_point *p1, double r)
|
|
|
443 |
{
|
|
|
444 |
p[0].x = c->x + p0->x * r;
|
|
|
445 |
p[0].y = c->y + p0->y * r;
|
|
|
446 |
p[1].x = c->x + p0->x * r + p1->x * r * quarter_arc_fraction;
|
|
|
447 |
p[1].y = c->y + p0->y * r + p1->y * r * quarter_arc_fraction;
|
|
|
448 |
p[2].x = c->x + p0->x * r * quarter_arc_fraction + p1->x * r;
|
|
|
449 |
p[2].y = c->y + p0->y * r * quarter_arc_fraction + p1->y * r;
|
|
|
450 |
p[3].x = c->x + p1->x * r;
|
|
|
451 |
p[3].y = c->y + p1->y * r;
|
|
|
452 |
}
|
|
|
453 |
|
|
|
454 |
|
|
|
455 |
/* ------ Path transformers ------ */
|
|
|
456 |
|
|
|
457 |
int
|
|
|
458 |
gs_dashpath(gs_state * pgs)
|
|
|
459 |
{
|
|
|
460 |
gx_path *ppath;
|
|
|
461 |
gx_path fpath;
|
|
|
462 |
int code;
|
|
|
463 |
|
|
|
464 |
if (gs_currentdash_length(pgs) == 0)
|
|
|
465 |
return 0; /* no dash pattern */
|
|
|
466 |
code = gs_flattenpath(pgs);
|
|
|
467 |
if (code < 0)
|
|
|
468 |
return code;
|
|
|
469 |
ppath = pgs->path;
|
|
|
470 |
gx_path_init_local(&fpath, ppath->memory);
|
|
|
471 |
code = gx_path_add_dash_expansion(ppath, &fpath, (gs_imager_state *)pgs);
|
|
|
472 |
if (code < 0) {
|
|
|
473 |
gx_path_free(&fpath, "gs_dashpath");
|
|
|
474 |
return code;
|
|
|
475 |
}
|
|
|
476 |
gx_path_assign_free(pgs->path, &fpath);
|
|
|
477 |
return 0;
|
|
|
478 |
}
|
|
|
479 |
|
|
|
480 |
int
|
|
|
481 |
gs_flattenpath(gs_state * pgs)
|
|
|
482 |
{
|
|
|
483 |
gx_path *ppath = pgs->path;
|
|
|
484 |
gx_path fpath;
|
|
|
485 |
int code;
|
|
|
486 |
|
|
|
487 |
if (!gx_path_has_curves(ppath))
|
|
|
488 |
return 0; /* nothing to do */
|
|
|
489 |
gx_path_init_local(&fpath, ppath->memory);
|
|
|
490 |
code = gx_path_add_flattened_accurate(ppath, &fpath, pgs->flatness,
|
|
|
491 |
pgs->accurate_curves);
|
|
|
492 |
if (code < 0) {
|
|
|
493 |
gx_path_free(&fpath, "gs_flattenpath");
|
|
|
494 |
return code;
|
|
|
495 |
}
|
|
|
496 |
gx_path_assign_free(ppath, &fpath);
|
|
|
497 |
return 0;
|
|
|
498 |
}
|
|
|
499 |
|
|
|
500 |
int
|
|
|
501 |
gs_reversepath(gs_state * pgs)
|
|
|
502 |
{
|
|
|
503 |
gx_path *ppath = pgs->path;
|
|
|
504 |
gx_path rpath;
|
|
|
505 |
int code;
|
|
|
506 |
|
|
|
507 |
gx_path_init_local(&rpath, ppath->memory);
|
|
|
508 |
code = gx_path_copy_reversed(ppath, &rpath);
|
|
|
509 |
if (code < 0) {
|
|
|
510 |
gx_path_free(&rpath, "gs_reversepath");
|
|
|
511 |
return code;
|
|
|
512 |
}
|
|
|
513 |
if (pgs->current_point_valid) {
|
|
|
514 |
/* Not empty. */
|
|
|
515 |
gx_setcurrentpoint(pgs, fixed2float(rpath.position.x),
|
|
|
516 |
fixed2float(rpath.position.y));
|
|
|
517 |
pgs->subpath_start.x = fixed2float(rpath.segments->contents.subpath_current->pt.x);
|
|
|
518 |
pgs->subpath_start.y = fixed2float(rpath.segments->contents.subpath_current->pt.y);
|
|
|
519 |
}
|
|
|
520 |
gx_path_assign_free(ppath, &rpath);
|
|
|
521 |
return 0;
|
|
|
522 |
}
|
|
|
523 |
|
|
|
524 |
/* ------ Accessors ------ */
|
|
|
525 |
|
|
|
526 |
int
|
|
|
527 |
gs_upathbbox(gs_state * pgs, gs_rect * pbox, bool include_moveto)
|
|
|
528 |
{
|
|
|
529 |
gs_fixed_rect fbox; /* box in device coordinates */
|
|
|
530 |
gs_rect dbox;
|
|
|
531 |
int code = gx_path_bbox_set(pgs->path, &fbox);
|
|
|
532 |
|
|
|
533 |
if (code < 0)
|
|
|
534 |
return code;
|
|
|
535 |
/* If the path ends with a moveto and include_moveto is true, */
|
|
|
536 |
/* include the moveto in the bounding box. */
|
|
|
537 |
if (path_last_is_moveto(pgs->path) && include_moveto) {
|
|
|
538 |
gs_fixed_point pt;
|
|
|
539 |
|
|
|
540 |
gx_path_current_point_inline(pgs->path, &pt);
|
|
|
541 |
if (pt.x < fbox.p.x)
|
|
|
542 |
fbox.p.x = pt.x;
|
|
|
543 |
if (pt.y < fbox.p.y)
|
|
|
544 |
fbox.p.y = pt.y;
|
|
|
545 |
if (pt.x > fbox.q.x)
|
|
|
546 |
fbox.q.x = pt.x;
|
|
|
547 |
if (pt.y > fbox.q.y)
|
|
|
548 |
fbox.q.y = pt.y;
|
|
|
549 |
}
|
|
|
550 |
/* Transform the result back to user coordinates. */
|
|
|
551 |
dbox.p.x = fixed2float(fbox.p.x);
|
|
|
552 |
dbox.p.y = fixed2float(fbox.p.y);
|
|
|
553 |
dbox.q.x = fixed2float(fbox.q.x);
|
|
|
554 |
dbox.q.y = fixed2float(fbox.q.y);
|
|
|
555 |
return gs_bbox_transform_inverse(&dbox, &ctm_only(pgs), pbox);
|
|
|
556 |
}
|
|
|
557 |
|
|
|
558 |
/* ------ Enumerators ------ */
|
|
|
559 |
|
|
|
560 |
/* Start enumerating a path */
|
|
|
561 |
int
|
|
|
562 |
gs_path_enum_copy_init(gs_path_enum * penum, const gs_state * pgs, bool copy)
|
|
|
563 |
{
|
|
|
564 |
gs_memory_t *mem = pgs->memory;
|
|
|
565 |
|
|
|
566 |
if (copy) {
|
|
|
567 |
gx_path *copied_path =
|
|
|
568 |
gx_path_alloc(mem, "gs_path_enum_init");
|
|
|
569 |
int code;
|
|
|
570 |
|
|
|
571 |
if (copied_path == 0)
|
|
|
572 |
return_error(gs_error_VMerror);
|
|
|
573 |
code = gx_path_copy(pgs->path, copied_path);
|
|
|
574 |
if (code < 0) {
|
|
|
575 |
gx_path_free(copied_path, "gs_path_enum_init");
|
|
|
576 |
return code;
|
|
|
577 |
}
|
|
|
578 |
gx_path_enum_init(penum, copied_path);
|
|
|
579 |
penum->copied_path = copied_path;
|
|
|
580 |
} else {
|
|
|
581 |
gx_path_enum_init(penum, pgs->path);
|
|
|
582 |
}
|
|
|
583 |
penum->memory = mem;
|
|
|
584 |
gs_currentmatrix(pgs, &penum->mat);
|
|
|
585 |
return 0;
|
|
|
586 |
}
|
|
|
587 |
|
|
|
588 |
/* Enumerate the next element of a path. */
|
|
|
589 |
/* If the path is finished, return 0; */
|
|
|
590 |
/* otherwise, return the element type. */
|
|
|
591 |
int
|
|
|
592 |
gs_path_enum_next(gs_path_enum * penum, gs_point ppts[3])
|
|
|
593 |
{
|
|
|
594 |
gs_fixed_point fpts[3];
|
|
|
595 |
int pe_op = gx_path_enum_next(penum, fpts);
|
|
|
596 |
int code;
|
|
|
597 |
|
|
|
598 |
switch (pe_op) {
|
|
|
599 |
case 0: /* all done */
|
|
|
600 |
case gs_pe_closepath:
|
|
|
601 |
break;
|
|
|
602 |
case gs_pe_curveto:
|
|
|
603 |
if ((code = gs_point_transform_inverse(
|
|
|
604 |
fixed2float(fpts[1].x),
|
|
|
605 |
fixed2float(fpts[1].y),
|
|
|
606 |
&penum->mat, &ppts[1])) < 0 ||
|
|
|
607 |
(code = gs_point_transform_inverse(
|
|
|
608 |
fixed2float(fpts[2].x),
|
|
|
609 |
fixed2float(fpts[2].y),
|
|
|
610 |
&penum->mat, &ppts[2])) < 0)
|
|
|
611 |
return code;
|
|
|
612 |
/* falls through */
|
|
|
613 |
case gs_pe_moveto:
|
|
|
614 |
case gs_pe_lineto:
|
|
|
615 |
if ((code = gs_point_transform_inverse(
|
|
|
616 |
fixed2float(fpts[0].x),
|
|
|
617 |
fixed2float(fpts[0].y),
|
|
|
618 |
&penum->mat, &ppts[0])) < 0)
|
|
|
619 |
return code;
|
|
|
620 |
default: /* error */
|
|
|
621 |
break;
|
|
|
622 |
}
|
|
|
623 |
return pe_op;
|
|
|
624 |
}
|
|
|
625 |
|
|
|
626 |
/* Clean up after a pathforall. */
|
|
|
627 |
void
|
|
|
628 |
gs_path_enum_cleanup(gs_path_enum * penum)
|
|
|
629 |
{
|
|
|
630 |
if (penum->copied_path != 0) {
|
|
|
631 |
gx_path_free(penum->copied_path, "gs_path_enum_cleanup");
|
|
|
632 |
penum->path = 0;
|
|
|
633 |
penum->copied_path = 0;
|
|
|
634 |
}
|
|
|
635 |
}
|