"use client"; import React, { useRef, useState, useEffect } from 'react'; import { Canvas, useFrame } from '@react-three/fiber'; import { OrbitControls, Environment, ContactShadows, RoundedBox, Text } from '@react-three/drei'; import * as THREE from 'three'; const categories = [ // Bins layout: Red(+Z), Green(+X), Grey(-Z), Yellow(-X) { id: 'B3', label: 'E-WASTE\nS. ELEKTRONIK', color: '#dc2626', angle: 0 }, // Merah { id: 'Organik', label: 'BAHAN KOMPOS\nSISA MAKANAN', color: '#16a34a', angle: Math.PI / 2 }, // Hijau { id: 'Residu', label: 'RESIDU\nSULIT DIDAUR', color: '#475569', angle: Math.PI }, // Abu-abu { id: 'Anorganik', label: 'ANORGANIK\nBERNILAI JUAL', color: '#eab308', angle: -Math.PI / 2 },// Kuning (gabungan plastik, kaleng, kertas, dus) ]; function HollowBin({ position, color, rotationY, label, items = [] }: { position: [number, number, number], color: string, rotationY: number, label: string, items?: string[] }) { const t = 0.06; // thickness const w = 2.0; // much wider bins const h = 2.5; // taller bins const d = 2.0; return ( {/* Bottom face at y=0 */} {/* Front */} {/* Label on the front */} {label} {/* Render Items with Boxes */} {items.map((item, i) => ( {i + 1}. {item} ))} {/* Back */} {/* Left */} {/* Right */} ); } function HardwarePlatform({ activeCategory }: { activeCategory: string }) { const platformGroup = useRef(null); const targetCategory = categories.find(c => activeCategory?.trim().toLowerCase() === c.id.toLowerCase()); const isMixed = activeCategory?.trim().toLowerCase() === 'campuran'; const tiltAngle = Math.PI / 3.5; let targetRotX = 0; let targetRotZ = 0; if (targetCategory && !isMixed) { if (targetCategory.id === 'B3') targetRotX = tiltAngle; else if (targetCategory.id === 'Residu') targetRotX = -tiltAngle; else if (targetCategory.id === 'Organik') targetRotZ = -tiltAngle; else if (targetCategory.id === 'Anorganik') targetRotZ = tiltAngle; } useFrame((state, delta) => { if (platformGroup.current) { if (isMixed) { // Shake logic for rejected waste (menggeleng/menolak) platformGroup.current.rotation.y = Math.sin(state.clock.elapsedTime * 15) * 0.15; platformGroup.current.rotation.x += (0 - platformGroup.current.rotation.x) * 0.1; platformGroup.current.rotation.z += (0 - platformGroup.current.rotation.z) * 0.1; } else { // Use lerp for smooth 4-directional tilt platformGroup.current.rotation.y += (0 - platformGroup.current.rotation.y) * 0.1; platformGroup.current.rotation.x += (targetRotX - platformGroup.current.rotation.x) * 0.1; platformGroup.current.rotation.z += (targetRotZ - platformGroup.current.rotation.z) * 0.1; } } }); return ( {/* Stationary Base */} {/* Multi-axis Tilting Platform */} {/* Ball Joint Visual */} {/* Flat White Rounded Square Platform */} {/* Debug Text on the platform */} {activeCategory && ( {isMixed ? 'CAMPURAN!\n(Pisahkan Dulu)' : `${activeCategory} ${targetCategory ? '(Match!)' : '(No Match!)'}`} )} ); } function SmartBinHardware({ activeCategory, history }: { activeCategory: string, history: Record }) { return ( {/* 4 Colored Bins */} {categories.map((cat, idx) => { const radius = 2.6; // Push bins further outward to fit the larger bins const px = Math.sin(cat.angle) * radius; const pz = Math.cos(cat.angle) * radius; const uniqueItems = history[cat.id] || []; return ( ); })} {/* Central Stand Pillar */} {/* 4 Diagonal Tripod Legs */} {[Math.PI/4, 3*Math.PI/4, -Math.PI/4, -3*Math.PI/4].map((rot, i) => ( {/* Legs touch ground at y=0 and lean onto the pillar */} ))} {/* Tilting Sorting Platform */} ); } export default function Simulation3D({ activeCategory, predictions = [] }: { activeCategory: string, predictions?: any[] }) { const [history, setHistory] = useState>({ 'B3': [], 'Organik': [], 'Residu': [], 'Anorganik': [] }); useEffect(() => { if (predictions && predictions.length > 0) { setHistory(prev => { const next = { ...prev }; let changed = false; predictions.forEach(p => { const cat = p.category; const item = p.class_name?.replace(/_/g, ' '); if (cat && item) { const key = Object.keys(next).find(k => k.toLowerCase() === cat.toLowerCase()); if (key) { const capitalized = item.charAt(0).toUpperCase() + item.slice(1); // Tambahkan jika belum ada di dalam daftar yang sudah ada if (!next[key].includes(capitalized)) { next[key] = [...next[key], capitalized]; // Batasi maksimal 5 item terakhir if (next[key].length > 5) { next[key] = next[key].slice(-5); } changed = true; } } } }); return changed ? next : prev; }); } }, [predictions]); return ( ); }