/*
* $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.ifx;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import com.sun.javafx.collections.NonIterableChange.SimpleUpdateChange;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ModifiableObservableListBase;
import org.eclipse.xtext.xbase.lib.Pure;
import org.arakhne.afc.math.geometry.MathFXAttributeNames;
import org.arakhne.afc.math.geometry.d3.ai.MultiShape3ai;
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 <T> the type of the shapes inside the multishape.
* @author $Author: tpiotrow$
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
public class MultiShape3ifx<T extends Shape3ifx<?>> extends AbstractShape3ifx<MultiShape3ifx<T>> implements
MultiShape3ai<Shape3ifx<?>, MultiShape3ifx<T>, T, PathElement3ifx, Point3ifx, Vector3ifx, RectangularPrism3ifx> {
private static final long serialVersionUID = -4727279807601027239L;
private ListProperty<T> elements;
/**
* Construct an empty multishape.
*/
public MultiShape3ifx() {
//
}
/** Construct a multishape with shapes inside.
*
* @param shapes the shapes to add into the multishape.
*/
public MultiShape3ifx(@SuppressWarnings("unchecked") T... shapes) {
assert shapes != null : AssertMessages.notNullParameter();
addAll(Arrays.asList(shapes));
}
/** Construct a multishape with shapes inside.
*
* @param shapes the shapes to add into the multishape.
*/
public MultiShape3ifx(Iterable<? extends T> shapes) {
assert shapes != null : AssertMessages.notNullParameter();
for (final T element : shapes) {
add(element);
}
}
@Pure
@Override
public List<T> getBackendDataList() {
return elementsProperty().get();
}
/** Replies the property that contains all the shapes in this multishape.
*
* @return the elements property.
*/
public ListProperty<T> elementsProperty() {
if (this.elements == null) {
this.elements = new SimpleListProperty<>(this, MathFXAttributeNames.ELEMENTS,
new InternalObservableList<>());
}
return this.elements;
}
@Override
public ObjectProperty<RectangularPrism3ifx> boundingBoxProperty() {
if (this.boundingBox == null) {
this.boundingBox = new SimpleObjectProperty<>(this, MathFXAttributeNames.BOUNDING_BOX);
this.boundingBox.bind(Bindings.createObjectBinding(() -> {
final RectangularPrism3ifx box = getGeomFactory().newBox();
final RectangularPrism3ifx shapeBox = getGeomFactory().newBox();
final Iterator<T> iterator = elementsProperty().iterator();
if (iterator.hasNext()) {
iterator.next().toBoundingBox(shapeBox);
box.set(shapeBox);
while (iterator.hasNext()) {
iterator.next().toBoundingBox(shapeBox);
box.setUnion(shapeBox);
}
}
return box;
},
elementsProperty()));
}
return this.boundingBox;
}
@SuppressWarnings("unchecked")
@Override
public MultiShape3ifx<T> clone() {
final MultiShape3ifx<T> clone = super.clone();
clone.elements = null;
if (this.elements != null) {
for (final T shape : this.elements) {
clone.elementsProperty().add((T) shape.clone());
}
}
clone.boundingBox = null;
return clone;
}
@Override
public int hashCode() {
return this.elements.hashCode();
}
@Pure
@Override
public RectangularPrism3ifx toBoundingBox() {
return boundingBoxProperty().get().clone();
}
@Pure
@Override
public void toBoundingBox(RectangularPrism3ifx box) {
assert box != null : AssertMessages.notNullParameter();
box.set(boundingBoxProperty().get());
}
/** Internal list.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
private static class InternalObservableList<T extends Shape3ifx<?>> extends ModifiableObservableListBase<T>
implements InvalidationListener {
private final List<T> internalList = new ArrayList<>();
/** Construct the list.
*/
InternalObservableList() {
//
}
private void bind(Shape3ifx<?> shape) {
assert shape != null;
final ObjectProperty<RectangularPrism3ifx> property = shape.boundingBoxProperty();
property.addListener(this);
}
private void unbind(Shape3ifx<?> shape) {
assert shape != null;
final ObjectProperty<RectangularPrism3ifx> property = shape.boundingBoxProperty();
property.removeListener(this);
}
@Override
public T get(int index) {
return this.internalList.get(index);
}
@Override
public int size() {
return this.internalList.size();
}
@Override
protected void doAdd(int index, T element) {
assert element != null : AssertMessages.notNullParameter(1);
this.internalList.add(index, element);
bind(element);
}
@Override
protected T doSet(int index, T element) {
assert element != null : AssertMessages.notNullParameter(1);
final T old = this.internalList.set(index, element);
unbind(old);
bind(element);
return old;
}
@Override
protected T doRemove(int index) {
final T old = this.internalList.remove(index);
unbind(old);
return old;
}
@Override
public void invalidated(Observable observable) {
final int position = indexOf(((Property<?>) observable).getBean());
if (position >= 0) {
fireChange(new SimpleUpdateChange<>(position, this));
}
}
}
}