由2点定义的在线点投影

时间:2013-03-05 19:22:44

标签: c# geometry

我一直试图解决这个问题。

要解决的问题..

说我有3分..

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

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

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

到目前为止我的代码..

        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();
        }

3 个答案:

答案 0 :(得分:5)

这是我们在工作中使用的一些javascript代码(一家GIS公司),在用户希望通过向其添加顶点来分割线条的情况下,找出鼠标旁边一条线上的最近点。应该很容易转移到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最近的点。

为清楚起见:

enter image description here

答案 1 :(得分:3)

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

请注意,当您这样做时: (P1.Y - P0.Y)*((P.X - 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);

将有效地将double值舍入为更接近的整数值。

修改

但是如果你只是想在两点之间的线上找到点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);
}

答案 2 :(得分: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;
}