ShapeDrawer.java

package io.brunoborges.jairosvg.draw;

import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;

import io.brunoborges.jairosvg.dom.Node;
import io.brunoborges.jairosvg.surface.Surface;
import io.brunoborges.jairosvg.util.ParsedPoint;

import static io.brunoborges.jairosvg.util.Helpers.*;

/**
 * SVG shape drawers. Port of CairoSVG shapes.py
 */
public final class ShapeDrawer {

    private ShapeDrawer() {
    }

    /** Draw a circle node. */
    public static void circle(Surface surface, Node node) {
        double r = size(surface, node.get("r"));
        if (r == 0)
            return;
        double cx = size(surface, node.get("cx"), "x");
        double cy = size(surface, node.get("cy"), "y");

        surface.path.append(new Ellipse2D.Double(cx - r, cy - r, 2 * r, 2 * r), false);
    }

    /** Draw an ellipse node. */
    public static void ellipse(Surface surface, Node node) {
        double rx = size(surface, node.get("rx"), "x");
        double ry = size(surface, node.get("ry"), "y");
        if (rx == 0 || ry == 0)
            return;
        double cx = size(surface, node.get("cx"), "x");
        double cy = size(surface, node.get("cy"), "y");

        surface.path.append(new Ellipse2D.Double(cx - rx, cy - ry, 2 * rx, 2 * ry), false);
    }

    /** Draw a line node. */
    public static void line(Surface surface, Node node) {
        double x1 = size(surface, node.get("x1"), "x");
        double y1 = size(surface, node.get("y1"), "y");
        double x2 = size(surface, node.get("x2"), "x");
        double y2 = size(surface, node.get("y2"), "y");

        surface.path.moveTo(x1, y1);
        surface.path.lineTo(x2, y2);

        double angle = pointAngle(x1, y1, x2, y2);
        node.vertices = new java.util.ArrayList<>();
        node.vertices.add(new double[]{x1, y1});
        node.vertices.add(new double[]{Math.PI - angle, angle});
        node.vertices.add(new double[]{x2, y2});
    }

    /** Draw a polygon node. */
    public static void polygon(Surface surface, Node node) {
        polyline(surface, node);
        surface.path.closePath();
    }

    /** Draw a polyline node. */
    public static void polyline(Surface surface, Node node) {
        String points = normalize(node.get("points", ""));
        if (points.isEmpty())
            return;

        node.vertices = new java.util.ArrayList<>();
        boolean first = true;
        while (!points.isBlank()) {
            ParsedPoint pt = pointWithRemainder(surface, points);
            double x = pt.x(), y = pt.y();
            points = pt.remainder();

            if (first) {
                surface.path.moveTo(x, y);
                node.vertices.add(new double[]{x, y});
                first = false;
            } else {
                double[] prev = getPrevPoint(node);
                double angle = pointAngle(prev[0], prev[1], x, y);
                node.vertices.add(new double[]{Math.PI - angle, angle});
                surface.path.lineTo(x, y);
                node.vertices.add(new double[]{x, y});
            }
        }
    }

    /** Draw a rect node. */
    public static void rect(Surface surface, Node node) {
        double x = size(surface, node.get("x"), "x");
        double y = size(surface, node.get("y"), "y");
        double width = size(surface, node.get("width"), "x");
        double height = size(surface, node.get("height"), "y");

        String rxStr = node.get("rx");
        String ryStr = node.get("ry");
        if (rxStr != null && ryStr == null)
            ryStr = rxStr;
        if (ryStr != null && rxStr == null)
            rxStr = ryStr;

        double rx = size(surface, rxStr, "x");
        double ry = size(surface, ryStr, "y");

        if (rx == 0 || ry == 0) {
            surface.path.append(new Rectangle2D.Double(x, y, width, height), false);
        } else {
            rx = Math.min(rx, width / 2);
            ry = Math.min(ry, height / 2);
            surface.path.append(new RoundRectangle2D.Double(x, y, width, height, rx * 2, ry * 2), false);
        }
    }

    private static double[] getPrevPoint(Node node) {
        for (int i = node.vertices.size() - 1; i >= 0; i--) {
            Object v = node.vertices.get(i);
            if (v instanceof double[] arr && arr.length == 2) {
                return arr;
            }
        }
        return new double[]{0, 0};
    }
}