001/* =========================================================== 002 * Orson Charts : a 3D chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C)opyright 2013-2022, by David Gilbert. All rights reserved. 006 * 007 * https://github.com/jfree/orson-charts 008 * 009 * This program is free software: you can redistribute it and/or modify 010 * it under the terms of the GNU General Public License as published by 011 * the Free Software Foundation, either version 3 of the License, or 012 * (at your option) any later version. 013 * 014 * This program is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public License 020 * along with this program. If not, see <http://www.gnu.org/licenses/>. 021 * 022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 023 * Other names may be trademarks of their respective owners.] 024 * 025 * If you do not wish to be bound by the terms of the GPL, an alternative 026 * commercial license can be purchased. For details, please see visit the 027 * Orson Charts home page: 028 * 029 * http://www.object-refinery.com/orsoncharts/index.html 030 * 031 */ 032 033package org.jfree.chart3d.graphics3d; 034 035import java.awt.Shape; 036import java.awt.geom.Path2D; 037import java.awt.geom.Point2D; 038import java.awt.geom.Rectangle2D; 039import java.util.List; 040import java.util.ArrayList; 041 042/** 043 * Rendering info returned from the {@link Drawable3D} {@code draw()} 044 * method. 045 * 046 * @since 1.3 047 */ 048public class RenderingInfo { 049 050 /** 051 * A list of the faces drawn in order of rendering. 052 */ 053 private final List<Face> faces; 054 055 /** The projected points for the vertices in the faces. */ 056 Point2D[] projPts; 057 058 /** The x-translation. */ 059 private final double dx; 060 061 /** The y-translation. */ 062 public double dy; 063 064 /** 065 * Storage for rendered elements in the model other than the 3D objects 066 * (caters for code that overlays other items such as labels). 067 */ 068 List<RenderedElement> otherElements; 069 070 List<RenderedElement> otherOffsetElements; 071 072 /** 073 * Creates a new instance. 074 * 075 * @param faces the rendered faces (in order of rendering). 076 * @param projPts the projected points for all vertices in the 3D model. 077 * @param dx the x-delta. 078 * @param dy the y-delta. 079 */ 080 public RenderingInfo(List<Face> faces, Point2D[] projPts, double dx, 081 double dy) { 082 this.faces = faces; 083 this.projPts = projPts; 084 this.dx = dx; 085 this.dy = dy; 086 this.otherElements = new ArrayList<>(); 087 this.otherOffsetElements = new ArrayList<>(); 088 } 089 090 /** 091 * Returns the list of faces rendered. 092 * 093 * @return The list of faces. 094 */ 095 public List<Face> getFaces() { 096 return this.faces; 097 } 098 099 /** 100 * Returns the projected points. 101 * 102 * @return The projected points. 103 */ 104 public Point2D[] getProjectedPoints() { 105 return this.projPts; 106 } 107 108 /** 109 * Returns the x-translation amount. All projected points are centered 110 * on (0, 0) but the rendering to the screen (or other Graphics2D target) 111 * performs two translations: the first is to the center of the bounding 112 * rectangle, and the second is to apply the translate2D attribute of the 113 * chart. The result of these translations is stored here and used in the 114 * fetchObjectAt(x, y) method. 115 * 116 * @return The x-translation. 117 */ 118 public double getDX() { 119 return this.dx; 120 } 121 122 /** 123 * Returns the y-translation amount. 124 * 125 * @return The y-translation. 126 */ 127 public double getDY() { 128 return this.dy; 129 } 130 131 /** 132 * Adds a rendered element to the rendering info. 133 * 134 * @param element the element ({@code null} not permitted). 135 */ 136 public void addElement(RenderedElement element) { 137 this.otherElements.add(element); 138 } 139 140 /** 141 * Adds a rendered element to the list of offset elements. 142 * 143 * @param element the element ({@code null} not permitted). 144 */ 145 public void addOffsetElement(RenderedElement element) { 146 this.otherOffsetElements.add(element); 147 } 148 149 /** 150 * Fetches the object, if any, that is rendered at {@code (x, y)}. 151 * 152 * @param x the x-coordinate. 153 * @param y the y-coordinate. 154 * 155 * @return The object (or {@code null}). 156 */ 157 public Object3D fetchObjectAt(double x, double y) { 158 for (int i = this.faces.size() - 1; i >= 0; i--) { 159 Face f = this.faces.get(i); 160 if (f instanceof LabelFace) { 161 Rectangle2D bounds 162 = (Rectangle2D) f.getOwner().getProperty("labelBounds"); 163 if (bounds != null && bounds.contains(x - dx, y - dy)) { 164 return f.getOwner(); 165 } 166 } else { 167 Path2D p = f.createPath(this.projPts); 168 if (p.contains(x - dx, y - dy)) { 169 return f.getOwner(); 170 } 171 } 172 } 173 return null; 174 } 175 176 /** 177 * Finds the rendered element, if any, at the location {@code (x, y)}. 178 * The method first calls fetchObjectAt(x, y) to see if there is an 179 * object at the specified location and, if there is, returns a new 180 * RenderedElement instance for that object. Otherwise, it searches the 181 * otherElements list to see if there is some other element (such as a 182 * title, legend, axis label or axis tick label) and returns that item. 183 * Finally, if no element is found, the method returns {@code null}. 184 * 185 * @param x the x-coordinate. 186 * @param y the y-coordinate. 187 * 188 * @return The interactive element or {@code null}. 189 */ 190 public RenderedElement findElementAt(double x, double y) { 191 for (int i = this.otherElements.size() - 1; i >= 0; i--) { 192 RenderedElement element = this.otherElements.get(i); 193 Shape bounds = (Shape) element.getProperty(RenderedElement.BOUNDS); 194 if (bounds.contains(x, y)) { 195 return element; 196 } 197 } 198 199 for (int i = this.otherOffsetElements.size() - 1; i >= 0; i--) { 200 RenderedElement element = this.otherOffsetElements.get(i); 201 Shape bounds = (Shape) element.getProperty(RenderedElement.BOUNDS); 202 if (bounds != null && bounds.contains(x - dx, y - dy)) { 203 return element; 204 } 205 } 206 207 Object3D obj = fetchObjectAt(x, y); 208 if (obj != null) { 209 RenderedElement element = new RenderedElement("obj3d", null); 210 element.setProperty(Object3D.ITEM_KEY, 211 obj.getProperty(Object3D.ITEM_KEY)); 212 if (obj.getProperty(Object3D.CLASS_KEY) != null) { 213 element.setProperty(Object3D.CLASS_KEY, 214 obj.getProperty(Object3D.CLASS_KEY)); 215 } 216 return element; 217 } 218 return null; 219 } 220 221}