386BSD 0.1 development
[unix-history] / usr / othersrc / public / ghostscript-2.4.1 / gxstroke.c
CommitLineData
51a81c37
WJ
1/* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved.
2 Distributed by Free Software Foundation, Inc.
3
4This file is part of Ghostscript.
5
6Ghostscript is distributed in the hope that it will be useful, but
7WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
8to anyone for the consequences of using it or for whether it serves any
9particular purpose or works at all, unless he says so in writing. Refer
10to the Ghostscript General Public License for full details.
11
12Everyone is granted permission to copy, modify and redistribute
13Ghostscript, but only under the conditions described in the Ghostscript
14General Public License. A copy of this license is supposed to have been
15given to you along with Ghostscript so you can know your rights and
16responsibilities. It should be in a file named COPYING. Among other
17things, the copyright notice and this notice must be preserved on all
18copies. */
19
20/* gxstroke.c */
21/* Path stroking procedures for Ghostscript library */
22#include "math_.h"
23#include "gx.h"
24#include "gserrors.h"
25#include "gxfixed.h"
26#include "gxarith.h"
27#include "gxmatrix.h"
28#include "gzstate.h"
29#include "gzdevice.h"
30#include "gzcolor.h" /* requires gxdevice.h */
31#include "gzline.h"
32#include "gzpath.h"
33
34/* stroke_add uses the following global for its path: */
35private gx_path stroke_path_body;
36private gx_path *stroke_path;
37
38/*
39 * Structure for a partial line (passed to the drawing routine).
40 * Two of these are required to do joins right.
41 * Each endpoint includes the two ends of the cap as well,
42 * and the deltas for square and round cap computation.
43 *
44 * The deltas (co, cdelta, ce) are in clockwise order in device space
45 * around the endpoint p: they are one-half the line width (suitably
46 * transformed) at 90 degrees counter-clockwise, straight ahead,
47 * and 90 degrees clockwise from the oriented line o->e,
48 * where "90 degrees" is measured in *user* coordinates.
49 * Note that the values at o are the negatives of the values at e.
50 *
51 * Initially, only o.p, e.p, o.cdelta, width, and thin are set.
52 * compute_caps fills in the rest when needed.
53 */
54typedef gs_fixed_point _ss *p_ptr;
55typedef struct endpoint_s {
56 gs_fixed_point p; /* the end of the line */
57 gs_fixed_point co, ce; /* ends of the cap, p +/- width */
58 gs_fixed_point cdelta; /* +/- cap length */
59} endpoint;
60typedef endpoint _ss *ep_ptr;
61typedef struct partial_line_s {
62 endpoint o; /* starting coordinate */
63 endpoint e; /* ending coordinate */
64 gs_fixed_point width; /* one-half line width, see above */
65 int thin; /* true if minimum-width line */
66} partial_line;
67typedef partial_line _ss *pl_ptr;
68
69/* Procedures that stroke a partial_line (the first argument). */
70/* If both partial_lines are non-null, the procedure creates */
71/* an appropriate join; otherwise, the procedure creates an */
72/* end cap. If the first int is 0, the procedure also starts with */
73/* an appropriate cap. */
74private int stroke_add(P4(int, pl_ptr, pl_ptr, gs_state *));
75private int stroke_fill(P4(int, pl_ptr, pl_ptr, gs_state *));
76
77/* Other forward declarations */
78private int stroke(P3(gx_path *,
79 int (*)(P4(int, pl_ptr, pl_ptr, gs_state *)),
80 gs_state *));
81private int expand_dashes(P3(subpath *, gx_path *, gs_state *));
82private void compute_caps(P1(pl_ptr));
83private int add_capped(P4(gx_path *, gs_line_cap,
84 int (*)(P3(gx_path *, fixed, fixed)),
85 ep_ptr));
86
87/* Stroke a path for drawing or saving */
88int
89gx_stroke_fill(gx_path *ppath, gs_state *pgs)
90{ int code;
91 stroke_path = 0;
92 code = stroke(ppath, stroke_fill, pgs);
93 if ( stroke_path ) /* set if filling needed */
94 { if ( code >= 0 )
95 code = gx_fill_path(stroke_path, pgs->dev_color, pgs,
96 gx_rule_winding_number, (fixed)0);
97 gx_path_release(stroke_path);
98 }
99 return code;
100}
101int
102gx_stroke_add(gx_path *ppath, gx_path *topath, gs_state *pgs)
103{ stroke_path = topath;
104 return stroke(ppath, stroke_add, pgs);
105}
106
107/* Stroke a path. Call line_proc (stroke_add or stroke_fill) */
108/* for each line segment. */
109private int
110stroke(gx_path *ppath,
111 int (*line_proc)(P4(int, pl_ptr, pl_ptr, gs_state *)),
112 gs_state *pgs)
113{ subpath *psub;
114 subpath *save_psub = 0;
115 int code = 0;
116 line_params *lp = pgs->line_params;
117 int dash_count = lp->dash.pattern_size;
118 gx_path fpath, dpath;
119 float xx = pgs->ctm.xx, xy = pgs->ctm.xy;
120 float yx = pgs->ctm.yx, yy = pgs->ctm.yy;
121 int skewed = !is_fzero2(xy, yx);
122 int uniform = (skewed ? 0 : xx == yy ? 1 : xx == -yy ? -1 : 0);
123 /*
124 * We are dealing with a reflected coordinate system
125 * if (1,0) is counter-clockwise from (0,1).
126 * See the note in stroke_add for the algorithm.
127 */
128 int reflected =
129 (uniform ? uniform > 0 :
130 skewed ? xy * yx < xx * yy :
131 (xx < 0) == (yy < 0));
132 float line_width = lp->width; /* this is *half* the line width! */
133 int always_thin;
134 float line_width_and_scale;
135#ifdef DEBUG
136if ( gs_debug['o'] )
137 { int count = lp->dash.pattern_size;
138 int i;
139 dprintf3("[o]half_width=%f, cap=%d, join=%d,\n",
140 lp->width, (int)lp->cap, (int)lp->join);
141 dprintf2(" miter_limit=%f, miter_check=%f,\n",
142 lp->miter_limit, lp->miter_check);
143 dprintf1(" dash pattern=%d", count);
144 for ( i = 0; i < count; i++ )
145 dprintf1(",%f", lp->dash.pattern[i]);
146 dprintf4(",\n offset=%f, init(ink_on=%d, index=%d, dist_left=%f)\n",
147 lp->dash.offset, lp->dash.init_ink_on, lp->dash.init_index,
148 lp->dash.init_dist_left);
149 }
150#endif
151 if ( line_width < 0 ) line_width = -line_width;
152 if ( is_fzero(line_width) )
153 always_thin = 1;
154 else if ( !skewed )
155 { float xxa = xx, yya = yy;
156 if ( xxa < 0 ) xxa = -xxa;
157 if ( yya < 0 ) yya = -yya;
158 always_thin = (max(xxa, yya) * line_width < 0.5);
159 }
160 else
161 { /* The check is more complicated, but it's worth it. */
162 float xsq = xx * xx + xy * xy;
163 float ysq = yx * yx + yy * yy;
164 float cross = xx * yx + xy * yy;
165 if ( cross < 0 ) cross = 0;
166 always_thin =
167 ((max(xsq, ysq) + cross) * line_width * line_width < 0.5);
168 }
169 line_width_and_scale = line_width * (float)int2fixed(1);
170#ifdef DEBUG
171if ( gs_debug['o'] )
172 dprintf5("[o]ctm=(%g,%g,%g,%g) thin=%d\n",
173 xx, xy, yx, yy, always_thin);
174#endif
175 /* Start by flattening the path. We should do this on-the-fly.... */
176 if ( !ppath->curve_count ) /* don't need to flatten */
177 { psub = ppath->first_subpath;
178 if ( !psub ) return 0;
179 }
180 else
181 { if ( (code = gx_path_flatten(ppath, &fpath, pgs->flatness)) < 0 ) return code;
182 psub = fpath.first_subpath;
183 }
184 if ( dash_count )
185 gx_path_init(&dpath, &ppath->memory_procs);
186 for ( ; ; )
187 { line_segment *pline;
188 fixed x, y;
189 partial_line pl, pl_prev, pl_first;
190 int first = 0;
191 int index = 0;
192 if ( !psub )
193 { /* Might just be the end of a dash expansion. */
194 if ( save_psub )
195 { gx_path_release(&dpath);
196 psub = (subpath *)save_psub->last->next;
197 if ( !psub ) break;
198 gx_path_init(&dpath, &ppath->memory_procs);
199 save_psub = 0;
200 }
201 else /* all done */
202 break;
203 }
204 if ( dash_count && !save_psub )
205 { code = expand_dashes(psub, &dpath, pgs);
206 if ( code < 0 ) goto exit;
207 save_psub = (subpath *)psub;
208 psub = dpath.first_subpath;
209 continue; /* psub might be null */
210 }
211 pline = (line_segment *)(psub->next);
212 x = psub->pt.x;
213 y = psub->pt.y;
214 while ( pline != 0 && pline->type != s_start )
215 { fixed sx = pline->pt.x;
216 fixed sy = pline->pt.y;
217 /* Compute the width parameters in device space. */
218 /* We work with unscaled values, for speed. */
219 pl.o.p.x = x, pl.o.p.y = y;
220 pl.e.p.x = sx, pl.e.p.y = sy;
221 if ( !always_thin )
222 { fixed udx = sx - x, udy = sy - y;
223 if ( !(udx | udy) ) /* degenerate */
224 { /* Only consider a degenerate segment */
225 /* if the entire subpath is degenerate and */
226 /* we are using round caps or joins. */
227 if ( index != 0 || (pline->next != 0 &&
228 pline->next->type != s_start) ||
229 (lp->cap != gs_cap_round &&
230 lp->join != gs_join_round)
231 )
232 goto nd;
233 /* Pick an arbitrary orientation. */
234 udx = int2fixed(1);
235 }
236 if ( uniform != 0 )
237 { /* We can save a lot of work in this case. */
238 float dpx = udx, dpy = udy;
239 float wl = line_width_and_scale * xx /
240 hypot(dpx, dpy);
241 if ( wl < 0 ) wl = -wl;
242 pl.e.cdelta.x = (fixed)(dpx * wl);
243 pl.e.cdelta.y = (fixed)(dpy * wl);
244 pl.width.x = -pl.e.cdelta.y;
245 pl.width.y = pl.e.cdelta.x;
246 pl.thin = 0; /* if not always_thin, */
247 /* then never thin. */
248 }
249 else
250 { gs_point dpt; /* unscaled */
251 float wl;
252 if ( skewed )
253 gs_idtransform(pgs,
254 (float)udx, (float)udy, &dpt);
255 else /* shortcut */
256 dpt.x = udx / xx,
257 dpt.y = udy / yy;
258 wl = line_width_and_scale /
259 hypot(dpt.x, dpt.y);
260 /* Construct the width vector in */
261 /* user space, still unscaled. */
262 dpt.x *= wl;
263 dpt.y *= wl;
264 /*
265 * We now compute both perpendicular
266 * and (optionally) parallel half-widths,
267 * as deltas in device space. We use
268 * a fixed-point, unscaled version of
269 * gs_dtransform. The second computation
270 * folds in a 90-degree rotation (in user
271 * space, before transforming) in the
272 * direction that corresponds to clockwise
273 * in device space.
274 */
275 pl.e.cdelta.x = (fixed)(dpt.x * xx);
276 pl.e.cdelta.y = (fixed)(dpt.y * yy);
277 if ( skewed )
278 pl.e.cdelta.x += (fixed)(dpt.y * yx),
279 pl.e.cdelta.y += (fixed)(dpt.x * xy);
280 if ( reflected )
281 dpt.x = -dpt.x, dpt.y = -dpt.y;
282 pl.width.x = (fixed)(dpt.y * xx),
283 pl.width.y = -(fixed)(dpt.x * yy);
284 if ( skewed )
285 pl.width.x -= (fixed)(dpt.x * yx),
286 pl.width.y += (fixed)(dpt.y * xy);
287 pl.thin =
288 any_abs(pl.width.x) + any_abs(pl.width.y) <
289 float2fixed(0.75);
290 }
291 if ( !pl.thin ) compute_caps(&pl);
292 }
293 else
294 pl.e.cdelta.x = pl.e.cdelta.y = 0,
295 pl.width.x = pl.width.y = 0,
296 pl.thin = 1;
297 if ( first++ == 0 ) pl_first = pl;
298 if ( index++ ) (*line_proc)(index - 2, &pl_prev, &pl, pgs);
299 pl_prev = pl;
300 x = sx, y = sy;
301nd: pline = (line_segment *)(pline->next);
302 }
303 if ( index )
304 { /* If closed, join back to start, else cap */
305 (*line_proc)(index - 1, &pl_prev,
306 (psub->closed ? &pl_first : (pl_ptr)0), pgs);
307 }
308 psub = (subpath *)pline;
309 if ( stroke_path == &stroke_path_body )
310 { /* Fill and release the accumulated path */
311 gx_fill_path(stroke_path, pgs->dev_color, pgs,
312 gx_rule_winding_number, (fixed)0);
313 gx_path_release(stroke_path);
314 stroke_path = 0;
315 }
316 }
317exit: if ( dash_count ) gx_path_release(&dpath);
318 if ( ppath->curve_count ) gx_path_release(&fpath);
319 return code;
320}
321
322/* ------ Internal routines ------ */
323
324/* Expand a dashed subpath into explicit segments. */
325/* The subpath contains no curves. */
326private int
327expand_dashes(subpath *psub, gx_path *ppath, gs_state *pgs)
328{ int skewed = is_skewed(&pgs->ctm);
329 dash_params *dash = &pgs->line_params->dash;
330 float *pattern = dash->pattern;
331 int count, ink_on, index;
332 float dist_left;
333 fixed x0 = psub->pt.x, y0 = psub->pt.y;
334 fixed x, y;
335 segment *pseg;
336 int wrap = (dash->init_ink_on && psub->closed ? -1 : 0);
337 int drawing = wrap;
338 int code;
339 if ( (code = gx_path_add_point(ppath, x0, y0)) < 0 )
340 return code;
341 /* To do the right thing at the beginning of a closed path, */
342 /* we have to skip any initial line, and then redo it at */
343 /* the end of the path. Drawing = -1 while skipping, */
344 /* 0 while drawing normally, and 1 on the second round. */
345top: count = dash->pattern_size;
346 ink_on = dash->init_ink_on;
347 index = dash->init_index;
348 dist_left = dash->init_dist_left;
349 x = x0, y = y0;
350 pseg = (segment *)psub;
351 while ( (pseg = pseg->next) != 0 && pseg->type != s_start )
352 { fixed sx = pseg->pt.x, sy = pseg->pt.y;
353 fixed udx = sx - x, udy = sy - y;
354 float length, dx, dy;
355 float dist;
356 if ( !(udx | udy) ) /* degenerate */
357 dx = 0, dy = 0, length = 0;
358 else
359 { gs_point d;
360 dx = udx, dy = udy; /* scaled as fixed */
361 if ( skewed )
362 gs_idtransform(pgs, dx, dy, &d);
363 else /* shortcut */
364 d.x = dx / pgs->ctm.xx,
365 d.y = dy / pgs->ctm.yy;
366 length = sqrt(d.x * d.x + d.y * d.y) *
367 (1 / (float)int2fixed(1));
368 }
369 dist = length;
370 while ( dist > dist_left )
371 { /* We are using up the dash element */
372 float fraction = dist_left / length;
373 fixed nx = x + (fixed)(dx * fraction);
374 fixed ny = y + (fixed)(dy * fraction);
375 if ( ink_on )
376 { if ( drawing >= 0 )
377 code = gx_path_add_line(ppath, nx, ny);
378 }
379 else
380 { if ( drawing > 0 ) return 0; /* done */
381 code = gx_path_add_point(ppath, nx, ny);
382 drawing = 0;
383 }
384 if ( code < 0 ) return code;
385 dist -= dist_left;
386 ink_on = !ink_on;
387 if ( ++index == count ) index = 0;
388 dist_left = pattern[index];
389 x = nx, y = ny;
390 }
391 dist_left -= dist;
392 /* Handle the last dash of a segment. */
393 if ( ink_on )
394 { if ( drawing >= 0 )
395 code =
396 (pseg->type == s_line_close && drawing > 0 ?
397 gx_path_close_subpath(ppath) :
398 gx_path_add_line(ppath, sx, sy));
399 }
400 else
401 { if ( drawing > 0 ) return 0; /* done */
402 code = gx_path_add_point(ppath, sx, sy);
403 drawing = 0;
404 }
405 if ( code < 0 ) return code;
406 x = sx, y = sy;
407 }
408 /* Check for wraparound. */
409 if ( wrap && drawing <= 0 )
410 { /* We skipped some initial lines. */
411 /* Go back and do them now. */
412 drawing = 1;
413 goto top;
414 }
415 return 0;
416}
417
418/* Compute the intersection of two lines. This is a messy algorithm */
419/* that somehow ought to be useful in more places than just here.... */
420private void
421line_intersect(
422 p_ptr pp1, /* point on 1st line */
423 p_ptr pd1, /* slope of 1st line (dx,dy) */
424 p_ptr pp2, /* point on 2nd line */
425 p_ptr pd2, /* slope of 2nd line */
426 p_ptr pi) /* return intersection here */
427{ /* We don't have to do any scaling, the factors all work out right. */
428 float u1 = pd1->x, v1 = pd1->y;
429 float u2 = pd2->x, v2 = pd2->y;
430 double denom = u1 * v2 - u2 * v1;
431 double num1 = v1 * pp1->x - u1 * pp1->y;
432 double num2 = v2 * pp2->x - u2 * pp2->y;
433 double xnum = u1 * num2 - u2 * num1;
434 double ynum = v1 * num2 - v2 * num1;
435 double max_result = any_abs(denom) * (double)max_fixed;
436#ifdef DEBUG
437if ( gs_debug['o'] )
438 { dprintf4("[o]Intersect %f,%f(%f/%f)",
439 fixed2float(pp1->x), fixed2float(pp1->y),
440 fixed2float(pd1->x), fixed2float(pd1->y));
441 dprintf4(" & %f,%f(%f/%f),\n",
442 fixed2float(pp2->x), fixed2float(pp2->y),
443 fixed2float(pd2->x), fixed2float(pd2->y));
444 dprintf4("\txnum=%f ynum=%f denom=%f max_result=%f ->\n",
445 xnum, ynum, denom, max_result);
446 }
447#endif
448 /* Check for degenerate result. */
449 if ( denom == 0 || any_abs(xnum) > max_result || any_abs(ynum) > max_result )
450 { /* The lines are nearly parallel, */
451 /* or one of them has zero length. Punt. */
452 *pi = *pp1;
453#ifdef DEBUG
454if ( gs_debug['o'] )
455 dprintf("\tdegenerate!\n");
456#endif
457 }
458 else
459 { pi->x = (fixed)(xnum / denom);
460 pi->y = (fixed)(ynum / denom);
461#ifdef DEBUG
462if ( gs_debug['o'] )
463 dprintf2("\t%f,%f\n", fixed2float(pi->x), fixed2float(pi->y));
464#endif
465 }
466}
467
468#define lix plp->o.p.x
469#define liy plp->o.p.y
470#define litox plp->e.p.x
471#define litoy plp->e.p.y
472
473/* Draw a line on the device. */
474private int
475stroke_fill(int first, register pl_ptr plp, pl_ptr nplp, gs_state *pgs)
476{ if ( plp->thin )
477 { /* Minimum-width line, don't have to be careful. */
478 /* We do have to check for the entire line being */
479 /* within the clipping rectangle, allowing for some */
480 /* slop at the ends. */
481 fixed dx = litox - lix, dy = litoy - liy;
482#define trsign(v, c) (v >= 0 ? c : -c)
483#define slop int2fixed(2)
484 fixed xslop = trsign(dx, slop);
485 fixed yslop = trsign(dy, slop);
486 if ( gx_cpath_includes_rectangle(pgs->clip_path,
487 lix - xslop, liy - yslop,
488 litox + xslop, litoy + yslop) )
489 return gz_draw_line_fixed(lix, liy, litox, litoy,
490 pgs->dev_color, pgs);
491#undef slop
492 /* We didn't set up the endpoint parameters before, */
493 /* because the line was thin. Do it now. */
494 /* We only approximate the width and height. */
495 if ( any_abs(dx) > any_abs(dy) )
496 { plp->width.x = plp->e.cdelta.y = 0;
497 plp->width.y = -(plp->e.cdelta.x =
498 trsign(dx, float2fixed(-0.5)));
499 }
500 else
501 { plp->width.y = plp->e.cdelta.x = 0;
502 plp->width.x = -(plp->e.cdelta.y =
503 trsign(dy, float2fixed(-0.5)));
504 }
505#undef trsign
506 compute_caps(plp);
507 }
508 { /* General case. */
509 /* Construct a path and hand it to the fill algorithm. */
510 if ( stroke_path == 0 )
511 { /* We are rendering, and haven't run into the */
512 /* general case yet. Initialize the path. */
513 stroke_path = &stroke_path_body; /* set global for stroke_add */
514 gx_path_init(stroke_path, &pgs->memory_procs);
515 }
516 stroke_add(first, plp, nplp, pgs);
517 { /****** PATCH ******/
518 if ( stroke_path == &stroke_path_body )
519 { gx_fill_path(stroke_path, pgs->dev_color, pgs,
520 gx_rule_winding_number, (fixed)0);
521 gx_path_release(stroke_path);
522 stroke_path = 0;
523 }
524 }
525 }
526 return 0;
527}
528
529#undef lix
530#undef liy
531#undef litox
532#undef litoy
533
534/* Add a segment to the path. This handles all the complex cases. */
535private int add_capped(P4(gx_path *, gs_line_cap, int (*)(P3(gx_path *, fixed, fixed)), ep_ptr));
536private int
537stroke_add(int first, register pl_ptr plp, pl_ptr nplp, gs_state *pgs)
538{ gx_path *ppath = stroke_path;
539 int code;
540 if ( ppath == 0 ) return 0; /****** strokepath is NYI ******/
541 if ( plp->thin )
542 { /* We didn't set up the endpoint parameters before, */
543 /* because the line was thin. Do it now. */
544 compute_caps(plp);
545 }
546 if ( (code = add_capped(ppath, (first == 0 ? pgs->line_params->cap : gs_cap_butt), gx_path_add_point, &plp->o)) < 0 )
547 return code;
548 if ( nplp == 0 )
549 { code = add_capped(ppath, pgs->line_params->cap, gx_path_add_line, &plp->e);
550 }
551 else if ( pgs->line_params->join == gs_join_round )
552 { code = add_capped(ppath, gs_cap_round, gx_path_add_line, &plp->e);
553 }
554 else if ( nplp->thin ) /* no join */
555 { code = add_capped(ppath, gs_cap_butt, gx_path_add_line, &plp->e);
556 }
557 else /* join_bevel or join_miter */
558 { gs_fixed_point jp1, jp2;
559 /*
560 * Set np to whichever of nplp->o.co or .ce
561 * is outside the current line.
562 * We use the interesting observation that
563 * point (x2,y2) is counter-clockwise from (x1,y1)
564 * relative to the origin iff x1*y2 < x2*y1.
565 * In this case x1,y1 are plp->width,
566 * x2,y2 are nplp->width, and the origin is
567 * their common point (plp->e.p, nplp->o.p).
568 */
569 float wx1 = plp->width.x, wy1 = plp->width.y;
570 float wx2 = nplp->width.x, wy2 = nplp->width.y;
571 int ccw = wx1 * wy2 < wx2 * wy1;
572 p_ptr outp, np, np1, np2;
573 /* Initialize for a bevel join. */
574 jp1.x = plp->e.co.x, jp1.y = plp->e.co.y;
575 jp2.x = plp->e.ce.x, jp2.y = plp->e.ce.y;
576 if ( ccw )
577 outp = &jp2, np2 = np = &nplp->o.co, np1 = &plp->e.p;
578 else
579 outp = &jp1, np1 = np = &nplp->o.ce, np2 = &plp->e.p;
580#ifdef DEBUG
581if ( gs_debug['o'] )
582 dprintf1("[o]use %s\n", (ccw ? "co (ccw)" : "ce (cw)"));
583#endif
584 /* Don't bother with the miter check if the two */
585 /* points to be joined are very close together, */
586 /* namely, in the same square half-pixel. */
587 if ( pgs->line_params->join == gs_join_miter &&
588 !(fixed2long(outp->x << 1) == fixed2long(np->x << 1) &&
589 fixed2long(outp->y << 1) == fixed2long(np->y << 1))
590 )
591 { /*
592 * Check whether a miter join is appropriate.
593 * Let a, b be the angles of the two lines.
594 * We check tan(a-b) against the miter_check
595 * by using the following formula:
596 * If tan(a)=u1/v1 and tan(b)=u2/v2, then
597 * tan(a-b) = (u1*v2 - u2*v1) / (u1*u2 + v1*v2).
598 * We can do all the computations unscaled,
599 * because we're only concerned with ratios.
600 */
601 float u1 = plp->e.cdelta.x, v1 = plp->e.cdelta.y;
602 float u2 = nplp->o.cdelta.x, v2 = nplp->o.cdelta.y;
603 float num = u1 * v2 - u2 * v1;
604 float denom = u1 * u2 + v1 * v2;
605 float check = pgs->line_params->miter_check;
606 /*
607 * We will want either tan(a-b) or tan(b-a)
608 * depending on the orientations of the lines.
609 * Fortunately we know the relative orientations already.
610 */
611 if ( !ccw ) /* have plp - nplp, want vice versa */
612 num = -num;
613#ifdef DEBUG
614if ( gs_debug['o'] )
615 { dprintf4("[o]Miter check: u1/v1=%f/%f, u2/v2=%f/%f,\n",
616 u1, v1, u2, v2);
617 dprintf3(" num=%f, denom=%f, check=%f\n",
618 num, denom, check);
619 }
620#endif
621 /* Use a miter if num / denom >= check. */
622 /* If check > 0, num < 0 always passes; */
623 /* if check < 0, num >= 0 always fails. */
624 if ( denom < 0 ) num = -num, denom = -denom;
625 if ( check > 0 ?
626 (num < 0 || num >= denom * check) :
627 (num < 0 && num >= denom * check)
628 )
629 { /* OK to use a miter join. */
630#ifdef DEBUG
631if ( gs_debug['o'] )
632 dputs(" ... passes.\n");
633#endif
634 /* Compute the intersection of */
635 /* the extended edge lines. */
636 line_intersect(outp, &plp->e.cdelta, np,
637 &nplp->o.cdelta, outp);
638 }
639 }
640 if ( (code = gx_path_add_line(ppath, jp1.x, jp1.y)) < 0 ||
641 (code = gx_path_add_line(ppath, np1->x, np1->y)) < 0 ||
642 (code = gx_path_add_line(ppath, np2->x, np2->y)) < 0 ||
643 (code = gx_path_add_line(ppath, jp2.x, jp2.y)) < 0
644 )
645 return code;
646 }
647 if ( code < 0 || (code = gx_path_close_subpath(ppath)) < 0 )
648 return code;
649 return 0;
650}
651
652/* Routines for cap computations */
653
654/* Compute the endpoints of the two caps of a segment. */
655private void
656compute_caps(register pl_ptr plp)
657{ fixed wx2 = plp->width.x;
658 fixed wy2 = plp->width.y;
659 plp->o.co.x = plp->o.p.x + wx2, plp->o.co.y = plp->o.p.y + wy2;
660 plp->o.cdelta.x = -plp->e.cdelta.x,
661 plp->o.cdelta.y = -plp->e.cdelta.y;
662 plp->o.ce.x = plp->o.p.x - wx2, plp->o.ce.y = plp->o.p.y - wy2;
663 plp->e.co.x = plp->e.p.x - wx2, plp->e.co.y = plp->e.p.y - wy2;
664 plp->e.ce.x = plp->e.p.x + wx2, plp->e.ce.y = plp->e.p.y + wy2;
665#ifdef DEBUG
666if ( gs_debug['o'] )
667 dprintf4("[o]Stroke o=(%f,%f) e=(%f,%f)\n",
668 fixed2float(plp->o.p.x), fixed2float(plp->o.p.y),
669 fixed2float(plp->e.p.x), fixed2float(plp->e.p.y)),
670 dprintf4("\twxy=(%f,%f) lxy=(%f,%f)\n",
671 fixed2float(wx2), fixed2float(wy2),
672 fixed2float(plp->e.cdelta.x), fixed2float(plp->e.cdelta.y));
673#endif
674}
675
676/* Add a properly capped line endpoint to the path. */
677/* The first point may require either moveto or lineto. */
678private int
679add_capped(gx_path *ppath, gs_line_cap type,
680 int (*add_proc)(P3(gx_path *, fixed, fixed)), /* gx_path_add_point/line */
681 register ep_ptr endp)
682{ int code;
683#define px endp->p.x
684#define py endp->p.y
685#define xo endp->co.x
686#define yo endp->co.y
687#define xe endp->ce.x
688#define ye endp->ce.y
689#define cdx endp->cdelta.x
690#define cdy endp->cdelta.y
691#ifdef DEBUG
692if ( gs_debug['o'] )
693 dprintf4("[o]cap: p=(%g,%g), co=(%g,%g),\n",
694 fixed2float(px), fixed2float(py),
695 fixed2float(xo), fixed2float(yo)),
696 dprintf4("[o]\tce=(%g,%g), cd=(%g,%g)\n",
697 fixed2float(xe), fixed2float(ye),
698 fixed2float(cdx), fixed2float(cdy));
699#endif
700 switch ( type )
701 {
702 case gs_cap_round:
703 { fixed xm = px + cdx;
704 fixed ym = py + cdy;
705 if ( (code = (*add_proc)(ppath, xo, yo)) < 0 ||
706 (code = gx_path_add_arc(ppath, xo, yo, xm, ym,
707 xo + cdx, yo + cdy, quarter_arc_fraction)) < 0 ||
708 (code = gx_path_add_arc(ppath, xm, ym, xe, ye,
709 xe + cdx, ye + cdy, quarter_arc_fraction)) < 0
710 ) return code;
711 }
712 break;
713 case gs_cap_square:
714 if ( (code = (*add_proc)(ppath, xo + cdx, yo + cdy)) < 0 ||
715 (code = gx_path_add_line(ppath, xe + cdx, ye + cdy)) < 0
716 ) return code;
717 break;
718 case gs_cap_butt:
719 if ( (code = (*add_proc)(ppath, xo, yo)) < 0 ||
720 (code = gx_path_add_line(ppath, xe, ye)) < 0
721 ) return code;
722 }
723 return code;
724}