<body id="home">
<h1>Challenge #9 - Bezier curves</h1>
<p>
My take on the <a target="_blank" href="http://weblog.jamisbuck.org/2016/9/24/weekly-programming-challenge-9.html">ninth weekly challenge by Jamis Buck</a>.
</p>
<canvas id="scene" width="500" height="500"></canvas>
</body>
<script>
// Inspired by http://rectangleworld.com/blog/archives/15
window.addEventListener("load", bezierApp, false);
function bezierApp() {
point = function(x, y, rad) {
if (!x) var x = 0;
if (!y) var y = 0;
if (!rad) var rad = 1;
return {
x: x,
y: y,
rad: rad
};
}
var canvas = document.getElementById('scene');
var cWidth = canvas.width;
var cHeight = canvas.height;
var context = canvas.getContext('2d');
var dragging = false;
// Control points
var c1 = new point(100, 100, 10);
var c2 = new point(250, 300, 10);
var c3 = new point(400, 100, 10);
init();
function init() {
drawScreen();
canvas.addEventListener("mousedown", mouseDownListener, false);
}
var hitCtrlPoint = null;
function mouseDownListener(evt) {
var bRect = canvas.getBoundingClientRect();
var mouseX = (evt.clientX - bRect.left) * (canvas.width / bRect.width);
var mouseY = (evt.clientY - bRect.top) * (canvas.height / bRect.height);
// Check if user clicked on any of the 3 control points
if (hitTest(c1, mouseX, mouseY))
hitCtrlPoint = c1;
else if (hitTest(c2, mouseX, mouseY))
hitCtrlPoint = c2;
else if (hitTest(c3, mouseX, mouseY))
hitCtrlPoint = c3;
if (hitCtrlPoint) {
dragging = true;
dragHoldX = mouseX - hitCtrlPoint.x;
dragHoldY = mouseY - hitCtrlPoint.y;
}
if (dragging)
window.addEventListener("mousemove", mouseMoveListener, false);
canvas.removeEventListener("mousedown", mouseDownListener, false);
window.addEventListener("mouseup", mouseUpListener, false);
return false;
}
function mouseUpListener(evt) {
canvas.addEventListener("mousedown", mouseDownListener, false);
window.removeEventListener("mouseup", mouseUpListener, false);
if (dragging) {
dragging = false;
window.removeEventListener("mousemove", mouseMoveListener, false);
}
}
function mouseMoveListener(evt) {
var posX;
var posY;
var shapeRad = hitCtrlPoint.rad;
var minX = shapeRad;
var maxX = canvas.width - shapeRad;
var minY = shapeRad;
var maxY = canvas.height - shapeRad;
// Compute mouse position
var bRect = canvas.getBoundingClientRect();
mouseX = (evt.clientX - bRect.left) * (canvas.width / bRect.width);
mouseY = (evt.clientY - bRect.top) * (canvas.height / bRect.height);
// Prevent object from dragging outside of canvas
posX = mouseX - dragHoldX;
posX = (posX < minX) ? minX : ((posX > maxX) ? maxX : posX);
posY = mouseY - dragHoldY;
posY = (posY < minY) ? minY : ((posY > maxY) ? maxY : posY);
hitCtrlPoint.x = posX;
hitCtrlPoint.y = posY;
drawScreen();
}
function hitTest(point, mx, my) {
var dx;
var dy;
dx = mx - point.x;
dy = my - (point.y);
// a "hit" will be registered if the distance away from the center
// is less than the radius of the circular object
return (dx * dx + dy * dy < point.rad * point.rad);
}
function drawScreen() {
context.fillStyle = "#000000";
context.fillRect(0, 0, cWidth, cHeight);
drawPoint(c1.x, c1.y, 10, "#F00");
drawPoint(c2.x, c2.y, 10, "#F00");
drawPoint(c3.x, c3.y, 10, "#F00");
// Check if control point 1 gets passed control point 3
var dx = 0;
if (c1.x > c3.x)
dx = c1.x - c3.x;
var length = Math.abs(c1.x - c3.x);
var segLength = parseFloat((length / 100).toFixed(2));
var xStart = c1.x;
for (var i = 0; i <= 1; i += 0.01) {
var bezierPoint;
if (dx > 0)
bezierPoint = computeBezierPoint(c3, c2, c1, i.toFixed(2));
else
bezierPoint = computeBezierPoint(c1, c2, c3, i.toFixed(2));
drawPoint(bezierPoint.x, bezierPoint.y);
xStart += segLength;
}
}
function computeBezierPoint(c1, c2, c3, t) {
var pX = c1.x * Math.pow((1 - t), 2) + 2 * c2.x * (1 - t) * t + c3.x * Math.pow(t, 2);
var pY = c1.y * Math.pow((1 - t), 2) + 2 * c2.y * (1 - t) * t + c3.y * Math.pow(t, 2);
return new point(pX, pY);
}
function drawPoint(x, y, radius, color) {
if (!radius)
radius = 1
if (!color)
color = "#ffffff";
context.fillStyle = color;
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI);
context.closePath();
context.fill();
}
}
</script>