diff --git a/__test__/Geometry/EdgeGeometry.test.ts b/__test__/Geometry/EdgeGeometry.test.ts new file mode 100644 index 000000000..40b07dd56 --- /dev/null +++ b/__test__/Geometry/EdgeGeometry.test.ts @@ -0,0 +1,21 @@ +import { LoadEntityFromFileData } from "../Utils/LoadEntity.util"; +import { Board } from "../../src/DatabaseServices/Board"; +import { RenderType } from "../../src/GraphicsSystem/RenderType"; +import { Line } from "three"; + +test('EdgeGeometry生成', () => +{ + let d = [1, "Board", 3, 2, 101, false, 1, 2, 0, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, -646.2420911908739, 230.97217145381728, -423.9845986472139, 1], 2, 484.63826594239083, 642.7439363026181, 18, false, "Polyline", 3, 2, 0, false, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 54.03714224287984, -40.88462318428341, 0, 1], 2, 6, [226.89796571085776, 338.3121692475904], 0, [248.97426264670497, 525.5228891266743], 0, [-54.03714224287984, 287.9783168893773], 0, [141.82369879876416, 40.88462318428341], 0, [588.7067940597382, 306.7620304373949], 0, [320.72013691164284, 378.99785818408446], 0, true, 1, 2, 109.48289947281157, 198.20905225226585, 18, false, "Polyline", 3, 2, 0, false, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -208.29382686305865, -201.59010496470867, 0, 1], 2, 4, [298.05328308824403, 206.16728215262995], 0, [208.29382686305865, 201.59010496470867], 0, [267.90926007565713, 311.07300443752024], 0, [406.5028791153245, 272.1962944735834], 0, true, 0, 3, 0, 0, 0, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, -646.2420911908739, 493.3031405597556, -263.2791168667886, 1], 3, 0, 0, 0, 2, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -314.3564875158372, 4019.556363898919, -46.81305616698228, 1], 1, "右侧板", "{\"roomName\":\"\",\"cabinetName\":\"\",\"boardName\":\"\",\"material\":\"\",\"color\":\"\",\"lines\":0,\"bigHoleDir\":0,\"drillType\":\"three\",\"composingFace\":2,\"highSealed\":[],\"sealedUp\":\"1\",\"sealedDown\":\"1\",\"sealedLeft\":\"1\",\"sealedRight\":\"1\",\"spliteHeight\":\"\",\"spliteWidth\":\"\",\"spliteThickness\":\"\"}", 0, 0]; + + let br: Board = LoadEntityFromFileData(d)[0] as Board; + + + let obj = br.GetDrawObjectFromRenderType(RenderType.Wireframe); + + let line = obj.children[0] as Line; + + let geo = line.geometry; + + //@ts-ignore + expect(geo.attributes.position.length).toBe(240); +}); diff --git a/src/DatabaseServices/3DSolid/GangDrill.ts b/src/DatabaseServices/3DSolid/GangDrill.ts index 0c5f93210..d69d010a4 100644 --- a/src/DatabaseServices/3DSolid/GangDrill.ts +++ b/src/DatabaseServices/3DSolid/GangDrill.ts @@ -7,7 +7,7 @@ import { Circle } from '../Circle'; import { Contour } from '../Contour'; import { Shape } from '../Shape'; import { Solid3D } from "./Solid3D"; -import { EdgesGeometry2 } from '../../Geometry/EdgeGeometry'; +import { EdgesGeometry } from '../../Geometry/EdgeGeometry'; import { ColorMaterial } from '../../Common/ColorPalette'; import { DisposeThreeObj } from '../../Common/Dispose'; @@ -87,7 +87,7 @@ export class GangDrill extends Solid3D { if (renderType === RenderType.Wireframe) { - return new LineSegments(new EdgesGeometry2(this.CreateGeometry(), 2), ColorMaterial.GetLineMaterial(this.ColorIndex)); + return new LineSegments(new EdgesGeometry(this.CreateGeometry(), 2), ColorMaterial.GetLineMaterial(this.ColorIndex)); } else return new THREE.Mesh(this.CreateGeometry(), new MeshNormalMaterial()); diff --git a/src/DatabaseServices/3DSolid/SweepSolid.ts b/src/DatabaseServices/3DSolid/SweepSolid.ts index 387156c54..740e677c1 100644 --- a/src/DatabaseServices/3DSolid/SweepSolid.ts +++ b/src/DatabaseServices/3DSolid/SweepSolid.ts @@ -10,7 +10,7 @@ import { PhysicalMaterialRecord } from '../PhysicalMaterialRecord'; import { IsPointInPolyLine } from '../PointInPolyline'; import { Polyline } from '../Polyline'; import { Solid3D } from "./Solid3D"; -import { EdgesGeometry2 } from '../../Geometry/EdgeGeometry'; +import { EdgesGeometry } from '../../Geometry/EdgeGeometry'; import { ColorMaterial } from '../../Common/ColorPalette'; import { DisposeThreeObj } from '../../Common/Dispose'; @@ -111,7 +111,7 @@ export class SweepSolid extends Solid3D { if (renderType === RenderType.Wireframe) { - let geo = new EdgesGeometry2(this.CreateGeomtry(), 2); + let geo = new EdgesGeometry(this.CreateGeomtry()); return new LineSegments(geo, ColorMaterial.GetLineMaterial(this.ColorIndex)); } else diff --git a/src/DatabaseServices/Extrude.ts b/src/DatabaseServices/Extrude.ts index 754513ef7..50ef2afe8 100644 --- a/src/DatabaseServices/Extrude.ts +++ b/src/DatabaseServices/Extrude.ts @@ -8,7 +8,7 @@ import { Status, UpdateDraw } from "../Common/Status"; import { ObjectSnapMode } from "../Editor/ObjectSnapMode"; import { boardUVGenerator } from "../Geometry/BoardUVGenerator"; import { BSPGroupParse } from "../Geometry/BSPGroupParse"; -import { EdgesGeometry2 } from "../Geometry/EdgeGeometry"; +import { EdgesGeometry } from "../Geometry/EdgeGeometry"; import { cZeroVec, equaln, equalv2, equalv3, isIntersect, isParallelTo, MoveMatrix } from "../Geometry/GeUtils"; import { CSG } from "../Geometry/ThreeCSG"; import { ScaleUV } from "../Geometry/UVUtils"; @@ -999,13 +999,13 @@ export class ExtureSolid extends Entity this._MeshGeometry = this.GeneralGeometry(); return this._MeshGeometry; } - private _EdgeGeometry: EdgesGeometry2; + private _EdgeGeometry: EdgesGeometry; private get EdgeGeometry() { if (this._EdgeGeometry) return this._EdgeGeometry; - this._EdgeGeometry = new EdgesGeometry2(this.MeshGeometry, 2); + this._EdgeGeometry = new EdgesGeometry(this.MeshGeometry); return this._EdgeGeometry; } diff --git a/src/Geometry/EdgeGeometry.ts b/src/Geometry/EdgeGeometry.ts index cef2d9e9f..31fce516c 100644 --- a/src/Geometry/EdgeGeometry.ts +++ b/src/Geometry/EdgeGeometry.ts @@ -1,155 +1,21 @@ -import { BufferGeometry, Vector3, Geometry, Line3, Face3, Float32BufferAttribute } from "three"; +import { BufferGeometry, Face3, Float32BufferAttribute, Geometry, Line3, Triangle, Vector3 } from "three"; +import { arraySortByNumber } from "../Common/ArrayExt"; //ref: https://github.com/mrdoob/js/issues/10517 -export class OutlinesGeometry extends BufferGeometry -{ - constructor(geometry, thresholdAngle: number) - { - super(); - - thresholdAngle = (thresholdAngle !== undefined) ? thresholdAngle : 1; - - let thresholdDot = Math.cos(Math.PI / 180 * thresholdAngle); - - let edge = [0, 0], hash = {}, i, j, l, face, key; - - function sortFunction(a, b) - { - return a - b; - } - - let keys = ['a', 'b', 'c']; - - let geometry2; - - if (geometry.isBufferGeometry) - { - geometry2 = new Geometry(); - geometry2.fromBufferGeometry(geometry); - } - else - { - geometry2 = geometry.clone(); - } - - geometry2.mergeVertices(); - geometry2.computeFaceNormals(); - - let vertices = geometry2.vertices; - let faces = geometry2.faces; +const keys = ['a', 'b', 'c']; - for (i = 0; i < faces.length; i++) - { - face = faces[i]; - for (j = 0; j < 3; j++) - { - edge[0] = face[keys[j]]; - edge[1] = face[keys[(j + 1) % 3]]; - - let line = new Line3(vertices[edge[0]], vertices[edge[1]]); - - // for each vertex checks if it lies in the edge - for (let e = vertices.length - 1; e >= 0; e--) - { - if (e === edge[0] || e === edge[1]) continue; - let v = vertices[e]; - let closestPoint = line.closestPointToPoint(v, true, new Vector3()); - if ((new Line3(closestPoint, v)).distance() < 1e-5) - { //1e-5 - // mark the current face as splitted so that his cords won't be considered - face.splitted = true; - // Add two new faces, created splitting the face in two - faces.push(new Face3( - e, face[keys[(j + 2) % 3]], face[keys[(j) % 3]], - face.normal, face.color, face.materialIndex - )); - faces.push(new Face3( - e, face[keys[(j + 2) % 3]], face[keys[(j + 1) % 3]], - face.normal, face.color, face.materialIndex - )); - break; - } - } - if (face.splitted) break; - } - } - - for (i = faces.length - 1; i >= 0; i--) - { - face = faces[i]; - if (face.splitted) continue; - for (j = 0; j < 3; j++) - { - edge[0] = face[keys[j]]; - edge[1] = face[keys[(j + 1) % 3]]; - edge.sort(sortFunction); - - key = edge.toString(); - - if (hash[key] === undefined) - { - hash[key] = { vert1: edge[0], vert2: edge[1], face1: i, face2: undefined }; - } - else - { - hash[key].face2 = i; - } - } - } - - let coords = []; - - for (key in hash) - { - let h = hash[key]; - // An edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. - if (h.face2 !== undefined && faces[h.face1].normal.dot(faces[h.face2].normal) <= thresholdDot) - { - let vertex = vertices[h.vert1]; - coords.push(vertex.x); - coords.push(vertex.y); - coords.push(vertex.z); - - vertex = vertices[h.vert2]; - coords.push(vertex.x); - coords.push(vertex.y); - coords.push(vertex.z); - } - } - this.addAttribute('position', new Float32BufferAttribute(coords, 3)); - } -} - -export class EdgesGeometry2 extends BufferGeometry +export class EdgesGeometry extends BufferGeometry { - constructor(geometry, thresholdAngle: number) + constructor(geometry, thresholdAngle: number = 1) { super(); - thresholdAngle = (thresholdAngle !== undefined) ? thresholdAngle : 1; - - let thresholdDot = Math.cos(Math.PI / 180 * thresholdAngle); - - let edge = [0, 0], hash = {}; - - function sortFunction(a, b) - { - return a - b; - } - - let keys = ['a', 'b', 'c']; - - let geometry2; + let geometry2: Geometry; if (geometry.isBufferGeometry) - { - geometry2 = new Geometry(); - geometry2.fromBufferGeometry(geometry); - } + geometry2 = new Geometry().fromBufferGeometry(geometry); else - { geometry2 = geometry.clone(); - } geometry2.mergeVertices(); geometry2.computeFaceNormals(); @@ -157,75 +23,88 @@ export class EdgesGeometry2 extends BufferGeometry let vertices = geometry2.vertices; let faces = geometry2.faces; + let faceHash = new Set(); for (let i = 0; i < faces.length; i++) { + if (i > 1000)//出错 + break; let face = faces[i]; + //fix CSG produces duplicate faces + let faceStr = `${face.a},${face.b},${face.c}`; + if (faceHash.has(faceStr)) + continue; + faceHash.add(faceStr); + for (let j = 0; j < 3; j++) { + let e1 = face[keys[j]]; + let e2 = face[keys[(j + 1) % 3]]; + let e3 = face[keys[(j + 2) % 3]]; - edge[0] = face[keys[j]]; - edge[1] = face[keys[(j + 1) % 3]]; - - let line = new Line3(vertices[edge[0]], vertices[edge[1]]); + let line = new Line3(vertices[e1], vertices[e2]); + //split triangle for (let e = 0, l = vertices.length; e < l; e++) { - if (e === edge[0] || e === edge[1]) continue; - let v = vertices[e]; - let closestPoint = line.closestPointToPoint(v, true, new Vector3()); - if (closestPoint.equals(vertices[edge[0]]) || closestPoint.equals(vertices[edge[0]])) continue; - if ((new Line3(closestPoint, v)).distance() < 1e-5) - { //1e-5 - // mark the current face as splitted so that his cords won't be considered - face.splitted = true; - // split the face in two using the new point - faces.push(new Face3( - e, face[keys[(j + 2) % 3]], face[keys[(j) % 3]], - face.normal, face.color, face.materialIndex - )); - faces.push(new Face3( - e, face[keys[(j + 2) % 3]], face[keys[(j + 1) % 3]], - face.normal, face.color, face.materialIndex - )); + if (e === e1 || e === e2 || e === e3) + continue; + + let p = vertices[e]; + let closestPoint = line.closestPointToPoint(p, true, new Vector3()); + if (closestPoint.equals(vertices[e1]) || closestPoint.equals(vertices[e2])) + continue; + if (closestPoint.distanceTo(p) < 1e-5) + { + face["splitted"] = true; + let f1 = new Face3(e, e3, e1, face.normal); + let f2 = new Face3(e, e3, e2, face.normal); + + if (FaceArea(f1, vertices) > 1 + && FaceArea(f2, vertices) > 1) + faces.push(f1, f2); break; } } - if (face.splitted) break; + if (face["splitted"]) + break; } } + let edge = [0, 0]; + let hash: { [key: string]: { vert1: number, vert2: number, face1: Face3, face2?: Face3 } } = {}; + for (let i = 0, l = faces.length; i < l; i++) { let face = faces[i]; - if (face.splitted) continue; + if (face["splitted"]) continue; for (let j = 0; j < 3; j++) { edge[0] = face[keys[j]]; edge[1] = face[keys[(j + 1) % 3]]; - edge.sort(sortFunction); + arraySortByNumber(edge); let key = edge.toString(); if (hash[key] === undefined) - { - hash[key] = { vert1: edge[0], vert2: edge[1], face1: i, face2: undefined }; - } + hash[key] = { vert1: edge[0], vert2: edge[1], face1: face }; else - { - hash[key].face2 = i; - } + hash[key].face2 = face; } } - let coords = []; + + let coords: number[] = []; + let thresholdDot = Math.cos(Math.PI / 180 * thresholdAngle); + for (let key in hash) { let h = hash[key]; + // An edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. - if (h.face2 !== undefined && faces[h.face1].normal.dot(faces[h.face2].normal) <= thresholdDot) + if (h.face2 && h.face1.normal.dot(h.face2.normal) <= thresholdDot) { let vertex = vertices[h.vert1]; coords.push(vertex.x); @@ -241,3 +120,13 @@ export class EdgesGeometry2 extends BufferGeometry this.addAttribute('position', new Float32BufferAttribute(coords, 3)); } } + +let triangle = new Triangle(); +function FaceArea(f: Face3, pts: Vector3[]) +{ + triangle.a = pts[f.a]; + triangle.b = pts[f.b]; + triangle.c = pts[f.c]; + + return triangle.getArea() +}