plt - Software for 2D Plots 2.5

File: <base>/plt/src/figure.c (7,932 bytes)
/* file: figure.c	Paul Albrecht		February 1988
			Last revised:	        25 March 2009
Figure-drawing functions for plt

Copyright (C) Paul Albrecht 1988

Recent changes (by George Moody, george@mit.edu):
  29 March 2001: now using SETCOLOR rather than SETGRAY to choose how to fill
		 the background of a legend box
  14 November 2002: fixed missing casts in azmem calls
  25 March 2009: added HISTOGRAM mode
_______________________________________________________________________________
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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.

You may contact the maintainer by e-mail (george@mit.edu) or postal mail
(MIT Room E25-505A, Cambridge, MA 02139 USA).  For updates to this software,
please visit PhysioNet (http://www.physionet.org/).
_______________________________________________________________________________
*/

#include "plt.h"

/* Prototypes of functions defined in this module */
void FigureInit(Mode mode);
void FigureDef(char type, Const coord, double x0, double y0,
	       double x1, double y1, char *fgName);
void FigureDraw(FigPtr f);
void transform(Ptype *xp, Ptype *yp, double xdbl, double ydbl, double dflt,
	       Const coord);
void LegendDef(long lineNum, long pltNum, char *name);
void LegendDraw(void);

static void minmax(double x, double y);

void FigureInit(Mode mode)
{
    Figure.xlPos = Figure.ylPos = FDEFAULT;
    Figure.xlDel = Figure.xlBoxScl = FDEFAULT;
}

void FigureDef(char type, Const coord, double x0, double y0,
	       double x1, double y1, char *fgName)
{
    FigPtr f;

    if (Figure.nFigs == Figure.maxFigs)
	Figure.figs = (FigPtr)azmem(Figure.figs, (size_t *)&Figure.maxFigs, 5,
				    sizeof(*Figure.figs));
    f = &Figure.figs[Figure.nFigs++];
    f->type = type;
    f->coord = coord;
    f->x0 = x0;
    f->y0 = y0;
    f->x1 = x1;
    f->y1 = y1;
    f->fgName = fgName;
    if (f->coord == DATAC) {
	minmax(x0, y0);
	minmax(x1, y1);
    }
}

void FigureDraw(FigPtr f)
{
    double x, y, scl;
    Ptype x0, y0, x1, y1, dx, dy;

    x0 = f->x0;
    y0 = f->y0;
    x1 = f->x1;
    y1 = f->y1;

    transform(&x0, &y0, f->x0, f->y0, 0.0, f->coord);
    transform(&x1, &y1, f->x1, f->y1, 1.0, f->coord);
    FontGroupSelect("f", f->fgName);
    switch (f->type) {
    case ARROW:
	move(x1, y1);
	cont(x0, y0);
	x = (double)(x1-x0)/xinch;
	y = (double)(y1-y0)/yinch;
	scl = (old_ps == -1) ? 1 : old_ps/DEFAULT_PS;
	scl *= 0.08/sqrt(x*x+y*y) * (p->xinches+p->yinches)/14.0;
	x1 = x0 + scl*(x1-x0);
	y1 = y0 + scl*(y1-y0);
	dx = 0.4*scl*y*xinch + 0.5;
	dy = 0.4*scl*x*yinch + 0.5;
	move(x1+dx, y1-dy);
	cont(x0, y0);
	cont(x1-dx, y1+dy);
	break;
    case BOX:
	move(x0, y0);
	cont(x0, y1);
	cont(x1, y1);
	cont(x1, y0);
	cont(x0, y0);
	break;
    case DARKBOX:
	PolyDef(x0, y0, CMOVE);
	PolyDef(x0, y1, CCONT);
	PolyDef(x1, y1, CCONT);
	PolyDef(x1, y0, CCONT);
	PolyDef(x0, y0, CFILL);
	break;
    case CONNECT:
	move(x0, y0);
	cont(x1, y1);
	break;
    }
}

static void minmax(double x, double y)
{
    if (x != DEFAULT) {
	if (x < xmin) xmin = x;
	else if (x > xmax) xmax = x;
    }
    if (y != DEFAULT) {	
	if (y < ymin) ymin = y;
	else if (y > ymax) ymax = y;
    }
}

void transform(Ptype *xp, Ptype *yp, double xdbl, double ydbl, double dflt,
	       Const coord)
{
    switch (coord) {
    case DATAC:
	if (xdbl == DEFAULT) *xp = dflt*(xwmax-xwmin)+xwmin+0.5;
	else *xp = X(xdbl) + 0.5;
	if (ydbl == DEFAULT) *yp = dflt*(ywmax-ywmin)+ywmin+0.5;
	else *yp = Y(ydbl) + 0.5;
	break;
    case WINC:
	*xp = ((xdbl == DEFAULT) ? dflt : xdbl)*(xwmax-xwmin) + xwmin+0.5;
	*yp = ((ydbl == DEFAULT) ? dflt : ydbl)*(ywmax-ywmin) + ywmin+0.5;
	break;
    case PDEVC:
	*xp = xdbl + 0.5;
	*yp = ydbl + 0.5;
	break;
    }
}

void LegendDef(long lineNum, long pltNum, char *name)
{
    LegPtr l;

    if (Figure.nLegs == Figure.maxLegs)
	Figure.legs = (LegPtr)azmem(Figure.legs, (size_t *)&Figure.maxLegs, 5,
				    sizeof(*Figure.legs));
    l = &Figure.legs[Figure.nLegs++];
    l->lineNum = lineNum;
    l->pltNum = pltNum;
    l->text = name;
}

