All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jfxtras.labs.util.ShapeConverter Maven / Gradle / Ivy

There is a newer version: 9.0-r1
Show newest version
/**
 * ShapeConverter.java
 *
 * Copyright (c) 2011-2015, JFXtras
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the organization nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL  BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package jfxtras.labs.util;

import java.util.List;

import javafx.geometry.Bounds;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcTo;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Circle;
import javafx.scene.shape.ClosePath;
import javafx.scene.shape.CubicCurve;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.HLineTo;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Polyline;
import javafx.scene.shape.QuadCurve;
import javafx.scene.shape.QuadCurveTo;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.SVGPath;
import javafx.scene.shape.SVGPathBuilder;
import javafx.scene.shape.Shape;
import javafx.scene.shape.VLineTo;
import javafx.scene.text.Text;


/**
 * Created by
 * User: hansolo
 * Date: 27.11.12
 * Time: 12:03
 */
public class ShapeConverter {
    private static final double KAPPA = 0.5522847498307935;

    public static String shapeToSvgString(final Shape SHAPE) {
        final StringBuilder fxPath = new StringBuilder();
        if (Line.class.equals(SHAPE.getClass())) {
            fxPath.append(convertLine((Line) SHAPE));
        } else if (Arc.class.equals(SHAPE.getClass())) {
            fxPath.append(convertArc((Arc) SHAPE));
        } else if (QuadCurve.class.equals(SHAPE.getClass())) {
            fxPath.append(convertQuadCurve((QuadCurve) SHAPE));
        } else if (CubicCurve.class.equals(SHAPE.getClass())) {
            fxPath.append(convertCubicCurve((CubicCurve) SHAPE));
        } else if (Rectangle.class.equals(SHAPE.getClass())) {
            fxPath.append(convertRectangle((Rectangle) SHAPE));
        } else if (Circle.class.equals(SHAPE.getClass())) {
            fxPath.append(convertCircle((Circle) SHAPE));
        } else if (Ellipse.class.equals(SHAPE.getClass())) {
            fxPath.append(convertEllipse((Ellipse) SHAPE));
        } else if (Text.class.equals(SHAPE.getClass())) {
            Path path = (Path)(Shape.subtract(SHAPE, new Rectangle(0, 0)));
            fxPath.append(convertPath(path));
        } else if (Path.class.equals(SHAPE.getClass())) {
            fxPath.append(convertPath((Path) SHAPE));
        } else if (Polygon.class.equals(SHAPE.getClass())) {
            fxPath.append(convertPolygon((Polygon) SHAPE));
        } else if (Polyline.class.equals(SHAPE.getClass())) {
            fxPath.append(convertPolyline((Polyline) SHAPE));
        } else if (SVGPath.class.equals(SHAPE.getClass())) {
            fxPath.append(((SVGPath) SHAPE).getContent());
        }
        return fxPath.toString();
    }

    public static SVGPath shapeToSvgPath(final Shape SHAPE) {
        return SVGPathBuilder.create().content(shapeToSvgString(SHAPE)).build();
    }

    public static String convertLine(final Line LINE) {
        final StringBuilder fxPath = new StringBuilder();
        fxPath.append("M ").append(LINE.getStartX()).append(" ").append(LINE.getStartY()).append(" ")
              .append("L ").append(LINE.getEndX()).append(" ").append(LINE.getEndY());
        return fxPath.toString();
    }

