import type { MarkerShape, MarkerTone } from '../annotation-marker'; export type FaceViewId = 'front' | 'profile-left' | 'profile-right'; export type FaceSide = 'left' | 'right' | 'midline'; export interface Point { x: number; y: number; } /** Raw zone shape emitted by the generator (cx/cy/side present for clickable * zones, absent on the contour guide). */ export interface FaceZoneSeed { id: string; labelKey: string; d: string; cx?: number; cy?: number; side?: FaceSide; } export interface FaceLandmarkSeed { id: string; labelKey: string; x: number; y: number; } /** A clickable face zone — a region of the canonical mesh. */ export interface FaceZone { id: string; /** `faceScheme.zone.` translation key. */ labelKey: string; /** SVG path data in the view's coordinate space. */ d: string; /** Path-bounds centroid — anchors the finding marker. */ cx: number; cy: number; /** Patient-anatomical side (not reading direction). */ side: FaceSide; } /** A canonical landmark anchor used to fit the mesh to a photo. */ export interface FaceLandmark { id: string; /** `faceScheme.landmark.` translation key. */ labelKey: string; x: number; y: number; } /** Axis-aligned box in view user units. */ export interface Box { x: number; y: number; width: number; height: number; } export interface FaceView { id: FaceViewId; /** `faceScheme.view.` translation key. */ labelKey: string; viewBox: string; /** Photoless window: measured identity-affine mesh bbox + 24-unit pad, pre-mirrored for the right profile. Re-measure if the mesh changes. */ schematicViewBox: string; /** Raw identity-affine mesh bbox in LOCAL (un-mirrored) coords — the auto-framing anchor. Same measurement as schematicViewBox, no pad. */ meshContentBox: Box; /** Pivot for the registration controls (the face centre). */ pivot: Point; /** The face outline, drawn as a non-interactive guide. */ contour: { id: string; labelKey: string; d: string; }; zones: FaceZone[]; /** Canonical anchors for landmark fitting / auto-detect. */ landmarks: FaceLandmark[]; /** Render the mesh horizontally mirrored (right profile = mirrored left). */ mirror?: boolean; } export declare const FACE_VIEWS: Record; export declare const FACE_VIEW_IDS: FaceViewId[]; export type FaceFindingType = 'observation' | 'treatment' | 'concern' | 'note'; export declare const FACE_FINDING_TYPES: FaceFindingType[]; export declare const FACE_FINDING_MARKER: Record; export interface FaceFinding { type: FaceFindingType; /** Optional free-text note (PHI — never surfaced in the agent snapshot). */ note?: string; } /** Findings keyed by zone id. */ export type FaceChart = Record; /** The drawing tools the annotation layer exposes. */ export type FaceAnnotationTool = 'marker' | 'pen' | 'highlighter' | 'arrow' | 'line' | 'rect' | 'ellipse' | 'text'; /** Toolbar order. */ export declare const FACE_ANNOTATION_TOOLS: FaceAnnotationTool[]; /** Shared fields on every annotation. `tone` is decorative reinforcement of the * KIND (shape / stroke style is the accessible channel), never colour alone. */ interface FaceAnnotationBase { /** Stable id — the kit generates one when the consumer doesn't supply it. */ id: string; tone?: MarkerTone; /** Linked clinical zone — a zone id, `null` = assessed outside every zone, * `undefined` = never assessed. Sticky: not re-assessed on mesh re-fit. */ zoneId?: string | null; } /** A shape glyph dropped at an arbitrary point (NOT snapped to a zone). */ export interface MarkerAnnotation extends FaceAnnotationBase { kind: 'marker'; x: number; y: number; shape: MarkerShape; } /** A freehand stroke — `pen` (opaque, thin) or `highlighter` (translucent, wide). */ export interface InkAnnotation extends FaceAnnotationBase { kind: 'pen' | 'highlighter'; points: Point[]; } /** A two-point vector primitive drawn by dragging. */ export interface ShapeAnnotation extends FaceAnnotationBase { kind: 'arrow' | 'line' | 'rect' | 'ellipse'; from: Point; to: Point; } /** A typed callout pinned to a point. `text` is PHI — never surfaced in the * agent snapshot, never persisted/logged/transmitted by the kit. */ export interface TextAnnotation extends FaceAnnotationBase { kind: 'text'; x: number; y: number; text: string; } export type FaceAnnotation = MarkerAnnotation | InkAnnotation | ShapeAnnotation | TextAnnotation; /** The distinct annotation kinds — the agent snapshot reports counts per kind. */ export type FaceAnnotationKind = FaceAnnotation['kind']; /** An annotation as passed to `addAnnotation` — the `id` is optional (the kit * generates a stable one when omitted). */ export type FaceAnnotationInput = (Omit & { id?: string; }) | (Omit & { id?: string; }) | (Omit & { id?: string; }) | (Omit & { id?: string; }); /** Annotations keyed by view — each view draws over its own photo. */ export type FaceAnnotations = Partial>; /** The annotation kind each tool produces. */ export declare const ANNOTATION_TOOL_KIND: Record; /** True when the tool draws by dragging between two points. */ export declare function isShapeTool(tool: FaceAnnotationTool): boolean; /** True when the tool captures a freehand point stream. */ export declare function isInkTool(tool: FaceAnnotationTool): boolean; /** True when the tool places a single-click annotation (marker / text). */ export declare function isPointTool(tool: FaceAnnotationTool): boolean; /** SVG path `d` for a freehand point list (a polyline). Pure — no React/DOM. */ export declare function inkPath(points: Point[]): string; /** SVG path `d` for a two-point shape (arrowhead drawn separately). Pure. */ export declare function shapePath(kind: ShapeAnnotation['kind'], from: Point, to: Point): string; /** SVG path `d` for a two-barb arrowhead at `to`, pointing along from→to. Pure. */ export declare function arrowHeadPath(from: Point, to: Point, size?: number): string; /** Axis-aligned bounding box of an annotation, in viewBox units. Pure. */ export declare function annotationBBox(a: FaceAnnotation): { x: number; y: number; width: number; height: number; }; /** Bounding-box centre of an annotation — anchors its focus outline / label. */ export declare function annotationAnchor(a: FaceAnnotation): Point; /** Stroke width of an annotation's invisible pointer hit clone, in viewBox units. */ export declare const ANN_HIT_STROKE = 28; /** Pointer hit radius around a point annotation (marker), in viewBox units. */ export declare const ANN_HIT_RADIUS = 26; export type AnnotationHitGeometry = { type: 'stroke'; d: string; } | { type: 'circle'; cx: number; cy: number; r: number; } | { type: 'rect'; x: number; y: number; width: number; height: number; }; /** Geometry for an annotation's transparent pointer hit target. Stroke kinds * get a fat clone of their own path (hit on the stroke only — a hollow rect / * ellipse interior stays click-through to the zones); marker gets a hit * circle, text an estimated glyph box. Pure. */ export declare function annotationHitGeometry(a: FaceAnnotation, textSize?: number): AnnotationHitGeometry; export type FaceAffine = readonly [ number, number, number, number, number, number ]; export declare const IDENTITY_AFFINE: FaceAffine; /** Per-view registration matrices. */ export type FaceRegistration = Partial>; export declare function affineToString(m: FaceAffine): string; export declare function applyAffine(m: FaceAffine, p: Point): Point; /** Inverse of a (non-degenerate) affine: photo space → mesh space. Pure. */ export declare function invertAffine(m: FaceAffine): FaceAffine; /** Share of an auto-framed window the registered mesh occupies. 0.84 is the * front mesh's natural canvas fill, so the front keeps its full frame and * the profiles (smaller canvas share) zoom in to match it. */ export declare const AUTO_FRAME_FRACTION = 0.84; /** * Photo window that frames the registered mesh at a consistent size — the * three-up "equal heads in equal cards" window. Transforms `meshBox` through * `affine` (reflecting for the mirrored right profile), then returns a * canvas-aspect window centred on the mesh, sized so the mesh fills * `fraction` of it, shifted (never shrunk) to stay inside the canvas. * Falls back to the full canvas when the window cannot fit or the input is * degenerate. Pure; a window only — never a transform, so mesh, photo and * annotation geometry are untouched. */ export declare function autoFrameViewBox(meshBox: Box, affine: FaceAffine, canvas: { width: number; height: number; }, opts?: { mirror?: boolean; fraction?: number; }): string; /** User zoom state: magnification `k` (1 = base window) plus the window * origin as fractions of the pannable range, so it survives base-window * changes (auto-frame recomputes, registration commits). */ export interface FaceZoomState { k: number; fx: number; fy: number; } export declare const FACE_ZOOM_MIN = 1; export declare const FACE_ZOOM_MAX = 8; export declare const FACE_ZOOM_RESET: FaceZoomState; /** Parse a `"x y w h"` viewBox string into a Box. */ export declare function parseViewBox(vb: string): Box; /** Stringify a Box as a rounded viewBox value. */ export declare function boxToViewBox(b: Box): string; /** The visible window for a zoom state: the base window shrunk by `k`, * positioned by the fractional origin. `k <= 1` returns the base. */ export declare function composeZoomWindow(base: Box, z: FaceZoomState): Box; /** Zoom by `factor` keeping the user-space point `p` fixed in the window. * Clamped to [FACE_ZOOM_MIN, FACE_ZOOM_MAX] and to the base bounds. */ export declare function zoomWindowAround(base: Box, z: FaceZoomState, p: { x: number; y: number; }, factor: number): FaceZoomState; /** Pan the zoom window by a user-space delta (drag the content by `dx,dy` * moves the window the opposite way), clamped to the base bounds. */ export declare function panZoomWindow(base: Box, z: FaceZoomState, dx: number, dy: number): FaceZoomState; /** Editable decomposition of an affine about a pivot — what the numeric * registration panel exposes. Angles are degrees. */ export interface AffineParts { scaleX: number; scaleY: number; /** degrees */ rotation: number; /** degrees */ shear: number; translateX: number; translateY: number; } export declare function composeAffine(parts: AffineParts, pivot: Point): FaceAffine; export declare function decomposeAffine(m: FaceAffine, pivot: Point): AffineParts; /** * Least-squares 6-DOF affine mapping `src` → `dst` (≥3 non-collinear pairs). * Independent horizontal + vertical scale fall out of the fit — this is the * shared solver behind both manual landmark drag and face-detector auto-fit. * Returns the identity if the system is degenerate. */ export declare function solveAffine(src: Point[], dst: Point[]): FaceAffine; export {}; //# sourceMappingURL=face-scheme-data.d.ts.map