2016-02-14 56 views
6

Widzę coś, co wydaje się być sprzecznym zachowaniem z logiki skrzyżowania WorldWind Sphere -Line Intersection. Tworzę sferę i linię, które przecinają się, ale potem skrzyżowanie zwraca null (kod skanowania dla komentarza: // *** To jest, gdzie robi się zwariowany).Błąd przecięcia linii WorldWind Sphere Line?

Oto co się dzieje wizualnie (linia jest szara to tam, ale trudno zobaczyć): Sphere-Line Intersecting

public class WWTest extends ApplicationTemplate { 

    public static class VisualizationFrame extends ApplicationTemplate.AppFrame { 

     public VisualizationFrame() { 
      super(new Dimension(1200, 1024)); 
      final Globe globe = getWwd().getModel().getGlobe(); 

      //Create a sphere at 0,0 on the surface of the Earth wtih a 60 NMi radius 
      final Vec4 sphereCenter = globe.computePointFromLocation(LatLon.ZERO); 
      final Sphere sphere = new Sphere(sphereCenter, 111120); 
      // Draw the sphere 
      final RenderableLayer sphereLayer = new RenderableLayer(); 
      sphereLayer.addRenderable(sphere); 

      final RenderableLayer pathLayer = new RenderableLayer(); 
      // Create a line at 10k feet (3048 meters) that starts outside the sphere at (2,-2) and proceeds into the sphere at (0.5, 0.5) 
      final Position lineStart = Position.fromDegrees(2, -2, 3048); 
      final Position lineEnds = Position.fromDegrees(0.5, 0.5, 3048); 
      final Path asPath = new Path(lineStart, lineEnds); 
      pathLayer.addRenderable(asPath); 

      // Now that we've visualized the line, let's do some intersection math 
      final Vec4 lineStartsAsVec = globe.computePointFromPosition(lineStart); 
      final Vec4 lineEndsAsVec = globe.computePointFromPosition(lineEnds); 
      final Line asLine = Line.fromSegment(lineStartsAsVec, lineEndsAsVec); 

      // *** This is where it gets whacky - true, but no intersection? 
      final boolean doesIntersect = sphere.intersects(asLine); 
      final Intersection[] intersection = sphere.intersect(asLine); 
      //outputs: Intersection found: null 
      System.out.println(doesIntersect ? "Intersection found: " + Arrays.toString(intersection) : "No intersection, Why Not!?!?"); 

      insertBeforeCompass(getWwd(), sphereLayer); 
      insertBeforeCompass(getWwd(), pathLayer); 
      getWwd().getView().setEyePosition(Position.fromDegrees(0, 0, 500_000)); 
      getLayerPanel().update(getWwd()); 
     } 
    } 

    public static void main(String[] args) { 
     ApplicationTemplate.start("World Wind Sphere-Line Intersection", VisualizationFrame.class); 

    } 
} 

A oto zależności oświadczyłem dostać WorldWind w moim projekcie maven (ja też nie starają wersja '2.0.0-986', ale to nie wydaje się pomóc):

<dependency> 
    <groupId>gov.nasa</groupId> 
    <artifactId>worldwind</artifactId> 
    <version>2.0.0</version> 
</dependency> 
<dependency> 
    <groupId>gov.nasa</groupId> 
    <artifactId>worldwindx</artifactId> 
    <version>2.0.0</version> 
</dependency> 
<dependency> 
    <groupId>org.jogamp.gluegen</groupId> 
    <artifactId>gluegen-rt-main</artifactId> 
    <version>2.2.4</version> 
</dependency> 
<dependency> 
    <groupId>org.jogamp.jogl</groupId> 
    <artifactId>jogl-all-main</artifactId> 
    <version>2.2.4</version> 
</dependency> 

aby być całkowicie dokładne, oto import kod:

import gov.nasa.worldwind.geom.Intersection; 
import gov.nasa.worldwind.geom.LatLon; 
import gov.nasa.worldwind.geom.Line; 
import gov.nasa.worldwind.geom.Position; 
import gov.nasa.worldwind.geom.Sphere; 
import gov.nasa.worldwind.geom.Vec4; 
import gov.nasa.worldwind.globes.Globe; 
import gov.nasa.worldwind.layers.RenderableLayer; 
import gov.nasa.worldwind.render.Path; 
import gov.nasa.worldwindx.examples.ApplicationTemplate; 
import static gov.nasa.worldwindx.examples.ApplicationTemplate.insertBeforeCompass; 
import java.awt.Dimension; 
import java.util.Arrays; 
+0

Skontaktowano się z głównym technicznym POCem ds. Świata i powiedziałem, że ten błąd zostanie umieszczony w kolejce do naprawy. Dzięki za pomoc Chris i Kyle :) –

Odpowiedz

2

Jeśli spojrzysz na implementację Sphere#intersect(), spodziewasz się linii w współrzędnych wyśrodkowanych na początku kuli (nie na Ziemi), co prawie na pewno jest błędem. Powinieneś być w stanie to zrobić:

final Vec4 pa = lineStartsAsVec.subtract3(sphereCenter); 
final Vec4 pb = lineEndsAsVec.subtract3(sphereCenter); 
final Line asLine2 = Line.fromSegment(pa, pb); 
final Intersection[] intersection = sphere.intersect(asLine2); 

