/******************************************************************************* * Copyright 2012 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.worldwind.common.layers.point.annotation; import gov.nasa.worldwind.geom.Position; import gov.nasa.worldwind.geom.Vec4; import gov.nasa.worldwind.render.AnnotationAttributes; import gov.nasa.worldwind.render.DrawContext; import gov.nasa.worldwind.render.GlobeAnnotation; import gov.nasa.worldwind.util.Logging; import gov.nasa.worldwind.util.OGLStackHandler; import gov.nasa.worldwind.util.WWMath; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import javax.media.opengl.GL2; /** * An enhanced annotation that supports: * <ul> * <li>Fade in/out based on eye elevation</li> * <li>Fade in/out based on eye distance</li> * </ul> * * @author James Navin (james.navin@ga.gov.au) */ public class EnhancedAnnotation extends GlobeAnnotation { public EnhancedAnnotation(String text, Position position, AnnotationAttributes defaults) { super(text, position, defaults); } public EnhancedAnnotation(String text, Position position, Font font, Color textColor) { super(text, position, font, textColor); } public EnhancedAnnotation(String text, Position position, Font font) { super(text, position, font); } public EnhancedAnnotation(String text, Position position) { super(text, position); } @Override public void setAttributes(AnnotationAttributes attributes) { if (attributes instanceof EnhancedAnnotationAttributes) { super.setAttributes(attributes); } else { super.setAttributes(new EnhancedAnnotationAttributes(attributes)); } } /** * Set the enhanced attributes associated with this annotation */ public void setAttributes(EnhancedAnnotationAttributes attributes) { super.setAttributes(attributes); } /** * @return The enhanced attributes associated with this annotation */ public EnhancedAnnotationAttributes getEnhancedAttributes() { AnnotationAttributes result = getAttributes(); if (result instanceof EnhancedAnnotationAttributes) { return (EnhancedAnnotationAttributes) result; } setAttributes(new EnhancedAnnotationAttributes(result)); return (EnhancedAnnotationAttributes) getAttributes(); } @Override protected void doRenderNow(DrawContext dc) { if (dc.isPickingMode() && this.getPickSupport() == null) { return; } Vec4 point = this.getAnnotationDrawPoint(dc); if (point == null || pointIsBehindView(dc, point)) { return; } Vec4 screenPoint = dc.getView().project(point); if (screenPoint == null) { return; } Dimension size = this.getPreferredSize(dc); Position pos = dc.getGlobe().computePositionFromPoint(point); // Determine scale and opacity factors based on distance from eye vs the distance to the look at point. double lookAtDistance = this.computeLookAtDistance(dc); double eyeDistance = dc.getView().getEyePoint().distanceTo3(point); double distanceFactor = Math.sqrt(lookAtDistance / eyeDistance); double scale = WWMath.clamp(distanceFactor, this.attributes.getDistanceMinScale(), this.attributes.getDistanceMaxScale()); double opacity = computeOpacity(eyeDistance, distanceFactor); if (opacity < 0.1) { getAttributes().setHighlighted(false); } // Don't draw picking if annotation is not visible if (opacity <= 0.0 && dc.isPickingMode()) { return; } this.setDepthFunc(dc, screenPoint); this.drawTopLevelAnnotation(dc, screenPoint.x, screenPoint.y, size.width, size.height, scale, opacity, pos); } /** * Compute the opacity of the annotation based on the distance between the * eye and the marker point * <p/> * Applies a linear fade based on the set <code>fadeDistance</code> */ private double computeOpacity(double eyeDistance, double distanceFactor) { double opacity = WWMath.clamp(distanceFactor, this.attributes.getDistanceMinOpacity(), 1); if (isLessThanMinDistance(eyeDistance) || isGreaterThanMaxDistance(eyeDistance)) { return 0.0; } // Use a linear fadeout: // y = m*x + d where m = 1/fadeDistance, x = eyeDistance and d = -m * minDistance // y = (1/fadeDistance) * eyeDistance - (1/fadeDistance) * minDistance // y = (1/fadeDistance)*(eyeDistance - minDistance) double fadeRate = 1 / getEnhancedAttributes().getFadeDistance(); Double minEyeDistance = getEnhancedAttributes().getMinEyeDistance(); if (minEyeDistance != null) { opacity = WWMath.clamp(fadeRate * (eyeDistance - minEyeDistance), 0d, opacity); } Double maxEyeDistance = getEnhancedAttributes().getMaxEyeDistance(); if (maxEyeDistance != null) { opacity = WWMath.clamp(fadeRate * (maxEyeDistance - eyeDistance), 0d, opacity); } return opacity; } private boolean isLessThanMinDistance(double eyeDistance) { return getEnhancedAttributes().getMinEyeDistance() != null && eyeDistance < getEnhancedAttributes().getMinEyeDistance(); } private boolean isGreaterThanMaxDistance(double eyeDistance) { return getEnhancedAttributes().getMaxEyeDistance() != null && eyeDistance > getEnhancedAttributes().getMaxEyeDistance(); } private boolean pointIsBehindView(DrawContext dc, Vec4 point) { return dc.getView().getFrustumInModelCoordinates().getNear().distanceTo(point) < 0; } protected void drawTopLevelAnnotation(DrawContext dc, double x, double y, int width, int height, double scale, double opacity, Position pickPosition) { if (dc == null) { String message = Logging.getMessage("nullValue.DrawContextIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } OGLStackHandler stackHandler = new OGLStackHandler(); this.beginDraw(dc, stackHandler); try { this.applyScreenTransform(dc, x, y, width, height, scale); this.draw(dc, width, height, opacity, pickPosition); } finally { this.endDraw(dc, stackHandler); } } protected void applyScreenTransform(DrawContext dc, double x, double y, double width, double height, double scale) { double finalScale = scale * this.computeScale(dc); java.awt.Point offset = this.getAttributes().getDrawOffset(); GL2 gl = dc.getGL().getGL2(); gl.glTranslated(x, y, 0); gl.glScaled(finalScale, finalScale, 1); gl.glTranslated(offset.x, offset.y, 0); gl.glTranslated(-width / 2, 0, 0); } }