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.ArrayList;
014import java.util.Collections;
015import java.util.Comparator;
016import java.util.List;
017
018import com.ardor3d.math.Ray3;
019
020/**
021 * PickResults stores information created during ray intersection tests. The results will contain a list of every
022 * {@link Pickable} element encountered in a pick test. Distance can be used to order the results. If checkDistance is
023 * set to true, objects will be ordered with the first element in the list being the closest picked object.
024 */
025public abstract class PickResults {
026
027    private final List<PickData> _nodeList;
028    private boolean _checkDistance;
029    private DistanceComparator _distanceCompare;
030
031    /**
032     * Constructor instantiates a new <code>PickResults</code> object.
033     */
034    public PickResults() {
035        _nodeList = new ArrayList<>();
036    }
037
038    /**
039     * remember modification of the list to allow sorting after all picks have been added - not each time.
040     */
041    private boolean modified = false;
042
043    /**
044     * Places a new geometry (enclosed in PickData) into the results list.
045     *
046     * @param data
047     *            the PickData to be placed in the results list.
048     */
049    public void addPickData(final PickData data) {
050        _nodeList.add(data);
051        modified = true;
052    }
053
054    /**
055     * <code>getNumber</code> retrieves the number of geometries that have been placed in the results.
056     *
057     * @return the number of Mesh objects in the list.
058     */
059    public int getNumber() {
060        return _nodeList.size();
061    }
062
063    /**
064     * Retrieves a geometry (enclosed in PickData) from a specific index.
065     *
066     * @param i
067     *            the index requested.
068     * @return the data at the specified index.
069     */
070    public PickData getPickData(final int i) {
071        if (modified) {
072            if (_checkDistance) {
073                Collections.sort(_nodeList, _distanceCompare);
074            }
075            modified = false;
076        }
077        return _nodeList.get(i);
078    }
079
080    /**
081     * <code>clear</code> clears the list of all Mesh objects.
082     */
083    public void clear() {
084        _nodeList.clear();
085    }
086
087    /**
088     * <code>addPick</code> generates an entry to be added to the list of picked objects. If checkDistance is true, the
089     * implementing class should order the object.
090     *
091     * @param ray
092     *            the ray that was cast for the pick calculation.
093     * @param p
094     *            the pickable object to add to the pick data.
095     */
096    public abstract void addPick(Ray3 ray, Pickable p);
097
098    /**
099     * Optional method that can be implemented by sub classes to define methods for handling picked objects. After
100     * calculating all pick results this method is called.
101     *
102     */
103    public void processPick() {}
104
105    /**
106     * Reports if these pick results will order the data by distance from the origin of the Ray.
107     *
108     * @return true if objects will be ordered by distance, false otherwise.
109     */
110    public boolean willCheckDistance() {
111        return _checkDistance;
112    }
113
114    /**
115     * Sets if these pick results will order the data by distance from the origin of the Ray.
116     *
117     * @param checkDistance
118     *            true if objects will be ordered by distance, false otherwise.
119     */
120    public void setCheckDistance(final boolean checkDistance) {
121        _checkDistance = checkDistance;
122        if (checkDistance) {
123            _distanceCompare = new DistanceComparator();
124        }
125    }
126
127    /**
128     * Implementation of comparator that uses the distance set in the pick data to order the objects.
129     */
130    private static class DistanceComparator implements Comparator<PickData> {
131
132        @Override
133        public int compare(final PickData o1, final PickData o2) {
134            if (o1.getIntersectionRecord().getClosestDistance() == o2.getIntersectionRecord().getClosestDistance()) {
135                return 0;
136            }
137            if (o1.getIntersectionRecord().getClosestDistance() < o2.getIntersectionRecord().getClosestDistance()) {
138                return -1;
139            }
140
141            return 1;
142        }
143    }
144
145    public PickData findFirstIntersectingPickData() {
146        int i = 0;
147        while (getNumber() > 0 && getPickData(i).getIntersectionRecord().getNumberOfIntersections() == 0
148                && ++i < getNumber()) {
149        }
150        return getNumber() > i ? getPickData(i) : null;
151    }
152}