Search in sources :

Example 56 with Path2D

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);
}
Also used : Path2D(java.awt.geom.Path2D) Vector(org.apache.sis.math.Vector) Line2D(java.awt.geom.Line2D)

Example 57 with Path2D

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;
}
Also used : Area(java.awt.geom.Area) Path2D(java.awt.geom.Path2D) Rectangle2D(java.awt.geom.Rectangle2D) RectangularShape(java.awt.geom.RectangularShape)

Example 58 with Path2D

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);
}
Also used : Path2D(java.awt.geom.Path2D) TransformException(org.opengis.referencing.operation.TransformException)

Example 59 with Path2D

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;
}
Also used : Path2D(java.awt.geom.Path2D) TransformException(org.opengis.referencing.operation.TransformException)

Example 60 with Path2D

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);
}
Also used : Shape(java.awt.Shape) RectangularShape(java.awt.geom.RectangularShape) AbstractShape(org.apache.sis.internal.referencing.j2d.AbstractShape) Point2D(java.awt.geom.Point2D) Path2D(java.awt.geom.Path2D)

Aggregations

Path2D (java.awt.geom.Path2D)126 Point2D (java.awt.geom.Point2D)20 Area (java.awt.geom.Area)16 Rectangle2D (java.awt.geom.Rectangle2D)13 Shape (java.awt.Shape)9 Point (java.awt.Point)8 Line2D (java.awt.geom.Line2D)8 PathIterator (java.awt.geom.PathIterator)8 ArrayList (java.util.ArrayList)8 AffineTransform (java.awt.geom.AffineTransform)7 GeneralPath (java.awt.geom.GeneralPath)7 Color (java.awt.Color)6 Graphics2D (java.awt.Graphics2D)6 Paint (java.awt.Paint)6 ShapeRoi (ij.gui.ShapeRoi)5 BasicStroke (java.awt.BasicStroke)4 RadialGradientPaint (java.awt.RadialGradientPaint)4 Point2D_F64 (georegression.struct.point.Point2D_F64)3 RoundRectangle2D (java.awt.geom.RoundRectangle2D)3 Vector2D (de.gurkenlabs.litiengine.util.geom.Vector2D)2