/* * $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.Iterator; 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.afp.RectangularPrism3afp; import org.arakhne.afc.vmutil.asserts.AssertMessages; /** 3D shape with 3d integer 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 Shape3ai< ST extends Shape3ai<?, ?, IE, P, V, B>, IT 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 Shape3D<ST, IT, PathIterator3ai<IE>, P, V, B> { /** Replies an iterator on the points covered by the perimeter of this shape. * * <p>The implementation of the iterator depends on the shape type. * There is no warranty about the order of the points. * * @return an iterator on the points that are located at the perimeter of the shape. */ @Pure Iterator<P> getPointIterator(); @Pure @Override default boolean contains(Point3D<?, ?> point) { return contains(point.ix(), point.iy(), point.iz()); } /** 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(int x, int y, int z); /** Replies if the given rectangle is inside this shape. * * @param box the rectangle to test. * @return <code>true</code> if the given box is inside the shape. */ @Pure boolean contains(RectangularPrism3ai<?, ?, ?, ?, ?, ?> box); @Pure @Unefficient @Override default boolean contains(Shape3D<?, ?, ?, ?, ?, ?> shape) { assert shape != null : AssertMessages.notNullParameter(); if (isEmpty()) { return false; } if (shape instanceof RectangularPrism3ai) { return contains((RectangularPrism3ai<?, ?, ?, ?, ?, ?>) shape); } final PathIterator3ai<?> iterator = getPathIterator(); final int crossings; if (shape instanceof Sphere3ai) { final Sphere3ai<?, ?, ?, ?, ?, ?> circle = (Sphere3ai<?, ?, ?, ?, ?, ?>) shape; crossings = Path3ai.computeCrossingsFromSphere( 0, iterator, circle.getX(), circle.getY(), circle.getZ(), circle.getRadius(), CrossingComputationType.STANDARD); } else if (shape instanceof Segment3ai) { final Segment3ai<?, ?, ?, ?, ?, ?> segment = (Segment3ai<?, ?, ?, ?, ?, ?>) shape; crossings = Path3ai.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 int minX; final int minY; final int minZ; final int maxX; final int maxY; final int maxZ; final Shape3D<?, ?, ?, ?, ?, ?> originalBounds = shape.toBoundingBox(); if (originalBounds instanceof RectangularPrism3afp) { final RectangularPrism3afp<?, ?, ?, ?, ?, ?> rect = (RectangularPrism3afp<?, ?, ?, ?, ?, ?>) originalBounds; minX = (int) Math.round(rect.getMinX()); minY = (int) Math.round(rect.getMinY()); minZ = (int) Math.round(rect.getMinZ()); maxX = (int) Math.round(rect.getMaxX()); maxY = (int) Math.round(rect.getMaxY()); maxZ = (int) Math.round(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 PathIterator3ai<?> shapePathIterator = iterator.getGeomFactory().convert(shape.getPathIterator()); crossings = Path3ai.computeCrossingsFromPath( 0, iterator, new BasicPathShadow3ai(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; } @Pure @Override default void translate(Vector3D<?, ?> vector) { translate(vector.ix(), vector.iy(), vector.iz()); } /** Translate the shape. * * @param dx x translation. * @param dy y translation. * @param dz z translation. */ void translate(int dx, int dy, int dz); @Pure @Override default B toBoundingBox() { final B box = getGeomFactory().newBox(); toBoundingBox(box); return box; } @Pure @Unefficient @Override default boolean intersects(Shape3D<?, ?, ?, ?, ?, ?> shape) { if (shape instanceof Sphere3ai) { return intersects((Sphere3ai<?, ?, ?, ?, ?, ?>) shape); } if (shape instanceof Path3ai) { return intersects((Path3ai<?, ?, ?, ?, ?, ?>) shape); } if (shape instanceof PathIterator3ai) { return intersects((PathIterator3ai<?>) shape); } if (shape instanceof RectangularPrism3ai) { return intersects((RectangularPrism3ai<?, ?, ?, ?, ?, ?>) shape); } if (shape instanceof Segment3ai) { return intersects((Segment3ai<?, ?, ?, ?, ?, ?>) shape); } return intersects(getPathIterator()); } /** Replies if this shape is intersecting the given rectangular prism. * * @param rectangularPrism the rectangular prism. * @return <code>true</code> if this shape is intersecting the given shape; * <code>false</code> if there is no intersection. */ @Pure boolean intersects(RectangularPrism3ai<?, ?, ?, ?, ?, ?> rectangularPrism); /** Replies if this shape is intersecting the given sphere. * * @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(Sphere3ai<?, ?, ?, ?, ?, ?> sphere); /** Replies if this shape is intersecting the given segment. * * @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(Segment3ai<?, ?, ?, ?, ?, ?> segment); /** 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(MultiShape3ai<?, ?, ?, ?, ?, ?, ?> multishape); /** Replies if this shape is intersecting the given path. * * @param path the path * @return <code>true</code> if this shape is intersecting the given shape; * <code>false</code> if there is no intersection. */ @Pure default boolean intersects(Path3ai<?, ?, ?, ?, ?, ?> path) { return intersects(path.getPathIterator()); } /** Replies if this shape is intersecting the path described by the given iterator. * * @param pathIterator the path Iterator * @return <code>true</code> if this shape is intersecting the given shape; * <code>false</code> if there is no intersection. */ @Pure boolean intersects(PathIterator3ai<?> pathIterator); @Pure @Unefficient @Override default double getDistanceSquared(Shape3D<?, ?, ?, ?, ?, ?> shape) { if (shape instanceof Sphere3ai) { return getDistanceSquared((Sphere3ai<?, ?, ?, ?, ?, ?>) shape); } if (shape instanceof Path3ai) { return getDistanceSquared((Path3ai<?, ?, ?, ?, ?, ?>) shape); } if (shape instanceof RectangularPrism3ai) { return getDistanceSquared((RectangularPrism3ai<?, ?, ?, ?, ?, ?>) shape); } if (shape instanceof Segment3ai) { return getDistanceSquared((Segment3ai<?, ?, ?, ?, ?, ?>) shape); } throw new IllegalArgumentException(); } /** 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(RectangularPrism3ai<?, ?, ?, ?, ?, ?> rectangularPrism) { assert rectangularPrism != null : AssertMessages.notNullParameter(); return rectangularPrism.getDistanceSquared(getClosestPointTo(rectangularPrism)); } /** 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(Sphere3ai<?, ?, ?, ?, ?, ?> sphere) { assert sphere != null : AssertMessages.notNullParameter(); return sphere.getDistanceSquared(getClosestPointTo(sphere)); } /** 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(Segment3ai<?, ?, ?, ?, ?, ?> segment) { assert segment != null : AssertMessages.notNullParameter(); return segment.getDistanceSquared(getClosestPointTo(segment)); } /** 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(MultiShape3ai<?, ?, ?, ?, ?, ?, ?> multishape) { assert multishape != null : AssertMessages.notNullParameter(); double minDist = Double.POSITIVE_INFINITY; double dist; for (final Shape3ai<?, ?, ?, ?, ?, ?> shape : multishape) { dist = getDistanceSquared(shape); if (dist < minDist) { minDist = dist; } } return minDist; } /** 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(Path3ai<?, ?, ?, ?, ?, ?> path) { assert path != null : AssertMessages.notNullParameter(); return path.getDistanceSquared(getClosestPointTo(path)); } @Pure @Unefficient @Override default P getClosestPointTo(Shape3D<?, ?, ?, ?, ?, ?> shape) { if (shape instanceof Sphere3ai) { return getClosestPointTo((Sphere3ai<?, ?, ?, ?, ?, ?>) shape); } if (shape instanceof MultiShape3ai) { return getClosestPointTo((MultiShape3ai<?, ?, ?, ?, ?, ?, ?>) shape); } if (shape instanceof Path3ai) { return getClosestPointTo((Path3ai<?, ?, ?, ?, ?, ?>) shape); } if (shape instanceof RectangularPrism3ai) { return getClosestPointTo((RectangularPrism3ai<?, ?, ?, ?, ?, ?>) shape); } if (shape instanceof Segment3ai) { return getClosestPointTo((Segment3ai<?, ?, ?, ?, ?, ?>) shape); } throw new IllegalArgumentException(); } /** Replies the closest point on this shape to the given rectangle. * * @param rectangle the rectangle. * @return the closest point on this shape to the given shape; or the point * if the point is in this shape. */ @Pure P getClosestPointTo(RectangularPrism3ai<?, ?, ?, ?, ?, ?> rectangle); /** Replies the closest point on this shape to the given rectangle. * * @param circle the circle. * @return the closest point on this shape to the given shape; or the point * if the point is in this shape. */ @Pure P getClosestPointTo(Sphere3ai<?, ?, ?, ?, ?, ?> circle); /** Replies the closest point on this shape to the given rectangle. * * @param segment the segment. * @return the closest point on this shape to the given shape; or the point * if the point is in this shape. */ @Pure P getClosestPointTo(Segment3ai<?, ?, ?, ?, ?, ?> segment); /** Replies the closest point on this shape to the given rectangle. * * @param multishape the multishape. * @return the closest point on this shape to the given shape; or the point * if the point is in this shape. */ @Pure default P getClosestPointTo(MultiShape3ai<?, ?, ?, ?, ?, ?, ?> multishape) { assert multishape != null : AssertMessages.notNullParameter(); Shape3ai<?, ?, ?, ?, ?, ?> closest = null; double minDist = Double.POSITIVE_INFINITY; double dist; for (final Shape3ai<?, ?, ?, ?, ?, ?> shape : multishape) { dist = getDistanceSquared(shape); if (dist < minDist) { minDist = dist; closest = shape; } } if (closest == null) { return getGeomFactory().newPoint(); } return getClosestPointTo(closest); } /** Replies the closest point on this shape to the given rectangle. * * @param path the path. * @return the closest point on this shape to the given shape; or the point * if the point is in this shape. */ @Pure P getClosestPointTo(Path3ai<?, ?, ?, ?, ?, ?> path); @Override GeomFactory3ai<IE, P, V, B> getGeomFactory(); @Pure @SuppressWarnings("unchecked") @Override default ST createTransformedShape(Transform3D transform) { if (transform == null || transform.isIdentity()) { return (ST) clone(); } final PathIterator3ai<?> pi = getPathIterator(transform); final GeomFactory3ai<IE, P, V, B> factory = getGeomFactory(); final Path3ai<?, ?, ?, P, V, ?> newPath = factory.newPath(pi.getWindingRule()); PathElement3ai 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; } }