I'm using HTML canvas to draw Line as shown in the following figure, but this line has an edges on the two sides of it.
As shown in the image the two edges are not perpendicular to the major line.
I tried the following sollutions but it didn't succeed:
* rotate the edges lines, but the rotation will transform them from the original position
* find the angle of the major line and then draw the lines with respect to the line, but this solution was not easy to implement (most likely i implement it wrongly).
this is my code but it will always draw vertical edges:
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="200" height="200" style="border:1px solid #d3d3d3;">
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var x1 = 100;
var x2 = 150;
var y1 = 50;
var y2 = 120;
ctx.beginPath();
ctx.strokeStyle = "purple"; // Purple path
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.stroke(); // Draw it
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x1,y1+10);
ctx.stroke();
ctx.restore();
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x1,(y1-10));
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x2,y2);
ctx.lineTo(x2,y2+10);
ctx.stroke();
ctx.restore();
ctx.beginPath();
ctx.moveTo(x2,y2);
ctx.lineTo(x2,(y2-10));
ctx.stroke();
</script>
</body>
</html>
can anyone help me with rotating the two edges lines in order to become perpendicular to the major line. Thanks.
Perpendicular lines are simple to calculate if you remember the rotate 90deg rule for a 2D vector.
A vector {x,y}
can be rotated 90 deg clockwise {-y,x}
or anti clockwise {y,-x}
. Swap x and y and negate the y for clockwise or x of anti clock wise
So for a line segment x1
,y1
to x2
,y2
convert to a vector, normalise that vector and rotate 90 deg as follows
function getPerpOfLine(x1,y1,x2,y2){ // the two points can not be the same
var nx = x2 - x1; // as vector
var ny = y2 - y1;
const len = Math.sqrt(nx * nx + ny * ny); // length of line
nx /= len; // make one unit long
ny /= len; // which we call normalising a vector
return [-ny, nx]; // return the normal rotated 90 deg
}
Then say you want to draw a 10 pixel line at the ends of the line segment
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
document.body.append(canvas);
ctx.strokeStyle = "black";
ctx.lineJoin = ctx.lineCap = "round";
ctx.lineWidth = 3;
// the line segment
const x1 = 40, y1 = 40, x2 = 260, y2 = 110;
const endLen = 10; // length of end lines
var px = y1 - y2; // as vector at 90 deg to the line
var py = x2 - x1;
const len = endLen / Math.hypot(px, py);
px *= len; // make leng 10 pixels
py *= len;
// draw line the start cap and end cap.
ctx.beginPath();
ctx.lineTo(x1, y1); // the line start
ctx.lineTo(x2, y2);
ctx.moveTo(x1 + px, y1 + py); // the start perp line
ctx.lineTo(x1 - px, y1 - py);
ctx.moveTo(x2 + px, y2 + py); // the end perp line
ctx.lineTo(x2 - px, y2 - py);
ctx.stroke();
Update
Simple solution to rendering content along a line, Uses the same 90deg rule.
There is an alternative method of rendering using the same vector rotation, but rather than set the perpendicular axis via vector multiplication, you set the y axis of the transform to 90deg from the x axis which is along the line. Set the origin to the start of the line and you simply render relative to the line.
setTransformToLine(x1, y1, x2, y2)
The following function will set the canvas transform to be along the line
// Set 2D context current transform along the line x1,y1,x2,y2 and origin to
// start of line. y Axis is rotated clockwise 90 from the line.
// Returns the line length as that is most frequently required when
// using the method saving some time.
function setTransformToLine(x1, y1, x2, y2) {
const vx = x2 - x1; // get the line as vector
const vy = y2 - y1;
const len = Math.hypot(vx, vy); // For <= IE11 use Math.sqrt(vx * vx + vy * vy)
const nx = vx / len; // Normalise the line vector. Making it one
const ny = vy / len; // pixel long. This sets the scale
// The transform is the normalised line vector for x axis, y at 90 deg
// and origin at line start
ctx.setTransform(nx, ny, -ny, nx, x1, y1); // set transform
return len;
}
This example show how to use the transform to do the same line but add annotated length as well.
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
document.body.append(canvas);
ctx.strokeStyle = "black";
ctx.lineJoin = ctx.lineCap = "round";
ctx.lineWidth = 3;
ctx.font = "16px arial";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
const x1 = 40, y1 = 40, x2 = 260, y2 = 110;
const endLen = 10;
function setTransformToLine(x1, y1, x2, y2) {
const vx = x2 - x1;
const vy = y2 - y1;
const len = Math.hypot(vx, vy);
const nx = vx / len;
const ny = vy / len;
ctx.setTransform(nx, ny, -ny, nx, x1, y1);
return len;
}
// Set the transform along the line. Keep the line length
// line len is need to get the x coord of the end of the line
const lineLen = setTransformToLine(x1, y1, x2, y2);
const lineLenStr = Math.round(lineLen) + "px";
const textWidth = ctx.measureText(lineLenStr).width;
const rlen = lineLen - textWidth - 16; // find the remaining line len after removing space for text
// Rendering is done in line local coordinates
// line is from (0,0) to (lineLen,0)
// Now draw the line the ends first and then along the line leaving gap for text
ctx.beginPath();
ctx.lineTo(0, -endLen); // start perp line
ctx.lineTo(0, endLen);
ctx.moveTo(lineLen, -endLen); // end of line is at lineLen
ctx.lineTo(lineLen, endLen);
ctx.moveTo(0,0); // line start segment
ctx.lineTo(rlen / 2, 0);
ctx.moveTo(lineLen - rlen / 2,0); // line end segment
ctx.lineTo(lineLen, 0);
ctx.stroke(); // render it.
// now add text at the line center
ctx.fillText(lineLenStr, lineLen / 2, 0);
// To restore the transform to its default use identity matrix
ctx.setTransform(1, 0, 0, 1, 0, 0);
You need only calculate the slope of the original line, which is (y2 - y1)/(x2 - x1)
and then use that to slope the new lines at the edges of the original. Here's a simple example:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var x1 = 100;
var x2 = 150;
var y1 = 50;
var y2 = 120;
ctx.beginPath();
ctx.strokeStyle = "purple"; // Purple path
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke(); // Draw it
var slope = (y2 - y1) / (x2 - x1);
ctx.beginPath();
ctx.lineTo(x1 + slope * 4, y1 - slope * 4);
ctx.lineTo(x1 - slope * 4, y1 + slope * 4);
ctx.stroke();
ctx.beginPath();
ctx.lineTo(x2 - slope * 4, y2 + slope * 4);
ctx.lineTo(x2 + slope * 4, y2 - slope * 4);
ctx.stroke();
<canvas id="myCanvas" width="200" height="200" style="border:1px solid #d3d3d3;">
Note that this is more of a mathematical theory question, thus the example just showcases the idea of how to do this. You should take a moment to read more about math and geometry to perfectly implement the logic behind this.
On a side note, your code has been simplified, as a few of the draw calls for the perpendicular lines were redundant and could have been merged.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With