| /* Copyright (C) 1989 Aladdin Enterprises. All rights reserved. |
| Distributed by Free Software Foundation, Inc. |
| |
| 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); |
| } |
| |
| /* ------ Points and lines ------ */ |
| |
| int |
| gs_currentpoint(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 */ |
| private int arc_either(P7(gs_state *, |
| floatp, floatp, floatp, floatp, floatp, int)); |
| private int arc_add(P8(gs_state *, |
| floatp, floatp, floatp, floatp, floatp, floatp, int)); |
| |
| int |
| gs_arc(gs_state *pgs, |
| floatp xc, floatp yc, floatp r, floatp ang1, floatp ang2) |
| { return arc_either(pgs, xc, yc, r, ang1, ang2, 0); |
| } |
| |
| int |
| gs_arcn(gs_state *pgs, |
| floatp xc, floatp yc, floatp r, floatp ang1, floatp ang2) |
| { return arc_either(pgs, xc, yc, r, ang1, ang2, 1); |
| } |
| |
| private int |
| arc_either(gs_state *pgs, |
| floatp axc, floatp ayc, floatp ar, floatp aang1, floatp aang2, |
| int clockwise) |
| { 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 ) return_error(gs_error_rangecheck); |
| #define fixed90 int2fixed(90) |
| #define fixed360 int2fixed(360) |
| /* Reduce the arc to at most 360 degrees. */ |
| if ( ang1 != ang2 ) |
| { ang1 %= fixed360; |
| ang2 %= fixed360; |
| if ( clockwise ) |
| { if ( ang2 >= ang1 ) ang1 += fixed360; |
| } |
| else |
| { if ( ang2 <= ang1 ) ang2 += fixed360; |
| } |
| } |
| ang1r = fixed2float(ang1) * degrees_to_radians; |
| sin0 = ar * sin(ang1r), cos0 = ar * cos(ang1r); |
| x0 = axc + cos0, y0 = ayc + sin0; |
| if ( clockwise ) |
| { /* Quadrant reduction */ |
| while ( (adiff = ang2 - ang1) < -fixed90 ) |
| { float w = sin0; sin0 = -cos0; cos0 = w; |
| x3r = axc + cos0, y3r = ayc + sin0; |
| /* Must cast doubles to floats explicitly */ |
| code = arc_add(pgs, x0, y0, x3r, y3r, |
| (x0 + cos0), |
| (y0 + sin0), |
| first); |
| if ( code < 0 ) return code; |
| x0 = x3r, y0 = y3r; |
| ang1 -= fixed90; |
| first = 0; |
| } |
| } |
| else |
| { /* Quadrant reduction */ |
| while ( (adiff = ang2 - ang1) > fixed90 ) |
| { float w = cos0; cos0 = -sin0; sin0 = w; |
| x3r = axc + cos0, y3r = ayc + sin0; |
| /* Must cast doubles to floats explicitly */ |
| code = arc_add(pgs, x0, y0, x3r, y3r, |
| (x0 + cos0), |
| (y0 + sin0), |
| first); |
| if ( code < 0 ) return code; |
| x0 = x3r, y0 = y3r; |
| ang1 += fixed90; |
| first = 0; |
| } |
| } |
| if ( adiff == 0 ) return 0; /* exact multiple of 90 */ |
| /* Compute the intersection of the tangents. */ |
| { float trad = tan(fixed2float(adiff) * (degrees_to_radians / 2)); |
| float ang2r = fixed2float(ang2) * degrees_to_radians; |
| code = arc_add(pgs, x0, y0, |
| (axc + ar * cos(ang2r)), |
| (ayc + ar * sin(ang2r)), |
| (x0 - trad * sin0), |
| (y0 + trad * cos0), |
| first); |
| } |
| 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, xt0, yt0, xt2, yt2, ax1, ay1, 1); |
| } |
| } |
| 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, |
| floatp x0, floatp y0, floatp x3, floatp y3, floatp xt, floatp yt, |
| int first) |
| { gx_path *path = pgs->path; |
| int code; |
| gs_fixed_point p0, p3, pt, cpt; |
| #ifdef DEBUG |
| if ( gs_debug['r'] ) |
| printf("[r]Arc 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); |
| } |
| |
| /* ------ 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); |
| } |