ObroundAperture.java

package com.varnernet.gerb4j;

import com.varnernet.gerb4j.render.GerberOutputTarget;

import java.awt.Shape;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;

/**
 * An obround (stadium-shaped) aperture.
 *
 * <p>An obround is a rectangle with semicircular ends. The diameter of the semicircles equals
 * the shorter dimension of the rectangle.
 */
public class ObroundAperture extends Aperture {
    private final double xSize;
    private final double ySize;
    private final double holeDiameter;

    /**
     * Creates a new obround aperture.
     *
     * @param id           the aperture identifier
     * @param xSize        the width of the obround
     * @param ySize        the height of the obround
     * @param holeDiameter the diameter of the optional hole (0.0 for no hole)
     */
    public ObroundAperture(
            final String id, final String xSize, final String ySize, final String holeDiameter) {
        super(id);
        this.xSize = Double.parseDouble(xSize);
        this.ySize = Double.parseDouble(ySize);
        this.holeDiameter = (holeDiameter == null) ? 0.0 : Double.parseDouble(holeDiameter);
    }

    /**
     * Returns the width of the obround.
     *
     * @return the width
     */
    public double getWidth() {
        return xSize;
    }

    /**
     * Returns the height of the obround.
     *
     * @return the height
     */
    public double getHeight() {
        return ySize;
    }

    /**
     * Returns the diameter of the optional hole.
     *
     * @return the hole diameter (0.0 if no hole)
     */
    public double getHoleDiameter() {
        return holeDiameter;
    }

    /**
     * Bounds centred at (0,0) — the enclosing rectangle of the stadium shape.
     */
    @Override
    public Rectangle2D getBounds() {
        return new Rectangle2D.Double(-xSize / 2.0, -ySize / 2.0, xSize, ySize);
    }

    /**
     * Stroke width for D01 linear draws. An obround (stadium) cross-section has semicircular ends
     * whose diameter equals the short axis: min(xSize, ySize).
     */
    @Override
    public double getStrokeWidth() {
        return Math.min(xSize, ySize);
    }

    /**
     * Returns the obround (stadium) cross-section for swept-region D01 rendering. Per Gerber spec
     * §4.8.4, the cross-section of the drawn line is the full stadium shape (rounded rectangle with
     * semicircular ends).
     */
    @Override
    public Shape getApertureShape() {
        double cornerDiam = Math.min(xSize, ySize);
        return new RoundRectangle2D.Double(
                -xSize / 2.0, -ySize / 2.0, xSize, ySize, cornerDiam, cornerDiam);
    }

    @Override
    public void render(final Point2D flashPoint, final GerberOutputTarget target, final Polarity polarity) {
        target.drawObround(flashPoint, xSize, ySize, 0.0, polarity);
        if (holeDiameter > 0) {
            target.drawCircle(flashPoint, holeDiameter, 0.0, invertPolarity(polarity));
        }
    }
}