home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved.
-
- This file is part of Ghostscript.
-
- Ghostscript is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
- to anyone for the consequences of using it or for whether it serves any
- particular purpose or works at all, unless he says so in writing. Refer
- to the Ghostscript General Public License for full details.
-
- Everyone is granted permission to copy, modify and redistribute
- Ghostscript, but only under the conditions described in the Ghostscript
- General Public License. A copy of this license is supposed to have been
- given to you along with Ghostscript so you can know your rights and
- responsibilities. It should be in a file named COPYING. Among other
- things, the copyright notice and this notice must be preserved on all
- copies. */
-
- /* gspath.c */
- /* Path construction routines for Ghostscript library */
- #include "math_.h"
- #include "gx.h"
- #include "gserrors.h"
- #include "gxfixed.h"
- #include "gxmatrix.h"
- #include "gxpath.h"
- #include "gzstate.h"
-
- /* Conversion parameters */
- #define degrees_to_radians (M_PI / 180.0)
-
- /* ------ Miscellaneous ------ */
-
- int
- gs_newpath(gs_state *pgs)
- { gx_path_release(pgs->path);
- gx_path_init(pgs->path, pgs->memory_procs);
- return 0;
- }
-
- int
- gs_closepath(gs_state *pgs)
- { return gx_path_close_subpath(pgs->path);
- }
-
- int
- gs_upmergepath(gs_state *pgs)
- { return gx_path_add_path(pgs->saved->path, pgs->path);
- }
-
- /* ------ Points and lines ------ */
-
- int
- gs_currentpoint(const gs_state *pgs, gs_point *ppt)
- { gs_fixed_point pt;
- int code = gx_path_current_point(pgs->path, &pt);
- if ( code < 0 ) return code;
- return gs_itransform(pgs, fixed2float(pt.x), fixed2float(pt.y), ppt);
- }
-
- int
- gs_moveto(gs_state *pgs, floatp x, floatp y)
- { int code;
- gs_fixed_point pt;
- if ( (code = gs_point_transform2fixed(&pgs->ctm, x, y, &pt)) >= 0 )
- code = gx_path_add_point(pgs->path, pt.x, pt.y);
- return code;
- }
-
- int
- gs_rmoveto(gs_state *pgs, floatp x, floatp y)
- { int code;
- gs_fixed_point dpt;
- if ( (code = gs_distance_transform2fixed(&pgs->ctm, x, y, &dpt)) >= 0 )
- code = gx_path_add_relative_point(pgs->path, dpt.x, dpt.y);
- return code;
- }
-
- int
- gs_lineto(gs_state *pgs, floatp x, floatp y)
- { int code;
- gs_fixed_point pt;
- if ( (code = gs_point_transform2fixed(&pgs->ctm, x, y, &pt)) >= 0 )
- code = gx_path_add_line(pgs->path, pt.x, pt.y);
- return code;
- }
-
- int
- gs_rlineto(gs_state *pgs, floatp x, floatp y)
- { gs_fixed_point cpt, dpt;
- int code = gx_path_current_point(pgs->path, &cpt);
- if ( code < 0 ) return code;
- if ( (code = gs_distance_transform2fixed(&pgs->ctm, x, y, &dpt)) >= 0 )
- code = gx_path_add_line(pgs->path, cpt.x + dpt.x, cpt.y + dpt.y);
- return code;
- }
-
- /* ------ Arcs ------ */
-
- /* Forward declarations */
- /*
- * Because of an obscure bug in the IBM RS/6000 compiler, the int argument
- * for arc_either and arc_add must come before the floatp arguments.
- */
- private int arc_either(P7(gs_state *, int,
- floatp, floatp, floatp, floatp, floatp));
- private int arc_add(P9(gs_state *, int,
- floatp, floatp, floatp, floatp, floatp, floatp, floatp));
-
- int
- gs_arc(gs_state *pgs,
- floatp xc, floatp yc, floatp r, floatp ang1, floatp ang2)
- { return arc_either(pgs, 0, xc, yc, r, ang1, ang2);
- }
-
- int
- gs_arcn(gs_state *pgs,
- floatp xc, floatp yc, floatp r, floatp ang1, floatp ang2)
- { return arc_either(pgs, 1, xc, yc, r, ang1, ang2);
- }
-
- private int
- arc_either(gs_state *pgs, int clockwise,
- floatp axc, floatp ayc, floatp arad, floatp aang1, floatp aang2)
- { float ar = arad;
- fixed ang1 = float2fixed(aang1), ang2 = float2fixed(aang2), adiff;
- float ang1r;
- float x0, y0, sin0, cos0;
- float x3r, y3r;
- int first = 1;
- int code;
- if ( ar < 0 )
- { ang1 += int2fixed(180);
- ang2 += int2fixed(180);
- ar = - ar;
- }
- #define fixed90 int2fixed(90)
- #define fixed360 int2fixed(360)
- ang1r = fixed2float(ang1 % fixed360) * degrees_to_radians;
- sin0 = ar * sin(ang1r), cos0 = ar * cos(ang1r);
- x0 = axc + cos0, y0 = ayc + sin0;
- if ( clockwise )
- { /* Quadrant reduction */
- while ( ang1 < ang2 ) ang2 -= fixed360;
- while ( (adiff = ang2 - ang1) < -fixed90 )
- { float w = sin0; sin0 = -cos0; cos0 = w;
- x3r = axc + cos0, y3r = ayc + sin0;
- code = arc_add(pgs, first, ar, x0, y0, x3r, y3r,
- (x0 + cos0),
- (y0 + sin0));
- if ( code < 0 ) return code;
- x0 = x3r, y0 = y3r;
- ang1 -= fixed90;
- first = 0;
- }
- }
- else
- { /* Quadrant reduction */
- while ( ang2 < ang1 ) ang2 += fixed360;
- while ( (adiff = ang2 - ang1) > fixed90 )
- { float w = cos0; cos0 = -sin0; sin0 = w;
- x3r = axc + cos0, y3r = ayc + sin0;
- code = arc_add(pgs, first, ar, x0, y0, x3r, y3r,
- (x0 + cos0),
- (y0 + sin0));
- if ( code < 0 ) return code;
- x0 = x3r, y0 = y3r;
- ang1 += fixed90;
- first = 0;
- }
- }
- /* Compute the intersection of the tangents. */
- { double trad = tan(fixed2float(adiff) * (degrees_to_radians / 2));
- float ang2r = fixed2float(ang2) * degrees_to_radians;
- code = arc_add(pgs, first, ar, x0, y0,
- (axc + ar * cos(ang2r)),
- (ayc + ar * sin(ang2r)),
- (x0 - trad * sin0),
- (y0 + trad * cos0));
- }
- return code;
- }
-
- int
- gs_arcto(gs_state *pgs,
- floatp ax1, floatp ay1, floatp ax2, floatp ay2, floatp arad,
- float *retxy) /* float retxy[4] */
- { float xt0, yt0, xt2, yt2;
- gs_point up0;
- #define ax0 up0.x
- #define ay0 up0.y
- int code;
- if ( arad < 0 )
- return_error(gs_error_undefinedresult);
- /* Transform the current point back into user coordinates */
- if ( (code = gs_currentpoint(pgs, &up0)) < 0 ) return code;
- { /* Now we have to compute the tangent points. */
- /* Basically, the idea is to compute the tangent */
- /* of the bisector by using tan(x+y) and tan(z/2) */
- /* formulas, without ever using any trig. */
- float dx0 = ax0 - ax1, dy0 = ay0 - ay1;
- float dx2 = ax2 - ax1, dy2 = ay2 - ay1;
- /* Compute the squared lengths from p1 to p0 and p2. */
- double sql0 = dx0 * dx0 + dy0 * dy0;
- double sql2 = dx2 * dx2 + dy2 * dy2;
- /* Compute the distance from p1 to the tangent points. */
- /* This is the only hairy part. */
- double num = dy0 * dx2 - dy2 * dx0;
- double denom = sqrt(sql0 * sql2) - (dx0 * dx2 + dy0 * dy2);
- /* Check for collinear points. */
- if ( fabs(num) < 1.0e-6 || fabs(denom) < 1.0e-6 )
- { gs_fixed_point pt;
- code = gs_point_transform2fixed(&pgs->ctm, ax1, ay1, &pt);
- if ( code >= 0 ) code = gx_path_add_line(pgs->path, pt.x, pt.y);
- xt0 = xt2 = ax1;
- yt0 = yt2 = ay1;
- }
- else /* not collinear */
- { double dist = fabs(arad * num / denom);
- double l0 = dist / sqrt(sql0), l2 = dist / sqrt(sql2);
- xt0 = ax1 + dx0 * l0;
- yt0 = ay1 + dy0 * l0;
- xt2 = ax1 + dx2 * l2;
- yt2 = ay1 + dy2 * l2;
- code = arc_add(pgs, 1, arad, xt0, yt0, xt2, yt2, ax1, ay1);
- }
- }
- if ( retxy != 0 )
- { retxy[0] = xt0;
- retxy[1] = yt0;
- retxy[2] = xt2;
- retxy[3] = yt2;
- }
- return code;
- }
-
- /* Internal routine for adding an arc to the path. */
- private int
- arc_add(gs_state *pgs, int first,
- floatp r, floatp x0, floatp y0, floatp x3, floatp y3, floatp xt, floatp yt)
- { gx_path *path = pgs->path;
- floatp dx = xt - x0, dy = yt - y0;
- floatp fraction;
- gs_fixed_point p0, p3, pt, cpt;
- int code;
- /* Compute the fraction coefficient for the curve. */
- /* See gx_path_add_arc for details. */
- if ( fabs(r) < 1.0e-4 ) /* almost zero radius */
- { fraction = 0.0;
- }
- else
- { double ratio2 = (dx * dx + dy * dy) / (r * r);
- fraction = (4.0/3.0) / (1 + sqrt(1 + ratio2));
- }
- #ifdef DEBUG
- if ( gs_debug['r'] )
- dprintf7("[r]Arc f=%f p0=(%f,%f) pt=(%f,%f) p3=(%f,%f) first=%d\n",
- x0, y0, xt, yt, x3, y3, first);
- #endif
- if ( (code = gs_point_transform2fixed(&pgs->ctm, x0, y0, &p0)) < 0 ||
- (code = gs_point_transform2fixed(&pgs->ctm, x3, y3, &p3)) < 0 ||
- (code = gs_point_transform2fixed(&pgs->ctm, xt, yt, &pt)) < 0 ||
- (first && (code = (gx_path_current_point(path, &cpt) >= 0 ?
- gx_path_add_line(path, p0.x, p0.y) :
- gx_path_add_point(path, p0.x, p0.y))) < 0)
- )
- return code;
- return gx_path_add_arc(path, p0.x, p0.y, p3.x, p3.y, pt.x, pt.y, fraction);
- }
-
- /* ------ Curves ------ */
-
- int
- gs_curveto(gs_state *pgs,
- floatp x1, floatp y1, floatp x2, floatp y2, floatp x3, floatp y3)
- { gs_fixed_point p1, p2, p3;
- int code;
- if ( (code = gs_point_transform2fixed(&pgs->ctm, x1, y1, &p1)) < 0 ||
- (code = gs_point_transform2fixed(&pgs->ctm, x2, y2, &p2)) < 0 ||
- (code = gs_point_transform2fixed(&pgs->ctm, x3, y3, &p3)) < 0
- ) return code;
- return gx_path_add_curve(pgs->path,
- p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
- }
-
- int
- gs_rcurveto(gs_state *pgs,
- floatp dx1, floatp dy1, floatp dx2, floatp dy2, floatp dx3, floatp dy3)
- { gs_fixed_point pt, p1, p2, p3;
- int code = gx_path_current_point(pgs->path, &pt);
- if ( code < 0 ) return code;
- if ( (code = gs_distance_transform2fixed(&pgs->ctm, dx1, dy1, &p1)) < 0 ||
- (code = gs_distance_transform2fixed(&pgs->ctm, dx2, dy2, &p2)) < 0 ||
- (code = gs_distance_transform2fixed(&pgs->ctm, dx3, dy3, &p3)) < 0
- ) return code;
- return gx_path_add_curve(pgs->path,
- pt.x + p1.x, pt.y + p1.y,
- pt.x + p2.x, pt.y + p2.y,
- pt.x + p3.x, pt.y + p3.y);
- }
-