Reputation: 83
I'm given an array of points for a rectangle, sorted from top-left position onwards to each corner clockwise. I need to find the start and end points for a line of each specific corner length from the corner to each side of the rectangle. My code is rotating the points by some random angle just to illustrate the problem but in my actual scenario I only have the corner points and need the start and end points to map out each corner. I'm currently offsetting each point as if the point using directional value array that match to the order of the points in the array when the rectangle isn't rotated, but it doesn't work because the points can be rotated at which point this order also changes. There's an application of sine and cosine that I fail to grasp I'm sure. How can I get the value of each corner point offset by a given length towards each of its sides aligned so as to everything rotates together uniformly?
function rotate(cx, cy, x, y, angle) {
var radians = (Math.PI / 180) * angle,
cos = Math.cos(radians),
sin = Math.sin(radians),
nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
return {x: nx, y: ny};
}
const colors =["red","blue", "green","black"];
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
let centroid = {};
let points = [{x:30,y:30},{x:110, y:30},{x:110,y:110},{x:30, y:110}];
function update() {
const randAngle = Math.random() * 180 * (Math.random() > 0.5 ? -1:1);
const length = points.length;
centroid = points.reduce((last, current)=> {
last.x += current.x / length;
last.y += current.y / length;
return last;
}, {x: 0, y:0});
points =
points.map(point=> rotate(centroid.x,
centroid.y,
point.x,
point.y,
randAngle));
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw centroid
ctx.beginPath();
ctx.arc(centroid.x, centroid.y, 4, 0, Math.PI * 2);
ctx.stroke();
// draw Square
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for(let i=1;i<points.length;i++) {
ctx.lineTo(points[i].x, points[i].y);
}
ctx.closePath();
ctx.stroke();
// draw corner points
for(let i=0;i < points.length;i++) {
ctx.beginPath();
ctx.fillStyle = colors[i%colors.length];
ctx.arc(points[i].x, points[i].y, 3, 0, Math.PI * 2);
ctx.fill();
}
const cornerLength = 10;
const startPointDirections = [{ x: 0, y: 1 },
{ x: -1, y: 0 },
{ x: 0, y: -1 },
{ x: 1, y: 0 }];
const endPointDirections = [{ x: 1, y: 0 },
{ x: 0, y: 1 },
{ x: -1, y: 0 },
{ x: 0, y: -1 }];
// draw corner start points and endpoints
for(let i=0;i < points.length;i++) {
ctx.beginPath();
ctx.fillStyle = colors[i%colors.length];
ctx.arc(startPointDirections[i].x * cornerLength + points[i].x, startPointDirections[i].y * cornerLength + points[i].y, 3, 0, Math.PI * 2);
ctx.arc(endPointDirections[i].x * cornerLength + points[i].x, endPointDirections[i].y * cornerLength + points[i].y, 3, 0, Math.PI * 2);
ctx.fill();
}
}
setInterval(()=> {
update();
draw();
}, 2000);
<canvas></canvas>
Upvotes: 2
Views: 984
Reputation: 80177
Seems you need to apply your rotate
function to offset points relative to rotated corner, but you have no angle value at this moment. So you can just get direction vectors from rotated sides.
function rotate(cx, cy, x, y, angle) {
var radians = (Math.PI / 180) * angle,
cos = Math.cos(radians),
sin = Math.sin(radians),
nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
return {x: nx, y: ny};
}
const colors =["red","blue", "green","black"];
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
let centroid = {};
let points = [{x:30,y:30},{x:110, y:30},{x:110,y:110},{x:30, y:110}];
function update() {
const randAngle = Math.random() * 180 * (Math.random() > 0.5 ? -1:1);
const length = points.length;
centroid = points.reduce((last, current)=> {
last.x += current.x / length;
last.y += current.y / length;
return last;
}, {x: 0, y:0});
points =
points.map(point=> rotate(centroid.x,
centroid.y,
point.x,
point.y,
randAngle));
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw centroid
ctx.beginPath();
ctx.arc(centroid.x, centroid.y, 4, 0, Math.PI * 2);
ctx.stroke();
// draw Square
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for(let i=1;i<points.length;i++) {
ctx.lineTo(points[i].x, points[i].y);
}
ctx.closePath();
ctx.stroke();
// draw corner points
for(let i=0;i < points.length;i++) {
ctx.beginPath();
ctx.fillStyle = colors[i%colors.length];
ctx.arc(points[i].x, points[i].y, 3, 0, Math.PI * 2);
ctx.fill();
}
const cornerLength = 10;
// draw corner start points and endpoints
for(let i=0;i < points.length;i++) {
ctx.beginPath();
ctx.fillStyle = colors[i%colors.length];
let dx = points[i].x - points[(i+3)%4].x //previous index in cyclic manner
let dy = points[i].y - points[(i+3)%4].y
let len = Math.sqrt(dx*dx+dy*dy) //really you know side length so use known value
dx = cornerLength * dx/len //normalized vector multiplied by magnitude
dy = cornerLength * dy/len
startx = points[i].x + dx
starty = points[i].y + dy
endx = points[i].x + dy //here apply rotated by -Pi/2 offset vector
endy = points[i].y - dx
ctx.arc(startx, starty, 3, 0, Math.PI * 2);
ctx.arc(endx, endy, 3, 0, Math.PI * 2);
ctx.fill();
}
}
setInterval(()=> {
update();
draw();
}, 2000);
<canvas></canvas>
(Really dx/len
and dy/len
are equal to cos(angle)
and sin(angle)
or vice versa, with different combinations of signs)
Upvotes: 2