Пример #1
0
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);
    }
}
Пример #2
0
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;
    }
}
Пример #3
0
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;
}
Пример #4
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++;
}
Пример #5
0
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);
      }
    }
}
Пример #6
0
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;
    }
}
Пример #7
0
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;
    }
}
Пример #8
0
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));
}
Пример #9
0
  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 */
Пример #10
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;
    }
}