Как рассчитать зеркальную точку вдоль линии? - PullRequest
7 голосов
/ 21 января 2012

В 2D плоскости у меня есть точка и линия.Как получить точку зеркала вдоль этой линии?

Ответы [ 8 ]

16 голосов
/ 21 января 2012

Когда подобные вещи выполняются в компьютерных программах, одной из проблем, с которыми вам, возможно, придется столкнуться, является выполнение этих вычислений только с использованием целочисленной арифметики (или в максимально возможной степени), предполагая, что ввод вводится в целых числах. Делать это как можно больше в целых числах - это отдельная проблема, которую я не буду здесь освещать.

Ниже приведено «математическое» решение, которое в буквальном смысле потребует вычислений с плавающей запятой. Я не знаю, приемлемо ли это в вашем случае. Вы можете оптимизировать его по своему вкусу.

(1) Представьте вашу линию L на

A * x + B * y + C = 0

уравнение. Обратите внимание, что вектор (A, B) является вектором нормали этой линии.

Например, если линия определяется двумя точками X1(x1, y1) и X2(x2, y2), то

A = y2 - y1
B = -(x2 - x1)
C = -A * x1 - B * y1

(2) Нормализовать уравнение путем деления всех коэффициентов на длину вектора (A, B). То есть рассчитать длину

M = sqrt(A * A + B * B)

и затем вычислите значения

A' = A / M
B' = B / M
C' = C / M

Уравнение

A' * x + B' * y + C' = 0

по-прежнему является эквивалентным уравнением вашей линии L за исключением того, что теперь нормальный вектор (A', B') является единичным вектором.

(3) Возьмите свою точку P(px, py) и вычислите значение

D = A' * px + B' * py + C'

Это даст вам подписанное расстояние D от вашей точки P до вашей линии L. Другими словами, это расстояние от P до ближайшей точки на L (нас не интересует сама ближайшая точка, нам просто нужно расстояние).

Знак говорит, на какой стороне линии L находится точка P. Если P лежит на той же стороне, на которую указывает вектор (A', B') («положительная» сторона), расстояние будет положительным. Если P лежит на другой стороне («отрицательной» стороне), расстояние будет отрицательным.

(4) Чтобы найти точку зеркала P'(px', py'), вам необходимо переместить вашу точку P на абсолютное расстояние |2 * D| через линию L в другую сторону.

«Через линию» действительно означает, что если точка P лежала на «положительной» стороне L, то мы должны переместить ее против направления вектора (A', B') в «отрицательную» сторону. И наоборот, если точка P лежала на «отрицательной» стороне L, то мы должны переместить ее в направлении вектора (A', B') в «положительную» сторону.

Это может быть просто выражено как перемещение точки на расстояние -2 * D (обратите внимание на минус) в направлении вектора (A', B').

Это означает, что

px' = px - 2 * A' * D
py' = py - 2 * B' * D

дает вам точку зеркала P'(px', py').


В качестве альтернативы вы можете использовать подход, основанный на нахождении фактической ближайшей точки N на линии L, а затем отражении вашей точки P относительно N. Это уже предлагается в других ответах, я просто опишу, как я это сделаю.

(1) Построить уравнение

A*x + B*y + C = 0

для вашей линии L точно так же, как описано в шаге 1 выше. Нет необходимости нормализовать это уравнение.

(2) Постройте уравнение для перпендикулярной линии, проходящей через P. Допустим, что перпендикулярная линия представлена ​​

D*x + E*y + F = 0

Коэффициенты D и E известны сразу

D = B
E = -A

, в то время как F можно рассчитать, подставив точку P в уравнение

F = -D*px - E*py

(3) Найти пересечение этих двух линий, решая систему двух линейных уравнений

A*x + B*y = -C
D*x + E*y = -F

Правило Крамера в этом случае работает очень хорошо. Формула, приведенная в статье Пересечение линий в Википедии, является не чем иным, как применением правила Крамера к этой системе.

Решение дает вам ближайшую точку N(nx, ny), которую мы искали.

(4) Теперь просто посчитайте

px' = nx + (nx - px)
py' = ny + (ny - py)

чтобы найти свою точку P'(px', py').

