/* * $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.d2.ai; import java.util.ArrayList; import java.util.Iterator; import java.util.List; 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.PathWindingRule; import org.arakhne.afc.math.geometry.d2.MultiShape2D; import org.arakhne.afc.math.geometry.d2.Point2D; import org.arakhne.afc.math.geometry.d2.Transform2D; import org.arakhne.afc.math.geometry.d2.Vector2D; 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 MultiShape2ai< ST extends Shape2ai<?, ?, IE, P, V, B>, IT extends MultiShape2ai<?, ?, CT, IE, P, V, B>, CT extends Shape2ai<?, ?, IE, P, V, B>, IE extends PathElement2ai, P extends Point2D<? super P, ? super V>, V extends Vector2D<? super V, ? super P>, B extends Rectangle2ai<?, ?, IE, P, V, B>> extends Shape2ai<ST, IT, IE, P, V, B>, MultiShape2D<ST, IT, CT, PathIterator2ai<IE>, P, V, B> { @Pure @Override default boolean intersects(Circle2ai<?, ?, ?, ?, ?, ?> circle) { assert circle != null : AssertMessages.notNullParameter(); if (circle.intersects(toBoundingBox())) { for (final CT shape : getBackendDataList()) { if (shape.intersects(circle)) { return true; } } } return false; } @Pure @Override default boolean intersects(Rectangle2ai<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); if (rectangle.intersects(toBoundingBox())) { for (final CT shape : getBackendDataList()) { if (shape.intersects(rectangle)) { return true; } } } return false; } @Pure @Override default boolean intersects(Segment2ai<?, ?, ?, ?, ?, ?> 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(PathIterator2ai<?> 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(MultiShape2ai<?, ?, ?, ?, ?, ?, ?> multishape) { assert multishape != null : AssertMessages.notNullParameter(); if (multishape.toBoundingBox().intersects(toBoundingBox())) { for (final CT shape1 : getBackendDataList()) { for (final Shape2ai<?, ?, ?, ?, ?, ?> shape2 : multishape.getBackendDataList()) { if (shape1.intersects(shape2)) { return true; } } } } return false; } @Pure @Override default boolean contains(int x, int y) { if (toBoundingBox().contains(x, y)) { for (final CT shape : getBackendDataList()) { if (shape.contains(x, y)) { return true; } } } return false; } @Pure @Override default boolean contains(Rectangle2ai<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); if (rectangle.intersects(toBoundingBox())) { for (final CT shape : getBackendDataList()) { if (shape.contains(rectangle)) { return true; } } } return false; } @Override default void translate(int dx, int dy) { for (final CT shape : getBackendDataList()) { shape.translate(dx, dy); } onBackendDataChange(); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Pure @Override default ST createTransformedShape(Transform2D transform) { final MultiShape2ai 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 PathIterator2ai<IE> getPathIterator(Transform2D transform) { if (transform == null || transform.isIdentity()) { return new MultiShapePathIterator<>(getBackendDataList(), getGeomFactory()); } return new TransformedMultiShapePathIterator<>(getBackendDataList(), getGeomFactory(), transform); } /** Replies a path iterator on this shape that is replacing the * curves by line approximations. * * @return the iterator on the approximation. * @see #getPathIterator() * @see MathConstants#SPLINE_APPROXIMATION_RATIO */ @Pure default PathIterator2ai<IE> getFlatteningPathIterator() { return new Path2ai.FlatteningPathIterator<>( getPathIterator(null), MathConstants.SPLINE_APPROXIMATION_RATIO, Path2ai.DEFAULT_FLATTENING_LIMIT); } @Override default Iterator<P> getPointIterator() { return new MultiShapePointIterator<>(getBackendDataList()); } @Override default P getClosestPointTo(Rectangle2ai<?, ?, ?, ?, ?, ?> 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(Circle2ai<?, ?, ?, ?, ?, ?> 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(Segment2ai<?, ?, ?, ?, ?, ?> 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(MultiShape2ai<?, ?, ?, ?, ?, ?, ?> 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; } @Override default P getClosestPointTo(Path2ai<?, ?, ?, ?, ?, ?> 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; } /** 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 PathElement2ai> implements PathIterator2ai<IE> { /** Iterated list. */ protected final List<? extends Shape2ai<?, ?, IE, ?, ?, ?>> list; private final GeomFactory2ai<IE, ?, ?, ?> factory; private Iterator<? extends Shape2ai<?, ?, IE, ?, ?, ?>> shapesIterator; private PathIterator2ai<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 Shape2ai<?, ?, IE, ?, ?, ?>> list, GeomFactory2ai<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 Shape2ai<?, ?, 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 PathIterator2ai<IE> getPathIteratorFrom(Shape2ai<?, ?, 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 GeomFactory2ai<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 PathElement2ai> extends AbstractMultiShapePathIterator<IE> { /** * @param list the list of the shapes to iterate on. * @param factory the factory of path elements. */ public MultiShapePathIterator(List<? extends Shape2ai<?, ?, IE, ?, ?, ?>> list, GeomFactory2ai<IE, ?, ?, ?> factory) { super(list, factory); delayedInit(list); } @Override public PathIterator2ai<IE> restartIterations() { return new MultiShapePathIterator<>(this.list, getGeomFactory()); } @Override protected PathIterator2ai<IE> getPathIteratorFrom(Shape2ai<?, ?, 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 PathElement2ai> extends AbstractMultiShapePathIterator<IE> { private final Transform2D 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 Shape2ai<?, ?, IE, ?, ?, ?>> list, GeomFactory2ai<IE, ?, ?, ?> factory, Transform2D transform) { super(list, factory); assert transform != null : AssertMessages.notNullParameter(2); this.transform = transform; delayedInit(list); } @Override public PathIterator2ai<IE> restartIterations() { return new TransformedMultiShapePathIterator<>(this.list, getGeomFactory(), this.transform); } @Override protected PathIterator2ai<IE> getPathIteratorFrom(Shape2ai<?, ?, 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 Point2D<? super P, ? super V>, V extends Vector2D<? super V, ? super P>> implements Iterator<P> { private final Iterator<? extends Shape2ai<?, ?, ?, P, V, ?>> elements; private Iterator<P> currentIterator; private P next; /** * @param list the list of the shapes to iterate on. * */ public MultiShapePointIterator(List<? extends Shape2ai<?, ?, ?, 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 : AssertMessages.notNullParameter(); final P point = this.next; searchNext(); return point; } } }