diff --git a/modules/brep/debug/debugger/utils.jsx b/modules/brep/debug/debugger/utils.jsx index 293eb18a..eff6989d 100644 --- a/modules/brep/debug/debugger/utils.jsx +++ b/modules/brep/debug/debugger/utils.jsx @@ -58,15 +58,18 @@ function createDirectedCurve(points, arrowDir, arrowTipPos, color) { obj.__tcad_debug_materials = []; let material = new THREE.LineBasicMaterial({color, linewidth: 10}); - let lg = new THREE.Geometry(); + const vertices = []; + let edgeLength = 0; for (let i = 1; i < points.length; ++i) { let a = points[i - 1]; let b = points[i]; - lg.vertices.push(a.three()); - lg.vertices.push(b.three()); + vertices.push(a.three()); + vertices.push(b.three()); edgeLength += distanceAB3(a, b); } + const lg = new THREE.BufferGeometry().setFromPoints( vertices ); + obj.__tcad_debug_materials.push(material); obj.add(new THREE.Line(lg, material)); diff --git a/modules/scene/geoms.js b/modules/scene/geoms.js index feb0d418..4909a58e 100644 --- a/modules/scene/geoms.js +++ b/modules/scene/geoms.js @@ -1,41 +1,26 @@ -import {BoxGeometry, Face3, Geometry, Vector3} from 'three'; +import {BoxGeometry, BufferGeometry, BufferAttribute, Vector3} from 'three'; +import {normalOfCCWSeq} from "cad/cad-utils"; export function createBoxGeometry(width, height, depth) { return new BoxGeometry(width, height, depth); } export function createMeshGeometry(triangles) { - const geometry = new Geometry(); - for (let tr of triangles) { - const a = geometry.vertices.length; - const b = a + 1; - const c = a + 2; - const face = new Face3(a, b, c); - tr.forEach(v => geometry.vertices.push(v.three())); - geometry.faces.push(face); - } - geometry.mergeVertices(); - geometry.computeFaceNormals(); + const vertices = []; + const normals = []; + triangles.forEach(tr => { + const normal = normalOfCCWSeq(tr); + tr.forEach(p => { + vertices.push(p.x, p.y, p.z); + normals.push(normal.x, normal.y, normal.z); + }) + }); + + + const geometry = new BufferGeometry(); + geometry.setAttribute('position', new BufferAttribute( new Float32Array(vertices), 3 ) ); + geometry.setAttribute('normal', new BufferAttribute( new Float32Array(normals), 3)); + return geometry; } - -export function createSmoothMeshGeometryFromData(tessInfo) { - const geometry = new Geometry(); - const vec = arr => new Vector3().fromArray(arr); - - for (let [tr, normals] of tessInfo) { - if (!normals || normals.find(n => n[0] === null || n[1] === null || n[2] === null)) { - normals = undefined; - } - const a = geometry.vertices.length; - const b = a + 1; - const c = a + 2; - const face = new Face3(a, b, c, normals && normals.map(vec)); - tr.forEach(v => geometry.vertices.push(vec(v))); - geometry.faces.push(face); - } - geometry.mergeVertices(); - geometry.computeFaceNormals(); - return geometry; -} \ No newline at end of file diff --git a/modules/scene/materials.js b/modules/scene/materials.js index 32f4b866..c3ebec7d 100644 --- a/modules/scene/materials.js +++ b/modules/scene/materials.js @@ -3,7 +3,7 @@ import {MeshPhongMaterial, LineBasicMaterial, FaceColors, DoubleSide} from 'thre export function createTransparentPhongMaterial(color, opacity) { return new MeshPhongMaterial({ - vertexColors: FaceColors, + // vertexColors: FaceColors, color, transparent: true, opacity: opacity, diff --git a/modules/scene/objectData.js b/modules/scene/objectData.js index 02b9baa6..7d673207 100644 --- a/modules/scene/objectData.js +++ b/modules/scene/objectData.js @@ -1,21 +1,23 @@ export function setAttribute(obj, key, value) { - getData(obj)[key] = value; + getData(obj, true)[key] = value; } export function getAttribute(obj, key) { - return getData(obj)[key]; + return getData(obj, false)[key]; } export function unsetAttribute(obj, key) { - delete getData(obj)[key]; + delete getData(obj, false)[key]; } -export function getData(obj) { +function getData(obj, create) { let data = obj.__TCAD_CUSTOM_DATA; if (data === undefined) { data = {}; - obj.__TCAD_CUSTOM_DATA = data; + if (create) { + obj.__TCAD_CUSTOM_DATA = data; + } } return data; } \ No newline at end of file diff --git a/modules/scene/objects/auxiliary.js b/modules/scene/objects/auxiliary.js index 31e33916..4e2dddaf 100644 --- a/modules/scene/objects/auxiliary.js +++ b/modules/scene/objects/auxiliary.js @@ -1,9 +1,6 @@ import DPR from 'dpr'; import {ArrowHelper, CylinderBufferGeometry, Mesh, MeshBasicMaterial, Object3D, Vector3} from 'three'; import {createMeshLineGeometry} from './meshLine'; -import {Sphere} from 'three/src/math/Sphere'; -import {Matrix4} from 'three/src/math/Matrix4'; -import {Ray} from 'three/src/math/Ray'; export function createArrow(length, arrowLength, arrowHead, axis, color, opacity, materialMixins) { let arrow = new ArrowHelper(new Vector3().copy(axis), new Vector3(0, 0, 0), length, color, arrowLength, arrowHead); diff --git a/modules/scene/objects/facetedCube.js b/modules/scene/objects/facetedCube.js deleted file mode 100644 index fdabfe72..00000000 --- a/modules/scene/objects/facetedCube.js +++ /dev/null @@ -1,145 +0,0 @@ -import {Face3, Geometry, Vector3} from 'three'; - -export default function facetedCube(size, w) { - let d = size * 0.5; - let l = d - w; - const v = (x,y,z) => new Vector3(x,y,z); - let geom = new Geometry(); - - //front - geom.vertices.push(v(-l, -l, d)); - geom.vertices.push(v(l, -l, d)); - geom.vertices.push(v(l, l, d)); - geom.vertices.push(v(-l, l, d)); - - //top - geom.vertices.push(v(-l, d, l)); - geom.vertices.push(v(l, d, l)); - geom.vertices.push(v(l, d, -l)); - geom.vertices.push(v(-l, d, -l)); - - //back - geom.vertices.push(v(-l, -l, -d)); - geom.vertices.push(v(l, -l, -d)); - geom.vertices.push(v(l, l, -d)); - geom.vertices.push(v(-l, l, -d)); - - //bottom - geom.vertices.push(v(-l, -d, l)); - geom.vertices.push(v(l, -d, l)); - geom.vertices.push(v(l, -d, -l)); - geom.vertices.push(v(-l, -d, -l)); - - //left - geom.vertices.push(v(-d, -l, -l)); - geom.vertices.push(v(-d, -l, l)); - geom.vertices.push(v(-d, l, l)); - geom.vertices.push(v(-d, l, -l)); - - //right - geom.vertices.push(v(d, -l, -l)); - geom.vertices.push(v(d, -l, l)); - geom.vertices.push(v(d, l, l)); - geom.vertices.push(v(d, l, -l)); - - //front - geom.faces.push( new Face3( 0, 1, 2 ) ); - geom.faces.push( new Face3( 2, 3, 0 ) ); - - //top - geom.faces.push( new Face3( 4, 5, 6 ) ); - geom.faces.push( new Face3( 6, 7, 4 ) ); - - //back - geom.faces.push( new Face3( 10, 9, 8) ); - geom.faces.push( new Face3( 8, 11, 10 ) ); - - //bottom - geom.faces.push( new Face3( 14, 13, 12) ); - geom.faces.push( new Face3( 12, 15, 14 ) ); - - //left - geom.faces.push( new Face3( 16, 17, 18 ) ); - geom.faces.push( new Face3( 18, 19, 16 ) ); - - // right - geom.faces.push( new Face3( 22, 21, 20 ) ); - geom.faces.push( new Face3( 20, 23, 22 ) ); - - //front-top - geom.faces.push( new Face3( 4, 3, 2) ); - geom.faces.push( new Face3( 2, 5, 4 ) ); - - //top-back - geom.faces.push( new Face3( 7, 6, 10) ); - geom.faces.push( new Face3( 10, 11, 7 ) ); - - // back-bottom - geom.faces.push( new Face3( 8, 9, 14) ); - geom.faces.push( new Face3( 14, 15, 8 ) ); - - //bottom-left - geom.faces.push( new Face3( 15, 12, 17) ); - geom.faces.push( new Face3( 17, 16, 15 ) ); - - //bottom-right - geom.faces.push( new Face3( 20, 21, 13) ); - geom.faces.push( new Face3( 13, 14, 20 ) ); - - //top-right - geom.faces.push( new Face3( 6, 5, 22) ); - geom.faces.push( new Face3( 22, 23, 6 ) ); - - //top-left - geom.faces.push( new Face3( 19, 18, 4) ); - geom.faces.push( new Face3( 4, 7, 19 ) ); - - //front-left - geom.faces.push( new Face3( 18, 17, 0) ); - geom.faces.push( new Face3( 0, 3, 18 ) ); - - //front-bottom - geom.faces.push( new Face3( 12, 13, 1) ); - geom.faces.push( new Face3( 1, 0, 12 ) ); - - //right-back - geom.faces.push( new Face3( 9, 10, 23) ); - geom.faces.push( new Face3( 23, 20, 9 ) ); - - //front-right - geom.faces.push( new Face3( 21, 22, 2) ); - geom.faces.push( new Face3( 2, 1, 21 ) ); - - //back-left - geom.faces.push( new Face3( 16, 19, 11) ); - geom.faces.push( new Face3( 11, 8, 16 ) ); - - - //front-top-left - geom.faces.push( new Face3( 4, 18, 3 ) ); - - //front-top-right - geom.faces.push( new Face3( 2, 22, 5 ) ); - - //top-right-back - geom.faces.push( new Face3( 23, 10, 6 ) ); - - // top-left-back - geom.faces.push( new Face3( 7, 11, 19 ) ); - - // front-left-bottom - geom.faces.push( new Face3( 17, 12, 0 ) ); - - // front-right-bottom - geom.faces.push( new Face3( 1, 13, 21 ) ); - - // back-right-bottom - geom.faces.push( new Face3( 20, 14, 9 ) ); - - // back-left-bottom - geom.faces.push( new Face3( 8, 15, 16 ) ); - - geom.computeFaceNormals(); - - return geom; -} \ No newline at end of file diff --git a/modules/scene/objects/meshLine.js b/modules/scene/objects/meshLine.js index b73670e5..d4ca9263 100644 --- a/modules/scene/objects/meshLine.js +++ b/modules/scene/objects/meshLine.js @@ -1,10 +1,11 @@ import * as vec from 'math/vec'; -import {Face3, Geometry, Vector3} from 'three'; +import {BufferAttribute, BufferGeometry} from 'three'; import {perpendicularVector} from "geom/euclidean"; export function createMeshLineGeometry(points, width) { - const vThree = arr => new Vector3().fromArray(arr); - const geometry = new Geometry(); + const geometry = new BufferGeometry(); + const vertices = []; + const index = []; let base = null; for (let i = 1; i < points.length; i++) { @@ -24,9 +25,9 @@ export function createMeshLineGeometry(points, width) { } let lid = dirs.map(d => vec.add(b, d)); - let off = geometry.vertices.length; - base.forEach(p => geometry.vertices.push(vThree(p))); - lid.forEach(p => geometry.vertices.push(vThree(p))); + let off = vertices.length; + base.forEach(p => vertices.push(...p)); + lid.forEach(p => vertices.push(...p)); base = lid; [ @@ -38,8 +39,10 @@ export function createMeshLineGeometry(points, width) { [5, 4, 0], [1, 2, 6], [6, 5, 1], - ].forEach(([a, b, c]) => geometry.faces.push(new Face3(a + off, b + off, c + off))); + ].forEach(([a, b, c]) => index.push(a + off, b + off, c + off)); } - geometry.computeFaceNormals(); + geometry.setIndex( index ); + geometry.setAttribute('position', new BufferAttribute( new Float32Array(vertices), 3)); + geometry.computeVertexNormals(); return geometry; } diff --git a/modules/scene/objects/scalableLine.js b/modules/scene/objects/scalableLine.js index 8e728018..740ac900 100644 --- a/modules/scene/objects/scalableLine.js +++ b/modules/scene/objects/scalableLine.js @@ -1,140 +1,63 @@ -import {Face3, FaceColors, Geometry, Mesh, MeshBasicMaterial, MeshPhongMaterial} from 'three'; -import {advancePseudoFrenetFrame, frenetFrame, pseudoFrenetFrame} from 'geom/curves/frenetFrame'; -import * as vec from 'math/vec'; -import {viewScaleFactor} from '../scaleHelper'; -import {arrToThree} from 'math/vectorAdapters'; -import {getSceneSetup} from '../sceneSetup'; -import calcFaceNormal from '../utils/calcFaceNormal'; -import {BufferGeometry} from "three/src/core/BufferGeometry"; -import {ORIGIN} from "math/vector"; +import {LineGeometry} from 'three/examples/jsm/lines/LineGeometry'; +import {LineMaterial} from 'three/examples/jsm/lines/LineMaterial'; +import {LineSegments2} from 'three/examples/jsm/lines/LineSegments2'; +import {Vector2} from 'three'; +import SceneSetUp from "scene/sceneSetup"; +// import {BufferGeometry, BufferAttribute} from "three/src/core/BufferGeometry"; -export default class ScalableLine extends Mesh { +export default class ScalableLine extends LineSegments2 { - constructor(tessellation, width, color, opacity, smooth, ambient, offset) { - super(createGeometry(tessellation, smooth), createMaterial(color, opacity, ambient, offset)); - this.width = width; - this.morphTargetInfluences = [0]; + constructor(sceneSetup, tessellation, width, color) { + super(createGeometry(tessellation), createMaterial(sceneSetup, color, width)); + this.resolutionListenerDisposer = sceneSetup.viewportSizeUpdate$.attach(() => { + this.material.resolution = getResolution(sceneSetup) + }); } - updateMatrix() { - let sceneSetup = getSceneSetup(this); - if (!sceneSetup) { - return; - } - let modelSize = 1; - let modelSizePx = this.width; - let k = viewScaleFactor(sceneSetup, ORIGIN, modelSizePx, modelSize); - let morphDistance = k * modelSize / 2 - modelSize; - this.morphTargetInfluences[0] = morphDistance / morphBase; - super.updateMatrix(); - } - dispose() { this.geometry.dispose(); this.material.dispose(); + this.resolutionListenerDisposer(); } } -function createMaterial(color, opacity, ambient, offset) { - let materialParams = { - vertexColors: FaceColors, - morphTargets: true, +function getResolution(sceneSetup) { + return new Vector2(sceneSetup.container.clientWidth, sceneSetup.container.clientHeight); +} + +function createMaterial(sceneSetup, color, width, opacity, ambient, offset) { + // let modelSize = 1; + // let modelSizePx = width; + // let k = viewScaleFactor(sceneSetup, ORIGIN, modelSizePx, modelSize); + + return new LineMaterial( { color, - }; - if (offset) { - Object.assign(materialParams, { - polygonOffset: true, - polygonOffsetFactor: -2.0, - polygonOffsetUnits: -1.0, - }); - } - if (!ambient) { - materialParams.shininess = 0; - } - if (opacity !== undefined) { - materialParams.transparent = true; - materialParams.opacity = opacity; - } - return ambient ? new MeshBasicMaterial(materialParams) : new MeshPhongMaterial(materialParams); -} - -function createGeometry(tessellation, smooth) { - const width = 1; - const geometry = new Geometry(); - const scaleTargets = []; - geometry.dynamic = true; - let tess = tessellation; - - // let frames = [pseudoFrenetFrame(edge.curve.tangentAtPoint(new Vector().set3(tess[0])).data())]; - let frames = [pseudoFrenetFrame(vec._normalize(vec.sub(tess[1], tess[0])))]; - // let frames = [calcFrame(tess[0]) || pseudoFrenetFrame(edge.curve.tangentAtPoint(new Vector().set3(tess[0])).data())]; - - for (let i = 1; i < tess.length; i++) { - let a = tess[i - 1]; - let b = tess[i]; - let ab = vec._normalize(vec.sub(b, a)); - let prevFrame = frames[i - 1]; - let T = vec._normalize(vec.add(prevFrame[0], ab)); - // frames.push(calcFrame(b) || advancePseudoFrenetFrame(prevFrame, T)); - frames.push(advancePseudoFrenetFrame(prevFrame, T)); - } - - let axises = frames.map(([T, N, B]) => { - let dirs = []; - dirs[0] = N; - dirs[1] = B; - dirs[2] = vec.negate(dirs[0]); - dirs[3] = vec.negate(dirs[1]); - return dirs; + linewidth: width, + resolution: getResolution(sceneSetup), + // worldUnits: false, + // linewidth: 0.0031,//width, + // vertexColors: true, + // dashed: false, + // alphaToCoverage: true, }); - let normals = smooth ? [] : null; - - axises.forEach((dirs, i) => { - dirs.forEach(dir => { - geometry.vertices.push(arrToThree(vec._add(vec.mul(dir, width), tess[i]))); - scaleTargets.push(arrToThree(vec._add(vec.mul(dir, width + morphBase), tess[i]))); - if (smooth) { - normals.push(arrToThree(dir)); - } - }); - - }); - - for (let i = 0; i < tess.length - 1; i++) { - let off = 4 * i; - [ - [0, 4, 3], - [3, 4, 7], - [2, 3, 7], - [7, 6, 2], - [0, 1, 5], - [5, 4, 0], - [1, 2, 6], - [6, 5, 1], - ].forEach(([a, b, c]) => { - let vertexNormales = smooth ? [normals[a + off], normals[b + off], normals[c + off]] : undefined; - let face = new Face3(a + off, b + off, c + off, vertexNormales); - geometry.faces.push(face); - if (!smooth) { - calcFaceNormal(face, geometry.vertices); - } - }); - } - - let startNormal = arrToThree(frames[0][0]).negate(); - geometry.faces.push(new Face3(2, 1, 0, startNormal)); - geometry.faces.push(new Face3(0, 3, 2, startNormal)); - - let endNormal = arrToThree(frames[frames.length - 1][0]); - let n = frames.length * 4 - 1; - geometry.faces.push(new Face3(n - 2, n - 1, n, endNormal)); - geometry.faces.push(new Face3(n, n - 3, n - 2, endNormal)); - - - geometry.morphTargets.push({name: 'scaleTargets', vertices: scaleTargets}); - - return new BufferGeometry().fromGeometry(geometry); + // + // if (opacity !== undefined) { + // materialParams.transparent = true; + // materialParams.opacity = opacity; + // } + // return ambient ? new MeshBasicMaterial(materialParams) : new MeshPhongMaterial(materialParams); } -const morphBase = 10; \ No newline at end of file +function createGeometry(tessellation) { + + const positions = []; + for ( let point of tessellation ) { + positions.push( ...point ); + } + const geometry = new LineGeometry(); + geometry.setPositions( positions ); + + return geometry; + +} diff --git a/modules/scene/sceneSetup.ts b/modules/scene/sceneSetup.ts index 636beb98..f58a5963 100644 --- a/modules/scene/sceneSetup.ts +++ b/modules/scene/sceneSetup.ts @@ -201,6 +201,11 @@ export default class SceneSetUp { createRaycaster(viewX, viewY) { let raycaster = new Raycaster(); raycaster.params.Line.threshold = 12 * (this._zoomMeasure() * 0.8); + + raycaster.params.Line2 = { + threshold: 20 + }; + let x = ( viewX / this.container.clientWidth ) * 2 - 1; let y = - ( viewY / this.container.clientHeight ) * 2 + 1; diff --git a/modules/scene/utils/calcFaceNormal.js b/modules/scene/utils/calcFaceNormal.js index 4fd7c91b..b1fc41b1 100644 --- a/modules/scene/utils/calcFaceNormal.js +++ b/modules/scene/utils/calcFaceNormal.js @@ -1,10 +1,7 @@ -import {Vector3} from 'three'; -export default function(face, vertices) { + +export default function(vA, vB, vC) { let ab = new Vector3(); - let vA = vertices[ face.a ]; - let vB = vertices[ face.b ]; - let vC = vertices[ face.c ]; face.normal.subVectors( vC, vB ); ab.subVectors( vA, vB ); face.normal.cross( ab ); diff --git a/package-lock.json b/package-lock.json index d3117466..a14948b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "react-icons": "^4.4.0", "react-toastify": "^5.5.0", "sprintf": "0.1.5", - "three": "^0.118.3", + "three": "^0.143.0", "xml2js": "^0.4.23" }, "devDependencies": { @@ -14687,9 +14687,9 @@ "dev": true }, "node_modules/three": { - "version": "0.118.3", - "resolved": "https://registry.npmjs.org/three/-/three-0.118.3.tgz", - "integrity": "sha512-ijECXrNzDkHieoeh2H69kgawTGH8DiamhR4uBN8jEM7VHSKvfTdEvOoHsA8Aq7dh7PHAxhlqBsN5arBI3KixSw==" + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.143.0.tgz", + "integrity": "sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg==" }, "node_modules/throttleit": { "version": "1.0.0", @@ -28609,9 +28609,9 @@ "dev": true }, "three": { - "version": "0.118.3", - "resolved": "https://registry.npmjs.org/three/-/three-0.118.3.tgz", - "integrity": "sha512-ijECXrNzDkHieoeh2H69kgawTGH8DiamhR4uBN8jEM7VHSKvfTdEvOoHsA8Aq7dh7PHAxhlqBsN5arBI3KixSw==" + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.143.0.tgz", + "integrity": "sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg==" }, "throttleit": { "version": "1.0.0", diff --git a/package.json b/package.json index 22c04a36..90096518 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "react-icons": "^4.4.0", "react-toastify": "^5.5.0", "sprintf": "0.1.5", - "three": "^0.118.3", + "three": "^0.143.0", "xml2js": "^0.4.23" } } diff --git a/web/app/cad/cad-utils.js b/web/app/cad/cad-utils.js index ccd47c2e..4eca7f4c 100644 --- a/web/app/cad/cad-utils.js +++ b/web/app/cad/cad-utils.js @@ -1,6 +1,5 @@ import Vector from 'math/vector'; import BBox from 'math/bbox' -import {MeshSceneSolid} from './scene/wrappers/meshSceneObject' import {Matrix3x4} from 'math/matrix'; import {equal} from 'math/equality'; import {area, isCCW, isPointInsidePolygon} from "geom/euclidean"; @@ -40,29 +39,6 @@ export function createBox(w, h, d) { return extrude(square, normal, normal.multiply(d), 1); } -export function createCSGBox(w, h, d) { - var csg = CSG.fromPolygons(createBox(w, h, d)); - return createSolid(csg); -} - -export function createSphere(radius) { - var csg = CSG.sphere({radius: radius, resolution: 48}); - var shared = createShared(); - shared.__tcad.csgInfo = { - derivedFrom : { - id : 0, - _class : 'TCAD.TWO.Circle' - } - }; - for (var i = 0; i < csg.polygons.length; i++) { - var poly = csg.polygons[i]; - poly.shared = shared; - } - var solid = createSolid(csg); - solid.cadGroup.remove(solid.wireframeGroup); - return solid; -} - export function checkPolygon(poly) { if (poly.length < 3) { throw new Error('Polygon should contain at least 3 point'); @@ -92,9 +68,9 @@ export function createPoint0(x, y, z) { " gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n" +'\n}' }); - - var geometry = new THREE.Geometry(); - geometry.vertices.push(new THREE.Vector3(x, y, z)); + + const geometry = new THREE.BufferGeometry().setFromPoints( [new THREE.Vector3(x, y, z)] ); + // geometry.vertices.push(new THREE.Vector3(x+.001, y+.001, z+.001)); // var line = new THREE.PointCloud(geometry, material); @@ -124,9 +100,12 @@ export function createLine(a, b, color) { color: color, linewidth: 1 }); - var geometry = new THREE.Geometry(); - geometry.vertices.push(new THREE.Vector3(a.x, a.y, a.z)); - geometry.vertices.push(new THREE.Vector3(b.x, b.y, b.z)); + + const vertices = [] + vertices.push(new THREE.Vector3(a.x, a.y, a.z)); + vertices.push(new THREE.Vector3(b.x, b.y, b.z)); + const geometry = new THREE.BufferGeometry().setFromPoints( vertices ); + return new THREE.Line(geometry, material); } @@ -142,10 +121,6 @@ export function createSolidMaterial() { }); } -export function createSolid(csg, id) { - return new MeshSceneSolid(csg, undefined, id); -} - export function intercept(obj, methodName, aspect) { var originFunc = obj[methodName]; obj[methodName] = function() { @@ -154,61 +129,6 @@ export function intercept(obj, methodName, aspect) { } } -export function createPlane(basis, depth) { - var initWidth = 1; - var boundingPolygon = [ - new Vector(0, 0, 0), - new Vector(initWidth, 0, 0), - new Vector(initWidth, initWidth, 0), - new Vector(0, initWidth, 0) - ]; - var shared = createShared(); - - var material = createSolidMaterial(); - material.transparent = true; - material.opacity = 0.5; - material.side = THREE.DoubleSide; - - var tr = new Matrix3x4().setBasis(basis); - var currentBounds = new BBox(); - var points = boundingPolygon.map(function(p) { p.z = depth; return tr._apply(p); }); - var polygon = new CSG.Polygon(points.map(function(p){return new CSG.Vertex(csgVec(p))}), shared); - var plane = new MeshSceneSolid(CSG.fromPolygons([polygon]), 'PLANE'); - plane.wireframeGroup.visible = false; - plane.mergeable = false; - - function setBounds(bbox) { - currentBounds = bbox; - const poly = new CSG.Polygon(bbox.toPolygon().map(function(p){p.z = depth; return new CSG.Vertex(csgVec( tr._apply(p) ))}), shared); - plane.csg = CSG.fromPolygons([poly]); - plane.dropGeometry(); - plane.createGeometry(); - } - var bb = new BBox(); - bb.checkBounds(-400, -400); - bb.checkBounds( 400, 400); - setBounds(bb); - - var sketchFace = plane.sceneFaces[0]; - intercept(sketchFace, 'syncSketches', function(invocation, args) { - var geom = args[0]; - invocation(geom); - var bbox = new BBox(); - var connections = geom.connections.concat(arrFlatten1L(geom.loops)); - for (var i = 0; i < connections.length; ++i) { - var l = connections[i]; - bbox.checkBounds(l.a.x, l.a.y); - bbox.checkBounds(l.b.x, l.b.y); - } - if (bbox.maxX > currentBounds.maxX || bbox.maxY > currentBounds.maxY || bbox.minX < currentBounds.minX || bbox.minY < currentBounds.minY) { - bbox.expand(50); - setBounds(bbox); - } - }); - - return plane; -} - export function fixCCW(path, normal) { var _2DTransformation = new Matrix3x4().setBasis(someBasis(path, normal)).invert(); var path2D = []; @@ -300,63 +220,6 @@ export function calculateExtrudedLid(sourcePolygon, normal, direction, expansion return lid; } -export function extrude(source, sourceNormal, target, expansionFactor) { - - var extrudeDistance = target.normalize().dot(sourceNormal); - if (extrudeDistance == 0) { - return []; - } - var negate = extrudeDistance < 0; - - var poly = [null, null]; - var lid = calculateExtrudedLid(source, sourceNormal, target, expansionFactor); - - var bottom, top; - if (negate) { - bottom = lid; - top = source; - } else { - bottom = source; - top = lid; - } - - var n = source.length; - for ( var p = n - 1, i = 0; i < n; p = i ++ ) { - var shared = createShared(); - shared.__tcad.csgInfo = {derivedFrom: source[p].sketchConnectionObject}; - var face = new CSG.Polygon([ - new CSG.Vertex(csgVec(bottom[p])), - new CSG.Vertex(csgVec(bottom[i])), - new CSG.Vertex(csgVec(top[i])), - new CSG.Vertex(csgVec(top[p])) - ], shared); - poly.push(face); - } - - var bottomNormal, topNormal; - if (negate) { - lid.reverse(); - bottomNormal = sourceNormal; - topNormal = sourceNormal.negate(); - } else { - source = source.slice(0); - source.reverse(); - bottomNormal = sourceNormal.negate(); - topNormal = sourceNormal; - } - - function vecToVertex(v) { - return new CSG.Vertex(csgVec(v)); - } - - var sourcePlane = new CSG.Plane(bottomNormal.csg(), bottomNormal.dot(source[0])); - var lidPlane = new CSG.Plane(topNormal.csg(), topNormal.dot(lid[0])); - - poly[0] = new CSG.Polygon(source.map(vecToVertex), createShared(), sourcePlane); - poly[1] = new CSG.Polygon(lid.map(vecToVertex), createShared(), lidPlane); - return poly; -} - export function triangulate(path, normal) { var _3dTransformation = new Matrix3x4().setBasis(someBasis2(normal)); var _2dTransformation = _3dTransformation.invert(); @@ -370,13 +233,6 @@ export function triangulate(path, normal) { // return THREE.Shape.utils.triangulateShape( f2d.shell, f2d.holes ); } -export function createShared() { - var id = Counters.shared ++; - var shared = new CSG.Polygon.Shared([id, id, id, id]); - shared.__tcad = {}; - return shared; -} - export function isCurveClass(className) { return false; } diff --git a/web/app/cad/craft/datum/datumObject.js b/web/app/cad/craft/datum/datumObject.js index 401caeb3..7ecb7b60 100644 --- a/web/app/cad/craft/datum/datumObject.js +++ b/web/app/cad/craft/datum/datumObject.js @@ -1,4 +1,4 @@ -import {Geometry, Line, LineBasicMaterial, MeshBasicMaterial, Object3D, Vector3} from 'three'; +import {BufferGeometry, Line, LineBasicMaterial, MeshBasicMaterial, Object3D, Vector3} from 'three'; import CSysObject3D from './csysObject'; @@ -56,10 +56,10 @@ export default class DatumObject3D extends Object3D { let ext = dir.multiply(this.viewer.sceneSetup.workingSphere); const material = new LineBasicMaterial({color}); - let geometry = new Geometry(); - - geometry.vertices.push(new Vector3().copy(this.csys.origin.minus(ext))); - geometry.vertices.push(new Vector3().copy(this.csys.origin.plus(ext))); + const geometry = new BufferGeometry().setFromPoints( [ + new Vector3().copy(this.csys.origin.minus(ext)), + new Vector3().copy(this.csys.origin.plus(ext)) + ]); let line = new Line(geometry, material); this.add(line); diff --git a/web/app/cad/debugPlugin.js b/web/app/cad/debugPlugin.js index 1541955d..9647d0a3 100644 --- a/web/app/cad/debugPlugin.js +++ b/web/app/cad/debugPlugin.js @@ -1,6 +1,5 @@ import {checkForSelectedFaces} from './actions/actionHelpers'; -import {brepFaceToGeom, surfaceToThreeGeom} from './scene/wrappers/brepSceneObject'; -import {createSolidMaterial} from './scene/wrappers/sceneObject'; +import {brepFaceToGeom, createSolidMaterial, surfaceToThreeGeom, tessDataToGeom} from './scene/views/viewUtils'; import DPR from 'dpr'; import Vector from 'math/vector'; import * as vec from 'math/vec'; @@ -10,7 +9,6 @@ import {toLoops} from 'brep/io/brepLoopsFormat'; import curveTess from 'geom/impl/curve/curve-tess'; import {LOG_FLAGS} from './logFlags'; import {state} from "lstream"; -import {BufferGeometry, BufferAttribute, Float32BufferAttribute, Int32BufferAttribute} from 'three'; const BREP_DEBUG_WINDOW_VISIBLE$ = state(false); @@ -116,17 +114,20 @@ function addGlobalDebugActions({viewer, cadScene, cadRegistry}) { }, AddVolume: (shell, color) => { color = color || 0xffffff; - const geometry = new THREE.Geometry(); - shell.faces.forEach(f => brepFaceToGeom(f, geometry)); - triangulateToThree(shell, geometry); - const mesh = new THREE.Mesh(geometry, createSolidMaterial({ - color, - transparent: true, - opacity: 0.3, - depthWrite: false, - depthTest: false - })); - debugVolumeGroup.add(mesh); + // const geometry = new THREE.Geometry(); + shell.faces.forEach(f => { + const geometry = brepFaceToGeom(f,) + + const mesh = new THREE.Mesh(geometry, createSolidMaterial({ + color, + transparent: true, + opacity: 0.3, + depthWrite: false, + depthTest: false + })); + debugVolumeGroup.add(mesh); + + }); // window.__DEBUG__.AddWireframe(shell, color); viewer.render(); }, @@ -134,19 +135,20 @@ function addGlobalDebugActions({viewer, cadScene, cadRegistry}) { color = color || 0xffffff; const visited = new Set(); for (let e of shell.edges) { - let lg = new THREE.Geometry(); - lg.vertices.push(e.halfEdge1.vertexA.point.three()); - lg.vertices.push(e.halfEdge2.vertexA.point.three()); - const line = new THREE.Line(lg, new THREE.LineBasicMaterial({color, linewidth: 3/DPR})); + const vertices = [] + vertices.push(e.halfEdge1.vertexA.point.three()); + vertices.push(e.halfEdge2.vertexA.point.three()); + + const lg = new THREE.BufferGeometry().setFromPoints( vertices ); + + const line = new THREE.Line(lg, new THREE.LineBasicMaterial({color, linewidth: 3/DPR})); debugVolumeGroup.add(line); } viewer.render(); }, AddParametricSurface: (srf, color) => { color = color || 0xffffff; - const geometry = new THREE.Geometry(); - surfaceToThreeGeom(srf, geometry); - geometry.computeFaceNormals(); + const geometry = surfaceToThreeGeom(srf); const mesh = new THREE.Mesh(geometry, createSolidMaterial({ color, transparent: true, @@ -192,22 +194,8 @@ function addGlobalDebugActions({viewer, cadScene, cadRegistry}) { AddTessDump: (triangles, color) => { const vec = arr => new THREE.Vector3().fromArray(arr); color = color || 0xffffff; - const geometry = new THREE.Geometry(); - for (let i = 0; i < triangles.length; ++i) { - let off = geometry.vertices.length; - let tr = triangles[i], normales; - if (Array.isArray(tr[0][0])) { - normales = tr[1]; - tr = tr[0]; - if (normales.find(n => n[0] === null || n[1] === null || n[2] === null)) { - normales = undefined; - } - } - tr.forEach(p => geometry.vertices.push(vec(p))); - const face = new THREE.Face3(off, off + 1, off + 2, normales && normales.map(vec)); - geometry.faces.push(face); - } - geometry.computeFaceNormals(); + + const geometry = tessDataToGeom(triangles); const mesh = new THREE.Mesh(geometry, createSolidMaterial({ vertexColors: THREE.FaceColors, color: 0xB0C4DE, @@ -282,9 +270,8 @@ function clearGroup(g) { export function createLine(a, b, color) { color = color || 0xFA8072; const debugLineMaterial = new THREE.LineBasicMaterial({color, linewidth: 10}); - const lg = new THREE.Geometry(); - lg.vertices.push(a.three()); - lg.vertices.push(b.three()); + const vertices = [a.three(), b.three()]; + const lg = new THREE.BufferGeometry().setFromPoints( vertices ); return new THREE.Line(lg, debugLineMaterial); } diff --git a/web/app/cad/exportPlugin.js b/web/app/cad/exportPlugin.js index 0501170d..8616dbc4 100644 --- a/web/app/cad/exportPlugin.js +++ b/web/app/cad/exportPlugin.js @@ -1,11 +1,12 @@ -import stlExporter from './stl/stlExporter'; +import {STLExporter} from './stl/stlExporter'; import exportTextData from 'gems/exportTextData'; export function activate(ctx) { function toStlAsciiString() { - let views = ctx.services.cadRegistry.shells.map(mShell => mShell.ext.view).filter(m => !!m); - return stlExporter(views); + const exporter = new STLExporter(); + const views = ctx.services.cadRegistry.shells.map(mShell => mShell.ext.view).filter(m => !!m); + return exporter.parse( views ); } function stlAscii() { diff --git a/web/app/cad/scene/controls/pickControlPlugin.ts b/web/app/cad/scene/controls/pickControlPlugin.ts index cc4bb78d..010368e5 100644 --- a/web/app/cad/scene/controls/pickControlPlugin.ts +++ b/web/app/cad/scene/controls/pickControlPlugin.ts @@ -289,8 +289,8 @@ export function traversePickResults(event, pickResults, kind, visitor) { return false; }, (pickResult) => { - if (mask.is(kind, PICK_KIND.LOOP) && !!pickResult.face) { - let faceV = getAttribute(pickResult.face, LOOP); + if (mask.is(kind, PICK_KIND.LOOP)) { + let faceV = getAttribute(pickResult.object, LOOP); if (faceV) { return !visitor(faceV.model, event, pickResult); } @@ -298,8 +298,8 @@ export function traversePickResults(event, pickResults, kind, visitor) { return false; }, (pickResult) => { - if (mask.is(kind, PICK_KIND.FACE) && !!pickResult.face) { - let faceV = getAttribute(pickResult.face, FACE); + if (mask.is(kind, PICK_KIND.FACE)) { + let faceV = getAttribute(pickResult.object, FACE); if (faceV) { return !visitor(faceV.model, event, pickResult); } diff --git a/web/app/cad/scene/views/curveBasedView.js b/web/app/cad/scene/views/curveBasedView.js index 9411f290..cfc77aab 100644 --- a/web/app/cad/scene/views/curveBasedView.js +++ b/web/app/cad/scene/views/curveBasedView.js @@ -2,29 +2,31 @@ import {View} from './view'; import * as SceneGraph from 'scene/sceneGraph'; import {setAttribute} from 'scene/objectData'; import ScalableLine from 'scene/objects/scalableLine'; +import {NULL_COLOR} from "cad/scene/views/faceView"; export class CurveBasedView extends View { - constructor(ctx, model, tessellation, visualWidth, markerWidth, color, defaultMarkColor, offset, markTable) { + constructor(ctx, model, tessellation, visualWidth, color, markTable) { super(ctx, model, undefined, markTable); this.rootGroup = SceneGraph.createGroup(); - this.representation = new ScalableLine(tessellation, visualWidth, color, undefined, false, true, offset); - this.marker = new ScalableLine(tessellation, markerWidth, defaultMarkColor, undefined, false, true, offset); - this.picker = new ScalableLine(tessellation, 10, 0xFA8072, undefined, false, true, offset); - this.marker.visible = false; - this.picker.material.visible = false; + this.representation = new ScalableLine(ctx.viewer.sceneSetup, tessellation, visualWidth, color, undefined, false, true, false); + this.setColor(color); + // this.marker = new ScalableLine(tessellation, markerWidth, defaultMarkColor, undefined, false, true, offset); + // this.picker = new ScalableLine(tessellation, 10, 0xFA8072, undefined, false, true, offset); + // this.marker.visible = false; + // this.picker.material.visible = false; setAttribute(this.representation, model.TYPE, this); - setAttribute(this.picker, model.TYPE, this); + // setAttribute(this.picker, model.TYPE, this); this.rootGroup.add(this.representation); - this.rootGroup.add(this.marker); - this.rootGroup.add(this.picker); - this.picker.onMouseEnter = () => { + // this.rootGroup.add(this.marker); + // this.rootGroup.add(this.picker); + this.representation.onMouseEnter = () => { if (!this.isDisposed) { this.ctx.highlightService.highlight(this.model.id); } } - this.picker.onMouseLeave = () => { + this.representation.onMouseLeave = () => { if (!this.isDisposed) { this.ctx.highlightService.unHighlight(this.model.id); } @@ -33,20 +35,21 @@ export class CurveBasedView extends View { updateVisuals() { - const markColor = this.markColor; - if (!markColor) { - this.marker.visible = false; - this.representation.visible = true; - } else { - this.marker.material.color.set(markColor); - this.marker.visible = true; - this.representation.visible = false; - } + + this.representation.material.color.set(this.markColor||this.color); + + // if (this.markColor) { + // + // this.representation.material.color.set( new Color().set(this.markColor) ); + // } + // this.representation.material.needsUpdate = true; + // this.representation.needsUpdate = true; + } dispose() { this.representation.dispose(); - this.marker.dispose(); + // this.marker.dispose(); super.dispose(); } } \ No newline at end of file diff --git a/web/app/cad/scene/views/edgeView.js b/web/app/cad/scene/views/edgeView.js index dbdb25e5..db2d0838 100644 --- a/web/app/cad/scene/views/edgeView.js +++ b/web/app/cad/scene/views/edgeView.js @@ -18,6 +18,6 @@ export class EdgeView extends CurveBasedView { constructor(ctx, edge) { let brepEdge = edge.brepEdge; let tess = brepEdge.data.tessellation ? brepEdge.data.tessellation : brepEdge.curve.tessellateToData(); - super(ctx, edge, tess, 2, 4, 0x2B3856, 0xc42720, false, MarkerTable); + super(ctx, edge, tess, 3, 0x000000, MarkerTable); } } diff --git a/web/app/cad/scene/views/faceView.js b/web/app/cad/scene/views/faceView.js index 759258b8..5b981b6d 100644 --- a/web/app/cad/scene/views/faceView.js +++ b/web/app/cad/scene/views/faceView.js @@ -1,13 +1,12 @@ -import {setAttribute} from 'scene/objectData'; -import {brepFaceToGeom, tessDataToGeom} from '../wrappers/brepSceneObject'; -import {FACE} from 'cad/model/entities'; +import {brepFaceToGeom, tessDataToGeom} from './viewUtils'; import * as SceneGraph from 'scene/sceneGraph'; import {SketchObjectView} from './sketchObjectView'; import {View} from './view'; import {SketchLoopView} from './sketchLoopView'; -import {createSolidMaterial} from "cad/scene/wrappers/sceneObject"; +import {createSolidMaterial} from "cad/scene/views/viewUtils"; import {SketchMesh} from "cad/scene/views/shellView"; -import {Geometry} from "three"; +import {FACE} from "cad/model/entities"; +import {setAttribute} from "scene/objectData"; export class SketchingView extends View { @@ -68,26 +67,18 @@ export class FaceView extends SketchingView { constructor(ctx, face, parent, skin) { super(ctx, face, parent); - const geom = new Geometry(); - geom.dynamic = true; - this.geometry = geom; + let geom; - this.material = createSolidMaterial(skin); this.meshFaces = []; - - const off = geom.faces.length; if (face.brepFace.data.tessellation) { - tessDataToGeom(face.brepFace.data.tessellation.data, geom) + geom = tessDataToGeom(face.brepFace.data.tessellation.data) } else { - brepFaceToGeom(face.brepFace, geom); + geom = brepFaceToGeom(face.brepFace); } - for (let i = off; i < geom.faces.length; i++) { - const meshFace = geom.faces[i]; - this.meshFaces.push(meshFace); - setAttribute(meshFace, FACE, this); - } - geom.mergeVertices(); + this.geometry = geom; + this.material = createSolidMaterial(skin); this.mesh = new SketchMesh(geom, this.material); + setAttribute(this.mesh, FACE, this); this.mesh.onMouseEnter = () => { this.ctx.highlightService.highlight(this.model.id); } @@ -114,5 +105,5 @@ export function setFacesColor(faces, color) { } } -export const NULL_COLOR = new THREE.Color(0xbfbfbf); +const NULL_COLOR = 0xbfbfbf; diff --git a/web/app/cad/scene/views/openFaceView.js b/web/app/cad/scene/views/openFaceView.js index 6cd30a1b..14c7c202 100644 --- a/web/app/cad/scene/views/openFaceView.js +++ b/web/app/cad/scene/views/openFaceView.js @@ -3,6 +3,9 @@ import {FACE, SHELL} from 'cad/model/entities'; import {SketchingView} from './faceView'; import {View} from './view'; import {SketchMesh} from './shellView'; +import {BufferAttribute, BufferGeometry} from 'three'; +import * as vec from "math/vec"; +import {normalOfCCWSeq} from "cad/cad-utils"; export class OpenFaceShellView extends View { @@ -27,7 +30,6 @@ export class OpenFaceView extends SketchingView { constructor(ctx, mFace, parent) { super(ctx, mFace, parent); this.material = new THREE.MeshPhongMaterial({ - vertexColors: THREE.FaceColors, // color: 0xB0C4DE, shininess: 0, polygonOffset : true, @@ -48,14 +50,26 @@ export class OpenFaceView extends SketchingView { } createGeometry() { - const geometry = new THREE.Geometry(); - geometry.dynamic = true; - this.bounds.forEach(v => geometry.vertices.push(v.three())); - geometry.faces.push(new THREE.Face3(0, 1, 2)); - geometry.faces.push(new THREE.Face3(0, 2, 3)); - geometry.faces.forEach(f => setAttribute(f, FACE, this)); - geometry.computeFaceNormals(); + + const vertices = [];; + const normals = [];; + const normal = normalOfCCWSeq(this.bounds); + this.bounds.forEach((v, i) => { + vertices.push(v.x, v.y, v.z); + normals.push(normal.x, normal.y, normal.z); + }); + const index = [ + 0, 1, 2, + 0, 2, 3 + ]; + + const geometry = new BufferGeometry(); + geometry.setAttribute('position', new BufferAttribute( new Float32Array(vertices), 3)); + geometry.setAttribute('normal', new BufferAttribute( new Float32Array(normals), 3)); + geometry.setIndex(index); + this.mesh = new SketchMesh(geometry, this.material); + setAttribute(this.mesh, FACE, this) this.rootGroup.add(this.mesh); this.mesh.onMouseEnter = () => { this.ctx.highlightService.highlight(this.model.id); diff --git a/web/app/cad/scene/views/sketchLoopView.js b/web/app/cad/scene/views/sketchLoopView.js index 880280d7..2f2ff23b 100644 --- a/web/app/cad/scene/views/sketchLoopView.js +++ b/web/app/cad/scene/views/sketchLoopView.js @@ -1,12 +1,12 @@ -import {MarkTracker, View} from './view'; +import {View} from './view'; import * as SceneGraph from 'scene/sceneGraph'; -import {tessellateLoopsOnSurface} from '../../tess/brep-tess'; -import {createSolidMaterial} from '../wrappers/sceneObject'; -import {DoubleSide, Geometry, Mesh} from 'three'; -import {surfaceAndPolygonsToGeom} from '../wrappers/brepSceneObject'; -import {TriangulatePolygons} from '../../tess/triangulation'; +import {tessellateLoopsOnSurface} from 'cad/tess/brep-tess'; +import {createSolidMaterial} from '../views/viewUtils'; +import {DoubleSide, Mesh} from 'three'; +import {surfaceAndPolygonsToGeom} from './viewUtils'; +import {TriangulatePolygons} from 'cad/tess/triangulation'; import Vector from 'math/vector'; -import {LOOP} from '../../model/entities'; +import {LOOP} from 'cad/model/entities'; import {setAttribute} from 'scene/objectData'; const HIGHLIGHT_COLOR = 0xDBFFD9; @@ -32,19 +32,7 @@ export class SketchLoopView extends View { super(ctx, mLoop, MarkerTable); this.rootGroup = SceneGraph.createGroup(); - const geometry = new Geometry(); - geometry.dynamic = true; - this.mesh = new Mesh(geometry, createSolidMaterial({ - // color: HIGHLIGHT_COLOR, - side: DoubleSide, - // transparent: true, - // depthTest: true, - // depthWrite: false, - polygonOffset: true, - polygonOffsetFactor: -1.0, // should less than offset of loop lines - polygonOffsetUnits: -1.0, - visible: false - })); + let surface = mLoop.face.surface; let tess; if (surface.simpleSurface && surface.simpleSurface.isPlane) { @@ -56,12 +44,21 @@ export class SketchLoopView extends View { seg => seg.inverted); } - surfaceAndPolygonsToGeom(surface, tess, this.mesh.geometry); - this.mesh.geometry.mergeVertices(); - for (let i = 0; i < geometry.faces.length; i++) { - const meshFace = geometry.faces[i]; - setAttribute(meshFace, LOOP, this); - } + const geometry = surfaceAndPolygonsToGeom(surface, tess); + + this.mesh = new Mesh(geometry, createSolidMaterial({ + // color: HIGHLIGHT_COLOR, + side: DoubleSide, + // transparent: true, + // depthTest: true, + // depthWrite: false, + polygonOffset: true, + polygonOffsetFactor: -1.0, // should less than offset of loop lines + polygonOffsetUnits: -1.0, + visible: false + })); + + setAttribute(this.mesh, LOOP, this); this.rootGroup.add(this.mesh); this.mesh.onMouseEnter = () => { diff --git a/web/app/cad/scene/views/sketchObjectView.js b/web/app/cad/scene/views/sketchObjectView.js index 2b51c39f..37318e11 100644 --- a/web/app/cad/scene/views/sketchObjectView.js +++ b/web/app/cad/scene/views/sketchObjectView.js @@ -5,8 +5,8 @@ export class SketchObjectView extends CurveBasedView { constructor(ctx, mSketchObject, sketchToWorldTransformation) { const color = mSketchObject.construction ? 0x964B00 : 0x0000FF; const tess = mSketchObject.sketchPrimitive.tessellate(10).map(sketchToWorldTransformation.apply).map(v => v.data()); - super(ctx, mSketchObject, tess, 3, 4, color, 0x49FFA5, true); - this.picker.onDblclick = () => { + super(ctx, mSketchObject, tess, 3, color); + this.representation.onDblclick = () => { ctx.sketcherService.sketchFace(this.model.face); } } diff --git a/web/app/cad/scene/views/viewUtils.js b/web/app/cad/scene/views/viewUtils.js new file mode 100644 index 00000000..e94697d5 --- /dev/null +++ b/web/app/cad/scene/views/viewUtils.js @@ -0,0 +1,105 @@ +import {BufferAttribute, BufferGeometry, DoubleSide} from "three"; + +import brepTess from '../../tess/brep-tess'; +import tessellateSurface from 'geom/surfaces/surfaceTess'; +import * as vec from 'math/vec'; + + +export function createSolidMaterial(skin) { + return new THREE.MeshPhongMaterial(Object.assign({ + // vertexColors: THREE.FaceColors, + color: 0xaeaeae, + shininess: 0, + polygonOffset : true, + polygonOffsetFactor : 1, + polygonOffsetUnits : 2, + side: DoubleSide, + }, skin)); +} + +const SMOOTH_RENDERING = true; + +export function tessDataToGeom(tessellation) { + const vertices = []; + const normals3 = []; + for (let [tr, normales] of tessellation) { + tr.forEach(p => vertices.push(...p)); + + if (normales && SMOOTH_RENDERING) { + normales.forEach(n => normals3.push(...n)); + } else { + const n = vec.normal3(tr); + normals3.push(...n, ...n, ...n); + } + } + + const geometry = new BufferGeometry(); + geometry.setAttribute('position', new BufferAttribute( new Float32Array(vertices), 3)); + geometry.setAttribute('normal', new BufferAttribute( new Float32Array(normals3), 3)); + return geometry; +} + +export function brepFaceToGeom(brepFace) { + const polygons = brepTess(brepFace); + return surfaceAndPolygonsToGeom(brepFace.surface, polygons); +} + +export function surfaceAndPolygonsToGeom(surface, polygons) { + + const vertices = []; + const normals = []; + const index = []; + + const isPlane = surface.simpleSurface && surface.simpleSurface.isPlane; + let planeNormal = isPlane ? surface.normalInMiddle().data() : null; + for (let p = 0; p < polygons.length; ++p) { + const off = vertices.length / 3; + const poly = polygons[p]; + const vLength = poly.length; + if (vLength < 3) continue; + + function pushVertex(vtx) { + vertices.push(vtx.x, vtx.y, vtx.z); + if (!isPlane) { + const normal = surface.normal(vtx); + normals.push(normal.x, normal.y, normal.z); + } else { + normals.push(...planeNormal); + } + + } + const firstVertex = poly[0]; + + pushVertex(firstVertex) + + for (let i = 2; i < vLength; i++) { + + let pVert = poly[i - 1]; + let iVert = poly[i]; + pushVertex(pVert); + pushVertex(iVert); + const a = off; + const b = (i - 1) + off; + const c = i + off; + index.push(a, b, c); + } + //view.setFaceColor(sceneFace, utils.isSmoothPiece(group.shared) ? 0xFF0000 : null); + } + + const geometry = new BufferGeometry(); + geometry.setAttribute('position', new BufferAttribute( new Float32Array(vertices), 3)); + geometry.setAttribute('normal', new BufferAttribute( new Float32Array(normals), 3)); + geometry.setIndex( index ); + return geometry; + +} + +export function surfaceToThreeGeom(srf, geom) { + const off = geom.vertices.length; + const tess = tessellateSurface(srf); + tess.points.forEach(p => geom.vertices.push(new THREE.Vector3().fromArray(p))); + for (let faceIndices of tess.faces) { + const face = new THREE.Face3(faceIndices[0] + off, faceIndices[1] + off, faceIndices[2] + off); + geom.faces.push(face); + } +} \ No newline at end of file diff --git a/web/app/cad/scene/wrappers/brepSceneObject.js b/web/app/cad/scene/wrappers/brepSceneObject.js deleted file mode 100644 index 2b472856..00000000 --- a/web/app/cad/scene/wrappers/brepSceneObject.js +++ /dev/null @@ -1,232 +0,0 @@ -import Vector from 'math/vector'; -import {SceneEdge, SceneFace, SceneSolid} from './sceneObject'; -import brepTess from '../../tess/brep-tess'; -import tessellateSurface from 'geom/surfaces/surfaceTess'; -import {setAttribute} from 'scene/objectData'; -import * as vec from 'math/vec'; -import {perpendicularVector} from "geom/euclidean"; - -const SMOOTH_RENDERING = true; - -export class BREPSceneSolid extends SceneSolid { - - constructor(shell, type, skin) { - super(type, undefined, skin); - this.shell = shell; - this.externals = this.shell.data.externals; - this.createGeometry(); - } - - createGeometry() { - const geometry = new THREE.Geometry(); - geometry.dynamic = true; - this.mesh = new THREE.Mesh(geometry, this.material); - this.cadGroup.add(this.mesh); - this.createFaces(); - this.createEdges(); - this.createVertices(); - } - - createFaces() { - const geom = this.mesh.geometry; - for (let brepFace of this.shell.faces) { - const sceneFace = new BREPSceneFace(brepFace, this); - this.sceneFaces.push(sceneFace); - let off = geom.faces.length; - if (brepFace.data.tessellation) { - tessDataToGeom(brepFace.data.tessellation.data, geom) - } else { - brepFaceToGeom(brepFace, geom); - } - for (let i = off; i < geom.faces.length; i++) { - sceneFace.registerMeshFace(geom.faces[i]); - } - } - geom.mergeVertices(); - } - - createEdges() { - for (let edge of this.shell.edges) { - this.createEdge(edge); - } - } - - createEdge(edge) { - const doEdge = (edge, aux, width, color, opacity) => { - const geometry = new THREE.Geometry(); - const scaleTargets = []; - geometry.dynamic = true; - let materialParams = { - color, - vertexColors: THREE.FaceColors, - shininess: 0, - visible: !aux, - morphTargets: true - }; - if (opacity !== undefined) { - materialParams.transparent = true; - materialParams.opacity = opacity; - } - let tess = edge.data.tessellation ? edge.data.tessellation : edge.curve.tessellateToData(); - let base = null; - for (let i = 1; i < tess.length; i++) { - - let a = tess[i - 1]; - let b = tess[i]; - let ab = vec._normalize(vec.sub(b, a)); - - let dirs = []; - dirs[0] = perpendicularVector(ab); - dirs[1] = vec.cross(ab, dirs[0]); - dirs[2] = vec.negate(dirs[0]); - dirs[3] = vec.negate(dirs[1]); - - dirs.forEach(d => vec._mul(d, width)); - if (base === null) { - base = dirs.map(d => vec.add(a, d)); - } - let lid = dirs.map(d => vec.add(b, d)); - - let off = geometry.vertices.length; - base.forEach(p => geometry.vertices.push(vThree(p))); - lid.forEach(p => geometry.vertices.push(vThree(p))); - - function addScaleTargets(points, origin) { - points.forEach(p => scaleTargets.push(vThree(vec._add(vec._mul(vec.sub(p, origin), 10), origin)))); - } - addScaleTargets(base, a); - addScaleTargets(lid, b); - - - base = lid; - - [ - [0, 4, 3], - [3, 4, 7], - [2, 3, 7], - [7, 6, 2], - [0, 1, 5], - [5, 4, 0], - [1, 2, 6], - [6, 5, 1], - ].forEach(([a, b, c]) => geometry.faces.push(new THREE.Face3(a + off, b + off, c + off))); - } - geometry.morphTargets.push( { name: "scaleTargets", vertices: scaleTargets } ); - geometry.computeFaceNormals(); - - let mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial(materialParams)); - this.wireframeGroup.add(mesh); - - // mesh.morphTargetInfluences[ 0 ] = 0.2; - return mesh; - }; - let sceneEdge = new SceneEdge(null, this); - sceneEdge.externals = edge.data.externals; - this.sceneEdges.push(sceneEdge); - let representation = doEdge(edge, false, 1, 0x2B3856); - let marker = doEdge(edge, true, 3, 0xFA8072, 0.8); - - setAttribute(representation, 'edge', sceneEdge); - setAttribute(marker, 'edge', sceneEdge); - - sceneEdge.representation = representation; - sceneEdge.marker = marker; - } - - createVertices() { - } -} - -class BREPSceneFace extends SceneFace { - constructor(brepFace, solid) { - super(solid, brepFace.id); - brepFace.id = this.id; - this.brepFace = brepFace; - this.externals = this.brepFace.data.externals; - } - - normal() { - return this.brepFace.surface.normalInMiddle(); - } - - depth() { - return this.brepFace.surface.tangentPlaneInMiddle().w; - } - - surface() { - return this.brepFace.surface; - } - - getBounds() { - const bounds = []; - for (let loop of this.brepFace.loops) { - bounds.push(loop.asPolygon().map(p => new Vector().setV(p))); - } - return bounds; - } -} - - -export function tessDataToGeom(tessellation, geom) { - for (let [tr, normales] of tessellation) { - let off = geom.vertices.length; - tr.forEach(p => geom.vertices.push(vThree(p))); - - if (normales && SMOOTH_RENDERING) { - normales = normales.map(vThree) - } else { - normales = vThree(vec.normal3(tr)); - } - const face = new THREE.Face3(off, off + 1, off + 2, normales); - geom.faces.push(face); - } -} - -export function brepFaceToGeom(brepFace, geom) { - const polygons = brepTess(brepFace); - return surfaceAndPolygonsToGeom(brepFace.surface, polygons, geom); -} - -export function surfaceAndPolygonsToGeom(surface, polygons, geom) { - - const isPlane = surface.simpleSurface && surface.simpleSurface.isPlane; - let normalOrNormals; - if (isPlane) { - normalOrNormals = surface.normalInMiddle().three(); - } - for (let p = 0; p < polygons.length; ++p) { - const off = geom.vertices.length; - const poly = polygons[p]; - const vLength = poly.length; - if (vLength < 3) continue; - const firstVertex = poly[0]; - geom.vertices.push(firstVertex.three()); - geom.vertices.push(poly[1].three()); - for (let i = 2; i < vLength; i++) { - geom.vertices.push(poly[i].three()); - const a = off; - const b = i - 1 + off; - const c = i + off; - - if (!isPlane) { - normalOrNormals = [firstVertex, poly[i - 1], poly[i]].map(v => surface.normal(v)); - } - const face = new THREE.Face3(a, b, c, normalOrNormals); - geom.faces.push(face); - } - //view.setFaceColor(sceneFace, utils.isSmoothPiece(group.shared) ? 0xFF0000 : null); - } -} - -export function surfaceToThreeGeom(srf, geom) { - const off = geom.vertices.length; - const tess = tessellateSurface(srf); - tess.points.forEach(p => geom.vertices.push(new THREE.Vector3().fromArray(p))); - for (let faceIndices of tess.faces) { - const face = new THREE.Face3(faceIndices[0] + off, faceIndices[1] + off, faceIndices[2] + off); - geom.faces.push(face); - } -} - - -const vThree = arr => new THREE.Vector3().fromArray(arr); diff --git a/web/app/cad/scene/wrappers/meshSceneObject.js b/web/app/cad/scene/wrappers/meshSceneObject.js deleted file mode 100644 index 31939b6b..00000000 --- a/web/app/cad/scene/wrappers/meshSceneObject.js +++ /dev/null @@ -1,214 +0,0 @@ -import {HashTable} from '../../../utils/hashmap' -//import {findOutline, reconstructSketchBounds, segmentsToPaths} from '../../legacy/mesh/workbench' -import {isCurveClass} from '../../cad-utils' -import {SceneFace, SceneSolid} from './sceneObject' - -export class MeshSceneSolid extends SceneSolid { - - constructor(csg, type, id) { - super(type, id); - csg = csg.reTesselated().canonicalized(); - this.csg = csg; - this.createGeometry(); - } - - createGeometry() { - const geometry = new THREE.Geometry(); - geometry.dynamic = true; - this.mesh = new THREE.Mesh(geometry, this.material); - this.cadGroup.add(this.mesh); - - this.wires = HashTable.forEdge(); - this.curvedSurfaces = {}; - - this.setupGeometry(); - } - - dropGeometry() { - this.cadGroup.remove( this.mesh ); - this.mesh.geometry.dispose(); - for(let i = this.wireframeGroup.children.length-1; i >=0 ; i--){ - this.wireframeGroup.remove(this.wireframeGroup.children[i]); - } - } - - setupGeometry() { - 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 sceneFace = new MeshSceneFace(this, group); - this.sceneFaces.push(sceneFace); - 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; - const face = sceneFace.createMeshFace(a, b, c); - 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(sceneFace, utils.isSmoothPiece(group.shared) ? 0xFF0000 : null); - off = geom.vertices.length; - } - this.collectCurvedSurface(sceneFace); - this.collectWires(sceneFace, group.polygons); - } - - geom.mergeVertices(); - - this.processWires(); - }; - - collectCurvedSurface(face) { - var derivedFrom = getDerivedFrom(face.csgGroup.shared); - if (derivedFrom === null || !isCurveClass(derivedFrom._class)) return; - var surfaces = this.curvedSurfaces[derivedFrom.id]; - if (surfaces === undefined) { - surfaces = []; - this.curvedSurfaces[derivedFrom.id] = surfaces; - } - surfaces.push(face); - face.curvedSurfaces = surfaces; - } - - collectWires(face, facePolygons) { - - function contains(planes, plane) { - for (var j = 0; j < planes.length; j++) { - if (planes[j].equals(plane)) { - return true; - } - } - return false; - } - - const outline = findOutline(facePolygons); - const paths = segmentsToPaths(outline); - - 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); - } - } - } - } - - processWires() { - 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 < 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]); - }); - } -} - -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; -} - -const SMOOTH_LIMIT = 10 * Math.PI / 180; - -class MeshSceneFace extends SceneFace { - constructor(solid, csgGroup) { - super(solid, csgGroup.shared.__tcad.faceId); - csgGroup.__face = this; - csgGroup.shared.__tcad.faceId = this.id; - - this.csgGroup = csgGroup; - this.curvedSurfaces = null; - } - - normal() { - return this.csgGroup.plane.normal; - } - - depth() { - return this.csgGroup.plane.w; - } - - surface() { - return this.csgGroup.plane; - } - - getBounds() { - return reconstructSketchBounds(this.solid.csg, this); - } -} - -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/cad/scene/wrappers/planeSceneObject.js b/web/app/cad/scene/wrappers/planeSceneObject.js deleted file mode 100644 index 3b2db719..00000000 --- a/web/app/cad/scene/wrappers/planeSceneObject.js +++ /dev/null @@ -1,80 +0,0 @@ -import Vector from 'math/vector'; -import {SceneFace, SceneSolid} from './sceneObject'; -import {createBoundingSurfaceFrom2DPoints} from 'brep/brep-builder'; - -const INIT_WIDTH_H = 750 * 0.5; -const INIT_HEIGHT_H = 750 * 0.5; - -export const INIT_BOUNDS = [ - new Vector(-INIT_WIDTH_H, -INIT_HEIGHT_H, 0), - new Vector( INIT_WIDTH_H, -INIT_HEIGHT_H, 0), - new Vector( INIT_WIDTH_H, INIT_HEIGHT_H, 0), - new Vector(-INIT_WIDTH_H, INIT_HEIGHT_H, 0) -]; - -export class PlaneSceneObject extends SceneSolid { - - constructor(plane, skin) { - super('PLANE', undefined, Object.assign({ - side : THREE.DoubleSide, - transparent: true, - opacity: 0.5 - }, skin)); - this.plane = plane; - this.surface = createBoundingSurfaceFrom2DPoints([ - new Vector(0,0,0), new Vector(0,100,0), new Vector(100,100,0), new Vector(100,0,0) - ], plane); - this.sceneFace = new PlaneSceneFace(this); - this.sceneFaces.push(this.sceneFace); // as part of the API - this.updateBounds(INIT_BOUNDS); - } - - createGeometry() { - const geometry = new THREE.Geometry(); - geometry.dynamic = true; - this.bounds.forEach(v => geometry.vertices.push(v.three())); - geometry.faces.push(new THREE.Face3(0, 1, 2)); - geometry.faces.push(new THREE.Face3(0, 2, 3)); - geometry.faces.forEach(f => this.sceneFace.registerMeshFace(f)); - geometry.computeFaceNormals(); - this.mesh = new THREE.Mesh(geometry, this.material); - this.cadGroup.add(this.mesh); - } - - dropGeometry() { - if (this.mesh) { - this.cadGroup.remove( this.mesh ); - this.mesh.geometry.dispose(); - this.sceneFace.meshFaces = []; - } - } - - updateBounds(bounds2d) { - this.dropGeometry(); - const tr = this.plane.get3DTransformation(); - this.bounds = bounds2d.map(v => tr.apply(v)); - this.createGeometry(); - } -} - -class PlaneSceneFace extends SceneFace { - constructor(scenePlane) { - super(scenePlane); - } - - normal() { - return this.solid.plane.normal; - } - - depth() { - return this.solid.plane.w; - } - - surface() { - return this.solid.surface; - } - - getBounds() { - return []; - } -} diff --git a/web/app/cad/scene/wrappers/sceneObject.js b/web/app/cad/scene/wrappers/sceneObject.js deleted file mode 100644 index 53bbfb3a..00000000 --- a/web/app/cad/scene/wrappers/sceneObject.js +++ /dev/null @@ -1,198 +0,0 @@ -import Vector from 'math/vector'; -import DPR from 'dpr' -import {getAttribute, setAttribute} from "scene/objectData"; -import {BasisForPlane} from 'math/basis'; -import {DoubleSide} from "three"; - -//todo: rename to shell -export class SceneSolid { - - ID = 0; - - constructor(type, id, skin) { - this.tCadType = type || 'SHELL'; - - this.cadGroup = new THREE.Object3D(); - setAttribute(this.cadGroup, 'shell', this); - - this.tCadId = SceneSolid.ID++; - this.id = id === undefined ? this.tCadId : id; // to keep identity through the history - this.faceCounter = 0; - this.edgeCounter = 0; - - this.wireframeGroup = new THREE.Object3D(); - this.cadGroup.add(this.wireframeGroup); - this.mergeable = true; - this.sceneFaces = []; - this.sceneEdges = []; - - this.sketch = null; - - this.material = createSolidMaterial(skin); - } - - addLineToScene(a, b) { - let lg = new THREE.Geometry(); - lg.vertices.push(a); - lg.vertices.push(b); - let line = new THREE.Line(lg, WIREFRAME_MATERIAL); - this.wireframeGroup.add(line); - return line; - } - - dispose() { - this.material.dispose(); - this.mesh.geometry.dispose(); - } -} - -export function createSolidMaterial(skin) { - return new THREE.MeshPhongMaterial(Object.assign({ - vertexColors: THREE.FaceColors, - color: 0xaeaeae, - shininess: 0, - polygonOffset : true, - polygonOffsetFactor : 1, - polygonOffsetUnits : 2, - side: DoubleSide, - }, skin)); -} - -const OFF_LINES_VECTOR = new Vector();//normal.multiply(0); // disable it. use polygon offset feature of material - -export class SceneFace { - constructor(solid, propagatedId) { - if (propagatedId === undefined) { - this.id = solid.tCadId + ":" + (solid.faceCounter++); - } else { - this.id = propagatedId; - } - this.solid = solid; - this.meshFaces = []; - this.sketch3DGroup = null; - } - - normal() { - throw 'not implemented'; - } - - depth() { - throw 'not implemented'; - } - - getBounds() { - throw 'not implemented'; - } - - surface() { - throw 'not implemented'; - } - - calcBasis() { - return BasisForPlane(this.normal()); - }; - - basis() { - if (!this._basis) { - this._basis = this.calcBasis(); - } - return this._basis; - } - - createMeshFace(a, b, c, normales) { - const face = new THREE.Face3(a, b, c, normales); - this.registerMeshFace(face); - return face; - } - - registerMeshFace(threeFace) { - this.meshFaces.push(threeFace); - setAttribute(threeFace, 'face', this); - } - - updateSketch(sketch) { - this.sketch = sketch; - if (this.sketch !== null) { - this.syncSketch(this.sketch); - } - } - - syncSketch(geom) { - if (this.sketch3DGroup !== null) { - for (let 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); - } - - let surface = this.surface(); - const _3dTransformation = surface.tangentPlaneInMiddle().get3DTransformation(); - const addSketchObjects = (sketchObjects, material, close) => { - for (let sketchObject of sketchObjects) { - let line = new THREE.Line(new THREE.Geometry(), material); - let sceneSketchObject = new SceneSketchObject(sketchObject, line); - setAttribute(line, 'sketchObject', sceneSketchObject); - const chunks = sketchObject.tessellate(10); - function addLine(p, q) { - const lg = line.geometry; - const a = _3dTransformation.apply(chunks[p]); - const b = _3dTransformation.apply(chunks[q]); - - lg.vertices.push(a._plus(OFF_LINES_VECTOR).three()); - lg.vertices.push(b._plus(OFF_LINES_VECTOR).three()); - } - for (let q = 1; q < chunks.length; q ++) { - addLine(q - 1, q); - } - this.sketch3DGroup.add(line); - } - }; - addSketchObjects(geom.constructionSegments, SKETCH_CONSTRUCTION_MATERIAL); - addSketchObjects(geom.connections, SKETCH_MATERIAL); - addSketchObjects(geom.loops, SKETCH_MATERIAL); - } - - findById(sketchObjectId) { - for (let o of this.sketch3DGroup.children) { - let sceneSketchObject = getAttribute(o, 'sketchObject'); - if (sceneSketchObject && sceneSketchObject.id === sketchObjectId) { - return sceneSketchObject; - } - } - } - - getSketchObjectVerticesIn3D(sketchObjectId) { - const object = this.findById(sketchObjectId); - if (!object) { - return undefined; - } - return object.geometry.vertices; - } -} - - -export class SceneEdge { - - constructor(curve, solid, representation, marker) { - this.id = solid.tCadId + ":" + (solid.edgeCounter++); - this.curve = curve; - this.solid = solid; - this.representation = representation; - this.marker = marker; - } -} - -export class SceneSketchObject { - - constructor(model, viewObject) { - this.id = model.id; - this.model = model; - this.viewObject = viewObject; - } -} - -export const SKETCH_MATERIAL = new THREE.LineBasicMaterial({color: 0xFFFFFF, linewidth: 3/DPR}); -export const SKETCH_CONSTRUCTION_MATERIAL = new THREE.LineBasicMaterial({color: 0x777777, linewidth: 2/DPR}); -export const WIREFRAME_MATERIAL = new THREE.LineBasicMaterial({color: 0x2B3856, linewidth: 3/DPR}); diff --git a/web/app/cad/stl/stlExporter.js b/web/app/cad/stl/stlExporter.js index d24d2f52..72b8e61f 100644 --- a/web/app/cad/stl/stlExporter.js +++ b/web/app/cad/stl/stlExporter.js @@ -1,76 +1,204 @@ +// copied from THREE.JS three/examples/js/exporters/STLExporter and slightly modified +import {Mesh} from 'three'; + + /** - * @author kovacsv / http://kovacsv.hu/ - * @author mrdoob / http://mrdoob.com/ + * Usage: + * const exporter = new STLExporter(); + * + * // second argument is a list of options + * const data = exporter.parse( mesh, { binary: true } ); + * */ -import {BufferGeometry, Geometry, Matrix3, Mesh, Vector3} from 'three'; + +export class STLExporter { + + parse(shellViews, options = {}) { + + const binary = options.binary !== undefined ? options.binary : false; // + + const objects = []; + let triangles = 0; -let vector = new Vector3(); -let normalMatrixWorld = new Matrix3(); + shellViews.forEach(function (view) { + if (!view.faceViews) { + return; + } -export default function (shellViews) { + view.faceViews.forEach(faceView => { + const object = faceView.mesh; + if (object instanceof Mesh) { + const geometry = object.geometry; - let output = ''; + if (geometry.isBufferGeometry !== true) { - shellViews.forEach(view => { + throw new Error('THREE.STLExporter: Geometry is not of type THREE.BufferGeometry.'); + + } + + const index = geometry.index; + const positionAttribute = geometry.getAttribute('position'); + triangles += index !== null ? index.count / 3 : positionAttribute.count / 3; + objects.push({ + object3d: object, + geometry: geometry, + modelId: view.model.id + }); + } + }); + }); + let output; + let offset = 80; // skip header + + if (binary === true) { + + const bufferLength = triangles * 2 + triangles * 3 * 4 * 4 + 80 + 4; + const arrayBuffer = new ArrayBuffer(bufferLength); + output = new DataView(arrayBuffer); + output.setUint32(offset, triangles, true); + offset += 4; + + } else { + + output = ''; + output += 'solid exported\n'; - if (!view.faceViews) { - return; } - output += `solid ${view.model.id}\n`; + const vA = new THREE.Vector3(); + const vB = new THREE.Vector3(); + const vC = new THREE.Vector3(); + const cb = new THREE.Vector3(); + const ab = new THREE.Vector3(); + const normal = new THREE.Vector3(); - view.faceViews.forEach(faceView => { - const mesh = faceView.mesh; - if (mesh instanceof Mesh) { + for (let i = 0, il = objects.length; i < il; i++) { - let geometry = mesh.geometry; - let matrixWorld = mesh.matrixWorld; + const object = objects[i].object3d; + const geometry = objects[i].geometry; + const index = geometry.index; + const positionAttribute = geometry.getAttribute('position'); - if (geometry instanceof BufferGeometry) { + if (index !== null) { - geometry = new Geometry().fromBufferGeometry(geometry); + // indexed geometry + for (let j = 0; j < index.count; j += 3) { + + const a = index.getX(j + 0); + const b = index.getX(j + 1); + const c = index.getX(j + 2); + writeFace(a, b, c, positionAttribute, object); } - if (geometry instanceof Geometry) { + } else { - let vertices = geometry.vertices; - let faces = geometry.faces; + // non-indexed geometry + for (let j = 0; j < positionAttribute.count; j += 3) { - normalMatrixWorld.getNormalMatrix(matrixWorld); - - for (let i = 0, l = faces.length; i < l; i++) { - - let face = faces[i]; - - vector.copy(face.normal).applyMatrix3(normalMatrixWorld).normalize(); - - output += '\tfacet normal ' + vector.x + ' ' + vector.y + ' ' + vector.z + '\n'; - output += '\t\touter loop\n'; - - let indices = [face.a, face.b, face.c]; - - for (let j = 0; j < 3; j++) { - - vector.copy(vertices[indices[j]]).applyMatrix4(matrixWorld); - - output += '\t\t\tvertex ' + vector.x + ' ' + vector.y + ' ' + vector.z + '\n'; - - } - - output += '\t\tendloop\n'; - output += '\tendfacet\n'; - - } + const a = j + 0; + const b = j + 1; + const c = j + 2; + writeFace(a, b, c, positionAttribute, object); } } - }) - output += `endsolid ${view.model.id}\n`; - }); + } + + if (binary === false) { + + output += 'endsolid exported\n'; + + } + + return output; + + function writeFace(a, b, c, positionAttribute, object) { + + vA.fromBufferAttribute(positionAttribute, a); + vB.fromBufferAttribute(positionAttribute, b); + vC.fromBufferAttribute(positionAttribute, c); + + if (object.isSkinnedMesh === true) { + + object.boneTransform(a, vA); + object.boneTransform(b, vB); + object.boneTransform(c, vC); + + } + + vA.applyMatrix4(object.matrixWorld); + vB.applyMatrix4(object.matrixWorld); + vC.applyMatrix4(object.matrixWorld); + writeNormal(vA, vB, vC); + writeVertex(vA); + writeVertex(vB); + writeVertex(vC); + + if (binary === true) { + + output.setUint16(offset, 0, true); + offset += 2; + + } else { + + output += '\t\tendloop\n'; + output += '\tendfacet\n'; + + } + + } + + function writeNormal(vA, vB, vC) { + + cb.subVectors(vC, vB); + ab.subVectors(vA, vB); + cb.cross(ab).normalize(); + normal.copy(cb).normalize(); + + if (binary === true) { + + output.setFloat32(offset, normal.x, true); + offset += 4; + output.setFloat32(offset, normal.y, true); + offset += 4; + output.setFloat32(offset, normal.z, true); + offset += 4; + + } else { + + output += '\tfacet normal ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n'; + output += '\t\touter loop\n'; + + } + + } + + function writeVertex(vertex) { + + if (binary === true) { + + output.setFloat32(offset, vertex.x, true); + offset += 4; + output.setFloat32(offset, vertex.y, true); + offset += 4; + output.setFloat32(offset, vertex.z, true); + offset += 4; + + } else { + + output += '\t\t\tvertex ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n'; + + } + + } + + } + +} + + - return output; -} \ No newline at end of file