    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}