Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e009f36
Use wall face mitering for draft angle arcs
sudhir9297 May 14, 2026
6af3a0a
Add draft angle arcs for walls and fences
sudhir9297 May 14, 2026
db5c743
Render curved wall measurements along the wall
sudhir9297 May 14, 2026
2b9bb76
feat: add camera-aware wall move handles and UI layer for floorplan e…
sudhir9297 May 14, 2026
5f17172
refactor: make onMove optional in FloorplanActionMenuEntry and remove…
sudhir9297 May 14, 2026
7cc11f0
feat: add fence move handles and duplication functionality to the flo…
sudhir9297 May 14, 2026
3800bd7
fix: increase stair opening buffer constraints and set default offset…
sudhir9297 May 14, 2026
cdde88f
Clamp spiral stair openings at full sweep
sudhir9297 May 14, 2026
4a40275
Fix floorplan labels and door/fence geometry
sudhir9297 May 14, 2026
2ee69e2
Organize door panel controls by door family
sudhir9297 May 14, 2026
89c6280
Refine window panel by family
sudhir9297 May 14, 2026
4fc57a6
Enhance elevator placement preview
sudhir9297 May 14, 2026
023cd51
Remove material strips from roof, stair, and fence panels
sudhir9297 May 14, 2026
5d53be2
Refine wall chaining and door arch clipping
sudhir9297 May 15, 2026
5bd2f51
Make fence drawing continue between segments
sudhir9297 May 15, 2026
d7a1ce8
Scope first-person overlay and unify elevator colors
sudhir9297 May 15, 2026
a3378a6
Preserve elevator stop order and handle viewport resize
sudhir9297 May 15, 2026
a9d2623
Refactor material catalog for roofing and flooring
sudhir9297 May 15, 2026
8b73d79
feat: expand material library with new wood and flooring textures whi…
sudhir9297 May 15, 2026
c31c08f
feat: implement StairOpeningSystem to handle automated stair opening …
sudhir9297 May 15, 2026
fe68dbe
Constrain wall moves along wall normals
sudhir9297 May 15, 2026
d48be77
Reduce wall rebuilds during window and door placement
sudhir9297 May 16, 2026
3adae8a
style: apply biome lint fixes after portforward
open-pascal May 20, 2026
251af2c
fix: add null guards for cursorRef in fence and wall tool click handlers
open-pascal May 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Reduce wall rebuilds during window and door placement
  • Loading branch information
