GraphicsState.java

package com.varnernet.gerb4j;

import java.awt.geom.Point2D;

/**
 * Immutable snapshot of the Gerber graphics state.
 *
 * <p>Per Ucamco specification page 15, the state saved/restored on block entry/exit (AB, SR)
 * includes:
 *
 * <ul>
 *   <li>Current point (X, Y)
 *   <li>Current aperture selection (Dnn)
 *   <li>Polarity (LP)
 *   <li>Mirroring (LM)
 *   <li>Rotation (LR)
 *   <li>Scaling (LS)
 * </ul>
 *
 * <p>Note: Aperture <em>definitions</em> (AD, AB) are global and are <strong>not</strong> part of
 * the graphics state. {@code PlotState} has been removed — {@link InterpolationMode} is the
 * canonical interpolation-mode type and is tracked separately.
 *
 * @param currentPoint      the current point
 * @param currentApertureId the current aperture ID
 * @param polarity          the polarity
 * @param mirroring         the mirroring
 * @param rotation          the rotation
 * @param scaling           the scaling
 */
public record GraphicsState(Point2D currentPoint, String currentApertureId, Polarity polarity, Mirror mirroring,
                            Double rotation, Double scaling) {

    /**
     * Create a snapshot with all given values.
     */
    public GraphicsState(
            final Point2D currentPoint,
            final String currentApertureId,
            final Polarity polarity,
            final Mirror mirroring,
            final Double rotation,
            final Double scaling) {
        this.currentPoint =
                currentPoint != null ? new Point2D.Double(currentPoint.getX(), currentPoint.getY()) : null;
        this.currentApertureId = currentApertureId;
        this.polarity = polarity != null ? polarity : Polarity.DARK;
        this.mirroring = mirroring != null ? mirroring : Mirror.NONE;
        this.rotation = rotation;
        this.scaling = scaling;
    }

    /**
     * Create a default (power-on) graphics state.
     */
    public GraphicsState() {
        this(null, null, Polarity.DARK, Mirror.NONE, null, null);
    }

    /**
     * Copy constructor — produces an independent snapshot.
     *
     * @param other the graphics state to copy
     */
    public GraphicsState(final GraphicsState other) {
        this(
                other.currentPoint,
                other.currentApertureId,
                other.polarity,
                other.mirroring,
                other.rotation,
                other.scaling);
    }

    // ── Accessors ─────────────────────────────────────────────────────────────

    /**
     * Current pen position, or {@code null} if not yet set.
     *
     * @return a defensive copy of the current point, or {@code null}
     */
    @Override
    public Point2D currentPoint() {
        return currentPoint != null
                ? new Point2D.Double(currentPoint.getX(), currentPoint.getY())
                : null;
    }

    @Override
    public String toString() {
        return String.format(
                "GraphicsState[point=%s, aperture=%s, polarity=%s, mirror=%s, rot=%s, scale=%s]",
                currentPoint, currentApertureId, polarity, mirroring, rotation, scaling);
    }
}