v19i012: plot3d - plot 3d surfaces as wire meshes, Part01/01
Adrian Mariano
adrian at milton.u.washington.edu
Fri May 3 01:49:45 AEST 1991
Submitted-by: Adrian Mariano <adrian at 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+ at 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 at 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 at 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 at 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 at 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 at 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 at uunet.uu.net.
More information about the Comp.sources.misc
mailing list