Обратите внимание, что этот подход может быть почти полностью реализован в целых числах.Единственный шаг, который может потерять точность, - это разделение внутри правила Крамера на шаге 3. Конечно, как обычно, цена, которую вам придется заплатить за «почти интегральное» решение, - это необходимость арифметики большого числа.Даже коэффициенты C и F могут переполниться, даже не упоминая вычисления внутри формул правила Крамера.

7 голосов
/ 22 января 2012

Детали зависят от того, как ваша линия представлена. Если вы представляете ее как произвольную точку P на линии вместе с вектором столбца единицы n вдоль линии, то точка зеркала Q 'в любой точке Q определяется как:

Q '= Q + 2 (I - nn T ) ( P - Q )

(Здесь I - единичная матрица 2x2, n T - транспонирование n (трактовка n как 2x1 матрица), и nn T - матрица 2x2, образованная стандартным умножением матрицы n на n T .) Нетрудно показать, что Q 'не изменится, если вы переместите P в любое место строки.

Нетрудно преобразовать другие линейные представления в векторное представление точки / единицы.

6 голосов
/ 21 января 2012

Предположим, уравнение линии ax + by + c = 0.Теперь представьте линию, перпендикулярную к ней, которая может быть представлена ​​-bx + ay + d = 0 (произведение уклонов двух перпендикулярных линий равно -1).Теперь проблема в том, чтобы найти d.Поместите координату точки во вторую строку, и вы легко получите значение d.

Вторая часть состоит в том, чтобы найти точку во второй строке, которая равноудалена отПервая точка от первой линии.Для этого вы можете найти пересечение двух линий.Рассчитайте разницу в x и y данной точки и точки пересечения.Теперь добавьте их к значениям x и y точки пересечения.Это дает необходимую вам точку зрения (вам может понадобиться свести на нет различия - это соответствует порядку вычитания, который вы используете).

2 голосов
/ 21 января 2012

Вычислить ближайшую точку на линии к рассматриваемой точке. Затем инвертируйте направление вектора между этими точками и добавьте его к ближайшей точке на линии. Вуаля, вы нашли зеркальную точку.

1 голос
/ 21 января 2012

Я сделал именно это для другой системы, которую я построил .. В моем коде намного больше этого;так что я надеюсь, что я извлек все необходимые биты ... Line.ClosestPoint(Point pt) - это метод, который вы хотите ...

Алгоритм основан на идее, что наклон линии, перпендикулярной любой данной линии, являетсяОтрицательный мультипликативный обратный наклон данной линии.то есть, если одна линия имеет наклон m, то другая линия имеет наклон -1 / m.Поэтому все, что вам нужно сделать, это сформировать линию через точку с наклоном, равным -1 / m, и найти пересечение этой линии с исходной линией.

public class Line
{
    protected const double epsilon = 1.0e-8;

    public Point Anchor { get; set; }

    public double Slope { get; set; }

    public virtual Point ClosestPoint(Point pt)
    { return Intersection(Make(pt, -1 / Slope)); }

    protected Line(Point anchor, double slope)
    {
        Anchor = anchor;
        Slope = slope;
    }

    public static Line Make(Point anchor, double slope)
    { return new Line(anchor, slope); }

    public virtual Point Intersection(Line line)
    {
        if (lib.Within(line.Slope, Slope, epsilon))              
            if( lib.Within(line.YIntcpt, YIntcpt, epsilon))
                // code for NoInterceptException not included
                throw new NoInterceptException(
                    "The two lines overlap.");
            else return Point.NullPoint;
        // ----------------------------------------
        double tm = Slope, om = line.Slope,
            tx = Anchor.X, ty = Anchor.Y,
            ox = line.Anchor.X, oy = line.Anchor.Y;

        var x = double.IsInfinity(tm) ? tx :
                double.IsInfinity(om) ? ox :
                   (tm * tx - om * ox + oy - ty) /
                          (tm - om);

        var y = double.IsInfinity(tm) ?
               om * (x - ox) + oy :
               tm * (x - tx) + ty;

        return Point.Make(x, y);
    }
}

public struct Point
{
    const double epsilon = 1.0e-12;
    private readonly bool isDef;
    private readonly double y;
    private readonly double x;
    public bool HasValue { get { return isDef; } }
    public bool IsNull { get { return !isDef; } }

    private Point(double xValue, double yValue)
    { x = xValue; y = yValue; isDef = true; }
    public static Point Make(double x, double y)
    { return new Point(x, y); }

