Skip to content

Commit 4ca603b

Browse files
authored
Self-intersecting output when cutting a multipolygon with a multiline #205 (#240)
* Update the `cut` method to support multipolygons. Include corresponding regression test. * Add `createFromArray` method to handle multiple polygons
1 parent b4807b9 commit 4ca603b

File tree

3 files changed

+47
-2
lines changed

3 files changed

+47
-2
lines changed

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ declare namespace Flatten {
545545
isEmpty(): boolean;
546546
isValid(): boolean;
547547
area(): number;
548+
createFromArray(polygons: Polygon): Polygon
548549
addFace(args: Array<Point> | Array<Segment | Arc> | Circle | Box): Face;
549550
addFace(edge_from: Edge, edge_to: Edge): Face;
550551
deleteFace(face: Face): boolean;

src/classes/polygon.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ export class Polygon {
116116
return polygon;
117117
}
118118

119+
/**
120+
* Create a polygon from an array of polygons. Each polygon is an outer face with all containing inner faces
121+
* @param polygons
122+
* @returns {Polygon}
123+
*/
124+
createFromArray(polygons) {
125+
const newPolygon = new Polygon();
126+
polygons.forEach(polygon => [...polygon.faces].forEach(face => newPolygon.addFace(face.shapes)));
127+
return newPolygon;
128+
}
129+
119130
/**
120131
* Return true is polygon has no edges or faces
121132
* @returns {boolean}
@@ -156,7 +167,7 @@ export class Polygon {
156167
}
157168

158169
/**
159-
* Add new face to polygon. Returns added face
170+
* Add a new face to polygon. Returns added face
160171
* @param {Point[]|Segment[]|Arc[]|Circle|Box} args - new face may be create with one of the following ways: <br/>
161172
* 1) array of points that describe closed path (edges are segments) <br/>
162173
* 2) array of shapes (segments and arcs) which describe closed path <br/>
@@ -293,11 +304,25 @@ export class Polygon {
293304

294305
/**
295306
* Cut polygon with multiline and return a new polygon
296-
* @param {Multiline} multiline
307+
* The cutting is done by intersection of multiline with edges of the polygon.
308+
* @param multiline
297309
* @returns {Polygon}
298310
*/
299311
cut(multiline) {
312+
const polygons = this.splitToIslands();
313+
const result = polygons.flatMap(polygon => polygon._cutSingleIsland(multiline))
314+
.filter(polygon => polygon.isValid() && polygon.isEmpty() === false);
315+
return this.createFromArray(result);
316+
}
317+
318+
/**
319+
* Cut polygon with multiline and return a new polygon
320+
* @param {Multiline} multiline
321+
* @returns {Polygon}
322+
*/
323+
_cutSingleIsland(inputMultiline) {
300324
let newPoly = this.clone()
325+
const multiline = inputMultiline.clone();
301326

302327
// smart intersections
303328
let intersections = {

test/classes/polygon.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,25 @@ describe('#Flatten.Polygon', function() {
11081108
console.error('Cut fail.')
11091109
}
11101110
})
1111+
it('Self-intersecting output when cutting a multipolygon with a multiline #205', () => {
1112+
const input = new Polygon([[
1113+
{"ps":{"x":0,"y":0,"name":"point"},"pe":{"x":0,"y":50,"name":"point"},"name":"segment"},
1114+
{"ps":{"x":0,"y":50,"name":"point"},"pe":{"x":100,"y":50,"name":"point"},"name":"segment"},
1115+
{"ps":{"x":100,"y":50,"name":"point"},"pe":{"x":100,"y":0,"name":"point"},"name":"segment"},
1116+
{"ps":{"x":100,"y":0,"name":"point"},"pe":{"x":0,"y":0,"name":"point"},"name":"segment"}
1117+
],[
1118+
{"ps":{"x":0,"y":50,"name":"point"},"pe":{"x":0,"y":100,"name":"point"},"name":"segment"},
1119+
{"ps":{"x":0,"y":100,"name":"point"},"pe":{"x":100,"y":100,"name":"point"},"name":"segment"},
1120+
{"ps":{"x":100,"y":100,"name":"point"},"pe":{"x":100,"y":50,"name":"point"},"name":"segment"},
1121+
{"ps":{"x":100,"y":50,"name":"point"},"pe":{"x":0,"y":50,"name":"point"},"name":"segment"}
1122+
]])
1123+
const cutline = new Multiline([
1124+
new Segment(point(50,110), point(50, -10))
1125+
])
1126+
const result = input.cut(cutline)
1127+
expect(result.faces.size).to.equal(4)
1128+
expect(result.edges.size).to.equal(16)
1129+
})
11111130
describe('#Intersections', function () {
11121131
it('Can perform intersection between polygons', function () {
11131132
const poly1 = new Polygon(

0 commit comments

Comments
 (0)