feat(3d-viewer): upgrade visuals with significant plant scaling and improved controls based on user feedback
Some checks are pending
Deploy to Production / deploy (push) Waiting to run
Test / backend-test (push) Waiting to run
Test / frontend-test (push) Waiting to run

This commit is contained in:
fullsizemalt 2025-12-17 07:49:18 -08:00
parent 9da01f6338
commit 1ebe7b4d34

View file

@ -65,7 +65,7 @@ function PlantInstances({ positions, onPlantClick }: { positions: any[], onPlant
return (
<group>
{/* Active Plants */}
{/* Active Plants - Render as Cylinders (Stalks/Canopy) for visibility */}
{plants.length > 0 && (
<Instances
range={plants.length}
@ -77,12 +77,14 @@ function PlantInstances({ positions, onPlantClick }: { positions: any[], onPlant
}
}}
>
<sphereGeometry args={[0.15, 16, 16]} />
{/* Make plants much taller and visible: Cylinder radius 0.2, height 0.8 */}
<cylinderGeometry args={[0.2, 0.1, 0.8, 8]} />
<meshStandardMaterial />
{plants.map((pos, i) => (
<Instance
key={pos.id || i}
position={[pos.x, pos.y, pos.z]}
// Lift y by 0.4 (half height) so it sits ON the shelf
position={[pos.x, pos.y + 0.4, pos.z]}
color={pos.plant?.stage === 'FLOWER' ? COLORS.FLOWER :
pos.plant?.stage === 'DRYING' ? COLORS.DRY :
pos.plant?.stage === 'CURE' ? COLORS.CURE : COLORS.VEG}
@ -91,11 +93,11 @@ function PlantInstances({ positions, onPlantClick }: { positions: any[], onPlant
</Instances>
)}
{/* Empty Slots */}
{/* Empty Slots - Keep subtle but visible */}
{emptySlots.length > 0 && (
<Instances range={emptySlots.length}>
<sphereGeometry args={[0.05, 8, 8]} />
<meshStandardMaterial color={COLORS.EMPTY_SLOT} opacity={0.5} transparent />
<cylinderGeometry args={[0.05, 0.05, 0.1, 8]} />
<meshStandardMaterial color={COLORS.EMPTY_SLOT} opacity={0.3} transparent />
{emptySlots.map((pos, i) => (
<Instance
key={pos.id || i}
@ -119,22 +121,30 @@ function FacilityScene({ data, onSelectPlant }: { data: Floor3DData, onSelectPla
<Text
position={[room.width / 2, 0.1, room.height / 2]}
rotation={[-Math.PI / 2, 0, 0]}
fontSize={0.5}
fontSize={1.5} // Larger text
color="white"
fillOpacity={0.5}
fillOpacity={0.8}
anchorX="center"
anchorY="middle"
>
{room.name}
</Text>
{/* Floor Plane */}
{/* Floor Plane - Darker for contrast */}
<mesh
rotation={[-Math.PI / 2, 0, 0]}
position={[room.width / 2, 0, room.height / 2]}
>
<planeGeometry args={[room.width, room.height]} />
<meshStandardMaterial color={COLORS.ROOM_FLOOR} />
<meshStandardMaterial color={COLORS.ROOM_FLOOR} metalness={0.2} roughness={0.8} />
</mesh>
{/* Room Border/Walls */}
<lineSegments position={[room.width / 2, 0.1, room.height / 2]} rotation={[-Math.PI / 2, 0, 0]}>
<edgesGeometry args={[new THREE.PlaneGeometry(room.width, room.height)]} />
<lineBasicMaterial color="#4b5563" linewidth={2} />
</lineSegments>
{/* Render sections and positions */}
{room.sections.map(section => {
// Calculate positions for this section
@ -151,10 +161,10 @@ function FacilityScene({ data, onSelectPlant }: { data: Floor3DData, onSelectPla
return (
<group key={section.id}>
{/* Section Label */}
{/* Section Label - Lifted higher */}
<Text
position={[section.posX, 2.5, section.posY]}
fontSize={0.3}
position={[section.posX, 3, section.posY]}
fontSize={0.5}
color="#9ca3af"
>
{section.code}
@ -170,16 +180,26 @@ function FacilityScene({ data, onSelectPlant }: { data: Floor3DData, onSelectPla
return (
<>
<ambientLight intensity={0.7} />
<pointLight position={[10, 10, 10]} intensity={1} />
<directionalLight position={[-10, 20, -10]} intensity={0.5} />
<ambientLight intensity={1.5} />
<pointLight position={[20, 30, 20]} intensity={2} />
<directionalLight position={[-10, 50, -10]} intensity={1} castShadow />
<group position={[-data.floor.width / 2, 0, -data.floor.height / 2]}>
{roomMeshes}
</group>
<OrbitControls makeDefault minPolarAngle={0} maxPolarAngle={Math.PI / 2.2} />
<gridHelper args={[100, 100, 0x444444, 0x222222]} />
{/* Improved Controls: Allow panning, damping for smoothness */}
<OrbitControls
makeDefault
enablePan={true}
enableZoom={true}
enableRotate={true}
minDistance={2}
maxDistance={100}
dampingFactor={0.05}
/>
<gridHelper args={[200, 100, 0x444444, 0x111111]} position={[0, -0.1, 0]} />
<axesHelper args={[5]} />
</>
);
}
@ -227,6 +247,7 @@ export default function Facility3DViewerPage() {
<p className="text-xs text-gray-400">
{floorData ? `${floorData.floor.name}${floorData.stats.occupiedPositions} Plants` : 'Loading...'}
</p>
<p className="text-[10px] text-gray-500">Controls: Left Click=Rotate, Right Click=Pan, Scroll=Zoom</p>
</div>
</div>
@ -287,7 +308,7 @@ export default function Facility3DViewerPage() {
</div>
)}
<Canvas camera={{ position: [10, 15, 10], fov: 50 }}>
<Canvas camera={{ position: [20, 30, 20], fov: 60 }}>
<ErrorBoundary>
<Suspense fallback={<Html center>Loading 3D Scene...</Html>}>
{floorData && (