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.geom.Point2D;
036import java.util.ArrayList;
037import java.util.Collection;
038import java.util.List;
039import java.util.Map;
040import java.util.Map.Entry;
041import org.jfree.chart3d.graphics3d.internal.Utils3D;
042import org.jfree.chart3d.internal.Args;
043
044/**
045 * A world is a model containing a collection of objects in 3D space and a 
046 * direction vector for the sunlight.  A viewing point ({@link ViewPoint3D}) is 
047 * specified externally.  Objects in the world are assigned to a partition, 
048 * providing the ability to group objects.
049 */
050public class World {
051
052    /** 
053     * The default partition key.  All objects in the world are added with
054     * a partition key.  There always exists at least one partition (the 
055     * default partition).
056     * 
057     * @since 1.2
058     */
059    public static final String DEFAULT_PARTITION_KEY = "default";
060    
061    /** The sunlight vector. */
062    private double sunX;
063    private double sunY;
064    private double sunZ;
065
066    /** 
067     * Storage for the objects in the world.  A map is used to store
068     * one or more lists of objects (the partitioning is useful so
069     * that updates can be made to subsets of the world).
070     */
071    private Map<String, List<Object3D>> objects;
072    
073    /**
074     * Creates a new empty world.
075     */
076    public World() {
077        this.objects = new java.util.TreeMap<>();
078        this.objects.put(DEFAULT_PARTITION_KEY, new ArrayList<>());
079        setSunSource(new Point3D(2, -1, 10));
080    }
081
082  
083    /**
084     * Returns the x-component of the sunlight vector.
085     *
086     * @return The x-component of the sunlight vector.
087     */
088    public double getSunX() {
089        return this.sunX;
090    }
091
092    /**
093     * Returns the y-component of the sunlight vector.
094     *
095     * @return The y-component of the sunlight vector.
096     */
097    public double getSunY() {
098        return this.sunY;
099    }
100
101    /**
102     * Returns the z-component of the sunlight vector.
103     *
104     * @return The z-component of the sunlight vector.
105     */
106    public double getSunZ() {
107        return this.sunZ;
108    }
109    
110    /**
111     * Sets the light source point.
112     * 
113     * @param x  the x-coordinate.
114     * @param y  the y-coordinate.
115     * @param z  the z-coordinate.
116     * 
117     * @since 1.2
118     */
119    public final void setSunSource(double x, double y, double z) {
120        setSunSource(new Point3D(x, y, z));
121    }
122    
123    /**
124     * Sets the light source point.
125     * 
126     * @param p  the point ({@code null} not permitted).
127     * 
128     * @since 1.2
129     */
130    public final void setSunSource(Point3D p) {
131        Args.nullNotPermitted(p, "p");
132        Point3D normal = Utils3D.normalise(p);
133        this.sunX = normal.getX();
134        this.sunY = normal.getY();
135        this.sunZ = normal.getZ();
136    }
137    
138    /**
139     * Adds an object to the world in the default partition.
140     *
141     * @param object  the object ({@code null} not permitted).
142     */
143    public void add(Object3D object) {
144        // defer argument checking
145        add(DEFAULT_PARTITION_KEY, object);
146    }
147
148    /**
149     * Adds an object to a specific partition.
150     * 
151     * @param partition  the partition ({@code null} not permitted).
152     * @param object  the object ({@code null} not permitted).
153     * 
154     * @since 1.2
155     */
156    public void add(String partition, Object3D object) {
157        Args.nullNotPermitted(partition, "partition");
158        Args.nullNotPermitted(object, "object");
159        List<Object3D> list = this.objects.get(partition);
160        if (list == null) {
161            list = new ArrayList<>();
162            this.objects.put(partition, list);
163        }
164        list.add(object);
165    }
166    
167    /**
168     * Adds a collection of objects to the world (in the default
169     * partition).
170     * 
171     * @param objects  the objects ({@code null} not permitted). 
172     */
173    public void addAll(Collection<Object3D> objects) {
174        Args.nullNotPermitted(objects, "objects");
175        for (Object3D object : objects) {
176            add(object);
177        }
178    }
179
180    /**
181     * Clears any objects belonging to the specified partition.
182     * 
183     * @param partitionKey  the partition key ({@code null} not permitted).
184     * 
185     * @since 1.2
186     */
187    public void clear(String partitionKey) {
188        Args.nullNotPermitted(partitionKey, "partitionKey");
189        this.objects.put(partitionKey, null);
190    }
191    
192    /**
193     * Returns the total number of vertices for all objects in this world.
194     *
195     * @return The total number of vertices.
196     */
197    public int getVertexCount() {
198        int count = 0;
199        for (Entry<String, List<Object3D>> entry : this.objects.entrySet()) {
200            List<Object3D> objs = entry.getValue();    
201            for (Object3D object: objs) {
202                count += object.getVertexCount();
203            }
204        }
205        return count;
206    }
207
208    /**
209     * Returns an array containing the vertices for all objects in this
210     * world, transformed to eye coordinates.
211     *
212     * @param vp  the view point ({@code null} not permitted).
213     *
214     * @return The eye coordinates.
215     */
216    public Point3D[] calculateEyeCoordinates(ViewPoint3D vp) {
217        Point3D[] result = new Point3D[getVertexCount()];
218        int index = 0;
219        for (Entry<String, List<Object3D>> entry : this.objects.entrySet()) {
220            List<Object3D> objs = entry.getValue();    
221            for (Object3D object : objs) {
222                Point3D[] vertices = object.calculateEyeCoordinates(vp);
223                System.arraycopy(vertices, 0, result, index, vertices.length);
224                index = index + vertices.length;
225            }
226        }
227        return result;
228    }
229
230    /**
231     * Calculates the projected points in 2D-space for all the vertices of the
232     * objects in the world.
233     * 
234     * @param vp  the view point ({@code null} not permitted).
235     * @param d  the distance.
236     * 
237     * @return The projected points.
238     */
239    public Point2D[] calculateProjectedPoints(ViewPoint3D vp, double d) {
240        Point2D[] result = new Point2D[getVertexCount()];
241        int index = 0;
242        for (Entry<String, List<Object3D>> entry : this.objects.entrySet()) {
243            List<Object3D> objs = entry.getValue();    
244            for (Object3D object : objs) {
245                Point2D[] pts = object.calculateProjectedPoints(vp, d);
246                System.arraycopy(pts, 0, result, index, pts.length);
247                index = index + pts.length;
248            }
249        }
250        return result;
251    }
252
253    /**
254     * Fetches the faces for all the objects in this world, updating the
255     * offset to match the current position.
256     *
257     * @return A list of faces.
258     */
259    public List<Face> getFaces() {
260        List<Face> result = new java.util.ArrayList<>();
261        int offset = 0;
262        for (Entry<String, List<Object3D>> entry : this.objects.entrySet()) {
263            List<Object3D> objs = entry.getValue();    
264            for (Object3D object : objs) {
265                for (Face f : object.getFaces()) {
266                    f.setOffset(offset);
267                }
268                offset += object.getVertexCount();
269                result.addAll(object.getFaces());
270            }
271        }
272        return result;
273    }
274    
275    /**
276     * Returns a newly created list containing all the objects in the world 
277     * model.
278     * 
279     * @return The list of objects.
280     * 
281     * @since 1.2
282     */
283    public List<Object3D> getObjects() {
284        List<Object3D> result = new ArrayList<>();
285        for (Entry<String, List<Object3D>> entry : this.objects.entrySet()) {
286            List<Object3D> objs = entry.getValue();    
287            result.addAll(objs);
288        }
289        return result;
290    }
291
292}