1 | /*
|
2 | ## License
|
3 |
|
4 | Copyright (c) 2014 bebbi (elghatta@gmail.com)
|
5 | Copyright (c) 2013 Eduard Bespalov (edwbes@gmail.com)
|
6 | Copyright (c) 2012 Joost Nieuwenhuijse (joost@newhouse.nl)
|
7 | Copyright (c) 2011 Evan Wallace (http://evanw.github.com/csg.js/)
|
8 | Copyright (c) 2012 Alexandre Girard (https://github.com/alx)
|
9 |
|
10 | All code released under MIT license
|
11 |
|
12 | ## Overview
|
13 |
|
14 | For an overview of the CSG process see the original csg.js code:
|
15 | http://evanw.github.com/csg.js/
|
16 |
|
17 | CSG operations through BSP trees suffer from one problem: heavy fragmentation
|
18 | of polygons. If two CSG solids of n polygons are unified, the resulting solid may have
|
19 | in the order of n*n polygons, because each polygon is split by the planes of all other
|
20 | polygons. After a few operations the number of polygons explodes.
|
21 |
|
22 | This version of CSG.js solves the problem in 3 ways:
|
23 |
|
24 | 1. Every polygon split is recorded in a tree (CSG.PolygonTreeNode). This is a separate
|
25 | tree, not to be confused with the CSG tree. If a polygon is split into two parts but in
|
26 | the end both fragments have not been discarded by the CSG operation, we can retrieve
|
27 | the original unsplit polygon from the tree, instead of the two fragments.
|
28 |
|
29 | This does not completely solve the issue though: if a polygon is split multiple times
|
30 | the number of fragments depends on the order of subsequent splits, and we might still
|
31 | end up with unncessary splits:
|
32 | Suppose a polygon is first split into A and B, and then into A1, B1, A2, B2. Suppose B2 is
|
33 | discarded. We will end up with 2 polygons: A and B1. Depending on the actual split boundaries
|
34 | we could still have joined A and B1 into one polygon. Therefore a second approach is used as well:
|
35 |
|
36 | 2. After CSG operations all coplanar polygon fragments are joined by a retesselating
|
37 | operation. See CSG.reTesselated(). Retesselation is done through a
|
38 | linear sweep over the polygon surface. The sweep line passes over the y coordinates
|
39 | of all vertices in the polygon. Polygons are split at each sweep line, and the fragments
|
40 | are joined horizontally and vertically into larger polygons (making sure that we
|
41 | will end up with convex polygons).
|
42 | This still doesn't solve the problem completely: due to floating point imprecisions
|
43 | we may end up with small gaps between polygons, and polygons may not be exactly coplanar
|
44 | anymore, and as a result the retesselation algorithm may fail to join those polygons.
|
45 | Therefore:
|
46 |
|
47 | 3. A canonicalization algorithm is implemented: it looks for vertices that have
|
48 | approximately the same coordinates (with a certain tolerance, say 1e-5) and replaces
|
49 | them with the same vertex. If polygons share a vertex they will actually point to the
|
50 | same CSG.Vertex instance. The same is done for polygon planes. See CSG.canonicalized().
|
51 |
|
52 | Performance improvements to the original CSG.js:
|
53 |
|
54 | Replaced the flip() and invert() methods by flipped() and inverted() which don't
|
55 | modify the source object. This allows to get rid of all clone() calls, so that
|
56 | multiple polygons can refer to the same CSG.Plane instance etc.
|
57 |
|
58 | The original union() used an extra invert(), clipTo(), invert() sequence just to remove the
|
59 | coplanar front faces from b; this is now combined in a single b.clipTo(a, true) call.
|
60 |
|
61 | Detection whether a polygon is in front or in back of a plane: for each polygon
|
62 | we are caching the coordinates of the bounding sphere. If the bounding sphere is
|
63 | in front or in back of the plane we don't have to check the individual vertices
|
64 | anymore.
|
65 |
|
66 | Other additions to the original CSG.js:
|
67 |
|
68 | CSG.Vector class has been renamed into CSG.Vector3D
|
69 |
|
70 | Classes for 3D lines, 2D vectors, 2D lines, and methods to find the intersection of
|
71 | a line and a plane etc.
|
72 |
|
73 | Transformations: CSG.transform(), CSG.translate(), CSG.rotate(), CSG.scale()
|
74 |
|
75 | Expanding or contracting a solid: CSG.expand() and CSG.contract(). Creates nice
|
76 | smooth corners.
|
77 |
|
78 | The vertex normal has been removed since it complicates retesselation. It's not needed
|
79 | for solid CAD anyway.
|
80 |
|
81 | */
|
82 |
|
83 | const {addTransformationMethodsToPrototype, addCenteringToPrototype} = require('./src/mutators')
|
84 | let CSG = require('./src/CSG')
|
85 | let CAG = require('./src/CAG')
|
86 |
|
87 | // FIXME: how many are actual usefull to be exposed as API ?? looks like a code smell
|
88 | const { _CSGDEBUG,
|
89 | defaultResolution2D,
|
90 | defaultResolution3D,
|
91 | EPS,
|
92 | angleEPS,
|
93 | areaEPS,
|
94 | all,
|
95 | top,
|
96 | bottom,
|
97 | left,
|
98 | right,
|
99 | front,
|
100 | back,
|
101 | staticTag,
|
102 | getTag} = require('./src/constants')
|
103 |
|
104 | CSG._CSGDEBUG = _CSGDEBUG
|
105 | CSG.defaultResolution2D = defaultResolution2D
|
106 | CSG.defaultResolution3D = defaultResolution3D
|
107 | CSG.EPS = EPS
|
108 | CSG.angleEPS = angleEPS
|
109 | CSG.areaEPS = areaEPS
|
110 | CSG.all = all
|
111 | CSG.top = top
|
112 | CSG.bottom = bottom
|
113 | CSG.left = left
|
114 | CSG.right = right
|
115 | CSG.front = front
|
116 | CSG.back = back
|
117 | CSG.staticTag = staticTag
|
118 | CSG.getTag = getTag
|
119 |
|
120 | // eek ! all this is kept for backwards compatibility...for now
|
121 | CSG.Vector2D = require('./src/math/Vector2')
|
122 | CSG.Vector3D = require('./src/math/Vector3')
|
123 | CSG.Vertex = require('./src/math/Vertex3')
|
124 | CAG.Vertex = require('./src/math/Vertex2')
|
125 | CSG.Plane = require('./src/math/Plane')
|
126 | CSG.Polygon = require('./src/math/Polygon3')
|
127 | CSG.Polygon2D = require('./src/math/Polygon2')
|
128 | CSG.Line2D = require('./src/math/Line2')
|
129 | CSG.Line3D = require('./src/math/Line3')
|
130 | CSG.Path2D = require('./src/math/Path2')
|
131 | CSG.OrthoNormalBasis = require('./src/math/OrthoNormalBasis')
|
132 | CSG.Matrix4x4 = require('./src/math/Matrix4')
|
133 |
|
134 | CAG.Side = require('./src/math/Side')
|
135 |
|
136 | CSG.Connector = require('./src/connectors').Connector
|
137 | CSG.ConnectorList = require('./src/connectors').ConnectorList
|
138 | CSG.Properties = require('./src/Properties')
|
139 |
|
140 | const {circle, ellipse, rectangle, roundedRectangle} = require('./src/primitives2d')
|
141 | const {sphere, cube, roundedCube, cylinder, roundedCylinder, cylinderElliptic, polyhedron} = require('./src/primitives3d')
|
142 |
|
143 | CSG.sphere = sphere
|
144 | CSG.cube = cube
|
145 | CSG.roundedCube = roundedCube
|
146 | CSG.cylinder = cylinder
|
147 | CSG.roundedCylinder = roundedCylinder
|
148 | CSG.cylinderElliptic = cylinderElliptic
|
149 | CSG.polyhedron = polyhedron
|
150 |
|
151 | CAG.circle = circle
|
152 | CAG.ellipse = ellipse
|
153 | CAG.rectangle = rectangle
|
154 | CAG.roundedRectangle = roundedRectangle
|
155 |
|
156 | //
|
157 | const {fromCompactBinary, fromObject, fromSlices} = require('./src/CSGFactories')
|
158 | CSG.fromCompactBinary = fromCompactBinary
|
159 | CSG.fromObject = fromObject
|
160 | CSG.fromSlices = fromSlices
|
161 |
|
162 | CSG.toPointCloud = require('./src/debugHelpers').toPointCloud
|
163 |
|
164 | const CAGMakers = require('./src/CAGFactories')
|
165 | CAG.fromObject = CAGMakers.fromObject
|
166 | CAG.fromPointsNoCheck = CAGMakers.fromPointsNoCheck
|
167 | CAG.fromPath2 = CAGMakers.fromPath2
|
168 |
|
169 | // ////////////////////////////////////
|
170 | addTransformationMethodsToPrototype(CSG.prototype)
|
171 | addTransformationMethodsToPrototype(CSG.Vector2D.prototype)
|
172 | addTransformationMethodsToPrototype(CSG.Vector3D.prototype)
|
173 | addTransformationMethodsToPrototype(CSG.Vertex.prototype)
|
174 | addTransformationMethodsToPrototype(CSG.Plane.prototype)
|
175 | addTransformationMethodsToPrototype(CSG.Polygon.prototype)
|
176 | addTransformationMethodsToPrototype(CSG.Line2D.prototype)
|
177 | addTransformationMethodsToPrototype(CSG.Line3D.prototype)
|
178 | addTransformationMethodsToPrototype(CSG.Path2D.prototype)
|
179 | addTransformationMethodsToPrototype(CSG.OrthoNormalBasis.prototype)
|
180 | addTransformationMethodsToPrototype(CSG.Connector.prototype)
|
181 |
|
182 | addTransformationMethodsToPrototype(CAG.prototype)
|
183 | addTransformationMethodsToPrototype(CAG.Side.prototype)
|
184 | addTransformationMethodsToPrototype(CAG.Vertex.prototype)
|
185 |
|
186 | addCenteringToPrototype(CSG.prototype, ['x', 'y', 'z'])
|
187 | addCenteringToPrototype(CAG.prototype, ['x', 'y'])
|
188 |
|
189 | module.exports = {CSG, CAG}
|