2 * Grace - GRaphing, Advanced Computation and Exploration of data
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
6 * Copyright (c) 1991-1995 Paul J Turner, Portland, OR
7 * Copyright (c) 1996-2004 Grace Development Team
9 * Maintained by Evgeny Stambulchik
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
42 #include "grace/canvas.h"
46 #include "core_utils.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
;
60 switch (quark_fid_get(q
)) {
63 set_draw_mode(canvas
, TRUE
);
75 draw_frame(q
, plot_rt
);
80 /* FIXME: stacked graphs */
81 plot_rt
->last_pass
= TRUE
;
82 plot_rt
->first_pass
= TRUE
;
88 if (draw_graph(q
, plot_rt
) != RETURN_SUCCESS
) {
89 closure
->descend
= FALSE
;
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
);
103 draw_set(q
, plot_rt
);
106 draw_axis(canvas
, q
);
109 draw_object(canvas
, q
);
112 draw_atext(canvas
, q
);
115 draw_region(canvas
, q
);
122 static void dproc(Canvas
*canvas
, void *data
)
124 Quark
*project
= (Quark
*) data
;
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
);
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
);
160 char fname
[GR_MAXPATHLEN
];
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
);
181 s
= get_print_cmd(grace
);
182 if (is_empty_string(s
)) {
183 errmsg("No print command defined, output aborted");
187 /* VMS doesn't like extensionless files */
188 strcat(fname
, ".prn");
191 prstream
= grace_openw(grace
, fname
);
192 if (prstream
== NULL
) {
196 canvas_set_prstream(canvas
, prstream
);
198 select_device(canvas
, rt
->hdevice
);
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
;
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
)) {
218 #ifndef PRINT_CMD_UNLINKS
222 if (truncated_out
== TRUE
) {
223 errmsg("Output is truncated - tune device dimensions");
229 int draw_graph(Quark
*gr
, plot_rt_t
*plot_rt
)
234 plot_rt
->offset
= 0.0;
236 if (plot_rt
->first_pass
) {
238 gtype
= graph_get_type(gr
);
240 if (gtype
== GRAPH_CHART
) {
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
) {
271 minmax(plot_rt
->refx
, plot_rt
->refn
, &xmin
, &xmax
, &imin
, &imax
);
272 plot_rt
->epsilon
= 1.0e-3*(xmax
- xmin
)/plot_rt
->refn
;
274 plot_rt
->epsilon
= 0.0;
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
)
300 if (!is_set_drawable(pset
)) {
304 gr
= get_parent_graph(pset
);
306 p
= set_get_data(pset
);
309 gtype
= graph_get_type(gr
);
312 switch (set_get_type(pset
)) {
317 drawsetline(pset
, plot_rt
);
318 drawsetsyms(pset
, plot_rt
);
319 drawsetavalues(pset
, plot_rt
);
322 drawsetline(pset
, plot_rt
);
323 drawsetbars(pset
, plot_rt
);
324 drawsetavalues(pset
, plot_rt
);
332 drawsetline(pset
, plot_rt
);
333 drawseterrbars(pset
, plot_rt
);
334 drawsetsyms(pset
, plot_rt
);
335 drawsetavalues(pset
, plot_rt
);
338 drawsethilo(pset
, plot_rt
);
339 drawsetavalues(pset
, plot_rt
);
342 drawsetline(pset
, plot_rt
);
343 drawsetvmap(pset
, plot_rt
);
344 drawsetsyms(pset
, plot_rt
);
345 drawsetavalues(pset
, plot_rt
);
348 drawsetline(pset
, plot_rt
);
349 drawsetboxplot(pset
, plot_rt
);
350 drawsetavalues(pset
, plot_rt
);
353 errmsg("Unsupported in XY graph set type");
358 switch (set_get_type(pset
)) {
363 drawsetline(pset
, plot_rt
);
364 drawsetsyms(pset
, plot_rt
);
365 drawsetavalues(pset
, plot_rt
);
373 drawsetline(pset
, plot_rt
);
374 drawseterrbars(pset
, plot_rt
);
375 drawsetsyms(pset
, plot_rt
);
376 drawsetavalues(pset
, plot_rt
);
379 drawcirclexy(pset
, plot_rt
);
380 drawsetsyms(pset
, plot_rt
);
381 drawsetavalues(pset
, plot_rt
);
384 drawsetline(pset
, plot_rt
);
385 drawsetvmap(pset
, plot_rt
);
386 drawsetsyms(pset
, plot_rt
);
387 drawsetavalues(pset
, plot_rt
);
390 errmsg("Unsupported in XY graph set type");
395 switch (set_get_type(pset
)) {
400 drawsetline(pset
, plot_rt
);
401 drawsetsyms(pset
, plot_rt
);
402 drawsetavalues(pset
, plot_rt
);
405 errmsg("Unsupported in polar graph set type");
410 switch (set_get_type(pset
)) {
414 draw_pie_chart_set(pset
, plot_rt
);
417 errmsg("Unsupported in pie chart set type");
422 /* check that abscissas are identical with refx */
423 x
= set_get_col(pset
, DATA_X
);
425 for (j
= 0; j
< set_get_length(pset
); j
++) {
426 if (fabs(x
[j
] - plot_rt
->refx
[j
]) > plot_rt
->epsilon
) {
433 sprintf(buf
, "Set %s has different abscissas, "
434 "skipped from the chart.",
435 quark_idstr_get(pset
));
440 if (graph_is_stacked(gr
) != TRUE
) {
441 plot_rt
->offset
+= 0.5*0.02*p
->sym
.size
;
444 switch (set_get_type(pset
)) {
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
);
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
);
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
);
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
);
488 if (plot_rt
->first_pass
) {
489 errmsg("Unsupported in XY chart set type");
494 if (graph_is_stacked(gr
) != TRUE
) {
495 plot_rt
->offset
+= 0.5*0.02*p
->sym
.size
+ graph_get_bargap(gr
);
497 for (j
= 0; j
< set_get_length(pset
); j
++) {
498 plot_rt
->refy
[j
] += p
->data
->ex
[1][j
];
506 void draw_ref_point(Canvas
*canvas
, Quark
*gr
)
511 if (is_refpoint_active(gr
)) {
512 locator
= graph_get_locator(gr
);
513 Wpoint2Vpoint(gr
, &locator
->origin
, &vp
);
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
;
533 f
= frame_get_data(q
);
534 frame_get_view(q
, &v
);
536 setclipping(canvas
, TRUE
);
538 setline(canvas
, &f
->outline
);
546 DrawRect(canvas
, &vps
[0], &vps
[1]);
548 case 1: /* half open */
555 DrawPolyline(canvas
, vps
, 3, POLYLINE_OPEN
);
557 case 2: /* break top */
566 DrawPolyline(canvas
, vps
, 4, POLYLINE_OPEN
);
568 case 3: /* break bottom */
577 DrawPolyline(canvas
, vps
, 4, POLYLINE_OPEN
);
579 case 4: /* break left */
588 DrawPolyline(canvas
, vps
, 4, POLYLINE_OPEN
);
590 case 5: /* break right */
599 DrawPolyline(canvas
, vps
, 4, POLYLINE_OPEN
);
603 draw_legends(q
, plot_rt
);
606 void fillframe(Canvas
*canvas
, Quark
*q
)
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
);
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
;
643 double xmin
, xmax
, ymin
, ymax
;
646 if (p
->line
.filltype
== SETFILL_NONE
) {
650 if (graph_get_type(gr
) == GRAPH_CHART
) {
652 setlen
= MIN2(set_get_length(pset
), plot_rt
->refn
);
655 setlen
= set_get_length(pset
);
659 if (graph_get_type(gr
) == GRAPH_CHART
&& graph_is_stacked(gr
) == TRUE
) {
660 stacked_chart
= TRUE
;
662 stacked_chart
= FALSE
;
665 setclipping(canvas
, TRUE
);
667 graph_get_world(gr
, &w
);
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
) {
678 vps
= (VPoint
*) xmalloc((len
+ 2) * sizeof(VPoint
));
680 errmsg("Can't xmalloc in drawsetfill");
684 for (i
= 0; i
< setlen
; 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
;
702 case LINE_TYPE_LEFTSTAIR
:
703 case LINE_TYPE_RIGHTSTAIR
:
705 vps
= (VPoint
*) xmalloc((len
+ 2) * sizeof(VPoint
));
707 errmsg("Can't xmalloc in drawsetfill");
711 for (i
= 0; i
< setlen
; 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
;
725 vps
[i
].x
= vps
[i
+ 1].x
;
726 vps
[i
].y
= vps
[i
- 1].y
;
734 switch (p
->line
.filltype
) {
735 case SETFILL_POLYGON
:
738 case SETFILL_BASELINE
:
739 if (stacked_chart
== TRUE
) {
742 getsetminmax(&pset
, 1, &xmin
, &xmax
, &ymin
, &ymax
);
743 ybase
= setybase(pset
);
745 wptmp
.x
= MIN2(xmax
, w
.xg2
);
747 Wpoint2Vpoint(gr
, &wptmp
, &vps
[len
]);
748 vps
[len
].x
+= plot_rt
->offset
;
749 wptmp
.x
= MAX2(xmin
, w
.xg1
);
751 Wpoint2Vpoint(gr
, &wptmp
, &vps
[len
+ 1]);
752 vps
[len
+ 1].x
+= plot_rt
->offset
;
760 setpen(canvas
, &p
->line
.fillpen
);
761 setfillrule(canvas
, p
->line
.fillrule
);
762 DrawPolygon(canvas
, vps
, polylen
);
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
);
777 int line_type
= p
->line
.type
;
778 VPoint vps
[4], *vpstmp
, vprev
;
783 double xmin
, xmax
, ymin
, ymax
;
784 int skip
= p
->symskip
+ 1;
787 if (graph_get_type(gr
) == GRAPH_CHART
) {
789 setlen
= MIN2(set_get_length(pset
), plot_rt
->refn
);
792 setlen
= set_get_length(pset
);
796 if (graph_get_type(gr
) == GRAPH_CHART
&& graph_is_stacked(gr
) == TRUE
) {
797 stacked_chart
= TRUE
;
799 stacked_chart
= FALSE
;
802 if (stacked_chart
== TRUE
) {
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
);
821 if (p
->line
.line
.style
!= 0 && p
->line
.line
.pen
.pattern
!= 0) {
826 case LINE_TYPE_STRAIGHT
:
827 vpstmp
= (VPoint
*) xmalloc(setlen
*sizeof(VPoint
));
828 if (vpstmp
== NULL
) {
829 errmsg("xmalloc failed in drawsetline()");
832 for (i
= 0; i
< setlen
; 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
);
846 case LINE_TYPE_SEGMENT2
:
847 for (i
= 0; i
< setlen
- 1; i
+= 2) {
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
;
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
;
866 DrawLine(canvas
, &vps
[0], &vps
[1]);
869 case LINE_TYPE_SEGMENT3
:
870 for (i
= 0; i
< setlen
- 2; i
+= 3) {
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
;
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
;
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
);
898 if (i
== setlen
- 2) {
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
;
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
;
917 DrawLine(canvas
, &vps
[0], &vps
[1]);
920 case LINE_TYPE_LEFTSTAIR
:
921 case LINE_TYPE_RIGHTSTAIR
:
923 vpstmp
= (VPoint
*) xmalloc(len
*sizeof(VPoint
));
924 if (vpstmp
== NULL
) {
925 errmsg("xmalloc failed in drawsetline()");
928 for (i
= 0; i
< setlen
; 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
;
942 vpstmp
[i
].x
= vpstmp
[i
+ 1].x
;
943 vpstmp
[i
].y
= vpstmp
[i
- 1].y
;
946 DrawPolyline(canvas
, vpstmp
, len
, POLYLINE_OPEN
);
950 errmsg("Invalid line type");
955 if (p
->line
.droplines
== TRUE
) {
956 for (i
= 0; i
< setlen
; i
+= skip
) {
958 if (stacked_chart
== TRUE
) {
959 wp
.y
= plot_rt
->refy
[i
];
963 Wpoint2Vpoint(gr
, &wp
, &vps
[0]);
964 vps
[0].x
+= plot_rt
->offset
;
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
;
976 hypot(vps
[1].x
-vprev
.x
, vps
[1].y
-vprev
.y
) < p
->symskipmindist
)
980 DrawLine(canvas
, &vps
[0], &vps
[1]);
984 getsetminmax(&pset
, 1, &xmin
, &xmax
, &ymin
, &ymax
);
986 if (p
->line
.baseline
== TRUE
&& stacked_chart
!= TRUE
) {
989 Wpoint2Vpoint(gr
, &wp
, &vps
[0]);
990 vps
[0].x
+= plot_rt
->offset
;
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
);
1009 double *x
, *y
, *z
, *c
;
1010 int skip
= p
->symskip
+ 1;
1012 double znorm
= graph_get_znorm(gr
);
1014 if (graph_get_type(gr
) == GRAPH_CHART
) {
1016 setlen
= MIN2(set_get_length(pset
), plot_rt
->refn
);
1019 setlen
= set_get_length(pset
);
1023 if (graph_get_type(gr
) == GRAPH_CHART
&& graph_is_stacked(gr
) == TRUE
) {
1024 stacked_chart
= TRUE
;
1026 stacked_chart
= FALSE
;
1029 if (p
->type
== SET_XYSIZE
) {
1038 if (p
->type
== SET_XYCOLOR
) {
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
) {
1056 if (stacked_chart
== TRUE
) {
1057 wp
.y
+= plot_rt
->refy
[i
];
1060 if (!is_validWPoint(gr
, &wp
)){
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
)
1072 sym
.size
= z
[i
]/znorm
;
1075 int color
= (int) rint(c
[i
]);
1076 sym
.fillpen
.color
= color
;
1078 if (drawxysym(canvas
, &vp
, &sym
) != RETURN_SUCCESS
) {
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
);
1108 int skip
= p
->symskip
+ 1;
1110 char str
[MAX_STRING_LENGTH
];
1114 if (avalue
.active
!= TRUE
) {
1118 if (graph_get_type(gr
) == GRAPH_CHART
) {
1120 setlen
= MIN2(set_get_length(pset
), plot_rt
->refn
);
1123 setlen
= set_get_length(pset
);
1127 if (set_get_ncols(pset
) > 2) {
1133 if (graph_get_type(gr
) == GRAPH_CHART
&& graph_is_stacked(gr
) == TRUE
) {
1134 stacked_chart
= TRUE
;
1136 stacked_chart
= FALSE
;
1139 for (i
= 0; i
< setlen
; i
+= skip
) {
1142 if (stacked_chart
== TRUE
) {
1143 wp
.y
+= plot_rt
->refy
[i
];
1146 if (!is_validWPoint(gr
, &wp
)){
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
)
1160 strcpy(str
, avalue
.prestr
);
1162 switch(avalue
.type
) {
1163 case AVALUE_TYPE_NONE
:
1166 strcat(str
, create_fstring(pr
, avalue
.format
, avalue
.prec
, wp
.x
,
1167 LFORMAT_TYPE_EXTENDED
));
1170 strcat(str
, create_fstring(pr
, avalue
.format
, avalue
.prec
, wp
.y
,
1171 LFORMAT_TYPE_EXTENDED
));
1173 case AVALUE_TYPE_XY
:
1174 strcat(str
, create_fstring(pr
, avalue
.format
, avalue
.prec
, wp
.x
,
1175 LFORMAT_TYPE_EXTENDED
));
1177 strcat(str
, create_fstring(pr
, avalue
.format
, avalue
.prec
, wp
.y
,
1178 LFORMAT_TYPE_EXTENDED
));
1180 case AVALUE_TYPE_STRING
:
1181 if (p
->data
->s
!= NULL
&& p
->data
->s
[i
] != NULL
) {
1182 strcat(str
, p
->data
->s
[i
]);
1187 strcat(str
, create_fstring(pr
, avalue
.format
, avalue
.prec
, z
[i
],
1188 LFORMAT_TYPE_EXTENDED
));
1192 errmsg("Invalid type of ann. value");
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
);
1209 double *dx_plus
, *dx_minus
, *dy_plus
, *dy_minus
, *dtmp
;
1210 PlacementType ptype
= p
->errbar
.ptype
;
1212 VPoint vp1
, vp2
, vprev
;
1214 int skip
= p
->symskip
+ 1;
1216 if (p
->errbar
.active
!= TRUE
) {
1220 if (graph_get_type(gr
) == GRAPH_CHART
) {
1222 n
= MIN2(set_get_length(pset
), plot_rt
->refn
);
1225 n
= set_get_length(pset
);
1229 if (graph_get_type(gr
) == GRAPH_CHART
&& graph_is_stacked(gr
) == TRUE
) {
1230 stacked_chart
= TRUE
;
1232 stacked_chart
= FALSE
;
1241 dx_plus
= p
->data
->ex
[2];
1245 dy_plus
= p
->data
->ex
[2];
1248 dx_plus
= p
->data
->ex
[2];
1249 dx_minus
= p
->data
->ex
[3];
1253 dy_plus
= p
->data
->ex
[2];
1254 dy_minus
= p
->data
->ex
[3];
1257 dx_plus
= p
->data
->ex
[2];
1258 dy_plus
= p
->data
->ex
[3];
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];
1271 case PLACEMENT_OPPOSITE
:
1279 case PLACEMENT_BOTH
:
1280 if (dx_minus
== NULL
&& dy_minus
== NULL
) {
1289 setclipping(canvas
, TRUE
);
1291 for (i
= 0; i
< n
; i
+= skip
) {
1294 if (stacked_chart
== TRUE
) {
1295 wp1
.y
+= plot_rt
->refy
[i
];
1297 if (is_validWPoint(gr
, &wp1
) == FALSE
) {
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
)
1308 if (dx_plus
!= NULL
) {
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
) {
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
) {
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
) {
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
);
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;
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
) {
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
)
1365 Wpoint2Vpoint(pset
, &wp
, &vp1
);
1367 Wpoint2Vpoint(pset
, &wp
, &vp2
);
1368 DrawLine(canvas
, &vp1
, &vp2
);
1370 Wpoint2Vpoint(pset
, &wp
, &vp1
);
1373 DrawLine(canvas
, &vp1
, &vp2
);
1375 Wpoint2Vpoint(pset
, &wp
, &vp1
);
1378 DrawLine(canvas
, &vp1
, &vp2
);
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
);
1393 double lw
, bw
= 0.01*p
->sym
.size
;
1394 int skip
= p
->symskip
+ 1;
1397 VPoint vp1
, vp2
, vprev
;
1400 if (graph_get_type(gr
) == GRAPH_CHART
) {
1402 n
= MIN2(set_get_length(pset
), plot_rt
->refn
);
1405 n
= set_get_length(pset
);
1409 if (graph_get_type(gr
) == GRAPH_CHART
&& graph_is_stacked(gr
) == TRUE
) {
1410 stacked_chart
= TRUE
;
1412 stacked_chart
= FALSE
;
1417 if (stacked_chart
== TRUE
) {
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
);
1431 if (p
->sym
.fillpen
.pattern
!= 0) {
1432 setpen(canvas
, &p
->sym
.fillpen
);
1433 for (i
= 0; i
< n
; i
+= skip
) {
1435 if (stacked_chart
== TRUE
) {
1436 wp
.y
= plot_rt
->refy
[i
];
1440 Wpoint2Vpoint(gr
, &wp
, &vp1
);
1442 vp1
.x
+= plot_rt
->offset
;
1444 if (stacked_chart
== TRUE
) {
1449 Wpoint2Vpoint(gr
, &wp
, &vp2
);
1451 vp2
.x
+= plot_rt
->offset
;
1458 hypot(vp2
.x
- vprev
.x
, vp2
.y
- vprev
.y
) < p
->symskipmindist
)
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
) {
1469 if (stacked_chart
== TRUE
) {
1470 wp
.y
= plot_rt
->refy
[i
];
1474 Wpoint2Vpoint(gr
, &wp
, &vp1
);
1476 vp1
.x
+= plot_rt
->offset
;
1478 if (stacked_chart
== TRUE
) {
1483 Wpoint2Vpoint(gr
, &wp
, &vp2
);
1485 vp2
.x
+= plot_rt
->offset
;
1492 hypot(vp2
.x
- vprev
.x
, vp2
.y
- vprev
.y
) < p
->symskipmindist
)
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
);
1507 int skip
= p
->symskip
+ 1;
1509 VPoint vp1
, vp2
, vprev
;
1511 setclipping(canvas
, TRUE
);
1513 setlen
= set_get_length(pset
);
1518 setfillrule(canvas
, p
->line
.fillrule
);
1519 setline(canvas
, &p
->line
.line
);
1521 for (i
= 0; i
< setlen
; i
+= skip
) {
1524 /* TODO: remove once ellipse clipping works */
1525 if (!is_validWPoint(pset
, &wp
)){
1530 Wpoint2Vpoint(pset
, &wp
, &vp1
);
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
)
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
);
1555 double znorm
= graph_get_znorm(gr
);
1556 int skip
= p
->symskip
+ 1;
1557 double *x
, *y
, *vx
, *vy
;
1559 VPoint vp1
, vp2
, vprev
;
1560 Arrow arrow
= {0, 1.0, 1.0, 0.0};
1562 Errbar eb
= p
->errbar
;
1564 setclipping(canvas
, TRUE
);
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
) {
1583 if (!is_validWPoint(gr
, &wp
)){
1586 Wpoint2Vpoint(gr
, &wp
, &vp1
);
1587 if (i
&& hypot(vp1
.x
- vprev
.x
, vp1
.y
- vprev
.y
) < p
->symskipmindist
)
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
);
1608 double *x
, *md
, *lb
, *ub
, *lw
, *uw
;
1609 double size
= 0.01*p
->sym
.size
;
1610 int skip
= p
->symskip
+ 1;
1612 VPoint vp1
, vp2
, vprev
;
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
) {
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
)
1633 Wpoint2Vpoint(pset
, &wp
, &vp1
);
1635 Wpoint2Vpoint(pset
, &wp
, &vp2
);
1638 if (p
->errbar
.active
== TRUE
) {
1641 Wpoint2Vpoint(pset
, &wp
, &vp3
);
1642 drawerrorbar(canvas
, &vp1
, &vp3
, &p
->errbar
);
1644 Wpoint2Vpoint(pset
, &wp
, &vp3
);
1645 drawerrorbar(canvas
, &vp2
, &vp3
, &p
->errbar
);
1651 setpen(canvas
, &p
->sym
.fillpen
);
1652 FillRect(canvas
, &vp1
, &vp2
);
1654 setline(canvas
, &p
->sym
.line
);
1655 DrawRect(canvas
, &vp1
, &vp2
);
1659 Wpoint2Vpoint(pset
, &wp
, &vp1
);
1663 DrawLine(canvas
, &vp1
, &vp2
);
1667 void draw_pie_chart_set(Quark
*pset
, plot_rt_t
*plot_rt
)
1673 VPoint vpc
, vp1
, vp2
, vps
[3], vpa
;
1675 double r
, start_angle
, stop_angle
;
1677 double *x
, *c
, *e
, *pt
;
1679 char str
[MAX_STRING_LENGTH
];
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
);
1697 if (plot_rt
->ndsets
> 1) {
1698 errmsg("Only one set per pie chart can be drawn");
1703 x
= set_get_col(pset
, DATA_X
);
1704 /* explode factor */
1705 e
= set_get_col(pset
, DATA_Y
);
1707 c
= set_get_col(pset
, DATA_Y1
);
1709 pt
= set_get_col(pset
, DATA_Y2
);
1711 /* get max explode factor */
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;
1720 for (i
= 0; i
< set_get_length(pset
); i
++) {
1722 errmsg("No negative values in pie charts allowed");
1726 errmsg("No negative offsets in pie charts allowed");
1733 for (i
= 0; i
< set_get_length(pset
); i
++) {
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
;
1752 pen
.color
= (int) rint(c
[i
]);
1754 pen
.color
= p
->sym
.fillpen
.color
;
1757 pen
.pattern
= (int) rint(pt
[i
]);
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
),
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
));
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
) {
1787 strcat(str
, create_fstring(pr
, avalue
.format
, avalue
.prec
, x
[i
],
1788 LFORMAT_TYPE_EXTENDED
));
1790 case AVALUE_TYPE_STRING
:
1791 if (p
->data
->s
!= NULL
&& p
->data
->s
[i
] != NULL
) {
1792 strcat(str
, p
->data
->s
[i
]);
1799 strcat(str
, avalue
.appstr
);
1801 drawtext(canvas
, &vpa
, &tprops
, str
);
1807 void symplus(Canvas
*canvas
, const VPoint
*vp
, double s
)
1815 DrawLine(canvas
, &vp1
, &vp2
);
1820 DrawLine(canvas
, &vp1
, &vp2
);
1823 void symx(Canvas
*canvas
, const VPoint
*vp
, double s
)
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
)
1853 symsize
= sym
->size
*0.01;
1855 switch (sym
->type
) {
1859 setpen(canvas
, &sym
->fillpen
);
1860 DrawFilledCircle(canvas
, vp
, symsize
);
1861 setpen(canvas
, &sym
->line
.pen
);
1862 DrawCircle(canvas
, vp
, symsize
);
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
);
1882 vps
[0].y
= vp
->y
+ symsize
;
1883 vps
[1].x
= vp
->x
- symsize
;
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
);
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
);
1909 vps
[0].x
= vp
->x
- 2*M_SQRT1_3
*symsize
;
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
);
1922 vps
[0].x
= vp
->x
- symsize
;
1923 vps
[0].y
= vp
->y
+ M_SQRT1_3
*symsize
;
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
);
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
;
1942 setpen(canvas
, &sym
->fillpen
);
1943 DrawPolygon(canvas
, vps
, 3);
1944 setline(canvas
, &sym
->line
);
1945 DrawPolyline(canvas
, vps
, 3, POLYLINE_CLOSED
);
1948 setline(canvas
, &sym
->line
);
1949 symplus(canvas
, vp
, symsize
);
1952 setline(canvas
, &sym
->line
);
1953 symx(canvas
, vp
, symsize
);
1956 setline(canvas
, &sym
->line
);
1957 symsplat(canvas
, vp
, symsize
);
1960 setline(canvas
, &sym
->line
);
1961 buf
[0] = sym
->symchar
;
1963 setcharsize(canvas
, sym
->size
);
1964 WriteString(canvas
, vp
, 0.0, JUST_CENTER
|JUST_MIDDLE
, buf
);
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
)
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
)
1993 VPoint vp_plus
, vp_minus
;
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) {
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
);
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
);
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
;
2045 VPoint vpc
, vpl
, vpr
, vps
[4];
2047 vlength
= hypot((vp2
->x
- vp1
->x
), (vp2
->y
- vp1
->y
));
2048 if (vlength
== 0.0) {
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
;
2073 setlinestyle(canvas
, 1);
2075 switch (arrowp
->type
) {
2076 case ARROW_TYPE_LINE
:
2077 setpen(canvas
, pen
);
2078 DrawPolyline(canvas
, vps
, 3, POLYLINE_OPEN
);
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
);
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);
2093 errmsg("Internal error in draw_arrowhead()");
2100 void draw_region(Canvas
*canvas
, Quark
*q
)
2102 Quark
*f
= get_parent_frame(q
);
2104 region
*r
= region_get_data(q
);
2109 frame_get_view(f
, &v
);
2110 if (terminal_device(canvas
) != TRUE
|| !r
) {
2114 setcolor(canvas
, r
->color
);
2115 setpattern(canvas
, 1);
2118 while (vp
.x
<= v
.xv2
) {
2120 while (vp
.y
<= v
.yv2
) {
2121 if (Vpoint2Wpoint(q
, &vp
, &wp
) == RETURN_SUCCESS
&&
2123 DrawPixel(canvas
, &vp
);
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
;
2146 int draw_line
, singlesym
;
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))) {
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) {
2173 vpf1
.y
= vp1
.y
- 0.01*l
->charsize
;
2175 vpf2
.y
= vp2
.y
+ 0.01*l
->charsize
;
2177 /* TODO: settable option for setfill presentation */
2178 setpen(canvas
, &p
->line
.fillpen
);
2180 FillRect(canvas
, &vpf1
, &vpf2
);
2182 DrawFilledEllipse(canvas
, &vpf1
, &vpf2
);
2184 setpen(canvas
, &p
->line
.line
.pen
);
2186 DrawRect(canvas
, &vpf1
, &vpf2
);
2188 DrawEllipse(canvas
, &vpf1
, &vpf2
);
2191 symvshift
= 0.01*l
->charsize
;
2193 DrawLine(canvas
, &vp1
, &vp2
);
2198 singlesym
= l
->singlesym
;
2203 setline(canvas
, &p
->sym
.line
);
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
);
2213 drawxysym(canvas
, &vp1
, &p
->sym
);
2214 drawxysym(canvas
, &vp2
, &p
->sym
);
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
);
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
) {
2257 void draw_legends(Quark
*q
, plot_rt_t
*plot_rt
)
2259 Canvas
*canvas
= plot_rt
->canvas
;
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
;
2270 leg_rt_t
*leg_entries
;
2272 l
= frame_get_legend(q
);
2273 if (l
->active
== FALSE
) {
2277 setclipping(canvas
, FALSE
);
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
));
2289 for (setno
= 0; setno
< nsets
; setno
++) {
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
;
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
) {
2343 vp
.x
= v
.xv1
+ l
->offset
.x
;
2344 vp
.y
= v
.yv1
+ l
->offset
.y
+ bb_height
;
2347 vp
.x
= v
.xv1
+ l
->offset
.x
;
2348 vp
.y
= v
.yv2
- l
->offset
.y
;
2352 vp
.x
= v
.xv2
- l
->offset
.x
- bb_width
;
2353 vp
.y
= v
.yv2
- l
->offset
.y
;
2356 vp
.x
= v
.xv2
- l
->offset
.x
- bb_width
;
2357 vp
.y
= v
.yv1
+ l
->offset
.y
+ bb_height
;
2361 vp2
.x
= vp
.x
+ bb_width
;
2362 vp2
.y
= vp
.y
- bb_height
;
2370 setpen(canvas
, &l
->boxfillpen
);
2371 FillRect(canvas
, &vp
, &vp2
);
2373 setline(canvas
, &l
->boxline
);
2374 DrawRect(canvas
, &vp
, &vp2
);
2377 vp
.x
+= l
->hgap
+ max_sym_width
/2;
2382 for (i
= 0; i
< nleg_entries
; i
++) {
2383 leg_rt_t
*leg_entry
;
2386 double y_middle
, ascent
, descent
;
2387 VPoint vpsym
, vpstr
;
2389 if (l
->invert
== FALSE
) {
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
);
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
;
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
);