<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>