Reset parser in grace_set_project().
[grace.git] / src / plotone.c
blob086a183bb8cc8577c6405b1e4fe65501c3120f31
1 /*
2 * Grace - GRaphing, Advanced Computation and Exploration of data
3 *
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
5 *
6 * Copyright (c) 1991-1995 Paul J Turner, Portland, OR
7 * Copyright (c) 1996-2004 Grace Development Team
8 *
9 * Maintained by Evgeny Stambulchik
12 * All Rights Reserved
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 * plotone.c - entry for graphics
35 #include <config.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
42 #include "grace/canvas.h"
44 #include "utils.h"
45 #include "files.h"
46 #include "core_utils.h"
47 #include "plotone.h"
48 #include "protos.h"
50 static int plotone_hook(Quark *q, void *udata, QTraverseClosure *closure)
52 plot_rt_t *plot_rt = (plot_rt_t *) udata;
53 Canvas *canvas = plot_rt->canvas;
55 if (!quark_is_active(q)) {
56 closure->descend = FALSE;
57 return TRUE;
60 switch (quark_fid_get(q)) {
61 case QFlavorProject:
62 if (!closure->post) {
63 set_draw_mode(canvas, TRUE);
65 closure->post = TRUE;
67 break;
68 case QFlavorFrame:
69 if (!closure->post) {
70 /* fill frame */
71 fillframe(canvas, q);
73 closure->post = TRUE;
74 } else {
75 draw_frame(q, plot_rt);
77 break;
78 case QFlavorGraph:
79 if (!closure->post) {
80 /* FIXME: stacked graphs */
81 plot_rt->last_pass = TRUE;
82 plot_rt->first_pass = TRUE;
84 plot_rt->refn = 0;
85 plot_rt->refx = NULL;
86 plot_rt->refy = NULL;
88 if (draw_graph(q, plot_rt) != RETURN_SUCCESS) {
89 closure->descend = FALSE;
92 closure->post = TRUE;
93 } else {
94 XCFREE(plot_rt->refy);
96 /* mark the reference points only if in interactive mode */
97 if (terminal_device(plot_rt->canvas) == TRUE) {
98 draw_ref_point(plot_rt->canvas, q);
101 break;
102 case QFlavorSet:
103 draw_set(q, plot_rt);
104 break;
105 case QFlavorAxis:
106 draw_axis(canvas, q);
107 break;
108 case QFlavorDObject:
109 draw_object(canvas, q);
110 break;
111 case QFlavorAText:
112 draw_atext(canvas, q);
113 break;
114 case QFlavorRegion:
115 draw_region(canvas, q);
116 break;
119 return TRUE;
122 static void dproc(Canvas *canvas, void *data)
124 Quark *project = (Quark *) data;
125 plot_rt_t plot_rt;
127 plot_rt.canvas = canvas;
129 quark_traverse(project, plotone_hook, &plot_rt);
133 * draw all active graphs
135 void drawgraph(const Quark *project)
137 Project *pr = project_get_data(project);
138 RunTime *rt = rt_from_quark(project);
140 if (pr && rt) {
141 canvas_set_docname(rt->canvas, project_get_docname(project));
142 canvas_set_pagefill(rt->canvas, pr->bgfill);
143 setbgcolor(rt->canvas, pr->bgcolor);
144 canvas_set_fontsize_scale(rt->canvas, pr->fscale);
145 canvas_set_linewidth_scale(rt->canvas, pr->lscale);
147 canvas_draw(rt->canvas, dproc, (void *) project);
152 * If writing to a file, check to see if it exists
154 void do_hardcopy(const Quark *project)
156 Grace *grace = grace_from_quark(project);
157 RunTime *rt;
158 Canvas *canvas;
159 char tbuf[128], *s;
160 char fname[GR_MAXPATHLEN];
161 view v;
162 double vx, vy;
163 int truncated_out;
164 FILE *prstream;
166 if (!grace) {
167 return;
170 rt = grace->rt;
171 canvas = rt->canvas;
173 if (get_ptofile(grace)) {
174 if (is_empty_string(rt->print_file)) {
175 Device_entry *dev = get_device_props(canvas, rt->hdevice);
176 sprintf(rt->print_file, "%s.%s",
177 get_docbname(project), dev->fext);
179 strcpy(fname, rt->print_file);
180 } else {
181 s = get_print_cmd(grace);
182 if (is_empty_string(s)) {
183 errmsg("No print command defined, output aborted");
184 return;
186 tmpnam(fname);
187 /* VMS doesn't like extensionless files */
188 strcat(fname, ".prn");
191 prstream = grace_openw(grace, fname);
192 if (prstream == NULL) {
193 return;
196 canvas_set_prstream(canvas, prstream);
198 select_device(canvas, rt->hdevice);
200 drawgraph(project);
202 grace_close(prstream);
204 get_bbox(canvas, BBOX_TYPE_GLOB, &v);
205 project_get_viewport(project, &vx, &vy);
206 if (v.xv1 < 0.0 || v.xv2 > vx || v.yv1 < 0.0 || v.yv2 > vy) {
207 truncated_out = TRUE;
208 } else {
209 truncated_out = FALSE;
212 if (get_ptofile(grace) == FALSE) {
213 sprintf(tbuf, "%s %s", get_print_cmd(grace), fname);
214 if (truncated_out == FALSE ||
215 yesno("Printout is truncated. Continue?", NULL, NULL, NULL)) {
216 system_wrap(tbuf);
218 #ifndef PRINT_CMD_UNLINKS
219 unlink(fname);
220 #endif
221 } else {
222 if (truncated_out == TRUE) {
223 errmsg("Output is truncated - tune device dimensions");
229 int draw_graph(Quark *gr, plot_rt_t *plot_rt)
231 GraphType gtype;
233 plot_rt->ndsets = 0;
234 plot_rt->offset = 0.0;
236 if (plot_rt->first_pass) {
238 gtype = graph_get_type(gr);
240 if (gtype == GRAPH_CHART) {
241 int setno, nsets;
242 Quark **psets;
244 nsets = get_descendant_sets(gr, &psets);
245 for (setno = 0; setno < nsets; setno++) {
246 Quark *pset = psets[setno];
247 if (is_set_drawable(pset)) {
248 set *p = set_get_data(pset);
249 if (set_get_length(pset) > plot_rt->refn) {
250 plot_rt->refn = set_get_length(pset);
251 plot_rt->refx = getx(pset);
253 if (graph_is_stacked(gr) != TRUE) {
254 plot_rt->offset -= 0.5*0.02*p->sym.size;
258 plot_rt->offset -= 0.5*(number_of_active_sets(gr) - 1)*graph_get_bargap(gr);
260 if (graph_is_stacked(gr) == TRUE) {
261 plot_rt->refy = xcalloc(plot_rt->refn, SIZEOF_DOUBLE);
262 if (plot_rt->refy == NULL) {
263 return FALSE;
268 if (plot_rt->refx) {
269 double xmin, xmax;
270 int imin, imax;
271 minmax(plot_rt->refx, plot_rt->refn, &xmin, &xmax, &imin, &imax);
272 plot_rt->epsilon = 1.0e-3*(xmax - xmin)/plot_rt->refn;
273 } else {
274 plot_rt->epsilon = 0.0;
279 } else
280 if (plot_rt->refy) {
281 int j;
282 for (j = 0; j < plot_rt->refn; j++) {
283 plot_rt->refy[j] = 0.0;
287 return RETURN_SUCCESS;
290 void draw_set(Quark *pset, plot_rt_t *plot_rt)
292 Quark *gr;
293 int x_ok;
294 double *x;
295 int j;
296 set *p;
297 int gtype;
300 if (!is_set_drawable(pset)) {
301 return;
304 gr = get_parent_graph(pset);
306 p = set_get_data(pset);
308 /* draw sets */
309 gtype = graph_get_type(gr);
310 switch (gtype) {
311 case GRAPH_XY:
312 switch (set_get_type(pset)) {
313 case SET_XY:
314 case SET_XYSIZE:
315 case SET_XYCOLOR:
316 case SET_XYZ:
317 drawsetline(pset, plot_rt);
318 drawsetsyms(pset, plot_rt);
319 drawsetavalues(pset, plot_rt);
320 break;
321 case SET_BAR:
322 drawsetline(pset, plot_rt);
323 drawsetbars(pset, plot_rt);
324 drawsetavalues(pset, plot_rt);
325 break;
326 case SET_XYDX:
327 case SET_XYDY:
328 case SET_XYDXDX:
329 case SET_XYDYDY:
330 case SET_XYDXDY:
331 case SET_XYDXDXDYDY:
332 drawsetline(pset, plot_rt);
333 drawseterrbars(pset, plot_rt);
334 drawsetsyms(pset, plot_rt);
335 drawsetavalues(pset, plot_rt);
336 break;
337 case SET_XYHILO:
338 drawsethilo(pset, plot_rt);
339 drawsetavalues(pset, plot_rt);
340 break;
341 case SET_XYVMAP:
342 drawsetline(pset, plot_rt);
343 drawsetvmap(pset, plot_rt);
344 drawsetsyms(pset, plot_rt);
345 drawsetavalues(pset, plot_rt);
346 break;
347 case SET_BOXPLOT:
348 drawsetline(pset, plot_rt);
349 drawsetboxplot(pset, plot_rt);
350 drawsetavalues(pset, plot_rt);
351 break;
352 default:
353 errmsg("Unsupported in XY graph set type");
354 break;
356 break;
357 case GRAPH_FIXED:
358 switch (set_get_type(pset)) {
359 case SET_XY:
360 case SET_XYSIZE:
361 case SET_XYCOLOR:
362 case SET_XYZ:
363 drawsetline(pset, plot_rt);
364 drawsetsyms(pset, plot_rt);
365 drawsetavalues(pset, plot_rt);
366 break;
367 case SET_XYDX:
368 case SET_XYDY:
369 case SET_XYDXDX:
370 case SET_XYDYDY:
371 case SET_XYDXDY:
372 case SET_XYDXDXDYDY:
373 drawsetline(pset, plot_rt);
374 drawseterrbars(pset, plot_rt);
375 drawsetsyms(pset, plot_rt);
376 drawsetavalues(pset, plot_rt);
377 break;
378 case SET_XYR:
379 drawcirclexy(pset, plot_rt);
380 drawsetsyms(pset, plot_rt);
381 drawsetavalues(pset, plot_rt);
382 break;
383 case SET_XYVMAP:
384 drawsetline(pset, plot_rt);
385 drawsetvmap(pset, plot_rt);
386 drawsetsyms(pset, plot_rt);
387 drawsetavalues(pset, plot_rt);
388 break;
389 default:
390 errmsg("Unsupported in XY graph set type");
391 break;
393 break;
394 case GRAPH_POLAR:
395 switch (set_get_type(pset)) {
396 case SET_XY:
397 case SET_XYSIZE:
398 case SET_XYCOLOR:
399 case SET_XYZ:
400 drawsetline(pset, plot_rt);
401 drawsetsyms(pset, plot_rt);
402 drawsetavalues(pset, plot_rt);
403 break;
404 default:
405 errmsg("Unsupported in polar graph set type");
406 break;
408 break;
409 case GRAPH_PIE:
410 switch (set_get_type(pset)) {
411 case SET_XY:
412 case SET_XYCOLOR:
413 case SET_XYCOLPAT:
414 draw_pie_chart_set(pset, plot_rt);
415 break;
416 default:
417 errmsg("Unsupported in pie chart set type");
418 break;
420 break;
421 case GRAPH_CHART:
422 /* check that abscissas are identical with refx */
423 x = set_get_col(pset, DATA_X);
424 x_ok = TRUE;
425 for (j = 0; j < set_get_length(pset); j++) {
426 if (fabs(x[j] - plot_rt->refx[j]) > plot_rt->epsilon) {
427 x_ok = FALSE;
428 break;
431 if (x_ok != TRUE) {
432 char buf[128];
433 sprintf(buf, "Set %s has different abscissas, "
434 "skipped from the chart.",
435 quark_idstr_get(pset));
436 errmsg(buf);
437 return;
440 if (graph_is_stacked(gr) != TRUE) {
441 plot_rt->offset += 0.5*0.02*p->sym.size;
444 switch (set_get_type(pset)) {
445 case SET_XY:
446 case SET_XYSIZE:
447 case SET_XYCOLOR:
448 if (plot_rt->first_pass) {
449 drawsetline(pset, plot_rt);
451 if (plot_rt->last_pass) {
452 drawsetsyms(pset, plot_rt);
453 drawsetavalues(pset, plot_rt);
455 break;
456 case SET_BAR:
457 if (plot_rt->first_pass) {
458 drawsetline(pset, plot_rt);
459 drawsetbars(pset, plot_rt);
461 if (plot_rt->last_pass) {
462 drawsetavalues(pset, plot_rt);
464 break;
465 case SET_BARDY:
466 case SET_BARDYDY:
467 if (plot_rt->first_pass) {
468 drawsetline(pset, plot_rt);
469 drawsetbars(pset, plot_rt);
471 if (plot_rt->last_pass) {
472 drawseterrbars(pset, plot_rt);
473 drawsetavalues(pset, plot_rt);
475 break;
476 case SET_XYDY:
477 case SET_XYDYDY:
478 if (plot_rt->first_pass) {
479 drawsetline(pset, plot_rt);
481 if (plot_rt->last_pass) {
482 drawseterrbars(pset, plot_rt);
483 drawsetsyms(pset, plot_rt);
484 drawsetavalues(pset, plot_rt);
486 break;
487 default:
488 if (plot_rt->first_pass) {
489 errmsg("Unsupported in XY chart set type");
491 break;
494 if (graph_is_stacked(gr) != TRUE) {
495 plot_rt->offset += 0.5*0.02*p->sym.size + graph_get_bargap(gr);
496 } else {
497 for (j = 0; j < set_get_length(pset); j++) {
498 plot_rt->refy[j] += p->data->ex[1][j];
502 break;
506 void draw_ref_point(Canvas *canvas, Quark *gr)
508 GLocator *locator;
509 VPoint vp;
511 if (is_refpoint_active(gr)) {
512 locator = graph_get_locator(gr);
513 Wpoint2Vpoint(gr, &locator->origin, &vp);
514 setcolor(canvas, 1);
515 setpattern(canvas, 1);
516 setlinewidth(canvas, 1.0);
517 setlinestyle(canvas, 1);
518 symplus(canvas, &vp, 0.01);
519 DrawCircle(canvas, &vp, 0.01);
524 * draw the graph frame
526 void draw_frame(Quark *q, plot_rt_t *plot_rt)
528 Canvas *canvas = plot_rt->canvas;
529 view v;
530 frame *f;
531 VPoint vps[4];
533 f = frame_get_data(q);
534 frame_get_view(q, &v);
536 setclipping(canvas, TRUE);
538 setline(canvas, &f->outline);
540 switch (f->type) {
541 case 0:
542 vps[0].x = v.xv1;
543 vps[0].y = v.yv1;
544 vps[1].x = v.xv2;
545 vps[1].y = v.yv2;
546 DrawRect(canvas, &vps[0], &vps[1]);
547 break;
548 case 1: /* half open */
549 vps[0].x = v.xv1;
550 vps[0].y = v.yv2;
551 vps[1].x = v.xv1;
552 vps[1].y = v.yv1;
553 vps[2].x = v.xv2;
554 vps[2].y = v.yv1;
555 DrawPolyline(canvas, vps, 3, POLYLINE_OPEN);
556 break;
557 case 2: /* break top */
558 vps[0].x = v.xv1;
559 vps[0].y = v.yv2;
560 vps[1].x = v.xv1;
561 vps[1].y = v.yv1;
562 vps[2].x = v.xv2;
563 vps[2].y = v.yv1;
564 vps[3].x = v.xv2;
565 vps[3].y = v.yv2;
566 DrawPolyline(canvas, vps, 4, POLYLINE_OPEN);
567 break;
568 case 3: /* break bottom */
569 vps[0].x = v.xv1;
570 vps[0].y = v.yv1;
571 vps[1].x = v.xv1;
572 vps[1].y = v.yv2;
573 vps[2].x = v.xv2;
574 vps[2].y = v.yv2;
575 vps[3].x = v.xv2;
576 vps[3].y = v.yv1;
577 DrawPolyline(canvas, vps, 4, POLYLINE_OPEN);
578 break;
579 case 4: /* break left */
580 vps[0].x = v.xv1;
581 vps[0].y = v.yv1;
582 vps[1].x = v.xv2;
583 vps[1].y = v.yv1;
584 vps[2].x = v.xv2;
585 vps[2].y = v.yv2;
586 vps[3].x = v.xv1;
587 vps[3].y = v.yv2;
588 DrawPolyline(canvas, vps, 4, POLYLINE_OPEN);
589 break;
590 case 5: /* break right */
591 vps[0].x = v.xv2;
592 vps[0].y = v.yv1;
593 vps[1].x = v.xv1;
594 vps[1].y = v.yv1;
595 vps[2].x = v.xv1;
596 vps[2].y = v.yv2;
597 vps[3].x = v.xv2;
598 vps[3].y = v.yv2;
599 DrawPolyline(canvas, vps, 4, POLYLINE_OPEN);
600 break;
603 draw_legends(q, plot_rt);
606 void fillframe(Canvas *canvas, Quark *q)
608 view v;
609 frame *f;
610 VPoint vp1, vp2;
612 frame_get_view(q, &v);
613 f = frame_get_data(q);
615 canvas_set_clipview(canvas, &v);
617 /* fill coordinate frame with background color */
618 if (f->fillpen.pattern != 0) {
619 setpen(canvas, &f->fillpen);
620 vp1.x = v.xv1;
621 vp1.y = v.yv1;
622 vp2.x = v.xv2;
623 vp2.y = v.yv2;
624 FillRect(canvas, &vp1, &vp2);
629 * draw a set filling polygon
631 void drawsetfill(Quark *pset, plot_rt_t *plot_rt)
633 Canvas *canvas = plot_rt->canvas;
634 Quark *gr = get_parent_graph(pset);
635 set *p = set_get_data(pset);
636 int i, len, setlen, polylen;
637 int line_type = p->line.type;
638 double *x, *y;
639 double ybase;
640 world w;
641 WPoint wptmp;
642 VPoint *vps;
643 double xmin, xmax, ymin, ymax;
644 int stacked_chart;
646 if (p->line.filltype == SETFILL_NONE) {
647 return;
650 if (graph_get_type(gr) == GRAPH_CHART) {
651 x = plot_rt->refx;
652 setlen = MIN2(set_get_length(pset), plot_rt->refn);
653 } else {
654 x = p->data->ex[0];
655 setlen = set_get_length(pset);
657 y = p->data->ex[1];
659 if (graph_get_type(gr) == GRAPH_CHART && graph_is_stacked(gr) == TRUE) {
660 stacked_chart = TRUE;
661 } else {
662 stacked_chart = FALSE;
665 setclipping(canvas, TRUE);
667 graph_get_world(gr, &w);
669 switch (line_type) {
670 case LINE_TYPE_STRAIGHT:
671 case LINE_TYPE_SEGMENT2:
672 case LINE_TYPE_SEGMENT3:
673 if (stacked_chart == TRUE && p->line.filltype == SETFILL_BASELINE) {
674 len = 2*setlen;
675 } else {
676 len = setlen;
678 vps = (VPoint *) xmalloc((len + 2) * sizeof(VPoint));
679 if (vps == NULL) {
680 errmsg("Can't xmalloc in drawsetfill");
681 return;
684 for (i = 0; i < setlen; i++) {
685 wptmp.x = x[i];
686 wptmp.y = y[i];
687 if (stacked_chart == TRUE) {
688 wptmp.y += plot_rt->refy[i];
690 Wpoint2Vpoint(gr, &wptmp, &vps[i]);
691 vps[i].x += plot_rt->offset;
693 if (stacked_chart == TRUE && p->line.filltype == SETFILL_BASELINE) {
694 for (i = 0; i < setlen; i++) {
695 wptmp.x = x[setlen - i - 1];
696 wptmp.y = plot_rt->refy[setlen - i - 1];
697 Wpoint2Vpoint(gr, &wptmp, &vps[setlen + i]);
698 vps[setlen + i].x += plot_rt->offset;
701 break;
702 case LINE_TYPE_LEFTSTAIR:
703 case LINE_TYPE_RIGHTSTAIR:
704 len = 2*setlen - 1;
705 vps = (VPoint *) xmalloc((len + 2) * sizeof(VPoint));
706 if (vps == NULL) {
707 errmsg("Can't xmalloc in drawsetfill");
708 return;
711 for (i = 0; i < setlen; i++) {
712 wptmp.x = x[i];
713 wptmp.y = y[i];
714 if (stacked_chart == TRUE) {
715 wptmp.y += plot_rt->refy[i];
717 Wpoint2Vpoint(gr, &wptmp, &vps[2*i]);
718 vps[2*i].x += plot_rt->offset;
720 for (i = 1; i < len; i += 2) {
721 if (line_type == LINE_TYPE_LEFTSTAIR) {
722 vps[i].x = vps[i - 1].x;
723 vps[i].y = vps[i + 1].y;
724 } else {
725 vps[i].x = vps[i + 1].x;
726 vps[i].y = vps[i - 1].y;
729 break;
730 default:
731 return;
734 switch (p->line.filltype) {
735 case SETFILL_POLYGON:
736 polylen = len;
737 break;
738 case SETFILL_BASELINE:
739 if (stacked_chart == TRUE) {
740 polylen = len;
741 } else {
742 getsetminmax(&pset, 1, &xmin, &xmax, &ymin, &ymax);
743 ybase = setybase(pset);
744 polylen = len + 2;
745 wptmp.x = MIN2(xmax, w.xg2);
746 wptmp.y = ybase;
747 Wpoint2Vpoint(gr, &wptmp, &vps[len]);
748 vps[len].x += plot_rt->offset;
749 wptmp.x = MAX2(xmin, w.xg1);
750 wptmp.y = ybase;
751 Wpoint2Vpoint(gr, &wptmp, &vps[len + 1]);
752 vps[len + 1].x += plot_rt->offset;
754 break;
755 default:
756 xfree(vps);
757 return;
760 setpen(canvas, &p->line.fillpen);
761 setfillrule(canvas, p->line.fillrule);
762 DrawPolygon(canvas, vps, polylen);
764 xfree(vps);
768 * draw set's connecting line
770 void drawsetline(Quark *pset, plot_rt_t *plot_rt)
772 Canvas *canvas = plot_rt->canvas;
773 Quark *gr = get_parent_graph(pset);
774 set *p = set_get_data(pset);
775 int setlen, len;
776 int i;
777 int line_type = p->line.type;
778 VPoint vps[4], *vpstmp, vprev;
779 WPoint wp;
780 double *x, *y;
781 double lw;
782 double ybase;
783 double xmin, xmax, ymin, ymax;
784 int skip = p->symskip + 1;
785 int stacked_chart;
787 if (graph_get_type(gr) == GRAPH_CHART) {
788 x = plot_rt->refx;
789 setlen = MIN2(set_get_length(pset), plot_rt->refn);
790 } else {
791 x = p->data->ex[0];
792 setlen = set_get_length(pset);
794 y = p->data->ex[1];
796 if (graph_get_type(gr) == GRAPH_CHART && graph_is_stacked(gr) == TRUE) {
797 stacked_chart = TRUE;
798 } else {
799 stacked_chart = FALSE;
802 if (stacked_chart == TRUE) {
803 ybase = 0.0;
804 } else {
805 ybase = setybase(pset);
808 setclipping(canvas, TRUE);
810 drawsetfill(pset, plot_rt);
812 setline(canvas, &p->line.line);
814 if (stacked_chart == TRUE) {
815 lw = getlinewidth(canvas);
816 } else {
817 lw = 0.0;
820 /* draw the line */
821 if (p->line.line.style != 0 && p->line.line.pen.pattern != 0) {
823 switch (line_type) {
824 case LINE_TYPE_NONE:
825 break;
826 case LINE_TYPE_STRAIGHT:
827 vpstmp = (VPoint *) xmalloc(setlen*sizeof(VPoint));
828 if (vpstmp == NULL) {
829 errmsg("xmalloc failed in drawsetline()");
830 break;
832 for (i = 0; i < setlen; i++) {
833 wp.x = x[i];
834 wp.y = y[i];
835 if (stacked_chart == TRUE) {
836 wp.y += plot_rt->refy[i];
838 Wpoint2Vpoint(gr, &wp, &vpstmp[i]);
839 vpstmp[i].x += plot_rt->offset;
841 vpstmp[i].y -= lw/2.0;
843 DrawPolyline(canvas, vpstmp, setlen, POLYLINE_OPEN);
844 xfree(vpstmp);
845 break;
846 case LINE_TYPE_SEGMENT2:
847 for (i = 0; i < setlen - 1; i += 2) {
848 wp.x = x[i];
849 wp.y = y[i];
850 if (stacked_chart == TRUE) {
851 wp.y += plot_rt->refy[i];
853 Wpoint2Vpoint(gr, &wp, &vps[0]);
854 vps[0].x += plot_rt->offset;
855 wp.x = x[i + 1];
856 wp.y = y[i + 1];
857 if (stacked_chart == TRUE) {
858 wp.y += plot_rt->refy[i + 1];
860 Wpoint2Vpoint(gr, &wp, &vps[1]);
861 vps[1].x += plot_rt->offset;
863 vps[0].y -= lw/2.0;
864 vps[1].y -= lw/2.0;
866 DrawLine(canvas, &vps[0], &vps[1]);
868 break;
869 case LINE_TYPE_SEGMENT3:
870 for (i = 0; i < setlen - 2; i += 3) {
871 wp.x = x[i];
872 wp.y = y[i];
873 if (stacked_chart == TRUE) {
874 wp.y += plot_rt->refy[i];
876 Wpoint2Vpoint(gr, &wp, &vps[0]);
877 vps[0].x += plot_rt->offset;
878 wp.x = x[i + 1];
879 wp.y = y[i + 1];
880 if (stacked_chart == TRUE) {
881 wp.y += plot_rt->refy[i + 1];
883 Wpoint2Vpoint(gr, &wp, &vps[1]);
884 vps[1].x += plot_rt->offset;
885 wp.x = x[i + 2];
886 wp.y = y[i + 2];
887 if (stacked_chart == TRUE) {
888 wp.y += plot_rt->refy[i + 2];
890 Wpoint2Vpoint(gr, &wp, &vps[2]);
891 vps[2].x += plot_rt->offset;
892 DrawPolyline(canvas, vps, 3, POLYLINE_OPEN);
894 vps[0].y -= lw/2.0;
895 vps[1].y -= lw/2.0;
896 vps[2].y -= lw/2.0;
898 if (i == setlen - 2) {
899 wp.x = x[i];
900 wp.y = y[i];
901 if (stacked_chart == TRUE) {
902 wp.y += plot_rt->refy[i];
904 Wpoint2Vpoint(gr, &wp, &vps[0]);
905 vps[0].x += plot_rt->offset;
906 wp.x = x[i + 1];
907 wp.y = y[i + 1];
908 if (stacked_chart == TRUE) {
909 wp.y += plot_rt->refy[i + 1];
911 Wpoint2Vpoint(gr, &wp, &vps[1]);
912 vps[1].x += plot_rt->offset;
914 vps[0].y -= lw/2.0;
915 vps[1].y -= lw/2.0;
917 DrawLine(canvas, &vps[0], &vps[1]);
919 break;
920 case LINE_TYPE_LEFTSTAIR:
921 case LINE_TYPE_RIGHTSTAIR:
922 len = 2*setlen - 1;
923 vpstmp = (VPoint *) xmalloc(len*sizeof(VPoint));
924 if (vpstmp == NULL) {
925 errmsg("xmalloc failed in drawsetline()");
926 break;
928 for (i = 0; i < setlen; i++) {
929 wp.x = x[i];
930 wp.y = y[i];
931 if (stacked_chart == TRUE) {
932 wp.y += plot_rt->refy[i];
934 Wpoint2Vpoint(gr, &wp, &vpstmp[2*i]);
935 vpstmp[2*i].x += plot_rt->offset;
937 for (i = 1; i < len; i += 2) {
938 if (line_type == LINE_TYPE_LEFTSTAIR) {
939 vpstmp[i].x = vpstmp[i - 1].x;
940 vpstmp[i].y = vpstmp[i + 1].y;
941 } else {
942 vpstmp[i].x = vpstmp[i + 1].x;
943 vpstmp[i].y = vpstmp[i - 1].y;
946 DrawPolyline(canvas, vpstmp, len, POLYLINE_OPEN);
947 xfree(vpstmp);
948 break;
949 default:
950 errmsg("Invalid line type");
951 break;
955 if (p->line.droplines == TRUE) {
956 for (i = 0; i < setlen; i += skip) {
957 wp.x = x[i];
958 if (stacked_chart == TRUE) {
959 wp.y = plot_rt->refy[i];
960 } else {
961 wp.y = ybase;
963 Wpoint2Vpoint(gr, &wp, &vps[0]);
964 vps[0].x += plot_rt->offset;
965 wp.x = x[i];
966 wp.y = y[i];
967 if (stacked_chart == TRUE) {
968 wp.y += plot_rt->refy[i];
970 Wpoint2Vpoint(gr, &wp, &vps[1]);
971 vps[1].x += plot_rt->offset;
973 vps[1].y -= lw/2.0;
975 if (i &&
976 hypot(vps[1].x-vprev.x, vps[1].y-vprev.y) < p->symskipmindist)
977 continue;
978 vprev = vps[1];
980 DrawLine(canvas, &vps[0], &vps[1]);
984 getsetminmax(&pset, 1, &xmin, &xmax, &ymin, &ymax);
986 if (p->line.baseline == TRUE && stacked_chart != TRUE) {
987 wp.x = xmin;
988 wp.y = ybase;
989 Wpoint2Vpoint(gr, &wp, &vps[0]);
990 vps[0].x += plot_rt->offset;
991 wp.x = xmax;
992 Wpoint2Vpoint(gr, &wp, &vps[1]);
993 vps[1].x += plot_rt->offset;
995 DrawLine(canvas, &vps[0], &vps[1]);
999 /* draw the symbols */
1000 void drawsetsyms(Quark *pset, plot_rt_t *plot_rt)
1002 Canvas *canvas = plot_rt->canvas;
1003 Quark *gr = get_parent_graph(pset);
1004 set *p = set_get_data(pset);
1005 int setlen;
1006 int i;
1007 VPoint vp, vprev;
1008 WPoint wp;
1009 double *x, *y, *z, *c;
1010 int skip = p->symskip + 1;
1011 int stacked_chart;
1012 double znorm = graph_get_znorm(gr);
1014 if (graph_get_type(gr) == GRAPH_CHART) {
1015 x = plot_rt->refx;
1016 setlen = MIN2(set_get_length(pset), plot_rt->refn);
1017 } else {
1018 x = p->data->ex[0];
1019 setlen = set_get_length(pset);
1021 y = p->data->ex[1];
1023 if (graph_get_type(gr) == GRAPH_CHART && graph_is_stacked(gr) == TRUE) {
1024 stacked_chart = TRUE;
1025 } else {
1026 stacked_chart = FALSE;
1029 if (p->type == SET_XYSIZE) {
1030 if (znorm == 0.0) {
1031 return;
1033 z = p->data->ex[2];
1034 } else {
1035 z = NULL;
1038 if (p->type == SET_XYCOLOR) {
1039 c = p->data->ex[2];
1040 } else {
1041 c = NULL;
1044 setclipping(canvas, FALSE);
1046 if ((p->sym.line.pen.pattern != 0 && p->sym.line.style != 0) ||
1047 (p->sym.fillpen.pattern != 0)) {
1049 Symbol sym = p->sym;
1051 setline(canvas, &sym.line);
1052 setfont(canvas, sym.charfont);
1053 for (i = 0; i < setlen; i += skip) {
1054 wp.x = x[i];
1055 wp.y = y[i];
1056 if (stacked_chart == TRUE) {
1057 wp.y += plot_rt->refy[i];
1060 if (!is_validWPoint(gr, &wp)){
1061 continue;
1064 Wpoint2Vpoint(gr, &wp, &vp);
1065 vp.x += plot_rt->offset;
1067 if (i && hypot(vp.x - vprev.x, vp.y - vprev.y) < p->symskipmindist)
1068 continue;
1069 vprev = vp;
1071 if (z) {
1072 sym.size = z[i]/znorm;
1074 if (c) {
1075 int color = (int) rint(c[i]);
1076 sym.fillpen.color = color;
1078 if (drawxysym(canvas, &vp, &sym) != RETURN_SUCCESS) {
1079 return;
1086 void drawtext(Canvas *canvas,
1087 const VPoint *vp, const TextProps *tprops, const char *s)
1089 setcolor(canvas, tprops->color);
1090 setfont(canvas, tprops->font);
1091 setcharsize(canvas, tprops->charsize);
1093 WriteString(canvas, vp, tprops->angle, tprops->just, s);
1096 /* draw the annotative values */
1097 void drawsetavalues(Quark *pset, plot_rt_t *plot_rt)
1099 Canvas *canvas = plot_rt->canvas;
1100 Quark *gr = get_parent_graph(pset);
1101 Quark *pr = get_parent_project(gr);
1102 set *p = set_get_data(pset);
1103 int i;
1104 int setlen;
1105 double *x, *y, *z;
1106 WPoint wp;
1107 VPoint vp, vprev;
1108 int skip = p->symskip + 1;
1109 AValue avalue;
1110 char str[MAX_STRING_LENGTH];
1111 int stacked_chart;
1113 avalue = p->avalue;
1114 if (avalue.active != TRUE) {
1115 return;
1118 if (graph_get_type(gr) == GRAPH_CHART) {
1119 x = plot_rt->refx;
1120 setlen = MIN2(set_get_length(pset), plot_rt->refn);
1121 } else {
1122 x = p->data->ex[0];
1123 setlen = set_get_length(pset);
1125 y = p->data->ex[1];
1127 if (set_get_ncols(pset) > 2) {
1128 z = p->data->ex[2];
1129 } else {
1130 z = NULL;
1133 if (graph_get_type(gr) == GRAPH_CHART && graph_is_stacked(gr) == TRUE) {
1134 stacked_chart = TRUE;
1135 } else {
1136 stacked_chart = FALSE;
1139 for (i = 0; i < setlen; i += skip) {
1140 wp.x = x[i];
1141 wp.y = y[i];
1142 if (stacked_chart == TRUE) {
1143 wp.y += plot_rt->refy[i];
1146 if (!is_validWPoint(gr, &wp)){
1147 continue;
1150 Wpoint2Vpoint(gr, &wp, &vp);
1152 vp.x += avalue.offset.x;
1153 vp.y += avalue.offset.y;
1154 vp.x += plot_rt->offset;
1156 if (i && hypot(vp.x - vprev.x, vp.y - vprev.y) < p->symskipmindist)
1157 continue;
1158 vprev = vp;
1160 strcpy(str, avalue.prestr);
1162 switch(avalue.type) {
1163 case AVALUE_TYPE_NONE:
1164 break;
1165 case AVALUE_TYPE_X:
1166 strcat(str, create_fstring(pr, avalue.format, avalue.prec, wp.x,
1167 LFORMAT_TYPE_EXTENDED));
1168 break;
1169 case AVALUE_TYPE_Y:
1170 strcat(str, create_fstring(pr, avalue.format, avalue.prec, wp.y,
1171 LFORMAT_TYPE_EXTENDED));
1172 break;
1173 case AVALUE_TYPE_XY:
1174 strcat(str, create_fstring(pr, avalue.format, avalue.prec, wp.x,
1175 LFORMAT_TYPE_EXTENDED));
1176 strcat(str, ", ");
1177 strcat(str, create_fstring(pr, avalue.format, avalue.prec, wp.y,
1178 LFORMAT_TYPE_EXTENDED));
1179 break;
1180 case AVALUE_TYPE_STRING:
1181 if (p->data->s != NULL && p->data->s[i] != NULL) {
1182 strcat(str, p->data->s[i]);
1184 break;
1185 case AVALUE_TYPE_Z:
1186 if (z != NULL) {
1187 strcat(str, create_fstring(pr, avalue.format, avalue.prec, z[i],
1188 LFORMAT_TYPE_EXTENDED));
1190 break;
1191 default:
1192 errmsg("Invalid type of ann. value");
1193 return;
1196 strcat(str, avalue.appstr);
1198 drawtext(canvas, &vp, &avalue.tprops, str);
1202 void drawseterrbars(Quark *pset, plot_rt_t *plot_rt)
1204 Canvas *canvas = plot_rt->canvas;
1205 Quark *gr = get_parent_graph(pset);
1206 set *p = set_get_data(pset);
1207 int i, n;
1208 double *x, *y;
1209 double *dx_plus, *dx_minus, *dy_plus, *dy_minus, *dtmp;
1210 PlacementType ptype = p->errbar.ptype;
1211 WPoint wp1, wp2;
1212 VPoint vp1, vp2, vprev;
1213 int stacked_chart;
1214 int skip = p->symskip + 1;
1216 if (p->errbar.active != TRUE) {
1217 return;
1220 if (graph_get_type(gr) == GRAPH_CHART) {
1221 x = plot_rt->refx;
1222 n = MIN2(set_get_length(pset), plot_rt->refn);
1223 } else {
1224 x = p->data->ex[0];
1225 n = set_get_length(pset);
1227 y = p->data->ex[1];
1229 if (graph_get_type(gr) == GRAPH_CHART && graph_is_stacked(gr) == TRUE) {
1230 stacked_chart = TRUE;
1231 } else {
1232 stacked_chart = FALSE;
1235 dx_plus = NULL;
1236 dx_minus = NULL;
1237 dy_plus = NULL;
1238 dy_minus = NULL;
1239 switch (p->type) {
1240 case SET_XYDX:
1241 dx_plus = p->data->ex[2];
1242 break;
1243 case SET_XYDY:
1244 case SET_BARDY:
1245 dy_plus = p->data->ex[2];
1246 break;
1247 case SET_XYDXDX:
1248 dx_plus = p->data->ex[2];
1249 dx_minus = p->data->ex[3];
1250 break;
1251 case SET_XYDYDY:
1252 case SET_BARDYDY:
1253 dy_plus = p->data->ex[2];
1254 dy_minus = p->data->ex[3];
1255 break;
1256 case SET_XYDXDY:
1257 dx_plus = p->data->ex[2];
1258 dy_plus = p->data->ex[3];
1259 break;
1260 case SET_XYDXDXDYDY:
1261 dx_plus = p->data->ex[2];
1262 dx_minus = p->data->ex[3];
1263 dy_plus = p->data->ex[4];
1264 dy_minus = p->data->ex[5];
1265 break;
1266 default:
1267 return;
1270 switch (ptype) {
1271 case PLACEMENT_OPPOSITE:
1272 dtmp = dx_minus;
1273 dx_minus = dx_plus;
1274 dx_plus = dtmp;
1275 dtmp = dy_minus;
1276 dy_minus = dy_plus;
1277 dy_plus = dtmp;
1278 break;
1279 case PLACEMENT_BOTH:
1280 if (dx_minus == NULL && dy_minus == NULL) {
1281 dx_minus = dx_plus;
1282 dy_minus = dy_plus;
1284 break;
1285 default:
1286 break;
1289 setclipping(canvas, TRUE);
1291 for (i = 0; i < n; i += skip) {
1292 wp1.x = x[i];
1293 wp1.y = y[i];
1294 if (stacked_chart == TRUE) {
1295 wp1.y += plot_rt->refy[i];
1297 if (is_validWPoint(gr, &wp1) == FALSE) {
1298 continue;
1301 Wpoint2Vpoint(gr, &wp1, &vp1);
1302 vp1.x += plot_rt->offset;
1304 if (i && hypot(vp1.x - vprev.x, vp1.y - vprev.y) < p->symskipmindist)
1305 continue;
1306 vprev = vp1;
1308 if (dx_plus != NULL) {
1309 wp2 = wp1;
1310 wp2.x += fabs(dx_plus[i]);
1311 Wpoint2Vpoint(gr, &wp2, &vp2);
1312 vp2.x += plot_rt->offset;
1313 drawerrorbar(canvas, &vp1, &vp2, &p->errbar);
1315 if (dx_minus != NULL) {
1316 wp2 = wp1;
1317 wp2.x -= fabs(dx_minus[i]);
1318 Wpoint2Vpoint(gr, &wp2, &vp2);
1319 vp2.x += plot_rt->offset;
1320 drawerrorbar(canvas, &vp1, &vp2, &p->errbar);
1322 if (dy_plus != NULL) {
1323 wp2 = wp1;
1324 wp2.y += fabs(dy_plus[i]);
1325 Wpoint2Vpoint(gr, &wp2, &vp2);
1326 vp2.x += plot_rt->offset;
1327 drawerrorbar(canvas, &vp1, &vp2, &p->errbar);
1329 if (dy_minus != NULL) {
1330 wp2 = wp1;
1331 wp2.y -= fabs(dy_minus[i]);
1332 Wpoint2Vpoint(gr, &wp2, &vp2);
1333 vp2.x += plot_rt->offset;
1334 drawerrorbar(canvas, &vp1, &vp2, &p->errbar);
1340 * draw hi/lo-open/close
1342 void drawsethilo(Quark *pset, plot_rt_t *plot_rt)
1344 Canvas *canvas = plot_rt->canvas;
1345 set *p = set_get_data(pset);
1346 int i;
1347 double *x = p->data->ex[0], *y1 = p->data->ex[1];
1348 double *y2 = p->data->ex[2], *y3 = p->data->ex[3], *y4 = p->data->ex[4];
1349 double ilen = 0.02*p->sym.size;
1350 int skip = p->symskip + 1;
1351 WPoint wp;
1352 VPoint vp1, vp2, vprev;
1354 if (p->sym.line.style != 0) {
1355 setline(canvas, &p->sym.line);
1356 for (i = 0; i < set_get_length(pset); i += skip) {
1357 wp.x = x[i];
1358 wp.y = (y1[i] + y2[i] + y3[i] + y4[i]) * 0.25;
1359 Wpoint2Vpoint(pset, &wp, &vp1);
1360 if (i && hypot(vp1.x-vprev.x, vp1.y-vprev.y) < p->symskipmindist)
1361 continue;
1362 vprev = vp1;
1364 wp.y = y1[i];
1365 Wpoint2Vpoint(pset, &wp, &vp1);
1366 wp.y = y2[i];
1367 Wpoint2Vpoint(pset, &wp, &vp2);
1368 DrawLine(canvas, &vp1, &vp2);
1369 wp.y = y3[i];
1370 Wpoint2Vpoint(pset, &wp, &vp1);
1371 vp2 = vp1;
1372 vp2.x -= ilen;
1373 DrawLine(canvas, &vp1, &vp2);
1374 wp.y = y4[i];
1375 Wpoint2Vpoint(pset, &wp, &vp1);
1376 vp2 = vp1;
1377 vp2.x += ilen;
1378 DrawLine(canvas, &vp1, &vp2);
1384 * draw 2D bars
1386 void drawsetbars(Quark *pset, plot_rt_t *plot_rt)
1388 Canvas *canvas = plot_rt->canvas;
1389 Quark *gr = get_parent_graph(pset);
1390 set *p = set_get_data(pset);
1391 int i, n;
1392 double *x, *y;
1393 double lw, bw = 0.01*p->sym.size;
1394 int skip = p->symskip + 1;
1395 double ybase;
1396 WPoint wp;
1397 VPoint vp1, vp2, vprev;
1398 int stacked_chart;
1400 if (graph_get_type(gr) == GRAPH_CHART) {
1401 x = plot_rt->refx;
1402 n = MIN2(set_get_length(pset), plot_rt->refn);
1403 } else {
1404 x = p->data->ex[0];
1405 n = set_get_length(pset);
1407 y = p->data->ex[1];
1409 if (graph_get_type(gr) == GRAPH_CHART && graph_is_stacked(gr) == TRUE) {
1410 stacked_chart = TRUE;
1411 } else {
1412 stacked_chart = FALSE;
1417 if (stacked_chart == TRUE) {
1418 ybase = 0.0;
1419 } else {
1420 ybase = setybase(pset);
1423 setline(canvas, &p->sym.line);
1424 if (graph_get_type(gr) == GRAPH_CHART &&
1425 p->sym.line.style != 0 && p->sym.line.pen.pattern != 0) {
1426 lw = getlinewidth(canvas);
1427 } else {
1428 lw = 0.0;
1431 if (p->sym.fillpen.pattern != 0) {
1432 setpen(canvas, &p->sym.fillpen);
1433 for (i = 0; i < n; i += skip) {
1434 wp.x = x[i];
1435 if (stacked_chart == TRUE) {
1436 wp.y = plot_rt->refy[i];
1437 } else {
1438 wp.y = ybase;
1440 Wpoint2Vpoint(gr, &wp, &vp1);
1441 vp1.x -= bw;
1442 vp1.x += plot_rt->offset;
1443 wp.x = x[i];
1444 if (stacked_chart == TRUE) {
1445 wp.y += y[i];
1446 } else {
1447 wp.y = y[i];
1449 Wpoint2Vpoint(gr, &wp, &vp2);
1450 vp2.x += bw;
1451 vp2.x += plot_rt->offset;
1453 vp1.x += lw/2.0;
1454 vp2.x -= lw/2.0;
1455 vp1.y += lw/2.0;
1457 if (i &&
1458 hypot(vp2.x - vprev.x, vp2.y - vprev.y) < p->symskipmindist)
1459 continue;
1460 vprev = vp2;
1462 FillRect(canvas, &vp1, &vp2);
1465 if (p->sym.line.style != 0 && p->sym.line.pen.pattern != 0) {
1466 setpen(canvas, &p->sym.line.pen);
1467 for (i = 0; i < n; i += skip) {
1468 wp.x = x[i];
1469 if (stacked_chart == TRUE) {
1470 wp.y = plot_rt->refy[i];
1471 } else {
1472 wp.y = ybase;
1474 Wpoint2Vpoint(gr, &wp, &vp1);
1475 vp1.x -= bw;
1476 vp1.x += plot_rt->offset;
1477 wp.x = x[i];
1478 if (stacked_chart == TRUE) {
1479 wp.y += y[i];
1480 } else {
1481 wp.y = y[i];
1483 Wpoint2Vpoint(gr, &wp, &vp2);
1484 vp2.x += bw;
1485 vp2.x += plot_rt->offset;
1487 vp1.x += lw/2.0;
1488 vp2.x -= lw/2.0;
1489 vp1.y += lw/2.0;
1491 if (i &&
1492 hypot(vp2.x - vprev.x, vp2.y - vprev.y) < p->symskipmindist)
1493 continue;
1494 vprev = vp2;
1496 DrawRect(canvas, &vp1, &vp2);
1501 void drawcirclexy(Quark *pset, plot_rt_t *plot_rt)
1503 Canvas *canvas = plot_rt->canvas;
1504 set *p = set_get_data(pset);
1505 int i, setlen;
1506 double *x, *y, *r;
1507 int skip = p->symskip + 1;
1508 WPoint wp;
1509 VPoint vp1, vp2, vprev;
1511 setclipping(canvas, TRUE);
1513 setlen = set_get_length(pset);
1514 x = p->data->ex[0];
1515 y = p->data->ex[1];
1516 r = p->data->ex[2];
1518 setfillrule(canvas, p->line.fillrule);
1519 setline(canvas, &p->line.line);
1521 for (i = 0; i < setlen; i += skip) {
1522 wp.x = x[i];
1523 wp.y = y[i];
1524 /* TODO: remove once ellipse clipping works */
1525 if (!is_validWPoint(pset, &wp)){
1526 continue;
1528 wp.x = x[i] - r[i];
1529 wp.y = y[i] - r[i];
1530 Wpoint2Vpoint(pset, &wp, &vp1);
1531 wp.x = x[i] + r[i];
1532 wp.y = y[i] + r[i];
1533 Wpoint2Vpoint(pset, &wp, &vp2);
1534 if (i && hypot((vp1.x+vp2.x)*0.5 - vprev.x,
1535 (vp1.y+vp2.y)*0.5 - vprev.y) < p->symskipmindist)
1536 continue;
1537 vprev.x = (vp1.x+vp2.x)*0.5;
1538 vprev.y = (vp1.y+vp2.y)*0.5;
1539 if (p->line.filltype != SETFILL_NONE) {
1540 setpen(canvas, &p->line.fillpen);
1541 DrawFilledEllipse(canvas, &vp1, &vp2);
1543 setpen(canvas, &p->line.line.pen);
1544 DrawEllipse(canvas, &vp1, &vp2);
1548 /* Arrows for vector map plots */
1549 void drawsetvmap(Quark *pset, plot_rt_t *plot_rt)
1551 Canvas *canvas = plot_rt->canvas;
1552 Quark *gr = get_parent_graph(pset);
1553 set *p = set_get_data(pset);
1554 int i, setlen;
1555 double znorm = graph_get_znorm(gr);
1556 int skip = p->symskip + 1;
1557 double *x, *y, *vx, *vy;
1558 WPoint wp;
1559 VPoint vp1, vp2, vprev;
1560 Arrow arrow = {0, 1.0, 1.0, 0.0};
1562 Errbar eb = p->errbar;
1564 setclipping(canvas, TRUE);
1566 if (znorm == 0.0) {
1567 return;
1570 setlen = set_get_length(pset);
1571 x = p->data->ex[DATA_X];
1572 y = p->data->ex[DATA_Y];
1573 vx = p->data->ex[DATA_Y1];
1574 vy = p->data->ex[DATA_Y2];
1576 arrow.length = 2*eb.barsize;
1578 setpen(canvas, &p->errbar.pen);
1580 for (i = 0; i < setlen; i += skip) {
1581 wp.x = x[i];
1582 wp.y = y[i];
1583 if (!is_validWPoint(gr, &wp)){
1584 continue;
1586 Wpoint2Vpoint(gr, &wp, &vp1);
1587 if (i && hypot(vp1.x - vprev.x, vp1.y - vprev.y) < p->symskipmindist)
1588 continue;
1589 vprev = vp1;
1590 vp2.x = vp1.x + vx[i]/znorm;
1591 vp2.y = vp1.y + vy[i]/znorm;
1593 setlinewidth(canvas, eb.riser_linew);
1594 setlinestyle(canvas, eb.riser_lines);
1595 DrawLine(canvas, &vp1, &vp2);
1597 setlinewidth(canvas, eb.linew);
1598 setlinestyle(canvas, eb.lines);
1599 draw_arrowhead(canvas, &vp1, &vp2, &arrow, &p->errbar.pen, &p->errbar.pen);
1603 void drawsetboxplot(Quark *pset, plot_rt_t *plot_rt)
1605 Canvas *canvas = plot_rt->canvas;
1606 set *p = set_get_data(pset);
1607 int i;
1608 double *x, *md, *lb, *ub, *lw, *uw;
1609 double size = 0.01*p->sym.size;
1610 int skip = p->symskip + 1;
1611 WPoint wp;
1612 VPoint vp1, vp2, vprev;
1614 x = p->data->ex[0];
1615 md = p->data->ex[1];
1616 lb = p->data->ex[2];
1617 ub = p->data->ex[3];
1618 lw = p->data->ex[4];
1619 uw = p->data->ex[5];
1621 setclipping(canvas, TRUE);
1623 for (i = 0; i < set_get_length(pset); i += skip) {
1624 wp.x = x[i];
1626 wp.y = md[i]; /* use median-line y for symskipmindist */
1627 Wpoint2Vpoint(pset, &wp, &vp1);
1628 if (i && hypot(vp1.x - vprev.x, vp1.y - vprev.y) < p->symskipmindist)
1629 continue;
1630 vprev = vp1;
1632 wp.y = lb[i];
1633 Wpoint2Vpoint(pset, &wp, &vp1);
1634 wp.y = ub[i];
1635 Wpoint2Vpoint(pset, &wp, &vp2);
1637 /* whiskers */
1638 if (p->errbar.active == TRUE) {
1639 VPoint vp3;
1640 wp.y = lw[i];
1641 Wpoint2Vpoint(pset, &wp, &vp3);
1642 drawerrorbar(canvas, &vp1, &vp3, &p->errbar);
1643 wp.y = uw[i];
1644 Wpoint2Vpoint(pset, &wp, &vp3);
1645 drawerrorbar(canvas, &vp2, &vp3, &p->errbar);
1648 /* box */
1649 vp1.x -= size;
1650 vp2.x += size;
1651 setpen(canvas, &p->sym.fillpen);
1652 FillRect(canvas, &vp1, &vp2);
1654 setline(canvas, &p->sym.line);
1655 DrawRect(canvas, &vp1, &vp2);
1657 /* median line */
1658 wp.y = md[i];
1659 Wpoint2Vpoint(pset, &wp, &vp1);
1660 vp2 = vp1;
1661 vp1.x -= size;
1662 vp2.x += size;
1663 DrawLine(canvas, &vp1, &vp2);
1667 void draw_pie_chart_set(Quark *pset, plot_rt_t *plot_rt)
1669 int i;
1670 view v;
1671 world w;
1672 int sgn;
1673 VPoint vpc, vp1, vp2, vps[3], vpa;
1674 VVector offset;
1675 double r, start_angle, stop_angle;
1676 double e_max, norm;
1677 double *x, *c, *e, *pt;
1678 AValue avalue;
1679 char str[MAX_STRING_LENGTH];
1680 set *p;
1681 Quark *gr, *pr;
1682 Canvas *canvas = plot_rt->canvas;
1684 gr = get_parent_graph(pset);
1685 pr = get_parent_project(gr);
1687 graph_get_viewport(gr, &v);
1688 vpc.x = (v.xv1 + v.xv2)/2;
1689 vpc.y = (v.yv1 + v.yv2)/2;
1691 graph_get_world(gr, &w);
1692 sgn = graph_is_xinvert(gr) ? -1:1;
1694 p = set_get_data(pset);
1696 plot_rt->ndsets++;
1697 if (plot_rt->ndsets > 1) {
1698 errmsg("Only one set per pie chart can be drawn");
1699 return;
1702 /* data */
1703 x = set_get_col(pset, DATA_X);
1704 /* explode factor */
1705 e = set_get_col(pset, DATA_Y);
1706 /* colors */
1707 c = set_get_col(pset, DATA_Y1);
1708 /* patterns */
1709 pt = set_get_col(pset, DATA_Y2);
1711 /* get max explode factor */
1712 e_max = 0.0;
1713 for (i = 0; i < set_get_length(pset); i++) {
1714 e_max = MAX2(e_max, e[i]);
1717 r = 0.8/(1.0 + e_max)*MIN2(v.xv2 - v.xv1, v.yv2 - v.yv1)/2;
1719 norm = 0.0;
1720 for (i = 0; i < set_get_length(pset); i++) {
1721 if (x[i] < 0.0) {
1722 errmsg("No negative values in pie charts allowed");
1723 return;
1725 if (e[i] < 0.0) {
1726 errmsg("No negative offsets in pie charts allowed");
1727 return;
1729 norm += x[i];
1732 stop_angle = w.xg1;
1733 for (i = 0; i < set_get_length(pset); i++) {
1734 Pen pen;
1736 start_angle = stop_angle;
1737 stop_angle = start_angle + sgn*2*M_PI*x[i]/norm;
1738 offset.x = e[i]*r*cos((start_angle + stop_angle)/2.0);
1739 offset.y = e[i]*r*sin((start_angle + stop_angle)/2.0);
1740 vps[0].x = vpc.x + r*cos(start_angle) + offset.x;
1741 vps[0].y = vpc.y + r*sin(start_angle) + offset.y;
1742 vps[1].x = vpc.x + offset.x;
1743 vps[1].y = vpc.y + offset.y;
1744 vps[2].x = vpc.x + r*cos(stop_angle) + offset.x;
1745 vps[2].y = vpc.y + r*sin(stop_angle) + offset.y;
1746 vp1.x = vpc.x - r + offset.x;
1747 vp1.y = vpc.y - r + offset.y;
1748 vp2.x = vpc.x + r + offset.x;
1749 vp2.y = vpc.y + r + offset.y;
1751 if (c != NULL) {
1752 pen.color = (int) rint(c[i]);
1753 } else {
1754 pen.color = p->sym.fillpen.color;
1756 if (pt != NULL) {
1757 pen.pattern = (int) rint(pt[i]);
1758 } else {
1759 pen.pattern = p->sym.fillpen.pattern;
1761 setpen(canvas, &pen);
1762 DrawFilledArc(canvas, &vp1, &vp2,
1763 180.0/M_PI*start_angle,
1764 180.0/M_PI*(stop_angle - start_angle),
1765 ARCFILL_PIESLICE);
1767 setline(canvas, &p->sym.line);
1768 DrawPolyline(canvas, vps, 3, POLYLINE_OPEN);
1769 DrawArc(canvas, &vp1, &vp2,
1770 180.0/M_PI*start_angle,
1771 180.0/M_PI*(stop_angle - start_angle));
1773 avalue = p->avalue;
1775 if (avalue.active == TRUE) {
1776 TextProps tprops = avalue.tprops;
1778 vpa.x = vpc.x + ((1 + e[i])*r + avalue.offset.y)*
1779 cos((start_angle + stop_angle)/2.0);
1780 vpa.y = vpc.y + ((1 + e[i])*r + avalue.offset.y)*
1781 sin((start_angle + stop_angle)/2.0);
1783 strcpy(str, avalue.prestr);
1785 switch (avalue.type) {
1786 case AVALUE_TYPE_X:
1787 strcat(str, create_fstring(pr, avalue.format, avalue.prec, x[i],
1788 LFORMAT_TYPE_EXTENDED));
1789 break;
1790 case AVALUE_TYPE_STRING:
1791 if (p->data->s != NULL && p->data->s[i] != NULL) {
1792 strcat(str, p->data->s[i]);
1794 break;
1795 default:
1796 continue;
1799 strcat(str, avalue.appstr);
1801 drawtext(canvas, &vpa, &tprops, str);
1807 void symplus(Canvas *canvas, const VPoint *vp, double s)
1809 VPoint vp1, vp2;
1810 vp1.x = vp->x - s;
1811 vp1.y = vp->y;
1812 vp2.x = vp->x + s;
1813 vp2.y = vp->y;
1815 DrawLine(canvas, &vp1, &vp2);
1816 vp1.x = vp->x;
1817 vp1.y = vp->y - s;
1818 vp2.x = vp->x;
1819 vp2.y = vp->y + s;
1820 DrawLine(canvas, &vp1, &vp2);
1823 void symx(Canvas *canvas, const VPoint *vp, double s)
1825 VPoint vp1, vp2;
1826 double side = M_SQRT1_2*s;
1828 vp1.x = vp->x - side;
1829 vp1.y = vp->y - side;
1830 vp2.x = vp->x + side;
1831 vp2.y = vp->y + side;
1832 DrawLine(canvas, &vp1, &vp2);
1834 vp1.x = vp->x - side;
1835 vp1.y = vp->y + side;
1836 vp2.x = vp->x + side;
1837 vp2.y = vp->y - side;
1838 DrawLine(canvas, &vp1, &vp2);
1841 void symsplat(Canvas *canvas, const VPoint *vp, double s)
1843 symplus(canvas, vp, s);
1844 symx(canvas, vp, s);
1847 int drawxysym(Canvas *canvas, const VPoint *vp, const Symbol *sym)
1849 double symsize;
1850 VPoint vps[4];
1851 char buf[2];
1853 symsize = sym->size*0.01;
1855 switch (sym->type) {
1856 case SYM_NONE:
1857 break;
1858 case SYM_CIRCLE:
1859 setpen(canvas, &sym->fillpen);
1860 DrawFilledCircle(canvas, vp, symsize);
1861 setpen(canvas, &sym->line.pen);
1862 DrawCircle(canvas, vp, symsize);
1863 break;
1864 case SYM_SQUARE:
1865 symsize *= 0.85;
1866 vps[0].x = vp->x - symsize;
1867 vps[0].y = vp->y - symsize;
1868 vps[1].x = vps[0].x;
1869 vps[1].y = vp->y + symsize;
1870 vps[2].x = vp->x + symsize;
1871 vps[2].y = vps[1].y;
1872 vps[3].x = vps[2].x;
1873 vps[3].y = vps[0].y;
1875 setpen(canvas, &sym->fillpen);
1876 DrawPolygon(canvas, vps, 4);
1877 setline(canvas, &sym->line);
1878 DrawPolyline(canvas, vps, 4, POLYLINE_CLOSED);
1879 break;
1880 case SYM_DIAMOND:
1881 vps[0].x = vp->x;
1882 vps[0].y = vp->y + symsize;
1883 vps[1].x = vp->x - symsize;
1884 vps[1].y = vp->y;
1885 vps[2].x = vps[0].x;
1886 vps[2].y = vp->y - symsize;
1887 vps[3].x = vp->x + symsize;
1888 vps[3].y = vps[1].y;
1890 setpen(canvas, &sym->fillpen);
1891 DrawPolygon(canvas, vps, 4);
1892 setline(canvas, &sym->line);
1893 DrawPolyline(canvas, vps, 4, POLYLINE_CLOSED);
1894 break;
1895 case SYM_TRIANG1:
1896 vps[0].x = vp->x;
1897 vps[0].y = vp->y + 2*M_SQRT1_3*symsize;
1898 vps[1].x = vp->x - symsize;
1899 vps[1].y = vp->y - M_SQRT1_3*symsize;
1900 vps[2].x = vp->x + symsize;
1901 vps[2].y = vps[1].y;
1903 setpen(canvas, &sym->fillpen);
1904 DrawPolygon(canvas, vps, 3);
1905 setline(canvas, &sym->line);
1906 DrawPolyline(canvas, vps, 3, POLYLINE_CLOSED);
1907 break;
1908 case SYM_TRIANG2:
1909 vps[0].x = vp->x - 2*M_SQRT1_3*symsize;
1910 vps[0].y = vp->y;
1911 vps[1].x = vp->x + M_SQRT1_3*symsize;
1912 vps[1].y = vp->y - symsize;
1913 vps[2].x = vps[1].x;
1914 vps[2].y = vp->y + symsize;
1916 setpen(canvas, &sym->fillpen);
1917 DrawPolygon(canvas, vps, 3);
1918 setline(canvas, &sym->line);
1919 DrawPolyline(canvas, vps, 3, POLYLINE_CLOSED);
1920 break;
1921 case SYM_TRIANG3:
1922 vps[0].x = vp->x - symsize;
1923 vps[0].y = vp->y + M_SQRT1_3*symsize;
1924 vps[1].x = vp->x;
1925 vps[1].y = vp->y - 2*M_SQRT1_3*symsize;
1926 vps[2].x = vp->x + symsize;
1927 vps[2].y = vps[0].y;
1929 setpen(canvas, &sym->fillpen);
1930 DrawPolygon(canvas, vps, 3);
1931 setline(canvas, &sym->line);
1932 DrawPolyline(canvas, vps, 3, POLYLINE_CLOSED);
1933 break;
1934 case SYM_TRIANG4:
1935 vps[0].x = vp->x - M_SQRT1_3*symsize;
1936 vps[0].y = vp->y + symsize;
1937 vps[1].x = vps[0].x;
1938 vps[1].y = vp->y - symsize;
1939 vps[2].x = vp->x + 2*M_SQRT1_3*symsize;
1940 vps[2].y = vp->y;
1942 setpen(canvas, &sym->fillpen);
1943 DrawPolygon(canvas, vps, 3);
1944 setline(canvas, &sym->line);
1945 DrawPolyline(canvas, vps, 3, POLYLINE_CLOSED);
1946 break;
1947 case SYM_PLUS:
1948 setline(canvas, &sym->line);
1949 symplus(canvas, vp, symsize);
1950 break;
1951 case SYM_X:
1952 setline(canvas, &sym->line);
1953 symx(canvas, vp, symsize);
1954 break;
1955 case SYM_SPLAT:
1956 setline(canvas, &sym->line);
1957 symsplat(canvas, vp, symsize);
1958 break;
1959 case SYM_CHAR:
1960 setline(canvas, &sym->line);
1961 buf[0] = sym->symchar;
1962 buf[1] = '\0';
1963 setcharsize(canvas, sym->size);
1964 WriteString(canvas, vp, 0.0, JUST_CENTER|JUST_MIDDLE, buf);
1965 break;
1966 default:
1967 errmsg("Invalid symbol type");
1968 return RETURN_FAILURE;
1970 return RETURN_SUCCESS;
1973 static void drawlegbarsym(Canvas *canvas, const VPoint *vp,
1974 double width, double height, const Pen *sympen, const Pen *symfillpen)
1976 VPoint vps[4];
1978 vps[0].x = vps[1].x = vp->x - width/2;
1979 vps[2].x = vps[3].x = vp->x + width/2;
1980 vps[0].y = vps[3].y = vp->y - height/2;
1981 vps[1].y = vps[2].y = vp->y + height/2;
1983 setpen(canvas, symfillpen);
1984 DrawPolygon(canvas, vps, 4);
1985 setpen(canvas, sympen);
1986 DrawPolyline(canvas, vps, 4, POLYLINE_CLOSED);
1989 void drawerrorbar(Canvas *canvas,
1990 const VPoint *vp1, const VPoint *vp2, Errbar *eb)
1992 double ilen;
1993 VPoint vp_plus, vp_minus;
1994 VVector lvv;
1995 double vlength;
1996 static Arrow arrow = {0, 1.0, 1.0, 0.0};
1998 lvv.x = vp2->x - vp1->x;
1999 lvv.y = vp2->y - vp1->y;
2001 vlength = hypot(lvv.x, lvv.y);
2002 if (vlength == 0.0) {
2003 return;
2006 lvv.x /= vlength;
2007 lvv.y /= vlength;
2009 setpen(canvas, &eb->pen);
2011 if (eb->arrow_clip && is_validVPoint(canvas, vp2) == FALSE) {
2012 vp_plus.x = vp1->x + eb->cliplen*lvv.x;
2013 vp_plus.y = vp1->y + eb->cliplen*lvv.y;
2014 setlinewidth(canvas, eb->riser_linew);
2015 setlinestyle(canvas, eb->riser_lines);
2016 DrawLine(canvas, vp1, &vp_plus);
2017 arrow.length = 2*eb->barsize;
2018 setlinewidth(canvas, eb->linew);
2019 setlinestyle(canvas, eb->lines);
2020 draw_arrowhead(canvas, vp1, &vp_plus, &arrow, &eb->pen, &eb->pen);
2021 } else {
2022 setlinewidth(canvas, eb->riser_linew);
2023 setlinestyle(canvas, eb->riser_lines);
2024 DrawLine(canvas, vp1, vp2);
2025 setlinewidth(canvas, eb->linew);
2026 setlinestyle(canvas, eb->lines);
2027 ilen = 0.01*eb->barsize;
2028 vp_minus.x = vp2->x - ilen*lvv.y;
2029 vp_minus.y = vp2->y + ilen*lvv.x;
2030 vp_plus.x = vp2->x + ilen*lvv.y;
2031 vp_plus.y = vp2->y - ilen*lvv.x;
2032 DrawLine(canvas, &vp_minus, &vp_plus);
2037 * draw arrow head
2039 void draw_arrowhead(Canvas *canvas,
2040 const VPoint *vp1, const VPoint *vp2, const Arrow *arrowp,
2041 const Pen *pen, const Pen *fill)
2043 double L, l, d, vlength;
2044 VVector vnorm;
2045 VPoint vpc, vpl, vpr, vps[4];
2047 vlength = hypot((vp2->x - vp1->x), (vp2->y - vp1->y));
2048 if (vlength == 0.0) {
2049 return;
2052 vnorm.x = (vp2->x - vp1->x)/vlength;
2053 vnorm.y = (vp2->y - vp1->y)/vlength;
2055 L = 0.01*arrowp->length;
2056 d = L*arrowp->dL_ff;
2057 l = L*arrowp->lL_ff;
2059 vpc.x = vp2->x - L*vnorm.x;
2060 vpc.y = vp2->y - L*vnorm.y;
2061 vpl.x = vpc.x + 0.5*d*vnorm.y;
2062 vpl.y = vpc.y - 0.5*d*vnorm.x;
2063 vpr.x = vpc.x - 0.5*d*vnorm.y;
2064 vpr.y = vpc.y + 0.5*d*vnorm.x;
2065 vpc.x += l*vnorm.x;
2066 vpc.y += l*vnorm.y;
2068 vps[0] = vpl;
2069 vps[1] = *vp2;
2070 vps[2] = vpr;
2071 vps[3] = vpc;
2073 setlinestyle(canvas, 1);
2075 switch (arrowp->type) {
2076 case ARROW_TYPE_LINE:
2077 setpen(canvas, pen);
2078 DrawPolyline(canvas, vps, 3, POLYLINE_OPEN);
2079 break;
2080 case ARROW_TYPE_FILLED:
2081 setpen(canvas, fill);
2082 DrawPolygon(canvas, vps, 4);
2083 setpen(canvas, pen);
2084 DrawPolyline(canvas, vps, 4, POLYLINE_CLOSED);
2085 break;
2086 case ARROW_TYPE_CIRCLE:
2087 setpen(canvas, fill);
2088 DrawFilledCircle(canvas, vp2, d/2);
2089 setpen(canvas, pen);
2090 DrawCircle(canvas, vp2, d/2);
2091 break;
2092 default:
2093 errmsg("Internal error in draw_arrowhead()");
2094 break;
2097 return;
2100 void draw_region(Canvas *canvas, Quark *q)
2102 Quark *f = get_parent_frame(q);
2103 view v;
2104 region *r = region_get_data(q);
2105 VPoint vp;
2106 WPoint wp;
2107 double dv = 0.003;
2109 frame_get_view(f, &v);
2110 if (terminal_device(canvas) != TRUE || !r) {
2111 return;
2114 setcolor(canvas, r->color);
2115 setpattern(canvas, 1);
2117 vp.x = v.xv1;
2118 while (vp.x <= v.xv2) {
2119 vp.y = v.yv1;
2120 while (vp.y <= v.yv2) {
2121 if (Vpoint2Wpoint(q, &vp, &wp) == RETURN_SUCCESS &&
2122 inregion(q, &wp)) {
2123 DrawPixel(canvas, &vp);
2125 vp.y += dv;
2127 vp.x += dv;
2132 /* ---------------------- legends ---------------------- */
2134 void draw_legend_syms(Quark *pset,
2135 plot_rt_t *plot_rt, const VPoint *vp, const legend *l)
2137 set *p = set_get_data(pset);
2138 Quark *gr = get_parent_graph(pset);
2140 if (is_set_drawable(pset) &&
2141 !is_empty_string(p->legstr) &&
2142 graph_get_type(gr) != GRAPH_PIE) {
2144 Canvas *canvas = plot_rt->canvas;
2145 double symvshift;
2146 int draw_line, singlesym;
2147 VPoint vp1, vp2;
2149 setfont(canvas, p->sym.charfont);
2151 if (l->len <= 0.0 || p->line.type == 0 ||
2152 ((p->line.line.style == 0 ||
2153 p->line.line.pen.pattern == 0) &&
2154 (p->line.filltype == SETFILL_NONE ||
2155 p->line.fillpen.pattern == 0))) {
2156 draw_line = FALSE;
2157 } else {
2158 draw_line = TRUE;
2161 symvshift = 0.0;
2162 if (draw_line) {
2163 vp1.x = vp->x - l->len/2;
2164 vp2.x = vp->x + l->len/2;
2165 vp2.y = vp1.y = vp->y;
2167 setline(canvas, &p->line.line);
2168 if (p->line.filltype != SETFILL_NONE &&
2169 p->line.fillpen.pattern != 0) {
2170 VPoint vpf1, vpf2;
2172 vpf1.x = vp1.x;
2173 vpf1.y = vp1.y - 0.01*l->charsize;
2174 vpf2.x = vp2.x;
2175 vpf2.y = vp2.y + 0.01*l->charsize;
2177 /* TODO: settable option for setfill presentation */
2178 setpen(canvas, &p->line.fillpen);
2179 if (1) {
2180 FillRect(canvas, &vpf1, &vpf2);
2181 } else {
2182 DrawFilledEllipse(canvas, &vpf1, &vpf2);
2184 setpen(canvas, &p->line.line.pen);
2185 if (1) {
2186 DrawRect(canvas, &vpf1, &vpf2);
2187 } else {
2188 DrawEllipse(canvas, &vpf1, &vpf2);
2191 symvshift = 0.01*l->charsize;
2192 } else {
2193 DrawLine(canvas, &vp1, &vp2);
2197 if (draw_line) {
2198 singlesym = l->singlesym;
2199 } else {
2200 singlesym = TRUE;
2203 setline(canvas, &p->sym.line);
2204 if (!singlesym) {
2205 if (p->type == SET_BAR || p->type == SET_BOXPLOT ||
2206 p->type == SET_BARDY || p->type == SET_BARDYDY) {
2208 drawlegbarsym(canvas, &vp1, 0.02*p->sym.size, 0.02*l->charsize,
2209 &p->sym.line.pen, &p->sym.fillpen);
2210 drawlegbarsym(canvas, &vp2, 0.02*p->sym.size, 0.02*l->charsize,
2211 &p->sym.line.pen, &p->sym.fillpen);
2212 } else {
2213 drawxysym(canvas, &vp1, &p->sym);
2214 drawxysym(canvas, &vp2, &p->sym);
2216 } else {
2217 VPoint vptmp;
2218 vptmp.x = vp->x;
2219 vptmp.y = vp->y + symvshift;
2221 if (p->type == SET_BAR || p->type == SET_BOXPLOT ||
2222 p->type == SET_BARDY || p->type == SET_BARDYDY) {
2223 drawlegbarsym(canvas, &vptmp, 0.02*p->sym.size, 0.02*l->charsize,
2224 &p->sym.line.pen, &p->sym.fillpen);
2225 } else {
2226 drawxysym(canvas, &vptmp, &p->sym);
2232 static int is_legend_drawable(Quark *pset)
2234 set *p = set_get_data(pset);
2235 Quark *gr = get_parent_graph(pset);
2237 if (is_set_drawable(pset) &&
2238 !is_empty_string(p->legstr) &&
2239 quark_is_active(gr) &&
2240 graph_get_type(gr) != GRAPH_PIE) {
2242 return TRUE;
2243 } else {
2244 return FALSE;
2248 typedef struct {
2249 view sym_bbox;
2250 view str_bbox;
2251 Quark *pset;
2252 } leg_rt_t;
2255 * draw the legends
2257 void draw_legends(Quark *q, plot_rt_t *plot_rt)
2259 Canvas *canvas = plot_rt->canvas;
2260 VPoint vp, vp2;
2261 double bb_width, bb_height;
2262 int i, nsets, setno, nleg_entries;
2263 double max_sym_width, max_str_width;
2265 view sym_bbox, str_bbox, v;
2266 legend *l;
2268 Quark **psets;
2270 leg_rt_t *leg_entries;
2272 l = frame_get_legend(q);
2273 if (l->active == FALSE) {
2274 return;
2277 setclipping(canvas, FALSE);
2279 vp.x = vp.y = 0.0;
2280 bb_height = l->vgap;
2281 max_sym_width = 0.0;
2282 max_str_width = 0.0;
2284 set_draw_mode(canvas, FALSE);
2286 nsets = get_descendant_sets(q, &psets);
2287 leg_entries = xmalloc(nsets*sizeof(leg_rt_t));
2288 nleg_entries = 0;
2289 for (setno = 0; setno < nsets; setno++) {
2290 Quark *pset;
2291 double sym_width, str_width, y_middle, ascent, descent, height;
2293 pset = psets[setno];
2295 if (is_legend_drawable(pset)) {
2296 set *p = set_get_data(pset);
2298 activate_bbox(canvas, BBOX_TYPE_TEMP, TRUE);
2299 reset_bbox(canvas, BBOX_TYPE_TEMP);
2300 update_bbox(canvas, BBOX_TYPE_TEMP, &vp);
2301 draw_legend_syms(pset, plot_rt, &vp, l);
2302 get_bbox(canvas, BBOX_TYPE_TEMP, &sym_bbox);
2303 sym_width = fabs(sym_bbox.xv2 - sym_bbox.xv1);
2304 if (sym_width > max_sym_width) {
2305 max_sym_width = sym_width;
2308 setcharsize(canvas, l->charsize);
2309 setfont(canvas, l->font);
2311 get_string_bbox(canvas,
2312 &vp, 0.0, JUST_LEFT|JUST_BLINE, p->legstr, &str_bbox);
2314 y_middle = str_bbox.yv2/2;
2315 str_width = fabs(str_bbox.xv2 - str_bbox.xv1);
2316 if (str_width > max_str_width) {
2317 max_str_width = str_width;
2320 ascent = MAX2(sym_bbox.yv2 + y_middle, str_bbox.yv2);
2321 descent = MIN2(sym_bbox.yv1 + y_middle, str_bbox.yv1);
2323 height = ascent - descent;
2324 bb_height += height + l->vgap;
2326 leg_entries[nleg_entries].pset = pset;
2327 leg_entries[nleg_entries].sym_bbox = sym_bbox;
2328 leg_entries[nleg_entries].str_bbox = str_bbox;
2329 nleg_entries++;
2333 xfree(psets);
2335 set_draw_mode(canvas, TRUE);
2337 bb_width = max_sym_width + max_str_width + 3*l->hgap;
2339 frame_get_view(q, &v);
2341 switch (l->acorner) {
2342 case CORNER_LL:
2343 vp.x = v.xv1 + l->offset.x;
2344 vp.y = v.yv1 + l->offset.y + bb_height;
2345 break;
2346 case CORNER_UL:
2347 vp.x = v.xv1 + l->offset.x;
2348 vp.y = v.yv2 - l->offset.y;
2349 break;
2350 case CORNER_UR:
2351 default:
2352 vp.x = v.xv2 - l->offset.x - bb_width;
2353 vp.y = v.yv2 - l->offset.y;
2354 break;
2355 case CORNER_LR:
2356 vp.x = v.xv2 - l->offset.x - bb_width;
2357 vp.y = v.yv1 + l->offset.y + bb_height;
2358 break;
2361 vp2.x = vp.x + bb_width;
2362 vp2.y = vp.y - bb_height;
2364 l->bb.xv1 = vp.x;
2365 l->bb.yv1 = vp2.y;
2366 l->bb.xv2 = vp2.x;
2367 l->bb.yv2 = vp.y;
2369 if (nleg_entries) {
2370 setpen(canvas, &l->boxfillpen);
2371 FillRect(canvas, &vp, &vp2);
2373 setline(canvas, &l->boxline);
2374 DrawRect(canvas, &vp, &vp2);
2376 /* correction */
2377 vp.x += l->hgap + max_sym_width/2;
2378 vp.y -= l->vgap;
2382 for (i = 0; i < nleg_entries; i++) {
2383 leg_rt_t *leg_entry;
2384 Quark *pset;
2385 set *p;
2386 double y_middle, ascent, descent;
2387 VPoint vpsym, vpstr;
2389 if (l->invert == FALSE) {
2390 setno = i;
2391 } else {
2392 setno = nleg_entries - i - 1;
2395 leg_entry = &leg_entries[setno];
2396 pset = leg_entry->pset;
2397 p = set_get_data(pset);
2399 y_middle = leg_entry->str_bbox.yv2/2;
2400 ascent = MAX2(leg_entry->sym_bbox.yv2 + y_middle, leg_entry->str_bbox.yv2);
2401 descent = MIN2(leg_entry->sym_bbox.yv1 + y_middle, leg_entry->str_bbox.yv1);
2403 vp.y -= ascent;
2405 vpsym.x = vp.x;
2406 vpsym.y = vp.y + y_middle;
2407 draw_legend_syms(pset, plot_rt, &vpsym, l);
2409 vpstr.x = vp.x + max_sym_width/2 + l->hgap;
2410 vpstr.y = vp.y;
2411 setcharsize(canvas, l->charsize);
2412 setfont(canvas, l->font);
2413 setcolor(canvas, l->color);
2414 WriteString(canvas, &vpstr, 0.0, JUST_LEFT|JUST_BLINE, p->legstr);
2416 vp.y -= (l->vgap - descent);
2419 xfree(leg_entries);