Skip to content

Commit f76b8ef

Browse files
committed
bsp is now reconstructed for each operation
1 parent 4964e99 commit f76b8ef

File tree

2 files changed

+68
-63
lines changed

2 files changed

+68
-63
lines changed

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@ Example usage:
2424

2525
All CSG operations are implemented in terms of two functions, `clipTo()` and `invert()`, which remove parts of a BSP tree inside another BSP tree and swap solid and empty space, respectively. To find the union of `a` and `b`, we want to remove everything in `a` inside `b` and everything in `b` inside `a`, then combine polygons from `a` and `b` into one solid:
2626

27-
a.root.clipTo(b.root);
28-
b.root.clipTo(a.root);
29-
a.root.build(b.root.allPolygons());
27+
a.clipTo(b);
28+
b.clipTo(a);
29+
a.build(b.allPolygons());
3030

3131
The only tricky part is handling overlapping coplanar polygons in both trees. The code above keeps both copies, but we need to keep them in one tree and remove them in the other tree. To remove them from `b` we can clip the inverse of `b` against `a`. The code for union now looks like this:
3232

33-
a.root.clipTo(b.root);
34-
b.root.clipTo(a.root);
35-
b.root.invert();
36-
b.root.clipTo(a.root);
37-
b.root.invert();
38-
a.root.build(b.root.allPolygons());
33+
a.clipTo(b);
34+
b.clipTo(a);
35+
b.invert();
36+
b.clipTo(a);
37+
b.invert();
38+
a.build(b.allPolygons());
3939

4040
Subtraction and intersection naturally follow from set operations. If union is `A | B`, subtraction is `A - B = ~(~A | B)` and intersection is `A & B = ~(~A | ~B)` where `~` is the complement operator.
4141

csg.js

Lines changed: 59 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,21 @@
1919
// want to remove everything in `a` inside `b` and everything in `b` inside `a`,
2020
// then combine polygons from `a` and `b` into one solid:
2121
//
22-
// a.root.clipTo(b.root);
23-
// b.root.clipTo(a.root);
24-
// a.root.build(b.root.allPolygons());
22+
// a.clipTo(b);
23+
// b.clipTo(a);
24+
// a.build(b.allPolygons());
2525
//
2626
// The only tricky part is handling overlapping coplanar polygons in both trees.
2727
// The code above keeps both copies, but we need to keep them in one tree and
2828
// remove them in the other tree. To remove them from `b` we can clip the
2929
// inverse of `b` against `a`. The code for union now looks like this:
3030
//
31-
// a.root.clipTo(b.root);
32-
// b.root.clipTo(a.root);
33-
// b.root.invert();
34-
// b.root.clipTo(a.root);
35-
// b.root.invert();
36-
// a.root.build(b.root.allPolygons());
31+
// a.clipTo(b);
32+
// b.clipTo(a);
33+
// b.invert();
34+
// b.clipTo(a);
35+
// b.invert();
36+
// a.build(b.allPolygons());
3737
//
3838
// Subtraction and intersection naturally follow from set operations. If
3939
// union is `A | B`, subtraction is `A - B = ~(~A | B)` and intersection is
@@ -49,29 +49,29 @@
4949
// be combined using the `union()`, `subtract()`, and `intersect()` methods.
5050

5151
CSG = function() {
52-
this.root = new CSG.Node();
52+
this.polygons = [];
5353
};
5454

5555
// Construct a CSG solid from a list of `CSG.Polygon` instances.
5656
CSG.fromPolygons = function(polygons) {
57-
var bsp = new CSG();
58-
bsp.root.build(polygons);
59-
return bsp;
57+
var csg = new CSG();
58+
csg.polygons = polygons;
59+
return csg;
6060
};
6161

