first commit
This commit is contained in:
@@ -0,0 +1,526 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
import { useSubModuleStore } from "../../../../store/useModuleStore";
|
||||
import { useSelectedAction, useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
|
||||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||
import { handleAddEventToProduct } from "../points/functions/handleAddEventToProduct";
|
||||
import { QuadraticBezierLine } from "@react-three/drei";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { useDeleteTool } from "../../../../store/builder/store";
|
||||
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";
|
||||
import { ArrowOnQuadraticBezier, Arrows } from "../arrows/arrows";
|
||||
import { useProductContext } from "../../products/productContext";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
interface ConnectionLine {
|
||||
id: string;
|
||||
startPointUuid: string;
|
||||
endPointUuid: string;
|
||||
trigger: TriggerSchema;
|
||||
}
|
||||
|
||||
function TriggerConnector() {
|
||||
const { gl, raycaster, scene, pointer, camera } = useThree();
|
||||
const { subModule } = useSubModuleStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { products, getPointByUuid, getIsEventInProduct, getActionByUuid, addTrigger, removeTrigger, addEvent, getEventByModelUuid, getPointUuidByActionUuid, getProductById } = useProductStore();
|
||||
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const [hoveredLineKey, setHoveredLineKey] = useState<string | null>(null);
|
||||
const groupRefs = useRef<Record<string, any>>({});
|
||||
const [helperlineColor, setHelperLineColor] = useState<string>("red");
|
||||
const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3; end: THREE.Vector3; mid: THREE.Vector3; } | null>(null);
|
||||
const { deleteTool } = useDeleteTool();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { selectedAction } = useSelectedAction();
|
||||
const { projectId } = useParams();
|
||||
|
||||
const [firstSelectedPoint, setFirstSelectedPoint] = useState<{
|
||||
productUuid: string;
|
||||
modelUuid: string;
|
||||
pointUuid: string;
|
||||
actionUuid?: string;
|
||||
} | null>(null);
|
||||
|
||||
const [connections, setConnections] = useState<ConnectionLine[]>([]);
|
||||
|
||||
const updateBackend = (
|
||||
productName: string,
|
||||
productUuid: string,
|
||||
projectId: string,
|
||||
eventData: EventsSchema
|
||||
) => {
|
||||
upsertProductOrEventApi({
|
||||
productName: productName,
|
||||
productUuid: productUuid,
|
||||
projectId: projectId,
|
||||
eventDatas: eventData
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const newConnections: ConnectionLine[] = [];
|
||||
|
||||
const product = getProductById(selectedProduct.productUuid);
|
||||
if (!product || products.length === 0) return;
|
||||
|
||||
product.eventDatas.forEach(event => {
|
||||
// Handle Conveyor points
|
||||
if (event.type === "transfer" && 'points' in event) {
|
||||
event.points.forEach(point => {
|
||||
if (point.action?.triggers) {
|
||||
point.action.triggers.forEach(trigger => {
|
||||
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
|
||||
newConnections.push({
|
||||
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
|
||||
startPointUuid: point.uuid,
|
||||
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||
trigger
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// Handle Vehicle point
|
||||
else if (event.type === "vehicle" && 'point' in event) {
|
||||
const point = event.point;
|
||||
if (point.action?.triggers) {
|
||||
point.action.triggers.forEach(trigger => {
|
||||
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
|
||||
newConnections.push({
|
||||
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
|
||||
startPointUuid: point.uuid,
|
||||
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||
trigger
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// Handle Robotic Arm points
|
||||
else if (event.type === "roboticArm" && 'point' in event) {
|
||||
const point = event.point;
|
||||
point.actions?.forEach(action => {
|
||||
action.triggers?.forEach(trigger => {
|
||||
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
|
||||
newConnections.push({
|
||||
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
|
||||
startPointUuid: point.uuid,
|
||||
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||
trigger
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// Handle Machine point
|
||||
else if (event.type === "machine" && 'point' in event) {
|
||||
const point = event.point;
|
||||
if (point.action?.triggers) {
|
||||
point.action.triggers.forEach(trigger => {
|
||||
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
|
||||
newConnections.push({
|
||||
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
|
||||
startPointUuid: point.uuid,
|
||||
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||
trigger
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// Handle StorageUnit point
|
||||
else if (event.type === "storageUnit" && 'point' in event) {
|
||||
const point = event.point;
|
||||
if (point.action?.triggers) {
|
||||
point.action.triggers.forEach(trigger => {
|
||||
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
|
||||
newConnections.push({
|
||||
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
|
||||
startPointUuid: point.uuid,
|
||||
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||
trigger
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setConnections(newConnections);
|
||||
}, [products, selectedProduct.productUuid]);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
let drag = false;
|
||||
let isRightMouseDown = false;
|
||||
|
||||
const onMouseDown = (evt: MouseEvent) => {
|
||||
if (selectedAsset) {
|
||||
clearSelectedAsset();
|
||||
}
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown = true;
|
||||
drag = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = (evt: MouseEvent) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isRightMouseDown) {
|
||||
drag = true;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRightClick = (evt: MouseEvent) => {
|
||||
if (drag) return;
|
||||
evt.preventDefault();
|
||||
|
||||
const intersects = raycaster
|
||||
.intersectObjects(scene.children, true)
|
||||
.filter(
|
||||
(intersect) =>
|
||||
intersect.object.name === ('Event-Sphere')
|
||||
);
|
||||
if (intersects.length === 0) {
|
||||
setFirstSelectedPoint(null);
|
||||
return;
|
||||
};
|
||||
|
||||
const currentObject = intersects[0].object;
|
||||
if (!currentObject || currentObject.name !== 'Event-Sphere') {
|
||||
setFirstSelectedPoint(null);
|
||||
return;
|
||||
};
|
||||
|
||||
const modelUuid = currentObject.userData.modelUuid;
|
||||
const pointUuid = currentObject.userData.pointUuid;
|
||||
|
||||
if (firstSelectedPoint && firstSelectedPoint.pointUuid === pointUuid) {
|
||||
setFirstSelectedPoint(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedProduct && getIsEventInProduct(selectedProduct.productUuid, modelUuid)) {
|
||||
|
||||
const point = getPointByUuid(
|
||||
selectedProduct.productUuid,
|
||||
modelUuid,
|
||||
pointUuid
|
||||
);
|
||||
|
||||
const event = getEventByModelUuid(selectedProduct.productUuid, modelUuid);
|
||||
const clickedPointUuid = getPointUuidByActionUuid(selectedProduct.productUuid, selectedAction.actionId || '');
|
||||
|
||||
if (!point || !event) {
|
||||
setFirstSelectedPoint(null);
|
||||
return;
|
||||
};
|
||||
|
||||
let actionUuid: string | undefined;
|
||||
if ('action' in point && point.action) {
|
||||
actionUuid = point.action.actionUuid;
|
||||
} else if ('actions' in point && point.actions.length === 1) {
|
||||
actionUuid = point.actions[0].actionUuid;
|
||||
}
|
||||
|
||||
if (!firstSelectedPoint) {
|
||||
if (point.uuid !== clickedPointUuid) return;
|
||||
setFirstSelectedPoint({
|
||||
productUuid: selectedProduct.productUuid,
|
||||
modelUuid,
|
||||
pointUuid,
|
||||
actionUuid: selectedAction.actionId || ''
|
||||
});
|
||||
} else {
|
||||
const trigger: TriggerSchema = {
|
||||
triggerUuid: THREE.MathUtils.generateUUID(),
|
||||
triggerName: `Trigger ${firstSelectedPoint.pointUuid.slice(0, 4)} → ${pointUuid.slice(0, 4)}`,
|
||||
triggerType: "onComplete",
|
||||
delay: 0,
|
||||
triggeredAsset: {
|
||||
triggeredModel: {
|
||||
modelName: event.modelName || 'Unknown',
|
||||
modelUuid: modelUuid
|
||||
},
|
||||
triggeredPoint: {
|
||||
pointName: 'Point',
|
||||
pointUuid: point.uuid
|
||||
},
|
||||
triggeredAction: actionUuid ? {
|
||||
actionName: getActionByUuid(selectedProduct.productUuid, actionUuid)?.actionName || 'Action',
|
||||
actionUuid: actionUuid
|
||||
} : null
|
||||
}
|
||||
};
|
||||
|
||||
if (firstSelectedPoint.actionUuid) {
|
||||
const event = addTrigger(selectedProduct.productUuid, firstSelectedPoint.actionUuid, trigger);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productUuid,
|
||||
projectId || '',
|
||||
event
|
||||
);
|
||||
}
|
||||
}
|
||||
setFirstSelectedPoint(null);
|
||||
}
|
||||
} else if (!getIsEventInProduct(selectedProduct.productUuid, modelUuid) && firstSelectedPoint) {
|
||||
handleAddEventToProduct({
|
||||
event: useEventsStore.getState().getEventByModelUuid(modelUuid),
|
||||
addEvent,
|
||||
selectedProduct,
|
||||
projectId: projectId || ''
|
||||
})
|
||||
|
||||
const point = getPointByUuid(
|
||||
selectedProduct.productUuid,
|
||||
modelUuid,
|
||||
pointUuid
|
||||
);
|
||||
|
||||
const event = getEventByModelUuid(selectedProduct.productUuid, modelUuid);
|
||||
|
||||
if (!point || !event) {
|
||||
setFirstSelectedPoint(null);
|
||||
return;
|
||||
};
|
||||
|
||||
let actionUuid: string | undefined;
|
||||
if ('action' in point && point.action) {
|
||||
actionUuid = point.action.actionUuid;
|
||||
} else if ('actions' in point && point.actions.length === 1) {
|
||||
actionUuid = point.actions[0].actionUuid;
|
||||
}
|
||||
|
||||
const trigger: TriggerSchema = {
|
||||
triggerUuid: THREE.MathUtils.generateUUID(),
|
||||
triggerName: `Trigger ${firstSelectedPoint.pointUuid.slice(0, 4)} → ${pointUuid.slice(0, 4)}`,
|
||||
triggerType: "onComplete",
|
||||
delay: 0,
|
||||
triggeredAsset: {
|
||||
triggeredModel: {
|
||||
modelName: event.modelName || 'Unknown',
|
||||
modelUuid: modelUuid
|
||||
},
|
||||
triggeredPoint: {
|
||||
pointName: 'Point',
|
||||
pointUuid: point.uuid
|
||||
},
|
||||
triggeredAction: actionUuid ? {
|
||||
actionName: getActionByUuid(selectedProduct.productUuid, actionUuid)?.actionName || 'Action',
|
||||
actionUuid: actionUuid
|
||||
} : null
|
||||
}
|
||||
};
|
||||
|
||||
if (firstSelectedPoint.actionUuid) {
|
||||
const event = addTrigger(selectedProduct.productUuid, firstSelectedPoint.actionUuid, trigger);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productUuid,
|
||||
projectId || '',
|
||||
event
|
||||
);
|
||||
}
|
||||
}
|
||||
setFirstSelectedPoint(null);
|
||||
} else if (firstSelectedPoint) {
|
||||
setFirstSelectedPoint(null);
|
||||
}
|
||||
};
|
||||
|
||||
if (subModule === 'mechanics' && !deleteTool && selectedAction.actionId && selectedAction.actionName) {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
canvasElement.addEventListener('contextmenu', handleRightClick);
|
||||
} else {
|
||||
setFirstSelectedPoint(null);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener('contextmenu', handleRightClick);
|
||||
};
|
||||
|
||||
}, [gl, subModule, selectedProduct, firstSelectedPoint, deleteTool, selectedAction]);
|
||||
|
||||
useFrame(() => {
|
||||
if (firstSelectedPoint) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster.intersectObjects(scene.children, true).filter(
|
||||
(intersect) =>
|
||||
!intersect.object.name.includes("Roof") &&
|
||||
!intersect.object.name.includes("MeasurementReference") &&
|
||||
!intersect.object.name.includes("agv-collider") &&
|
||||
!intersect.object.name.includes("zonePlane") &&
|
||||
!intersect.object.name.includes("SelectionGroup") &&
|
||||
!intersect.object.name.includes("selectionAssetGroup") &&
|
||||
!intersect.object.name.includes("SelectionGroupBoundingBoxLine") &&
|
||||
!intersect.object.name.includes("SelectionGroupBoundingBox") &&
|
||||
!intersect.object.name.includes("SelectionGroupBoundingLine") &&
|
||||
intersect.object.type !== "GridHelper" &&
|
||||
!intersect.object.name.includes("ArrowWithTube") &&
|
||||
!intersect.object.parent?.name.includes("Zone") &&
|
||||
intersect.object.type !== "Line2"
|
||||
);
|
||||
|
||||
let point: THREE.Vector3 | null = null;
|
||||
|
||||
if (intersects.length > 0) {
|
||||
point = intersects[0].point;
|
||||
if (point.y < 0.05) {
|
||||
point = new THREE.Vector3(point.x, 0.05, point.z);
|
||||
}
|
||||
}
|
||||
|
||||
const sphereIntersects = raycaster.intersectObjects(scene.children, true).filter((intersect) => intersect.object.name === ('Event-Sphere'));
|
||||
|
||||
if (sphereIntersects.length > 0 && sphereIntersects[0].object.uuid === firstSelectedPoint.pointUuid) {
|
||||
setHelperLineColor('red');
|
||||
setCurrentLine(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const startPoint = getWorldPositionFromScene(firstSelectedPoint.pointUuid);
|
||||
|
||||
if (point && startPoint) {
|
||||
if (sphereIntersects.length > 0) {
|
||||
point = sphereIntersects[0].object.getWorldPosition(new THREE.Vector3());
|
||||
}
|
||||
const distance = startPoint.distanceTo(point);
|
||||
const heightFactor = Math.max(0.5, distance * 0.2);
|
||||
const midPoint = new THREE.Vector3(
|
||||
(startPoint.x + point.x) / 2,
|
||||
Math.max(startPoint.y, point.y) + heightFactor,
|
||||
(startPoint.z + point.z) / 2
|
||||
);
|
||||
|
||||
const endPoint = point;
|
||||
|
||||
setCurrentLine({
|
||||
start: startPoint,
|
||||
mid: midPoint,
|
||||
end: endPoint,
|
||||
});
|
||||
|
||||
setHelperLineColor(sphereIntersects.length > 0 ? "#6cf542" : "red");
|
||||
} else {
|
||||
setCurrentLine(null);
|
||||
}
|
||||
} else {
|
||||
setCurrentLine(null);
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
const getWorldPositionFromScene = (pointUuid: string): THREE.Vector3 | null => {
|
||||
const pointObj = scene.getObjectByProperty("uuid", pointUuid);
|
||||
if (!pointObj) return null;
|
||||
|
||||
const worldPosition = new THREE.Vector3();
|
||||
pointObj.getWorldPosition(worldPosition);
|
||||
return worldPosition;
|
||||
};
|
||||
|
||||
const removeConnection = (connection: ConnectionLine) => {
|
||||
if (connection.trigger.triggerUuid) {
|
||||
const event = removeTrigger(selectedProduct.productUuid, connection.trigger.triggerUuid);
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productUuid,
|
||||
projectId || '',
|
||||
event
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<group name="simulationConnectionGroup" visible={!isPlaying}>
|
||||
{connections.map((connection) => {
|
||||
const startPoint = getWorldPositionFromScene(connection.startPointUuid);
|
||||
const endPoint = getWorldPositionFromScene(connection.endPointUuid);
|
||||
|
||||
if (!startPoint || !endPoint) return null;
|
||||
|
||||
const distance = startPoint.distanceTo(endPoint);
|
||||
const heightFactor = Math.max(0.5, distance * 0.2);
|
||||
const midPoint = new THREE.Vector3(
|
||||
(startPoint.x + endPoint.x) / 2,
|
||||
Math.max(startPoint.y, endPoint.y) + heightFactor,
|
||||
(startPoint.z + endPoint.z) / 2
|
||||
);
|
||||
|
||||
return (
|
||||
<QuadraticBezierLine
|
||||
key={connection.id}
|
||||
ref={(el) => (groupRefs.current[connection.id] = el!)}
|
||||
start={startPoint.toArray()}
|
||||
end={endPoint.toArray()}
|
||||
mid={midPoint.toArray()}
|
||||
color={deleteTool && hoveredLineKey === connection.id ? "red" : "#42a5f5"}
|
||||
lineWidth={4}
|
||||
dashed={deleteTool && hoveredLineKey === connection.id ? false : true}
|
||||
dashSize={0.75}
|
||||
dashScale={20}
|
||||
onPointerOver={() => setHoveredLineKey(connection.id)}
|
||||
onPointerOut={() => setHoveredLineKey(null)}
|
||||
onClick={() => {
|
||||
if (deleteTool) {
|
||||
setHoveredLineKey(null);
|
||||
setCurrentLine(null);
|
||||
removeConnection(connection);
|
||||
}
|
||||
}}
|
||||
userData={connection.trigger}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
<Arrows connections={connections} />
|
||||
|
||||
{currentLine && (
|
||||
<>
|
||||
<QuadraticBezierLine
|
||||
start={currentLine.start.toArray()}
|
||||
end={currentLine.end.toArray()}
|
||||
mid={currentLine.mid.toArray()}
|
||||
color={helperlineColor}
|
||||
lineWidth={4}
|
||||
dashed
|
||||
dashSize={1}
|
||||
dashScale={20}
|
||||
/>
|
||||
<ArrowOnQuadraticBezier
|
||||
start={currentLine.start.toArray()}
|
||||
mid={currentLine.mid.toArray()}
|
||||
end={currentLine.end.toArray()}
|
||||
color={helperlineColor}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default TriggerConnector;
|
||||
Reference in New Issue
Block a user