Skip to content

3D Editor

The 3D editor is the core of the YAPTIDE frontend. It provides a visual environment for building particle transport simulation geometries, defining beams, and configuring scoring.

The editor is implemented in ThreeEditor/js/YaptideEditor.js — a ~780-line imperative class using the prototype pattern (forked from the Three.js editor project). It is exposed globally as window.YAPTIDE_EDITOR for debugging.

The Store context holds the singleton YaptideEditor instance, accessible via useStore().

The editor uses a manager pattern to organize different simulation components:

ManagerWhat it ManagesLocation
FigureManagerGeometric primitivesSimulation/Figures/
ZoneManagerBoolean CSG zonesSimulation/Zones/
MaterialManagerMaterial definitionsSimulation/Materials/
DetectorManagerScoring detectorsSimulation/Detectors/
ScoringManagerOutputs, quantities, filtersSimulation/Scoring/
SpecialComponentManagerCT cubes, beam modulatorsSimulation/Special/
BeamParticle sourceSimulation/Physics/Beam.ts
PhysicsPhysics model settingsSimulation/Physics/

Manages 3D solid primitives:

Figure TypeThree.js GeometryParameters
BoxFigureBoxGeometryxLength, yLength, zLength
CylinderFigureHollowCylinderGeometryradius, height (+ optional inner radius)
SphereFigureSphereGeometryradius

Each figure has position, rotation, and a unique UUID. Figures are rendered in the 3D viewport and referenced by zones.

Zones define regions via Constructive Solid Geometry (CSG):

  • Union — combine figures to form a larger region
  • Intersection — take the overlap of figures
  • Subtraction — remove one figure’s volume from another

Each zone is assigned a material. The worldZone is a special bounding zone that defines the simulation boundary.

Four detector types for scoring:

TypeGeometryUse Case
CylinderCylindrical meshDepth-dose curves, radial profiles
MeshRectangular grid2D/3D dose maps
ZoneMatches a zone’s geometryScore within a specific region
AllEntire simulation volumeGlobal scoring

ThreeEditor/js/viewport/ViewportManager.js manages the 3D rendering:

The viewport supports a quad-split layout:

┌──────────────┬──────────────┐
│ Top (XY) │ Top (XZ) │
│ │ │
├──────────────┼──────────────┤
│ Side (YZ) │ Perspective │
│ │ (3D) │
└──────────────┴──────────────┘

Each pane can be individually resized. The split is implemented using split-grid.

  • Orbit controls — rotate, pan, zoom the perspective view
  • Transform gizmos — translate, rotate, and scale selected objects
  • Selection box — click-to-select objects in any view
  • Grid helpers — reference grid for spatial orientation
  • Camera helpers — beam direction indicator

The viewport supports CSG clipped views for cross-section visualization. This lets users see inside complex zone configurations.

ThreeEditor/components/Sidebar/EditorSidebar.tsx provides a 3-tab sidebar:

  • Figures — tree view of all primitives, with add/remove/edit
  • Zones — tree view of boolean zone definitions
  • Detectors — tree view of scoring detectors
  • Special components — modulators, CT cubes
  • Outputs — each output links a detector to scored quantities
  • Quantities — dose, fluence, LET, etc.
  • Filters — particle filters for conditional scoring
  • Beam — particle type, energy, position, direction, divergence
  • Physics — energy loss, nuclear reactions, scattering models

EditorContext.ts manages which sidebar context is active:

ContextSidebar ContentViewport Behavior
geometryFigures, zones, detectorsFull 3D editing with gizmos
scoringOutputs, quantities, filtersDetector geometry visualization
settingsBeam, physicsBeam direction visualization

The editor uses the signals library for event-driven communication between the imperative editor core and the React UI layer.

SignalTriggered When
objectAddedA new object is added to the scene
objectRemovedAn object is removed
objectChangedAn object’s properties change
objectSelectedAn object is clicked/selected
zoneGeometryChangedA zone’s CSG definition changes
scoringQuantityChangedA scoring quantity is added/modified
editorClearedThe scene is completely cleared
sceneGraphChangedThe scene hierarchy changes
historyChangedThe undo/redo stack changes

Use the useSignal hook to subscribe to signals in React components:

import { useSignal } from '../hooks/useSignal';
function MyComponent() {
const [selectedObject, setSelectedObject] = useState(null);
useSignal('objectSelected', (object) => {
setSelectedObject(object);
});
return <div>{selectedObject?.name}</div>;
}

The editor serializes its entire state to a JSON format. This JSON:

  • Is the input to the converter (JSON → simulator input files)
  • Is auto-saved to localStorage on every change
  • Can be exported/imported as .json files
  • Is sent to the backend when submitting a simulation

The serialization captures:

  • All figures with geometry, position, rotation
  • Zone definitions (boolean operations + material assignments)
  • Beam configuration
  • Detector geometries
  • Scoring outputs, quantities, and filters
  • Physics settings
  • Special components