8

我一直在努力解决这个问题。。

要解决的问题。。

说我有3分..

P1 ---------- P2, and P3 can be anywhere around P1 and P2

将 P3 插值到 P1 和 P2 之间的线上的计算公式是什么?

我需要一个公式来计算位于 P1 和 P2 之间的线上的 P3 的新 X、Y 坐标。

到目前为止我的代码..

        public Point lerp(Point P0, Point P1, Point P) 
        {
            double y1 = P0.Y + (P1.Y - P0.Y) * ((P.X - P0.X) / (P1.X - P0.X));
            double x1 = P.X;

            double y2 = P.Y;
            double x2 = P0.X + (P1.X - P0.X) * ((P.Y - P0.Y) / (P1.Y - P0.Y));

            return new Point((x1 + x2) / 2, (y1 + y2) / 2);
        }

我的参考.. http://en.wikipedia.org/wiki/Linear_interpolation

上面的代码让它接近了,但它有点偏离......

这是 Corey Ogburn 转换后的 javascript 代码

        public Point _pointOnLine(Point pt1, Point pt2, Point pt)
        {
            bool isValid = false;

            var r = new Point(0, 0);
            if (pt1.Y == pt2.Y && pt1.X == pt2.X) { pt1.Y -= 0.00001; }

            var U = ((pt.Y - pt1.Y) * (pt2.Y - pt1.Y)) + ((pt.X - pt1.X) * (pt2.X - pt1.X));

            var Udenom = Math.Pow(pt2.Y - pt1.Y, 2) + Math.Pow(pt2.X - pt1.X, 2);

            U /= Udenom;

            r.Y = pt1.Y + (U * (pt2.Y - pt1.Y));
            r.X = pt1.X + (U * (pt2.X - pt1.X));

            double minx, maxx, miny, maxy;

            minx = Math.Min(pt1.X, pt2.X);
            maxx = Math.Max(pt1.X, pt2.X);

            miny = Math.Min(pt1.Y, pt2.Y);
            maxy = Math.Max(pt1.Y, pt2.Y);

            isValid = (r.X >= minx && r.X <= maxx) && (r.Y >= miny && r.Y <= maxy);

            return isValid ? r : new Point();
        }
4

3 回答 3

8

这是我们在工作中(一家 GIS 公司)使用的一些 javascript 代码,用于在用户想要通过向其添加顶点来分割线的情况下找出鼠标旁边的最近点。应该很容易转移到 C#:

function _pointOnLine(line1, line2, pt) {
    var isValid = false;

    var r = new Microsoft.Maps.Location(0, 0);
    if (line1.latitude == line2.latitude && line1.longitude == line2.longitude) line1.latitude -= 0.00001;

    var U = ((pt.latitude - line1.latitude) * (line2.latitude - line1.latitude)) + ((pt.longitude - line1.longitude) * (line2.longitude - line1.longitude));

    var Udenom = Math.pow(line2.latitude - line1.latitude, 2) + Math.pow(line2.longitude - line1.longitude, 2);

    U /= Udenom;

    r.latitude = line1.latitude + (U * (line2.latitude - line1.latitude));
    r.longitude = line1.longitude + (U * (line2.longitude - line1.longitude));

    var minx, maxx, miny, maxy;

    minx = Math.min(line1.latitude, line2.latitude);
    maxx = Math.max(line1.latitude, line2.latitude);

    miny = Math.min(line1.longitude, line2.longitude);
    maxy = Math.max(line1.longitude, line2.longitude);

    isValid = (r.latitude >= minx && r.latitude <= maxx) && (r.longitude >= miny && r.longitude <= maxy);

    return isValid ? r : null;
}

line1是一个带有经纬度的点,表示线的端点之一,相当于你的P1。line2是另一个端点:P2。pt是你的P3。这将返回 P3 垂直通过的线上的点。如果 P3 超过了线的任一端,这将返回 null,这意味着两个端点之一是离 P3 最近的点。

为了清楚起见:

在此处输入图像描述

于 2013-03-05T19:55:19.623 回答
4

问题是您的 Point 具有 X 和 Y 的整数值,因此您正在进行整数除法。尝试将您的值转换为floator double,进行计算,然后将它们返回为整数。

请注意,当您这样做时: (P1.Y - P0.Y) * ((PX - P0.X) / (P1.X - P0.X)) 由于 5/2 的结果,您实际上正在失去精度是 2,而不是 2.5,但是当您的值是实数时,5.0/2.0 确实是 2.5。

你应该试试这个:

double y1 = P0.Y + (double)(P1.Y - P0.Y) * ((double)(P.X - P0.X) / (double)(P1.X - P0.X));
double x1 = P.X; //these two are implicit casts

double y2 = P.Y;
double x2 = P0.X + (double)(P1.X - P0.X) * ((double)(P.Y - P0.Y) / (double)(P1.Y - P0.Y));

return new Point((x1 + x2) / 2.0, (y1 + y2) / 2.0); //put 2.0 for any case even though x1+x2 is `double`

此外,然后您正在从 double 转换为 int,数字的小数部分会自动切断,因此例如 3.87 将变为 3。如果您可以使用它,那么您的最后一行应该更精确:

   return new Point((x1 + x2) / 2.0 + 0.5, (y1 + y2) / 2.0 + 0.5);

这将有效地将双精度值舍入到更接近的整数值。

编辑:

但是如果你只是想在两点之间的线上找到点 p3,那么使用这种方法会更容易:

public Point lerp(Point P0, Point P1) 
{
      double x = ((double)P0.X + P1.X)/2.0;

      double y = (double)P0.Y + (double)(P1.Y - P0.Y) * ((double)(x - P0.X) / (double)(P1.X - P0.X));
      return new Point(x + 0.5, y + 0.5);
}
于 2013-03-05T19:27:03.757 回答
0

这是一个老问题,我发现 Corey Ogburn 解决方案非常有用。但我认为发布较少“地图”版本的 javascript 代码可能对其他人有所帮助——我在画布绘图中使用了该代码。

export const pointOnLine = (p0, p1, q) => {

  // p0 and p1 define the line segment
  // q is the reference point (aka mouse)
  // returns point on the line closest to px

  if (p0.x == p1.x && p0.y == p1.y) p0.x -= 0.00001;

  const Unumer = ((q.x - p0.x) * (p1.x - p0.x)) + ((q.y - p0.y) * (p1.y - p0.y));
  const Udenom = Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2);
  const U = Unumer / Udenom;

  const r = {
    x: p0.x + (U * (p1.x - p0.x)),
    y: p0.y + (U * (p1.y - p0.y))
  }

  const minx = Math.min(p0.x, p1.x);
  const maxx = Math.max(p0.x, p1.x);
  const miny = Math.min(p0.y, p1.y);
  const maxy = Math.max(p0.y, p1.y);

  const isValid = (r.x >= minx && r.x <= maxx) && (r.y >= miny && r.y <= maxy);

  return isValid ? r : null;
}
于 2020-09-29T14:57:59.113 回答