From 99bbe1b7aedcc0f620740a70898691e301ac6f48 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 9 Sep 2016 18:30:13 -0700 Subject: [PATCH] extract Solid class out --- web/app/{utils => 3d}/cad-utils.js | 286 +---------------------------- web/app/3d/counters.js | 7 + web/app/3d/ctrl.js | 2 +- web/app/3d/modeler-app.js | 2 +- web/app/3d/solid.js | 284 ++++++++++++++++++++++++++++ web/app/3d/viewer.js | 2 +- web/app/3d/wizards/wizards.js | 2 +- web/app/3d/workbench.js | 7 +- 8 files changed, 301 insertions(+), 291 deletions(-) rename web/app/{utils => 3d}/cad-utils.js (70%) create mode 100644 web/app/3d/counters.js create mode 100644 web/app/3d/solid.js diff --git a/web/app/utils/cad-utils.js b/web/app/3d/cad-utils.js similarity index 70% rename from web/app/utils/cad-utils.js rename to web/app/3d/cad-utils.js index 4d4e2085..8401e688 100644 --- a/web/app/utils/cad-utils.js +++ b/web/app/3d/cad-utils.js @@ -3,16 +3,12 @@ import {HashTable} from '../utils/hashmap' import {Graph} from '../math/graph' import * as math from '../math/math' import {Matrix3, AXIS, ORIGIN} from '../math/l3space' -import {reconstructSketchBounds} from '../3d/workbench' //FIXME +import Counters from './counters' +import {Solid} from './solid' export const DPR = (window.devicePixelRatio) ? window.devicePixelRatio : 1; export const FACE_COLOR = 0xB0C4DE; -export var Counters = { - solid : 0, - shared : 0 -}; - export function createSquare(width) { width /= 2; @@ -544,25 +540,6 @@ export function triangulate(path, normal) { // return THREE.Shape.utils.triangulateShape( f2d.shell, f2d.holes ); } -export function groupCSG(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; -} - export function createShared() { var id = Counters.shared ++; var shared = new CSG.Polygon.Shared([id, id, id, id]); @@ -575,265 +552,6 @@ export function isSmoothPiece(shared) { (shared.__tcad.csgInfo.derivedFrom._class === 'TCAD.TWO.Arc' || shared.__tcad.csgInfo.derivedFrom._class === 'TCAD.TWO.Circle'); } -export function sameID(id1, id2) { - if (id1 === null || id2 === null) { - return false; - } - return id1 === id2; -} - -export function getDerivedID(shared) { - return shared.__tcad && !!shared.__tcad.csgInfo && !!shared.__tcad.csgInfo.derivedFrom ? shared.__tcad.csgInfo.derivedFrom.id : null; -} - -export function getDerivedFrom(shared) { - return shared.__tcad && !!shared.__tcad.csgInfo && !!shared.__tcad.csgInfo.derivedFrom ? shared.__tcad.csgInfo.derivedFrom : null; -} - -/** @constructor */ -export function Solid(csg, material, type) { - csg = csg.reTesselated().canonicalized(); - this.tCadType = type || 'SOLID'; - this.csg = csg; - - this.cadGroup = new THREE.Object3D(); - this.cadGroup.__tcad_solid = this; - - var geometry = new THREE.Geometry(); - geometry.dynamic = true; - this.mesh = new THREE.Mesh(geometry, material); - this.cadGroup.add(this.mesh); - - this.tCadId = Counters.solid ++; - this.faceCounter = 0; - - this.wireframeGroup = new THREE.Object3D(); - this.cadGroup.add(this.wireframeGroup); - - this.polyFaces = []; - this.wires = HashTable.forEdge(); - this.curvedSurfaces = {}; - this.mergeable = true; - - this.setupGeometry(); -} - -Solid.prototype.setupGeometry = function() { - function threeV(v) {return new THREE.Vector3( v.x, v.y, v.z )} - - var off = 0; - var groups = groupCSG(this.csg); - var geom = this.mesh.geometry; - for (var gIdx in groups) { - var group = groups[gIdx]; - if (group.shared.__tcad === undefined) group.shared.__tcad = {}; - var polyFace = new SketchFace(this, group); - this.polyFaces.push(polyFace); - for (var p = 0; p < group.polygons.length; ++p) { - var poly = group.polygons[p]; - var vLength = poly.vertices.length; - if (vLength < 3) continue; - var firstVertex = poly.vertices[0]; - geom.vertices.push(threeV(firstVertex.pos)); - geom.vertices.push(threeV(poly.vertices[1].pos)); - var normal = threeV(poly.plane.normal); - for (var i = 2; i < vLength; i++) { - geom.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 = normal; - face.materialIndex = gIdx; - geom.faces.push(face); - //face.color.set(new THREE.Color().setRGB( Math.random(), Math.random(), Math.random())); - } - //view.setFaceColor(polyFace, utils.isSmoothPiece(group.shared) ? 0xFF0000 : null); - off = geom.vertices.length; - } - this.collectCurvedSurface(polyFace); - this.collectWires(polyFace); - } - - geom.mergeVertices(); - - this.processWires(); -}; - -Solid.prototype.vanish = function() { - this.cadGroup.parent.remove( this.cadGroup ); - this.mesh.material.dispose(); - this.mesh.geometry.dispose(); -}; - -Solid.prototype.collectCurvedSurface = function(face) { - var derivedFrom = getDerivedFrom(face.csgGroup.shared); - if (derivedFrom === null || derivedFrom._class !== "TCAD.TWO.Arc" && derivedFrom._class !== "TCAD.TWO.Circle" ) return; - var surfaces = this.curvedSurfaces[derivedFrom.id]; - if (surfaces === undefined) { - surfaces = []; - this.curvedSurfaces[derivedFrom.id] = surfaces; - } - surfaces.push(face); - face.curvedSurfaces = surfaces; -}; - -Solid.prototype.collectWires = function(face) { - - function contains(planes, plane) { - for (var j = 0; j < planes.length; j++) { - if (planes[j].equals(plane)) { - return true; - } - } - return false; - } - var paths = reconstructSketchBounds(this.csg, face, true); - for (var i = 0; i < paths.length; i++) { - var path = paths[i]; - var p, q, n = path.vertices.length; - for (q = 0, p = n - 1; q < n; p = q++) { - var edge = [path.vertices[p], path.vertices[q]]; - var data = this.wires.get(edge); - - if (data === null) { - data = { - sharedPlanes : [face.csgGroup.plane], - sharedFaces : [face] - }; - this.wires.put(edge, data); - } else { - if (!contains(data.sharedPlanes, face.csgGroup.plane)) { - data.sharedPlanes.push(face.csgGroup.plane); - } - data.sharedFaces.push(face); - } - } - } -}; - -Solid.SMOOTH_LIMIT = 10 * Math.PI / 180; - -Solid.prototype.processWires = function() { - var solid = this; - this.wires.entries(function(edge, data) { - if (data.sharedPlanes.length > 1) { - var plane0 = data.sharedPlanes[0]; - var plane1 = data.sharedPlanes[1]; - var angle = Math.acos(plane0.normal.dot(plane1.normal)); - if (angle < Solid.SMOOTH_LIMIT) { - return; - } - } - for (var i = 0; i < data.sharedFaces.length; ++i) { - for (var j = i + 1; j < data.sharedFaces.length; ++j) { - var face0 = data.sharedFaces[0]; - var face1 = data.sharedFaces[1]; - if (sameID(getDerivedID(face0.csgGroup.shared), getDerivedID(face1.csgGroup.shared))) { - return; - } - } - } - - solid.addLineToScene(edge[0], edge[1]); - }); -}; - -Solid.prototype.addLineToScene = function(a, b) { - var lg = new THREE.Geometry(); - lg.vertices.push(a); - lg.vertices.push(b); - var line = new THREE.Line(lg, SketchFace.prototype.WIREFRAME_MATERIAL); - this.wireframeGroup.add(line); -}; - -/** @constructor */ -function SketchFace(solid, csgGroup) { - csgGroup.__face = this; - if (csgGroup.shared.__tcad.faceId === undefined) { - this.id = solid.tCadId + ":" + (solid.faceCounter++); - } else { - this.id = csgGroup.shared.__tcad.faceId; - } - csgGroup.shared.__tcad.faceId = this.id; - - this.solid = solid; - this.csgGroup = csgGroup; - this.faces = []; - this.sketch3DGroup = null; - this.curvedSurfaces = null; -} - -if (typeof THREE !== "undefined") { - SketchFace.prototype.SKETCH_MATERIAL = new THREE.LineBasicMaterial({ - color: 0xFFFFFF, linewidth: 3/DPR}); - SketchFace.prototype.WIREFRAME_MATERIAL = new THREE.LineBasicMaterial({ - color: 0x2B3856, linewidth: 3/DPR}); -} - -SketchFace.prototype.calcBasis = function() { - var normal = vec(this.csgGroup.plane.normal); - var alignPlane, x, y; - if (Math.abs(normal.dot(AXIS.Y)) < 0.5) { - alignPlane = normal.cross(AXIS.Y); - } else { - alignPlane = normal.cross(AXIS.Z); - } - y = alignPlane.cross(normal); - x = y.cross(normal); - return [x, y, normal]; -}; - -SketchFace.prototype.basis = function() { - if (!this._basis) { - this._basis = this.calcBasis(); - } - return this._basis; - //return someBasis(this.csgGroup.polygons[0].vertices.map(function (v) { - // return vec(v.pos) - //}), vec(this.csgGroup.plane.normal)); -}; - -SketchFace.prototype.depth = function() { - return this.csgGroup.plane.w; -}; - -SketchFace.prototype.syncSketches = function(geom) { - var i; - var normal = this.csgGroup.plane.normal; - var offVector = normal.scale(0); // disable it. use polygon offset feature of material - - if (this.sketch3DGroup != null) { - for (i = this.sketch3DGroup.children.length - 1; i >= 0; --i) { - this.sketch3DGroup.remove(this.sketch3DGroup.children[i]); - } - } else { - this.sketch3DGroup = new THREE.Object3D(); - this.solid.cadGroup.add(this.sketch3DGroup); - } - - var basis = this.basis(); - var _3dTransformation = new Matrix3().setBasis(basis); - //we lost depth or z off in 2d sketch, calculate it again - var depth = this.csgGroup.plane.w; - var connections = geom.connections.concat(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); - var b = _3dTransformation.apply(l.b); - - lg.vertices.push(a.plus(offVector).three()); - lg.vertices.push(b.plus(offVector).three()); - var line = new THREE.Line(lg, this.SKETCH_MATERIAL); - this.sketch3DGroup.add(line); - } -}; - var POLYGON_COUNTER = 0; /** @constructor */ export function Polygon(shell, holes, normal) { diff --git a/web/app/3d/counters.js b/web/app/3d/counters.js new file mode 100644 index 00000000..ec171101 --- /dev/null +++ b/web/app/3d/counters.js @@ -0,0 +1,7 @@ + +const Counters = { + solid : 0, + shared : 0 +}; + +export default Counters; \ No newline at end of file diff --git a/web/app/3d/ctrl.js b/web/app/3d/ctrl.js index 1a364f3e..3deef3a8 100644 --- a/web/app/3d/ctrl.js +++ b/web/app/3d/ctrl.js @@ -1,5 +1,5 @@ import * as tk from '../ui/toolkit' -import * as cad_utils from '../utils/cad-utils' +import * as cad_utils from './cad-utils' import * as math from '../math/math' import * as workbench from './workbench' import {ExtrudeWizard, PlaneWizard} from './wizards/wizards' diff --git a/web/app/3d/modeler-app.js b/web/app/3d/modeler-app.js index fe84e7a7..ef7acaaf 100644 --- a/web/app/3d/modeler-app.js +++ b/web/app/3d/modeler-app.js @@ -4,7 +4,7 @@ import {UI} from './ctrl' import Vector from '../math/vector' import {Matrix3, AXIS, ORIGIN, IDENTITY_BASIS} from '../math/l3space' import * as workbench from './workbench' -import * as cad_utils from '../utils/cad-utils' +import * as cad_utils from './cad-utils' import * as math from '../math/math' function App() { diff --git a/web/app/3d/solid.js b/web/app/3d/solid.js new file mode 100644 index 00000000..24b46438 --- /dev/null +++ b/web/app/3d/solid.js @@ -0,0 +1,284 @@ +import {HashTable} from '../utils/hashmap' +import Vector from '../math/vector' +import Counters from './counters' +import {reconstructSketchBounds} from './workbench' +import {Matrix3, AXIS} from '../math/l3space' +import {DPR, arrFlatten1L} from './cad-utils' + +/** @constructor */ +export function Solid(csg, material, type) { + csg = csg.reTesselated().canonicalized(); + this.tCadType = type || 'SOLID'; + this.csg = csg; + + this.cadGroup = new THREE.Object3D(); + this.cadGroup.__tcad_solid = this; + + var geometry = new THREE.Geometry(); + geometry.dynamic = true; + this.mesh = new THREE.Mesh(geometry, material); + this.cadGroup.add(this.mesh); + + this.tCadId = Counters.solid ++; + this.faceCounter = 0; + + this.wireframeGroup = new THREE.Object3D(); + this.cadGroup.add(this.wireframeGroup); + + this.polyFaces = []; + this.wires = HashTable.forEdge(); + this.curvedSurfaces = {}; + this.mergeable = true; + + this.setupGeometry(); +} + +function groupCSG(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; +} + +Solid.prototype.setupGeometry = function() { + function threeV(v) {return new THREE.Vector3( v.x, v.y, v.z )} + + var off = 0; + var groups = groupCSG(this.csg); + var geom = this.mesh.geometry; + for (var gIdx in groups) { + var group = groups[gIdx]; + if (group.shared.__tcad === undefined) group.shared.__tcad = {}; + var polyFace = new SketchFace(this, group); + this.polyFaces.push(polyFace); + for (var p = 0; p < group.polygons.length; ++p) { + var poly = group.polygons[p]; + var vLength = poly.vertices.length; + if (vLength < 3) continue; + var firstVertex = poly.vertices[0]; + geom.vertices.push(threeV(firstVertex.pos)); + geom.vertices.push(threeV(poly.vertices[1].pos)); + var normal = threeV(poly.plane.normal); + for (var i = 2; i < vLength; i++) { + geom.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 = normal; + face.materialIndex = gIdx; + geom.faces.push(face); + //face.color.set(new THREE.Color().setRGB( Math.random(), Math.random(), Math.random())); + } + //view.setFaceColor(polyFace, utils.isSmoothPiece(group.shared) ? 0xFF0000 : null); + off = geom.vertices.length; + } + this.collectCurvedSurface(polyFace); + this.collectWires(polyFace); + } + + geom.mergeVertices(); + + this.processWires(); +}; + +Solid.prototype.vanish = function() { + this.cadGroup.parent.remove( this.cadGroup ); + this.mesh.material.dispose(); + this.mesh.geometry.dispose(); +}; + +Solid.prototype.collectCurvedSurface = function(face) { + var derivedFrom = getDerivedFrom(face.csgGroup.shared); + if (derivedFrom === null || derivedFrom._class !== "TCAD.TWO.Arc" && derivedFrom._class !== "TCAD.TWO.Circle" ) return; + var surfaces = this.curvedSurfaces[derivedFrom.id]; + if (surfaces === undefined) { + surfaces = []; + this.curvedSurfaces[derivedFrom.id] = surfaces; + } + surfaces.push(face); + face.curvedSurfaces = surfaces; +}; + +Solid.prototype.collectWires = function(face) { + + function contains(planes, plane) { + for (var j = 0; j < planes.length; j++) { + if (planes[j].equals(plane)) { + return true; + } + } + return false; + } + var paths = reconstructSketchBounds(this.csg, face, true); + for (var i = 0; i < paths.length; i++) { + var path = paths[i]; + var p, q, n = path.vertices.length; + for (q = 0, p = n - 1; q < n; p = q++) { + var edge = [path.vertices[p], path.vertices[q]]; + var data = this.wires.get(edge); + + if (data === null) { + data = { + sharedPlanes : [face.csgGroup.plane], + sharedFaces : [face] + }; + this.wires.put(edge, data); + } else { + if (!contains(data.sharedPlanes, face.csgGroup.plane)) { + data.sharedPlanes.push(face.csgGroup.plane); + } + data.sharedFaces.push(face); + } + } + } +}; + +Solid.SMOOTH_LIMIT = 10 * Math.PI / 180; + +Solid.prototype.processWires = function() { + var solid = this; + this.wires.entries(function(edge, data) { + if (data.sharedPlanes.length > 1) { + var plane0 = data.sharedPlanes[0]; + var plane1 = data.sharedPlanes[1]; + var angle = Math.acos(plane0.normal.dot(plane1.normal)); + if (angle < Solid.SMOOTH_LIMIT) { + return; + } + } + for (var i = 0; i < data.sharedFaces.length; ++i) { + for (var j = i + 1; j < data.sharedFaces.length; ++j) { + var face0 = data.sharedFaces[0]; + var face1 = data.sharedFaces[1]; + if (sameID(getDerivedID(face0.csgGroup.shared), getDerivedID(face1.csgGroup.shared))) { + return; + } + } + } + + solid.addLineToScene(edge[0], edge[1]); + }); +}; + +Solid.prototype.addLineToScene = function(a, b) { + var lg = new THREE.Geometry(); + lg.vertices.push(a); + lg.vertices.push(b); + var line = new THREE.Line(lg, SketchFace.prototype.WIREFRAME_MATERIAL); + this.wireframeGroup.add(line); +}; + +/** @constructor */ +function SketchFace(solid, csgGroup) { + csgGroup.__face = this; + if (csgGroup.shared.__tcad.faceId === undefined) { + this.id = solid.tCadId + ":" + (solid.faceCounter++); + } else { + this.id = csgGroup.shared.__tcad.faceId; + } + csgGroup.shared.__tcad.faceId = this.id; + + this.solid = solid; + this.csgGroup = csgGroup; + this.faces = []; + this.sketch3DGroup = null; + this.curvedSurfaces = null; +} + +if (typeof THREE !== "undefined") { + SketchFace.prototype.SKETCH_MATERIAL = new THREE.LineBasicMaterial({ + color: 0xFFFFFF, linewidth: 3/DPR}); + SketchFace.prototype.WIREFRAME_MATERIAL = new THREE.LineBasicMaterial({ + color: 0x2B3856, linewidth: 3/DPR}); +} + +SketchFace.prototype.calcBasis = function() { + var normal = new Vector().setV(this.csgGroup.plane.normal); + var alignPlane, x, y; + if (Math.abs(normal.dot(AXIS.Y)) < 0.5) { + alignPlane = normal.cross(AXIS.Y); + } else { + alignPlane = normal.cross(AXIS.Z); + } + y = alignPlane.cross(normal); + x = y.cross(normal); + return [x, y, normal]; +}; + +SketchFace.prototype.basis = function() { + if (!this._basis) { + this._basis = this.calcBasis(); + } + return this._basis; + //return someBasis(this.csgGroup.polygons[0].vertices.map(function (v) { + // return vec(v.pos) + //}), vec(this.csgGroup.plane.normal)); +}; + +SketchFace.prototype.depth = function() { + return this.csgGroup.plane.w; +}; + +SketchFace.prototype.syncSketches = function(geom) { + var i; + var normal = this.csgGroup.plane.normal; + var offVector = normal.scale(0); // disable it. use polygon offset feature of material + + if (this.sketch3DGroup != null) { + for (i = this.sketch3DGroup.children.length - 1; i >= 0; --i) { + this.sketch3DGroup.remove(this.sketch3DGroup.children[i]); + } + } else { + this.sketch3DGroup = new THREE.Object3D(); + this.solid.cadGroup.add(this.sketch3DGroup); + } + + var basis = this.basis(); + var _3dTransformation = new Matrix3().setBasis(basis); + //we lost depth or z off in 2d sketch, calculate it again + var depth = this.csgGroup.plane.w; + var connections = geom.connections.concat(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); + var b = _3dTransformation.apply(l.b); + + lg.vertices.push(a.plus(offVector).three()); + lg.vertices.push(b.plus(offVector).three()); + var line = new THREE.Line(lg, this.SKETCH_MATERIAL); + this.sketch3DGroup.add(line); + } +}; + +function sameID(id1, id2) { + if (id1 === null || id2 === null) { + return false; + } + return id1 === id2; +} + +function getDerivedID(shared) { + return shared.__tcad && !!shared.__tcad.csgInfo && !!shared.__tcad.csgInfo.derivedFrom ? shared.__tcad.csgInfo.derivedFrom.id : null; +} + +function getDerivedFrom(shared) { + return shared.__tcad && !!shared.__tcad.csgInfo && !!shared.__tcad.csgInfo.derivedFrom ? shared.__tcad.csgInfo.derivedFrom : null; +} \ No newline at end of file diff --git a/web/app/3d/viewer.js b/web/app/3d/viewer.js index 270f93a4..15c73f2d 100644 --- a/web/app/3d/viewer.js +++ b/web/app/3d/viewer.js @@ -1,4 +1,4 @@ -import * as cad_utils from '../utils/cad-utils' +import * as cad_utils from './cad-utils' import {Matrix3, AXIS, ORIGIN} from '../math/l3space' function Viewer(bus) { diff --git a/web/app/3d/wizards/wizards.js b/web/app/3d/wizards/wizards.js index 14b5b23f..a9de72d9 100644 --- a/web/app/3d/wizards/wizards.js +++ b/web/app/3d/wizards/wizards.js @@ -1,5 +1,5 @@ import Vector from '../../math/vector' -import * as cad_utils from '../../utils/cad-utils' +import * as cad_utils from '../cad-utils' import * as math from '../../math/math' import {Matrix3, AXIS, ORIGIN, IDENTITY_BASIS} from '../../math/l3space' diff --git a/web/app/3d/workbench.js b/web/app/3d/workbench.js index 8e715023..bceba361 100644 --- a/web/app/3d/workbench.js +++ b/web/app/3d/workbench.js @@ -1,8 +1,9 @@ import Vector from '../math/vector' -import * as cad_utils from '../utils/cad-utils' +import * as cad_utils from './cad-utils' import * as math from '../math/math' import {Matrix3, AXIS, ORIGIN} from '../math/l3space' import {HashTable} from '../utils/hashmap' +import Counters from './counters' function SketchConnection(a, b, sketchObject) { this.a = a; @@ -620,8 +621,8 @@ Craft.prototype.loadHistory = function(history) { }; Craft.prototype.reset = function(modifications) { - cad_utils.Counters.solid = 0; - cad_utils.Counters.shared = 0; + Counters.solid = 0; + Counters.shared = 0; this.app.findAllSolids().forEach(function(s) {s.vanish()}) for (var i = 0; i < modifications.length; i++) { var request = materialize(this.app.indexEntities(), modifications[i]);