/* =========================================================== * Orson Charts : a 3D chart library for the Java(tm) platform * =========================================================== * * (C)opyright 2013-2016, by Object Refinery Limited. All rights reserved. * * http://www.object-refinery.com/orsoncharts/index.html * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * If you do not wish to be bound by the terms of the GPL, an alternative * commercial license can be purchased. For details, please see visit the * Orson Charts home page: * * http://www.object-refinery.com/orsoncharts/index.html * */ package com.orsoncharts.graphics3d; import java.awt.Shape; import java.awt.geom.Path2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.List; import java.util.ArrayList; /** * Rendering info returned from the {@link Drawable3D} {@code draw()} * method. * * @since 1.3 */ public class RenderingInfo { /** * A list of the faces drawn in order of rendering. */ private List<Face> faces; /** The projected points for the vertices in the faces. */ Point2D[] projPts; /** The x-translation. */ private double dx; /** The y-translation. */ public double dy; /** * Storage for rendered elements in the model other than the 3D objects * (caters for code that overlays other items such as labels). */ List<RenderedElement> otherElements; List<RenderedElement> otherOffsetElements; /** * Creates a new instance. * * @param faces the rendered faces (in order of rendering). * @param projPts the projected points for all vertices in the 3D model. * @param dx the x-delta. * @param dy the y-delta. */ public RenderingInfo(List<Face> faces, Point2D[] projPts, double dx, double dy) { this.faces = faces; this.projPts = projPts; this.dx = dx; this.dy = dy; this.otherElements = new ArrayList<RenderedElement>(); this.otherOffsetElements = new ArrayList<RenderedElement>(); } /** * Returns the list of faces rendered. * * @return The list of faces. */ public List<Face> getFaces() { return this.faces; } /** * Returns the projected points. * * @return The projected points. */ public Point2D[] getProjectedPoints() { return this.projPts; } /** * Returns the x-translation amount. All projected points are centered * on (0, 0) but the rendering to the screen (or other Graphics2D target) * performs two translations: the first is to the center of the bounding * rectangle, and the second is to apply the translate2D attribute of the * chart. The result of these translations is stored here and used in the * fetchObjectAt(x, y) method. * * @return The x-translation. */ public double getDX() { return this.dx; } /** * Returns the y-translation amount. * * @return The y-translation. */ public double getDY() { return this.dy; } /** * Adds a rendered element to the rendering info. * * @param element the element ({@code null} not permitted). */ public void addElement(RenderedElement element) { this.otherElements.add(element); } /** * Adds a rendered element to the list of offset elements. * * @param element the element ({@code null} not permitted). */ public void addOffsetElement(RenderedElement element) { this.otherOffsetElements.add(element); } /** * Fetches the object, if any, that is rendered at {@code (x, y)}. * * @param x the x-coordinate. * @param y the y-coordinate. * * @return The object (or {@code null}). */ public Object3D fetchObjectAt(double x, double y) { for (int i = this.faces.size() - 1; i >= 0; i--) { Face f = this.faces.get(i); if (f instanceof LabelFace) { Rectangle2D bounds = (Rectangle2D) f.getOwner().getProperty("labelBounds"); if (bounds != null && bounds.contains(x - dx, y - dy)) { return f.getOwner(); } } else { Path2D p = f.createPath(this.projPts); if (p.contains(x - dx, y - dy)) { return f.getOwner(); } } } return null; } /** * Finds the rendered element, if any, at the location {@code (x, y)}. * The method first calls fetchObjectAt(x, y) to see if there is an * object at the specified location and, if there is, returns a new * RenderedElement instance for that object. Otherwise, it searches the * otherElements list to see if there is some other element (such as a * title, legend, axis label or axis tick label) and returns that item. * Finally, if no element is found, the method returns {@code null}. * * @param x the x-coordinate. * @param y the y-coordinate. * * @return The interactive element or {@code null}. */ public RenderedElement findElementAt(double x, double y) { for (int i = this.otherElements.size() - 1; i >= 0; i--) { RenderedElement element = this.otherElements.get(i); Shape bounds = (Shape) element.getProperty(RenderedElement.BOUNDS); if (bounds.contains(x, y)) { return element; } } for (int i = this.otherOffsetElements.size() - 1; i >= 0; i--) { RenderedElement element = this.otherOffsetElements.get(i); Shape bounds = (Shape) element.getProperty(RenderedElement.BOUNDS); if (bounds != null && bounds.contains(x - dx, y - dy)) { return element; } } Object3D obj = fetchObjectAt(x, y); if (obj != null) { RenderedElement element = new RenderedElement("obj3d", null); element.setProperty(Object3D.ITEM_KEY, obj.getProperty(Object3D.ITEM_KEY)); if (obj.getProperty(Object3D.CLASS_KEY) != null) { element.setProperty(Object3D.CLASS_KEY, obj.getProperty(Object3D.CLASS_KEY)); } return element; } return null; } }