All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.gov.nasa.worldwind.symbology.milstd2525.graphics.TriangleWavePositionIterator Maven / Gradle / Ivy

Go to download

World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.

There is a newer version: 2.0.0-986
Show newest version
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

package gov.nasa.worldwind.symbology.milstd2525.graphics;

import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.util.Logging;

import java.util.*;

/**
 * Iterator that computes the positions required to draw a triangle wave along a line specified by control positions.
 * The generated wave looks like this:
 * 

*

 *           /\             /\         <--- Amplitude
 *          /  \           /  \
 * ________/    \_________/    \_____
 * ^            ^
 * | Wave length|
 * 
* * @author pabercrombie * @version $Id: TriangleWavePositionIterator.java 423 2012-03-02 21:43:57Z pabercrombie $ */ public class TriangleWavePositionIterator implements Iterator { /** Initial state. */ protected static final int STATE_FIRST = 0; /** Drawing connecting line between waves. */ protected static final int STATE_LINE = 1; /** About to draw wave. */ protected static final int STATE_WAVE_START = 2; /** At peak of wave. */ protected static final int STATE_TOOTH_PEAK = 3; /** Current state of the state machine. */ protected int state = STATE_FIRST; /** Control positions. */ protected Iterator positions; /** Globe used to compute geographic positions. */ protected Globe globe; /** Amplitude of the wave, in meters. */ protected double amplitude; /** Wavelength, as a geographic angle. */ protected Angle halfWaveLength; /** Current position. */ protected Position thisPosition; /** Position of the next control point. */ protected Position nextControlPosition; /** First position along the line. */ protected Position firstPosition; /** End position for the current wave. */ protected Position waveEndPosition; /** Distance (in meters) to the next wave start or end. */ protected double thisStep; /** * Create a new iterator to compute the positions of a triangle wave. * * @param positions Control positions for the triangle wave line. * @param waveLength Distance (in meters) between waves. * @param amplitude Amplitude (in meters) of the wave. This is the distance from the base line to the tip of each * triangular wave. * @param globe Globe used to compute geographic positions. */ public TriangleWavePositionIterator(Iterable positions, double waveLength, double amplitude, Globe globe) { if (positions == null) { String message = Logging.getMessage("nullValue.PositionsListIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (globe == null) { String message = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (waveLength <= 0 || amplitude <= 0) { String message = Logging.getMessage("generic.LengthIsInvalid"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } this.globe = globe; this.amplitude = amplitude; this.halfWaveLength = Angle.fromRadians(waveLength / (2.0 * this.globe.getRadius())); this.thisStep = this.halfWaveLength.degrees; this.positions = positions.iterator(); this.thisPosition = this.positions.next(); this.firstPosition = this.thisPosition; this.nextControlPosition = this.thisPosition; } /** {@inheritDoc} */ public boolean hasNext() { return this.nextControlPosition != null; } /** {@inheritDoc} */ public Position next() { Position ret; // The iterator is implemented as a state machine. For each call to next() we return the appropriate // position, and transition to the next state. // // /\ /\ // / \ / \ // ________/ \_________/ \_____ // ^ ^ ^ ^ // | | | | // Line | | | // | | | // Wave start // | | // Wave peak // | // Line switch (this.state) { // First call to the iterator. Just return the starting position. case STATE_FIRST: ret = this.thisPosition; this.state = STATE_LINE; break; // Draw a straight segment between waves. Compute the next point and return. case STATE_LINE: ret = this.computeNext(); break; // Draw a wave. Compute the end position of the wave, then compute and return the peak position. case STATE_WAVE_START: Position prevPos = this.thisPosition; // Keep track of where the wave starts this.waveEndPosition = this.computeNext(); Vec4 thisPoint = this.globe.computePointFromLocation(prevPos); Vec4 pNext = this.globe.computePointFromLocation(this.waveEndPosition); LatLon ll = LatLon.interpolateGreatCircle(0.5, prevPos, this.waveEndPosition); Vec4 midPoint = this.globe.computePointFromLocation(ll); Vec4 vAB = pNext.subtract3(thisPoint); Vec4 normal = this.globe.computeSurfaceNormalAtLocation(ll.latitude, ll.longitude); Vec4 perpendicular = vAB.cross3(normal); perpendicular = perpendicular.normalize3().multiply3(this.amplitude); // Compute the two points that form the tooth. Vec4 toothPoint = midPoint.add3(perpendicular); ret = this.globe.computePositionFromPoint(toothPoint); break; // Return previously computed wave end position, and transition to Line state. case STATE_TOOTH_PEAK: ret = this.waveEndPosition; this.state = STATE_LINE; break; default: throw new IllegalStateException(); } return ret; } /** * Compute the next position along the line, and transition the state machine to the next state (if appropriate). *

* If the current state is STATE_LINE, this method returns either the next control point (if it is less than * waveLength meters from the current position, or a position waveLength meters along the control line. The state * machine will transition to STATE_WAVE_START only if the returned position is a full wavelength from the current * position. *

* If the current state is STATE_WAVE_START, this method returns a position waveLength meters from the current * position along the control line, and transitions to state STATE_WAVE_PEAK. * * @return next position along the line. */ protected Position computeNext() { Angle distToNext = LatLon.greatCircleDistance(this.thisPosition, this.nextControlPosition); double diff = distToNext.degrees - this.thisStep; while (diff < 0) { if (this.positions.hasNext()) { this.thisPosition = this.nextControlPosition; this.nextControlPosition = this.positions.next(); // If we're drawing a line segment between waves then return the current control point and do not // transition states. We retain all of the control points between waves in order to keep the line // as close to the application's specification as possible. if (this.state == STATE_LINE) { this.thisStep -= distToNext.degrees; return this.thisPosition; } } // Handle a polygon that is not closed. else if (this.firstPosition != null && !this.firstPosition.equals(this.nextControlPosition)) { this.thisPosition = this.nextControlPosition; this.nextControlPosition = this.firstPosition; this.firstPosition = null; if (this.state == STATE_LINE) { this.thisStep -= distToNext.degrees; return this.thisPosition; } } else { Position next = this.nextControlPosition; this.nextControlPosition = null; return next; } // The tooth wraps around a corner. Adjust step size. this.thisStep -= distToNext.degrees; distToNext = LatLon.greatCircleDistance(this.thisPosition, this.nextControlPosition); diff = distToNext.degrees - thisStep; } Angle azimuth = LatLon.greatCircleAzimuth(this.thisPosition, this.nextControlPosition); LatLon ll = LatLon.greatCircleEndPosition(this.thisPosition, azimuth, Angle.fromDegrees(this.thisStep)); // Transition to the next state. If we were drawing a line we are now drawing a wave. If we were starting a // wave, we're now at the wave peak. switch (this.state) { case STATE_LINE: this.state = STATE_WAVE_START; break; case STATE_WAVE_START: this.state = STATE_TOOTH_PEAK; break; default: throw new IllegalStateException(); } this.thisStep = this.halfWaveLength.degrees; this.thisPosition = new Position(ll, 0); return this.thisPosition; } /** Not supported. */ public void remove() { throw new UnsupportedOperationException(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy