Reputation: 265
I have a requirement using canvas that I need to draw a curve, using quadraticCurveTo.
I understand how this works but the problem I have is that I have a diagonal line and I want to specify the "control point" to be perpendicular to this diagonal and at a certain distance (px) away from it. So, for example:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.moveTo(0, 0);
ctx.quadraticCurveTo(?W, ?H, 100, 100);
ctx.stroke();
I want the control point (?W, ?H) to be, say 50px from the "imaginary line" (0, 0) to (100, 100) and perpendicular to the centre point of that imaginary line.
I hope this makes sense. To maybe explain further, it is easy with a straight line because:
ctx.moveTo(0, 0);
ctx.quadraticCurveTo(?W, ?H, 100, 0);
ctx.stroke();
So the ?W would be 50 and the ?H would be 50px (or whatever distance I want it to be prependicular to the imaginary line (0, 0) to (0, 100).
With a simple slope, like in my example, it could probably be worked out but the slope of the diagonal could be anything, dependant on the start and finish point. The start and finish points are worked out from user input so the control point needs to dynamically calculate itself from that.
I have spent months on this and with my knowledge of canvas I'm getting no-where.
Will appreciate any help!
Thank in advance!
Mike
Upvotes: 2
Views: 363
Reputation:
Simply subtract start point from end point. We will reuse these in the next steps:
var diffX = x2 - x1,
diffY = y2 - y1;
We reuse the diffs to find angle using atan2()
function:
var angle = Math.atan2(diffY, diffX);
Using linear interpolation we again reuse the diffs with a normalized value representing the center point:
var mx = x1 + diffX * 0.5,
my = y1 + diffY * 0.5;
And finally, put everything together by using the mid-point with a radius at the tangent angle (radius here is the length out from the mid-point in pixels).
We here reverse cos/sin order for x/y, and negate cos to use the tangent of the angle instead of the actual angle itself:
var cx = mx + radius * Math.sin(angle),
cy = my - radius * Math.cos(angle);
Now we can pass in the values to the methods:
ctx.moveTo(x1, y1);
ctx.quadraticCurveTo(cx, cy, x2, y2);
You can negate radius to get the curve to go the other way, or switch +/- for the cos/sin in the final calculation. It's a matter of personal taste really..
var ctx = document.querySelector("canvas").getContext("2d");
var x1 = 10, y1 = 10, x2 = 250, y2 = 250, radius = 150;
// Step 1: Calculate the difference between the start and end point
var diffX = x2 - x1,
diffY = y2 - y1;
// Step 2: Find angle between the two points
var angle = Math.atan2(diffY, diffX);
// Step 3: find center of line
var mx = x1 + diffX * 0.5,
my = y1 + diffY * 0.5;
// Step 4: produce control point
var cx = mx + radius * Math.sin(angle),
cy = my - radius * Math.cos(angle);
// pre-viz:
ctx.fillStyle = ctx.strokeStyle = "#d00";
ctx.fillRect(cx-2,cy-2,4,4);
ctx.moveTo(x1, y1); ctx.lineTo(x2, y2);
ctx.moveTo(mx, my); ctx.lineTo(cx, cy);
ctx.stroke(); ctx.beginPath();
// result:
ctx.strokeStyle = "#000";
ctx.lineWidth = 3;
ctx.moveTo(x1, y1);
ctx.quadraticCurveTo(cx, cy, x2, y2);
ctx.stroke();
<canvas height=300></canvas>
Upvotes: 3