diff --git a/src/3dModels/CameraControls/OrbitCameraControls.tsx b/src/3dModels/CameraControls/OrbitCameraControls.tsx
index 5d707c3..0385cdd 100644
--- a/src/3dModels/CameraControls/OrbitCameraControls.tsx
+++ b/src/3dModels/CameraControls/OrbitCameraControls.tsx
@@ -26,9 +26,9 @@ interface Props {
export default function OrbitCameraControls(props: Props) {
const { target, clampVerticalRotation, minPhi = 0.01, maxPhi = Math.PI - 0.01 } = props;
const { camera, gl } = useThree();
- const panPlane = useRef(new Plane());
- const panStartPoint = useRef(new Vector3());
- const raycaster = useRef(new Raycaster());
+ // const panPlane = useRef(new Plane());
+ // const panStartPoint = useRef(new Vector3());
+ // const raycaster = useRef(new Raycaster());
const mouse = useRef(new Vector2());
const targetRef = useRef({
x: target?.x ?? 0,
@@ -39,6 +39,7 @@ export default function OrbitCameraControls(props: Props) {
const isDragging = useRef(false);
const isPanning = useRef(false);
const lastPos = useRef({ x: 0, y: 0 });
+ const panCameraPosition = useRef(new Vector3());
// Spherical coords
const spherical = useRef({
@@ -57,7 +58,11 @@ export default function OrbitCameraControls(props: Props) {
const y = radius * Math.cos(phi);
const z = radius * Math.sin(phi) * Math.cos(theta);
- camera.position.set(x, y, z);
+ camera.position.set(
+ targetRef.current.x + x,
+ targetRef.current.y + y,
+ targetRef.current.z + z
+ );
camera.lookAt(
targetRef.current.x,
targetRef.current.y,
@@ -71,6 +76,7 @@ export default function OrbitCameraControls(props: Props) {
if (e.target !== canvas) return;
lastPos.current = { x: e.clientX, y: e.clientY };
+ panCameraPosition.current.copy(camera.position);
// Left click = rotate
if (e.button === 0) {
@@ -87,28 +93,29 @@ export default function OrbitCameraControls(props: Props) {
const camDir = new Vector3();
camera.getWorldDirection(camDir);
- panPlane.current.setFromNormalAndCoplanarPoint(
- camDir,
- new Vector3(
- targetRef.current.x,
- targetRef.current.y,
- targetRef.current.z
- )
- );
+ // panPlane.current.setFromNormalAndCoplanarPoint(
+ // camDir,
+ // new Vector3(
+ // targetRef.current.x,
+ // targetRef.current.y,
+ // targetRef.current.z
+ // )
+ // );
+
+ // const rect = canvas.getBoundingClientRect();
+
+ // raycaster.current.setFromCamera(
+ // new Vector2(
+ // ((e.clientX - rect.left) / rect.width) * 2 - 1,
+ // -((e.clientY - rect.top) / rect.height) * 2 + 1
+ // ),
+ // camera
+ // );
- // starting intersection point
- raycaster.current.setFromCamera(
- new Vector2(
- (e.clientX / window.innerWidth) * 2 - 1,
- -(e.clientY / window.innerHeight) * 2 + 1
- ),
- camera
- );
-
- raycaster.current.ray.intersectPlane(
- panPlane.current,
- panStartPoint.current
- );
+ // raycaster.current.ray.intersectPlane(
+ // panPlane.current,
+ // panStartPoint.current
+ // );
}
};
@@ -132,28 +139,61 @@ export default function OrbitCameraControls(props: Props) {
}
// PAN
+ // if (isPanning.current) {
+ // const rect = canvas.getBoundingClientRect();
+
+ // mouse.current.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
+ // mouse.current.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
+
+ // const currentPoint = new Vector3();
+
+ // const tempCamera = camera.clone();
+ // tempCamera.position.copy(panCameraPosition.current);
+ // tempCamera.updateMatrixWorld();
+
+ // raycaster.current.setFromCamera(mouse.current, tempCamera);
+
+ // raycaster.current.ray.intersectPlane(panPlane.current, currentPoint);
+
+ // const delta = new Vector3().subVectors(
+ // panStartPoint.current,
+ // currentPoint
+ // );
+
+ // targetRef.current.x += delta.x;
+ // targetRef.current.y += delta.y;
+ // targetRef.current.z += delta.z;
+
+ // panStartPoint.current.copy(currentPoint);
+ // }
if (isPanning.current) {
- const raycaster = new Raycaster();
+ const deltaX = e.clientX - lastPos.current.x;
+ const deltaY = e.clientY - lastPos.current.y;
- mouse.current.x = (e.clientX / window.innerWidth) * 2 - 1;
- mouse.current.y = -(e.clientY / window.innerHeight) * 2 + 1;
+ // distance-based scaling (important for consistent feel)
+ const distance = spherical.current.radius;
- raycaster.setFromCamera(mouse.current, camera);
+ const panSpeed = 0.002 * distance;
- const currentPoint = new Vector3();
+ const offset = new Vector3();
- raycaster.ray.intersectPlane(panPlane.current, currentPoint);
+ // get camera basis vectors
+ const right = new Vector3();
+ const up = new Vector3();
- const delta = new Vector3().subVectors(
- panStartPoint.current,
- currentPoint
- );
+ camera.getWorldDirection(offset); // forward
+ right.crossVectors(offset, camera.up).normalize(); // right
+ up.copy(camera.up).normalize();
- targetRef.current.x += delta.x;
- targetRef.current.y += delta.y;
- targetRef.current.z += delta.z;
+ // apply movement
+ right.multiplyScalar(-deltaX * panSpeed);
+ up.multiplyScalar(deltaY * panSpeed);
- panStartPoint.current.copy(currentPoint);
+ const pan = new Vector3().addVectors(right, up);
+
+ targetRef.current.x += pan.x;
+ targetRef.current.y += pan.y;
+ targetRef.current.z += pan.z;
}
lastPos.current = { x: e.clientX, y: e.clientY };
diff --git a/src/bin/3dView/BinCables/BinCables.tsx b/src/bin/3dView/BinCables/BinCables.tsx
deleted file mode 100644
index e7b5684..0000000
--- a/src/bin/3dView/BinCables/BinCables.tsx
+++ /dev/null
@@ -1,201 +0,0 @@
-import GrainCable, { CableData } from "./GrainCable";
-import { avg } from "utils";
-import { Vector3 } from "three";
-import { useMemo } from "react";
-import { Bin } from "models";
-import React from "react";
-
-interface CableOrbit {
- orbit: number;
- radius: number; // in cm
- expectedCount: number;
- assignedCount?: number;
-}
-
-interface Props {
- bin: Bin
- binCenter: Vector3
-}
-
-export default function BinCables(props: Props){
- const {bin, binCenter} = props
- const getLayoutFromDiameter = (diameterCm: number): CableOrbit[] => {
- const diameterFt = diameterCm / 30.48;
-
- let summaries: { Orbit: number; DistanceFromCenter: number; NumberOfCables: number }[] = [];
-
- if (diameterFt < 24) {
- summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
- } else if (diameterFt < 36) {
- summaries.push({ Orbit: 1, DistanceFromCenter: 7, NumberOfCables: 3 });
- } else if (diameterFt < 42) {
- summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
- summaries.push({ Orbit: 1, DistanceFromCenter: 9, NumberOfCables: 3 });
- } else if (diameterFt < 48) {
- summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
- summaries.push({ Orbit: 1, DistanceFromCenter: 14, NumberOfCables: 4 });
- } else if (diameterFt < 54) {
- summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
- summaries.push({ Orbit: 1, DistanceFromCenter: 16, NumberOfCables: 5 });
- } else if (diameterFt < 60) {
- summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
- summaries.push({ Orbit: 1, DistanceFromCenter: 18, NumberOfCables: 6 });
- } else if (diameterFt < 72) {
- summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
- summaries.push({ Orbit: 1, DistanceFromCenter: 20, NumberOfCables: 7 });
- } else if (diameterFt < 78) {
- summaries.push({ Orbit: 1, DistanceFromCenter: 10, NumberOfCables: 4 });
- summaries.push({ Orbit: 2, DistanceFromCenter: 27, NumberOfCables: 9 });
- } else if (diameterFt < 90) {
- summaries.push({ Orbit: 1, DistanceFromCenter: 10, NumberOfCables: 4 });
- summaries.push({ Orbit: 2, DistanceFromCenter: 29, NumberOfCables: 10 });
- } else if (diameterFt < 105) {
- summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
- summaries.push({ Orbit: 1, DistanceFromCenter: 18, NumberOfCables: 6 });
- summaries.push({ Orbit: 2, DistanceFromCenter: 36, NumberOfCables: 12 });
- } else {
- summaries.push({ Orbit: 1, DistanceFromCenter: 9, NumberOfCables: 3 });
- summaries.push({ Orbit: 2, DistanceFromCenter: 26.5, NumberOfCables: 9 });
- summaries.push({ Orbit: 3, DistanceFromCenter: 44, NumberOfCables: 14 });
- }
-
- return summaries.map(s => ({
- orbit: s.Orbit,
- radius: s.DistanceFromCenter * 30.48, // ft → cm
- expectedCount: s.NumberOfCables
- }));
- };
-
- const distributeCables = (cableCount: number, layout: CableOrbit[]) => {
- const totalExpected = layout.reduce((sum, o) => sum + o.expectedCount, 0);
-
- let assigned = layout.map(o => ({
- ...o,
- assignedCount: Math.round((o.expectedCount / totalExpected) * cableCount)
- }));
-
- // normalize rounding
- let currentTotal = assigned.reduce((sum, o) => sum + (o.assignedCount ?? 0), 0);
-
- while (currentTotal < cableCount) {
- assigned[assigned.length - 1].assignedCount!++;
- currentTotal++;
- }
-
- while (currentTotal > cableCount) {
- assigned[assigned.length - 1].assignedCount!--;
- currentTotal--;
- }
-
- return assigned;
- };
-
- const getTopY = (radiusFromCenter: number) => {
- const binRadius = bin.diameter() / 2;
-
- const distanceFromEdge = binRadius - radiusFromCenter;
-
- const angleRad = bin.roofAngle() * (Math.PI / 180);
-
- const roofRise = Math.tan(angleRad) * distanceFromEdge;
-
- return bin.sidewallHeight() / 2 + roofRise;
- };
-
- const getBottomY = (radiusFromCenter: number) => {
- const binRadius = bin.diameter() / 2;
-
- const distanceFromCenter = binRadius - radiusFromCenter;
-
- const angleRad = bin.hopperAngle() * (Math.PI / 180);
-
- const hopperDrop = Math.tan(angleRad) * distanceFromCenter;
-
- const clearance = 60 //this is in cm
-
- return -bin.sidewallHeight() / 2 - hopperDrop + clearance;
- };
-
- const buildCablePositions = (bin: Bin): CableData[] => {
- const cables = [...(bin.status.grainCables ?? [])];
-
- if (cables.length === 0) return [];
-
- // optional: sort cables (better visual consistency later)
- //cables.sort((a, b) => b.celcius.length - a.celcius.length);
- cables.sort((a, b) => {
- //if the cables have the same number of nodes
- if (b.celcius.length === a.celcius.length) {
- //sort by temp only last and any type that has humidity first
- if ((avg(a.relativeHumidity) === 0) && (avg(b.relativeHumidity) !== 0)) {
- return 1;
- } else if ((avg(b.relativeHumidity) === 0) && (avg(a.relativeHumidity) !== 0)) {
- return -1;
- }
- else {
- return a.key > b.key ? 1 : -1;
- }
- }
- //otherwise sort by the longest cable first
- return b.celcius.length - a.celcius.length;
- });
-
- const layout = getLayoutFromDiameter(bin.diameter());
- const distributed = distributeCables(cables.length, layout);
-
-
- const cableRadius = 5;
-
- const result: CableData[] = [];
-
- let cableIndex = 0;
-
- distributed.forEach(orbit => {
- const count = orbit.assignedCount ?? 0;
-
- const topY = getTopY(orbit.radius)
- const bottomY = getBottomY(orbit.radius)
-
- if (orbit.orbit === 0 && count > 0) {
- const cable = cables[cableIndex++];
-
- result.push({
- radius: cableRadius,
- topPosition: new Vector3(0, topY, 0),
- bottomPosition: new Vector3(0, bottomY, 0),
- grainCable: cable
- // optionally attach cable data later
- });
- return;
- }
-
- for (let i = 0; i < count; i++) {
- const angle = (i / count) * Math.PI * 2;
- const x = Math.cos(angle) * orbit.radius;
- const z = Math.sin(angle) * orbit.radius;
- const cable = cables[cableIndex++];
-
- result.push({
- radius: cableRadius,
- topPosition: new Vector3(x,topY,z),
- bottomPosition: new Vector3(x,bottomY,z),
- grainCable: cable
- });
- }
- });
-
- return result;
- };
-
- const cables3D = useMemo(() => {
- return buildCablePositions(bin);
- }, [bin]);
-
- return (
-
- {cables3D.map((cable, i) => (
-
- ))}
-
- )
-}
\ No newline at end of file
diff --git a/src/bin/3dView/BinCables/GrainCable.tsx b/src/bin/3dView/BinCables/GrainCable.tsx
deleted file mode 100644
index e889893..0000000
--- a/src/bin/3dView/BinCables/GrainCable.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import Cylinder, { CylinderGeometry } from "3dModels/Shapes/3D/Cylinder"
-import { pond } from "protobuf-ts/pond"
-import React from "react"
-import { Vector3 } from "three"
-import CableNode, { NodeData } from "./CableNode"
-import { Bin } from "models"
-
-export interface CableData {
- radius: number
- topPosition: Vector3
- bottomPosition: Vector3
- grainCable: pond.GrainCable
- radialSegments?: number
- heightSegments?: number
-}
-
-interface Props {
- cable: CableData
- bin: Bin
- binCenter: Vector3
-}
-export default function GrainCable(props: Props) {
- const {cable, bin, binCenter} = props
-
- const calculateHeight = () => {
- return cable.topPosition.distanceTo(cable.bottomPosition)
- }
-
- const calculateCenter = () => {
- return cable.bottomPosition.clone()
- .add(
- cable.topPosition.clone()
- .sub(cable.bottomPosition)
- .multiplyScalar(0.5)
- );
- };
-
- const buildCableNodes = () => {
- const nodeRadius = 20
- const grainCable = cable.grainCable
-
- const nodeCount = grainCable.celcius.length;
-
- if (nodeCount === 0) return [];
-
- const bottomY = cable.bottomPosition.y;
- const topY = cable.topPosition.y;
- const height = topY - bottomY;
-
- const spacing = height / nodeCount;
-
- let nodeData: NodeData[] = []
- grainCable.celcius.forEach((celcius,i) => {
- const nodeY = bottomY + (i * spacing);
-
- const humidity = grainCable.relativeHumidity[i] || undefined;
- const moisture = grainCable.moisture[i] || undefined;
-
- nodeData.push({
- radius: nodeRadius,
- position: new Vector3(cable.bottomPosition.x, nodeY, cable.bottomPosition.z),
- celcius: celcius,
- humidity: humidity,
- moisture: moisture,
- topNode: grainCable.topNode === i+1, //top node tracking starts at 1 not 0 so add one to the index
- excluded: grainCable.excludedNodes.includes(i),
- inGrain: i+1 <= grainCable.topNode
- })
-
- })
-
- return nodeData
- }
-
- const geometry = React.useMemo(() => ({
- radiusTop: cable.radius,
- radiusBottom: cable.radius,
- height: calculateHeight(),
- radialSegments: cable.radialSegments ?? 10,
- heightSegments: cable.heightSegments ?? 2
- } as CylinderGeometry), [cable]);
-
- return (
-
-
- {buildCableNodes().map((node, i) => (
-
- ))}
-
- )
-}
\ No newline at end of file
diff --git a/src/bin/3dView/Data/BuildCableData.ts b/src/bin/3dView/Data/BuildCableData.ts
new file mode 100644
index 0000000..f9493a0
--- /dev/null
+++ b/src/bin/3dView/Data/BuildCableData.ts
@@ -0,0 +1,194 @@
+import { Bin } from "models";
+import { pond } from "protobuf-ts/pond";
+import { Vector3 } from "three";
+import { avg } from "utils";
+
+export interface CableData {
+ cableIndex: number
+ radius: number
+ topPosition: Vector3
+ bottomPosition: Vector3
+ grainCable: pond.GrainCable
+ radialSegments?: number
+ heightSegments?: number
+}
+
+export interface CableOrbit {
+ orbit: number;
+ radius: number; // in cm
+ expectedCount: number;
+ assignedCount?: number;
+}
+
+
+function getLayoutFromDiameter(diameterCm: number): CableOrbit[] {
+ const diameterFt = diameterCm / 30.48;
+
+ let summaries: { Orbit: number; DistanceFromCenter: number; NumberOfCables: number }[] = [];
+
+ if (diameterFt < 24) {
+ summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
+ } else if (diameterFt < 36) {
+ summaries.push({ Orbit: 1, DistanceFromCenter: 7, NumberOfCables: 3 });
+ } else if (diameterFt < 42) {
+ summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
+ summaries.push({ Orbit: 1, DistanceFromCenter: 9, NumberOfCables: 3 });
+ } else if (diameterFt < 48) {
+ summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
+ summaries.push({ Orbit: 1, DistanceFromCenter: 14, NumberOfCables: 4 });
+ } else if (diameterFt < 54) {
+ summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
+ summaries.push({ Orbit: 1, DistanceFromCenter: 16, NumberOfCables: 5 });
+ } else if (diameterFt < 60) {
+ summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
+ summaries.push({ Orbit: 1, DistanceFromCenter: 18, NumberOfCables: 6 });
+ } else if (diameterFt < 72) {
+ summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
+ summaries.push({ Orbit: 1, DistanceFromCenter: 20, NumberOfCables: 7 });
+ } else if (diameterFt < 78) {
+ summaries.push({ Orbit: 1, DistanceFromCenter: 10, NumberOfCables: 4 });
+ summaries.push({ Orbit: 2, DistanceFromCenter: 27, NumberOfCables: 9 });
+ } else if (diameterFt < 90) {
+ summaries.push({ Orbit: 1, DistanceFromCenter: 10, NumberOfCables: 4 });
+ summaries.push({ Orbit: 2, DistanceFromCenter: 29, NumberOfCables: 10 });
+ } else if (diameterFt < 105) {
+ summaries.push({ Orbit: 0, DistanceFromCenter: 2, NumberOfCables: 1 });
+ summaries.push({ Orbit: 1, DistanceFromCenter: 18, NumberOfCables: 6 });
+ summaries.push({ Orbit: 2, DistanceFromCenter: 36, NumberOfCables: 12 });
+ } else {
+ summaries.push({ Orbit: 1, DistanceFromCenter: 9, NumberOfCables: 3 });
+ summaries.push({ Orbit: 2, DistanceFromCenter: 26.5, NumberOfCables: 9 });
+ summaries.push({ Orbit: 3, DistanceFromCenter: 44, NumberOfCables: 14 });
+ }
+
+ return summaries.map(s => ({
+ orbit: s.Orbit,
+ radius: s.DistanceFromCenter * 30.48, // ft → cm
+ expectedCount: s.NumberOfCables
+ }));
+};
+
+function distributeCables(cableCount: number, layout: CableOrbit[]) {
+ const totalExpected = layout.reduce((sum, o) => sum + o.expectedCount, 0);
+
+ let assigned = layout.map(o => ({
+ ...o,
+ assignedCount: Math.round((o.expectedCount / totalExpected) * cableCount)
+ }));
+
+ // normalize rounding
+ let currentTotal = assigned.reduce((sum, o) => sum + (o.assignedCount ?? 0), 0);
+
+ while (currentTotal < cableCount) {
+ assigned[assigned.length - 1].assignedCount!++;
+ currentTotal++;
+ }
+
+ while (currentTotal > cableCount) {
+ assigned[assigned.length - 1].assignedCount!--;
+ currentTotal--;
+ }
+
+ return assigned;
+};
+
+function getTopY(radiusFromCenter: number, bin: Bin) {
+ const binRadius = bin.diameter() / 2;
+
+ const distanceFromEdge = binRadius - radiusFromCenter;
+
+ const angleRad = bin.roofAngle() * (Math.PI / 180);
+
+ const roofRise = Math.tan(angleRad) * distanceFromEdge;
+
+ return bin.sidewallHeight() / 2 + roofRise;
+};
+
+function getBottomY(radiusFromCenter: number, bin: Bin) {
+ const binRadius = bin.diameter() / 2;
+
+ const distanceFromCenter = binRadius - radiusFromCenter;
+
+ const angleRad = bin.hopperAngle() * (Math.PI / 180);
+
+ const hopperDrop = Math.tan(angleRad) * distanceFromCenter;
+
+ const clearance = 60 //this is in cm
+
+ return -bin.sidewallHeight() / 2 - hopperDrop + clearance;
+};
+
+
+export function BuildCableData(bin: Bin): CableData[] {
+ const cables = [...(bin.status.grainCables ?? [])];
+
+ if (cables.length === 0) return [];
+
+ // optional: sort cables (better visual consistency later)
+ //cables.sort((a, b) => b.celcius.length - a.celcius.length);
+ cables.sort((a, b) => {
+ //if the cables have the same number of nodes
+ if (b.celcius.length === a.celcius.length) {
+ //sort by temp only last and any type that has humidity first
+ if ((avg(a.relativeHumidity) === 0) && (avg(b.relativeHumidity) !== 0)) {
+ return 1;
+ } else if ((avg(b.relativeHumidity) === 0) && (avg(a.relativeHumidity) !== 0)) {
+ return -1;
+ }
+ else {
+ return a.key > b.key ? 1 : -1;
+ }
+ }
+ //otherwise sort by the longest cable first
+ return b.celcius.length - a.celcius.length;
+ });
+
+ const layout = getLayoutFromDiameter(bin.diameter());
+ const distributed = distributeCables(cables.length, layout);
+
+
+ const cableRadius = 5;
+
+ const result: CableData[] = [];
+
+ let cableIndex = 0;
+
+ distributed.forEach(orbit => {
+ const count = orbit.assignedCount ?? 0;
+
+ const topY = getTopY(orbit.radius, bin)
+ const bottomY = getBottomY(orbit.radius, bin)
+
+ if (orbit.orbit === 0 && count > 0) {
+ const cable = cables[cableIndex];
+ if (!cable) return
+ result.push({
+ cableIndex: cableIndex,
+ radius: cableRadius,
+ topPosition: new Vector3(0, topY, 0),
+ bottomPosition: new Vector3(0, bottomY, 0),
+ grainCable: cable
+ });
+ cableIndex++
+ return;
+ }
+
+ for (let i = 0; i < count; i++) {
+ const angle = (i / count) * Math.PI * 2;
+ const x = Math.cos(angle) * orbit.radius;
+ const z = Math.sin(angle) * orbit.radius;
+ const cable = cables[cableIndex];
+
+ result.push({
+ cableIndex: cableIndex,
+ radius: cableRadius,
+ topPosition: new Vector3(x,topY,z),
+ bottomPosition: new Vector3(x,bottomY,z),
+ grainCable: cable
+ });
+ cableIndex++
+ }
+ });
+
+ return result;
+};
\ No newline at end of file
diff --git a/src/bin/3dView/Data/BuildNodeData.ts b/src/bin/3dView/Data/BuildNodeData.ts
new file mode 100644
index 0000000..de14d23
--- /dev/null
+++ b/src/bin/3dView/Data/BuildNodeData.ts
@@ -0,0 +1,60 @@
+import { Vector3 } from "three"
+import { CableData } from "./BuildCableData"
+
+export interface NodeData {
+ cableIndex: number
+ nodeIndex: number
+ radius: number
+ position: Vector3
+ celcius: number
+ humidity?: number
+ moisture?: number
+ topNode?: boolean
+ excluded?: boolean
+ inGrain?: boolean
+}
+
+export function BuildNodeData(cables: CableData[]): NodeData[] {
+ const nodeRadius = 20
+ let nodeData: NodeData[] = []
+
+ cables.forEach((cable, cableIndex) => {
+ const grainCable = cable.grainCable
+
+ const nodeCount = grainCable.celcius.length;
+
+ if (nodeCount === 0) return;
+
+ const bottomY = cable.bottomPosition.y;
+ const topY = cable.topPosition.y;
+ const height = topY - bottomY;
+
+ const spacing = height / nodeCount;
+
+
+ grainCable.celcius.forEach((celcius,i) => {
+ const nodeY = bottomY + (i * spacing);
+
+ const humidity = grainCable.relativeHumidity[i] || undefined;
+ const moisture = grainCable.moisture[i] || undefined;
+
+ nodeData.push({
+ cableIndex: cableIndex,
+ nodeIndex: i,
+ radius: nodeRadius,
+ position: new Vector3(cable.bottomPosition.x, nodeY, cable.bottomPosition.z),
+ celcius: celcius,
+ humidity: humidity,
+ moisture: moisture,
+ topNode: grainCable.topNode === i+1, //top node tracking starts at 1 not 0 so add one to the index
+ excluded: grainCable.excludedNodes?.includes(i) ?? false,
+ inGrain: i+1 <= grainCable.topNode
+ // optional: (may want to add this to the node data later)
+ // normalizedHeight: (nodeY - bottomY) / height
+ })
+
+ })
+ })
+
+ return nodeData
+}
\ No newline at end of file
diff --git a/src/bin/3dView/Bin3dView.tsx b/src/bin/3dView/Scene/Bin3dView.tsx
similarity index 83%
rename from src/bin/3dView/Bin3dView.tsx
rename to src/bin/3dView/Scene/Bin3dView.tsx
index 6b6213d..5be25d8 100644
--- a/src/bin/3dView/Bin3dView.tsx
+++ b/src/bin/3dView/Scene/Bin3dView.tsx
@@ -1,11 +1,13 @@
import OrbitCameraControls from "3dModels/CameraControls/OrbitCameraControls";
import { Canvas } from "@react-three/fiber";
import { Bin } from "models";
-import BinShell from "./BinParts/BinShell";
-import GrainFillFlat from "./BinInventory/GrainFillFlat";
-import BinCables from "./BinCables/BinCables";
+import BinShell from "../BinParts/BinShell";
+import GrainFillFlat from "../Systems/Inventory/GrainFillFlat";
+import BinCables from "../Systems/Cables/BinCables";
import { Vector3 } from "three";
import { useMemo } from "react";
+import { BuildCableData } from "../Data/BuildCableData";
+import { BuildNodeData } from "../Data/BuildNodeData";
interface Props {
/**
@@ -30,8 +32,8 @@ export default function Bin3dView(props: Props){
// it might even work better because we can control the smoothness easier with it being only one mesh rather than 3
const {bin, scale = 100, fillPercent} = props
const binCenter = useMemo(() => new Vector3(0, 0, 0), []);
-
- console.log(bin)
+ const cableData = useMemo(() => BuildCableData(bin), [bin]);
+ const nodeData = useMemo(() => BuildNodeData(cableData), [cableData]);
return (