    public double X 
    {
        get
        {
                  // code for AlgebraicException not included
            if (IsNull) throw new AlgebraicException("Null Point Object"); 
            return x;
        }
    }
    public double Y
    {
        get
        {
                  // code for AlgebraicException not included
            if (IsNull) throw new AlgebraicException("Null Point Object");
            return y;
        }
    }

    public static Point NullPoint { get { return new Point();}}

    //  Other functionality

} 
1 голос
/ 21 января 2012

Я предполагаю, что у вас есть местоположение точки и уравнение для вашей линии, т.е.

P=(x1,y1) and y = ax + b

Во-первых, очевидный случай, когда a = 0 (то есть линия, параллельная оси x), дает

P'(x2,y2), with x2 = x1, y2 = y1 + 2(b-y1)

Для более общего случая,

  1. Получите общее уравнение для линии, ортогональной вашей: y = cx + d, с ac = -1

    ==> c = -1 / a

  2. Определите b так, чтобы P принадлежало ортогональной линии: y1 = -x1 / a + d

    ==> d = y1 + x1 / a

  3. Получите пересечение между двумя линиями: y = -x / a + y1 + x1 / a = ax + b

    ==> x = (y1 + x1 / a -b) / (a ​​+ 1 / a), y = a (y1 + x1 / a -b) / (a ​​+ 1 / a) + b

  4. Получите вектор между точкой пересечения и точкой P, добавьте его к точке пересечения, чтобы получить точку отражения P '.

    ==> P '(x2, y2), с x2 = x1 + 2 ((y1 + x1 / a -b) / (a ​​+ 1 / a) - x1) y2 = y1 + 2 (a (y1 + x1 / a -b) / (a ​​+ 1 / a) + b - y1)

Некоторые шаги можно упростить, но это общая идея. Я делал алгебру во время набора текста, чтобы не было ошибок. Если вы найдете один, пожалуйста, дайте мне знать.

0 голосов
/ 27 июня 2019

Я разрабатываю код JS, который вычисляет формулу из ответа Теда Хопа : Q '= Q + 2 (I - nn T ) ( P - Q ) преобразовано в

Q '= Q + 2 (I - d 2 vv T ) ( P - Q )

Где вектор-столбец v = R - P является нормальным к строке, но не единицей (как n ), где P и R - две произвольные точки линии; vv T является внешним продуктом ; d=1/sqrt(vx*vx + vy*vy) имеет обратную длину v . Поскольку мы используем d 2 (= r в коде), мы можем оптимизировать вычисления, опуская sqrt

// 2D Points P=[x,y] and R are points on line, 
// Q is point for which we want to find reflection
function mirror(Q,[P,R]) {
  let [vx,vy]= [ R[0]-P[0], R[1]-P[1] ];
  let [x,y]  = [ P[0]-Q[0], P[1]-Q[1] ];
  let r= 1/(vx*vx+vy*vy);
  return [ Q[0] +2*(x -x*vx*vx*r -y*vx*vy*r), 
           Q[1] +2*(y -y*vy*vy*r -x*vx*vy*r)  ];
}

console.log( mirror([3,2], [[2,4],[4,5]]) )
0 голосов
/ 07 июня 2019

Вы можете использовать простую формулу, чтобы найти это. Сначала вы должны написать формулу строки, используя

ax+by+c=0

допустим, ваша строка y = x. и точка, которую вы пытаетесь отразить, (3,2). мы все знаем, что это станет (2,3) в любом случае напишите y = x в стандартной форме, которая станет такой:

y-x=0

Теперь используйте эту формулу и вставьте значения точки вместо x и y.

    (ax+by+c)/a^2+b^2
    (a^2 means a square.)

вы получите значение, поэтому давайте назовем его K. в этом случае, где точка (3,2) и линия y-x = 0, K будет равно:

(-3+2)/(1+1)= -1/2

Теперь, чтобы вычислить новые координаты, все, что вам нужно сделать, это вставить значения в эту формулу:

 x-2ak
 y-2bk

где x и y - исходные координаты, а a и b - значения, использованные в исходной формуле строки (y-x = 0 a = -1, b = 1, c = 0) Таким образом, значение, которое мы получаем:

3 - 2 x -1 x -1/2 = 3-1 = 2
2 - 2 x +1 x -1/2 = 2+1 = 3
...