    public static String convertArc(final Arc ARC) {
        StringBuilder fxPath = new StringBuilder();
        double centerX    = ARC.getCenterX();
        double centerY    = ARC.getCenterY();
        double radiusX    = ARC.getRadiusX();
        double radiusY    = ARC.getRadiusY();
        double startAngle = ARC.getStartAngle();
        double length     = ARC.getLength();
        double alpha      = ARC.getLength() + startAngle;
        startAngle        = Math.toRadians(startAngle);
        alpha             = Math.toRadians(alpha);
        double phiOffset  = Math.toRadians(-90); // -90 needed for JavaFX

        double startX = centerX + Math.cos(phiOffset) * radiusX * Math.cos(startAngle) + Math.sin(-phiOffset) * radiusY * Math.sin(startAngle);
        double startY = centerY + Math.sin(phiOffset) * radiusX * Math.cos(startAngle) + Math.cos(phiOffset) * radiusY * Math.sin(startAngle);

        double endX   = centerX + Math.cos(phiOffset) * radiusX * Math.cos(alpha) + Math.sin(-phiOffset) * radiusY * Math.sin(alpha);
        double endY   = centerY + Math.sin(phiOffset) * radiusX * Math.cos(alpha) + Math.cos(phiOffset) * radiusY * Math.sin(alpha);

        int xAxisRot  = 0;
        int largeArc  = (length > 180) ? 1 : 0;
        int sweep     = (length > 0) ? 1 : 0;

        fxPath.append("M ").append(centerX).append(" ").append(centerY).append(" ");
        if (ArcType.ROUND == ARC.getType()) {
            fxPath.append("h ").append(startX - centerX).append(" v ").append(startY - centerY);
        }
        fxPath.append("A ").append(radiusX).append(" ").append(radiusY).append(" ")
              .append(xAxisRot).append(" ").append(largeArc).append(" ").append(sweep).append(" ")
              .append(endX).append(" ").append(endY).append(" ");
        if (ArcType.CHORD == ARC.getType() || ArcType.ROUND == ARC.getType()) {
            fxPath.append("Z");
        }
        return fxPath.toString();
    }

    public static String convertQuadCurve(final QuadCurve QUAD_CURVE) {
        final StringBuilder fxPath = new StringBuilder();
        fxPath.append("M ").append(QUAD_CURVE.getStartX()).append(" ").append(QUAD_CURVE.getStartY()).append(" ")
              .append("Q ").append(QUAD_CURVE.getControlX()).append(" ").append(QUAD_CURVE.getControlY())
              .append(QUAD_CURVE.getEndX()).append(" ").append(QUAD_CURVE.getEndY());
        return fxPath.toString();
    }

    public static String convertCubicCurve(final CubicCurve CUBIC_CURVE) {
        final StringBuilder fxPath = new StringBuilder();
        fxPath.append("M ").append(CUBIC_CURVE.getStartX()).append(" ").append(CUBIC_CURVE.getStartY()).append(" ")
              .append("C ").append(CUBIC_CURVE.getControlX1()).append(" ").append(CUBIC_CURVE.getControlY1()).append(" ")
              .append(CUBIC_CURVE.getControlX2()).append(" ").append(CUBIC_CURVE.getControlY2()).append(" ")
              .append(CUBIC_CURVE.getEndX()).append(" ").append(CUBIC_CURVE.getEndY());
        return fxPath.toString();
    }

    public static String convertRectangle(final Rectangle RECTANGLE) {
        final StringBuilder fxPath = new StringBuilder();
        final Bounds        bounds = RECTANGLE.getBoundsInLocal();
        if (Double.compare(RECTANGLE.getArcWidth(), 0.0) == 0 && Double.compare(RECTANGLE.getArcHeight(), 0.0) == 0) {
            fxPath.append("M ").append(bounds.getMinX()).append(" ").append(bounds.getMinY()).append(" ")
                  .append("H ").append(bounds.getMaxX()).append(" ")
                  .append("V ").append(bounds.getMaxY()).append(" ")
                  .append("H ").append(bounds.getMinX()).append(" ")
                  .append("V ").append(bounds.getMinY()).append(" ")
                  .append("Z");
        } else {
            double x         = bounds.getMinX();
            double y         = bounds.getMinY();
            double width     = bounds.getWidth();
            double height    = bounds.getHeight();
            double arcWidth  = RECTANGLE.getArcWidth();
            double arcHeight = RECTANGLE.getArcHeight();
            double r         = x + width;
            double b         = y + height;
            fxPath.append("M ").append(x + arcWidth).append(" ").append(y).append(" ")
                  .append("L ").append(r - arcWidth).append(" ").append(y).append(" ")
                  .append("Q ").append(r).append(" ").append(y).append(" ").append(r).append(" ").append(y + arcHeight).append(" ")
                  .append("L ").append(r).append(" ").append(y + height - arcHeight).append(" ")
                  .append("Q ").append(r).append(" ").append(b).append(" ").append(r - arcWidth).append(" ").append(b).append(" ")
                  .append("L ").append(x + arcWidth).append(" ").append(b).append(" ")
                  .append("Q ").append(x).append(" ").append(b).append(" ").append(x).append(" ").append(b - arcHeight).append(" ")
                  .append("L ").append(x).append(" ").append(y + arcHeight).append(" ")
                  .append("Q ").append(x).append(" ").append(y).append(" ").append(x + arcWidth).append(" ").append(y).append(" ")
                  .append("Z");
        }
        return fxPath.toString();
    }

