#include <helianthus.h>
#include <math.h>
#define COUNT 16
#define SIZE 17
#define RADIUS 5
#define PRECISION 1e-5
double points[COUNT][2];
double *curPoint;
double curPointOffset[2];
int graphs[10];
double value(int ix)
{ return points[ ((ix + COUNT/2)%COUNT + COUNT)%COUNT ][1]; }
double nearest(double x)
{ return value(floor(x)); }
double linear(double x) {
x -= 0.5;
int ix = floor(x);
x -= ix;
return value(ix)*(1-x) + value(ix+1)*x;
}
double cubic(double x) {
x -= 0.5;
int ix = floor(x);
x -= ix;
double a = value(ix-1);
double b = value(ix+0);
double c = value(ix+1);
double d = value(ix+2);
return a*( -0.5*x*x*x + x*x - 0.5*x )
+ b*( 1.5*x*x*x - 2.5*x*x + 1.0 )
+ c*( -1.5*x*x*x + 2.0*x*x + 0.5*x )
+ d*( 0.5*x*x*x - 0.5*x*x );
}
double bspline(double x) {
x -= 0.5;
int ix = floor(x);
x -= ix;
double a = value(ix-1);
double b = value(ix+0);
double c = value(ix+1);
double d = value(ix+2);
return a*( -x*x*x + 3*x*x - 3*x + 1)/6
+ b*( 3*x*x*x - 6*x*x + 4 )/6
+ c*(-3*x*x*x + 3*x*x + 3*x + 1)/6
+ d*( x*x*x )/6;
}
void graph(double kx, double ky, double (*func)(double), unsigned int color) {
saveState();
strokeWidth(2);
stroke(color);
int hs = (int)ceil(SIZE*kx/2);
for(int x = -hs; x <= hs; ++x)
lineTo(x, func(x/kx)*ky);
strokePath();
restoreState();
}
void grid(double kx, double ky) {
saveState();
strokeWidth(1);
stroke(colorByRGBA(0, 0, 1, 0.125));
double hs = 0.5*SIZE;
for(int step = 16; step; step >>= 1) {
for(int i = 0; i <= SIZE/2; i += step) {
line(-hs*kx, i*ky, hs*kx, i*ky);
line(-hs*kx, -i*ky, hs*kx, -i*ky);
line( i*kx, -hs*ky, i*kx, hs*ky);
line(-i*kx, -hs*ky, -i*kx, hs*ky);
}
}
restoreState();
}
void randomize() {
for(int i = 0; i < COUNT; ++i)
points[i][1] = randomFloat()*SIZE - SIZE/2.0;
}
void zero() {
for(int i = 0; i < COUNT; ++i)
points[i][1] = 0;
}
void init() {
for(int i = 0; i < COUNT; ++i)
points[i][0] = i - COUNT/2 + 0.5;
randomize();
}
void draw() {
double w = windowGetWidth();
double h = windowGetHeight();
double kx = w/SIZE;
double ky = h/SIZE;
saveState();
translate(w/2, h/2);
scale(1, -1);
if (keyWentDown("r")) { randomize(); curPoint = 0; }
if (keyWentDown("z")) { zero(); curPoint = 0; }
char key[] = "0";
for(int i = 0; i < 10; ++i, ++*key)
if (keyWentDown(key)) graphs[i] = !graphs[i];
int m = mouseWentDown("left");
double mx = mouseTransformedX();
double my = mouseTransformedY();
if (!mouseDown("left"))
{ m = FALSE; curPoint = 0; }
if (curPoint) {
//curPoint[0] = (mx + curPointOffset[0])/kx;
curPoint[1] = (my + curPointOffset[1])/ky;
}
grid(kx, ky);
if (graphs[0]) graph(kx, ky, nearest, colorByRGBA(0, 0, 0, 0.5));
if (graphs[1]) graph(kx, ky, linear , colorByRGBA(0, 1, 0, 0.5));
if (graphs[2]) graph(kx, ky, cubic , colorByRGBA(0, 0, 1, 0.5));
if (graphs[3]) graph(kx, ky, bspline, colorByRGBA(1, 0, 0, 0.5));
strokeWidth(2);
stroke(COLOR_BLUE);
fill(COLOR_WHITE);
for(int i = 0; i < COUNT; ++i) {
double x = points[i][0]*kx, y = points[i][1]*ky;
line(x, 0, x, y);
circle(x, y, RADIUS);
if (i == COUNT/2)
circle(x, y, RADIUS + 1);
double dx = x-mx, dy = y-my;
if (m && dx*dx + dy*dy <= RADIUS*RADIUS) {
curPoint = points[i];
curPointOffset[0] = dx;
curPointOffset[1] = dy;
}
}
restoreState();
}
int main() {
windowSetVariableFrameRate();
windowSetResizable(TRUE);
windowSetInit(&init);
windowSetDraw(&draw);
windowRun();
return 0;
}