Refactor API imports and restructure services
- Updated import paths for `upsertProductOrEventApi`, `deleteEventDataApi`, `deleteProductApi`, `getProductApi`, `getAllProductsApi`, and `renameProductApi` to point to the new `products` directory. - Removed old API files for `UpsertProductOrEventApi`, `deleteEventDataApi`, `deleteProductApi`, `getProductApi`, `getAllProductsApi`, and `renameProductApi`. - Introduced new implementations for the above APIs in the `products` directory. - Added `MaterialCollisionDetector` component to handle material collision detection using a web worker. - Updated various components to utilize the new API structure and ensure proper functionality.
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
||||
useSelectedProduct,
|
||||
} from "../../../../../../store/simulation/useSimulationStore";
|
||||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
|
||||
interface ActionsListProps {
|
||||
selectedPointData: any;
|
||||
|
||||
@@ -11,7 +11,7 @@ import Trigger from "../trigger/Trigger";
|
||||
import { useSelectedAction, useSelectedEventData, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore";
|
||||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||
import ActionsList from "../components/ActionsList";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
|
||||
function ConveyorMechanics() {
|
||||
const [activeOption, setActiveOption] = useState<"default" | "spawn" | "swap" | "delay" | "despawn">("default");
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useSelectedAction, useSelectedEventData, useSelectedProduct } from "../
|
||||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||
import ProcessAction from "../actions/ProcessAction";
|
||||
import ActionsList from "../components/ActionsList";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
|
||||
function MachineMechanics() {
|
||||
const [activeOption, setActiveOption] = useState<"default" | "process">("default");
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||
import PickAndPlaceAction from "../actions/PickAndPlaceAction";
|
||||
import ActionsList from "../components/ActionsList";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
|
||||
function RoboticArmMechanics() {
|
||||
const [activeOption, setActiveOption] = useState<"default" | "pickAndPlace">(
|
||||
|
||||
@@ -4,7 +4,7 @@ import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
|
||||
import Trigger from "../trigger/Trigger";
|
||||
import StorageAction from "../actions/StorageAction";
|
||||
import ActionsList from "../components/ActionsList";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||
import { useStorageUnitStore } from "../../../../../../store/simulation/useStorageUnitStore";
|
||||
import { useSelectedAction, useSelectedEventData, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore";
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||
import TravelAction from "../actions/TravelAction";
|
||||
import ActionsList from "../components/ActionsList";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { useVehicleStore } from "../../../../../../store/simulation/useVehicleStore";
|
||||
|
||||
function VehicleMechanics() {
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
useSelectedAction,
|
||||
useSelectedProduct,
|
||||
} from "../../../../../../store/simulation/useSimulationStore";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
|
||||
type TriggerProps = {
|
||||
selectedPointData?: PointsScheme | undefined;
|
||||
|
||||
@@ -17,10 +17,10 @@ import RenderOverlay from "../../../templates/Overlay";
|
||||
import EditWidgetOption from "../../../ui/menu/EditWidgetOption";
|
||||
import { handleAddEventToProduct } from "../../../../modules/simulation/events/points/functions/handleAddEventToProduct";
|
||||
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||
import { deleteEventDataApi } from "../../../../services/simulation/deleteEventDataApi";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { deleteProductApi } from "../../../../services/simulation/deleteProductApi";
|
||||
import { renameProductApi } from "../../../../services/simulation/renameProductApi";
|
||||
import { deleteEventDataApi } from "../../../../services/simulation/products/deleteEventDataApi";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { deleteProductApi } from "../../../../services/simulation/products/deleteProductApi";
|
||||
import { renameProductApi } from "../../../../services/simulation/products/renameProductApi";
|
||||
import { determineExecutionMachineSequences } from "../../../../modules/simulation/simulator/functions/determineExecutionMachineSequences";
|
||||
import ComparePopUp from "../../../ui/compareVersion/Compare";
|
||||
import {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifie
|
||||
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { snapControls } from "../../../../utils/handleSnap";
|
||||
import DistanceFindingControls from "./distanceFindingControls";
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as Types from "../../../../types/world/worldTypes";
|
||||
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
|
||||
function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) {
|
||||
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
|
||||
|
||||
@@ -9,7 +9,7 @@ import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifie
|
||||
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
|
||||
|
||||
export default function TransformControl() {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModi
|
||||
import { useSelectedEventSphere, useSelectedEventData, } from "../../../../../store/simulation/useSimulationStore";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore";
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
|
||||
function PointsCreator() {
|
||||
const { gl, raycaster, scene, pointer, camera } = useThree();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
|
||||
interface HandleAddEventToProductParams {
|
||||
event: EventsSchema | undefined;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||
import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
|
||||
import { handleAddEventToProduct } from "../points/functions/handleAddEventToProduct";
|
||||
import { QuadraticBezierLine } from "@react-three/drei";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { useDeleteTool } from "../../../../store/builder/store";
|
||||
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";
|
||||
import { ArrowOnQuadraticBezier, Arrows } from "../arrows/arrows";
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useMaterialStore } from '../../../../store/simulation/useMaterialStore';
|
||||
import { useThree, useFrame } from '@react-three/fiber';
|
||||
import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore';
|
||||
import { CameraControls } from '@react-three/drei';
|
||||
|
||||
const collisionWorker = new Worker(new URL('../../../../services/simulation/webWorkers/collisionWorker.ts', import.meta.url));
|
||||
|
||||
export default function MaterialCollisionDetector() {
|
||||
const { materials } = useMaterialStore();
|
||||
const { scene, controls, camera } = useThree();
|
||||
const positionsRef = useRef<Record<string, THREE.Vector3>>({});
|
||||
const sizesRef = useRef<Record<string, number>>({});
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { isPaused, setIsPaused } = usePauseButtonStore();
|
||||
const { isReset } = useResetButtonStore();
|
||||
const collisionStateRef = useRef<Record<string, boolean>>({});
|
||||
|
||||
useEffect(() => {
|
||||
if (isReset) {
|
||||
collisionStateRef.current = {};
|
||||
}
|
||||
}, [isReset])
|
||||
|
||||
useEffect(() => {
|
||||
collisionWorker.onmessage = (e) => {
|
||||
const { collisions } = e.data;
|
||||
handleCollisions(collisions);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useFrame(() => {
|
||||
if (materials.length < 2 || !isPlaying || isPaused) return;
|
||||
|
||||
const { positions, sizes } = getCurrentMaterialData();
|
||||
positionsRef.current = positions;
|
||||
sizesRef.current = sizes;
|
||||
|
||||
collisionWorker.postMessage({
|
||||
materials: materials,
|
||||
positions: positions,
|
||||
sizes: sizes
|
||||
});
|
||||
});
|
||||
|
||||
const getCurrentMaterialData = () => {
|
||||
const positions: Record<string, THREE.Vector3> = {};
|
||||
const sizes: Record<string, number> = {};
|
||||
|
||||
materials.forEach(material => {
|
||||
if (material.isVisible) {
|
||||
const obj = scene.getObjectByProperty('uuid', material.materialId);
|
||||
if (obj) {
|
||||
const position = new THREE.Vector3();
|
||||
obj.getWorldPosition(position);
|
||||
positions[material.materialId] = position;
|
||||
|
||||
const boundingBox = new THREE.Box3().setFromObject(obj);
|
||||
const sizeVector = new THREE.Vector3();
|
||||
boundingBox.getSize(sizeVector);
|
||||
sizes[material.materialId] = Math.max(sizeVector.x, sizeVector.y, sizeVector.z) / 2;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { positions, sizes };
|
||||
};
|
||||
|
||||
const handleCollisions = (currentCollisions: Array<{
|
||||
materialId1: string;
|
||||
materialId2: string;
|
||||
distance: number;
|
||||
}>) => {
|
||||
const newCollisionState: Record<string, boolean> = {};
|
||||
|
||||
currentCollisions.forEach(collision => {
|
||||
const key = `${collision.materialId1}|-|${collision.materialId2}`;
|
||||
newCollisionState[key] = true;
|
||||
});
|
||||
|
||||
Object.keys(collisionStateRef.current).forEach(key => {
|
||||
if (!newCollisionState[key]) {
|
||||
const [id1, id2] = key.split('|-|');
|
||||
echo.error(`Collision ended between ${id1} and ${id2}`);
|
||||
}
|
||||
});
|
||||
|
||||
currentCollisions.forEach(collision => {
|
||||
const key = `${collision.materialId1}|-|${collision.materialId2}`;
|
||||
if (!collisionStateRef.current[key]) {
|
||||
setIsPaused(true);
|
||||
echo.error(`Collision started between ${collision.materialId1} and ${collision.materialId2}`);
|
||||
const obj = scene.getObjectByProperty('uuid', collision.materialId1)
|
||||
if (obj) {
|
||||
const collisionPos = new THREE.Vector3();
|
||||
obj.getWorldPosition(collisionPos);
|
||||
|
||||
const currentPos = new THREE.Vector3().copy(camera.position);
|
||||
|
||||
const target = new THREE.Vector3();
|
||||
(controls as CameraControls).getTarget(target);
|
||||
const direction = new THREE.Vector3().subVectors(target, currentPos).normalize();
|
||||
|
||||
const offsetDistance = 5;
|
||||
const newCameraPos = new THREE.Vector3().copy(collisionPos).sub(direction.multiplyScalar(offsetDistance));
|
||||
|
||||
camera.position.copy(newCameraPos);
|
||||
(controls as CameraControls).setLookAt(newCameraPos.x, newCameraPos.y, newCameraPos.z, collisionPos.x, collisionPos.y, collisionPos.z, true);
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
collisionStateRef.current = newCollisionState;
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -95,7 +95,7 @@ function MaterialInstance({ material }: { material: MaterialSchema }) {
|
||||
<>
|
||||
|
||||
{material.isRendered &&
|
||||
<MaterialModel matRef={matRef} materialType={material.materialType} visible={material.isVisible} position={position} />
|
||||
<MaterialModel materialId={material.materialId} matRef={matRef} materialType={material.materialType} visible={material.isVisible} position={position} />
|
||||
}
|
||||
|
||||
<MaterialAnimator
|
||||
|
||||
@@ -17,11 +17,12 @@ const modelPaths: Record<string, string> = {
|
||||
type ModelType = keyof typeof modelPaths;
|
||||
|
||||
interface ModelProps extends React.ComponentProps<'group'> {
|
||||
materialId: string;
|
||||
materialType: ModelType;
|
||||
matRef: React.Ref<THREE.Group<THREE.Object3DEventMap>>
|
||||
}
|
||||
|
||||
export function MaterialModel({ materialType, matRef, ...props }: Readonly<ModelProps>) {
|
||||
export function MaterialModel({ materialId, materialType, matRef, ...props }: Readonly<ModelProps>) {
|
||||
const path = modelPaths[materialType] || modelPaths['Default material'];
|
||||
const gltf = useGLTF(path);
|
||||
const cloned = useMemo(() => gltf?.scene.clone(), [gltf]);
|
||||
@@ -29,7 +30,7 @@ export function MaterialModel({ materialType, matRef, ...props }: Readonly<Model
|
||||
if (!cloned) return null;
|
||||
|
||||
return (
|
||||
<group ref={matRef} {...props} dispose={null}>
|
||||
<group uuid={materialId} ref={matRef} {...props} dispose={null}>
|
||||
<primitive
|
||||
object={cloned}
|
||||
scale={[0.4, 0.4, 0.4]}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect } from 'react'
|
||||
import MaterialInstances from './instances/materialInstances'
|
||||
import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore';
|
||||
import { useMaterialStore } from '../../../store/simulation/useMaterialStore';
|
||||
import MaterialCollisionDetector from './collisionDetection/materialCollitionDetector';
|
||||
|
||||
function Materials() {
|
||||
const { clearMaterials } = useMaterialStore();
|
||||
@@ -23,6 +24,8 @@ function Materials() {
|
||||
|
||||
}
|
||||
|
||||
<MaterialCollisionDetector />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useEffect } from 'react';
|
||||
import { useProductStore } from '../../../store/simulation/useProductStore';
|
||||
import { useSelectedProduct } from '../../../store/simulation/useSimulationStore';
|
||||
import AddOrRemoveEventsInProducts from './events/addOrRemoveEventsInProducts';
|
||||
import { upsertProductOrEventApi } from '../../../services/simulation/UpsertProductOrEventApi';
|
||||
import { getAllProductsApi } from '../../../services/simulation/getallProductsApi';
|
||||
import { upsertProductOrEventApi } from '../../../services/simulation/products/UpsertProductOrEventApi';
|
||||
import { getAllProductsApi } from '../../../services/simulation/products/getallProductsApi';
|
||||
import { useVehicleStore } from '../../../store/simulation/useVehicleStore';
|
||||
import { useArmBotStore } from '../../../store/simulation/useArmBotStore';
|
||||
import { useConveyorStore } from '../../../store/simulation/useConveyorStore';
|
||||
|
||||
@@ -57,6 +57,7 @@ export default function MaterialAnimator({ ikSolver, armBot, currentPhase }: Rea
|
||||
<>
|
||||
{isRendered && (
|
||||
<MaterialModel
|
||||
materialId={armBot.currentAction?.materialId ?? ''}
|
||||
matRef={sphereRef}
|
||||
materialType={armBot.currentAction?.materialType ?? 'Default material'}
|
||||
/>
|
||||
|
||||
@@ -10,7 +10,7 @@ import * as THREE from 'three';
|
||||
|
||||
import armPick from "../../../../assets/gltf-glb/ui/arm_ui_pick.glb";
|
||||
import armDrop from "../../../../assets/gltf-glb/ui/arm_ui_drop.glb";
|
||||
import { upsertProductOrEventApi } from '../../../../services/simulation/UpsertProductOrEventApi';
|
||||
import { upsertProductOrEventApi } from '../../../../services/simulation/products/UpsertProductOrEventApi';
|
||||
|
||||
type Positions = {
|
||||
pick: [number, number, number];
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from "../../../../store/simulation/useSimulationStore";
|
||||
import { useVehicleStore } from "../../../../store/simulation/useVehicleStore";
|
||||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { DoubleSide, Group, Plane, Vector3 } from "three";
|
||||
|
||||
import startPoint from "../../../../assets/gltf-glb/ui/arrow_green.glb";
|
||||
|
||||
@@ -63,6 +63,7 @@ const MaterialAnimator = ({
|
||||
storage.currentMaterials.map((mat, index) => (
|
||||
<MaterialModel
|
||||
key={`${index}-${mat.materialId}`}
|
||||
materialId={mat.materialId}
|
||||
matRef={meshRef}
|
||||
materialType={mat.materialType ?? "Default material"}
|
||||
position={materialPositions[index]}
|
||||
|
||||
@@ -194,6 +194,7 @@ export function useTriggerHandler() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setIsPaused(materialId, true);
|
||||
addArmBotToMonitor(armBot.modelUuid,
|
||||
() => {
|
||||
const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
|
||||
@@ -36,6 +36,7 @@ const MaterialAnimator = ({ agvDetail }: MaterialAnimatorProps) => {
|
||||
{agvDetail.currentMaterials.length > 0 &&
|
||||
<MaterialModel
|
||||
matRef={meshRef}
|
||||
materialId={agvDetail.currentMaterials[0].materialId || ''}
|
||||
materialType={agvDetail.currentMaterials[0].materialType || 'Default material'}
|
||||
/>
|
||||
}
|
||||
|
||||
40
app/src/services/simulation/webWorkers/collisionWorker.ts
Normal file
40
app/src/services/simulation/webWorkers/collisionWorker.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
onmessage = function (e) {
|
||||
const { materials, positions, sizes } = e.data;
|
||||
const collisions = [];
|
||||
const allPairs = [];
|
||||
|
||||
for (let i = 0; i < materials.length; i++) {
|
||||
for (let j = i + 1; j < materials.length; j++) {
|
||||
const mat1 = materials[i];
|
||||
const mat2 = materials[j];
|
||||
const pos1 = positions[mat1.materialId];
|
||||
const pos2 = positions[mat2.materialId];
|
||||
|
||||
if (!pos1 || !pos2) continue;
|
||||
|
||||
const size1 = sizes[mat1.materialId] || 0.2;
|
||||
const size2 = sizes[mat2.materialId] || 0.2;
|
||||
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(pos1.x - pos2.x, 2) +
|
||||
Math.pow(pos1.y - pos2.y, 2) +
|
||||
Math.pow(pos1.z - pos2.z, 2)
|
||||
);
|
||||
|
||||
allPairs.push({
|
||||
materialId1: mat1.materialId,
|
||||
materialId2: mat2.materialId
|
||||
});
|
||||
|
||||
if (distance < (size1 + size2)) {
|
||||
collisions.push({
|
||||
materialId1: mat1.materialId,
|
||||
materialId2: mat2.materialId,
|
||||
distance: distance
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
postMessage({ collisions, allPairs });
|
||||
};
|
||||
Reference in New Issue
Block a user