also displaying the inventory control type next to "Bin Fill" in the inventory summary and added a fallback to display the inventory in the bin using the basic grain fill when there are no nodes to be able to render the heatmaps, also adjusted the brightness and colour of the basic grain fill

This commit is contained in:
csawatzky 2026-06-16 12:40:07 -06:00
parent 370134a401
commit a4ed52db1e
5 changed files with 61 additions and 36 deletions

View file

@ -6,7 +6,7 @@ import { Bin } from "models";
import BinShell from "../BinParts/BinShell"; import BinShell from "../BinParts/BinShell";
import GrainFillFlat from "../Systems/Inventory/GrainFillFlat"; import GrainFillFlat from "../Systems/Inventory/GrainFillFlat";
import BinCables from "../Systems/Cables/BinCables"; import BinCables from "../Systems/Cables/BinCables";
import { Vector3 } from "three"; // import { Vector3 } from "three";
import { useMemo } from "react"; import { useMemo } from "react";
import { BuildCableData, CableData } from "../Data/BuildCableData"; import { BuildCableData, CableData } from "../Data/BuildCableData";
import { BuildNodeData, NodeData } from "../Data/BuildNodeData"; import { BuildNodeData, NodeData } from "../Data/BuildNodeData";
@ -14,10 +14,10 @@ import { pond } from "protobuf-ts/pond";
import GrainCableFill from "../Systems/Inventory/GrainCableFill"; import GrainCableFill from "../Systems/Inventory/GrainCableFill";
import TempHeatMap from "../Systems/Heatmap/TempHeatMap"; import TempHeatMap from "../Systems/Heatmap/TempHeatMap";
import NodePointCloud from "../Systems/Heatmap/NodePointCloud"; import NodePointCloud from "../Systems/Heatmap/NodePointCloud";
import { Box } from "@mui/material"; // import { Box } from "@mui/material";
import MoistureHeatMap from "../Systems/Heatmap/MoistureHeatmap"; import MoistureHeatMap from "../Systems/Heatmap/MoistureHeatmap";
import { grainYBoundsFromFillPercent } from "../utils/grainFillBounds"; import { grainYBoundsFromFillPercent } from "../utils/grainFillBounds";
import { RadiansToDegrees } from "common/TrigFunctions"; // import { RadiansToDegrees } from "common/TrigFunctions";
// import { GrainCable } from "models/GrainCable" // import { GrainCable } from "models/GrainCable"
// Fallback dimensions used when a bin has no advanced dimensions configured. // Fallback dimensions used when a bin has no advanced dimensions configured.
@ -40,6 +40,10 @@ interface Props {
*/ */
fillPercent?: number fillPercent?: number
nodeClick?: (node: NodeData, cable: CableData) => void nodeClick?: (node: NodeData, cable: CableData) => void
/**
* When true, renders the basic fill and suppresses the heatmaps.
* Heatmaps already simulate the grain level so showing both is redundant.
*/
showGrain?: boolean showGrain?: boolean
showTempHeatmap?: boolean showTempHeatmap?: boolean
showMoistureHeatmap?: boolean showMoistureHeatmap?: boolean
@ -108,25 +112,30 @@ export default function Bin3dView(props: Props) {
const resetCameraFn = React.useRef<(() => void) | null>(null); const resetCameraFn = React.useRef<(() => void) | null>(null);
const grainInventory = () => { const grainInventory = () => {
const grainColour = "#fff300";
//note that adjusting the opacity is how to adjust the brightness, less makes it dimmer
if (isCableInventory) { if (isCableInventory) {
return ( return (
<GrainCableFill <GrainCableFill
diameter={dims.diameter} diameter={dims.diameter}
colour={grainColour}
nodes={nodeData} nodes={nodeData}
sidewallHeight={dims.sidewallHeight} sidewallHeight={dims.sidewallHeight}
fallbackFillPercent={fillPercent} fallbackFillPercent={fillPercent}
hopperHeight={dims.hopperHeight} hopperHeight={dims.hopperHeight}
grainOpacity={0.3} // grainOpacity={0.7}
/> />
) )
} else if (fillPercent) { } else if (fillPercent) {
return ( return (
<GrainFillFlat <GrainFillFlat
diameter={dims.diameter} diameter={dims.diameter}
colour={grainColour}
sidewallHeight={dims.sidewallHeight} sidewallHeight={dims.sidewallHeight}
hopperHeight={dims.hopperHeight} hopperHeight={dims.hopperHeight}
fillPercent={fillPercent} fillPercent={fillPercent}
grainOpacity={0.3} // grainOpacity={0.7}
/> />
) )
} }
@ -155,8 +164,6 @@ export default function Bin3dView(props: Props) {
renderOrder={4} renderOrder={4}
/> />
{showGrain && grainInventory()}
<BinCables <BinCables
displayTemp={showTemp} displayTemp={showTemp}
displayMoisture={showMoisture} displayMoisture={showMoisture}
@ -172,24 +179,25 @@ export default function Bin3dView(props: Props) {
{showHotspots && <NodePointCloud bin={bin} nodes={nodeData} />} {showHotspots && <NodePointCloud bin={bin} nodes={nodeData} />}
{/* note that the heatmaps internally use render order 1,2, and 3 for the three separate coloured meshes */} {/* note that the heatmaps internally use render order 1,2, and 3 for the three separate coloured meshes */}
{showTempHeatmap && ( {showGrain ? grainInventory() :
<TempHeatMap showTempHeatmap && nodeData.length > 0
? <TempHeatMap
binDimensions={dims} binDimensions={dims}
targetTemp={bin.targetTemp()} targetTemp={bin.targetTemp()}
nodes={nodeData} nodes={nodeData}
flatMaxY={flatGrainBounds?.maxY} flatMaxY={flatGrainBounds?.maxY}
flatMinY={flatGrainBounds?.minY} flatMinY={flatGrainBounds?.minY}
/> />
)} : showMoistureHeatmap && nodeData.length > 0
{showMoistureHeatmap && ( ? <MoistureHeatMap
<MoistureHeatMap binDimensions={dims}
binDimensions={dims} targetMoisture={bin.targetMoisture()}
targetMoisture={bin.targetMoisture()} nodes={nodeData}
nodes={nodeData} flatMaxY={flatGrainBounds?.maxY}
flatMaxY={flatGrainBounds?.maxY} flatMinY={flatGrainBounds?.minY}
flatMinY={flatGrainBounds?.minY} />
/> : grainInventory()
)} }
</group> </group>

View file

@ -16,6 +16,7 @@ interface Props {
* If undefined and no top nodes exist, nothing is rendered. * If undefined and no top nodes exist, nothing is rendered.
*/ */
fallbackFillPercent?: number; fallbackFillPercent?: number;
colour?: string
} }
// Tuning knobs // Tuning knobs
@ -59,13 +60,13 @@ export default function GrainCableFill(props: Props) {
hopperHeight = 0, hopperHeight = 0,
nodes, nodes,
fallbackFillPercent, fallbackFillPercent,
grainOpacity grainOpacity,
colour
} = props; } = props;
const binRadius = diameter / 2; const binRadius = diameter / 2;
// Slightly inset to avoid z-fighting with the shell // Slightly inset to avoid z-fighting with the shell
const grainRadius = binRadius * 0.98; const grainRadius = binRadius * 0.98;
const grainColour = "#fff302";
// --- Collect top nodes (non-excluded, inGrain) --- // --- Collect top nodes (non-excluded, inGrain) ---
const topNodes = useMemo(() => const topNodes = useMemo(() =>
@ -256,7 +257,7 @@ export default function GrainCableFill(props: Props) {
}} }}
position={new Vector3(0, -(sidewallHeight / 2 + hopperHeight) + hopperFillHeight / 2, 0)} position={new Vector3(0, -(sidewallHeight / 2 + hopperHeight) + hopperFillHeight / 2, 0)}
rotation={new Euler(Math.PI, 0, 0)} rotation={new Euler(Math.PI, 0, 0)}
colour={grainColour} colour={colour}
roughness={1} roughness={1}
metalness={0} metalness={0}
opacity={grainOpacity} opacity={grainOpacity}
@ -276,7 +277,7 @@ export default function GrainCableFill(props: Props) {
openEnded: false openEnded: false
}} }}
position={new Vector3(0, -sidewallHeight / 2 + cylinderFillHeight / 2, 0)} position={new Vector3(0, -sidewallHeight / 2 + cylinderFillHeight / 2, 0)}
colour={grainColour} colour={colour}
roughness={1} roughness={1}
metalness={0} metalness={0}
opacity={grainOpacity} opacity={grainOpacity}
@ -296,7 +297,7 @@ export default function GrainCableFill(props: Props) {
{surfaceGeometry && ( {surfaceGeometry && (
<mesh renderOrder={0} geometry={surfaceGeometry}> <mesh renderOrder={0} geometry={surfaceGeometry}>
<meshStandardMaterial <meshStandardMaterial
color={grainColour} color={colour}
roughness={1} roughness={1}
metalness={0} metalness={0}
side={THREE.DoubleSide} side={THREE.DoubleSide}
@ -319,7 +320,7 @@ export default function GrainCableFill(props: Props) {
heightSegments: 1, heightSegments: 1,
openEnded: false openEnded: false
}} }}
colour={grainColour} colour={colour}
roughness={1} roughness={1}
opacity={grainOpacity} opacity={grainOpacity}
depthWrite={false} depthWrite={false}
@ -338,7 +339,7 @@ export default function GrainCableFill(props: Props) {
}} }}
position={hopperPosition} position={hopperPosition}
rotation={hopperRotation} rotation={hopperRotation}
colour={grainColour} colour={colour}
roughness={1} roughness={1}
metalness={0} metalness={0}
opacity={grainOpacity} opacity={grainOpacity}

