# How to Use Pascal Editor Wall Cutaway Mode to Reveal Interior Spaces

> Master Pascal Editor's wall cutaway mode to reveal interior spaces. Learn how to toggle views and enhance your architectural visualizations with real-time Three.js material swapping.

- Repository: [Pascal/editor](https://github.com/pascalorg/editor)
- Tags: how-to-guide
- Published: 2026-03-25

---

**Pascal Editor provides a non-destructive wall cutaway mode that toggles between full-height, cutaway, and low wall views using a persisted Zustand store and real-time Three.js material swapping.**

The **pascalorg/editor** repository implements this architectural visualization feature across three coordinated layers: a global state manager in the viewer package, a toggle button in the editor overlay, and a frame-based rendering system that dynamically swaps wall materials based on camera angle and selected mode.

## State Management with `useViewer`

The wall cutaway mode persists user preferences through a Zustand store defined in [`packages/viewer/src/store/use-viewer.ts`](https://github.com/pascalorg/editor/blob/main/packages/viewer/src/store/use-viewer.ts).

The store exposes a `wallMode` field with three distinct states:

- **`'up'`** – Renders walls at full height
- **`'cutaway'`** – Hides walls facing away from the camera to reveal interiors
- **`'down'`** – Hides all walls completely

```ts
// packages/viewer/src/store/use-viewer.ts
wallMode: 'up' | 'cutaway' | 'down'
setWallMode: (mode: 'up' | 'cutaway' | 'down') => void

```

The state automatically persists under the *viewer-preferences* storage key, ensuring the selected mode survives page reloads.

## Toggling Wall Modes in the UI

The `ViewerOverlay` component in [`packages/editor/src/components/viewer-overlay.tsx`](https://github.com/pascalorg/editor/blob/main/packages/editor/src/components/viewer-overlay.tsx) provides the primary interface for switching modes. An **ActionButton** positioned at the bottom-center of the viewport cycles through the three states when clicked.

```tsx
// packages/editor/src/components/viewer-overlay.tsx
const wallModeConfig = {
  up:      { icon: FullHeightIcon, label: 'Full Height' },
  cutaway: { icon: CutawayIcon, label: 'Cutaway' },
  down:    { icon: LowIcon, label: 'Low' }
}

<ActionButton
  className={ wallMode !== 'cutaway' ? 'bg-white/10' : 'opacity-60 grayscale' }
  label={`Walls: ${wallModeConfig[wallMode].label}`}
  onClick={() => {
    const modes: ('cutaway'|'up'|'down')[] = ['cutaway','up','down']
    const nextIndex = (modes.indexOf(wallMode)+1) % modes.length
    useViewer.getState().setWallMode(modes[nextIndex] ?? 'cutaway')
  }}
>
  {(() => {
    const Icon = wallModeConfig[wallMode].icon
    return <Icon className="h-[28px] w-[28px]" />
  })()}
</ActionButton>

```

The component reads the current mode using `useViewer((s) => s.wallMode)`, ensuring the icon and label update immediately when the state changes.

## How the Cutaway Rendering System Works

The core rendering logic lives in [`packages/viewer/src/systems/wall/wall-cutout.tsx`](https://github.com/pascalorg/editor/blob/main/packages/viewer/src/systems/wall/wall-cutout.tsx). A `useFrame` hook executes on every animation frame to determine wall visibility based on the active `wallMode`.

### The Three Rendering Modes

The system evaluates each wall mesh against the current mode:

- **Full Height (`'up'`)**: Sets `hideWall = false`, rendering all walls with solid white material
- **Low (`'down'`)**: Sets `hideWall = true`, hiding all walls to show only the floor plan
- **Cutaway (`'cutaway'`)**: Calculates the dot product between the wall's world normal and the camera-to-wall vector to determine which surfaces face away from the viewer

```tsx
// packages/viewer/src/systems/wall/wall-cutout.tsx
useFrame(({ camera, clock }) => {
  const wallMode = useViewer.getState().wallMode
  
  if (wallMode === 'up') {
    hideWall = false
  } else if (wallMode === 'down') {
    hideWall = true
  } else {
    wallMesh.getWorldDirection(v)
    if (v.dot(u) < 0) {  // Front side check
      if (wallNode.frontSide === 'exterior' && wallNode.backSide !== 'exterior') 
        hideWall = true
    } else {  // Back side check
      if (wallNode.backSide === 'exterior' && wallNode.frontSide !== 'exterior') 
        hideWall = true
    }
  }
  
  ;(wallMesh as Mesh).material = hideWall ? invsibleWallMaterial : wallMaterial
})

```

### Camera-Based Visibility Logic

In **cutaway** mode, the system uses vector mathematics to determine wall orientation. It calculates the dot product between the wall's normalized direction vector (`v`) and the vector from camera to wall (`u`). A negative result indicates the wall faces the camera (front side), while a positive result indicates the back side faces the camera.

Walls are only hidden when they represent exterior surfaces facing away from the viewer, preserving interior partition walls to maintain spatial context.

### Material Switching and Dot Patterns

The system swaps between two `MeshStandardNodeMaterial` instances:

**Solid Wall Material**:

```tsx
const wallMaterial = new MeshStandardNodeMaterial({ 
  color: 'white', 
  roughness: 1, 
  metalness: 0 
})

```

**Cutaway Material**:

```tsx
const invsibleWallMaterial = new MeshStandardNodeMaterial({
  transparent: true,
  opacityNode: mix(float(0.0), float(0.24), dotPattern()),
  color: 'white',
  depthWrite: false,
  emissive: 'white'
})

```

The **dot pattern** shader (lines 13-34 of [`wall-cutout.tsx`](https://github.com/pascalorg/editor/blob/main/wall-cutout.tsx)) generates a grid of semi-transparent circles that fade toward the wall tops, providing visual context while maintaining transparency.

### Performance Optimization

The `useFrame` hook implements throttling to prevent unnecessary recomputation. Updates only occur when:

- The camera moves more than 0.5 meters **or** changes direction by more than 0.3 meters, **and** at least 100ms have elapsed
- The `wallMode` state changes
- The wall count in the scene changes

Previous values are cached in `useRef` hooks (`lastCameraPosition`, `lastWallMode`) to minimize frame calculations.

## Implementing Custom Wall Cutaway Controls

You can programmatically control the wall cutaway mode from any component in the application.

### Programmatic Mode Switching

```tsx
import useViewer from '@pascal-app/viewer'

// Enable cutaway mode to reveal interior spaces
useViewer.getState().setWallMode('cutaway')

// Restore full-height walls
useViewer.getState().setWallMode('up')

```

### Custom Toggle Component

```tsx
import { useViewer } from '@pascal-app/viewer'
import { Button } from '@pascal-app/ui'

export const CutawayToggle = () => {
  const wallMode = useViewer((s) => s.wallMode)

  const toggle = () => {
    const order: ('cutaway'|'up'|'down')[] = ['cutaway', 'up', 'down']
    const next = order[(order.indexOf(wallMode) + 1) % order.length]
    useViewer.getState().setWallMode(next)
  }

  return (
    <Button onClick={toggle}>
      {wallMode === 'cutaway' ? 'Show All Walls' : 'Enable Cutaway'}
    </Button>
  )
}

```

### Extending Cutaway Logic

To customize which walls remain visible, modify the evaluation logic in `WallCutout`. For example, to always hide load-bearing walls regardless of camera angle:

```tsx
if (wallMode === 'cutaway') {
  wallMesh.getWorldDirection(v)
  const isBackFacing = v.dot(u) >= 0
  if (isBackFacing || wallNode.isLoadBearing) hideWall = true
}

```

## Key Source Files and Architecture

| File | Purpose |
|------|---------|
| [`packages/viewer/src/store/use-viewer.ts`](https://github.com/pascalorg/editor/blob/main/packages/viewer/src/store/use-viewer.ts) | Defines the persisted `wallMode` state and `setWallMode` action |
| [`packages/editor/src/components/viewer-overlay.tsx`](https://github.com/pascalorg/editor/blob/main/packages/editor/src/components/viewer-overlay.tsx) | Implements the bottom-center toggle button with mode-specific icons |
| [`packages/viewer/src/systems/wall/wall-cutout.tsx`](https://github.com/pascalorg/editor/blob/main/packages/viewer/src/systems/wall/wall-cutout.tsx) | Contains the `useFrame` loop and material switching logic |
| [`packages/editor/src/components/ui/action-menu/view-toggles.tsx`](https://github.com/pascalorg/editor/blob/main/packages/editor/src/components/ui/action-menu/view-toggles.tsx) | Alternative menu entry for wall mode in the side action menu |
| [`packages/editor/src/components/ui/command-palette/editor-commands.tsx`](https://github.com/pascalorg/editor/blob/main/packages/editor/src/components/ui/command-palette/editor-commands.tsx) | Keyboard-accessible commands for switching wall modes |

This architecture separates UI concerns from rendering logic, allowing the cutaway system to run independently of the React component tree while remaining synchronized through the Zustand store.

## Summary

- **Pascal Editor wall cutaway mode** uses a three-state system (`up`, `cutaway`, `down`) managed by the `useViewer` Zustand store
- The mode persists automatically via the *viewer-preferences* storage key
- Rendering occurs in [`packages/viewer/src/systems/wall/wall-cutout.tsx`](https://github.com/pascalorg/editor/blob/main/packages/viewer/src/systems/wall/wall-cutout.tsx) using a `useFrame` hook that evaluates camera angle and wall normals
- **Cutaway** mode hides only exterior-facing walls using dot-product calculations, while **Low** mode hides all walls
- Materials swap between solid white and a custom dot-pattern shader with variable opacity
- The system optimizes performance through camera movement throttling and cached state comparisons

## Frequently Asked Questions

### How does the wall cutaway mode determine which walls to hide?

The system calculates the dot product between each wall's world normal vector and the vector from camera to wall. In `cutaway` mode, walls facing away from the camera (positive dot product) with exterior-facing sides are hidden, while interior partitions remain visible. This logic resides in the `useFrame` hook within [`packages/viewer/src/systems/wall/wall-cutout.tsx`](https://github.com/pascalorg/editor/blob/main/packages/viewer/src/systems/wall/wall-cutout.tsx).

### Can I extend the wall cutaway mode to support custom wall properties?

Yes. Modify the `else` block in `WallCutout` where `hideWall` is determined. You can access any custom properties on `wallNode` (such as `isLoadBearing` or user-defined tags) and combine them with the existing camera-facing logic to create custom visibility rules while maintaining the existing mode structure.

### Where is the wall mode state persisted, and does it survive refreshes?

The state persists in `localStorage` under the *viewer-preferences* key via Zustand's persistence middleware defined in [`packages/viewer/src/store/use-viewer.ts`](https://github.com/pascalorg/editor/blob/main/packages/viewer/src/store/use-viewer.ts). When users reload the page, the `wallMode` value automatically restores to the previously selected setting.

### What performance optimizations does the cutaway system use?

The `useFrame` hook implements distance-based throttling, only recalculating visibility when the camera moves more than 0.5 meters or changes direction significantly, with a minimum 100ms delay between updates. It also caches the previous `wallMode`, camera position, and wall count in refs to avoid unnecessary material swaps.