From 0729c1b6500109541371e0724625f08e79907901 Mon Sep 17 00:00:00 2001 From: ChenX Date: Fri, 29 May 2020 11:23:18 +0800 Subject: [PATCH] =?UTF-8?q?!1067=20=E4=BC=98=E5=8C=96:=E6=8B=89=E4=BC=B8?= =?UTF-8?q?=E5=AE=9E=E4=BD=93=E7=9A=84=E6=98=BE=E7=A4=BA=E4=BC=98=E5=8C=96?= =?UTF-8?q?(=E7=BA=BF=E6=A1=86=E5=92=8C=E5=AE=9E=E4=BD=93=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __test__/Geometry/EdgeGeometry.test.ts | 19 +- .../__snapshots__/EdgeGeometry.test.ts.snap | 30 +- config/webpack.dev.ts | 4 +- src/Add-on/Erase.ts | 17 +- src/Add-on/test/TestTape.ts | 58 +- src/DatabaseServices/Entity/Curve.ts | 2 +- src/DatabaseServices/Entity/Polyline.ts | 2 +- src/Geometry/ExportObj2.ts | 5 +- src/Geometry/ExtrudeEdgeGeometry2.ts | 512 +++++++++--------- src/index.html | 28 +- 10 files changed, 337 insertions(+), 340 deletions(-) diff --git a/__test__/Geometry/EdgeGeometry.test.ts b/__test__/Geometry/EdgeGeometry.test.ts index eb0a90ae7..720ee3729 100644 --- a/__test__/Geometry/EdgeGeometry.test.ts +++ b/__test__/Geometry/EdgeGeometry.test.ts @@ -39,7 +39,7 @@ test('Geometry构建测试', () => { let builder = new ExtrudeGeometryBuilder(br); expect(builder.verticesArray.length).toMatchSnapshot(); - expect(builder.edgeAndLidBuilder.verticesArray.length).toMatchSnapshot(); + expect(builder.edgeAndLidBuilder.lineVerticesArray.length).toMatchSnapshot(); } }); @@ -53,6 +53,21 @@ test('极限共线', () => { let builder = new ExtrudeGeometryBuilder(br); expect(builder.verticesArray.length).toMatchSnapshot(); - expect(builder.edgeAndLidBuilder.verticesArray.length).toMatchSnapshot(); + expect(builder.edgeAndLidBuilder.lineVerticesArray.length).toMatchSnapshot(); + } +}); + +test('盖子构建失败', () => +{ + //原因是因为交点太多 + let d = + { "file": [1, "Board", 8, 2, 110, false, 1, 11, 0, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 4300.009279073407, 2076.9410852395504, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4300.009279073407, 2076.9410852395504, -500.99609312660925, 1], 0, 3, 555.6884165302121, 564, 18, true, "Polyline", 8, 2, 0, false, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, -500.99609312660925, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 2, 4, [0, 500.99609312660925], 0, [564, 500.9960931266093], 0, [564, 1056.6845096568213], 0, [0, 1056.6845096568213], 0, true, 1, 3, 2.650032406101512, 711.383779232115, 2.7331290961030845, true, "Polyline", 8, 2, 0, false, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 2, 4, [0, 0], 0, [711.383779232115, 0], 0, [711.383779232115, 2.650032406101512], 0, [0, 2.650032406101512], 0, true, 0, 3, 0, 0, 0, 0, 0, [0, 0.7928210010759529, 0, 0, 0, 0, 0.7928210010759529, 0, 1, 0, 0, 0, 4315.276149977304, 2076.9410852395504, 6.233907470232225e-7, 1], 3, 0, 0, 0, 0, 0, 9, 1, "立板", "1", "1", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true], "basePt": { "x": 4300.009279073407, "y": 2076.9410852395504, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }; + + let brs = LoadBoardsFromFileData(d); + for (let br of brs) + { + let builder = new ExtrudeGeometryBuilder(br); + expect(builder.verticesArray.length).toMatchSnapshot(); + expect(builder.edgeAndLidBuilder.lineVerticesArray.length).toMatchSnapshot(); } }); diff --git a/__test__/Geometry/__snapshots__/EdgeGeometry.test.ts.snap b/__test__/Geometry/__snapshots__/EdgeGeometry.test.ts.snap index 5b1215d98..211f750b8 100644 --- a/__test__/Geometry/__snapshots__/EdgeGeometry.test.ts.snap +++ b/__test__/Geometry/__snapshots__/EdgeGeometry.test.ts.snap @@ -2,52 +2,56 @@ exports[`EdgeGeometry生成 1`] = `114`; -exports[`EdgeGeometry生成 2`] = `360`; +exports[`EdgeGeometry生成 2`] = `206`; exports[`EdgeGeometry生成 3`] = `60`; -exports[`EdgeGeometry生成2 1`] = `80`; +exports[`EdgeGeometry生成2 1`] = `66`; exports[`Geometry构建测试 1`] = `540`; -exports[`Geometry构建测试 2`] = `408`; +exports[`Geometry构建测试 2`] = `372`; exports[`Geometry构建测试 3`] = `378`; -exports[`Geometry构建测试 4`] = `288`; +exports[`Geometry构建测试 4`] = `234`; exports[`Geometry构建测试 5`] = `1188`; -exports[`Geometry构建测试 6`] = `888`; +exports[`Geometry构建测试 6`] = `744`; exports[`Geometry构建测试 7`] = `774`; -exports[`Geometry构建测试 8`] = `552`; +exports[`Geometry构建测试 8`] = `450`; exports[`Geometry构建测试 9`] = `630`; -exports[`Geometry构建测试 10`] = `456`; +exports[`Geometry构建测试 10`] = `300`; exports[`Geometry构建测试 11`] = `1548`; -exports[`Geometry构建测试 12`] = `1056`; +exports[`Geometry构建测试 12`] = `570`; exports[`Geometry构建测试 13`] = `639`; -exports[`Geometry构建测试 14`] = `480`; +exports[`Geometry构建测试 14`] = `378`; exports[`Geometry构建测试 15`] = `1998`; -exports[`Geometry构建测试 16`] = `1368`; +exports[`Geometry构建测试 16`] = `786`; exports[`Geometry构建测试 17`] = `1908`; -exports[`Geometry构建测试 18`] = `1344`; +exports[`Geometry构建测试 18`] = `744`; exports[`Geometry构建测试 19`] = `2232`; -exports[`Geometry构建测试 20`] = `1584`; +exports[`Geometry构建测试 20`] = `924`; exports[`极限共线 1`] = `432`; -exports[`极限共线 2`] = `360`; +exports[`极限共线 2`] = `294`; + +exports[`盖子构建失败 1`] = `180`; + +exports[`盖子构建失败 2`] = `138`; diff --git a/config/webpack.dev.ts b/config/webpack.dev.ts index d6cd0250f..17d7fe547 100644 --- a/config/webpack.dev.ts +++ b/config/webpack.dev.ts @@ -10,9 +10,9 @@ const config: webpack.Configuration = merge( output: { pathinfo: false }, devtool: "eval-source-map", //https://www.webpackjs.com/configuration/stats/ - stats: { + stats: "errors-only" || { assets: false, - timings: true, + timings: false, builtAt: false, cachedAssets: false, diff --git a/src/Add-on/Erase.ts b/src/Add-on/Erase.ts index 06e858928..fb3b0c5a2 100644 --- a/src/Add-on/Erase.ts +++ b/src/Add-on/Erase.ts @@ -1,4 +1,5 @@ import { app } from '../ApplicationServices/Application'; +import { IsDev } from '../Common/Deving'; import { GetEntity } from '../Common/Utils'; import { Command } from '../Editor/CommandMachine'; import { PromptStatus } from '../Editor/PromptResult'; @@ -7,8 +8,22 @@ export class Command_Erase implements Command { async exec() { + if (IsDev() && app.Editor.SelectCtrl.SelectSet.SelectObjectCount) + { + for (let obj of app.Editor.SelectCtrl.SelectSet.SelectObjectList) + { + let ent = GetEntity(obj); + if (ent) + ent.Erase(); + else + obj.visible = false; + } + app.Editor.SelectCtrl.Cancel(); + return; + } + let ssRes = await app.Editor.GetSelection({ UseSelect: true }); - if (ssRes.Status != PromptStatus.OK) return; + if (ssRes.Status !== PromptStatus.OK) return; for (let obj of ssRes.SelectSet.SelectObjectList) { diff --git a/src/Add-on/test/TestTape.ts b/src/Add-on/test/TestTape.ts index 5eb13ff39..3670753b7 100644 --- a/src/Add-on/test/TestTape.ts +++ b/src/Add-on/test/TestTape.ts @@ -17,35 +17,35 @@ export class Command_TestTape let br = enRes.Entity as Board; let geo = new ExtrudeGeometryBuilder(br); return; - let k = 0; - let i = 1; - let testOut = true; - for (let s of geo.shapes) - { - if (testOut) - { - let m = MoveMatrix(new Vector3(0, k * 20)); - for (let t of s.contour.Tape) - { - t.Curve.ApplyMatrix(m); - TestDraw(t.Curve, s.depth); - } - k++; - } - else - { - for (let w of s.holes) - { - let m = MoveMatrix(new Vector3(0, k * 20)); - for (let t of w.Tape) - { - t.Curve.ApplyMatrix(m); - TestDraw(t.Curve, w.depth); - } - k++; - } - } - } + // let k = 0; + // let i = 1; + // let testOut = true; + // for (let s of geo) + // { + // if (testOut) + // { + // let m = MoveMatrix(new Vector3(0, k * 20)); + // for (let t of s.contourWall.Tape) + // { + // t.Curve.ApplyMatrix(m); + // TestDraw(t.Curve, s.depth); + // } + // k++; + // } + // else + // { + // for (let w of s.holeWalls) + // { + // let m = MoveMatrix(new Vector3(0, k * 20)); + // for (let t of w.Tape) + // { + // t.Curve.ApplyMatrix(m); + // TestDraw(t.Curve, w.depth); + // } + // k++; + // } + // } + // } // let gs = br.Grooves; // let brOcsInv = br.OCSInv; // let alm = new Matrix4; diff --git a/src/DatabaseServices/Entity/Curve.ts b/src/DatabaseServices/Entity/Curve.ts index 28391455f..7f2545add 100644 --- a/src/DatabaseServices/Entity/Curve.ts +++ b/src/DatabaseServices/Entity/Curve.ts @@ -73,7 +73,7 @@ export abstract class Curve extends Entity get Area2(): number { return 0; } get Length(): number { return 0; } get IsClose(): boolean { return false; } - //曲线为顺时针 + /** 曲线为顺时针 */ get IsClockWise(): boolean { return this.Area2 < 0; } abstract get Shape(): Shape; diff --git a/src/DatabaseServices/Entity/Polyline.ts b/src/DatabaseServices/Entity/Polyline.ts index b689f3e05..76d3138f6 100644 --- a/src/DatabaseServices/Entity/Polyline.ts +++ b/src/DatabaseServices/Entity/Polyline.ts @@ -476,7 +476,7 @@ export class Polyline extends Curve } //参数已经大于索引,证明参数在线外. - if (paramFloor != cuCout) + if (paramFloor !== cuCout) { dist += this.GetCurveAtParam(param).GetDistAtParam(param - cuCout); } diff --git a/src/Geometry/ExportObj2.ts b/src/Geometry/ExportObj2.ts index e100b848a..39cf32164 100644 --- a/src/Geometry/ExportObj2.ts +++ b/src/Geometry/ExportObj2.ts @@ -23,10 +23,7 @@ export async function ExportObj(brs: Entity[]) for (let b of brs) { let o: Object3D; - if (userConfig.RenderType === RenderType.Physical) - o = b.Clone().ApplyMatrix(mtx).GetDrawObjectFromRenderType(RenderType.Physical); - else - o = b.Clone().ApplyMatrix(mtx).GetDrawObjectFromRenderType(RenderType.Conceptual); + o = b.Clone().ApplyMatrix(mtx).GetDrawObjectFromRenderType(RenderType.Physical); g.add(o); } diff --git a/src/Geometry/ExtrudeEdgeGeometry2.ts b/src/Geometry/ExtrudeEdgeGeometry2.ts index af03295f9..dc4ff74ea 100644 --- a/src/Geometry/ExtrudeEdgeGeometry2.ts +++ b/src/Geometry/ExtrudeEdgeGeometry2.ts @@ -1,4 +1,4 @@ -import { Box3, BufferGeometry, Float32BufferAttribute, Matrix4, Shape as TShape, ShapeUtils, Vector3, MathUtils } from "three"; +import { BufferGeometry, Float32BufferAttribute, MathUtils, Matrix4, Shape as TShape, ShapeUtils, Vector3 } from "three"; import { arrayRemoveDuplicateBySort, arraySortByNumber } from "../Common/ArrayExt"; import { curveLinkGroup } from "../Common/CurveUtils"; import { clamp, FixIndex } from "../Common/Utils"; @@ -9,9 +9,10 @@ import { Circle } from "../DatabaseServices/Entity/Circle"; import { Curve } from "../DatabaseServices/Entity/Curve"; import { ExtrudeSolid, ExtureContourCurve } from "../DatabaseServices/Entity/Extrude"; import { Line } from "../DatabaseServices/Entity/Line"; -import { Polyline } from "../DatabaseServices/Entity/Polyline"; +import { Polyline, PolylineProps } from "../DatabaseServices/Entity/Polyline"; import { IntersectOption, IntersectResult } from "../GraphicsSystem/IntersectWith"; import { LinesType } from "../UI/Store/BoardInterface"; +import { IntersectsBox } from "./Box"; import { AsVector2, equaln, equalv3, IdentityMtx4 } from "./GeUtils"; import { RegionParse } from "./RegionParse"; @@ -22,138 +23,90 @@ export enum DepthType All = 3, } -class Shape2 -{ - constructor(public contour: Contour, public holes: Contour[]) - { - - } - - Parse(contour: Contour, s: this, isOut: boolean) - { - let [res1,] = Parse(this.contour.Curve, s.contour.Curve); - if (!isOut) - [res1.reverse, res1.syntropy] = [res1.syntropy, res1.reverse]; - if (res1.container.length > 0) - { - for (let h of s.holes) - { - let [res2,] = Parse(this.contour.Curve, h.Curve); - if (isOut) - [res2.reverse, res2.syntropy] = [res2.syntropy, res2.reverse]; - - res1.container = SubtractRanges(res1.container, res2.container, contour.Curve.EndParam); - res1.container = SubtractRanges(res1.container, res2.syntropy, contour.Curve.EndParam); - res1.container = SubtractRanges(res1.container, res2.reverse, contour.Curve.EndParam); - - //被网洞包含等于在形状外部 - res1.outer.push(...res2.container); - - res1.syntropy.push(...res2.syntropy); - res1.reverse.push(...res2.reverse); - } - } - return res1; - } - - Sub(s: Shape2) - { - - } -} - - /** - * 由ExtudeWall围成的形状,包含外轮廓和网洞 + * 槽的几何数据,包括槽的墙面和槽的盖子 */ -export class ExtudeWallShape +export class Groove { - contour: ExtudeWall; - holes: ExtudeWall[] = []; - - box: Box3; - shape: CurveTapeShape; + contourWall: ExtudeWall;//槽轮廓的墙 + holeWalls: ExtudeWall[] = [];//槽的网洞的墙 + private lid: CurveTapeShape;//槽的盖子 constructor(contour: Contour, holes: Contour[], public depthType: DepthType, public depth: number, - public allDepth: number) + public allDepth: number, + private box = contour.BoundingBox + ) { - this.contour = new ExtudeWall(contour.Curve, depthType, depth, allDepth, DirectionType.Inner); - this.box = contour.BoundingBox; - + this.contourWall = new ExtudeWall(contour.Curve, depthType, depth, allDepth, DirectionType.Inner); for (let h of holes) - this.holes.push(new ExtudeWall(h.Curve, depthType, depth, allDepth, DirectionType.Outer)); + this.holeWalls.push(new ExtudeWall(h.Curve, depthType, depth, allDepth, DirectionType.Outer)); - this.shape = new CurveTapeShape(contour, holes); + this.lid = new CurveTapeShape(contour, holes); } /** - * - * @param shape + * @param groove this - groove * @param [eachOther=true] 相互裁剪 */ - ClipWall(shape: ExtudeWallShape, eachOther = true) + ClipTo(groove: Groove, eachOther = true) { //相同深度和面不用操作 - if (shape.depthType === this.depthType && shape.depth === this.depth) return; + if (groove.depthType === this.depthType && groove.depth === this.depth) return; - if (!this.box.intersectsBox(shape.box)) return; + if (!IntersectsBox(this.box, groove.box)) return; - this.ClipShape(shape); - shape.ClipShape(this); + this.ClipLid(groove); + groove.ClipLid(this); //一正一反,不交集 - if (this.depthType + shape.depthType === 3 && this.depth + shape.depth < this.allDepth) + if (this.depthType + groove.depthType === 3 && this.depth + groove.depth < this.allDepth) return; - this.contour.Clip(shape, true); - for (let c of this.holes) - c.Clip(shape, true); + this.contourWall.ClipTo(groove, true); + for (let wall of this.holeWalls) + wall.ClipTo(groove, true); if (eachOther) { - shape.contour.Clip(this, false); - for (let c of shape.holes) - c.Clip(this, false); + groove.contourWall.ClipTo(this, false); + for (let wall of groove.holeWalls) + wall.ClipTo(this, false); } } - ClipShape(shape: ExtudeWallShape) + private ClipLid(groove: Groove) { if (this.depthType === DepthType.All) return; - if (shape.depthType === DepthType.All) return; + if (groove.depthType === DepthType.All) return; - if (this.depthType === shape.depthType) + if (this.depthType === groove.depthType) { - if (shape.depth > this.depth) - this.shape.ClipTo(shape.shape, true); + if (groove.depth > this.depth) + this.lid.ClipTo(groove.lid, true); else - { - this.shape.SplitTo(shape.shape); - } + this.lid.SplitTo(groove.lid); } else { - if (this.depth + shape.depth >= this.allDepth) - this.shape.ClipTo(shape.shape, true); + if (this.depth + groove.depth >= this.allDepth) + this.lid.ClipTo(groove.lid, true); else - { - this.shape.SplitTo(shape.shape); - } + this.lid.SplitTo(groove.lid); } } Draw(verticesArray: number[], uvArray: number[], edgeBuild: EdgeGeometryBuild, rotateUv: boolean) { - this.contour.Draw(verticesArray, uvArray, edgeBuild); - for (let wall of this.holes) + this.contourWall.Draw(verticesArray, uvArray, edgeBuild); + for (let wall of this.holeWalls) wall.Draw(verticesArray, uvArray, edgeBuild); if (this.depthType === DepthType.All) return; let isFront = this.depthType === DepthType.Front; - this.shape.Draw(verticesArray, uvArray, isFront, isFront ? this.allDepth - this.depth : this.depth, rotateUv); + this.lid.Draw(verticesArray, uvArray, isFront, isFront ? this.allDepth - this.depth : this.depth, rotateUv); } } @@ -189,18 +142,16 @@ enum DirectionType Inner = 1 //内墙 } -//轮廓树,用于重新确认外墙和网洞的关系 -class ContourTree +//轮廓树节点,用于重新确认外墙和网洞的关系 +export class ContourTreeNode { - parent: ContourTree; - constructor(public contour: Contour, public children: ContourTree[] = []) - { - } + parent: ContourTreeNode;//当存在Parent时,表示它是一个洞 + constructor(public contour: Contour, public children: ContourTreeNode[] = []) { } - SetParent(s: ContourTree) + SetParent(node: ContourTreeNode) { - this.parent = s; - s.children.push(this); + this.parent = node; + node.children.push(this); } Draw(verticesArray: number[], uvArray: number[], front: boolean, z: number, rotateUv: boolean)//, depth = 1 @@ -253,20 +204,20 @@ class ContourTree } } - static ParseContourTree(contourTrees: ContourTree[]) + static ParseContourTree(contourNodes: ContourTreeNode[]): void { - contourTrees.sort((c1, c2) => c1.contour.Curve.Area - c2.contour.Curve.Area); - for (let i = 0; i < contourTrees.length; i++) + contourNodes.sort((c1, c2) => c1.contour.Curve.Area - c2.contour.Curve.Area); + for (let i = 0; i < contourNodes.length; i++) { - const s = contourTrees[i]; - let p = s.contour.Curve.StartPoint; - for (let j = i + 1; j < contourTrees.length; j++) + const node1 = contourNodes[i]; + let p = node1.contour.Curve.StartPoint; + for (let j = i + 1; j < contourNodes.length; j++) { - const s2 = contourTrees[j]; - if (s2.contour.BoundingBox.intersectsBox(s.contour.BoundingBox) - && s2.contour.Curve.PtInCurve(p)) + const node2 = contourNodes[j]; + if (node2.contour.BoundingBox.intersectsBox(node1.contour.BoundingBox) + && node2.contour.Curve.PtInCurve(p)) { - s.SetParent(s2); + node1.SetParent(node2); break; } } @@ -276,7 +227,7 @@ class ContourTree class EdgeGeometryBuild { - verticesArray: number[] = []; + lineVerticesArray: number[] = []; frontLines: Line[] = []; backLines: Line[] = []; @@ -299,19 +250,6 @@ class EdgeGeometryBuild } } - /** - * @param pts3d 给了首尾相等的点表 - */ - AddEdgePolygon(pts3d: Vector3[]) - { - for (let i = 0; i < pts3d.length - 1; i++) - { - let p = pts3d[i]; - let np = pts3d[i + 1]; - this.verticesArray.push(p.x, p.y, p.z, np.x, np.y, np.z); - } - } - BuildLid(verticesArray: number[], uvArray: number[], rotateUv: boolean) { let arr = [this.backLines, this.frontLines]; @@ -320,7 +258,7 @@ class EdgeGeometryBuild { let lines = arr[index]; let parse = new RegionParse(lines); - let contours: ContourTree[] = []; + let contourNodes: ContourTreeNode[] = []; for (let routes of parse.RegionsOutline) { let cs: Curve[] = []; @@ -328,24 +266,27 @@ class EdgeGeometryBuild cs.push(r.curve); let c = Contour.CreateContour(cs, false); if (c) - contours.push(new ContourTree(c)); + contourNodes.push(new ContourTreeNode(c)); else console.error("未能构建盖子"); } - ContourTree.ParseContourTree(contours); + ContourTreeNode.ParseContourTree(contourNodes); - for (let j = contours.length; j--;) + for (let j = contourNodes.length; j--;) { - let s = contours[j]; - if (s.parent) continue; + let node = contourNodes[j]; + if (node.parent) continue; - s.Draw(verticesArray, uvArray, index === 1, this.allDepth * index, rotateUv); + node.Draw(verticesArray, uvArray, index === 1, this.allDepth * index, rotateUv); } } } } +/** + * 胶带 + */ class Tape { constructor( @@ -405,7 +346,10 @@ class Tape } } -class CurveTapeShape +/** + * 二维形状,内部用曲线胶带表示(用来计算盖子差集算法) + */ +export class CurveTapeShape { children: CurveTapeShape[] = []; contour: CurveTape; @@ -439,7 +383,7 @@ class CurveTapeShape } } - //合理打断 + //合理打断(以保证三维网格对齐(否则圆弧点将无法正确的对齐)) SplitTo(s: CurveTapeShape) { for (let c of [this.contour, ...this.holes]) @@ -452,7 +396,10 @@ class CurveTapeShape } } - ReverseClipTo(s: CurveTapeShape): this + /** + * 只保留被包含部分 + */ + private ReverseClipTo(s: CurveTapeShape): this { for (let c of [this.contour, ... this.holes]) if (c.tapes.length > 0) @@ -494,25 +441,25 @@ class CurveTapeShape // TestDraw(polylines, z); let groups = curveLinkGroup(polylines); - let contourTrees: ContourTree[] = []; + let contourNodes: ContourTreeNode[] = []; for (let cus of groups) { let c = Contour.CreateContour(cus, false); if (c) - contourTrees.push(new ContourTree(c)); + contourNodes.push(new ContourTreeNode(c)); else console.error("出错"); } - ContourTree.ParseContourTree(contourTrees); + ContourTreeNode.ParseContourTree(contourNodes); - for (let j = contourTrees.length; j--;) + for (let j = contourNodes.length; j--;) { - let s = contourTrees[j]; + let node = contourNodes[j]; // TestDraw(s.contour.Curve.Clone(), z); - if (s.parent) continue; + if (node.parent) continue; - s.Draw(verticesArray, uvArray, front, z, rotateUv); + node.Draw(verticesArray, uvArray, front, z, rotateUv); } } } @@ -562,11 +509,12 @@ function SplitCurveParams(cu: ExtureContourCurve): number[] return xparams; } +/** + * 曲线胶带(一维) + */ class CurveTape { - type: DirectionType; tapes: Range[]; - splitParams: number[] = []; constructor(public contour: Contour, public wallType: DirectionType) { @@ -585,11 +533,9 @@ class CurveTape let polylines: Polyline[] = []; - function TD(p: Vector3) + function TD(p: Vector3): PolylineProps { - return { - pt: AsVector2(p), bul: 0 - }; + return { pt: AsVector2(p), bul: 0 }; } const addPolyline = (t: Range) => @@ -621,16 +567,19 @@ class CurveTape return polylines; } - Parse(s: CurveTapeShape): CurveRange + /** + * 分析与另一个形状的包含关系 + */ + Parse(s: CurveTapeShape): CurveParamRangeRelation { - let [res1] = Parse(this.contour.Curve, s.contour.contour.Curve); + let [res1] = ParseCurveParamRangeRelation(this.contour.Curve, s.contour.contour.Curve); if (this.wallType === DirectionType.Inner) [res1.syntropy, res1.reverse] = [res1.reverse, res1.syntropy]; if (res1.container.length > 0) { for (let h of s.holes) { - let [res2] = Parse(this.contour.Curve, h.contour.Curve); + let [res2] = ParseCurveParamRangeRelation(this.contour.Curve, h.contour.Curve); if (this.wallType === DirectionType.Outer) [res2.syntropy, res2.reverse] = [res2.reverse, res2.syntropy]; @@ -663,8 +612,7 @@ class CurveTape */ ReverseClipTo(s: CurveTapeShape): this { - let d = this.Parse(s); - this.tapes = d.container; + this.tapes = this.Parse(s).container; return this; } } @@ -672,7 +620,7 @@ class CurveTape class ExtudeWall { //胶带(立面) - Tape: Tape[]; + private Tape: Tape[]; constructor(public curve: ExtureContourCurve, public depthType: DepthType, public depth: number, @@ -685,20 +633,20 @@ class ExtudeWall } /** - * 裁剪 - * @param shape + * 减去在另一个groove内的部分 + * @param groove this - groove * @param [clipSyntropy=false] 删除同向的面 */ - Clip(shape: ExtudeWallShape, clipSyntropy = false) + ClipTo(groove: Groove, clipSyntropy = false) { - let [res1, res2] = Parse(this.curve, shape.contour.curve); - if (this.wallType !== shape.contour.wallType) + let [res1] = ParseCurveParamRangeRelation(this.curve, groove.contourWall.curve); + if (this.wallType !== groove.contourWall.wallType) [res1.syntropy, res1.reverse] = [res1.reverse, res1.syntropy]; if (res1.container.length > 0) { - for (let h of shape.holes) + for (let h of groove.holeWalls) { - let [resh1, resh2] = Parse(this.curve, h.curve); + let [resh1] = ParseCurveParamRangeRelation(this.curve, h.curve); //翻转 if (this.wallType !== h.wallType) @@ -726,14 +674,12 @@ class ExtudeWall params.push(...res1.syntropy); for (let c of params) - { - this.ClipFromParam(c[0], c[1], shape.depthType, shape.depth); - } + this.ClipFromParam(c[0], c[1], groove.depthType, groove.depth); } ClipReverse(wall: this) { - let [res1, res2] = Parse(this.curve, wall.curve); + let [res1] = ParseCurveParamRangeRelation(this.curve, wall.curve); for (let c of res1.syntropy) this.ClipFromParam(c[0], c[1], wall.depthType, wall.depth); } @@ -775,66 +721,119 @@ class ExtudeWall verticesArray.push(v.y); verticesArray.push(v.z); } + + let tapes: Tape[] = []; + this.Tape.sort((t1, t2) => t1.start - t2.start); for (let tape of this.Tape) + tapes.push(...tape.Split(xparams)); + for (let i = 0; i < tapes.length; i++) { - let tapes = tape.Split(xparams); - for (let t of tapes) + let preIndex = FixIndex(i - 1, tapes); + let nextIndex = FixIndex(i + 1, tapes); + + let tape = tapes[i]; + let preTape = tapes[preIndex]; + let nextTape = tapes[nextIndex]; + + let p1 = this.curve.GetPointAtParam(tape.start).setZ(tape.bottom); + let p2 = this.curve.GetPointAtParam(tape.end).setZ(tape.bottom); + let vs = [p1, p2, p2.clone().setZ(tape.top), p1.clone().setZ(tape.top), p1]; + edgeBuild.AddLidLine(p1, p2, tape.bottom); + edgeBuild.AddLidLine(p1, p2, tape.top); + + //#region 构造线框 { - let p1 = this.curve.GetPointAtParam(t.start).setZ(t.bottom); - let p2 = this.curve.GetPointAtParam(t.end).setZ(t.bottom); - let vs = [p1, p2, p2.clone().setZ(t.top), p1.clone().setZ(t.top), p1]; - edgeBuild.AddLidLine(p1, p2, t.bottom); - edgeBuild.AddLidLine(p1, p2, t.top); - edgeBuild.AddEdgePolygon(vs); + let leftRanges: Range[]; + let rightRange: Range[]; + + const IsInteger = (n: number) => equaln(n, Math.round(n), 1e-8); + + if (!IsInteger(tape.start) && equaln(tape.start, preTape.end)) + leftRanges = SubtractRange(tape.bottom, tape.top, preTape.bottom, preTape.top, this.allDepth); + else + leftRanges = [[tape.bottom, tape.top]]; + + if (equaln(tape.end, nextTape.start)) + rightRange = SubtractRange(tape.bottom, tape.top, nextTape.bottom, nextTape.top, this.allDepth); + else + rightRange = [[tape.bottom, tape.top]]; + + //上下两条线 + edgeBuild.lineVerticesArray.push( + p1.x, p1.y, p1.z, + p2.x, p2.y, p2.z, - //和X平行平行 - let isXPar = (vs[0].y + vs[1].y + vs[2].y) < 0.01; + p1.x, p1.y, tape.top, + p2.x, p2.y, tape.top, + ); - function AddUv(p: Vector3) + //左右线 + for (let range of leftRanges) { - if (isXPar) - uvArray.push((p.z - 1) * 1e-3, p.y * 1e-3); - else - uvArray.push((p.z - 1) * 1e-3, p.x * 1e-3); + edgeBuild.lineVerticesArray.push( + p1.x, p1.y, range[0], + p1.x, p1.y, range[1]); } - if (this.wallType === DirectionType.Outer) + for (let range of rightRange) { - AddVertice(vs[0]); - AddUv(vs[0]); - AddVertice(vs[1]); - AddUv(vs[1]); - AddVertice(vs[2]); - AddUv(vs[2]); - - AddVertice(vs[0]); - AddUv(vs[0]); - AddVertice(vs[2]); - AddUv(vs[2]); - AddVertice(vs[3]); - AddUv(vs[3]); + edgeBuild.lineVerticesArray.push( + p2.x, p2.y, range[0], + p2.x, p2.y, range[1]); } + } + //#endregion + + //和X平行平行 + let isXPar = (vs[0].y + vs[1].y + vs[2].y) < 0.01; + + function AddUv(p: Vector3) + { + if (isXPar) + uvArray.push((p.z - 1) * 1e-3, p.y * 1e-3); else - { - AddVertice(vs[0]); - AddUv(vs[0]); - AddVertice(vs[2]); - AddUv(vs[2]); - AddVertice(vs[1]); - AddUv(vs[1]); - - AddVertice(vs[0]); - AddUv(vs[0]); - AddVertice(vs[3]); - AddUv(vs[3]); - AddVertice(vs[2]); - AddUv(vs[2]); - } + uvArray.push((p.z - 1) * 1e-3, p.x * 1e-3); + } + if (this.wallType === DirectionType.Outer) + { + AddVertice(vs[0]); + AddUv(vs[0]); + AddVertice(vs[1]); + AddUv(vs[1]); + AddVertice(vs[2]); + AddUv(vs[2]); + + AddVertice(vs[0]); + AddUv(vs[0]); + AddVertice(vs[2]); + AddUv(vs[2]); + AddVertice(vs[3]); + AddUv(vs[3]); + } + else + { + AddVertice(vs[0]); + AddUv(vs[0]); + AddVertice(vs[2]); + AddUv(vs[2]); + AddVertice(vs[1]); + AddUv(vs[1]); + + AddVertice(vs[0]); + AddUv(vs[0]); + AddVertice(vs[3]); + AddUv(vs[3]); + AddVertice(vs[2]); + AddUv(vs[2]); } } } } -interface CurveRange +/** + * 曲线参数范围关系(包含,分离,同向共线,反向共线) + * 用来表示某一曲线在另一个曲线内的关系 + */ +interface CurveParamRangeRelation { outer: Range[];//外部 container: Range[];//被包含 @@ -842,7 +841,7 @@ interface CurveRange reverse: Range[];//反向 } -function CloneCurveRange(r: CurveRange): CurveRange +function CloneCurveRange(r: CurveParamRangeRelation): CurveParamRangeRelation { return { outer: r.outer.slice(), @@ -878,7 +877,7 @@ function binarySearch(ar: number[], el: number): number return -m - 1; } -function CurveSplit(cu: Curve, range: CurveRange): CurveSegs +function CurveSplit(cu: Curve, range: CurveParamRangeRelation): CurveSegs { let segs = { outer: [], container: [], syntropy: [], reverse: [] }; @@ -903,18 +902,14 @@ function CurveSplit(cu: Curve, range: CurveRange): CurveSegs } /** - * 分析两个曲线的状态 - * @param cu1 - * @param cu2 - * @param ins - * @returns + * 分析两个曲线关系(包含,分离,同向共线,反向共线)(用参数范围表示) */ -function Parse(cu1: ExtureContourCurve, cu2: ExtureContourCurve, reverseParse = false): [CurveRange, CurveRange] +function ParseCurveParamRangeRelation(cu1: ExtureContourCurve, cu2: ExtureContourCurve, reverseParse = false): [CurveParamRangeRelation, CurveParamRangeRelation] { let ins = GetIntersection(cu1, cu2); - let c1Res: CurveRange = { container: [], syntropy: [], reverse: [], outer: [] }; - let c2Res: CurveRange = { container: [], syntropy: [], reverse: [], outer: [] }; + let c1Res: CurveParamRangeRelation = { container: [], syntropy: [], reverse: [], outer: [] }; + let c2Res: CurveParamRangeRelation = { container: [], syntropy: [], reverse: [], outer: [] }; if (ins.length === 0) { if (cu2.PtInCurve(cu1.StartPoint)) @@ -943,6 +938,9 @@ function Parse(cu1: ExtureContourCurve, cu2: ExtureContourCurve, reverseParse = let c2Curves: CurveSeg[] = []; ins.sort((a1, a2) => a1.thisParam - a2.thisParam); + //点重复->下方ins会sort,导致交点对应不上,导致错误 + arrayRemoveDuplicateBySort(ins, (i1, i2) => equalv3(i1.pt, i2.pt, 1e-4)); + if (ins.length > 1 && equalv3(ins[0].pt, ins[ins.length - 1].pt, 1e-4)) ins.pop(); for (let i = 0; i < ins.length; i++) { let n1 = ins[i]; @@ -957,7 +955,6 @@ function Parse(cu1: ExtureContourCurve, cu2: ExtureContourCurve, reverseParse = c2Curves.push({ startParam: n1.argParam, endParam: n2.argParam, startPoint: n1.pt, endPoint: n2.pt }); } - //分析共边关系和包含关系 for (let c of c1Curves) { @@ -968,7 +965,7 @@ function Parse(cu1: ExtureContourCurve, cu2: ExtureContourCurve, reverseParse = continue; let c2MidPoint = CenterPoint(cu2, c2.startParam, c2.endParam); - if (!equalv3(c1MidPoint, c2MidPoint)) + if (!equalv3(c1MidPoint, c2MidPoint, 1e-4)) continue; c.used = true; @@ -1031,7 +1028,6 @@ function CenterPoint(cu: ExtureContourCurve, start: number, end: number) return cu.GetPointAtDistance(lenStart + lenDiv); } - //求参数并集部分,交集部分,差集部分 //求 ab 和 cd 的并集部分 @@ -1136,46 +1132,38 @@ function IntersectRange(a: number, b: number, c: number, d: number, end: number) return [c1, Math.min(b1, d1)]; } +const alMatrix4 = new Matrix4; + export class ExtrudeGeometryBuilder { - brOcsInv: Matrix4; - alMatrix4: Matrix4 = new Matrix4; - - contour: Contour; - - shapes: ExtudeWallShape[] = []; - - verticesArray: number[] = []; - uvArray: number[] = []; + verticesArray: number[] = [];//用于构建三维网格 + uvArray: number[] = [];//uv edgeAndLidBuilder: EdgeGeometryBuild; constructor(private br: ExtrudeSolid) { this.edgeAndLidBuilder = new EdgeGeometryBuild(this.br.Thickness); - this.brOcsInv = br.OCSInv; let rotateUv = (br instanceof Board && br.BoardProcessOption.lines === LinesType.Reverse); - this.contour = Contour.CreateContour(br.ContourCurve.Clone()); - - this.ParseGrooves(); - - //计算墙 - let outerWall = new ExtudeWall(this.contour.Curve, DepthType.All, br.Thickness, br.Thickness, DirectionType.Outer); - for (let i = 0; i < this.shapes.length; i++) + //计算墙(创建轮廓取出,为了得到正确的轮廓曲线(逆时针之类的)) + let outerWall = new ExtudeWall(Contour.CreateContour(br.ContourCurve.Clone()).Curve, DepthType.All, br.Thickness, br.Thickness, DirectionType.Outer); + let grooves = this.ParseGrooves(); + for (let i = 0; i < grooves.length; i++) { - let s1 = this.shapes[i]; - outerWall.Clip(s1, false); - s1.contour.ClipReverse(outerWall); - for (let j = i + 1; j < this.shapes.length; j++) + let s1 = grooves[i]; + outerWall.ClipTo(s1, false); + s1.contourWall.ClipReverse(outerWall); + for (let j = i + 1; j < grooves.length; j++) { - let s2 = this.shapes[j]; - s1.ClipWall(s2, true); + let s2 = grooves[j]; + s1.ClipTo(s2, true); } s1.Draw(this.verticesArray, this.uvArray, this.edgeAndLidBuilder, rotateUv); } outerWall.Draw(this.verticesArray, this.uvArray, this.edgeAndLidBuilder); + //这里构建盖子 this.edgeAndLidBuilder.BuildLid(this.verticesArray, this.uvArray, rotateUv); intCache.clear(); @@ -1193,53 +1181,57 @@ export class ExtrudeGeometryBuilder get EdgeGeometry() { let geo = new BufferGeometry(); - geo.setAttribute('position', new Float32BufferAttribute(this.edgeAndLidBuilder.verticesArray, 3)); + geo.setAttribute('position', new Float32BufferAttribute(this.edgeAndLidBuilder.lineVerticesArray, 3)); return geo; } private ParseGrooves() { let br = this.br; - for (let g of br.Grooves) + const brOcsInv = br.OCSInv; + let grooves: Groove[] = []; + for (let groove of br.Grooves) { //判断槽正反面 let type: DepthType; - if (equaln(g.Thickness, br.Thickness)) + if (equaln(groove.Thickness, br.Thickness)) type = DepthType.All; else { - if (equaln(g.Position.applyMatrix4(this.brOcsInv).z, 0)) + if (equaln(groove.Position.applyMatrix4(brOcsInv).z, 0)) type = DepthType.Back; else type = DepthType.Front; } - this.alMatrix4.multiplyMatrices(this.brOcsInv, g.OCSNoClone); + alMatrix4.multiplyMatrices(brOcsInv, groove.OCSNoClone); //槽轮廓 - let gContour = g.ContourCurve.Clone(); - gContour.ApplyMatrix(this.alMatrix4); - gContour.Z0(); - if (gContour instanceof Polyline) gContour.UpdateMatrixTo(IdentityMtx4);//不可能改变这个 - let contour = Contour.CreateContour(gContour); + let grooveContourCurve = groove.ContourCurve.Clone(); + grooveContourCurve.ApplyMatrix(alMatrix4); + grooveContourCurve.Z0(); + if (grooveContourCurve instanceof Polyline) grooveContourCurve.UpdateMatrixTo(IdentityMtx4);//不可能改变这个 + let grooveContour = Contour.CreateContour(grooveContourCurve); - let holes: Contour[] = []; + let grooveHoleContours: Contour[] = []; //孤岛 - for (let gg of g.Grooves) + for (let grooveChild of groove.Grooves) { - let c = gg.ContourCurve.Clone(); - this.alMatrix4.multiplyMatrices(this.brOcsInv, gg.OCSNoClone); - c.ApplyMatrix(this.alMatrix4).Z0(); - if (c instanceof Polyline) c.UpdateMatrixTo(IdentityMtx4); - let contour = Contour.CreateContour(c); - holes.push(contour); + let grooveChildContourCurve = grooveChild.ContourCurve.Clone(); + alMatrix4.multiplyMatrices(brOcsInv, grooveChild.OCSNoClone); + grooveChildContourCurve.ApplyMatrix(alMatrix4).Z0(); + if (grooveChildContourCurve instanceof Polyline) grooveChildContourCurve.UpdateMatrixTo(IdentityMtx4); + let grooveChildContour = Contour.CreateContour(grooveChildContourCurve); + grooveHoleContours.push(grooveChildContour); } - this.shapes.push(new ExtudeWallShape(contour, holes, type, g.Thickness, br.Thickness)); + grooves.push(new Groove(grooveContour, grooveHoleContours, type, groove.Thickness, br.Thickness)); } + + return grooves; } } let intCache = new Map>(); -function GetIntersection(cu1: Curve, cu2: Curve) +function GetIntersection(cu1: Curve, cu2: Curve): IntersectResult[] { let m = intCache.get(cu1); if (m) diff --git a/src/index.html b/src/index.html index 7b585d6b4..c7e10a238 100644 --- a/src/index.html +++ b/src/index.html @@ -5,33 +5,7 @@ - + WebCAD