UNPKG

@expofp/floorplan

Version:

Interactive floor plan library for expos and events

182 lines • 8.56 kB
import { Box, Polygon } from '@expofp/geometry'; import { type RegularBooth } from '../../../../store/BoothStore.js'; /** What a badge's anchor search reads off its booth. */ export type BadgeBooth = Pick<RegularBooth, 'rect' | 'rotate'>; /** What a booth's badge must fit in. */ export interface BadgeFootprint { /** The body's shape, built once for the outline and the badge's anchor search alike. */ readonly geometry: BadgeGeometry; /** How far the outline reaches into the body; 0 without one. */ readonly outlineInset: number; } /** A footprint's shape, as the anchor search reads it. */ export interface BadgeGeometry { /** The booth's body (its first path) as triangles; empty without paths. */ readonly triangles: number[][][]; readonly polygon: Polygon; /** The footprint's outer edge. */ readonly edges: FootprintEdge[]; readonly bbox: Box | null; } /** * The badge's footprint: the booth's first path. Later paths are usually an outline drawn as a band * over the body's edge, so their reach into the body comes along for the badge to keep clear of. A * path that does not reach the edge is an inner element, not an outline, and is left out. * @param booth - The booth. * @returns The body and the outline's reach into it. */ export declare function getBoothBadgeFootprint(booth: Pick<RegularBooth, 'paths' | 'layer'>): BadgeFootprint; /** Where a badge sits in one booth corner, and how large it may grow there. */ export interface BadgeFit { /** The x of the badge's top-left corner on the plan, before rotation. */ readonly x: number; /** The y of the badge's top-left corner on the plan, before rotation. */ readonly y: number; /** The largest scale that keeps the badge inside the booth's footprint. */ readonly maxScale: number; } /** * The places a booth's badge can sit, one per booth corner, each computed on the first visit to * that corner and kept. * * The plan's roll moves the badge to another booth corner every quarter turn, and every badge on * the plan moves in the same frame. Finding a corner's anchor tests the booth's vertices against * its own triangles, so it is too slow to repeat for hundreds of badges at once. The result * depends only on the corner: the rotation the scale search uses is the booth's own plus the * quarter turns to that corner, the same at every visit to it up to whole turns of 2π. * * The first visit to a corner still searches, for every badge at once, so {@link prefetch} works * the other corners out ahead, while the page is idle. */ export declare class BoothBadgeAnchors { private readonly booth; private readonly border; private readonly source; /** The badges with corners still to work out while the page is idle, in the order queued. */ private static readonly idle; private static idleScheduled; private readonly fits; /** The rotation at corner 0 the idle work counts quarter turns from. */ private prefetchRotation; private readonly triangles; private readonly polygon; /** The footprint's outer edge. */ private readonly edges; private readonly bbox; /** The booth's distinct vertices in first-seen order, built on the first search. */ private vertices; /** * @param booth - The booth the badge belongs to. * @param geometry - The booth's footprint, from {@link buildBadgeGeometry}. * @param border - How far the badge stays in from the booth's edge. * @param source - The badge's width and height at scale 1. */ constructor(booth: BadgeBooth, geometry: BadgeGeometry, border: number, source: { readonly x: number; readonly y: number; }); /** * The badge's place in a booth corner. * @param corner - The booth corner, clockwise from top-left (0) to bottom-left (3). * @param rotation - The badge's rotation in radians: the booth's own plus the quarter turns that * brought the badge to this corner. Read on the first visit only. * @returns The anchor and the largest scale, the same object at every visit. */ fit(corner: number, rotation: number): BadgeFit; /** * Works out the three corners not yet visited while the page is idle, so the first quarter turn * into each finds it ready. Queuing again while queued changes nothing. * @param rotation - The badge's rotation at corner 0: the booth's own. */ prefetch(rotation: number): void; /** Drops the idle work {@link prefetch} queued and has not done yet. */ cancelPrefetch(): void; private static scheduleIdle; /** Runs the idle work for one {@link FRAME_SLICE_MS} slice, where no idle period is on offer. */ private static runSlice; /** * Works out one corner after another while the idle period lasts, for at most a slice, then asks * for another. * @param deadline - How much of the idle period is left. */ private static runIdle; /** * Works out the next corner not yet known, at the rotation the quarter turns to it give. * @returns Whether a corner is still unknown after this one. */ private prefetchNext; /** * Searches a corner: the anchor, the largest scale at it, then the move off slanted sides. * @param corner - The booth corner, clockwise from top-left (0). * @param rotation - The badge's rotation at that corner, in radians. * @returns The corner's fit. */ private compute; /** * Moves the anchor along the badge's axes where a slanted booth side comes nearer than the border * to the badge's left or top side. * @param anchor - The badge's anchor. * @param scale - The badge's largest scale. * @param rotation - The badge's rotation, in radians. * @returns The moved anchor; `null` when no move is needed or it would leave the booth. */ private nudge; /** * The largest scale the booth's rect allows. At odd corners the badge's width and height lie * along the booth's height and width. * @param swapped - Whether the badge's axes are swapped against the booth's. * @returns The scale. */ private rectMaxScale; /** * The booth vertex, moved in by the border, nearest the target corner of the booth's bounding * box and still inside the booth. Uses booth.rotate (the booth's true tilt) for the fallback, * never the badge's stepped rotation: the booth does not move when the badge steps. * * The candidates are tested nearest first, so the search stops at the first one inside instead * of testing every vertex. Ties keep the vertex seen first, as a full scan would. * @param signX - 1 to aim at the left edge, -1 at the right. * @param signY - 1 to aim at the top edge, -1 at the bottom. * @returns The anchor. */ private findAnchor; /** * Binary-searches the largest scale, up to the rect's cap, that keeps all badge corners inside * the booth's footprint. * @param anchorX - The badge's anchor. * @param anchorY - The badge's anchor. * @param rectMaxScale - The cap the booth's rect allows. * @param rotation - The badge's rotation, in radians. * @returns The scale; the cap itself when the booth has no footprint or the anchor lies outside. */ private clampScale; /** * Whether the badge's three corners away from the anchor lie inside the booth's footprint. * @param anchorX - The badge's anchor. * @param anchorY - The badge's anchor. * @param scale - The badge's scale. * @param rotation - The badge's rotation, in radians. * @returns Whether they do. */ private fitsAt; /** * The booth's distinct vertices, in the order the triangles first name them. * @returns The vertices, built once. */ private distinctVertices; } /** * The bounding box of a booth's triangles. * @param triangles - The triangles, as `[x, y]` vertex triples. * @returns The box, or `null` when there are no triangles. */ export declare function trianglesBoundingBox(triangles: number[][][]): Box | null; /** A segment of a footprint's outer edge. */ export type FootprintEdge = [number[], number[]]; /** * The shape the anchor search reads off a footprint's triangles. * @param triangles - The triangles. * @returns The triangles with their polygon, outer edge and bounding box. */ export declare function buildBadgeGeometry(triangles: number[][][]): BadgeGeometry; //# sourceMappingURL=booth-badge-anchor.d.ts.map