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

上記のコードはそれに近づきますが、少しずれています...

これがCoreyOgburnから変換された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

これは、ユーザーが頂点を追加して線を分割したい状況で、マウスが隣にある線上の最も近い点を見つけるためにここで使用した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に相当する、線の端点の1つを表す緯度と経度のポイントです。line2もう一方のエンドポイントはP2です。ptあなたのP3です。これにより、P3が垂直になる線上の点が返されます。P3が線のいずれかの端を超えている場合、これはnullを返します。これは、2つの端点の1つがP3に最も近い点であることを意味します。

明確にするために:

ここに画像の説明を入力してください

于 2013-03-05T19:55:19.623 に答える
4

問題は、PointにXとYの整数値があるため、整数除算を実行していることです。値をfloatまたはにキャストdoubleし、計算を行ってから、それらを整数に戻します。

これを行うとき:(P1.Y-P0.Y)*((PX-P0.X)/(P1.X-P0.X))5/2の結果以降、実際には精度が失われていることに注意してください。は2.5ではなく2ですが、値が実数の場合、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値がより近い整数値に効果的に丸められます。

編集:

ただし、2つのポイント間の線上でポイント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

これは古い質問であり、CoreyOgburnソリューションが非常に役立つことがわかりました。しかし、私がキャンバスの描画で使用した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 に答える