652 lines
18 KiB
C
652 lines
18 KiB
C
/* File: graph.c */
|
|
|
|
/****************************************************************
|
|
Topograph - A function browser
|
|
Copyright (C) 1996 Marc Culler
|
|
|
|
culler@math.uic.edu
|
|
|
|
Department of Mathematics (M/C 249)
|
|
University of Illinois at Chicago
|
|
851 S. Morgan St.
|
|
Chicago, IL 60607-7045
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
******************************************************************/
|
|
|
|
#include "Topograph.h"
|
|
#include "graph.h"
|
|
|
|
/*
|
|
This module contains the methods for the graph.
|
|
*/
|
|
extern struct BitMap *graphBitMap;
|
|
extern struct Screen *graphScreen;
|
|
|
|
/*
|
|
THIS CODE ASSUMES THAT THE GRAPH WIDTH IS A MULTIPLE OF 4.
|
|
IT IS REALLY BEST IF IT IS DIVISIBLE BY 16.
|
|
*/
|
|
|
|
/* Defined in pixelpacker.a */
|
|
extern void __asm packPixels(register __a0 unsigned char *bytemap,
|
|
register __a1 struct BitMap *bitmap,
|
|
register __d4 int left,
|
|
register __d5 int top,
|
|
register __d6 int width,
|
|
register __d7 int height);
|
|
|
|
|
|
/* Assembly language functions from hacks.a */
|
|
extern void __asm loadMinMax(register __fp1 double zMin,
|
|
register __fp2 double zMax,
|
|
register __fp3 double deltaZ);
|
|
|
|
extern unsigned char __asm getColor(register __fp0 double zValue);
|
|
|
|
extern void __asm loadY(register __fp6 double Y);
|
|
|
|
/* Defined in graphtask,c */
|
|
extern BOOL stopped(void);
|
|
|
|
/* Defined in CP.c */
|
|
extern void CP_setTitle(char *string);
|
|
extern void CP_displayExtrema(struct graph *G);
|
|
|
|
/* Defined in graphevents.c */
|
|
extern BOOL printxyz(int mouseX, int mouseY);
|
|
|
|
struct graph *createGraph(int width, int height);
|
|
void destroyGraph(struct graph *G);
|
|
void drawGraph(struct graph *G, BOOL computeNewValues);
|
|
void extremizeGraph(struct graph*G);
|
|
void colorGraph(struct graph *G);
|
|
BOOL quickDraw(struct graph *G);
|
|
void computeX(struct graph *G);
|
|
void computeY(struct graph *G);
|
|
BOOL computeZ(struct graph *G, int left, int top, int width, int height);
|
|
void computeColors(struct graph *G, int left, int top, int width, int height);
|
|
void computeExtrema(struct graph *G);
|
|
void moveRight(struct graph *G, int pixels);
|
|
void moveLeft(struct graph *G, int pixels);
|
|
void moveUp(struct graph *G, int pixels);
|
|
void moveDown(struct graph *G, int pixels);
|
|
void printX(struct graph *G, char *string, int xIndex);
|
|
void printY(struct graph *G, char *string, int yIndex);
|
|
void printZ(struct graph *G, char *string, int xIndex, int yIndex);
|
|
double readX(struct graph *G, int xIndex);
|
|
double readY(struct graph *G, int yIndex);
|
|
double readZ(struct graph *G, int xIndex, int yIndex);
|
|
void markLevels(struct graph *G);
|
|
void scrollGraph(struct graph *G, int right, int down);
|
|
|
|
|
|
struct graph *createGraph(int width, int height){
|
|
struct graph *G;
|
|
|
|
G = (struct graph *)calloc(1,sizeof(struct graph));
|
|
if (G == NULL) return NULL;
|
|
G->width = width;
|
|
G->height = height;
|
|
G->x = (double *)calloc(G->width, sizeof(double));
|
|
if (G->x == NULL) return NULL;
|
|
G->y = (double *)calloc(G->height, sizeof(double));
|
|
if (G->y == NULL) return NULL;
|
|
G->z = (double *)calloc(G->width*G->height, sizeof(double));
|
|
if (G->z == NULL) return NULL;
|
|
G->bytemap = (unsigned char *)calloc(G->width*G->height, sizeof(unsigned char));
|
|
if (G->bytemap == NULL) return NULL;
|
|
G->xOrigin = 0;
|
|
G->yOrigin = 0;
|
|
G->xMin = -1.0;
|
|
G->xMax = 1.0;
|
|
G->yMin = -0.625;
|
|
G->yMax = 0.625;
|
|
G->zMin = -1.0;
|
|
G->zMax = 1.0;
|
|
G->extremaAreKnown = NO;
|
|
G->evaluate= NULL;
|
|
|
|
return(G);
|
|
}
|
|
|
|
void destroyGraph(struct graph *G){
|
|
|
|
if (G->evaluate) free(G->evaluate);
|
|
if (G->bytemap) free(G->bytemap);
|
|
if (G->z) free(G->z);
|
|
if (G->y) free(G->y);
|
|
if (G->x) free(G->x);
|
|
if (G) free(G);
|
|
}
|
|
|
|
void drawGraph(struct graph *G, BOOL computeNewValues){
|
|
|
|
if (computeNewValues == YES){
|
|
CP_setTitle("Computing Z values ...");
|
|
if (quickDraw(G) == OK) extremizeGraph(G);
|
|
CP_setTitle(NULL);
|
|
}
|
|
else colorGraph(G);
|
|
printxyz(graphScreen->MouseX, graphScreen->MouseY);
|
|
}
|
|
|
|
void extremizeGraph(struct graph *G){
|
|
|
|
if (G->extremaAreKnown == NO){
|
|
CP_setTitle("Finding extrema ...");
|
|
computeExtrema(G);
|
|
CP_displayExtrema(G);
|
|
}
|
|
}
|
|
|
|
void colorGraph(struct graph *G){
|
|
CP_setTitle("Computing colors ...");
|
|
computeColors(G, 0, 0, G->width, G->height);
|
|
packPixels(G->bytemap, graphBitMap, 0, 0, G->width, G->height);
|
|
CP_setTitle(NULL);
|
|
}
|
|
|
|
BOOL quickDraw(struct graph *G){
|
|
int i,j;
|
|
int w = G->width;
|
|
int h = G->height;
|
|
double *x = G->x;
|
|
double *y = G->y;
|
|
double *z = G->z;
|
|
double *zz = G->z;
|
|
unsigned char *pixelAddress = G->bytemap;
|
|
double deltaZ = (G->zMax - G->zMin)/NUMCOLORS;
|
|
double (* __asm evaluate)(register __fp5 double) =
|
|
(double (* __asm)(register __fp5 double))G->evaluate;
|
|
|
|
G->xOrigin = G->yOrigin = 0;
|
|
G->extremaAreKnown = NO;
|
|
|
|
/* Fill the screen with a hash pattern */
|
|
SetDrMd(&(graphScreen->RastPort),JAM2);
|
|
RectFill(&(graphScreen->RastPort),0,0,w-1,h-1);
|
|
WaitBlit();
|
|
SetDrMd(&(graphScreen->RastPort),COMPLEMENT);
|
|
|
|
computeX(G);
|
|
computeY(G);
|
|
for(j=0;j<h;j++){
|
|
loadY(*y++);
|
|
for(i=0;i<w;i++) *z++ = evaluate(x[i]);
|
|
loadMinMax(G->zMin,G->zMax,deltaZ);
|
|
for(i=0;i<w;i++) *pixelAddress++ = getColor(*zz++);
|
|
packPixels(G->bytemap, graphBitMap,0, j, w, 1);
|
|
if (stopped() == TRUE) return(FALSE);
|
|
}
|
|
|
|
return(OK);
|
|
}
|
|
|
|
|
|
|
|
void computeX(struct graph *G){
|
|
double deltaX;
|
|
double xValue;
|
|
double *xAddress;
|
|
|
|
deltaX = (G->xMax - G->xMin)/(G->width - 1);
|
|
xValue = G->xMin;
|
|
|
|
for (xAddress = G->x + G->xOrigin; xAddress < G->x + G->width; xAddress++){
|
|
*xAddress = xValue;
|
|
xValue += deltaX;
|
|
}
|
|
|
|
for (xAddress = G->x; xAddress < G->x + G->xOrigin; xAddress++){
|
|
*xAddress = xValue;
|
|
xValue += deltaX;
|
|
}
|
|
}
|
|
|
|
void computeY(struct graph *G){
|
|
double deltaY;
|
|
double yValue;
|
|
double *yAddress;
|
|
|
|
deltaY = (G->yMax - G->yMin)/(G->height - 1);
|
|
yValue = G->yMax;
|
|
|
|
for (yAddress = G->y + G->yOrigin; yAddress < G->y + G->height; yAddress++){
|
|
*yAddress = yValue;
|
|
yValue -= deltaY;
|
|
}
|
|
|
|
for (yAddress = G->y; yAddress < G->y + G->yOrigin; yAddress++){
|
|
*yAddress = yValue;
|
|
yValue -= deltaY;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL computeZ(struct graph *G, int left, int top, int width, int height){
|
|
|
|
int i, j, k, iStart, iEnd, jEnd, zSize, kJump;
|
|
|
|
/* The computation function takes its arguments x and y in
|
|
registers fp5 and fp6. These registers are not changed during
|
|
the computation. So we can save time by not loading y into fp6
|
|
when we are going across a row. To do this we use loadY and
|
|
introduce a dummy function which only takes a single argument in fp5. */
|
|
|
|
double (* __asm evaluate)(register __fp5 double) =
|
|
(double (* __asm)(register __fp5 double))G->evaluate;
|
|
|
|
/*Really we should save registers here*/
|
|
|
|
iStart = G->xOrigin + left;
|
|
if (iStart >= G->width) iStart -= G->width;
|
|
|
|
j = G->yOrigin + top;
|
|
if (j >= G->height) j -= G->height;
|
|
|
|
k = G->xOrigin + left + G->width*(G->yOrigin + top);
|
|
zSize = G->width*G->height;
|
|
if (k >= zSize) k -= zSize;
|
|
|
|
iEnd = iStart + width ;
|
|
if (iEnd >= G->width) iEnd -= G->width;
|
|
|
|
jEnd = j + height;
|
|
if (jEnd >= G->height) jEnd -= G->height;
|
|
|
|
kJump = G->width - width;
|
|
|
|
i = iStart;
|
|
|
|
do {
|
|
loadY(G->y[j]);
|
|
do {
|
|
G->z[k] = evaluate(G->x[i]);
|
|
i++;
|
|
if (i >= G->width) i -= G->width;
|
|
k++;
|
|
if (k >= zSize) k -= zSize;
|
|
} while (i != iEnd);
|
|
|
|
i = iStart;
|
|
k = k + kJump;
|
|
if (k >= zSize) k -= zSize;
|
|
j++;
|
|
if ( j >= G->height) j -= G->height;
|
|
if (stopped()) return(FALSE);
|
|
} while (j != jEnd);
|
|
|
|
/* Really we should restore registers here. */
|
|
}
|
|
|
|
|
|
|
|
void computeColors(struct graph *G, int left, int top, int width, int height){
|
|
int start = G->xOrigin + left + G->width*(G->yOrigin + top);
|
|
double *zAddress;
|
|
int zLength = G->width*G->height;
|
|
double *zEnd = G->z + zLength;
|
|
unsigned char *pixelAddress = G->bytemap + left + G->width*top;
|
|
int i,j;
|
|
int jump = G->width - width;
|
|
|
|
if (start >= zLength) start -= zLength;
|
|
zAddress = G->z + start;
|
|
|
|
loadMinMax(G->zMin,G->zMax,(G->zMax - G->zMin)/NUMCOLORS);
|
|
|
|
for (j = height; j > 0; j--){
|
|
for ( i = width; i > 0; i--){
|
|
*pixelAddress = getColor(*zAddress);
|
|
zAddress++;
|
|
if (zAddress >= zEnd) zAddress -= zLength;
|
|
pixelAddress++;
|
|
}
|
|
pixelAddress = pixelAddress + jump;
|
|
zAddress = zAddress + jump;
|
|
if (zAddress >= zEnd) zAddress -= zLength;
|
|
}
|
|
}
|
|
|
|
void computeExtrema(struct graph *G){
|
|
double *zAddress;
|
|
int i, size = G->width*G->height;
|
|
register double zValue;
|
|
register double maximum;
|
|
register double minimum;
|
|
|
|
zAddress = G->z;
|
|
minimum = *zAddress;
|
|
maximum = minimum;
|
|
|
|
for(i=0;i<size;i++) {
|
|
zValue = *zAddress++;
|
|
if (zValue < minimum) minimum=zValue;
|
|
if (zValue > maximum) maximum=zValue;
|
|
}
|
|
|
|
G->maximum = maximum;
|
|
G->minimum = minimum;
|
|
G->extremaAreKnown = YES;
|
|
}
|
|
|
|
void moveRight(struct graph *G, int pixels){
|
|
double deltaX = (G->xMax - G->xMin)/(G->width - 1);
|
|
double xValue;
|
|
int newOrigin,i,j,quadPixels,left;
|
|
long *quad = (long *)G->bytemap;
|
|
|
|
/*We round pixels down to the nearest multiple of
|
|
four to be sure we can scroll the bytemap efficiently.*/
|
|
|
|
pixels &= ~3;
|
|
|
|
/* Shift the x origin. */
|
|
newOrigin = G->xOrigin + pixels;
|
|
if (newOrigin >= G->width) newOrigin -= G->width;
|
|
|
|
/* Compute new x values. */
|
|
xValue = G->xMax;
|
|
i = G->xOrigin;
|
|
do {
|
|
xValue += deltaX;
|
|
G->x[i] = xValue;
|
|
i++;
|
|
if (i >= G->width) i -= G->width;
|
|
} while (i != newOrigin);
|
|
G->xOrigin = newOrigin;
|
|
|
|
/* Reset xMin and xMax */
|
|
G->xMin = G->x[G->xOrigin];
|
|
G->xMax = xValue;
|
|
|
|
left = G->width - pixels;
|
|
computeZ(G, left, 0, pixels, G->height);
|
|
|
|
/* Scroll the Bytemap. */
|
|
quadPixels = pixels >> 2;
|
|
for (i = G->height; i > 0; i--){
|
|
for(j = left; j > 0; j -= 4){
|
|
*quad = *(quad + quadPixels);
|
|
quad++;
|
|
};
|
|
quad += quadPixels;
|
|
}
|
|
|
|
computeColors(G, left, 0, pixels, G->height);
|
|
}
|
|
|
|
void moveLeft(struct graph *G, int pixels){
|
|
double deltaX = (G->xMax - G->xMin)/(G->width - 1);
|
|
double xValue;
|
|
int newOrigin,i,j,quadPixels,last;
|
|
long *quad = (long *)G->bytemap;
|
|
|
|
quad += (G->width*G->height >>2) - 1;
|
|
|
|
/*We round pixels down to the nearest multiple of
|
|
four to be sure we can scroll the bytemap efficiently.*/
|
|
|
|
pixels &= ~03;
|
|
|
|
/* Shift the x origin. */
|
|
newOrigin = G->xOrigin - pixels;
|
|
if (newOrigin < 0) newOrigin += G->width;
|
|
|
|
/* Compute new x values. */
|
|
xValue = G->xMin;
|
|
i = G->xOrigin;
|
|
do {
|
|
i--;
|
|
if (i < 0) i += G->width;
|
|
xValue -= deltaX;
|
|
G->x[i] = xValue;
|
|
|
|
} while (i != newOrigin);
|
|
G->xOrigin = newOrigin;
|
|
|
|
last = G->xOrigin - 1;
|
|
if (last < 0) last += G->width;
|
|
|
|
/* Reset xMin and xMax */
|
|
G->xMax = G->x[last];
|
|
G->xMin = xValue;
|
|
|
|
computeZ(G, 0, 0, pixels, G->height);
|
|
|
|
/* Scroll the Bytemap. */
|
|
quadPixels = pixels >> 2;
|
|
for (i=G->height;i>0;i--){
|
|
for(j = G->width - pixels ; j>0; j -= 4){
|
|
*quad = *(quad - quadPixels);
|
|
quad--;
|
|
};
|
|
quad -= quadPixels;
|
|
}
|
|
|
|
computeColors(G, 0, 0, pixels, G->height);
|
|
}
|
|
|
|
void moveUp(struct graph *G, int pixels){
|
|
double deltaY = (G->yMax - G->yMin)/(G->height - 1);
|
|
double yValue;
|
|
int newOrigin,i,last,quadShift;
|
|
int quadSize = G->width*G->height >> 2;
|
|
long *quad = (long *)G->bytemap + quadSize - 1;
|
|
|
|
/* Shift the y origin. */
|
|
newOrigin = G->yOrigin - pixels;
|
|
if (newOrigin < 0) newOrigin += G->height;
|
|
|
|
/* Compute new y values. */
|
|
yValue = G->yMax;
|
|
i = G->yOrigin;
|
|
do {
|
|
i--;
|
|
if (i < 0) i += G->height;
|
|
yValue += deltaY;
|
|
G->y[i] = yValue;
|
|
} while (i != newOrigin);
|
|
G->yOrigin = newOrigin;
|
|
|
|
/* Reset xMin and xMax */
|
|
last = G->yOrigin - 1;
|
|
if (last < 0) last += G->height;
|
|
G->yMin = G->y[last];
|
|
G->yMax = yValue;
|
|
|
|
computeZ(G, 0, 0, G->width, pixels);
|
|
|
|
/* Scroll the Bytemap. */
|
|
quadShift = pixels*(G->width >> 2);
|
|
for (i= quadSize - quadShift; i>0; i--){
|
|
*quad = *(quad - quadShift);
|
|
quad--;
|
|
}
|
|
|
|
computeColors(G, 0, 0, G->width, pixels);
|
|
}
|
|
|
|
|
|
void moveDown(struct graph *G, int pixels){
|
|
double deltaY = (G->yMax - G->yMin)/(G->height - 1);
|
|
double yValue;
|
|
int newOrigin, i, quadShift, top;
|
|
int quadSize = G->width*G->height >> 2;
|
|
long *quad = (long *)G->bytemap;
|
|
|
|
/* Shift the y origin. */
|
|
newOrigin = G->yOrigin + pixels;
|
|
if (newOrigin >= G->height) newOrigin -= G->height;
|
|
|
|
/* Compute new y values. */
|
|
yValue = G->yMin;
|
|
i = G->yOrigin;
|
|
do {
|
|
yValue -= deltaY;
|
|
G->y[i] = yValue;
|
|
i++;
|
|
if (i >= G->height) i -= G->height;
|
|
} while (i != newOrigin);
|
|
G->yOrigin = newOrigin;
|
|
|
|
/* Reset xMin and xMax */
|
|
G->yMin = yValue;
|
|
G->yMax = G->y[G->yOrigin];
|
|
|
|
top = G->height - pixels;
|
|
computeZ(G, 0, top, G->width, pixels);
|
|
|
|
/* Scroll the Bytemap. */
|
|
quadShift = pixels*(G->width >> 2);
|
|
for (i= quadSize - quadShift; i>0; i--){
|
|
*quad = *(quad + quadShift);
|
|
quad++;
|
|
}
|
|
|
|
computeColors(G, 0, top, G->width, pixels);
|
|
}
|
|
|
|
void printX(struct graph *G, char *string, int xIndex){
|
|
|
|
xIndex += G->xOrigin;
|
|
if (xIndex >= G->width) xIndex -= G->width;
|
|
sprintf(string, "%11.10f", G->x[xIndex]);
|
|
}
|
|
|
|
void printY(struct graph *G, char *string, int yIndex){
|
|
|
|
yIndex += G->yOrigin;
|
|
if (yIndex >= G->height) yIndex -= G->height;
|
|
sprintf(string, "%11.10f", G->y[yIndex]);
|
|
}
|
|
|
|
void printZ(struct graph *G, char *string, int xIndex, int yIndex){
|
|
|
|
int zIndex;
|
|
|
|
zIndex = G->xOrigin + xIndex + G->width*(G->yOrigin + yIndex);
|
|
if (zIndex >= G->width*G->height) zIndex -= G->width*G->height;
|
|
sprintf(string, "%11.10f", G->z[zIndex]);
|
|
}
|
|
|
|
double readX(struct graph *G, int xIndex){
|
|
|
|
xIndex += G->xOrigin;
|
|
if (xIndex >= G->width) xIndex -= G->width;
|
|
return(G->x[xIndex]);
|
|
}
|
|
|
|
double readY(struct graph *G, int yIndex){
|
|
|
|
yIndex += G->yOrigin;
|
|
if (yIndex >= G->height) yIndex -= G->height;
|
|
return(G->y[yIndex]);
|
|
}
|
|
|
|
double readZ(struct graph *G, int xIndex, int yIndex){
|
|
|
|
int zIndex;
|
|
|
|
zIndex = G->xOrigin + xIndex + G->width*(G->yOrigin + yIndex);
|
|
if (zIndex >= G->width*G->height) zIndex -= G->width*G->height;
|
|
return(G->z[zIndex]);
|
|
}
|
|
|
|
/*This is NOT EFFICIENT */
|
|
void markLevels(struct graph *G){
|
|
|
|
int i,j;
|
|
int h = G->height;
|
|
int w = G->width;
|
|
unsigned char *e, *s;
|
|
unsigned char X,E,S;
|
|
|
|
e = G->bytemap;
|
|
s = G->bytemap + G->width;
|
|
X = *e&0x1f;
|
|
e++;
|
|
for (i=1;i<h; i++){
|
|
for (j=1;j<w;j++){
|
|
E = *e&0x1f;
|
|
S = *s&0x1f;
|
|
if (X<E) *(e-1) |= 0x20;
|
|
if (E<X) *e |= 0x20;
|
|
if (X<S) *(e-1) |= 0x20;
|
|
if (S<X) *s |= 0x20;
|
|
e++;
|
|
s++;
|
|
X = E;
|
|
}
|
|
/* Last column. */
|
|
E = *e&0x1f;
|
|
S = *s&0x1f;
|
|
if (X<S) *(e-1) |= 0x20;
|
|
if (S<X) *s |= 0x20;
|
|
e++;
|
|
s++;
|
|
X = E;
|
|
}
|
|
/* Last Row. */
|
|
for (j=1;j<w;j++){
|
|
E = *e&0x1f;
|
|
if (X<E) *(e-1) |= 0x20;
|
|
if (E<X) *e |= 0x20;
|
|
e++;
|
|
s++;
|
|
X = E;
|
|
}
|
|
}
|
|
|
|
void scrollGraph(struct graph *G, int right, int down){
|
|
|
|
G->extremaAreKnown = NO;
|
|
CP_displayExtrema(G);
|
|
|
|
if (down < 0){
|
|
moveUp(G, -down);
|
|
ScrollRaster(&(graphScreen->RastPort),
|
|
0,down,0,0,graphScreen->Width,graphScreen->Height);
|
|
packPixels(G->bytemap, graphBitMap,
|
|
0, 0, G->width, -down);
|
|
}
|
|
|
|
if (down > 0){
|
|
moveDown(G, down);
|
|
ScrollRaster(&(graphScreen->RastPort),
|
|
0,down,0,0,graphScreen->Width,graphScreen->Height);
|
|
packPixels(G->bytemap, graphBitMap,
|
|
0, G->height - down, G->width, down);
|
|
}
|
|
|
|
if (right < 0){
|
|
moveLeft(G, -right);
|
|
ScrollRaster(&(graphScreen->RastPort),
|
|
right,0,0,0,graphScreen->Width,graphScreen->Height);
|
|
packPixels(G->bytemap, graphBitMap,
|
|
0, 0, -right, G->height);
|
|
}
|
|
|
|
if (right > 0){
|
|
moveRight(G, right);
|
|
ScrollRaster(&(graphScreen->RastPort),
|
|
right,0,0,0,graphScreen->Width,graphScreen->Height);
|
|
packPixels(G->bytemap, graphBitMap,
|
|
G->width - right, 0, right, G->height);
|
|
}
|
|
printxyz(graphScreen->MouseX, graphScreen->MouseY);
|
|
}
|