ivan_ivanovich_ivanoff
ivan_ivanovich_ivanoff

Reputation: 19453

Need a function to limit a line (known by its coordinates) in its length

I need a function which takes a line (known by its coordinates) and return a line with same angle, but limited to certain length.

My code gives correct values only when the line is turned 'right'
(proven only empirically, sorry).

Am I missing 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 that returning arrays in Java is stupid, but it's just for the example.

Upvotes: 0

Views: 350

Answers (6)

RichieHindle
RichieHindle

Reputation: 281715

In Python because I don't have a Java compiler handy:

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-)

Upvotes: 2

phihag
phihag

Reputation: 288140

Just use the Pythagorean theorem, like so:

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];}
    }
}

Upvotes: 0

Adam Rosenfield
Adam Rosenfield

Reputation: 400502

No need to use trig, which can have some nasty edge cases. 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)};
}

Upvotes: 1

Jherico
Jherico

Reputation: 29240

Encapsulate 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 line of arbitrary length l by calling

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

Upvotes: 1

ghempton
ghempton

Reputation: 7947

It would be easier to just treat it as a vector. Normalize it by dividing my its magnitude then multiply by a factor of the desired length.

In your example, however, try Math.atan2.

Upvotes: 3

duffymo
duffymo

Reputation: 308948

It's an easy problem if you understand something about vectors.

Given two points (x1, y1) and (x2, y2), you can calculate the 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 magnitude of v by taking the square root of the sum of squares of the components:

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

The unit vector from point 1 to point 2 equals v12 divided by its magnitude.

Given that, you can calculate the point along the unit vector that's the desired distance away by multiply the unit vector times the length and adding that to point 1.

Upvotes: 1

Related Questions