0

I am using an HTML canvas to draw details on an image.

One of the details I am trying to draw is the direction of the North Pole, provided as an angle between 0-360 degrees. I want to paint a little sign somewhere along the side of the image pointing towards north.

I want to convert the angle into a point on the sides of the rectangular image whereby

  • 0 degrees is at coordinates { x: width / 2, y: 0 }
  • 90 degrees is at coordinates { x: width , y: height / 2 }
  • 180 degrees is at coordinates { x: width / 2, y: height }
  • 270 degrees is at coordinates { x: 0 , y: height / 2 }

After many attempts I came up with a function that works almost as intended. I draw a 'ray' from the center of the rectangle to a point on the side of the rectangle with the angle provided. I can then calculate the slope and use that to get the position of the missing x or y value.

The function does not work as intended, mostly when it comes to angles that are closer to the corners. I suspect (but remember too little from school geometry) that it has something to do with the radian.

interface Point {
  x: number;
  y: number;
}

interface Line {
  point1: Point;
  point2: Point;
}


function calcSlope(line: Line): number {
  return (line.point1.y - line.point2.y) / (line.point1.x - line.point2.x);
}

/**
 *  takes an angle in degree and rectangle dimensions returning point on rectangle's outline representing angle
 */
function convertAngleToPoint(
  width: number,
  height: number,
  angle = 360
): Point | null {
  // Calculate the midpoint of the rectangle
  const centerX = width / 2;
  const centerY = height / 2;

  // Calculate the endpoint of the line from the midpoint to (width/2, 0)
  const endX = width / 2;
  const endY = 0;

  // Calculate the angle in radians
  const angleInRadians = (angle * Math.PI) / 180;

  // Calculate the coordinates of the ray 
  const targetX =
    Math.cos(angleInRadians) * (endX - centerX) -
    Math.sin(angleInRadians) * (endY - centerY) +
    centerX;
  const targetY =
    Math.sin(angleInRadians) * (endX - centerX) +
    Math.cos(angleInRadians) * (endY - centerY) +
    centerY;

  const slope = calcSlope({
    point1: { x: centerX, y: centerY },
    point2: { x: targetX, y: targetY },
  });

  if (angle === 0) {
    return { x: width / 2, y: 0 };
  }
  if (angle < 45) {
    // label on top
    const x = targetX + targetY / slope;
    return { x, y: 0 };
  }
  if (angle < 135) {
    // label right
    const y = targetY + (width - targetX) * slope;
    return { x: width, y };
  }
  if (angle < 225) {
    // label bottom
    const x = targetX + (height - targetY) / -slope;
    return { x, y: height };
  }
  if (angle < 315) {
    // label left
    const y = targetY + (0 - targetX) * -slope;
    return { x: 0, y };
  }

  if (angle < 361) {
    // label on top
    const x = targetX + targetY / slope;
    return { x, y: 0 };
  }

  return null;
}
szk
  • 11
  • 1
  • consider that the distance ( length of line ) will be longer for a corner. maybe change your line length to default to the corner length ( Pythagorean formula ) and then trim it back to where it intersects with your original box side – This Guy Aug 01 '23 at 16:40
  • https://stackoverflow.com/questions/5271654/intersection-between-circle-and-axis-aligned-rectangle this might help give you what you want and some additional resource reading https://math.stackexchange.com/questions/4039873/given-a-point-on-a-circle-find-it-on-a-given-square – This Guy Aug 01 '23 at 16:44

0 Answers0