From 07cc007e20be8b4393ea9467ab4ee14b23084044 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 4 Sep 2015 00:49:17 -0700 Subject: [PATCH 01/21] csg experiment --- web/app/engine.js | 186 ++++++++++++++++++++++++++++++------------- web/app/workbench.js | 83 ++++++++++++++++--- 2 files changed, 201 insertions(+), 68 deletions(-) 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; } }; From 2780b03e69541d049789d40d0fb22223b151099e Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 4 Sep 2015 17:02:54 -0700 Subject: [PATCH 02/21] always use csg object --- web/app/engine.js | 131 ++++++++++++++++++++++++++++--------------- web/app/workbench.js | 30 ++++------ 2 files changed, 96 insertions(+), 65 deletions(-) diff --git a/web/app/engine.js b/web/app/engine.js index b72433d1..cee628bf 100644 --- a/web/app/engine.js +++ b/web/app/engine.js @@ -15,12 +15,32 @@ TCAD.utils.createSquare = function(width) { return new TCAD.Polygon(shell); }; +TCAD.utils.csgVec = function(v) { + return new CSG.Vector3D(v.x, v.y, v.z); +}; + +TCAD.utils.vec = function(v) { + return new TCAD.Vector(v.x, v.y, v.z); +}; + 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]) } ); var polygons = TCAD.geom.extrude(square, square.normal.multiply(width)); - return TCAD.utils.createSolidMesh(TCAD.utils.toCsgGroups(polygons)); + return polygons; +}; + +TCAD.utils.createCSGBox = function(width) { + var csg = CSG.fromPolygons(TCAD.utils.createBox(width).map(function (p) { + var vertices = []; + for (var vi = 0; vi < p.shell.length; vi++) { + var v = p.shell[vi]; + vertices.push(new CSG.Vertex(TCAD.utils.csgVec(v))); + } + return new CSG.Polygon(vertices, TCAD.utils.createShared(p.id)); + })); + return TCAD.utils.createSolidMesh(csg); }; TCAD.utils.toCsgGroups = function(polygons) { @@ -94,7 +114,7 @@ TCAD.utils.createLine = function (a, b, color) { return new THREE.Segment(geometry, material); }; -TCAD.utils.createSolidMesh = function(csgGroups) { +TCAD.utils.createSolidMesh = function(csg) { var material = new THREE.MeshPhongMaterial({ vertexColors: THREE.FaceColors, color: TCAD.view.FACE_COLOR, @@ -104,7 +124,7 @@ TCAD.utils.createSolidMesh = function(csgGroups) { polygonOffsetUnits : 1 }); - var geometry = new TCAD.Solid(csgGroups, material); + var geometry = new TCAD.Solid(csg, material); return geometry.meshObject; }; @@ -403,9 +423,35 @@ TCAD.CSGGroup.prototype.toCSGPolygons = function() { return csgPolygons; }; +TCAD.utils.groupCSG = function(csg) { + var csgPolygons = csg.toPolygons(); + var groups = {}; + for (var i = 0; i < csgPolygons.length; i++) { + var p = csgPolygons[i]; + var tag = p.shared.getTag(); + if (groups[tag] === undefined) { + groups[tag] = { + tag : tag, + polygons : [], + shared : p.shared, + plane : p.plane + }; + } + groups[tag].polygons.push(p); + } + return groups; +}; + +TCAD.utils.createShared = function(id) { + var shared = new CSG.Polygon.Shared([id, id, id, id]); + shared.__tcad = {}; + return shared; +}; + /** @constructor */ -TCAD.Solid = function(csgPolygonGroups, material) { +TCAD.Solid = function(csg, material) { THREE.Geometry.call( this ); + this.csg = csg; this.dynamic = true; //true by default this.meshObject = new THREE.Mesh(this, material); @@ -418,54 +464,40 @@ TCAD.Solid = function(csgPolygonGroups, material) { this.polyFaces = []; var scope = this; - function pushVertices(vertices) { - for ( var v = 0; v < vertices.length; ++ v ) { - scope.vertices.push( new THREE.Vector3( vertices[v].x, vertices[v].y, vertices[v].z ) ); - } - } + function threeV(v) {return new THREE.Vector3( v.x, v.y, v.z )} var off = 0; - for (var gIdx = 0; gIdx < csgPolygonGroups.length; ++ gIdx) { - var group = csgPolygonGroups[gIdx]; + var groups = TCAD.utils.groupCSG(csg); + for (var gIdx in groups) { + var group = groups[gIdx]; + if (group.shared.__tcad === undefined) group.shared.__tcad = {}; var polyFace = new TCAD.SketchFace(this, group); this.polyFaces.push(polyFace); - 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; - } - 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 vLength = poly.vertices.length; + if (vLength < 3) continue; + var firstVertex = poly.vertices[0]; + this.vertices.push(threeV(firstVertex.pos)); + this.vertices.push(threeV(poly.vertices[1].pos)); + var normal = threeV(poly.plane.normal); + for (var i = 2; i < vLength; i++) { + this.vertices.push(threeV(poly.vertices[i].pos)); + var a = off; + var b = i - 1 + off; + var c = i + off; var face = new THREE.Face3(a, b, c); polyFace.faces.push(face); face.__TCAD_polyFace = polyFace; - face.normal = poly.normal.three(); + face.normal = normal; face.materialIndex = gIdx; this.faces.push(face); } off = this.vertices.length; } } + this.mergeVertices(); //this.makeWireframe(polygons); @@ -505,16 +537,15 @@ TCAD.Solid.prototype.makeWireframe = function(polygons) { /** @constructor */ TCAD.SketchFace = function(solid, csgGroup) { - var proto = csgGroup.__face; csgGroup.__face = this; - if (proto === undefined) { + this.sketchGeom = null; + if (csgGroup.shared.__tcad.faceId === undefined) { this.id = solid.tCadId + ":" + (solid.faceCounter++); - this.sketchGeom = null; } else { - this.id = proto.id; - this.sketchGeom = proto.sketchGeom; + this.id = csgGroup.shared.__tcad.faceId; } - + csgGroup.shared.__tcad.faceId = this.id; + this.solid = solid; this.csgGroup = csgGroup; this.faces = []; @@ -532,10 +563,17 @@ if (typeof THREE !== "undefined") { color: 0x2B3856, linewidth: 3}); } +TCAD.SketchFace.prototype.basis = function() { + var vec = TCAD.utils.vec; + return TCAD.geom.someBasis(this.csgGroup.polygons[0].vertices.map(function (v) { + return vec(v.pos) + }), vec(this.csgGroup.plane.normal)); +} + TCAD.SketchFace.prototype.syncSketches = function(geom) { var i; - var normal = this.csgGroup.normal; - var offVector = normal.multiply(0); // disable it. use polygon offset feature of material + var normal = this.csgGroup.plane.normal; + var offVector = normal.scale(0); // disable it. use polygon offset feature of material if (this.sketch3DGroup != null) { for (var i = this.sketch3DGroup.children.length - 1; i >= 0; --i) { @@ -546,9 +584,10 @@ TCAD.SketchFace.prototype.syncSketches = function(geom) { this.solid.meshObject.add(this.sketch3DGroup); } - var _3dTransformation = new TCAD.Matrix().setBasis(this.csgGroup.basis()); + var basis = this.basis(); + var _3dTransformation = new TCAD.Matrix().setBasis(basis); //we lost depth or z off in 2d sketch, calculate it again - var depth = normal.dot(this.csgGroup.polygons[0].vertices[0]); + var depth = this.csgGroup.plane.w; 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 5ef20eff..f6321c9d 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -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(face.csgGroup.basis()); + var _3dTransformation = new TCAD.Matrix().setBasis(face.basis()); //we lost depth or z off in 2d sketch, calculate it again - depth = normal.dot(face.csgGroup.polygons[0].vertices[0]); + depth = face.csgGroup.plane.w; } var shell = []; @@ -494,11 +494,10 @@ TCAD.craft._makeFromPolygons = function(polygons) { Array.prototype.push.apply( points, poly.holes[h] ); } var pid = poly.id; - var shared = new CSG.Polygon.Shared([pid, pid, pid, pid]); - shared.__tcad = { - csgInfo : poly.csgInfo, - face : poly.__face - }; + var shared = TCAD.utils.createShared(pid); + shared.__tcad.csgInfo = poly.csgInfo; + //shared.__tcad.faceId = poly.__face.id; + var refs = poly.triangulate(); for ( var i = 0; i < refs.length; ++ i ) { var a = refs[i][0] + off; @@ -763,16 +762,10 @@ TCAD.craft.cut = function(app, request) { 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)); - } + //face.c.__face = undefined; + var work = request.solids[0].csg; - var normal = face.csgGroup.normal; + var normal = TCAD.utils.vec(face.csgGroup.plane.normal); var cutter = []; for (var i = 0; i < sketchedPolygons.length; i++) { var extruded = TCAD.geom.extrude(sketchedPolygons[i], normal.multiply( - request.depth)); @@ -782,8 +775,7 @@ TCAD.craft.cut = function(app, request) { var cut = work.subtract(CSG.fromPolygons(cutter)); - var solid = TCAD.utils.createSolidMesh(TCAD.craft.toGroups(cut.polygons)).geometry; - //solid.__cached_csg = cut; + var solid = TCAD.utils.createSolidMesh(cut).geometry; return solid; }; @@ -837,6 +829,6 @@ TCAD.craft.OPS = { CUT : TCAD.craft.cut, PAD : TCAD.craft.extrude, BOX : function(app, request) { - return TCAD.utils.createBox(request.size).geometry; + return TCAD.utils.createCSGBox(request.size).geometry; } }; From 4843578e5ad5c5400a79c6ae1f6e2f4bc118abfb Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 4 Sep 2015 18:27:29 -0700 Subject: [PATCH 03/21] get back sketching faces --- web/app/3d/main.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/web/app/3d/main.js b/web/app/3d/main.js index 6effa6e1..21b3ff75 100644 --- a/web/app/3d/main.js +++ b/web/app/3d/main.js @@ -70,11 +70,16 @@ TCAD.App.prototype.sketchFace = function() { } return a.sketchConnectionObject.id === b.sketchConnectionObject.id; } - var paths = []; - polyFace.polygon.collectPaths(paths); - var _2dTr = polyFace.polygon.get2DTransformation(); + + var paths = TCAD.craft._mergeCSGPolygons(polyFace.csgGroup.polygons, []); + + //polyFace.polygon.collectPaths(paths); + var _3dTransformation = new TCAD.Matrix().setBasis(polyFace.basis()); + var _2dTr = _3dTransformation.invert(); + + for (var i = 0; i < paths.length; i++) { - var path = paths[i]; + var path = paths[i].vertices; var shift = 0; TCAD.utils.iteratePath(path, 0, function(a, b, ai, bi) { shift = bi; From b10749c56696ea1384b088b555e0ace6336783ac Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 4 Sep 2015 21:23:11 -0700 Subject: [PATCH 04/21] remove junk --- web/app/workbench.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/web/app/workbench.js b/web/app/workbench.js index f6321c9d..6f35e153 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -145,27 +145,6 @@ TCAD.craft._pointOnLine = function(p, a, b) { return apLength > 0 && apLength < abLength && TCAD.utils.areEqual(abLength * apLength, dp, 1E-20); }; -TCAD.craft._mergeCSGPolygonsTest = function() { - - function cppol(points) { - return { - vertices : points.map(function(e) { - return new TCAD.Vector(e[0], e[1], 0); - }), - normal : new TCAD.Vector(0,0,1) - } - - } - var paths = TCAD.craft._mergeCSGPolygons( - [ - cppol([0,0], [50,0], [50,10], [0,10]), - cppol([0,10], [50,10], [50,60], [0,60]), - cppol([0,60], [50,60], [50,100], [0,100]) - ] - ); - console.log(paths); -}; - TCAD.craft._mergeCSGPolygons = function (__cgsPolygons, allPoints) { function vec(p) { From d59842ef36b80f8f9c0158ef65866e56d5589602 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 4 Sep 2015 21:23:46 -0700 Subject: [PATCH 05/21] remove junk --- web/app/workbench.js | 53 -------------------------------------------- 1 file changed, 53 deletions(-) diff --git a/web/app/workbench.js b/web/app/workbench.js index 6f35e153..2008b50a 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -406,59 +406,6 @@ TCAD.craft._mergeCSGPolygons = function (__cgsPolygons, allPoints) { return filteredPaths; }; - - -TCAD.craft._mergeCSGPolygonsTest0 = function(data) { - TCAD.craft._mergeCSGPolygonsTester( - [ - [[0,0], [50,0], [50,10], [0,10]], - [[0,10], [50,10], [50,60], [0,60]], - [[0,60], [50,60], [50,100], [0,100]] - ] - ); -}; - -TCAD.craft._mergeCSGPolygonsTest1 = function(data) { - TCAD.craft._mergeCSGPolygonsTester( - [ - [[0,0], [50,0], [50,10], [0,10]], - [[0,10], [20,10], [20,60], [0,60]], - [[20,10], [30,10], [30,60], [20,60]], - [[40,10], [50,10], [50,60], [40,60]], - [[0,60], [50,60], [50,100], [0,100]] - ] - ); -}; - -TCAD.craft._mergeCSGPolygonsTest2 = function(data) { - TCAD.craft._mergeCSGPolygonsTester( - [ - [[0,0], [25,0], [50,0], [50,10], [0,10]], - [[0,10], [20,10], [20,60], [0,60]], - [[20,10], [30,10], [30,60], [20,60]], - [[40,10], [50,10], [50,60], [40,60]], - [[0,60], [50,60], [50,100], [0,100]] - ] - ); -}; - -TCAD.craft._mergeCSGPolygonsTester = function(data) { - - function cppol(points) { - return { - vertices : points.map(function(e) { - return {pos: new TCAD.Vector(e[0], e[1], 0)}; - }), - plane: {normal : new TCAD.Vector(0,0,1), w : 0} - - } - - } - var paths = TCAD.craft._mergeCSGPolygons(data.map(function(p) { - return cppol(p); - })); -}; - TCAD.craft._makeFromPolygons = function(polygons) { function csgVec(v) { return new CSG.Vector3D(v.x, v.y, v.z); From b0d5a087f037a6a75b34c6f9d4a20e7789c8a1d2 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Sun, 6 Sep 2015 00:24:57 -0700 Subject: [PATCH 06/21] different algo for reconstruct auxiliary edges --- web/app/3d/main.js | 2 +- web/app/workbench.js | 103 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/web/app/3d/main.js b/web/app/3d/main.js index 21b3ff75..62893946 100644 --- a/web/app/3d/main.js +++ b/web/app/3d/main.js @@ -71,7 +71,7 @@ TCAD.App.prototype.sketchFace = function() { return a.sketchConnectionObject.id === b.sketchConnectionObject.id; } - var paths = TCAD.craft._mergeCSGPolygons(polyFace.csgGroup.polygons, []); + var paths = TCAD.craft.reconstructSketchBounds(polyFace.solid.csg, polyFace); //polyFace.polygon.collectPaths(paths); var _3dTransformation = new TCAD.Matrix().setBasis(polyFace.basis()); diff --git a/web/app/workbench.js b/web/app/workbench.js index 2008b50a..fc7f68b6 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -145,6 +145,109 @@ TCAD.craft._pointOnLine = function(p, a, b) { return apLength > 0 && apLength < abLength && TCAD.utils.areEqual(abLength * apLength, dp, 1E-20); }; +TCAD.craft.reconstructSketchBounds = function(csg, face) { + + var polygons = csg.toPolygons(); + var plane = face.csgGroup.plane; + var sketchSegments = []; + for (var pi = 0; pi < polygons.length; pi++) { + var poly = polygons[pi]; + if (poly.plane.equals(plane)) { + continue; + } + var p, q, n = poly.vertices.length; + for(p = n - 1, q = 0; q < n; p = q ++) { + var a = poly.vertices[p]; + var b = poly.vertices[q]; + var ab = b.pos.minus(a.pos); + var parallelTpPlane = TCAD.utils.equal(ab.unit().dot(plane.normal), 0); + var pointOnPlane = TCAD.utils.equal(plane.signedDistanceToPoint(a.pos), 0); + if (parallelTpPlane && pointOnPlane) { + sketchSegments.push([a.pos, b.pos, poly]); + } + } + } + return TCAD.craft.segmentsToPaths(sketchSegments); +}; + +TCAD.craft.segmentsToPaths = function(segments) { + + var veq = TCAD.struct.hashTable.vectorEquals; + + var paths = []; + var csgDatas = []; + var index = TCAD.struct.hashTable.forVector3d(); + + function indexPoint(p, edge) { + var edges = index.get(p); + if (edges === null) { + edges = []; + index.put(p, edges); + } + edges.push(edge); + } + + for (var si = 0; si < segments.length; si++) { + var k = segments[si]; + indexPoint(k[0], k); + indexPoint(k[1], k); + k[3] = false; + } + + function nextPoint(p) { + var edges = index.get(p); + if (edges === null) return null; + for (var i = 0; i < edges.length; i++) { + var edge = edges[i] + if (edge[3]) continue; + var res = null; + if (veq(p, edge[0])) res = edge[1]; + if (veq(p, edge[1])) res = edge[0]; + if (res != null) { + edge[3] = true; + return res; + } + } + return null; + } + + for (var ei = 0; ei < segments.length; ei++) { + var edge = segments[ei]; + if (edge[3]) { + continue; + } + edge[3] = true; + var path = [edge[0], edge[1]]; + paths.push(path); + csgDatas.push(edge[2]); + var next = nextPoint(edge[1]); + while (next !== null) { + if (!veq(next, path[0])) { + path.push(next); + next = nextPoint(next); + } else { + next = null; + } + } + } + + var filteredPaths = []; + for (var i = 0; i < paths.length; i++) { + var path = paths[i]; + var csgData = csgDatas[i]; + if (path.length > 2) { + filteredPaths.push({ + vertices : path, + normal : csgData.plane.normal, + w : csgData.plane.w, + shared : csgData.shared + }); + } + } + + return filteredPaths; +}; + TCAD.craft._mergeCSGPolygons = function (__cgsPolygons, allPoints) { function vec(p) { From d470ece71c86119167a2acee5c2bf2a3e62b2161 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Mon, 7 Sep 2015 00:17:31 -0700 Subject: [PATCH 07/21] reconstruct edges --- web/app/workbench.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/web/app/workbench.js b/web/app/workbench.js index fc7f68b6..bd2d0ae7 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -175,8 +175,8 @@ TCAD.craft.segmentsToPaths = function(segments) { var veq = TCAD.struct.hashTable.vectorEquals; var paths = []; - var csgDatas = []; var index = TCAD.struct.hashTable.forVector3d(); + var csgIndex = TCAD.struct.hashTable.forEdge(); function indexPoint(p, edge) { var edges = index.get(p); @@ -191,6 +191,10 @@ TCAD.craft.segmentsToPaths = function(segments) { var k = segments[si]; indexPoint(k[0], k); indexPoint(k[1], k); + var csgInfo = k[2]; + if (csgInfo !== undefined && csgInfo !== null) { + csgIndex.put([k[0], k[1]], csgInfo); + } k[3] = false; } @@ -219,7 +223,6 @@ TCAD.craft.segmentsToPaths = function(segments) { edge[3] = true; var path = [edge[0], edge[1]]; paths.push(path); - csgDatas.push(edge[2]); var next = nextPoint(edge[1]); while (next !== null) { if (!veq(next, path[0])) { @@ -231,16 +234,22 @@ TCAD.craft.segmentsToPaths = function(segments) { } } + TCAD.utils.iteratePath(path, 0, function(a, b) { + var fromPolygon = csgIndex.get([a, b]); + if (fromPolygon !== null) { + if (fromPolygon.shared.__tcad.csgInfo) { + a.sketchConnectionObject = fromPolygon.shared.__tcad.csgInfo.derivedFrom; + } + } + return true; + }); + var filteredPaths = []; for (var i = 0; i < paths.length; i++) { var path = paths[i]; - var csgData = csgDatas[i]; if (path.length > 2) { filteredPaths.push({ - vertices : path, - normal : csgData.plane.normal, - w : csgData.plane.w, - shared : csgData.shared + vertices : path }); } } From 0fa554a7557fddce73f38ec273de83878db19eca Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Mon, 7 Sep 2015 00:54:47 -0700 Subject: [PATCH 08/21] delete redundant points --- web/app/workbench.js | 50 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/web/app/workbench.js b/web/app/workbench.js index bd2d0ae7..3414ccc1 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -170,6 +170,32 @@ TCAD.craft.reconstructSketchBounds = function(csg, face) { return TCAD.craft.segmentsToPaths(sketchSegments); }; +TCAD.craft.deleteRedundantPoints = function(path) { + var cleanedPath = []; + //Delete redundant point + var pathLength = path.length; + for (var pi = 0; pi < pathLength; pi++) { + var bIdx = ((pi + 1) % pathLength); + var a = path[pi]; + var b = path[bIdx]; + var c = path[(pi + 2) % pathLength]; + var eq = TCAD.utils.equal; + if (!eq(a.minus(b).unit().dot(a.minus(c).unit()), 1)) { + cleanedPath.push(b); + for (var ii = 0; ii < pathLength - pi; ++ii) { + a = path[(ii + bIdx) % pathLength]; + b = path[(ii + bIdx + 1) % pathLength]; + c = path[(ii + bIdx + 2) % pathLength]; + if (!eq(a.minus(b).unit().dot(a.minus(c).unit()), 1)) { + cleanedPath.push(b); + } + } + break; + } + } + return cleanedPath; +}; + TCAD.craft.segmentsToPaths = function(segments) { var veq = TCAD.struct.hashTable.vectorEquals; @@ -234,22 +260,24 @@ TCAD.craft.segmentsToPaths = function(segments) { } } - TCAD.utils.iteratePath(path, 0, function(a, b) { - var fromPolygon = csgIndex.get([a, b]); - if (fromPolygon !== null) { - if (fromPolygon.shared.__tcad.csgInfo) { - a.sketchConnectionObject = fromPolygon.shared.__tcad.csgInfo.derivedFrom; - } - } - return true; - }); - var filteredPaths = []; for (var i = 0; i < paths.length; i++) { var path = paths[i]; + + //Set derived from object to be able to recunstruct + TCAD.utils.iteratePath(path, 0, function (a, b) { + var fromPolygon = csgIndex.get([a, b]); + if (fromPolygon !== null) { + if (fromPolygon.shared.__tcad.csgInfo) { + a.sketchConnectionObject = fromPolygon.shared.__tcad.csgInfo.derivedFrom; + } + } + return true; + }); + path = TCAD.craft.deleteRedundantPoints(path); if (path.length > 2) { filteredPaths.push({ - vertices : path + vertices: path }); } } From 247acc0f386c782d85987d33085c6d5d011aeea6 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Tue, 8 Sep 2015 22:09:05 -0700 Subject: [PATCH 09/21] circle support --- web/app/3d/main.js | 91 +++++++++++++++++++++++++++--------------- web/app/engine.js | 46 ++++++++++++++------- web/app/sketcher/io.js | 23 ++++++++--- web/app/workbench.js | 38 ++++++++++++------ 4 files changed, 132 insertions(+), 66 deletions(-) diff --git a/web/app/3d/main.js b/web/app/3d/main.js index 62893946..76e67660 100644 --- a/web/app/3d/main.js +++ b/web/app/3d/main.js @@ -63,7 +63,7 @@ TCAD.App.prototype.sketchFace = function() { } else { data = JSON.parse(savedFace); } - data.boundary = {lines : [], arcs : []}; + data.boundary = {lines : [], arcs : [], circles : []}; function sameSketchObject(a, b) { if (a.sketchConnectionObject === undefined || b.sketchConnectionObject === undefined) { return false; @@ -77,46 +77,71 @@ TCAD.App.prototype.sketchFace = function() { var _3dTransformation = new TCAD.Matrix().setBasis(polyFace.basis()); var _2dTr = _3dTransformation.invert(); + function addSegment(a, b) { + data.boundary.lines.push({ + a : {x : a.x, y: a.y}, + b : {x : b.x, y: b.y} + }); + } + function addArc(arc) { + if (arc.length < 2) { + return; + } + var a = arc[0], b = arc[arc.length - 1]; + if (arc.length == 2) { + addSegment(a, b); + return; + } + var mid = (arc.length / 2) >> 0; + var c = TCAD.math.circleFromPoints(arc[0], arc[mid], arc[arc.length-1]); + if (c == null) { + return; + } + if (!TCAD.geom.isCCW([arc[0], arc[mid], arc[arc.length-1]])) { + var t = a; + a = b; + b = t; + } + data.boundary.arcs.push({ + a : {x : a.x, y: a.y}, + b : {x : b.x, y: b.y}, + c : {x : c.x, y : c.y} + }); + } + function addCircle(circle) { + var n = circle.length; + //var c = TCAD.math.circleFromPoints(circle[0], circle[((n / 3) >> 0) % n], circle[((2 * n / 3) >> 0) % n]); + var c = TCAD.math.circleFromPoints(circle[0], circle[1], circle[2]); + if (c === null) return; + var r = TCAD.math.distanceAB(circle[0], c); + data.boundary.circles.push({ + c : {x : c.x, y: c.y}, + r : r + }); + } + function isCircle(path) { + return path.length > 0 && path[0].sketchConnectionObject !== undefined && path[0].sketchConnectionObject._class == 'TCAD.TWO.Circle'; + } + + function trPath (path) { + var out = []; + for (var i = 0; i < path.length; i++) { + out.push(_2dTr.apply(path[i])); + } + return out; + } for (var i = 0; i < paths.length; i++) { var path = paths[i].vertices; var shift = 0; + if (isCircle(path)) { + addCircle(trPath(path)); + continue; + } TCAD.utils.iteratePath(path, 0, function(a, b, ai, bi) { shift = bi; return sameSketchObject(a, b); }); - - function addSegment(a, b) { - data.boundary.lines.push({ - a : {x : a.x, y: a.y}, - b : {x : b.x, y: b.y} - }); - } - function addArc(arc) { - if (arc.length < 2) { - return; - } - var a = arc[0], b = arc[arc.length - 1]; - if (arc.length == 2) { - addSegment(a, b); - return; - } - var mid = (arc.length / 2) >> 0; - var c = TCAD.math.circleFromPoints(arc[0], arc[mid], arc[arc.length-1]); - if (c == null) { - return; - } - if (!TCAD.geom.isCCW([arc[0], arc[mid], arc[arc.length-1]])) { - var t = a; - a = b; - b = t; - } - data.boundary.arcs.push({ - a : {x : a.x, y: a.y}, - b : {x : b.x, y: b.y}, - c : {x : c.x, y : c.y} - }); - } var currSko = null; var arc = null; TCAD.utils.iteratePath(path, shift+1, function(a, b, ai, bi, iterNumber, path) { diff --git a/web/app/engine.js b/web/app/engine.js index cee628bf..ca457963 100644 --- a/web/app/engine.js +++ b/web/app/engine.js @@ -142,12 +142,6 @@ TCAD.utils.fixCCW = function(path, normal) { return path; }; -TCAD.utils.addAll = function(arr, arrToAdd) { - for (var i = 0; i < arrToAdd.length; i++) { - arr.push(arrToAdd[i]); - } -}; - TCAD.TOLERANCE = 1E-6; TCAD.utils.areEqual = function(v1, v2, tolerance) { @@ -284,6 +278,19 @@ TCAD.utils.sketchToPolygons = function(geom) { console.warn("Points count < 3!"); } } + for (var li = 0; li < geom.loops.length; ++li) { + var loop = geom.loops[li]; + var polyPoints = []; + for (var si = 0; si < loop.length; si++) { + var conn = loop[si]; + //reuse a point and ignore b point since it's a guaranteed loop + conn.a.sketchConnectionObject = conn.sketchObject; + polyPoints.push(conn.a); + } + if (polyPoints.length >= 3) { + polygons.push(new TCAD.Polygon(polyPoints)); + } + } return polygons; }; @@ -538,7 +545,6 @@ TCAD.Solid.prototype.makeWireframe = function(polygons) { /** @constructor */ TCAD.SketchFace = function(solid, csgGroup) { csgGroup.__face = this; - this.sketchGeom = null; if (csgGroup.shared.__tcad.faceId === undefined) { this.id = solid.tCadId + ":" + (solid.faceCounter++); } else { @@ -550,10 +556,6 @@ TCAD.SketchFace = function(solid, csgGroup) { this.csgGroup = csgGroup; this.faces = []; this.sketch3DGroup = null; - - if (this.sketchGeom != null) { - this.syncSketches(this.sketchGeom); - } }; if (typeof THREE !== "undefined") { @@ -588,8 +590,9 @@ TCAD.SketchFace.prototype.syncSketches = function(geom) { var _3dTransformation = new TCAD.Matrix().setBasis(basis); //we lost depth or z off in 2d sketch, calculate it again var depth = this.csgGroup.plane.w; - for (i = 0; i < geom.connections.length; ++i) { - var l = geom.connections[i]; + var connections = geom.connections.concat(TCAD.utils.arrFlatten1L(geom.loops)); + for (i = 0; i < connections.length; ++i) { + var l = connections[i]; var lg = new THREE.Geometry(); l.a.z = l.b.z = depth; var a = _3dTransformation.apply(l.a); @@ -600,8 +603,6 @@ TCAD.SketchFace.prototype.syncSketches = function(geom) { var line = new THREE.Segment(lg, this.SKETCH_MATERIAL); this.sketch3DGroup.add(line); } - this.sketchGeom = geom; - this.sketchGeom.depth = depth; }; TCAD.POLYGON_COUNTER = 0; @@ -739,3 +740,18 @@ TCAD.utils.iteratePath = function(path, shift, callback) { } } }; + +TCAD.utils.addAll = function(arr, arrToAdd) { + for (var i = 0; i < arrToAdd.length; i++) { + arr.push(arrToAdd[i]); + } +}; + +TCAD.utils.arrFlatten1L = function(arr) { + var result = []; + for (var i = 0; i < arr.length; i++) { + TCAD.utils.addAll(result, arr[i]); + + } + return result; +}; diff --git a/web/app/sketcher/io.js b/web/app/sketcher/io.js index 7b0af7e1..2024dfce 100644 --- a/web/app/sketcher/io.js +++ b/web/app/sketcher/io.js @@ -213,6 +213,7 @@ TCAD.IO.prototype.updateBoundary = function (boundary) { this.boundaryLayer.readOnly = true; this.viewer.layers.splice(0, 0, this.boundaryLayer); } + var layer = this.boundaryLayer; // if (bbox[0] < Number.MAX_VALUE && bbox[1] < Number.MAX_VALUE && -bbox[2] < Number.MAX_VALUE && -bbox[3] < Number.MAX_VALUE) { // this.viewer.showBounds(bbox[0], bbox[1], bbox[2], bbox[3]) // } @@ -233,13 +234,16 @@ TCAD.IO.prototype.updateBoundary = function (boundary) { // } // } //} + var id = 0; + function __makeAux(obj) { + obj.accept(function(o){o.aux = true; return true;}); + obj.edge = id ++; + } - var id, i = 0; - for (i = 0; i < boundary.lines.length; ++i, ++id) { + for (var i = 0; i < boundary.lines.length; ++i, ++id) { var edge = boundary.lines[i]; var seg = this.viewer.addSegment(edge.a.x, edge.a.y, edge.b.x, edge.b.y, this.boundaryLayer); - seg.accept(function(o){o.aux = true; return true;}); - seg.edge = id ++; + __makeAux(seg); } for (i = 0; i < boundary.arcs.length; ++i, ++id) { var a = boundary.arcs[i]; @@ -249,8 +253,15 @@ TCAD.IO.prototype.updateBoundary = function (boundary) { new TCAD.TWO.EndPoint(a.c.x, a.c.y) ); this.boundaryLayer.objects.push(arc); - arc.accept(function(o){o.aux = true; return true;}); - arc.edge = id ++; + __makeAux(arc); + } + for (i = 0; i < boundary.circles.length; ++i, ++id) { + var obj = boundary.circles[i]; + var circle = new TCAD.TWO.Circle(new TCAD.TWO.EndPoint(obj.c.x, obj.c.y)); + circle.r.set(obj.r); + this.boundaryLayer.objects.push(circle); + __makeAux(circle); + } }; diff --git a/web/app/workbench.js b/web/app/workbench.js index 3414ccc1..0a576b14 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -7,7 +7,7 @@ TCAD.workbench.SketchConnection = function(a, b, sketchObject) { }; TCAD.workbench.readSketchGeom = function(sketch) { - var out = {connections : []}; + var out = {connections : [], loops : []}; var id = 0; if (sketch.layers !== undefined) { for (var l = 0; l < sketch.layers.length; ++l) { @@ -15,25 +15,29 @@ TCAD.workbench.readSketchGeom = function(sketch) { var obj = sketch.layers[l].data[i]; if (obj.edge !== undefined) continue; if (!!obj.aux) continue; - var a = new TCAD.Vector(obj.points[0][1][1], obj.points[0][2][1], 0); - var b = new TCAD.Vector(obj.points[1][1][1], obj.points[1][2][1], 0); if (obj._class === 'TCAD.TWO.Segment') { - out.connections.push(new TCAD.workbench.SketchConnection( - a, b, {_class : obj._class, id : id++} - )); + var a = new TCAD.Vector(obj.points[0][1][1], obj.points[0][2][1], 0); + var b = new TCAD.Vector(obj.points[1][1][1], obj.points[1][2][1], 0); + out.connections.push(new TCAD.workbench.SketchConnection(a, b, {_class : obj._class, id : id++})); } else if (obj._class === 'TCAD.TWO.Arc') { + var a = new TCAD.Vector(obj.points[0][1][1], obj.points[0][2][1], 0); + var b = new TCAD.Vector(obj.points[1][1][1], obj.points[1][2][1], 0); var center = new TCAD.Vector(obj.points[2][1][1], obj.points[2][2][1], 0); var approxArc = TCAD.workbench.approxArc(a, b, center, 20); var data = {_class : obj._class, id : id++}; for (var j = 0; j < approxArc.length - 1; j++) { - var pa = approxArc[j]; - var pb = approxArc[j+1]; - out.connections.push(new TCAD.workbench.SketchConnection( - pa, pb, data - )); + out.connections.push(new TCAD.workbench.SketchConnection(approxArc[j], approxArc[j+1], data)); } - } else if (obj._class === 'TCAD.TWO.Circle') { + var center = new TCAD.Vector(obj.c[1][1], obj.c[2][1], 0); + var approxCircle = TCAD.workbench.approxCircle(center, obj.r, 20); + var data = {_class : obj._class, id : id++}; + var loop = []; + var p, q, n = approxCircle.length; + for (var p = n - 1, q = 0; q < n; p = q++) { + loop.push(new TCAD.workbench.SketchConnection(approxCircle[p], approxCircle[q], data)); + } + out.loops.push(loop); } } } @@ -61,6 +65,16 @@ TCAD.workbench.approxArc = function(ao, bo, c, k) { return points; }; +TCAD.workbench.approxCircle = function(c, r, k) { + var points = []; + var step = (2 * Math.PI) / k; + + for (var i = 0, angle = 0; i < k; ++i, angle += step) { + points.push(new TCAD.Vector(c.x + r*Math.cos(angle), c.y + r*Math.sin(angle))); + } + return points; +}; + TCAD.workbench.serializeSolid = function(solid) { var data = {}; data.faceCounter = TCAD.geom.FACE_COUNTER; From cfe4dbf261d68add52267392f37c876c7b21891d Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Wed, 9 Sep 2015 20:12:38 -0700 Subject: [PATCH 10/21] transform controls --- web/app/3d/main.js | 3 + web/app/3d/viewer.js | 54 +- web/app/workbench.js | 36 +- web/index.html | 2 + web/lib/three/TransformControls.js | 981 +++++++++++++++++++++++++++++ 5 files changed, 1045 insertions(+), 31 deletions(-) create mode 100644 web/lib/three/TransformControls.js diff --git a/web/app/3d/main.js b/web/app/3d/main.js index 76e67660..5119c6df 100644 --- a/web/app/3d/main.js +++ b/web/app/3d/main.js @@ -42,6 +42,9 @@ TCAD.App = function() { } } + this.bus.subscribe("craft", function() { + app._refreshSketches(); + }); window.addEventListener('storage', storage_handler, false); }; diff --git a/web/app/3d/viewer.js b/web/app/3d/viewer.js index 8198f9dc..6a08f80e 100644 --- a/web/app/3d/viewer.js +++ b/web/app/3d/viewer.js @@ -51,26 +51,46 @@ TCAD.Viewer = function(bus) { **/ // controls = new THREE.OrbitControls( camera , renderer.domElement); - var controls = new THREE.TrackballControls( camera , renderer.domElement); + var trackballControls = new THREE.TrackballControls( camera , renderer.domElement); // document.addEventListener( 'mousemove', function(){ // controls.update(); // }, false ); - controls.rotateSpeed = 3.8; - controls.zoomSpeed = 1.2; - controls.panSpeed = 0.8; + trackballControls.rotateSpeed = 3.8; + trackballControls.zoomSpeed = 1.2; + trackballControls.panSpeed = 0.8; - controls.noZoom = false; - controls.noPan = false; + trackballControls.noZoom = false; + trackballControls.noPan = false; - controls.staticMoving = true; - controls.dynamicDampingFactor = 0.3; + trackballControls.staticMoving = true; + trackballControls.dynamicDampingFactor = 0.3; - controls.keys = [ 65, 83, 68 ]; - controls.addEventListener( 'change', render ); - this.controls = controls; + trackballControls.keys = [ 65, 83, 68 ]; + trackballControls.addEventListener( 'change', render ); + this.trackballControls = trackballControls; + + var transformControls = new THREE.TransformControls( camera, renderer.domElement ); + transformControls.addEventListener( 'change', render ); + scene.add( transformControls ); + this.transformControls = transformControls; + + function updateTransformControls() { + if (transformControls.object !== undefined) { + if (transformControls.object.parent === undefined) { + transformControls.detach(); + render(); + } + transformControls.update(); + } + } + + function updateControlsAndHelpers() { + trackballControls.update(); + updateTransformControls(); + } /** * TOOLS @@ -110,9 +130,15 @@ TCAD.Viewer = function(bus) { if (scope.selectionMgr.contains(poly)) { scope.toolMgr.handleClick(poly, pickResult); } else { - scope.select(poly); - pickResult.object.geometry.colorsNeedUpdate = true; + if (e.shiftKey) { + scope.transformControls.attach(pickResult.object); + } else { + scope.select(poly); + pickResult.object.geometry.colorsNeedUpdate = true; + } } + } else { + scope.transformControls.detach(); } render(); } @@ -148,7 +174,7 @@ TCAD.Viewer = function(bus) { function animate() { // console.log("animate"); requestAnimationFrame( animate ); - controls.update(); + updateControlsAndHelpers(); } render(); diff --git a/web/app/workbench.js b/web/app/workbench.js index 0a576b14..0657ccb0 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -842,21 +842,23 @@ TCAD.craft.cut = function(app, request) { var sketchedPolygons = TCAD.craft.getSketchedPolygons3D(app, face); if (sketchedPolygons == null) return null; - //face.c.__face = undefined; - var work = request.solids[0].csg; - var normal = TCAD.utils.vec(face.csgGroup.plane.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 cutterCSG = CSG.fromPolygons(cutter); - - var cut = work.subtract(CSG.fromPolygons(cutter)); - - var solid = TCAD.utils.createSolidMesh(cut).geometry; - return solid; + //face.c.__face = undefined; + var outSolids = []; + for (var si = 0; si < request.solids.length; si++) { + var work = request.solids[si].csg; + var cut = work.subtract(cutterCSG); + var solidMesh = TCAD.utils.createSolidMesh(cut); + outSolids.push(solidMesh.geometry); + } + return outSolids; }; TCAD.Craft = function(app) { @@ -889,18 +891,18 @@ TCAD.Craft.prototype.modify = function(request) { if (!op) return; var detachedRequest = TCAD.craft.detach(request); - var newGroups = op(this.app, request); + var newSolids = op(this.app, request); - 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 ); + if (newSolids == null) return; + var i; + for (i = 0; i < request.solids.length; i++) { + this.app.viewer.scene.remove( request.solids[i].meshObject ); + } + for (i = 0; i < newSolids.length; i++) { + this.app.viewer.scene.add(newSolids[i].meshObject); } - this.app.viewer.scene.add(newGroups.meshObject); this.history.push(detachedRequest); this.app.bus.notify('craft'); - //REMOVE IT - this.app._refreshSketches(); this.app.viewer.render(); }; @@ -909,6 +911,6 @@ TCAD.craft.OPS = { CUT : TCAD.craft.cut, PAD : TCAD.craft.extrude, BOX : function(app, request) { - return TCAD.utils.createCSGBox(request.size).geometry; + return [TCAD.utils.createCSGBox(request.size).geometry]; } }; diff --git a/web/index.html b/web/index.html index ce488e0f..17119553 100644 --- a/web/index.html +++ b/web/index.html @@ -13,8 +13,10 @@ + + diff --git a/web/lib/three/TransformControls.js b/web/lib/three/TransformControls.js new file mode 100644 index 00000000..6a513443 --- /dev/null +++ b/web/lib/three/TransformControls.js @@ -0,0 +1,981 @@ +/** + * @author arodic / https://github.com/arodic + */ +/*jshint sub:true*/ + +(function () { + + 'use strict'; + + var GizmoMaterial = function ( parameters ) { + + THREE.MeshBasicMaterial.call( this ); + + this.depthTest = false; + this.depthWrite = false; + this.side = THREE.FrontSide; + this.transparent = true; + + this.setValues( parameters ); + + this.oldColor = this.color.clone(); + this.oldOpacity = this.opacity; + + this.highlight = function( highlighted ) { + + if ( highlighted ) { + + this.color.setRGB( 1, 1, 0 ); + this.opacity = 1; + + } else { + + this.color.copy( this.oldColor ); + this.opacity = this.oldOpacity; + + } + + }; + + }; + + GizmoMaterial.prototype = Object.create( THREE.MeshBasicMaterial.prototype ); + + var GizmoLineMaterial = function ( parameters ) { + + THREE.LineBasicMaterial.call( this ); + + this.depthTest = false; + this.depthWrite = false; + this.transparent = true; + this.linewidth = 1; + + this.setValues( parameters ); + + this.oldColor = this.color.clone(); + this.oldOpacity = this.opacity; + + this.highlight = function( highlighted ) { + + if ( highlighted ) { + + this.color.setRGB( 1, 1, 0 ); + this.opacity = 1; + + } else { + + this.color.copy( this.oldColor ); + this.opacity = this.oldOpacity; + + } + + }; + + }; + + GizmoLineMaterial.prototype = Object.create( THREE.LineBasicMaterial.prototype ); + + THREE.TransformGizmo = function () { + + var scope = this; + var showPickers = false; //debug + var showActivePlane = false; //debug + + this.init = function () { + + THREE.Object3D.call( this ); + + this.handles = new THREE.Object3D(); + this.pickers = new THREE.Object3D(); + this.planes = new THREE.Object3D(); + + this.add(this.handles); + this.add(this.pickers); + this.add(this.planes); + + //// PLANES + + var planeGeometry = new THREE.PlaneGeometry( 50, 50, 2, 2 ); + var planeMaterial = new THREE.MeshBasicMaterial( { wireframe: true } ); + planeMaterial.side = THREE.DoubleSide; + + var planes = { + "XY": new THREE.Mesh( planeGeometry, planeMaterial ), + "YZ": new THREE.Mesh( planeGeometry, planeMaterial ), + "XZ": new THREE.Mesh( planeGeometry, planeMaterial ), + "XYZE": new THREE.Mesh( planeGeometry, planeMaterial ) + }; + + this.activePlane = planes["XYZE"]; + + planes["YZ"].rotation.set( 0, Math.PI/2, 0 ); + planes["XZ"].rotation.set( -Math.PI/2, 0, 0 ); + + for (var i in planes) { + planes[i].name = i; + this.planes.add(planes[i]); + this.planes[i] = planes[i]; + planes[i].visible = false; + } + + //// HANDLES AND PICKERS + + var setupGizmos = function( gizmoMap, parent ) { + + for ( var name in gizmoMap ) { + + for ( i = gizmoMap[name].length; i--;) { + + var object = gizmoMap[name][i][0]; + var position = gizmoMap[name][i][1]; + var rotation = gizmoMap[name][i][2]; + + object.name = name; + + if ( position ) object.position.set( position[0], position[1], position[2] ); + if ( rotation ) object.rotation.set( rotation[0], rotation[1], rotation[2] ); + + parent.add( object ); + + } + + } + + }; + + setupGizmos(this.handleGizmos, this.handles); + setupGizmos(this.pickerGizmos, this.pickers); + + // reset Transformations + + this.traverse(function ( child ) { + if (child instanceof THREE.Mesh) { + child.updateMatrix(); + + var tempGeometry = new THREE.Geometry(); + tempGeometry.merge( child.geometry, child.matrix ); + + child.geometry = tempGeometry; + child.position.set( 0, 0, 0 ); + child.rotation.set( 0, 0, 0 ); + child.scale.set( 1, 1, 1 ); + } + }); + + }; + + this.hide = function () { + this.traverse(function( child ) { + child.visible = false; + }); + }; + + this.show = function () { + this.traverse(function( child ) { + child.visible = true; + if (child.parent == scope.pickers ) child.visible = showPickers; + if (child.parent == scope.planes ) child.visible = false; + }); + this.activePlane.visible = showActivePlane; + }; + + this.highlight = function ( axis ) { + this.traverse(function( child ) { + if ( child.material && child.material.highlight ){ + if ( child.name == axis ) { + child.material.highlight( true ); + } else { + child.material.highlight( false ); + } + } + }); + }; + + }; + + THREE.TransformGizmo.prototype = Object.create( THREE.Object3D.prototype ); + + THREE.TransformGizmo.prototype.update = function ( rotation, eye ) { + + var vec1 = new THREE.Vector3( 0, 0, 0 ); + var vec2 = new THREE.Vector3( 0, 1, 0 ); + var lookAtMatrix = new THREE.Matrix4(); + + this.traverse(function(child) { + if ( child.name.search("E") != -1 ) { + child.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( eye, vec1, vec2 ) ); + } else if ( child.name.search("X") != -1 || child.name.search("Y") != -1 || child.name.search("Z") != -1 ) { + child.quaternion.setFromEuler( rotation ); + } + }); + + }; + + THREE.TransformGizmoTranslate = function () { + + THREE.TransformGizmo.call( this ); + + var arrowGeometry = new THREE.Geometry(); + var mesh = new THREE.Mesh( new THREE.CylinderGeometry( 0, 0.05, 0.2, 12, 1, false ) ); + mesh.position.y = 0.5; + mesh.updateMatrix(); + + arrowGeometry.merge( mesh.geometry, mesh.matrix ); + + var lineXGeometry = new THREE.Geometry(); + lineXGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 1, 0, 0 ) ); + + var lineYGeometry = new THREE.Geometry(); + lineYGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) ); + + var lineZGeometry = new THREE.Geometry(); + lineZGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 1 ) ); + + this.handleGizmos = { + X: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, -Math.PI/2 ] ], + [ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ] + ], + Y: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ], + [ new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ] + ], + Z: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI/2, 0, 0 ] ], + [ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ] + ], + XYZ: [ + [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, 0, 0 ] ] + ], + XY: [ + [ new THREE.Mesh( new THREE.PlaneGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ), [ 0.15, 0.15, 0 ] ] + ], + YZ: [ + [ new THREE.Mesh( new THREE.PlaneGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0x00ffff, opacity: 0.25 } ) ), [ 0, 0.15, 0.15 ], [ 0, Math.PI/2, 0 ] ] + ], + XZ: [ + [ new THREE.Mesh( new THREE.PlaneGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xff00ff, opacity: 0.25 } ) ), [ 0.15, 0, 0.15 ], [ -Math.PI/2, 0, 0 ] ] + ] + }; + + this.pickerGizmos = { + X: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0.6, 0, 0 ], [ 0, 0, -Math.PI/2 ] ] + ], + Y: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0.6, 0 ] ] + ], + Z: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0.6 ], [ Math.PI/2, 0, 0 ] ] + ], + XYZ: [ + [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.2, 0 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ] + ], + XY: [ + [ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ), [ 0.2, 0.2, 0 ] ] + ], + YZ: [ + [ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0x00ffff, opacity: 0.25 } ) ), [ 0, 0.2, 0.2 ], [ 0, Math.PI/2, 0 ] ] + ], + XZ: [ + [ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0xff00ff, opacity: 0.25 } ) ), [ 0.2, 0, 0.2 ], [ -Math.PI/2, 0, 0 ] ] + ] + }; + + this.setActivePlane = function ( axis, eye ) { + + var tempMatrix = new THREE.Matrix4(); + eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) ); + + if ( axis == "X" ) { + this.activePlane = this.planes[ "XY" ]; + if ( Math.abs(eye.y) > Math.abs(eye.z) ) this.activePlane = this.planes[ "XZ" ]; + } + + if ( axis == "Y" ){ + this.activePlane = this.planes[ "XY" ]; + if ( Math.abs(eye.x) > Math.abs(eye.z) ) this.activePlane = this.planes[ "YZ" ]; + } + + if ( axis == "Z" ){ + this.activePlane = this.planes[ "XZ" ]; + if ( Math.abs(eye.x) > Math.abs(eye.y) ) this.activePlane = this.planes[ "YZ" ]; + } + + if ( axis == "XYZ" ) this.activePlane = this.planes[ "XYZE" ]; + + if ( axis == "XY" ) this.activePlane = this.planes[ "XY" ]; + + if ( axis == "YZ" ) this.activePlane = this.planes[ "YZ" ]; + + if ( axis == "XZ" ) this.activePlane = this.planes[ "XZ" ]; + + this.hide(); + this.show(); + + }; + + this.init(); + + }; + + THREE.TransformGizmoTranslate.prototype = Object.create( THREE.TransformGizmo.prototype ); + + THREE.TransformGizmoRotate = function () { + + THREE.TransformGizmo.call( this ); + + var CircleGeometry = function ( radius, facing, arc ) { + + var geometry = new THREE.Geometry(); + arc = arc ? arc : 1; + for ( var i = 0; i <= 64 * arc; ++i ) { + if ( facing == 'x' ) geometry.vertices.push( new THREE.Vector3( 0, Math.cos( i / 32 * Math.PI ), Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) ); + if ( facing == 'y' ) geometry.vertices.push( new THREE.Vector3( Math.cos( i / 32 * Math.PI ), 0, Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) ); + if ( facing == 'z' ) geometry.vertices.push( new THREE.Vector3( Math.sin( i / 32 * Math.PI ), Math.cos( i / 32 * Math.PI ), 0 ).multiplyScalar(radius) ); + } + + return geometry; + }; + + this.handleGizmos = { + X: [ + [ new THREE.Line( new CircleGeometry(1,'x',0.5), new GizmoLineMaterial( { color: 0xff0000 } ) ) ] + ], + Y: [ + [ new THREE.Line( new CircleGeometry(1,'y',0.5), new GizmoLineMaterial( { color: 0x00ff00 } ) ) ] + ], + Z: [ + [ new THREE.Line( new CircleGeometry(1,'z',0.5), new GizmoLineMaterial( { color: 0x0000ff } ) ) ] + ], + E: [ + [ new THREE.Line( new CircleGeometry(1.25,'z',1), new GizmoLineMaterial( { color: 0xcccc00 } ) ) ] + ], + XYZE: [ + [ new THREE.Line( new CircleGeometry(1,'z',1), new GizmoLineMaterial( { color: 0x787878 } ) ) ] + ] + }; + + this.pickerGizmos = { + X: [ + [ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, -Math.PI/2, -Math.PI/2 ] ] + ], + Y: [ + [ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ Math.PI/2, 0, 0 ] ] + ], + Z: [ + [ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, 0, -Math.PI/2 ] ] + ], + E: [ + [ new THREE.Mesh( new THREE.TorusGeometry( 1.25, 0.12, 2, 24 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ) ] + ], + XYZE: [ + [ new THREE.Mesh( new THREE.Geometry() ) ]// TODO + ] + }; + + this.setActivePlane = function ( axis ) { + + if ( axis == "E" ) this.activePlane = this.planes[ "XYZE" ]; + + if ( axis == "X" ) this.activePlane = this.planes[ "YZ" ]; + + if ( axis == "Y" ) this.activePlane = this.planes[ "XZ" ]; + + if ( axis == "Z" ) this.activePlane = this.planes[ "XY" ]; + + this.hide(); + this.show(); + + }; + + this.update = function ( rotation, eye2 ) { + + THREE.TransformGizmo.prototype.update.apply( this, arguments ); + + var group = { + handles: this["handles"], + pickers: this["pickers"], + }; + + var tempMatrix = new THREE.Matrix4(); + var worldRotation = new THREE.Euler( 0, 0, 1 ); + var tempQuaternion = new THREE.Quaternion(); + var unitX = new THREE.Vector3( 1, 0, 0 ); + var unitY = new THREE.Vector3( 0, 1, 0 ); + var unitZ = new THREE.Vector3( 0, 0, 1 ); + var quaternionX = new THREE.Quaternion(); + var quaternionY = new THREE.Quaternion(); + var quaternionZ = new THREE.Quaternion(); + var eye = eye2.clone(); + + worldRotation.copy( this.planes["XY"].rotation ); + tempQuaternion.setFromEuler( worldRotation ); + + tempMatrix.makeRotationFromQuaternion( tempQuaternion ).getInverse( tempMatrix ); + eye.applyMatrix4( tempMatrix ); + + this.traverse(function(child) { + + tempQuaternion.setFromEuler( worldRotation ); + + if ( child.name == "X" ) { + quaternionX.setFromAxisAngle( unitX, Math.atan2( -eye.y, eye.z ) ); + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX ); + child.quaternion.copy( tempQuaternion ); + } + + if ( child.name == "Y" ) { + quaternionY.setFromAxisAngle( unitY, Math.atan2( eye.x, eye.z ) ); + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY ); + child.quaternion.copy( tempQuaternion ); + } + + if ( child.name == "Z" ) { + quaternionZ.setFromAxisAngle( unitZ, Math.atan2( eye.y, eye.x ) ); + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ ); + child.quaternion.copy( tempQuaternion ); + } + + }); + + }; + + this.init(); + + }; + + THREE.TransformGizmoRotate.prototype = Object.create( THREE.TransformGizmo.prototype ); + + THREE.TransformGizmoScale = function () { + + THREE.TransformGizmo.call( this ); + + var arrowGeometry = new THREE.Geometry(); + var mesh = new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ) ); + mesh.position.y = 0.5; + mesh.updateMatrix(); + + arrowGeometry.merge( mesh.geometry, mesh.matrix ); + + var lineXGeometry = new THREE.Geometry(); + lineXGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 1, 0, 0 ) ); + + var lineYGeometry = new THREE.Geometry(); + lineYGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) ); + + var lineZGeometry = new THREE.Geometry(); + lineZGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 1 ) ); + + this.handleGizmos = { + X: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, -Math.PI/2 ] ], + [ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ] + ], + Y: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ], + [ new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ] + ], + Z: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI/2, 0, 0 ] ], + [ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ] + ], + XYZ: [ + [ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ] + ] + }; + + this.pickerGizmos = { + X: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0.6, 0, 0 ], [ 0, 0, -Math.PI/2 ] ] + ], + Y: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0.6, 0 ] ] + ], + Z: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0.6 ], [ Math.PI/2, 0, 0 ] ] + ], + XYZ: [ + [ new THREE.Mesh( new THREE.BoxGeometry( 0.4, 0.4, 0.4 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ] + ] + }; + + this.setActivePlane = function ( axis, eye ) { + + var tempMatrix = new THREE.Matrix4(); + eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) ); + + if ( axis == "X" ) { + this.activePlane = this.planes[ "XY" ]; + if ( Math.abs(eye.y) > Math.abs(eye.z) ) this.activePlane = this.planes[ "XZ" ]; + } + + if ( axis == "Y" ){ + this.activePlane = this.planes[ "XY" ]; + if ( Math.abs(eye.x) > Math.abs(eye.z) ) this.activePlane = this.planes[ "YZ" ]; + } + + if ( axis == "Z" ){ + this.activePlane = this.planes[ "XZ" ]; + if ( Math.abs(eye.x) > Math.abs(eye.y) ) this.activePlane = this.planes[ "YZ" ]; + } + + if ( axis == "XYZ" ) this.activePlane = this.planes[ "XYZE" ]; + + this.hide(); + this.show(); + + }; + + this.init(); + + }; + + THREE.TransformGizmoScale.prototype = Object.create( THREE.TransformGizmo.prototype ); + + THREE.TransformControls = function ( camera, domElement ) { + + // TODO: Make non-uniform scale and rotate play nice in hierarchies + // TODO: ADD RXYZ contol + + THREE.Object3D.call( this ); + + domElement = ( domElement !== undefined ) ? domElement : document; + + this.gizmo = {}; + this.gizmo["translate"] = new THREE.TransformGizmoTranslate(); + this.gizmo["rotate"] = new THREE.TransformGizmoRotate(); + this.gizmo["scale"] = new THREE.TransformGizmoScale(); + + this.add(this.gizmo["translate"]); + this.add(this.gizmo["rotate"]); + this.add(this.gizmo["scale"]); + + this.gizmo["translate"].hide(); + this.gizmo["rotate"].hide(); + this.gizmo["scale"].hide(); + + this.object = undefined; + this.snap = null; + this.space = "world"; + this.size = 1; + this.axis = null; + + var scope = this; + + var _dragging = false; + var _mode = "translate"; + var _plane = "XY"; + + var changeEvent = { type: "change" }; + + var ray = new THREE.Raycaster(); + var projector = new THREE.Projector(); + var pointerVector = new THREE.Vector3(); + + var point = new THREE.Vector3(); + var offset = new THREE.Vector3(); + + var rotation = new THREE.Vector3(); + var offsetRotation = new THREE.Vector3(); + var scale = 1; + + var lookAtMatrix = new THREE.Matrix4(); + var eye = new THREE.Vector3(); + + var tempMatrix = new THREE.Matrix4(); + var tempVector = new THREE.Vector3(); + var tempQuaternion = new THREE.Quaternion(); + var unitX = new THREE.Vector3( 1, 0, 0 ); + var unitY = new THREE.Vector3( 0, 1, 0 ); + var unitZ = new THREE.Vector3( 0, 0, 1 ); + + var quaternionXYZ = new THREE.Quaternion(); + var quaternionX = new THREE.Quaternion(); + var quaternionY = new THREE.Quaternion(); + var quaternionZ = new THREE.Quaternion(); + var quaternionE = new THREE.Quaternion(); + + var oldPosition = new THREE.Vector3(); + var oldScale = new THREE.Vector3(); + var oldRotationMatrix = new THREE.Matrix4(); + + var parentRotationMatrix = new THREE.Matrix4(); + var parentScale = new THREE.Vector3(); + + var worldPosition = new THREE.Vector3(); + var worldRotation = new THREE.Euler(); + var worldRotationMatrix = new THREE.Matrix4(); + var camPosition = new THREE.Vector3(); + var camRotation = new THREE.Euler(); + + domElement.addEventListener( "mousedown", onPointerDown, false ); + domElement.addEventListener( "touchstart", onPointerDown, false ); + + domElement.addEventListener( "mousemove", onPointerHover, false ); + domElement.addEventListener( "touchmove", onPointerHover, false ); + + domElement.addEventListener( "mousemove", onPointerMove, false ); + domElement.addEventListener( "touchmove", onPointerMove, false ); + + domElement.addEventListener( "mouseup", onPointerUp, false ); + domElement.addEventListener( "mouseout", onPointerUp, false ); + domElement.addEventListener( "touchend", onPointerUp, false ); + domElement.addEventListener( "touchcancel", onPointerUp, false ); + domElement.addEventListener( "touchleave", onPointerUp, false ); + + this.attach = function ( object ) { + + scope.object = object; + + this.gizmo["translate"].hide(); + this.gizmo["rotate"].hide(); + this.gizmo["scale"].hide(); + this.gizmo[_mode].show(); + + scope.update(); + + }; + + this.detach = function ( object ) { + + scope.object = undefined; + this.axis = undefined; + + this.gizmo["translate"].hide(); + this.gizmo["rotate"].hide(); + this.gizmo["scale"].hide(); + + }; + + this.setMode = function ( mode ) { + + _mode = mode ? mode : _mode; + + if ( _mode == "scale" ) scope.space = "local"; + + this.gizmo["translate"].hide(); + this.gizmo["rotate"].hide(); + this.gizmo["scale"].hide(); + this.gizmo[_mode].show(); + + this.update(); + scope.dispatchEvent( changeEvent ); + + }; + + this.setSnap = function ( snap ) { + + scope.snap = snap; + + }; + + this.setSize = function ( size ) { + + scope.size = size; + this.update(); + scope.dispatchEvent( changeEvent ); + + }; + + this.setSpace = function ( space ) { + + scope.space = space; + this.update(); + scope.dispatchEvent( changeEvent ); + + }; + + this.update = function () { + + if ( scope.object === undefined ) return; + + scope.object.updateMatrixWorld(); + worldPosition.setFromMatrixPosition( scope.object.matrixWorld ); + worldRotation.setFromRotationMatrix( tempMatrix.extractRotation( scope.object.matrixWorld ) ); + + camera.updateMatrixWorld(); + camPosition.setFromMatrixPosition( camera.matrixWorld ); + camRotation.setFromRotationMatrix( tempMatrix.extractRotation( camera.matrixWorld ) ); + + scale = worldPosition.distanceTo( camPosition ) / 6 * scope.size; + this.position.copy( worldPosition ); + this.scale.set( scale, scale, scale ); + + eye.copy( camPosition ).sub( worldPosition ).normalize(); + + if ( scope.space == "local" ) + this.gizmo[_mode].update( worldRotation, eye ); + + else if ( scope.space == "world" ) + this.gizmo[_mode].update( new THREE.Euler(), eye ); + + this.gizmo[_mode].highlight( scope.axis ); + + }; + + function onPointerHover( event ) { + + if ( scope.object === undefined || _dragging === true ) return; + + event.preventDefault(); + + var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event; + + var intersect = intersectObjects( pointer, scope.gizmo[_mode].pickers.children ); + + if ( intersect ) { + + scope.axis = intersect.object.name; + scope.update(); + scope.dispatchEvent( changeEvent ); + + } else if ( scope.axis !== null ) { + + scope.axis = null; + scope.update(); + scope.dispatchEvent( changeEvent ); + + } + + } + + function onPointerDown( event ) { + + if ( scope.object === undefined || _dragging === true ) return; + + event.preventDefault(); + event.stopPropagation(); + + var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event; + + if ( pointer.button === 0 || pointer.button === undefined ) { + + var intersect = intersectObjects( pointer, scope.gizmo[_mode].pickers.children ); + + if ( intersect ) { + + scope.axis = intersect.object.name; + + scope.update(); + + eye.copy( camPosition ).sub( worldPosition ).normalize(); + + scope.gizmo[_mode].setActivePlane( scope.axis, eye ); + + var planeIntersect = intersectObjects( pointer, [scope.gizmo[_mode].activePlane] ); + + oldPosition.copy( scope.object.position ); + oldScale.copy( scope.object.scale ); + + oldRotationMatrix.extractRotation( scope.object.matrix ); + worldRotationMatrix.extractRotation( scope.object.matrixWorld ); + + parentRotationMatrix.extractRotation( scope.object.parent.matrixWorld ); + parentScale.setFromMatrixScale( tempMatrix.getInverse( scope.object.parent.matrixWorld ) ); + + offset.copy( planeIntersect.point ); + + } + + } + + _dragging = true; + + } + + function onPointerMove( event ) { + + if ( scope.object === undefined || scope.axis === null || _dragging === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + var pointer = event.changedTouches? event.changedTouches[0] : event; + + var planeIntersect = intersectObjects( pointer, [scope.gizmo[_mode].activePlane] ); + + point.copy( planeIntersect.point ); + + if ( _mode == "translate" ) { + + point.sub( offset ); + point.multiply(parentScale); + + if ( scope.space == "local" ) { + + point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) ); + + if ( scope.axis.search("X") == -1 ) point.x = 0; + if ( scope.axis.search("Y") == -1 ) point.y = 0; + if ( scope.axis.search("Z") == -1 ) point.z = 0; + + point.applyMatrix4( oldRotationMatrix ); + + scope.object.position.copy( oldPosition ); + scope.object.position.add( point ); + + } + + if ( scope.space == "world" || scope.axis.search("XYZ") != -1 ) { + + if ( scope.axis.search("X") == -1 ) point.x = 0; + if ( scope.axis.search("Y") == -1 ) point.y = 0; + if ( scope.axis.search("Z") == -1 ) point.z = 0; + + point.applyMatrix4( tempMatrix.getInverse( parentRotationMatrix ) ); + + scope.object.position.copy( oldPosition ); + scope.object.position.add( point ); + + } + + if ( scope.snap !== null ) { + + if ( scope.axis.search("X") != -1 ) scope.object.position.x = Math.round( scope.object.position.x / scope.snap ) * scope.snap; + if ( scope.axis.search("Y") != -1 ) scope.object.position.y = Math.round( scope.object.position.y / scope.snap ) * scope.snap; + if ( scope.axis.search("Z") != -1 ) scope.object.position.z = Math.round( scope.object.position.z / scope.snap ) * scope.snap; + + } + + } else if ( _mode == "scale" ) { + + point.sub( offset ); + point.multiply(parentScale); + + if ( scope.space == "local" ) { + + if ( scope.axis == "XYZ") { + + scale = 1 + ( ( point.y ) / 50 ); + + scope.object.scale.x = oldScale.x * scale; + scope.object.scale.y = oldScale.y * scale; + scope.object.scale.z = oldScale.z * scale; + + } else { + + point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) ); + + if ( scope.axis == "X" ) scope.object.scale.x = oldScale.x * ( 1 + point.x / 50 ); + if ( scope.axis == "Y" ) scope.object.scale.y = oldScale.y * ( 1 + point.y / 50 ); + if ( scope.axis == "Z" ) scope.object.scale.z = oldScale.z * ( 1 + point.z / 50 ); + + } + + } + + } else if ( _mode == "rotate" ) { + + point.sub( worldPosition ); + point.multiply(parentScale); + tempVector.copy(offset).sub( worldPosition ); + tempVector.multiply(parentScale); + + if ( scope.axis == "E" ) { + + point.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) ); + tempVector.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) ); + + rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) ); + offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) ); + + tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) ); + + quaternionE.setFromAxisAngle( eye, rotation.z - offsetRotation.z ); + quaternionXYZ.setFromRotationMatrix( worldRotationMatrix ); + + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionE ); + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ ); + + scope.object.quaternion.copy( tempQuaternion ); + + } else if ( scope.axis == "XYZE" ) { + + quaternionE.setFromEuler( point.clone().cross(tempVector).normalize() ); // rotation axis + + tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) ); + quaternionX.setFromAxisAngle( quaternionE, - point.clone().angleTo(tempVector) ); + quaternionXYZ.setFromRotationMatrix( worldRotationMatrix ); + + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX ); + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ ); + + scope.object.quaternion.copy( tempQuaternion ); + + } else if ( scope.space == "local" ) { + + point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) ); + + tempVector.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) ); + + rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) ); + offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) ); + + quaternionXYZ.setFromRotationMatrix( oldRotationMatrix ); + quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x ); + quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y ); + quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z ); + + if ( scope.axis == "X" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionX ); + if ( scope.axis == "Y" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionY ); + if ( scope.axis == "Z" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionZ ); + + scope.object.quaternion.copy( quaternionXYZ ); + + } else if ( scope.space == "world" ) { + + rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) ); + offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) ); + + tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) ); + + quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x ); + quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y ); + quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z ); + quaternionXYZ.setFromRotationMatrix( worldRotationMatrix ); + + if ( scope.axis == "X" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX ); + if ( scope.axis == "Y" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY ); + if ( scope.axis == "Z" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ ); + + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ ); + + scope.object.quaternion.copy( tempQuaternion ); + + } + + } + + scope.update(); + scope.dispatchEvent( changeEvent ); + + } + + function onPointerUp( event ) { + + _dragging = false; + onPointerHover( event ); + + } + + function intersectObjects( pointer, objects ) { + + var rect = domElement.getBoundingClientRect(); + var x = (pointer.clientX - rect.left) / rect.width; + var y = (pointer.clientY - rect.top) / rect.height; + pointerVector.set( ( x ) * 2 - 1, - ( y ) * 2 + 1, 0.5 ); + + projector.unprojectVector( pointerVector, camera ); + ray.set( camPosition, pointerVector.sub( camPosition ).normalize() ); + + var intersections = ray.intersectObjects( objects, true ); + return intersections[0] ? intersections[0] : false; + + } + + }; + + THREE.TransformControls.prototype = Object.create( THREE.Object3D.prototype ); + +}()); \ No newline at end of file From 74d81aa8acd04b757a11a374f8ce4201041582d7 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Thu, 10 Sep 2015 23:52:35 -0700 Subject: [PATCH 11/21] renew face cutting face --- web/app/workbench.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/workbench.js b/web/app/workbench.js index 0657ccb0..c13c83a4 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -850,7 +850,7 @@ TCAD.craft.cut = function(app, request) { } var cutterCSG = CSG.fromPolygons(cutter); - //face.c.__face = undefined; + face.csgGroup.shared.__tcad.faceId = undefined; var outSolids = []; for (var si = 0; si < request.solids.length; si++) { var work = request.solids[si].csg; From 0b684d389f06e919c3adee18457a2e53b96d4621 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 11 Sep 2015 12:20:15 -0700 Subject: [PATCH 12/21] adding XYZ axises --- web/app/3d/viewer.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/web/app/3d/viewer.js b/web/app/3d/viewer.js index 6a08f80e..58308375 100644 --- a/web/app/3d/viewer.js +++ b/web/app/3d/viewer.js @@ -87,6 +87,17 @@ TCAD.Viewer = function(bus) { } } + function addAxis(axis, color) { + var lineMaterial = new THREE.LineBasicMaterial({color: color, linewidth: 1}); + var axisGeom = new THREE.Geometry(); + axisGeom.vertices.push(axis.multiply(-1000)); + axisGeom.vertices.push(axis.multiply(1000)); + scene.add(new THREE.Segment(axisGeom, lineMaterial)); + } + addAxis(TCAD.math.AXIS.X, 0xFF0000); + addAxis(TCAD.math.AXIS.Y, 0x00FF00); + addAxis(TCAD.math.AXIS.Z, 0x0000FF); + function updateControlsAndHelpers() { trackballControls.update(); updateTransformControls(); From b51bb23e83e6f5077e2ddcb7cda30e0b6efd9375 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 11 Sep 2015 12:33:56 -0700 Subject: [PATCH 13/21] fix rayCasting bug --- web/app/3d/viewer.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/web/app/3d/viewer.js b/web/app/3d/viewer.js index 58308375..21694aa7 100644 --- a/web/app/3d/viewer.js +++ b/web/app/3d/viewer.js @@ -90,8 +90,8 @@ TCAD.Viewer = function(bus) { function addAxis(axis, color) { var lineMaterial = new THREE.LineBasicMaterial({color: color, linewidth: 1}); var axisGeom = new THREE.Geometry(); - axisGeom.vertices.push(axis.multiply(-1000)); - axisGeom.vertices.push(axis.multiply(1000)); + axisGeom.vertices.push(axis.multiply(-1000).three()); + axisGeom.vertices.push(axis.multiply(1000).three()); scene.add(new THREE.Segment(axisGeom, lineMaterial)); } addAxis(TCAD.math.AXIS.X, 0xFF0000); @@ -134,8 +134,10 @@ TCAD.Viewer = function(bus) { var scope = this; function onClick(e) { var intersects = scope.raycast(e); - if (intersects.length > 0) { - var pickResult = intersects[0]; + if (intersects.length === 0) scope.transformControls.detach(); + for (var ii = 0; ii < intersects.length; ii++) { + var pickResult = intersects[ii]; + if (!pickResult.face) continue; if (pickResult.face.__TCAD_polyFace !== undefined) { var poly = pickResult.face.__TCAD_polyFace; if (scope.selectionMgr.contains(poly)) { @@ -148,10 +150,9 @@ TCAD.Viewer = function(bus) { pickResult.object.geometry.colorsNeedUpdate = true; } } - } else { - scope.transformControls.detach(); } render(); + break; } } From 11d9d718ed4a62ef95397b4be30d61ed0fdabf62 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 11 Sep 2015 17:21:31 -0700 Subject: [PATCH 14/21] make basis stable --- web/app/engine.js | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/web/app/engine.js b/web/app/engine.js index ca457963..4307e711 100644 --- a/web/app/engine.js +++ b/web/app/engine.js @@ -565,12 +565,29 @@ if (typeof THREE !== "undefined") { color: 0x2B3856, linewidth: 3}); } -TCAD.SketchFace.prototype.basis = function() { +TCAD.SketchFace.prototype.calcBasis = function() { var vec = TCAD.utils.vec; - return TCAD.geom.someBasis(this.csgGroup.polygons[0].vertices.map(function (v) { - return vec(v.pos) - }), vec(this.csgGroup.plane.normal)); -} + var normal = vec(this.csgGroup.plane.normal); + var alignPlane, x, y; + if (Math.abs(normal.dot(TCAD.math.AXIS.Y)) < 0.5) { + alignPlane = normal.cross(TCAD.math.AXIS.Y); + } else { + alignPlane = normal.cross(TCAD.math.AXIS.Z); + } + y = alignPlane.cross(normal); + x = y.cross(normal); + return [x, y, normal]; +}; + +TCAD.SketchFace.prototype.basis = function() { + if (!this._basis) { + this._basis = this.calcBasis(); + } + return this._basis; + //return TCAD.geom.someBasis(this.csgGroup.polygons[0].vertices.map(function (v) { + // return vec(v.pos) + //}), vec(this.csgGroup.plane.normal)); +}; TCAD.SketchFace.prototype.syncSketches = function(geom) { var i; From 88a6f9ba305fa4a95dfb0d6017a57533f9a792e5 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 11 Sep 2015 18:26:22 -0700 Subject: [PATCH 15/21] get extrusion back --- web/app/workbench.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/web/app/workbench.js b/web/app/workbench.js index c13c83a4..3d3d44bc 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -128,22 +128,17 @@ TCAD.craft.extrude = function(app, request) { var face = request.face; var sketchedPolygons = TCAD.craft.getSketchedPolygons3D(app, face); if (sketchedPolygons == null) return null; - face.polygon.__face = undefined; - - var faces = TCAD.craft.collectFaces(request.solids); - - var normal = face.polygon.normal; + var normal = TCAD.utils.vec(face.csgGroup.plane.normal); var toMeldWith = []; for (var i = 0; i < sketchedPolygons.length; i++) { var extruded = TCAD.geom.extrude(sketchedPolygons[i], normal.multiply(request.height)); toMeldWith = toMeldWith.concat(TCAD.craft._makeFromPolygons(extruded)); } - var work = TCAD.craft._makeFromPolygons(faces.map(function(f){ return f.polygon })); + var meld = request.solids[0].csg.union(CSG.fromPolygons(toMeldWith)); - var meld = CSG.fromPolygons(work).union(CSG.fromPolygons(toMeldWith)); - - return TCAD.craft.reconstruct(meld); + face.csgGroup.shared.__tcad.faceId = undefined; + return [TCAD.utils.createSolidMesh(meld).geometry]; }; TCAD.craft._pointOnLine = function(p, a, b) { From 67dc278fed0f3ef0a19928a4417d9c492a80b569 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 11 Sep 2015 18:58:53 -0700 Subject: [PATCH 16/21] keep order of sketches --- web/app/workbench.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/workbench.js b/web/app/workbench.js index 3d3d44bc..b16316fd 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -137,7 +137,7 @@ TCAD.craft.extrude = function(app, request) { } var meld = request.solids[0].csg.union(CSG.fromPolygons(toMeldWith)); - face.csgGroup.shared.__tcad.faceId = undefined; + face.csgGroup.shared.__tcad.faceId += '$'; return [TCAD.utils.createSolidMesh(meld).geometry]; }; @@ -845,7 +845,7 @@ TCAD.craft.cut = function(app, request) { } var cutterCSG = CSG.fromPolygons(cutter); - face.csgGroup.shared.__tcad.faceId = undefined; + face.csgGroup.shared.__tcad.faceId += '$'; var outSolids = []; for (var si = 0; si < request.solids.length; si++) { var work = request.solids[si].csg; From af1d83b512b8043675cb6c73aec2d703e8258ed8 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 11 Sep 2015 19:23:39 -0700 Subject: [PATCH 17/21] fix adding extra point in delete redundant points algo --- web/app/workbench.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/workbench.js b/web/app/workbench.js index b16316fd..a3e92087 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -191,7 +191,7 @@ TCAD.craft.deleteRedundantPoints = function(path) { var eq = TCAD.utils.equal; if (!eq(a.minus(b).unit().dot(a.minus(c).unit()), 1)) { cleanedPath.push(b); - for (var ii = 0; ii < pathLength - pi; ++ii) { + for (var ii = 0; ii < pathLength - pi - 1; ++ii) { a = path[(ii + bIdx) % pathLength]; b = path[(ii + bIdx + 1) % pathLength]; c = path[(ii + bIdx + 2) % pathLength]; From 908cf09e142d1b61339800956c8e8814dbea304f Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 11 Sep 2015 23:07:06 -0700 Subject: [PATCH 18/21] not use TCAD.Polygon anywhere --- web/app/engine.js | 159 ++++++++++++++++++------------------------- web/app/workbench.js | 62 +++++++---------- 2 files changed, 91 insertions(+), 130 deletions(-) diff --git a/web/app/engine.js b/web/app/engine.js index 4307e711..90f85a03 100644 --- a/web/app/engine.js +++ b/web/app/engine.js @@ -5,14 +5,12 @@ TCAD.utils.createSquare = function(width) { width /= 2; - var shell = [ + return [ new TCAD.Vector(-width, -width, 0), new TCAD.Vector( width, -width, 0), new TCAD.Vector( width, width, 0), new TCAD.Vector(-width, width, 0) ]; - - return new TCAD.Polygon(shell); }; TCAD.utils.csgVec = function(v) { @@ -26,20 +24,13 @@ TCAD.utils.vec = function(v) { 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]) } ); - var polygons = TCAD.geom.extrude(square, square.normal.multiply(width)); - return polygons; + square.forEach(function(v) { rot._apply(v) } ); + var normal = TCAD.geom.normalOfCCWSeq(square); + return TCAD.geom.extrude(square, normal.multiply(width), normal); }; TCAD.utils.createCSGBox = function(width) { - var csg = CSG.fromPolygons(TCAD.utils.createBox(width).map(function (p) { - var vertices = []; - for (var vi = 0; vi < p.shell.length; vi++) { - var v = p.shell[vi]; - vertices.push(new CSG.Vertex(TCAD.utils.csgVec(v))); - } - return new CSG.Polygon(vertices, TCAD.utils.createShared(p.id)); - })); + var csg = CSG.fromPolygons(TCAD.utils.createBox(width)); return TCAD.utils.createSolidMesh(csg); }; @@ -259,6 +250,7 @@ TCAD.utils.sketchToPolygons = function(geom) { var polygons = []; for (var li = 0; li < loops.length; ++li) { var loop = loops[li]; + if (!TCAD.geom.isCCW(loop)) loop.reverse(); var polyPoints = []; for (var pi = 0; pi < loop.length; ++pi) { var point = loop[pi]; @@ -272,23 +264,23 @@ TCAD.utils.sketchToPolygons = function(geom) { point.sketchConnectionObject = edge.sketchObject; } if (polyPoints.length >= 3) { - var polygon = new TCAD.Polygon(polyPoints); - polygons.push(polygon); + polygons.push(polyPoints); } else { console.warn("Points count < 3!"); } } for (var li = 0; li < geom.loops.length; ++li) { var loop = geom.loops[li]; - var polyPoints = []; - for (var si = 0; si < loop.length; si++) { - var conn = loop[si]; + var polyPoints = loop.slice(0); + if (!TCAD.geom.isCCW(polyPoints)) polyPoints.reverse(); + for (var si = 0; si < polyPoints.length; si++) { + var conn = polyPoints[si]; //reuse a point and ignore b point since it's a guaranteed loop conn.a.sketchConnectionObject = conn.sketchObject; - polyPoints.push(conn.a); + polyPoints[si] = conn.a; } if (polyPoints.length >= 3) { - polygons.push(new TCAD.Polygon(polyPoints)); + polygons.push(polyPoints); } } return polygons; @@ -296,6 +288,12 @@ TCAD.utils.sketchToPolygons = function(geom) { TCAD.geom = {}; +TCAD.geom.someBasis2 = function(normal) { + var x = normal.cross(normal.randomNonParallelVector()); + var y = normal.cross(x).unit(); + return [x, y, normal]; +}; + TCAD.geom.someBasis = function(twoPointsOnPlane, normal) { var a = twoPointsOnPlane[0]; var b = twoPointsOnPlane[1]; @@ -337,99 +335,73 @@ TCAD.geom.isCCW = function(path2D) { return TCAD.geom.area(path2D) >= 0; }; -TCAD.geom.extrude = function(source, target) { +TCAD.geom.extrude = function(source, target, sourceNormal) { - var dotProduct = target.normalize().dot(source.normal); - if (dotProduct == 0) { + var extrudeDistance = target.normalize().dot(sourceNormal); + if (extrudeDistance == 0) { return []; } - if (dotProduct > 0) { - source = source.flip(); + var negate = extrudeDistance < 0; + + var poly = [null, null]; + var lid = []; + for (var si = 0; si < source.length; ++si) { + lid[si] = source[si].plus(target); } - var poly = [source]; + var bottom, top; + if (negate) { + bottom = lid; + top = source; + } else { + bottom = source; + top = lid; + } - var lid = source.shift(target).flip(); - poly.push(lid); - var lidShell = lid.shell.slice(0); - lidShell.reverse(); - - var n = source.shell.length; + var n = source.length; for ( var p = n - 1, i = 0; i < n; p = i ++ ) { - var face = new TCAD.Polygon([ - source.shell[i], - source.shell[p], - lidShell[p], - lidShell[i] - ]); - face.csgInfo = {derivedFrom: source.shell[i].sketchConnectionObject}; + var shared = TCAD.utils.createShared(); + shared.__tcad.csgInfo = {derivedFrom: source[p].sketchConnectionObject}; + var face = new CSG.Polygon([ + new CSG.Vertex(TCAD.utils.csgVec(bottom[p])), + new CSG.Vertex(TCAD.utils.csgVec(bottom[i])), + new CSG.Vertex(TCAD.utils.csgVec(top[i])), + new CSG.Vertex(TCAD.utils.csgVec(top[p])) + ], shared); poly.push(face); } + + if (negate) { + lid.reverse(); + } else { + source = source.slice(0); + source.reverse(); + } + + function vecToVertex(v) { + return new CSG.Vertex(TCAD.utils.csgVec(v)); + } + + poly[0] = new CSG.Polygon(source.map(vecToVertex), TCAD.utils.createShared()); + poly[1] = new CSG.Polygon(lid.map(vecToVertex), TCAD.utils.createShared()); return poly; }; 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)); +TCAD.geom.triangulate = function(path, normal) { + var _3dTransformation = new TCAD.Matrix().setBasis(TCAD.geom.someBasis2(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 < path.length; ++i) { + shell[i] = _2dTransformation.apply(path[i].pos); } - // - //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; -}; - TCAD.utils.groupCSG = function(csg) { var csgPolygons = csg.toPolygons(); var groups = {}; @@ -449,7 +421,9 @@ TCAD.utils.groupCSG = function(csg) { return groups; }; -TCAD.utils.createShared = function(id) { +TCAD.utils.SHARED_COUNTER = 0; +TCAD.utils.createShared = function() { + var id = TCAD.utils.SHARED_COUNTER ++; var shared = new CSG.Polygon.Shared([id, id, id, id]); shared.__tcad = {}; return shared; @@ -500,6 +474,7 @@ TCAD.Solid = function(csg, material) { face.normal = normal; face.materialIndex = gIdx; this.faces.push(face); + TCAD.view.setFaceColor(polyFace, !!group.shared.__tcad.csgInfo && !!group.shared.__tcad.csgInfo.derivedFrom && group.shared.__tcad.csgInfo.derivedFrom._class === 'TCAD.TWO.Arc' ? 0xFF0000 : null); } off = this.vertices.length; } diff --git a/web/app/workbench.js b/web/app/workbench.js index a3e92087..ecf244d7 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -101,7 +101,7 @@ TCAD.craft.getSketchedPolygons3D = function(app, face) { var sketchedPolygons = []; for (var i = 0; i < polygons2D.length; i++) { var poly2D = polygons2D[i]; - if (poly2D.shell.length < 3) continue; + if (poly2D.length < 3) continue; if (depth == null) { var _3dTransformation = new TCAD.Matrix().setBasis(face.basis()); @@ -109,16 +109,16 @@ TCAD.craft.getSketchedPolygons3D = function(app, face) { depth = face.csgGroup.plane.w; } - var shell = []; - for (var m = 0; m < poly2D.shell.length; ++m) { - var vec = poly2D.shell[m]; + var polygon = []; + for (var m = 0; m < poly2D.length; ++m) { + var vec = poly2D[m]; vec.z = depth; // var a = _3dTransformation.apply(new TCAD.Vector(poly2D[m][0], poly2D[m][1], depth)); var a = _3dTransformation.apply(vec); a.sketchConnectionObject = vec.sketchConnectionObject; - shell.push(a); + polygon.push(a); } - var polygon = new TCAD.Polygon(shell); + sketchedPolygons.push(polygon); } return sketchedPolygons; @@ -132,10 +132,11 @@ TCAD.craft.extrude = function(app, request) { var normal = TCAD.utils.vec(face.csgGroup.plane.normal); var toMeldWith = []; for (var i = 0; i < sketchedPolygons.length; i++) { - var extruded = TCAD.geom.extrude(sketchedPolygons[i], normal.multiply(request.height)); - toMeldWith = toMeldWith.concat(TCAD.craft._makeFromPolygons(extruded)); + var extruded = TCAD.geom.extrude(sketchedPolygons[i], normal.multiply(request.height), normal); + toMeldWith = toMeldWith.concat(extruded); } - var meld = request.solids[0].csg.union(CSG.fromPolygons(toMeldWith)); + + var meld = request.solids[0].csg.union(CSG.fromPolygons(TCAD.craft._triangulateCSG(toMeldWith))); face.csgGroup.shared.__tcad.faceId += '$'; return [TCAD.utils.createSolidMesh(meld).geometry]; @@ -555,42 +556,27 @@ TCAD.craft._mergeCSGPolygons = function (__cgsPolygons, allPoints) { return filteredPaths; }; -TCAD.craft._makeFromPolygons = function(polygons) { +TCAD.craft._triangulateCSG = function(polygons) { function csgVec(v) { return new CSG.Vector3D(v.x, v.y, v.z); } - var points = []; - var csgPolygons = []; - var off = 0; + var triangled = []; for (var ei = 0; ei < polygons.length; ++ei) { var poly = polygons[ei]; - Array.prototype.push.apply( points, poly.shell ); - for ( var h = 0; h < poly.holes.length; h ++ ) { - Array.prototype.push.apply( points, poly.holes[h] ); - } - var pid = poly.id; - var shared = TCAD.utils.createShared(pid); - shared.__tcad.csgInfo = poly.csgInfo; - //shared.__tcad.faceId = poly.__face.id; - - var refs = poly.triangulate(); + var points = poly.vertices; + var refs = TCAD.geom.triangulate(points, poly.plane.normal); for ( var i = 0; i < refs.length; ++ i ) { - var a = refs[i][0] + off; - var b = refs[i][1] + off; - var c = refs[i][2] + off; - if (points[b].minus(points[a]).cross(points[c].minus(points[a])).length() === 0) { + var a = refs[i][0]; + var b = refs[i][1]; + var c = refs[i][2]; + if (points[b].pos.minus(points[a].pos).cross(points[c].pos.minus(points[a].pos)).length() === 0) { continue; } - var csgPoly = new CSG.Polygon([ - new CSG.Vertex(csgVec(points[a]), csgVec(poly.normal)), - new CSG.Vertex(csgVec(points[b]), csgVec(poly.normal)), - new CSG.Vertex(csgVec(points[c]), csgVec(poly.normal)) - ], shared); - csgPolygons.push(csgPoly); + var csgPoly = new CSG.Polygon([points[a], points[b], points[c]], poly.shared, poly.plane); + triangled.push(csgPoly); } - off = points.length; } - return csgPolygons; + return triangled; }; TCAD.craft.recoverySketchInfo = function(polygons) { @@ -840,10 +826,10 @@ TCAD.craft.cut = function(app, request) { var normal = TCAD.utils.vec(face.csgGroup.plane.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 extruded = TCAD.geom.extrude(sketchedPolygons[i], normal.multiply( - request.depth), normal); + cutter = cutter.concat(extruded); } - var cutterCSG = CSG.fromPolygons(cutter); + var cutterCSG = CSG.fromPolygons(TCAD.craft._triangulateCSG(cutter)); face.csgGroup.shared.__tcad.faceId += '$'; var outSolids = []; From 450ef8a3f184e23fd7df55a8db897f3ce31df6c2 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Sat, 12 Sep 2015 02:19:02 -0700 Subject: [PATCH 19/21] support multiple projects --- web/app/3d/main.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/app/3d/main.js b/web/app/3d/main.js index 5119c6df..d9d34f0c 100644 --- a/web/app/3d/main.js +++ b/web/app/3d/main.js @@ -2,7 +2,10 @@ TCAD = {}; TCAD.App = function() { - this.id = "DEFAULT"; + this.id = window.location.hash.substring(1); + if (!this.id) { + this.id = "DEFAULT"; + } this.bus = new TCAD.Bus(); this.viewer = new TCAD.Viewer(this.bus); this.ui = new TCAD.UI(this); From 5d704ac06cb2706c7f0ebc89be183dd036ad0960 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Sat, 12 Sep 2015 02:40:50 -0700 Subject: [PATCH 20/21] fix circle reverse bug --- web/app/engine.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/app/engine.js b/web/app/engine.js index 90f85a03..89279568 100644 --- a/web/app/engine.js +++ b/web/app/engine.js @@ -272,13 +272,14 @@ TCAD.utils.sketchToPolygons = function(geom) { for (var li = 0; li < geom.loops.length; ++li) { var loop = geom.loops[li]; var polyPoints = loop.slice(0); - if (!TCAD.geom.isCCW(polyPoints)) polyPoints.reverse(); for (var si = 0; si < polyPoints.length; si++) { var conn = polyPoints[si]; //reuse a point and ignore b point since it's a guaranteed loop conn.a.sketchConnectionObject = conn.sketchObject; polyPoints[si] = conn.a; } + // we assume that connection object is the same al other the loop. That's why reverse is safe. + if (!TCAD.geom.isCCW(polyPoints)) polyPoints.reverse(); if (polyPoints.length >= 3) { polygons.push(polyPoints); } From 038edc03741c49ff2f4568f865a2dcb8eb797ad2 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Sat, 12 Sep 2015 02:51:54 -0700 Subject: [PATCH 21/21] fixed splitted circle case --- web/app/3d/main.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/web/app/3d/main.js b/web/app/3d/main.js index d9d34f0c..348ef667 100644 --- a/web/app/3d/main.js +++ b/web/app/3d/main.js @@ -126,7 +126,15 @@ TCAD.App.prototype.sketchFace = function() { }); } function isCircle(path) { - return path.length > 0 && path[0].sketchConnectionObject !== undefined && path[0].sketchConnectionObject._class == 'TCAD.TWO.Circle'; + for (var i = 0; i < path.length; i++) { + var p = path[i]; + if (p.sketchConnectionObject === undefined + || p.sketchConnectionObject._class !== 'TCAD.TWO.Circle' + || p.sketchConnectionObject.id !== path[0].sketchConnectionObject.id) { + return false; + } + } + return true; } function trPath (path) { @@ -139,6 +147,7 @@ TCAD.App.prototype.sketchFace = function() { for (var i = 0; i < paths.length; i++) { var path = paths[i].vertices; + if (path.length < 3) continue; var shift = 0; if (isCircle(path)) { addCircle(trPath(path)); @@ -151,7 +160,8 @@ TCAD.App.prototype.sketchFace = function() { var currSko = null; var arc = null; TCAD.utils.iteratePath(path, shift+1, function(a, b, ai, bi, iterNumber, path) { - var isArc = a.sketchConnectionObject !== undefined && a.sketchConnectionObject._class == 'TCAD.TWO.Arc'; + var isArc = a.sketchConnectionObject !== undefined && + (a.sketchConnectionObject._class == 'TCAD.TWO.Arc' || a.sketchConnectionObject._class == 'TCAD.TWO.Circle'); //if circle gets splitted var a2d = _2dTr.apply(a); if (isArc) { if (currSko !== a.sketchConnectionObject.id) {