6262
CSG.prototype = {
6363
clone: function() {
64-
var bsp = new CSG();
65-
bsp.root = this.root.clone();
66-
return bsp;
64+
var csg = new CSG();
65+
csg.polygons = this.polygons.map(function(p) { return p.clone(); });
66+
return csg;
6767
},
6868

6969
toPolygons: function() {
70-
return this.root.allPolygons();
70+
return this.polygons;
7171
},
7272

7373
// Return a new CSG solid representing space in either this solid or in the
74-
// solid `bsp`. Neither this solid nor the solid `bsp` are modified.
74+
// solid `csg`. Neither this solid nor the solid `csg` are modified.
7575
//
7676
// A.union(B)
7777
//
@@ -84,19 +84,20 @@ CSG.prototype = {
8484
// | | | |
8585
// +-------+ +-------+
8686
//
87-
union: function(bsp) {
88-
var a = this.clone(), b = bsp.clone();
89-
a.root.clipTo(b.root);
90-
b.root.clipTo(a.root);
91-
b.root.invert();
92-
b.root.clipTo(a.root);
93-
b.root.invert();
94-
a.root.build(b.root.allPolygons());
95-
return a;
87+
union: function(csg) {
88+
var a = new CSG.Node(this.clone().polygons);
89+
var b = new CSG.Node(csg.clone().polygons);
90+
a.clipTo(b);
91+
b.clipTo(a);
92+
b.invert();
93+
b.clipTo(a);
94+
b.invert();
95+
a.build(b.allPolygons());
96+
return CSG.fromPolygons(a.allPolygons());
9697
},
9798

9899
// Return a new CSG solid representing space in this solid but not in the
99-
// solid `bsp`. Neither this solid nor the solid `bsp` are modified.
100+
// solid `csg`. Neither this solid nor the solid `csg` are modified.
100101
//
101102
// A.subtract(B)
102103
//
@@ -109,21 +110,22 @@ CSG.prototype = {
109110
// | |
110111
// +-------+
111112
//
112-
subtract: function(bsp) {
113-
var a = this.clone(), b = bsp.clone();
114-
a.root.invert();
115-
a.root.clipTo(b.root);
116-
b.root.clipTo(a.root);
117-
b.root.invert();
118-
b.root.clipTo(a.root);
119-
b.root.invert();
120-
a.root.build(b.root.allPolygons());
121-
a.root.invert();
122-
return a;
113+
subtract: function(csg) {
114+
var a = new CSG.Node(this.clone().polygons);
115+
var b = new CSG.Node(csg.clone().polygons);
116+
a.invert();
117+
a.clipTo(b);
118+
b.clipTo(a);
119+
b.invert();
120+
b.clipTo(a);
121+
b.invert();
122+
a.build(b.allPolygons());
123+
a.invert();
124+
return CSG.fromPolygons(a.allPolygons());
123125
},
124126

125127
// Return a new CSG solid representing space both this solid and in the
126-
// solid `bsp`. Neither this solid nor the solid `bsp` are modified.
128+
// solid `csg`. Neither this solid nor the solid `csg` are modified.
127129
//
128130
// A.intersect(B)
129131
//
@@ -136,24 +138,25 @@ CSG.prototype = {
136138
// | |
137139
// +-------+
138140
//
139-
intersect: function(bsp) {
140-
var a = this.clone(), b = bsp.clone();
141-
a.root.invert();
142-
b.root.clipTo(a.root);
143-
b.root.invert();
144-
a.root.clipTo(b.root);
145-
b.root.clipTo(a.root);
146-
a.root.build(b.root.allPolygons());
147-
a.root.invert();
148-
return a;
141+
intersect: function(csg) {
142+
var a = new CSG.Node(this.clone().polygons);
143+
var b = new CSG.Node(csg.clone().polygons);
144+
a.invert();
145+
b.clipTo(a);
146+
b.invert();
147+
a.clipTo(b);
148+
b.clipTo(a);
149+
a.build(b.allPolygons());
150+
a.invert();
151+
return CSG.fromPolygons(a.allPolygons());
149152
},
150153

151154
// Return a new CSG solid with solid and empty space switched. This solid is
152155
// not modified.
153156
inverse: function() {
154-
var bsp = this.clone();
155-
bsp.root.invert();
156-
return bsp;
157+
var csg = this.clone();
158+
csg.polygons.map(function(p) { p.flip(); });
159+
return csg;
157160
}
158161
};
159162

@@ -506,11 +509,12 @@ CSG.Polygon.prototype = {
506509
// the front and/or back subtrees. This is not a leafy BSP tree since there is
507510
// no distinction between internal and leaf nodes.
508511

509-
CSG.Node = function() {
512+
CSG.Node = function(polygons) {
510513
this.plane = null;
511514
this.front = null;
512515
this.back = null;
513516
this.polygons = [];
517+
if (polygons) this.build(polygons);
514518
};
515519

516520
CSG.Node.prototype = {
@@ -539,6 +543,7 @@ CSG.Node.prototype = {
539543
// Recursively remove all polygons in `polygons` that are inside this BSP
540544
// tree.
541545
clipPolygons: function(polygons) {
546+
if (!this.plane) return polygons.slice();
542547
var front = [], back = [];
543548
for (var i = 0; i < polygons.length; i++) {
544549
this.plane.splitPolygon(polygons[i], front, back, front, back);

0 commit comments

Comments
 (0)