1 /* This file is part of the GNU plotutils package. Copyright (C) 1995,
2 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3
4 The GNU plotutils package is free software. You may redistribute it
5 and/or modify it under the terms of the GNU General Public License as
6 published by the Free Software foundation; either version 2, or (at your
7 option) any later version.
8
9 The GNU plotutils package is distributed in the hope that it will be
10 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with the GNU plotutils package; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17 Boston, MA 02110-1301, USA. */
18
19 /* This file contains the internal paint_path() and paint_paths() methods,
20 which the public method endpath() is a wrapper around. */
21
22 /* This file also contains the internal path_is_flushable() method, which
23 is invoked after any path segment is added to the segment list, provided
24 (0) the segment list has become greater than or equal to the
25 `max_unfilled_path_length' Plotter parameter, (1) the path isn't to be
26 filled. In most Plotters, this operation simply returns true. */
27
28 /* This file also contains the internal maybe_prepaint_segments() method.
29 It is called immediately after any segment is added to a path. Some
30 Plotters, at least under some circumstances, treat endpath() as a no-op.
31 Instead, they plot the segments of a path in real time. */
32
33 /**********************************************************************/
34
35 /* This version of paint_path() is for XDrawablePlotters (and XPlotters).
36 By construction, for such Plotters our path buffer always contains
37 either a segment list, or an ellipse object. If it's a segment list, it
38 contains either (1) a sequence of line segments, or (2) a single
39 circular or elliptic arc segment. Those are all sorts of path that X11
40 can handle. (For an ellipse or circular/elliptic arc segment to have
41 been added to the path buffer, the map from user to device coordinates
42 must preserve axes.) */
43
44 /* If the line style is "solid" and the path has zero width, it's actually
45 drawn in real time, before endpath() and paint_path() are called; see
46 the maybe_prepaint_segments() method further below in this file. So if the
47 path doesn't need to be filled, we don't do anything in paint_path().
48 If it does, we fill it, and then redraw it. */
49
50 /* Note that when filling a polyline, we look at
51 _plotter->drawstate->path->primitive to determine which X11 rendering
52 algorithm to use. Our default algorithm is "Complex" (i.e. generic),
53 but when drawing polygonal approximations to ellipse and rectangle
54 primitives, which we know must be convex, we specify "Convex" instead,
55 to speed up rendering. */
56
57 #include "sys-defines.h"
58 #include "extern.h"
59
60 /* number of XPoint structs we can store on the stack, for speed, without
61 invoking malloc */
62 #define MAX_NUM_POINTS_ON_STACK 128
63
64 #define DIST(p1, p2) sqrt( ((p1).x - (p2).x) * ((p1).x - (p2).x) \
65 + ((p1).y - (p2).y) * ((p1).y - (p2).y))
66
67 void
_pl_x_paint_path(S___ (Plotter * _plotter))68 _pl_x_paint_path (S___(Plotter *_plotter))
69 {
70 if (_plotter->drawstate->pen_type == 0
71 && _plotter->drawstate->fill_type == 0)
72 /* nothing to draw */
73 return;
74
75 switch ((int)_plotter->drawstate->path->type)
76 {
77 case (int)PATH_SEGMENT_LIST:
78 {
79 bool closed; /* not currently used */
80 int is_a_rectangle;
81 int i, polyline_len;
82 plPoint p0, p1, pc;
83 XPoint *xarray, local_xarray[MAX_NUM_POINTS_ON_STACK];
84 bool heap_storage;
85 double xu_last, yu_last;
86 bool identical_user_coordinates;
87
88 /* sanity checks */
89 if (_plotter->drawstate->path->num_segments == 0)/* nothing to do */
90 break;
91 if (_plotter->drawstate->path->num_segments == 1) /*shouldn't happen */
92 break;
93
94 if (_plotter->drawstate->path->num_segments == 2
95 && _plotter->drawstate->path->segments[1].type == S_ARC)
96 /* segment buffer contains a single circular arc, not a polyline */
97 {
98 p0 = _plotter->drawstate->path->segments[0].p;
99 p1 = _plotter->drawstate->path->segments[1].p;
100 pc = _plotter->drawstate->path->segments[1].pc;
101
102 /* use native X rendering to draw the (transformed) circular
103 arc */
104 _pl_x_draw_elliptic_arc (R___(_plotter) p0, p1, pc);
105
106 break;
107 }
108
109 if (_plotter->drawstate->path->num_segments == 2
110 && _plotter->drawstate->path->segments[1].type == S_ELLARC)
111 /* segment buffer contains a single elliptic arc, not a polyline */
112 {
113 p0 = _plotter->drawstate->path->segments[0].p;
114 p1 = _plotter->drawstate->path->segments[1].p;
115 pc = _plotter->drawstate->path->segments[1].pc;
116
117 /* use native X rendering to draw the (transformed) elliptic
118 arc */
119 _pl_x_draw_elliptic_arc_2 (R___(_plotter) p0, p1, pc);
120
121 break;
122 }
123
124 /* neither of above applied, so segment buffer contains a polyline,
125 not an arc */
126
127 if ((_plotter->drawstate->path->num_segments >= 3)/*check for closure*/
128 && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.x == _plotter->drawstate->path->segments[0].p.x)
129 && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.y == _plotter->drawstate->path->segments[0].p.y))
130 closed = true;
131 else
132 closed = false; /* 2-point ones should be open */
133
134 /* Check whether we `pre-drew' the polyline, i.e., drew every line
135 segment in real time. (See the maybe_prepaint_segments() method
136 further below, which is invoked to do that.) Our convention: we
137 pre-draw only if pen width is zero, and line style is "solid".
138 Also, we don't do it if we're drawing a polygonalized built-in
139 object (i.e. a rectangle or ellipse).
140
141 If we pre-drew, we don't do anything here unless there's filling
142 to be done. If so, we'll fill the polyline and re-edge it. */
143
144 if ((_plotter->drawstate->pen_type != 0 /* pen is present */
145 && _plotter->drawstate->line_type == PL_L_SOLID
146 && !_plotter->drawstate->dash_array_in_effect /* really solid */
147 && _plotter->drawstate->points_are_connected /* really, really */
148 && _plotter->drawstate->quantized_device_line_width == 0
149 && !_plotter->drawstate->path->primitive) /* not builtin object */
150 /* we pre-drew */
151 &&
152 _plotter->drawstate->fill_type == 0)
153 /* there's no filling to be done, so we're out of here */
154 break;
155
156 /* At this point we know that we didn't pre-draw, or we did, but
157 the polyline was drawn unfilled and it'll need to be re-drawn as
158 filled. */
159
160 /* prepare an array of XPoint structures (X11 uses short ints for
161 these) */
162 if (_plotter->drawstate->path->num_segments
163 <= MAX_NUM_POINTS_ON_STACK)
164 /* store XPoints on stack, for speed */
165 {
166 xarray = local_xarray;
167 heap_storage = false;
168 }
169 else
170 /* store XPoints in heap */
171 {
172 xarray = (XPoint *)_pl_xmalloc (_plotter->drawstate->path->num_segments * sizeof(XPoint));
173 heap_storage = true;
174 }
175
176 /* convert vertices to device coordinates, removing runs; also keep
177 track of whether or not all points in user space are the same */
178
179 polyline_len = 0;
180 xu_last = 0.0;
181 yu_last = 0.0;
182 identical_user_coordinates = true;
183 for (i = 0; i < _plotter->drawstate->path->num_segments; i++)
184 {
185 plPathSegment datapoint;
186 double xu, yu, xd, yd;
187 int device_x, device_y;
188
189 datapoint = _plotter->drawstate->path->segments[i];
190 xu = datapoint.p.x;
191 yu = datapoint.p.y;
192 xd = XD(xu, yu);
193 yd = YD(xu, yu);
194 device_x = IROUND(xd);
195 device_y = IROUND(yd);
196
197 if (X_OOB_INT(device_x) || X_OOB_INT(device_y))
198 /* point position can't be represented using X11's 2-byte
199 ints, so truncate the polyline right here */
200 {
201 _plotter->warning (R___(_plotter)
202 "truncating a polyline that extends too far for X11");
203 break;
204 }
205
206 if (i > 0 && (xu != xu_last || yu != yu_last))
207 /* in user space, not all points are the same */
208 identical_user_coordinates = false;
209
210 if ((polyline_len == 0)
211 || (device_x != xarray[polyline_len-1].x)
212 || (device_y != xarray[polyline_len-1].y))
213 /* add point, in integer X coordinates, to the array */
214 {
215 xarray[polyline_len].x = device_x;
216 xarray[polyline_len].y = device_y;
217 polyline_len++;
218
219 if (polyline_len >= _plotter->x_max_polyline_len)
220 /* polyline is getting too long for the X server to
221 handle (we determined the maximum X request size when
222 openpl() was invoked), so truncate it right here */
223 {
224 _plotter->warning (R___(_plotter)
225 "truncating a polyline that's too long for the X display");
226 break;
227 }
228 }
229
230 xu_last = xu;
231 yu_last = yu;
232 }
233
234 /* Is this path a rectangle in device space? We check this because
235 by calling XFillRectangle (and XDrawRectangle too, if the edging
236 is solid), we can save a bit of time, or at least network
237 bandwidth. */
238
239 /* N.B. This checks only for rectangles traced counterclockwise
240 from the lower left corner. Should improve this. */
241
242 #define IS_A_RECTANGLE(len,q) \
243 ((len) == 5 && (q)[0].x == (q)[4].x && (q)[0].y == (q)[4].y && \
244 (q)[0].x == (q)[3].x && (q)[1].x == (q)[2].x && \
245 (q)[0].y == (q)[1].y && (q)[2].y == (q)[3].y && \
246 (q)[0].x < (q)[1].x && (q)[0].y > (q)[2].y) /* note flipped-y convention */
247
248 is_a_rectangle = IS_A_RECTANGLE(polyline_len, xarray);
249
250 /* N.B. If a rectangle, upper left corner = q[3], and also, width
251 = q[1].x - q[0].x and height = q[0].y - q[2].y (note flipped-y
252 convention). */
253
254 /* compute the square size, and offset of upper left vertex from
255 center of square, that we'll use when handling the notorious
256 special case: all user-space points in the polyline get mapped
257 to a single integer X pixel */
258
259 /* FIRST TASK: fill the polygon (if necessary). */
260
261 if (_plotter->drawstate->fill_type)
262 /* not transparent, so fill the path */
263 {
264 int x_polygon_type
265 = (_plotter->drawstate->path->primitive ? Convex : Complex);
266
267 /* update GC used for filling */
268 _pl_x_set_attributes (R___(_plotter) X_GC_FOR_FILLING);
269
270 /* select fill color as foreground color in GC used for filling */
271 _pl_x_set_fill_color (S___(_plotter));
272
273 if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
274 {
275 if (_plotter->drawstate->path->num_segments > 1
276 && polyline_len == 1)
277 /* special case: all user-space points in the polyline
278 were mapped to a single integer X pixel */
279 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3,
280 _plotter->drawstate->x_gc_fill,
281 (int)(xarray[0].x), (int)(xarray[0].y));
282 else
283 /* general case */
284 {
285 if (is_a_rectangle)
286 /* call XFillRectangle, for speed */
287 XFillRectangle (_plotter->x_dpy, _plotter->x_drawable3,
288 _plotter->drawstate->x_gc_fill,
289 (int)(xarray[3].x), (int)(xarray[3].y),
290 (unsigned int)(xarray[1].x - xarray[0].x),
291 /* flipped y */
292 (unsigned int)(xarray[0].y - xarray[2].y));
293 else
294 /* not a rectangle, call XFillPolygon */
295 XFillPolygon (_plotter->x_dpy, _plotter->x_drawable3,
296 _plotter->drawstate->x_gc_fill,
297 xarray, polyline_len,
298 x_polygon_type, CoordModeOrigin);
299 }
300 }
301 else /* not double buffering, no `x_drawable3' */
302 {
303 if (_plotter->drawstate->path->num_segments > 1
304 && polyline_len == 1)
305 /* special case: all user-space points in the polyline
306 were mapped to a single integer X pixel. */
307 {
308 if (_plotter->x_drawable1)
309 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1,
310 _plotter->drawstate->x_gc_fill,
311 (int)(xarray[0].x), (int)(xarray[0].y));
312 if (_plotter->x_drawable2)
313 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2,
314 _plotter->drawstate->x_gc_fill,
315 (int)(xarray[0].x), (int)(xarray[0].y));
316 }
317 else
318 /* general case */
319 {
320 if (is_a_rectangle)
321 /* call XFillRectangle, for speed */
322 {
323 if (_plotter->x_drawable1)
324 XFillRectangle (_plotter->x_dpy, _plotter->x_drawable1,
325 _plotter->drawstate->x_gc_fill,
326 (int)(xarray[3].x), (int)(xarray[3].y),
327 (unsigned int)(xarray[1].x - xarray[0].x),
328 /* flipped y */
329 (unsigned int)(xarray[0].y - xarray[2].y));
330 if (_plotter->x_drawable2)
331 XFillRectangle (_plotter->x_dpy, _plotter->x_drawable2,
332 _plotter->drawstate->x_gc_fill,
333 (int)(xarray[3].x), (int)(xarray[3].y),
334 (unsigned int)(xarray[1].x - xarray[0].x),
335 /* flipped y */
336 (unsigned int)(xarray[0].y - xarray[2].y));
337 }
338 else
339 /* not a rectangle, call XFillPolygon */
340 {
341 if (_plotter->x_drawable1)
342 XFillPolygon (_plotter->x_dpy, _plotter->x_drawable1,
343 _plotter->drawstate->x_gc_fill,
344 xarray, polyline_len,
345 x_polygon_type, CoordModeOrigin);
346 if (_plotter->x_drawable2)
347 XFillPolygon (_plotter->x_dpy, _plotter->x_drawable2,
348 _plotter->drawstate->x_gc_fill,
349 xarray, polyline_len,
350 x_polygon_type, CoordModeOrigin);
351 }
352 }
353 }
354 }
355
356 /* SECOND TASK: edge the polygon (if necessary). */
357
358 if (_plotter->drawstate->pen_type)
359 /* pen is present, so edge the path */
360 {
361 int xloc = 0, yloc = 0;
362 unsigned int sp_size = 1;
363
364 /* update GC used for drawing */
365 _pl_x_set_attributes (R___(_plotter) X_GC_FOR_DRAWING);
366
367 /* select pen color as foreground color in GC used for drawing */
368 _pl_x_set_pen_color (S___(_plotter));
369
370 /* Check first for the special case: all points in the polyline
371 were mapped to a single integer X pixel. If (1) they
372 weren't all the same to begin with, or (2) they were all the
373 same to begin with and the cap mode is "round", then draw as
374 a filled circle of diameter equal to the line width;
375 otherwise draw nothing. (If the circle would have diameter
376 1 or less, we draw a point instead.) */
377
378 if (_plotter->drawstate->path->num_segments > 1
379 && polyline_len == 1)
380 /* this is the special case, so compute quantities needed for
381 drawing the filled circle */
382 {
383 int sp_offset;
384
385 sp_size = (unsigned int)_plotter->drawstate->quantized_device_line_width;
386 if (sp_size == 0)
387 sp_size = 1;
388 sp_offset = (_plotter->drawstate->quantized_device_line_width + 1) / 2;
389 xloc = xarray[0].x - sp_offset;
390 yloc = xarray[0].y - sp_offset;
391 }
392
393 if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
394 /* double buffering, have a `x_drawable3' to draw into */
395 {
396 if (_plotter->drawstate->path->num_segments > 1
397 && polyline_len == 1)
398 /* special case */
399 {
400 if (identical_user_coordinates == false
401 || _plotter->drawstate->cap_type == PL_CAP_ROUND)
402 {
403 if (sp_size == 1)
404 /* subcase: just draw a point */
405 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3,
406 _plotter->drawstate->x_gc_fg,
407 (int)(xarray[0].x), (int)(xarray[0].y));
408 else
409 /* draw filled circle */
410 XFillArc(_plotter->x_dpy, _plotter->x_drawable3,
411 _plotter->drawstate->x_gc_fg,
412 xloc, yloc, sp_size, sp_size,
413 0, 64 * 360);
414 }
415 }
416 else
417 /* general case */
418 /* NOTE: this code is what libplot uses to draw nearly all
419 polylines, in the case when double buffering is used */
420 {
421 if (is_a_rectangle
422 && _plotter->drawstate->dash_array_in_effect == false
423 && _plotter->drawstate->line_type == PL_L_SOLID)
424 /* call XDrawRectangle, for speed */
425 XDrawRectangle (_plotter->x_dpy, _plotter->x_drawable3,
426 _plotter->drawstate->x_gc_fg,
427 (int)(xarray[3].x), (int)(xarray[3].y),
428 (unsigned int)(xarray[1].x - xarray[0].x),
429 /* flipped y */
430 (unsigned int)(xarray[0].y - xarray[2].y));
431 else
432 /* can't call XDrawRectangle */
433 XDrawLines (_plotter->x_dpy, _plotter->x_drawable3,
434 _plotter->drawstate->x_gc_fg,
435 xarray, polyline_len,
436 CoordModeOrigin);
437 }
438 }
439 else
440 /* not double buffering, have no `x_drawable3' */
441 {
442 if (_plotter->drawstate->path->num_segments > 1
443 && polyline_len == 1)
444 /* special case */
445 {
446 if (identical_user_coordinates == false
447 || _plotter->drawstate->cap_type == PL_CAP_ROUND)
448 {
449 if (sp_size == 1)
450 /* subcase: just draw a point */
451 {
452 if (_plotter->x_drawable1)
453 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1,
454 _plotter->drawstate->x_gc_fg,
455 (int)(xarray[0].x), (int)(xarray[0].y));
456 if (_plotter->x_drawable2)
457 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2,
458 _plotter->drawstate->x_gc_fg,
459 (int)(xarray[0].x), (int)(xarray[0].y));
460 }
461 else
462 /* draw filled circle */
463 {
464 if (_plotter->x_drawable1)
465 XFillArc(_plotter->x_dpy, _plotter->x_drawable1,
466 _plotter->drawstate->x_gc_fg,
467 xloc, yloc, sp_size, sp_size,
468 0, 64 * 360);
469 if (_plotter->x_drawable2)
470 XFillArc(_plotter->x_dpy, _plotter->x_drawable2,
471 _plotter->drawstate->x_gc_fg,
472 xloc, yloc, sp_size, sp_size,
473 0, 64 * 360);
474 }
475 }
476 }
477 else
478 /* general case */
479 /* NOTE: this code is what libplot uses to draw nearly all
480 polylines; at least, if double buffering is not used */
481 {
482 if (is_a_rectangle
483 && _plotter->drawstate->dash_array_in_effect == false
484 && _plotter->drawstate->line_type == PL_L_SOLID)
485 /* call XDrawRectangle, for speed */
486 {
487 if (_plotter->x_drawable1)
488 XDrawRectangle (_plotter->x_dpy, _plotter->x_drawable1,
489 _plotter->drawstate->x_gc_fg,
490 (int)(xarray[3].x), (int)(xarray[3].y),
491 (unsigned int)(xarray[1].x - xarray[0].x),
492 /* flipped y */
493 (unsigned int)(xarray[0].y - xarray[2].y));
494 if (_plotter->x_drawable2)
495 XDrawRectangle (_plotter->x_dpy, _plotter->x_drawable2,
496 _plotter->drawstate->x_gc_fg,
497 (int)(xarray[3].x), (int)(xarray[3].y),
498 (unsigned int)(xarray[1].x - xarray[0].x),
499 /* flipped y */
500 (unsigned int)(xarray[0].y - xarray[2].y));
501 }
502 else
503 /* can't use XDrawRectangle() */
504 {
505 if (_plotter->x_drawable1)
506 XDrawLines (_plotter->x_dpy, _plotter->x_drawable1,
507 _plotter->drawstate->x_gc_fg,
508 xarray, polyline_len,
509 CoordModeOrigin);
510 if (_plotter->x_drawable2)
511 XDrawLines (_plotter->x_dpy, _plotter->x_drawable2,
512 _plotter->drawstate->x_gc_fg,
513 xarray, polyline_len,
514 CoordModeOrigin);
515 }
516 }
517 }
518 }
519
520 /* reset buffer used for array of XPoint structs */
521 if (_plotter->drawstate->path->num_segments > 0)
522 {
523 if (heap_storage)
524 free (xarray); /* free malloc'd array of XPoints */
525 }
526 }
527 break;
528
529 case (int)PATH_ELLIPSE:
530 {
531 int ninetymult;
532 int x_orientation, y_orientation;
533 int xorigin, yorigin;
534 unsigned int squaresize_x, squaresize_y;
535 plPoint pc;
536 double rx, ry, angle;
537
538 pc = _plotter->drawstate->path->pc;
539 rx = _plotter->drawstate->path->rx;
540 ry = _plotter->drawstate->path->ry;
541 angle = _plotter->drawstate->path->angle;
542
543 /* if angle is multiple of 90 degrees, modify to permit use of
544 X11 arc rendering */
545 ninetymult = IROUND(angle / 90.0);
546 if (angle == (double) (90 * ninetymult))
547 {
548 angle = 0.0;
549 if (ninetymult % 2)
550 {
551 double temp;
552
553 temp = rx;
554 rx = ry;
555 ry = temp;
556 }
557 }
558
559 rx = (rx < 0.0 ? -rx : rx); /* avoid obscure libxmi problems */
560 ry = (ry < 0.0 ? -ry : ry);
561
562 /* axes flipped? (by default y-axis is, due to libxmi's flipped-y
563 convention) */
564 x_orientation = (_plotter->drawstate->transform.m[0] >= 0 ? 1 : -1);
565 y_orientation = (_plotter->drawstate->transform.m[3] >= 0 ? 1 : -1);
566
567 /* location of `origin' (upper left corner of bounding rect. for
568 ellipse) and width and height; X11's flipped-y convention
569 affects these values */
570 xorigin = IROUND(XD(pc.x - x_orientation * rx,
571 pc.y - y_orientation * ry));
572 yorigin = IROUND(YD(pc.x - x_orientation * rx,
573 pc.y - y_orientation * ry));
574 squaresize_x = (unsigned int)IROUND(XDV(2 * x_orientation * rx, 0.0));
575 squaresize_y = (unsigned int)IROUND(YDV(0.0, 2 * y_orientation * ry));
576 /* Because this ellipse object was added to the path buffer, we
577 already know that (1) the user->device frame map preserves
578 coordinate axes, (2) effectively, angle == 0. These are
579 necessary for the libxmi scan-conversion module to do the
580 drawing. */
581
582 /* draw ellipse (elliptic arc aligned with the coordinate axes, arc
583 range = 64*360 64'ths of a degree) */
584 _pl_x_draw_elliptic_arc_internal (R___(_plotter)
585 xorigin, yorigin,
586 squaresize_x, squaresize_y,
587 0, 64 * 360);
588 }
589 break;
590
591 default: /* shouldn't happen */
592 break;
593 }
594
595 /* maybe flush X output buffer and handle X events (a no-op for
596 XDrawablePlotters, which is overridden for XPlotters) */
597 _maybe_handle_x_events (S___(_plotter));
598 }
599
600 /* Use native X rendering to draw what would be a circular arc in the user
601 frame on an X display. If this is called, the map from user to device
602 coordinates is assumed to preserve coordinate axes (it may be
603 anisotropic [x and y directions scaled differently], and it may include
604 a reflection through either or both axes). So it will be a circular or
605 elliptic arc in the device frame, of the sort that X11 supports. */
606
607 void
_pl_x_draw_elliptic_arc(R___ (Plotter * _plotter)plPoint p0,plPoint p1,plPoint pc)608 _pl_x_draw_elliptic_arc (R___(Plotter *_plotter) plPoint p0, plPoint p1, plPoint pc)
609 {
610 double radius;
611 double theta0, theta1;
612 int startangle, anglerange;
613 int x_orientation, y_orientation;
614 int xorigin, yorigin;
615 unsigned int squaresize_x, squaresize_y;
616
617 /* axes flipped? (by default y-axis is, due to X's flipped-y convention) */
618 x_orientation = (_plotter->drawstate->transform.m[0] >= 0 ? 1 : -1);
619 y_orientation = (_plotter->drawstate->transform.m[3] >= 0 ? 1 : -1);
620
621 /* radius of circular arc in user frame is distance to p0, and also to p1 */
622 radius = DIST(pc, p0);
623
624 /* location of `origin' (upper left corner of bounding rect. on display)
625 and width and height; X's flipped-y convention affects these values */
626 xorigin = IROUND(XD(pc.x - x_orientation * radius,
627 pc.y - y_orientation * radius));
628 yorigin = IROUND(YD(pc.x - x_orientation * radius,
629 pc.y - y_orientation * radius));
630 squaresize_x = (unsigned int)IROUND(XDV(2 * x_orientation * radius, 0.0));
631 squaresize_y = (unsigned int)IROUND(YDV(0.0, 2 * y_orientation * radius));
632
633 theta0 = _xatan2 (-y_orientation * (p0.y - pc.y),
634 x_orientation * (p0.x - pc.x)) / M_PI;
635 theta1 = _xatan2 (-y_orientation * (p1.y - pc.y),
636 x_orientation * (p1.x - pc.x)) / M_PI;
637
638 if (theta1 < theta0)
639 theta1 += 2.0; /* adjust so that difference > 0 */
640 if (theta0 < 0.0)
641 {
642 theta0 += 2.0; /* adjust so that startangle > 0 */
643 theta1 += 2.0;
644 }
645
646 if (theta1 - theta0 > 1.0) /* swap if angle appear to be > 180 degrees */
647 {
648 double tmp;
649
650 tmp = theta0;
651 theta0 = theta1;
652 theta1 = tmp;
653 theta1 += 2.0; /* adjust so that difference > 0 */
654 }
655
656 if (theta0 >= 2.0 && theta1 >= 2.0)
657 /* avoid obscure X bug */
658 {
659 theta0 -= 2.0;
660 theta1 -= 2.0;
661 }
662
663 startangle = IROUND(64 * theta0 * 180.0); /* in 64'ths of a degree */
664 anglerange = IROUND(64 * (theta1 - theta0) * 180.0); /* likewise */
665
666 _pl_x_draw_elliptic_arc_internal (R___(_plotter)
667 xorigin, yorigin,
668 squaresize_x, squaresize_y,
669 startangle, anglerange);
670 }
671
672 /* Use native X rendering to draw what would be a quarter-ellipse in the
673 user frame on an X display. If this is called, the map from user to
674 device coordinates is assumed to preserve coordinate axes (it may be
675 anisotropic [x and y directions scaled differently], and it may include
676 a reflection through either or both axes). So it will be a
677 quarter-ellipse in the device frame, of the sort that the X11 drawing
678 protocol supports. */
679 void
_pl_x_draw_elliptic_arc_2(R___ (Plotter * _plotter)plPoint p0,plPoint p1,plPoint pc)680 _pl_x_draw_elliptic_arc_2 (R___(Plotter *_plotter) plPoint p0, plPoint p1, plPoint pc)
681 {
682 double rx, ry;
683 double x0, y0, x1, y1, xc, yc;
684 int startangle, endangle, anglerange;
685 int x_orientation, y_orientation;
686 int xorigin, yorigin;
687 unsigned int squaresize_x, squaresize_y;
688
689 /* axes flipped? (by default y-axis is, due to X's flipped-y convention) */
690 x_orientation = (_plotter->drawstate->transform.m[0] >= 0 ? 1 : -1);
691 y_orientation = (_plotter->drawstate->transform.m[3] >= 0 ? 1 : -1);
692
693 xc = pc.x, yc = pc.y;
694 x0 = p0.x, y0 = p0.y;
695 x1 = p1.x, y1 = p1.y;
696
697 if (y0 == yc && x1 == xc)
698 /* initial pt. on x-axis, final pt. on y-axis */
699 {
700 /* semi-axes in user frame */
701 rx = (x0 > xc) ? x0 - xc : xc - x0;
702 ry = (y1 > yc) ? y1 - yc : yc - y1;
703 /* starting and ending angles; note flipped-y convention */
704 startangle = ((x0 > xc ? 1 : -1) * x_orientation == 1) ? 0 : 180;
705 endangle = ((y1 > yc ? 1 : -1) * y_orientation == -1) ? 90 : 270;
706 }
707 else
708 /* initial pt. on y-axis, final pt. on x-axis */
709 {
710 /* semi-axes in user frame */
711 rx = (x1 > xc) ? x1 - xc : xc - x1;
712 ry = (y0 > yc) ? y0 - yc : yc - y0;
713 /* starting and ending angles; note flipped-y convention */
714 startangle = ((y0 > yc ? 1 : -1) * y_orientation == -1) ? 90 : 270;
715 endangle = ((x1 > xc ? 1 : -1) * x_orientation == 1) ? 0 : 180;
716 }
717
718 if (endangle < startangle)
719 endangle += 360;
720 anglerange = endangle - startangle; /* always 90 or 270 */
721
722 /* our convention: a quarter-ellipse can only be 90 degrees
723 of an X ellipse, not 270 degrees, so interchange points */
724 if (anglerange == 270)
725 {
726 int tmp;
727
728 tmp = startangle;
729 startangle = endangle;
730 endangle = tmp;
731 anglerange = 90;
732 }
733
734 if (startangle >= 360)
735 /* avoid obscure X bug */
736 startangle -= 360; /* endangle no longer relevant */
737
738 /* location of `origin' (upper left corner of bounding rect. on display)
739 and width and height; X's flipped-y convention affects these values */
740 xorigin = IROUND(XD(xc - x_orientation * rx,
741 yc - y_orientation * ry));
742 yorigin = IROUND(YD(xc - x_orientation * rx,
743 yc - y_orientation * ry));
744 squaresize_x = (unsigned int)IROUND(XDV(2 * x_orientation * rx, 0.0));
745 squaresize_y = (unsigned int)IROUND(YDV(0.0, 2 * y_orientation * ry));
746
747 /* reexpress in 64'ths of a degree (X11 convention) */
748 startangle *= 64;
749 anglerange *= 64;
750
751 _pl_x_draw_elliptic_arc_internal (R___(_plotter)
752 xorigin, yorigin,
753 squaresize_x, squaresize_y,
754 startangle, anglerange);
755 }
756
757 /* Use native X rendering to draw an elliptic arc on an X display. Takes
758 account of the possible presence of more than one drawable, and the
759 possible need for filling.
760
761 The cases squaresize_{x,y} <= 1 are handled specially, since XFillArc()
762 and XDrawArc() don't support them in the way we wish. More accurately,
763 they don't support squaresize_{x,y} = 0 (documented), and don't support
764 squaresize_{x,y} = 1 in the way we'd like (undocumented). */
765
766 void
_pl_x_draw_elliptic_arc_internal(R___ (Plotter * _plotter)int xorigin,int yorigin,unsigned int squaresize_x,unsigned int squaresize_y,int startangle,int anglerange)767 _pl_x_draw_elliptic_arc_internal (R___(Plotter *_plotter) int xorigin, int yorigin, unsigned int squaresize_x, unsigned int squaresize_y, int startangle, int anglerange)
768 {
769 if (X_OOB_INT(xorigin) || X_OOB_INT(yorigin) || X_OOB_UNSIGNED(squaresize_x)
770 || X_OOB_UNSIGNED(squaresize_y))
771 /* dimensions can't be represented using X11's 2-byte ints, so punt */
772 {
773 _plotter->warning (R___(_plotter)
774 "not drawing an arc that extends too far for X11");
775 return;
776 }
777
778 if (_plotter->drawstate->fill_type)
779 /* not transparent, so fill the arc */
780 {
781 /* update GC used for filling */
782 _pl_x_set_attributes (R___(_plotter) X_GC_FOR_FILLING);
783
784 /* select fill color as foreground color in GC used for filling */
785 _pl_x_set_fill_color (S___(_plotter));
786
787 if (squaresize_x <= 1 || squaresize_y <= 1)
788 /* a special case, which XFillArc() doesn't handle in the way we'd
789 like; just paint a single pixel, irrespective of angle range */
790 {
791 if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
792 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3,
793 _plotter->drawstate->x_gc_fill,
794 xorigin, yorigin);
795 else
796 {
797 if (_plotter->x_drawable1)
798 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1,
799 _plotter->drawstate->x_gc_fill,
800 xorigin, yorigin);
801 if (_plotter->x_drawable2)
802 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2,
803 _plotter->drawstate->x_gc_fill,
804 xorigin, yorigin);
805 }
806 }
807 else
808 /* default case, almost always used */
809 {
810 if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
811 XFillArc(_plotter->x_dpy, _plotter->x_drawable3,
812 _plotter->drawstate->x_gc_fill,
813 xorigin, yorigin, squaresize_x, squaresize_y,
814 startangle, anglerange);
815 else
816 {
817 if (_plotter->x_drawable1)
818 XFillArc(_plotter->x_dpy, _plotter->x_drawable1,
819 _plotter->drawstate->x_gc_fill,
820 xorigin, yorigin, squaresize_x, squaresize_y,
821 startangle, anglerange);
822 if (_plotter->x_drawable2)
823 XFillArc(_plotter->x_dpy, _plotter->x_drawable2,
824 _plotter->drawstate->x_gc_fill,
825 xorigin, yorigin, squaresize_x, squaresize_y,
826 startangle, anglerange);
827 }
828 }
829 }
830
831 if (_plotter->drawstate->pen_type)
832 /* pen is present, so edge the arc */
833 {
834 unsigned int sp_size = 0; /* keep compiler happy */
835
836 /* update GC used for drawing */
837 _pl_x_set_attributes (R___(_plotter) X_GC_FOR_DRAWING);
838
839 /* select pen color as foreground color in GC used for drawing */
840 _pl_x_set_pen_color (S___(_plotter));
841
842 if (squaresize_x <= 1 || squaresize_y <= 1)
843 /* Won't call XDrawArc in the usual way, because it performs poorly
844 when one of these two is zero, at least. Irrespective of angle
845 range, will fill a disk of diameter equal to line width */
846 {
847 int sp_offset;
848
849 sp_size
850 = (unsigned int)_plotter->drawstate->quantized_device_line_width;
851 sp_offset
852 = (int)(_plotter->drawstate->quantized_device_line_width + 1) / 2;
853
854 if (sp_size == 0)
855 sp_size = 1;
856 xorigin -= sp_offset;
857 yorigin -= sp_offset;
858 }
859
860 if (squaresize_x <= 1 || squaresize_y <= 1)
861 /* special case */
862 {
863 if (sp_size == 1)
864 /* special subcase: line width is small too, so just paint a
865 single pixel rather than filling abovementioned disk */
866 {
867 if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
868 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3,
869 _plotter->drawstate->x_gc_fg,
870 xorigin, yorigin);
871 else
872 {
873 if (_plotter->x_drawable1)
874 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1,
875 _plotter->drawstate->x_gc_fg,
876 xorigin, yorigin);
877 if (_plotter->x_drawable2)
878 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2,
879 _plotter->drawstate->x_gc_fg,
880 xorigin, yorigin);
881 }
882 }
883 else
884 /* normal version of special case: fill a disk of diameter
885 equal to line width */
886 {
887 if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
888 XFillArc(_plotter->x_dpy, _plotter->x_drawable3,
889 _plotter->drawstate->x_gc_fg,
890 xorigin, yorigin, sp_size, sp_size,
891 0, 64 * 360);
892 else
893 {
894 if (_plotter->x_drawable1)
895 XFillArc(_plotter->x_dpy, _plotter->x_drawable1,
896 _plotter->drawstate->x_gc_fg,
897 xorigin, yorigin, sp_size, sp_size,
898 0, 64 * 360);
899 if (_plotter->x_drawable2)
900 XFillArc(_plotter->x_dpy, _plotter->x_drawable2,
901 _plotter->drawstate->x_gc_fg,
902 xorigin, yorigin, sp_size, sp_size,
903 0, 64 * 360);
904 }
905 }
906 }
907 else
908 /* default case, which is what is almost always used */
909 {
910 if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
911 XDrawArc(_plotter->x_dpy, _plotter->x_drawable3,
912 _plotter->drawstate->x_gc_fg,
913 xorigin, yorigin, squaresize_x, squaresize_y,
914 startangle, anglerange);
915 else
916 {
917 if (_plotter->x_drawable1)
918 XDrawArc(_plotter->x_dpy, _plotter->x_drawable1,
919 _plotter->drawstate->x_gc_fg,
920 xorigin, yorigin, squaresize_x, squaresize_y,
921 startangle, anglerange);
922 if (_plotter->x_drawable2)
923 XDrawArc(_plotter->x_dpy, _plotter->x_drawable2,
924 _plotter->drawstate->x_gc_fg,
925 xorigin, yorigin, squaresize_x, squaresize_y,
926 startangle, anglerange);
927 }
928 }
929 }
930 }
931
932 /**********************************************************************/
933
934 /* This version of the internal method path_is_flushable() is for XDrawable
935 and X Plotters, which will under some circumstances `pre-draw', i.e.,
936 draw line segments in real time. (This requires a zero line width and a
937 "solid" line style.). In that case, this returns false; otherwise it
938 will return true. */
939
940 bool
_pl_x_path_is_flushable(S___ (Plotter * _plotter))941 _pl_x_path_is_flushable (S___(Plotter *_plotter))
942 {
943 if (_plotter->drawstate->pen_type != 0 /* pen is present */
944 && _plotter->drawstate->line_type == PL_L_SOLID
945 && !_plotter->drawstate->dash_array_in_effect /* really solid */
946 && _plotter->drawstate->points_are_connected /* really, really */
947 && _plotter->drawstate->quantized_device_line_width == 0
948 && !_plotter->drawstate->path->primitive) /* not a builtin */
949 /* we're pre-drawing rather than drawing when endpath() is finally
950 invoked, so flushing out a partially drawn path by invoking
951 endpath() early would be absurd */
952 return false;
953 else
954 /* endpath() will be invoked */
955 return true;
956 }
957
958 /**********************************************************************/
959
960 /* This version of the internal method maybe_prepaint_segments() is for
961 XDrawable and X Plotters. It will draw line segments in real time if
962 the line width is zero, the line style is "solid", and there's no
963 filling to be done. Also, it requires that the polyline being drawn be
964 unfilled, and not be one one of the polygonalized convex closed
965 primitives (box/circle/ellipse).
966
967 This hack makes it possible, after doing `graph -TX' (which has a
968 default line width of zero), to type in points manually, and see the
969 corresponding polyline drawn in real time. The `-x' and `-y' options
970 must of course be specified too, to set the axis limits in advance. */
971
972 void
_pl_x_maybe_prepaint_segments(R___ (Plotter * _plotter)int prev_num_segments)973 _pl_x_maybe_prepaint_segments (R___(Plotter *_plotter) int prev_num_segments)
974 {
975 int i;
976 bool something_drawn = false;
977
978 /* sanity check */
979 if (_plotter->drawstate->path->num_segments < 2)
980 return;
981
982 if (_plotter->drawstate->path->num_segments == prev_num_segments)
983 /* nothing to paint */
984 return;
985
986 /* Our criteria for pre-drawing line segments: zero-width solid line, and
987 we're not drawing this line segment as part of a polygonalized
988 built-in object (i.e. a rectangles or ellipse). If the criteria
989 aren't met, we wait until endpath() is invoked, or in general until
990 the path is flushed out, before painting it.
991
992 If we pre-draw, we don't also draw when endpath() is invoked. One
993 exception: if the polyline is to be filled. In that case, at endpath
994 time, we'll fill it and re-edge it. */
995
996 if (!(_plotter->drawstate->pen_type != 0 /* pen is present */
997 && _plotter->drawstate->line_type == PL_L_SOLID
998 && !_plotter->drawstate->dash_array_in_effect /* really solid */
999 && _plotter->drawstate->points_are_connected /* really, really */
1000 && _plotter->drawstate->quantized_device_line_width == 0
1001 && !_plotter->drawstate->path->primitive)) /* not a built-in object */
1002 /* we're not pre-drawing */
1003 return;
1004
1005 /* An X/XDrawable Plotter's segment list, at painting time, will only
1006 contain a polyline (i.e. a sequence of line segments) or a single
1007 circular or elliptic arc. That's because X/XDrawable Plotters can't
1008 handle `mixed paths'.
1009
1010 Since they can't handle mixed paths, any single arc that's placed in a
1011 previously empty segment list will need to be replaced by a polygonal
1012 approximation, when and if additional segments need to be added. The
1013 maybe_replace_arc() function, which is invoked in the base Plotter
1014 code in several places, takes care of that.
1015
1016 Because of this replacement procedure, maybe_prepaint_segments() may
1017 be invoked on a segment list that consists of a single moveto-arc or
1018 moveto-ellarc pair. We don't prepaint such things. Of course if the
1019 arc is subsequently replaced by a polygonal approximation, then we'll
1020 prepaint the polygonal approximation, at that time. */
1021
1022 if (prev_num_segments == 0 &&
1023 _plotter->drawstate->path->num_segments == 2
1024 && _plotter->drawstate->path->segments[0].type == S_MOVETO
1025 && (_plotter->drawstate->path->segments[1].type == S_ARC
1026 || _plotter->drawstate->path->segments[1].type == S_ELLARC))
1027 return;
1028
1029 if (prev_num_segments == 0)
1030 /* first segment of path; this must be a `moveto' */
1031 {
1032 /* update GC used for drawing */
1033 _pl_x_set_attributes (R___(_plotter) X_GC_FOR_DRAWING);
1034
1035 /* select pen color as foreground color in GC used for drawing */
1036 _pl_x_set_pen_color (S___(_plotter));
1037 }
1038
1039 /* Iterate over all segments to be painted. Because X/XDrawable Plotters
1040 can't handle `mixed paths', this function ends up being called only on
1041 sequences of line segments. */
1042
1043 for (i = IMAX(1, prev_num_segments);
1044 i < _plotter->drawstate->path->num_segments;
1045 i++)
1046 {
1047 /* use same variables for points #1 and #2, since reusing them works
1048 around an obscure bug in gcc 2.7.2.3 that rears its head if -O2 is
1049 used */
1050 double xu, yu, xd, yd;
1051 double x, y;
1052 int x1, y1, x2, y2;
1053
1054 /* starting and ending points for zero-width line segment: (xu,yu)
1055 and (x,y) respectively */
1056 xu = _plotter->drawstate->path->segments[i-1].p.x;
1057 yu = _plotter->drawstate->path->segments[i-1].p.y;
1058 x = _plotter->drawstate->path->segments[i].p.x;
1059 y = _plotter->drawstate->path->segments[i].p.y;
1060
1061 /* convert to integer X11 coordinates */
1062 xd = XD(xu, yu);
1063 yd = YD(xu, yu);
1064 x1 = IROUND(xd);
1065 y1 = IROUND(yd);
1066 xd = XD(x,y);
1067 yd = YD(x,y);
1068 x2 = IROUND(xd);
1069 y2 = IROUND(yd);
1070
1071 if (x1 != x2 || y1 != y2)
1072 /* line segment has nonzero length, so draw it */
1073 {
1074 if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
1075 /* double buffering, have a `x_drawable3' to draw into */
1076 XDrawLine (_plotter->x_dpy, _plotter->x_drawable3,
1077 _plotter->drawstate->x_gc_fg, x1, y1, x2, y2);
1078 else
1079 {
1080 if (_plotter->x_drawable1)
1081 XDrawLine (_plotter->x_dpy, _plotter->x_drawable1,
1082 _plotter->drawstate->x_gc_fg, x1, y1, x2, y2);
1083 if (_plotter->x_drawable2)
1084 XDrawLine (_plotter->x_dpy, _plotter->x_drawable2,
1085 _plotter->drawstate->x_gc_fg, x1, y1, x2, y2);
1086 }
1087
1088 something_drawn = true;
1089 }
1090 else
1091 /* line segment in terms of integer device coordinates has zero
1092 length; but if it has nonzero length in user coordinates, draw
1093 it as a single pixel unless cap type is "butt" */
1094 if (!(_plotter->drawstate->cap_type == PL_CAP_BUTT
1095 && xu == x && yu == y))
1096 {
1097 if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
1098 /* double buffering, have a `x_drawable3' to draw into */
1099 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3,
1100 _plotter->drawstate->x_gc_fg,
1101 x1, y1);
1102 else
1103 /* not double buffering */
1104 {
1105 if (_plotter->x_drawable1)
1106 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1,
1107 _plotter->drawstate->x_gc_fg,
1108 x1, y1);
1109 if (_plotter->x_drawable2)
1110 XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2,
1111 _plotter->drawstate->x_gc_fg,
1112 x1, y1);
1113 }
1114
1115 something_drawn = true;
1116 }
1117 }
1118
1119 if (something_drawn)
1120 /* maybe flush X output buffer and handle X events (a no-op for
1121 XDrawablePlotters, which is overridden for XPlotters) */
1122 _maybe_handle_x_events (S___(_plotter));
1123 }
1124
1125 bool
_pl_x_paint_paths(S___ (Plotter * _plotter))1126 _pl_x_paint_paths (S___(Plotter *_plotter))
1127 {
1128 return false;
1129 }
1130