Skip to content

Commit

Permalink
refactor(polygon): use polybool for merging
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulHax committed Jul 3, 2024
1 parent 11d93ed commit c351d64
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 34 deletions.
29 changes: 23 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@kitware/vtk.js": "^29.0.0",
"@netlify/edge-functions": "^2.0.0",
"@sentry/vue": "^7.54.0",
"@velipso/polybool": "^1.1.0",
"@vueuse/core": "^10.7.0",
"core-js": "3.22.5",
"deep-equal": "^2.0.5",
Expand Down
12 changes: 12 additions & 0 deletions patches/@velipso+polybool+1.1.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff --git a/node_modules/@velipso/polybool/package.json b/node_modules/@velipso/polybool/package.json
index c0bf880..00899ce 100644
--- a/node_modules/@velipso/polybool/package.json
+++ b/node_modules/@velipso/polybool/package.json
@@ -11,6 +11,7 @@
"require": "./dist/polybool.cjs.js"
}
},
+ "types": "dist/polybool.d.ts",
"scripts": {
"test": "tsx src/polybool.test.ts",
"build": "rollup -c && rollup -c rollup.config.cjs.js",
2 changes: 1 addition & 1 deletion src/components/tools/polygon/PolygonTool.vue
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export default defineComponent({
onToolPlaced,
contextMenu,
openContextMenu,
mergeTools: activeToolStore.mergeTools,
mergeTools: activeToolStore.mergeSelectedTools,
mergePossible,
activeToolStore,
onHover,
Expand Down
69 changes: 42 additions & 27 deletions src/store/tools/polygons.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { intersection, union, Geom } from 'polyclip-ts';
import polybool, { Polygon } from '@velipso/polybool';
import type { Vector3 } from '@kitware/vtk.js/types';
import { computed } from 'vue';
import {
Expand Down Expand Up @@ -33,16 +33,46 @@ export const usePolygonStore = defineAnnotationToolStore('polygon', () => {
// -- merge tool helpers -- //
type Tool = (typeof toolAPI.tools.value)[number];

const toGeoJSON = (polygon: Tool) => {
const toPolyLibStructure = (polygon: Tool) => {
const { to2D } = getPlaneTransforms(polygon.frameOfReference);
if (polygon.points.length === 0) return undefined; // empty polygons are invalid to polyclip-ts
return [polygon.points.map(to2D)]; // GeoJSON Polygon can have multiple rings so wrap in array
if (polygon.points.length === 0) return undefined; // empty polygons are invalid
return {
regions: [polygon.points.map(to2D)],
inverted: false,
};
};

const polygonsOverlap = (a: Tool, b: Tool) => {
const [aGeo, bGeo] = [a, b].map(toGeoJSON);
const [aGeo, bGeo] = [a, b].map(toPolyLibStructure);
if (!aGeo || !bGeo) return false;
return intersection(aGeo, bGeo).length > 0;
return polybool.intersect(aGeo, bGeo).regions.length > 0;
};

const mergePolygons = (polygons: Array<Tool>) => {
const libPolygons = polygons.map(toPolyLibStructure) as Array<Polygon>;
if (libPolygons.some((p) => p === undefined))
throw new Error('Trying to merge invalid polygons');

let segments = polybool.segments(libPolygons[0]);
for (let i = 1; i < libPolygons.length; i++) {
const seg2 = polybool.segments(libPolygons[i]);
const comb = polybool.combine(segments, seg2);
segments = polybool.selectUnion(comb);
}
const unionPoly = polybool.polygon(segments);

const firstTool = polygons[0];
const { to3D } = getPlaneTransforms(firstTool.frameOfReference);

const points = unionPoly.regions[0].map(to3D);

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { id: _, ...toolProps } = polygons[0];
const mergedTool = {
...toolProps,
points,
};
return mergedTool;
};

const sameSliceAndLabel = (a: Tool, b: Tool) =>
Expand Down Expand Up @@ -81,29 +111,14 @@ export const usePolygonStore = defineAnnotationToolStore('polygon', () => {
return overlapping;
});

function mergeToolGroup(mergeGroup: Tool[]) {
const polygons = mergeGroup.map(toGeoJSON);
if (polygons.some((p) => !p))
throw new Error('Trying to merge invalid polygons');

const [first, ...rest] = polygons as unknown as Geom[];
const merged = union(first, ...rest);
const firstTool = mergeGroup[0];
const { to3D } = getPlaneTransforms(firstTool.frameOfReference);
const points = merged.flatMap((p) => p.flatMap((ring) => ring.map(to3D)));

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { id: _, ...toolProps } = firstTool;
const mergedTool = {
...toolProps,
points,
};
function mergeTools(mergeGroup: Tool[]) {
const mergedTool = mergePolygons(mergeGroup);
toolAPI.addTool(mergedTool);
mergeGroup.map(({ id }) => id).forEach(toolAPI.removeTool);
}

function mergeTools() {
mergeToolGroup(mergeableTools.value);
function mergeSelectedTools() {
mergeTools(mergeableTools.value);
}

function mergeWithOtherTools(id: ToolID) {
Expand All @@ -114,7 +129,7 @@ export const usePolygonStore = defineAnnotationToolStore('polygon', () => {
if (!lastTool || olderTools.length === 0) return;
const mergeable = olderTools.filter((older) => mergable(older, lastTool));
if (mergeable.length === 0) return;
mergeToolGroup([lastTool, ...mergeable]);
mergeTools([lastTool, ...mergeable]);
}

// --- serialization --- //
Expand All @@ -131,7 +146,7 @@ export const usePolygonStore = defineAnnotationToolStore('polygon', () => {
...toolAPI,
getPoints,
mergeableTools,
mergeTools,
mergeSelectedTools,
mergeWithOtherTools,
serialize,
deserialize,
Expand Down

0 comments on commit c351d64

Please sign in to comment.