var canvasParams = {
|
|
h: 1000,
|
|
w: 2000,
|
|
lineWidth : 10,
|
|
handleRadius : 40
|
|
};
|
|
|
|
/**
|
|
* An IlluminationPoint is a point on the illumination curve.
|
|
* It both represents a point on the drawn canvas (via x and y getters)
|
|
* and a point on the "illumination curve space" (between 0 and 1, with u and v getters)
|
|
*/
|
|
class IlluminationPoint {
|
|
_u = 0;
|
|
_v = 0;
|
|
|
|
static fromCanvas(x, y) {
|
|
var ret = new IlluminationPoint();
|
|
|
|
ret._u = x/canvasParams.w;
|
|
ret._v = 1 - (y/canvasParams.h);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static fromIllumination(u, v) {
|
|
var ret = new IlluminationPoint();
|
|
|
|
ret._u = u;
|
|
ret._v = v;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static distCanvas(p1, p2) {
|
|
const x = p1.x - p2.x;
|
|
const y = p1.y - p2.y;
|
|
|
|
return Math.sqrt(x*x + y*y);
|
|
}
|
|
|
|
get x() {
|
|
return this._u * canvasParams.w;
|
|
}
|
|
|
|
get y() {
|
|
return (1-this._v) * canvasParams.h;
|
|
}
|
|
|
|
get u() {
|
|
return this._u;
|
|
}
|
|
|
|
get v() {
|
|
return this._v;
|
|
}
|
|
}
|
|
|
|
class Drawer {
|
|
constructor(context) {
|
|
this.draw = context;
|
|
this.draw.lineWidth = canvasParams.lineWidth;
|
|
}
|
|
|
|
bezier(p1, c1, c2, p2) {
|
|
this.draw.strokeStyle = "black";
|
|
|
|
this.draw.beginPath();
|
|
this.draw.moveTo(p1.x, p1.y);
|
|
this.draw.bezierCurveTo(c1.x, c1.y, c2.x, c2.y, p2.x, p2.y);
|
|
this.draw.stroke();
|
|
}
|
|
|
|
line(p1, p2) {
|
|
this.draw.beginPath();
|
|
this.draw.moveTo(p1.x, p1.y);
|
|
this.draw.lineTo(p2.x, p2.y);
|
|
this.draw.stroke();
|
|
}
|
|
|
|
circle(c, r) {
|
|
this.draw.beginPath();
|
|
this.draw.arc(c.x, c.y, r, 0, 2*Math.PI);
|
|
this.draw.fill();
|
|
}
|
|
|
|
controlHandle(p, c) {
|
|
//line between p and c
|
|
this.draw.strokeStyle = "grey";
|
|
|
|
this.line(p, c);
|
|
this.circle(c, canvasParams.handleRadius);
|
|
}
|
|
|
|
drawCurve(p1, c1, c2, p2) {
|
|
this.draw.clearRect(0, 0, canvasParams.w, canvasParams.h);
|
|
|
|
this.bezier(p1, c1, c2, p2);
|
|
this.controlHandle(p1, c1);
|
|
this.controlHandle(p2, c2);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* enumeration to encode if the user is moving a handle
|
|
*/
|
|
const ControlPointMoving = Object.freeze({
|
|
none: 0,
|
|
c1: 1,
|
|
c2: 2
|
|
});
|
|
|
|
//main script below
|
|
window.addEventListener("load", function() {
|
|
var canvas = document.getElementById("illuminationCurve");
|
|
canvasParams.h = canvas.height;
|
|
canvasParams.w = canvas.width;
|
|
|
|
var context = canvas.getContext("2d");
|
|
var drawer = new Drawer(context);
|
|
|
|
var p1 = IlluminationPoint.fromIllumination(0,0);
|
|
var p2 = IlluminationPoint.fromIllumination(1,1);
|
|
|
|
var c1 = IlluminationPoint.fromIllumination(0.2, 0);
|
|
var c2 = IlluminationPoint.fromIllumination(0.8, 1);
|
|
|
|
function getCursorPosition(e) {
|
|
const bbox = canvas.getBoundingClientRect();
|
|
const u = (e.clientX - bbox.left) / bbox.width;
|
|
const v = 1 - ((e.clientY - bbox.top) / bbox.height);
|
|
return IlluminationPoint.fromIllumination(u, v);
|
|
}
|
|
|
|
drawer.drawCurve(p1, c1, c2, p2);
|
|
|
|
//interaction
|
|
var currentlyMoving = ControlPointMoving.none;
|
|
canvas.addEventListener("mousedown", function(e) {
|
|
const cursor = getCursorPosition(e);
|
|
|
|
const maxDist = canvasParams.handleRadius;
|
|
const distC1 = IlluminationPoint.distCanvas(cursor, c1);
|
|
const distC2 = IlluminationPoint.distCanvas(cursor, c2);
|
|
|
|
if(distC1 <= maxDist) {currentlyMoving = ControlPointMoving.c1;}
|
|
if(distC2 <= maxDist) {currentlyMoving = ControlPointMoving.c2;}
|
|
});
|
|
|
|
canvas.addEventListener("mouseup", function(e) {
|
|
currentlyMoving = ControlPointMoving.none;
|
|
});
|
|
|
|
canvas.addEventListener("mousemove", function(e) {
|
|
if(currentlyMoving == ControlPointMoving.c1) {c1 = getCursorPosition(e);}
|
|
if(currentlyMoving == ControlPointMoving.c2) {c2 = getCursorPosition(e);}
|
|
|
|
if(currentlyMoving != ControlPointMoving.none) {drawer.drawCurve(p1, c1, c2, p2);}
|
|
});
|
|
});
|