/*
* $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.dfx;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.SimpleObjectProperty;
import org.eclipse.xtext.xbase.lib.Pure;
import org.arakhne.afc.math.geometry.MathFXAttributeNames;
import org.arakhne.afc.math.geometry.d3.Point3D;
import org.arakhne.afc.math.geometry.d3.afp.RectangularPrism3afp;
import org.arakhne.afc.vmutil.asserts.AssertMessages;
/** Rectangular Prism with 3 double precision floating-point FX properties.
*
* @author $Author: sgalland$
* @author $Author: olamotte$
* @author $Author: hjaffali$
* @author $Author: tpiotrow$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
public class RectangularPrism3dfx extends AbstractShape3dfx<RectangularPrism3dfx> implements
RectangularPrism3afp<Shape3dfx<?>, RectangularPrism3dfx, PathElement3dfx, Point3dfx, Vector3dfx, RectangularPrism3dfx> {
private static final long serialVersionUID = -1393290109630714626L;
private Point3dfx min = new Point3dfx();
private Point3dfx max = new Point3dfx();
/** width property.
*/
private ReadOnlyDoubleWrapper width;
/** height property.
*/
private ReadOnlyDoubleWrapper height;
/** height property.
*/
private ReadOnlyDoubleWrapper depth;
/** Construct an empty rectangular prism.
*/
public RectangularPrism3dfx() {
addListeners();
}
/** Construct a rectangular prism with the given minimum and maximum corners.
* @param min is the min corner of the rectangular prism.
* @param max is the max corner of the rectangular prism.
*/
public RectangularPrism3dfx(Point3D<?, ?> min, Point3D<?, ?> max) {
assert min != null : AssertMessages.notNullParameter(0);
assert max != null : AssertMessages.notNullParameter(1);
setFromCorners(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ());
}
/** Construct a recatngular prism by setting the minimum and maximum corners.
* @param min is the point to set as the min corner of the rectangular prism.
* @param max is the point to set as the max corner of the rectangular prism.
*/
public RectangularPrism3dfx(Point3dfx min, Point3dfx max) {
assert min != null : AssertMessages.notNullParameter(0);
assert max != null : AssertMessages.notNullParameter(1);
this.max = max;
this.min = min;
}
/** Construct a rectangle.
* @param x x coordinate of the minimum corner.
* @param y y coordinate of the minimum corner.
* @param z z coordinate of the minimum corner.
* @param width width of the rectangle.
* @param height height of the rectangle.
* @param depth depth of the rectangle.
*/
@SuppressWarnings("checkstyle:magicnumber")
public RectangularPrism3dfx(double x, double y, double z, double width, double height, double depth) {
assert width >= 0. : AssertMessages.positiveOrZeroParameter(3);
assert height >= 0. : AssertMessages.positiveOrZeroParameter(4);
assert depth >= 0. : AssertMessages.positiveOrZeroParameter(5);
setFromCorners(x, y, z, x + width, y + height, z + depth);
}
/** Constructor by copy.
* @param rectangularPrism the shape to copy.
*/
public RectangularPrism3dfx(RectangularPrism3afp<?, ?, ?, ?, ?, ?> rectangularPrism) {
assert rectangularPrism != null : AssertMessages.notNullParameter();
setFromCorners(rectangularPrism.getMinX(), rectangularPrism.getMinY(), rectangularPrism.getMinZ(),
rectangularPrism.getMaxX(), rectangularPrism.getMaxY(), rectangularPrism.getMaxZ());
}
/** Constructor by setting.
* @param rectangularPrism the shape to set.
*/
public RectangularPrism3dfx(RectangularPrism3dfx rectangularPrism) {
assert rectangularPrism != null : AssertMessages.notNullParameter();
this.min = rectangularPrism.min;
this.max = rectangularPrism.max;
}
@Override
public RectangularPrism3dfx clone() {
final RectangularPrism3dfx clone = super.clone();
if (clone.min != null) {
clone.min = null;
clone.min = this.min.clone();
}
if (clone.max != null) {
clone.max = null;
clone.max = this.max.clone();
}
clone.width = null;
clone.height = null;
clone.depth = null;
return clone;
}
@Pure
@Override
public int hashCode() {
int bits = 1;
bits = 31 * bits + Double.hashCode(getMinX());
bits = 31 * bits + Double.hashCode(getMinY());
bits = 31 * bits + Double.hashCode(getMinZ());
bits = 31 * bits + Double.hashCode(getMaxX());
bits = 31 * bits + Double.hashCode(getMaxY());
bits = 31 * bits + Double.hashCode(getMaxZ());
return bits ^ (bits >> 31);
}
@Override
public void setFromCorners(double x1, double y1, double z1, double x2, double y2, double z2) {
if (x1 <= x2) {
minXProperty().set(x1);
maxXProperty().set(x2);
} else {
minXProperty().set(x2);
maxXProperty().set(x1);
}
if (y1 <= y2) {
minYProperty().set(y1);
maxYProperty().set(y2);
} else {
minYProperty().set(y2);
maxYProperty().set(y1);
}
if (z1 <= z2) {
minZProperty().set(z1);
maxZProperty().set(z2);
} else {
minZProperty().set(z2);
maxZProperty().set(z1);
}
addListeners();
}
/**
* Add a listener to the point properties to observe correct min-max behavior.
*/
private void addListeners() {
this.min.xProperty().addListener((observable, oldValue, nValue) -> {
final double currentMin = nValue.doubleValue();
final double currentMax = getMaxX();
if (currentMax < currentMin) {
// min-max constrain is broken
maxXProperty().set(currentMin);
}
});
this.min.yProperty().addListener((observable, oValue, nValue) -> {
final double currentMin = nValue.doubleValue();
final double currentMax = getMaxY();
if (currentMax < currentMin) {
// min-max constrain is broken
maxYProperty().set(currentMin);
}
});
this.min.zProperty().addListener((observable, oValue, nValue) -> {
final double currentMin = nValue.doubleValue();
final double currentMax = getMaxZ();
if (currentMax < currentMin) {
// min-max constrain is broken
maxZProperty().set(currentMin);
}
});
this.max.xProperty().addListener((observable, oValue, nValue) -> {
final double currentMax = nValue.doubleValue();
final double currentMin = getMinX();
if (currentMax < currentMin) {
// min-max constrain is broken
minXProperty().set(currentMax);
}
});
this.max.yProperty().addListener((observable, oValue, nValue) -> {
final double currentMax = nValue.doubleValue();
final double currentMin = getMinY();
if (currentMax < currentMin) {
// min-max constrain is broken
minYProperty().set(currentMax);
}
});
this.max.zProperty().addListener((observable, oValue, nValue) -> {
final double currentMax = nValue.doubleValue();
final double currentMin = getMinZ();
if (currentMax < currentMin) {
// min-max constrain is broken
minZProperty().set(currentMax);
}
});
}
@Pure
@Override
public double getMinX() {
return this.min.getX();
}
@Override
public void setMinX(double x) {
minXProperty().set(x);
}
/** Replies the property that is the minimum x coordinate of the box.
*
* @return the minX property.
*/
@Pure
public DoubleProperty minXProperty() {
return this.min.xProperty();
}
@Pure
@Override
public double getMaxX() {
return this.max.getX();
}
@Override
public void setMaxX(double x) {
maxXProperty().set(x);
}
/** Replies the property that is the maximum x coordinate of the box.
*
* @return the maxX property.
*/
@Pure
public DoubleProperty maxXProperty() {
return this.max.xProperty();
}
@Pure
@Override
public double getMinY() {
return this.min.getY();
}
@Override
public void setMinY(double y) {
minYProperty().set(y);
}
/** Replies the property that is the minimum y coordinate of the box.
*
* @return the minY property.
*/
@Pure
public DoubleProperty minYProperty() {
return this.min.yProperty();
}
@Pure
@Override
public double getMaxY() {
return this.max.getY();
}
@Override
public void setMaxY(double y) {
maxYProperty().set(y);
}
/** Replies the property that is the maximum y coordinate of the box.
*
* @return the maxY property.
*/
@Pure
public DoubleProperty maxYProperty() {
return this.max.yProperty();
}
@Pure
@Override
public double getMinZ() {
return this.min.getZ();
}
@Override
public void setMinZ(double z) {
minZProperty().set(z);
}
/** Replies the property that is the minimum z coordinate of the box.
*
* @return the minZ property.
*/
@Pure
public DoubleProperty minZProperty() {
return this.min.zProperty();
}
@Pure
@Override
public double getMaxZ() {
return this.max.getZ();
}
@Override
public void setMaxZ(double z) {
maxZProperty().set(z);
}
/** Replies the property that is the maximum z coordinate of the box.
*
* @return the maxZ property.
*/
@Pure
public DoubleProperty maxZProperty() {
return this.max.zProperty();
}
@Override
public double getWidth() {
return widthProperty().get();
}
/** Replies the property that is the width of the box.
*
* @return the width property.
*/
@Pure
public DoubleProperty widthProperty() {
if (this.width == null) {
this.width = new ReadOnlyDoubleWrapper(this, MathFXAttributeNames.WIDTH);
this.width.bind(Bindings.subtract(maxXProperty(), minXProperty()));
}
return this.width;
}
@Override
public double getHeight() {
return heightProperty().get();
}
/** Replies the property that is the height of the box.
*
* @return the height property.
*/
@Pure
public DoubleProperty heightProperty() {
if (this.height == null) {
this.height = new ReadOnlyDoubleWrapper(this, MathFXAttributeNames.HEIGHT);
this.height.bind(Bindings.subtract(maxYProperty(), minYProperty()));
}
return this.height;
}
@Override
public double getDepth() {
return depthProperty().get();
}
/** Replies the property that is the depth of the box.
*
* @return the depth property.
*/
@Pure
public DoubleProperty depthProperty() {
if (this.depth == null) {
this.depth = new ReadOnlyDoubleWrapper(this, MathFXAttributeNames.DEPTH);
this.depth.bind(Bindings.subtract(maxZProperty(), minZProperty()));
}
return this.depth;
}
@Override
public ObjectProperty<RectangularPrism3dfx> boundingBoxProperty() {
if (this.boundingBox == null) {
this.boundingBox = new SimpleObjectProperty<>(this, MathFXAttributeNames.BOUNDING_BOX);
this.boundingBox.bind(Bindings.createObjectBinding(() ->
toBoundingBox(),
minXProperty(), minYProperty(), minZProperty(), widthProperty(), heightProperty(), depthProperty()));
}
return this.boundingBox;
}
}