Pascal Editor Level System Display Modes: Stacked, Exploded, and Solo
The Pascal Editor level system supports three display modes—stacked, exploded, and solo—that control how multi-floor building levels are positioned and rendered in the 3D viewer.
The architectural visualization engine in pascalorg/editor manages multi-floor displays through the Viewer package's Level System. It reads the current levelMode from the global Zustand store and animates every LevelNode Object3D each frame. These display modes enable architects to inspect building structures as compact stacked floors, separated exploded views, or isolated single levels.
Understanding the Three Display Modes
The level system distinguishes between visualization states based on the levelMode value stored in useViewer (packages/viewer/src/store/use-viewer.ts).
Stacked Mode
In stacked mode, levels are placed one on top of another using the real computed height of each floor. The system accumulates the actual vertical space occupied by walls and ceilings without adding artificial gaps. Each level's target Y-position equals the cumulative height of all floors beneath it, calculated by getLevelHeight in packages/viewer/src/systems/level/level-utils.ts.
Exploded Mode
Exploded mode maintains the same base stacking logic but inserts a fixed separation gap between floors. According to packages/viewer/src/systems/level/level-system.tsx, the system adds EXPLODED_GAP = 5 units multiplied by the level index to create the visual "explosion" effect. This separation makes structural elements between floors visible for detailed inspection while preserving the relative order of levels.
Solo Mode
Solo mode isolates visibility to show only the currently selected level. When levelMode is set to 'solo', the system evaluates the selectedLevel ID from the viewer store. The visibility guard in packages/viewer/src/systems/level/level-system.tsx ensures that obj.visible remains true only for the matching level ID, or for all levels if nothing is selected, effectively hiding all non-selected floors.
Core Implementation and Animation
The system executes every render frame via useFrame with priority 5, ensuring updates occur after other geometry systems finish processing.
// packages/viewer/src/systems/level/level-system.tsx
useFrame((_, delta) => {
const nodes = useScene.getState().nodes
const levelMode = useViewer.getState().levelMode
const selectedLevel = useViewer.getState().selection.levelId
// ... position and visibility logic
}, 5)
For each frame, the system calculates target positions and applies linear interpolation for smooth animation. The cumulative Y offset advances by the true height of each level.
const baseY = cumulativeY
const explodedExtra = levelMode === 'exploded' ? index * EXPLODED_GAP : 0
const targetY = baseY + explodedExtra
obj.position.y = lerp(obj.position.y, targetY, delta * 12)
The animation uses a lerp factor of delta * 12 to create responsive yet smooth transitions between display states.
Height Calculation and Performance
Accurate stacking requires knowing each level's true vertical extent. The getLevelHeight function in packages/viewer/src/systems/level/level-utils.ts computes this by iterating over a level's children, examining wall mesh positions and ceiling heights, then returning the maximum top value. If no geometry is found, it returns a default height of 2.5 units.
Results are memoized against the nodes object reference, providing O(1) lookups after initial computation. Levels are collected from the scene registry using sceneRegistry.byType.level and sorted by their logical floor index (level?.level) before processing.
Changing Display Modes Programmatically
The levelMode state lives in the global viewer Zustand store and persists across sessions via viewer-preferences.
Switching to Exploded View
import useViewer from '@pascal-app/viewer/src/store/use-viewer';
const showExploded = () => {
useViewer.getState().setLevelMode('exploded');
};
Focusing on a Single Level
import useViewer from '@pascal-app/viewer/src/store/use-viewer';
const focusOnLevel = (levelId: string) => {
useViewer.getState().setSelection({ levelId });
useViewer.getState().setLevelMode('solo');
};
UI components in the editor package, such as packages/editor/src/components/ui/command-palette/index.tsx and packages/editor/src/components/ui/action-menu/view-toggles.tsx, invoke setLevelMode through the same store interface.
Manual Snap Helper for Static Rendering
When generating high-resolution exports, animated transitions may cause misalignment. The snapLevelsToTruePositions helper in packages/viewer/src/systems/level/level-utils.ts temporarily forces every level to its exact stacked Y-position, bypassing the current animation mode.
import { snapLevelsToTruePositions } from '@pascal-app/viewer/src/systems/level/level-utils';
const restore = snapLevelsToTruePositions();
// Execute render pass here
restore(); // Resume animated positions
This function returns a restoration callback that re-enables the animation system after the static render completes.
Summary
- The stacked mode places floors directly atop one another using real computed heights from
getLevelHeight. - Exploded mode adds a fixed 5-unit gap between floors for structural inspection.
- Solo mode hides all non-selected levels via the visibility guard in
level-system.tsx. - Animation runs each frame via
useFramepriority 5, lerping positions with a factor ofdelta * 12. - Height calculations are memoized in
level-utils.tsfor O(1) performance after initial computation. - Programmatic control is available through
useViewer.getState().setLevelMode()andsetSelection(). - The
snapLevelsToTruePositionshelper enables accurate static renders by temporarily disabling animations.
Frequently Asked Questions
How does the Pascal Editor calculate the vertical gap in exploded mode?
The system adds a fixed constant EXPLODED_GAP set to 5 units, multiplied by the level's index in the stack. This value is added to the base cumulative height in packages/viewer/src/systems/level/level-system.tsx to create the separation effect between floors.
What happens to level visibility when switching to solo mode?
When levelMode is set to 'solo', the system evaluates obj.visible based on whether the level ID matches the selectedLevel from the viewer store. If no level is selected, all levels remain visible; otherwise, only the selected level renders while others are hidden immediately without animation.
Where is the level display mode state stored in the Pascal Editor?
The state persists in the Zustand store defined in packages/viewer/src/store/use-viewer.ts, which includes the levelMode property and setLevelMode action. The store persists user preferences via viewer-preferences so the selected mode survives page reloads.
How can I temporarily disable animations for a screenshot export?
Import snapLevelsToTruePositions from packages/viewer/src/systems/level/level-utils.ts and call it before rendering. This function immediately snaps all levels to their true stacked positions and returns a restore function to resume the animated level system afterward.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →