0

For example I have 3 points: (y:0, x:0), (y:100, x:10), (y:50, x:100). So I need to get 10 points among this polyline the distance between those is equal. I know how to get points between 2 ones, but I defenitely don't know how to receive among several points.

For receiving distance between 2 points I use this:

function getDistance(y1,x1,y2,x2){
    return Math.sqrt(Math.pow(y2-y1, 2) + Math.pow(x2-x1, 2))
}

For computing single points I use it:

function interpolate(a, b, frac){
    return {
        y: a.y+(b.y-a.y)*frac,
        x: a.x+(b.x-a.x)*frac
    };
}

Can anyone help me?

MaximPro
  • 563
  • 8
  • 21

2 Answers2

1

This is working fine (for the example I'm using 3 points on the same line, but it should work for every combination)

function getDistance({y: y1, x:x1}, {y:y2, x:x2}){
  return Math.sqrt(Math.pow(y2-y1, 2) + Math.pow(x2-x1, 2))
}
function interpolate(a, b, frac){
  return {
      x: a.x+(b.x-a.x)*frac,
      y: a.y+(b.y-a.y)*frac,
  };
}
//generate N point in a line
function generateOnLineEveryDistance(a, b, n, distance){
  let res = [];
  for(let i = 0; i < n ; i++){
    // add a point interpolating on a line after (i + 1) * distance from the beginning point (i+1 to avoid the starting point 0,0)
    res.push(interpolate(a, b, (i + 1) * distance))
  }
  return res;
}
function generate(points, n){
  // calculate total distance to find out how distant we have to place the points
  let totalDistance = 0;
  for(let i = 1; i < points.length; i++){
    totalDistance += getDistance(points[i - 1], points[i])
  }
  // distance to place the points
  const pointDistance = totalDistance / (n - 1);
  // the first point is always included
  let res = [points[0]];
  // now, we will consider a segment as point[i - 1] & point[i], and we will consider only the piece where we can fit point:
  // eg. in a segment long 10 ([x:0, y:0], [x:0, y:10]), and a pointDistance of 4, we will  consider only [x:0, y:0], [x:0, y:8]
  // and the remainder is 2... this remainder will be used in the next segment, "appending it at the beginning"
  // let's say the second segment is [x:0, y:10], [x:0, y:20], with the remainder it will be [x:0, y:8], [x:0, y:20]
  let remainder = 0;
  for(let i = 1; i < points.length ; i++){
    // add the remainder if exists at the beginning of the current segment (point[i-1], point[i])
    // source https://stackoverflow.com/questions/7740507/extend-a-line-segment-a-specific-distance
    if(remainder > 0){
      let a = points[i];
      let b = points[i - 1];
      let lengthAB = Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)) 
      points[i - 1].x = b.x + (b.x - a.x) / lengthAB * remainder;
      points[i - 1].y = b.y + (b.y - a.y) / lengthAB * remainder;
    }
    // points we need to generate
    let nPoints = Math.floor(getDistance(points[i - 1], points[i]) / pointDistance)
    // remainder to add to the next iteration
    remainder = getDistance(points[i - 1], points[i]) - nPoints * pointDistance;
    // add to the result the points
    res = [
        ...res, // previous result
        ...generateOnLineEveryDistance( // new points
              points[i - 1], // first point to consider
              points[i],     // second point to consider
              nPoints,       // number of points to generate
              pointDistance / getDistance(points[i - 1], points[i]) // the ratio we want to use to generate them
        )
      ]
  }
  // small fix in case of .333333 decimals that will "miss" the last point because it's .9999 and not 1.00000
  if(res.length < n){
    res = [...res, points[points.length - 1]]
  }
  return res;
}
const points = [
  {
    x: 0,
    y: 0
  } , {
    x: 0,
    y: 10
  } , {
    x: 0,
    y: 20
  }
]

console.log(generate(points, 4))

however, I can see this library already doing it, I've not checked it out, but maybe is worth checking it out because the code I'm providing is pretty untested and unreadable

UPDATE:
I've tested it against the examples they are providing and it's giving back the same result, so GG

Alberto Sinigaglia
  • 12,097
  • 2
  • 20
  • 48
  • Seems working, I should invistigate your code, I want sort out with it. Thanks for the reply. I will write you if I have the questions. Sure, you have deserved +1 for the answer :) – MaximPro Aug 07 '21 at 01:53
  • @MaximPro hi, seen that you are interested to understand the code, I've added some more comments in the hope it makes it more readable – Alberto Sinigaglia Aug 07 '21 at 09:47
  • Well, I've sorted out. And I am too glad, that you've answered. Algorithm is really interesting. And I liked how you used my two util functions and used them next. P.S Thanks, for more comments for the code. – MaximPro Aug 07 '21 at 10:06
0

If the final goal of doing this is to draw a curve that crosses over all these points, feel free to use this library.

If you do something like this:

const path = BezierInterpolation.pointsToBezierCurves(points, false).toPath();

you can use that path inside a svg, like this (reactjs):

<svg>
  <path d={path} />
</svg>

Can check this demo code.

Raúl Otaño
  • 4,640
  • 3
  • 31
  • 65