@react-arch/renderer-3d 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -19,6 +19,9 @@ function build3DScene(doc, opts) {
19
19
  const allPts = [];
20
20
  let minY = Infinity;
21
21
  let maxY = -Infinity;
22
+ const addPlanRect = (cx, cz, width, depth) => {
23
+ allPts.push([cx - width / 2, cz - depth / 2], [cx + width / 2, cz + depth / 2]);
24
+ };
22
25
  ordered.forEach((floor, index) => {
23
26
  if (!(opts.floorIds === "all" ? floor.visible : opts.floorIds.includes(floor.id))) return;
24
27
  const elevation = floor.elevation + (opts.exploded ? index * gap : 0);
@@ -44,6 +47,14 @@ function build3DScene(doc, opts) {
44
47
  const along = (s.along0 + s.along1) / 2;
45
48
  const px = wall.start[0] + dir[0] * along;
46
49
  const py = wall.start[1] + dir[1] * along;
50
+ const size = [
51
+ Math.max(s.along1 - s.along0, .001),
52
+ Math.max(s.z1 - s.z0, .001),
53
+ wall.thickness
54
+ ];
55
+ addPlanRect(px, py, size[0], size[2]);
56
+ minY = Math.min(minY, elevation + s.z0);
57
+ maxY = Math.max(maxY, elevation + s.z1);
47
58
  boxes.push({
48
59
  key: `${wall.id}-${i}`,
49
60
  entityId: wall.id,
@@ -55,16 +66,13 @@ function build3DScene(doc, opts) {
55
66
  py
56
67
  ],
57
68
  rotationY: -theta,
58
- size: [
59
- Math.max(s.along1 - s.along0, .001),
60
- Math.max(s.z1 - s.z0, .001),
61
- wall.thickness
62
- ],
69
+ size,
63
70
  materialId: wall.materialId
64
71
  });
65
72
  });
66
73
  }
67
74
  for (const o of floor.openings) {
75
+ if (o.type === "opening") continue;
68
76
  const wall = wallById.get(o.wallId);
69
77
  if (!wall) continue;
70
78
  const dir = wallDirection(wall);
@@ -72,6 +80,14 @@ function build3DScene(doc, opts) {
72
80
  const px = wall.start[0] + dir[0] * o.offset;
73
81
  const py = wall.start[1] + dir[1] * o.offset;
74
82
  const depth = o.type === "window" ? .05 : wall.thickness * .6;
83
+ const size = [
84
+ Math.max(o.width - .04, .05),
85
+ Math.max(o.height - .04, .05),
86
+ depth
87
+ ];
88
+ addPlanRect(px, py, size[0], size[2]);
89
+ minY = Math.min(minY, elevation + o.sillHeight);
90
+ maxY = Math.max(maxY, elevation + o.sillHeight + o.height);
75
91
  panels.push({
76
92
  key: o.id,
77
93
  entityId: o.id,
@@ -83,15 +99,14 @@ function build3DScene(doc, opts) {
83
99
  py
84
100
  ],
85
101
  rotationY: -theta,
86
- size: [
87
- Math.max(o.width - .04, .05),
88
- Math.max(o.height - .04, .05),
89
- depth
90
- ]
102
+ size
91
103
  });
92
104
  }
93
105
  for (const ob of floor.objects) {
94
106
  const d = furnitureDims(ob.type, ob.scale);
107
+ addPlanRect(ob.position[0], ob.position[1], d.width, d.depth);
108
+ minY = Math.min(minY, elevation + ob.position[2]);
109
+ maxY = Math.max(maxY, elevation + ob.position[2] + d.height);
95
110
  objects.push({
96
111
  key: ob.id,
97
112
  entityId: ob.id,
@@ -151,7 +166,7 @@ function build3DScene(doc, opts) {
151
166
  }
152
167
  //#endregion
153
168
  //#region src/Building3D.tsx
154
- const ACCENT = "#4c8eff";
169
+ const ACCENT = "#2563eb";
155
170
  function colorFor(doc, materialId, fallback) {
156
171
  if (!materialId) return fallback;
157
172
  return doc.materials.find((m) => m.id === materialId)?.baseColor ?? fallback;
@@ -174,8 +189,7 @@ function CameraRig({ scene }) {
174
189
  return /* @__PURE__ */ jsx(OrbitControls, {
175
190
  ref: controls,
176
191
  makeDefault: true,
177
- enableDamping: true,
178
- dampingFactor: .1
192
+ enableDamping: true
179
193
  });
180
194
  }
181
195
  function Building3D(props) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/scene3d.ts","../src/Building3D.tsx"],"sourcesContent":["import type { BuildingDocument } from \"@react-arch/core\";\nimport { allFloors, furnitureDims } from \"@react-arch/core\";\nimport { bounds, wallBoxes, wallDirection, wallLength, type Vec2 } from \"@react-arch/geometry\";\n\n/**\n * Plan→world mapping: plan X → world X, plan Y → world Z, height → world Y.\n * All records below carry positions/rotations/sizes ready to drop onto meshes.\n */\n\nexport interface BoxMesh {\n key: string;\n entityId: string;\n floorId: string;\n kind: \"wall\";\n position: [number, number, number];\n rotationY: number;\n size: [number, number, number];\n materialId?: string;\n}\nexport interface SlabMesh {\n key: string;\n entityId: string;\n floorId: string;\n kind: \"slab\";\n position: [number, number, number];\n size: [number, number, number];\n}\nexport interface PanelMesh {\n key: string;\n entityId: string;\n floorId: string;\n kind: \"door\" | \"window\" | \"opening\";\n position: [number, number, number];\n rotationY: number;\n size: [number, number, number];\n}\n\nexport interface ObjectMesh {\n key: string;\n entityId: string;\n floorId: string;\n kind: \"object\";\n objectType: string;\n position: [number, number, number];\n rotationY: number;\n size: [number, number, number];\n}\n\nexport interface Scene3D {\n boxes: BoxMesh[];\n slabs: SlabMesh[];\n panels: PanelMesh[];\n objects: ObjectMesh[];\n center: [number, number, number];\n radius: number;\n}\n\nexport interface Build3DOptions {\n floorIds: string[] | \"all\";\n exploded?: boolean;\n explodeGap?: number;\n showSlabs?: boolean;\n}\n\nconst SLAB_THICKNESS = 0.14;\n/** Drop the slab top slightly below the floor line to avoid sharing a plane\n * with the wall-tops of the floor below (prevents z-fighting). 2 cm, invisible. */\nconst SLAB_TOP_GAP = 0.02;\n\nexport function build3DScene(doc: BuildingDocument, opts: Build3DOptions): Scene3D {\n const floors = allFloors(doc);\n const ordered = [...floors].sort((a, b) => a.elevation - b.elevation);\n const gap = opts.explodeGap ?? 3;\n\n const boxes: BoxMesh[] = [];\n const slabs: SlabMesh[] = [];\n const panels: PanelMesh[] = [];\n const objects: ObjectMesh[] = [];\n const allPts: Vec2[] = [];\n let minY = Infinity;\n let maxY = -Infinity;\n\n ordered.forEach((floor, index) => {\n const visible = opts.floorIds === \"all\" ? floor.visible : opts.floorIds.includes(floor.id);\n if (!visible) return;\n const elevation = floor.elevation + (opts.exploded ? index * gap : 0);\n minY = Math.min(minY, elevation);\n maxY = Math.max(maxY, elevation + floor.height);\n\n const wallById = new Map(floor.walls.map((w) => [w.id, w]));\n const openingsByWall = new Map<string, typeof floor.openings>();\n for (const o of floor.openings) {\n const arr = openingsByWall.get(o.wallId) ?? [];\n arr.push(o);\n openingsByWall.set(o.wallId, arr);\n }\n\n for (const wall of floor.walls) {\n allPts.push(wall.start, wall.end);\n const dir = wallDirection(wall);\n const theta = Math.atan2(dir[1], dir[0]);\n const wallOpenings = (openingsByWall.get(wall.id) ?? []).map((o) => ({\n offset: o.offset,\n width: o.width,\n height: o.height,\n sillHeight: o.sillHeight,\n }));\n // Extend each end by half-thickness so walls overlap and fill corners.\n const segs = wallBoxes(wall, wallOpenings, wall.height, wall.thickness / 2);\n segs.forEach((s, i) => {\n const along = (s.along0 + s.along1) / 2;\n const px = wall.start[0] + dir[0] * along;\n const py = wall.start[1] + dir[1] * along;\n boxes.push({\n key: `${wall.id}-${i}`,\n entityId: wall.id,\n floorId: floor.id,\n kind: \"wall\",\n position: [px, elevation + (s.z0 + s.z1) / 2, py],\n rotationY: -theta,\n size: [Math.max(s.along1 - s.along0, 0.001), Math.max(s.z1 - s.z0, 0.001), wall.thickness],\n materialId: wall.materialId,\n });\n });\n }\n\n for (const o of floor.openings) {\n const wall = wallById.get(o.wallId);\n if (!wall) continue;\n const dir = wallDirection(wall);\n const theta = Math.atan2(dir[1], dir[0]);\n const px = wall.start[0] + dir[0] * o.offset;\n const py = wall.start[1] + dir[1] * o.offset;\n const depth = o.type === \"window\" ? 0.05 : wall.thickness * 0.6;\n panels.push({\n key: o.id,\n entityId: o.id,\n floorId: floor.id,\n kind: o.type,\n position: [px, elevation + o.sillHeight + o.height / 2, py],\n rotationY: -theta,\n size: [Math.max(o.width - 0.04, 0.05), Math.max(o.height - 0.04, 0.05), depth],\n });\n }\n\n for (const ob of floor.objects) {\n const d = furnitureDims(ob.type, ob.scale);\n objects.push({\n key: ob.id,\n entityId: ob.id,\n floorId: floor.id,\n kind: \"object\",\n objectType: ob.type,\n position: [ob.position[0], elevation + ob.position[2] + d.height / 2, ob.position[1]],\n rotationY: -ob.rotation[2],\n size: [d.width, d.height, d.depth],\n });\n }\n\n if (opts.showSlabs !== false && floor.walls.length > 0) {\n const b = bounds(floor.walls.flatMap((w) => [w.start, w.end]));\n slabs.push({\n key: `slab-${floor.id}`,\n entityId: floor.id,\n floorId: floor.id,\n kind: \"slab\",\n // Sit the slab top SLAB_TOP_GAP below the floor line so it is never\n // coplanar with the tops of the walls of the floor below (which reach\n // exactly this elevation) — the root cause of the z-fighting seam.\n position: [(b.min[0] + b.max[0]) / 2, elevation - SLAB_TOP_GAP - SLAB_THICKNESS / 2, (b.min[1] + b.max[1]) / 2],\n size: [b.width + 0.4, SLAB_THICKNESS, b.height + 0.4],\n });\n }\n });\n\n const b = bounds(allPts);\n const cx = (b.min[0] + b.max[0]) / 2;\n const cz = (b.min[1] + b.max[1]) / 2;\n const cy = Number.isFinite(minY) ? (minY + maxY) / 2 : 1.5;\n const radius = Math.max(b.width, b.height, maxY - minY, 4) * 0.75;\n return { boxes, slabs, panels, objects, center: [cx, cy, cz], radius };\n}\n","import { useEffect, useMemo, useRef } from \"react\";\nimport { Canvas, useThree } from \"@react-three/fiber\";\nimport { OrbitControls } from \"@react-three/drei\";\nimport type { BuildingDocument, EntityRef } from \"@react-arch/core\";\nimport { furnitureColor } from \"@react-arch/core\";\nimport { build3DScene, type Scene3D } from \"./scene3d.js\";\n\nexport interface Building3DProps {\n document: BuildingDocument;\n floorIds: string[] | \"all\";\n selected?: EntityRef | null;\n onSelect?: (ref: EntityRef | null) => void;\n exploded?: boolean;\n explodeGap?: number;\n wireframe?: boolean;\n xray?: boolean;\n showSlabs?: boolean;\n className?: string;\n}\n\nconst ACCENT = \"#4c8eff\";\n\nfunction colorFor(doc: BuildingDocument, materialId: string | undefined, fallback: string): string {\n if (!materialId) return fallback;\n return doc.materials.find((m) => m.id === materialId)?.baseColor ?? fallback;\n}\n\nfunction CameraRig({ scene }: { scene: Scene3D }) {\n const { camera } = useThree();\n const controls = useRef<any>(null);\n useEffect(() => {\n const [cx, cy, cz] = scene.center;\n const r = scene.radius;\n camera.position.set(cx + r * 1.4, cy + r * 1.3, cz + r * 1.6);\n // Keep the near/far range tight for good depth-buffer precision (reduces\n // z-fighting between stacked floors that share a plane).\n camera.near = Math.max(0.05, r * 0.02);\n camera.far = r * 8 + 40;\n camera.updateProjectionMatrix();\n if (controls.current) {\n controls.current.target.set(cx, cy, cz);\n controls.current.update();\n }\n }, [scene, camera]);\n return <OrbitControls ref={controls} makeDefault enableDamping dampingFactor={0.1} />;\n}\n\nexport function Building3D(props: Building3DProps) {\n const { document: doc, floorIds } = props;\n const scene = useMemo(\n () => build3DScene(doc, { floorIds, exploded: props.exploded, explodeGap: props.explodeGap, showSlabs: props.showSlabs }),\n [doc, floorIds, props.exploded, props.explodeGap, props.showSlabs],\n );\n\n const wallColor = colorFor(doc, undefined, \"#e8e6e1\");\n const isSelected = (id: string) => props.selected?.id === id;\n\n return (\n <div className={props.className} style={{ width: \"100%\", height: \"100%\", background: \"#0f1115\" }}>\n <Canvas\n shadows\n dpr={[1, 2]}\n camera={{ position: [12, 10, 14], fov: 45 }}\n gl={{ antialias: true }}\n onPointerMissed={() => props.onSelect?.(null)}\n >\n <color attach=\"background\" args={[\"#0f1115\"]} />\n <hemisphereLight intensity={0.55} groundColor=\"#1a1c22\" />\n <ambientLight intensity={0.35} />\n <directionalLight\n position={[scene.center[0] + 20, scene.center[1] + 35, scene.center[2] + 15]}\n intensity={1.1}\n castShadow\n shadow-mapSize={[2048, 2048]}\n />\n\n {/* Ground reference grid */}\n <gridHelper args={[200, 200, \"#2a2d35\", \"#1c1e24\"]} position={[scene.center[0], -0.01, scene.center[2]]} />\n\n {scene.slabs.map((s) => (\n <mesh key={s.key} position={s.position} receiveShadow\n onClick={(e) => { e.stopPropagation(); props.onSelect?.({ kind: \"floor\", id: s.floorId }); }}>\n <boxGeometry args={s.size} />\n {/* polygonOffset pushes the slab slightly back in depth so it never\n ties with a coplanar wall-top from the floor below. */}\n <meshStandardMaterial\n color={isSelected(s.floorId) ? ACCENT : \"#3a3d44\"}\n roughness={0.95}\n polygonOffset\n polygonOffsetFactor={1}\n polygonOffsetUnits={1}\n />\n </mesh>\n ))}\n\n {scene.boxes.map((b) => {\n const sel = isSelected(b.entityId);\n return (\n <mesh key={b.key} position={b.position} rotation={[0, b.rotationY, 0]} castShadow receiveShadow\n onClick={(e) => { e.stopPropagation(); props.onSelect?.({ kind: \"wall\", id: b.entityId }); }}>\n <boxGeometry args={b.size} />\n <meshStandardMaterial\n color={sel ? ACCENT : colorFor(doc, b.materialId, wallColor)}\n roughness={0.9}\n wireframe={props.wireframe}\n transparent={props.xray}\n opacity={props.xray ? 0.25 : 1}\n />\n </mesh>\n );\n })}\n\n {scene.panels.map((p) => {\n const sel = isSelected(p.entityId);\n const isGlass = p.kind === \"window\";\n return (\n <mesh key={p.key} position={p.position} rotation={[0, p.rotationY, 0]} castShadow\n onClick={(e) => { e.stopPropagation(); props.onSelect?.({ kind: \"opening\", id: p.entityId }); }}>\n <boxGeometry args={p.size} />\n <meshStandardMaterial\n color={sel ? ACCENT : isGlass ? \"#bcd6e6\" : \"#6b4b2f\"}\n roughness={isGlass ? 0.05 : 0.6}\n metalness={isGlass ? 0.1 : 0}\n transparent={isGlass || props.xray}\n opacity={isGlass ? 0.4 : props.xray ? 0.3 : 1}\n wireframe={props.wireframe}\n />\n </mesh>\n );\n })}\n\n {scene.objects.map((o) => {\n const sel = isSelected(o.entityId);\n return (\n <mesh key={o.key} position={o.position} rotation={[0, o.rotationY, 0]} castShadow receiveShadow\n onClick={(e) => { e.stopPropagation(); props.onSelect?.({ kind: \"object\", id: o.entityId }); }}>\n <boxGeometry args={o.size} />\n <meshStandardMaterial\n color={sel ? ACCENT : furnitureColor(o.objectType)}\n roughness={0.7}\n wireframe={props.wireframe}\n transparent={props.xray}\n opacity={props.xray ? 0.35 : 1}\n />\n </mesh>\n );\n })}\n\n <CameraRig scene={scene} />\n </Canvas>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/scene3d.ts","../src/Building3D.tsx"],"sourcesContent":["import type { BuildingDocument } from \"@react-arch/core\";\nimport { allFloors, furnitureDims } from \"@react-arch/core\";\nimport { bounds, wallBoxes, wallDirection, type Vec2 } from \"@react-arch/geometry\";\n\n/**\n * Plan→world mapping: plan X → world X, plan Y → world Z, height → world Y.\n * All records below carry positions/rotations/sizes ready to drop onto meshes.\n */\n\nexport interface BoxMesh {\n key: string;\n entityId: string;\n floorId: string;\n kind: \"wall\";\n position: [number, number, number];\n rotationY: number;\n size: [number, number, number];\n materialId?: string;\n}\nexport interface SlabMesh {\n key: string;\n entityId: string;\n floorId: string;\n kind: \"slab\";\n position: [number, number, number];\n size: [number, number, number];\n}\nexport interface PanelMesh {\n key: string;\n entityId: string;\n floorId: string;\n kind: \"door\" | \"window\" | \"opening\";\n position: [number, number, number];\n rotationY: number;\n size: [number, number, number];\n}\n\nexport interface ObjectMesh {\n key: string;\n entityId: string;\n floorId: string;\n kind: \"object\";\n objectType: string;\n position: [number, number, number];\n rotationY: number;\n size: [number, number, number];\n}\n\nexport interface Scene3D {\n boxes: BoxMesh[];\n slabs: SlabMesh[];\n panels: PanelMesh[];\n objects: ObjectMesh[];\n center: [number, number, number];\n radius: number;\n}\n\nexport interface Build3DOptions {\n floorIds: string[] | \"all\";\n exploded?: boolean;\n explodeGap?: number;\n showSlabs?: boolean;\n}\n\nconst SLAB_THICKNESS = 0.14;\n/** Drop the slab top slightly below the floor line to avoid sharing a plane\n * with the wall-tops of the floor below (prevents z-fighting). 2 cm, invisible. */\nconst SLAB_TOP_GAP = 0.02;\n\nexport function build3DScene(doc: BuildingDocument, opts: Build3DOptions): Scene3D {\n const floors = allFloors(doc);\n const ordered = [...floors].sort((a, b) => a.elevation - b.elevation);\n const gap = opts.explodeGap ?? 3;\n\n const boxes: BoxMesh[] = [];\n const slabs: SlabMesh[] = [];\n const panels: PanelMesh[] = [];\n const objects: ObjectMesh[] = [];\n const allPts: Vec2[] = [];\n let minY = Infinity;\n let maxY = -Infinity;\n\n const addPlanRect = (cx: number, cz: number, width: number, depth: number) => {\n allPts.push([cx - width / 2, cz - depth / 2], [cx + width / 2, cz + depth / 2]);\n };\n\n ordered.forEach((floor, index) => {\n const visible = opts.floorIds === \"all\" ? floor.visible : opts.floorIds.includes(floor.id);\n if (!visible) return;\n const elevation = floor.elevation + (opts.exploded ? index * gap : 0);\n minY = Math.min(minY, elevation);\n maxY = Math.max(maxY, elevation + floor.height);\n\n const wallById = new Map(floor.walls.map((w) => [w.id, w]));\n const openingsByWall = new Map<string, typeof floor.openings>();\n for (const o of floor.openings) {\n const arr = openingsByWall.get(o.wallId) ?? [];\n arr.push(o);\n openingsByWall.set(o.wallId, arr);\n }\n\n for (const wall of floor.walls) {\n allPts.push(wall.start, wall.end);\n const dir = wallDirection(wall);\n const theta = Math.atan2(dir[1], dir[0]);\n const wallOpenings = (openingsByWall.get(wall.id) ?? []).map((o) => ({\n offset: o.offset,\n width: o.width,\n height: o.height,\n sillHeight: o.sillHeight,\n }));\n // Extend each end by half-thickness so walls overlap and fill corners.\n const segs = wallBoxes(wall, wallOpenings, wall.height, wall.thickness / 2);\n segs.forEach((s, i) => {\n const along = (s.along0 + s.along1) / 2;\n const px = wall.start[0] + dir[0] * along;\n const py = wall.start[1] + dir[1] * along;\n const size: [number, number, number] = [Math.max(s.along1 - s.along0, 0.001), Math.max(s.z1 - s.z0, 0.001), wall.thickness];\n addPlanRect(px, py, size[0], size[2]);\n minY = Math.min(minY, elevation + s.z0);\n maxY = Math.max(maxY, elevation + s.z1);\n boxes.push({\n key: `${wall.id}-${i}`,\n entityId: wall.id,\n floorId: floor.id,\n kind: \"wall\",\n position: [px, elevation + (s.z0 + s.z1) / 2, py],\n rotationY: -theta,\n size,\n materialId: wall.materialId,\n });\n });\n }\n\n for (const o of floor.openings) {\n if (o.type === \"opening\") continue;\n const wall = wallById.get(o.wallId);\n if (!wall) continue;\n const dir = wallDirection(wall);\n const theta = Math.atan2(dir[1], dir[0]);\n const px = wall.start[0] + dir[0] * o.offset;\n const py = wall.start[1] + dir[1] * o.offset;\n const depth = o.type === \"window\" ? 0.05 : wall.thickness * 0.6;\n const size: [number, number, number] = [Math.max(o.width - 0.04, 0.05), Math.max(o.height - 0.04, 0.05), depth];\n addPlanRect(px, py, size[0], size[2]);\n minY = Math.min(minY, elevation + o.sillHeight);\n maxY = Math.max(maxY, elevation + o.sillHeight + o.height);\n panels.push({\n key: o.id,\n entityId: o.id,\n floorId: floor.id,\n kind: o.type,\n position: [px, elevation + o.sillHeight + o.height / 2, py],\n rotationY: -theta,\n size,\n });\n }\n\n for (const ob of floor.objects) {\n const d = furnitureDims(ob.type, ob.scale);\n addPlanRect(ob.position[0], ob.position[1], d.width, d.depth);\n minY = Math.min(minY, elevation + ob.position[2]);\n maxY = Math.max(maxY, elevation + ob.position[2] + d.height);\n objects.push({\n key: ob.id,\n entityId: ob.id,\n floorId: floor.id,\n kind: \"object\",\n objectType: ob.type,\n position: [ob.position[0], elevation + ob.position[2] + d.height / 2, ob.position[1]],\n rotationY: -ob.rotation[2],\n size: [d.width, d.height, d.depth],\n });\n }\n\n if (opts.showSlabs !== false && floor.walls.length > 0) {\n const b = bounds(floor.walls.flatMap((w) => [w.start, w.end]));\n slabs.push({\n key: `slab-${floor.id}`,\n entityId: floor.id,\n floorId: floor.id,\n kind: \"slab\",\n // Sit the slab top SLAB_TOP_GAP below the floor line so it is never\n // coplanar with the tops of the walls of the floor below (which reach\n // exactly this elevation) — the root cause of the z-fighting seam.\n position: [(b.min[0] + b.max[0]) / 2, elevation - SLAB_TOP_GAP - SLAB_THICKNESS / 2, (b.min[1] + b.max[1]) / 2],\n size: [b.width + 0.4, SLAB_THICKNESS, b.height + 0.4],\n });\n }\n });\n\n const b = bounds(allPts);\n const cx = (b.min[0] + b.max[0]) / 2;\n const cz = (b.min[1] + b.max[1]) / 2;\n const cy = Number.isFinite(minY) ? (minY + maxY) / 2 : 1.5;\n const radius = Math.max(b.width, b.height, maxY - minY, 4) * 0.75;\n return { boxes, slabs, panels, objects, center: [cx, cy, cz], radius };\n}\n","import { useEffect, useMemo, useRef } from \"react\";\nimport { Canvas, useThree } from \"@react-three/fiber\";\nimport { OrbitControls } from \"@react-three/drei\";\nimport type { BuildingDocument, EntityRef } from \"@react-arch/core\";\nimport { furnitureColor } from \"@react-arch/core\";\nimport { build3DScene, type Scene3D } from \"./scene3d.js\";\n\nexport interface Building3DProps {\n document: BuildingDocument;\n floorIds: string[] | \"all\";\n selected?: EntityRef | null;\n onSelect?: (ref: EntityRef | null) => void;\n exploded?: boolean;\n explodeGap?: number;\n wireframe?: boolean;\n xray?: boolean;\n showSlabs?: boolean;\n className?: string;\n}\n\nconst ACCENT = \"#2563eb\";\n\nfunction colorFor(doc: BuildingDocument, materialId: string | undefined, fallback: string): string {\n if (!materialId) return fallback;\n return doc.materials.find((m) => m.id === materialId)?.baseColor ?? fallback;\n}\n\nfunction CameraRig({ scene }: { scene: Scene3D }) {\n const { camera } = useThree();\n const controls = useRef<any>(null);\n useEffect(() => {\n const [cx, cy, cz] = scene.center;\n const r = scene.radius;\n camera.position.set(cx + r * 1.4, cy + r * 1.3, cz + r * 1.6);\n // Keep the near/far range tight for good depth-buffer precision (reduces\n // z-fighting between stacked floors that share a plane).\n camera.near = Math.max(0.05, r * 0.02);\n camera.far = r * 8 + 40;\n camera.updateProjectionMatrix();\n if (controls.current) {\n controls.current.target.set(cx, cy, cz);\n controls.current.update();\n }\n }, [scene, camera]);\n return <OrbitControls ref={controls} makeDefault enableDamping />;\n}\n\nexport function Building3D(props: Building3DProps) {\n const { document: doc, floorIds } = props;\n const scene = useMemo(\n () => build3DScene(doc, { floorIds, exploded: props.exploded, explodeGap: props.explodeGap, showSlabs: props.showSlabs }),\n [doc, floorIds, props.exploded, props.explodeGap, props.showSlabs],\n );\n\n const wallColor = colorFor(doc, undefined, \"#e8e6e1\");\n const isSelected = (id: string) => props.selected?.id === id;\n\n return (\n <div className={props.className} style={{ width: \"100%\", height: \"100%\", background: \"#0f1115\" }}>\n <Canvas\n shadows\n dpr={[1, 2]}\n camera={{ position: [12, 10, 14], fov: 45 }}\n gl={{ antialias: true }}\n onPointerMissed={() => props.onSelect?.(null)}\n >\n <color attach=\"background\" args={[\"#0f1115\"]} />\n <hemisphereLight intensity={0.55} groundColor=\"#1a1c22\" />\n <ambientLight intensity={0.35} />\n <directionalLight\n position={[scene.center[0] + 20, scene.center[1] + 35, scene.center[2] + 15]}\n intensity={1.1}\n castShadow\n shadow-mapSize={[2048, 2048]}\n />\n\n {/* Ground reference grid */}\n <gridHelper args={[200, 200, \"#2a2d35\", \"#1c1e24\"]} position={[scene.center[0], -0.01, scene.center[2]]} />\n\n {scene.slabs.map((s) => (\n <mesh key={s.key} position={s.position} receiveShadow\n onClick={(e) => { e.stopPropagation(); props.onSelect?.({ kind: \"floor\", id: s.floorId }); }}>\n <boxGeometry args={s.size} />\n {/* polygonOffset pushes the slab slightly back in depth so it never\n ties with a coplanar wall-top from the floor below. */}\n <meshStandardMaterial\n color={isSelected(s.floorId) ? ACCENT : \"#3a3d44\"}\n roughness={0.95}\n polygonOffset\n polygonOffsetFactor={1}\n polygonOffsetUnits={1}\n />\n </mesh>\n ))}\n\n {scene.boxes.map((b) => {\n const sel = isSelected(b.entityId);\n return (\n <mesh key={b.key} position={b.position} rotation={[0, b.rotationY, 0]} castShadow receiveShadow\n onClick={(e) => { e.stopPropagation(); props.onSelect?.({ kind: \"wall\", id: b.entityId }); }}>\n <boxGeometry args={b.size} />\n <meshStandardMaterial\n color={sel ? ACCENT : colorFor(doc, b.materialId, wallColor)}\n roughness={0.9}\n wireframe={props.wireframe}\n transparent={props.xray}\n opacity={props.xray ? 0.25 : 1}\n />\n </mesh>\n );\n })}\n\n {scene.panels.map((p) => {\n const sel = isSelected(p.entityId);\n const isGlass = p.kind === \"window\";\n return (\n <mesh key={p.key} position={p.position} rotation={[0, p.rotationY, 0]} castShadow\n onClick={(e) => { e.stopPropagation(); props.onSelect?.({ kind: \"opening\", id: p.entityId }); }}>\n <boxGeometry args={p.size} />\n <meshStandardMaterial\n color={sel ? ACCENT : isGlass ? \"#bcd6e6\" : \"#6b4b2f\"}\n roughness={isGlass ? 0.05 : 0.6}\n metalness={isGlass ? 0.1 : 0}\n transparent={isGlass || props.xray}\n opacity={isGlass ? 0.4 : props.xray ? 0.3 : 1}\n wireframe={props.wireframe}\n />\n </mesh>\n );\n })}\n\n {scene.objects.map((o) => {\n const sel = isSelected(o.entityId);\n return (\n <mesh key={o.key} position={o.position} rotation={[0, o.rotationY, 0]} castShadow receiveShadow\n onClick={(e) => { e.stopPropagation(); props.onSelect?.({ kind: \"object\", id: o.entityId }); }}>\n <boxGeometry args={o.size} />\n <meshStandardMaterial\n color={sel ? ACCENT : furnitureColor(o.objectType)}\n roughness={0.7}\n wireframe={props.wireframe}\n transparent={props.xray}\n opacity={props.xray ? 0.35 : 1}\n />\n </mesh>\n );\n })}\n\n <CameraRig scene={scene} />\n </Canvas>\n </div>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-arch/renderer-3d",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -12,9 +12,9 @@
12
12
  }
13
13
  },
14
14
  "dependencies": {
15
- "@react-arch/core": "0.1.2",
16
- "@react-arch/shared": "0.1.2",
17
- "@react-arch/geometry": "0.1.2"
15
+ "@react-arch/core": "0.1.3",
16
+ "@react-arch/geometry": "0.1.3",
17
+ "@react-arch/shared": "0.1.3"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@react-three/drei": ">=9",
@@ -29,7 +29,8 @@
29
29
  "@types/three": "^0.169.0",
30
30
  "react": "^18.3.1",
31
31
  "three": "^0.169.0",
32
- "typescript": "^5.7.2"
32
+ "typescript": "^5.7.2",
33
+ "vitest": "^2.1.8"
33
34
  },
34
35
  "files": [
35
36
  "dist"