2009-05-19 8 views
0

je besoin d'une fonction qui prend une ligne (connue par ses coordonnées) et retourner une ligne avec même angle, mais limitée à certaine longueur.Besoin d'une fonction pour limiter une ligne (connue par ses coordonnées) dans sa longueur

Mon code donne des valeurs correctes ne lorsque la ligne est activée « droite »
(seulement prouvé de façon empirique, désolé).

Ai-je raté quelque chose?

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: Je sais que les tableaux de retour en Java est stupide, mais il est juste pour l'exemple.

+0

Votre chèque de NaN renvoie toujours faux que rien n'est jamais égal à NaN. Le retour de 0 pour erreur est également mauvais car 0 est un angle valide. –

+0

Oh, vous avez raison!C'est à partir d'un code plus ancien. –

Répondre

2

En Python parce que je n'ai pas un compilateur Java pratique:

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

+1 - Je commence à apprécier grandement la brièveté et l'élégance de Python. Agréable. Pas besoin de "if" vérifier la longueur - c'est valable pour toutes les situations sauf la longueur == 0. C'est la vérification que vous devez faire. Pas besoin d'abs(), non plus, car les carrés sont toujours positifs. – duffymo

+0

@duffymo: Bon point sur abs() - Je l'ai supprimé. Mais je pense que le 'si' est nécessaire, sinon ça va * développer * la ligne quand elle est plus courte que la limite. – RichieHindle

+0

Oui, mais ce n'est pas nécessairement un résultat incorrect. Il ne répond que partiellement aux exigences strictes de l'affiche. Il est également valable d'étendre la ligne dans un autre cas d'utilisation. – duffymo

0

Il suffit d'utiliser le Pythagorean theorem, comme ceci:

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

Il est un problème facile si vous comprenez quelque chose sur des vecteurs.

Compte tenu de deux points (x1, y1) et (x2, y2), on peut calculer le vecteur de point 1 à 2:

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

où i et j sont des vecteurs unitaires dans les directions x et y.

On peut calculer l'amplitude de v en prenant la racine carrée de la somme des carrés des composantes:

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

Le vecteur unitaire du point 1 au point 2 est égal à v12 divisé par son amplitude.

Étant donné que, vous pouvez calculer le point le long du vecteur d'unité qui est la distance souhaitée loin en multiplier les temps de vecteur unité la longueur et en ajoutant que le point 1.

3

Il serait plus facile de traiter simplement comme vecteur. Normaliser en divisant ma grandeur puis multiplier par un facteur de la longueur désirée. Dans votre exemple, essayez cependant Math.atan2. Dans votre exemple, essayez Math.atan2.

+0

Exactement, atan2 est la méthode à utiliser ici. Il a plus d'informations à utiliser, car il obtient x et y comme arguments séparés, et peut renvoyer correctement les angles de -π à + π. atan a seulement assez d'informations pour couvrir une gamme de -π/2 à π/2. – erickson

1

Encapsuler une ligne dans une classe, ajouter une méthode d'unité et une méthode d'échelle.

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

} 
} 

Maintenant vous pouvez obtenir une ligne de longueur arbitraire en appelant l

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

Pas besoin d'utiliser triglycéride, qui peut avoir des cas de pointe désagréables. Il suffit d'utiliser des triangles similaires:

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