File: p_ellipse.c

package info (click to toggle)
plotutils 2.0-2
  • links: PTS
  • area: main
  • in suites: hamm
  • size: 5,964 kB
  • ctags: 2,522
  • sloc: ansic: 38,416; sh: 1,853; yacc: 856; makefile: 181; lex: 144
file content (250 lines) | stat: -rw-r--r-- 9,094 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/* This file contains the circle method, which is a standard part of
   libplot.  It draws an object: a circle with center x,y and radius r. */

/* This file also contains the ellipse method, which is a GNU extension to
   libplot.  It draws an object: an ellipse with center xc,yc and semi-axes
   of length rx and ry (the former at a specified angle with the x-axis).
   Both methods produce output in PS format, and both call
   _p_draw_ellipse_internal(). */

#include "sys-defines.h"
#include "plot.h"
#include "extern.h"

/* forward references */
void _p_fellipse_internal __P((double x, double y, double rx, double ry, double angle, bool circlep));

int
#ifdef _HAVE_PROTOS
_p_fcircle (double x, double y, double radius)
#else
_p_fcircle (x, y, radius)
     double x, y, radius;
#endif
{
  if (!_plotter->open)
    {
      _plotter->error ("fcircle: invalid operation");
      return -1;
    }

  _plotter->endpath (); /* flush polyline if any */

  /* final arg flags this for idraw as a circle, not an ellipse */
  _p_fellipse_internal (x, y, radius, radius, 0.0, true);

  return 0;
}

int
#ifdef _HAVE_PROTOS
_p_fellipse (double x, double y, double rx, double ry, double angle)
#else
_p_fellipse (x, y, rx, ry, angle)
     double x, y, rx, ry, angle;
#endif
{
  if (!_plotter->open)
    {
      _plotter->error ("fellipse: invalid operation");
      return -1;
    }

  _plotter->endpath (); /* flush polyline if any */

  /* final arg flags this for idraw as an ellipse, not a circle */
  _p_fellipse_internal (x, y, rx, ry, angle, false);

  return 0;
}

void
#ifdef _HAVE_PROTOS
_p_fellipse_internal (double x, double y, double rx, double ry, double angle, bool circlep)
#else
_p_fellipse_internal (x, y, rx, ry, angle, circlep)
     double x, y, rx, ry, angle;
     bool circlep;		/* drawn as a circle in user frame? */
