Skip to content

Commit e2aaa71

Browse files
committed
support right-hand rule
1 parent 67382ab commit e2aaa71

11 files changed

Lines changed: 281 additions & 178 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ Boolean operations on polygons (union, intersection, difference, xor).
1212
4. Uses formulas that take floating point irregularities into account (via
1313
configurable epsilon)
1414
5. Provides an API for constructing efficient sequences of operations
15-
6. Handles line segments (stable) and bezier cubic curves (experimental)
16-
7. TypeScript implementation
15+
6. Outputs exterior paths as counter-clockwise, and holes as clockwise (right-hand rule)
16+
7. Handles line segments (stable) and bezier cubic curves (experimental)
17+
8. TypeScript implementation
1718

1819
# Resources
1920

20-
* [Demo + Animation](https://unpkg.com/@velipso/polybool@2.0.9/demo/demo.html)
21+
* [Demo + Animation](https://unpkg.com/@velipso/polybool@2.0.10/demo/demo.html)
2122
* [Companion Tutorial](https://sean.fun/a/polygon-clipping-pt2)
2223
* Based somewhat on the F. Martinez (2008) algorithm:
2324
[Paper](http://www.cs.ucr.edu/~vbz/cs230papers/martinez_boolean.pdf),

demo/demo.html

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -797,10 +797,16 @@
797797
}
798798

799799
function segStart(seg) {
800+
if ('seg' in seg) {
801+
return segStart(seg.seg);
802+
}
800803
return 'data' in seg ? seg.data.p0 : seg.p0;
801804
}
802805

803806
function segEnd(seg) {
807+
if ('seg' in seg) {
808+
return segEnd(seg.seg);
809+
}
804810
if ('data' in seg) {
805811
if ('p3' in seg.data) {
806812
return seg.data.p3;
@@ -817,6 +823,9 @@
817823
if ('data' in seg) {
818824
return segPoint(seg.data, t);
819825
}
826+
if ('seg' in seg) {
827+
return segPoint(seg.seg, t);
828+
}
820829
if ('p3' in seg) {
821830
const p0 = seg.p0;
822831
const p1 = seg.p1;
@@ -856,7 +865,10 @@
856865

857866
function segPath(seg, ctx, skipMoveTo) {
858867
if ('data' in seg) {
859-
return segPath(seg.data, ctx);
868+
return segPath(seg.data, ctx, skipMoveTo);
869+
}
870+
if ('seg' in seg) {
871+
return segPath(seg.seg, ctx, skipMoveTo);
860872
}
861873
if (!skipMoveTo) {
862874
ctx.moveTo(seg.p0[0], seg.p0[1]);
@@ -1061,56 +1073,57 @@
10611073
break;
10621074
case 'chain_new':
10631075
setChainClosed(bl.closed);
1064-
bl_chains_x.push([bl.seg]);
1076+
bl_chains_x.push([bl.sf]);
10651077
bl_chainids_x.push(bl_nextchainid++);
10661078
bl_last_seg_keep = false;
10671079
break;
10681080
case 'chain_rev': {
10691081
setChainClosed(bl.closed);
10701082
const newChain = [];
10711083
for (let i = 0; i < bl_chains_x[bl.index].length; i++) {
1072-
let seg = bl_chains_x[bl.index][i];
1084+
const sf = bl_chains_x[bl.index][i];
1085+
let seg = sf.seg;
10731086
if ('p3' in seg) {
10741087
seg = { p0: seg.p3, p1: seg.p2, p2: seg.p1, p3: seg.p0 };
10751088
} else {
10761089
seg = { p0: seg.p1, p1: seg.p0 };
10771090
}
1078-
newChain.unshift(seg);
1091+
newChain.unshift({ seg, fill: !sf.fill });
10791092
}
10801093
bl_chains_x[bl.index] = newChain;
10811094
break;
10821095
}
10831096
case 'chain_add_head':
10841097
setChainClosed(bl.closed);
1085-
bl_chains_x[bl.index].unshift(bl.seg);
1098+
bl_chains_x[bl.index].unshift(bl.sf);
10861099
bl_last_seg_keep = false;
10871100
break;
10881101
case 'chain_add_tail':
10891102
setChainClosed(bl.closed);
1090-
bl_chains_x[bl.index].push(bl.seg);
1103+
bl_chains_x[bl.index].push(bl.sf);
10911104
bl_last_seg_keep = false;
10921105
break;
10931106
case 'chain_simp_head':
10941107
setChainClosed(bl.closed);
10951108
bl_chains_x[bl.index].shift();
1096-
bl_chains_x[bl.index][0] = bl.seg;
1109+
bl_chains_x[bl.index][0] = bl.sf;
10971110
break;
10981111
case 'chain_simp_tail':
10991112
setChainClosed(bl.closed);
11001113
bl_chains_x[bl.index].pop();
1101-
bl_chains_x[bl.index][bl_chains_x[bl.index].length - 1] = bl.seg;
1114+
bl_chains_x[bl.index][bl_chains_x[bl.index].length - 1] = bl.sf;
11021115
break;
11031116
case 'chain_simp_close':
11041117
setChainClosed(bl.closed);
11051118
bl_chains_x[bl.index].pop();
11061119
bl_chains_x[bl.index].shift();
1107-
bl_chains_x[bl.index].unshift(bl.seg);
1120+
bl_chains_x[bl.index].unshift(bl.sf);
11081121
break;
11091122
case 'chain_simp_join':
11101123
setChainClosed(bl.closed);
11111124
bl_chains_x[bl.index1].pop();
11121125
bl_chains_x[bl.index2].shift();
1113-
bl_chains_x[bl.index1].push(bl.seg);
1126+
bl_chains_x[bl.index1].push(bl.sf);
11141127
break;
11151128
case 'chain_match':
11161129
bl_last_seg_keep_match = { i: bl.index, c: bl.closed };
@@ -1387,18 +1400,34 @@
13871400
);
13881401
}
13891402

1390-
function drawChains(bl_chains, bl_chainids, color) {
1403+
function drawChains(bl_chains, bl_chainids, color, fillFlagColor) {
13911404
bl_chains.forEach(function(ch, chi){
13921405
var rad = 3;
13931406
for (var i = 0; i < ch.length; i++){
1394-
const seg = ch[i];
1407+
const seg = ch[i].seg;
13951408
var pt1 = segStart(seg);
13961409
var pt2 = segEnd(seg);
13971410
drawarrow(seg, rad);
13981411
ctx.lineWidth = 1;
13991412
ctx.strokeStyle = color;
14001413
ctx.stroke();
14011414

1415+
if (fillFlagColor) {
1416+
const m1 = segPoint(seg, 0.49);
1417+
const m2 = segPoint(seg, 0.51);
1418+
var ang = Math.atan2(m2[1] - m1[1], m2[0] - m1[0]) + Math.PI / 2;
1419+
const dist = ch[i].fill ? 6 : -6;
1420+
ctx.beginPath();
1421+
ctx.arc(
1422+
dist * Math.cos(ang) + (m1[0] + m2[0]) / 2,
1423+
dist * Math.sin(ang) + (m1[1] + m2[1]) / 2,
1424+
3,
1425+
0, Math.PI * 2
1426+
);
1427+
ctx.fillStyle = fillFlagColor;
1428+
ctx.fill();
1429+
}
1430+
14021431
ctx.beginPath();
14031432
ctx.arc(pt1[0], pt1[1], rad, 0, Math.PI * 2);
14041433
ctx.fillStyle = color;
@@ -1416,15 +1445,15 @@
14161445
}
14171446
});
14181447
}
1419-
drawChains(bl_chains_o, bl_chainids_o, '#777');
1420-
drawChains(bl_chains_c, bl_chainids_c, '#070');
1448+
drawChains(bl_chains_o, bl_chainids_o, '#777', false);
1449+
drawChains(bl_chains_c, bl_chainids_c, '#070', '#060');
14211450

14221451
if (bl_last_seg_keep){
1423-
var pt1 = segStart(bl_last_seg_keep.seg);
1424-
var pt2 = segEnd(bl_last_seg_keep.seg);
1452+
var pt1 = segStart(bl_last_seg_keep.sf.seg);
1453+
var pt2 = segEnd(bl_last_seg_keep.sf.seg);
14251454
const color = bl_last_seg_keep.closed ? '#0f0' : '#aaa';
14261455
ctx.beginPath();
1427-
drawarrow(bl_last_seg_keep.seg, 3.5);
1456+
drawarrow(bl_last_seg_keep.sf.seg, 3.5);
14281457
ctx.lineWidth = 2;
14291458
ctx.strokeStyle = color;
14301459
ctx.stroke();

0 commit comments

Comments
 (0)