Files
uLib/docs/transformation_system.md
2026-03-27 15:02:17 +00:00

3.6 KiB

Transformation Flow and Synchronization System

This document describes how transformations are applied and synchronized between the interactive 3D viewport, the visualization puppets, and the underlying mathematical models within the uLib framework.

Architecture Overview

The system follows a Model-View-Controller (MVC) like pattern where:

  • Model: uLib::AffineTransform (or derived classes like ContainerBox).
  • View/Puppet: uLib::Vtk::Puppet (and specialized derivations like Vtk::Assembly).
  • Controller/Interaction: vtkHandlerWidget (the transformation gizmo).

1. Interaction Flow (Gizmo -> Model)

When a user interacts with the vtkHandlerWidget (dragging arrows, rings, or cubes), the following chain of events occurs:

sequenceDiagram
    participant User
    participant HW as vtkHandlerWidget
    participant VP as vtkViewport
    participant P as vtkPuppet
    participant M as uLib Model

    User->>HW: Drag handle (MouseMove)
    HW->>HW: Calculate Delta Matrix (op)
    HW->>HW: Total = StartState * op
    HW->>HW: Decompose Total into P, O, S
    HW->>P: SetPosition, SetOrientation, SetScale
    HW-->>VP: Invoke InteractionEvent
    VP->>P: SyncFromVtk()
    P->>P: Get local matrix from VTK Prop
    P->>M: SetMatrix(matrix)
    M-->>M: Update local properties (P, O, S)
    M-->>P: Emit Updated signal
    P->>P: Puppet::Update()
    P->>P: (Redundant sanity write to Prop)

Key Principles:

  • Single Source of Truth: The uLib::AffineTransform is the owner of the transformation state.
  • Internal TRS vs UserMatrix: We apply transformations directly to VTK's internal Position, Orientation, and Scale properties. This ensures the data is "visible" to VTK actors and simplifies decomposition.
  • Cumulative Bias Avoidance: The HandlerWidget calculates transformations relative to the state at the start of the click, preventing numerical drift during a single drag operation.

2. Synchronization Loop Resolution

To prevent infinite loops and "double-transformation" artifacts (especially in assemblies), the following protections are in place:

  1. Hierarchy Isolation: The Puppet base class distinguishes between the Root Property (which receives the puppet's master transformation) and Sub-Parts (which only receive appearance updates like color/visibility). This prevents parts from inheriting the same displacement twice.
  2. Re-entrancy Guards: Puppets use an m_InUpdate flag to prevent a feedback loop where SyncFromVtk triggers a Model Update, which then re-triggers the Puppet Update.
  3. Signal Blocking: In specialized cases (like vtkAssembly), m_BlockUpdate is used to prevent the model-to-puppet push during a puppet-to-model sync.

3. Undo System (Ctrl-Z)

Current Implementation (Delta Chain)

Currently, the system maintains a m_TransformChain of delta matrices.

  • Record: After every drag, a delta matrix (M_{delta} = M_{end} \cdot M_{start}^{-1}) is appended to the chain.
  • Undo: The last delta is removed, and the prop is reconstructed by reapplying the remaining chain from a BaseMatrix.

Planned Improvement (TRS Snapshots)

We are migrating to a uLib::TRS snapshot system for Undo.

  • Record: At the start of a drag, the current TRS state of the object is pushed to the m_UndoStack.
  • Undo: The top TRS is popped and applied directly to the model.

This approach is more robust because:

  • It eliminates matrix multiplication error accumulation.
  • It bypasses rotation convention/order issues (Gimbal lock in deltas).
  • It returns the object to exactly its previous property values.