/** * Spatial Training Data Generator * * Transforms HoloScript compositions with spatial constraints into labeled * spatial relationship datasets for fine-tuning Vision-Language Models (VLMs) * on spatial reasoning tasks. * * Generates ground truth for three core spatial relationship families: * - **adjacent**: Two entities are within a distance threshold * - **contains**: One entity's bounding volume encloses another * - **reachable**: An unobstructed path exists between entities * * Zone-level relationships (P.PROCGEN.01): * - **zone_adjacent**: Two zones share a boundary * - **zone_contains**: A zone contains a set of objects * - **biome_transition**: Two zones have a biome transition boundary * * P.PROCGEN.03: Self-bootstrapping LLM training pipeline. * Flow: SpatialDataGenerator → JSONL → fine-tune VLM → SemanticExpander. * * @module spatial/SpatialDataGenerator * @version 2.0.0 */ import type { Vector3, Quaternion, BoundingBox, BoundingSphere } from './SpatialTypes'; /** * A spatial object extracted from a HoloScript composition. * This is the input format - each object in a composition maps to one of these. */ export interface SpatialObject { /** Unique identifier within the composition */ id: string; /** Object name as declared in HoloScript (e.g., "Table", "Chair1") */ name: string; /** Entity type / geometry type (e.g., "cube", "sphere", "plane") */ type: string; /** World-space position [x, y, z] */ position: Vector3; /** Rotation as quaternion (identity if not specified) */ rotation?: Quaternion; /** Scale factors [sx, sy, sz] */ scale?: Vector3; /** Axis-aligned bounding box in world space */ bounds?: BoundingBox; /** Bounding sphere in world space */ boundingSphere?: BoundingSphere; /** Parent object ID (for hierarchy / group containment) */ parentId?: string; /** HoloScript traits applied to this object (e.g., ["physics", "grabbable"]) */ traits?: string[]; /** Whether this object is a static collider / obstacle */ isStatic?: boolean; /** Arbitrary metadata from the composition */ metadata?: Record; } /** * A HoloScript composition parsed into spatial objects with optional constraints. */ export interface SpatialComposition { /** Composition name */ name: string; /** All spatial objects in the composition */ objects: SpatialObject[]; /** Environment metadata (skybox, gravity, etc.) */ environment?: Record; /** Source HoloScript code (optional, for provenance) */ sourceCode?: string; } /** * The spatial relationship types this generator labels. * P.PROCGEN.01: Expanded with zone-level relationship types. */ export type SpatialRelationshipType = 'adjacent' | 'contains' | 'reachable' | 'zone_adjacent' | 'zone_contains' | 'biome_transition'; /** * P.PROCGEN.01: Zone metadata for zone-level spatial relationships. */ export interface ZoneMetadata { /** Zone identifier */ zoneId: string; /** Zone biome type (e.g., 'forest', 'desert', 'urban') */ biome: string; /** Zone bounding box in world space */ bounds: BoundingBox; /** Objects contained in this zone */ objectIds: string[]; /** Adjacent zone IDs */ adjacentZones: string[]; /** Level/floor number (for multi-level worlds) */ level?: number; } /** * Directional qualifier for spatial relationships. */ export type DirectionLabel = 'above' | 'below' | 'left_of' | 'right_of' | 'in_front_of' | 'behind' | 'near' | 'far' | 'inside' | 'outside' | 'overlapping'; /** * A single labeled spatial relationship between two objects. */ export interface SpatialRelationship { /** Relationship type */ type: SpatialRelationshipType; /** Source entity ID */ sourceId: string; /** Source entity name */ sourceName: string; /** Target entity ID */ targetId: string; /** Target entity name */ targetName: string; /** Whether the relationship holds (true) or does not hold (false) */ holds: boolean; /** Euclidean distance between entity centers (meters) */ distance: number; /** Directional qualifiers */ directions: DirectionLabel[]; /** Relationship-specific parameters */ parameters: SpatialRelationshipParameters; } /** * Parameters specific to each relationship type. */ export interface SpatialRelationshipParameters { /** Distance threshold used for adjacency check */ adjacencyThreshold?: number; /** Axis used for distance measurement */ axis?: string; /** Whether containment is strict (full bounds) or center-only */ strict?: boolean; /** Containment margin */ margin?: number; /** Percentage of target volume inside container (0-1) */ overlapRatio?: number; /** Whether line of sight is clear */ lineOfSightClear?: boolean; /** Straight-line distance */ straightLineDistance?: number; /** IDs of blocking obstacles */ blockingObstacles?: string[]; /** Estimated path length (may be greater than straight line) */ estimatedPathLength?: number; } /** * A single training sample in the output JSONL. * Designed for VLM fine-tuning on spatial reasoning. */ export interface SpatialTrainingSample { /** Unique sample identifier */ id: string; /** Composition this sample was generated from */ compositionName: string; /** The spatial relationship being labeled */ relationship: SpatialRelationship; /** Ground truth positions of both entities */ groundTruth: { source: { position: Vector3; rotation?: Quaternion; scale?: Vector3; bounds?: BoundingBox; }; target: { position: Vector3; rotation?: Quaternion; scale?: Vector3; bounds?: BoundingBox; }; }; /** Scene context: all objects and their positions */ sceneContext: { objectCount: number; objects: Array<{ id: string; name: string; type: string; position: Vector3; }>; }; /** Natural language description of the relationship */ description: string; /** Question-answer pair for VLM training */ qa: { question: string; answer: string; }; /** Tags for filtering and stratification */ tags: string[]; /** Difficulty level based on spatial complexity */ difficulty: 'easy' | 'medium' | 'hard'; /** Generation metadata */ metadata: { generatorVersion: string; timestamp: string; compositionHash: string; }; } /** * Configuration for the SpatialDataGenerator. */ export interface SpatialDataGeneratorConfig { /** * Distance thresholds for adjacency checks. * Objects within this distance are considered adjacent. * Multiple thresholds generate multiple samples at different granularities. */ adjacencyThresholds: number[]; /** * Whether to generate negative samples (relationship does NOT hold). * Essential for training classifiers. */ generateNegatives: boolean; /** * Ratio of negative to positive samples (e.g., 1.0 = equal, 2.0 = 2x negatives). */ negativeRatio: number; /** * Containment margin for "contains" checks (meters). */ containmentMargin: number; /** * Whether to use strict containment (full bounds) or center-only. */ strictContainment: boolean; /** * Maximum straight-line distance for reachability (meters). * Pairs beyond this are not checked for reachability. */ maxReachabilityDistance: number; /** * Entity types to treat as obstacles for reachability checks. */ obstacleTypes: string[]; /** * Whether to include the full scene context in each sample. */ includeSceneContext: boolean; /** * Whether to generate QA pairs for VLM instruction tuning. */ generateQA: boolean; /** * Random seed for reproducible negative sampling. */ seed: number; /** * Maximum number of samples per composition (0 = unlimited). */ maxSamplesPerComposition: number; } /** * Default configuration. */ export declare const DEFAULT_SPATIAL_DATA_CONFIG: SpatialDataGeneratorConfig; /** * Statistics from a generation run. */ export interface GenerationStats { totalSamples: number; adjacentPositive: number; adjacentNegative: number; containsPositive: number; containsNegative: number; reachablePositive: number; reachableNegative: number; compositionsProcessed: number; objectsProcessed: number; pairsEvaluated: number; generationTimeMs: number; } /** * Generates labeled spatial relationship datasets from HoloScript compositions. * * @example * ```typescript * const generator = new SpatialDataGenerator(); * * const composition: SpatialComposition = { * name: "Meeting Room", * objects: [ * { id: "table", name: "Table", type: "cube", position: [0, 0.75, 0], * bounds: { min: [-1.5, 0.7, -0.75 ], max: [1.5, 0.8, 0.75 ] } }, * { id: "chair1", name: "Chair1", type: "cube", position: [-1, 0.5, 1.2] }, * { id: "chair2", name: "Chair2", type: "cube", position: [1, 0.5, 1.2] }, * ] * }; * * const samples = generator.generate(composition); * const jsonl = generator.toJSONL(samples); * ``` */ export declare class SpatialDataGenerator { private config; private sampleCounter; private rng; constructor(config?: Partial); /** * Generate spatial training samples from a single composition. */ generate(composition: SpatialComposition): SpatialTrainingSample[]; /** * Generate samples from multiple compositions. */ generateBatch(compositions: SpatialComposition[]): { samples: SpatialTrainingSample[]; stats: GenerationStats; }; /** * Convert samples to JSONL format (one JSON object per line). */ toJSONL(samples: SpatialTrainingSample[]): string; /** * Convert samples to instruction-tuning format (conversation-style JSONL). * Compatible with OpenAI fine-tuning, Axolotl, and similar frameworks. */ toInstructionJSONL(samples: SpatialTrainingSample[]): string; /** * Get current configuration. */ getConfig(): Readonly; /** * Update configuration. */ updateConfig(partial: Partial): void; private generateAdjacentSamples; private generateContainsSamples; private generateReachableSamples; /** * Enrich an object with auto-computed bounds if missing. */ private enrichObject; /** * Check if container contains the target object. */ private checkContainment; /** * Compute the ratio of target volume that overlaps with the container. */ private computeOverlapRatio; /** * Check line-of-sight reachability between two objects. */ private checkReachability; /** * Ray-AABB intersection test. */ private rayIntersectsAABB; /** * Compute directional labels between two positions. */ private computeDirections; /** * Build a complete training sample from a relationship. */ private buildSample; /** * Generate a natural language description of a spatial relationship. */ private generateDescription; /** * Generate a question-answer pair for VLM instruction tuning. */ private generateQA; /** * Build a scene description for instruction tuning context. */ private buildSceneDescription; /** * Generate tags for a sample. */ private generateTags; /** * Compute difficulty level for a sample. */ private computeDifficulty; /** * Format a Vector3 as a readable string. */ private formatVec3; /** * Compute a simple hash for a composition (for provenance tracking). */ private hashComposition; /** * Compute statistics for a generation run. */ private computeStats; /** * Shuffle array using seeded RNG for reproducibility. */ private shuffleArray; } /** * Create a SpatialDataGenerator with optional configuration overrides. * * @example * ```typescript * const generator = createSpatialDataGenerator({ * adjacencyThresholds: [1.0, 3.0, 5.0], * generateNegatives: true, * negativeRatio: 1.5, * }); * ``` */ export declare function createSpatialDataGenerator(config?: Partial): SpatialDataGenerator; /** * Parse a simplified HoloScript composition string into a SpatialComposition. * * This is a lightweight parser for the common case of compositions with * objects that have positions, scales, and basic types. For full HoloScript * parsing, use the main parser pipeline. * * @example * ```typescript * const composition = parseSimpleComposition(` * composition "TestScene" { * object "Table" { * geometry: "cube" * position: [0, 0.75, 0] * scale: [3, 0.1, 1.5] * } * object "Chair" { * geometry: "cube" * position: [1, 0.5, 1.2] * scale: [0.4, 0.5, 0.4] * } * } * `); * ``` */ export declare function parseSimpleComposition(source: string): SpatialComposition; //# sourceMappingURL=SpatialDataGenerator.d.ts.map