DrawingOperation.java

package com.varnernet.gerb4j.render;

import com.varnernet.gerb4j.ApertureTransform;
import com.varnernet.gerb4j.Mirror;
import com.varnernet.gerb4j.Polarity;
import com.varnernet.gerb4j.QuadrantMode;

import java.awt.geom.Point2D;

/**
 * Represents a single drawing operation (D01, D02, D03). Captures the operation type, aperture,
 * coordinates, and the graphics-state snapshot at the time of the operation.
 *
 * <p>All fields are immutable (final). The graphics-state snapshot (polarity, mirroring, rotation,
 * scaling) is grouped into an {@link ApertureTransform}.
 */
public final class DrawingOperation implements GerberOperation {

    /**
     * The type of drawing operation corresponding to a Gerber D-code.
     */
    public enum Type {
        /**
         * Move operation (D02).
         */
        MOVE, // D02
        /**
         * Draw operation (D01).
         */
        DRAW, // D01
        /**
         * Flash operation (D03).
         */
        FLASH // D03
    }

    private final Type type;
    private final String apertureId;
    private final Point2D point;

    /**
     * For arc operations: the (I, J) center offset relative to the start point.
     */
    private final Point2D interpolationPoint;

    private final Polarity polarity;
    private final ApertureTransform transform;

    /**
     * For arc DRAW operations: {@code true} = G02 (clockwise), {@code false} = G03.
     */
    private final boolean clockwise;

    /**
     * For arc DRAW operations: quadrant mode active at parse time (G74 vs G75).
     */
    private final QuadrantMode quadrantMode;

    // ── Constructors ──────────────────────────────────────────────────────────

    /**
     * Convenience constructor for simple operations without graphics state.
     *
     * @param type       the operation type
     * @param apertureId the aperture ID
     * @param point      the point
     */
    public DrawingOperation(final Type type, final String apertureId, final Point2D point) {
        this(
                type, apertureId, point, null, ApertureTransform.IDENTITY, null, false, QuadrantMode.MULTI);
    }

    /**
     * Constructor with separate transform components (convenience for callers).
     *
     * @param type               Operation type
     * @param apertureId         Aperture ID string
     * @param point              (X, Y) coordinate
     * @param polarity           Polarity snapshot at record time
     * @param transform          Aperture transform (mirroring, rotation, scaling)
     * @param interpolationPoint (I, J) arc-centre offset, or null for linear ops
     */
    public DrawingOperation(
            final Type type,
            final String apertureId,
            final Point2D point,
            final Polarity polarity,
            final ApertureTransform transform,
            final Point2D interpolationPoint) {
        this(
                type,
                apertureId,
                point,
                polarity,
                transform,
                interpolationPoint,
                false,
                QuadrantMode.MULTI);
    }

    /**
     * Constructor with separate transform components (convenience for callers).
     *
     * @param type               Operation type
     * @param apertureId         Aperture ID string
     * @param point              (X, Y) coordinate
     * @param polarity           Polarity snapshot at record time
     * @param mirroring          Mirror snapshot (may be null → NONE)
     * @param rotation           Rotation snapshot in degrees (may be null → 0)
     * @param scaling            Scaling snapshot (may be null → 1.0)
     * @param interpolationPoint (I, J) arc-centre offset, or null for linear ops
     */
    public DrawingOperation(
            final Type type,
            final String apertureId,
            final Point2D point,
            final Polarity polarity,
            final Mirror mirroring,
            final Double rotation,
            final Double scaling,
            final Point2D interpolationPoint) {
        this(
                type,
                apertureId,
                point,
                polarity,
                new ApertureTransform(mirroring, rotation, scaling),
                interpolationPoint,
                false,
                QuadrantMode.MULTI);
    }

    /**
     * Full constructor used during parsing (with explicit clockwise flag and quadrant mode).
     *
     * @param type               the operation type
     * @param apertureId         the aperture ID
     * @param point              the point
     * @param polarity           the polarity
     * @param transform          the aperture transform
     * @param interpolationPoint the interpolation point
     * @param clockwise          the clockwise flag
     * @param quadrantMode       the quadrant mode
     */
    public DrawingOperation(
            final Type type,
            final String apertureId,
            final Point2D point,
            final Polarity polarity,
            final ApertureTransform transform,
            final Point2D interpolationPoint,
            final boolean clockwise,
            final QuadrantMode quadrantMode) {
        this.type = type;
        this.apertureId = apertureId;
        this.point = point != null ? new Point2D.Double(point.getX(), point.getY()) : null;
        this.polarity = polarity;
        this.transform = transform != null ? transform : ApertureTransform.IDENTITY;
        this.interpolationPoint = interpolationPoint;
        this.clockwise = clockwise;
        this.quadrantMode = quadrantMode != null ? quadrantMode : QuadrantMode.MULTI;
    }

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

    /**
     * Returns the operation type.
     *
     * @return the type
     */
    public Type getType() {
        return type;
    }

    /**
     * Returns the aperture ID.
     *
     * @return the aperture ID
     */
    public String getApertureId() {
        return apertureId;
    }

    /**
     * Returns the point.
     *
     * @return the point
     */
    public Point2D getPoint() {
        return point;
    }

    /**
     * Returns the interpolation point.
     *
     * @return the interpolation point
     */
    public Point2D getInterpolationPoint() {
        return interpolationPoint;
    }

    /**
     * Returns the polarity.
     *
     * @return the polarity
     */
    public Polarity getPolarity() {
        return polarity;
    }

    /**
     * Returns the full aperture-transform snapshot for this operation.
     *
     * @return the aperture transform
     */
    public ApertureTransform getApertureTransform() {
        return transform;
    }

    // ── Legacy accessors kept for compatibility ────────────────────────────────

    /**
     * Returns the mirroring.
     *
     * @return the mirroring
     */
    public Mirror getMirroring() {
        return transform.mirroring();
    }

    /**
     * Returns the rotation.
     *
     * @return the rotation
     */
    public Double getRotation() {
        return transform.rotation();
    }

    /**
     * Returns the scaling.
     *
     * @return the scaling
     */
    public Double getScaling() {
        return transform.scaling();
    }

    /**
     * For arc DRAW operations: {@code true} = G02 clockwise, {@code false} = G03.
     *
     * @return true if clockwise
     */
    public boolean isClockwise() {
        return clockwise;
    }

    /**
     * For arc DRAW operations: the quadrant mode in effect at parse time.
     *
     * @return the quadrant mode
     */
    public QuadrantMode getQuadrantMode() {
        return quadrantMode;
    }
}