use of java.awt.geom.Path2D in project sis by apache.
the class Factory method createPolyline.
/**
* Creates a path from the given coordinate values.
* Each {@link Double#NaN} coordinate value starts a new path.
* The geometry may be backed by {@code float} or {@code double} primitive type,
* depending on the type used by the given vectors.
*
* @param polygon whether to return the path as a polygon instead of polyline.
* @param dimension the number of dimensions ({@value #BIDIMENSIONAL} or {@value #TRIDIMENSIONAL}).
* @param coordinates sequence of (x,y) or (x,y,z) tuples.
* @throws UnsupportedOperationException if this operation is not implemented for the given number of dimensions.
*/
@Override
public Shape createPolyline(final boolean polygon, final int dimension, final Vector... coordinates) {
if (dimension != BIDIMENSIONAL) {
throw new UnsupportedOperationException(unsupported(dimension));
}
/*
* Computes the total length of all vectors and verifies if any vector
* requires double-precision numbers instead of single-precision.
*/
int length = 0;
boolean isFloat = true;
for (final Vector v : coordinates) {
if (v != null) {
length = Math.addExact(length, v.size());
if (isFloat) {
isFloat = v.isSinglePrecision();
}
}
}
/*
* Shortcut (for performance reason) when building a single line segment.
* Note: Point2D is not an instance of Shape, so we can not make a special case for it.
*/
length /= BIDIMENSIONAL;
if (length == 2 && coordinates.length == 1) {
final Vector v = coordinates[0];
final double x1, y1, x2, y2;
if (!Double.isNaN(x1 = v.doubleValue(0)) && !Double.isNaN(y1 = v.doubleValue(1)) && !Double.isNaN(x2 = v.doubleValue(2)) && !Double.isNaN(y2 = v.doubleValue(3))) {
final Line2D path = isFloat ? new Line2D.Float() : new Line2D.Double();
path.setLine(x1, y1, x2, y2);
return path;
}
}
/*
* General case if we could not use a shortcut.
*/
final Path2D path = createPath(isFloat, length);
boolean lineTo = false;
for (final Vector v : coordinates) {
final int size = v.size();
for (int i = 0; i < size; ) {
final double x = v.doubleValue(i++);
final double y = v.doubleValue(i++);
if (Double.isNaN(x) || Double.isNaN(y)) {
if (polygon) {
path.closePath();
}
lineTo = false;
} else if (lineTo) {
path.lineTo(x, y);
} else {
path.moveTo(x, y);
lineTo = true;
}
}
}
if (polygon) {
path.closePath();
}
return ShapeUtilities.toPrimitive(path);
}
use of java.awt.geom.Path2D in project sis by apache.
the class AffineTransforms2D method transform.
/**
* Transforms the given shape.
* This method is similar to {@link AffineTransform#createTransformedShape(Shape)} except that:
*
* <ul>
* <li>It tries to preserve the shape kind when possible. For example if the given shape
* is an instance of {@link RectangularShape} and the given transform does not involve
* rotation, then the returned shape may be some instance of the same class.</li>
* <li>It tries to recycle the given object if {@code overwrite} is {@code true}.</li>
* </ul>
*
* @param transform the affine transform to use.
* @param shape the shape to transform, or {@code null}.
* @param allowOverwrite if {@code true}, this method is allowed to overwrite {@code shape} with the
* transform result. If {@code false}, then {@code shape} is never modified.
* @return the transform of the given shape, or {@code null} if the given shape was null.
* May or may not be the same instance than the given shape.
*
* @see AffineTransform#createTransformedShape(Shape)
*/
public static Shape transform(final AffineTransform transform, Shape shape, boolean allowOverwrite) {
ArgumentChecks.ensureNonNull("transform", transform);
if (shape == null) {
return null;
}
final int type = transform.getType();
if (type == TYPE_IDENTITY) {
return shape;
}
/*
* If there is only scale, flip, quadrant rotation or translation,
* then we can optimize the transformation of rectangular shapes.
*/
if ((type & (TYPE_GENERAL_ROTATION | TYPE_GENERAL_TRANSFORM)) == 0) {
// For a Rectangle input, the output should be a rectangle as well.
if (shape instanceof Rectangle2D) {
final Rectangle2D rect = (Rectangle2D) shape;
return transform(transform, rect, allowOverwrite ? rect : null);
}
/*
* For other rectangular shapes, we restrict to cases without
* rotation or flip because we don't know if the shape is symmetric.
*/
if ((type & (TYPE_FLIP | TYPE_MASK_ROTATION)) == 0) {
if (shape instanceof RectangularShape) {
RectangularShape rect = (RectangularShape) shape;
if (!allowOverwrite) {
rect = (RectangularShape) rect.clone();
}
final Rectangle2D frame = rect.getFrame();
rect.setFrame(transform(transform, frame, frame));
return rect;
}
}
}
if (shape instanceof Path2D) {
final Path2D path = (Path2D) shape;
if (allowOverwrite) {
path.transform(transform);
} else {
shape = path.createTransformedShape(transform);
}
} else if (shape instanceof Area) {
final Area area = (Area) shape;
if (allowOverwrite) {
area.transform(transform);
} else {
shape = area.createTransformedArea(transform);
}
} else {
shape = new Path2D.Double(shape, transform);
}
return shape;
}
use of java.awt.geom.Path2D in project sis by apache.
the class GeodeticCalculator method createGeodesicPath2D.
/**
* Creates an approximation of the geodesic track from start point to end point as a Java2D object.
* The coordinates are expressed in the coordinate reference system specified at creation time.
* The approximation uses linear, quadratic or cubic Bézier curves.
* The returned path has the following characteristics:
*
* <ol>
* <li>The first point is {@link #getStartPoint()}.</li>
* <li>The beginning of the curve (more specifically, the tangent at starting point) is oriented toward the direction given
* by {@linkplain #getStartingAzimuth()}, adjusted for the map projection (if any) deformation at that location.</li>
* <li>The point B(½) in the middle of the Bézier curve is a point of the geodesic path.</li>
* <li>The end of the curve (more specifically, the tangent at ending point) is oriented toward the direction given by
* {@linkplain #getEndingAzimuth()}, adjusted for the map projection (if any) deformation at that location.</li>
* <li>The last point is {@link #getEndPoint()}, potentially with 360° added or subtracted to the longitude.</li>
* </ol>
*
* This method tries to stay within the given tolerance threshold of the geodesic track.
* The {@code tolerance} parameter should not be too small for avoiding creation of unreasonably long chain of Bézier curves.
* For example a value of 1/10 of geodesic length may be sufficient.
*
* <div class="note"><b>Note:</b>
* this method depends on the presence of {@code java.desktop} module. This constraint may be addressed
* in a future Apache SIS version (see <a href="https://issues.apache.org/jira/browse/SIS-453">SIS-453</a>).
* The "2D" suffix in the method name represents this relationship with Java2D.
* The {@code createGeodesicPath(…)} method name (without suffix) is reserved for a future version
* using ISO curves instead.</div>
*
* @param tolerance maximal error between the approximated curve and actual geodesic track
* in the units of measurement given by {@link #getDistanceUnit()}.
* This is approximate; the actual errors may vary around that value.
* @return an approximation of geodesic track as Bézier curves in a Java2D object.
* @throws IllegalStateException if some required properties have not been specified.
* @throws GeodeticException if some coordinates can not be computed.
*/
public Shape createGeodesicPath2D(final double tolerance) {
ArgumentChecks.ensureStrictlyPositive("tolerance", tolerance);
if (isInvalid(START_POINT | STARTING_AZIMUTH | END_POINT | ENDING_AZIMUTH | GEODESIC_DISTANCE)) {
if (isInvalid(END_POINT)) {
computeEndPoint();
} else {
computeDistance();
}
}
final PathBuilder bezier = new PathBuilder(tolerance);
final Path2D path;
try {
path = bezier.build();
} catch (TransformException e) {
throw new GeodeticException(transformError(true), e);
} finally {
bezier.reset();
}
return ShapeUtilities.toPrimitive(path);
}
use of java.awt.geom.Path2D in project sis by apache.
the class GeodeticCalculator method createGeodesicCircle2D.
/**
* Creates an approximation of the curve at a constant geodesic distance around the start point.
* The returned shape is circlelike with the {@linkplain #getStartPoint() start point} in its center.
* The coordinates are expressed in the coordinate reference system specified at creation time.
* The approximation uses cubic Bézier curves.
*
* <div class="note"><b>Note:</b>
* some authors define geodesic circle as the curve which enclose the maximum area for a given perimeter.
* This method adopts a different definition, the locus of points at a fixed geodesic distance from center point.
* </div>
*
* This method tries to stay within the given tolerance threshold of the geodesic track.
* The {@code tolerance} parameter should not be too small for avoiding creation of unreasonably long chain of Bézier curves.
* For example a value of 1/10 of geodesic length may be sufficient.
*
* <div class="note"><b>Note:</b>
* this method depends on the presence of {@code java.desktop} module. This constraint may be addressed
* in a future Apache SIS version (see <a href="https://issues.apache.org/jira/browse/SIS-453">SIS-453</a>).
* The "2D" suffix in the method name represents this relationship with Java2D.
* The {@code createGeodesicCircle(…)} method name (without suffix) is reserved for a future version
* using ISO curves instead.</div>
*
* @param tolerance maximal error in the units of measurement given by {@link #getDistanceUnit()}.
* This is approximate; the actual errors may vary around that value.
* @return an approximation of circular region as a Java2D object.
* @throws IllegalStateException if some required properties have not been specified.
* @throws GeodeticException if some coordinates can not be computed.
*/
public Shape createGeodesicCircle2D(final double tolerance) {
ArgumentChecks.ensureStrictlyPositive("tolerance", tolerance);
if (isInvalid(START_POINT | GEODESIC_DISTANCE)) {
computeDistance();
}
final CircularPath bezier = new CircularPath(tolerance);
final Path2D path;
try {
path = bezier.build();
} catch (TransformException e) {
throw new GeodeticException(transformError(true), e);
} finally {
bezier.reset();
}
path.closePath();
return path;
}
use of java.awt.geom.Path2D in project sis by apache.
the class Wrapper method mergePolylines.
/**
* Implementation of {@link #mergePolylines(Iterator)} also shared by {@link PointWrapper}.
*/
static Shape mergePolylines(Object next, final Iterator<?> polylines) {
boolean isFloat = AbstractShape.isFloat(next);
Path2D path = isFloat ? new Path2D.Float() : new Path2D.Double();
boolean lineTo = false;
add: for (; ; ) {
if (next instanceof Point2D) {
final double x = ((Point2D) next).getX();
final double y = ((Point2D) next).getY();
if (Double.isNaN(x) || Double.isNaN(y)) {
lineTo = false;
} else if (lineTo) {
path.lineTo(x, y);
} else {
path.moveTo(x, y);
lineTo = true;
}
} else {
path.append((Shape) next, false);
lineTo = false;
}
/*
* 'polylines.hasNext()' check is conceptually part of 'for' instruction,
* except that we need to skip this condition during the first iteration.
*/
do if (!polylines.hasNext())
break add; while ((next = polylines.next()) == null);
/*
* Convert the path from single-precision to double-precision if needed.
*/
if (isFloat && !AbstractShape.isFloat(next)) {
path = new Path2D.Double(path);
isFloat = false;
}
}
return ShapeUtilities.toPrimitive(path);
}
Aggregations