set up some defaults for bins that are not using advanced dimensions so that they can still have a visual of some sort
This commit is contained in:
parent
1d19a7e993
commit
160d2a9d31
5 changed files with 102 additions and 56 deletions
|
|
@ -17,7 +17,14 @@ import NodePointCloud from "../Systems/Heatmap/NodePointCloud";
|
|||
import { Box } from "@mui/material";
|
||||
import MoistureHeatMap from "../Systems/Heatmap/MoistureHeatmap";
|
||||
import { grainYBoundsFromFillPercent } from "../utils/grainFillBounds";
|
||||
// import { GrainCable } from "models/GrainCable";
|
||||
import { RadiansToDegrees } from "common/TrigFunctions";
|
||||
// import { GrainCable } from "models/GrainCable"
|
||||
|
||||
// Fallback dimensions used when a bin has no advanced dimensions configured.
|
||||
// Chosen to resemble a typical mid-size grain bin.
|
||||
const DEFAULT_ROOF_HEIGHT_CM = 200; // 2 m
|
||||
const DEFAULT_FLAT_HEIGHT_CM = 0; // flat bottom
|
||||
const DEFAULT_HOPPER_HEIGHT_CM = 280; // hopper bottom
|
||||
|
||||
interface Props {
|
||||
bin: Bin
|
||||
|
|
@ -46,18 +53,28 @@ interface Props {
|
|||
export default function Bin3dView(props: Props) {
|
||||
const { bin, scale = 100, fillPercent, nodeClick, showTempHeatmap, showGrain, showHotspots, showResetButton, showMoistureHeatmap, showTemp, showMoisture, xOffset } = props
|
||||
|
||||
// Resolve effective dimensions, falling back to defaults when advanced
|
||||
// dimensions have not been configured (bin methods return 0 in that case).
|
||||
const dims = useMemo(() => ({
|
||||
diameter: bin.diameter(),
|
||||
sidewallHeight: bin.sidewallHeight() || bin.diameter() + 50, //calculated using the diameter so 'default' bins always have the same shape
|
||||
roofHeight: bin.roofHeight() || DEFAULT_ROOF_HEIGHT_CM,
|
||||
hopperHeight: bin.hopperHeight() || (bin.binShape() === pond.BinShape.BIN_SHAPE_HOPPER_BOTTOM ? DEFAULT_HOPPER_HEIGHT_CM : DEFAULT_FLAT_HEIGHT_CM),
|
||||
}), [bin]);
|
||||
|
||||
// Compute the initial camera radius so the full bin fits in frame.
|
||||
// Uses the perspective camera FOV (default 75°) with a padding factor.
|
||||
// Both the diameter and total height are considered so neither is clipped.
|
||||
const initialCameraRadius = useMemo(() => {
|
||||
const totalHeight = (bin.sidewallHeight() + bin.roofHeight() + (bin.hopperHeight() ?? 0)) / scale;
|
||||
const diameter = bin.diameter() / scale;
|
||||
const totalHeight = (dims.sidewallHeight + dims.roofHeight + dims.hopperHeight) / scale;
|
||||
const diameter = dims.diameter / scale;
|
||||
const largestDim = Math.max(totalHeight, diameter);
|
||||
const fovRad = (75 * Math.PI) / 180;
|
||||
const padding = 1.3; // 30% breathing room
|
||||
return (largestDim / (2 * Math.tan(fovRad / 2))) * padding;
|
||||
}, [bin, scale]);
|
||||
const cableData = useMemo(() => BuildCableData(bin), [bin]);
|
||||
}, [dims, scale]);
|
||||
|
||||
const cableData = useMemo(() => BuildCableData(bin, dims), [bin, dims]);
|
||||
const nodeData = useMemo(() => BuildNodeData(cableData), [cableData]);
|
||||
|
||||
const isCableInventory =
|
||||
|
|
@ -71,12 +88,12 @@ export default function Bin3dView(props: Props) {
|
|||
const flatGrainBounds = useMemo(() => {
|
||||
if (isCableInventory || fillPercent === undefined) return undefined;
|
||||
return grainYBoundsFromFillPercent(
|
||||
bin.diameter(),
|
||||
bin.sidewallHeight(),
|
||||
bin.hopperHeight() ?? 0,
|
||||
dims.diameter,
|
||||
dims.sidewallHeight,
|
||||
dims.hopperHeight,
|
||||
fillPercent
|
||||
);
|
||||
}, [isCableInventory, fillPercent, bin]);
|
||||
}, [isCableInventory, fillPercent, dims]);
|
||||
|
||||
// Internal ref — wired to OrbitCameraControls and called by CameraOverlay
|
||||
const resetCameraFn = React.useRef<(() => void) | null>(null);
|
||||
|
|
@ -85,20 +102,20 @@ export default function Bin3dView(props: Props) {
|
|||
if (isCableInventory) {
|
||||
return (
|
||||
<GrainCableFill
|
||||
diameter={bin.diameter()}
|
||||
diameter={dims.diameter}
|
||||
nodes={nodeData}
|
||||
sidewallHeight={bin.sidewallHeight()}
|
||||
sidewallHeight={dims.sidewallHeight}
|
||||
fallbackFillPercent={fillPercent}
|
||||
hopperHeight={bin.hopperHeight()}
|
||||
hopperHeight={dims.hopperHeight}
|
||||
grainOpacity={0.3}
|
||||
/>
|
||||
)
|
||||
} else if (fillPercent) {
|
||||
return (
|
||||
<GrainFillFlat
|
||||
diameter={bin.diameter()}
|
||||
sidewallHeight={bin.sidewallHeight()}
|
||||
hopperHeight={bin.hopperHeight()}
|
||||
diameter={dims.diameter}
|
||||
sidewallHeight={dims.sidewallHeight}
|
||||
hopperHeight={dims.hopperHeight}
|
||||
fillPercent={fillPercent}
|
||||
grainOpacity={0.3}
|
||||
/>
|
||||
|
|
@ -122,10 +139,10 @@ export default function Bin3dView(props: Props) {
|
|||
binMetalness={0.5}
|
||||
binRoughness={0.7}
|
||||
binOpacity={0.2}
|
||||
diameter={bin.diameter()}
|
||||
sidewallHeight={bin.sidewallHeight()}
|
||||
roofHeight={bin.roofHeight()}
|
||||
hopperHeight={bin.hopperHeight()}
|
||||
diameter={dims.diameter}
|
||||
sidewallHeight={dims.sidewallHeight}
|
||||
roofHeight={dims.roofHeight}
|
||||
hopperHeight={dims.hopperHeight}
|
||||
renderOrder={4}
|
||||
/>
|
||||
|
||||
|
|
@ -145,24 +162,26 @@ export default function Bin3dView(props: Props) {
|
|||
|
||||
{showHotspots && <NodePointCloud bin={bin} nodes={nodeData} />}
|
||||
|
||||
{/* note that the heatmaps insternally use render order 1,2, and 3 for the three seperate coloured meshes */}
|
||||
{/* note that the heatmaps internally use render order 1,2, and 3 for the three separate coloured meshes */}
|
||||
{showTempHeatmap && (
|
||||
<TempHeatMap
|
||||
bin={bin}
|
||||
binDimensions={dims}
|
||||
targetTemp={bin.targetTemp()}
|
||||
nodes={nodeData}
|
||||
flatMaxY={flatGrainBounds?.maxY}
|
||||
flatMinY={flatGrainBounds?.minY}
|
||||
/>
|
||||
)}
|
||||
{showMoistureHeatmap && (
|
||||
<MoistureHeatMap
|
||||
bin={bin}
|
||||
<MoistureHeatMap
|
||||
binDimensions={dims}
|
||||
targetMoisture={bin.targetMoisture()}
|
||||
nodes={nodeData}
|
||||
flatMaxY={flatGrainBounds?.maxY}
|
||||
flatMinY={flatGrainBounds?.minY}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
</group>
|
||||
|
||||
<ambientLight intensity={0.2} color={"white"} />
|
||||
|
|
@ -171,6 +190,10 @@ export default function Bin3dView(props: Props) {
|
|||
<directionalLight intensity={0.2} color={"white"} position={[5, 0, 0]} />
|
||||
<directionalLight intensity={0.2} color={"white"} position={[-5, 0, 0]} />
|
||||
|
||||
{/* Overlay — must be inside <Canvas> so it shares the same stacking context */}
|
||||
{showResetButton && (
|
||||
<CameraOverlay onReset={() => resetCameraFn.current?.()} />
|
||||
)}
|
||||
</Canvas>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue