/*
* $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 org.eclipse.xtext.xbase.lib.Pure;
import org.arakhne.afc.math.MathConstants;
import org.arakhne.afc.math.Unefficient;
import org.arakhne.afc.math.geometry.CrossingComputationType;
import org.arakhne.afc.math.geometry.PathWindingRule;
import org.arakhne.afc.math.geometry.d3.Point3D;
import org.arakhne.afc.math.geometry.d3.Shape3D;
import org.arakhne.afc.math.geometry.d3.Transform3D;
import org.arakhne.afc.math.geometry.d3.Vector3D;
import org.arakhne.afc.math.geometry.d3.ai.RectangularPrism3ai;
import org.arakhne.afc.vmutil.asserts.AssertMessages;
/** 2D shape with 2D floating coordinates.
*
* @param <ST> is the type of the general implementation.
* @param <IT> is the type of the implementation of this shape.
* @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: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
public interface Shape3afp<
ST extends Shape3afp<?, ?, IE, P, V, B>,
IT 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 Shape3D<ST, IT, PathIterator3afp<IE>, P, V, B> {
@Pure
@Override
default boolean contains(Point3D<?, ?> point) {
assert point != null : AssertMessages.notNullParameter();
return contains(point.getX(), point.getY(), point.getZ());
}
/** Replies if the given shape is inside this shape.
*
* @param shape the shape to be inside.
* @return <code>true</code> if the given shape is inside this
* shape, otherwise <code>false</code>.
* @since 14.0
*/
@Pure
@Unefficient
@Override
default boolean contains(Shape3D<?, ?, ?, ?, ?, ?> shape) {
assert shape != null : AssertMessages.notNullParameter();
if (isEmpty()) {
return false;
}
if (shape instanceof RectangularPrism3afp) {
return contains((RectangularPrism3afp<?, ?, ?, ?, ?, ?>) shape);
}
final PathIterator3afp<?> iterator = getPathIterator();
final int crossings;
if (shape instanceof Sphere3afp) {
final Sphere3afp<?, ?, ?, ?, ?, ?> sphere = (Sphere3afp<?, ?, ?, ?, ?, ?>) shape;
crossings = Path3afp.computeCrossingsFromSphere(
0, iterator,
sphere.getCenterX(), sphere.getCenterY(), sphere.getCenterZ(), sphere.getRadius(),
CrossingComputationType.STANDARD);
} else if (shape instanceof Segment3afp) {
final Segment3afp<?, ?, ?, ?, ?, ?> segment = (Segment3afp<?, ?, ?, ?, ?, ?>) shape;
crossings = Path3afp.computeCrossingsFromSegment(
0, iterator,
segment.getX1(), segment.getY1(), segment.getZ1(),
segment.getX2(), segment.getY2(), segment.getZ2(),
CrossingComputationType.STANDARD);
} else if (!iterator.isPolygon()) {
// Only a polygon can contain another shape.
return false;
} else {
final double minX;
final double minY;
final double minZ;
final double maxX;
final double maxY;
final double maxZ;
final Shape3D<?, ?, ?, ?, ?, ?> originalBounds = shape.toBoundingBox();
if (originalBounds instanceof RectangularPrism3afp) {
final RectangularPrism3afp<?, ?, ?, ?, ?, ?> rect = (RectangularPrism3afp<?, ?, ?, ?, ?, ?>) originalBounds;
minX = rect.getMinX();
minY = rect.getMinY();
minZ = rect.getMinZ();
maxX = rect.getMaxX();
maxY = rect.getMaxY();
maxZ = rect.getMaxZ();
} else {
assert originalBounds instanceof RectangularPrism3ai;
final RectangularPrism3ai<?, ?, ?, ?, ?, ?> rect = (RectangularPrism3ai<?, ?, ?, ?, ?, ?>) originalBounds;
minX = rect.getMinX();
minY = rect.getMinY();
minZ = rect.getMinZ();
maxX = rect.getMaxX();
maxY = rect.getMaxY();
maxZ = rect.getMaxZ();
}
final PathIterator3afp<?> shapePathIterator = iterator.getGeomFactory().convert(shape.getPathIterator());
crossings = Path3afp.computeCrossingsFromPath(
0, iterator,
new BasicPathShadow3afp(shapePathIterator, minX, minY, minZ, maxX, maxY, maxZ),
CrossingComputationType.STANDARD);
}
final int mask = iterator.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2;
return crossings != MathConstants.SHAPE_INTERSECTS
&& (crossings & mask) != 0;
}
/** Replies if the given rectangular prism is inside this shape.
*
* @param rectangularPrism the rectangular prism.
* @return <code>true</code> if the given rectangle is inside this
* shape, otherwise <code>false</code>.
*/
@Pure
boolean contains(RectangularPrism3afp<?, ?, ?, ?, ?, ?> rectangularPrism);
/** Replies if the given point is inside this shape.
*
* @param x x coordinate of the point to test.
* @param y y coordinate of the point to test.
* @param z z coordinate of the point to test.
* @return <code>true</code> if the given point is inside this
* shape, otherwise <code>false</code>.
*/
@Pure
boolean contains(double x, double y, double z);
/** Translate the shape.
*
* @param dx x translation.
* @param dy y translation.
* @param dz z translation.
*/
void translate(double dx, double dy, double dz);
@Pure
@Override
default void translate(Vector3D<?, ?> vector) {
assert vector != null : AssertMessages.notNullParameter();
translate(vector.getX(), vector.getY(), vector.getZ());
}
@Pure
@Unefficient
@Override
default boolean intersects(Shape3D<?, ?, ?, ?, ?, ?> shape) {
if (shape instanceof MultiShape3afp) {
return intersects((MultiShape3afp<?, ?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof Sphere3afp) {
return intersects((Sphere3afp<?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof Path3afp) {
return intersects((Path3afp<?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof PathIterator3afp) {
return intersects((PathIterator3afp<?>) shape);
}
if (shape instanceof RectangularPrism3afp) {
return intersects((RectangularPrism3afp<?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof Segment3afp) {
return intersects((Segment3afp<?, ?, ?, ?, ?, ?>) shape);
}
return intersects(getPathIterator());
}
/** Replies if this shape is intersecting the given circle.
*
* @param sphere the sphere
* @return <code>true</code> if this shape is intersecting the given shape;
* <code>false</code> if there is no intersection.
*/
@Pure
boolean intersects(Sphere3afp<?, ?, ?, ?, ?, ?> sphere);
/** Replies if this shape is intersecting the given Prism.
*
* @param prism the prism
* @return <code>true</code> if this shape is intersecting the given shape;
* <code>false</code> if there is no intersection.
*/
@Pure
boolean intersects(RectangularPrism3afp<?, ?, ?, ?, ?, ?> prism);
/** Replies if this shape is intersecting the given line.
*
* @param segment the segment
* @return <code>true</code> if this shape is intersecting the given shape;
* <code>false</code> if there is no intersection.
*/
@Pure
boolean intersects(Segment3afp<?, ?, ?, ?, ?, ?> segment);
/** Replies if this shape is intersecting the given path.
*
* @param path the path
* @return <code>true</code> if this shape is intersecting the given path;
* <code>false</code> if there is no intersection.
*/
@Pure
default boolean intersects(Path3afp<?, ?, ?, ?, ?, ?> path) {
assert path != null : AssertMessages.notNullParameter();
return intersects(path.getPathIterator());
}
/** Replies if this shape is intersecting the shape representing the given path iterator.
*
* @param iterator the iterator
* @return <code>true</code> if this shape is intersecting the given shape;
* <code>false</code> if there is no intersection.
*/
@Pure
boolean intersects(PathIterator3afp<?> iterator);
/** Replies if this shape is intersecting the given multishape.
*
* @param multishape the multishape
* @return <code>true</code> if this shape is intersecting the given shape;
* <code>false</code> if there is no intersection.
*/
@Pure
boolean intersects(MultiShape3afp<?, ?, ?, ?, ?, ?, ?> multishape);
@Pure
@Unefficient
@Override
@SuppressWarnings({"checkstyle:returncount", "checkstyle:npathcomplexity"})
default double getDistanceSquared(Shape3D<?, ?, ?, ?, ?, ?> shape) {
if (shape instanceof Sphere3afp) {
return getDistanceSquared((Sphere3afp<?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof MultiShape3afp) {
return getDistanceSquared((MultiShape3afp<?, ?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof Path3afp) {
return getDistanceSquared((Path3afp<?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof RectangularPrism3afp) {
return getDistanceSquared((RectangularPrism3afp<?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof Segment3afp) {
return getDistanceSquared((Segment3afp<?, ?, ?, ?, ?, ?>) shape);
}
throw new IllegalArgumentException();
}
/** Replies the minimum distance between this shape and the given sphere.
*
* @param sphere the sphere.
* @return the minimum distance between the two shapes.
*/
@Pure
default double getDistanceSquared(Sphere3afp<?, ?, ?, ?, ?, ?> sphere) {
assert sphere != null : AssertMessages.notNullParameter();
return sphere.getDistanceSquared(getClosestPointTo(sphere));
}
/** Replies the minimum distance between this shape and the given rectangular prism.
*
* @param rectangularPrism the rectangular prism.
* @return the minimum distance between the two shapes.
*/
@Pure
default double getDistanceSquared(RectangularPrism3afp<?, ?, ?, ?, ?, ?> rectangularPrism) {
assert rectangularPrism != null : AssertMessages.notNullParameter();
return rectangularPrism.getDistanceSquared(getClosestPointTo(rectangularPrism));
}
/** Replies the minimum distance between this shape and the given segment.
*
* @param segment the segment.
* @return the minimum distance between the two shapes.
*/
@Pure
default double getDistanceSquared(Segment3afp<?, ?, ?, ?, ?, ?> segment) {
assert segment != null : AssertMessages.notNullParameter();
return segment.getDistanceSquared(getClosestPointTo(segment));
}
/** Replies the minimum distance between this shape and the given path.
*
* @param path the path.
* @return the minimum distance between the two shapes.
*/
@Pure
default double getDistanceSquared(Path3afp<?, ?, ?, ?, ?, ?> path) {
assert path != null : AssertMessages.notNullParameter();
return path.getDistanceSquared(getClosestPointTo(path));
}
/** Replies the minimum distance between this shape and the given multishape.
*
* @param multishape the multishape.
* @return the minimum distance between the two shapes.
*/
@Pure
default double getDistanceSquared(MultiShape3afp<?, ?, ?, ?, ?, ?, ?> multishape) {
assert multishape != null : AssertMessages.notNullParameter();
double minDist = Double.POSITIVE_INFINITY;
double dist;
for (final Shape3afp<?, ?, ?, ?, ?, ?> shape : multishape) {
dist = getDistanceSquared(shape);
if (dist < minDist) {
minDist = dist;
}
}
return minDist;
}
@Pure
@Unefficient
@Override
@SuppressWarnings({"checkstyle:returncount", "checkstyle:npathcomplexity"})
default P getClosestPointTo(Shape3D<?, ?, ?, ?, ?, ?> shape) {
if (shape instanceof Sphere3afp) {
return getClosestPointTo((Sphere3afp<?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof MultiShape3afp) {
return getClosestPointTo((MultiShape3afp<?, ?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof Path3afp) {
return getClosestPointTo((Path3afp<?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof RectangularPrism3afp) {
return getClosestPointTo((RectangularPrism3afp<?, ?, ?, ?, ?, ?>) shape);
}
if (shape instanceof Segment3afp) {
return getClosestPointTo((Segment3afp<?, ?, ?, ?, ?, ?>) shape);
}
throw new IllegalArgumentException();
}
/** Replies the closest point on this shape to the given sphere.
*
* <p>If the two shapes are intersecting, the replied point is always at the intersection
* of the two shapes. This function does not enforce the meaning of the replied point
* in the case of shape intersection. In other words, this function is warranting that
* the reply point is the either the penetration point, nor a perimeter point, nor any point
* with a specific meaning.
*
* @param sphere the sphere.
* @return the closest point on the shape; or the point itself
* if it is inside the shape.
*/
@Pure
@Unefficient
P getClosestPointTo(Sphere3afp<?, ?, ?, ?, ?, ?> sphere);
/** Replies the closest point on this shape to the given rectangular prism.
*
* <p>If the two shapes are intersecting, the replied point is always at the intersection
* of the two shapes. This function does not enforce the meaning of the replied point
* in the case of shape intersection. In other words, this function is warranting that
* the reply point is the either the penetration point, nor a perimeter point, nor any point
* with a specific meaning.
*
* @param rectangularPrism the rectangular prism.
* @return the closest point on the shape; or the point itself
* if it is inside the shape.
*/
@Pure
P getClosestPointTo(RectangularPrism3afp<?, ?, ?, ?, ?, ?> rectangularPrism);
/** Replies the closest point on this shape to the given segment.
*
* <p>If the two shapes are intersecting, the replied point is always at the intersection
* of the two shapes. This function does not enforce the meaning of the replied point
* in the case of shape intersection. In other words, this function is warranting that
* the reply point is the either the penetration point, nor a perimeter point, nor any point
* with a specific meaning.
*
* @param segment the segment.
* @return the closest point on the shape; or the point itself
* if it is inside the shape.
*/
@Pure
P getClosestPointTo(Segment3afp<?, ?, ?, ?, ?, ?> segment);
/** Replies the closest point on this shape to the given path.
*
* <p>If the two shapes are intersecting, the replied point is always at the intersection
* of the two shapes. This function does not enforce the meaning of the replied point
* in the case of shape intersection. In other words, this function is warranting that
* the reply point is the either the penetration point, nor a perimeter point, nor any point
* with a specific meaning.
*
* @param path the path.
* @return the closest point on the shape; or the point itself
* if it is inside the shape.
*/
@Pure
P getClosestPointTo(Path3afp<?, ?, ?, ?, ?, ?> path);
/** Replies the closest point on this shape to the given multishape.
*
* <p>If the two shapes are intersecting, the replied point is always at the intersection
* of the two shapes. This function does not enforce the meaning of the replied point
* in the case of shape intersection. In other words, this function is warranting that
* the reply point is the either the penetration point, nor a perimeter point, nor any point
* with a specific meaning.
*
* @param multishape the multishape.
* @return the closest point on the shape; or the point itself
* if it is inside the shape.
*/
@Pure
default P getClosestPointTo(MultiShape3afp<?, ?, ?, ?, ?, ?, ?> multishape) {
assert multishape != null : AssertMessages.notNullParameter();
Shape3afp<?, ?, ?, ?, ?, ?> closest = null;
double minDist = Double.POSITIVE_INFINITY;
double dist;
for (final Shape3afp<?, ?, ?, ?, ?, ?> shape : multishape) {
dist = getDistanceSquared(shape);
if (dist < minDist) {
minDist = dist;
closest = shape;
}
}
if (closest == null) {
return getGeomFactory().newPoint();
}
return getClosestPointTo(closest);
}
@Override
GeomFactory3afp<IE, P, V, B> getGeomFactory();
@Pure
@SuppressWarnings("unchecked")
@Override
default ST createTransformedShape(Transform3D transform) {
if (transform == null || transform.isIdentity()) {
return (ST) clone();
}
final PathIterator3afp<?> pi = getPathIterator(transform);
final GeomFactory3afp<IE, P, V, B> factory = getGeomFactory();
final Path3afp<?, ?, ?, P, V, ?> newPath = factory.newPath(pi.getWindingRule());
PathElement3afp element;
while (pi.hasNext()) {
element = pi.next();
switch (element.getType()) {
case MOVE_TO:
newPath.moveTo(element.getToX(), element.getToY(), element.getToZ());
break;
case LINE_TO:
newPath.lineTo(element.getToX(), element.getToY(), element.getToZ());
break;
case QUAD_TO:
newPath.quadTo(element.getCtrlX1(), element.getCtrlY1(), element.getCtrlZ1(), element.getToX(), element.getToY(),
element.getToZ());
break;
case CURVE_TO:
newPath.curveTo(element.getCtrlX1(), element.getCtrlY1(), element.getCtrlZ1(), element.getCtrlX2(),
element.getCtrlY2(), element.getCtrlZ2(), element.getToX(), element.getToY(), element.getToZ());
break;
case CLOSE:
newPath.closePath();
break;
case ARC_TO:
default:
}
}
return (ST) newPath;
}
@Override
default B toBoundingBox() {
final B box = getGeomFactory().newBox();
toBoundingBox(box);
return box;
}
}