frontend/three_d_garden/bot.tsx
/* eslint-disable complexity */
import React, { useEffect, useState } from "react";
import * as THREE from "three";
import {
Cylinder, Extrude, Line, Trail, Tube, useGLTF, useTexture,
} from "@react-three/drei";
import { DoubleSide, Shape, RepeatWrapping } from "three";
import {
easyCubicBezierCurve3, threeSpace, zDir, zZero as zZeroFunc,
} from "./helpers";
import { Config } from "./config";
import { GLTF } from "three-stdlib";
import { ASSETS, ElectronicsBoxMaterial, LIB_DIR, PartName } from "./constants";
import { SVGLoader } from "three/examples/jsm/Addons.js";
import { range } from "lodash";
import { CrossSlide, CrossSlideFull } from "./parts/cross_slide";
import { GantryWheelPlate, GantryWheelPlateFull } from "./parts/gantry_wheel_plate";
import { RotaryTool, RotaryToolFull } from "./parts/rotary_tool";
import { DistanceIndicator } from "./distance_indicator";
import { VacuumPumpCover, VacuumPumpCoverFull } from "./parts/vacuum_pump_cover";
import { SoilSensor, SoilSensorFull } from "./parts/soil_sensor";
import {
SeedTroughAssembly, SeedTroughAssemblyFull,
} from "./parts/seed_trough_assembly";
import { SeedTroughHolder, SeedTroughHolderFull } from "./parts/seed_trough_holder";
import { PowerSupply } from "./power_supply";
import { XAxisWaterTube } from "./x_axis_water_tube";
import { Group, Mesh, MeshPhongMaterial } from "./components";
import { IColor } from "../settings/pin_bindings/model";
const extrusionWidth = 20;
const utmRadius = 35;
const utmHeight = 35;
const xTrackPadding = 280;
type LeftBracket = GLTF & {
nodes: { [PartName.leftBracket]: THREE.Mesh };
materials: never;
}
type RightBracket = GLTF & {
nodes: { [PartName.rightBracket]: THREE.Mesh };
materials: never;
}
type ZStop = GLTF & {
nodes: { [PartName.zStop]: THREE.Mesh };
materials: never;
}
type BeltClip = GLTF & {
nodes: { [PartName.beltClip]: THREE.Mesh };
materials: never;
}
type UTM = GLTF & {
nodes: { [PartName.utm]: THREE.Mesh };
materials: { PaletteMaterial001: THREE.MeshStandardMaterial };
}
type CCHorizontal = GLTF & {
nodes: { [PartName.ccHorizontal]: THREE.Mesh };
materials: never;
}
type CCVertical = GLTF & {
nodes: { [PartName.ccVertical]: THREE.Mesh };
materials: never;
}
type HousingVertical = GLTF & {
nodes: { [PartName.housingVertical]: THREE.Mesh };
materials: never;
}
type HorizontalMotorHousing = GLTF & {
nodes: { [PartName.horizontalMotorHousing]: THREE.Mesh };
materials: { PaletteMaterial001: THREE.MeshStandardMaterial };
}
type ZAxisMotorMount = GLTF & {
nodes: { [PartName.zAxisMotorMount]: THREE.Mesh };
materials: { PaletteMaterial001: THREE.MeshStandardMaterial };
}
type Toolbay3 = GLTF & {
nodes: {
[PartName.toolbay3]: THREE.Mesh;
[PartName.toolbay3Logo]: THREE.Mesh;
};
materials: never;
}
type WateringNozzle = GLTF & {
nodes: { [PartName.wateringNozzle]: THREE.Mesh };
materials: { PaletteMaterial001: THREE.MeshStandardMaterial };
}
type SeedBin = GLTF & {
nodes: { [PartName.seedBin]: THREE.Mesh };
materials: never;
}
type SeedTray = GLTF & {
nodes: { [PartName.seedTray]: THREE.Mesh };
materials: never;
}
type CameraMountHalf = GLTF & {
nodes: { [PartName.cameraMountHalf]: THREE.Mesh };
materials: never;
}
type Box = GLTF & {
nodes: {
Electronics_Box: THREE.Mesh;
Electronics_Box_Gasket: THREE.Mesh;
Electronics_Box_Lid: THREE.Mesh;
};
materials: {
[ElectronicsBoxMaterial.box]: THREE.MeshStandardMaterial;
[ElectronicsBoxMaterial.gasket]: THREE.MeshStandardMaterial;
[ElectronicsBoxMaterial.lid]: THREE.MeshStandardMaterial;
};
}
type Btn = GLTF & {
nodes: {
["Push_Button_-_Red"]: THREE.Mesh;
};
materials: {
[ElectronicsBoxMaterial.button]: THREE.MeshStandardMaterial;
};
}
type Led = GLTF & {
nodes: {
LED: THREE.Mesh;
};
materials: {
[ElectronicsBoxMaterial.led]: THREE.MeshStandardMaterial;
};
}
type Pi = GLTF & {
nodes: { [PartName.pi]: THREE.Mesh };
materials: { PaletteMaterial001: THREE.MeshStandardMaterial };
}
type Farmduino = GLTF & {
nodes: { [PartName.farmduino]: THREE.Mesh };
materials: { PaletteMaterial001: THREE.MeshStandardMaterial };
}
type Solenoid = GLTF & {
nodes: { [PartName.solenoid]: THREE.Mesh };
materials: { PaletteMaterial001: THREE.MeshStandardMaterial };
}
type XAxisCCMount = GLTF & {
nodes: { [PartName.xAxisCCMount]: THREE.Mesh };
materials: never;
}
Object.values(ASSETS.models).map(model => useGLTF.preload(model, LIB_DIR));
const ccPath =
(axisLength: number, y: number, curveDia: number, isX?: boolean) => {
const lowerLength = (y + axisLength + 180) / 2;
const upperLength = lowerLength - y;
const outerRadius = curveDia / 2;
const height = isX ? 15 : 20;
const innerRadius = outerRadius - height;
const path = new Shape();
path.moveTo(y + 20, 0);
path.lineTo(y + upperLength, 0);
path.arc(0, outerRadius, outerRadius, -Math.PI / 2, Math.PI / 2);
path.lineTo(0, curveDia);
path.lineTo(0, curveDia - 5);
path.lineTo(20, curveDia - height);
path.lineTo(lowerLength, curveDia - height);
path.arc(0, -innerRadius, innerRadius, Math.PI / 2, -Math.PI / 2, true);
if (isX) {
path.lineTo(y + 20, height - 1);
path.lineTo(y, 5);
path.lineTo(y, 0);
} else {
path.lineTo(y, height - 1);
path.lineTo(y, height - 5);
}
path.lineTo(y + 20, 0);
return path;
};
export interface FarmbotModelProps {
config: Config;
activeFocus: string;
}
export const Bot = (props: FarmbotModelProps) => {
const config = props.config;
const {
x, y, z, botSizeX, botSizeY, botSizeZ, beamLength, trail, laser, soilHeight,
bedXOffset, bedYOffset, bedLengthOuter, bedWidthOuter, tracks, zDimension,
columnLength, zAxisLength, zGantryOffset, bedWallThickness, tool, bedHeight,
cableCarriers, bounds,
} = props.config;
const zZero = zZeroFunc(props.config);
const zero = {
x: threeSpace(bedXOffset, bedLengthOuter),
y: threeSpace(bedYOffset, bedWidthOuter),
z: zZero,
};
const extents = {
x: threeSpace(bedXOffset + botSizeX, bedLengthOuter),
y: threeSpace(bedYOffset + botSizeY, bedWidthOuter),
z: zZero + zDir * botSizeZ,
};
const zDip = (x: number, y: number): [number, number, number][] => [
[x, y, extents.z],
[x, y, zero.z],
[x, y, extents.z],
];
const gantryWheelPlate =
useGLTF(ASSETS.models.gantryWheelPlate, LIB_DIR) as GantryWheelPlateFull;
const GantryWheelPlateComponent = GantryWheelPlate(gantryWheelPlate);
const leftBracket = useGLTF(ASSETS.models.leftBracket, LIB_DIR) as LeftBracket;
const rightBracket = useGLTF(ASSETS.models.rightBracket, LIB_DIR) as RightBracket;
const crossSlide = useGLTF(ASSETS.models.crossSlide, LIB_DIR) as CrossSlideFull;
const CrossSlideComponent = CrossSlide(crossSlide);
const beltClip = useGLTF(ASSETS.models.beltClip, LIB_DIR) as BeltClip;
const zStop = useGLTF(ASSETS.models.zStop, LIB_DIR) as ZStop;
const utm = useGLTF(ASSETS.models.utm, LIB_DIR) as UTM;
const ccHorizontal = useGLTF(ASSETS.models.ccHorizontal, LIB_DIR) as CCHorizontal;
const ccVertical = useGLTF(ASSETS.models.ccVertical, LIB_DIR) as CCVertical;
const housingVertical = useGLTF(
ASSETS.models.housingVertical, LIB_DIR) as HousingVertical;
const horizontalMotorHousing = useGLTF(
ASSETS.models.horizontalMotorHousing, LIB_DIR) as HorizontalMotorHousing;
const zAxisMotorMount = useGLTF(
ASSETS.models.zAxisMotorMount, LIB_DIR) as ZAxisMotorMount;
const toolbay3 = useGLTF(ASSETS.models.toolbay3, LIB_DIR) as Toolbay3;
const rotaryTool = useGLTF(ASSETS.models.rotaryTool, LIB_DIR) as RotaryToolFull;
const RotaryToolComponent = RotaryTool(rotaryTool);
const vacuumPumpCover = useGLTF(
ASSETS.models.vacuumPumpCover, LIB_DIR) as VacuumPumpCoverFull;
const VacuumPumpCoverComponent = VacuumPumpCover(vacuumPumpCover);
const seedBin = useGLTF(ASSETS.models.seedBin, LIB_DIR) as SeedBin;
const seedTray = useGLTF(ASSETS.models.seedTray, LIB_DIR) as SeedTray;
const seedTroughHolder = useGLTF(
ASSETS.models.seedTroughHolder, LIB_DIR) as SeedTroughHolderFull;
const SeedTroughHolderComponent = SeedTroughHolder(seedTroughHolder);
const seedTroughAssembly = useGLTF(
ASSETS.models.seedTroughAssembly, LIB_DIR) as SeedTroughAssemblyFull;
const SeedTroughAssemblyComponent = SeedTroughAssembly(seedTroughAssembly);
const soilSensor = useGLTF(ASSETS.models.soilSensor, LIB_DIR) as SoilSensorFull;
const SoilSensorComponent = SoilSensor(soilSensor);
const wateringNozzle = useGLTF(
ASSETS.models.wateringNozzle, LIB_DIR) as WateringNozzle;
const cameraMountHalf = useGLTF(
ASSETS.models.cameraMountHalf, LIB_DIR) as CameraMountHalf;
const box = useGLTF(ASSETS.models.box, LIB_DIR) as Box;
const btn = useGLTF(ASSETS.models.btn, LIB_DIR) as Btn;
const led = useGLTF(ASSETS.models.led, LIB_DIR) as Led;
const pi = useGLTF(ASSETS.models.pi, LIB_DIR) as Pi;
const farmduino = useGLTF(ASSETS.models.farmduino, LIB_DIR) as Farmduino;
const solenoid = useGLTF(ASSETS.models.solenoid, LIB_DIR) as Solenoid;
const xAxisCCMount = useGLTF(ASSETS.models.xAxisCCMount, LIB_DIR) as XAxisCCMount;
const [trackShape, setTrackShape] = useState<Shape>();
const [beamShape, setBeamShape] = useState<Shape>();
const [columnShape, setColumnShape] = useState<Shape>();
const [zAxisShape, setZAxisShape] = useState<Shape>();
useEffect(() => {
if (!(trackShape && beamShape && columnShape && zAxisShape)) {
const loader = new SVGLoader();
loader.load(ASSETS.shapes.track,
svg => {
const smallCutout = SVGLoader.createShapes(svg.paths[0])[0];
const largeCutout = SVGLoader.createShapes(svg.paths[1])[0];
const outline = SVGLoader.createShapes(svg.paths[2])[0];
outline.holes.push(smallCutout);
outline.holes.push(largeCutout);
setTrackShape(outline);
});
loader.load(ASSETS.shapes.beam,
svg => {
const outline = SVGLoader.createShapes(svg.paths[0])[0];
range(1, 6).map(i => {
const hole = SVGLoader.createShapes(svg.paths[i])[0];
outline.holes.push(hole);
});
setBeamShape(outline);
});
loader.load(ASSETS.shapes.column,
svg => {
const outline = SVGLoader.createShapes(svg.paths[3])[0];
range(3).map(i => {
const hole = SVGLoader.createShapes(svg.paths[i])[0];
outline.holes.push(hole);
});
setColumnShape(outline);
});
loader.load(ASSETS.shapes.zAxis,
svg => {
const hole = SVGLoader.createShapes(svg.paths[1])[0];
const outline = SVGLoader.createShapes(svg.paths[0])[0];
outline.holes.push(hole);
setZAxisShape(outline);
});
}
});
const aluminumTexture = useTexture(ASSETS.textures.aluminum + "?=bot");
aluminumTexture.wrapS = RepeatWrapping;
aluminumTexture.wrapT = RepeatWrapping;
aluminumTexture.repeat.set(0.01, 0.0003);
const yBeltPath = () => {
const radius = 12;
const path = new Shape();
path.moveTo(0, 0);
path.lineTo(0, y + 30);
path.arc(radius, -5, radius, Math.PI, Math.PI / 2, true);
path.lineTo(45, y + 30);
path.arc(0, 10, 10, -Math.PI / 2, Math.PI / 4);
path.lineTo(0, y + 100);
path.arc(radius, 5, radius, (-3 / 4) * Math.PI, Math.PI, true);
path.lineTo(0, botSizeY + 220);
path.lineTo(-2, botSizeY + 220);
path.lineTo(-2, y + 100);
path.arc(radius, 4, radius, Math.PI, (-3 / 4) * Math.PI);
path.lineTo(45, y + 50);
path.arc(0, -10, 8, Math.PI / 4, -Math.PI / 2, true);
path.lineTo(radius, y + 40);
path.arc(-2, -radius, radius, Math.PI / 2, Math.PI);
path.lineTo(-2, 0);
return path;
};
const distanceToSoil = soilHeight + zDir * z;
const bedCCSupportHeight = Math.min(150, bedHeight / 2);
const isJr = props.config.sizePreset == "Jr";
return <Group name={"bot"}
visible={props.config.bot && props.activeFocus != "Planter bed"}>
{[0 - extrusionWidth, bedWidthOuter].map((y, index) => {
const bedColumnYOffset =
(tracks ? 0 : extrusionWidth) * (index == 0 ? 1 : -1);
return <Group key={y}>
<Extrude name={"columns"}
castShadow={true}
args={[
columnShape,
{ steps: 1, depth: columnLength, bevelEnabled: false },
]}
position={[
threeSpace(x - extrusionWidth - 12, bedLengthOuter) + bedXOffset,
threeSpace(y + bedColumnYOffset, bedWidthOuter),
30,
]}
rotation={[0, 0, Math.PI / 2]}>
<MeshPhongMaterial
color={"white"}
map={aluminumTexture}
side={DoubleSide} />
</Extrude>
<Mesh name={index == 0 ? "leftBracket" : "rightBracket"}
position={[
threeSpace(x - extrusionWidth - 12, bedLengthOuter) + bedXOffset,
threeSpace(y - (index == 0 ? 0 : 170) + bedColumnYOffset,
bedWidthOuter),
columnLength - 30,
]}
rotation={[Math.PI / 2, Math.PI / 2, 0]}
scale={1000}
geometry={index == 0
? leftBracket.nodes[PartName.leftBracket].geometry
: rightBracket.nodes[PartName.rightBracket].geometry}>
<MeshPhongMaterial color={"silver"} side={DoubleSide} />
</Mesh>
<Mesh name={index == 0 ? "leftMotor" : "rightMotor"}
position={[
threeSpace(x - (index == 0 ? 47 : 77), bedLengthOuter) + bedXOffset,
threeSpace(y - (index == 0 ? 0 : -20) + bedColumnYOffset,
bedWidthOuter),
columnLength + 70,
]}
rotation={[Math.PI / 2, (index == 0 ? Math.PI : 0), Math.PI / 2]}
scale={1000}
geometry={undefined}
material={undefined} />
<Mesh name={index == 0 ? "leftMotor" : "rightMotor"}
position={[
threeSpace(x - 68, bedLengthOuter) + bedXOffset,
threeSpace(y - (index == 0 ? 5 : -25) + bedColumnYOffset,
bedWidthOuter),
columnLength + 80,
]}
rotation={[0, Math.PI, (index == 0 ? 0 : Math.PI)]}
scale={1000}
geometry={
horizontalMotorHousing.nodes[PartName.horizontalMotorHousing].geometry}>
<MeshPhongMaterial color={"silver"} side={DoubleSide} />
</Mesh>
<Cylinder name={"motorPulley"}
args={[8, 8, 40]}
position={[
threeSpace(x - 63, bedLengthOuter) + bedXOffset,
threeSpace(y - (index == 0 ? 5 : -25) + bedColumnYOffset,
bedWidthOuter),
columnLength + 55,
]}
rotation={[0, 0, 0]}>
<MeshPhongMaterial color={"#999"} />
</Cylinder>
<Extrude name={"tracks"} visible={tracks}
castShadow={true}
args={[
trackShape,
{ steps: 1, depth: botSizeX + xTrackPadding, bevelEnabled: false },
]}
position={[
threeSpace(index == 0
? botSizeX + xTrackPadding / 2
: -xTrackPadding / 2, bedLengthOuter) + bedXOffset,
threeSpace(y + (index == 0 ? 2.5 : 17.5), bedWidthOuter),
2,
]}
rotation={[
index == 0 ? -Math.PI / 2 : -Math.PI / 2,
index == 0 ? -Math.PI / 2 : Math.PI / 2,
0,
]}>
<MeshPhongMaterial
color={"white"}
map={aluminumTexture}
side={DoubleSide} />
</Extrude>
<Mesh name={"xStopMin"}
position={[
threeSpace(-132, bedLengthOuter) + bedXOffset,
threeSpace(y + 10 + bedColumnYOffset, bedWidthOuter),
2 + (index == 0 ? 0 : 5),
]}
rotation={[
0,
index == 0 ? 0 : Math.PI,
(index == 0 ? 1 : -1) * Math.PI / 2,
]}
scale={1000}
geometry={beltClip.nodes[PartName.beltClip].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<Mesh name={"xStopMax"}
position={[
threeSpace(botSizeX + 128, bedLengthOuter) + bedXOffset,
threeSpace(y + 10 + bedColumnYOffset, bedWidthOuter),
2 + (index == 0 ? 5 : 0),
]}
rotation={[
0,
index == 0 ? Math.PI : 0,
(index == 0 ? 1 : -1) * Math.PI / 2,
]}
scale={1000}
geometry={beltClip.nodes[PartName.beltClip].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<GantryWheelPlateComponent name={"gantryWheelPlate"}
position={[
threeSpace(x - 42, bedLengthOuter) + bedXOffset,
threeSpace(
y + (index == 0 ? 0 : extrusionWidth + 5)
- 2 - (index == 0 ? 1 : 0)
+ bedColumnYOffset,
bedWidthOuter),
-30,
]}
rotation={[0, 0, Math.PI / 2 + (index == 0 ? Math.PI : 0)]}
scale={[1000, 1000 * (index == 0 ? -1 : 1), 1000]} />
</Group>;
})}
<Mesh name={"xCCMount"}
position={[
threeSpace(x - 32, bedLengthOuter) + bedXOffset,
threeSpace(-12, bedWidthOuter),
-40,
]}
rotation={[0, 0, Math.PI / 2]}
scale={1000}
geometry={xAxisCCMount.nodes[PartName.xAxisCCMount].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<Extrude name={"xCC"} visible={cableCarriers}
castShadow={true}
args={[
ccPath(botSizeX / 2, botSizeX / 2 - x + 20, bedCCSupportHeight - 40, true),
{ steps: 1, depth: 22, bevelEnabled: false },
]}
position={[
threeSpace(botSizeX / 2, bedLengthOuter) + bedXOffset,
threeSpace((tracks ? 0 : extrusionWidth) - 15, bedWidthOuter),
-40,
]}
rotation={[-Math.PI / 2, -Math.PI, 0 * Math.PI]}>
<MeshPhongMaterial color={"black"} />
</Extrude>
<CrossSlideComponent name={"crossSlide"}
position={[
threeSpace(x - 1.5, bedLengthOuter) + bedXOffset,
threeSpace(y + 5, bedWidthOuter) + bedYOffset,
columnLength + 105,
]}
rotation={[0, 0, Math.PI / 2]}
scale={1000} />
<Extrude name={"z-axis"}
castShadow={true}
args={[
zAxisShape,
{ steps: 1, depth: zAxisLength, bevelEnabled: false },
]}
position={[
threeSpace(x, bedLengthOuter) + bedXOffset,
threeSpace(y + utmRadius, bedWidthOuter) + bedYOffset,
zZero + zDir * z,
]}
rotation={[0, 0, 0]}>
<MeshPhongMaterial color={"white"} map={aluminumTexture} side={DoubleSide} />
</Extrude>
<Group name={"zMotor"}>
<Mesh name={"zMotorHousing"}
position={[
threeSpace(x + 4, bedLengthOuter) + bedXOffset,
threeSpace(y + utmRadius - 47, bedWidthOuter) + bedYOffset,
zZero + zDir * z + zAxisLength - 80,
]}
rotation={[0, 0, Math.PI]}
scale={1000}
geometry={housingVertical.nodes[PartName.housingVertical].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<Mesh name={"zMotor"}
position={[
threeSpace(x + 10, bedLengthOuter) + bedXOffset,
threeSpace(y + utmRadius - 5, bedWidthOuter) + bedYOffset,
zZero + zDir * z + zAxisLength - 140,
]}
rotation={[Math.PI / 2, 0, 0]}
scale={1000}
geometry={undefined}
material={undefined} />
<Mesh name={"zMotorMount"}
position={[
threeSpace(x + 5, bedLengthOuter) + bedXOffset,
threeSpace(y + utmRadius - 65, bedWidthOuter) + bedYOffset,
zZero + zDir * z + zAxisLength - 80,
]}
rotation={[0, 0, Math.PI]}
scale={1000}
geometry={zAxisMotorMount.nodes[PartName.zAxisMotorMount].geometry}>
<MeshPhongMaterial color={"silver"} side={DoubleSide} />
</Mesh>
<Cylinder name={"motorShaft"}
args={[2.5, 2.5, 40]}
position={[
threeSpace(x + 5, bedLengthOuter) + bedXOffset,
threeSpace(y + utmRadius - 65, bedWidthOuter) + bedYOffset,
zZero + zDir * z + zAxisLength - 80,
]}
rotation={[Math.PI / 2, 0, 0]}>
<MeshPhongMaterial color={"#999"} />
</Cylinder>
</Group>
<Mesh name={"shaftCoupler"}
position={[
threeSpace(x + 5, bedLengthOuter) + bedXOffset,
threeSpace(y - 30, bedWidthOuter) + bedYOffset,
zZero + zDir * z + zAxisLength - 120,
]}
rotation={[0, 0, 0]}
scale={1000}
geometry={undefined}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<Cylinder name={"shaftCoupler"}
args={[10, 10, 25]}
position={[
threeSpace(x + 5, bedLengthOuter) + bedXOffset,
threeSpace(y - 30, bedWidthOuter) + bedYOffset,
zZero + zDir * z + zAxisLength - 120 + 25 / 2,
]}
rotation={[Math.PI / 2, 0, 0]}>
<MeshPhongMaterial color={"silver"} />
</Cylinder>
<Cylinder name={"leadscrew"}
material-color={"#555"}
args={[4, 4, zAxisLength - 200]}
position={[
threeSpace(x + 6, bedLengthOuter) + bedXOffset,
threeSpace(y - 30, bedWidthOuter) + bedYOffset,
zZero + zDir * z + zAxisLength / 2,
]}
rotation={[Math.PI / 2, 0, 0]} />
<Group name={"ccVertical"}>
{range((zAxisLength - 350) / 200).map(i =>
<Mesh key={i}
position={[
threeSpace(x + 20, bedLengthOuter) + bedXOffset,
threeSpace(y + 55, bedWidthOuter) + bedYOffset,
zZero + zDir * z + i * 200 + 125,
]}
rotation={[0, 0, Math.PI / 2]}
scale={1000}
geometry={ccVertical.nodes[PartName.ccVertical].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>)}
</Group>
<Extrude name={"zCC"} visible={cableCarriers}
castShadow={true}
args={[
ccPath(botSizeZ + zGantryOffset - 100, z + zGantryOffset - 15, 87),
{ steps: 1, depth: 60, bevelEnabled: false },
]}
position={[
threeSpace(x - 41, bedLengthOuter) + bedXOffset,
threeSpace(y - 25, bedWidthOuter) + bedYOffset,
zZero + zDir * z + 125,
]}
rotation={[Math.PI / 2, Math.PI, Math.PI / 2]}>
<MeshPhongMaterial color={"black"} />
</Extrude>
<Mesh name={"zStopMax"}
position={[
threeSpace(x - 5, bedLengthOuter) + bedXOffset,
threeSpace(y + utmRadius + extrusionWidth / 2, bedWidthOuter) + bedYOffset,
zZero + zDir * z - 30 + zGantryOffset,
]}
rotation={[0, Math.PI / 2, 0]}
scale={1000}
geometry={zStop.nodes[PartName.zStop].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<Mesh name={"zStopMin"}
position={[
threeSpace(x - 5, bedLengthOuter) + bedXOffset,
threeSpace(y + utmRadius + extrusionWidth / 2, bedWidthOuter) + bedYOffset,
zZero + zDir * z + botSizeZ + 140 + zGantryOffset,
]}
rotation={[0, Math.PI / 2, 0]}
scale={1000}
geometry={zStop.nodes[PartName.zStop].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<Mesh name={"vacuumPump"}
position={[
threeSpace(x + 28, bedLengthOuter) + bedXOffset,
threeSpace(y, bedWidthOuter) + bedYOffset,
zZero + zDir * z + 40,
]}
rotation={[0, 0, Math.PI / 2]}
scale={1000}
geometry={undefined}
material={undefined} />
<Tube name={"air-tube"}
castShadow={true}
receiveShadow={true}
args={[easyCubicBezierCurve3(
[
threeSpace(x + 28, bedLengthOuter) + bedXOffset,
threeSpace(y, bedWidthOuter) + bedYOffset,
zZero + zDir * z + 40,
],
[0, 0, 100],
[0, 0, -200],
[
threeSpace(x + 80, bedLengthOuter) + bedXOffset,
threeSpace(y + 100, bedWidthOuter) + bedYOffset,
zZero + zDir * z + 245,
],
), 20, 5, 8]}>
<MeshPhongMaterial
color={"white"}
transparent={true}
opacity={0.75}
/>
</Tube>
<VacuumPumpCoverComponent
rotation={[0, 0, Math.PI / 2]}
scale={1000}
position={[
threeSpace(x + 12, bedLengthOuter) + bedXOffset,
threeSpace(y + 55, bedWidthOuter) + bedYOffset,
zZero + zDir * z + 490,
]} />
<Group name={"camera"}
rotation={[Math.PI, 0, 0]}
position={[
threeSpace(x + 23, bedLengthOuter) + bedXOffset,
threeSpace(y + 25 + extrusionWidth / 2, bedWidthOuter) + bedYOffset,
zZero + zDir * z - 140 + zGantryOffset,
]}>
<Mesh name={"cameraMount"}
rotation={[0, 0, 0]}
position={[0, 0, -40]}
scale={1000}
geometry={cameraMountHalf.nodes[PartName.cameraMountHalf].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<Mesh name={"cameraMount"}
rotation={[0, Math.PI, 0]}
scale={1000}
geometry={cameraMountHalf.nodes[PartName.cameraMountHalf].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
</Group>
<Trail
width={trail ? 500 : 0}
color={"red"}
length={100}
decay={0.5}
local={false}
stride={0}
interval={1}>
<Group name={"UTM"}
position={[
threeSpace(x + 11, bedLengthOuter) + bedXOffset,
threeSpace(y, bedWidthOuter) + bedYOffset,
zZero + zDir * z + utmHeight / 2 - 18,
]}
rotation={[0, 0, Math.PI / 2]}
scale={1000}>
<Mesh
geometry={utm.nodes.M5_Barb.geometry}
material={utm.materials.PaletteMaterial001}
position={[0.015, 0.009, 0.036]}
rotation={[0, 0, 2.094]} />
</Group>
</Trail>
<RotaryToolComponent name={"rotaryTool"} visible={tool == "rotaryTool"}
position={[
threeSpace(x + 11, bedLengthOuter) + bedXOffset,
threeSpace(y, bedWidthOuter) + bedYOffset,
zZero + zDir * z + utmHeight / 2 - 15,
]}
rotation={[0, 0, Math.PI / 2]}
scale={1000} />
<Cylinder
visible={laser}
material-color={"red"}
args={[5, 5, distanceToSoil]}
position={[
threeSpace(x, bedLengthOuter) + bedXOffset,
threeSpace(y, bedWidthOuter) + bedYOffset,
zZero + zDir * z - distanceToSoil / 2,
]}
rotation={[Math.PI / 2, 0, 0]} />
<Extrude name={"gantry-beam"}
castShadow={true}
args={[
beamShape,
{ steps: 1, depth: beamLength, bevelEnabled: false },
]}
position={[
threeSpace(x - extrusionWidth - 8, bedLengthOuter) + bedXOffset,
threeSpace((bedWidthOuter + beamLength) / 2, bedWidthOuter),
columnLength + 40,
]}
rotation={[Math.PI / 2, 0, 0]}>
<MeshPhongMaterial color={"white"} map={aluminumTexture} side={DoubleSide} />
</Extrude>
<Group name={"ccHorizontal"}>
{range((botSizeY - 10) / 300).map(i =>
<Mesh key={i}
position={[
threeSpace(x - 28, bedLengthOuter) + bedXOffset,
threeSpace(50 + i * 300, bedWidthOuter) + bedYOffset,
columnLength + 60,
]}
rotation={[Math.PI / 2, 0, 0]}
scale={1000}
geometry={ccHorizontal.nodes[PartName.ccHorizontal].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>)}
</Group>
<Extrude name={"yCC"} visible={cableCarriers}
castShadow={true}
args={[
ccPath(botSizeY, y + 40, 70),
{ steps: 1, depth: 60, bevelEnabled: false },
]}
position={[
threeSpace(x - 28, bedLengthOuter) + bedXOffset,
threeSpace(20, bedWidthOuter) + bedYOffset,
columnLength + 150,
]}
rotation={[-Math.PI / 2, -Math.PI / 2, 0]}>
<MeshPhongMaterial color={"black"} />
</Extrude>
<Mesh name={"yStopMin"}
position={[
threeSpace(x - extrusionWidth + 2, bedLengthOuter) + bedXOffset,
threeSpace(bedYOffset - 125, bedWidthOuter),
columnLength + 40 + extrusionWidth * 3,
]}
rotation={[0, 0, Math.PI]}
scale={1000}
geometry={beltClip.nodes[PartName.beltClip].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<Extrude name={"yBelt"}
args={[
yBeltPath(),
{ steps: 1, depth: 6, bevelEnabled: false },
]}
position={[
threeSpace(x - 14.5, bedLengthOuter) + bedXOffset,
threeSpace(-100, bedWidthOuter) + bedYOffset,
columnLength + 100,
]}
rotation={[0, -Math.PI / 2, 0]}>
<MeshPhongMaterial color={"black"} />
</Extrude>
<Mesh name={"yStopMax"}
position={[
threeSpace(x - extrusionWidth + 2, bedLengthOuter) + bedXOffset,
threeSpace(botSizeY + bedYOffset + 135, bedWidthOuter),
columnLength + 40 + extrusionWidth * 3 + 5,
]}
rotation={[0, Math.PI, 0]}
scale={1000}
geometry={beltClip.nodes[PartName.beltClip].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<Mesh name={"solenoid"}
position={[
threeSpace(x - 104, bedLengthOuter) + bedXOffset,
threeSpace(20, bedWidthOuter),
columnLength - 200,
]}
rotation={[0, 0, -Math.PI / 2]}
scale={1000}
geometry={solenoid.nodes[PartName.solenoid].geometry}
material={solenoid.materials.PaletteMaterial001} />
<Group name={"electronics-box"}
position={new THREE.Vector3(
threeSpace(x - 62, bedLengthOuter) + bedXOffset,
threeSpace(-20, bedWidthOuter),
columnLength - 190,
)}>
<Group name={"box"}
rotation={[0, 0, Math.PI / 2]}>
<Mesh name={"electronicsBox"}
geometry={box.nodes.Electronics_Box.geometry}
material={box.materials[ElectronicsBoxMaterial.box]}
scale={1000}
material-color={0xffffff}
material-emissive={0x999999} />
<Mesh name={"electronicsBoxGasket"}
geometry={box.nodes.Electronics_Box_Gasket.geometry}
material={box.materials[ElectronicsBoxMaterial.gasket]}
scale={1000} />
<Mesh name={"electronicsBoxLid"}
geometry={box.nodes.Electronics_Box_Lid.geometry}
material={box.materials[ElectronicsBoxMaterial.lid]}
scale={1000} />
<Group name={"buttons-and-leds"}
position={[0, 0, 130]}>
{[
{ position: -60, color: IColor.estop.on },
{ position: -30, color: IColor.unlock.on },
{ position: 0, color: IColor.blank.on },
{ position: 30, color: IColor.blank.on },
{ position: 60, color: IColor.blank.on },
].map(button => {
const { position, color } = button;
const btnPosition = position;
return <Group key={btnPosition} name={"button-group"}>
<Mesh name={"button-housing"}
geometry={btn.nodes["Push_Button_-_Red"].geometry}
material={btn.materials[ElectronicsBoxMaterial.button]}
position={[-30, btnPosition, 0]}
scale={1000}
material-color={0xcccccc} />
<Cylinder
name={"button-color"}
material-color={color}
args={[9, 0, 3.5]}
position={[-30, btnPosition, 0]}
rotation={[Math.PI / 2, 0, 0]} />
<Cylinder name={"button-center"}
material-color={0xcccccc}
args={[6.75, 0, 4]}
position={[-30, btnPosition, 0]}
rotation={[Math.PI / 2, 0, 0]} />
</Group>;
})}
{[
{ position: -45, color: IColor.sync.on },
{ position: -15, color: IColor.connect.on },
{ position: 15, color: IColor.blank.on },
{ position: 45, color: IColor.blank.on },
].map(ledIndicator => {
const { position, color } = ledIndicator;
return <Group key={position}>
<Mesh name={"led-housing"}
geometry={led.nodes.LED.geometry}
material={led.materials[ElectronicsBoxMaterial.led]}
position={[-50, position, 0]}
material-color={0xcccccc}
scale={1000} />
<Cylinder name={"led-color"}
material-color={color}
args={[6.75, 6.75, 3]}
position={[-50, position, 0]}
rotation={[Math.PI / 2, 0, 0]} />
</Group>;
})}
</Group>
</Group>
<Mesh name={"farmduino"}
position={[-60, -10, -110]}
rotation={[Math.PI / 2, 0, 0]}
scale={1000}
geometry={farmduino.nodes[PartName.farmduino].geometry}
material={farmduino.materials.PaletteMaterial001} />
<Mesh name={"pi"}
position={[-15, -10, 40]}
rotation={[Math.PI / 2, 0, Math.PI]}
scale={1000}
geometry={pi.nodes[PartName.pi].geometry}
material={pi.materials.PaletteMaterial001} />
</Group>
<Group
position={[
threeSpace(x - 32, bedLengthOuter) + bedXOffset,
threeSpace(2, bedWidthOuter),
100,
]}
rotation={[0, 0, Math.PI / 2]}>
<SeedTroughAssemblyComponent name={"seedTroughAssembly"}
position={[3, -15, 30]}
scale={1000} />
<SeedTroughHolderComponent name={"seedTroughHolder"}
scale={1000} />
</Group>
<Group name={"toolbay3"}>
{(isJr ? [0] : [-200, 200]).map(yPosition =>
<Group key={yPosition}>
{[
{ node: PartName.toolbay3, color: "black", id: "toolbay3" },
{ node: PartName.toolbay3Logo, color: "white", id: "toolbay3Logo" },
].map(part =>
<Mesh name={part.id} key={part.id}
position={[
threeSpace(105 + bedWallThickness, bedLengthOuter),
threeSpace(yPosition + bedWidthOuter / 2, bedWidthOuter),
60,
]}
rotation={[0, 0, -Math.PI / 2]}
scale={1000}
geometry={
toolbay3.nodes[part.node as keyof Toolbay3["nodes"]].geometry}>
<MeshPhongMaterial color={part.color} />
</Mesh>)}
</Group>)}
</Group>
<RotaryToolComponent name={"rotaryTool"} visible={tool != "rotaryTool"}
position={[
threeSpace(105 + bedWallThickness, bedLengthOuter),
threeSpace((isJr ? 0 : 100) + bedWidthOuter / 2, bedWidthOuter),
70,
]}
rotation={[0, 0, Math.PI / 2]}
scale={1000} />
<Mesh name={"wateringNozzle"}
position={[
threeSpace(11 + 105 + bedWallThickness, bedLengthOuter),
threeSpace(10 + (isJr ? 100 : 200) + bedWidthOuter / 2, bedWidthOuter),
5 + 70,
]}
rotation={[0, 0, 2.094 + Math.PI / 2]}
scale={1000}
geometry={wateringNozzle.nodes[PartName.wateringNozzle].geometry}
material={wateringNozzle.materials.PaletteMaterial001} />
<Mesh name={"seedBin"}
position={[
threeSpace(110 + bedWallThickness, bedLengthOuter),
threeSpace((isJr ? 200 : 300) + bedWidthOuter / 2, bedWidthOuter),
55,
]}
rotation={[0, 0, Math.PI / 2]}
scale={1000}
geometry={seedBin.nodes[PartName.seedBin].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<Mesh name={"seedTray"}
position={[
threeSpace(110 + bedWallThickness, bedLengthOuter),
threeSpace((isJr ? -100 : -200) + bedWidthOuter / 2, bedWidthOuter),
55,
]}
rotation={[0, 0, Math.PI / 2]}
scale={1000}
geometry={seedTray.nodes[PartName.seedTray].geometry}>
<MeshPhongMaterial color={"silver"} />
</Mesh>
<SoilSensorComponent name={"soilSensor"}
position={[
threeSpace(105 + bedWallThickness, bedLengthOuter),
threeSpace((isJr ? -200 : -300) + bedWidthOuter / 2, bedWidthOuter),
70,
]}
rotation={[0, 0, Math.PI / 2]}
scale={1000} />
<PowerSupply config={config} />
<XAxisWaterTube config={config} />
<Line name={"bounds"}
visible={bounds}
color={"white"}
points={[
[zero.x, zero.y, zero.z],
[zero.x, extents.y, zero.z],
[extents.x, extents.y, zero.z],
[extents.x, zero.y, zero.z],
[zero.x, zero.y, zero.z],
...zDip(zero.x, zero.y),
...zDip(zero.x, extents.y),
...zDip(extents.x, extents.y),
...zDip(extents.x, zero.y),
[zero.x, zero.y, extents.z],
]} />
<Group visible={zDimension}>
<DistanceIndicator
start={{
x: threeSpace(0, bedLengthOuter),
y: threeSpace(bedWidthOuter, bedWidthOuter),
z: 0,
}}
end={{
x: threeSpace(0, bedLengthOuter),
y: threeSpace(bedWidthOuter, bedWidthOuter),
z: zZero - z + zAxisLength,
}} />
</Group>
</Group>;
};