/* * $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.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.d2.Point2D; import org.arakhne.afc.math.geometry.d2.afp.Rectangle2afp; import org.arakhne.afc.vmutil.asserts.AssertMessages; /** Rectangle with 2 double precision floating-point FX properties. * * @author $Author: sgalland$ * @author $Author: olamotte$ * @author $Author: hjaffali$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ public class Rectangle2dfx extends AbstractShape2dfx<Rectangle2dfx> implements Rectangle2afp<Shape2dfx<?>, Rectangle2dfx, PathElement2dfx, Point2dfx, Vector2dfx, Rectangle2dfx> { private static final long serialVersionUID = -1393290109630714626L; private Point2dfx min = new Point2dfx(); private Point2dfx max = new Point2dfx(); /** width property. */ private ReadOnlyDoubleWrapper width; /** height property. */ private ReadOnlyDoubleWrapper height; /** Construct an empty rectangle. */ public Rectangle2dfx() { addListeners(); } /** Construct a rectangle with the given minimum and maximum corners. * @param min is the min corner of the rectangle. * @param max is the max corner of the rectangle. */ public Rectangle2dfx(Point2D<?, ?> min, Point2D<?, ?> max) { assert min != null : AssertMessages.notNullParameter(0); assert max != null : AssertMessages.notNullParameter(1); setFromCorners(min.getX(), min.getY(), max.getX(), max.getY()); } /** Construct a rectangle by setting the given minimum and maximum corners. * @param min is the min corner of the rectangle. * @param max is the max corner of the rectangle. */ public Rectangle2dfx(Point2dfx min, Point2dfx max) { assert min != null : AssertMessages.notNullParameter(0); assert max != null : AssertMessages.notNullParameter(1); this.min = min; this.max = max; } /** Construct a rectangle. * @param x x coordinate of the minimum corner. * @param y y coordinate of the minimum corner. * @param width width of the rectangle. * @param height height of the rectangle. */ public Rectangle2dfx(double x, double y, double width, double height) { assert width >= 0. : AssertMessages.positiveOrZeroParameter(2); assert height >= 0. : AssertMessages.positiveOrZeroParameter(3); setFromCorners(x, y, x + width, y + height); } /** Constructor by copy. * @param rectangle the shape to copy. */ public Rectangle2dfx(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); set(rectangle.getMinX(), rectangle.getMinY(), rectangle.getMaxX(), rectangle.getMaxY()); } /** Constructor by setting. * @param rectangle the shape to set. */ public Rectangle2dfx(Rectangle2dfx rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); this.min = rectangle.min; this.max = rectangle.max; } @Override public Rectangle2dfx clone() { final Rectangle2dfx 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; 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(getMaxX()); bits = 31 * bits + Double.hashCode(getMaxY()); return bits ^ (bits >> 31); } @Override public void setFromCorners(double x1, double y1, double x2, double y2) { 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); } 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.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); } }); } @Pure @Override public double getMinX() { return this.min.getX(); } @Override public void setMinX(double x) { this.min.setX(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) { this.max.setX(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) { this.min.setY(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) { this.max.setY(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(); } @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 ObjectProperty<Rectangle2dfx> boundingBoxProperty() { if (this.boundingBox == null) { this.boundingBox = new SimpleObjectProperty<>(this, MathFXAttributeNames.BOUNDING_BOX); this.boundingBox.bind(Bindings.createObjectBinding(() -> toBoundingBox(), minXProperty(), minYProperty(), widthProperty(), heightProperty())); } return this.boundingBox; } }