Нужна функция для ограничения линии (известной по ее координатам) по ее длине

Мне нужна функция, которая берет линию (известную по ее координатам) и возвращает линию с тем же углом, но ограниченным определенной длиной.

Мой код дает правильные значения только тогда, когда линия повернута "вправо"
(доказано только опытным путем, извините).

Я что-то пропустил?

public static double getAngleOfLine(int x1, int y1, int x2, int y2) {
  double opposite = y2 - y1;
  double adjacent = x2 - x1;

  if (adjacent == Double.NaN) {
    return 0;
  }

  return Math.atan(opposite / adjacent);
}

// returns newly calculated destX and destY values as int array
public static int[] getLengthLimitedLine(int startX, int startY,
    int destX, int destY, int lengthLimit) {

  double angle = getAngleOfLine(startX, startY, destX, destY);

  return new int[]{
        (int) (Math.cos(angle) * lengthLimit) + startX,
        (int) (Math.sin(angle) * lengthLimit) + startY
      };
}

Кстати: я знаю, что возвращать массивы в Java глупо, но это только для примера.

6 ответов

Решение

В Python, потому что у меня нет удобного компилятора Java:

import math

def getLengthLimitedLine(x1, y1, x2, y2, lengthLimit):
    length = math.sqrt((x2-x1)**2 + (y2-y1)**2)
    if length > lengthLimit:
       shrink_factor = lengthLimit / length
       x2 = x1 + (x2-x1) * shrink_factor
       y2 = y1 + (y2-y1) * shrink_factor
    return x2, y2

print getLengthLimitedLine(10, 20, 25, -5, 12)
# Prints (16.17, 9.71) which looks right to me 8-)

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

В вашем примере, однако, попробуйте Math.atan2.

Инкапсулируйте Line в классе, добавьте метод unit и метод scale.

public class Line {
private float x;
private float y;

public Line(float x1, float x2, float y1, float y2) {
    this(x2 - x1, y2 - y1);
}

public Line(float x, float y) {
    this.x = x;
    this.y = y;
}

public float getLength() {
    return (float) Math.sqrt((x * x) + (y * y));
}

public Line unit() {
    return scale(1 / getLength());
}

public Line scale(float scale) {
    return new Line(x * scale, y * scale);

}
}

Теперь вы можете получить строку произвольной длины l, вызвав

Line result = new Line(x1, x2, y1, y2).unit().scale(l);

Это простая проблема, если вы понимаете что-то о векторах.

Учитывая две точки (x1, y1) и (x2, y2), вы можете вычислить вектор от точки 1 до 2:

v12 = (x2-x1) i + (y2-y2) j

где i и j - единичные векторы в направлениях x и y.

Вы можете рассчитать величину v, взяв квадратный корень из суммы квадратов компонентов:

v = sqrt ((x2-x2) ^ 2 + (y2-y1) ^ 2)

Единичный вектор от точки 1 до точки 2 равен v12, деленному на его величину.

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

Не нужно использовать триг, который может иметь некоторые неприятные крайние случаи. Просто используйте похожие треугольники:

public static int[] getLengthLimitedLine(int startX, int startY,
    int destX, int destY, int lengthLimit)
{
    int deltaX = destX - startX;
    int deltaY = destY - startY;
    int lengthSquared = deltaX * deltaX + deltaY * deltaY;
    // already short enough
    if(lengthSquared <= lengthLimit * lengthLimit)
        return new int[]{destX, destY};

    double length = Math.sqrt(lengthSquared);
    double newDeltaX = deltaX * lengthLimit / length;
    double newDeltaY = deltaY * lengthLimit / length;

    return new int[]{(int)(startX + newDeltaX), (int)(startY + newDeltaY)};
}

Просто используйте теорему Пифагора, вот так:

public static int[] getLengthLimitedLine(int start[], int dest[], int lengthLimit) {
    int xlen = dest[0] - start[0]
    int ylen = dest[1] - start[1]
    double length = Math.sqrt(xlen * xlen + ylen * ylen)

    if (length > lengthLimit) {
        return new int[] {start[0], start[1],
                start[0] + xlen / lengthLimit,
                start[1] + ylen / lengthLimit}
    } else {
        return new int[] {start[0], start[1], dest[0], dest[1];}
    }
}
Другие вопросы по тегам