long long int XD(int c,int d) { if(c>0 && d>0 && c<=a && d<=b) { XP[c][d]=0; if(XP[c-1][d]==1) XD(c-1,d); if(XP[c][d-1]==1) XD(c,d-1); if(XP[c][d+1]==1) XD(c,d+1); if(XP[c+1][d]==1) XD(c+1,d); } }
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; } }
int main() { long long int n,i,j,k; while(scanf("%lld",&n)!=EOF) { for(i=1; i<=n; i++) { for(j=1; j<=n; j++) { scanf("%d",XP[i][j]); } } for(i=1; i<=a; i++) { for(j=1; j<=b; j++) { if(XP[i][j]=='@') XP[i][j]=1; else XP[i][j]=0; } } k=0; for(i=1; i<=a; i++) { for(j=1; j<=b; j++) { if(XP[i][j]==1) { XD(i,j); k=k+1; } } } printf("%lld\n",k); } return 0; }
void _pl_x_paint_point (S___(Plotter *_plotter)) { double xx, yy; int ix, iy; plColor oldcolor, newcolor; if (_plotter->drawstate->pen_type != 0) /* have a pen to draw with */ { /* set pen color as foreground color in GC used for drawing (but first, check whether we can avoid a function call) */ newcolor = _plotter->drawstate->fgcolor; oldcolor = _plotter->drawstate->x_current_fgcolor; /* as stored in gc */ if (newcolor.red != oldcolor.red || newcolor.green != oldcolor.green || newcolor.blue != oldcolor.blue || ! _plotter->drawstate->x_gc_fgcolor_status) _pl_x_set_pen_color (S___(_plotter)); xx = XD(_plotter->drawstate->pos.x, _plotter->drawstate->pos.y); yy = YD(_plotter->drawstate->pos.x, _plotter->drawstate->pos.y); ix = IROUND(xx); iy = IROUND(yy); if (_plotter->x_double_buffering != X_DBL_BUF_NONE) /* double buffering, have a `x_drawable3' to draw into */ XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3, _plotter->drawstate->x_gc_fg, ix, iy); else /* not double buffering, have no `x_drawable3' */ { if (_plotter->x_drawable1) XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1, _plotter->drawstate->x_gc_fg, ix, iy); if (_plotter->x_drawable2) XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2, _plotter->drawstate->x_gc_fg, ix, iy); } } /* maybe flush X output buffer and handle X events (a no-op for XDrawablePlotters, which is overridden for XPlotters) */ if (_plotter->x_paint_pixel_count % X_POINT_FLUSH_PERIOD == 0) _maybe_handle_x_events (S___(_plotter)); _plotter->x_paint_pixel_count++; }
void _pl_c_paint_point (S___(Plotter *_plotter)) { double xd, yd; int i_x, i_y; if (_plotter->drawstate->pen_type != 0) /* have a pen to draw with */ { if (_plotter->cgm_marker_type != CGM_M_DOT) /* emit "MARKER TYPE" command */ { int byte_count, data_byte_count, data_len; data_len = 2; /* number of bytes per index */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 6, data_len, &byte_count, "MARKERTYPE"); _cgm_emit_index (_plotter->data->page, false, _plotter->cgm_encoding, CGM_M_DOT, data_len, &data_byte_count, &byte_count); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update marker type */ _plotter->cgm_marker_type = CGM_M_DOT; } /* N.B. Should we set the marker size as well? Any good CGM interpreter should draw a dot marker as a very small dot, perhaps a single pixel. */ /* set CGM marker color */ _pl_c_set_pen_color (R___(_plotter) CGM_OBJECT_MARKER); /* compute location in device frame */ xd = XD(_plotter->drawstate->pos.x, _plotter->drawstate->pos.y); yd = YD(_plotter->drawstate->pos.x, _plotter->drawstate->pos.y); i_x = IROUND(xd); i_y = IROUND(yd); /* emit "POLYMARKER" command, to draw a single marker */ { int byte_count, data_byte_count, data_len; data_len = 1 * 2 * CGM_BINARY_BYTES_PER_INTEGER; byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_GRAPHICAL_PRIMITIVE_ELEMENT, 3, data_len, &byte_count, "MARKER"); _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, i_x, i_y, data_len, &data_byte_count, &byte_count); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); } } }
void _pl_a_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: { int i, numpoints; bool closed; double linewidth; /* 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 */ /* set fill color and pen color */ if (_plotter->drawstate->fill_type) /* will be filling the path */ _pl_a_set_fill_color (R___(_plotter) false); else /* won't be filling the path, but set AI's fill color anyway; in particular, to be the same as the pen color (this is a convenience for AI users who may wish e.g. to switch from stroking to filling) */ _pl_a_set_fill_color (R___(_plotter) true); _pl_a_set_pen_color (S___(_plotter)); /* update line attributes (cap style, join style, line width), if necessary */ _pl_a_set_attributes (S___(_plotter)); linewidth = _plotter->drawstate->line_width; numpoints = _plotter->drawstate->path->num_segments; /* loop over segments in path */ for (i = 0; i < numpoints; i++) { bool smooth_join_point; /* if a path join point, a smooth one? */ /* update bounding box to take into account the segment's terminal point (which is either a path join point or a path end point) */ if (!closed && (i == 0 || i == numpoints - 1)) /* for the path, an end rather than a join */ { double xcurrent, ycurrent, xother, yother; smooth_join_point = false; /* compute path end point, and a nearby point, the vector to which will determine the shape of the path end */ xcurrent = _plotter->drawstate->path->segments[i].p.x; ycurrent = _plotter->drawstate->path->segments[i].p.y; if (i == 0) /* i = 0, initial end point */ { if (_plotter->drawstate->path->segments[i+1].type == S_CUBIC) { xother = _plotter->drawstate->path->segments[i+1].pc.x; yother = _plotter->drawstate->path->segments[i+1].pc.y; } else /* line segment */ { xother = _plotter->drawstate->path->segments[i+1].p.x; yother = _plotter->drawstate->path->segments[i+1].p.y; } } else /* i = numpoints - 1, final end point */ { if (_plotter->drawstate->path->segments[i].type == S_CUBIC) { xother = _plotter->drawstate->path->segments[i].pd.x; yother = _plotter->drawstate->path->segments[i].pd.y; } else /* line segment */ { xother = _plotter->drawstate->path->segments[i-1].p.x; yother = _plotter->drawstate->path->segments[i-1].p.y; } } /* take path end into account: update bounding box */ _set_line_end_bbox (_plotter->data->page, xcurrent, ycurrent, xother, yother, linewidth, _plotter->drawstate->cap_type, _plotter->drawstate->transform.m); } else /* for the path, a join rather than an end */ { int a, b, c; double xcurrent, ycurrent, xleft, yleft, xright, yright; if (closed && (i == 0 || i == numpoints - 1)) /* wrap */ { a = numpoints - 2; b = numpoints - 1; c = 1; } else /* normal join */ { a = i - 1; b = i; c = i + 1; } xcurrent = _plotter->drawstate->path->segments[b].p.x; ycurrent = _plotter->drawstate->path->segments[b].p.y; /* compute points to left and right, vectors to which will determine the shape of the path join */ switch ((int)_plotter->drawstate->path->segments[b].type) { case (int)S_LINE: default: xleft = _plotter->drawstate->path->segments[a].p.x; yleft = _plotter->drawstate->path->segments[a].p.y; break; case (int)S_CUBIC: xleft = _plotter->drawstate->path->segments[b].pd.x; yleft = _plotter->drawstate->path->segments[b].pd.y; break; } switch ((int)_plotter->drawstate->path->segments[c].type) { case (int)S_LINE: default: xright = _plotter->drawstate->path->segments[c].p.x; yright = _plotter->drawstate->path->segments[c].p.y; break; case (int)S_CUBIC: xright = _plotter->drawstate->path->segments[c].pc.x; yright = _plotter->drawstate->path->segments[c].pc.y; break; } /* take path join into account: update bounding box */ _set_line_join_bbox(_plotter->data->page, xleft, yleft, xcurrent, ycurrent, xright, yright, linewidth, _plotter->drawstate->join_type, _plotter->drawstate->miter_limit, _plotter->drawstate->transform.m); /* is join smooth? */ { double ux, uy, vx, vy, cross, dot, uselfdot, vselfdot; ux = xleft - xcurrent; uy = yleft - ycurrent; vx = xright - xcurrent; vy = yright - ycurrent; cross = ux * vy - uy * vx; dot = ux * vx + uy * vy; uselfdot = ux * ux + uy * uy; vselfdot = vx * vx + vy * vy; if (cross * cross < MAX_SQUARED_SINE * uselfdot * vselfdot && dot < 0.0) smooth_join_point = true; else smooth_join_point = false; } } /* output to Illustrator the points that define this segment */ if (i != 0 && (_plotter->drawstate->path->segments)[i].type == S_CUBIC) /* cubic Bezier segment, so output control points */ { sprintf (_plotter->data->page->point, "%.4f %.4f %.4f %.4f ", XD(_plotter->drawstate->path->segments[i].pc.x, _plotter->drawstate->path->segments[i].pc.y), YD(_plotter->drawstate->path->segments[i].pc.x, _plotter->drawstate->path->segments[i].pc.y), XD(_plotter->drawstate->path->segments[i].pd.x, _plotter->drawstate->path->segments[i].pd.y), YD(_plotter->drawstate->path->segments[i].pd.x, _plotter->drawstate->path->segments[i].pd.y)); _update_buffer (_plotter->data->page); /* update bounding box due to extremal x/y values in device frame */ _set_bezier3_bbox (_plotter->data->page, _plotter->drawstate->path->segments[i-1].p.x, _plotter->drawstate->path->segments[i-1].p.y, _plotter->drawstate->path->segments[i].pc.x, _plotter->drawstate->path->segments[i].pc.y, _plotter->drawstate->path->segments[i].pd.x, _plotter->drawstate->path->segments[i].pd.y, _plotter->drawstate->path->segments[i].p.x, _plotter->drawstate->path->segments[i].p.y, _plotter->drawstate->device_line_width, _plotter->drawstate->transform.m); } /* output terminal point of segment */ sprintf (_plotter->data->page->point, "%.4f %.4f ", XD(_plotter->drawstate->path->segments[i].p.x, _plotter->drawstate->path->segments[i].p.y), YD(_plotter->drawstate->path->segments[i].p.x, _plotter->drawstate->path->segments[i].p.y)); _update_buffer (_plotter->data->page); /* tell Illustrator what sort of path segment this is */ if (i == 0) /* start of path, so just move to point */ sprintf (_plotter->data->page->point, "m\n"); else /* append line segment or Bezier segment to path */ switch ((int)_plotter->drawstate->path->segments[i].type) { case (int)S_LINE: default: sprintf (_plotter->data->page->point, smooth_join_point ? "l\n" : "L\n"); break; case (int)S_CUBIC: sprintf (_plotter->data->page->point, smooth_join_point ? "c\n" : "C\n"); break; } _update_buffer (_plotter->data->page); } /* end of loop over segments */ if (_plotter->drawstate->pen_type) /* have a pen to draw with */ { /* emit `closepath' if path is closed; stroke and maybe fill */ if (_plotter->drawstate->fill_type) { if (closed) /* close path, fill and stroke */ sprintf (_plotter->data->page->point, "b\n"); else /* fill and stroke */ sprintf (_plotter->data->page->point, "B\n"); } else { if (closed) /* close path, stroke */ sprintf (_plotter->data->page->point, "s\n"); else /* stroke */ sprintf (_plotter->data->page->point, "S\n"); } } else /* no pen to draw with, but we may do filling */ { /* emit `closepath' if path is closed; don't stroke */ if (_plotter->drawstate->fill_type) { if (closed) /* close path, fill */ sprintf (_plotter->data->page->point, "f\n"); else /* fill */ sprintf (_plotter->data->page->point, "F\n"); } } _update_buffer (_plotter->data->page); } break; default: /* shouldn't happen */ break; } }
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; } }
void _pl_x_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: { bool closed; /* not currently used */ int is_a_rectangle; int i, polyline_len; plPoint p0, p1, pc; XPoint *xarray, local_xarray[MAX_NUM_POINTS_ON_STACK]; bool heap_storage; double xu_last, yu_last; bool identical_user_coordinates; /* 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 == 2 && _plotter->drawstate->path->segments[1].type == S_ARC) /* segment buffer contains a single circular arc, not a polyline */ { p0 = _plotter->drawstate->path->segments[0].p; p1 = _plotter->drawstate->path->segments[1].p; pc = _plotter->drawstate->path->segments[1].pc; /* use native X rendering to draw the (transformed) circular arc */ _pl_x_draw_elliptic_arc (R___(_plotter) p0, p1, pc); break; } if (_plotter->drawstate->path->num_segments == 2 && _plotter->drawstate->path->segments[1].type == S_ELLARC) /* segment buffer contains a single elliptic arc, not a polyline */ { p0 = _plotter->drawstate->path->segments[0].p; p1 = _plotter->drawstate->path->segments[1].p; pc = _plotter->drawstate->path->segments[1].pc; /* use native X rendering to draw the (transformed) elliptic arc */ _pl_x_draw_elliptic_arc_2 (R___(_plotter) p0, p1, pc); break; } /* neither of above applied, so segment buffer contains a polyline, not an arc */ 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 */ /* Check whether we `pre-drew' the polyline, i.e., drew every line segment in real time. (See the maybe_prepaint_segments() method further below, which is invoked to do that.) Our convention: we pre-draw only if pen width is zero, and line style is "solid". Also, we don't do it if we're drawing a polygonalized built-in object (i.e. a rectangle or ellipse). If we pre-drew, we don't do anything here unless there's filling to be done. If so, we'll fill the polyline and re-edge it. */ if ((_plotter->drawstate->pen_type != 0 /* pen is present */ && _plotter->drawstate->line_type == PL_L_SOLID && !_plotter->drawstate->dash_array_in_effect /* really solid */ && _plotter->drawstate->points_are_connected /* really, really */ && _plotter->drawstate->quantized_device_line_width == 0 && !_plotter->drawstate->path->primitive) /* not builtin object */ /* we pre-drew */ && _plotter->drawstate->fill_type == 0) /* there's no filling to be done, so we're out of here */ break; /* At this point we know that we didn't pre-draw, or we did, but the polyline was drawn unfilled and it'll need to be re-drawn as filled. */ /* prepare an array of XPoint structures (X11 uses short ints for these) */ if (_plotter->drawstate->path->num_segments <= MAX_NUM_POINTS_ON_STACK) /* store XPoints on stack, for speed */ { xarray = local_xarray; heap_storage = false; } else /* store XPoints in heap */ { xarray = (XPoint *)_pl_xmalloc (_plotter->drawstate->path->num_segments * sizeof(XPoint)); heap_storage = true; } /* convert vertices to device coordinates, removing runs; also keep track of whether or not all points in user space are the same */ polyline_len = 0; xu_last = 0.0; yu_last = 0.0; identical_user_coordinates = true; for (i = 0; i < _plotter->drawstate->path->num_segments; i++) { plPathSegment datapoint; double xu, yu, xd, yd; int device_x, device_y; datapoint = _plotter->drawstate->path->segments[i]; xu = datapoint.p.x; yu = datapoint.p.y; xd = XD(xu, yu); yd = YD(xu, yu); device_x = IROUND(xd); device_y = IROUND(yd); if (X_OOB_INT(device_x) || X_OOB_INT(device_y)) /* point position can't be represented using X11's 2-byte ints, so truncate the polyline right here */ { _plotter->warning (R___(_plotter) "truncating a polyline that extends too far for X11"); break; } if (i > 0 && (xu != xu_last || yu != yu_last)) /* in user space, not all points are the same */ identical_user_coordinates = false; if ((polyline_len == 0) || (device_x != xarray[polyline_len-1].x) || (device_y != xarray[polyline_len-1].y)) /* add point, in integer X coordinates, to the array */ { xarray[polyline_len].x = device_x; xarray[polyline_len].y = device_y; polyline_len++; if (polyline_len >= _plotter->x_max_polyline_len) /* polyline is getting too long for the X server to handle (we determined the maximum X request size when openpl() was invoked), so truncate it right here */ { _plotter->warning (R___(_plotter) "truncating a polyline that's too long for the X display"); break; } } xu_last = xu; yu_last = yu; } /* Is this path a rectangle in device space? We check this because by calling XFillRectangle (and XDrawRectangle too, if the edging is solid), we can save a bit of time, or at least network bandwidth. */ /* N.B. This checks only for rectangles traced counterclockwise from the lower left corner. Should improve this. */ #define IS_A_RECTANGLE(len,q) \ ((len) == 5 && (q)[0].x == (q)[4].x && (q)[0].y == (q)[4].y && \ (q)[0].x == (q)[3].x && (q)[1].x == (q)[2].x && \ (q)[0].y == (q)[1].y && (q)[2].y == (q)[3].y && \ (q)[0].x < (q)[1].x && (q)[0].y > (q)[2].y) /* note flipped-y convention */ is_a_rectangle = IS_A_RECTANGLE(polyline_len, xarray); /* N.B. If a rectangle, upper left corner = q[3], and also, width = q[1].x - q[0].x and height = q[0].y - q[2].y (note flipped-y convention). */ /* compute the square size, and offset of upper left vertex from center of square, that we'll use when handling the notorious special case: all user-space points in the polyline get mapped to a single integer X pixel */ /* FIRST TASK: fill the polygon (if necessary). */ if (_plotter->drawstate->fill_type) /* not transparent, so fill the path */ { int x_polygon_type = (_plotter->drawstate->path->primitive ? Convex : Complex); /* update GC used for filling */ _pl_x_set_attributes (R___(_plotter) X_GC_FOR_FILLING); /* select fill color as foreground color in GC used for filling */ _pl_x_set_fill_color (S___(_plotter)); if (_plotter->x_double_buffering != X_DBL_BUF_NONE) { if (_plotter->drawstate->path->num_segments > 1 && polyline_len == 1) /* special case: all user-space points in the polyline were mapped to a single integer X pixel */ XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3, _plotter->drawstate->x_gc_fill, (int)(xarray[0].x), (int)(xarray[0].y)); else /* general case */ { if (is_a_rectangle) /* call XFillRectangle, for speed */ XFillRectangle (_plotter->x_dpy, _plotter->x_drawable3, _plotter->drawstate->x_gc_fill, (int)(xarray[3].x), (int)(xarray[3].y), (unsigned int)(xarray[1].x - xarray[0].x), /* flipped y */ (unsigned int)(xarray[0].y - xarray[2].y)); else /* not a rectangle, call XFillPolygon */ XFillPolygon (_plotter->x_dpy, _plotter->x_drawable3, _plotter->drawstate->x_gc_fill, xarray, polyline_len, x_polygon_type, CoordModeOrigin); } } else /* not double buffering, no `x_drawable3' */ { if (_plotter->drawstate->path->num_segments > 1 && polyline_len == 1) /* special case: all user-space points in the polyline were mapped to a single integer X pixel. */ { if (_plotter->x_drawable1) XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1, _plotter->drawstate->x_gc_fill, (int)(xarray[0].x), (int)(xarray[0].y)); if (_plotter->x_drawable2) XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2, _plotter->drawstate->x_gc_fill, (int)(xarray[0].x), (int)(xarray[0].y)); } else /* general case */ { if (is_a_rectangle) /* call XFillRectangle, for speed */ { if (_plotter->x_drawable1) XFillRectangle (_plotter->x_dpy, _plotter->x_drawable1, _plotter->drawstate->x_gc_fill, (int)(xarray[3].x), (int)(xarray[3].y), (unsigned int)(xarray[1].x - xarray[0].x), /* flipped y */ (unsigned int)(xarray[0].y - xarray[2].y)); if (_plotter->x_drawable2) XFillRectangle (_plotter->x_dpy, _plotter->x_drawable2, _plotter->drawstate->x_gc_fill, (int)(xarray[3].x), (int)(xarray[3].y), (unsigned int)(xarray[1].x - xarray[0].x), /* flipped y */ (unsigned int)(xarray[0].y - xarray[2].y)); } else /* not a rectangle, call XFillPolygon */ { if (_plotter->x_drawable1) XFillPolygon (_plotter->x_dpy, _plotter->x_drawable1, _plotter->drawstate->x_gc_fill, xarray, polyline_len, x_polygon_type, CoordModeOrigin); if (_plotter->x_drawable2) XFillPolygon (_plotter->x_dpy, _plotter->x_drawable2, _plotter->drawstate->x_gc_fill, xarray, polyline_len, x_polygon_type, CoordModeOrigin); } } } } /* SECOND TASK: edge the polygon (if necessary). */ if (_plotter->drawstate->pen_type) /* pen is present, so edge the path */ { int xloc = 0, yloc = 0; unsigned int sp_size = 1; /* update GC used for drawing */ _pl_x_set_attributes (R___(_plotter) X_GC_FOR_DRAWING); /* select pen color as foreground color in GC used for drawing */ _pl_x_set_pen_color (S___(_plotter)); /* Check first for the special case: all points in the polyline were mapped to a single integer X 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 the circle would have diameter 1 or less, we draw a point instead.) */ if (_plotter->drawstate->path->num_segments > 1 && polyline_len == 1) /* this is the special case, so compute quantities needed for drawing the filled circle */ { int sp_offset; sp_size = (unsigned int)_plotter->drawstate->quantized_device_line_width; if (sp_size == 0) sp_size = 1; sp_offset = (_plotter->drawstate->quantized_device_line_width + 1) / 2; xloc = xarray[0].x - sp_offset; yloc = xarray[0].y - sp_offset; } if (_plotter->x_double_buffering != X_DBL_BUF_NONE) /* double buffering, have a `x_drawable3' to draw into */ { if (_plotter->drawstate->path->num_segments > 1 && polyline_len == 1) /* special case */ { if (identical_user_coordinates == false || _plotter->drawstate->cap_type == PL_CAP_ROUND) { if (sp_size == 1) /* subcase: just draw a point */ XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3, _plotter->drawstate->x_gc_fg, (int)(xarray[0].x), (int)(xarray[0].y)); else /* draw filled circle */ XFillArc(_plotter->x_dpy, _plotter->x_drawable3, _plotter->drawstate->x_gc_fg, xloc, yloc, sp_size, sp_size, 0, 64 * 360); } } else /* general case */ /* NOTE: this code is what libplot uses to draw nearly all polylines, in the case when double buffering is used */ { if (is_a_rectangle && _plotter->drawstate->dash_array_in_effect == false && _plotter->drawstate->line_type == PL_L_SOLID) /* call XDrawRectangle, for speed */ XDrawRectangle (_plotter->x_dpy, _plotter->x_drawable3, _plotter->drawstate->x_gc_fg, (int)(xarray[3].x), (int)(xarray[3].y), (unsigned int)(xarray[1].x - xarray[0].x), /* flipped y */ (unsigned int)(xarray[0].y - xarray[2].y)); else /* can't call XDrawRectangle */ XDrawLines (_plotter->x_dpy, _plotter->x_drawable3, _plotter->drawstate->x_gc_fg, xarray, polyline_len, CoordModeOrigin); } } else /* not double buffering, have no `x_drawable3' */ { if (_plotter->drawstate->path->num_segments > 1 && polyline_len == 1) /* special case */ { if (identical_user_coordinates == false || _plotter->drawstate->cap_type == PL_CAP_ROUND) { if (sp_size == 1) /* subcase: just draw a point */ { if (_plotter->x_drawable1) XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1, _plotter->drawstate->x_gc_fg, (int)(xarray[0].x), (int)(xarray[0].y)); if (_plotter->x_drawable2) XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2, _plotter->drawstate->x_gc_fg, (int)(xarray[0].x), (int)(xarray[0].y)); } else /* draw filled circle */ { if (_plotter->x_drawable1) XFillArc(_plotter->x_dpy, _plotter->x_drawable1, _plotter->drawstate->x_gc_fg, xloc, yloc, sp_size, sp_size, 0, 64 * 360); if (_plotter->x_drawable2) XFillArc(_plotter->x_dpy, _plotter->x_drawable2, _plotter->drawstate->x_gc_fg, xloc, yloc, sp_size, sp_size, 0, 64 * 360); } } } else /* general case */ /* NOTE: this code is what libplot uses to draw nearly all polylines; at least, if double buffering is not used */ { if (is_a_rectangle && _plotter->drawstate->dash_array_in_effect == false && _plotter->drawstate->line_type == PL_L_SOLID) /* call XDrawRectangle, for speed */ { if (_plotter->x_drawable1) XDrawRectangle (_plotter->x_dpy, _plotter->x_drawable1, _plotter->drawstate->x_gc_fg, (int)(xarray[3].x), (int)(xarray[3].y), (unsigned int)(xarray[1].x - xarray[0].x), /* flipped y */ (unsigned int)(xarray[0].y - xarray[2].y)); if (_plotter->x_drawable2) XDrawRectangle (_plotter->x_dpy, _plotter->x_drawable2, _plotter->drawstate->x_gc_fg, (int)(xarray[3].x), (int)(xarray[3].y), (unsigned int)(xarray[1].x - xarray[0].x), /* flipped y */ (unsigned int)(xarray[0].y - xarray[2].y)); } else /* can't use XDrawRectangle() */ { if (_plotter->x_drawable1) XDrawLines (_plotter->x_dpy, _plotter->x_drawable1, _plotter->drawstate->x_gc_fg, xarray, polyline_len, CoordModeOrigin); if (_plotter->x_drawable2) XDrawLines (_plotter->x_dpy, _plotter->x_drawable2, _plotter->drawstate->x_gc_fg, xarray, polyline_len, CoordModeOrigin); } } } } /* reset buffer used for array of XPoint structs */ if (_plotter->drawstate->path->num_segments > 0) { if (heap_storage) free (xarray); /* free malloc'd array of XPoints */ } } break; case (int)PATH_ELLIPSE: { int ninetymult; int x_orientation, y_orientation; int xorigin, yorigin; unsigned int squaresize_x, squaresize_y; plPoint pc; double rx, ry, angle; pc = _plotter->drawstate->path->pc; rx = _plotter->drawstate->path->rx; ry = _plotter->drawstate->path->ry; angle = _plotter->drawstate->path->angle; /* if angle is multiple of 90 degrees, modify to permit use of X11 arc rendering */ ninetymult = IROUND(angle / 90.0); if (angle == (double) (90 * ninetymult)) { angle = 0.0; if (ninetymult % 2) { double temp; temp = rx; rx = ry; ry = temp; } } rx = (rx < 0.0 ? -rx : rx); /* avoid obscure libxmi problems */ ry = (ry < 0.0 ? -ry : ry); /* axes flipped? (by default y-axis is, due to libxmi's flipped-y convention) */ x_orientation = (_plotter->drawstate->transform.m[0] >= 0 ? 1 : -1); y_orientation = (_plotter->drawstate->transform.m[3] >= 0 ? 1 : -1); /* location of `origin' (upper left corner of bounding rect. for ellipse) and width and height; X11's flipped-y convention affects these values */ xorigin = IROUND(XD(pc.x - x_orientation * rx, pc.y - y_orientation * ry)); yorigin = IROUND(YD(pc.x - x_orientation * rx, pc.y - y_orientation * ry)); squaresize_x = (unsigned int)IROUND(XDV(2 * x_orientation * rx, 0.0)); squaresize_y = (unsigned int)IROUND(YDV(0.0, 2 * y_orientation * ry)); /* Because this ellipse object was added to the path buffer, we already know that (1) the user->device frame map preserves coordinate axes, (2) effectively, angle == 0. These are necessary for the libxmi scan-conversion module to do the drawing. */ /* draw ellipse (elliptic arc aligned with the coordinate axes, arc range = 64*360 64'ths of a degree) */ _pl_x_draw_elliptic_arc_internal (R___(_plotter) xorigin, yorigin, squaresize_x, squaresize_y, 0, 64 * 360); } break; default: /* shouldn't happen */ break; } /* maybe flush X output buffer and handle X events (a no-op for XDrawablePlotters, which is overridden for XPlotters) */ _maybe_handle_x_events (S___(_plotter)); }
double theta0, theta1; int startangle, anglerange; int x_orientation, y_orientation; int xorigin, yorigin; unsigned int squaresize_x, squaresize_y; /* axes flipped? (by default y-axis is, due to X's flipped-y convention) */ x_orientation = (_plotter->drawstate->transform.m[0] >= 0 ? 1 : -1); y_orientation = (_plotter->drawstate->transform.m[3] >= 0 ? 1 : -1); /* radius of circular arc in user frame is distance to p0, and also to p1 */ radius = DIST(pc, p0); /* location of `origin' (upper left corner of bounding rect. on display) and width and height; X's flipped-y convention affects these values */ xorigin = IROUND(XD(pc.x - x_orientation * radius, pc.y - y_orientation * radius)); yorigin = IROUND(YD(pc.x - x_orientation * radius, pc.y - y_orientation * radius)); squaresize_x = (unsigned int)IROUND(XDV(2 * x_orientation * radius, 0.0)); squaresize_y = (unsigned int)IROUND(YDV(0.0, 2 * y_orientation * radius)); theta0 = _xatan2 (-y_orientation * (p0.y - pc.y), x_orientation * (p0.x - pc.x)) / M_PI; theta1 = _xatan2 (-y_orientation * (p1.y - pc.y), x_orientation * (p1.x - pc.x)) / M_PI; if (theta1 < theta0) theta1 += 2.0; /* adjust so that difference > 0 */ if (theta0 < 0.0) { theta0 += 2.0; /* adjust so that startangle > 0 */
void _pl_c_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: { bool closed; plIntPathSegment *xarray; int polyline_len; bool draw_as_cgm_compound, path_is_single_polyline; int pass; plPathSegmentType first_element_type; int i, byte_count, data_byte_count, data_len; int desired_interior_style; const char *desired_interior_style_string; /* sanity checks */ if (_plotter->drawstate->path->num_segments == 0)/* nothing to do */ break; if (_plotter->drawstate->path->num_segments == 1) /*shouldn't happen */ break; /* check for closure */ if ((_plotter->drawstate->path->num_segments >= 3) && (_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 */ /* set CGM pen/fill colors and line attributes, by emitting appropriate commands */ /* N.B. pen color and line attributes don't need to be set if pen_type is zero, signifying an edgeless (presumably filled) path */ _pl_c_set_pen_color (R___(_plotter) closed ? CGM_OBJECT_CLOSED : CGM_OBJECT_OPEN); _pl_c_set_fill_color (R___(_plotter) closed ? CGM_OBJECT_CLOSED : CGM_OBJECT_OPEN); _pl_c_set_attributes (R___(_plotter) closed ? CGM_OBJECT_CLOSED : CGM_OBJECT_OPEN); /* 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[] (a moveto, presumably) */ 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; /* convert to integer CGM coordinates (unlike the HP-GL case [see h_path.c], we don't remove runs, so after this loop completes, polyline_len equals _plotter->drawstate->path->num_segments) */ 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; xdev = XD(xuser, yuser); ydev = YD(xuser, yuser); device_x = IROUND(xdev); device_y = IROUND(ydev); { 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 || element_type == S_ELLARC) /* an arc or elliptic arc element, so compute center too */ { 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; } 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)); } polyline_len++; } } /* A hack for CGM: if a circular or elliptic arc segment in integer device coordinates looks bogus, i.e. endpoints are the same or either is the same as the center point, replace it by a line segment. This will allow us to assume, later, that the displacement vectors from the center to the two endpoints are nonzero and unequal. */ for (i = 1; i < polyline_len; i++) { if (xarray[i].type == S_ARC || xarray[i].type == S_ELLARC) if ((xarray[i-1].p.x == xarray[i].p.x && xarray[i-1].p.y == xarray[i].p.y) || (xarray[i-1].p.x == xarray[i].pc.x && xarray[i-1].p.y == xarray[i].pc.y) || (xarray[i].p.x == xarray[i].pc.x && xarray[i].p.y == xarray[i].pc.y)) xarray[i].type = S_LINE; } /* set CGM attributes (differently, depending on whether path is closed or open, because different CGM graphical primitives will be emitted in the two cases to draw the path) */ if (closed) { if (_plotter->drawstate->fill_type == 0) /* won't do filling */ { desired_interior_style = CGM_INT_STYLE_EMPTY; desired_interior_style_string = "empty"; } else /* will do filling */ { desired_interior_style = CGM_INT_STYLE_SOLID; desired_interior_style_string = "solid"; } if (_plotter->cgm_interior_style != desired_interior_style) /* emit "INTERIOR STYLE" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 22, data_len, &byte_count, "INTSTYLE"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, desired_interior_style, data_len, &data_byte_count, &byte_count, desired_interior_style_string); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update interior style */ _plotter->cgm_interior_style = desired_interior_style; } if (_plotter->drawstate->pen_type) /* should draw the closed path so that edge is visible */ { if (_plotter->cgm_edge_is_visible != true) /* emit "EDGE VISIBILITY" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 30, data_len, &byte_count, "EDGEVIS"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, 1, data_len, &data_byte_count, &byte_count, "on"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update edge visibility */ _plotter->cgm_edge_is_visible = true; } } else /* shouldn't edge the closed path */ { if (_plotter->cgm_edge_is_visible != false) /* emit "EDGE VISIBILITY" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 30, data_len, &byte_count, "EDGEVIS"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, 0, data_len, &data_byte_count, &byte_count, "off"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update edge visibility */ _plotter->cgm_edge_is_visible = false; } } } else /* open! */ { if (_plotter->drawstate->fill_type != 0) /* will `fill' the path by first drawing an edgeless solid-filled polygon, or an edgeless solid-filled closed figure; in both cases edge visibility will be turned off */ { if (_plotter->cgm_interior_style != CGM_INT_STYLE_SOLID) /* emit "INTERIOR STYLE" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 22, data_len, &byte_count, "INTSTYLE"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, CGM_INT_STYLE_SOLID, data_len, &data_byte_count, &byte_count, "solid"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update interior style */ _plotter->cgm_interior_style = CGM_INT_STYLE_SOLID; } if (_plotter->cgm_edge_is_visible) /* emit "EDGE VISIBILITY" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 30, data_len, &byte_count, "EDGEVIS"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, 0, data_len, &data_byte_count, &byte_count, "off"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update edge visibility */ _plotter->cgm_edge_is_visible = false; } } } /* Will path be drawn as a CGM compound primitive, containing > 1 graphical primitives? If it contains more than one type of path segment, or if it contains more than a single circular arc segment or elliptic arc segment, answer is `yes'. Because of our policies, implemented elsewhere, on what may be stored in the segment buffer (see above), we'll draw as a compound primitive only if CGM_MAX_VERSION >= 3. */ draw_as_cgm_compound = false; first_element_type = xarray[1].type; for (i = 2; i < polyline_len; i++) { if (xarray[i].type == S_ARC || xarray[i].type == S_ELLARC || xarray[i].type != first_element_type) { draw_as_cgm_compound = true; break; } } /* is path simply a polyline? */ { path_is_single_polyline = true; for (i = 1; i < polyline_len; i++) { if (xarray[i].type != S_LINE) { path_is_single_polyline = false; break; } } } /* Make two passes through segment buffer: (0) draw and fill, if necessary, a closed CGM object, e.g. a `closed figure' [necessary iff path is closed, or is open and filled], and (1) edge an open CGM object, e.g. a `compound line' [necessary iff path is open]. */ for (pass = 0; pass < 2; pass++) { int primitives_emitted; if (pass == 0 && !(closed || _plotter->drawstate->fill_type != 0)) /* no drawing of a closed object needed: skip pass 0 */ continue; if (pass == 1 && (closed || (!closed && _plotter->drawstate->pen_type == 0))) /* no need for a special `draw edge' pass: skip pass 1 */ continue; /* keep track of individual graphical primitives emitted per pass (profile requires <=128 per composite primitive, closed or open) */ primitives_emitted = 0; if (pass == 0 && !path_is_single_polyline) /* emit `BEGIN CLOSED FIGURE' command (no parameters); drawing of closed polylines and filling of open ones is handled specially (see below) */ { data_len = 0; byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_DELIMITER_ELEMENT, 8, data_len, &byte_count, "BEGFIGURE"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update CGM version needed for this page */ _plotter->cgm_page_version = IMAX(2, _plotter->cgm_page_version); } if (pass == 1 && draw_as_cgm_compound) /* emit `BEGIN COMPOUND LINE' command (no parameters) */ { data_len = 0; byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_DELIMITER_ELEMENT, 15, data_len, &byte_count, "BEGCOMPOLINE"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update CGM version needed for this page */ _plotter->cgm_page_version = IMAX(3, _plotter->cgm_page_version); } /* iterate over path elements, combining runs of line segments into polylines, and runs of Beziers into poly-Beziers, but emitting each circular arc and elliptic arc individually (since CGM doesn't support poly-arcs) */ i = 0; while (i + 1 < polyline_len) { int j, end_of_run; plPathSegmentType element_type; /* determine `run' (relevant only for lines, Beziers) */ element_type = xarray[i + 1].type; for (j = i + 1; j < polyline_len && xarray[j].type == element_type; j++) ; end_of_run = j - 1; switch ((int)element_type) { case (int)S_LINE: if ((pass == 0 && !path_is_single_polyline) || (pass == 1)) /* normal case: emit "POLYLINE" command to draw polyline */ /* number of line segments in polyline: end_of_run - i */ /* number of points in polyline: 1 + (end_of_run - i) */ { /* update CGM profile for this page */ if (1 + (end_of_run - i) > 4096) _plotter->cgm_page_profile = IMAX(_plotter->cgm_page_profile, CGM_PROFILE_NONE); data_len = 2 * CGM_BINARY_BYTES_PER_INTEGER * (1 + end_of_run - i); byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_GRAPHICAL_PRIMITIVE_ELEMENT, 1, data_len, &byte_count, "LINE"); /* combine line segments into polyline */ for ( ; i <= end_of_run; i++) _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i].p.x, xarray[i].p.y, data_len, &data_byte_count, &byte_count); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); primitives_emitted++; /* next CGM component object begins at i=end_of_run */ i--; } else /* Special case: we're running pass 0, and path consists of a single polyline. So emit "POLYGON" command, omitting the final point if the polyline is closed, to agree with CGM conventions. */ { /* update CGM profile for this page */ if (polyline_len - (closed ? 1 : 0) > 4096) _plotter->cgm_page_profile = IMAX(_plotter->cgm_page_profile, CGM_PROFILE_NONE); data_len = 2 * CGM_BINARY_BYTES_PER_INTEGER * (polyline_len - (closed ? 1 : 0)); byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_GRAPHICAL_PRIMITIVE_ELEMENT, 7, data_len, &byte_count, "POLYGON"); for (i = 0; i < polyline_len - (closed ? 1 : 0); i++) _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i].p.x, xarray[i].p.y, data_len, &data_byte_count, &byte_count); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); primitives_emitted++; /* we've used up the entire segment buffer: no more primitives to emit */ i = polyline_len - 1; } break; case (int)S_ARC: /* emit "CIRCULAR ARC CENTRE [REVERSED]" command */ { int delta0_x = xarray[i].p.x - xarray[i + 1].pc.x; int delta0_y = xarray[i].p.y - xarray[i + 1].pc.y; int delta1_x = xarray[i + 1].p.x - xarray[i + 1].pc.x; int delta1_y = xarray[i + 1].p.y - xarray[i + 1].pc.y; double radius = sqrt((double)delta0_x * (double)delta0_x + (double)delta0_y * (double)delta0_y); int i_radius = IROUND(radius); double dot = ((double)delta0_x * (double)delta1_y - (double)delta0_y * (double)delta1_x); bool reversed = (dot >= 0.0 ? false : true); /* args: 1 point, 2 vectors, and the radius */ data_len = (3 * 2 + 1) * CGM_BINARY_BYTES_PER_INTEGER; byte_count = data_byte_count = 0; if (reversed) _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_GRAPHICAL_PRIMITIVE_ELEMENT, 20, data_len, &byte_count, "ARCCTRREV"); else _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_GRAPHICAL_PRIMITIVE_ELEMENT, 15, data_len, &byte_count, "ARCCTR"); /* center point */ _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i + 1].pc.x, xarray[i + 1].pc.y, data_len, &data_byte_count, &byte_count); /* vector from center to starting point */ _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, delta0_x, delta0_y, data_len, &data_byte_count, &byte_count); /* vector from center to ending point */ _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, delta1_x, delta1_y, data_len, &data_byte_count, &byte_count); /* radius (distance from center to starting point) */ _cgm_emit_integer (_plotter->data->page, false, _plotter->cgm_encoding, i_radius, data_len, &data_byte_count, &byte_count); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); primitives_emitted++; /* update CGM version needed for this page */ if (reversed) _plotter->cgm_page_version = IMAX(2, _plotter->cgm_page_version); } /* on to next CGM component object */ i++; break; case (int)S_ELLARC: /* emit "ELLIPTICAL ARC" command to draw quarter-ellipse */ { /* args: 3 points, 2 vectors */ data_len = 5 * 2 * CGM_BINARY_BYTES_PER_INTEGER; byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_GRAPHICAL_PRIMITIVE_ELEMENT, 18, data_len, &byte_count, "ELLIPARC"); /* center point */ _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i + 1].pc.x, xarray[i + 1].pc.y, data_len, &data_byte_count, &byte_count); /* starting point */ _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i].p.x, xarray[i].p.y, data_len, &data_byte_count, &byte_count); /* ending point */ _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i + 1].p.x, xarray[i + 1].p.y, data_len, &data_byte_count, &byte_count); /* vector from center to starting point */ _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i].p.x - xarray[i + 1].pc.x, xarray[i].p.y - xarray[i + 1].pc.y, data_len, &data_byte_count, &byte_count); /* vector from center to ending point */ _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i + 1].p.x - xarray[i + 1].pc.x, xarray[i + 1].p.y - xarray[i + 1].pc.y, data_len, &data_byte_count, &byte_count); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); primitives_emitted++; } /* on to next CGM component object */ i++; break; case (int)S_CUBIC: /* emit "POLYBEZIER" command */ /* number of Bezier segments in path: end_of_run - i */ /* number of points in path: 1 + 3 * (end_of_run - i) */ /* Note: arguments include also a single `continuity indicator' (a two-byte CGM index) */ { /* update CGM profile for this page */ if (1 + 3 * (end_of_run - i) > 4096) _plotter->cgm_page_profile = IMAX(_plotter->cgm_page_profile, CGM_PROFILE_NONE); data_len = 2 + (2 * CGM_BINARY_BYTES_PER_INTEGER) * (1 + 3 * (end_of_run - i)); byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_GRAPHICAL_PRIMITIVE_ELEMENT, 26, data_len, &byte_count, "POLYBEZIER"); _cgm_emit_index (_plotter->data->page, false, _plotter->cgm_encoding, /* poly-Bezier continuity index: `2' means successive Beziers abut, so (after the first) each is specified by only three points; `1' means they don't abut. Our Beziers are contiguous, so we specify `2'. We used to specify `1' if there's only one Bezier, but the browser plug-in from SYSDEV didn't like that (it produced a parse error when such a Bezier was the only element of a CGM `closed figure'). */ #if 0 (end_of_run - i > 1 ? 2 : 1), #else (end_of_run - i > 1 ? 2 : 2), #endif data_len, &data_byte_count, &byte_count); /* starting point */ _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i].p.x, xarray[i].p.y, data_len, &data_byte_count, &byte_count); i++; /* combine Bezier segments into poly-Bezier */ for ( ; i <= end_of_run; i++) { _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i].pc.x, xarray[i].pc.y, data_len, &data_byte_count, &byte_count); _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i].pd.x, xarray[i].pd.y, data_len, &data_byte_count, &byte_count); _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xarray[i].p.x, xarray[i].p.y, data_len, &data_byte_count, &byte_count); } _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); primitives_emitted++; /* update CGM version needed for this page */ _plotter->cgm_page_version = IMAX(3, _plotter->cgm_page_version); /* next CGM component object begins at i=end_of_run */ i--; } break; default: /* shouldn't happen: unknown path segment type, ignore */ i++; break; } } if (pass == 0 && !path_is_single_polyline) /* emit `END CLOSED FIGURE' command (no parameters) */ { data_len = 0; byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_DELIMITER_ELEMENT, 9, data_len, &byte_count, "ENDFIGURE"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update CGM version needed for this page */ _plotter->cgm_page_version = IMAX(2, _plotter->cgm_page_version); /* update CGM profile for this page */ if (primitives_emitted > 128) _plotter->cgm_page_profile = IMAX(_plotter->cgm_page_profile, CGM_PROFILE_NONE); } if (pass == 1 && draw_as_cgm_compound) /* emit `END COMPOUND LINE' command (no parameters) */ { data_len = 0; byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_DELIMITER_ELEMENT, 16, data_len, &byte_count, "ENDCOMPOLINE"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update CGM version needed for this page */ _plotter->cgm_page_version = IMAX(3, _plotter->cgm_page_version); /* update CGM profile for this page */ if (primitives_emitted > 128) _plotter->cgm_page_profile = IMAX(_plotter->cgm_page_profile, CGM_PROFILE_NONE); } } /* end of loop over passes */ /* free arrays of device-frame points */ free (xarray); } break; case (int)PATH_BOX: { plPoint p0, p1; int xd0, xd1, yd0, yd1; /* in integer device coordinates */ int byte_count, data_byte_count, data_len; int desired_interior_style; const char *desired_interior_style_string; p0 = _plotter->drawstate->path->p0; p1 = _plotter->drawstate->path->p1; /* compute corners in device coors */ xd0 = IROUND(XD(p0.x, p0.y)); yd0 = IROUND(YD(p0.x, p0.y)); xd1 = IROUND(XD(p1.x, p1.y)); yd1 = IROUND(YD(p1.x, p1.y)); /* set CGM edge color and attributes, by emitting appropriate commands */ _pl_c_set_pen_color (R___(_plotter) CGM_OBJECT_CLOSED); _pl_c_set_fill_color (R___(_plotter) CGM_OBJECT_CLOSED); _pl_c_set_attributes (R___(_plotter) CGM_OBJECT_CLOSED); if (_plotter->drawstate->fill_type == 0) /* won't do filling */ { desired_interior_style = CGM_INT_STYLE_EMPTY; desired_interior_style_string = "empty"; } else /* will do filling */ { desired_interior_style = CGM_INT_STYLE_SOLID; desired_interior_style_string = "solid"; } if (_plotter->cgm_interior_style != desired_interior_style) /* emit "INTERIOR STYLE" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 22, data_len, &byte_count, "INTSTYLE"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, desired_interior_style, data_len, &data_byte_count, &byte_count, desired_interior_style_string); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update interior style */ _plotter->cgm_interior_style = desired_interior_style; } if (_plotter->drawstate->pen_type) /* should edge the rectangle */ { if (_plotter->cgm_edge_is_visible != true) /* emit "EDGE VISIBILITY" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 30, data_len, &byte_count, "EDGEVIS"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, 1, data_len, &data_byte_count, &byte_count, "on"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update edge visibility */ _plotter->cgm_edge_is_visible = true; } } else /* shouldn't edge the rectangle */ { if (_plotter->cgm_edge_is_visible != false) /* emit "EDGE VISIBILITY" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 30, data_len, &byte_count, "EDGEVIS"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, 0, data_len, &data_byte_count, &byte_count, "off"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update edge visibility */ _plotter->cgm_edge_is_visible = false; } } /* emit "RECTANGLE" command */ { data_len = 2 * 2 * CGM_BINARY_BYTES_PER_INTEGER; byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_GRAPHICAL_PRIMITIVE_ELEMENT, 11, data_len, &byte_count, "RECT"); _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xd0, yd0, data_len, &data_byte_count, &byte_count); _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, xd1, yd1, data_len, &data_byte_count, &byte_count); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); } } break; case (int)PATH_CIRCLE: { double xd, yd, radius_d; int i_x, i_y, i_radius; /* center and radius, quantized */ plPoint pc; double radius; int byte_count, data_byte_count, data_len; int desired_interior_style; const char *desired_interior_style_string; pc = _plotter->drawstate->path->pc; radius = _plotter->drawstate->path->radius; /* known to be a circle in device frame, so compute center and radius in that frame */ xd = XD(pc.x, pc.y); yd = YD(pc.x, pc.y); radius_d = sqrt (XDV(radius,0) * XDV(radius,0) + YDV(radius,0) * YDV(radius,0)); i_x = IROUND(xd); i_y = IROUND(yd); i_radius = IROUND(radius_d); /* set CGM edge color and attributes, by emitting appropriate commands */ _pl_c_set_pen_color (R___(_plotter) CGM_OBJECT_CLOSED); _pl_c_set_fill_color (R___(_plotter) CGM_OBJECT_CLOSED); _pl_c_set_attributes (R___(_plotter) CGM_OBJECT_CLOSED); if (_plotter->drawstate->fill_type == 0) /* won't do filling */ { desired_interior_style = CGM_INT_STYLE_EMPTY; desired_interior_style_string = "empty"; } else /* will do filling */ { desired_interior_style = CGM_INT_STYLE_SOLID; desired_interior_style_string = "solid"; } if (_plotter->cgm_interior_style != desired_interior_style) /* emit "INTERIOR STYLE" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 22, data_len, &byte_count, "INTSTYLE"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, desired_interior_style, data_len, &data_byte_count, &byte_count, desired_interior_style_string); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update interior style */ _plotter->cgm_interior_style = desired_interior_style; } if (_plotter->drawstate->pen_type) /* should edge the circle */ { if (_plotter->cgm_edge_is_visible != true) /* emit "EDGE VISIBILITY" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 30, data_len, &byte_count, "EDGEVIS"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, 1, data_len, &data_byte_count, &byte_count, "on"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update edge visibility */ _plotter->cgm_edge_is_visible = true; } } else { if (_plotter->cgm_edge_is_visible != false) /* emit "EDGE VISIBILITY" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 30, data_len, &byte_count, "EDGEVIS"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, 0, data_len, &data_byte_count, &byte_count, "off"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update edge visibility */ _plotter->cgm_edge_is_visible = false; } } /* emit "CIRCLE" command */ { data_len = 3 * CGM_BINARY_BYTES_PER_INTEGER; byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_GRAPHICAL_PRIMITIVE_ELEMENT, 12, data_len, &byte_count, "CIRCLE"); _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, i_x, i_y, data_len, &data_byte_count, &byte_count); _cgm_emit_integer (_plotter->data->page, false, _plotter->cgm_encoding, i_radius, data_len, &data_byte_count, &byte_count); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); } } break; case (int)PATH_ELLIPSE: { double xd, yd; /* center, in device frame */ int i_x, i_y; /* center, quantized */ double theta, costheta, sintheta; double cd1_endpoint_x, cd1_endpoint_y; /* conjugate diameter endpts */ double cd2_endpoint_x, cd2_endpoint_y; int i1_x, i1_y, i2_x, i2_y; /* same, quantized */ plPoint pc; double rx, ry, angle; int byte_count, data_byte_count, data_len; int desired_interior_style; const char *desired_interior_style_string; pc = _plotter->drawstate->path->pc; rx = _plotter->drawstate->path->rx; ry = _plotter->drawstate->path->ry; angle = _plotter->drawstate->path->angle; /* compute center, in device frame */ xd = XD(pc.x, pc.y); yd = YD(pc.x, pc.y); i_x = IROUND(xd); i_y = IROUND(yd); /* inclination angle (radians), in user frame */ theta = M_PI * angle / 180.0; costheta = cos (theta); sintheta = sin (theta); /* perform affine user->device coor transformation, computing endpoints of conjugate diameter pair, in device frame */ cd1_endpoint_x = XD(pc.x + rx * costheta, pc.y + rx * sintheta); cd1_endpoint_y = YD(pc.x + rx * costheta, pc.y + rx * sintheta); cd2_endpoint_x = XD(pc.x - ry * sintheta, pc.y + ry * costheta); cd2_endpoint_y = YD(pc.x - ry * sintheta, pc.y + ry * costheta); i1_x = IROUND(cd1_endpoint_x); i1_y = IROUND(cd1_endpoint_y); i2_x = IROUND(cd2_endpoint_x); i2_y = IROUND(cd2_endpoint_y); /* set CGM edge color and attributes, by emitting appropriate commands */ _pl_c_set_pen_color (R___(_plotter) CGM_OBJECT_CLOSED); _pl_c_set_fill_color (R___(_plotter) CGM_OBJECT_CLOSED); _pl_c_set_attributes (R___(_plotter) CGM_OBJECT_CLOSED); if (_plotter->drawstate->fill_type == 0) /* won't do filling */ { desired_interior_style = CGM_INT_STYLE_EMPTY; desired_interior_style_string = "empty"; } else /* will do filling */ { desired_interior_style = CGM_INT_STYLE_SOLID; desired_interior_style_string = "solid"; } if (_plotter->cgm_interior_style != desired_interior_style) /* emit "INTERIOR STYLE" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 22, data_len, &byte_count, "INTSTYLE"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, desired_interior_style, data_len, &data_byte_count, &byte_count, desired_interior_style_string); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update interior style */ _plotter->cgm_interior_style = desired_interior_style; } if (_plotter->drawstate->pen_type) /* should edge the ellipse */ { if (_plotter->cgm_edge_is_visible != true) /* emit "EDGE VISIBILITY" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 30, data_len, &byte_count, "EDGEVIS"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, 1, data_len, &data_byte_count, &byte_count, "on"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update edge visibility */ _plotter->cgm_edge_is_visible = true; } } else { if (_plotter->cgm_edge_is_visible != false) /* emit "EDGE VISIBILITY" command */ { data_len = 2; /* 2 bytes per enum */ byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_ATTRIBUTE_ELEMENT, 30, data_len, &byte_count, "EDGEVIS"); _cgm_emit_enum (_plotter->data->page, false, _plotter->cgm_encoding, 0, data_len, &data_byte_count, &byte_count, "off"); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); /* update edge visibility */ _plotter->cgm_edge_is_visible = false; } } /* emit "ELLIPSE" command */ { data_len = 3 * 2 * CGM_BINARY_BYTES_PER_INTEGER; byte_count = data_byte_count = 0; _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding, CGM_GRAPHICAL_PRIMITIVE_ELEMENT, 17, data_len, &byte_count, "ELLIPSE"); _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, i_x, i_y, data_len, &data_byte_count, &byte_count); _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, i1_x, i1_y, data_len, &data_byte_count, &byte_count); _cgm_emit_point (_plotter->data->page, false, _plotter->cgm_encoding, i2_x, i2_y, data_len, &data_byte_count, &byte_count); _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding, &byte_count); } } break; default: /* shouldn't happen */ break; } }