/*
* $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.afp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
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 MultiShape3afp<
ST extends Shape3afp<?, ?, IE, P, V, B>,
IT extends MultiShape3afp<?, ?, CT, IE, P, V, B>,
CT extends Shape3afp<?, ?, IE, P, V, B>,
IE extends PathElement3afp,
P extends Point3D<? super P, ? super V>,
V extends Vector3D<? super V, ? super P>,
B extends RectangularPrism3afp<?, ?, IE, P, V, B>>
extends Shape3afp<ST, IT, IE, P, V, B>,
MultiShape3D<ST, IT, CT, PathIterator3afp<IE>, P, V, B> {
@Pure
@Override
default boolean intersects(Sphere3afp<?, ?, ?, ?, ?, ?> 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(RectangularPrism3afp<?, ?, ?, ?, ?, ?> prism) {
assert prism != null : AssertMessages.notNullParameter();
if (prism.intersects(toBoundingBox())) {
for (final CT shape : getBackendDataList()) {
if (shape.intersects(prism)) {
return true;
}
}
}
return false;
}
@Pure
@Override
default boolean intersects(Segment3afp<?, ?, ?, ?, ?, ?> 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(PathIterator3afp<?> 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(MultiShape3afp<?, ?, ?, ?, ?, ?, ?> multishape) {
assert multishape != null : AssertMessages.notNullParameter();
if (multishape.toBoundingBox().intersects(toBoundingBox())) {
for (final CT shape1 : getBackendDataList()) {
for (final Shape3afp<?, ?, ?, ?, ?, ?> shape2 : multishape.getBackendDataList()) {
if (shape1.intersects(shape2)) {
return true;
}
}
}
}
return false;
}
@Pure
@Override
default boolean contains(double x, double y, double 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(RectangularPrism3afp<?, ?, ?, ?, ?, ?> 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(double dx, double dy, double dz) {
for (final CT shape : getBackendDataList()) {
shape.translate(dx, dy, dz);
}
onBackendDataChange();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Pure
@Override
default ST createTransformedShape(Transform3D transform) {
final MultiShape3afp 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 PathIterator3afp<IE> getPathIterator(Transform3D transform) {
if (transform == null || transform.isIdentity()) {
return new MultiShapePathIterator<>(getBackendDataList(), getGeomFactory());
}
return new TransformedMultiShapePathIterator<>(getBackendDataList(), getGeomFactory(), transform);
}
@Override
default P getClosestPointTo(Sphere3afp<?, ?, ?, ?, ?, ?> circle) {
assert circle != null : AssertMessages.notNullParameter();
double min = Double.POSITIVE_INFINITY;
final P closest = getGeomFactory().newPoint();
P point;
double dist;
for (final CT innerShape : getBackendDataList()) {
point = innerShape.getClosestPointTo(circle);
dist = circle.getDistanceSquared(point);
if (dist < min) {
min = dist;
closest.set(point);
}
}
return closest;
}
@Override
default P getClosestPointTo(Segment3afp<?, ?, ?, ?, ?, ?> segment) {
assert segment != null : AssertMessages.notNullParameter();
double min = Double.POSITIVE_INFINITY;
final P closest = getGeomFactory().newPoint();
P point;
double dist;
for (final CT innerShape : getBackendDataList()) {
point = innerShape.getClosestPointTo(segment);
dist = segment.getDistanceSquared(point);
if (dist < min) {
min = dist;
closest.set(point);
}
}
return closest;
}
@Override
default P getClosestPointTo(RectangularPrism3afp<?, ?, ?, ?, ?, ?> rectangle) {
assert rectangle != null : AssertMessages.notNullParameter();
double min = Double.POSITIVE_INFINITY;
final P closest = getGeomFactory().newPoint();
P point;
double dist;
for (final CT innerShape : getBackendDataList()) {
point = innerShape.getClosestPointTo(rectangle);
dist = rectangle.getDistanceSquared(point);
if (dist < min) {
min = dist;
closest.set(point);
}
}
return closest;
}
@Override
default P getClosestPointTo(Path3afp<?, ?, ?, ?, ?, ?> path) {
assert path != null : AssertMessages.notNullParameter();
double min = Double.POSITIVE_INFINITY;
final P closest = getGeomFactory().newPoint();
P point;
double dist;
for (final CT innerShape : getBackendDataList()) {
point = innerShape.getClosestPointTo(path);
dist = path.getDistanceSquared(point);
if (dist < min) {
min = dist;
closest.set(point);
}
}
return closest;
}
@Override
default P getClosestPointTo(MultiShape3afp<?, ?, ?, ?, ?, ?, ?> multishape) {
assert multishape != null : AssertMessages.notNullParameter();
double min = Double.POSITIVE_INFINITY;
final P closest = getGeomFactory().newPoint();
P point;
double dist;
for (final CT innerShape : getBackendDataList()) {
point = innerShape.getClosestPointTo(multishape);
dist = multishape.getDistanceSquared(point);
if (dist < min) {
min = dist;
closest.set(point);
}
}
return closest;
}
/** 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 PathElement3afp> implements PathIterator3afp<IE> {
/** Iterated list.
*/
protected final List<? extends Shape3afp<?, ?, IE, ?, ?, ?>> list;
private final GeomFactory3afp<IE, ?, ?, ?> factory;
private Iterator<? extends Shape3afp<?, ?, IE, ?, ?, ?>> shapesIterator;
private PathIterator3afp<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 Shape3afp<?, ?, IE, ?, ?, ?>> list,
GeomFactory3afp<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 Shape3afp<?, ?, 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 PathIterator3afp<IE> getPathIteratorFrom(Shape3afp<?, ?, IE, ?, ?, ?> shape);
@Override
public boolean hasNext() {
return this.next != null;
}
@Override
public IE next() {
assert this.next != null;
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 GeomFactory3afp<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 PathElement3afp> extends AbstractMultiShapePathIterator<IE> {
/**
* @param list the list of the shapes to iterate on.
* @param factory the factory of path elements.
*/
public MultiShapePathIterator(List<? extends Shape3afp<?, ?, IE, ?, ?, ?>> list,
GeomFactory3afp<IE, ?, ?, ?> factory) {
super(list, factory);
delayedInit(list);
}
@Override
public PathIterator3afp<IE> restartIterations() {
return new MultiShapePathIterator<>(this.list, getGeomFactory());
}
@Override
protected PathIterator3afp<IE> getPathIteratorFrom(Shape3afp<?, ?, 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 PathElement3afp> 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 Shape3afp<?, ?, IE, ?, ?, ?>> list,
GeomFactory3afp<IE, ?, ?, ?> factory, Transform3D transform) {
super(list, factory);
assert transform != null : AssertMessages.notNullParameter(2);
this.transform = transform;
delayedInit(list);
}
@Override
public PathIterator3afp<IE> restartIterations() {
return new TransformedMultiShapePathIterator<>(this.list, getGeomFactory(), this.transform);
}
@Override
protected PathIterator3afp<IE> getPathIteratorFrom(Shape3afp<?, ?, IE, ?, ?, ?> shape) {
return shape.getPathIterator(this.transform);
}
}
}