import React, { useState, useMemo, useEffect } from 'react'; import { Edges } from '@react-three/drei'; import * as THREE from 'three'; const FeatureSlice = ({ texture, gridSize, index, sizeY, sizeZ, xOffset, geometry }) => { const sliceTexture = useMemo(() => { const cloned = texture.clone(); cloned.magFilter = THREE.NearestFilter; cloned.minFilter = THREE.NearestFilter; cloned.repeat.set(1 / gridSize, 1 / gridSize); const col = index % gridSize; const row = Math.floor(index / gridSize); cloned.offset.set(col / gridSize, 1 - (row + 1) / gridSize); if (cloned.image && cloned.image.width > 0) { cloned.needsUpdate = true; } return cloned; }, [texture, gridSize, index]); useEffect(() => { return () => { sliceTexture.dispose(); }; }, [sliceTexture]); return ( ); }; export const LayerCube = ({ position, size, shape, textureUrl, isSelected }) => { const [sizeX, sizeY, sizeZ] = size; const channels = shape[2] || 1; const numFeatures = Math.min(channels, 24); const gridSize = Math.ceil(Math.sqrt(Math.min(channels, 64))); const [imageEl, setImageEl] = useState(null); useEffect(() => { if (!textureUrl) return; const img = new window.Image(); img.src = textureUrl; img.onload = () => setImageEl(img); }, [textureUrl]); const sliceGeometry = useMemo(() => new THREE.PlaneGeometry(sizeZ, sizeY), [sizeZ, sizeY]); const fallbackTexture = useMemo(() => new THREE.Texture(), []); const baseTexture = useMemo(() => { if (!imageEl) return fallbackTexture; const tex = new THREE.Texture(imageEl); tex.needsUpdate = true; return tex; }, [imageEl, fallbackTexture]); return ( {Array.from({ length: numFeatures }).map((_, index) => { const xOffset = (-sizeX / 2) * 0.95 + (sizeX * 0.95 * index) / Math.max(1, numFeatures - 1); return ( ); })} ); };