/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.arakhne.afc.math.geometry.d3;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import org.eclipse.xtext.xbase.lib.Pure;
import org.arakhne.afc.math.Unefficient;
import org.arakhne.afc.vmutil.asserts.AssertMessages;
/** Container for grouping of shapes.
*
* <p>The coordinates of the shapes inside the multishape are global. They are not relative to the multishape.
*
* @param <ST> is the type of the general implementation.
* @param <IT> is the type of the implementation of this multishape.
* @param <CT> is the type of the shapes that are inside this multishape.
* @param <I> is the type of the iterator used to obtain the elements of the path.
* @param <P> is the type of the points.
* @param <V> is the type of the vectors.
* @param <B> is the type of the bounding boxes.
* @author $Author: tpiotrow$
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public interface MultiShape3D<
ST extends Shape3D<?, ?, I, P, V, B>,
IT extends MultiShape3D<?, ?, CT, I, P, V, B>,
CT extends Shape3D<?, ?, I, P, V, B>,
I extends PathIterator3D<?>,
P extends Point3D<? super P, ? super V>,
V extends Vector3D<? super V, ? super P>,
B extends Shape3D<?, ?, I, P, V, B>> extends Shape3D<ST, IT, I, P, V, B>, List<CT> {
/** Get the first shape in this multishape that is containing the given point.
*
* @param point the point.
* @return the shape, or <code>null</code> if no shape contains the given point.
*/
@Pure
default CT getFirstShapeContaining(Point3D<?, ?> point) {
assert point != null : AssertMessages.notNullParameter();
if (toBoundingBox().contains(point)) {
for (final CT shape : getBackendDataList()) {
if (shape.contains(point)) {
return shape;
}
}
}
return null;
}
/** Get the shapes in this multishape that are containing the given point.
*
* @param point the point.
* @return the shapes, or an empty list.
*/
@Pure
@Unefficient
default List<CT> getShapesContaining(Point3D<?, ?> point) {
assert point != null : AssertMessages.notNullParameter();
final List<CT> list = new ArrayList<>();
if (toBoundingBox().contains(point)) {
for (final CT shape : getBackendDataList()) {
if (shape.contains(point)) {
list.add(shape);
}
}
}
return list;
}
/** Get the first shape in this multishape that is intersecting the given shape.
*
* @param shape the shape.
* @return the shape, or <code>null</code> if no shape intersecting the given shape.
*/
@Pure
CT getFirstShapeIntersecting(ST shape);
/** Get the shapes in this multishape that are intersecting the given shape.
*
* @param shape the shape.
* @return the shapes, or an empty list.
*/
@Pure
List<CT> getShapesIntersecting(ST shape);
/** Replies the list that contains the backend data.
*
* <p>Use this function with caution. Indeed, any change made in the replied list
* has no consequence on the internal attributes of this multishape object.
*
* @return the backend data list.
*/
@Pure
List<CT> getBackendDataList();
/** Invoked each time the backend data has changed.
*/
default void onBackendDataChange() {
//
}
@Override
default void set(IT multishape) {
assert multishape != null : AssertMessages.notNullParameter();
final List<CT> backend = getBackendDataList();
backend.clear();
backend.addAll(multishape.getBackendDataList());
onBackendDataChange();
}
@Override
default CT set(int index, CT element) {
final CT old = getBackendDataList().set(index, element);
onBackendDataChange();
return old;
}
@Override
@Pure
default boolean equalsToShape(IT shape) {
if (shape == null) {
return false;
}
return getBackendDataList().equals(shape.getBackendDataList());
}
@Override
default void clear() {
getBackendDataList().clear();
onBackendDataChange();
}
@Pure
@Override
default boolean isEmpty() {
if (getBackendDataList().isEmpty()) {
return true;
}
return toBoundingBox().isEmpty();
}
@Pure
@Override
default P getClosestPointTo(Point3D<?, ?> point) {
P closestPoint = null;
double minDist = Double.POSITIVE_INFINITY;
for (final CT shape : getBackendDataList()) {
final P close = shape.getClosestPointTo(point);
final double dist = close.getDistanceSquared(point);
if (dist < minDist) {
minDist = dist;
closestPoint = close;
}
}
return closestPoint;
}
@Pure
@Override
default P getFarthestPointTo(Point3D<?, ?> point) {
P farthestPoint = null;
double maxDist = Double.NEGATIVE_INFINITY;
for (final CT shape : getBackendDataList()) {
final P far = shape.getFarthestPointTo(point);
final double dist = far.getDistanceSquared(point);
if (dist > maxDist) {
maxDist = dist;
farthestPoint = far;
}
}
return farthestPoint;
}
@Pure
@Override
default double getDistanceSquared(Point3D<?, ?> point) {
double minDist = Double.POSITIVE_INFINITY;
for (final CT shape : getBackendDataList()) {
final double dist = shape.getDistanceSquared(point);
if (dist < minDist) {
minDist = dist;
}
}
return minDist;
}
@Pure
@Override
default double getDistanceL1(Point3D<?, ?> point) {
double minDist = Double.POSITIVE_INFINITY;
for (final CT shape : getBackendDataList()) {
final double dist = shape.getDistanceL1(point);
if (dist < minDist) {
minDist = dist;
}
}
return minDist;
}
@Pure
@Override
default double getDistanceLinf(Point3D<?, ?> point) {
double minDist = Double.POSITIVE_INFINITY;
for (final CT shape : getBackendDataList()) {
final double dist = shape.getDistanceLinf(point);
if (dist < minDist) {
minDist = dist;
}
}
return minDist;
}
@Pure
@Override
default Object[] toArray() {
return getBackendDataList().toArray();
}
@Override
default <T> T[] toArray(T[] array) {
return getBackendDataList().toArray(array);
}
@Pure
@Override
default int size() {
return getBackendDataList().size();
}
@Pure
@Override
default boolean contains(Object obj) {
return getBackendDataList().contains(obj);
}
@Pure
@Override
default Iterator<CT> iterator() {
return new BackendIterator<>(this, getBackendDataList().listIterator());
}
@Override
default boolean add(CT element) {
if (getBackendDataList().add(element)) {
onBackendDataChange();
return true;
}
return false;
}
@Override
default void add(int index, CT element) {
getBackendDataList().add(index, element);
onBackendDataChange();
}
@Override
default boolean remove(Object obj) {
if (getBackendDataList().remove(obj)) {
onBackendDataChange();
return true;
}
return false;
}
@Override
default CT remove(int index) {
final CT removed = getBackendDataList().remove(index);
onBackendDataChange();
return removed;
}
@Pure
@Override
default boolean containsAll(Collection<?> collection) {
return getBackendDataList().containsAll(collection);
}
@Override
default boolean addAll(Collection<? extends CT> collection) {
if (getBackendDataList().addAll(collection)) {
onBackendDataChange();
return true;
}
return false;
}
@Override
default boolean addAll(int index, Collection<? extends CT> collection) {
if (getBackendDataList().addAll(index, collection)) {
onBackendDataChange();
return true;
}
return false;
}
@Override
default boolean removeAll(Collection<?> collection) {
if (getBackendDataList().removeAll(collection)) {
onBackendDataChange();
return true;
}
return false;
}
@Override
default boolean retainAll(Collection<?> collection) {
if (getBackendDataList().retainAll(collection)) {
onBackendDataChange();
return true;
}
return false;
}
@Pure
@Override
default CT get(int index) {
return getBackendDataList().get(index);
}
@Pure
@Override
default int indexOf(Object obj) {
return getBackendDataList().indexOf(obj);
}
@Pure
@Override
default int lastIndexOf(Object obj) {
return getBackendDataList().lastIndexOf(obj);
}
@Pure
@Override
default ListIterator<CT> listIterator() {
return new BackendIterator<>(this, getBackendDataList().listIterator());
}
@Pure
@Override
default ListIterator<CT> listIterator(int index) {
return new BackendIterator<>(this, getBackendDataList().listIterator(index));
}
@Override
default List<CT> subList(int fromIndex, int toIndex) {
return new BackendList<>(this, getBackendDataList().subList(fromIndex, toIndex));
}
/** Iterator on elements of a list that is able to notify the backend when
* the iterator has change the backend data.
*
* @param <CT> the type of the iterated shapes.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
class BackendIterator<CT extends Shape3D<?, ?, ?, ?, ?, ?>> implements ListIterator<CT> {
private final MultiShape3D<?, ?, CT, ?, ?, ?, ?> backend;
private final ListIterator<CT> iterator;
/**
* @param backend the associated backend.
* @param iterator the original iterator.
*/
public BackendIterator(MultiShape3D<?, ?, CT, ?, ?, ?, ?> backend, ListIterator<CT> iterator) {
assert backend != null : AssertMessages.notNullParameter();
assert iterator != null : AssertMessages.notNullParameter();
this.backend = backend;
this.iterator = iterator;
}
@Pure
@Override
public boolean hasNext() {
return this.iterator.hasNext();
}
@Override
public CT next() {
return this.iterator.next();
}
@Override
public void remove() {
this.iterator.remove();
this.backend.onBackendDataChange();
}
@Pure
@Override
public boolean hasPrevious() {
return this.iterator.hasPrevious();
}
@Override
public CT previous() {
return this.iterator.previous();
}
@Pure
@Override
public int nextIndex() {
return this.iterator.nextIndex();
}
@Pure
@Override
public int previousIndex() {
return this.iterator.previousIndex();
}
@Override
public void set(CT element) {
this.iterator.set(element);
this.backend.onBackendDataChange();
}
@Override
public void add(CT element) {
this.iterator.add(element);
this.backend.onBackendDataChange();
}
}
/** View on a list that is able to notify the backend when
* the view has change the backend data.
*
* @param <CT> the type of the shapes in the list view.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
class BackendList<CT extends Shape3D<?, ?, ?, ?, ?, ?>> implements List<CT> {
private final MultiShape3D<?, ?, CT, ?, ?, ?, ?> backend;
private final List<CT> list;
/**
* @param backend the associated backend.
* @param list the original list.
*/
public BackendList(MultiShape3D<?, ?, CT, ?, ?, ?, ?> backend, List<CT> list) {
assert backend != null : AssertMessages.notNullParameter();
assert list != null : AssertMessages.notNullParameter();
this.backend = backend;
this.list = list;
}
@Pure
@Override
public int size() {
return this.list.size();
}
@Pure
@Override
public boolean isEmpty() {
return this.list.isEmpty();
}
@Pure
@Override
public boolean contains(Object obj) {
return this.list.contains(obj);
}
@Pure
@Override
public Iterator<CT> iterator() {
return new BackendIterator<>(this.backend, this.list.listIterator());
}
@Pure
@Override
public Object[] toArray() {
return this.list.toArray();
}
@Override
public <T> T[] toArray(T[] array) {
return this.list.toArray(array);
}
@Override
public boolean add(CT element) {
if (this.list.add(element)) {
this.backend.onBackendDataChange();
return true;
}
return false;
}
@Override
public void add(int index, CT element) {
this.list.add(index, element);
this.backend.onBackendDataChange();
}
@Override
public boolean remove(Object obj) {
if (this.list.remove(obj)) {
this.backend.onBackendDataChange();
return true;
}
return false;
}
@Override
public CT remove(int index) {
final CT old = this.list.remove(index);
this.backend.onBackendDataChange();
return old;
}
@Pure
@Override
public boolean containsAll(Collection<?> collection) {
return this.list.containsAll(collection);
}
@Override
public boolean addAll(Collection<? extends CT> collection) {
if (this.list.addAll(collection)) {
this.backend.onBackendDataChange();
return true;
}
return false;
}
@Override
public boolean addAll(int index, Collection<? extends CT> collection) {
if (this.list.addAll(index, collection)) {
this.backend.onBackendDataChange();
return true;
}
return false;
}
@Override
public boolean removeAll(Collection<?> collection) {
if (this.list.removeAll(collection)) {
this.backend.onBackendDataChange();
return true;
}
return false;
}
@Override
public boolean retainAll(Collection<?> collection) {
if (this.list.retainAll(collection)) {
this.backend.onBackendDataChange();
return true;
}
return false;
}
@Override
public void clear() {
this.list.clear();
this.backend.onBackendDataChange();
}
@Pure
@Override
public CT get(int index) {
return this.list.get(index);
}
@Override
public CT set(int index, CT element) {
final CT old = this.list.set(index, element);
this.backend.onBackendDataChange();
return old;
}
@Pure
@Override
public int indexOf(Object obj) {
return this.list.indexOf(obj);
}
@Pure
@Override
public int lastIndexOf(Object obj) {
return this.list.lastIndexOf(obj);
}
@Pure
@Override
public ListIterator<CT> listIterator() {
return new BackendIterator<>(this.backend, this.list.listIterator());
}
@Override
public ListIterator<CT> listIterator(int index) {
return new BackendIterator<>(this.backend, this.list.listIterator(index));
}
@Override
public List<CT> subList(int fromIndex, int toIndex) {
return new BackendList<>(this.backend, this.list.subList(fromIndex, toIndex));
}
}
}