import React, { useMemo } from "react"; import Cylinder from "3dModels/Shapes/3D/Cylinder"; import Cone from "3dModels/Shapes/3D/Cone"; import { Vector3, Euler } from "three"; interface Props { diameter: number; sidewallHeight: number; hopperHeight?: number; fillPercent: number; } export default function GrainFillFlat(props: Props) { const { diameter, sidewallHeight, hopperHeight = 0, fillPercent } = props; const radius = diameter / 2; // Slightly smaller to avoid z-fighting with shell const grainRadius = radius * 0.98; // --- volumes --- const cylinderVolume = Math.PI * radius * radius * sidewallHeight; const hopperVolume = hopperHeight > 0 ? (1 / 3) * Math.PI * radius * radius * hopperHeight : 0; const totalVolume = cylinderVolume + hopperVolume; const filledVolume = totalVolume * fillPercent; // --- result values --- let hopperFillHeight = 0; let cylinderFillHeight = 0; if (hopperHeight > 0 && filledVolume <= hopperVolume) { // ONLY hopper filling const ratio = filledVolume / hopperVolume; hopperFillHeight = hopperHeight * Math.pow(ratio, 1 / 3); cylinderFillHeight = 0; } else { // hopper full (or no hopper) hopperFillHeight = hopperHeight; const remainingVolume = filledVolume - hopperVolume; cylinderFillHeight = Math.max( 0, remainingVolume / (Math.PI * radius * radius) ); } // --- positions --- const hopperPosition = useMemo( () => new Vector3( 0, -(sidewallHeight / 2 + hopperHeight) + hopperFillHeight / 2, //adding the hopper height so it starts at the cone and not the flat side 0 ), [sidewallHeight, hopperFillHeight] ); //flip the cone so it matches the hoppers rotation with the cone pointed down const hopperRotation = useMemo( () => new Euler(Math.PI, 0, 0), [] ); const cylinderPosition = useMemo( () => new Vector3( 0, -sidewallHeight / 2 + cylinderFillHeight / 2, 0 ), [sidewallHeight, cylinderFillHeight] ); // --- material --- const grainColour = "#fff302"; return ( <> {/* Hopper fill */} {hopperHeight > 0 && hopperFillHeight > 0 && ( )} {/* Cylinder fill */} {cylinderFillHeight > 0 && ( )} ); }