@data-profile/fingerprint 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../temp/src/palette.ts", "../temp/src/hash.ts", "../temp/src/form.ts", "../temp/src/ridges.ts", "../temp/src/slots.ts", "../temp/src/layout.ts", "../temp/src/opacity.ts", "../temp/src/callouts.ts", "../temp/src/describe.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * The default categorical palette, for consumers that give categories no colour. Ten hues, chosen\n * to stay distinguishable on light and dark backgrounds; beyond ten they repeat, which is why the\n * legend and list, not colour, carry identity.\n */\nexport const DEFAULT_PALETTE = [\n \"#2f6fde\",\n \"#2bb3e6\",\n \"#f26b3a\",\n \"#f5b82e\",\n \"#14a39a\",\n \"#7a5af5\",\n \"#3fae6b\",\n \"#d9488f\",\n \"#8c6d46\",\n \"#5e8c99\",\n] as const;\n\n/** Unknowns are drawn in a neutral tone, outside the palette, so they never look like a category. */\nexport const UNKNOWN_COLOR = \"#b8bcc4\";\n",
6
+ "/** FNV-1a, 32-bit. Stable across runtimes, so a seed draws the same picture everywhere. */\nexport function stableHash(value: string): number {\n let hash = 2166136261;\n for (let i = 0; i < value.length; i++) hash = Math.imul(hash ^ value.charCodeAt(i), 16777619);\n return hash >>> 0;\n}\n\n/** A value in [0, 1) derived from a key: the only source of variation in a layout. */\nexport const unitHash = (key: string) => stableHash(key) / 2 ** 32;\n\n/** Code-unit order, so results never depend on the runtime's locale. */\nexport const compareIds = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0);\n",
7
+ "import { unitHash } from \"./hash\";\nimport type { FingerprintDimension, FingerprintItem, FingerprintForm } from \"./types\";\n\nconst clamp = (value: number, low: number, high: number) => Math.min(high, Math.max(low, value));\n/** Coarse steps, so a collection's fingerprint holds still as it grows by an item or two. */\nconst quantise = (value: number, step = 0.2) => Math.round(value / step) * step;\n\n/** A plausible shape from a seed alone: the default when the consumer gives none. */\nexport function formFromSeed(seed: string): FingerprintForm {\n const h = (key: string) => unitHash(`${seed}:${key}`);\n return {\n pattern: \"loop\",\n hand: h(\"hand\") < 0.5 ? -1 : 1,\n coreX: quantise(h(\"x\") * 2 - 1) * 0.5,\n coreY: quantise(h(\"y\") * 2 - 1) * 0.5,\n spread: quantise(h(\"spread\")),\n tilt: Math.round((h(\"tilt\") - 0.5) * 12),\n };\n}\n\nexport function resolveForm(seed: string, shape: Partial<FingerprintForm> = {}): FingerprintForm {\n const resolved = { ...formFromSeed(seed), ...shape };\n return {\n ...resolved,\n coreX: clamp(resolved.coreX, -1, 1),\n coreY: clamp(resolved.coreY, -1, 1),\n spread: clamp(resolved.spread, 0, 1),\n tilt: clamp(resolved.tilt, -10, 10),\n };\n}\n\n/** Mean position (0 to 1) of known values within a dimension's category order, or 0.5. */\nfunction lean(items: readonly FingerprintItem[], dimension: FingerprintDimension | undefined) {\n if (!dimension || dimension.categories.length < 2) return 0.5;\n const index = new Map(\n dimension.categories.map((c, i) => [c.id, i / (dimension.categories.length - 1)]),\n );\n let sum = 0;\n let n = 0;\n for (const item of items) {\n const at = index.get(item.values[dimension.id] ?? \"\");\n if (at !== undefined) {\n sum += at;\n n += 1;\n }\n }\n return n ? sum / n : 0.5;\n}\n\n/**\n * A shape that is the collection's own: derived only from the data, so the same collection always\n * draws the same fingerprint, and it changes slowly as the collection grows. It is a signature,\n * not a chart; say so beside it. The mapping, which a caption can state:\n *\n * - **Pattern.** An arch while there are fewer than 12 items; then a loop when one category of the\n * first dimension holds 40% or more of the items (a focused collection); otherwise a whorl.\n * - **Core across, and loop direction.** Where the second dimension's values sit, on average, in its\n * category order: earlier categories pull the core left, later ones right.\n * - **Core height.** The same for the third dimension.\n * - **Spread.** The share of all categories that have at least one item: broader reads looser.\n * - **Tilt.** From the seed only, so two equal collections still differ slightly.\n */\nexport function formFromItems(\n items: readonly FingerprintItem[],\n dimensions: readonly FingerprintDimension[],\n seed = \"fingerprint\",\n): FingerprintForm {\n const [first, second, third] = dimensions;\n let top = 0;\n if (first) {\n const counts = new Map<string, number>();\n for (const item of items) {\n const value = item.values[first.id];\n if (value != null) counts.set(value, (counts.get(value) ?? 0) + 1);\n }\n top = items.length ? Math.max(0, ...counts.values()) / items.length : 0;\n }\n const total = dimensions.reduce((sum, d) => sum + d.categories.length, 0);\n const used = dimensions.reduce(\n (sum, d) =>\n sum + d.categories.filter((c) => items.some((item) => item.values[d.id] === c.id)).length,\n 0,\n );\n const across = lean(items, second);\n return resolveForm(seed, {\n pattern: items.length < 12 ? \"arch\" : top >= 0.4 ? \"loop\" : \"whorl\",\n hand: across < 0.5 ? -1 : 1,\n coreX: quantise((across - 0.5) * 2),\n coreY: quantise((lean(items, third) - 0.5) * 2),\n spread: total ? quantise(used / total) : 0.5,\n });\n}\n",
8
+ "import type { FingerprintForm, Point } from \"./types\";\n\n/** The fixed frame every layout is drawn in: a portrait fingertip with a small margin. */\nexport const FRAME = { width: 400, height: 520, cx: 200, cy: 260, rx: 180, ry: 240 } as const;\n\nconst DEG = Math.PI / 180;\n\n/** Normalised radius of the fingertip outline through a point: 1 on the outline. */\nexport const outlineRadius = (x: number, y: number) =>\n Math.hypot((x - FRAME.cx) / FRAME.rx, (y - FRAME.cy) / FRAME.ry);\n\nconst smoothstep = (a: number, b: number, x: number) => {\n const t = Math.min(1, Math.max(0, (x - a) / (b - a)));\n return t * t * (3 - 2 * t);\n};\n\n/** Where the ridges curl (cores) and split three ways (deltas). */\nexport interface Singularities {\n centre: Point;\n cores: Point[];\n deltas: Point[];\n}\n\n/**\n * Places a shape's singular points. A loop has one core and one delta below it to one side; a\n * whorl two cores and a delta either side; an arch none.\n */\nexport function singularities(shape: FingerprintForm): Singularities {\n const { rx, ry, cx, cy } = FRAME;\n const centre: Point = [cx + shape.coreX * 0.12 * rx, cy - 0.1 * ry + shape.coreY * 0.1 * ry];\n const [x, y] = centre;\n if (shape.pattern === \"arch\") return { centre, cores: [], deltas: [] };\n if (shape.pattern === \"loop\")\n return {\n centre,\n cores: [centre],\n deltas: [\n [x + shape.hand * (0.4 + 0.25 * shape.spread) * rx, y + (0.38 + 0.12 * shape.spread) * ry],\n ],\n };\n const twist = 0.06 * rx;\n return {\n centre,\n cores: [\n [x - twist, y - shape.hand * 0.025 * ry],\n [x + twist, y + shape.hand * 0.025 * ry],\n ],\n deltas: [\n [x - (0.5 + 0.2 * shape.spread) * rx, y + 0.42 * ry],\n [x + (0.5 + 0.2 * shape.spread) * rx, y + 0.42 * ry],\n ],\n };\n}\n\n/**\n * Ridge direction at a point, in radians, modulo pi.\n *\n * Inside, the Sherlock-Monro model: half the sum of the angles to each core minus half the sum to\n * each delta, which is what makes ridges curl round a core and split at a delta. An arch has no\n * singular points, so its ridges follow a raised bump instead. Near the outline, and mostly over the\n * top, ridges bend to follow it, as they do round a fingertip.\n */\nexport function orientation(\n x: number,\n y: number,\n shape: FingerprintForm,\n s: Singularities,\n): number {\n const tilt = shape.tilt * DEG;\n let theta: number;\n if (shape.pattern === \"arch\") {\n const width = (0.35 + 0.25 * shape.spread) * FRAME.rx;\n const u = (x - s.centre[0]) / width;\n const height = 0.3 * FRAME.ry * Math.max(0, 1 - Math.abs(y - s.centre[1]) / FRAME.ry);\n theta = tilt + Math.atan(((2 * height * u) / width) * Math.exp(-u * u));\n } else {\n let sum = 0;\n for (const [px, py] of s.cores) sum += Math.atan2(y - py, x - px);\n for (const [px, py] of s.deltas) sum -= Math.atan2(y - py, x - px);\n theta = tilt + sum / 2;\n }\n // Blend towards the outline's tangent, as doubled angles so pi-periodic directions average.\n const r = outlineRadius(x, y);\n // Strongest over the top of the fingertip, easing to 40% below it: a step here kinks ridges.\n const over = smoothstep(FRAME.cy + 0.25 * FRAME.ry, FRAME.cy - 0.25 * FRAME.ry, y);\n const weight = smoothstep(0.68, 1, r) * (0.4 + 0.6 * over);\n if (weight === 0) return theta;\n const tangent = Math.atan2((x - FRAME.cx) / FRAME.rx ** 2, -(y - FRAME.cy) / FRAME.ry ** 2);\n const vx = (1 - weight) * Math.cos(2 * theta) + weight * Math.cos(2 * tangent);\n const vy = (1 - weight) * Math.sin(2 * theta) + weight * Math.sin(2 * tangent);\n return Math.atan2(vy, vx) / 2;\n}\n\n/** A spatial hash of every accepted ridge point, for the spacing checks. */\nclass PointGrid {\n private cells = new Map<number, { x: number; y: number; line: number; index: number }[]>();\n constructor(private size: number) {}\n private key = (gx: number, gy: number) => gx * 4096 + gy;\n /** Returns the cell key, so a rejected ridge can be taken out again cheaply. */\n add(x: number, y: number, line: number, index: number) {\n const k = this.key(Math.floor(x / this.size), Math.floor(y / this.size));\n const cell = this.cells.get(k);\n if (cell) cell.push({ x, y, line, index });\n else this.cells.set(k, [{ x, y, line, index }]);\n return k;\n }\n removeLine(line: number, keys: Iterable<number>) {\n for (const k of new Set(keys)) {\n const kept = (this.cells.get(k) ?? []).filter((p) => p.line !== line);\n if (kept.length) this.cells.set(k, kept);\n else this.cells.delete(k);\n }\n }\n /** True when a point closer than `distance` passes `counts`. `distance` must not exceed the cell size. */\n near(\n x: number,\n y: number,\n distance: number,\n counts: (p: { line: number; index: number }) => boolean,\n ) {\n const gx = Math.floor(x / this.size);\n const gy = Math.floor(y / this.size);\n for (let dx = -1; dx <= 1; dx++)\n for (let dy = -1; dy <= 1; dy++)\n for (const p of this.cells.get(this.key(gx + dx, gy + dy)) ?? [])\n if ((p.x - x) ** 2 + (p.y - y) ** 2 < distance ** 2 && counts(p)) return true;\n return false;\n }\n}\n\n/** Ridges stop this far inside the outline. */\nconst OUTLINE_LIMIT = 0.985;\n\n/**\n * Evenly spaced ridges along the shape's flow field (Jobard and Lefer's streamline placement),\n * core outwards. Each ridge is traced both ways from a seed until it would come within `spacing`\n * of another ridge, of a singular point, of a distant part of itself, or of the outline; new seeds\n * are tried one spacing either side of every accepted ridge. A dropped-out ridge end is a ridge\n * ending, exactly as in a real print.\n *\n * Every point of every ridge is at least about `spacing` from every other ridge, which is what lets\n * marks of that size sit on them without touching.\n */\nexport function ridges(shape: FingerprintForm, spacing: number): Point[][] {\n const s = singularities(shape);\n const step = spacing * 0.3;\n const selfGap = Math.ceil((3 * spacing) / step);\n const minLength = Math.ceil((2 * spacing) / step);\n const maxSteps = Math.ceil((4 * Math.PI * (FRAME.rx + FRAME.ry)) / step);\n const grid = new PointGrid(spacing);\n const lines: Point[][] = [];\n const singular = [...s.cores, ...s.deltas];\n\n const free = (x: number, y: number, line: number, index: number) =>\n outlineRadius(x, y) <= OUTLINE_LIMIT &&\n singular.every(([px, py]) => Math.hypot(x - px, y - py) > spacing * 0.6) &&\n !grid.near(x, y, spacing, (p) => p.line !== line || Math.abs(p.index - index) > selfGap);\n\n const direction = (x: number, y: number, previous: Point): Point => {\n const theta = orientation(x, y, shape, s);\n const v: Point = [Math.cos(theta), Math.sin(theta)];\n return v[0] * previous[0] + v[1] * previous[1] < 0 ? [-v[0], -v[1]] : v;\n };\n\n const trace = (seed: Point): Point[] | null => {\n const line = lines.length;\n const theta = orientation(seed[0], seed[1], shape, s);\n const keys = [grid.add(seed[0], seed[1], line, 0)];\n const halves = ([1, -1] as const).map((sign) => {\n const points: Point[] = [];\n let p = seed;\n let previous: Point = [sign * Math.cos(theta), sign * Math.sin(theta)];\n for (let i = 1; i <= maxSteps; i++) {\n // Second-order Runge-Kutta: sample the direction half a step ahead.\n const v1 = direction(p[0], p[1], previous);\n const v2 = direction(p[0] + (v1[0] * step) / 2, p[1] + (v1[1] * step) / 2, v1);\n // A ridge turns gently; a sharp turn in one step means the field folds here, so end it.\n if (v2[0] * previous[0] + v2[1] * previous[1] < 0.8) break;\n const q: Point = [p[0] + v2[0] * step, p[1] + v2[1] * step];\n if (!free(q[0], q[1], line, sign * i)) break;\n keys.push(grid.add(q[0], q[1], line, sign * i));\n points.push(q);\n p = q;\n previous = v2;\n }\n return points;\n });\n const points = [...halves[1]!.reverse(), seed, ...halves[0]!];\n if (points.length < minLength) {\n grid.removeLine(line, keys);\n return null;\n }\n return points;\n };\n\n const queue: Point[] = [];\n const seedFrom = (points: Point[]) => {\n const every = Math.max(1, Math.round(spacing / step / 2));\n for (let i = 1; i < points.length - 1; i += every) {\n const [ax, ay] = points[i - 1]!;\n const [bx, by] = points[i + 1]!;\n const length = Math.hypot(bx - ax, by - ay) || 1;\n const [nx, ny] = [-(by - ay) / length, (bx - ax) / length];\n const [x, y] = points[i]!;\n queue.push([x + nx * spacing, y + ny * spacing], [x - nx * spacing, y - ny * spacing]);\n }\n };\n const attempt = (seed: Point) => {\n if (!free(seed[0], seed[1], -1, 0)) return;\n const points = trace(seed);\n if (!points) return;\n lines.push(points);\n seedFrom(points);\n };\n let next = 0;\n const drain = () => {\n while (next < queue.length) attempt(queue[next++]!);\n };\n\n // Start just above the core and grow outwards; then sweep for any region the growth missed.\n queue.push([s.centre[0], s.centre[1] - spacing]);\n drain();\n const sweep: Point[] = [];\n for (let x = FRAME.cx - FRAME.rx; x <= FRAME.cx + FRAME.rx; x += spacing / 2)\n for (let y = FRAME.cy - FRAME.ry; y <= FRAME.cy + FRAME.ry; y += spacing / 2)\n if (outlineRadius(x, y) <= OUTLINE_LIMIT) sweep.push([x, y]);\n const distance = ([x, y]: Point) => Math.hypot(x - s.centre[0], y - s.centre[1]);\n sweep.sort((a, b) => distance(a) - distance(b) || a[0] - b[0] || a[1] - b[1]);\n for (const seed of sweep) {\n attempt(seed);\n drain();\n }\n\n return orderFromCore(lines, s.centre);\n}\n\n/**\n * Ridges sorted by how close they come to the core, each running clockwise round it, so marks laid\n * along them in turn form runs from the core outwards.\n */\nfunction orderFromCore(lines: Point[][], [cx, cy]: Point): Point[][] {\n const reach = (line: Point[]) => Math.min(...line.map(([x, y]) => Math.hypot(x - cx, y - cy)));\n const swept = (line: Point[]) => {\n let total = 0;\n for (let i = 1; i < line.length; i++) {\n let d =\n Math.atan2(line[i]![1] - cy, line[i]![0] - cx) -\n Math.atan2(line[i - 1]![1] - cy, line[i - 1]![0] - cx);\n if (d > Math.PI) d -= 2 * Math.PI;\n if (d < -Math.PI) d += 2 * Math.PI;\n total += d;\n }\n return total;\n };\n return lines\n .map((line) => ({ line: swept(line) < 0 ? [...line].reverse() : line, reach: reach(line) }))\n .sort(\n (a, b) => a.reach - b.reach || a.line[0]![0] - b.line[0]![0] || a.line[0]![1] - b.line[0]![1],\n )\n .map(({ line }) => line);\n}\n",
9
+ "import type { Point } from \"./types\";\n\nexport function polylineLength(points: readonly Point[]): number {\n let length = 0;\n for (let i = 1; i < points.length; i++)\n length += Math.hypot(points[i]![0] - points[i - 1]![0], points[i]![1] - points[i - 1]![1]);\n return length;\n}\n\n/**\n * Splits `count` across ridges so the most crowded ridge is as uncrowded as possible: every ridge\n * first gets as many slots as fit at the average spacing, then each remaining slot goes to the\n * ridge whose spacing it shrinks least (the D'Hondt rule). Ties go to the earlier ridge, so the\n * result is stable.\n */\nfunction allocate(lengths: readonly number[], count: number): number[] {\n const total = lengths.reduce((sum, length) => sum + length, 0);\n if (total === 0 || count === 0) return lengths.map(() => 0);\n const average = total / count;\n const counts = lengths.map((length) => Math.floor(length / average));\n for (let left = count - counts.reduce((sum, n) => sum + n, 0); left > 0; left--) {\n let best = 0;\n for (let i = 1; i < lengths.length; i++)\n if (lengths[i]! / (counts[i]! + 1) > lengths[best]! / (counts[best]! + 1)) best = i;\n counts[best]! += 1;\n }\n return counts;\n}\n\n/** The point and direction (degrees) at arc length `at` along a polyline. */\nfunction pointAt(line: readonly Point[], at: number) {\n let walked = 0;\n for (let i = 1; i < line.length; i++) {\n const [x0, y0] = line[i - 1]!;\n const [x1, y1] = line[i]!;\n const segment = Math.hypot(x1 - x0, y1 - y0);\n if (at <= walked + segment || i === line.length - 1) {\n const f = segment === 0 ? 0 : Math.min(1, (at - walked) / segment);\n return {\n x: x0 + (x1 - x0) * f,\n y: y0 + (y1 - y0) * f,\n rotation: (Math.atan2(y1 - y0, x1 - x0) * 180) / Math.PI,\n };\n }\n walked += segment;\n }\n const [x, y] = line[0]!;\n return { x, y, rotation: 0 };\n}\n\n/**\n * `count` positions along the ridges, in ridge order. Each ridge gets slots by `allocate` and spaces them evenly over its whole length, so a closed ring has no crowded seam where\n * its end meets its start. `pitch` is the smallest spacing between\n * neighbours on any ridge.\n */\nexport function slotsAlong(lines: readonly Point[][], count: number) {\n const lengths = lines.map(polylineLength);\n const counts = allocate(lengths, count);\n const slots: { x: number; y: number; rotation: number }[] = [];\n let pitch = Infinity;\n lines.forEach((line, k) => {\n const n = counts[k]!;\n if (n === 0) return;\n const step = lengths[k]! / n;\n // A ridge with one slot has no neighbour along it, so its length says nothing about crowding.\n if (n > 1) pitch = Math.min(pitch, step);\n for (let i = 0; i < n; i++) slots.push(pointAt(line, (i + 0.5) * step));\n });\n // Infinity when no ridge holds two slots: nothing along a ridge then limits mark size.\n return { slots, pitch };\n}\n",
10
+ "import { compareIds } from \"./hash\";\nimport { DEFAULT_PALETTE, UNKNOWN_COLOR } from \"./palette\";\nimport { FRAME, ridges } from \"./ridges\";\nimport { resolveForm } from \"./form\";\nimport { polylineLength, slotsAlong } from \"./slots\";\nimport type {\n FingerprintChannel,\n FingerprintDimension,\n FingerprintEncoding,\n FingerprintItem,\n FingerprintLayout,\n FingerprintLegendEntry,\n FingerprintLegendGroup,\n FingerprintNode,\n FingerprintForm,\n LayoutInput,\n Point,\n} from \"./types\";\n\n/** The id of the category unknown values fall into. Reserved: a dimension may not use it. */\nexport const UNKNOWN_CATEGORY_ID = \"~unknown\";\n\n/**\n * 600 marks: the most SVG glides smoothly between views (measured in Chrome: 56-59 fps at 400,\n * 43-50 at 800, 16-27 at 1,500), and large enough that a mark stays legible at phone width.\n */\nconst DEFAULTS = { seed: \"fingerprint\", maxMarks: 600, minRidges: 6 } as const;\n/** A mark fills this share of the space between its neighbours. */\nconst MARK_FILL = 0.85;\n\n/**\n * Data the layout cannot draw truthfully: duplicate item ids, a weight that is not positive, a\n * reserved or duplicated category id, an encoding naming no dimension, or lightness on an unordered\n * dimension. `layoutFingerprint` throws it; `useFingerprint` returns it as `error`.\n */\nexport class FingerprintDataError extends Error {\n override name = \"FingerprintDataError\";\n}\n\n/** A usable count, or undefined for anything else (undefined, NaN, zero, negative). */\nconst positive = (value: number | undefined) =>\n value !== undefined && Number.isFinite(value) && value >= 1 ? Math.floor(value) : undefined;\n\n/** Beyond these, a property stops telling categories apart: palette colours repeat, and the default renderer has five shapes. */\nconst DISTINCT = { color: 10, shape: 5 } as const;\n\n/** What a layout needs of an items array whatever the view: validated once, then reused. */\ninterface PreparedItems {\n /** `idRank[i]` is item i's place in id order: the stable tie-break. */\n idRank: Uint32Array;\n /** Each dimension's category rank per item; unknown ranks after every category. */\n ranks: WeakMap<FingerprintDimension, Int32Array>;\n}\n\n/**\n * Items arrays are treated as immutable: switching view lays out the same array again, so its\n * validation, id order and per-dimension ranks are worked out once and kept with it. This is what\n * keeps a view change fast for a very large collection; only a sort of numbers remains.\n */\nconst prepared = new WeakMap<readonly FingerprintItem[], PreparedItems>();\nfunction prepare(items: readonly FingerprintItem[]): PreparedItems {\n const hit = prepared.get(items);\n if (hit) return hit;\n const seen = new Set<string>();\n for (const item of items) {\n if (seen.has(item.id)) throw new FingerprintDataError(`Duplicate item id \"${item.id}\"`);\n seen.add(item.id);\n const weight = item.weight ?? 1;\n if (!Number.isFinite(weight) || weight <= 0)\n throw new FingerprintDataError(\n `Item \"${item.id}\" has weight ${weight}; weights must be positive`,\n );\n }\n const byId = Uint32Array.from(items.keys()).sort((a, b) =>\n compareIds(items[a]!.id, items[b]!.id),\n );\n const idRank = new Uint32Array(items.length);\n byId.forEach((index, rank) => (idRank[index] = rank));\n const result = { idRank, ranks: new WeakMap<FingerprintDimension, Int32Array>() };\n prepared.set(items, result);\n return result;\n}\n\nfunction ranksIn(items: readonly FingerprintItem[], ready: PreparedItems, d: FingerprintDimension) {\n const hit = ready.ranks.get(d);\n if (hit) return hit;\n const index = new Map(d.categories.map((c, i) => [c.id, i]));\n const unknown = d.categories.length;\n const ranks = new Int32Array(items.length);\n items.forEach((item, i) => {\n const value = item.values[d.id];\n ranks[i] = (value != null ? index.get(value) : undefined) ?? unknown;\n });\n ready.ranks.set(d, ranks);\n return ranks;\n}\n\nfunction validateDimension(dimension: FingerprintDimension) {\n const categories = new Set<string>();\n for (const category of dimension.categories) {\n if (category.id === UNKNOWN_CATEGORY_ID)\n throw new FingerprintDataError(`Category id \"${UNKNOWN_CATEGORY_ID}\" is reserved`);\n if (categories.has(category.id))\n throw new FingerprintDataError(`Duplicate category id \"${category.id}\" in \"${dimension.id}\"`);\n categories.add(category.id);\n }\n}\n\n/** Fills in an encoding's defaults: colour and shape follow `arrange` unless set, lightness is off. */\nfunction resolveEncoding(\n dimensions: readonly FingerprintDimension[],\n encoding: FingerprintEncoding | undefined,\n): Required<FingerprintEncoding> {\n const arrange = encoding?.arrange ?? dimensions[0]?.id;\n if (arrange === undefined) throw new FingerprintDataError(\"At least one dimension is needed\");\n const resolved = {\n arrange,\n color: encoding?.color === undefined ? arrange : encoding.color,\n shape: encoding?.shape === undefined ? arrange : encoding.shape,\n lightness: encoding?.lightness ?? null,\n };\n for (const [channel, id] of Object.entries(resolved)) {\n if (id === null) continue;\n const dimension = dimensions.find((d) => d.id === id);\n if (!dimension)\n throw new FingerprintDataError(\n `The ${channel} encoding names \"${id}\", which is not a dimension`,\n );\n if (channel === \"lightness\" && !dimension.ordered)\n throw new FingerprintDataError(\n `\"${id}\" drives lightness but is not ordered; lighter and darker would read as less and more`,\n );\n }\n return resolved;\n}\n\n/**\n * Ridges depend only on the form and spacing, not on the active dimension, so switching lens\n * reuses them: the marks move, the ridges stay. A few recent results are kept; the layout stays a\n * pure function because equal inputs always gave equal ridges.\n */\nconst ridgeCache = new Map<string, Point[][]>();\nfunction cachedRidges(form: FingerprintForm, spacing: number): Point[][] {\n const key = `${JSON.stringify(form)}:${spacing}`;\n const hit = ridgeCache.get(key);\n if (hit) return hit;\n const traced = ridges(form, spacing);\n ridgeCache.set(key, traced);\n if (ridgeCache.size > 8) ridgeCache.delete(ridgeCache.keys().next().value!);\n return traced;\n}\n\n/**\n * Lays the items out along fingerprint ridges.\n *\n * `encoding` says which dimension arranges the marks and which drive colour, shape and lightness\n * (see `FingerprintEncoding`); left out, the first dimension does all but lightness. Items are\n * sorted by the arranging dimension's category order, then by each other encoded dimension's,\n * then by id, and laid along the ridges from the core outwards, so categories form runs. Mark\n * **area** is proportional to weight; positions only arrange.\n *\n * A pure function of its input: the same items, dimensions, encoding and options give the same\n * layout whatever order the items arrive in and whatever size it is drawn at. The ridges come from\n * `form` (see `formFromItems`). The items array is treated as immutable: work is cached against it.\n *\n * Above `maxMarks` items, consecutive items that agree on **every** encoded dimension share a mark\n * (`itemsPerMark` > 1), so a mark never misrepresents any item in it. Nothing is sampled away.\n */\nexport function layoutFingerprint(input: LayoutInput): FingerprintLayout {\n const { items, dimensions, encoding } = input;\n // Read field by field: spreading the input would let an explicit `undefined` (which callers pass\n // through when they have no value) replace a default, and an undefined cap means one mark per item.\n const seed = input.seed ?? DEFAULTS.seed;\n const maxMarks = positive(input.maxMarks) ?? DEFAULTS.maxMarks;\n const minRidges = positive(input.minRidges) ?? DEFAULTS.minRidges;\n const form = resolveForm(seed, input.form);\n const ready = prepare(items);\n const encoded = resolveEncoding(dimensions, encoding);\n const byId = new Map(dimensions.map((d) => [d.id, d]));\n // Every dimension the encoding uses, once, in legend order: colour, shape, lightness, arrange.\n const used = [...new Set([encoded.color, encoded.shape, encoded.lightness, encoded.arrange])]\n .filter((id): id is string => id !== null)\n .map((id) => byId.get(id)!);\n used.forEach(validateDimension);\n // Sort priority: arrangement first, then the other properties, so a run splits into sub-runs.\n const priority = [...new Set([encoded.arrange, encoded.color, encoded.shape, encoded.lightness])]\n .filter((id): id is string => id !== null)\n .map((id) => byId.get(id)!);\n\n const ranks = new Map(used.map((d) => [d.id, ranksIn(items, ready, d)]));\n const categoryAt = (d: FingerprintDimension, rank: number) =>\n d.categories[rank]?.id ?? UNKNOWN_CATEGORY_ID;\n\n const groups: FingerprintLegendGroup[] = used.map((d) => {\n const channels = ([\"color\", \"shape\", \"lightness\", \"arrange\"] as const).filter(\n (c) => encoded[c] === d.id,\n );\n const drives = (c: FingerprintChannel) => channels.includes(c);\n const entry = (\n id: string,\n label: string,\n i: number | null,\n color?: string,\n href?: string,\n ): FingerprintLegendEntry => ({\n id,\n label,\n color: drives(\"color\")\n ? i === null\n ? UNKNOWN_COLOR\n : (color ?? DEFAULT_PALETTE[i % DEFAULT_PALETTE.length]!)\n : \"currentColor\",\n shape: drives(\"shape\") ? i : 0,\n lightness:\n drives(\"lightness\") && i !== null\n ? d.categories.length > 1\n ? i / (d.categories.length - 1)\n : 1\n : null,\n href,\n unknown: i === null,\n count: 0,\n weight: 0,\n });\n return {\n dimensionId: d.id,\n label: d.label,\n channels,\n entries: [\n ...d.categories.map((c, i) => entry(c.id, c.label, i, c.color, c.href)),\n entry(UNKNOWN_CATEGORY_ID, d.unknownLabel ?? \"Unknown\", null),\n ],\n };\n });\n const groupOf = new Map(groups.map((g) => [g.dimensionId, g]));\n const entryOf = (dimensionId: string, categoryId: string) => {\n const d = byId.get(dimensionId)!;\n const rank = d.categories.findIndex((c) => c.id === categoryId);\n return groupOf.get(dimensionId)!.entries[rank === -1 ? d.categories.length : rank]!;\n };\n\n // Counts, per dimension, straight from the rank arrays.\n for (const d of used) {\n const entries = groupOf.get(d.id)!.entries;\n const r = ranks.get(d.id)!;\n for (let i = 0; i < items.length; i++) {\n const entry = entries[r[i]!]!;\n entry.count += 1;\n entry.weight += items[i]!.weight ?? 1;\n }\n }\n\n // Sort by the encoded categories in priority order, packed into one number per item as a\n // mixed-radix key, then by id rank. Comparing numbers in typed arrays is what keeps a view change\n // fast; comparing strings and objects took most of a second at 100,000 items in a browser.\n const n = items.length;\n const comboKeys = new Float64Array(n);\n const priorityRanks = priority.map((d) => ranks.get(d.id)!);\n const radix = priority.map((d) => d.categories.length + 1);\n for (let i = 0; i < n; i++) {\n let key = 0;\n for (let k = 0; k < priority.length; k++) key = key * radix[k]! + priorityRanks[k]![i]!;\n comboKeys[i] = key;\n }\n const order = Uint32Array.from(items.keys()).sort(\n (a, b) => comboKeys[a]! - comboKeys[b]! || ready.idRank[a]! - ready.idRank[b]!,\n );\n\n // Group consecutive items that agree on every encoded dimension into marks of at most `itemsPerMark`.\n const itemsPerMark = Math.max(1, Math.ceil(n / maxMarks));\n const marksOf: { key: string; categories: Record<string, string>; members: FingerprintItem[] }[] =\n [];\n for (let k = 0; k < n; k++) {\n const item = items[order[k]!]!;\n const last = marksOf.at(-1);\n if (\n last &&\n comboKeys[order[k]!] === comboKeys[order[k - 1]!] &&\n last.members.length < itemsPerMark\n ) {\n last.members.push(item);\n continue;\n }\n const categories = Object.fromEntries(\n used.map((d) => [d.id, categoryAt(d, ranks.get(d.id)![order[k]!]!)]),\n );\n marksOf.push({ key: used.map((d) => categories[d.id]).join(\"|\"), categories, members: [item] });\n }\n\n // Ridges are spaced so that, laid end to end, they hold every mark about one spacing apart. The\n // first trace estimates their length from the frame's area; one retrace corrects the spacing from\n // the length actually traced, since ridge endings and the outline make that hard to predict.\n const marks = Math.max(1, marksOf.length);\n const widest = FRAME.rx / minRidges;\n let spacing = Math.min(widest, Math.sqrt((Math.PI * FRAME.rx * FRAME.ry * 0.85) / marks));\n let lines = cachedRidges(form, spacing);\n if (marksOf.length > 0) {\n const traced = lines.reduce((sum, line) => sum + polylineLength(line), 0);\n const corrected = Math.min(widest, Math.sqrt((traced * spacing) / marks));\n if (Math.abs(corrected - spacing) / spacing > 0.03) {\n spacing = corrected;\n lines = cachedRidges(form, spacing);\n }\n }\n const { slots, pitch } = slotsAlong(lines, marksOf.length);\n const footprint = Math.min(pitch, spacing) * MARK_FILL;\n\n const weights = marksOf.map((mark) =>\n mark.members.reduce((sum, item) => sum + (item.weight ?? 1), 0),\n );\n const heaviest = Math.max(0, ...weights);\n const perKey = new Map<string, number>();\n const nodes: FingerprintNode[] = marksOf.map((mark, i) => {\n const n = perKey.get(mark.key) ?? 0;\n perKey.set(mark.key, n + 1);\n const slot = slots[i]!;\n const property = (channel: \"color\" | \"shape\" | \"lightness\") => {\n const id = encoded[channel];\n return id === null ? null : entryOf(id, mark.categories[id]!);\n };\n return {\n id: itemsPerMark === 1 ? mark.members[0]!.id : `${mark.key}#${n}`,\n itemIds: mark.members.map((item) => item.id),\n categories: mark.categories,\n x: slot.x,\n y: slot.y,\n rotation: slot.rotation,\n size: footprint * Math.sqrt(weights[i]! / heaviest),\n weight: weights[i]!,\n color: property(\"color\")?.color ?? \"currentColor\",\n shape: encoded.shape === null ? 0 : property(\"shape\")!.shape,\n lightness: property(\"lightness\")?.lightness ?? null,\n };\n });\n\n // The unknown entry is listed only when something is unknown.\n for (const group of groups)\n group.entries = group.entries.filter((e) => !e.unknown || e.count > 0);\n const warnings: string[] = [];\n for (const channel of [\"color\", \"shape\"] as const) {\n const id = encoded[channel];\n if (id === null) continue;\n const shown = groupOf.get(id)!.entries.filter((e) => !e.unknown && e.count > 0).length;\n if (shown > DISTINCT[channel])\n warnings.push(\n `${channel === \"color\" ? \"Colour\" : \"Shape\"} shows ${shown} categories of \"${byId.get(id)!.label}\"; beyond ${DISTINCT[channel]} they repeat, so only the legend tells them apart.`,\n );\n }\n\n return {\n width: FRAME.width,\n height: FRAME.height,\n encoding: encoded,\n nodes,\n ridges: lines,\n form,\n ridgeSpacing: spacing,\n legends: groups,\n warnings,\n itemsPerMark,\n itemCount: items.length,\n };\n}\n\n/** A layout with nothing in it: what `useFingerprint` shows while its data is invalid. */\nexport function emptyLayout(): FingerprintLayout {\n return {\n width: FRAME.width,\n height: FRAME.height,\n encoding: { arrange: \"\", color: null, shape: null, lightness: null },\n nodes: [],\n ridges: [],\n form: resolveForm(DEFAULTS.seed),\n ridgeSpacing: FRAME.rx / DEFAULTS.minRidges,\n legends: [],\n warnings: [],\n itemsPerMark: 1,\n itemCount: 0,\n };\n}\n",
11
+ "import type { FingerprintNode, RidgeStyle } from \"./types\";\n\n/**\n * The opacity that shows a mark's lightness: 0.3 for the first category of an ordered dimension,\n * full strength for the last, and full strength when lightness is not encoded or unknown. Renderers\n * use it so lightness reads the same whatever they draw.\n */\nexport const markOpacity = (node: Pick<FingerprintNode, \"lightness\">) =>\n node.lightness === null ? 1 : 0.3 + 0.7 * node.lightness;\n\n/**\n * The ridges' opacity for a collection of `itemCount` items under `style`: constant for `always`,\n * zero for `never`, and for `auto` (the default) full until `fadeFrom` items, fading to nothing at\n * `fadeTo`.\n */\nexport function ridgeOpacity(itemCount: number, style: RidgeStyle = {}): number {\n const { show = \"auto\", fadeFrom = 60, fadeTo = 160, opacity = 0.22 } = style;\n if (show === \"never\") return 0;\n if (show === \"always\" || itemCount <= fadeFrom) return opacity;\n if (itemCount >= fadeTo) return 0;\n return opacity * (1 - (itemCount - fadeFrom) / (fadeTo - fadeFrom));\n}\n",
12
+ "import type { FingerprintAnnotation, FingerprintLayout } from \"./types\";\n\n/** Horizontal room either side of the print for callout labels, in layout units. */\nexport const CALLOUT_GUTTER = 190;\n/** How far apart two callouts' anchors stay: a numbered marker is 22 units across. */\nconst ANCHOR_CLEARANCE = 28;\n/** Vertical room one callout label takes, in layout units. */\nexport const CALLOUT_HEIGHT = 96;\n\n/** Where `placeCallouts` puts one annotation, in the layout's frame. */\nexport interface PlacedCallout {\n /** The annotation placed. */\n annotation: FingerprintAnnotation;\n /** The mark the leader points at: the named categories' mark nearest their centre. */\n anchor: { x: number; y: number };\n /** Which gutter the label sits in: the side of the print its anchor is on. */\n side: \"left\" | \"right\";\n /** Top of the label, in layout units. */\n labelY: number;\n /** 1-based, in reading order: left side top to bottom, then right. */\n number: number;\n}\n\n/**\n * Where each callout goes. Its anchor is a real mark of the categories it names, so the leader\n * never points at empty paper; the label sits in the gutter on the anchor's side, as level with it\n * as the other labels allow. Annotations whose categories have no marks are left out.\n */\nexport function placeCallouts(\n layout: FingerprintLayout,\n annotations: readonly FingerprintAnnotation[],\n): PlacedCallout[] {\n // Anchors already taken: a later callout's anchor keeps this far from them, so numbered markers\n // never hide each other.\n const taken: { x: number; y: number }[] = [];\n const anchored = annotations.flatMap((annotation) => {\n const wanted = new Set(annotation.categoryIds);\n const dimensionId = annotation.dimensionId ?? layout.encoding.arrange;\n const marks = layout.nodes.filter((node) => wanted.has(node.categories[dimensionId] ?? \"\"));\n if (marks.length === 0) return [];\n const cx = marks.reduce((sum, node) => sum + node.x, 0) / marks.length;\n const cy = marks.reduce((sum, node) => sum + node.y, 0) / marks.length;\n const byCloseness = [...marks].sort(\n (a, b) => Math.hypot(a.x - cx, a.y - cy) - Math.hypot(b.x - cx, b.y - cy),\n );\n const clear = (node: { x: number; y: number }) =>\n taken.every((t) => Math.hypot(node.x - t.x, node.y - t.y) >= ANCHOR_CLEARANCE);\n const anchor = byCloseness.find(clear) ?? byCloseness[0]!;\n taken.push(anchor);\n const side: PlacedCallout[\"side\"] = anchor.x < layout.width / 2 ? \"left\" : \"right\";\n return [{ annotation, anchor: { x: anchor.x, y: anchor.y }, side }];\n });\n\n const placed: PlacedCallout[] = [];\n for (const side of [\"left\", \"right\"] as const) {\n const column = anchored.filter((c) => c.side === side).sort((a, b) => a.anchor.y - b.anchor.y);\n // Level with the anchor where possible, pushed down to clear the label above, then the whole\n // column pulled back up if the last label would run off the bottom.\n const tops: number[] = [];\n for (const callout of column)\n tops.push(\n Math.max(\n callout.anchor.y - CALLOUT_HEIGHT / 3,\n (tops.at(-1) ?? -Infinity) + CALLOUT_HEIGHT,\n ),\n );\n const overflow = Math.max(0, (tops.at(-1) ?? 0) + CALLOUT_HEIGHT - layout.height);\n column.forEach((callout, i) =>\n placed.push({ ...callout, labelY: Math.max(0, tops[i]! - overflow), number: 0 }),\n );\n }\n return placed.map((callout, i) => ({ ...callout, number: i + 1 }));\n}\n",
13
+ "import type { FingerprintChannel, FingerprintLayout } from \"./types\";\n\nconst PROPERTY: Record<Exclude<FingerprintChannel, \"arrange\">, string> = {\n color: \"colour\",\n shape: \"shape\",\n lightness: \"lightness\",\n};\n\n/**\n * A plain-language summary of what a layout shows, for a screen reader or a caption: the total, the\n * largest categories of the arranging dimension with their counts, which dimension each visual\n * property shows when they differ, and how many items each mark stands for. Every number in it is\n * one the legends also show.\n */\nexport function describeLayout(layout: FingerprintLayout, unit = \"items\"): string {\n if (layout.itemCount === 0) return `No ${unit} yet.`;\n const arrange = layout.legends.find((group) => group.dimensionId === layout.encoding.arrange)!;\n const ranked = arrange.entries\n .filter((entry) => entry.count > 0)\n .sort((a, b) => b.count - a.count);\n const named = ranked\n .slice(0, 3)\n .map((entry) => `${entry.label} ${entry.count.toLocaleString(\"en-GB\")}`);\n const rest = ranked.length - named.length;\n const parts = [\n `${layout.itemCount.toLocaleString(\"en-GB\")} ${unit} by ${arrange.label.toLowerCase()}: ${named.join(\", \")}${\n rest > 0 ? `, and ${rest} more ${rest === 1 ? \"category\" : \"categories\"}` : \"\"\n }.`,\n ];\n if (layout.legends.length > 1) {\n const shown = layout.legends.flatMap((group) =>\n group.channels\n .filter(\n (channel): channel is Exclude<FingerprintChannel, \"arrange\"> => channel !== \"arrange\",\n )\n .map((channel) =>\n channel === \"lightness\"\n ? `lightness shows ${group.label.toLowerCase()} (fainter is earlier)`\n : `${PROPERTY[channel]} shows ${group.label.toLowerCase()}`,\n ),\n );\n if (shown.length) parts.push(`${shown.join(\", \").replace(/^./, (c) => c.toUpperCase())}.`);\n }\n if (layout.itemsPerMark > 1)\n parts.push(`Each mark stands for up to ${layout.itemsPerMark} ${unit}.`);\n return parts.join(\" \");\n}\n"
14
+ ],
15
+ "mappings": "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",
16
+ "debugId": "C8FF8090BDB0F57164756E2164756E21",
17
+ "names": []
18
+ }
@@ -0,0 +1,350 @@
1
+ /**
2
+ * `@data-profile/fingerprint`: the layout, as plain data, with no React. Lay items out
3
+ * with `layoutFingerprint`, derive a form with `formFromItems`, describe it with `describeLayout`.
4
+ *
5
+ * @packageDocumentation
6
+ * @module core
7
+ */
8
+
9
+ /**
10
+ * The default categorical palette, for consumers that give categories no colour. Ten hues, chosen
11
+ * to stay distinguishable on light and dark backgrounds; beyond ten they repeat, which is why the
12
+ * legend and list, not colour, carry identity.
13
+ */
14
+ export declare const DEFAULT_PALETTE: readonly ["#2f6fde", "#2bb3e6", "#f26b3a", "#f5b82e", "#14a39a", "#7a5af5", "#3fae6b", "#d9488f", "#8c6d46", "#5e8c99"];
15
+
16
+ /**
17
+ * A plain-language summary of what a layout shows, for a screen reader or a caption: the total, the
18
+ * largest categories of the arranging dimension with their counts, which dimension each visual
19
+ * property shows when they differ, and how many items each mark stands for. Every number in it is
20
+ * one the legends also show.
21
+ */
22
+ export declare function describeLayout(layout: FingerprintLayout, unit?: string): string;
23
+
24
+ /**
25
+ * A note pinned to the fingerprint, such as "Mostly fiction". The consumer writes the words; the
26
+ * canvas points a leader at a real mark of the named categories.
27
+ */
28
+ export declare interface FingerprintAnnotation {
29
+ /** Unique among the annotations. */
30
+ id: string;
31
+ /** Categories the note is about. Hidden when none has a mark. */
32
+ categoryIds: readonly string[];
33
+ /** The dimension those categories belong to. Defaults to the arranging dimension. */
34
+ dimensionId?: string;
35
+ /** The note's headline. */
36
+ title: string;
37
+ /** A sentence under it. */
38
+ body?: string;
39
+ }
40
+
41
+ /** One category of a dimension. */
42
+ export declare interface FingerprintCategory {
43
+ /** Unique within its dimension. `"~unknown"` is reserved. */
44
+ id: string;
45
+ /** Shown in legends, lists and descriptions. */
46
+ label: string;
47
+ /** Any CSS colour, custom properties included. Defaults to the package palette, by position. */
48
+ color?: string;
49
+ /** A link for this category, shown in `FingerprintList`. */
50
+ href?: string;
51
+ }
52
+
53
+ /** The visual properties a dimension can drive. `arrange` orders marks along the ridges. */
54
+ export declare type FingerprintChannel = "arrange" | "color" | "shape" | "lightness";
55
+
56
+ /**
57
+ * Data the layout cannot draw truthfully: duplicate item ids, a weight that is not positive, a
58
+ * reserved or duplicated category id, an encoding naming no dimension, or lightness on an unordered
59
+ * dimension. `layoutFingerprint` throws it; `useFingerprint` returns it as `error`.
60
+ */
61
+ export declare class FingerprintDataError extends Error {
62
+ name: string;
63
+ }
64
+
65
+ /** One way of grouping the items. Category order is the order marks are laid out in. */
66
+ export declare interface FingerprintDimension {
67
+ /** Unique; the key items use in `values`. */
68
+ id: string;
69
+ /** Shown as the legend's heading and in descriptions. */
70
+ label: string;
71
+ /** In the order they should be drawn and listed. */
72
+ categories: readonly FingerprintCategory[];
73
+ /** Label of the category unknown values fall into. Defaults to "Unknown". */
74
+ unknownLabel?: string;
75
+ /**
76
+ * True when the categories run in a meaningful order (decades, sizes). Only an ordered dimension
77
+ * may drive lightness: on unordered categories, fainter and stronger would read as less and more.
78
+ */
79
+ ordered?: boolean;
80
+ }
81
+
82
+ /**
83
+ * Which dimension drives which visual property, by dimension id. `color` and `shape` default to
84
+ * the `arrange` dimension, so `{ arrange: "genre" }` is a single-dimension view; `null` turns a
85
+ * property off. Size is never available: mark area is reserved for weight.
86
+ */
87
+ export declare interface FingerprintEncoding {
88
+ /** Orders marks along the ridges, so its categories form runs. */
89
+ arrange: string;
90
+ /** Colours each mark by this dimension's categories. Defaults to `arrange`. */
91
+ color?: string | null;
92
+ /** Gives each category of this dimension its own mark shape. Defaults to `arrange`. */
93
+ shape?: string | null;
94
+ /** Fades marks along this ordered dimension, first category faintest. Off by default. */
95
+ lightness?: string | null;
96
+ }
97
+
98
+ /**
99
+ * The print's form: its pattern and where its singular points sit. Every number is a small,
100
+ * bounded nudge, so any form still reads as a fingertip. (A mark's *shape* is something else: an
101
+ * encoding.)
102
+ */
103
+ export declare interface FingerprintForm {
104
+ /** Loop, whorl or arch. */
105
+ pattern: RidgePattern;
106
+ /** Which way a loop opens, or a whorl twists. */
107
+ hand: -1 | 1;
108
+ /** -1 to 1: moves the core left or right of centre. */
109
+ coreX: number;
110
+ /** -1 to 1: moves the core up or down. */
111
+ coreY: number;
112
+ /** 0 to 1: how far the deltas sit from the core; wider reads looser. */
113
+ spread: number;
114
+ /** Degrees, -10 to 10: the whole pattern's lean. */
115
+ tilt: number;
116
+ }
117
+
118
+ /**
119
+ * The fingerprint's data contract. It knows items, dimensions and categories, nothing about what
120
+ * is being collected: every domain word arrives as data.
121
+ */
122
+ /** One thing in the collection, drawn as one mark (below the mark cap). */
123
+ export declare interface FingerprintItem {
124
+ /** Stable and unique. Marks keep this identity when the view changes. */
125
+ id: string;
126
+ /** Mark area is proportional to weight. Defaults to 1; must be positive. */
127
+ weight?: number;
128
+ /**
129
+ * The item's category in each dimension, keyed by dimension id. `null`, `undefined`, a missing
130
+ * key or a category id the dimension does not list all place the item in that dimension's
131
+ * unknown category: unknowns are drawn, never dropped.
132
+ */
133
+ values: Readonly<Record<string, string | null | undefined>>;
134
+ }
135
+
136
+ /** What `layoutFingerprint` returns: everything a renderer needs, in a fixed frame. */
137
+ export declare interface FingerprintLayout {
138
+ /** The fixed coordinate frame's width; resizing scales it, never changes it. */
139
+ width: number;
140
+ /** The fixed coordinate frame's height. */
141
+ height: number;
142
+ /** Every property resolved to a dimension id, or null when off. */
143
+ encoding: Required<FingerprintEncoding>;
144
+ /** The marks, laid along the ridges from the core outwards. */
145
+ nodes: FingerprintNode[];
146
+ /** The ridges marks sit on, core outwards, as polylines of `[x, y]` points, for renderers that draw them. */
147
+ ridges: (readonly [x: number, y: number])[][];
148
+ /** The form actually drawn, every field resolved. */
149
+ form: FingerprintForm;
150
+ /** Centre-to-centre distance between neighbouring ridges. */
151
+ ridgeSpacing: number;
152
+ /** One group per encoded dimension: colour first, then shape, lightness and arrangement. */
153
+ legends: FingerprintLegendGroup[];
154
+ /** Encodings that will be hard to read, such as more colours than stay distinguishable. */
155
+ warnings: string[];
156
+ /** 1 unless the item count exceeded `maxMarks`. Say so on screen when it is not 1. */
157
+ itemsPerMark: number;
158
+ /** All the items, whatever `itemsPerMark`. */
159
+ itemCount: number;
160
+ }
161
+
162
+ /** One category in a legend, with how its marks are drawn and how many items it holds. */
163
+ export declare interface FingerprintLegendEntry {
164
+ /** The category id, or `UNKNOWN_CATEGORY_ID`. */
165
+ id: string;
166
+ /** The category label, or the dimension's `unknownLabel`. */
167
+ label: string;
168
+ /** The colour a mark of this category gets, or `currentColor` when this dimension drives no colour. */
169
+ color: string;
170
+ /** As `FingerprintNode.shape`, for this category; `0` when this dimension drives no shape. */
171
+ shape: number | null;
172
+ /** As `FingerprintNode.lightness`, for this category; null when this dimension drives no lightness. */
173
+ lightness: number | null;
174
+ /** The category's link, if it has one. */
175
+ href?: string;
176
+ /** True for the unknown category. */
177
+ unknown: boolean;
178
+ /** Items in this category, whatever their weight. */
179
+ count: number;
180
+ /** The summed weight of those items. */
181
+ weight: number;
182
+ }
183
+
184
+ /** One dimension's legend, and which visual properties that dimension drives. */
185
+ export declare interface FingerprintLegendGroup {
186
+ /** The dimension's id. */
187
+ dimensionId: string;
188
+ /** The dimension's label. */
189
+ label: string;
190
+ /** What this dimension drives in the current encoding. */
191
+ channels: readonly FingerprintChannel[];
192
+ /** In category order, the unknown category last and only when it has items. */
193
+ entries: FingerprintLegendEntry[];
194
+ }
195
+
196
+ /** A laid-out mark, in the layout's fixed coordinate frame. */
197
+ export declare interface FingerprintNode {
198
+ /** The item id, or a combination key and index when the mark stands for several items. */
199
+ id: string;
200
+ /** The items this mark stands for: one, unless the collection exceeded `maxMarks`. */
201
+ itemIds: readonly string[];
202
+ /**
203
+ * The mark's category in every encoded dimension, keyed by dimension id; an unknown value is
204
+ * `UNKNOWN_CATEGORY_ID`. The arranging category is `categories[layout.encoding.arrange]`.
205
+ */
206
+ categories: Readonly<Record<string, string>>;
207
+ /** Centre, in the layout's frame. */
208
+ x: number;
209
+ /** Centre, in the layout's frame. */
210
+ y: number;
211
+ /** Degrees, the ridge's direction at this mark. */
212
+ rotation: number;
213
+ /** Diameter of the mark's footprint. Area is proportional to weight. */
214
+ size: number;
215
+ /** The summed weight of `itemIds`. */
216
+ weight: number;
217
+ /** From the colour dimension; `currentColor` when nothing drives colour. */
218
+ color: string;
219
+ /**
220
+ * The mark's shape: the index of its category in the shape dimension (0, 1, 2, …). `0` also when
221
+ * nothing drives shape, so a plain mark is the first shape; `null` when its shape category is
222
+ * unknown.
223
+ */
224
+ shape: number | null;
225
+ /** 0 (faintest) to 1 (full strength) along an ordered dimension; null when unknown or not encoded. */
226
+ lightness: number | null;
227
+ }
228
+
229
+ /**
230
+ * A shape that is the collection's own: derived only from the data, so the same collection always
231
+ * draws the same fingerprint, and it changes slowly as the collection grows. It is a signature,
232
+ * not a chart; say so beside it. The mapping, which a caption can state:
233
+ *
234
+ * - **Pattern.** An arch while there are fewer than 12 items; then a loop when one category of the
235
+ * first dimension holds 40% or more of the items (a focused collection); otherwise a whorl.
236
+ * - **Core across, and loop direction.** Where the second dimension's values sit, on average, in its
237
+ * category order: earlier categories pull the core left, later ones right.
238
+ * - **Core height.** The same for the third dimension.
239
+ * - **Spread.** The share of all categories that have at least one item: broader reads looser.
240
+ * - **Tilt.** From the seed only, so two equal collections still differ slightly.
241
+ */
242
+ export declare function formFromItems(items: readonly FingerprintItem[], dimensions: readonly FingerprintDimension[], seed?: string): FingerprintForm;
243
+
244
+ /**
245
+ * Lays the items out along fingerprint ridges.
246
+ *
247
+ * `encoding` says which dimension arranges the marks and which drive colour, shape and lightness
248
+ * (see `FingerprintEncoding`); left out, the first dimension does all but lightness. Items are
249
+ * sorted by the arranging dimension's category order, then by each other encoded dimension's,
250
+ * then by id, and laid along the ridges from the core outwards, so categories form runs. Mark
251
+ * **area** is proportional to weight; positions only arrange.
252
+ *
253
+ * A pure function of its input: the same items, dimensions, encoding and options give the same
254
+ * layout whatever order the items arrive in and whatever size it is drawn at. The ridges come from
255
+ * `form` (see `formFromItems`). The items array is treated as immutable: work is cached against it.
256
+ *
257
+ * Above `maxMarks` items, consecutive items that agree on **every** encoded dimension share a mark
258
+ * (`itemsPerMark` > 1), so a mark never misrepresents any item in it. Nothing is sampled away.
259
+ */
260
+ export declare function layoutFingerprint(input: LayoutInput): FingerprintLayout;
261
+
262
+ /**
263
+ * `layoutFingerprint`'s one argument. The items array is treated as immutable: work is cached
264
+ * against it, so pass a new array when the collection changes.
265
+ */
266
+ export declare interface LayoutInput extends LayoutOptions {
267
+ /** One per mark (below the cap). Unique ids. */
268
+ items: readonly FingerprintItem[];
269
+ /** Every dimension the items carry values for; the encoding picks among them. */
270
+ dimensions: readonly FingerprintDimension[];
271
+ }
272
+
273
+ /** Everything `layoutFingerprint` takes besides the data. */
274
+ export declare interface LayoutOptions {
275
+ /** Which dimension drives what. Defaults to the first dimension arranging, colouring and shaping. */
276
+ encoding?: FingerprintEncoding;
277
+ /** Seeds any form field not given, and nothing else. Same seed, same picture. */
278
+ seed?: string;
279
+ /** Overrides the print's form. `formFromItems` derives one from the data. */
280
+ form?: Partial<FingerprintForm>;
281
+ /**
282
+ * Above this many items, marks stand for several items each. Defaults to 600, the most marks a
283
+ * canvas glides smoothly (see `FingerprintCanvas`'s `motion`); raise it for denser prints. A
284
+ * layout can exceed it slightly, since every combination of encoded categories needs a mark.
285
+ */
286
+ maxMarks?: number;
287
+ /** Ridges never sit further apart than the frame's half-width divided by this. Defaults to 6. */
288
+ minRidges?: number;
289
+ }
290
+
291
+ /**
292
+ * The opacity that shows a mark's lightness: 0.3 for the first category of an ordered dimension,
293
+ * full strength for the last, and full strength when lightness is not encoded or unknown. Renderers
294
+ * use it so lightness reads the same whatever they draw.
295
+ */
296
+ export declare const markOpacity: (node: Pick<FingerprintNode, "lightness">) => number;
297
+
298
+ /**
299
+ * Where each callout goes. Its anchor is a real mark of the categories it names, so the leader
300
+ * never points at empty paper; the label sits in the gutter on the anchor's side, as level with it
301
+ * as the other labels allow. Annotations whose categories have no marks are left out.
302
+ */
303
+ export declare function placeCallouts(layout: FingerprintLayout, annotations: readonly FingerprintAnnotation[]): PlacedCallout[];
304
+
305
+ /** Where `placeCallouts` puts one annotation, in the layout's frame. */
306
+ export declare interface PlacedCallout {
307
+ /** The annotation placed. */
308
+ annotation: FingerprintAnnotation;
309
+ /** The mark the leader points at: the named categories' mark nearest their centre. */
310
+ anchor: {
311
+ x: number;
312
+ y: number;
313
+ };
314
+ /** Which gutter the label sits in: the side of the print its anchor is on. */
315
+ side: "left" | "right";
316
+ /** Top of the label, in layout units. */
317
+ labelY: number;
318
+ /** 1-based, in reading order: left side top to bottom, then right. */
319
+ number: number;
320
+ }
321
+
322
+ /** The three classes every fingerprint falls into. */
323
+ export declare type RidgePattern = "loop" | "whorl" | "arch";
324
+
325
+ /**
326
+ * How the ridges behind the marks are drawn. `auto` shows them while a collection is too small for
327
+ * its marks alone to read as a fingerprint, fading out as it fills.
328
+ */
329
+ export declare interface RidgeStyle {
330
+ /** `auto` (the default), `always` or `never`. */
331
+ show?: "auto" | "always" | "never";
332
+ /** `auto`: full opacity at or below this many items. Defaults to 60. */
333
+ fadeFrom?: number;
334
+ /** `auto`: gone at or above this many items. Defaults to 160. */
335
+ fadeTo?: number;
336
+ /** Opacity at full strength. Defaults to 0.22. */
337
+ opacity?: number;
338
+ /** Any CSS colour. Defaults to `currentColor`. */
339
+ color?: string;
340
+ /** Stroke width as a share of the ridge spacing. Defaults to 0.12. */
341
+ width?: number;
342
+ }
343
+
344
+ /** The id of the category unknown values fall into. Reserved: a dimension may not use it. */
345
+ export declare const UNKNOWN_CATEGORY_ID = "~unknown";
346
+
347
+ /** Unknowns are drawn in a neutral tone, outside the palette, so they never look like a category. */
348
+ export declare const UNKNOWN_COLOR = "#b8bcc4";
349
+
350
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ import {
2
+ DEFAULT_PALETTE,
3
+ FingerprintDataError,
4
+ UNKNOWN_CATEGORY_ID,
5
+ UNKNOWN_COLOR,
6
+ describeLayout,
7
+ formFromItems,
8
+ layoutFingerprint,
9
+ markOpacity,
10
+ placeCallouts
11
+ } from "./chunk-eygy4k4w.js";
12
+ export {
13
+ placeCallouts,
14
+ markOpacity,
15
+ layoutFingerprint,
16
+ formFromItems,
17
+ describeLayout,
18
+ UNKNOWN_COLOR,
19
+ UNKNOWN_CATEGORY_ID,
20
+ FingerprintDataError,
21
+ DEFAULT_PALETTE
22
+ };
23
+
24
+ //# debugId=38AD33507207ECF664756E2164756E21
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "38AD33507207ECF664756E2164756E21",
8
+ "names": []
9
+ }