/* * Grace - GRaphing, Advanced Computation and Exploration of data * * Home page: http://plasma-gate.weizmann.ac.il/Grace/ * * Copyright (c) 1991-1995 Paul J Turner, Portland, OR * Copyright (c) 1996-2003 Grace Development Team * * Maintained by Evgeny Stambulchik * * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * * plotone.c - entry for graphics * */ #include #include #include #include #include #include #include "globals.h" #include "utils.h" #include "files.h" #include "graphs.h" #include "draw.h" #include "device.h" #include "plotone.h" #include "protos.h" FILE *prstream; char print_file[GR_MAXPATHLEN] = ""; /* * draw all active graphs */ void drawgraph(void) { int i; VPoint vp1, vp2; Pen pen; int saveg; saveg = get_cg(); if (initgraphics() == RETURN_FAILURE) { errmsg("Device wasn't properly initialized"); return; } setclipping(FALSE); if (getbgfill() == TRUE) { pen.color = getbgcolor(); pen.pattern = 1; setpen(pen); vp1.x = 0.0; vp1.y = 0.0; get_page_viewport(&vp2.x, &vp2.y); FillRect(vp1, vp2); } reset_bboxes(); activate_bbox(BBOX_TYPE_GLOB, TRUE); activate_bbox(BBOX_TYPE_TEMP, FALSE); for (i = 0; i < number_of_graphs(); i++) { plotone(i); } /* draw objects NOT clipped to a particular graph */ draw_objects(-1); draw_timestamp(); if (get_cg() != saveg) { select_graph(saveg); } leavegraphics(); } /* * If writing to a file, check to see if it exists */ void do_hardcopy(void) { char tbuf[128], *s; char fname[GR_MAXPATHLEN]; view v; double vx, vy; int truncated_out; if (get_ptofile()) { if (print_file[0] == '\0') { Device_entry dev = get_device_props(hdevice); sprintf(print_file, "%s.%s", get_docbname(), dev.fext); } strcpy(fname, print_file); } else { s = get_print_cmd(); if (s == NULL || s[0] == '\0') { errmsg("No print command defined, output aborted"); return; } tmpnam(fname); /* VMS doesn't like extensionless files */ strcat(fname, ".prn"); } prstream = grace_openw(fname); if (prstream == NULL) { return; } select_device(hdevice); drawgraph(); grace_close(prstream); v = get_bbox(BBOX_TYPE_GLOB); get_page_viewport(&vx, &vy); if (v.xv1 < 0.0 || v.xv2 > vx || v.yv1 < 0.0 || v.yv2 > vy) { truncated_out = TRUE; } else { truncated_out = FALSE; } if (get_ptofile() == FALSE) { sprintf(tbuf, "%s %s", get_print_cmd(), fname); if (truncated_out == FALSE || yesno("Printout is truncated. Continue?", NULL, NULL, NULL)) { system_wrap(tbuf); } #ifndef PRINT_CMD_UNLINKS unlink(fname); #endif } else { if (truncated_out == TRUE) { errmsg("Output is truncated - tune device dimensions"); } } select_device(tdevice); } void plotone(int gno) { GraphType gtype; if (is_graph_active(gno) != TRUE || is_graph_hidden(gno) == TRUE) { return; } setclipping(TRUE); set_draw_mode(TRUE); if (select_graph(gno) != RETURN_SUCCESS) { return; } /* fill frame */ fillframe(gno); gtype = get_graph_type(gno); if (gtype != GRAPH_PIE) { /* calculate tick mark positions for all axes */ calculate_tickgrid(gno); /* draw grid lines */ drawgrid(gno); } /* plot type specific routines */ switch(gtype) { case GRAPH_POLAR: draw_polar_graph(gno); break; case GRAPH_SMITH: draw_smith_chart(gno); break; case GRAPH_PIE: draw_pie_chart(gno); break; default: xyplot(gno); break; } if (gtype != GRAPH_PIE) { /* plot axes and tickmarks */ drawaxes(gno); } /* plot frame */ drawframe(gno); /* plot objects */ draw_objects(gno); if (gtype != GRAPH_PIE) { /* plot legends */ dolegend(gno); } /* draw title and subtitle */ draw_titles(gno); /* draw regions and mark the reference points only if in interactive mode */ if (terminal_device() == TRUE) { draw_regions(gno); draw_ref_point(gno); } } void draw_smith_chart(int gno) { } void draw_pie_chart(int gno) { int i, setno, nsets = 0; plotarr p; view v; world w; int sgn; VPoint vpc, vp1, vp2, vps[3], vpa; VVector offset; double r, start_angle, stop_angle; double e_max, norm; double *x, *c, *e, *pt; AValue avalue; char str[MAX_STRING_LENGTH]; get_graph_viewport(gno, &v); vpc.x = (v.xv1 + v.xv2)/2; vpc.y = (v.yv1 + v.yv2)/2; get_graph_world(gno, &w); sgn = is_graph_xinvert(gno) ? -1:1; for (setno = 0; setno < number_of_sets(gno); setno++) { if (is_set_drawable(gno, setno)) { nsets++; if (nsets > 1) { errmsg("Only one set per pie chart can be drawn"); return; } switch (dataset_type(gno, setno)) { case SET_XY: case SET_XYCOLOR: case SET_XYCOLPAT: get_graph_plotarr(gno, setno, &p); /* data */ x = getcol(gno, setno, DATA_X); /* explode factor */ e = getcol(gno, setno, DATA_Y); /* colors */ c = getcol(gno, setno, DATA_Y1); /* patterns */ pt = getcol(gno, setno, DATA_Y2); /* get max explode factor */ e_max = 0.0; for (i = 0; i < p.data.len; i++) { e_max = MAX2(e_max, e[i]); } r = 0.8/(1.0 + e_max)*MIN2(v.xv2 - v.xv1, v.yv2 - v.yv1)/2; norm = 0.0; for (i = 0; i < p.data.len; i++) { if (x[i] < 0.0) { errmsg("No negative values in pie charts allowed"); return; } if (e[i] < 0.0) { errmsg("No negative offsets in pie charts allowed"); return; } norm += x[i]; } stop_angle = w.xg1; for (i = 0; i < p.data.len; i++) { Pen pen; start_angle = stop_angle; stop_angle = start_angle + sgn*2*M_PI*x[i]/norm; offset.x = e[i]*r*cos((start_angle + stop_angle)/2.0); offset.y = e[i]*r*sin((start_angle + stop_angle)/2.0); vps[0].x = vpc.x + r*cos(start_angle) + offset.x; vps[0].y = vpc.y + r*sin(start_angle) + offset.y; vps[1].x = vpc.x + offset.x; vps[1].y = vpc.y + offset.y; vps[2].x = vpc.x + r*cos(stop_angle) + offset.x; vps[2].y = vpc.y + r*sin(stop_angle) + offset.y; vp1.x = vpc.x - r + offset.x; vp1.y = vpc.y - r + offset.y; vp2.x = vpc.x + r + offset.x; vp2.y = vpc.y + r + offset.y; if (c != NULL) { pen.color = (int) rint(c[i]); } else { pen.color = p.symfillpen.color; } if (pt != NULL) { pen.pattern = (int) rint(pt[i]); } else { pen.pattern = p.symfillpen.pattern; } setpen(pen); DrawFilledArc(vp1, vp2, (int) rint(180.0/M_PI*start_angle), (int) rint(180.0/M_PI*stop_angle), ARCFILL_PIESLICE); setpen(p.sympen); setlinewidth(p.symlinew); setlinestyle(p.symlines); DrawPolyline(vps, 3, POLYLINE_OPEN); DrawArc(vp1, vp2, (int) rint(180.0/M_PI*start_angle), (int) rint(180.0/M_PI*stop_angle)); avalue = p.avalue; if (avalue.active == TRUE) { vpa.x = vpc.x + ((1 + e[i])*r + avalue.offset.y)* cos((start_angle + stop_angle)/2.0); vpa.y = vpc.y + ((1 + e[i])*r + avalue.offset.y)* sin((start_angle + stop_angle)/2.0); strcpy(str, avalue.prestr); switch(avalue.type) { case AVALUE_TYPE_X: strcat(str, create_fstring(avalue.format, avalue.prec, x[i], LFORMAT_TYPE_EXTENDED)); break; case AVALUE_TYPE_STRING: if (p.data.s != NULL && p.data.s[i] != NULL) { strcat(str, p.data.s[i]); } break; default: continue; } strcat(str, avalue.appstr); setcharsize(avalue.size); setfont(avalue.font); setcolor(avalue.color); WriteString(vpa, avalue.angle, JUST_CENTER|JUST_MIDDLE, str); } } break; default: errmsg("Unsupported in pie chart set type"); break; } } } } void draw_polar_graph(int gno) { int i; plotarr p; for (i = 0; i < number_of_sets(gno); i++) { if (is_set_drawable(gno, i)) { get_graph_plotarr(gno, i, &p); switch (dataset_type(gno, i)) { case SET_XY: case SET_XYSIZE: case SET_XYCOLOR: case SET_XYZ: drawsetline(gno, i, &p, 0, NULL, NULL, 0.0); drawsetsyms(gno, i, &p, 0, NULL, NULL, 0.0); drawsetavalues(gno, i, &p, 0, NULL, NULL, 0.0); break; default: errmsg("Unsupported in polar graph set type"); break; } } } } void xyplot(int gno) { int i, j; plotarr p; int refn; double *refx, *refy; double offset, epsilon; refn = 0; offset = 0.0; refx = NULL; refy = NULL; /* draw sets */ switch (get_graph_type(gno)) { case GRAPH_XY: for (i = 0; i < number_of_sets(gno); i++) { if (is_set_drawable(gno, i)) { get_graph_plotarr(gno, i, &p); switch (dataset_type(gno, i)) { case SET_XY: case SET_XYSIZE: case SET_XYCOLOR: case SET_XYZ: drawsetline(gno, i, &p, 0, NULL, NULL, 0.0); drawsetsyms(gno, i, &p, 0, NULL, NULL, 0.0); drawsetavalues(gno, i, &p, 0, NULL, NULL, 0.0); break; case SET_BAR: drawsetline(gno, i, &p, 0, NULL, NULL, 0.0); drawsetbars(gno, i, &p, 0, NULL, NULL, 0.0); drawsetavalues(gno, i, &p, 0, NULL, NULL, 0.0); break; case SET_BARDY: case SET_BARDYDY: drawsetline(gno, i, &p, refn, refx, refy, offset); drawsetbars(gno, i, &p, refn, refx, refy, offset); drawseterrbars(gno, i, &p, refn, refx, refy, offset); drawsetavalues(gno, i, &p, refn, refx, refy, offset); break; case SET_XYDX: case SET_XYDY: case SET_XYDXDX: case SET_XYDYDY: case SET_XYDXDY: case SET_XYDXDXDYDY: drawsetline(gno, i, &p, 0, NULL, NULL, 0.0); drawseterrbars(gno, i, &p, 0, NULL, NULL, 0.0); drawsetsyms(gno, i, &p, 0, NULL, NULL, 0.0); drawsetavalues(gno, i, &p, 0, NULL, NULL, 0.0); break; case SET_XYHILO: drawsethilo(&p); drawsetavalues(gno, i, &p, 0, NULL, NULL, 0.0); break; case SET_XYVMAP: drawsetline(gno, i, &p, 0, NULL, NULL, 0.0); drawsetvmap(gno, &p); drawsetsyms(gno, i, &p, 0, NULL, NULL, 0.0); drawsetavalues(gno, i, &p, 0, NULL, NULL, 0.0); break; case SET_BOXPLOT: drawsetline(gno, i, &p, 0, NULL, NULL, 0.0); drawsetboxplot(&p); drawsetavalues(gno, i, &p, 0, NULL, NULL, 0.0); break; default: errmsg("Unsupported in XY graph set type"); break; } } } break; case GRAPH_CHART: for (i = 0; i < number_of_sets(gno); i++) { get_graph_plotarr(gno, i, &p); if (is_set_drawable(gno, i)) { if (p.data.len > refn) { refn = p.data.len; refx = p.data.ex[0]; } if (is_graph_stacked(gno) != TRUE) { offset -= 0.5*0.02*p.symsize; } } } offset -= 0.5*(nactive(gno) - 1)*get_graph_bargap(gno); if (is_graph_stacked(gno) == TRUE) { refy = xcalloc(refn, SIZEOF_DOUBLE); if (refy == NULL) { return; } } if (refx) { double xmin, xmax; int imin, imax; minmax(refx, refn, &xmin, &xmax, &imin, &imax); epsilon = 1.0e-3*(xmax - xmin)/refn; } else { epsilon = 0.0; } for (i = 0; i < number_of_sets(gno); i++) { int x_ok; double *x; get_graph_plotarr(gno, i, &p); if (is_set_drawable(gno, i)) { /* check that abscissas are identical with refx */ x = getcol(gno, i, DATA_X); x_ok = TRUE; for (j = 0; j < getsetlength(gno, i); j++) { if (fabs(x[j] - refx[j]) > epsilon) { x_ok = FALSE; break; } } if (x_ok != TRUE) { char buf[128]; sprintf(buf, "Set G%d.S%d has different abscissas, " "skipped from the chart.", gno, i); errmsg(buf); continue; } if (is_graph_stacked(gno) != TRUE) { offset += 0.5*0.02*p.symsize; } switch (dataset_type(gno, i)) { case SET_XY: case SET_XYSIZE: case SET_XYCOLOR: drawsetline(gno, i, &p, refn, refx, refy, offset); if (is_graph_stacked(gno) != TRUE) { drawsetsyms(gno, i, &p, refn, refx, refy, offset); drawsetavalues(gno, i, &p, refn, refx, refy, offset); } break; case SET_BAR: drawsetline(gno, i, &p, refn, refx, refy, offset); drawsetbars(gno, i, &p, refn, refx, refy, offset); if (is_graph_stacked(gno) != TRUE) { drawsetavalues(gno, i, &p, refn, refx, refy, offset); } break; case SET_BARDY: case SET_BARDYDY: drawsetline(gno, i, &p, refn, refx, refy, offset); drawsetbars(gno, i, &p, refn, refx, refy, offset); if (is_graph_stacked(gno) != TRUE) { drawseterrbars(gno, i, &p, refn, refx, refy, offset); drawsetavalues(gno, i, &p, refn, refx, refy, offset); } break; case SET_XYDY: case SET_XYDYDY: drawsetline(gno, i, &p, refn, refx, refy, offset); if (is_graph_stacked(gno) != TRUE) { drawseterrbars(gno, i, &p, refn, refx, refy, offset); drawsetsyms(gno, i, &p, refn, refx, refy, offset); drawsetavalues(gno, i, &p, refn, refx, refy, offset); } break; default: errmsg("Unsupported in XY chart set type"); break; } if (is_graph_stacked(gno) != TRUE) { offset += 0.5*0.02*p.symsize + get_graph_bargap(gno); } else { for (j = 0; j < p.data.len; j++) { refy[j] += p.data.ex[1][j]; } } } } if (is_graph_stacked(gno) == TRUE) { /* Second pass for stacked charts: symbols and avalues */ offset = 0.0; for (j = 0; j < refn; j++) { refy[j] = 0.0; } for (i = 0; i < number_of_sets(gno); i++) { get_graph_plotarr(gno, i, &p); if (is_set_drawable(gno, i)) { switch (dataset_type(gno, i)) { case SET_XY: case SET_XYSIZE: case SET_XYCOLOR: drawsetsyms(gno, i, &p, refn, refx, refy, offset); drawsetavalues(gno, i, &p, refn, refx, refy, offset); break; case SET_BAR: drawsetavalues(gno, i, &p, refn, refx, refy, offset); break; case SET_BARDY: case SET_BARDYDY: drawseterrbars(gno, i, &p, refn, refx, refy, offset); drawsetavalues(gno, i, &p, refn, refx, refy, offset); break; case SET_XYDY: case SET_XYDYDY: drawseterrbars(gno, i, &p, refn, refx, refy, offset); drawsetsyms(gno, i, &p, refn, refx, refy, offset); drawsetavalues(gno, i, &p, refn, refx, refy, offset); break; } for (j = 0; j < p.data.len; j++) { refy[j] += p.data.ex[1][j]; } } } } if (refy != NULL) { xfree(refy); } break; case GRAPH_FIXED: for (i = 0; i < number_of_sets(gno); i++) { if (is_set_drawable(gno, i)) { get_graph_plotarr(gno, i, &p); switch (dataset_type(gno, i)) { case SET_XY: case SET_XYSIZE: case SET_XYCOLOR: case SET_XYZ: drawsetline(gno, i, &p, 0, NULL, NULL, 0.0); drawsetsyms(gno, i, &p, 0, NULL, NULL, 0.0); drawsetavalues(gno, i, &p, 0, NULL, NULL, 0.0); break; case SET_XYDX: case SET_XYDY: case SET_XYDXDX: case SET_XYDYDY: case SET_XYDXDY: case SET_XYDXDXDYDY: drawsetline(gno, i, &p, 0, NULL, NULL, 0.0); drawseterrbars(gno, i, &p, 0, NULL, NULL, 0.0); drawsetsyms(gno, i, &p, 0, NULL, NULL, 0.0); drawsetavalues(gno, i, &p, 0, NULL, NULL, 0.0); break; case SET_XYR: drawcirclexy(&p); drawsetsyms(gno, i, &p, 0, NULL, NULL, 0.0); drawsetavalues(gno, i, &p, 0, NULL, NULL, 0.0); break; case SET_XYVMAP: drawsetline(gno, i, &p, 0, NULL, NULL, 0.0); drawsetvmap(gno, &p); drawsetsyms(gno, i, &p, 0, NULL, NULL, 0.0); drawsetavalues(gno, i, &p, 0, NULL, NULL, 0.0); break; default: errmsg("Unsupported in XY graph set type"); break; } } } break; } /* end g.type */ } void draw_regions(int gno) { int i; setclipping(TRUE); /* draw any defined regions for this graph */ for (i = 0; i < MAXREGION; i++) { if (rg[i].active && rg[i].linkto == gno) { setcolor(rg[i].color); setpattern(1); setlinewidth(rg[i].linew); setlinestyle(rg[i].lines); draw_region(i); } } } void draw_ref_point(int gno) { GLocator locator; WPoint wp; VPoint vp; if (is_refpoint_active(gno)) { get_graph_locator(gno, &locator); wp.x = locator.dsx; wp.y = locator.dsy; vp = Wpoint2Vpoint(wp); setcolor(1); setpattern(1); setlinewidth(1.0); setlinestyle(1); symplus(vp, 0.01); DrawCircle (vp, 0.01); } } /* draw title and subtitle */ void draw_titles(int gno) { view v; labels lab; VPoint vp1, vp2; get_graph_viewport(gno, &v); get_graph_labels(gno, &lab); vp1.x = (v.xv2 + v.xv1) / 2; vp1.y = (v.yv2 < v.yv1)? v.yv1 : v.yv2; vp2 = vp1; if (lab.title.s && lab.title.s[0]) { setcolor(lab.title.color); setcharsize(lab.title.charsize); setfont(lab.title.font); vp1.y += 0.06; WriteString(vp1, 0, JUST_CENTER|JUST_BOTTOM, lab.title.s); } if (lab.stitle.s && lab.stitle.s[0]) { setcolor(lab.stitle.color); setcharsize(lab.stitle.charsize); setfont(lab.stitle.font); vp2.y += 0.02; WriteString(vp2, 0, JUST_CENTER|JUST_BOTTOM, lab.stitle.s); } } /* * draw the graph frame */ void drawframe(int gno) { view v; framep f; VPoint vps[4]; get_graph_viewport(gno, &v); get_graph_framep(gno, &f); setpen(f.pen); setlinewidth(f.linew); setlinestyle(f.lines); switch (f.type) { case 0: vps[0].x = v.xv1; vps[0].y = v.yv1; vps[1].x = v.xv2; vps[1].y = v.yv2; DrawRect(vps[0], vps[1]); break; case 1: /* half open */ vps[0].x = v.xv1; vps[0].y = v.yv2; vps[1].x = v.xv1; vps[1].y = v.yv1; vps[2].x = v.xv2; vps[2].y = v.yv1; DrawPolyline(vps, 3, POLYLINE_OPEN); break; case 2: /* break top */ vps[0].x = v.xv1; vps[0].y = v.yv2; vps[1].x = v.xv1; vps[1].y = v.yv1; vps[2].x = v.xv2; vps[2].y = v.yv1; vps[3].x = v.xv2; vps[3].y = v.yv2; DrawPolyline(vps, 4, POLYLINE_OPEN); break; case 3: /* break bottom */ vps[0].x = v.xv1; vps[0].y = v.yv1; vps[1].x = v.xv1; vps[1].y = v.yv2; vps[2].x = v.xv2; vps[2].y = v.yv2; vps[3].x = v.xv2; vps[3].y = v.yv1; DrawPolyline(vps, 4, POLYLINE_OPEN); break; case 4: /* break left */ vps[0].x = v.xv1; vps[0].y = v.yv1; vps[1].x = v.xv2; vps[1].y = v.yv1; vps[2].x = v.xv2; vps[2].y = v.yv2; vps[3].x = v.xv1; vps[3].y = v.yv2; DrawPolyline(vps, 4, POLYLINE_OPEN); break; case 5: /* break right */ vps[0].x = v.xv2; vps[0].y = v.yv1; vps[1].x = v.xv1; vps[1].y = v.yv1; vps[2].x = v.xv1; vps[2].y = v.yv2; vps[3].x = v.xv2; vps[3].y = v.yv2; DrawPolyline(vps, 4, POLYLINE_OPEN); break; } } void fillframe(int gno) { view v; framep f; VPoint vp1, vp2; get_graph_viewport(gno, &v); get_graph_framep(gno, &f); /* fill coordinate frame with background color */ if (f.fillpen.pattern != 0) { setpen(f.fillpen); vp1.x = v.xv1; vp1.y = v.yv1; vp2.x = v.xv2; vp2.y = v.yv2; FillRect(vp1, vp2); } } /* * draw a set filling polygon */ void drawsetfill(int gno, int setno, plotarr *p, int refn, double *refx, double *refy, double offset) { int i, len, setlen, polylen; int line_type = p->linet; double *x, *y; double ybase; world w; WPoint wptmp; VPoint *vps; double xmin, xmax, ymin, ymax; int stacked_chart; if (p->filltype == SETFILL_NONE) { return; } if (get_graph_type(gno) == GRAPH_CHART) { x = refx; setlen = MIN2(p->data.len, refn); } else { x = p->data.ex[0]; setlen = p->data.len; } y = p->data.ex[1]; if (get_graph_type(gno) == GRAPH_CHART && is_graph_stacked(gno) == TRUE) { stacked_chart = TRUE; } else { stacked_chart = FALSE; } setclipping(TRUE); get_graph_world(gno, &w); switch (line_type) { case LINE_TYPE_STRAIGHT: case LINE_TYPE_SEGMENT2: case LINE_TYPE_SEGMENT3: if (stacked_chart == TRUE && p->filltype == SETFILL_BASELINE) { len = 2*setlen; } else { len = setlen; } vps = (VPoint *) xmalloc((len + 2) * sizeof(VPoint)); if (vps == NULL) { errmsg("Can't xmalloc in drawsetfill"); return; } for (i = 0; i < setlen; i++) { wptmp.x = x[i]; wptmp.y = y[i]; if (stacked_chart == TRUE) { wptmp.y += refy[i]; } vps[i] = Wpoint2Vpoint(wptmp); vps[i].x += offset; } if (stacked_chart == TRUE && p->filltype == SETFILL_BASELINE) { for (i = 0; i < setlen; i++) { wptmp.x = x[setlen - i - 1]; wptmp.y = refy[setlen - i - 1]; vps[setlen + i] = Wpoint2Vpoint(wptmp); vps[setlen + i].x += offset; } } break; case LINE_TYPE_LEFTSTAIR: case LINE_TYPE_RIGHTSTAIR: len = 2*setlen - 1; vps = (VPoint *) xmalloc((len + 2) * sizeof(VPoint)); if (vps == NULL) { errmsg("Can't xmalloc in drawsetfill"); return; } for (i = 0; i < setlen; i++) { wptmp.x = x[i]; wptmp.y = y[i]; if (stacked_chart == TRUE) { wptmp.y += refy[i]; } vps[2*i] = Wpoint2Vpoint(wptmp); vps[2*i].x += offset; } for (i = 1; i < len; i += 2) { if (line_type == LINE_TYPE_LEFTSTAIR) { vps[i].x = vps[i - 1].x; vps[i].y = vps[i + 1].y; } else { vps[i].x = vps[i + 1].x; vps[i].y = vps[i - 1].y; } } break; default: return; } switch (p->filltype) { case SETFILL_POLYGON: polylen = len; break; case SETFILL_BASELINE: if (stacked_chart == TRUE) { polylen = len; } else { getsetminmax(gno, setno, &xmin, &xmax, &ymin, &ymax); ybase = setybase(gno, setno); polylen = len + 2; wptmp.x = MIN2(xmax, w.xg2); wptmp.y = ybase; vps[len] = Wpoint2Vpoint(wptmp); vps[len].x += offset; wptmp.x = MAX2(xmin, w.xg1); wptmp.y = ybase; vps[len + 1] = Wpoint2Vpoint(wptmp); vps[len + 1].x += offset; } break; default: xfree(vps); return; } setpen(p->setfillpen); setfillrule(p->fillrule); DrawPolygon(vps, polylen); xfree(vps); } /* * draw set's connecting line */ void drawsetline(int gno, int setno, plotarr *p, int refn, double *refx, double *refy, double offset) { int setlen, len; int i, ly = p->lines; int line_type = p->linet; VPoint vps[4], *vpstmp; WPoint wp; double *x, *y; double lw; double ybase; double xmin, xmax, ymin, ymax; int stacked_chart; if (get_graph_type(gno) == GRAPH_CHART) { x = refx; setlen = MIN2(p->data.len, refn); } else { x = p->data.ex[0]; setlen = p->data.len; } y = p->data.ex[1]; if (get_graph_type(gno) == GRAPH_CHART && is_graph_stacked(gno) == TRUE) { stacked_chart = TRUE; } else { stacked_chart = FALSE; } if (stacked_chart == TRUE) { ybase = 0.0; } else { ybase = setybase(gno, setno); } setclipping(TRUE); drawsetfill(gno, setno, p, refn, refx, refy, offset); setpen(p->linepen); setlinewidth(p->linew); setlinestyle(ly); if (stacked_chart == TRUE) { lw = getlinewidth(); } else { lw = 0.0; } /* draw the line */ if (ly != 0 && p->linepen.pattern != 0) { switch (line_type) { case LINE_TYPE_NONE: break; case LINE_TYPE_STRAIGHT: vpstmp = (VPoint *) xmalloc(setlen*sizeof(VPoint)); if (vpstmp == NULL) { errmsg("xmalloc failed in drawsetline()"); break; } for (i = 0; i < setlen; i++) { wp.x = x[i]; wp.y = y[i]; if (stacked_chart == TRUE) { wp.y += refy[i]; } vpstmp[i] = Wpoint2Vpoint(wp); vpstmp[i].x += offset; vpstmp[i].y -= lw/2.0; } DrawPolyline(vpstmp, setlen, POLYLINE_OPEN); xfree(vpstmp); break; case LINE_TYPE_SEGMENT2: for (i = 0; i < setlen - 1; i += 2) { wp.x = x[i]; wp.y = y[i]; if (stacked_chart == TRUE) { wp.y += refy[i]; } vps[0] = Wpoint2Vpoint(wp); vps[0].x += offset; wp.x = x[i + 1]; wp.y = y[i + 1]; if (stacked_chart == TRUE) { wp.y += refy[i + 1]; } vps[1] = Wpoint2Vpoint(wp); vps[1].x += offset; vps[0].y -= lw/2.0; vps[1].y -= lw/2.0; DrawLine(vps[0], vps[1]); } break; case LINE_TYPE_SEGMENT3: for (i = 0; i < setlen - 2; i += 3) { wp.x = x[i]; wp.y = y[i]; if (stacked_chart == TRUE) { wp.y += refy[i]; } vps[0] = Wpoint2Vpoint(wp); vps[0].x += offset; wp.x = x[i + 1]; wp.y = y[i + 1]; if (stacked_chart == TRUE) { wp.y += refy[i + 1]; } vps[1] = Wpoint2Vpoint(wp); vps[1].x += offset; wp.x = x[i + 2]; wp.y = y[i + 2]; if (stacked_chart == TRUE) { wp.y += refy[i + 2]; } vps[2] = Wpoint2Vpoint(wp); vps[2].x += offset; DrawPolyline(vps, 3, POLYLINE_OPEN); vps[0].y -= lw/2.0; vps[1].y -= lw/2.0; vps[2].y -= lw/2.0; } if (i == setlen - 2) { wp.x = x[i]; wp.y = y[i]; if (stacked_chart == TRUE) { wp.y += refy[i]; } vps[0] = Wpoint2Vpoint(wp); vps[0].x += offset; wp.x = x[i + 1]; wp.y = y[i + 1]; if (stacked_chart == TRUE) { wp.y += refy[i + 1]; } vps[1] = Wpoint2Vpoint(wp); vps[1].x += offset; vps[0].y -= lw/2.0; vps[1].y -= lw/2.0; DrawLine(vps[0], vps[1]); } break; case LINE_TYPE_LEFTSTAIR: case LINE_TYPE_RIGHTSTAIR: len = 2*setlen - 1; vpstmp = (VPoint *) xmalloc(len*sizeof(VPoint)); if (vpstmp == NULL) { errmsg("xmalloc failed in drawsetline()"); break; } for (i = 0; i < setlen; i++) { wp.x = x[i]; wp.y = y[i]; if (stacked_chart == TRUE) { wp.y += refy[i]; } vpstmp[2*i] = Wpoint2Vpoint(wp); vpstmp[2*i].x += offset; } for (i = 1; i < len; i += 2) { if (line_type == LINE_TYPE_LEFTSTAIR) { vpstmp[i].x = vpstmp[i - 1].x; vpstmp[i].y = vpstmp[i + 1].y; } else { vpstmp[i].x = vpstmp[i + 1].x; vpstmp[i].y = vpstmp[i - 1].y; } } DrawPolyline(vpstmp, len, POLYLINE_OPEN); xfree(vpstmp); break; default: errmsg("Invalid line type"); break; } } if (p->dropline == TRUE) { for (i = 0; i < setlen; i ++) { wp.x = x[i]; if (stacked_chart == TRUE) { wp.y = refy[i]; } else { wp.y = ybase; } vps[0] = Wpoint2Vpoint(wp); vps[0].x += offset; wp.x = x[i]; wp.y = y[i]; if (stacked_chart == TRUE) { wp.y += refy[i]; } vps[1] = Wpoint2Vpoint(wp); vps[1].x += offset; vps[1].y -= lw/2.0; DrawLine(vps[0], vps[1]); } } getsetminmax(gno, setno, &xmin, &xmax, &ymin, &ymax); if (p->baseline == TRUE && stacked_chart != TRUE) { wp.x = xmin; wp.y = ybase; vps[0] = Wpoint2Vpoint(wp); vps[0].x += offset; wp.x = xmax; vps[1] = Wpoint2Vpoint(wp); vps[1].x += offset; DrawLine(vps[0], vps[1]); } } /* draw the symbols */ void drawsetsyms(int gno, int setno, plotarr *p, int refn, double *refx, double *refy, double offset) { int setlen; int i, sy = p->sym; double symsize; VPoint vp; WPoint wp; double *x, *y, *z, *c; int skip = p->symskip + 1; int stacked_chart; double znorm = get_graph_znorm(gno); if (get_graph_type(gno) == GRAPH_CHART) { x = refx; setlen = MIN2(p->data.len, refn); } else { x = p->data.ex[0]; setlen = p->data.len; } y = p->data.ex[1]; if (get_graph_type(gno) == GRAPH_CHART && is_graph_stacked(gno) == TRUE) { stacked_chart = TRUE; } else { stacked_chart = FALSE; } if (p->type == SET_XYSIZE) { if (znorm == 0.0) { return; } z = p->data.ex[2]; } else { z = NULL; } if (p->type == SET_XYCOLOR) { c = p->data.ex[2]; } else { c = NULL; } setclipping(FALSE); if ((p->sympen.pattern != 0 && p->symlines != 0) || (p->symfillpen.pattern != 0)) { Pen fillpen; setlinewidth(p->symlinew); setlinestyle(p->symlines); setfont(p->charfont); for (i = 0; i < setlen; i += skip) { wp.x = x[i]; wp.y = y[i]; if (stacked_chart == TRUE) { wp.y += refy[i]; } if (!is_validWPoint(wp)){ continue; } vp = Wpoint2Vpoint(wp); vp.x += offset; if (z) { symsize = z[i]/znorm; } else { symsize = p->symsize; } if (c) { fillpen.color = (int) rint(c[i]); if (get_colortype(fillpen.color) != COLOR_MAIN) { fillpen.color = 1; } } else { fillpen.color = p->symfillpen.color; } fillpen.pattern = p->symfillpen.pattern; if (drawxysym(vp, symsize, sy, p->sympen, fillpen, p->symchar) != RETURN_SUCCESS) { return; } } } } /* draw the annotative values */ void drawsetavalues(int gno, int setno, plotarr *p, int refn, double *refx, double *refy, double offset) { int i; int setlen; double *x, *y, *z; WPoint wp; VPoint vp; int skip = p->symskip + 1; AValue avalue; char str[MAX_STRING_LENGTH]; int stacked_chart; avalue = p->avalue; if (avalue.active != TRUE) { return; } if (get_graph_type(gno) == GRAPH_CHART) { x = refx; setlen = MIN2(p->data.len, refn); } else { x = p->data.ex[0]; setlen = p->data.len; } y = p->data.ex[1]; if (dataset_cols(gno, setno) > 2) { z = p->data.ex[2]; } else { z = NULL; } if (get_graph_type(gno) == GRAPH_CHART && is_graph_stacked(gno) == TRUE) { stacked_chart = TRUE; } else { stacked_chart = FALSE; } setcharsize(avalue.size); setfont(avalue.font); for (i = 0; i < setlen; i += skip) { wp.x = x[i]; wp.y = y[i]; if (stacked_chart == TRUE) { wp.y += refy[i]; } if (!is_validWPoint(wp)){ continue; } vp = Wpoint2Vpoint(wp); vp.x += avalue.offset.x; vp.y += avalue.offset.y; vp.x += offset; strcpy(str, avalue.prestr); switch(avalue.type) { case AVALUE_TYPE_NONE: break; case AVALUE_TYPE_X: strcat(str, create_fstring(avalue.format, avalue.prec, wp.x, LFORMAT_TYPE_EXTENDED)); break; case AVALUE_TYPE_Y: strcat(str, create_fstring(avalue.format, avalue.prec, wp.y, LFORMAT_TYPE_EXTENDED)); break; case AVALUE_TYPE_XY: strcat(str, create_fstring(avalue.format, avalue.prec, wp.x, LFORMAT_TYPE_EXTENDED)); strcat(str, ", "); strcat(str, create_fstring(avalue.format, avalue.prec, wp.y, LFORMAT_TYPE_EXTENDED)); break; case AVALUE_TYPE_STRING: if (p->data.s != NULL && p->data.s[i] != NULL) { strcat(str, p->data.s[i]); } break; case AVALUE_TYPE_Z: if (z != NULL) { strcat(str, create_fstring(avalue.format, avalue.prec, z[i], LFORMAT_TYPE_EXTENDED)); } break; default: errmsg("Invalid type of ann. value"); return; } strcat(str, avalue.appstr); setcolor(avalue.color); WriteString(vp, avalue.angle, JUST_CENTER|JUST_BOTTOM, str); } } void drawseterrbars(int gno, int setno, plotarr *p, int refn, double *refx, double *refy, double offset) { int i, n; double *x, *y; double *dx_plus, *dx_minus, *dy_plus, *dy_minus, *dtmp; PlacementType ptype = p->errbar.ptype; WPoint wp1, wp2; VPoint vp1, vp2; int stacked_chart; int skip = p->symskip + 1; if (p->errbar.active != TRUE) { return; } if (get_graph_type(gno) == GRAPH_CHART) { x = refx; n = MIN2(p->data.len, refn); } else { x = p->data.ex[0]; n = p->data.len; } y = p->data.ex[1]; if (get_graph_type(gno) == GRAPH_CHART && is_graph_stacked(gno) == TRUE) { stacked_chart = TRUE; } else { stacked_chart = FALSE; } dx_plus = NULL; dx_minus = NULL; dy_plus = NULL; dy_minus = NULL; switch (p->type) { case SET_XYDX: dx_plus = p->data.ex[2]; break; case SET_XYDY: case SET_BARDY: dy_plus = p->data.ex[2]; break; case SET_XYDXDX: dx_plus = p->data.ex[2]; dx_minus = p->data.ex[3]; break; case SET_XYDYDY: case SET_BARDYDY: dy_plus = p->data.ex[2]; dy_minus = p->data.ex[3]; break; case SET_XYDXDY: dx_plus = p->data.ex[2]; dy_plus = p->data.ex[3]; break; case SET_XYDXDXDYDY: dx_plus = p->data.ex[2]; dx_minus = p->data.ex[3]; dy_plus = p->data.ex[4]; dy_minus = p->data.ex[5]; break; default: return; } switch (ptype) { case PLACEMENT_OPPOSITE: dtmp = dx_minus; dx_minus = dx_plus; dx_plus = dtmp; dtmp = dy_minus; dy_minus = dy_plus; dy_plus = dtmp; break; case PLACEMENT_BOTH: if (dx_minus == NULL && dy_minus == NULL) { dx_minus = dx_plus; dy_minus = dy_plus; } break; default: break; } setclipping(TRUE); for (i = 0; i < n; i += skip) { wp1.x = x[i]; wp1.y = y[i]; if (stacked_chart == TRUE) { wp1.y += refy[i]; } if (is_validWPoint(wp1) == FALSE) { continue; } vp1 = Wpoint2Vpoint(wp1); vp1.x += offset; if (dx_plus != NULL) { wp2 = wp1; wp2.x += fabs(dx_plus[i]); vp2 = Wpoint2Vpoint(wp2); vp2.x += offset; drawerrorbar(vp1, vp2, &p->errbar); } if (dx_minus != NULL) { wp2 = wp1; wp2.x -= fabs(dx_minus[i]); vp2 = Wpoint2Vpoint(wp2); vp2.x += offset; drawerrorbar(vp1, vp2, &p->errbar); } if (dy_plus != NULL) { wp2 = wp1; wp2.y += fabs(dy_plus[i]); vp2 = Wpoint2Vpoint(wp2); vp2.x += offset; drawerrorbar(vp1, vp2, &p->errbar); } if (dy_minus != NULL) { wp2 = wp1; wp2.y -= fabs(dy_minus[i]); vp2 = Wpoint2Vpoint(wp2); vp2.x += offset; drawerrorbar(vp1, vp2, &p->errbar); } } } /* * draw hi/lo-open/close */ void drawsethilo(plotarr *p) { int i; double *x = p->data.ex[0], *y1 = p->data.ex[1]; double *y2 = p->data.ex[2], *y3 = p->data.ex[3], *y4 = p->data.ex[4]; double ilen = 0.02*p->symsize; int skip = p->symskip + 1; WPoint wp; VPoint vp1, vp2; if (p->symlines != 0) { setpen(p->sympen); setlinewidth(p->symlinew); setlinestyle(p->symlines); for (i = 0; i < p->data.len; i += skip) { wp.x = x[i]; wp.y = y1[i]; vp1 = Wpoint2Vpoint(wp); wp.y = y2[i]; vp2 = Wpoint2Vpoint(wp); DrawLine(vp1, vp2); wp.y = y3[i]; vp1 = Wpoint2Vpoint(wp); vp2 = vp1; vp2.x -= ilen; DrawLine(vp1, vp2); wp.y = y4[i]; vp1 = Wpoint2Vpoint(wp); vp2 = vp1; vp2.x += ilen; DrawLine(vp1, vp2); } } } /* * draw 2D bars */ void drawsetbars(int gno, int setno, plotarr *p, int refn, double *refx, double *refy, double offset) { int i, n; double *x, *y; double lw, bw = 0.01*p->symsize; int skip = p->symskip + 1; double ybase; WPoint wp; VPoint vp1, vp2; int stacked_chart; if (get_graph_type(gno) == GRAPH_CHART) { x = refx; n = MIN2(p->data.len, refn); } else { x = p->data.ex[0]; n = p->data.len; } y = p->data.ex[1]; if (get_graph_type(gno) == GRAPH_CHART && is_graph_stacked(gno) == TRUE) { stacked_chart = TRUE; } else { stacked_chart = FALSE; } if (stacked_chart == TRUE) { ybase = 0.0; } else { ybase = setybase(gno, setno); } setlinewidth(p->symlinew); setlinestyle(p->symlines); if (get_graph_type(gno) == GRAPH_CHART && p->symlines != 0 && p->sympen.pattern != 0) { lw = getlinewidth(); } else { lw = 0.0; } if (p->symfillpen.pattern != 0) { setpen(p->symfillpen); for (i = 0; i < n; i += skip) { wp.x = x[i]; if (stacked_chart == TRUE) { wp.y = refy[i]; } else { wp.y = ybase; } vp1 = Wpoint2Vpoint(wp); vp1.x -= bw; vp1.x += offset; wp.x = x[i]; if (stacked_chart == TRUE) { wp.y += y[i]; } else { wp.y = y[i]; } vp2 = Wpoint2Vpoint(wp); vp2.x += bw; vp2.x += offset; vp1.x += lw/2.0; vp2.x -= lw/2.0; vp1.y += lw/2.0; FillRect(vp1, vp2); } } if (p->symlines != 0 && p->sympen.pattern != 0) { setpen(p->sympen); for (i = 0; i < n; i += skip) { wp.x = x[i]; if (stacked_chart == TRUE) { wp.y = refy[i]; } else { wp.y = ybase; } vp1 = Wpoint2Vpoint(wp); vp1.x -= bw; vp1.x += offset; wp.x = x[i]; if (stacked_chart == TRUE) { wp.y += y[i]; } else { wp.y = y[i]; } vp2 = Wpoint2Vpoint(wp); vp2.x += bw; vp2.x += offset; vp1.x += lw/2.0; vp2.x -= lw/2.0; vp1.y += lw/2.0; DrawRect(vp1, vp2); } } } void drawcirclexy(plotarr *p) { int i, setlen; double *x, *y, *r; int skip = p->symskip + 1; WPoint wp; VPoint vp1, vp2; setclipping(TRUE); setlen = p->data.len; x = p->data.ex[0]; y = p->data.ex[1]; r = p->data.ex[2]; setfillrule(p->fillrule); setlinewidth(p->linew); setlinestyle(p->lines); for (i = 0; i < setlen; i += skip) { wp.x = x[i]; wp.y = y[i]; /* TODO: remove once ellipse clipping works */ if (!is_validWPoint(wp)){ continue; } wp.x = x[i] - r[i]; wp.y = y[i] - r[i]; vp1 = Wpoint2Vpoint(wp); wp.x = x[i] + r[i]; wp.y = y[i] + r[i]; vp2 = Wpoint2Vpoint(wp); if (p->filltype != SETFILL_NONE) { setpen(p->setfillpen); DrawFilledEllipse(vp1, vp2); } setpen(p->linepen); DrawEllipse(vp1, vp2); } } /* Arrows for vector map plots */ void drawsetvmap(int gno, plotarr *p) { int i, setlen; double znorm = get_graph_znorm(gno); int skip = p->symskip + 1; double *x, *y, *vx, *vy; WPoint wp; VPoint vp1, vp2; Arrow arrow = {0, 1.0, 1.0, 0.0}; Errbar eb = p->errbar; setclipping(TRUE); if (znorm == 0.0) { return; } setlen = p->data.len; x = p->data.ex[DATA_X]; y = p->data.ex[DATA_Y]; vx = p->data.ex[DATA_Y1]; vy = p->data.ex[DATA_Y2]; arrow.length = 2*eb.barsize; setpen(p->errbar.pen); for (i = 0; i < setlen; i += skip) { wp.x = x[i]; wp.y = y[i]; if (!is_validWPoint(wp)){ continue; } vp1 = Wpoint2Vpoint(wp); vp2.x = vp1.x + vx[i]/znorm; vp2.y = vp1.y + vy[i]/znorm; setlinewidth(eb.riser_linew); setlinestyle(eb.riser_lines); DrawLine(vp1, vp2); setlinewidth(eb.linew); setlinestyle(eb.lines); draw_arrowhead(vp1, vp2, &arrow); } } void drawsetboxplot(plotarr *p) { int i; double *x, *md, *lb, *ub, *lw, *uw; double size = 0.01*p->symsize; int skip = p->symskip + 1; WPoint wp; VPoint vp1, vp2; x = p->data.ex[0]; md = p->data.ex[1]; lb = p->data.ex[2]; ub = p->data.ex[3]; lw = p->data.ex[4]; uw = p->data.ex[5]; setclipping(TRUE); for (i = 0; i < p->data.len; i += skip) { wp.x = x[i]; wp.y = lb[i]; vp1 = Wpoint2Vpoint(wp); wp.y = ub[i]; vp2 = Wpoint2Vpoint(wp); /* whiskers */ if (p->errbar.active == TRUE) { VPoint vp3; wp.y = lw[i]; vp3 = Wpoint2Vpoint(wp); drawerrorbar(vp1, vp3, &p->errbar); wp.y = uw[i]; vp3 = Wpoint2Vpoint(wp); drawerrorbar(vp2, vp3, &p->errbar); } /* box */ vp1.x -= size; vp2.x += size; setpen(p->symfillpen); FillRect(vp1, vp2); setpen(p->sympen); setlinewidth(p->symlinew); setlinestyle(p->symlines); DrawRect(vp1, vp2); /* median line */ wp.y = md[i]; vp2 = vp1 = Wpoint2Vpoint(wp); vp1.x -= size; vp2.x += size; DrawLine(vp1, vp2); } } int drawxysym(VPoint vp, double size, int symtype, Pen sympen, Pen symfillpen, char s) { double symsize; VPoint vps[4]; char buf[2]; symsize = size*0.01; switch (symtype) { case SYM_NONE: break; case SYM_CIRCLE: setpen(symfillpen); DrawFilledCircle (vp, symsize); setpen(sympen); DrawCircle (vp, symsize); break; case SYM_SQUARE: symsize *= 0.85; vps[0].x = vp.x - symsize; vps[0].y = vp.y - symsize; vps[1].x = vps[0].x; vps[1].y = vp.y + symsize; vps[2].x = vp.x + symsize; vps[2].y = vps[1].y; vps[3].x = vps[2].x; vps[3].y = vps[0].y; setpen(symfillpen); DrawPolygon (vps, 4); setpen(sympen); DrawPolyline (vps, 4, POLYLINE_CLOSED); break; case SYM_DIAMOND: vps[0].x = vp.x; vps[0].y = vp.y + symsize; vps[1].x = vp.x - symsize; vps[1].y = vp.y; vps[2].x = vps[0].x; vps[2].y = vp.y - symsize; vps[3].x = vp.x + symsize; vps[3].y = vps[1].y; setpen(symfillpen); DrawPolygon (vps, 4); setpen(sympen); DrawPolyline (vps, 4, POLYLINE_CLOSED); break; case SYM_TRIANG1: vps[0].x = vp.x; vps[0].y = vp.y + 2*M_SQRT1_3*symsize; vps[1].x = vp.x - symsize; vps[1].y = vp.y - M_SQRT1_3*symsize; vps[2].x = vp.x + symsize; vps[2].y = vps[1].y; setpen(symfillpen); DrawPolygon (vps, 3); setpen(sympen); DrawPolyline (vps, 3, POLYLINE_CLOSED); break; case SYM_TRIANG2: vps[0].x = vp.x - 2*M_SQRT1_3*symsize; vps[0].y = vp.y; vps[1].x = vp.x + M_SQRT1_3*symsize; vps[1].y = vp.y - symsize; vps[2].x = vps[1].x; vps[2].y = vp.y + symsize; setpen(symfillpen); DrawPolygon (vps, 3); setpen(sympen); DrawPolyline (vps, 3, POLYLINE_CLOSED); break; case SYM_TRIANG3: vps[0].x = vp.x - symsize; vps[0].y = vp.y + M_SQRT1_3*symsize; vps[1].x = vp.x; vps[1].y = vp.y - 2*M_SQRT1_3*symsize; vps[2].x = vp.x + symsize; vps[2].y = vps[0].y; setpen(symfillpen); DrawPolygon (vps, 3); setpen(sympen); DrawPolyline (vps, 3, POLYLINE_CLOSED); break; case SYM_TRIANG4: vps[0].x = vp.x - M_SQRT1_3*symsize; vps[0].y = vp.y + symsize; vps[1].x = vps[0].x; vps[1].y = vp.y - symsize; vps[2].x = vp.x + 2*M_SQRT1_3*symsize; vps[2].y = vp.y; setpen(symfillpen); DrawPolygon (vps, 3); setpen(sympen); DrawPolyline (vps, 3, POLYLINE_CLOSED); break; case SYM_PLUS: setpen(sympen); symplus(vp, symsize); break; case SYM_X: setpen(sympen); symx(vp, symsize); break; case SYM_SPLAT: setpen(sympen); symsplat(vp, symsize); break; case SYM_CHAR: setcolor(sympen.color); buf[0] = s; buf[1] = '\0'; setcharsize(size); WriteString(vp, 0, JUST_CENTER|JUST_MIDDLE, buf); break; default: errmsg("Invalid symbol type"); return RETURN_FAILURE; } return RETURN_SUCCESS; } static void drawlegbarsym(VPoint vp, double size, Pen sympen, Pen symfillpen) { double width, height; VPoint vps[4]; width = 0.02*size; height = 0.02*getcharsize(); vps[0].x = vps[1].x = vp.x - width/2; vps[2].x = vps[3].x = vp.x + width/2; vps[0].y = vps[3].y = vp.y - height/2; vps[1].y = vps[2].y = vp.y + height/2; setpen(symfillpen); DrawPolygon (vps, 4); setpen(sympen); DrawPolyline (vps, 4, POLYLINE_CLOSED); } void drawerrorbar(VPoint vp1, VPoint vp2, Errbar *eb) { double ilen; VPoint vp_plus, vp_minus; VVector lvv; double vlength; static Arrow arrow = {0, 1.0, 1.0, 0.0}; lvv.x = vp2.x - vp1.x; lvv.y = vp2.y - vp1.y; vlength = hypot(lvv.x, lvv.y); if (vlength == 0.0) { return; } lvv.x /= vlength; lvv.y /= vlength; setpen(eb->pen); if (eb->arrow_clip && is_validVPoint(vp2) == FALSE) { vp2.x = vp1.x + eb->cliplen*lvv.x; vp2.y = vp1.y + eb->cliplen*lvv.y; setlinewidth(eb->riser_linew); setlinestyle(eb->riser_lines); DrawLine(vp1, vp2); arrow.length = 2*eb->barsize; setlinewidth(eb->linew); setlinestyle(eb->lines); draw_arrowhead(vp1, vp2, &arrow); } else { setlinewidth(eb->riser_linew); setlinestyle(eb->riser_lines); DrawLine(vp1, vp2); setlinewidth(eb->linew); setlinestyle(eb->lines); ilen = 0.01*eb->barsize; vp_minus.x = vp2.x - ilen*lvv.y; vp_minus.y = vp2.y + ilen*lvv.x; vp_plus.x = vp2.x + ilen*lvv.y; vp_plus.y = vp2.y - ilen*lvv.x; DrawLine(vp_minus, vp_plus); } } /* --------------------------------------------------------------- */ /* Objects ... TODO: move to draw.c or separate file */ void draw_objects(int gno) { int i; setclipping(FALSE); /* shut down clipping for strings, boxes, * lines, and legends */ /* Temporarily; pattern property should be part of object props */ setpattern(1); for (i = 0; i < number_of_boxes(); i++) { if (isactive_box(i)) { draw_box(gno, i); } } for (i = 0; i < number_of_ellipses(); i++) { if (isactive_ellipse(i)) { draw_ellipse(gno, i); } } for (i = 0; i < number_of_lines(); i++) { if (isactive_line(i)) { draw_line(gno, i); } } for (i = 0; i < number_of_strings(); i++) { if (isactive_string(i)) { draw_string(gno, i); } } setclipping(TRUE); } /* * draw annotative text */ void draw_string(int gno, int i) { plotstr pstr; VPoint vp; WPoint wptmp; get_graph_string(i, &pstr); if (gno != -2) { if (pstr.loctype == COORD_WORLD && pstr.gno != gno) { return; } if (pstr.loctype == COORD_VIEW && gno != -1) { return; } } if (strlen(pstr.s) && (pstr.charsize > 0.0) && pstr.active) { if (pstr.loctype == COORD_WORLD) { wptmp.x = pstr.x; wptmp.y = pstr.y; vp = Wpoint2Vpoint(wptmp); } else { vp.x = pstr.x; vp.y = pstr.y; } setcolor(pstr.color); setpattern(1); setcharsize(pstr.charsize); setfont(pstr.font); activate_bbox(BBOX_TYPE_TEMP, TRUE); reset_bbox(BBOX_TYPE_TEMP); WriteString(vp, pstr.rot, pstr.just, pstr.s); pstr.bb = get_bbox(BBOX_TYPE_TEMP); set_graph_string(i, &pstr); } } /* * draw annotative boxes */ void draw_box(int gno, int i) { boxtype b; WPoint wptmp; VPoint vp1, vp2; get_graph_box(i, &b); if (gno != -2) { if (b.loctype == COORD_WORLD && b.gno != gno) { return; } if (b.loctype == COORD_VIEW && gno != -1) { return; } } if (b.active) { setclipping(FALSE); if (b.loctype == COORD_WORLD) { wptmp.x = b.x1; wptmp.y = b.y1; vp1 = Wpoint2Vpoint(wptmp); wptmp.x = b.x2; wptmp.y = b.y2; vp2 = Wpoint2Vpoint(wptmp); } else { vp1.x = b.x1; vp1.y = b.y1; vp2.x = b.x2; vp2.y = b.y2; } activate_bbox(BBOX_TYPE_TEMP, TRUE); reset_bbox(BBOX_TYPE_TEMP); setcolor(b.fillcolor); setpattern(b.fillpattern); FillRect(vp1, vp2); setcolor(b.color); setlinewidth(b.linew); setlinestyle(b.lines); setpattern(1); DrawRect(vp1, vp2); b.bb = get_bbox(BBOX_TYPE_TEMP); set_graph_box(i, &b); setclipping(TRUE); } } /* draw annotative ellipses */ void draw_ellipse(int gno, int i) { WPoint wptmp; VPoint vp1, vp2; ellipsetype b; b = ellip[i]; if (gno != -2) { if (b.loctype == COORD_WORLD && b.gno != gno) { return; } if (b.loctype == COORD_VIEW && gno != -1) { return; } } if (b.active) { setclipping(FALSE); if (b.loctype == COORD_WORLD) { wptmp.x = b.x1; wptmp.y = b.y1; vp1 = Wpoint2Vpoint(wptmp); wptmp.x = b.x2; wptmp.y = b.y2; vp2 = Wpoint2Vpoint(wptmp); } else { vp1.x = b.x1; vp1.y = b.y1; vp2.x = b.x2; vp2.y = b.y2; } activate_bbox(BBOX_TYPE_TEMP, TRUE); reset_bbox(BBOX_TYPE_TEMP); setcolor(b.fillcolor); setpattern(b.fillpattern); DrawFilledEllipse(vp1, vp2); setcolor(b.color); setlinewidth(b.linew); setlinestyle(b.lines); setpattern(1); DrawEllipse(vp1, vp2); b.bb = get_bbox(BBOX_TYPE_TEMP); set_graph_ellipse(i, &b); setclipping(TRUE); } } /* * draw annotative lines */ void draw_line(int gno, int i) { linetype l; WPoint wptmp; VPoint vp1, vp2; get_graph_line(i, &l); if (gno != -2) { if (l.loctype == COORD_WORLD && l.gno != gno) { return; } if (l.loctype == COORD_VIEW && gno != -1) { return; } } if (l.active) { setclipping(FALSE); if (l.loctype == COORD_WORLD) { wptmp.x = l.x1; wptmp.y = l.y1; vp1 = Wpoint2Vpoint(wptmp); wptmp.x = l.x2; wptmp.y = l.y2; vp2 = Wpoint2Vpoint(wptmp); } else { vp1.x = l.x1; vp1.y = l.y1; vp2.x = l.x2; vp2.y = l.y2; } activate_bbox(BBOX_TYPE_TEMP, TRUE); reset_bbox(BBOX_TYPE_TEMP); setcolor(l.color); setlinewidth(l.linew); setlinestyle(l.lines); DrawLine(vp1, vp2); switch (l.arrow_end) { case 0: break; case 1: draw_arrowhead(vp2, vp1, &l.arrow); break; case 2: draw_arrowhead(vp1, vp2, &l.arrow); break; case 3: draw_arrowhead(vp2, vp1, &l.arrow); draw_arrowhead(vp1, vp2, &l.arrow); break; } l.bb = get_bbox(BBOX_TYPE_TEMP); set_graph_line(i, &l); setclipping(TRUE); } } /* * draw arrow head */ void draw_arrowhead(VPoint vp1, VPoint vp2, const Arrow *arrowp) { double L, l, d, vlength; VVector vnorm; VPoint vpc, vpl, vpr, vps[4]; int lines; int fg; vlength = hypot((vp2.x - vp1.x), (vp2.y - vp1.y)); if (vlength == 0.0) { return; } vnorm.x = (vp2.x - vp1.x)/vlength; vnorm.y = (vp2.y - vp1.y)/vlength; L = 0.01*arrowp->length; d = L*arrowp->dL_ff; l = L*arrowp->lL_ff; vpc.x = vp2.x - L*vnorm.x; vpc.y = vp2.y - L*vnorm.y; vpl.x = vpc.x + 0.5*d*vnorm.y; vpl.y = vpc.y - 0.5*d*vnorm.x; vpr.x = vpc.x - 0.5*d*vnorm.y; vpr.y = vpc.y + 0.5*d*vnorm.x; vpc.x += l*vnorm.x; vpc.y += l*vnorm.y; vps[0] = vpl; vps[1] = vp2; vps[2] = vpr; vps[3] = vpc; lines = getlinestyle(); setlinestyle(1); switch (arrowp->type) { case 0: DrawPolyline(vps, 3, POLYLINE_OPEN); break; case 1: setpattern(1); DrawPolygon(vps, 4); DrawPolyline(vps, 4, POLYLINE_CLOSED); break; case 2: fg = getcolor(); setcolor(getbgcolor()); setpattern(1); DrawPolygon(vps, 4); setcolor(fg); DrawPolyline(vps, 4, POLYLINE_CLOSED); break; default: errmsg("Internal error in draw_arrowhead()"); break; } setlinestyle(lines); return; } void draw_region(int r) { int i; double vshift = 0.05; double xshift = 0.0, yshift = 0.0; region *this; int rgndouble=0; Arrow arrow; WPoint wptmp, wp1, wp2, wp3, wp4; VPoint vps[4], *vpstmp; set_default_arrow(&arrow); this=&rg[r]; switch (this->type) { case REGION_POLYI: case REGION_POLYO: if (this->x != NULL && this->y != NULL && this->n > 2) { vpstmp = xmalloc (this->n*sizeof(VPoint)); if (vpstmp == NULL) { errmsg("xmalloc error in draw_region()"); return; } else { for (i = 0; i < this->n; i++) { wptmp.x = this->x[i]; wptmp.y = this->y[i]; vpstmp[i] = Wpoint2Vpoint(wptmp); } DrawPolyline(vpstmp, this->n, POLYLINE_CLOSED); xfree(vpstmp); } } return; case REGION_ABOVE: xshift = 0.0; yshift = vshift; break; case REGION_BELOW: xshift = 0.0; yshift = -vshift; break; case REGION_TOLEFT: xshift = -vshift; yshift = 0.0; break; case REGION_TORIGHT: xshift = vshift; yshift = 0.0; break; case REGION_HORIZI: case REGION_HORIZO: wp1.x=this->x1; wp1.y=this->y1; wp2.x=this->x1; wp2.y=this->y2; wp3.x=this->x2; wp3.y=this->y1; wp4.x=this->x2; wp4.y=this->y2; rgndouble=1; break; case REGION_VERTI: case REGION_VERTO: wp1.x=this->x1; wp1.y=this->y1; wp2.x=this->x2; wp2.y=this->y1; wp3.x=this->x1; wp3.y=this->y2; wp4.x=this->x2; wp4.y=this->y2; rgndouble=1; break; default: errmsg("Internal error in draw_region"); return; } if(!rgndouble) { wptmp.x = this->x1; wptmp.y = this->y1; vps[1] = Wpoint2Vpoint(wptmp); wptmp.x = this->x2; wptmp.y = this->y2; vps[2] = Wpoint2Vpoint(wptmp); vps[0].x = vps[1].x + xshift; vps[0].y = vps[1].y + yshift; vps[3].x = vps[2].x + xshift; vps[3].y = vps[2].y + yshift; DrawPolyline(vps, 4, POLYLINE_OPEN); draw_arrowhead(vps[1], vps[0], &arrow); draw_arrowhead(vps[2], vps[3], &arrow); } else { vps[0] = Wpoint2Vpoint(wp1); vps[1] = Wpoint2Vpoint(wp2); DrawLine(vps[0], vps[1]); vps[0] = Wpoint2Vpoint(wp3); vps[1] = Wpoint2Vpoint(wp4); DrawLine(vps[0], vps[1]); wp1.x=(wp1.x+wp2.x)/2; wp1.y=(wp1.y+wp2.y)/2; wp3.x=(wp3.x+wp4.x)/2; wp3.y=(wp3.y+wp4.y)/2; vps[0] = Wpoint2Vpoint(wp1); vps[1] = Wpoint2Vpoint(wp3); DrawLine(vps[0], vps[1]); draw_arrowhead(vps[0], vps[1], &arrow); } } /* ---------------------- legends ---------------------- */ /* * draw the legend */ void dolegend(int gno) { int i; int draw_flag; double maxsymsize; double ldist, sdist, yskip; WPoint wptmp; VPoint vp, vp2; view v; legend l; plotarr p; get_graph_legend(gno, &l); if (l.active == FALSE) { return; } maxsymsize = 0.0; draw_flag = FALSE; for (i = 0; i < number_of_sets(gno); i++) { if (is_set_drawable(gno, i)) { get_graph_plotarr(gno, i, &p); if (p.lstr[0] != '\0') { draw_flag = TRUE; } if (p.symsize > maxsymsize) { maxsymsize = p.symsize; } } } if (draw_flag == FALSE) { l.bb.xv1 = l.bb.xv2 = l.bb.yv1 = l.bb.yv2 = 0.0; /* The bb update shouldn't change the dirtystate flag */ lock_dirtystate(TRUE); set_graph_legend(gno, &l); lock_dirtystate(FALSE); return; } setclipping(FALSE); if (l.loctype == COORD_WORLD) { wptmp.x = l.legx; wptmp.y = l.legy; vp = Wpoint2Vpoint(wptmp); } else { vp.x = l.legx; vp.y = l.legy; } ldist = 0.01*l.len; sdist = 0.01*(l.hgap + maxsymsize); yskip = 0.01*l.vgap; activate_bbox(BBOX_TYPE_TEMP, TRUE); reset_bbox(BBOX_TYPE_TEMP); update_bbox(BBOX_TYPE_TEMP, vp); set_draw_mode(FALSE); putlegends(gno, vp, ldist, sdist, yskip); v = get_bbox(BBOX_TYPE_TEMP); vp2.x = vp.x + (v.xv2 - v.xv1) + 2*0.01*l.hgap; vp2.y = vp.y - (v.yv2 - v.yv1) - 2*0.01*l.vgap; l.bb.xv1 = vp.x; l.bb.yv1 = vp2.y; l.bb.xv2 = vp2.x; l.bb.yv2 = vp.y; /* The bb update shouldn't change the dirtystate flag */ lock_dirtystate(TRUE); set_graph_legend(gno, &l); lock_dirtystate(FALSE); set_draw_mode(TRUE); setpen(l.boxfillpen); FillRect(vp, vp2); if (l.boxlines != 0 && l.boxpen.pattern != 0) { setpen(l.boxpen); setlinewidth(l.boxlinew); setlinestyle(l.boxlines); DrawRect(vp, vp2); } /* correction */ vp.x += (vp.x - v.xv1) + 0.01*l.hgap; vp.y += (vp.y - v.yv2) - 0.01*l.vgap; reset_bbox(BBOX_TYPE_TEMP); update_bbox(BBOX_TYPE_TEMP, vp); putlegends(gno, vp, ldist, sdist, yskip); } void putlegends(int gno, VPoint vp, double ldist, double sdist, double yskip) { int i, setno; VPoint vp2, vpstr; plotarr p; legend l; vp2.y = vp.y; vp2.x = vp.x + ldist; vpstr.y = vp.y; vpstr.x = vp2.x + sdist; get_graph_legend(gno, &l); for (i = 0; i < number_of_sets(gno); i++) { if (l.invert == FALSE) { setno = i; } else { setno = number_of_sets(gno) - i - 1; } if (is_set_drawable(gno, setno)) { get_graph_plotarr(gno, setno, &p); if (p.lstr == NULL || p.lstr[0] == '\0') { continue; } setcharsize(l.charsize); setfont(l.font); setcolor(l.color); WriteString(vpstr, 0, JUST_LEFT|JUST_TOP, p.lstr); vp.y = (vpstr.y + get_bbox(BBOX_TYPE_TEMP).yv1)/2; vp2.y = vp.y; vpstr.y = get_bbox(BBOX_TYPE_TEMP).yv1 - yskip; setfont(p.charfont); if (l.len != 0 && p.lines != 0 && p.linet != 0) { setpen(p.linepen); setlinewidth(p.linew); setlinestyle(p.lines); DrawLine(vp, vp2); setlinewidth(p.symlinew); setlinestyle(p.symlines); if (p.type == SET_BAR || p.type == SET_BOXPLOT || p.type == SET_BARDY || p.type == SET_BARDYDY) { drawlegbarsym(vp, p.symsize, p.sympen, p.symfillpen); drawlegbarsym(vp2, p.symsize, p.sympen, p.symfillpen); } else { drawxysym(vp, p.symsize, p.sym, p.sympen, p.symfillpen, p.symchar); drawxysym(vp2, p.symsize, p.sym, p.sympen, p.symfillpen, p.symchar); } } else { VPoint vptmp; vptmp.x = (vp.x + vp2.x)/2; vptmp.y = vp.y; setlinewidth(p.symlinew); setlinestyle(p.symlines); if (p.type == SET_BAR || p.type == SET_BOXPLOT || p.type == SET_BARDY || p.type == SET_BARDYDY) { drawlegbarsym(vptmp, p.symsize, p.sympen, p.symfillpen); } else { drawxysym(vptmp, p.symsize, p.sym, p.sympen, p.symfillpen, p.symchar); } } } } } /* plot time stamp */ void draw_timestamp(void) { if (timestamp.active) { VPoint vp; setfont(timestamp.font); setcharsize(timestamp.charsize); setcolor(timestamp.color); vp.x = timestamp.x; vp.y = timestamp.y; activate_bbox(BBOX_TYPE_TEMP, TRUE); reset_bbox(BBOX_TYPE_TEMP); WriteString(vp, timestamp.rot, timestamp.just, timestamp.s); timestamp.bb = get_bbox(BBOX_TYPE_TEMP); } }