diff --git a/web/app/engine.js b/web/app/engine.js index 66eb323f..b72433d1 100644 --- a/web/app/engine.js +++ b/web/app/engine.js @@ -18,8 +18,23 @@ TCAD.utils.createSquare = function(width) { TCAD.utils.createBox = function(width) { var square = TCAD.utils.createSquare(width); var rot = TCAD.math.rotateMatrix(3/4, TCAD.math.AXIS.Z, TCAD.math.ORIGIN); - square.eachVertex(function(path, i) { rot._apply(path[i]) } ) - return TCAD.geom.extrude(square, square.normal.multiply(width)); + square.eachVertex(function(path, i) { rot._apply(path[i]) } ); + var polygons = TCAD.geom.extrude(square, square.normal.multiply(width)); + return TCAD.utils.createSolidMesh(TCAD.utils.toCsgGroups(polygons)); +}; + +TCAD.utils.toCsgGroups = function(polygons) { + var groups = []; + for (var i = 0; i < polygons.length; i++) { + var p = polygons[i]; + if (p.holes.length === 0) { + groups.push( new TCAD.CSGGroup([new TCAD.SimplePolygon(p.shell, p.normal)], p.normal) ); + } else { + // TODO: triangulation needed + groups.push( new TCAD.CSGGroup([new TCAD.SimplePolygon(p.shell, p.normal)], p.normal) ); + } + } + return groups; }; TCAD.utils.checkPolygon = function(poly) { @@ -79,7 +94,7 @@ TCAD.utils.createLine = function (a, b, color) { return new THREE.Segment(geometry, material); }; -TCAD.utils.createSolidMesh = function(faces) { +TCAD.utils.createSolidMesh = function(csgGroups) { var material = new THREE.MeshPhongMaterial({ vertexColors: THREE.FaceColors, color: TCAD.view.FACE_COLOR, @@ -89,7 +104,7 @@ TCAD.utils.createSolidMesh = function(faces) { polygonOffsetUnits : 1 }); - var geometry = new TCAD.Solid(faces, material); + var geometry = new TCAD.Solid(csgGroups, material); return geometry.meshObject; }; @@ -328,16 +343,79 @@ TCAD.geom.extrude = function(source, target) { TCAD.geom.SOLID_COUNTER = 0; +TCAD.SimplePolygon = function(vertices, normal) { + this.vertices = vertices; + this.normal = normal; +}; + +TCAD.SimplePolygon.prototype.triangulate = function() { + var _3dTransformation = new TCAD.Matrix().setBasis(TCAD.geom.someBasis(this.vertices, this.normal)); + var _2dTransformation = _3dTransformation.invert(); + var i; + var shell = []; + for (i = 0; i < this.vertices.length; ++i) { + shell[i] = _2dTransformation.apply(this.vertices[i]); + } + // + //for (i = 0; i < shell.length; ++i) { + // shell[i] = shell[i].three(); + //} + + var myTriangulator = new PNLTRI.Triangulator(); + return myTriangulator.triangulate_polygon( [ shell ] ); +// return THREE.Shape.utils.triangulateShape( f2d.shell, f2d.holes ); +}; + +TCAD.GROUPS_COUNTER = 0; + +TCAD.CSGGroup = function(simplePolygons, normal, w) { + this.id = TCAD.GROUPS_COUNTER ++; + this.polygons = simplePolygons; + this.normal = normal; + this.w = w || normal.dot(this.polygons[0].vertices[0]); +}; + +TCAD.CSGGroup.prototype.basis = function() { + return TCAD.geom.someBasis(this.polygons[0].vertices, this.normal); +}; + +TCAD.CSGGroup.prototype.toCSGPolygons = function() { + function csgVec(v) { + return new CSG.Vector3D(v.x, v.y, v.z); + } + var csgPolygons = []; + var pid = this.id; + var shared = new CSG.Polygon.Shared([pid, pid, pid, pid]); + shared.__tcad = { + csgInfo : this.csgInfo, + face : this.__face + }; + var plane = CSG.Plane.fromObject(this); + var vertices = []; + for (var pi = 0; pi < this.polygons.length; ++pi) { + var poly = this.polygons[pi]; + for (var vi = 0; vi < poly.vertices.length; vi++) { + var v = poly.vertices[vi]; + vertices.push(new CSG.Vertex(csgVec(v))); + } + csgPolygons.push(new CSG.Polygon(vertices, shared, plane)); + } + return csgPolygons; +}; + /** @constructor */ -TCAD.Solid = function(polygons, material) { +TCAD.Solid = function(csgPolygonGroups, material) { THREE.Geometry.call( this ); this.dynamic = true; //true by default - + this.meshObject = new THREE.Mesh(this, material); - this.id = TCAD.geom.SOLID_COUNTER ++; + this.tCadId = TCAD.geom.SOLID_COUNTER ++; this.faceCounter = 0; + this.wireframeGroup = new THREE.Object3D(); + this.meshObject.add(this.wireframeGroup); + this.polyFaces = []; var scope = this; function pushVertices(vertices) { @@ -347,52 +425,50 @@ TCAD.Solid = function(polygons, material) { } var off = 0; - for (var p = 0; p < polygons.length; ++ p) { - var poly = polygons[p]; - try { - var faces = poly.triangulate(); - } catch(e) { - console.log(e); - continue; - } - pushVertices(poly.shell); - for ( var h = 0; h < poly.holes.length; ++ h ) { - pushVertices(poly.holes[ h ]); - } - var polyFace = new TCAD.SketchFace(this, poly); + for (var gIdx = 0; gIdx < csgPolygonGroups.length; ++ gIdx) { + var group = csgPolygonGroups[gIdx]; + var polyFace = new TCAD.SketchFace(this, group); this.polyFaces.push(polyFace); - for ( var i = 0; i < faces.length; ++ i ) { - - var a = faces[i][0] + off; - var b = faces[i][1] + off; - var c = faces[i][2] + off; - - var fNormal = TCAD.geom.normalOfCCWSeqTHREE([ - this.vertices[a], this.vertices[b], this.vertices[c]]); - - if (!TCAD.utils.vectorsEqual(fNormal, poly.normal)) { - console.log("ASSERT"); - var _a = a; - a = c; - c = _a; + for (var p = 0; p < group.polygons.length; ++p) { + var poly = group.polygons[p]; + try { + var faces = poly.triangulate(); + } catch (e) { + console.log(e); + continue; } - - var face = new THREE.Face3( a, b, c ); - polyFace.faces.push(face); - face.__TCAD_polyFace = polyFace; - face.normal = poly.normal.three(); - face.materialIndex = p; - this.faces.push( face ); - } - off = this.vertices.length; - } + pushVertices(poly.vertices); + for (var i = 0; i < faces.length; ++i) { + + var a = faces[i][0] + off; + var b = faces[i][1] + off; + var c = faces[i][2] + off; + + var fNormal = TCAD.geom.normalOfCCWSeqTHREE([ + this.vertices[a], this.vertices[b], this.vertices[c]]); + + if (!TCAD.utils.vectorsEqual(fNormal, poly.normal)) { + console.log("ASSERT"); + var _a = a; + a = c; + c = _a; + } + + var face = new THREE.Face3(a, b, c); + polyFace.faces.push(face); + face.__TCAD_polyFace = polyFace; + face.normal = poly.normal.three(); + face.materialIndex = gIdx; + this.faces.push(face); + } + off = this.vertices.length; + } + } this.mergeVertices(); - this.wireframeGroup = new THREE.Object3D(); - this.meshObject.add(this.wireframeGroup); - this.makeWireframe(polygons); + //this.makeWireframe(polygons); }; if (typeof THREE !== "undefined") { @@ -428,11 +504,11 @@ TCAD.Solid.prototype.makeWireframe = function(polygons) { }; /** @constructor */ -TCAD.SketchFace = function(solid, poly) { - var proto = poly.__face; - poly.__face = this; +TCAD.SketchFace = function(solid, csgGroup) { + var proto = csgGroup.__face; + csgGroup.__face = this; if (proto === undefined) { - this.id = solid.id + ":" + (solid.faceCounter++); + this.id = solid.tCadId + ":" + (solid.faceCounter++); this.sketchGeom = null; } else { this.id = proto.id; @@ -440,7 +516,7 @@ TCAD.SketchFace = function(solid, poly) { } this.solid = solid; - this.polygon = poly; + this.csgGroup = csgGroup; this.faces = []; this.sketch3DGroup = null; @@ -458,7 +534,7 @@ if (typeof THREE !== "undefined") { TCAD.SketchFace.prototype.syncSketches = function(geom) { var i; - var normal = this.polygon.normal; + var normal = this.csgGroup.normal; var offVector = normal.multiply(0); // disable it. use polygon offset feature of material if (this.sketch3DGroup != null) { @@ -470,9 +546,9 @@ TCAD.SketchFace.prototype.syncSketches = function(geom) { this.solid.meshObject.add(this.sketch3DGroup); } - var _3dTransformation = new TCAD.Matrix().setBasis(TCAD.geom.someBasis(this.polygon.shell, normal)); + var _3dTransformation = new TCAD.Matrix().setBasis(this.csgGroup.basis()); //we lost depth or z off in 2d sketch, calculate it again - var depth = normal.dot(this.polygon.shell[0]); + var depth = normal.dot(this.csgGroup.polygons[0].vertices[0]); for (i = 0; i < geom.connections.length; ++i) { var l = geom.connections[i]; var lg = new THREE.Geometry(); diff --git a/web/app/workbench.js b/web/app/workbench.js index 35728495..5ef20eff 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -82,7 +82,7 @@ TCAD.craft.getSketchedPolygons3D = function(app, face) { var geom = TCAD.workbench.readSketchGeom(JSON.parse(savedFace)); var polygons2D = TCAD.utils.sketchToPolygons(geom); - var normal = face.polygon.normal; + var normal = face.csgGroup.normal; var depth = null; var sketchedPolygons = []; for (var i = 0; i < polygons2D.length; i++) { @@ -90,9 +90,9 @@ TCAD.craft.getSketchedPolygons3D = function(app, face) { if (poly2D.shell.length < 3) continue; if (depth == null) { - var _3dTransformation = new TCAD.Matrix().setBasis(TCAD.geom.someBasis(face.polygon.shell, normal)); + var _3dTransformation = new TCAD.Matrix().setBasis(face.csgGroup.basis()); //we lost depth or z off in 2d sketch, calculate it again - depth = normal.dot(face.polygon.shell[0]); + depth = normal.dot(face.csgGroup.polygons[0].vertices[0]); } var shell = []; @@ -707,27 +707,84 @@ TCAD.craft.collectFaces = function(solids) { return faces; }; +TCAD.craft.collectCSGPolygons = function(faces) { + var out = []; + for (var fi = 0; fi < faces.length; fi++) { + var face = faces[fi]; + TCAD.utils.addAll(out, face.csgGroup.toCSGPolygons()); + } + return out; +}; + +TCAD.craft.toGroups = function(csgPolygons) { + + function vec(p) { + var v = new TCAD.Vector(); + v.setV(p); + return v; + } + + var byShared = {}; + var infos = {}; + for (var i = 0; i < csgPolygons.length; i++) { + var p = csgPolygons[i]; + var tag = p.shared.getTag(); + infos[tag] = p.shared; + if (byShared[tag] === undefined) byShared[tag] = []; + byShared[tag].push(p); + } + var result = []; + for (var tag in byShared) { + var groupedPolygons = byShared[tag]; + if (groupedPolygons.length === 0) continue; + var plane = groupedPolygons[0].plane; + var normal = vec(plane.normal); + + var simplePolygons = groupedPolygons.map(function (p) { + + var vertices = p.vertices.map(function (v) { + return vec(v.pos); + }); + return new TCAD.SimplePolygon(vertices, normal); + }); + var csgGroup = new TCAD.CSGGroup(simplePolygons, normal, plane.w); + var tcadShared = infos[tag].__tcad; + if (tcadShared !== undefined) { + csgGroup.csgInfo = tcadShared.csgInfo; + csgGroup.__face = tcadShared.face; + } + result.push(csgGroup); + } + return result; +}; + TCAD.craft.cut = function(app, request) { var face = request.face; var sketchedPolygons = TCAD.craft.getSketchedPolygons3D(app, face); if (sketchedPolygons == null) return null; //face.polygon.__face = undefined; + var work; + if (!!request.solids[0].__cached_csg) { + work = request.solids[0].__cached_csg; + } else { + var faces = TCAD.craft.collectFaces(request.solids); + work = CSG.fromPolygons(TCAD.craft.collectCSGPolygons(faces)); + } - var faces = TCAD.craft.collectFaces(request.solids); - - var normal = face.polygon.normal; + var normal = face.csgGroup.normal; var cutter = []; for (var i = 0; i < sketchedPolygons.length; i++) { var extruded = TCAD.geom.extrude(sketchedPolygons[i], normal.multiply( - request.depth)); cutter = cutter.concat(TCAD.craft._makeFromPolygons(extruded)); } - var work = TCAD.craft._makeFromPolygons(faces.map(function(f){ return f.polygon })); - var cut = CSG.fromPolygons(work).subtract(CSG.fromPolygons(cutter)); + var cut = work.subtract(CSG.fromPolygons(cutter)); - return TCAD.craft.reconstruct(cut); + var solid = TCAD.utils.createSolidMesh(TCAD.craft.toGroups(cut.polygons)).geometry; + //solid.__cached_csg = cut; + return solid; }; TCAD.Craft = function(app) { @@ -760,14 +817,14 @@ TCAD.Craft.prototype.modify = function(request) { if (!op) return; var detachedRequest = TCAD.craft.detach(request); - var newFaces = op(this.app, request); + var newGroups = op(this.app, request); - if (newFaces == null) return; + if (newGroups == null) return; for (var i = 0; i < request.solids.length; i++) { var solid = request.solids[i]; this.app.viewer.scene.remove( solid.meshObject ); } - this.app.viewer.scene.add(TCAD.utils.createSolidMesh(newFaces)); + this.app.viewer.scene.add(newGroups.meshObject); this.history.push(detachedRequest); this.app.bus.notify('craft'); //REMOVE IT @@ -780,6 +837,6 @@ TCAD.craft.OPS = { CUT : TCAD.craft.cut, PAD : TCAD.craft.extrude, BOX : function(app, request) { - return TCAD.utils.createBox(request.size); + return TCAD.utils.createBox(request.size).geometry; } };