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