224 lines
5.5 KiB
TypeScript
224 lines
5.5 KiB
TypeScript
|
|
/**
|
||
|
|
* Domain definitions for deterministic scene logic.
|
||
|
|
*
|
||
|
|
* A domain is a finite set of geometry subjects extracted from a scene.
|
||
|
|
* Domains are the ground over which quantifiers range.
|
||
|
|
*
|
||
|
|
* Invariant: every domain is enumerable and closed within a single
|
||
|
|
* materialized GeometryWorld. No domain may silently default to an
|
||
|
|
* empty set; empty domains must be reported explicitly.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import type { ImhotepId, SubjectKind } from './types.js'
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Domain Descriptor
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export interface DomainDescriptor {
|
||
|
|
domainId: ImhotepId
|
||
|
|
kind: DomainKind
|
||
|
|
selector?: string
|
||
|
|
parentVar?: string
|
||
|
|
subjectKind?: SubjectKind
|
||
|
|
}
|
||
|
|
|
||
|
|
export type DomainKind =
|
||
|
|
| 'elements'
|
||
|
|
| 'descendants'
|
||
|
|
| 'lineBoxes'
|
||
|
|
| 'textRuns'
|
||
|
|
| 'fragments'
|
||
|
|
| 'frames'
|
||
|
|
| 'custom'
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Domain Value (materialized)
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export interface DomainValue {
|
||
|
|
domainId: ImhotepId
|
||
|
|
/** Stable numeric subject ids, stored as a flat array for fast iteration. */
|
||
|
|
subjectIds: Uint32Array
|
||
|
|
/** Source selector or domain expression that produced this set. */
|
||
|
|
provenance: string
|
||
|
|
/** If true, the domain is known to be complete for the scene. */
|
||
|
|
closed: boolean
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Built-in Domain Functions
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export interface DomainFunctions {
|
||
|
|
/** Select elements matching a CSS selector. */
|
||
|
|
elements(selector: string): DomainDescriptor
|
||
|
|
|
||
|
|
/** Select descendants of a bound parent variable matching a selector. */
|
||
|
|
descendants(parentVar: string, selector: string): DomainDescriptor
|
||
|
|
|
||
|
|
/** Select line boxes for a given text node subject. */
|
||
|
|
lineBoxes(textNodeId: ImhotepId): DomainDescriptor
|
||
|
|
|
||
|
|
/** Select text runs for a given text node subject. */
|
||
|
|
textRuns(textNodeId: ImhotepId): DomainDescriptor
|
||
|
|
|
||
|
|
/** Select fragment boxes for a given subject. */
|
||
|
|
fragments(subjectId: ImhotepId): DomainDescriptor
|
||
|
|
|
||
|
|
/** Select frames of a given kind. */
|
||
|
|
frames(frameKind: string): DomainDescriptor
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Domain Enumeration Result
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export interface DomainEnumeration {
|
||
|
|
domainId: ImhotepId
|
||
|
|
subjectIds: Uint32Array
|
||
|
|
empty: boolean
|
||
|
|
diagnostic?: DomainDiagnostic
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface DomainDiagnostic {
|
||
|
|
code: string
|
||
|
|
message: string
|
||
|
|
domainId: ImhotepId
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Default Domain Registry Implementation
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export class DomainRegistry {
|
||
|
|
private registry = new Map<ImhotepId, DomainDescriptor>()
|
||
|
|
|
||
|
|
register(descriptor: DomainDescriptor): void {
|
||
|
|
this.registry.set(descriptor.domainId, descriptor)
|
||
|
|
}
|
||
|
|
|
||
|
|
lookup(domainId: ImhotepId): DomainDescriptor | undefined {
|
||
|
|
return this.registry.get(domainId)
|
||
|
|
}
|
||
|
|
|
||
|
|
list(): DomainDescriptor[] {
|
||
|
|
return Array.from(this.registry.values())
|
||
|
|
}
|
||
|
|
|
||
|
|
clear(): void {
|
||
|
|
this.registry.clear()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Global default instance for backward compatibility.
|
||
|
|
const globalDomainRegistry = new DomainRegistry()
|
||
|
|
|
||
|
|
export function registerDomain(descriptor: DomainDescriptor): void {
|
||
|
|
globalDomainRegistry.register(descriptor)
|
||
|
|
}
|
||
|
|
|
||
|
|
export function lookupDomain(domainId: ImhotepId): DomainDescriptor | undefined {
|
||
|
|
return globalDomainRegistry.lookup(domainId)
|
||
|
|
}
|
||
|
|
|
||
|
|
export function listDomains(): DomainDescriptor[] {
|
||
|
|
return globalDomainRegistry.list()
|
||
|
|
}
|
||
|
|
|
||
|
|
export function clearDomainRegistry(): void {
|
||
|
|
globalDomainRegistry.clear()
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Domain Factory
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export function createElementsDomain(
|
||
|
|
domainId: ImhotepId,
|
||
|
|
selector: string,
|
||
|
|
): DomainDescriptor {
|
||
|
|
return {
|
||
|
|
domainId,
|
||
|
|
kind: 'elements',
|
||
|
|
selector,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createDescendantsDomain(
|
||
|
|
domainId: ImhotepId,
|
||
|
|
parentVar: string,
|
||
|
|
selector: string,
|
||
|
|
): DomainDescriptor {
|
||
|
|
return {
|
||
|
|
domainId,
|
||
|
|
kind: 'descendants',
|
||
|
|
parentVar,
|
||
|
|
selector,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createLineBoxesDomain(
|
||
|
|
domainId: ImhotepId,
|
||
|
|
textNodeId: ImhotepId,
|
||
|
|
): DomainDescriptor {
|
||
|
|
return {
|
||
|
|
domainId,
|
||
|
|
kind: 'lineBoxes',
|
||
|
|
selector: textNodeId,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createTextRunsDomain(
|
||
|
|
domainId: ImhotepId,
|
||
|
|
textNodeId: ImhotepId,
|
||
|
|
): DomainDescriptor {
|
||
|
|
return {
|
||
|
|
domainId,
|
||
|
|
kind: 'textRuns',
|
||
|
|
selector: textNodeId,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createFragmentsDomain(
|
||
|
|
domainId: ImhotepId,
|
||
|
|
subjectId: ImhotepId,
|
||
|
|
): DomainDescriptor {
|
||
|
|
return {
|
||
|
|
domainId,
|
||
|
|
kind: 'fragments',
|
||
|
|
selector: subjectId,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createFramesDomain(
|
||
|
|
domainId: ImhotepId,
|
||
|
|
frameKind: string,
|
||
|
|
): DomainDescriptor {
|
||
|
|
return {
|
||
|
|
domainId,
|
||
|
|
kind: 'frames',
|
||
|
|
selector: frameKind,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Domain Value Factory
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export function createDomainValue(
|
||
|
|
domainId: ImhotepId,
|
||
|
|
subjectIds: Uint32Array,
|
||
|
|
provenance: string,
|
||
|
|
closed = true,
|
||
|
|
): DomainValue {
|
||
|
|
return {
|
||
|
|
domainId,
|
||
|
|
subjectIds,
|
||
|
|
provenance,
|
||
|
|
closed,
|
||
|
|
}
|
||
|
|
}
|