#endif
{  
  double costheta, sintheta;
  double offcenter_rotation_matrix[6];
  double ellipse_transformation_matrix[6];
  double invnorm = 0.0, granularity = 1.0;
  bool singular_map;
  int i;

  /* compute reciprocal norm of user->device affine transformation; need
     this because transformation matrices we'll emit must be normalized */
  {
    double det, norm;
    
    det = _plotter->drawstate->transform.m[0] * _plotter->drawstate->transform.m[3]
      - _plotter->drawstate->transform.m[1] * _plotter->drawstate->transform.m[2];
    norm = sqrt(fabs(det));
    /* granularity = scaleup factor for user coordinates, so that when
       they're emitted as integers, resolution loss won't be excessive.
       CTM entries will be scaled down by this factor. */
    granularity = norm / (PS_MIN_RESOLUTION);
    if (norm != 0.0)
      {
	/* invnorm is `norm' of device->user coordinate transformation */
	invnorm = 1.0 / norm;
	singular_map = false;
      }
    else
      singular_map = true;
  }

  /* prologue instruction and idraw directive: start of Elli or Circ */
  if (circlep)
    strcpy (_plotter->outbuf.current, "Begin %I Circ\n");
  else
    strcpy (_plotter->outbuf.current, "Begin %I Elli\n");
  _update_buffer(&_plotter->outbuf);
  
  /* redefine `originalCTM' matrix (relevant to line width only) */
  if (singular_map != true)
    {
      int integer_linewidth = _plotter->drawstate->quantized_device_line_width;
      double double_linewidth = _plotter->drawstate->device_line_width;
      double linewidth_adjust;

      /* adjustment needed, due to our specifying line widths as integers */
      if (integer_linewidth != 0)
	linewidth_adjust = double_linewidth / integer_linewidth;
      else
	linewidth_adjust = 1.0;

      sprintf (_plotter->outbuf.current, "[");
      _update_buffer (&_plotter->outbuf);

      for (i = 0; i < 4; i++)
	{
	  sprintf (_plotter->outbuf.current, "%.7g ", 
		   linewidth_adjust * invnorm * _plotter->drawstate->transform.m[i]);
	  _update_buffer (&_plotter->outbuf);
	}
      _update_buffer (&_plotter->outbuf);
      sprintf (_plotter->outbuf.current, 
	       "0 0 ] trueoriginalCTM originalCTM\n concatmatrix pop\n");
      _update_buffer (&_plotter->outbuf);
    }

  /* specify cap style and join style */
  sprintf (_plotter->outbuf.current, "%d setlinecap %d setlinejoin\n",
	   _ps_cap_style[_plotter->drawstate->cap_type], 
	   _ps_join_style[_plotter->drawstate->join_type]);
  _update_buffer (&_plotter->outbuf);

  /* idraw directive: set line bit vector */
  sprintf (_plotter->outbuf.current, "%%I b %ld\n", 
	   _ps_line_type_bit_vector[_plotter->drawstate->line_type]);
  _update_buffer(&_plotter->outbuf);

  /* PS instruction: SetB (i.e. setbrush), with args
     LineWidth, LeftArrow, RightArrow, DashArray, DashOffset. */
  /* Note LineWidth must be an integer for idraw compatibility. */
  sprintf (_plotter->outbuf.current, "%d 0 0 [ %s ] 0 SetB\n", 
	   _plotter->drawstate->quantized_device_line_width,
	   _ps_line_type_setdash[_plotter->drawstate->line_type]);
  _update_buffer(&_plotter->outbuf);

  /* idraw directive and prologue instruction: set foreground color */
  _plotter->set_pen_color();	/* invoked lazily, i.e. when needed */
  sprintf (_plotter->outbuf.current, "%%I cfg %s\n%g %g %g SetCFg\n",
	   _idraw_stdcolornames[_plotter->drawstate->idraw_fgcolor],
	   _plotter->drawstate->ps_fgcolor_red, 
	   _plotter->drawstate->ps_fgcolor_green, 
	   _plotter->drawstate->ps_fgcolor_blue);
  _update_buffer(&_plotter->outbuf);

  /* includes idraw directive: set background color */
  _plotter->set_fill_color();	/* invoked lazily, i.e. when needed */
  sprintf (_plotter->outbuf.current, "%%I cbg %s\n%g %g %g SetCBg\n",
	   _idraw_stdcolornames[_plotter->drawstate->idraw_bgcolor],
	   _plotter->drawstate->ps_fillcolor_red, 
	   _plotter->drawstate->ps_fillcolor_green, 
	   _plotter->drawstate->ps_fillcolor_blue);
  _update_buffer(&_plotter->outbuf);

  /* includes idraw directive: set fill pattern */
  if (_plotter->drawstate->fill_level == 0)	/* transparent */
    sprintf (_plotter->outbuf.current, "%%I p\nnone SetP\n");
  else	/* filled, i.e. shaded, in the sense of idraw */
    sprintf (_plotter->outbuf.current, "%%I p\n%f SetP\n", 
	     _idraw_stdshadings[_plotter->drawstate->idraw_shading]);
  _update_buffer(&_plotter->outbuf);

  costheta = cos (M_PI * angle / 180.0);
  sintheta = sin (M_PI * angle / 180.0);

  /* An affine tranformation must be applied to the ellipse produced by the
   Elli routine in the idraw prologue, in order to turn it into the ellipse
   we want.  The Elli routine produces an ellipse with specified semi-axes,
   aligned parallel to the coordinate axes in user space, and centered on 
   the point (x,y).  I.e. it produces, symbolically,

   	[unit circle centered on (0,0)] S T

  where S is a diagonal matrix that scales the unit circle to give the
  specified semi-axis lengths, and T translates (0,0) to (x,y).  This is
  not what we want, since the ellipse is not rotated (it has zero
  inclination angle).  What we want is 
 
   	[unit circle centered on (0,0)] S R T

  where R is a rotation matrix.  This may be rewritten as

   	[unit circle centered on (0,0)] S T  (T^{-1} R T)

  where T^{-1} R T is a so-called offcenter rotation matrix, which rotates
  about the point (x,y).  So the ellipse transformation matrix we'll place
  in the PS code will be (T^{-1} R T) times the matrix that transforms from
  user space to device space. */

  offcenter_rotation_matrix[0] = costheta; /* 1st 4 elements are those of R */
  offcenter_rotation_matrix[1] = sintheta;
  offcenter_rotation_matrix[2] = - sintheta;
  offcenter_rotation_matrix[3] = costheta;
  offcenter_rotation_matrix[4] = x * (1.0 - costheta) + y * sintheta;
  offcenter_rotation_matrix[5] = y * (1.0 - costheta) - x * sintheta;
  
  _matrix_product (offcenter_rotation_matrix, _plotter->drawstate->transform.m,
		   ellipse_transformation_matrix);
  
  /* includes idraw directive: transformation matrix (all 6 elements) */
  sprintf (_plotter->outbuf.current, "%%I t\n[");
  _update_buffer(&_plotter->outbuf);
  for (i = 0; i < 6; i++)
    {
      if ((i==0) || (i==1) || (i==2) || (i==3))
	sprintf (_plotter->outbuf.current, "%.7g ", 
		 ellipse_transformation_matrix[i] / granularity);
      else
	sprintf (_plotter->outbuf.current, "%.7g ", 
		 ellipse_transformation_matrix[i]);
      _update_buffer(&_plotter->outbuf);
    }
  sprintf (_plotter->outbuf.current, "] concat\n");
  _update_buffer(&_plotter->outbuf);

  /* includes idraw directive: draw Elli, and end Elli (or same for Circ) */
  if (circlep)
    sprintf (_plotter->outbuf.current, "%%I\n%d %d %d Circ\nEnd\n\n", 
	     IROUND(granularity * x), IROUND(granularity * y), 
	     IROUND(granularity * rx));
  else
    sprintf (_plotter->outbuf.current, "%%I\n%d %d %d %d Elli\nEnd\n\n", 
	     IROUND(granularity * x), IROUND(granularity * y), 
	     IROUND(granularity * rx), IROUND(granularity * ry));
  _update_buffer(&_plotter->outbuf);

  /* update bounding box */
  _set_ellipse_bbox (x, y, rx, ry, costheta, sintheta, 
		     _plotter->drawstate->line_width);

  _plotter->drawstate->pos.x = x;	/* move to center of ellipse */
  _plotter->drawstate->pos.y = y;

}