/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 1999-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.display.shape;
import java.awt.Shape;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.QuadCurve2D;
import org.geotoolkit.lang.Static;
import static java.lang.Math.*;
/**
* Static methods operating on shapes from the {@link java.awt.geom} package.
*
* @author Martin Desruisseaux (MPO, IRD, Geomatys)
* @module
*/
public final class ShapeUtilities extends Static {
/**
* Do not allow instantiation of this class.
*/
private ShapeUtilities() {
}
/**
* Returns the intersection point between two line segments. The lines do not continue
* to infinity; if the intersection do not occurs between the ending points {@linkplain
* Line2D#getP1 P1} and {@linkplain Line2D#getP2 P2} of the two line segments, then this
* method returns {@code null}.
*
* @param a The first line segment.
* @param b The second line segment.
* @return The intersection point, or {@code null} if none.
*
* @deprecated Moved to Apache SIS as {@link org.apache.sis.geometry.Shapes2D#intersectionPoint(Line2D, Line2D)}.
*/
@Deprecated
public static Point2D.Double intersectionPoint(final Line2D a, final Line2D b) {
return org.apache.sis.internal.referencing.j2d.ShapeUtilities.intersectionPoint(
a.getX1(), a.getY1(), a.getX2(), a.getY2(),
b.getX1(), b.getY1(), b.getX2(), b.getY2());
}
/**
* Returns the point on the given {@code line} segment which is closest to the given
* {@code point}. Let {@code result} be the returned point. This method guarantees
* (except for rounding errors) that:
* <p>
* <ul>
* <li>{@code result} is a point on the {@code line} segment. It is located between
* the {@linkplain Line2D#getP1 P1} and {@linkplain Line2D#getP2 P2} ending points
* of that line segment.</li>
* <li>The distance between the {@code result} point and the given {@code point} is
* the shortest distance among the set of points meeting the previous condition.
* This distance can be obtained with {@code point.distance(result)}.</li>
* </ul>
*
* @param segment The line on which to search for a point.
* @param point A point close to the given line.
* @return The nearest point on the given line.
*
* @see #colinearPoint(Line2D, Point2D, double)
*
* @deprecated Moved to Apache SIS as {@link org.apache.sis.geometry.Shapes2D#nearestColinearPoint(Line2D, Point2D)}.
*/
@Deprecated
public static Point2D.Double nearestColinearPoint(final Line2D segment, final Point2D point) {
return org.apache.sis.internal.referencing.j2d.ShapeUtilities.nearestColinearPoint(
segment.getX1(), segment.getY1(),
segment.getX2(), segment.getY2(),
point.getX(), point.getY());
}
/**
* Returns a point on the given {@code line} segment located at the given {@code distance}
* from that line. Let {@code result} be the returned point. If {@code result} is not null,
* then this method guarantees (except for rounding error) that:
* <p>
* <ul>
* <li>{@code result} is a point on the {@code line} segment. It is located between
* the {@linkplain Line2D#getP1 P1} and {@linkplain Line2D#getP2 P2} ending points
* of that line segment.</li>
* <li>The distance between the {@code result} and the given {@code point} is exactly
* equal to {@code distance}.</li>
* </ul>
* <p>
* If no result point meets those conditions, then this method returns {@code null}.
* If two result points meet those conditions, then this method returns the point
* which is the closest to {@code line.getP1()}.
*
* @param line The line on which to search for a point.
* @param point A point close to the given line.
* @param distance The distance between the given point and the point to be returned.
* @return A point on the given line located at the given distance from the given point.
*
* @see #nearestColinearPoint(Line2D, Point2D)
*
* @deprecated Moved to Apache SIS as {@link org.apache.sis.geometry.Shapes2D#colinearPoint(Line2D, Point2D, double)}.
*/
@Deprecated
public static Point2D.Double colinearPoint(Line2D line, Point2D point, double distance) {
return org.apache.sis.internal.referencing.j2d.ShapeUtilities.colinearPoint(
line.getX1(), line.getY1(), line.getX2(), line.getY2(),
point.getX(), point.getY(), distance);
}
/**
* Returns a quadratic curve passing by the 3 given points. There is an infinity of quadratic
* curves passing by 3 points. We can express the curve we are looking for as a parabolic
* equation of the form {@code y=ax²+bx+c} but where the <var>x</var> axis is not necessarily
* horizontal. The orientation of the <var>x</var> axis in the above equation is determined
* by the {@code horizontal} parameter:
* <p>
* <ul>
* <li>A value of {@code true} means that the <var>x</var> axis must be horizontal.
* The quadratic curve will then look like an ordinary parabolic curve as we see
* in mathematic school book.</li>
* <li>A value of {@code false} means that the <var>x</var> axis must be parallel to the
* line segment joining the {@code P0} and {@code P2} ending points.</li>
* </ul>
* <p>
* Note that if {@code P0.y == P2.y}, then both {@code horizontal} values produce the same
* result.
*
* @param P0 The starting point of the quadratic curve.
* @param P1 A point by which the quadratic curve must pass by.
* @param P2 The ending point of the quadratic curve.
* @param horizontal If {@code true}, the <var>x</var> axis is considered horizontal while
* computing the {@code y=ax²+bx+c} equation terms. If {@code false}, it is considered
* parallel to the line joining the {@code P0} and {@code P2} points.
* @return A quadratic curve passing by the given points. The curve starts at {@code P0} and
* ends at {@code P2}. If two points are too close or if the three points are colinear,
* then this method returns {@code null}.
*/
public static QuadCurve2D.Double fitParabol(
final Point2D P0, final Point2D P1, final Point2D P2, final boolean horizontal)
{
return org.apache.sis.internal.referencing.j2d.ShapeUtilities.fitParabol(
P0.getX(), P0.getY(),
P1.getX(), P1.getY(),
P2.getX(), P2.getY(), horizontal);
}
/**
* Returns the control point of a quadratic curve passing by the 3 given points. There is an
* infinity of quadratic curves passing by 3 points. We can express the curve we are looking
* for as a parabolic equation of the form {@code y=ax²+bx+c} but where the <var>x</var> axis
* is not necessarily horizontal. The orientation of the <var>x</var> axis in the above equation
* is determined by the {@code horizontal} parameter:
* <p>
* <ul>
* <li>A value of {@code true} means that the <var>x</var> axis must be horizontal.
* The quadratic curve will then look like an ordinary parabolic curve as we see
* in mathematic school book.</li>
* <li>A value of {@code false} means that the <var>x</var> axis must be parallel to the
* line segment joining the {@code P0} and {@code P2} ending points.</li>
* </ul>
* <p>
* Note that if {@code P0.y == P2.y}, then both {@code horizontal} values produce the same result.
*
* @param P0 The starting point of the quadratic curve.
* @param P1 A point by which the quadratic curve must pass by.
* @param P2 The ending point of the quadratic curve.
* @param horizontal If {@code true}, the <var>x</var> axis is considered horizontal while
* computing the {@code y=ax²+bx+c} equation terms. If {@code false}, it is considered
* parallel to the line joining the {@code P0} and {@code P2} points.
* @return The control point of a quadratic curve passing by the given points. The curve
* starts at {@code P0} and ends at {@code P2}. If two points are too
* close or if the three points are colinear, then this method returns {@code null}.
*
* @since 3.20
*/
public static Point2D.Double parabolicControlPoint(
final Point2D P0, final Point2D P1, final Point2D P2, final boolean horizontal)
{
return org.apache.sis.internal.referencing.j2d.ShapeUtilities.parabolicControlPoint(
P0.getX(), P0.getY(),
P1.getX(), P1.getY(),
P2.getX(), P2.getY(), horizontal);
}
/**
* Returns a circle passing by the 3 given points.
*
* @param P1 The first point.
* @param P2 The second point.
* @param P3 The third point.
* @return A circle passing by the given points.
*
* @deprecated Moved to Apache SIS as {@link org.apache.sis.geometry.Shapes2D#circle(Point2D, Point2D, Point2D)
*/
@Deprecated
public static Ellipse2D.Double fitCircle(final Point2D P1, final Point2D P2, final Point2D P3) {
final Point2D center = org.apache.sis.internal.referencing.j2d.ShapeUtilities.circleCentre(
P1.getX(), P1.getY(),
P2.getX(), P2.getY(),
P3.getX(), P3.getY());
final double radius = center.distance(P2);
return new Ellipse2D.Double(center.getX() - radius,
center.getY() - radius,
2*radius, 2*radius);
}
/**
* Attempts to replace an arbitrary shape by one of the standard Java2D constructs.
* For example if the given {@code path} is a {@link Path2D} containing only a single
* line or a quadratic curve, then this method replaces it by a {@link Line2D} or
* {@link QuadCurve2D} object respectively.
*
* @param path The shape to replace by a simpler Java2D construct. This is generally
* an instance of {@link Path2D}, but doesn't have to.
* @return A simpler Java construct, or {@code path} if no better construct is proposed.
*/
public static Shape toPrimitive(final Shape path) {
return org.apache.sis.internal.referencing.j2d.ShapeUtilities.toPrimitive(path);
}
/**
* Returns a suggested value for the {@code flatness} argument in
* {@link Shape#getPathIterator(AffineTransform,double)} for the specified shape.
*
* @param shape The shape for which to compute a flatness factor.
* @return The suggested flatness factor.
*/
public static double getFlatness(final Shape shape) {
final Rectangle2D bounds = shape.getBounds2D();
final double dx = bounds.getWidth();
final double dy = bounds.getHeight();
return max(0.025 * min(dx, dy),
0.001 * max(dx, dy));
}
}