pallandotheblue
pallandotheblue

Reputation: 83

Get the points for the corners of a rectangle

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

Answers (1)

MBo
MBo

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.

enter image description here

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

Related Questions