fix placement

This commit is contained in:
wass08
2026-02-25 15:21:19 +09:00
parent b114e60df7
commit ced76b0475
5 changed files with 38 additions and 19 deletions
@@ -20,7 +20,7 @@ const isNodeInCurrentLevel = (node: AnyNode): boolean => {
return nodeLevelId === currentLevelId;
};
type SelectableNodeType = "wall" | "item" | "building" | "zone" | 'slab' | 'ceiling' | 'roof' | 'window';
type SelectableNodeType = "wall" | "item" | "building" | "zone" | 'slab' | 'ceiling' | 'roof' | 'window' | 'door';
interface SelectionStrategy {
types: SelectableNodeType[];
@@ -44,7 +44,7 @@ const SELECTION_STRATEGIES: Record<string, SelectionStrategy> = {
},
structure: {
types: ["wall", "item", "zone", "slab", "ceiling", "roof", "window"],
types: ["wall", "item", "zone", "slab", "ceiling", "roof", "window", "door"],
handleSelect: (node, isShift) => {
const { selection, setSelection } = useViewer.getState();
if (node.type === 'zone') {
@@ -80,7 +80,7 @@ const SELECTION_STRATEGIES: Record<string, SelectionStrategy> = {
(node as ItemNode).asset.category === "window"
);
}
if (node.type === "window") return true;
if (node.type === "window" || node.type === "door") return true;
return false;
}
+28 -11
View File
@@ -91,6 +91,23 @@ export function DoorPanel() {
setSelection({ selectedIds: [] })
}, [node, setMovingNode, setSelection])
const setSegmentHeightRatio = (segIdx: number, newVal: number) => {
const numSegs = node!.segments.length
const totalH = node!.segments.reduce((sum, s) => sum + s.heightRatio, 0)
const normH = node!.segments.map(s => s.heightRatio / totalH)
const clamped = Math.max(0.05, Math.min(0.95, newVal))
const neighborIdx = segIdx < numSegs - 1 ? segIdx + 1 : segIdx - 1
const delta = clamped - normH[segIdx]!
const neighborVal = Math.max(0.05, normH[neighborIdx]! - delta)
const newRatios = normH.map((v, i) => {
if (i === segIdx) return clamped
if (i === neighborIdx) return neighborVal
return v
})
const updated = node!.segments.map((s, idx) => ({ ...s, heightRatio: newRatios[idx]! }))
handleUpdate({ segments: updated })
}
const setSegmentColumnRatio = (segIdx: number, colIdx: number, newVal: number) => {
const seg = node!.segments[segIdx]!
const normRatios = (() => {
@@ -115,6 +132,9 @@ export function DoorPanel() {
if (!node || node.type !== 'door' || selectedIds.length !== 1) return null
const hSum = node.segments.reduce((s, seg) => s + seg.heightRatio, 0)
const normHeights = node.segments.map(seg => seg.heightRatio / hSum)
return (
<div className="pointer-events-auto fixed top-20 right-4 z-50 flex w-82 flex-col overflow-hidden rounded-lg border border-border bg-background/95 shadow-xl backdrop-blur-md max-h-[calc(100dvh-100px)]">
{/* Header */}
@@ -455,19 +475,16 @@ export function DoorPanel() {
</div>
<div className="flex items-center gap-1.5">
<NumberInput
label="Height ratio"
value={Math.round(seg.heightRatio * 100) / 100}
onChange={(v) => {
const updated = node.segments.map((s, idx) =>
idx === i ? { ...s, heightRatio: Math.max(0.05, v) } : s,
)
handleUpdate({ segments: updated })
}}
min={0.05}
precision={2}
step={0.05}
label="Height"
value={Math.round(normHeights[i]! * 100 * 10) / 10}
onChange={(v) => setSegmentHeightRatio(i, v / 100)}
min={5}
max={95}
precision={1}
step={1}
className="flex-1"
/>
<span className="text-muted-foreground text-xs shrink-0">%</span>
</div>
{/* Columns */}
<div className="space-y-1">
@@ -8,6 +8,7 @@ export const DoorRenderer = ({ node }: { node: DoorNode }) => {
useRegistry(node.id, 'door', ref)
const handlers = useNodeEvents(node, 'door')
const isTransient = !!(node.metadata as Record<string, unknown> | null)?.isTransient
return (
<mesh
@@ -17,7 +18,7 @@ export const DoorRenderer = ({ node }: { node: DoorNode }) => {
visible={node.visible}
position={node.position}
rotation={node.rotation}
{...handlers}
{...(isTransient ? {} : handlers)}
>
{/* DoorSystem replaces this geometry each time the node is dirty */}
<boxGeometry args={[0, 0, 0]} />
@@ -12,11 +12,11 @@ export const WallRenderer = ({ node }: { node: WallNode }) => {
const handlers = useNodeEvents(node, 'wall')
return (
<mesh ref={ref} castShadow receiveShadow visible={node.visible} {...handlers}>
<mesh ref={ref} castShadow receiveShadow visible={node.visible}>
{/* WallSystem will replace this geometry in the next frame */}
<boxGeometry args={[0, 0, 0]} />
{/* Collision mesh: full-wall geometry (no cutouts) for WallSystem, no event handlers */}
<mesh name="collision-mesh" visible={false}>
{/* Collision mesh: full-wall geometry (no cutouts) for pointer events */}
<mesh name="collision-mesh" visible={false} {...handlers}>
<boxGeometry args={[0, 0, 0]} />
</mesh>
@@ -8,6 +8,7 @@ export const WindowRenderer = ({ node }: { node: WindowNode }) => {
useRegistry(node.id, 'window', ref)
const handlers = useNodeEvents(node, 'window')
const isTransient = !!(node.metadata as Record<string, unknown> | null)?.isTransient
return (
<mesh
@@ -17,7 +18,7 @@ export const WindowRenderer = ({ node }: { node: WindowNode }) => {
visible={node.visible}
position={node.position}
rotation={node.rotation}
{...handlers}
{...(isTransient ? {} : handlers)}
>
{/* WindowSystem replaces this geometry each time the node is dirty */}
<boxGeometry args={[0, 0, 0]} />