@expofp/floorplan
Version:
Interactive floor plan library for expos and events
182 lines • 8.56 kB
TypeScript
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