Należy pamiętać, że punkty przecięcia zwracane są nadal we współrzędnych kartezjańskich skupionych w miejscu pochodzenia kuli, więc, aby przekształcić je z powrotem do World Wind kartezjańskiego trzeba zrobić:

final Vec4 intersectionPos = intersection[0].getIntersectionPoint().add3(sphereCenter); 

Wdrożenie uważa również, że linia jest nieskończenie długa, więc zwróci dwa punkty, a nie jeden.

Byłoby całkiem prosto do wdrożenia własnej wersji intersect(), która działa w normalnych współrzędnych i bierze pod uwagę długość linii, patrz here.

+0

@Chris_K jeśli jesteś dostępny/zainteresowany pomaganiem w napisaniu trudnych, wydajnych procedur obliczeniowych geometrii WGS-84, proszę zastrzel mi wiadomość, powinienem być łatwy do znalezienia na FB, Twitterze, LinkedIn, itp. –

2

Jeśli spojrzeć na Worldwind source code konkretnie intersects() i przecinają() metody klasy kuli i przejść przez nich z kodem jako wejście, można zobaczyć następujące:

Metoda:

public boolean intersects(Line line) 

zwraca wartość true, ponieważ odległość od środka kuli do linii jest mniejsza niż promień kuli, zgodnie z oczekiwaniami.

Dla metody:

public final Intersection[] intersect(Line line) 

okazuje się, że wyróżnik kwadratowego jest mniejszy niż zero (to znaczy nie ma żadnych realnych korzeni do równania kwadratowego - dwa różne złożone korzenie).

Numer referencyjny interfejsu API WorldWind to here.

konkretnych metod zaangażowane są:

/** 
* Tests for intersection with a <code>Line</code>. 
* 
* @param line the <code>Line</code> with which to test for intersection 
* 
* @return true if <code>line</code> intersects or makes a tangent with the surface of this <code>Sphere</code> 
* 
* @throws IllegalArgumentException if <code>line</code> is null 
*/ 
public boolean intersects(Line line) 
{ 
    if (line == null) 
    { 
     String msg = Logging.getMessage("nullValue.LineIsNull"); 
     Logging.logger().severe(msg); 
     throw new IllegalArgumentException(msg); 
    } 
    return line.distanceTo(this.center) <= this.radius; 
} 

oraz:

/** 
* Obtains the intersections of this sphere with a line. The returned array may be either null or of zero length if 
* no intersections are discovered. It does not contain null elements and will have a size of 2 at most. Tangential 
* intersections are marked as such. <code>line</code> is considered to have infinite length in both directions. 
* 
* @param line the <code>Line</code> with which to intersect this <code>Sphere</code> 
* 
* @return an array containing all the intersections of this <code>Sphere</code> and <code>line</code> 
* 
* @throws IllegalArgumentException if <code>line</code> is null 
*/ 
public final Intersection[] intersect(Line line) 
{ 
    if (line == null) 
    { 
     String message = Logging.getMessage("nullValue.LineIsNull"); 
     Logging.logger().severe(message); 
     throw new IllegalArgumentException(message); 
    } 

    double a = line.getDirection().getLengthSquared3(); 
    double b = 2 * line.selfDot(); 
    double c = line.getOrigin().getLengthSquared3() - this.radius * this.radius; 

    double discriminant = Sphere.discriminant(a, b, c); 
    if (discriminant < 0) 
     return null; 

    double discriminantRoot = Math.sqrt(discriminant); 
    if (discriminant == 0) 
    { 
     Vec4 p = line.getPointAt((-b - discriminantRoot)/(2 * a)); 
     return new Intersection[] {new Intersection(p, true)}; 
    } 
    else // (discriminant > 0) 
    { 
     Vec4 near = line.getPointAt((-b - discriminantRoot)/(2 * a)); 
     Vec4 far = line.getPointAt((-b + discriminantRoot)/(2 * a)); 
     return new Intersection[] {new Intersection(near, false), new Intersection(far, false)}; 
    } 
} 

który wykorzystuje:

/** 
* Calculates a discriminant. A discriminant is useful to determine the number of roots to a quadratic equation. If 
* the discriminant is less than zero, there are no roots. If it equals zero, there is one root. If it is greater 
* than zero, there are two roots. 
* 
* @param a the coefficient of the second order pronumeral 
* @param b the coefficient of the first order pronumeral 
* @param c the constant parameter in the quadratic equation 
* 
* @return the discriminant "b squared minus 4ac" 
*/ 
private static double discriminant(double a, double b, double c) 
{ 
    return b * b - 4 * a * c; 
} 

w tym przypadku kod nie powiedzie się:

if (discriminant < 0) 
Test

.

Wygląda na to, że byłem trochę powolny w odpowiadaniu na to pytanie, aw międzyczasie został wskazany przez Chrisa K, że jest to spowodowane metodą przecinającą się(), która zakłada, że ​​współrzędne linii mają być pochodzenie sfery, a nie Ziemi.

Jak powiedział Chris K., wygląda na to, że jest to błąd i prawdopodobnie powinien być zalogowany z opiekunami tego kodu źródłowego.