History Systems
BREP keeps several independent “history” timelines so modeling, PMI, and assembly constraint editing can all replay their steps deterministically. Each history owns the data it needs to recompute results but follows a shared pattern: store a list of entries, redo work by iterating through that list, and serialize just the declarative inputs plus any persistent outputs.
Shared building blocks
ListEntityBase(src/core/entities/ListEntityBase.js) standardizes the three buckets found across every history entry:inputParams(user-editable data),persistentData(saved outputs), andruntimeAttributes(ephemeral session state).HistoryCollectionBase(src/core/entities/HistoryCollectionBase.js) provides the minimal scaffolding for history managers: it keeps theentriesarray, anentityClassRegistry, listener bookkeeping, and a monotonically increasing_idCounter. PMI annotations inherit from this base directly, while the other histories implement the same shape manually for flexibility.- Registries translate user-facing strings into constructors. Modeling relies on
FeatureRegistry, PMI usesAnnotationRegistry, and assembly usesAssemblyConstraintRegistry. Every registry exposes tolerant lookups (getSafe) so importing legacy files or plugins does not blow up the run loop. - Each history exposes
toSerializable/toJSONstyle helpers that deep-clone fields suitable for storage (3MF metadata,.BREP.jsondumps, etc.) and matching load/replace helpers so the UI can restore state without bespoke glue.
Part History (Modeling)
PartHistory (src/PartHistory.js:20) is the backbone of modeling mode. It wires together the feature registry, the shared Three.js scene, PMI views, metadata, and the assembly constraint history.
- Constructor duties include initializing
features,scene, registries,PMIViewsManager, and a default expression scratchpad (src/PartHistory.js:20-38). The class also injects a soft override onscene.add/removeso debugging geometry churn is possible. runHistory()iterates overfeatures, lazily instantiates the correct feature class, sanitizes inputs, tracks “dirty” state, runs the feature, and records timestamps plus error metadata (src/PartHistory.js:120-280). Clearing happens up front so each rerun rebuilds the scene from a clean slate.newFeature()allocates the declarative entry object{ type, inputParams, persistentData }, seeds defaults viaextractDefaultValues, and assigns a uniqueidproduced bygenerateId()(src/PartHistory.js:642-654).sanitizeInputParams()later evaluates expressions, resolves reference selections back into live scene objects, and normalizes booleans, vectors, transforms, or boolean-operation payloads (src/PartHistory.js:665-758).- Dialog field types, defaults, and selection filters come from each entry’s
inputParamsSchema; see Input Params Schema for every widget option. toJSON()exports the bare inputs for every feature, expression code, PMI views, metadata, and a snapshot of the assembly constraint history (src/PartHistory.js:426-455).fromJSON()performs the inverse and forwards constraint payloads intoAssemblyConstraintHistory.replaceAll()so the solver can pick up where it left off (src/PartHistory.js:457-478).- File manager save/load uses the JSON helper to embed the complete part history (including PMI and constraints) inside a 3MF metadata entry (
src/UI/fileManagerWidget.js:248-320), ensuring a single file captures all three timelines.
PMI Annotation History
PMI mode keeps its own list of annotations per saved PMI view. AnnotationHistory (src/UI/pmi/AnnotationHistory.js:9) extends HistoryCollectionBase so it inherits the listener set, _idCounter, and serializable entry helpers.
- On construction, it registers every available annotation handler with the underlying registry and remembers the active
PMIModeviewer (src/UI/pmi/AnnotationHistory.js:9-20).PMIModewires this up whenever a PMI view is opened so annotation changes stay scoped to that view (src/UI/pmi/PMIMode.js:66-88). load()andtoSerializable()deep-clone the stored annotation descriptors, strip runtime-only flags (like accordion open state), and keep the declarative input payloads tiny for 3MF persistence (src/UI/pmi/AnnotationHistory.js:23-55).createAnnotation()looks up the handler viaAnnotationRegistry, merges schema defaults with the caller-provided seed, assigns a generated ID, and marks runtime flags like__openorenabled(src/UI/pmi/AnnotationHistory.js:95-129). Because annotations subclassListEntityBase, they expose arun()method that PMIMode calls when rebuilding temporary geometry, but only their inputs/persistent data get serialized.- Helpers such as
setAnnotationEnabled(),moveUp()/moveDown(), andclear()are thin wrappers aroundentriesmutations that also emit listener events to refresh the PMI widget (src/UI/pmi/AnnotationHistory.js:84-159).
Assembly Constraint History
AssemblyConstraintHistory (src/assemblyConstraints/AssemblyConstraintHistory.js:168-188) mirrors the modeling history but focuses on constraint records and solver execution.
- The constructor receives the owning
PartHistoryplus a registry and sets up arrays, listeners, and auto-run bookkeeping. Internally it normalizes constraint IDs, links runtime attributes (open state, entity references), and keepsconstraintClasspointers on each entry so the solver can call static helpers likeapplyParams. snapshot()andreplaceAll()provide the serialization surface.snapshot()deep-clonestype,inputParams, andpersistentDatasoPartHistory.toJSON()can embed the full constraint set (src/assemblyConstraints/AssemblyConstraintHistory.js:512-546).replaceAll()rebuilds the constraints array from imported JSON, copies schema defaults, rehydrates runtime links, and rebuilds the ID counter before notifying listeners (src/assemblyConstraints/AssemblyConstraintHistory.js:512-620).generateId()encodes the constraint type into the prefix (e.g.,DISTANCE1) so UI rows remain human-readable even after reordering (src/assemblyConstraints/AssemblyConstraintHistory.js:741-748).runAll()is the high-level solver. It pulls the activePartHistory, crawls the scene to resolve constraint selections, applies translation/rotation corrections with configurable gains, and emits viewer render hooks for debugging (src/assemblyConstraints/AssemblyConstraintHistory.js:749-860). The helper integrates with controller hooks so UI widgets can report progress or cancel iterations.- Error checking (
checkConstraintErrors()), duplicate detection, and auto-run scheduling keep the timeline responsive; listener callbacks keep the assembly constraint widget UI synchronized whenever entries change.
Using the histories together
Part history is the orchestrator: it owns the Three.js scene and instantiates both PMI and assembly histories. Each PMI view stores a serialized annotation history entry, and every part export bundles feature inputs + annotation descriptors + constraint graph. Because all three systems follow the same entry/persistent-data conventions, plugins or new tools can treat them uniformly—populate inputParams, call the relevant history manager, and rely on the shared JSON helpers to persist the result.