MD Rijwan
MD Rijwan

Reputation: 481

How to find the closest coordinate on a line from a given coordinate at given angle in HTML Canvas

I intend to find the coordinate on the line that is closest to from a given coordinate. Let's suppose I have a line.

var line={x0:114,y0:366,x1:306,y1:30};

And I have some random coordinate like 300 and 200 both are in pixel. I am able to connect the line with straight line concept. The function of shortest coordinate on line from given coordinate is:

function getClosestPointOnLine(line,x,y) {
    lerp=function(a,b,x){ return(a+x*(b-a)); };
    var dx=line.x1-line.x0;
    var dy=line.y1-line.y0;
    var t=((x-line.x0)*dx+(y-line.y0)*dy)/(dx*dx+dy*dy);
    t=Math.min(1,Math.max(0,t));
    var lineX=lerp(line.x0, line.x1, t);
    var lineY=lerp(line.y0, line.y1, t);
    return({x:lineX,y:lineY});
};

And connected the line with :

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cord=getClosestPointOnLine(line_d,x,y);
ctx.lineTo(cord3.x,cord3.y);

The example is:

Straight line joining to the line

But I wanted to join the line at some angle which parallel to the stripes given the figure. Such as: The desired output

How should I change my approach to get the desired result.

Upvotes: 0

Views: 639

Answers (1)

Kaiido
Kaiido

Reputation: 136627

You have one point on a line, and an angle, so you can easily draw your line using a bit of trigo:

const pt1 = {
  x: 120,
  y: 100
};
const r = 30; // length of our segment, not really needed afterward


const ctx = canvas.getContext('2d');

function draw() {
  const angle = inp.value;
  // find pt2 using trigonometry
  const pt2 = {
    x: pt1.x + r * Math.cos(angle),
    y: pt1.y + r * Math.sin(angle)
  };

  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillRect(pt1.x - 2, pt1.y - 2, 4, 4);
  ctx.fillRect(pt2.x - 2, pt2.y - 2, 4, 4);
  ctx.beginPath();
  ctx.moveTo(pt1.x, pt1.y);
  ctx.lineTo(pt2.x, pt2.y);
  ctx.stroke();
}

inp.oninput = draw;
draw();
<input type="range" id="inp" min="0" max="6.29" step="0.01"><br>
<canvas id="canvas"></canvas>

So now, all you need to find is the point of intersection of these two lines:

const line1 = {
  x1: 30,
  y1: 30,
  x2: 10,
  y2: 100
};

const pt1 = {
  x: 80,
  y: 80
}

const ctx = canvas.getContext('2d');

function draw(x, y) {
  angle = inp.value;
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.strokeStyle = 'gray';
  ctx.beginPath();
  ctx.moveTo(line1.x1, line1.y1);
  ctx.lineTo(line1.x2, line1.y2);
  ctx.stroke();

  const line2 = {
    x1: pt1.x,
    y1: pt1.y,
    // here our radius can be hardcoded
    x2: pt1.x + 1 * Math.cos(angle),
    y2: pt1.y + 1 * Math.sin(angle)
  }
  const inter = intersect(
    line1.x1, line1.y1, line1.x2, line1.y2,
    line2.x1, line2.y1, line2.x2, line2.y2
  );
  if (!inter) {
    ctx.fillText('parrallel', 20, 20);
    return;
  }
  if (inter.x < Math.min(line1.x1, line1.x2) || inter.x > Math.max(line1.x1, line1.x2) ||
    inter.y < Math.min(line1.y1, line1.y2) || inter.y > Math.max(line1.y1, line1.y2)) {
    ctx.fillText('Out of bounds', 20, 20);
    return;
  }
  ctx.strokeStyle = 'green';
  ctx.beginPath();
  ctx.moveTo(pt1.x, pt1.y);
  ctx.lineTo(inter.x, inter.y);
  ctx.stroke();
}

document.onmousemove = e => {
  const rect = canvas.getBoundingClientRect();
  pt1.x = e.clientX - rect.left;
  pt1.y = e.clientY - rect.top;
  draw();
};
inp.oninput = draw;
draw();

/* Edited from http://paulbourke.net/geometry/pointlineplane/javascript.txt */
function intersect(x1, y1, x2, y2, x3, y3, x4, y4) {

  // Check if none of the lines are of length 0
  if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) {
    return false
  }

  denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))

  // Lines are parallel
  if (denominator === 0) {
    return false
  }

  let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator
  let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator

  // Return a object with the x and y coordinates of the intersection
  let x = x1 + ua * (x2 - x1)
  let y = y1 + ua * (y2 - y1)

  return {
    x,
    y
  }
}
<input type="range" id="inp" min="0" max="6.29" step="0.01" value="0"><br>
<canvas id="canvas"></canvas>

And you will note that there is no "closest" point in this case, because you will always have at most only a single point that will meet these conditions.

Upvotes: 2

Related Questions