Files
amiga-topograph/Source/graph.c
T
2017-04-17 00:10:12 +02:00

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);
}