imported mining stuff, can add a mine and list them

This commit is contained in:
Carter 2025-03-13 11:42:50 -06:00
parent 7dad5217e7
commit 5fcb99ef40
36 changed files with 6622 additions and 32 deletions

860
src/ventilation/Editor.tsx Normal file
View file

@ -0,0 +1,860 @@
import React, { useEffect, useState } from "react";
import Canvas from "ventilation/Canvas";
import Zoom from "ventilation/Zoom";
import { pond } from "protobuf-ts/pond";
import {
CircularProgress,
ListItemIcon,
ListItemText,
Menu,
MenuItem,
ToggleButton,
Typography,
useTheme
} from "@mui/material";
import { Info, AddCircle, InfoRounded } from "@mui/icons-material";
import { VentShaft } from "./drawable/VentShaft";
import { VentCorner } from "./drawable/VentCorner";
import { Placeable } from "./drawable/Placeable";
import VentDialog from "./VentDialog";
import DeleteIcon from "@mui/icons-material/DeleteForever";
import ClockwiseIcon from "@mui/icons-material/RotateRight";
import CounterclockwiseIcon from "@mui/icons-material/RotateLeft";
import { useGlobalState, useMineAPI } from "providers";
import { Component, Device } from "models";
import { Sensor } from "./drawable/Sensor";
// import { ToggleButton } from "@material-ui/lab";
import { VentTBreak } from "./drawable/VentTBreak";
import InteractionSettings from "interactions/InteractionSettings";
import { UnitMeasurement } from "models/UnitMeasurement";
interface Props {
width?: number;
height?: number;
mode?: Mode;
undoRef: any;
redoRef: any;
saveRef: any;
addBlockRef: any;
devices: pond.Device[];
blocks: Placeable[];
setBlocks: React.Dispatch<React.SetStateAction<Placeable[]>>;
components: Component[];
setComponents: React.Dispatch<React.SetStateAction<Component[]>>;
componentPreferences: Map<string, pond.MineComponentPreferences>;
setComponentPreferences: React.Dispatch<
React.SetStateAction<Map<string, pond.MineComponentPreferences>>
>;
componentDevices: Map<string, number>;
sensors: Sensor[];
setSensors: React.Dispatch<React.SetStateAction<Sensor[]>>;
loading: boolean;
}
enum Mode {
NORMAL,
MOVE,
ADD,
DELETE
}
enum ViewMode {
NORMAL,
INFO
}
export default function Editor(props: Props) {
const {
blocks,
setBlocks,
devices,
components,
componentDevices,
componentPreferences,
sensors,
setSensors,
setComponentPreferences,
loading
} = props;
const [selected, setSelected] = useState<Placeable[]>([]);
const [width, setWidth] = useState<number>(props.width ? props.width : 800);
const [height, setHeight] = useState<number>(props.width ? props.width : 600);
const [cameraOffsetX, setCameraOffsetX] = useState<number>(0);
const [cameraOffsetY, setCameraOffsetY] = useState<number>(0);
const [clickX, setClickX] = useState<number>(0);
const [clickY, setClickY] = useState<number>(0);
const [mode, setMode] = useState<Mode>(props.mode ? props.mode : Mode.NORMAL);
const [viewMode, setViewMode] = useState<ViewMode>(ViewMode.NORMAL);
const [mouseDown, setMouseDown] = useState<boolean>(false);
const [undoStates, setUndoStates] = useState<Placeable[][]>([]);
const [redoStates, setRedoStates] = useState<Placeable[][]>([]);
const [ctrl, setCtrl] = useState<boolean>(false);
const [moveOrigin, setMoveOrigin] = useState<Placeable[]>([]);
const [mousePosX, setMousePosX] = useState<number>(0);
const [mousePosY, setMousePosY] = useState<number>(0);
const [hoveredBlock, setHoveredBlock] = useState<number | undefined>(undefined);
const [placementBlock, setPlacementBlock] = useState<Placeable | undefined>(undefined);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [anchorPosX, setAnchorPosX] = useState<number>(0);
const [anchorPosY, setAnchorPosY] = useState<number>(0);
const [ventType, setVentType] = useState<pond.ShaftType | undefined>(undefined);
//const [placeableType, setPlaceableType] = useState<pond.PlaceableType | undefined>(undefined);
const [direction, setDirection] = useState<number>(0);
const [scale, setScale] = useState(1);
const [interactionDevice, setInteractionDevice] = useState<Device>();
const [interactionComponent, setInteractionComponent] = useState<Component>();
const [interactionSensor, setInteractionSensor] = useState<Sensor>();
const [isAddInteractionOpen, setIsAddInteractionOpen] = useState(false);
const mineAPI = useMineAPI();
const theme = useTheme();
const [componentMeasurements, setComponentMeasurements] = useState<
undefined | Map<string, UnitMeasurement[]>
>(undefined);
const [{ user }] = useGlobalState();
const save = () => {
let settings = pond.MineSettings.create();
props.devices.forEach(device => {
if (device.settings) settings.devices.push(device.settings?.deviceId);
});
mineAPI.addMine(settings).then((resp: any) => {
console.log(resp);
});
};
useEffect(() => {
setWidth(props.width ? props.width : 800);
setHeight(props.height ? props.height : 600);
}, [props.width, props.height]);
useEffect(() => {
setMode(props.mode ? props.mode : Mode.NORMAL);
}, [props.mode]);
useEffect(() => {
if (selected.length > 0) {
selected[0].setDirection(direction);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [direction]);
useEffect(() => {
let compMeasurements: Map<string, UnitMeasurement[]> = new Map();
components.forEach(comp => {
let measurements: UnitMeasurement[] = [];
comp.lastMeasurement.forEach(m => {
let um = UnitMeasurement.create(m, user);
measurements.push(um);
});
compMeasurements.set(comp.key(), measurements);
});
setComponentMeasurements(compMeasurements);
}, [components, user]);
useEffect(() => {
if (
componentMeasurements === undefined ||
componentPreferences.size < 1 ||
components.length < 1 ||
!user ||
componentDevices.size < 1 ||
componentMeasurements.size < 1
)
return;
let sensors: Sensor[] = [];
components.forEach(comp => {
if (componentPreferences.get(comp.key())) {
let p = componentPreferences.get(comp.key());
if (p?.sensors)
p?.sensors.forEach((s, i) => {
if (s.enabled) {
let id = componentDevices.get(comp.key());
let device = devices.find(dev => dev.settings?.deviceId === id);
let measurements = componentMeasurements.get(comp.key());
if (measurements !== undefined)
sensors.push(Sensor.create(comp, Device.create(device), s, measurements, i, user));
}
});
}
});
setSensors(sensors);
}, [
components,
componentPreferences,
setSensors,
user,
devices,
componentDevices,
componentMeasurements
]);
const getOffsetX = (e: any): number => {
if (e.offsetParent) {
return e.offsetLeft + getOffsetX(e.offsetParent);
}
return e.offsetLeft;
};
const getAbsoluteX = (e: any): number => {
return e.pageX - getOffsetX(e.currentTarget);
};
const getX = (e: any): number => {
let offset = getOffsetX(e.currentTarget);
offset = offset - cameraOffsetX * scale;
return (e.pageX - offset) / scale;
};
const getOffsetY = (e: any): number => {
if (e.offsetParent) {
return e.offsetTop + getOffsetY(e.offsetParent);
}
return e.offsetTop;
};
const getAbsoluteY = (e: any): number => {
return e.pageY - getOffsetY(e.currentTarget);
};
const getY = (e: any): number => {
let offset = getOffsetY(e.currentTarget);
offset = offset - cameraOffsetY * scale;
//offset = offset + mouseOffsetY;
return (e.pageY - offset) / scale;
};
const addPlaceable = (p: Placeable) => {
let r = blocks;
saveState();
if (selected.length > 0) {
let xy = selected[0].getEndXAndY();
p.y = xy.y;
p.x = xy.x;
p.setAngle(selected[0].getEndAngle(), selected[0].getEndAngle());
} else {
p.x = clickX;
p.y = clickY;
}
selected[0] = p;
r.push(p);
setBlocks([...r]);
};
useEffect(() => {
props.addBlockRef.current = addPlaceable;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selected, addPlaceable, blocks]);
const add = (x?: number, y?: number) => {
let r = blocks;
saveState();
let p = VentShaft.create();
if (x && y) {
let v = VentCorner.create(x, y, 32, 0);
setBlocks([...r]);
return v;
} else if (Boolean(anchorEl)) {
p.x = anchorPosX + cameraOffsetX;
p.y = anchorPosY + cameraOffsetY;
p.angle = 0;
} else {
p.x = p.x + cameraOffsetX;
p.y = p.y + cameraOffsetY;
}
r.push(p);
setBlocks([...r]);
//return p;
};
const saveState = () => {
let u = undoStates;
let newBlocks: Placeable[] = [];
blocks.forEach(block => {
let b = block.clone();
newBlocks.push(b);
});
u.push(newBlocks);
setRedoStates([]);
};
const click = (e: any) => {
if (hoveredBlock !== undefined) {
setMode(hoveredBlock);
setMouseDown(false);
return;
}
selected.forEach(s => {
s.selected = false;
});
let hit = false;
let r = blocks;
let x = getX(e);
let y = getY(e);
setClickX(x);
setClickY(y);
let newBlocks: Placeable[] = [];
if (mode === Mode.NORMAL) {
r.forEach(block => {
newBlocks.push(block.clone());
if (block.clickCheck(x, y, scale)) {
hit = true;
setSelected([block]);
}
});
sensors.forEach(sensor => {
if (sensor.clickCheck(x, y, scale)) {
hit = true;
setSelected([sensor]);
}
});
}
if (!hit) setSelected([]);
if (e.button === 2) return;
if (hit) setMoveOrigin(newBlocks);
if (mode === Mode.ADD) {
add();
} else if (mode === Mode.DELETE) {
deleteBlock(x, y, true);
}
setMouseDown(true);
};
const mouseUp = (e: any) => {
setMouseDown(false);
if (mode === Mode.NORMAL && selected !== undefined) {
let state = [...moveOrigin];
let states = [...undoStates];
states.push(state);
setUndoStates([...states]);
setRedoStates([]);
selected.forEach(placeable => {
if (placeable.type === pond.PlaceableType.PLACEABLE_TYPE_SENSOR) {
let s = placeable as Sensor;
let prefs = componentPreferences.get(s.getComponent().key());
if (prefs?.sensors[s.index]) {
prefs.sensors[s.index].x = s.x;
prefs.sensors[s.index].y = s.y;
componentPreferences.set(s.getComponent().key(), prefs);
setComponentPreferences(new Map(componentPreferences));
}
}
});
}
//setSelected(undefined);
};
const keyDown = (e: any) => {
if (e.key === "1") {
setMode(Mode.NORMAL);
} else if (e.key === "2") {
setMode(Mode.MOVE);
} else if (e.key === "3") {
setMode(Mode.ADD);
} else if (e.key === "4") {
setMode(Mode.DELETE);
} else if (e.key === "Control") {
setCtrl(true);
} else if (e.key === "z" && ctrl) {
undo();
} else if (e.key === "Z" && ctrl) {
redo();
} else if (e.key === "s" && ctrl) {
save();
} else if (e.key === "v") {
if (viewMode === ViewMode.NORMAL) {
setViewMode(ViewMode.INFO);
} else {
setViewMode(ViewMode.NORMAL);
}
}
};
const keyUp = (e: any) => {
if (e.key === "Control") {
setCtrl(false);
}
};
const deleteBlock = (x: number, y: number, save?: boolean) => {
if (save) saveState();
let b = [...blocks];
blocks.forEach((block, index) => {
if (block.clickCheck(x + cameraOffsetX, y + cameraOffsetY, scale)) {
b.splice(index, 1);
return;
}
});
setBlocks(b);
};
const drag = (e: any) => {
setHoveredBlock(undefined);
let x = getAbsoluteX(e);
let y = getAbsoluteY(e);
setMousePosX(x);
setMousePosY(y);
if (!mouseDown) {
// Set position for placement block
if (mode === Mode.ADD && placementBlock) {
let p = placementBlock.clone() as Placeable;
p.x = x; // - p.width() / 2;
p.y = y; // - p.height() / 2;
setPlacementBlock(p);
}
} else {
// If the mouse is clicked down
if (mode === Mode.MOVE) {
setCameraOffsetX(clickX - x / scale);
setCameraOffsetY(clickY - y / scale);
} else if (selected.length > 0 && mode === Mode.NORMAL) {
selected[0].drag(getX(e), getY(e), scale);
blocks.forEach(block => {
if (block.x !== selected[0].x && block.y !== selected[0].y) {
selected[0].snap(block, 16);
}
});
} else if (mode === Mode.DELETE) {
deleteBlock(x, y);
}
}
};
const redo = () => {
if (redoStates.length < 1) return;
let rects = redoStates.pop();
let undos = undoStates;
if (rects) {
undos.push(rects);
setUndoStates([...undos]);
setBlocks(rects);
}
};
const undo = () => {
let states = undoStates;
let redo = redoStates;
let rects = states.pop();
if (rects) {
redo.push([...rects]);
setRedoStates([...redo]);
setUndoStates([...states]);
setBlocks(rects);
}
};
useEffect(() => {
props.saveRef.current = save;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [save]);
useEffect(() => {
props.undoRef.current = undo;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [undo]);
useEffect(() => {
props.redoRef.current = redo;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [redo]);
const wheel = (e: WheelEvent) => {
// Scroll down
if (e.deltaY > 0) {
let newScale = scale - 0.05;
if (newScale > 0) {
let mouseRatioX = mousePosX / width;
let mouseRatioY = mousePosY / height;
let newWidth = width / newScale;
let dx = (width - newWidth) * mouseRatioX;
let olddx = (width - width / scale) * mouseRatioX;
let newHeight = height / newScale;
let dy = (height - newHeight) * mouseRatioY;
let olddy = (height - height / scale) * mouseRatioY;
setScale(Math.round(newScale * 1000) / 1000);
setCameraOffsetX(cameraOffsetX - olddx + dx);
setCameraOffsetY(cameraOffsetY - olddy + dy);
}
}
// Scroll up
if (e.deltaY < 0) {
let mouseRatioX = mousePosX / width;
let mouseRatioY = mousePosY / height;
let newScale = scale + 0.05;
let newWidth = width / newScale;
let dx = (width - newWidth) * mouseRatioX;
let olddx = (width - width / scale) * mouseRatioX;
let newHeight = height / newScale;
let dy = (height - newHeight) * mouseRatioY;
let olddy = (height - height / scale) * mouseRatioY;
setScale(Math.round(newScale * 1000) / 1000);
setCameraOffsetX(cameraOffsetX - olddx + dx);
setCameraOffsetY(cameraOffsetY - olddy + dy);
}
// If the mouse is at the top, scroll mode selection
/*if (mousePosY < 100) {
// Scroll down
if (e.deltaY > 0) {
//if (mode === Mode.NORMAL) setMode(Mode.DELETE);
//else setMode(mode - 1);
console.log("scroll down")
let newScale = scale - 0.1
setScale(newScale)
}
// Scroll up
if (e.deltaY < 0) {
//if (mode === Mode.DELETE) setMode(Mode.NORMAL);
//else setMode(mode + 1);
console.log("scroll up")
let newScale = scale + 0.1
setScale(newScale)
}
}*/
};
const drawBlocks = (context: CanvasRenderingContext2D) => {
blocks.forEach((block, index) => {
if (block.draw) block.draw(context, cameraOffsetX, cameraOffsetY, scale);
});
selected.forEach(block => {
block.highlight(context, cameraOffsetX, cameraOffsetY, scale);
});
};
const drawSensors = (context: CanvasRenderingContext2D) => {
/*components.forEach(comp => {
let x = or(componentPreferences.get(comp.key())?.x, 10)
let y = or(componentPreferences.get(comp.key())?.y, 10)
context.arc(x - cameraOffsetX, y - cameraOffsetY, 25, 2 * Math.PI, 0, false)
context.fill()
})*/
sensors.forEach(sensor => {
sensor.draw(context, cameraOffsetX, cameraOffsetY, scale);
if (viewMode === ViewMode.INFO)
sensor.highlight(context, cameraOffsetX, cameraOffsetY, scale);
});
};
const draw = (context: CanvasRenderingContext2D) => {
context.clearRect(0, 0, width, height);
context.fillStyle = "rgba(150, 150, 255, 150)";
context.strokeStyle = "rgba(247, 202, 24, 1)";
drawBlocks(context);
drawSensors(context);
// Draw placement block if in add mode
if (mode === Mode.ADD) {
context.globalAlpha = 0.3;
if (placementBlock) placementBlock.draw(context);
context.globalAlpha = 1;
}
// Draw garbage can if in delete mode
if (mode === Mode.DELETE) {
let i = new Image();
i.src = "https://img.icons8.com/material-outlined/48/000000/delete-forever.png";
context.globalAlpha = 1;
context.drawImage(i, mousePosX - 16, mousePosY - 16, 32, 32);
}
};
const contextMenu = (e: Event) => {
e.preventDefault();
setAnchorPosX(getAbsoluteX(e));
setAnchorPosY(getAbsoluteY(e));
if (!anchorEl) setAnchorEl(e.currentTarget as any);
};
/*const isTBreak = (p: Placeable) => {
if (p.type === pond.PlaceableType.PLACEABLE_TYPE_SHAFT) {
if (p.subtype === pond.ShaftType.SHAFT_TYPE_T_BREAK) {
return true;
}
}
return false;
};*/
const isDefaultShaft = (p: Placeable) => {
if (p.type === pond.PlaceableType.PLACEABLE_TYPE_SHAFT) {
if (p.subtype === pond.ShaftType.SHAFT_TYPE_DEFAULT) {
return true;
}
}
return false;
};
return (
<div
style={{
cursor:
mode === Mode.MOVE
? mouseDown
? "grabbing"
: "grab"
: mode === Mode.ADD || mode === Mode.DELETE
? "none"
: "default"
}}>
<div style={{ position: "relative" }}>
<div
style={{
borderRadius: 4,
backgroundColor: "rgba(255,255,255,0.1)",
position: "absolute",
bottom: 0,
right: 0,
margin: theme.spacing(2)
}}>
<Zoom scale={scale} setScale={setScale} />
</div>
{loading && (
<div
style={{
position: "absolute",
margin: theme.spacing(8)
}}>
<CircularProgress
size={theme.spacing(8)}
//style={{ margin: theme.spacing(6) }}
/>
<Typography>Loading...</Typography>
</div>
)}
<div
style={{
//borderRadius: 4,
//backgroundColor: "rgba(255,255,255,0.1)",
position: "absolute",
bottom: 0,
right: theme.spacing(19),
margin: theme.spacing(2)
}}>
<ToggleButton
value={viewMode}
selected={viewMode === ViewMode.INFO}
onChange={() => {
if (viewMode === ViewMode.INFO) {
setViewMode(ViewMode.NORMAL);
} else {
setViewMode(ViewMode.INFO);
}
}}>
<InfoRounded />
</ToggleButton>
</div>
<Canvas
draw={draw}
height={height}
width={width}
onMouseDown={click}
onMouseMove={drag}
onMouseUp={mouseUp}
onKeyDown={keyDown}
onKeyUp={keyUp}
onWheel={wheel}
onContextMenu={contextMenu}
/>
</div>
<VentDialog type={ventType} setType={setVentType} setDirection={setDirection} />
{interactionDevice && interactionComponent && (
<InteractionSettings
device={interactionDevice}
components={components}
initialComponent={interactionComponent}
mode="add"
isDialogOpen={isAddInteractionOpen}
closeDialogCallback={() => setIsAddInteractionOpen(false)}
refreshCallback={() => {}}
canEdit={true}
sensor={interactionSensor}
/>
)}
<Menu
id="groupMenu"
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={() => setAnchorEl(null)}
// getContentAnchorEl={null}
anchorOrigin={{ vertical: anchorPosY, horizontal: anchorPosX }}
keepMounted
onContextMenu={(e: any) => {
e.preventDefault();
setAnchorEl(null);
}}
disableAutoFocusItem>
{selected.length > 0 && selected[0].type === pond.PlaceableType.PLACEABLE_TYPE_SHAFT && (
<MenuItem
dense
divider
onClick={() => {
setAnchorEl(null);
setVentType(selected[0].subtype);
}}
>
<ListItemIcon>
<Info />
</ListItemIcon>
<ListItemText primary="Edit Shaft" />
</MenuItem>
)}
{selected.length > 0 && selected[0].type === pond.PlaceableType.PLACEABLE_TYPE_SENSOR && (
<MenuItem
dense
divider
onClick={() => {
let s = selected[0] as Sensor;
setAnchorEl(null);
//setVentType(selected[0].subtype);
setInteractionComponent(s.getComponent());
setInteractionDevice(s.getDevice());
setInteractionSensor(s);
setIsAddInteractionOpen(true);
}}
>
<ListItemIcon>
<AddCircle />
</ListItemIcon>
<ListItemText primary={"Add Interaction"} />
</MenuItem>
)}
{!(selected.length > 0 && isDefaultShaft(selected[0])) && (
<MenuItem
dense
onClick={() => {
setAnchorEl(null);
let v = VentShaft.create();
addPlaceable(v);
}}
>
<ListItemIcon>
<AddCircle />
</ListItemIcon>
<ListItemText primary="Add Straight Shaft" />
</MenuItem>
)}
<MenuItem
dense
onClick={() => {
setAnchorEl(null);
let v = VentCorner.create();
addPlaceable(v);
}}
>
<ListItemIcon>
<AddCircle />
</ListItemIcon>
<ListItemText primary="Add Corner" />
</MenuItem>
<MenuItem
dense
onClick={() => {
let v = VentTBreak.create();
addPlaceable(v);
setAnchorEl(null);
}}
>
<ListItemIcon>
<AddCircle />
</ListItemIcon>
<ListItemText primary="Add T-Break" />
</MenuItem>
{selected.length > 0 &&
(selected[0].subtype === pond.ShaftType.SHAFT_TYPE_CORNER ||
selected[0].subtype === pond.ShaftType.SHAFT_TYPE_T_BREAK) && (
<MenuItem
dense
onClick={() => {
selected[0].setDirection(0);
setAnchorEl(null);
}}
>
<ListItemIcon>
<ClockwiseIcon />
</ListItemIcon>
<ListItemText primary="Clockwise" />
</MenuItem>
)}
{selected.length > 0 &&
(selected[0].subtype === pond.ShaftType.SHAFT_TYPE_CORNER ||
selected[0].subtype === pond.ShaftType.SHAFT_TYPE_T_BREAK) && (
<MenuItem
dense
onClick={() => {
selected[0].setDirection(1);
setAnchorEl(null);
}}
>
<ListItemIcon>
<CounterclockwiseIcon />
</ListItemIcon>
<ListItemText primary="Counterclockwise" />
</MenuItem>
)}
{selected.length > 0 && (
<MenuItem
style={{ color: "red" }}
dense
onClick={() => {
saveState();
let b = [...blocks];
blocks.forEach((block, index) => {
if (
selected[0].x === block.x &&
selected[0].y === block.y &&
block.angle === selected[0].angle &&
block.type === selected[0].type
) {
b.splice(index, 1);
return;
}
});
setAnchorEl(null);
setSelected([]);
setBlocks(b);
}}
>
<ListItemIcon>
<DeleteIcon />
</ListItemIcon>
<ListItemText primary="Delete" />
</MenuItem>
)}
{selected.length > 0 && false && (
<MenuItem
dense
onClick={() => {
setAnchorEl(null);
let xy = selected[0].getEndXAndY();
let x = xy.x;
let y = xy.y;
//let startBlock = selected[0].clone();
let block = add(x, y);
if (block) {
//block.circle.selected = true;
//block.startSnap = startBlock;
//setSelected([block]);
}
//setMouseDown(true);
}}
>
<ListItemIcon>
<AddCircle />
</ListItemIcon>
<ListItemText primary="Extend" />
</MenuItem>
)}
</Menu>
</div>
);
}