001/**
002 * Copyright (c) 2008-2014 Ardor Labs, Inc.
003 *
004 * This file is part of Ardor3D.
005 *
006 * Ardor3D is free software: you can redistribute it and/or modify it
007 * under the terms of its license which may be found in the accompanying
008 * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
009 */
010
011package com.ardor3d.intersection;
012
013import java.util.Arrays;
014import java.util.List;
015
016import com.ardor3d.math.Vector3;
017import com.ardor3d.util.Ardor3dException;
018
019public class IntersectionRecord {
020
021    private static final class Intersection implements Comparable<Intersection> {
022        private final double _distance;
023        private final Vector3 _point;
024        private final Vector3 _normal;
025        private final PrimitiveKey _primitiveKey;
026
027        private Intersection(final double distance, final Vector3 point, final Vector3 normal,
028                final PrimitiveKey primitiveKey) {
029            _distance = distance;
030            _point = point;
031            _normal = normal;
032            _primitiveKey = primitiveKey;
033        }
034
035        @Override
036        public int compareTo(final Intersection other) {
037            return _distance == other._distance ? 0 : (_distance < other._distance ? -1 : 1);
038        }
039    }
040
041    private final Intersection[] _intersections;
042
043    private boolean _isSorted = true;
044
045    /**
046     * Instantiates a new IntersectionRecord defining the distances and points.
047     *
048     * @param distances
049     *            the distances of this intersection.
050     * @param points
051     *            the points of this intersection.
052     * @throws Ardor3dException
053     *             if distances.length != points.length
054     */
055    public IntersectionRecord(final double[] distances, final Vector3[] points) {
056        this(distances, points, null);
057    }
058
059    /**
060     * Instantiates a new IntersectionRecord defining the distances and points.
061     *
062     * @param distances
063     *            the distances of this intersection.
064     * @param points
065     *            the points of this intersection.
066     * @param primitives
067     *            the primitives at each index. May be null.
068     * @throws Ardor3dException
069     *             if distances.length != points.length or points.length != primitives.size() (if primitives is not
070     *             null)
071     */
072    public IntersectionRecord(final double[] distances, final Vector3[] points, final List<PrimitiveKey> primitives) {
073        this(distances, points, null, primitives);
074    }
075
076    /**
077     * Instantiates a new IntersectionRecord defining the distances and points.
078     *
079     * @param distances
080     *            the distances of this intersection.
081     * @param points
082     *            the points of this intersection.
083     * @param normals
084     *            the normals of this intersection.
085     * @param primitives
086     *            the primitives at each index. May be null.
087     * @throws Ardor3dException
088     *             if distances.length != points.length or points.length != primitives.size() (if primitives is not
089     *             null)
090     */
091    public IntersectionRecord(final double[] distances, final Vector3[] points, final Vector3[] normals,
092            final List<PrimitiveKey> primitives) {
093        if (distances.length != points.length || (primitives != null && points.length != primitives.size())
094                || (normals != null && points.length != normals.length)) {
095            throw new Ardor3dException("All arguments must have an equal number of elements.");
096        }
097        _isSorted = distances.length < 2;
098        _intersections = new Intersection[distances.length];
099        for (int i = 0; i < distances.length; i++) {
100            _intersections[i] = new Intersection(distances[i], points[i], normals != null ? normals[i] : null,
101                    primitives != null ? primitives.get(i) : null);
102        }
103    }
104
105    /**
106     * Sorts intersections from near to far
107     */
108    public void sortIntersections() {
109        if (!_isSorted) {
110            Arrays.sort(_intersections);
111            _isSorted = true;
112        }
113    }
114
115    /**
116     * @return the number of intersections that occurred.
117     */
118    public int getNumberOfIntersections() {
119        return _intersections.length;
120    }
121
122    /**
123     * Returns an intersection point at a provided index.
124     *
125     * @param index
126     *            the index of the point to obtain.
127     * @return the point at the index of the array.
128     */
129    public Vector3 getIntersectionPoint(final int index) {
130        return _intersections[index]._point;
131    }
132
133    /**
134     * Returns an intersection normal at a provided index.
135     *
136     * @param index
137     *            the index of the point to obtain.
138     * @return the normal at the index of the array.
139     */
140    public Vector3 getIntersectionNormal(final int index) {
141        return _intersections[index]._normal;
142    }
143
144    /**
145     * Returns an intersection distance at a provided index.
146     *
147     * @param index
148     *            the index of the distance to obtain.
149     * @return the distance at the index of the array.
150     */
151    public double getIntersectionDistance(final int index) {
152        return _intersections[index]._distance;
153    }
154
155    /**
156     * @param index
157     *            the index of the primitive to obtain.
158     * @return the primitive at the given index.
159     */
160    public PrimitiveKey getIntersectionPrimitive(final int index) {
161        return _intersections[index]._primitiveKey;
162    }
163
164    /**
165     * @return the smallest distance in the distance array or -1 if there are no distances in this.
166     */
167    public double getClosestDistance() {
168        final int i = getClosestIntersection();
169        return i != -1 ? _intersections[i]._distance : -1.0;
170    }
171
172    /**
173     * @return the largest distance in the distance array or -1 if there are no distances in this.
174     */
175    public double getFurthestDistance() {
176        final int i = getFurthestIntersection();
177        return i != -1 ? _intersections[i]._distance : -1.0;
178    }
179
180    /**
181     * @return the index in this record with the smallest relative distance or -1 if there are no distances in this
182     *         record.
183     */
184    public int getClosestIntersection() {
185        int index = -1;
186        if (_isSorted) {
187            index = _intersections.length > 0 ? 0 : -1;
188        } else {
189            double min = Double.MAX_VALUE;
190            for (int i = _intersections.length; --i >= 0;) {
191                final double val = _intersections[i]._distance;
192                if (val < min) {
193                    min = val;
194                    index = i;
195                }
196            }
197        }
198        return index;
199    }
200
201    /**
202     * @return the index in this record with the largest relative distance or -1 if there are no distances in this
203     *         record.
204     */
205    public int getFurthestIntersection() {
206        int index = -1;
207        if (_isSorted) {
208            index = _intersections.length > 0 ? _intersections.length - 1 : -1;
209        } else {
210            double max = -Double.MAX_VALUE;
211            for (int i = _intersections.length; --i >= 0;) {
212                final double val = _intersections[i]._distance;
213                if (val > max) {
214                    max = val;
215                    index = i;
216                }
217            }
218        }
219        return index;
220    }
221
222}