/** * Compute the positions of the arrow head of the graphic's legs. * * @param dc Current draw context * @param base Position of the arrow's starting point. * @param tip Position of the arrow head tip. * @param arrowLength Length of the arrowhead as a fraction of the total line length. * @param arrowAngle Angle of the arrow head. * @return Positions required to draw the arrow head. */ protected List<Position> computeArrowheadPositions( DrawContext dc, Position base, Position tip, double arrowLength, Angle arrowAngle) { // Build a triangle to represent the arrowhead. The triangle is built from two vectors, one // parallel to the // segment, and one perpendicular to it. Globe globe = dc.getGlobe(); Vec4 ptA = globe.computePointFromPosition(base); Vec4 ptB = globe.computePointFromPosition(tip); // Compute parallel component Vec4 parallel = ptA.subtract3(ptB); Vec4 surfaceNormal = globe.computeSurfaceNormalAtPoint(ptB); // Compute perpendicular component Vec4 perpendicular = surfaceNormal.cross3(parallel); double finalArrowLength = arrowLength * parallel.getLength3(); double arrowHalfWidth = finalArrowLength * arrowAngle.tanHalfAngle(); perpendicular = perpendicular.normalize3().multiply3(arrowHalfWidth); parallel = parallel.normalize3().multiply3(finalArrowLength); // Compute geometry of direction arrow Vec4 vertex1 = ptB.add3(parallel).add3(perpendicular); Vec4 vertex2 = ptB.add3(parallel).subtract3(perpendicular); return TacticalGraphicUtil.asPositionList(globe, vertex1, vertex2, ptB); }
/** * Causes the View attached to the specified WorldWindow to animate to the specified sector. The * View starts animating at its current location and stops when the sector fills the window. * * @param wwd the WorldWindow who's View animates. * @param sector the sector to go to. * @throws IllegalArgumentException if either the <code>wwd</code> or the <code>sector</code> are * <code>null</code>. */ public static void goTo(WorldWindow wwd, Sector sector) { if (wwd == null) { String message = Logging.getMessage("nullValue.WorldWindow"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (sector == null) { String message = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } // Create a bounding box for the specified sector in order to estimate its size in model // coordinates. Box extent = Sector.computeBoundingBox( wwd.getModel().getGlobe(), wwd.getSceneController().getVerticalExaggeration(), sector); // Estimate the distance between the center position and the eye position that is necessary to // cause the sector to // fill a viewport with the specified field of view. Note that we change the distance between // the center and eye // position here, and leave the field of view constant. Angle fov = wwd.getView().getFieldOfView(); double zoom = extent.getRadius() / fov.cosHalfAngle() / fov.tanHalfAngle(); // Configure OrbitView to look at the center of the sector from our estimated distance. This // causes OrbitView to // animate to the specified position over several seconds. To affect this change immediately use // the following: // ((OrbitView) wwd.getView()).setCenterPosition(new Position(sector.getCentroid(), 0d)); // ((OrbitView) wwd.getView()).setZoom(zoom); wwd.getView().goTo(new Position(sector.getCentroid(), 0d), zoom); }
/** * Creates a <code>Frustum</code> from a horizontal field-of-view, viewport aspect ratio and * distance to near and far depth clipping planes. The near plane must be closer than the far * plane, and both near and far values must be positive. * * @param horizontalFieldOfView horizontal field-of-view angle in the range (0, 180) * @param viewportWidth the width of the viewport in screen pixels * @param viewportHeight the height of the viewport in screen pixels * @param near distance to the near depth clipping plane * @param far distance to far depth clipping plane * @return Frustum configured from the specified perspective parameters. * @throws IllegalArgumentException if fov is not in the range (0, 180), if either near or far are * negative, or near is greater than or equal to far */ public Frustum setPerspective( Angle horizontalFieldOfView, double viewportWidth, double viewportHeight, double near, double far) { if (horizontalFieldOfView == null) { String msg = Logging.getMessage("nullValue.FieldOfViewIsNull"); Logging.error(msg); throw new IllegalArgumentException(msg); } if (horizontalFieldOfView.degrees <= 0 || horizontalFieldOfView.degrees > 180) { String msg = Logging.getMessage("generic.FieldOfViewIsInvalid", horizontalFieldOfView); Logging.error(msg); throw new IllegalArgumentException(msg); } if (viewportWidth < 0) { String msg = Logging.getMessage("generic.WidthIsInvalid", viewportWidth); Logging.error(msg); throw new IllegalArgumentException(msg); } if (viewportHeight < 0) { String msg = Logging.getMessage("generic.HeightIsInvalid", viewportHeight); Logging.error(msg); throw new IllegalArgumentException(msg); } if (near <= 0 || near > far) { String msg = Logging.getMessage("generic.ClipDistancesAreInvalid", near, far); Logging.error(msg); throw new IllegalArgumentException(msg); } if (viewportWidth == 0) viewportWidth = 1; if (viewportHeight == 0) viewportHeight = 1; if (near == far) far = near + 1; double focalLength = 1d / horizontalFieldOfView.tanHalfAngle(); double aspect = viewportHeight / viewportWidth; double lrLen = Math.sqrt(focalLength * focalLength + 1); double btLen = Math.sqrt(focalLength * focalLength + aspect * aspect); this.left.set(focalLength / lrLen, 0, -1d / lrLen, 0); this.right.set(-focalLength / lrLen, 0, -1d / lrLen, 0); this.bottom.set(0, focalLength / btLen, -aspect / btLen, 0); this.top.set(0, -focalLength / btLen, -aspect / btLen, 0); this.near.set(0, 0, -1, -near); this.far.set(0, 0, 1, far); return this; }