/*
* $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.ai;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.eclipse.xtext.xbase.lib.Pure;
import org.arakhne.afc.math.Unefficient;
import org.arakhne.afc.math.geometry.PathWindingRule;
import org.arakhne.afc.math.geometry.d3.MultiShape3D;
import org.arakhne.afc.math.geometry.d3.Point3D;
import org.arakhne.afc.math.geometry.d3.Transform3D;
import org.arakhne.afc.math.geometry.d3.Vector3D;
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 <IE> is the type of the path elements.
* @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$
* @since 13.0
*/
public interface MultiShape3ai<
ST extends Shape3ai<?, ?, IE, P, V, B>,
IT extends MultiShape3ai<?, ?, CT, IE, P, V, B>,
CT extends Shape3ai<?, ?, IE, P, V, B>,
IE extends PathElement3ai,
P extends Point3D<? super P, ? super V>,
V extends Vector3D<? super V, ? super P>,
B extends RectangularPrism3ai<?, ?, IE, P, V, B>>
extends Shape3ai<ST, IT, IE, P, V, B>,
MultiShape3D<ST, IT, CT, PathIterator3ai<IE>, P, V, B> {
@Pure
@Override
default boolean intersects(Sphere3ai<?, ?, ?, ?, ?, ?> sphere) {
assert sphere != null : AssertMessages.notNullParameter();
if (sphere.intersects(toBoundingBox())) {
for (final CT shape : getBackendDataList()) {
if (shape.intersects(sphere)) {
return true;
}
}
}
return false;
}
@Pure
@Override
default boolean intersects(RectangularPrism3ai<?, ?, ?, ?, ?, ?> rectangularPrism) {
assert rectangularPrism != null : AssertMessages.notNullParameter();
if (rectangularPrism.intersects(toBoundingBox())) {
for (final CT shape : getBackendDataList()) {
if (shape.intersects(rectangularPrism)) {
return true;
}
}
}
return false;
}
@Pure
@Override
default boolean intersects(Segment3ai<?, ?, ?, ?, ?, ?> segment) {
assert segment != null : AssertMessages.notNullParameter();
if (segment.intersects(toBoundingBox())) {
for (final CT shape : getBackendDataList()) {
if (shape.intersects(segment)) {
return true;
}
}
}
return false;
}
@Pure
@Override
default boolean intersects(PathIterator3ai<?> iterator) {
if (toBoundingBox().intersects(iterator)) {
for (final CT shape : getBackendDataList()) {
if (shape.intersects(iterator.restartIterations())) {
return true;
}
}
}
return false;
}
@Pure
@Override
@Unefficient
default boolean intersects(MultiShape3ai<?, ?, ?, ?, ?, ?, ?> multishape) {
assert multishape != null : AssertMessages.notNullParameter();
if (multishape.toBoundingBox().intersects(toBoundingBox())) {
for (final CT shape1 : getBackendDataList()) {
for (final Shape3ai<?, ?, ?, ?, ?, ?> shape2 : multishape.getBackendDataList()) {
if (shape1.intersects(shape2)) {
return true;
}
}
}
}
return false;
}
@Pure
@Override
default boolean contains(int x, int y, int z) {
if (toBoundingBox().contains(x, y, z)) {
for (final CT shape : getBackendDataList()) {
if (shape.contains(x, y, z)) {
return true;
}
}
}
return false;
}
@Pure
@Override
default boolean contains(RectangularPrism3ai<?, ?, ?, ?, ?, ?> rectangularPrism) {
assert rectangularPrism != null : AssertMessages.notNullParameter();
if (rectangularPrism.intersects(toBoundingBox())) {
for (final CT shape : getBackendDataList()) {
if (shape.contains(rectangularPrism)) {
return true;
}
}
}
return false;
}
@Override
default void translate(int dx, int dy, int dz) {
for (final CT shape : getBackendDataList()) {
shape.translate(dx, dy, dz);
}
onBackendDataChange();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Pure
@Override
default ST createTransformedShape(Transform3D transform) {
final MultiShape3ai multishape = getGeomFactory().newMultiShape();
for (final CT shape : getBackendDataList()) {
multishape.add(shape.createTransformedShape(transform));
}
return (ST) multishape;
}
@Unefficient
@Pure
@Override
default CT getFirstShapeIntersecting(ST shape) {
assert shape != null : AssertMessages.notNullParameter();
if (shape.intersects(toBoundingBox())) {
for (final CT innerShape : getBackendDataList()) {
if (innerShape.intersects(shape)) {
return innerShape;
}
}
}
return null;
}
@Unefficient
@Override
@Pure
default List<CT> getShapesIntersecting(ST shape) {
assert shape != null : AssertMessages.notNullParameter();
final List<CT> list = new ArrayList<>();
if (shape.intersects(toBoundingBox())) {
for (final CT subshape : getBackendDataList()) {
if (subshape.intersects(shape)) {
list.add(subshape);
}
}
}
return list;
}
@Pure
@Override
default void toBoundingBox(B box) {
assert box != null : AssertMessages.notNullParameter();
final Iterator<CT> iterator = getBackendDataList().iterator();
if (iterator.hasNext()) {
iterator.next().toBoundingBox(box);
final B subbounds = getGeomFactory().newBox();
while (iterator.hasNext()) {
final CT element = iterator.next();
element.toBoundingBox(subbounds);
box.setUnion(subbounds);
}
}
}
@Override
default PathIterator3ai<IE> getPathIterator(Transform3D transform) {
if (transform == null || transform.isIdentity()) {
return new MultiShapePathIterator<>(getBackendDataList(), getGeomFactory());
}
return new TransformedMultiShapePathIterator<>(getBackendDataList(), getGeomFactory(), transform);
}
@Override
default Iterator<P> getPointIterator() {
return new MultiShapePointIterator<>(getBackendDataList());
}
@Override
default P getClosestPointTo(RectangularPrism3ai<?, ?, ?, ?, ?, ?> rectangle) {
throw new UnsupportedOperationException();
}
@Override
default P getClosestPointTo(Sphere3ai<?, ?, ?, ?, ?, ?> circle) {
throw new UnsupportedOperationException();
}
@Override
default P getClosestPointTo(Segment3ai<?, ?, ?, ?, ?, ?> segment) {
throw new UnsupportedOperationException();
}
@Override
default P getClosestPointTo(MultiShape3ai<?, ?, ?, ?, ?, ?, ?> multishape) {
throw new UnsupportedOperationException();
}
@Override
default P getClosestPointTo(Path3ai<?, ?, ?, ?, ?, ?> path) {
throw new UnsupportedOperationException();
}
/** Abstract iterator on the path elements of the multishape.
*
* @param <IE> the type of the path elements.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
abstract class AbstractMultiShapePathIterator<IE extends PathElement3ai> implements PathIterator3ai<IE> {
/** Iterated list.
*/
protected final List<? extends Shape3ai<?, ?, IE, ?, ?, ?>> list;
private final GeomFactory3ai<IE, ?, ?, ?> factory;
private Iterator<? extends Shape3ai<?, ?, IE, ?, ?, ?>> shapesIterator;
private PathIterator3ai<IE> shapeIterator;
private IE next;
private boolean isCurved;
private boolean isPolyline;
private boolean isPolygon;
private boolean isMultiParts;
/**
* @param list the list of the shapes to iterate on.
* @param factory the factory of path elements.
*/
public AbstractMultiShapePathIterator(List<? extends Shape3ai<?, ?, IE, ?, ?, ?>> list,
GeomFactory3ai<IE, ?, ?, ?> factory) {
assert list != null : AssertMessages.notNullParameter(0);
assert factory != null : AssertMessages.notNullParameter(1);
this.list = list;
this.factory = factory;
this.shapesIterator = list.iterator();
}
/** Initialization.
*
* @param list the list to iterate on.
*/
protected void delayedInit(List<? extends Shape3ai<?, ?, IE, ?, ?, ?>> list) {
if (this.shapesIterator.hasNext()) {
this.shapeIterator = getPathIteratorFrom(this.shapesIterator.next());
searchNext();
}
this.isMultiParts = list.size() > 1
|| (this.shapeIterator != null
&& this.shapeIterator.isMultiParts());
this.isPolygon = list.size() == 1
&& this.shapeIterator != null
&& this.shapeIterator.isPolygon();
this.isPolyline = list.size() == 1
&& this.shapeIterator != null
&& this.shapeIterator.isPolyline();
this.isCurved = !list.isEmpty()
&& this.shapeIterator != null
&& this.shapeIterator.isCurved();
}
/** Replies the path iterator of the shape.
*
* @param shape the shape.
* @return the path iterator.
*/
protected abstract PathIterator3ai<IE> getPathIteratorFrom(Shape3ai<?, ?, IE, ?, ?, ?> shape);
@Override
public boolean hasNext() {
return this.next != null;
}
@Override
public IE next() {
assert this.next != null : AssertMessages.notNullParameter();
final IE elementToReturn = this.next;
searchNext();
return elementToReturn;
}
private void searchNext() {
this.next = null;
while (!this.shapeIterator.hasNext()) {
if (this.shapesIterator.hasNext()) {
this.shapeIterator = getPathIteratorFrom(this.shapesIterator.next());
this.isCurved |= this.shapeIterator.isCurved();
} else {
return;
}
}
assert this.shapeIterator != null;
assert this.shapeIterator.hasNext();
this.next = this.shapeIterator.next();
}
@Override
public PathWindingRule getWindingRule() {
return PathWindingRule.EVEN_ODD;
}
@Override
public boolean isPolyline() {
return this.isPolyline;
}
@Override
public boolean isCurved() {
return this.isCurved;
}
@Override
public boolean isMultiParts() {
return this.isMultiParts;
}
@Override
public boolean isPolygon() {
return this.isPolygon;
}
@Override
public GeomFactory3ai<IE, ?, ?, ?> getGeomFactory() {
return this.factory;
}
}
/** Iterator on the path elements of the multishape.
*
* @param <IE> the type of the path elements.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
class MultiShapePathIterator<IE extends PathElement3ai> extends AbstractMultiShapePathIterator<IE> {
/**
* @param list the list of the shapes to iterate on.
* @param factory the factory of path elements.
*/
public MultiShapePathIterator(List<? extends Shape3ai<?, ?, IE, ?, ?, ?>> list,
GeomFactory3ai<IE, ?, ?, ?> factory) {
super(list, factory);
delayedInit(list);
}
@Override
public PathIterator3ai<IE> restartIterations() {
return new MultiShapePathIterator<>(this.list, getGeomFactory());
}
@Override
protected PathIterator3ai<IE> getPathIteratorFrom(Shape3ai<?, ?, IE, ?, ?, ?> shape) {
return shape.getPathIterator();
}
}
/** Iterator on the path elements of the multishape.
*
* @param <IE> the type of the path elements.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
class TransformedMultiShapePathIterator<IE extends PathElement3ai> extends AbstractMultiShapePathIterator<IE> {
private final Transform3D transform;
/**
* @param list the list of the shapes to iterate on.
* @param factory the factory of path elements.
* @param transform the transformation to apply.
*/
public TransformedMultiShapePathIterator(List<? extends Shape3ai<?, ?, IE, ?, ?, ?>> list,
GeomFactory3ai<IE, ?, ?, ?> factory, Transform3D transform) {
super(list, factory);
assert transform != null : AssertMessages.notNullParameter();
this.transform = transform;
delayedInit(list);
}
@Override
public PathIterator3ai<IE> restartIterations() {
return new TransformedMultiShapePathIterator<>(this.list, getGeomFactory(), this.transform);
}
@Override
protected PathIterator3ai<IE> getPathIteratorFrom(Shape3ai<?, ?, IE, ?, ?, ?> shape) {
return shape.getPathIterator(this.transform);
}
}
/** Iterator on the points of the multishape.
*
* @param <P> the type of the points.
* @param <V> the type of the vectors.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
class MultiShapePointIterator<P extends Point3D<? super P, ? super V>,
V extends Vector3D<? super V, ? super P>> implements Iterator<P> {
private final Iterator<? extends Shape3ai<?, ?, ?, P, V, ?>> elements;
private Iterator<P> currentIterator;
private P next;
/**
* @param list the list of the shapes to iterate on.
*
*/
public MultiShapePointIterator(List<? extends Shape3ai<?, ?, ?, P, V, ?>> list) {
assert list != null : AssertMessages.notNullParameter();
this.elements = list.iterator();
if (this.elements.hasNext()) {
this.currentIterator = this.elements.next().getPointIterator();
searchNext();
}
}
private void searchNext() {
this.next = null;
while (true) {
if (this.currentIterator.hasNext()) {
this.next = this.currentIterator.next();
return;
} else if (this.elements.hasNext()) {
this.currentIterator = this.elements.next().getPointIterator();
} else {
return;
}
}
}
@Pure
@Override
public boolean hasNext() {
return this.next != null;
}
@Override
public P next() {
assert this.next != null : new NoSuchElementException();
final P point = this.next;
searchNext();
return point;
}
}
}