adrian@milton.u.washington.edu (Adrian Mariano) (05/02/91)
Submitted-by: Adrian Mariano <adrian@milton.u.washington.edu> Posting-number: Volume 19, Issue 12 Archive-name: plot3d/part01 This program plots 3d surfaces as wire meshes. It supports cartesian, cylindrical and spherical coordinates. This version compiles under MSDOS with Turbo C. It shouldn't be very difficult to port to other environments, however. Adrian Mariano ---------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # calc.c # graph.c # makefile # plot.c # plot3d.c # plot3d.h # readme # This archive created: Tue Apr 30 14:21:27 1991 export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'calc.c' then echo shar: "will not over-write existing file 'calc.c'" else cat << \SHAR_EOF > 'calc.c' /* Plot3d -- calc.c, the function evaluation code By Karl Crary -- kc2m+@andrew.cmu.edu Copyright (c) 1991 by Karl Crary You may use and distribute this program as much as you like so long as you do not charge for this service. I am not liable for failure of this program to perform in any way. */ #include "plot3d.h" int convert(char string[],char **errormsg,char stackno); /* convert infix to RPN in fstack */ void process(char operator,unsigned char *findex,char cstack[],char *cstacktop,term fstack[]); char precedence(char operator, char push); void simplify(char stackno); double gamma(double arg); extern char stop; char ferr; double ff(double arg1, double arg2, char stackno) { static unsigned char begin = 0; long double stack[25], intermediate; unsigned char findex; char height, ii, recurse; if ((arg1 < -MAXDOUBLE) || (arg1 > MAXDOUBLE)) { ferr = 1; return(0); } if (range(-MINDOUBLE,arg1,MINDOUBLE)) arg1 = 0; ferr = 0; height = recurse = 0; if (begin) recurse = 1; for ((findex = begin),(begin = 0); fstack[stackno][findex].operator != '='; findex++) { if (!stop && kbhit()) { ii = getch(); if (ii == ' ') stop = 2; if (ii == 24) terminate(); if (ii == 27) { stop = 1; return(0); } } switch (fstack[stackno][findex].operator) { case '#' : if (height >= 25) { ferr = 1; break; } for (ii = height; ii > 0; ii--) stack[ii] = stack[ii-1]; height++; stack[0] = fstack[stackno][findex].operand; break; case 'y' : if (height >= 25) { ferr = 1; break; } for (ii = height; ii > 0; ii--) stack[ii] = stack[ii-1]; height++; stack[0] = arg2; break; case 'x' : if (height >= 25) { ferr = 1; break; } for (ii = height; ii > 0; ii--) stack[ii] = stack[ii-1]; height++; stack[0] = arg1; break; case '^' : if (((stack[1] == 0) && (stack[0] <= 0)) || ((stack[1] < 0) && (stack[0] != floor(stack[0])))) ferr = 1; else intermediate = pow(stack[1],stack[0]); if (intermediate == HUGE_VAL) ferr = 1; else stack[0] = intermediate; goto popstack; case '\\' : /* root */ if ((stack[0] == 0) && (stack[1] <= 0)) ferr = 1; /* division by zero */ if (stack[1] == 0) ferr = 1; /* zeroth root */ if ((stack[0] < 0) && (floor((stack[1]+1)/2) != (stack[1]+1)/2)) ferr = 1; if (!ferr) stack[0] = (stack[0] < 0 ? -1 : 1)*pow(fabs(stack[0]),1/stack[1]); goto popstack; case '*' : stack[0] = stack[0] * stack[1]; goto popstack; case '/' : if (stack[0] == 0) ferr = 1; else stack[0] = stack[1] / stack[0]; goto popstack; case '+' : stack[0] = stack[0] + stack[1]; goto popstack; case '-' : stack[0] = stack[1] - stack[0]; goto popstack; case 'm' : stack[0] = -stack[0]; break; case 's' : stack[0] = sin(stack[0]); break; case 'c' : stack[0] = cos(stack[0]); break; case 't' : stack[0] = tan(stack[0]); break; case 'S' : if (!range(-1,stack[0],1)) ferr = 1; else stack[0] = asin(stack[0]); break; case 'C' : if (!range(-1,stack[0],1)) ferr = 1; else stack[0] = acos(stack[0]); break; case 'T' : stack[0] = atan(stack[0]); break; case 1 : /* sinh */ stack[0] = sinh(stack[0]); break; case 2 : /* cosh */ stack[0] = cosh(stack[0]); break; case 3 : /* tanh */ stack[0] = tanh(stack[0]); break; case 4 : /* invsinh */ stack[0] = log(stack[0] + sqrt(pow(stack[0],2) + 1)); break; case 5 : /* invcosh */ intermediate = pow(stack[0],2); if (intermediate < 1) ferr = 1; else { intermediate = stack[0] + sqrt(intermediate - 1); if (intermediate <= 0) ferr = 1; else stack[0] = log(stack[0] + sqrt(pow(stack[0],2) - 1)); } break; case 6 : /* invtanh */ if (stack[0] == 1) ferr = 1; else { intermediate = (1 + stack[0])/(1 - stack[0]); if (intermediate <= 0) ferr = 1; else stack[0] = log(intermediate)/2; } break; case 'l' : if (stack[0] <= 0) ferr = 1; else stack[0] = log10(stack[0]); break; case 'n' : if (stack[0] <= 0) ferr = 1; else stack[0] = log(stack[0]); break; case 'a' : /* absolute value */ stack[0] = fabs(stack[0]); break; case 'g' : /* gamma */ stack[0] = gamma(stack[0]); break; case 'f' : /* user function */ stack[0] = ff(stack[0],0,1); break; popstack: height--; for (ii = 1; ii < height; ii++) stack[ii] = stack[ii+1]; break; } if ((stack[0] < -2 * MAXDOUBLE) || (stack[0] > 2 * MAXDOUBLE)) ferr = 1; /* roundoff makes MAXDOUBLE > MAXDOUBLE */ if (ferr) return(0); } if (recurse) begin = findex; /* return where ended */ return(stack[0]); } int matherr(struct exception *e) { ferr = 1; return(1); } double gamma(double arg) { double xx, total; char ii; /* magic gamma generating coefficients */ static double coef[7] = { 0.988205891,-0.897056937, 0.918206857, -0.756704078, 0.482199394,-0.193527818, 0.035868343}; if (!stop && kbhit()) { ii = getch(); if (ii == ' ') stop = 2; if (ii == 24) terminate(); if (ii == 27) { stop = 1; return(0); } } if (arg <= 0) { if (arg == floor(arg)) { ferr = 1; return(0); } else { arg = -arg + 1; /* recurse if x < 0 */ return(M_PI/(gamma(arg)*sin(M_PI*arg))); } } else if (arg < 1) return(gamma(arg+1)/arg); /* recurse if x < 1 */ else if (arg <= 2) { arg--; total = 1 - 0.577191652 * arg; xx = arg; for (ii = 0; ii <= 6; ii++) { xx *= arg; total += coef[ii] * xx; } return(total); } else { /* arg > 2 */ xx = arg * arg; /* another approximation for larger x */ total = (arg - 0.5) * log(arg); total -= arg; total += log(2 * M_PI) / 2; total += 1 / (12 * arg); total -= 1 / (360 * arg * xx); total += 1 / (1260 * arg * xx * xx); total -= arg / (1680 * xx * xx * xx * xx); return(exp(total)); } } /* errormessages */ char errorlist[6][45] = {"Values must precede each binary operator.",\ "Unmatched right parenthesis.",\ "Expression must end with value.",\ "Stack overflow.",\ "Function ends in operator.",\ "Unbalanced parentheses."}; int convert(char string[], char **errormsg, char stackno) { unsigned char findex; char *cursor, value, inverse; char cstack[100], cstacktop; #define PROCESS(operator) process((operator),&findex,cstack,&cstacktop,fstack[stackno]) cursor = string; findex = value = inverse = 0; cstack[0] = '('; /* initialize cstack */ cstacktop = 0; while (*cursor != 0) { switch (*cursor) { case '=' : /* explicit definition */ cursor++; findex = value = inverse = 0; /* discard everything and start over */ cstack[0] = '('; cstacktop = 0; break; case 'h' : /* hyperbolics already taken care of */ cursor++; break; case '0' : /* numbers */ case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : case '8' : case '9' : case '.' : if (value) PROCESS('$'); /* process implicit multiplication */ fstack[stackno][findex].operator = '#'; /* term is a number */ fstack[stackno][findex].operand = strtod(cursor,&cursor); /* store number */ findex++; value = 1; break; case 'x' : /* variables */ case 'y' : case 'o' : /* theta */ case 'r' : case 'u' : case 'p' : /* pi */ case 'e' : /* e */ case '!' : /* infinity */ if (value) PROCESS('$'); switch (*cursor) { case 'p' : fstack[stackno][findex].operator = '#'; fstack[stackno][findex].operand = M_PI; break; case 'e' : fstack[stackno][findex].operator = '#'; fstack[stackno][findex].operand = M_E; break; case '!' : fstack[stackno][findex].operator = '#'; fstack[stackno][findex].operand = MAXDOUBLE; break; case 'y' : fstack[stackno][findex].operator = 'y'; break; default : /* variables */ fstack[stackno][findex].operator = 'x'; break; } findex++; cursor++; value = 1; break; case '^' : /* operators */ case '\\' : case '*' : case '/' : case '+' : case '-' : if (!value) { /* process unary operators */ if (*cursor == '+') { /* unary plus, do nothing */ cursor++; break; } else if (*cursor == '-') { /* unary minus */ PROCESS('m'); cursor++; break; } if (errormsg != NULL) (*errormsg) = errorlist[0]; return(cursor-string); /* return location of error */ } PROCESS(*cursor); /* process operator */ cursor++; value = 0; break; case '(' : if (value) PROCESS('$'); /* process implicit multiplication */ PROCESS('('); /* process paren */ cursor++; value = 0; break; case ')' : if (!value) { /* expression ends in operator */ if (errormsg != NULL) *errormsg = errorlist[2]; return(cursor-string); } PROCESS(')'); cstacktop--; /* zap open paren */ if (cstacktop < 0) { if (errormsg != NULL) (*errormsg) = errorlist[1]; return(cursor-string); /* unbalanced parens */ } cursor++; value = 1; break; case 'd' : if (value) PROCESS('$'); /* process implicit multiplication */ fstack[stackno][findex].operator = 'd'; /* mark start of derivative */ findex++; PROCESS('='); /* process end of derivative */ cursor++; /* will be followed by parenthesis */ value = 0; break; case 'A' : cursor++; /* inverse of non-trig prevented in IO */ inverse = 32; case 's' : case 'c' : case 't' : if (value) PROCESS('$'); /* process implicit multiplication */ if (cursor[1] != 'h') PROCESS(*cursor - inverse); /* inverse is lowercase */ else switch (*cursor) { case 's' : PROCESS(inverse ? 4 : 1); break; case 'c' : PROCESS(inverse ? 5 : 2); break; case 't' : PROCESS(inverse ? 6 : 3); break; } /* advance cursor on next pass */ cursor++; value = inverse = 0; break; case ' ' : cursor++; break; default : /* functions */ if (value) PROCESS('$'); /* process implicit multiplication */ PROCESS(*cursor); cursor++; value = 0; break; } if ((cstacktop >= 100) || (findex >= 99)) { if (errormsg != NULL) *errormsg = errorlist[3]; return(cursor-string); /* stack overflow */ } } if (!value) { if (errormsg != NULL) *errormsg = errorlist[4]; return(cursor-1-string); /* function ends with operator, return location error */ } PROCESS(')'); /* flush conversion stack */ if (cstacktop != 0) { /* unbalanced parens */ if (errormsg != NULL) *errormsg = errorlist[5]; return(cursor-string); } if (findex >= 99) { if (errormsg != NULL) *errormsg = errorlist[3]; return(cursor-string); /* stack overflow or unbalanced parens */ } fstack[stackno][findex].operator = '='; return(-1); } void process(char operator,unsigned char *findex,char cstack[],char *cstacktop,term fstack[]) { char pushprec; pushprec = precedence(operator,1); while (precedence(cstack[*cstacktop],0) >= pushprec) { if (cstack[*cstacktop] == '$') cstack[*cstacktop] = '*'; fstack[*findex].operator = cstack[*cstacktop]; if (++(*findex) >= 99) break; /* stack overflow */ (*cstacktop)--; } if ((operator != ')') && (*cstacktop < 99)) { (*cstacktop)++; cstack[*cstacktop] = operator; } } char precedence(char operator, char push) { switch (operator) { case '(' : return(push ? 10 : 0); case ')' : return(1); case '=' : /* end of derivative */ return(9); case '$' : /* implicit multiplication */ return(6); case '\\' : case '^' : return(push ? 7 : 5); case '*' : case '/' : return(3); case '+' : case '-' : return(2); default : /* functions */ return(push ? 8 : 4); } } void simplify(char stackno) { int ii, jj, restart; char last, prevlast; restart = -1; last = prevlast = 0; for (ii = 0; fstack[stackno][ii].operator != '='; ii++) switch (fstack[stackno][ii].operator) { case '+' : /* binary operators */ case '-' : case '/' : case '*' : case '\\' : case '^' : if (restart == ii - 1) { restart = ii; break; } if (last && prevlast) { /* load auxiliary function to calculate simplified value */ fstack[maxffs][0].operator = fstack[stackno][ii-2].operator; /* might not be # */ fstack[maxffs][0].operand = fstack[stackno][ii-2].operand; fstack[maxffs][1].operator = fstack[stackno][ii-1].operator; /* might not be # */ fstack[maxffs][1].operand = fstack[stackno][ii-1].operand; fstack[maxffs][2].operator = fstack[stackno][ii].operator; fstack[maxffs][3].operator = '='; /* simplify stack */ ii -= 2; /* update stack position */ fstack[stackno][ii].operand = ff(0,0,maxffs); /* calculate new value */ fstack[stackno][ii].operator = '#'; for (jj = ii+1; fstack[stackno][jj+1].operator != '='; jj++) { /* shift stack left by 2 */ fstack[stackno][jj].operator = fstack[stackno][jj+2].operator; fstack[stackno][jj].operand = fstack[stackno][jj+2].operand; } last = prevlast = 0; ii = restart; /* restart */ continue; } else last = 0; /* prevlast irrelevant */ break; case '#' : case 'p' : case 'e' : case '!' : prevlast = last; last = 1; break; case 'x' : case 'y' : restart = ii; /* no need to restart before unsimplifyable character */ last = 0; /* prevlast irrelevant */ break; case 'm' : if (fstack[stackno][ii-1].operator == 'm') { /* nuke double negate */ for (jj = ii-1; fstack[stackno][jj+1].operator != '='; jj++) { /* shift stack left by 2 */ fstack[stackno][jj].operator = fstack[stackno][jj+2].operator; fstack[stackno][jj].operand = fstack[stackno][jj+2].operand; } last = prevlast = 0; ii = restart; continue; } /* no break */ default : /* unary operators */ if (restart == ii - 1) { restart = ii; break; } if (last) { /* load auxiliary function to calculate simplified value */ fstack[maxffs][0].operator = fstack[stackno][ii-1].operator; /* might not be # */ fstack[maxffs][0].operand = fstack[stackno][ii-1].operand; fstack[maxffs][1].operator = fstack[stackno][ii].operator; fstack[maxffs][2].operator = '='; /* simplify stack */ ii -= 1; /* update stack position */ fstack[stackno][ii].operand = ff(0,0,maxffs); /* calculate new value */ fstack[stackno][ii].operator = '#'; for (jj = ii+1; fstack[stackno][jj].operator != '='; jj++) { /* shift stack left */ fstack[stackno][jj].operator = fstack[stackno][jj+1].operator; fstack[stackno][jj].operand = fstack[stackno][jj+1].operand; } last = prevlast = 0; ii = restart; /* restart */ continue; } else last = 0; /* prevlast irrelevant */ break; } } SHAR_EOF fi if test -f 'graph.c' then echo shar: "will not over-write existing file 'graph.c'" else cat << \SHAR_EOF > 'graph.c' /* Plot3d -- graph.c, the graphics code By Adrian Mariano -- adrian@milton.u.washington.edu Copyright (c) 1991 by Adrian Mariano You may use and distribute this program as much as you like so long as you do not charge for this service. I am not liable for failure of this program to perform in any way. */ #include <graphics.h> #include <conio.h> #include <dos.h> int returnmode, maxx, maxy, mode; /* This function should return to text mode from graphics mode */ void backtotext() { restorecrtmode(); textmode(returnmode); } /* Enter graphics mode from text mode */ void entergraphics() { setgraphmode(mode); cleardevice(); setfillstyle(1, 0); /* Sections will be black filled */ } /* Do graphics initialization, including setting maxx and maxy to the maximum x and y values */ void initgraphics() { int mm, driver = DETECT; char *path; struct text_info ti; gettextinfo(&ti); if (ti.screenheight == 50 || ti.screenheight == 43) returnmode = C4350; else returnmode = C80; path = ""; detectgraph((int far *) (&driver), (int far *) (&mode)); initgraph((int far *) (&driver), (int far *) (&mode), (char far *) (path)); if ((mm = graphresult()) != grOk) { printf("Error: %d\n", mm); exit(0); } maxx = getmaxx(); maxy = getmaxy(); backtotext(); } /* Draw the quadrilateral specified by the four coordinate pairs, filled in with a different color than the boundaries are drawn in */ #pragma argsused void fill(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { fillpoly(4, MK_FP(_SS, &x1)); } /* Draw a line */ void drawline(int x1, int x2, int x3, int x4) { line(x1, x2, x3, x4); } SHAR_EOF fi if test -f 'makefile' then echo shar: "will not over-write existing file 'makefile'" else cat << \SHAR_EOF > 'makefile' # Makefile for Turbo C++ 1.0 # Place your library directory below LIB=c:\prog\c\lib plot3d.exe: plot.obj calc.obj plot3d.obj graph.obj tlink /c /x $(LIB)\c0h plot3d plot calc graph,plot3d,,\ $(LIB)\graphics $(LIB)\emu $(LIB)\mathh $(LIB)\ch .c.obj: bcc -mh -c -G $*.c SHAR_EOF fi if test -f 'plot.c' then echo shar: "will not over-write existing file 'plot.c'" else cat << \SHAR_EOF > 'plot.c' /* Plot3d -- plot.c, the plotting code By Adrian Mariano -- adrian@milton.u.washington.edu Copyright (c) 1991 by Adrian Mariano You may use and distribute this program as much as you like so long as you do not charge for this service. I am not liable for failure of this program to perform in any way. */ #include "plot3d.h" /******************************************************************/ /* */ /* Parameters to plot3d() */ float xmin=-3; /* Dimensions of display area */ float xmax=3; float ymin=-3; float ymax=3; float zmin=-3; float zmax=3; float aspect=1.0; /* Aspect ratio */ float distance=3; /* Perspective distance */ float tip=-30.0; /* Rotation around y axis */ float spin=0.0; /* Rotation around z axis */ float var1start; float var2start; float var1end; float var2end; long var1stepcount=30; /* Number of steps over var 1 */ long var2stepcount=30; /* Number of steps over var 2 */ /* */ /******************************************************************/ #define TRUE 1 #define FRONT 1 #define FALSE 0 #define BACK 0 #define PI 3.14159265 /************************************* Internal variables Global for convenience *************************************/ struct sectiontype { int x1,y1,x2,y2,x3,y3,x4,y4; float x; /* char ferr;*/ }; float xrange,yrange,zrange,sinspin,cosspin,sintip,costip; float multx,multy,addx,addy; /* The main function */ int plot3d(void (*transform)(float *,float *,float *,float,float,float)); /* Takes (x,y,z) and returns (x,y,z) in screen coordinates */ void realscale(float *xx,float *yy,float *zz); /* Compare to sections -- used by qsort */ int sectioncmp(struct sectiontype *a,struct sectiontype *b); /* plots the data contained the section */ void plotsections(struct sectiontype *section,int count); /* Get screen coordinates from float (x,y,z) */ void scale(int *xint,int *yint,float x,float y,float z); /* Draw 3d line */ void line3d(float a,float b,float c,float d,float e,float f); /* Initialize multx, multy, addx, and addy for use in scaling */ void calcscale(float xmin,float ymin,float zmin,float xmax,float ymax,float zmax); /* Draw the axes */ void drawaxes(char flag); /* Is this face (of the cube) visible ? */ int visible(int i); /* Function to be plotted */ #define FIX(flag,min,max) ( flag==1 ? max : min ) #define round(x) floor((x)+0.5) typedef int(*function)(const void *,const void *); int plot3d(void (*transform)(float *,float *,float *,float,float,float)) /* transform points to transform to cartesian */ { int i,edgecount,steps,steps2; /* Counters */ float x,y,z; /* Cartesian coordinates */ float var1step,var2step,var1,var2; /* Values for independent vars */ struct sectiontype *section; /* Table of sections */ /* Get some memory */ section=farcalloc(var1stepcount+var2stepcount+3+(var1stepcount+1)*(var2stepcount+1),sizeof(struct sectiontype)); if (!section){ printf("Insufficient memory\n\n"); return 0; } xrange=xmax-xmin; /* Calculate widths for x, y, and z */ yrange=ymax-ymin; zrange=zmax-zmin; costip=cos(tip*PI/180); /* Precalculate trig functions for angles */ sintip=sin(tip*PI/180); sinspin=sin(spin*PI/180); cosspin=cos(spin*PI/180); calcscale(xmin,ymin,zmin,xmax,ymax,zmax); /* Calculate scaling factors */ var1step=(var1end-var1start)/(var1stepcount);/* Set stepsizes for the */ var2step=(var2end-var2start)/(var2stepcount);/* independent variables */ i=0; edgecount=0; var1=var1start; /* Initialize var1 */ /* First make a partial table of needed boundary values. These are given */ /* very large x distances so that they will get sorted to the end of the */ /* section list. A second counter, 'edgecount' is kept. When the */ /* sections are plotted, the this value will be subtracted from the */ /* total counter so these edge sections won't be plotted */ /* Create a table of the edge values along the "top" of the function */ for(var2=var2start,steps=var2stepcount+1; steps ;steps--, i++,edgecount++,var2+=var2step) { (*transform)(&x,&y,&z,ff(var1,var2-var2step,0),var1,var2-var2step); realscale(&x,&y,&z); (section+i)->x4=round(addx+multx*x); /* Bottom left point */ (section+i)->y4=round(addy+multy*y); (section+i)->x=1e37; /* This section will get sorted to the end */ (*transform)(&x,&y,&z,ff(var1,var2,0),var1,var2); realscale(&x,&y,&z); (section+i)->x3=round(addx+multx*x); /* Bottom right point */ (section+i)->y3=round(addy+multy*y); } /* Main loop, cycles through var 1 */ for (var1=var1start+var1step,steps2=var1stepcount; steps2 ;steps2--, var1+=var1step) { /* Calculate value for left edge of function */ var2=var2start; (*transform)(&x,&y,&z,ff(var1,var2 ,0),var1,var2); realscale(&x,&y,&z); (section+i)->x3=round(addx+multx*x); /* Bottom right point */ (section+i)->y3=round(addy+multy*y); (section+i)->x=1e37; /* Will get sorted to the end */ (*transform)(&x,&y,&z,ff(var1-var1step,var2,0),var1-var1step,var2); realscale(&x,&y,&z); (section+i)->x2=round(addx+multx*x); /* Top right point */ (section+i)->y2=round(addy+multy*y); edgecount++; i++; /* Main loop. Calculate sections for one cycle of var 2 */ for(var2=var2start+var2step,steps=var2stepcount; steps ; i++ , steps--, var2+=var2step) { (*transform)(&x,&y,&z,ff(var1,var2,0),var1,var2); realscale(&x,&y,&z); (section+i)->x3=round(addx+multx*x); /* Bottom right point */ (section+i)->y3=round(addy+multy*y); (section+i)->x=z; /* These points have already been calculated */ (section+i)->x4=(section+i-1)->x3; /* Bottom left point */ (section+i)->y4=(section+i-1)->y3; (section+i)->x2=(section+i-var2stepcount-1)->x3; /* Top right */ (section+i)->y2=(section+i-var2stepcount-1)->y3; (section+i)->x1=(section+i-var2stepcount-1)->x4; /* Top left */ (section+i)->y1=(section+i-var2stepcount-1)->y4; } printf("."); /* The it hasn't crashed indicator */ } printf("\nSorting..."); qsort(section,i,sizeof(struct sectiontype),(function)sectioncmp); printf("\done"); entergraphics(); i-=edgecount; /* Subtract the edges out */ drawaxes(BACK); /* Draw back part of axes */ plotsections(section,i); drawaxes(FRONT); /* And plot those sections */ farfree(section); return 1; } void realscale(float *xx,float *yy,float *zz) { float x,y,z,dist; x=(*xx/xrange)-xmin/xrange/2-xmax/xrange/2; y=(*yy/yrange)-ymin/yrange/2-ymax/yrange/2; z=(*zz/zrange)-zmin/zrange/2-zmax/zrange/2; *zz=(x*cosspin-y*sinspin)*costip-z*sintip; if (distance==0.0) dist=1; else dist=distance+0.5-*zz; *xx=(y*cosspin+x*sinspin)*aspect/dist; *yy=(z*costip+(x*cosspin-y*sinspin)*sintip)/dist; } /* Compare to sections for qsort() */ int sectioncmp(struct sectiontype *a,struct sectiontype *b) { if (a->x < b->x) return(-1); if (a->x == b->x) return(0); return(1); } void plotsections(struct sectiontype *section,int count) /* Plot count sections */ { int i; for(i=0;i<count;i++) fill((section+i)->x1,(section+i)->y1,(section+i)->x2,(section+i)->y2, (section+i)->x3,(section+i)->y3,(section+i)->x4,(section+i)->y4); } void scale(int *xint,int *yint,float x,float y,float z) { realscale(&x,&y,&z); *xint=round(addx+multx*x); *yint=round(addy+multy*y); } void line3d(float a,float b,float c,float d,float e,float f) { int x1,y1,x2,y2; scale(&x1,&y1,a,b,c); scale(&x2,&y2,d,e,f); drawline(x1,y1,x2,y2); } /* Determine scaling constants */ void calcscale(float xmin,float ymin,float zmin,float xmax,float ymax,float zmax) { float xs[2],ys[2],zs[2],yb=-1e37,ym=1e37,xm=1e37,xb=-1e37; char i,j,k; multx=1; addx=0; multy=1; addy=0; xs[0]=xmin; xs[1]=xmax; ys[0]=ymin; ys[1]=ymax; zs[0]=zmin; zs[1]=zmax; for(i=0;i<2;i++) for(j=0;j<2;j++) for(k=0;k<2;k++){ xmin=xs[i]; ymin=ys[j]; zmin=zs[k]; realscale(&xmin,&ymin,&zmin); if (xmin>xb) xb=xmin; else if (xmin<xm) xm=xmin; if (ymin>yb) yb=ymin; else if (ymin<ym) ym=ymin; } multx=((float)maxx)/(xb-xm); multy=((float)maxy)/(yb-ym); if (multx>multy) multx=multy; else multy=multx; multy=-multy; addx=-xm*multx+((float)maxx-multx*(xb-xm))/2; addy=(float)maxy-ym*multy-((float)maxy+multy*(yb-ym))/2; } static int faces[6][3]={{0,0,-1},{-1,0,0},{0,1,0},{1,0,0},{0,-1,0},{0,0,1}}; static int edges[12][8]={ {0,1,-1,-1,-1,-1,1,-1}, {0,2,-1,1,-1,1,1,-1}, {0,3,1,-1,-1,1,1,-1}, {0,4,-1,-1,-1,1,-1,-1}, {4,1,-1,-1,-1,-1,-1,1}, {1,2,-1,1,-1,-1,1,1}, {2,3,1,1,-1,1,1,1}, {3,4,1,-1,-1,1,-1,1}, {5,1,-1,-1,1,-1, 1,1}, {5,2,-1, 1,1, 1, 1,1}, {5,3, 1,-1,1, 1, 1,1}, {5,4,-1,-1,1, 1,-1,1}}; void drawaxes(char flag) /* Draw the axes */ { int i; for(i=0;i<12;i++) if ((visible(edges[i][0]) && visible(edges[i][1]))==flag) line3d(FIX(edges[i][2],xmin,xmax), FIX(edges[i][3],ymin,ymax), FIX(edges[i][4],zmin,zmax), FIX(edges[i][5],xmin,xmax), FIX(edges[i][6],ymin,ymax), FIX(edges[i][7],zmin,zmax)); } int visible(int i) /* determine if a face of the coordinate cube is visible */ { return(faces[i][2] * sintip-(faces[i][0]*cosspin-faces[i][1]*sinspin)*costip<0); } SHAR_EOF fi if test -f 'plot3d.c' then echo shar: "will not over-write existing file 'plot3d.c'" else cat << \SHAR_EOF > 'plot3d.c' /* Plot3d -- plot3d.c, the main program By Adrian Mariano -- adrian@milton.u.washington.edu Copyright (c) 1991 by Adrian Mariano You may use and distribute this program as much as you like so long as you do not charge for this service. I am not liable for failure of this program to perform in any way. */ #include "plot3d.h" #include <string.h> extern unsigned _stklen = 32000;/* stack space to make qsort happy */ /* status variables */ char stop; void terminate(void) { backtotext(); exit(0); } term fstack[maxffs + 1][100]; /* function stack for ff evaluation */ typedef struct { float v1start, v1end, v2start, v2end; char *name; } bound; bound *currentbound; /* Functions to be passed to plot3d() for different plot types */ void sphererho(float *x, float *y, float *z, float rho, float phi, float theta); void spherephi(float *x, float *y, float *z, float phi, float rho, float theta); void spheretheta(float *x, float *y, float *z, float theta, float phi, float rho); void cartz(float *x, float *y, float *z, float zval, float xval, float yval); void cartx(float *x, float *y, float *z, float xval, float yval, float zval); void carty(float *x, float *y, float *z, float yval, float xval, float zval); void cylinderz(float *x, float *y, float *z, float zval, float rval, float theta); void cylinderr(float *x, float *y, float *z, float rval, float theta, float zval); void cylindertheta(float *x, float *y, float *z, float theta, float rval, float zval); void parametric(float *x, float *y, float *z, float dummy, float u, float v); bound sphererho_bound = {0, M_PI, 0, PI2, "sphererho"}; bound spherephi_bound = {0, 3, 0, PI2, "spherephi"}; bound spheretheta_bound = {0, M_PI, 0, 3, "spheretheta"}; bound cartz_bound = {-3, 3, -3, 3, "cartz"}; bound cartx_bound = {-3, 3, -3, 3, "cartx"}; bound carty_bound = {-3, 3, -3, 3, "carty"}; bound cylinderz_bound = {0, 3, 0, PI2, "cylinderz"}; bound cylinderr_bound = {0, PI2, -3, 3, "cylinderr"}; bound cylindertheta_bound = {0, 3, -3, 3, "cylindertheta"}; char *xstr = "x"; char *ystr = "y"; char *zstr = "z"; char *thetastr = "theta"; char *rhostr = "rho"; char *phistr = "phi"; char *rstr = "r"; char *var1str; char *var2str; void (*plottype) (float *, float *, float *, float, float, float); struct { char *st, *en; } replaces[] = { {"sinh", "sh"}, {"cosh", "ch"}, {"tanh", "th"},{"sin", "s"}, {"cos", "c"}, {"arc", "A"}, {"ln", "n"}, {"log", "l"},{"abs","a"} }; void setbounds(bound * newbound) { var1start = newbound->v1start; var2start = newbound->v2start; var1end = newbound->v1end; var2end = newbound->v2end; currentbound = newbound; } void substitute(char *str, char *start, char *end) { while (str = strstr(str, start)) { strnset(str, ' ', strlen(start)); strncpy(str, end, strlen(end)); } } void fixfun(char *fun) { int i; substitute(fun, var1str, "X"); substitute(fun, var2str, "Y"); substitute(fun, "X", "x"); substitute(fun, "Y", "y"); for (i = 0; i < sizeof(replaces) / (2 * sizeof(char *)); i++) substitute(fun, replaces[i].st, replaces[i].en); } void doreplot() { if (plot3d(plottype)) { getch(); backtotext(); } } void doplot(char *params) { char *errmesg; int where; fixfun(params); if ((where = convert(params, &errmesg, 0)) == -1) { simplify(0); if (plot3d(plottype)) {; getch(); backtotext(); } } else { while (where--) putchar(' '); printf(" ^\n%s\n", errmesg); } } float getval(char *expr, float def) { int where; char *errmesg; if ((where = convert(expr, &errmesg, maxffs)) == -1) return ff(def, def, maxffs); else { while (where--) putchar(' '); printf(" ^\n%s\n", errmesg); return def; } } void dotype(char *newtype) { if (!strcmp(newtype, "cartx")) { plottype = cartx; setbounds(&cartx_bound); var1str = ystr; var2str = zstr; } else if (!strcmp(newtype, "carty")) { plottype = carty; setbounds(&carty_bound); var1str = xstr; var2str = zstr; } else if (!strcmp(newtype, "cartz")) { plottype = cartz; setbounds(&cartz_bound); var1str = xstr; var2str = ystr; } else if (!strcmp(newtype, "sphererho")) { plottype = sphererho; setbounds(&sphererho_bound); var1str = phistr; var2str = thetastr; } else if (!strcmp(newtype, "spherephi")) { plottype = spherephi; setbounds(&spherephi_bound); var1str = rhostr; var2str = thetastr; } else if (!strcmp(newtype, "spheretheta")) { plottype = spheretheta; setbounds(&spheretheta_bound); var1str = phistr; var2str = rhostr; } else if (!strcmp(newtype, "cylinderz")) { plottype = cylinderz; setbounds(&cylinderz_bound); var1str = rstr; var2str = thetastr; } else if (!strcmp(newtype, "cylinderr")) { plottype = cylinderr; setbounds(&cylinderr_bound); var1str = thetastr; var2str = zstr; } else if (!strcmp(newtype, "cylindertheta")) { plottype = cylindertheta; setbounds(&cylindertheta_bound); var1str = rstr; var2str = zstr; } else printf("Unknown coordinate system: %s\n\n", newtype); } void doshow() { printf("Coordinate system: %s\n",currentbound->name); printf("%s range: [%.17g,%.17g]\n%s range: [%.17g,%.17g]\n", var1str, var1start, var1end, var2str, var2start, var2end); printf("X: [%.17g,%.17g]\nY: [%.17g,%.17g]\nZ: [%.17g,%.17g]\n", xmin, xmax, ymin, ymax, zmin, zmax); printf("%s steps: %d\t%s steps: %d\n", var1str, (int) var1stepcount, var2str, (int) var2stepcount); printf("Aspect: %.17g\tDistance: %.17g\n", aspect, distance); printf("Spin: %.17g \tTip: %.17g\n", spin, tip); } void dohelp() { printf( "Commands:\n\n" "plot <function> -- function is in terms of the current coordinate system\n" "replot -- plot the same function again.\n" "type <cartz|cartx|carty|sphererho|spheretheta|spherephi|cylinderz|\n" " cylindertheta|cylinderr> -- Selects coordinate system for plotting\n" " where the variable name specified (sphereRHO) is the dependent variable\n" "show -- displays current plotting parameters\n" "xmin, xmax, ymin, ymax, zmin, zmax <value> -- set region to display on screen\n" "%sstart, %send, %sstart, %send, %ssteps, %ssteps <value> --\n" " Set the range for the independent variables, and the number of steps at\n" " which to sample the function\n" "aspect <value> -- set aspect ratio of screen\n" "distance <value> -- set perspect distance, 0 for no perspective\n" "spin, tip <value> -- set view angle in degrees\n\n" "Functions supported:\n" " [arc]sin, [arc]cos, [arc]tan, [arc]sinh, [arc]cosh, [arc]tanh\n" " ln, log, abs\n",var1str,var1str,var2str,var2str,var1str,var2str); } #define IS(XX) (!strcmp(command,XX)) void main() { char input[100], command[100], params[100]; char xcount[15], ycount[15], xstart[15], xend[15], ystart[15], yend[15]; initgraphics(); plottype = cartz; setbounds(&cartz_bound); var1str = xstr; var2str = ystr; printf(" -- Plot3d Version 1.0 by Adrian Mariano --\n" " -- Type 'help' for help --\n"); do { printf("> "); gets(input); *command = *params = 0; sscanf(input, "%s %[^\n\r]", command, params); /* printf("'%s' '%s'\n",command,params); */ strcpy(xcount, var1str); strcat(xcount, "steps"); strcpy(ycount, var2str); strcat(ycount, "steps"); strcpy(xstart, var1str); strcat(xstart, "start"); strcpy(ystart, var2str); strcat(ystart, "start"); strcpy(xend, var1str); strcat(xend, "end"); strcpy(yend, var2str); strcat(yend, "end"); if IS ("quit") break; else if IS ("plot") doplot(params); else if IS ("type") dotype(params); else if IS ("show") doshow(); else if IS ("help") dohelp(); else if IS ("zmin") zmin = getval(params, zmin); else if IS ("zmax") zmax = getval(params, zmax); else if IS ("ymin") ymin = getval(params, ymin); else if IS ("xmin") xmin = getval(params, xmin); else if IS ("ymax") ymax = getval(params, ymax); else if IS ("xmax") xmax = getval(params, xmax); else if IS (xcount) var1stepcount = getval(params, var1stepcount); else if IS (ycount) var2stepcount = getval(params, var2stepcount); else if IS (xstart) { currentbound->v1start = getval(params, var1start); setbounds(currentbound); } else if IS (ystart) { currentbound->v2start = getval(params, var2start); setbounds(currentbound); } else if IS (xend) { currentbound->v1end = getval(params, var1end); setbounds(currentbound); } else if IS (yend) { currentbound->v2end = getval(params, var2end); setbounds(currentbound); } else if IS ("replot") doreplot(); else if IS ("aspect") aspect = getval(params, aspect); else if IS ("tip") tip = getval(params, tip); else if IS ("spin") spin = getval(params, spin); else if IS ("distance") distance = getval(params, distance); else if (!*command); else printf("Unknown command: %s\n", command); } while (1); clrscr(); } /* A transform function for sphere notation */ void sphererho(float *x, float *y, float *z, float rho, float phi, float theta) { *x = rho * sin(phi); *y = *x; *x *= cos(theta); *y *= sin(theta); *z = rho * cos(phi); } void spherephi(float *x, float *y, float *z, float phi, float rho, float theta) { *x = rho * sin(phi); *y = *x; *x *= cos(theta); *y *= sin(theta); *z = rho * cos(phi); } void spheretheta(float *x, float *y, float *z, float theta, float phi, float rho) { *x = rho * sin(phi); *y = *x; *x *= cos(theta); *y *= sin(theta); *z = rho * cos(phi); } void cartz(float *x, float *y, float *z, float zval, float xval, float yval) { *x = xval; *y = yval; *z = zval; } void cartx(float *x, float *y, float *z, float xval, float yval, float zval) { *x = xval; *y = yval; *z = zval; } void carty(float *x, float *y, float *z, float yval, float xval, float zval) { *x = xval; *y = yval; *z = zval; } void cylinderz(float *x, float *y, float *z, float zval, float rval, float theta) { *x = rval * cos(theta); *y = rval * sin(theta); *z = zval; } void cylinderr(float *x, float *y, float *z, float rval, float theta, float zval) { *x = rval * cos(theta); *y = rval * sin(theta); *z = zval; } void cylindertheta(float *x, float *y, float *z, float theta, float rval, float zval) { *x = rval * cos(theta); *y = rval * sin(theta); *z = zval; } #pragma argsused void parametric(float *x, float *y, float *z, float dummy, float u, float v) { /* *x =1.7*cos(2*PI*u); y =1.7*sin(2*PI*u); z =v; * * x = 0.5*v*cos(2*PI*u); y = 0.5*v*sin(2*PI*u); z = 0.866*v; * * x = cos(2*PI*u)*sin(PI*v); y = sin(2*PI*u)*sin(PI*v); z = cos(PI*v); * * x = cos(PI*(2*u-1))*sin(PI*v); y = sin(PI*(2*u-1))*sin(PI*v); z = cos(PI*v); * * x = exp(u+v); y = exp(u*v); z = u*v*v; * * x = -v + 1; y = u - v; z = v; */ float a = 2.0; float b = 0.8; *x = (a + b * cos(u)) * cos(v); *y = (a + b * cos(u)) * sin(v); *z = b * sin(u); } SHAR_EOF fi if test -f 'plot3d.h' then echo shar: "will not over-write existing file 'plot3d.h'" else cat << \SHAR_EOF > 'plot3d.h' /* header files */ #include <conio.h> #include <dos.h> #include <stdio.h> #include <math.h> #include <alloc.h> #include <stdlib.h> #include <values.h> /* constants */ #define maxffs 3 #define PI2 6.28318530 /* macros */ #define range(min,value,max) (((min) <= (value)) && ((value) <= (max))) /* typedefs */ typedef struct { char operator; double operand; } term; extern char ferr; extern term fstack[maxffs+1][100]; /* function stack for ff evaluation */ extern int maxx,maxy,mode; extern float xmin; /* Dimensions of display area */ extern float xmax; extern float ymin; extern float ymax; extern float zmin; extern float zmax; extern float aspect; /* Aspect ratio */ extern float distance; /* Perspective distance */ extern float tip; /* Rotation around y axis */ extern float spin; /* Rotation around z axis */ extern float var1start; extern float var2start; extern float var1end; extern float var2end; extern long var1stepcount; /* Number of steps over var 1 */ extern long var2stepcount; /* Number of steps over var 2 */ extern double ff(double arg1,double arg2, char stackno); /* function evaluator */ extern int plot3d(void (*transform)(float *,float *,float *,float,float,float)); extern void backtotext(); extern void entergraphics(); extern void initgraphics(); extern void fill(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4); extern void drawline(int x1,int x2,int x3,int x4); SHAR_EOF fi if test -f 'readme' then echo shar: "will not over-write existing file 'readme'" else cat << \SHAR_EOF > 'readme' Plot3d by Adrian Mariano and Karl Crary Copyright 1991 by Adrian Mariano You may use and distribute this program as much as you like so long as you do not charge for this service. I am not liable for failure of this program to perform in any way. Please send any comments, patches, or improvements to me at: adrian@milton.u.washington.edu Plot3d is a program for plotting 3 dimensional surfaces specified via one of the three standard coordinate systems. It uses a command driven interface with the following commands: type <type><dependent var> Selects coordinate system for plotting. The three available coordinate systems are: cartesian, a right handed orthoginal coordinate system with x, y and z as the variables; cylindrical, polar coordinate system with r the orthogonal distance from the z axis, theta the angle around the z axis, and z the distance along the z axis; and spherical, where rho is the distance from the origin, theta is the angle around the z axis, and phi is the angle between the z axis and the point. The <type> above should be one of cart, cylinder, or sphere. The dependent variable is the one which depends on the other two. It should be specified immediately after the type (without spaces). The default is cartz, which allows you to plot surfaces of the form z=f(x,y). To plot phi=f(rho, theta) in spherical coordinates, use the command 'type spherephi' plot <function> Plots the function with the current settings. The function should be specified WITHOUT an equals sign. So x^2+y^2 is ok. z=x^2+y^2 is not ok. Functions use the standard operations, +, -, /, *, and ^ (for exponents). e give 2.71828..., and pi produces the value pi. The '*' for multiplication may be omitted. The following functions are supported: [arc]sin, [arc]cos, [arc]tan, [arc]sinh, [arc]cosh, [arc]tanh ln, log, abs replot plots the same function again with the current settings (which may be different from when the function was last plotted). show displays current plotting parameters xmin, xmax, ymin, ymax, zmin, zmax <number> set the region to display on screen. <var>start, <var>end, <var>steps The program maintains separate values for the ranges over when the independent variables should vary, and the number of steps to take over this range for each coordinate system. You can set these values for the current coordinate system only by using these commands, where <var> is the variable you want to affect. aspect <number> Set aspect ratio of your monitor distance <number> Set perspective distance from image. Use 0 for no perspective. Distance is measured in multiples of the image depth (direction in the dimension perpendicular to your monitor). spin, tip <number> Set view angle in degrees. Spin rotates the image around the z axis, tip rotates the image around the y axis. LIMITATIONS Selecting more than about 55 x 55 resolution results in weird behavior for reasons I don't understand. EXAMPLES Here is an example session that produces a few interesting graphs: plot sin(x) plot sin(y) plot sin(x)*sin(y) type cylinderr plot 2.5 plot sin(2z) plot sin(3z)+.5 type sphererho plot 2.5sin(3theta) thetasteps 50 phisteps 50 replot SHAR_EOF fi exit 0 # End of shell archive exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.