Нужна функция для ограничения линии (известной по ее координатам) по ее длине
Мне нужна функция, которая берет линию (известную по ее координатам) и возвращает линию с тем же углом, но ограниченным определенной длиной.
Мой код дает правильные значения только тогда, когда линия повернута "вправо"
(доказано только опытным путем, извините).
Я что-то пропустил?
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];}
}
}