# Cubic Bezier Generator

HTML
```<html>
<canvas id="bezier" width="336" height="336">
</canvas>
<form>
<label for="x1">x1 = </label><input onchange="updateCanvas();" type="text" maxlength=6 id="x1" value="0.79" class='field'>
<label for="y1">y1 = </label><input onchange="updateCanvas();return true;"  type="text" maxlength=6 id="y1" value="0.33" class='field'>
<label for="x2">x2 = </label><input onchange="updateCanvas();return true;"  type="text" maxlength=6 id="x2" value="0.14" class='field'>
<label for="y2">y2 = </label><input onchange="updateCanvas();return true;"  type="text" maxlength=6 id="y2" value="0.53" class='field'>
<br>
<output id="output">Log</output>
</form>
</html>```
CSS
```.field {
width: 40px;
}```
JavaScript
```function updateCanvas() {

var x1 = document.getElementById('x1').value;
var y1 = document.getElementById('y1').value;
var x2 = document.getElementById('x2').value;
var y2 = document.getElementById('y2').value;

drawBezierCurve(x1, y1, x2, y2);
}

// Place needed to draw the rulers
const rulers = 30.5;
const margin = 10.5;
const basic_scale_size = 5; // Size of 0.1 tick on the rulers
var scaling; //LIMITATION: scaling is computed once: if canvas.height/canvas.width change it won't be recalculated
var dragSM = 0; // Drag state machine: 0 = nodrag, others = object being dragged

function initCanvas() {
// get the canvas element using the DOM
var canvas = document.getElementById('bezier');

// Make sure we don't execute when canvas isn't supported
if (canvas.getContext) {
// use getContext to use the canvas for drawing
var ctx = canvas.getContext('2d');

scaling = Math.min(canvas.height - rulers - margin, canvas.width - rulers - margin);

canvas.onmousedown = mouseDown;
canvas.onmouseup = mouseUp;
} else {
alert('You need Safari or Firefox 1.5+ to see this demo.');
}
}

function cX(x) {
return x * scaling + rulers;
}

function reverseX(x) {
return (x - rulers) / scaling;
}

function lX(x) {
//Used when drawing vertical lines to prevent subpixel blur
var result = cX(x);
return Math.round(result) == result ? result + 0.5 : result;
}

function cY(y) {

return (1 - y) * scaling + margin;
}

function reverseY(y) {
return (margin - y) / scaling + 1;
}

function lY(y) {
// Used when drawing horizontal lines to prevent subpixel blur
var result = cY(y);
return Math.round(result) == result ? result + 0.5 : result;
}

function drawBezierCurve(x1, y1, x2, y2) {

// get the canvas element using the DOM
var canvas = document.getElementById('bezier');

// Make sure we don't execute when canvas isn't supported
if (canvas.getContext) {

// use getContext to use the canvas for drawing
var ctx = canvas.getContext('2d');

// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);

// Draw the rulers
ctx.beginPath();
ctx.strokeStyle = "black";

// Draw the Y axis
ctx.moveTo(cX(0), cY(0));
ctx.lineTo(cX(1), cY(0));
ctx.textAlign = "right";

for (var i = 0.1; i <= 1; i = i + 0.1) {
ctx.moveTo(-basic_scale_size + cX(0), lY(i));
if ((i == 0.5) || (i > 0.9)) {
ctx.moveTo(-2 * basic_scale_size + cX(0), lY(i));
ctx.fillText(Math.round(i * 10) / 10, -3 * basic_scale_size + cX(0), cY(i) + 4);
// Limitation the constant 4 should be font size dependant
}
ctx.lineTo(cX(0), lY(i));
}
ctx.stroke();
ctx.closePath();
ctx.beginPath();

// Draw the Y axis label
ctx.save();
ctx.rotate(-Math.PI / 2);
ctx.textAlign = "left";
ctx.fillText("Output (Value Ratio)", -cY(0), -3 * basic_scale_size + cX(0));
ctx.restore();

// Draw the X axis
ctx.moveTo(cX(0), cY(0));
ctx.lineTo(cX(0), cY(1));
ctx.textAlign = "center";
for (i = 0.1; i <= 1; i = i + 0.1) {
ctx.moveTo(lX(i), basic_scale_size + cY(0));
if ((i == 0.5) || (i > 0.9)) {
ctx.moveTo(lX(i), 2 * basic_scale_size + cY(0));
ctx.fillText(Math.round(i * 10) / 10, cX(i), 4 * basic_scale_size + cY(0));
// Limitation the constant 4 should be dependant of the font size
}
ctx.lineTo(lX(i), cY(0));
}

// Draw the X axis label
ctx.textAlign = "left";
ctx.fillText("Input (Time Duration Ratio)", cX(0), 4 * basic_scale_size + cY(0));
// Limitation the constant 4 should be dependant of the font size
ctx.stroke();
ctx.closePath();

// Draw the Bézier Curve
ctx.beginPath();
ctx.moveTo(cX(0), cY(0));
ctx.strokeStyle = 'blue';
ctx.bezierCurveTo(cX(x1), cY(y1), cX(x2), cY(y2), cX(1), cY(1));
ctx.stroke();
ctx.closePath();

// Draw the P2 point (with a line to P0)
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.moveTo(cX(x1), cY(y1));
ctx.lineTo(cX(0), cY(0));
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.moveTo(cX(x1), cY(y1));
ctx.arc(cX(x1), cY(y1), radius, 0, 2 * Math.PI);
ctx.stroke();
ctx.fillStyle = 'white';
ctx.fill();
ctx.closePath();

// Draw the P3 point (with a line to P1)
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.moveTo(cX(x2), cY(y2));
ctx.lineTo(cX(1), cY(1));
ctx.stroke();
ctx.closePath();

ctx.beginPath();
ctx.moveTo(cX(x2), cY(y2));
ctx.arc(cX(x2), cY(y2), radius, 0, 2 * Math.PI);
ctx.stroke();
ctx.fill();
ctx.closePath();

// Draw the P1(1,1) point (with dashed hints)
ctx.beginPath();
ctx.moveTo(cX(1), cY(1));
ctx.strokeStyle = 'lightgrey';
ctx.lineTo(cX(0), cY(1));
ctx.moveTo(cX(1), cY(1));
ctx.lineTo(cX(1), cY(0));
ctx.stroke();
ctx.closePath();

ctx.beginPath();
ctx.strokeStyle = "black";
ctx.fillStyle = "black";
ctx.arc(cX(1), cY(1), radius, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.closePath();

// Draw the P0(0,0) point
ctx.beginPath();
ctx.arc(cX(0), cY(0), radius, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.closePath();

} else {
alert('You need Safari or Firefox 1.5+ to see this demo.');
}
}

function mouseDown(e) {

var canvas = document.getElementById('bezier');

var x1 = cX(document.getElementById('x1').value);
var y1 = cY(document.getElementById('y1').value);

var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;

var output = document.getElementById('output');
output.value = "(" + x + "," + y + ") should be (" + x1 + "," + y1 + ")";

if ((x1 + radius >= x) && (x1 - radius <= x) && (y1 + radius >= y) && (y1 - radius <= y)) {
var output = document.getElementById('output');
output.value = "P1!";
dragSM = 1;
}

var x2 = cX(document.getElementById('x2').value);
var y2 = cY(document.getElementById('y2').value);

if ((x2 + radius >= x) && (x2 - radius <= x) && (y2 + radius >= y) && (y2 - radius <= y)) {
var output = document.getElementById('output');
output.value = "P2!";
dragSM = 2;
}

// If we are starting a drag
if (dragSM != 0) {
canvas.onmousemove = mouseMove;
}
}

function mouseUp(e) {

var output = document.getElementById('output');
output.value = "Mouse up!";
dragSM = 0;
canvas.onmousemove = null;
}

function mouseMove(e) {
if (dragSM != 0) {
var canvas = document.getElementById('bezier');

var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;

var output = document.getElementById('output');
output.value = "Drag!";

if (dragSM == 1) {
var x1 = document.getElementById('x1');
var y1 = document.getElementById('y1');
x1.value = reverseX(x);
x1.value = Math.round(x1.value * 10000) / 10000;
y1.value = reverseY(y);
y1.value = Math.round(y1.value * 10000) / 10000;
if (x1.value < 0) { x1.value = 0; }
if (x1.value > 1) { x1.value = 1; }
} else if (dragSM == 2) {
var x2 = document.getElementById('x2');
var y2 = document.getElementById('y2');
x2.value = reverseX(x);
x2.value = Math.round(x2.value * 10000) / 10000;
y2.value = reverseY(y);
y2.value = Math.round(y2.value * 10000) / 10000;
if (x2.value < 0) { x2.value = 0; }
if (x2.value > 1) { x2.value = 1; }
}
updateCanvas();
}
}

initCanvas();
updateCanvas();```

Draft