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

gov.nasa.worldwind.render.IconRenderer Maven / Gradle / Ivy

The 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.render;

import com.jogamp.opengl.util.texture.TextureCoords;
import gov.nasa.worldwind.Locatable;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.exception.WWRuntimeException;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.layers.Layer;
import gov.nasa.worldwind.pick.PickSupport;
import gov.nasa.worldwind.terrain.SectorGeometryList;
import gov.nasa.worldwind.util.*;

import com.jogamp.opengl.*;
import java.awt.*;
import java.util.Iterator;
import java.util.logging.Level;

/**
 * IconRenderer processes collections of {@link gov.nasa.worldwind.render.WWIcon} instances for picking and rendering.
 * IconRenderer applies batch processing techniques to improve the runtime performance of picking and rendering large
 * collections of icons.
 * 

* During the draw pass, IconRenderer records feedback information for each WWIcon which has the property key {@link * gov.nasa.worldwind.avlist.AVKey#FEEDBACK_ENABLED} set to true. IconRenderer does not record any feedback * information during the pick pass. When feedback is enabled, IconRenderer puts properties which describe how each * WWIcon has been processed in key-value pairs attached to the WWIcon. Any of these properties may be null, indicating * that processing of the WWIcon was terminated before this information became available. The feedback properties for * WWIcon are as follows:

KeyDescription
{@link * gov.nasa.worldwind.avlist.AVKey#FEEDBACK_REFERENCE_POINT}The icon's reference point in model * coordinates.
{@link gov.nasa.worldwind.avlist.AVKey#FEEDBACK_SCREEN_BOUNDS}The icon's * bounding rectangle in screen coordinates.
* * @author tag * @version $Id: IconRenderer.java 2260 2014-08-23 00:14:06Z tgaskins $ */ public class IconRenderer { protected Pedestal pedestal; protected boolean horizonClippingEnabled = false; protected boolean viewClippingEnabled = true; protected boolean pickFrustumClippingEnabled = true; protected boolean alwaysUseAbsoluteElevation = false; protected OGLStackHandler oglStackHandler = new OGLStackHandler(); protected boolean allowBatchPicking = true; protected PickSupport pickSupport = new PickSupport(); public IconRenderer() { } public Pedestal getPedestal() { return pedestal; } public void setPedestal(Pedestal pedestal) { this.pedestal = pedestal; } /** * Indicates whether horizon clipping is performed. * * @return true if horizon clipping is performed, otherwise false. * * @see #setHorizonClippingEnabled(boolean) */ public boolean isHorizonClippingEnabled() { return horizonClippingEnabled; } /** * Indicates whether to render icons beyond the horizon. If view culling is enabled, the icon is also tested for * view volume inclusion. The default is false, horizon clipping is not performed. * * @param horizonClippingEnabled true if horizon clipping should be performed, otherwise * false. * * @see #setViewClippingEnabled(boolean) */ public void setHorizonClippingEnabled(boolean horizonClippingEnabled) { this.horizonClippingEnabled = horizonClippingEnabled; } /** * Indicates whether view volume clipping is performed. * * @return true if view volume clipping is performed, otherwise false. * * @see #setViewClippingEnabled(boolean) */ public boolean isViewClippingEnabled() { return viewClippingEnabled; } /** * Indicates whether to render icons outside the view volume. This is primarily to control icon visibility beyond * the far view clipping plane. Some important use cases demand that clipping not be performed. If horizon clipping * is enabled, the icon is also tested for horizon clipping. The default is true, view volume clipping * is not performed. * * @param viewClippingEnabled true if view clipping should be performed, otherwise false. * * @see #setHorizonClippingEnabled(boolean) */ public void setViewClippingEnabled(boolean viewClippingEnabled) { this.viewClippingEnabled = viewClippingEnabled; } /** * Indicates whether picking volume clipping is performed. * * @return true if picking volume clipping is performed, otherwise false. * * @see #setPickFrustumClippingEnabled(boolean) */ public boolean isPickFrustumClippingEnabled() { return pickFrustumClippingEnabled; } /** * Indicates whether to render icons outside the picking volume when in pick mode. This increases performance by * only drawing the icons within the picking volume when picking is enabled. Some important use cases demand that * clipping not be performed. The default is false, picking volume clipping is not performed. * * @param pickFrustumClippingEnabled true if picking clipping should be performed, otherwise * false. */ public void setPickFrustumClippingEnabled(boolean pickFrustumClippingEnabled) { this.pickFrustumClippingEnabled = pickFrustumClippingEnabled; } protected static boolean isIconValid(WWIcon icon, boolean checkPosition) { if (icon == null || icon.getImageTexture() == null) return false; //noinspection RedundantIfStatement if (checkPosition && icon.getPosition() == null) return false; return true; } /** * Indicates whether an icon's elevation is treated as an offset from the terrain or an absolute elevation above sea * level. * * @return true if icon elevations are treated as absolute, false if they're treated as * offsets from the terrain. */ public boolean isAlwaysUseAbsoluteElevation() { return alwaysUseAbsoluteElevation; } /** * Normally, an icon's elevation is treated as an offset from the terrain when it is less than the globe's maximum * elevation. Setting #setAlwaysUseAbsoluteElevation to true causes the elevation to be treated as an * absolute elevation above sea level. * * @param alwaysUseAbsoluteElevation true to treat icon elevations as absolute, false to * treat them as offsets from the terrain. */ public void setAlwaysUseAbsoluteElevation(boolean alwaysUseAbsoluteElevation) { this.alwaysUseAbsoluteElevation = alwaysUseAbsoluteElevation; } /** * Indicates whether icons are picked as a batch and therefore a {@link gov.nasa.worldwind.event.SelectEvent} will * contain only one icon from a given layer. Batch picking is much faster than individual picking, so this attribute * should be used judiciously. * * @return true if batch picking is allowed, otherwise false. * * @see #setAllowBatchPicking(boolean) */ public boolean isAllowBatchPicking() { return this.allowBatchPicking; } /** * Specifies whether batch picking is allowed. If so, a {@link gov.nasa.worldwind.event.SelectEvent} from a layer * will contain only one icon even if several overlapping icons are at the pick point. Batch picking is much faster * than individual picking so the default value is true. * * @param allowBatchPicking true if batch picking is allowed, otherwise false. */ public void setAllowBatchPicking(boolean allowBatchPicking) { this.allowBatchPicking = allowBatchPicking; } @SuppressWarnings({"UnusedDeclaration"}) public void pick(DrawContext dc, Iterable icons, java.awt.Point pickPoint, Layer layer) { this.drawMany(dc, icons, layer); } public void render(DrawContext dc, Iterable icons) { this.drawMany(dc, icons, null); } protected void drawMany(DrawContext dc, Iterable icons, Layer layer) { if (dc == null) { String msg = Logging.getMessage("nullValue.DrawContextIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (dc.getVisibleSector() == null) return; SectorGeometryList geos = dc.getSurfaceGeometry(); //noinspection RedundantIfStatement if (geos == null) return; if (icons == null) { String msg = Logging.getMessage("nullValue.IconIterator"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } Iterator iterator = icons.iterator(); if (!iterator.hasNext()) return; double horizon = dc.getView().getHorizonDistance(); while (iterator.hasNext()) { WWIcon icon = iterator.next(); if (!isIconValid(icon, true)) { // Record feedback data for this WWIcon if feedback is enabled. if (icon != null) this.recordFeedback(dc, icon, null, null); continue; } if (!icon.isVisible()) { // Record feedback data for this WWIcon if feedback is enabled. this.recordFeedback(dc, icon, null, null); continue; } // Determine Cartesian position from the surface geometry if the icon is near the surface, // otherwise draw it from the globe. Position pos = icon.getPosition(); Vec4 iconPoint = null; if (dc.is2DGlobe()) { iconPoint = dc.getGlobe().computePointFromLocation(pos); } else if (pos.getElevation() < dc.getGlobe().getMaxElevation() && !this.isAlwaysUseAbsoluteElevation()) { iconPoint = dc.getSurfaceGeometry().getSurfacePoint(icon.getPosition()); } if (iconPoint == null) { Angle lat = pos.getLatitude(); Angle lon = pos.getLongitude(); double elevation = pos.getElevation(); if (!this.isAlwaysUseAbsoluteElevation()) elevation += dc.getGlobe().getElevation(lat, lon); iconPoint = dc.getGlobe().computePointFromPosition(lat, lon, elevation); } double eyeDistance = icon.isAlwaysOnTop() ? 0 : dc.getView().getEyePoint().distanceTo3(iconPoint); if (this.isHorizonClippingEnabled() && !dc.is2DGlobe() && eyeDistance > horizon) { // Record feedback data for this WWIcon if feedback is enabled. this.recordFeedback(dc, icon, iconPoint, null); continue; // don't render horizon-clipped icons } // If enabled, eliminate icons outside the view volume. Primarily used to control icon visibility beyond // the view volume's far clipping plane. if (this.isViewClippingEnabled() && !dc.getView().getFrustumInModelCoordinates().contains(iconPoint)) { // Record feedback data for this WWIcon if feedback is enabled. this.recordFeedback(dc, icon, iconPoint, null); continue; // don't render frustum-clipped icons } // The icons aren't drawn here, but added to the ordered queue to be drawn back-to-front. dc.addOrderedRenderable(new OrderedIcon(icon, iconPoint, layer, eyeDistance, horizon)); if (icon.isShowToolTip()) this.addToolTip(dc, icon, iconPoint); } } protected void addToolTip(DrawContext dc, WWIcon icon, Vec4 iconPoint) { if (icon.getToolTipFont() == null && icon.getToolTipText() == null) return; Vec4 screenPoint = dc.getView().project(iconPoint); if (screenPoint == null) return; if (icon.getToolTipOffset() != null) screenPoint = screenPoint.add3(icon.getToolTipOffset()); OrderedText tip = new OrderedText(icon.getToolTipText(), icon.getToolTipFont(), screenPoint, icon.getToolTipTextColor(), 0d); dc.addOrderedRenderable(tip); } protected class OrderedText implements OrderedRenderable { protected Font font; protected String text; protected Vec4 point; protected double eyeDistance; protected java.awt.Point pickPoint; protected Layer layer; protected java.awt.Color color; public OrderedText(String text, Font font, Vec4 point, java.awt.Color color, double eyeDistance) { this.text = text; this.font = font; this.point = point; this.eyeDistance = eyeDistance; this.color = color; } public OrderedText(String text, Font font, Vec4 point, java.awt.Point pickPoint, Layer layer, double eyeDistance) { this.text = text; this.font = font; this.point = point; this.eyeDistance = eyeDistance; this.pickPoint = pickPoint; this.layer = layer; } public double getDistanceFromEye() { return this.eyeDistance; } public void render(DrawContext dc) { ToolTipRenderer toolTipRenderer = this.getToolTipRenderer(dc); toolTipRenderer.render(dc, this.text, (int) this.point.x, (int) this.point.y); } public void pick(DrawContext dc, java.awt.Point pickPoint) { } @SuppressWarnings({"UnusedDeclaration"}) protected ToolTipRenderer getToolTipRenderer(DrawContext dc) { ToolTipRenderer tr = (this.font != null) ? new ToolTipRenderer(this.font) : new ToolTipRenderer(); if (this.color != null) { tr.setTextColor(this.color); tr.setOutlineColor(this.color); tr.setInteriorColor(ToolTipRenderer.getContrastingColor(this.color)); } else { tr.setUseSystemLookAndFeel(true); } return tr; } } protected class OrderedIcon implements OrderedRenderable, Locatable { protected WWIcon icon; protected Vec4 point; protected double eyeDistance; protected double horizonDistance; protected Layer layer; public OrderedIcon(WWIcon icon, Vec4 point, Layer layer, double eyeDistance, double horizonDistance) { this.icon = icon; this.point = point; this.eyeDistance = eyeDistance; this.horizonDistance = horizonDistance; this.layer = layer; } public double getDistanceFromEye() { return this.eyeDistance; } public Position getPosition() { return this.icon.getPosition(); } public IconRenderer getRenderer() { return IconRenderer.this; } public Vec4 getPoint() { return this.point; } public WWIcon getIcon() { return this.icon; } public double getHorizonDistance() { return horizonDistance; } public Layer getLayer() { return layer; } public void render(DrawContext dc) { IconRenderer.this.beginDrawIcons(dc); try { IconRenderer.this.drawIconsInBatch(dc, this); } catch (WWRuntimeException e) { Logging.logger().log(Level.SEVERE, "generic.ExceptionWhileRenderingIcon", e); } catch (Exception e) { Logging.logger().log(Level.SEVERE, "generic.ExceptionWhileRenderingIcon", e); } finally { IconRenderer.this.endDrawIcons(dc); } } public void pick(DrawContext dc, java.awt.Point pickPoint) { IconRenderer.this.pickSupport.clearPickList(); IconRenderer.this.beginDrawIcons(dc); try { if (IconRenderer.this.isAllowBatchPicking()) IconRenderer.this.pickIconsInBatch(dc, this); else IconRenderer.this.drawIcon(dc, this); } catch (WWRuntimeException e) { Logging.logger().log(Level.SEVERE, "generic.ExceptionWhileRenderingIcon", e); } catch (Exception e) { Logging.logger().log(Level.SEVERE, "generic.ExceptionWhilePickingIcon", e); } finally { IconRenderer.this.endDrawIcons(dc); IconRenderer.this.pickSupport.resolvePick(dc, pickPoint, layer); IconRenderer.this.pickSupport.clearPickList(); // to ensure entries can be garbage collected } } } protected void beginDrawIcons(DrawContext dc) { GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility. this.oglStackHandler.clear(); int attributeMask = GL2.GL_DEPTH_BUFFER_BIT // for depth test, depth mask and depth func | GL2.GL_TRANSFORM_BIT // for modelview and perspective | GL2.GL_VIEWPORT_BIT // for depth range | GL2.GL_CURRENT_BIT // for current color | GL2.GL_COLOR_BUFFER_BIT // for alpha test func and ref, and blend | GL2.GL_DEPTH_BUFFER_BIT // for depth func | GL2.GL_ENABLE_BIT; // for enable/disable changes this.oglStackHandler.pushAttrib(gl, attributeMask); // Apply the depth buffer but don't change it. if ((!dc.isDeepPickingEnabled())) gl.glEnable(GL.GL_DEPTH_TEST); gl.glDepthMask(false); // Suppress any fully transparent image pixels gl.glEnable(GL2.GL_ALPHA_TEST); gl.glAlphaFunc(GL2.GL_GREATER, 0.001f); // Load a parallel projection with dimensions (viewportWidth, viewportHeight) this.oglStackHandler.pushProjectionIdentity(gl); gl.glOrtho(0d, dc.getView().getViewport().width, 0d, dc.getView().getViewport().height, -1d, 1d); this.oglStackHandler.pushModelview(gl); this.oglStackHandler.pushTexture(gl); if (dc.isPickingMode()) { this.pickSupport.beginPicking(dc); // Set up to replace the non-transparent texture colors with the single pick color. gl.glEnable(GL.GL_TEXTURE_2D); gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_COMBINE); gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_SRC0_RGB, GL2.GL_PREVIOUS); gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_COMBINE_RGB, GL2.GL_REPLACE); } else { gl.glEnable(GL.GL_TEXTURE_2D); gl.glEnable(GL.GL_BLEND); gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); } } protected void endDrawIcons(DrawContext dc) { if (dc.isPickingMode()) this.pickSupport.endPicking(dc); GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility. if (dc.isPickingMode()) { gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, OGLUtil.DEFAULT_TEX_ENV_MODE); gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_SRC0_RGB, OGLUtil.DEFAULT_SRC0_RGB); gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_COMBINE_RGB, OGLUtil.DEFAULT_COMBINE_RGB); } gl.glBindTexture(GL.GL_TEXTURE_2D, 0); this.oglStackHandler.pop(gl); } protected void drawIconsInBatch(DrawContext dc, OrderedIcon uIcon) { this.drawIcon(dc, uIcon); // Draw as many as we can in a batch to save ogl state switching. Object nextItem = dc.peekOrderedRenderables(); while (nextItem != null && nextItem instanceof OrderedIcon) { OrderedIcon oi = (OrderedIcon) nextItem; if (oi.getRenderer() != this) return; dc.pollOrderedRenderables(); // take it off the queue this.drawIcon(dc, oi); nextItem = dc.peekOrderedRenderables(); } } protected void pickIconsInBatch(DrawContext dc, OrderedIcon uIcon) { this.drawIcon(dc, uIcon); // Draw as many as we can in a batch to save ogl state switching. // Note that there's a further qualification here than in render(): only items associated with the // same layer can be batched because the pick resolution step at the end of batch rendering // associates the item's layer with the resolved picked object. Object nextItem = dc.peekOrderedRenderables(); while (nextItem != null && nextItem instanceof OrderedIcon && ((OrderedIcon) nextItem).layer == uIcon.layer) { OrderedIcon oi = (OrderedIcon) nextItem; if (oi.getRenderer() != this) return; dc.pollOrderedRenderables(); // take it off the queue this.drawIcon(dc, oi); nextItem = dc.peekOrderedRenderables(); } } protected Vec4 drawIcon(DrawContext dc, OrderedIcon uIcon) { if (uIcon.point == null) { String msg = Logging.getMessage("nullValue.PointIsNull"); Logging.logger().severe(msg); // Record feedback data for this WWIcon if feedback is enabled. if (uIcon.icon != null) this.recordFeedback(dc, uIcon.icon, null, null); return null; } WWIcon icon = uIcon.icon; if (dc.getView().getFrustumInModelCoordinates().getNear().distanceTo(uIcon.point) < 0) { // Record feedback data for this WWIcon if feedback is enabled. this.recordFeedback(dc, icon, uIcon.point, null); return null; } final Vec4 screenPoint = dc.getView().project(uIcon.point); if (screenPoint == null) { // Record feedback data for this WWIcon if feedback is enabled. this.recordFeedback(dc, icon, uIcon.point, null); return null; } double pedestalScale; double pedestalSpacing; if (this.pedestal != null) { pedestalScale = this.pedestal.getScale(); pedestalSpacing = pedestal.getSpacingPixels(); } else { pedestalScale = 0d; pedestalSpacing = 0d; } GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility. this.setDepthFunc(dc, uIcon, screenPoint); gl.glMatrixMode(GL2.GL_MODELVIEW); gl.glLoadIdentity(); Dimension size = icon.getSize(); double width = size != null ? size.getWidth() : icon.getImageTexture().getWidth(dc); double height = size != null ? size.getHeight() : icon.getImageTexture().getHeight(dc); gl.glTranslated(screenPoint.x - width / 2, screenPoint.y + (pedestalScale * height) + pedestalSpacing, 0d); if (icon.isHighlighted()) { double heightDelta = this.pedestal != null ? 0 : height / 2; // expand only above the pedestal gl.glTranslated(width / 2, heightDelta, 0); gl.glScaled(icon.getHighlightScale(), icon.getHighlightScale(), icon.getHighlightScale()); gl.glTranslated(-width / 2, -heightDelta, 0); } Rectangle rect = new Rectangle((int) (screenPoint.x - width / 2), (int) (screenPoint.y), (int) width, (int) (height + (pedestalScale * height) + pedestalSpacing)); if (dc.isPickingMode()) { //If in picking mode and pick clipping is enabled, check to see if the icon is within the pick volume. if (this.isPickFrustumClippingEnabled() && !dc.getPickFrustums().intersectsAny(rect)) { // Record feedback data for this WWIcon if feedback is enabled. this.recordFeedback(dc, icon, uIcon.point, rect); return screenPoint; } else { java.awt.Color color = dc.getUniquePickColor(); int colorCode = color.getRGB(); this.pickSupport.addPickableObject(colorCode, icon, uIcon.getPosition(), false); gl.glColor3ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue()); } } if (icon.getBackgroundTexture() != null) this.applyBackground(dc, icon, screenPoint, width, height, pedestalSpacing, pedestalScale); if (icon.getImageTexture().bind(dc)) { TextureCoords texCoords = icon.getImageTexture().getTexCoords(); gl.glScaled(width, height, 1d); dc.drawUnitQuad(texCoords); } if (this.pedestal != null && this.pedestal.getImageTexture() != null) { gl.glLoadIdentity(); gl.glTranslated(screenPoint.x - (pedestalScale * (width / 2)), screenPoint.y, 0d); gl.glScaled(width * pedestalScale, height * pedestalScale, 1d); if (this.pedestal.getImageTexture().bind(dc)) { TextureCoords texCoords = this.pedestal.getImageTexture().getTexCoords(); dc.drawUnitQuad(texCoords); } } // Record feedback data for this WWIcon if feedback is enabled. this.recordFeedback(dc, icon, uIcon.point, rect); return screenPoint; } protected void applyBackground(DrawContext dc, WWIcon icon, Vec4 screenPoint, double width, double height, double pedestalSpacing, double pedestalScale) { GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility. double backgroundScale; backgroundScale = icon.getBackgroundScale(); if (icon.getBackgroundTexture() != null) { if (icon.getBackgroundTexture().bind(dc)) { TextureCoords texCoords = icon.getBackgroundTexture().getTexCoords(); gl.glPushMatrix(); gl.glLoadIdentity(); double bgwidth = backgroundScale * width; double bgheight = backgroundScale * height; // Offset the background for the highlighted scale. //if (icon.isHighlighted()) //{ // gl.glTranslated(0d, height * (icon.getHighlightScale() - 1) / 2, 0d); //} // Offset the background for the pedestal height. gl.glTranslated(0d, (pedestalScale * height) + pedestalSpacing, 0d); // Place the background centered behind the icon. gl.glTranslated(screenPoint.x - bgwidth / 2, screenPoint.y - (bgheight - height) / 2, 0d); // Scale to the background image dimension. gl.glScaled(bgwidth, bgheight, 1d); dc.drawUnitQuad(texCoords); gl.glPopMatrix(); } } } protected void setDepthFunc(DrawContext dc, OrderedIcon uIcon, Vec4 screenPoint) { GL gl = dc.getGL(); if (uIcon.icon.isAlwaysOnTop()) { gl.glDepthFunc(GL.GL_ALWAYS); return; } Position eyePos = dc.getView().getEyePosition(); if (eyePos == null) { gl.glDepthFunc(GL.GL_ALWAYS); return; } double altitude = eyePos.getElevation(); if (altitude < (dc.getGlobe().getMaxElevation() * dc.getVerticalExaggeration())) { double depth = screenPoint.z - (8d * 0.00048875809d); depth = depth < 0d ? 0d : (depth > 1d ? 1d : depth); gl.glDepthFunc(GL.GL_LESS); gl.glDepthRange(depth, depth); } else if (uIcon.eyeDistance > uIcon.horizonDistance) { gl.glDepthFunc(GL.GL_EQUAL); gl.glDepthRange(1d, 1d); } else { gl.glDepthFunc(GL.GL_ALWAYS); } } @Override public String toString() { return Logging.getMessage("layers.IconLayer.Name"); } //**************************************************************// //******************** Feedback ******************************// //**************************************************************// /** * Returns true if the IconRenderer should record feedback about how the specified WWIcon has been processed. * * @param dc the current DrawContext. * @param icon the WWIcon to record feedback information for. * * @return true to record feedback; false otherwise. */ protected boolean isFeedbackEnabled(DrawContext dc, WWIcon icon) { if (dc.isPickingMode()) return false; Boolean b = (Boolean) icon.getValue(AVKey.FEEDBACK_ENABLED); return (b != null && b); } /** * If feedback is enabled for the specified WWIcon, this method records feedback about how the specified WWIcon has * been processed. * * @param dc the current DrawContext. * @param icon the icon which the feedback information refers to. * @param modelPoint the icon's reference point in model coordinates. * @param screenRect the icon's bounding rectangle in screen coordinates. */ protected void recordFeedback(DrawContext dc, WWIcon icon, Vec4 modelPoint, Rectangle screenRect) { if (!this.isFeedbackEnabled(dc, icon)) return; this.doRecordFeedback(dc, icon, modelPoint, screenRect); } /** * Records feedback about how the specified WWIcon has been processed. * * @param dc the current DrawContext. * @param icon the icon which the feedback information refers to. * @param modelPoint the icon's reference point in model coordinates. * @param screenRect the icon's bounding rectangle in screen coordinates. */ @SuppressWarnings({"UnusedDeclaration"}) protected void doRecordFeedback(DrawContext dc, WWIcon icon, Vec4 modelPoint, Rectangle screenRect) { icon.setValue(AVKey.FEEDBACK_REFERENCE_POINT, modelPoint); icon.setValue(AVKey.FEEDBACK_SCREEN_BOUNDS, screenRect); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy