import type { Wafer } from './wafer.js';
import type { Die, DieSpec, PositionedDie } from './dies.js';
export interface DataRow {
    [key: string]: string | number;
}
export interface MapOptions {
    xField?: string;
    yField?: string;
    iField?: string;
    jField?: string;
    valueField: string;
    /** 'xy' matches by wafer coordinates; 'ij' matches by grid indices (default: 'xy') */
    matchBy?: 'xy' | 'ij';
}
export interface TransformOptions {
    /** Additional rotation in degrees, applied on top of existing die coordinates. */
    rotation?: number;
    /** Mirror x around the wafer centre. Applied after rotation. */
    flipX?: boolean;
    /** Mirror y around the wafer centre. Applied after rotation. */
    flipY?: boolean;
}
/** Rotate (x, y) clockwise by angleDegrees around (cx, cy). */
export declare function rotatePoint(x: number, y: number, angleDegrees: number, cx?: number, cy?: number): {
    x: number;
    y: number;
};
/**
 * A 2D affine transform, applied as:
 * ```
 *   x' = a·x + c·y + e
 *   y' = b·x + d·y + f
 * ```
 * `From`/`To` are phantom coordinate-frame tags — see {@link CoordFrame}.
 */
export interface Affine<From extends CoordFrame = CoordFrame, To extends CoordFrame = CoordFrame> {
    a: number;
    b: number;
    c: number;
    d: number;
    e: number;
    f: number;
    /** Phantom source frame — never present at runtime. */
    readonly __from?: From;
    /** Phantom target frame — never present at runtime. */
    readonly __to?: To;
}
/**
 * The distinct coordinate frames a point can live in. Mixing them up is the
 * single most common source of misplaced-geometry bugs in this library, so the
 * frame travels in the type of the matrix that converts between them.
 *
 * - `physical` — wafer-local mm describing a *physical wafer feature* (the
 *   boundary outline, the notch, ring/quadrant region borders). These follow
 *   `wafer.orientation` but NOT the data-axis flip: that flip exists to make the
 *   rendered image physically correct for a prober convention (e.g.
 *   `yAxisDirection: 'down'` puts row 1 at the top), so the physical wafer must
 *   stay put while the die grid moves relative to it.
 * - `grid` — wafer-local mm for geometry *aligned to the die grid* (reticle
 *   fields, the +X/+Y axis indicator). These DO follow the data-axis flip,
 *   because they must stay locked to the dies they describe.
 * - `baked` — what `Die.physX`/`physY` holds after `buildWaferMap` has applied
 *   `applyOrientation` then `transformDies`. Public API; do not redefine.
 * - `screen` — final display mm after the interactive rotate/flip. This is what
 *   `View.hoverPoints` holds and what hit-testing compares against.
 */
export type CoordFrame = 'physical' | 'grid' | 'baked' | 'screen';
/** The identity transform. */
export declare function affineIdentity<F extends CoordFrame>(): Affine<F, F>;
/**
 * Clockwise rotation by `angleDegrees` about (cx, cy) — the same convention as
 * {@link rotatePoint}, which this reproduces exactly.
 */
export declare function affineRotation<From extends CoordFrame, To extends CoordFrame>(angleDegrees: number, cx?: number, cy?: number): Affine<From, To>;
/** Mirror through (cx, cy) on each requested axis. */
export declare function affineMirror<From extends CoordFrame, To extends CoordFrame>(flipX: boolean, flipY: boolean, cx?: number, cy?: number): Affine<From, To>;
/**
 * Compose two transforms: `inner` runs first, then `outer`. The frame brands must
 * meet (`inner` ends where `outer` begins), so an out-of-order composition — the
 * exact mistake that mis-drew reticle fields — will not type-check.
 */
export declare function affineCompose<A extends CoordFrame, B extends CoordFrame, C extends CoordFrame>(outer: Affine<B, C>, inner: Affine<A, B>): Affine<A, C>;
/** Invert a transform. Every transform built here is a rotation/mirror, so it is always invertible. */
export declare function affineInvert<From extends CoordFrame, To extends CoordFrame>(m: Affine<From, To>): Affine<To, From>;
/** Transform a point (translation applies). */
export declare function affinePoint<From extends CoordFrame, To extends CoordFrame>(m: Affine<From, To>, x: number, y: number): {
    x: number;
    y: number;
};
/**
 * Transform a direction/offset (translation does NOT apply). Use for arrows and
 * any other quantity that is a vector rather than a position.
 */
export declare function affineVector<From extends CoordFrame, To extends CoordFrame>(m: Affine<From, To>, dx: number, dy: number): {
    x: number;
    y: number;
};
/**
 * True when the transform maps the x-axis onto the y-axis (the 90°/270° class,
 * with or without mirroring) — i.e. when an axis-aligned rectangle's width and
 * height must be exchanged to describe its image.
 *
 * Rotations that are not a multiple of 90° do not map an axis-aligned rectangle
 * to an axis-aligned rectangle at all; those return `false`, matching the
 * library's existing behaviour of drawing dies as unrotated rectangles.
 */
export declare function affineSwapsAxes(m: Affine): boolean;
/**
 * Check whether (x, y) — in wafer-local (pre-rotation) coordinates —
 * lies inside the wafer boundary, including the notch/flat exclusion zone.
 */
export declare function isInsideWafer(x: number, y: number, wafer: Wafer): boolean;
/**
 * Clip dies to the wafer boundary (circle + optional notch/flat).
 * When dieConfig is supplied, all four corners are checked to detect partial dies.
 * Operates on wafer-local coordinates (before applyOrientation).
 */
export declare function clipDiesToWafer(dies: Die[], wafer: Wafer, dieConfig?: DieSpec): Die[];
/**
 * Rotate all die display coordinates by wafer.orientation around wafer.center.
 * Call this after clipping and data mapping so that i/j indices remain intact.
 */
export declare function applyOrientation(dies: PositionedDie[], wafer: Wafer): PositionedDie[];
/**
 * Apply interactive transforms (rotation + flip) to die display coordinates.
 * Rotation is around wafer.center; flip mirrors through wafer.center.
 * Call this at render time — baseDies already have applyOrientation baked in.
 */
export declare function transformDies(dies: PositionedDie[], options: TransformOptions, center?: {
    x: number;
    y: number;
}): PositionedDie[];
/**
 * Attach data values to dies.
 * matchBy='xy'  — matches by wafer coordinates (v0.1 behaviour)
 * matchBy='ij'  — matches by grid indices
 *
 * Note: duplicate rows with the same coordinate produce last-wins behaviour.
 * Deduplicate `data` upstream if needed.
 */
export declare function mapDataToDies(dies: Die[], data: DataRow[], options: MapOptions): Die[];
//# sourceMappingURL=transforms.d.ts.map