/* 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;jzMin,G->zMax,deltaZ); for(i=0;ibytemap, 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 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;iextremaAreKnown = 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); }