#include <helianthus.h>
#define COUNT 5
#define RADIUS 5
#define PRECISION 1e-5
double points[COUNT][2];
double *curPoint;
double curPointOffset[2];
double calcCurveValue(double *curve, int count, double x) {
if (count <= 0) return 0;
if (count == 1) return curve[1];
if (x <= curve[0]) return curve[1];
if (x >= curve[(count-1)*2]) return curve[(count-1)*2 + 1];
int i = 0;
while(i+1 < count && curve[(i+1)*2] < x)
++i;
double *p1 = &curve[i*2];
double *p0 = i > 0 ? p1-2 : p1;
double *p2 = i+1 < count ? p1+2 : p1;
double *p3 = i+2 < count ? p2+2 : p2;
double dx = p2[0] - p1[0];
if (dx <= PRECISION) return p1[1];
double a = p1[1];
double b = p2[1];
double ta = p2[0]-p0[0]; ta = ta > PRECISION ? (p2[1]-p0[1])/ta*dx : 0;
double tb = p3[0]-p1[0]; tb = tb > PRECISION ? (p3[1]-p1[1])/tb*dx : 0;
double l = (x - p1[0])/dx;
return a + (ta + (3*(b-a)-ta-ta-tb + (2*(a-b)+ta+tb)*l)*l)*l;
}
void init() {
for(int i = 0; i < COUNT; ++i)
points[i][0] = points[i][1] = i/(COUNT - 1.0);
calcCurveValue(points[0], COUNT, 0.3);
}
void draw() {
double w = windowGetWidth();
double h = windowGetHeight();
saveState();
translate(0, h);
scale(1, -1);
noFill();
stroke(colorByRGBA(0.75, 0.75, 0.75, 1));
strokeWidth(2);
rect(0, 0, w, h);
strokeWidth(2);
line(0, 0, w, h);
int m = mouseWentDown("left");
double mx = mouseTransformedX();
double my = mouseTransformedY();
if (!mouseDown("left"))
{ m = FALSE; curPoint = 0; }
if (curPoint) {
curPoint[0] = (mx + curPointOffset[0])/w;
curPoint[1] = (my + curPointOffset[1])/h;
}
stroke(COLOR_BLUE);
fill(COLOR_WHITE);
for(int i = 0; i < w+1; ++i)
lineTo(i, calcCurveValue(points[0], COUNT, i/w)*h);
strokePath();
for(int i = 0; i < COUNT; ++i) {
double x = points[i][0]*w, y = points[i][1]*h;
circle(x, y, RADIUS);
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;
}