"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 (
);
}