sudhir9297 authored and open-pascal committed May 20, 2026
commit d48be77f5dd0cc70dca21429bb27abddfcd66533
Binary file modified apps/editor/public/icons/elevator.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ export function usePlacementCoordinator(config: PlacementCoordinatorConfig): Rea
const basePlaneRef = useRef<Mesh>(null!)
const gridPosition = useRef(new Vector3(0, 0, 0))
const lastRawPos = useRef(new Vector3(0, 0, 0))
const lastWallDirtyAtRef = useRef(new Map<string, number>())
const placementState = useRef<PlacementState>(
config.initialState ?? {
surface: 'floor',
Expand Down Expand Up @@ -742,7 +743,13 @@ export function usePlacementCoordinator(config: PlacementCoordinatorConfig): Rea
}
// Mark parent wall dirty so it rebuilds geometry — only when position changed
if (result.dirtyNodeId && posChanged) {
useScene.getState().dirtyNodes.add(result.dirtyNodeId)
const now = globalThis.performance?.now?.() ?? Date.now()
const last = lastWallDirtyAtRef.current.get(result.dirtyNodeId) ?? 0
// Wall rebuilds can trigger expensive CSG; throttle live previews to avoid FPS collapse.
if (now - last > 120) {
lastWallDirtyAtRef.current.set(result.dirtyNodeId, now)
useScene.getState().dirtyNodes.add(result.dirtyNodeId)
}
}

// Publish live transform for 2D floorplan
Expand Down
7 changes: 6 additions & 1 deletion packages/nodes/src/column/move-tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ function MoveColumnTool({ node }: { node: ColumnNode }) {
useEffect(() => {
useScene.temporal.getState().pause()
let committed = false
const meta =
typeof node.metadata === 'object' && node.metadata !== null
? (node.metadata as Record<string, unknown>)
: {}
const isNew = !!meta.isNew

const applyPreview = (position: [number, number, number]) => {
setPreviewPosition(position)
Expand All @@ -66,7 +71,7 @@ function MoveColumnTool({ node }: { node: ColumnNode }) {
committed = true
useLiveTransforms.getState().clear(nodeId)
useScene.temporal.getState().resume()
useScene.getState().updateNode(nodeId, { position })
useScene.getState().updateNode(nodeId, { position, ...(isNew ? { metadata: {} } : {}) })
} else if (node.parentId) {
const column = ColumnNodeSchema.parse({
...node,
Expand Down
15 changes: 13 additions & 2 deletions packages/nodes/src/door/move-tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ const MoveDoorTool: React.FC<{ node: DoorNode }> = ({ node: movingDoorNode }) =>
const markWallDirty = (wallId: string | null) => {
if (wallId) useScene.getState().dirtyNodes.add(wallId as AnyNodeId)
}
const lastWallDirtyAt = new Map<string, number>()
const markWallDirtyThrottled = (wallId: string | null) => {
if (!wallId) return
const now = globalThis.performance?.now?.() ?? Date.now()
const last = lastWallDirtyAt.get(wallId) ?? 0
// Wall rebuilds can trigger expensive CSG; throttle live previews to avoid FPS collapse.
if (now - last > 120) {
lastWallDirtyAt.set(wallId, now)
markWallDirty(wallId)
}
}

const getLevelId = () => useViewer.getState().selection.levelId
const getLevelYOffset = () => {
Expand Down Expand Up @@ -144,7 +155,7 @@ const MoveDoorTool: React.FC<{ node: DoorNode }> = ({ node: movingDoorNode }) =>
})

if (prevWallId && prevWallId !== event.node.id) markWallDirty(prevWallId)
markWallDirty(event.node.id)
markWallDirtyThrottled(event.node.id)

const valid = !hasWallChildOverlap(
event.node.id,
Expand Down Expand Up @@ -212,7 +223,7 @@ const MoveDoorTool: React.FC<{ node: DoorNode }> = ({ node: movingDoorNode }) =>
position: [clampedX, clampedY, 0],
rotation: itemRotation,
})
markWallDirty(event.node.id)
markWallDirtyThrottled(event.node.id)

const valid = !hasWallChildOverlap(
event.node.id,
Expand Down
51 changes: 27 additions & 24 deletions packages/nodes/src/window/move-tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,29 @@ const MoveWindowTool: React.FC<{ node: WindowNode }> = ({ node: movingWindowNode
metadata: movingWindowNode.metadata,
}

if (!isNew) {
// Move mode: mark the existing window as transient so it hides while being repositioned
useScene.getState().updateNode(movingWindowNode.id, {
metadata: { ...meta, isTransient: true },
})
}
// Mark the moving window as transient so it doesn't intercept wall raycasts while repositioning.
// Without this, duplicates can block `wall:*` events which breaks the cursor box and can cause
// rapid enter/leave churn (triggering expensive wall CSG rebuilds).
useScene.getState().updateNode(movingWindowNode.id, {
metadata: { ...meta, isTransient: true },
})

let currentWallId: string | null = movingWindowNode.parentId

const markWallDirty = (wallId: string | null) => {
if (wallId) useScene.getState().dirtyNodes.add(wallId as AnyNodeId)
}
const lastWallDirtyAt = new Map<string, number>()
const markWallDirtyThrottled = (wallId: string | null) => {
if (!wallId) return
const now = globalThis.performance?.now?.() ?? Date.now()
const last = lastWallDirtyAt.get(wallId) ?? 0
// Wall rebuilds can trigger expensive CSG; throttle live previews to avoid FPS collapse.
if (now - last > 120) {
lastWallDirtyAt.set(wallId, now)
markWallDirty(wallId)
}
}

const getLevelId = () => useViewer.getState().selection.levelId
const getLevelYOffset = () => {
Expand Down Expand Up @@ -151,7 +162,7 @@ const MoveWindowTool: React.FC<{ node: WindowNode }> = ({ node: movingWindowNode
})

if (prevWallId && prevWallId !== event.node.id) markWallDirty(prevWallId)
markWallDirty(event.node.id)
markWallDirtyThrottled(event.node.id)

const valid = !hasWallChildOverlap(
event.node.id,
Expand Down Expand Up @@ -224,7 +235,7 @@ const MoveWindowTool: React.FC<{ node: WindowNode }> = ({ node: movingWindowNode
position: [clampedX, clampedY, 0],
rotation: itemRotation,
})
markWallDirty(event.node.id)
markWallDirtyThrottled(event.node.id)

const valid = !hasWallChildOverlap(
event.node.id,
Expand Down Expand Up @@ -286,28 +297,20 @@ const MoveWindowTool: React.FC<{ node: WindowNode }> = ({ node: movingWindowNode
useScene.getState().deleteNode(movingWindowNode.id)
useScene.temporal.getState().resume()

const cloned = structuredClone(movingWindowNode) as any
delete cloned.id
if (cloned.metadata && typeof cloned.metadata === 'object') {
delete cloned.metadata.isNew
delete cloned.metadata.isTransient
}

const node = WindowNode.parse({
...cloned,
position: [clampedX, clampedY, 0],
rotation: [0, itemRotation, 0],
side,
wallId: event.node.id,
parentId: event.node.id,
width: movingWindowNode.width,
height: movingWindowNode.height,
windowType: movingWindowNode.windowType,
operationState: movingWindowNode.operationState,
awningDirection: movingWindowNode.awningDirection,
casementStyle: movingWindowNode.casementStyle,
hingesSide: movingWindowNode.hingesSide,
frameThickness: movingWindowNode.frameThickness,
frameDepth: movingWindowNode.frameDepth,
columnRatios: movingWindowNode.columnRatios,
rowRatios: movingWindowNode.rowRatios,
columnDividerThickness: movingWindowNode.columnDividerThickness,
rowDividerThickness: movingWindowNode.rowDividerThickness,
sill: movingWindowNode.sill,
sillDepth: movingWindowNode.sillDepth,
sillThickness: movingWindowNode.sillThickness,
})
useScene.getState().createNode(node, event.node.id as AnyNodeId)
placedId = node.id
Expand Down
5 changes: 4 additions & 1 deletion packages/viewer/src/systems/door/door-system.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ export const DoorSystem = () => {
clearDirty(id as AnyNodeId)

// Rebuild the parent wall so its cutout reflects the updated door geometry
if ((node as DoorNode).parentId) {
// Avoid triggering expensive wall CSG rebuilds while the door is being interactively moved/duplicated.
// The editor tools will request a final wall rebuild on commit.
const isTransient = !!(node.metadata as Record<string, unknown> | null)?.isTransient
if (!isTransient && (node as DoorNode).parentId) {
useScene.getState().dirtyNodes.add((node as DoorNode).parentId as AnyNodeId)
}
})
Expand Down
5 changes: 4 additions & 1 deletion packages/viewer/src/systems/window/window-system.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export const WindowSystem = () => {
clearDirty(id as AnyNodeId)

// Rebuild the parent wall so its cutout reflects the updated window geometry
if ((node as WindowNode).parentId) {
// Avoid triggering expensive wall CSG rebuilds while the window is being interactively moved/duplicated.
// The editor tools will request a final wall rebuild on commit.
const isTransient = !!(node.metadata as Record<string, unknown> | null)?.isTransient
if (!isTransient && (node as WindowNode).parentId) {
useScene.getState().dirtyNodes.add((node as WindowNode).parentId as AnyNodeId)
}
})
Expand Down