/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include /* * Header: /a/max/homes/max/gresh/code/svs/src/libdx/RCS/axes.c,v 5.0 92/11/12 09:07:33 svs Exp Locker: gresh * Locker: gresh */ #include #include #include static RGBColor TICKCOLOR = {1.0, 1.0, 0.0}; static RGBColor LABELCOLOR = {1.0, 1.0, 1.0}; static RGBColor AXESCOLOR = {1.0, 1.0, 0.0}; static RGBColor GRIDCOLOR = {0.3, 0.3, 0.3}; static RGBColor BACKGROUNDCOLOR = {0.07, .07, 0.07}; #if defined(sgi) #define log10 log10f #endif #define EPSILON 0.01 /* distance away from face for marks */ #define XYZ 0.1 /* cross in middle of cube */ #define XY0 0.05 /* cross in middle of face, toward corner */ #define XY1 0.05 /* cross in middle of face, toward edge */ #define X0 0.05 /* tick mark in corner of cube */ #define X1 0.08 /* tick mark at edge of cube */ #define MAJOR 0.05 /* default major tick proportion of axis length */ #define MAJORMIN 6 /* minimum major tick length in pixels */ #define MINOR 0.4 /* minor tick length is this times major tick length */ #define FS 0.8 /* font scaled by FS times minimum delta */ #define FMAX 20 /* maximun font scale */ #define OFFSET 0.7 /* tick labels are offset by OFFSET times font ht */ #define MAXWIDTH 7 /* numbers wider than this are printed in exp notation */ /* can be overridden by DXAXESMAXWIDTH env var */ #define LABELSCALE 1.2 /* axes labels can be bigger than tic labels */ #define ABS(x) ((x)<0? -(x) : (x)) #define SGN(x) ((x)<0? -1 : 1) #define MIN(a,b) ((a)<(b)? (a) : (b)) #define MAX(a,b) ((a)>(b)? (a) : (b)) #define E 0.0001 /* fuzz */ #define FL(x) ((x)+E>0? (int)((x)+E) : (int)((x)+E)-1) /* fuzzy floor */ #define CL(x) ((x)-E>0? (int)((x)-E)+1 : (int)((x)-E)) /* fuzzy ceiling */ extern Error _dxfGetFormat(float *, char *, int, float); extern Error _dxfCheckLocationsArray(Array, int *, float **); static Error GetProjections(Vector cameraup, Vector eye, float *flipx, float *flipy, float *flipz, float *flipxlab, float *flipylab, float *flipzlab, Vector sgn) { Vector cameraupprime, standardprime, crossprod, standard, up, right; Vector xprime, yprime, zprime, xfup, yfup, zfup; /* I want upprime and standardprime to be the projections of up and [0 1 0] in the plane perpendicular to the eye vector */ /* first derive up and right. They must simply be mutually perpendicular with eye */ /* if ((eye.x==0)&&(eye.y==0)&&(eye.z==1)) { up = DXVec(0, 1, 0); right = DXVec(1, 0, 0); } else if ((eye.x==0)&&(eye.y==0)&&(eye.z==-1)) { up = DXVec(0, 1, 0); right = DXVec(1, 0, 0); } else { up = DXVec(0,0,1); up = DXCross(up,eye); right = DXCross(up,eye); } */ /* now do the projection */ cameraupprime = DXSub(cameraup, DXMul(eye, DXDot(cameraup, eye))); /* do the projections of the axes now */ xprime = DXSub(DXVec(1,0,0), DXMul(eye, DXDot(DXVec(1,0,0), eye))); yprime = DXSub(DXVec(0,1,0), DXMul(eye, DXDot(DXVec(0,1,0), eye))); zprime = DXSub(DXVec(0,0,1), DXMul(eye, DXDot(DXVec(0,0,1), eye))); /* now get the "font up" directions, which are the cross products of x,y,zprime and eye */ xfup = DXCross(xprime, eye); yfup = DXCross(yprime, eye); zfup = DXCross(zprime, eye); if (DXDot(xprime, cameraupprime) != 0) *flipx = SGN(DXDot(xprime, cameraupprime)); else *flipx = -1; *flipy = SGN(DXDot(yprime, cameraupprime)); *flipz = SGN(DXDot(zprime, cameraupprime)); /* need to do some fudging for the sgn param. This was trial and error, I admit */ if ((sgn.x < 0 && sgn.y < 0 && sgn.z < 0)|| (sgn.x > 0 && sgn.y > 0 && sgn.z > 0)) { *flipxlab = -SGN(DXDot(xfup, cameraupprime)); if (DXDot(yfup, cameraupprime) != 0) *flipylab = -SGN(DXDot(yfup, cameraupprime)); else *flipylab = -1; *flipzlab = -SGN(DXDot(zfup, cameraupprime)); } /* else exactly one is negative. Flip the two that aren't negative */ else if (sgn.x * sgn.y * sgn.z < 0) { *flipxlab = -SGN(DXDot(xfup, cameraupprime))* (-sgn.x); if (DXDot(yfup, cameraupprime) != 0) *flipylab = -SGN(DXDot(yfup, cameraupprime))* (-sgn.y); else *flipylab = -1; *flipzlab = -SGN(DXDot(zfup, cameraupprime))* (-sgn.z); } /* else two are negative. Flip those two. */ else { *flipxlab = -SGN(DXDot(xfup, cameraupprime))* (sgn.x); if (DXDot(yfup, cameraupprime) != 0) *flipylab = -SGN(DXDot(yfup, cameraupprime))* (sgn.y); else *flipylab = -1; *flipzlab = -SGN(DXDot(zfup, cameraupprime))* (sgn.z); } return OK; } static Error _dxfSetTextColor(Field f, RGBColor color) { Array pos, col; int numpos; pos = (Array)DXGetComponentValue(f,"positions"); DXGetArrayInfo(pos,&numpos,NULL,NULL,NULL,NULL); col = (Array)DXNewConstantArray(numpos, (Pointer)&color, TYPE_FLOAT, CATEGORY_REAL, 1, 3); DXSetComponentValue(f,"colors",(Object)col); return OK; } /* * Do a face of the box. */ static void face( Field f, /* field to hold the faces */ int *pt, /* point numbers */ Point o, /* origin */ Vector sx, /* "x" vector */ Vector sy, /* "y" vector */ RGBColor c /* color of face */ ) { Vector normal; int i; float d1, d2; /* face corner points */ DXAddPoint(f, *pt+0, o); DXAddPoint(f, *pt+1, DXAdd(o,sx)); DXAddPoint(f, *pt+2, DXAdd(sy,DXAdd(o,sx))); DXAddPoint(f, *pt+3, DXAdd(o,sy)); /* face colors, normals */ normal = DXNormalize(DXCross(sx,sy)); for (i=0; i<4; i++) { DXAddColor(f, *pt+i, c); DXAddNormal(f, *pt+i, normal); } /* face */ _dxfBeginFace(f); for (i=0; i<4; i++) _dxfNextPoint(f, *pt+i); _dxfEndFace(f); *pt += 4; } /* * Macro to do a line segment, adding points, * of the given color. */ #define LINE(rgb, x0,y0,z0, x1,y1,z1) { \ Line line; \ line.p = pt; \ line.q = pt+1; \ DXAddLine(f, ln++, line); \ DXAddPoint(f, line.p, DXPt(x0,y0,z0)); \ DXAddPoint(f, line.q, DXPt(x1,y1,z1)); \ DXAddColor(f, line.p, rgb); \ DXAddColor(f, line.q, rgb); \ pt += 2; \ } #define LINE1(rgb, x0,y0,z0, x1,y1,z1) { \ Line line; \ line.p = pt1; \ line.q = pt1+1; \ DXAddLine(f1, ln1++, line); \ DXAddPoint(f1, line.p, DXPt(x0,y0,z0)); \ DXAddPoint(f1, line.q, DXPt(x1,y1,z1)); \ DXAddColor(f1, line.p, rgb); \ DXAddColor(f1, line.q, rgb); \ pt1 += 2; \ } /* * Make tick marks, cursor, labels for a plane */ #define BETWEEN(a, x, b) (((a)<(x) && (x)<(b)) || ((b)<(x) && (x)<(a))) static Error mark( Group g, /* group to hold result */ int show_front, /* whether to show front outlines */ int show_axes, /* whether to show axes lines */ char *xlabel, /* x axis label */ char *ylabel, /* y axis label */ int fliplx, /* flip the x label */ int fliply, /* flip the y label */ int reverse, /* whteher to reverse labels */ Point o, /* origin */ Vector s, /* size */ Vector sgn, /* sign of size */ Point *c, /* cursor position */ int nx, /* number of x tick marks */ int flipxtics, int subx, /* number of x sub tick marks */ double lx, /* first tick mark on x axis */ double dx, /* spacing of x tick marks */ double lsx, /* label scaling for x */ int flipx, /* whether to flip x tick labels */ char *fmtx, /* format for x tick labels */ int ny, /* number of y tick marks */ int flipytics, int suby, /* number of y sub tick marks */ double ly, /* first tick mark on y axis */ double dy, /* spacing of y tick marks */ double lsy, /* label scaling for y */ int flipy, /* whether to flip y tick labels */ char *fmty, /* format for x tick labels */ double fs, /* font scaling for labels */ int px, int py, int pz, /* permutation */ int grid, /* whether to draw the grid */ double lengthx, /* axis length for tick mark calculation */ double lengthy, /* axis length for tick mark calculation */ RGBColor labelcolor, /* color of labels */ RGBColor ticcolor, /* color of ticks */ RGBColor axescolor, /* color of axes */ RGBColor gridcolor, /* color of grid */ float labelscale, /* scalefactor for labels */ char *fontname, /* font to use for labels */ float *xlocs, float *ylocs, /* list of locations for tics */ char **xlabels, char **ylabels /* list of labels for tics */ ) { static Matrix zero; /* zero matrix */ Matrix t; /* matrix */ float z; /* z distance above face for marks */ Object font; /* font for labels */ static float ascent; /* baseline to top of label */ float xwidth; /* max width of x axis tick labels */ float ywidth; /* max width of y axis tick labels */ float width; /* width temp variable */ Group group = NULL; /* group to hold labels */ Object text; /* the label */ char buf[100]; /* the label */ float offset; /* amount to move label so 0's don't collide */ float hdir, vdir; /* direction of tick label compared to axis */ Field f = NULL; /* field for marks */ Field f1 = NULL; /* field for tics */ Xform xform = NULL; /* various transformed objects */ int pt=0, ln=0; /* current point, line number within f */ int pt1=0, ln1=0; /* current point, line number within f */ float x, y, a, b, major, minor, xx, yy; int i, j, xlog, ylog, zlog; char *tempstring; int createdxlocs = 0, createdylocs = 0; int createdxlabels = 0, createdylabels = 0; int axesdirection; z = o.z /*+EPSILON*s.z*/; /* z distance above face for marks */ group = DXNewGroup(); if (!group) goto error; /* a field for the ticks. They have a larger fuzz than the grid lines */ f1 = DXNewField(); if (!f1) goto error; DXSetFloatAttribute((Object)f1, "fuzz", 2); DXSetMember(group, NULL, (Object) f1); /* a field for everything else */ f = DXNewField(); if (!f) goto error; DXSetFloatAttribute((Object)f, "fuzz", 1); DXSetMember(group, NULL, (Object) f); /* * Put cursor projections on xy plane as follows: * * B * y ------+-------- o+s * | | * C + + + D * | E | * | | * o ------+-------- x * A */ if (c) { /* cursor projections */ LINE(axescolor, c->x, o.y, z, c->x, o.y+s.y*X0, z); /* A */ LINE(axescolor, c->x, o.y+s.y, z, c->x, o.y+s.y*(1-X1), z); /* B */ LINE(axescolor, o.x, c->y, z, o.x+s.x*X0, c->y, z); /* C */ LINE(axescolor, o.x+s.x, c->y, z, o.x+s.x*(1-X1), c->y, z); /* D */ LINE(axescolor, c->x+s.x*XY1, c->y, z, c->x-s.x*XY0, c->y, z); /* E */ LINE(axescolor, c->x, c->y+s.y*XY1, z, c->x, c->y-s.y*XY0, z); /* E */ /* our contribution to cursor in middle of box */ if (s.z!=0) LINE(axescolor, c->x+s.x*XYZ, c->y, c->z, c->x-s.x*XYZ, c->y, c->z); } /* our contribution to front face outlines */ /* for a three-d plot */ if (s.z != 0){ if (show_front) LINE(axescolor, o.x, o.y+s.y, o.z+s.z, o.x+s.x, o.y+s.y, o.z+s.z); } /* for a two-d plot */ else { /* reset showaxes to 1; it looks funny otherwise */ show_axes = 1; if (show_front) { LINE(axescolor, o.x, o.y+s.y, o.z+s.z, o.x, o.y, o.z+s.z); LINE(axescolor, o.x+s.x, o.y, o.z+s.z, o.x, o.y, o.z+s.z); } } /* x-axis tick mark length, based on y-axis size */ major = MAJOR; lengthy = ABS(lengthy); if (lengthy>0 && major*lengthy0 && major*lengthx 0) x = x+2*offset; if (!flipxtics) y = o.y + s.y + sgn.y*fs + (sgn.y*hdir>0? 0 : sgn.y*fs*labelscale*width); else y = o.y + s.y + sgn.y*fs + major*s.y + (sgn.y*hdir>0? 0 : sgn.y*fs*labelscale*width); t = DXScale(hdir*fs*labelscale, vdir*fs*labelscale, 1); xform = DXNewXform((Object)text, t); xform = DXNewXform((Object)xform, DXRotateZ(90*DEG)); xform = DXNewXform((Object)xform, DXTranslate(DXVec(x,y,z))); if (!xform) goto error; DXSetMember(group, NULL, (Object)xform); if (width>xwidth) xwidth = width; } /* y axis tick labels */ a = o.y - .001*s.y; b = o.y + 1.001*s.y; axesdirection = SGN(ylocs[ny-1]-ylocs[0]); offset = .5*ascent*fs*labelscale; for (i=0, ywidth=0; i0? 0 : sgn.x*fs*width*labelscale); else x = o.x + s.x + sgn.x*fs + major*s.x + (sgn.x*hdir>0? 0 : sgn.x*fs*width*labelscale); if (i==0) y = (axesdirection==1) ? ylocs[0] : ylocs[0]-2*offset; else if (i==ny-1) y = (axesdirection==1) ? ylocs[i]-2*offset : ylocs[i]; /* these end the last label at the tick mark */ /* and starts the 1st label at the tick mark */ else y = ylocs[i]-offset; /* this centers the label on the tick mark */ /* to allow for the effect of the mirror scale */ if (vdir < 0) y = y+2*offset; t = DXScale(hdir*fs*labelscale, vdir*fs*labelscale, 1); xform = DXNewXform((Object)text, t); xform = DXNewXform((Object)xform, DXTranslate(DXVec(x,y,z))); if (!xform) goto error; DXSetMember(group, NULL, (Object)xform); if (width>ywidth) ywidth = width; } /* use proportional font here */ DXDelete(font); if (!strcmp(fontname,"standard")) font = DXGetFont("variable", &ascent, NULL); else font = DXGetFont(fontname, &ascent, NULL); if (!font) goto error; /* x label */ if (xlabel) { if (strcmp(xlabel,"")) { text = DXGeometricText(xlabel, font, &width); _dxfSetTextColor((Field)text,labelcolor); x = o.x + s.x/2 - LABELSCALE*fliplx*sgn.x*fs*labelscale*width/2 *reverse; if (!flipxtics) y = o.y + s.y + sgn.y*fs*(1 + labelscale*(xwidth*1.2) + ascent*labelscale) + (fliplx*sgn.y < 0 ? fs*ascent*labelscale : 0); else y = o.y + s.y + sgn.y*fs*(1 + labelscale*(xwidth*1.2) + ascent*labelscale) + major*s.y + + (fliplx*sgn.y < 0 ? fs*ascent*labelscale : 0); t = DXScale(fliplx*sgn.x*fs*reverse*LABELSCALE*labelscale, fliplx*sgn.y*fs*LABELSCALE*labelscale, 0); xform = DXNewXform((Object)text, t); xform = DXNewXform((Object)xform, DXTranslate(DXVec(x,y,z))); if (!xform) goto error; DXSetMember(group, NULL, (Object)xform); } } /* y label */ if (ylabel) { if (strcmp(ylabel,"")) { text = DXGeometricText(ylabel, font, &width); _dxfSetTextColor((Field)text,labelcolor); if (!flipytics) x = o.x + s.x + sgn.x*fs*(1 + labelscale*(ywidth*1.2) + ascent*labelscale) + (fliply*sgn.x < 0 ? fs*ascent*labelscale : 0); else x = o.x + s.x + sgn.x*fs*(1 + labelscale*(ywidth*1.2) + ascent*labelscale) + major*s.x + + (fliply*sgn.x < 0 ? fs*ascent*labelscale : 0); y = o.y + s.y/2 + LABELSCALE*fliply*sgn.y*fs*width*labelscale/2 *reverse; t = DXScale(fliply*sgn.y*fs *reverse*LABELSCALE*labelscale, fliply*sgn.x*fs*LABELSCALE*labelscale, 0); xform = DXNewXform((Object)text, t); xform = DXNewXform((Object)xform, DXRotateZ(-90*DEG)); xform = DXNewXform((Object)xform, DXTranslate(DXVec(x,y,z))); if (!xform) goto error; DXSetMember(group, NULL, (Object)xform); } } /* end field */ if (!DXEndField(f)) goto error; if (!DXEndField(f1)) goto error; /* permute and put in group */ t = zero; t.A[px][0] = 1; t.A[py][1] = 1; t.A[pz][2] = 1; xform = DXNewXform((Object)group, t); if (!xform) goto error; DXSetMember(g, NULL, (Object)xform); DXSetFloatAttribute((Object)group, "fuzz", 4); /* all ok */ DXDelete(font); if (createdxlocs) { DXFree((Pointer)xlocs); } if (createdxlabels) { for (i=0;i0) { n1 = FL(so/sd); n2 = CL((so+ss)/sd); } else { n1 = CL(so/sd); n2 = FL((so+ss)/sd); } /* adjust axes size to major tick? */ if (adjust) { so = n1*sd; ss = n2*sd - so; } /* number ranges */ lo = n1 * sd; /* lo value (towards o end of range) */ hi = n2 * sd; /* hi value (towards o+s end) */ /* return new values, possibly adjusted */ *n = (n2 - n1) * SGN(ss) + 1; /* number of divisions */ *sub = ds[i].sub; /* sub tick marks */ *l = lo / scale; /* lo value */ *o = so / scale; /* origin */ *s = ss / scale; /* size */ *d = sd * SGN(ss) / scale; /* delta */ /* format */ precision = -k + ds[i].precision; /* precision */ if (precision < 0) precision = 0; lg = log10(MAX(ABS(lo),ABS(hi))); /* integer part */ width = 1 + FL(lg); if (width<1) width = 1; if (precision>0) /* fraction and decimal point */ width += precision + 1; if (lo<0 || hi<0) /* sign */ width += 1; cstring = (char *)getenv("DXAXESMAXWIDTH"); if (cstring != NULL) maxwidth = atoi(cstring); else maxwidth = MAXWIDTH; if (width <= maxwidth) { sprintf(fmt, "%%%d.%df", width, precision); return; } /* if the width of the format would be too long to look nice, * use exponential notation and try to print the minimum number * of significant digits. */ { char tbuf[32], *cp; int z, lastz; int prec; int minprec; double val; /* exp format is [-]m.dddddde+XX, where the precision controls the * number of digits in the fraction. start with the default precision * of 6 and look for trailing 0's. if there are none, you are done. * if some or all are 0's, go through each tick label and take the * largest value which isn't 0. the minimum return value is 1; this * could be changed to 0 if you prefer "me+XX" instead of "m.0e+XX". */ prec = 6; minprec = 1; range = ABS(hi-lo); for (i=0; i< *n; i++) { val = lo + i*sd; /* check for essentially zero relative to the range */ if (ABS(val) < range/100000.) val=0.; sprintf(tbuf, "%.*e", prec, val); lastz = prec + 1 + (tbuf[0]=='-' ? 1 : 0); /* least significant digit */ for (z=prec, cp= &tbuf[lastz]; z>minprec; --z, --cp) if (*cp != '0') break; if (z == prec) { sprintf(fmt, "%%.%de", prec); return; } minprec = MAX(minprec, z); } sprintf(fmt, "%%.%de", minprec); } } static Object _Axes( Point o, /* origin */ Vector s, /* size */ Vector sgn, /* sign of s */ Vector ls, /* label scale */ Vector length, /* on-screen lengths of each axis */ Point *c, /* cursor */ RGBColor cxy, /* color of xy plane */ RGBColor cyz, /* color of yz plane */ RGBColor czx, /* color of zx plane */ char *xlabel, /* x axis label */ char *ylabel, /* y axis label */ char *zlabel, /* z axis label */ int frame, /* whether to show the front faces as outline */ int n, /* number of tick labels */ int nx, /* number for x axis, if n==0 */ int ny, /* number for y axis, if n==0 */ int nz, /* number for z axis, if n==0 */ int adjust, /* whether to move endpoints of axes to */ int grid, /* whether to draw the grid */ RGBColor labelcolor, /* color of labels */ RGBColor ticcolor, /* color of tics */ RGBColor axescolor, /* color of axes */ RGBColor gridcolor, /* color of grid */ float labelscale, /* scalefactor for labels */ char *fontname, /* font to use for labels */ Vector up, /* camera up vector */ Vector eye, /* camera from-to vector */ Array xlocs, Array ylocs, Array zlocs, Array xlabels, Array ylabels, Array zlabels ) { Field f = NULL; Group g = NULL; int pt=0, subx=0, suby=0, subz=0; float dx, dy, dz, lx=0, ly=0, lz=0, angle; float fs, sum, flipx, flipy, flipz, ax, ay, az, p; float flipxlab, flipylab, flipzlab; float major, minor, temp; int reverse, i; int background, frontframe, showaxes, quad; char fmtx[20], fmty[20], fmtz[20]; Error rc; float *xptr=NULL, *yptr=NULL, *zptr=NULL; char **xlabptr=NULL, **ylabptr=NULL, **zlabptr=NULL, *tmpstring; Type type; int numlabels, flipxtics, flipytics, flipztics; /* number of tick labels on each axis */ ax = ABS(s.x); ay = ABS(s.y); az = ABS(s.z); flipxtics = 0; flipytics = 0; flipztics = 0; if (n>0) { if (n==1) n = 2; sum = ax + ay + az; nx = ax / sum * n + 0.5; ny = ay / sum * n + 0.5; nz = az / sum * n + 0.5; if (nx < 2) nx=2; if (ny < 2) ny=2; if (nz < 2) nz=2; } if (n<0) { flipxtics = 1; flipytics = 1; flipztics = 1; n = -n; if (n==1) n = 2; sum = ax + ay + az; nx = ax / sum * n + 0.5; ny = ay / sum * n + 0.5; nz = az / sum * n + 0.5; } if (nx < 0) { flipxtics = 1; nx = -nx; } if (ny < 0) { flipytics = 1; ny = -ny; } if (nz < 0) { flipztics = 1; nz = -nz; } if (nx < 2) nx=2; if (ny < 2) ny=2; if (nz < 2) nz=2; /* First deal with xlocs, ylocs, zlocs, and xlabels, ylabels, zlabels */ /* Redo origin and s if xlocs etc are given */ if (xlocs) { if (!_dxfCheckLocationsArray(xlocs, &nx, &xptr)) goto error; /* o.x = xptr[0]; s.x = xptr[nx-1]-o.x; */ subx = 0; if (xlabels) { if (!DXGetArrayInfo((Array)xlabels, &numlabels, &type,NULL, NULL,NULL)) return ERROR; if (numlabels != nx) { DXSetError(ERROR_INVALID_DATA, "number of xlabels must match number of xlocations"); goto error; } if (type != TYPE_STRING) { DXSetError(ERROR_INVALID_DATA,"xlabels must be a string list"); goto error; } xlabptr = (char **)DXAllocate(nx*sizeof(char *)); for (i = 0; i< nx; i++) { if (!DXExtractNthString((Object)xlabels, i, &tmpstring)) { DXSetError(ERROR_INVALID_DATA,"invalid xlabels"); goto error; } xlabptr[i] = tmpstring; } } } if (ylocs) { if (!_dxfCheckLocationsArray(ylocs, &ny, &yptr)) goto error; /* o.y = yptr[0]; s.y = yptr[ny-1]-o.y; */ suby = 0; if (ylabels) { if (!DXGetArrayInfo((Array)ylabels, &numlabels, &type,NULL, NULL,NULL)) return ERROR; if (numlabels != ny) { DXSetError(ERROR_INVALID_DATA, "number of ylabels must match number of ylocations"); goto error; } if (type != TYPE_STRING) { DXSetError(ERROR_INVALID_DATA,"ylabels must be a string list"); goto error; } ylabptr = (char **)DXAllocate(ny*sizeof(char *)); for (i = 0; i< ny; i++) { if (!DXExtractNthString((Object)ylabels, i, &tmpstring)) { DXSetError(ERROR_INVALID_DATA,"invalid ylabels"); goto error; } ylabptr[i] = tmpstring; } } } if (zlocs) { if (!_dxfCheckLocationsArray(zlocs, &nz, &zptr)) goto error; /* o.z = zptr[0]; s.z = zptr[nz-1]-o.z; */ subz = 0; if (zlabels) { if (!DXGetArrayInfo((Array)zlabels, &numlabels, &type,NULL, NULL,NULL)) return ERROR; if (numlabels != nz) { DXSetError(ERROR_INVALID_DATA, "number of zlabels must match number of zlocations"); goto error; } if (type != TYPE_STRING) { DXSetError(ERROR_INVALID_DATA,"zlabels must be a string list"); goto error; } zlabptr = (char **)DXAllocate(nz*sizeof(char *)); for (i = 0; i< nz; i++) { if (!DXExtractNthString((Object)zlabels, i, &tmpstring)) { DXSetError(ERROR_INVALID_DATA,"invalid zlabels"); goto error; } zlabptr[i] = tmpstring; } } } /* only draw the axes lines if the background is not going to be drawn */ /* frame = 2 or 3 means that the background is not drawn */ if ((frame == 0) || (frame == 1)) { background = 1; showaxes = 0; } else { background = 0; showaxes = 1; } if ((frame == 1) || (frame == 3)) frontframe = 1; else frontframe = 0; g = DXNewGroup(); if (!g) goto error; /* deltas - may change o and s, so do this before faces */ if (nx>0) if (!xlocs) _dxfaaxes_delta(&o.x, &s.x, ls.x, &nx, &lx, &dx, fmtx, &subx, adjust); else { lx = xptr[0]; dx = s.x/(nx-1); if (!xlabels) _dxfGetFormat(xptr, fmtx, nx, ls.x); } if (ny>0) if (!ylocs) _dxfaaxes_delta(&o.y, &s.y, ls.y, &ny, &ly, &dy, fmty, &suby, adjust); else { ly = yptr[0]; dy = s.y/(ny-1); if (!ylabels) _dxfGetFormat(yptr, fmty, ny, ls.y); } if (nz>0) if (!zlocs) _dxfaaxes_delta(&o.z, &s.z, ls.z, &nz, &lz, &dz, fmtz, &subz, adjust); else { lz = zptr[0]; dz = s.z/(nz-1); if (!zlabels) _dxfGetFormat(zptr, fmtz, nz, ls.z); } /* font scaling is related to minimum delta, and limited by max length */ if (nx==0) dx = 1e20; /* XXX */ if (ny==0) dy = 1e20; /* XXX */ if (nz==0) dz = 1e20; /* XXX */ fs = FS * MIN(ABS(dx),MIN(ABS(dy),ABS(dz))); /* normalize up vector */ up = DXNormalize(up); eye = DXNormalize(eye); /* find the quadrant with respect to up */ GetProjections(up, eye, &flipx, &flipy, &flipz, &flipxlab, &flipylab, &flipzlab, sgn); reverse = sgn.x * sgn.y * sgn.z; /* futz for scaling */ if (xlocs) { for (i=0; i FMAX) fs = FMAX / p; } if (s.y!=0) { p = ABS(length.y/s.y); if (fs * p > FMAX) fs = FMAX / p; } if (s.z!=0) { p = ABS(length.z/s.z); if (fs * p > FMAX) fs = FMAX / p; } /* only draw faces for frame = 0 or 1 */ if (background) { /* back faces */ f = DXNewField(); if (!f) goto error; DXSetMember(g, NULL, (Object) f); DXSetFloatAttribute((Object)f, "fuzz", -4); DXSetFloatAttribute((Object)f, "specular", 0); if (ax>0 && ay>0) face(f, &pt, o, DXVec(s.x,0,0), DXVec(0,s.y,0), cxy); if (ay>0 && az>0) face(f, &pt, o, DXVec(0,s.y,0), DXVec(0,0,s.z), cyz); if (az>0 && ax>0) face(f, &pt, o, DXVec(0,0,s.z), DXVec(s.x,0,0), czx); if (!DXEndField(f)) goto error; } if (ax>0 && ay>0) { rc = mark(g, frontframe, showaxes, xlabel, ylabel, flipxlab, -flipylab, reverse, o, s, sgn, c, nx,flipxtics, subx,lx,dx,ls.x,-flipx,fmtx, ny,flipytics, suby,ly,dy,ls.y,flipy,fmty,fs,0,1,2, grid, length.x, length.y, labelcolor, ticcolor, axescolor, gridcolor, labelscale, fontname, xptr, yptr, xlabptr, ylabptr); if (!rc) goto error; } if (ay>0 && az>0) { Vector cc; if (c) cc = DXVec(c->y, c->z, c->x); rc = mark(g, frontframe, showaxes, ylabel, zlabel, flipylab, -flipzlab, reverse, DXVec(o.y,o.z,o.x), DXVec(s.y,s.z,s.x), DXVec(sgn.y,sgn.z,sgn.x), c ? &cc : NULL, ny,flipytics, suby,ly,dy,ls.y,-flipy,fmty, nz,flipztics, subz,lz,dz,ls.z,flipz,fmtz,fs,2,0,1, grid, length.y, length.z, labelcolor, ticcolor, axescolor, gridcolor, labelscale, fontname, yptr, zptr, ylabptr, zlabptr); if (!rc) goto error; } if (az>0 && ax>0) { Vector cc; if (c) cc = DXVec(c->z, c->x, c->y); rc = mark(g, frontframe, showaxes, zlabel, xlabel, flipzlab, -flipxlab, reverse, DXVec(o.z,o.x,o.y), DXVec(s.z,s.x,s.y), DXVec(sgn.z,sgn.x,sgn.y), c? &cc : NULL, nz,flipztics, subz,lz,dz,ls.z,-flipz,fmtz, nx,flipxtics, subx,lx,dx,ls.x,flipx,fmtx, fs,1,2,0, grid, length.z, length.x, labelcolor, ticcolor, axescolor, gridcolor, labelscale, fontname, zptr,xptr,zlabptr,xlabptr); if (!rc) goto error; } /* all ok */ DXFree((Pointer)xptr); DXFree((Pointer)yptr); DXFree((Pointer)zptr); DXFree((Pointer)xlabptr); DXFree((Pointer)ylabptr); DXFree((Pointer)zlabptr); return (Object) g; error: DXFree((Pointer)xptr); DXFree((Pointer)yptr); DXFree((Pointer)zptr); DXFree((Pointer)xlabptr); DXFree((Pointer)ylabptr); DXFree((Pointer)zlabptr); DXDelete((Object)g); return NULL; } struct axes_struct{ Object object; Camera camera; char *xlabel; char *ylabel; char *zlabel; float labelscale; char *font; int t; int tx; int ty; int tz; Object corners; int frame; int adjust; Point *cursor; char *plottypex; char *plottypey; char *plottypez; int grid; RGBColor labelcolor; RGBColor ticcolor; RGBColor axescolor; RGBColor gridcolor; RGBColor backgroundcolor; Array xlocs; Array ylocs; Array zlocs; Array xlabels; Array ylabels; Array zlabels; }; extern Object _dxfAutoAxes(Pointer p) { Object object, corners; Camera cam; char *xlabel, *ylabel, *zlabel, *plottypex, *plottypey, *plottypez; char *fontname; int n, nx, ny, nz, frame, adjust, grid; Point *cursor; RGBColor labelcolor, ticcolor, axescolor, gridcolor, backgroundcolor; Point box[8], origin, eye, min, max, mm[2], c; float mm2[4]; Vector scale, sgn, ls, length, o, from, to, up; int i, numx, numy, numz; float l, labelscale; Object axes; Group g; Matrix t; Array xlocs, ylocs, zlocs, xlabels, ylabels, zlabels; float *xptr, *yptr, *zptr; struct axes_struct *axes_struct = (struct axes_struct *)p; object = axes_struct->object; cam = axes_struct->camera; xlabel = axes_struct->xlabel; ylabel = axes_struct->ylabel; zlabel = axes_struct->zlabel; labelscale = axes_struct->labelscale; fontname = axes_struct->font; n = axes_struct->t; nx = axes_struct->tx; ny = axes_struct->ty; nz = axes_struct->tz; corners = axes_struct->corners; frame = axes_struct->frame; adjust = axes_struct->adjust; cursor = axes_struct->cursor; plottypex = axes_struct->plottypex; plottypey = axes_struct->plottypey; plottypez = axes_struct->plottypez; grid = axes_struct->grid; labelcolor = axes_struct->labelcolor; ticcolor = axes_struct->ticcolor; axescolor = axes_struct->axescolor; gridcolor = axes_struct->gridcolor; backgroundcolor = axes_struct->backgroundcolor; xlocs = axes_struct->xlocs; ylocs = axes_struct->ylocs; zlocs = axes_struct->zlocs; xlabels = axes_struct->xlabels; ylabels = axes_struct->ylabels; zlabels = axes_struct->zlabels; /* label scale based on transform */ if (DXGetObjectClass(object)==CLASS_XFORM) { Matrix m; DXGetXformInfo((Xform)object, NULL, &m); if (m.A[0][1]==0 && m.A[0][2]==0 && m.A[1][0]==0 && m.A[1][2]==0 && m.A[2][0]==0 && m.A[2][1]==0) ls = DXVec(1.0/m.A[0][0], 1.0/m.A[1][1], 1.0/m.A[2][2]); else ls = DXVec(1, 1, 1); } else { ls = DXVec(1, 1, 1); } /* get camera info */ if (cam) { if (DXGetObjectClass((Object)cam)!=CLASS_CAMERA) DXErrorReturn(ERROR_BAD_PARAMETER, "bad camera"); DXGetView(cam, &from, &to, &up); } else { to = DXVec(0,0,0); from = DXVec(0,0,1); up = DXVec(0,1,0); } eye = DXSub(from, to); /* obtain min/max */ if (DXExtractParameter(corners, TYPE_FLOAT, 3, 2, (Pointer)mm)) { min.x = mm[0].x / ls.x; min.y = mm[0].y / ls.y; min.z = mm[0].z / ls.z; max.x = mm[1].x / ls.x; max.y = mm[1].y / ls.y; max.z = mm[1].z / ls.z; } else if (DXExtractParameter(corners, TYPE_FLOAT, 2, 2, (Pointer)mm2)) { min.x = mm2[0] / ls.x; min.y = mm2[1] / ls.y; min.z = 0; max.x = mm2[2] / ls.x; max.y = mm2[3] / ls.y; max.z = 0; } else { if (corners) { if (!DXBoundingBox(corners, box)) { /* corners parameter must either be 2 vectors or be an object with a bounding box */ DXSetError(ERROR_BAD_PARAMETER, "#11815"); return ERROR; } min.x = min.y = min.z = DXD_MAX_FLOAT; max.x = max.y = max.z = -DXD_MAX_FLOAT; for (i=0; i<8; i++) { if (box[i].x < min.x) min.x = box[i].x; if (box[i].y < min.y) min.y = box[i].y; if (box[i].z < min.z) min.z = box[i].z; if (box[i].x > max.x) max.x = box[i].x; if (box[i].y > max.y) max.y = box[i].y; if (box[i].z > max.z) max.z = box[i].z; } } else { /* corners were not given; use the actual bounding box */ if (!DXBoundingBox(object, box)) { if (DXGetError()==ERROR_NONE) DXSetError(ERROR_BAD_PARAMETER, "bad input parameter (has no bounding box)"); return ERROR; } min.x = min.y = min.z = DXD_MAX_FLOAT; max.x = max.y = max.z = -DXD_MAX_FLOAT; for (i=0; i<8; i++) { if (box[i].x < min.x) min.x = box[i].x; if (box[i].y < min.y) min.y = box[i].y; if (box[i].z < min.z) min.z = box[i].z; if (box[i].x > max.x) max.x = box[i].x; if (box[i].y > max.y) max.y = box[i].y; if (box[i].z > max.z) max.z = box[i].z; } /* if the user gave tic positions, these can override the object's bounding box (but should not override given corners) */ if (xlocs) { if (!DXGetArrayInfo(xlocs, &numx, NULL, NULL, NULL, NULL)) return ERROR; xptr = DXGetArrayData(xlocs); if (!xptr) return ERROR; if (xptr[0]/ls.x < min.x) min.x = xptr[0]/ls.x; if (xptr[numx-1]/ls.x > max.x) max.x = xptr[numx-1]/ls.x; } if (ylocs) { if (!DXGetArrayInfo(ylocs, &numy, NULL, NULL, NULL, NULL)) return ERROR; yptr = DXGetArrayData(ylocs); if (!yptr) return ERROR; if (yptr[0]/ls.y < min.y) min.y = yptr[0]/ls.y; if (yptr[numy-1]/ls.y > max.y) max.y = yptr[numy-1]/ls.y; } if (zlocs) { if (!DXGetArrayInfo(zlocs, &numz, NULL, NULL, NULL, NULL)) return ERROR; zptr = DXGetArrayData(zlocs); if (!zptr) return ERROR; if (zptr[0]/ls.z < min.z) min.z = zptr[0]/ls.z; if (zptr[numz-1]/ls.z > max.z) max.z = zptr[numz-1]/ls.z; } } } /* XXX - for now, align the box */ box[0] = DXPt(min.x, min.y, min.z); box[1] = DXPt(min.x, min.y, max.z); box[2] = DXPt(min.x, max.y, min.z); box[3] = DXPt(min.x, max.y, max.z); box[4] = DXPt(max.x, min.y, min.z); box[5] = DXPt(max.x, min.y, max.z); box[6] = DXPt(max.x, max.y, min.z); box[7] = DXPt(max.x, max.y, max.z); /* origin and scale */ origin = box[0]; scale = DXSub(box[7], origin); sgn.x = SGN(scale.x); sgn.y = SGN(scale.y); sgn.z = SGN(scale.z); /* 3-d: adjust for viewpoint */ /* problem is that this takes no account of "up" */ if (scale.z != 0) { if (eye.x * sgn.x < 0) origin.x += scale.x, scale.x = -scale.x, sgn.x = -sgn.x; if (eye.y * sgn.y < 0) origin.y += scale.y, scale.y = -scale.y, sgn.y = -sgn.y; if (eye.z * sgn.z < 0) origin.z += scale.z, scale.z = -scale.z, sgn.z = -sgn.z; /* 2-d: labels left, bottom */ } else { if (sgn.x * eye.z > 0) origin.x += scale.x, scale.x = -scale.x, sgn.x = -sgn.x; if (sgn.y > 0) origin.y += scale.y, scale.y = -scale.y, sgn.y = -sgn.y; if (eye.z < 0) sgn.z = -sgn.z; } /* transform the box, record on-screen lengths of each side */ if (cam) { Vector tx, ty, tz; Matrix m; m = DXGetCameraMatrix(cam); o = DXApply(origin, m); tx = DXApply(DXAdd(origin,DXVec(scale.x,0,0)), m); ty = DXApply(DXAdd(origin,DXVec(0,scale.y,0)), m); tz = DXApply(DXAdd(origin,DXVec(0,0,scale.z)), m); if (DXGetPerspective(cam, NULL, NULL)) { o.x /= o.z; o.y /= o.z; /* XXX */ tx.x /= tx.z; tx.y /= tx.z; /* XXX */ ty.x /= ty.z; ty.y /= ty.z; /* XXX */ tz.x /= tz.z; tz.y /= tz.z; /* XXX */ } tx = DXSub(tx,o); ty = DXSub(ty,o); tz = DXSub(tz,o); tx.z = ty.z = tz.z = 0; length.x = DXLength(tx); length.y = DXLength(ty); length.z = DXLength(tz); /* add up the sides, figure # labels from that */ /* default condition */ if (n==-9999) { l = length.x + length.y + length.z; n = l / 30; if (n>100) { DXWarning("using 100 tick marks instead of %d", n); n = 100; } if (n < 2) n=2; } } else { /* assume on-screen size is same as given size */ length = scale; } /* scale the cursor */ if (cursor) c = *cursor; /* do the axes */ axes = _Axes(origin, scale, sgn, ls, length, cursor? &c : NULL, backgroundcolor, backgroundcolor, backgroundcolor, xlabel, ylabel, zlabel, frame, n, nx, ny, nz, adjust, grid, labelcolor, ticcolor, axescolor, gridcolor, labelscale, fontname, up, eye, xlocs,ylocs,zlocs,xlabels,ylabels,zlabels); if (!axes) return NULL; if (! DXSetIntegerAttribute(axes, "pickable", 0)) { DXDelete((Object)axes); return NULL; } g = DXNewGroup(); if (!g) return NULL; DXSetMember(g, NULL, axes); DXSetMember(g, NULL, object); return (Object)g; } extern Pointer _dxfNewAxesObject(void) { struct axes_struct *new; new = (struct axes_struct *)DXAllocate(sizeof(struct axes_struct)); /* fill the defaults */ if (new) { new->object=NULL; new->xlabel="x"; new->ylabel="y"; new->zlabel="z"; new->font="fixed"; new->t=10; new->tx=0; new->ty=0; new->tz=0; new->corners=NULL; new->frame=0; new->adjust=0; new->cursor=NULL; new->plottypex="lin"; new->plottypey="lin"; new->plottypez="lin"; new->grid=0; new->labelcolor=LABELCOLOR; new->ticcolor=TICKCOLOR; new->axescolor=AXESCOLOR; new->gridcolor=GRIDCOLOR; new->backgroundcolor=BACKGROUNDCOLOR; return (Pointer)new; } return NULL; } extern Error _dxfSetAxesCharacteristic(Pointer p, char *characteristic, Pointer value) { struct axes_struct *st = (struct axes_struct *)p; /* objects */ if (!strcmp(characteristic,"OBJECT")) st->object = *(Object *)value; else if (!strcmp(characteristic,"CORNERS")) st->corners = *(Object *)value; /* cameras */ else if (!strcmp(characteristic,"CAMERA")) st->camera = *(Camera *)value; /* strings */ else if (!strcmp(characteristic,"XLABEL")) st->xlabel = (char *)value; else if (!strcmp(characteristic,"YLABEL")) st->ylabel = (char *)value; else if (!strcmp(characteristic,"ZLABEL")) st->zlabel = (char *)value; else if (!strcmp(characteristic,"TYPEX")) st->plottypex = (char *)value; else if (!strcmp(characteristic,"TYPEY")) st->plottypey = (char *)value; else if (!strcmp(characteristic,"TYPEZ")) st->plottypez = (char *)value; else if (!strcmp(characteristic,"FONT")) st->font = (char *)value; /* integers */ else if (!strcmp(characteristic,"NUMTICS")) st->t = *(int *)value; else if (!strcmp(characteristic,"NUMTICSX")) st->tx = *(int *)value; else if (!strcmp(characteristic,"NUMTICSY")) st->ty = *(int *)value; else if (!strcmp(characteristic,"NUMTICSZ")) st->tz = *(int *)value; else if (!strcmp(characteristic,"FRAME")) st->frame = *(int *)value; else if (!strcmp(characteristic,"ADJUST")) st->adjust = *(int *)value; else if (!strcmp(characteristic,"GRID")) st->grid = *(int *)value; /* Point * */ else if (!strcmp(characteristic,"CURSOR")) st->cursor = (Point *)value; /* float */ else if (!strcmp(characteristic,"LABELSCALE")) { st->labelscale= *(float *)value; } /* colors */ else if (!strcmp(characteristic,"TICKCOLOR")) st->ticcolor = *(RGBColor *)value; else if (!strcmp(characteristic,"LABELCOLOR")) st->labelcolor = *(RGBColor *)value; else if (!strcmp(characteristic,"AXESCOLOR")) st->axescolor = *(RGBColor *)value; else if (!strcmp(characteristic,"GRIDCOLOR")) st->gridcolor = *(RGBColor *)value; else if (!strcmp(characteristic,"BACKGROUNDCOLOR")) st->backgroundcolor = *(RGBColor *)value; /* Arrays */ else if (!strcmp(characteristic,"XLOCS")) st->xlocs = (Array)value; else if (!strcmp(characteristic,"YLOCS")) st->ylocs = (Array)value; else if (!strcmp(characteristic,"ZLOCS")) st->zlocs = (Array)value; else if (!strcmp(characteristic,"XLABELS")) st->xlabels = (Array)value; else if (!strcmp(characteristic,"YLABELS")) st->ylabels = (Array)value; else if (!strcmp(characteristic,"ZLABELS")) st->zlabels = (Array)value; else { DXSetError(ERROR_INVALID_DATA,"unknown option %s", characteristic); return ERROR; } return OK; } extern Error _dxfCheckLocationsArray(Array locs, int *n, float **p) { int numitems; float *ptr=NULL; /* check whether the array is floating point real and all that */ if (!DXGetArrayInfo(locs, &numitems, NULL, NULL, NULL, NULL)) goto error; ptr = (float *)DXAllocate(numitems*sizeof(float)); *n = numitems; if (!DXExtractParameter((Object)locs, TYPE_FLOAT, 0, numitems, ptr)) { DXSetError(ERROR_INVALID_DATA,"tic locations must be int or floats"); goto error; } *p = ptr; return OK; error: return ERROR; } extern Error _dxfGetFormat(float *locs, char *fmt, int num, float scale) { int width, precision, i; char tbuf[32], *cp; int prec,z,minprec=1, lastz, maxwidth; char *cstring; float hi,lo,lg,range; double val; /* format */ prec = 6; minprec = 1; lo = locs[0]; hi = locs[num-1]; lg = log10(MAX(ABS(lo),ABS(hi))); width = 1 + FL(lg); sprintf(tbuf, "%%%d.%df", width, prec); /* look for trailing zeros */ for (i=0; iminprec; --z, --cp) if (*cp != '0') break; minprec = MAX(minprec, z); } if (width<1) width = 1; if (minprec>0) width += minprec + 1; if (lo<0 || hi<0) width += 1; cstring = (char *)getenv("DXAXESMAXWIDTH"); if (cstring != NULL) maxwidth =atoi(cstring); else maxwidth = MAXWIDTH; if (width <= maxwidth) { sprintf(fmt, "%%%d.%df", width, minprec); return OK; } /* if the width of the format would be too long to look nice, * use exponential notation and try to print the minimum number * of significant digits. */ { /* exp format is [-]m.dddddde+XX, where the precision controls the * number of digits in the fraction. start with the default precision * of 6 and look for trailing 0's. if there are none, you are done. * if some or all are 0's, go through each tick label and take the * largest value which isn't 0. the minimum return value is 1; this * could be changed to 0 if you prefer "me+XX" instead of "m.0e+XX". */ range = ABS(hi-lo); for (i=0; i< num; i++) { val = locs[i]; /* check for essentially zero relative to the range */ if (ABS(val) < range/100000.) val=0.; sprintf(tbuf, "%.*e", prec, val); lastz = prec + 1 + (tbuf[0]=='-' ? 1 : 0); /* least significant digit */ for (z=prec, cp= &tbuf[lastz]; z>minprec; --z, --cp) if (*cp != '0') break; if (z == prec) { sprintf(fmt, "%%.%de", prec); return OK; } minprec = MAX(minprec, z); } sprintf(fmt, "%%.%de", minprec); } return OK; }