void LegendDraw(void)
{
    PltPtr plt;
    LegPtr l;
    double gray;
    Ptype n, xb0, yb0, xba, xb1, yb1, xMargin, yMargin, textWidth, x, y,
	x0, x1, xsize, ysize;
    Boolean lineSeg, havePlts;

    if (Figure.xlPos == DEFAULT) Figure.xlPos = 0.7;
    if (Figure.ylPos == DEFAULT) Figure.ylPos = 0.85;
    if (Figure.xlDel == DEFAULT) Figure.xlDel = 1.0;
    Figure.xlPos = xa.min + Figure.xlPos*(xa.max-xa.min);
    Figure.ylPos = ya.min + Figure.ylPos*(ya.max-ya.min);
    if (Figure.xlDel < 0.25) Figure.xlDel /= 0.05;
    havePlts = lineSeg = NO;
    xba = X(Figure.xlPos);
    yb0 = Y(Figure.ylPos);
    yb1 = p->yfull;
    textWidth = 0;
    FontGroupSelect("f", Figure.legfg);
    xMargin = chwd;
    yMargin = chht/3;
    for (n=0;  n < Figure.nLegs;  n++) {
	l = &Figure.legs[n];
	if (l->lineNum == DEFAULT) l->lineNum = n;
	strsize(l->text, &xsize, &ysize, 0.0);
	l->yPos = yb0 - ysize*(3*l->lineNum+2)/3 - yMargin;
	if (yb1 > l->yPos) yb1 = l->yPos;
	if (xsize > textWidth) textWidth = xsize;
	if (l->pltNum == DEFAULT) continue;
	if (l->pltNum < 0 || l->pltNum >= Plot.nPlts) {
	    err(YES, "Bad plot number %d for legend", l->pltNum);
	    continue;
	}
	havePlts = YES;
	plt = &Plot.plts[l->pltNum];
	if (plt->pm != SCATTER && plt->pm != SCATTER_STD) lineSeg = YES;
    }	
    if ((fixed_font && Figure.xlBoxScl != 0) || Figure.xlBoxScl == DEFAULT)
	Figure.xlBoxScl = 1;
    x1 = xba - xMargin;
    if (havePlts) x0 = x1 - (int)(lineSeg ? 2.5*Figure.xlDel*chwd : chwd);
    else x0 = x1;
    xb0  = x0 - xMargin;
    xb1  = xba + (int)(textWidth*Figure.xlBoxScl) + xMargin;
    yb1 -= yMargin;
    if (Figure.eStat == 0)
	Figure.eStat = (omode & ERASE) ? "yes" : "no";
    if (*Figure.eStat == 'y' || *Figure.eStat == 'Y') {
	PtrUnion arg0, arg1;
	
	arg0.c = "white";
	special(SETCOLOR, arg0, arg1);
	PolyDef(xb0, yb0, CMOVE);
	PolyDef(xb1, yb0, CCONT);
	PolyDef(xb1, yb1, CCONT);
	PolyDef(xb0, yb1, CFILL);
	FontGroupSelect("f", Figure.legfg);
    }
    if (Figure.xlBoxScl > 0) {
	PolyDef(xb0, yb0, CMOVE);
	PolyDef(xb1, yb0, CCONT);
	PolyDef(xb1, yb1, CCONT);
	PolyDef(xb0, yb1, CCONT);
	PolyDef(xb0, yb0, CSTROKE);
    }
    for (n=0;  n < Figure.nLegs;  n++) {
	l = &Figure.legs[n];
	FontGroupSelect("f", Figure.legfg);
	plabel(l->text, xba, l->yPos, "LB", 0.0);
	x = (x0+x1)/2;
	y = l->yPos + chht/3;
	if (l->pltNum == DEFAULT) continue;
	plt = &Plot.plts[l->pltNum];
	FontGroupSelect("p", plt->fgName);
	switch (plt->pm) {
	case NORMAL:
	case NCOLZERO:
	case LINES:
	    move(x0, y);
	    cont(x1, y);
	    break;
	case FILLED:
	case FILLBETWEEN:
	case HISTOGRAM:
	case OUTLINE:
	    PolyDef(x0, y-chht/3, CMOVE);
	    PolyDef(x0, y+chht/3, CCONT);
	    PolyDef(x1, y+chht/3, CCONT);
	    PolyDef(x1, y-chht/3, CCONT);
	    if (plt->pm == OUTLINE) PolyDef(x0, y-chht/3, CSTROKE);
	    else PolyDef(x0, y-chht/2, CFILL);
	    break;
	case OUTLINEFILL:
	    PolyDef(x0, y-chht/3, CMOVE);
	    PolyDef(x0, y+chht/3, CBBCONT);
	    PolyDef(x1, y+chht/3, CBBCONT);
	    PolyDef(x1, y-chht/3, CBBCONT);
	    PolyDef(x0, y-chht/3, CBBFILL);
	    break;
	case SYMBOL:
	case SCATTER:
	case SYMBOL_STD:
	case SCATTER_STD:
	    if (plt->pc == ' ') SmallBox(x, y);
	    else scatterplot(plt, x, y, y, y);
	    break;
	default:
	    err(NO, "No legend for plot type `%c'", plt->pm);
	    }
	}
}