View file

@ -9,6 +9,7 @@ interface Props {
hopperHeight?: number; hopperHeight?: number;
fillPercent: number; fillPercent: number;
grainOpacity?: number; grainOpacity?: number;
colour?: string
} }
export default function GrainFillFlat(props: Props) { export default function GrainFillFlat(props: Props) {
@ -17,7 +18,8 @@ export default function GrainFillFlat(props: Props) {
sidewallHeight, sidewallHeight,
hopperHeight = 0, hopperHeight = 0,
fillPercent, fillPercent,
grainOpacity grainOpacity,
colour
} = props; } = props;
const radius = diameter / 2; const radius = diameter / 2;
@ -82,9 +84,6 @@ export default function GrainFillFlat(props: Props) {
[sidewallHeight, cylinderFillHeight] [sidewallHeight, cylinderFillHeight]
); );
// --- material ---
const grainColour = "#fff302";
return ( return (
<> <>
{/* Hopper fill */} {/* Hopper fill */}
@ -98,7 +97,7 @@ export default function GrainFillFlat(props: Props) {
}} }}
position={hopperPosition} position={hopperPosition}
rotation={hopperRotation} rotation={hopperRotation}
colour={grainColour} colour={colour}
roughness={1} roughness={1}
metalness={0} metalness={0}
opacity={grainOpacity} opacity={grainOpacity}
@ -119,7 +118,7 @@ export default function GrainFillFlat(props: Props) {
openEnded: false openEnded: false
}} }}
position={cylinderPosition} position={cylinderPosition}
colour={grainColour} colour={colour}
roughness={1} roughness={1}
metalness={0} metalness={0}
opacity={grainOpacity} opacity={grainOpacity}