    public static String convertCircle(final Circle CIRCLE) {
        final StringBuilder fxPath = new StringBuilder();
        final double CENTER_X         = CIRCLE.getCenterX() == 0 ? CIRCLE.getRadius() : CIRCLE.getCenterX();
        final double CENTER_Y         = CIRCLE.getCenterY() == 0 ? CIRCLE.getRadius() : CIRCLE.getCenterY();
        final double RADIUS           = CIRCLE.getRadius();
        final double CONTROL_DISTANCE = RADIUS * KAPPA;
        // Move to first point
        fxPath.append("M ").append(CENTER_X).append(" ").append(CENTER_Y - RADIUS).append(" ");
        // 1. quadrant
        fxPath.append("C ").append(CENTER_X + CONTROL_DISTANCE).append(" ").append(CENTER_Y - RADIUS).append(" ")
              .append(CENTER_X + RADIUS).append(" ").append(CENTER_Y - CONTROL_DISTANCE).append(" ")
              .append(CENTER_X + RADIUS).append(" ").append(CENTER_Y).append(" ");
        // 2. quadrant
        fxPath.append("C ").append(CENTER_X + RADIUS).append(" ").append(CENTER_Y + CONTROL_DISTANCE).append(" ")
              .append(CENTER_X + CONTROL_DISTANCE).append(" ").append(CENTER_Y + RADIUS).append(" ")
              .append(CENTER_X).append(" ").append(CENTER_Y + RADIUS).append(" ");
        // 3. quadrant
        fxPath.append("C ").append(CENTER_X - CONTROL_DISTANCE).append(" ").append(CENTER_Y + RADIUS).append(" ")
              .append(CENTER_X - RADIUS).append(" ").append(CENTER_Y + CONTROL_DISTANCE).append(" ")
              .append(CENTER_X - RADIUS).append(" ").append(CENTER_Y).append(" ");
        // 4. quadrant
        fxPath.append("C ").append(CENTER_X - RADIUS).append(" ").append(CENTER_Y - CONTROL_DISTANCE).append(" ")
              .append(CENTER_X - CONTROL_DISTANCE).append(" ").append(CENTER_Y - RADIUS).append(" ")
              .append(CENTER_X).append(" ").append(CENTER_Y - RADIUS).append(" ");
        // Close path
        fxPath.append("Z");
        return fxPath.toString();
    }

    public static String convertEllipse(final Ellipse ELLIPSE) {
        final StringBuilder fxPath = new StringBuilder();
        final double CENTER_X           = ELLIPSE.getCenterX() == 0 ? ELLIPSE.getRadiusX() : ELLIPSE.getCenterX();
        final double CENTER_Y           = ELLIPSE.getCenterY() == 0 ? ELLIPSE.getRadiusY() : ELLIPSE.getCenterY();
        final double RADIUS_X           = ELLIPSE.getRadiusX();
        final double RADIUS_Y           = ELLIPSE.getRadiusY();
        final double CONTROL_DISTANCE_X = RADIUS_X * KAPPA;
        final double CONTROL_DISTANCE_Y = RADIUS_Y * KAPPA;
        // Move to first point
        fxPath.append("M ").append(CENTER_X).append(" ").append(CENTER_Y - RADIUS_Y).append(" ");
        // 1. quadrant
        fxPath.append("C ").append(CENTER_X + CONTROL_DISTANCE_X).append(" ").append(CENTER_Y - RADIUS_Y).append(" ")
              .append(CENTER_X + RADIUS_X).append(" ").append(CENTER_Y - CONTROL_DISTANCE_Y).append(" ")
              .append(CENTER_X + RADIUS_X).append(" ").append(CENTER_Y).append(" ");
        // 2. quadrant
        fxPath.append("C ").append(CENTER_X + RADIUS_X).append(" ").append(CENTER_Y + CONTROL_DISTANCE_Y).append(" ")
              .append(CENTER_X + CONTROL_DISTANCE_X).append(" ").append(CENTER_Y + RADIUS_Y).append(" ")
              .append(CENTER_X).append(" ").append(CENTER_Y + RADIUS_Y).append(" ");
        // 3. quadrant
        fxPath.append("C ").append(CENTER_X - CONTROL_DISTANCE_X).append(" ").append(CENTER_Y + RADIUS_Y).append(" ")
              .append(CENTER_X - RADIUS_X).append(" ").append(CENTER_Y + CONTROL_DISTANCE_Y).append(" ")
              .append(CENTER_X - RADIUS_X).append(" ").append(CENTER_Y).append(" ");
        // 4. quadrant
        fxPath.append("C ").append(CENTER_X - RADIUS_X).append(" ").append(CENTER_Y - CONTROL_DISTANCE_Y).append(" ")
              .append(CENTER_X - CONTROL_DISTANCE_X).append(" ").append(CENTER_Y - RADIUS_Y).append(" ")
              .append(CENTER_X).append(" ").append(CENTER_Y - RADIUS_Y).append(" ");
        // Close path
        fxPath.append("Z");
        return fxPath.toString();
    }

