A function is required to limit the line (known by its coordinates) along the length
I need a function that takes a string (known by its coordinates) and return a string with the same angle, but limited to a specific length .
My code gives the correct values only when the line is turned "right"
(proven empirically, sorry).
Did I miss something?
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
};
}
BTW: I know returning arrays in Java are silly, but this is just for example.
a source to share
In Python, because I don't have a Java compiler:
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-)
a source to share
This is a simple problem if you understand anything about vectors.
Given two points (x1, y1) and (x2, y2), you can compute a vector from point 1 to 2:
v12 = (x2-x1) i + (y2-y2) j
where i and j are unit vectors in the x and y directions.
You can calculate the value of v by taking the square root of the sum of the squares of the components:
v = sqrt ((x2-x2) ^ 2 + (y2-y1) ^ 2)
The unit vector from point 1 to point 2 is v12 divided by its value.
With this in mind, you can calculate a point along the unit vector to multiply the unit vector by the length at the desired distance and add that to point 1.
a source to share
Encapsulate the line in a class, add a unit method and a scale method.
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);
}
}
Now you can get a string of arbitrary length l by calling
Line result = new Line(x1, x2, y1, y2).unit().scale(l);
a source to share
It is not necessary to use a trigger which may have some nasty edges. Just use similar triangles:
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)};
}
a source to share
Just use the Pythagorean theorem , for example:
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];}
}
}
a source to share