    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}