View file

@ -414,8 +414,6 @@ export default function bin3dVisualizer(props: Props){
showMoistureHeatmap={showMoistureHeatmap} showMoistureHeatmap={showMoistureHeatmap}
showTemp={showTemp && showLabels} showTemp={showTemp && showLabels}
showMoisture={showMoisture && showLabels} showMoisture={showMoisture && showLabels}
// showGrain={showFill}
// showHotspots={showHotspots}
xOffset={isMobile ? undefined : -120} xOffset={isMobile ? undefined : -120}
/> />
{isMobile ? mobileHUD() : desktopHUD()} {isMobile ? mobileHUD() : desktopHUD()}

View file

@ -71,6 +71,25 @@ export default function BinSummary(props: Props){
// } // }
// },[devices]) // },[devices])
const inventoryControl = () => {
switch (bin.inventoryControl()) {
case pond.BinInventoryControl.BIN_INVENTORY_CONTROL_MANUAL:
return "Manual"
case pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC:
return "Auto Cable"
case pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR:
return "Auto Lidar"
case pond.BinInventoryControl.BIN_INVENTORY_CONTROL_HYBRID_CABLE:
return "Hybrid Cable"
case pond.BinInventoryControl.BIN_INVENTORY_CONTROL_HYBRID_LIDAR:
return "Hybrid Lidar"
case pond.BinInventoryControl.BIN_INVENTORY_CONTROL_LIBRACART:
return "Libra Cart"
default:
return "Not Set"
}
}
const activity = (reading: string) => { const activity = (reading: string) => {
//if the device hasnt checked in in 6 hours = missing 12 hours = inactive within 6 hours = live //if the device hasnt checked in in 6 hours = missing 12 hours = inactive within 6 hours = live
let status = "Live" let status = "Live"
@ -139,7 +158,7 @@ export default function BinSummary(props: Props){
{/* Fill row */} {/* Fill row */}
<Box sx={{ borderTop: `1px solid ${grey[800]}`, px: 2, pt: 1.5, pb: 1.75 }}> <Box sx={{ borderTop: `1px solid ${grey[800]}`, px: 2, pt: 1.5, pb: 1.75 }}>
<Box display="flex" alignItems="baseline" justifyContent="space-between" sx={{ mb: 1 }}> <Box display="flex" alignItems="baseline" justifyContent="space-between" sx={{ mb: 1 }}>
<Typography variant="caption" sx={{ color: grey[500] }}>Bin fill</Typography> <Typography variant="caption" sx={{ color: grey[500] }}>Bin fill ({inventoryControl()})</Typography>
<Box display="flex" alignItems="baseline" gap={0.75}> <Box display="flex" alignItems="baseline" gap={0.75}>
<Typography sx={{ fontSize: 20, fontWeight: 500 }}> <Typography sx={{ fontSize: 20, fontWeight: 500 }}>
{bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_MANUAL {bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_MANUAL
@ -210,7 +229,7 @@ export default function BinSummary(props: Props){
{/* Bin Fill */} {/* Bin Fill */}
<Box sx={{ flexGrow: 1, maxWidth: 450, mx: 4 }}> <Box sx={{ flexGrow: 1, maxWidth: 450, mx: 4 }}>
<Typography variant="caption" sx={{ color: grey[500] }}> <Typography variant="caption" sx={{ color: grey[500] }}>
Bin Fill Bin Fill ({inventoryControl()})
</Typography> </Typography>
<Box display="flex" alignItems="center" gap={1} sx={{ mt: 0.5 }}> <Box display="flex" alignItems="center" gap={1} sx={{ mt: 0.5 }}>
{/* hidden measuring span, renders off-screen */} {/* hidden measuring span, renders off-screen */}