|
1 | | -<html> |
2 | | -<body style="margin:0;overflow:hidden;"> |
3 | | -<script src="../csg.js"></script> |
4 | | -<script src="lightgl.js"></script> |
5 | | -<script src="coplanar.js"></script> |
6 | | -</body> |
7 | | -</html> |
| 1 | +<!DOCTYPE html> |
| 2 | +<html><head> |
| 3 | + <script src="lightgl.js"></script> |
| 4 | + <script src="../csg.js"></script> |
| 5 | + <style> |
| 6 | + |
| 7 | +body { |
| 8 | + font: 14px/20px 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif; |
| 9 | + max-width: 800px; |
| 10 | + margin: 0 auto; |
| 11 | + padding: 50px 50px 200px 50px; |
| 12 | +} |
| 13 | + |
| 14 | +pre, code { |
| 15 | + font: 12px/20px Monaco, monospace; |
| 16 | + border: 1px solid #CCC; |
| 17 | + border-radius: 3px; |
| 18 | + background: #F9F9F9; |
| 19 | + padding: 0 3px; |
| 20 | + color: #555; |
| 21 | +} |
| 22 | +pre { padding: 10px; } |
| 23 | + |
| 24 | +h1, h2 { font: bold 50px/50px 'Helvetica Neue', Helvetica, Arial; } |
| 25 | +h2 { font-size: 30px; margin: 100px 0 0 0; } |
| 26 | +a { color: inherit; } |
| 27 | +.viewer { width: 200px; height: 200px; background: #EEE; } |
| 28 | +table { border-collapse: collapse; margin: 0 auto; } |
| 29 | +td { padding: 5px; text-align: center; } |
| 30 | +td code { background: none; border: none; color: inherit; } |
| 31 | +canvas { cursor: move; } |
| 32 | + |
| 33 | + </style> |
| 34 | +</head><body> |
| 35 | + <h1>csg.js</h1> |
| 36 | + <p><b>Source code:</b> <a href="http://github.com/evanw/csg.js/">http://github.com/evanw/csg.js/</a><br> |
| 37 | + <b>Documentation:</b> <a href="http://evanw.github.com/csg.js/docs/">http://evanw.github.com/csg.js/docs/</a></p> |
| 38 | + |
| 39 | + <h2>Coplanar Test Cases</h2> |
| 40 | + <p>These tests cover all the cases of overlapping coplanar polygons in both solids. To avoid duplicate polygons, the overlapping polygons are kept in the first solid and removed from the second solid.</p> |
| 41 | + <table> |
| 42 | + <tr> |
| 43 | + <td><div id="0" class="viewer"></div><code>a</code></td> |
| 44 | + <td><div id="1" class="viewer"></div><code>b</code></td> |
| 45 | + </tr> |
| 46 | + <tr> |
| 47 | + <td><div id="2" class="viewer"></div><code>a.inverse()</code></td> |
| 48 | + <td><div id="3" class="viewer"></div><code>b.inverse()</code></td> |
| 49 | + </tr> |
| 50 | + <tr> |
| 51 | + <td><div id="4" class="viewer"></div><code>a.union(b)</code></td> |
| 52 | + <td><div id="5" class="viewer"></div><code>b.union(a)</code></td> |
| 53 | + </tr> |
| 54 | + <tr> |
| 55 | + <td><div id="6" class="viewer"></div><code>a.subtract(b)</code></td> |
| 56 | + <td><div id="7" class="viewer"></div><code>b.subtract(a)</code></td> |
| 57 | + </tr> |
| 58 | + <tr> |
| 59 | + <td><div id="8" class="viewer"></div><code>a.intersect(b)</code></td> |
| 60 | + <td><div id="9" class="viewer"></div><code>b.intersect(a)</code></td> |
| 61 | + </tr> |
| 62 | + </table> |
| 63 | + <table> |
| 64 | + <tr> |
| 65 | + <td><div id="10" class="viewer"></div><code>c</code></td> |
| 66 | + <td><div id="11" class="viewer"></div><code>d</code></td> |
| 67 | + </tr> |
| 68 | + <tr> |
| 69 | + <td><div id="12" class="viewer"></div><code>c.inverse()</code></td> |
| 70 | + <td><div id="13" class="viewer"></div><code>d.inverse()</code></td> |
| 71 | + </tr> |
| 72 | + <tr> |
| 73 | + <td><div id="14" class="viewer"></div><code>c.union(d)</code></td> |
| 74 | + <td><div id="15" class="viewer"></div><code>d.union(c)</code></td> |
| 75 | + </tr> |
| 76 | + <tr> |
| 77 | + <td><div id="16" class="viewer"></div><code>c.subtract(d)</code></td> |
| 78 | + <td><div id="17" class="viewer"></div><code>d.subtract(c)</code></td> |
| 79 | + </tr> |
| 80 | + <tr> |
| 81 | + <td><div id="18" class="viewer"></div><code>c.intersect(d)</code></td> |
| 82 | + <td><div id="19" class="viewer"></div><code>d.intersect(c)</code></td> |
| 83 | + </tr> |
| 84 | + </table> |
| 85 | + |
| 86 | + <script> |
| 87 | + |
| 88 | +// Set the color of all polygons in this solid |
| 89 | +CSG.prototype.setColor = function(r, g, b) { |
| 90 | + this.toPolygons().map(function(polygon) { |
| 91 | + polygon.shared = [r, g, b]; |
| 92 | + }); |
| 93 | +}; |
| 94 | + |
| 95 | +// Convert from CSG solid to GL.Mesh object |
| 96 | +CSG.prototype.toMesh = function() { |
| 97 | + var mesh = new GL.Mesh({ normals: true, colors: true }); |
| 98 | + var indexer = new GL.Indexer(); |
| 99 | + this.toPolygons().map(function(polygon) { |
| 100 | + var indices = polygon.vertices.map(function(vertex) { |
| 101 | + vertex.color = polygon.shared; |
| 102 | + return indexer.add(vertex); |
| 103 | + }); |
| 104 | + for (var i = 2; i < indices.length; i++) { |
| 105 | + mesh.triangles.push([indices[0], indices[i - 1], indices[i]]); |
| 106 | + } |
| 107 | + }); |
| 108 | + mesh.vertices = indexer.unique.map(function(v) { return [v.pos.x, v.pos.y, v.pos.z]; }); |
| 109 | + mesh.normals = indexer.unique.map(function(v) { return [v.normal.x, v.normal.y, v.normal.z]; }); |
| 110 | + mesh.colors = indexer.unique.map(function(v) { return v.color; }); |
| 111 | + mesh.computeWireframe(); |
| 112 | + return mesh; |
| 113 | +}; |
| 114 | + |
| 115 | +var angleX = 20; |
| 116 | +var angleY = 20; |
| 117 | +var viewers = []; |
| 118 | + |
| 119 | +// A viewer is a WebGL canvas that lets the user view a mesh. The user can |
| 120 | +// tumble it around by dragging the mouse. |
| 121 | +function Viewer(csg, width, height, depth) { |
| 122 | + viewers.push(this); |
| 123 | + |
| 124 | + // Get a new WebGL canvas |
| 125 | + var gl = GL.create(); |
| 126 | + this.gl = gl; |
| 127 | + this.mesh = csg.toMesh(); |
| 128 | + |
| 129 | + // Set up the viewport |
| 130 | + gl.canvas.width = width; |
| 131 | + gl.canvas.height = height; |
| 132 | + gl.viewport(0, 0, width, height); |
| 133 | + gl.matrixMode(gl.PROJECTION); |
| 134 | + gl.loadIdentity(); |
| 135 | + gl.perspective(45, width / height, 0.1, 100); |
| 136 | + gl.matrixMode(gl.MODELVIEW); |
| 137 | + |
| 138 | + // Set up WebGL state |
| 139 | + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); |
| 140 | + gl.clearColor(0.93, 0.93, 0.93, 1); |
| 141 | + gl.enable(gl.DEPTH_TEST); |
| 142 | + gl.enable(gl.CULL_FACE); |
| 143 | + gl.polygonOffset(1, 1); |
| 144 | + |
| 145 | + // Black shader for wireframe |
| 146 | + this.blackShader = new GL.Shader('\ |
| 147 | + void main() {\ |
| 148 | + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\ |
| 149 | + }\ |
| 150 | + ', '\ |
| 151 | + void main() {\ |
| 152 | + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.3);\ |
| 153 | + }\ |
| 154 | + '); |
| 155 | + |
| 156 | + // Shader with diffuse and specular lighting |
| 157 | + this.lightingShader = new GL.Shader('\ |
| 158 | + varying vec3 color;\ |
| 159 | + varying vec3 normal;\ |
| 160 | + varying vec3 light;\ |
| 161 | + void main() {\ |
| 162 | + const vec3 lightDir = vec3(1.0, 2.0, 3.0) / 3.741657386773941;\ |
| 163 | + light = (gl_ModelViewMatrix * vec4(lightDir, 0.0)).xyz;\ |
| 164 | + color = gl_Color.rgb;\ |
| 165 | + normal = gl_NormalMatrix * gl_Normal;\ |
| 166 | + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\ |
| 167 | + }\ |
| 168 | + ', '\ |
| 169 | + varying vec3 color;\ |
| 170 | + varying vec3 normal;\ |
| 171 | + varying vec3 light;\ |
| 172 | + void main() {\ |
| 173 | + vec3 n = normalize(normal);\ |
| 174 | + float diffuse = max(0.0, dot(light, n));\ |
| 175 | + float specular = pow(max(0.0, -reflect(light, n).z), 32.0) * sqrt(diffuse);\ |
| 176 | + gl_FragColor = vec4(mix(color * (0.3 + 0.7 * diffuse), vec3(1.0), specular), 1.0);\ |
| 177 | + }\ |
| 178 | + '); |
| 179 | + |
| 180 | + gl.onmousemove = function(e) { |
| 181 | + if (e.dragging) { |
| 182 | + angleY += e.deltaX * 2; |
| 183 | + angleX += e.deltaY * 2; |
| 184 | + angleX = Math.max(-90, Math.min(90, angleX)); |
| 185 | + |
| 186 | + viewers.map(function(viewer) { |
| 187 | + viewer.gl.ondraw(); |
| 188 | + }); |
| 189 | + } |
| 190 | + }; |
| 191 | + |
| 192 | + var that = this; |
| 193 | + gl.ondraw = function() { |
| 194 | + gl.makeCurrent(); |
| 195 | + |
| 196 | + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| 197 | + gl.loadIdentity(); |
| 198 | + gl.translate(0, 0, -depth); |
| 199 | + gl.rotate(angleX, 1, 0, 0); |
| 200 | + gl.rotate(angleY, 0, 1, 0); |
| 201 | + |
| 202 | + gl.enable(gl.POLYGON_OFFSET_FILL); |
| 203 | + that.lightingShader.draw(that.mesh, gl.TRIANGLES); |
| 204 | + gl.disable(gl.POLYGON_OFFSET_FILL); |
| 205 | + |
| 206 | + gl.enable(gl.BLEND); |
| 207 | + that.blackShader.draw(that.mesh, gl.LINES); |
| 208 | + gl.disable(gl.BLEND); |
| 209 | + }; |
| 210 | + |
| 211 | + gl.ondraw(); |
| 212 | +} |
| 213 | + |
| 214 | +var nextID = 0; |
| 215 | +function addViewer(viewer) { |
| 216 | + document.getElementById(nextID++).appendChild(viewer.gl.canvas); |
| 217 | +} |
| 218 | + |
| 219 | +// Test all coplanar cases |
| 220 | +var a = CSG.cube(); |
| 221 | +var b = CSG.cylinder({ slices: 8, start: new CSG.Vector(-1, 0, 0), end: new CSG.Vector(1, 0, 0) }); |
| 222 | +b = b.union(CSG.cylinder({ slices: 8, start: new CSG.Vector(0, -1, 0), end: new CSG.Vector(0, 1, 0) })); |
| 223 | +b = b.union(CSG.cylinder({ slices: 8, start: new CSG.Vector(0, 0, -1), end: new CSG.Vector(0, 0, 1) })); |
| 224 | +var c = CSG.cube({ radius: 0.5 }); |
| 225 | +var d = CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(-1, 0, 0), end: new CSG.Vector(-0.5, 0, 0) }); |
| 226 | +d = d.union(CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(0.5, 0, 0), end: new CSG.Vector(1, 0, 0) })); |
| 227 | +d = d.union(CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(0, -1, 0), end: new CSG.Vector(0, -0.5, 0) })); |
| 228 | +d = d.union(CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(0, 0.5, 0), end: new CSG.Vector(0, 1, 0) })); |
| 229 | +d = d.union(CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(0, 0, -1), end: new CSG.Vector(0, 0, -0.5) })); |
| 230 | +d = d.union(CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(0, 0, 0.5), end: new CSG.Vector(0, 0, 1) })); |
| 231 | +a.setColor(1, 0, 0); |
| 232 | +b.setColor(0, 0, 1); |
| 233 | +c.setColor(0, 1, 0); |
| 234 | +d.setColor(1, 1, 0); |
| 235 | +var operations = [ |
| 236 | + a, |
| 237 | + b, |
| 238 | + a.inverse(), |
| 239 | + b.inverse(), |
| 240 | + a.union(b), |
| 241 | + b.union(a), |
| 242 | + a.subtract(b), |
| 243 | + b.subtract(a), |
| 244 | + a.intersect(b), |
| 245 | + b.intersect(a), |
| 246 | + c, |
| 247 | + d, |
| 248 | + c.inverse(), |
| 249 | + d.inverse(), |
| 250 | + c.union(d), |
| 251 | + d.union(c), |
| 252 | + c.subtract(d), |
| 253 | + d.subtract(c), |
| 254 | + c.intersect(d), |
| 255 | + d.intersect(c) |
| 256 | +]; |
| 257 | +for (var i = 0; i < operations.length; i++) { |
| 258 | + addViewer(new Viewer(operations[i], 200, 200, 6)); |
| 259 | +} |
| 260 | + |
| 261 | + </script> |
| 262 | +</body></html> |
0 commit comments