/** * Internal priority-label placement for the DS PriorityMatrix component. * * STRUCTURAL TWIN of packages/dataviz-core/src/priorityMatrix.ts: same * algorithm, same types, same behaviour. Duplicated (not imported) on purpose: * the DS layer must not depend on @sentropic/dataviz-core, so this copy is * kept in sync by hand. A shared golden-vector test pins all five copies to * identical placements. Not exported from the package barrel (internal * component use only). */ /** * Priority-matrix label placement (2x2 value/effort matrix). * * Pure TypeScript, no DOM. Given anchor points in frame pixels, label box * sizes and a plot frame, returns label positions with leader lines back to * their anchors. Deterministic: a fixed-seed PRNG drives the simulated- * annealing refinement (d3-labeler style energy), so repeated calls with * identical inputs return byte-identical outputs. * * Pipeline: greedy 8-candidate seeding around each anchor, then annealing over * overlap area + anchor distance + point-cover + out-of-frame penalties, with * hard clamping into the frame after every move. * * Best effort, not a guarantee: on dense sets labels may still overlap each * other, cover points, cross the threshold lines or hide a quadrant name. * Known limits (recommended cap around 50 points, estimated boxes, fixed * frame, input-order dependence) are documented on `placePriorityLabels`. */ /** One matrix point in DATA space (0-100 per axis). */ export interface PriorityMatrixPoint { x: number; y: number; label: string; } /** Label box size in frame pixels (measured or estimated by the caller). */ export interface PriorityMatrixBox { w: number; h: number; } /** Plot frame in pixels: outer size plus inner plot margins. */ export interface PriorityMatrixFrame { width: number; height: number; marginLeft: number; marginTop: number; plotWidth: number; plotHeight: number; } /** * Fixed obstacle rectangle in frame pixels: a threshold keep-out band or a * quadrant-name box derived by the caller from its own layout. Overlap with an * obstacle is a soft cost weighted 4x above label/label overlap per px2, so a * label crosses a thin threshold band only when every alternative is worse. * Degenerate rectangles (non-finite or non-positive size) are ignored. */ export interface PriorityMatrixObstacle { x: number; y: number; w: number; h: number; } /** Placed label: top-left box origin plus leader segment endpoints. */ export interface PriorityMatrixPlacement { /** Index into the input points array. */ index: number; /** Top-left box origin, clamped inside the frame. */ x: number; y: number; w: number; h: number; /** Anchor (point) position in frame pixels. */ anchorX: number; anchorY: number; /** Leader line from box edge (x1,y1) to anchor (x2,y2). */ leaderX1: number; leaderY1: number; leaderX2: number; leaderY2: number; /** Quadrant id of the datum. */ quadrant: PriorityQuadrant; } export type PriorityQuadrant = 'quick-wins' | 'major-projects' | 'wait' | 'drop'; /** * Quadrant of a datum given thresholds (defaults 50/50). * x = complexity (low left), y = value (high top). */ export declare function priorityQuadrant(x: number, y: number, xThreshold?: number, yThreshold?: number): PriorityQuadrant; /** * Rough box estimate from a label string when the caller has no measurement. * Single line up to 12 chars, two lines beyond; capped at 120px wide. * * This is a glyph-count MODEL, not a measurement: it assumes narrow latin * glyphs at 11px. Wide latin runs overflow the box (Inter 11px measured in * Chromium, review figures: "MMMMMMMMMM" by 17.7px, "WWWWWWWWWWWW" by 26.7px; * same-direction check with DejaVu Sans 11px advances: 12.9px and 34.5px) and * CJK text overflows further ("データ可視化の基盤" by 24.0px in Inter 11px). * The non-overlap behaviour only holds for narrow latin; there is currently * no prop to pass caller-measured boxes through the DS components. */ export declare function estimatePriorityLabelBox(label: string): PriorityMatrixBox; export interface PlacePriorityLabelsOptions { xThreshold?: number; yThreshold?: number; /** * PRNG seed for the annealing refinement; default 42. Identical inputs with * the same seed return byte-identical outputs. Note: output is NOT stable * across input orders — reversing the input array moves every label (13/13 * on the reference set, up to 177.4px, leaders 343px to 306px). Sort or * stabilise the input order upstream when stability across data refreshes * matters. */ seed?: number; /** * Fixed obstacle rectangles (threshold bands, quadrant-name boxes) in frame * pixels. Overlap with an obstacle costs 4x a label/label overlap per px2. */ obstacles?: readonly PriorityMatrixObstacle[]; } /** * Place one label per point. Never throws on degenerate input: non-finite * coordinates fall back to the plot origin, empty arrays yield []. * * Cost model: label/label overlap area, point-cover (a label box covering ANY * anchor — including its own — costs 300 in seeding, 800 in annealing, so * opaque label boxes avoid hiding points), obstacle overlap, anchor distance, * and out-of-frame penalties, refined by fixed-schedule simulated annealing. * * Complexity is O(n^2) per energy evaluation with max(1500, n*120) iterations, * i.e. O(n^3) overall (the 1500 floor only dominates below 13 points, since n*120 passes 1500 at n=13). Measured * (Node 22, desktop class, seeded random sets with long 2-line labels): * n=13 in 3.2ms, n=30 in 13.7ms, n=50 in 36.8ms, n=80 in 137.8ms, n=100 in * 258.9ms. Overlap counts are set-dependent (0/1/15/83/176 on that * adversarial set). Recommended cap: around 50 points; beyond that both * latency and overlaps degrade. * * Frame floor: with the shipped DS configuration (splitLabel boxes, threshold * and name obstacles) the 13-point reference set is clean at 640x400, shows * 1 overlap at 480x300 and 400x250, 9 overlaps at 320x200, 35 at 200x140, and * 78 overlaps plus 12 out-of-plot boxes at 120x90. Do not render below * roughly 480x300. */ export declare function placePriorityLabels(points: readonly PriorityMatrixPoint[], boxes: readonly PriorityMatrixBox[], frame: PriorityMatrixFrame, opts?: PlacePriorityLabelsOptions): PriorityMatrixPlacement[]; //# sourceMappingURL=priorityLabels.d.ts.map