123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616 |
- /* This file is part of the GNU plotutils package. Copyright (C) 1995,
- 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
- The GNU plotutils package is free software. You may redistribute it
- and/or modify it under the terms of the GNU General Public License as
- published by the Free Software foundation; either version 2, or (at your
- option) any later version.
- The GNU plotutils package is distributed in the hope that it will be
- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with the GNU plotutils package; see the file COPYING. If not, write to
- the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
- Boston, MA 02110-1301, USA. */
- /* This file contains the internal paint_path() and paint_paths() methods,
- which the public method endpath() is a wrapper around. */
- /* This version is for HPGL and PCL Plotters. By construction, for these
- Plotters our path buffer always contains either a segment list, or a
- rectangle or circle object. If it's a segment list, it may include an
- arbitrary sequence of line, circular arc, and Bezier elements. (For
- circular arcs to be included, the map from user to device coordinates
- must be uniform, so that e.g. the angle subtended by the arc will be the
- same in user and device coordinates.) */
- #include "sys-defines.h"
- #include "extern.h"
- #define DIST(p0,p1) (sqrt( ((p0).x - (p1).x)*((p0).x - (p1).x) \
- + ((p0).y - (p1).y)*((p0).y - (p1).y)))
- void
- _pl_h_paint_path (S___(Plotter *_plotter))
- {
- if (_plotter->drawstate->pen_type == 0
- && _plotter->drawstate->fill_type == 0)
- /* nothing to draw */
- return;
- switch ((int)_plotter->drawstate->path->type)
- {
- case (int)PATH_SEGMENT_LIST:
- {
- plIntPathSegment *xarray;
- plPoint p0, pp1, pc, savedpoint;
- bool closed, use_polygon_buffer;
- double last_x, last_y;
- int i, polyline_len;
- bool identical_user_coordinates = true;
- /* sanity checks */
- if (_plotter->drawstate->path->num_segments == 0)/* nothing to do */
- break;
- if (_plotter->drawstate->path->num_segments == 1) /* shouldn't happen*/
- break;
- if ((_plotter->drawstate->path->num_segments >= 3)/*check for closure*/
- && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.x == _plotter->drawstate->path->segments[0].p.x)
- && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.y == _plotter->drawstate->path->segments[0].p.y))
- closed = true;
- else
- closed = false; /* 2-point ones should be open */
-
- /* convert vertices to integer device coordinates, removing runs */
-
- /* array for points, with positions expressed in integer device coors*/
- xarray = (plIntPathSegment *)_pl_xmalloc (_plotter->drawstate->path->num_segments * sizeof(plIntPathSegment));
-
- /* add first point of path to xarray[] (type field is a moveto) */
- xarray[0].p.x = IROUND(XD(_plotter->drawstate->path->segments[0].p.x,
- _plotter->drawstate->path->segments[0].p.y));
- xarray[0].p.y = IROUND(YD(_plotter->drawstate->path->segments[0].p.x,
- _plotter->drawstate->path->segments[0].p.y));
- polyline_len = 1;
- /* save user coors of last point added to xarray[] */
- last_x = _plotter->drawstate->path->segments[0].p.x;
- last_y = _plotter->drawstate->path->segments[0].p.y;
-
- for (i = 1; i < _plotter->drawstate->path->num_segments; i++)
- {
- plPathSegment datapoint;
- double xuser, yuser, xdev, ydev;
- int device_x, device_y;
-
- datapoint = _plotter->drawstate->path->segments[i];
- xuser = datapoint.p.x;
- yuser = datapoint.p.y;
- if (xuser != last_x || yuser != last_y)
- /* in user space, not all points are the same */
- identical_user_coordinates = false;
-
- xdev = XD(xuser, yuser);
- ydev = YD(xuser, yuser);
- device_x = IROUND(xdev);
- device_y = IROUND(ydev);
-
- if (device_x != xarray[polyline_len-1].p.x
- || device_y != xarray[polyline_len-1].p.y)
- /* integer device coor(s) changed, so stash point (incl. type
- field) */
- {
- plPathSegmentType element_type;
- int device_xc, device_yc;
-
- xarray[polyline_len].p.x = device_x;
- xarray[polyline_len].p.y = device_y;
- element_type = datapoint.type;
- xarray[polyline_len].type = element_type;
-
- if (element_type == S_ARC)
- /* an arc element, so compute center, subtended angle too */
- {
- double angle;
-
- device_xc = IROUND(XD(datapoint.pc.x, datapoint.pc.y));
- device_yc = IROUND(YD(datapoint.pc.x, datapoint.pc.y));
- xarray[polyline_len].pc.x = device_xc;
- xarray[polyline_len].pc.y = device_yc;
- p0.x = last_x;
- p0.y = last_y;
- pp1 = datapoint.p;
- pc = datapoint.pc;
- angle = _angle_of_arc (p0, pp1, pc);
- /* if user coors -> device coors includes a reflection,
- flip sign */
- if (!_plotter->drawstate->transform.nonreflection)
- angle = -angle;
- xarray[polyline_len].angle = angle;
- }
- else if (element_type == S_CUBIC)
- /* a cubic Bezier element, so compute control points too */
- {
- xarray[polyline_len].pc.x
- = IROUND(XD(datapoint.pc.x, datapoint.pc.y));
- xarray[polyline_len].pc.y
- = IROUND(YD(datapoint.pc.x, datapoint.pc.y));
- xarray[polyline_len].pd.x
- = IROUND(XD(datapoint.pd.x, datapoint.pd.y));
- xarray[polyline_len].pd.y
- = IROUND(YD(datapoint.pd.x, datapoint.pd.y));
- }
-
- /* save user coors of last point added to xarray[] */
- last_x = datapoint.p.x;
- last_y = datapoint.p.y;
- polyline_len++;
- }
- }
-
- /* Check first for special subcase: all user-space juncture points
- in the polyline were mapped to a single integer HP-GL
- pseudo-pixel. If (1) they weren't all the same to begin with,
- or (2) they were all the same to begin with and the cap mode is
- "round", then draw as a filled circle, of diameter equal to the
- line width; otherwise draw nothing. */
-
- if (_plotter->drawstate->path->num_segments > 1 && polyline_len == 1)
- /* all points mapped to a single integer pseudo-pixel */
- {
- if (identical_user_coordinates == false
- || _plotter->drawstate->cap_type == PL_CAP_ROUND)
- {
- double r = 0.5 * _plotter->drawstate->line_width;
- double device_frame_radius;
-
- /* draw single filled circle, using HP-GL's native
- circle-drawing facility */
-
- /* move to center of circle */
- savedpoint = _plotter->drawstate->pos;
- _plotter->drawstate->pos =
- _plotter->drawstate->path->segments[0].p;
- _pl_h_set_position (S___(_plotter));
- _plotter->drawstate->pos = savedpoint;
-
- /* set fill color to pen color, arrange to do filling; sync
- attributes too, incl. pen width */
- {
- /* emit HP-GL directives; select a fill color that's
- actually the pen color */
- _pl_h_set_fill_color (R___(_plotter) true);
- _pl_h_set_attributes (S___(_plotter));
- }
- /* compute radius in device frame */
- device_frame_radius =
- sqrt(XDV(r,0)*XDV(r,0)+YDV(r,0)*YDV(r,0));
-
- /* Syncing the fill color may have set the
- _plotter->hpgl_bad_pen flag (e.g. if optimal pen is #0
- [white] and we're not allowed to use pen #0 to draw
- with). So we test _plotter->hpgl_bad_pen before using
- the pen. */
- if (_plotter->hpgl_bad_pen == false)
- /* fill the circle (360 degree wedge) */
- {
- sprintf (_plotter->data->page->point, "WG%d,0,360;",
- IROUND(device_frame_radius));
- _update_buffer (_plotter->data->page);
- }
- /* KLUDGE: in pre-HP-GL/2, our `set_fill_color' function
- may alter the line type, since it may request *solid*
- crosshatching; so reset the line type */
- if (_plotter->hpgl_version < 2)
- _pl_h_set_attributes (S___(_plotter));
- }
-
- /* free our temporary array and depart */
- free (xarray);
- break;
- }
-
- /* At this point, we know we have a nondegenerate path in our
- pseudo-integer device space. */
-
- /* will draw vectors (or arcs) into polygon buffer if appropriate */
- use_polygon_buffer = (_plotter->hpgl_version == 2
- || (_plotter->hpgl_version == 1 /* i.e. "1.5" */
- && (polyline_len > 2
- || _plotter->drawstate->fill_type)) ? true : false);
-
- /* Sync pen color. This is needed here only if HPGL_VERSION is 1,
- but we always do it here so that HP-GL/2 output that draws a
- polyline, if sent erroneously to a generic HP-GL device, will
- yield a polyline in the correct color, so long as the color
- isn't white. */
- _pl_h_set_pen_color (R___(_plotter) HPGL_OBJECT_PATH);
-
- /* set_pen_color() sets the advisory bad_pen flag if white pen (pen
- #0) would have been selected, and we can't use pen #0 to draw
- with. Such a situation isn't fatal if HPGL_VERSION is "1.5" or
- "2", since we may be filling the polyline with a nonwhite color,
- as well as using a white pen to draw it. But if HPGL_VERSION is
- "1", we don't fill polylines, so we might as well punt right
- now. */
- if (_plotter->hpgl_bad_pen && _plotter->hpgl_version == 1)
- {
- /* free integer storage buffer and depart */
- free (xarray);
- break;
- }
-
- /* sync attributes, incl. pen width if possible; move pen to p0 */
- _pl_h_set_attributes (S___(_plotter));
-
- savedpoint = _plotter->drawstate->pos;
- _plotter->drawstate->pos = _plotter->drawstate->path->segments[0].p;
- _pl_h_set_position (S___(_plotter));
- _plotter->drawstate->pos = savedpoint;
-
- if (use_polygon_buffer)
- /* have a polygon buffer, and can use it to fill polyline */
- {
- /* enter polygon mode */
- strcpy (_plotter->data->page->point, "PM0;");
- _update_buffer (_plotter->data->page);
- }
-
- if (use_polygon_buffer || _plotter->drawstate->pen_type)
- /* either (1) we'll be drawing into a polygon buffer, and will be
- using it for at least one of (a) filling and (b) edging, or
- (2) we won't be drawing into a polygon buffer, so we won't be
- filling, but we'll be edging (because pen_type isn't zero) */
- {
- /* ensure that pen is down for drawing */
- if (_plotter->hpgl_pendown == false)
- {
- strcpy (_plotter->data->page->point, "PD;");
- _update_buffer (_plotter->data->page);
- _plotter->hpgl_pendown = true;
- }
-
- /* loop through points in xarray[], emitting HP-GL instructions */
- i = 1;
- while (i < polyline_len)
- {
- switch ((int)xarray[i].type)
- {
- case (int)S_LINE:
- /* emit one or more pen advances */
- strcpy (_plotter->data->page->point, "PA");
- _update_buffer (_plotter->data->page);
- sprintf (_plotter->data->page->point, "%d,%d",
- xarray[i].p.x, xarray[i].p.y);
- _update_buffer (_plotter->data->page);
- i++;
- while (i < polyline_len && xarray[i].type == S_LINE)
- {
- sprintf (_plotter->data->page->point,
- ",%d,%d", xarray[i].p.x, xarray[i].p.y);
- _update_buffer (_plotter->data->page);
- i++;
- }
- sprintf (_plotter->data->page->point, ";");
- _update_buffer (_plotter->data->page);
- break;
-
- case (int)S_CUBIC:
- /* emit one or more cubic Bezier segments */
- strcpy (_plotter->data->page->point, "BZ");
- _update_buffer (_plotter->data->page);
- sprintf (_plotter->data->page->point, "%d,%d,%d,%d,%d,%d",
- xarray[i].pc.x, xarray[i].pc.y,
- xarray[i].pd.x, xarray[i].pd.y,
- xarray[i].p.x, xarray[i].p.y);
- _update_buffer (_plotter->data->page);
- i++;
- while (i < polyline_len && xarray[i].type == S_CUBIC)
- {
- sprintf (_plotter->data->page->point, ",%d,%d,%d,%d,%d,%d",
- xarray[i].pc.x, xarray[i].pc.y,
- xarray[i].pd.x, xarray[i].pd.y,
- xarray[i].p.x, xarray[i].p.y);
- _update_buffer (_plotter->data->page);
- i++;
- }
- sprintf (_plotter->data->page->point, ";");
- _update_buffer (_plotter->data->page);
- break;
-
- case (int)S_ARC:
- {
- double degrees;
- int int_degrees;
- /* emit an arc, using integer sweep angle if possible */
- degrees = 180.0 * xarray[i].angle / M_PI;
- int_degrees = IROUND (degrees);
- if (_plotter->hpgl_version > 0)
- /* HPGL_VERSION = 1.5 or 2 */
- {
- if (degrees == (double)int_degrees)
- sprintf (_plotter->data->page->point, "AA%d,%d,%d;",
- xarray[i].pc.x, xarray[i].pc.y,
- int_degrees);
- else
- sprintf (_plotter->data->page->point, "AA%d,%d,%.3f;",
- xarray[i].pc.x, xarray[i].pc.y,
- degrees);
- }
- else
- /* HPGL_VERSION = 1, i.e. generic HP-GL */
- /* note: generic HP-GL can only handle integer
- sweep angles */
- sprintf (_plotter->data->page->point, "AA%d,%d,%d;",
- xarray[i].pc.x, xarray[i].pc.y,
- int_degrees);
- _update_buffer (_plotter->data->page);
- i++;
- }
- break;
-
- default:
- /* shouldn't happen: unknown type for path segment,
- ignore */
- i++;
- break;
- }
- }
- }
-
- if (use_polygon_buffer)
- /* using polygon mode; will now employ polygon buffer to do
- filling (possibly) and edging */
- {
- if (!closed)
- /* polyline is open, so lift pen and exit polygon mode */
- {
- strcpy (_plotter->data->page->point, "PU;");
- _update_buffer (_plotter->data->page);
- _plotter->hpgl_pendown = false;
- strcpy (_plotter->data->page->point, "PM2;");
- _update_buffer (_plotter->data->page);
- }
- else
- /* polyline is closed, so exit polygon mode and then lift pen */
- {
- strcpy (_plotter->data->page->point, "PM2;");
- _update_buffer (_plotter->data->page);
- strcpy (_plotter->data->page->point, "PU;");
- _update_buffer (_plotter->data->page);
- _plotter->hpgl_pendown = false;
- }
-
- if (_plotter->drawstate->fill_type)
- /* polyline should be filled */
- {
- /* Sync fill color. This may set the
- _plotter->hpgl_bad_pen flag (if optimal pen is #0
- [white] and we're not allowed to use pen #0 to draw
- with). So we test _plotter->hpgl_bad_pen before using
- the pen to fill with. */
- _pl_h_set_fill_color (R___(_plotter) false);
- if (_plotter->hpgl_bad_pen == false)
- /* fill polyline, specifying nonzero winding rule if
- necessary */
- {
- switch (_plotter->drawstate->fill_rule_type)
- {
- case PL_FILL_ODD_WINDING:
- default:
- strcpy (_plotter->data->page->point, "FP;");
- break;
- case PL_FILL_NONZERO_WINDING:
- if (_plotter->hpgl_version == 2)
- strcpy (_plotter->data->page->point, "FP1;");
- else /* pre-HP-GL/2 doesn't support nonzero rule */
- strcpy (_plotter->data->page->point, "FP;");
- break;
- }
- _update_buffer (_plotter->data->page);
- }
- /* KLUDGE: in pre-HP-GL/2, our `set_fill_color' function may
- alter the line type, since it may request *solid*
- crosshatching; so reset the line type */
- if (_plotter->hpgl_version < 2)
- _pl_h_set_attributes (S___(_plotter));
- }
-
- if (_plotter->drawstate->pen_type)
- /* polyline should be edged */
- {
- /* Sync pen color. This may set the _plotter->hpgl_bad_pen
- flag (if optimal pen is #0 and we're not allowed to use
- pen #0 to draw with). So we test _plotter->hpgl_bad_pen
- before using the pen. */
- _pl_h_set_pen_color (R___(_plotter) HPGL_OBJECT_PATH);
- if (_plotter->hpgl_bad_pen == false)
- /* select appropriate pen for edging, and edge the
- polyline */
- {
- _pl_h_set_pen_color (R___(_plotter) HPGL_OBJECT_PATH);
- strcpy (_plotter->data->page->point, "EP;");
- _update_buffer (_plotter->data->page);
- }
- }
- }
-
- /* We know where the pen now is: if we used a polygon buffer, then
- _plotter->hpgl_pos is now xarray[0].p. If we didn't (as would
- be the case if we're outputting generic HP-GL), then
- _plotter->hpgl_pos is now xarray[polyline_len - 1].p.
- Unfortunately we can't simply update _plotter->hpgl_pos, because
- we want the generated HP-GL[/2] code to work properly on both
- HP-GL and HP-GL/2 devices. So we punt. */
- _plotter->hpgl_position_is_unknown = true;
-
- /* free integer storage buffer and depart */
- free (xarray);
- }
- break;
-
- case (int)PATH_BOX:
- {
- plPoint p0, p1, savedpoint;
- p0 = _plotter->drawstate->path->p0;
- p1 = _plotter->drawstate->path->p1;
- /* sync line attributes, incl. pen width */
- _pl_h_set_attributes (S___(_plotter));
- /* move HP-GL pen to first vertex */
- savedpoint = _plotter->drawstate->pos;
- _plotter->drawstate->pos = p0;
- _pl_h_set_position (S___(_plotter));
- _plotter->drawstate->pos = savedpoint;
-
- if (_plotter->drawstate->fill_type)
- /* rectangle should be filled */
- {
- /* Sync fill color. This may set the _plotter->hpgl_bad_pen
- flag (e.g. if optimal pen is #0 [white] and we're not
- allowed to use pen #0 to draw with). So we test
- _plotter->hpgl_bad_pen before using the pen. */
- _pl_h_set_fill_color (R___(_plotter) false);
- if (_plotter->hpgl_bad_pen == false)
- /* fill the rectangle */
- {
- sprintf (_plotter->data->page->point, "RA%d,%d;",
- IROUND(XD(p1.x,p1.y)), IROUND(YD(p1.x,p1.y)));
- _update_buffer (_plotter->data->page);
- }
- /* KLUDGE: in pre-HP-GL/2, our `set_fill_color' function may
- alter the line type, since it may request *solid*
- crosshatching; so reset it */
- if (_plotter->hpgl_version < 2)
- _pl_h_set_attributes (S___(_plotter));
- }
-
- if (_plotter->drawstate->pen_type)
- /* rectangle should be edged */
- {
- /* Sync pen color. This may set the _plotter->hpgl_bad_pen
- flag (e.g. if optimal pen is #0 [white] and we're not
- allowed to use pen #0 to draw with). So we test
- _plotter->hpgl_bad_pen before using the pen. */
- _pl_h_set_pen_color (R___(_plotter) HPGL_OBJECT_PATH);
- if (_plotter->hpgl_bad_pen == false)
- /* edge the rectangle */
- {
- sprintf (_plotter->data->page->point, "EA%d,%d;",
- IROUND(XD(p1.x,p1.y)), IROUND(YD(p1.x,p1.y)));
- _update_buffer (_plotter->data->page);
- }
- }
- }
- break;
- case (int)PATH_CIRCLE:
- {
- plPoint pc, savedpoint;
- double r = _plotter->drawstate->path->radius;
- double radius = sqrt(XDV(r,0)*XDV(r,0)+YDV(r,0)*YDV(r,0));
- pc = _plotter->drawstate->path->pc;
-
- /* sync attributes, incl. pen width; move to center of circle */
- _pl_h_set_attributes (S___(_plotter));
- savedpoint = _plotter->drawstate->pos;
- _plotter->drawstate->pos = pc;
- _pl_h_set_position (S___(_plotter));
- _plotter->drawstate->pos = savedpoint;
-
- if (_plotter->drawstate->fill_type)
- /* circle should be filled */
- {
- /* Sync fill color. This may set the _plotter->hpgl_bad_pen
- flag (e.g. if optimal pen is #0 [white] and we're not
- allowed to use pen #0 to draw with). So we test
- _plotter->hpgl_bad_pen before using the pen. */
- _pl_h_set_fill_color (R___(_plotter) false);
- if (_plotter->hpgl_bad_pen == false)
- /* fill the circle (360 degree wedge) */
- {
- sprintf (_plotter->data->page->point, "WG%d,0,360;",
- IROUND(radius));
- _update_buffer (_plotter->data->page);
- }
- /* KLUDGE: in pre-HP-GL/2, our `set_fill_color' function may
- alter the line type, since it may request *solid*
- crosshatching; so reset it */
- if (_plotter->hpgl_version < 2)
- _pl_h_set_attributes (S___(_plotter));
- }
-
- if (_plotter->drawstate->pen_type)
- /* circle should be edged */
- {
- /* Sync pen color. This may set the _plotter->hpgl_bad_pen
- flag (e.g. if optimal pen is #0 [white] and we're not
- allowed to use pen #0 to draw with). So we test
- _plotter->hpgl_bad_pen before using the pen. */
- _pl_h_set_pen_color (R___(_plotter) HPGL_OBJECT_PATH);
- if (_plotter->hpgl_bad_pen == false)
- /* do the edging */
- {
- sprintf (_plotter->data->page->point, "CI%d;", IROUND(radius));
- _update_buffer (_plotter->data->page);
- }
- }
- }
- break;
- default: /* unrecognized path type, shouldn't happen */
- break;
- }
- }
- /* A low-level method for moving the pen position of an HP-GL pen plotter
- to agree with the Plotter's notion of what the graphics cursor position
- should be. The state of the pen (up vs. down) after calling this
- function is not uniquely determined. */
- void
- _pl_h_set_position (S___(Plotter *_plotter))
- {
- int xnew, ynew;
-
- /* if plotter's pen position doesn't agree with what it should be,
- adjust it */
- xnew = IROUND(XD(_plotter->drawstate->pos.x, _plotter->drawstate->pos.y));
- ynew = IROUND(YD(_plotter->drawstate->pos.x, _plotter->drawstate->pos.y));
- if (_plotter->hpgl_position_is_unknown == true
- || xnew != _plotter->hpgl_pos.x || ynew != _plotter->hpgl_pos.y)
- {
- if (_plotter->hpgl_pendown == true)
- {
- sprintf (_plotter->data->page->point, "PU;PA%d,%d;", xnew, ynew);
- _plotter->hpgl_pendown = false;
- }
- else
- sprintf (_plotter->data->page->point, "PA%d,%d;", xnew, ynew);
- _update_buffer (_plotter->data->page);
- /* update our knowledge of pen position */
- _plotter->hpgl_position_is_unknown = false;
- _plotter->hpgl_pos.x = xnew;
- _plotter->hpgl_pos.y = ynew;
- }
- }
- bool
- _pl_h_paint_paths (S___(Plotter *_plotter))
- {
- return false;
- }
|