    public static String convertPath(final Path PATH) {
        final StringBuilder fxPath = new StringBuilder();
        for (PathElement element : PATH.getElements()) {
            if (MoveTo.class.equals(element.getClass())) {
                fxPath.append("M ")
                      .append(((MoveTo) element).getX()).append(" ")
                      .append(((MoveTo) element).getY()).append(" ");
            } else if (LineTo.class.equals(element.getClass())) {
                fxPath.append("L ")
                      .append(((LineTo) element).getX()).append(" ")
                      .append(((LineTo) element).getY()).append(" ");
            } else if (CubicCurveTo.class.equals(element.getClass())) {
                fxPath.append("C ")
                      .append(((CubicCurveTo) element).getControlX1()).append(" ")
                      .append(((CubicCurveTo) element).getControlY1()).append(" ")
                      .append(((CubicCurveTo) element).getControlX2()).append(" ")
                      .append(((CubicCurveTo) element).getControlY2()).append(" ")
                      .append(((CubicCurveTo) element).getX()).append(" ")
                      .append(((CubicCurveTo) element).getY()).append(" ");
            } else if (QuadCurveTo.class.equals(element.getClass())) {
                fxPath.append("Q ")
                      .append(((QuadCurveTo) element).getControlX()).append(" ")
                      .append(((QuadCurveTo) element).getControlY()).append(" ")
                      .append(((QuadCurveTo) element).getX()).append(" ")
                      .append(((QuadCurveTo) element).getY()).append(" ");
            } else if (ArcTo.class.equals(element.getClass())) {
                fxPath.append("A ")
                      .append(((ArcTo) element).getX()).append(" ")
                      .append(((ArcTo) element).getY()).append(" ")
                      .append(((ArcTo) element).getRadiusX()).append(" ")
                      .append(((ArcTo) element).getRadiusY()).append(" ");
            } else if (HLineTo.class.equals(element.getClass())) {
                fxPath.append("H ")
                      .append(((HLineTo) element).getX()).append(" ");
            } else if (VLineTo.class.equals(element.getClass())) {
                fxPath.append("V ")
                      .append(((VLineTo) element).getY()).append(" ");
            } else if (ClosePath.class.equals(element.getClass())) {
                fxPath.append("Z");
            }
        }
        return fxPath.toString();
    }

    public static String convertPolygon(final Polygon POLYGON) {
        final StringBuilder fxPath = new StringBuilder();
        final int           size   = POLYGON.getPoints().size();
        if (size % 2 == 0) {
            List coordinates     = POLYGON.getPoints();
            for (int i = 0 ; i < size ; i += 2) {
                fxPath.append(i == 0 ? "M " : "L ")
                      .append(coordinates.get(i)).append(" ").append(coordinates.get(i + 1)).append(" ");
            }
            fxPath.append("Z");
        }
        return fxPath.toString();
    }

    public static String convertPolyline(final Polyline POLYLINE) {
        final StringBuilder fxPath = new StringBuilder();
        final int           size   = POLYLINE.getPoints().size();
        if (size % 2 == 0) {
            List coordinates     = POLYLINE.getPoints();
            for (int i = 0 ; i < size ; i += 2) {
                fxPath.append(i == 0 ? "M " : "L ")
                      .append(coordinates.get(i)).append(" ").append(coordinates.get(i + 1)).append(" ");
            }
        }
        return fxPath.toString();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy