!1067 优化:拉伸实体的显示优化(线框和实体模式)

pull/1067/MERGE
ChenX 4 years ago
parent cbdcaafca5
commit 0729c1b650

@ -39,7 +39,7 @@ test('Geometry构建测试', () =>
{ {
let builder = new ExtrudeGeometryBuilder(br); let builder = new ExtrudeGeometryBuilder(br);
expect(builder.verticesArray.length).toMatchSnapshot(); 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); let builder = new ExtrudeGeometryBuilder(br);
expect(builder.verticesArray.length).toMatchSnapshot(); 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();
} }
}); });

@ -2,52 +2,56 @@
exports[`EdgeGeometry生成 1`] = `114`; exports[`EdgeGeometry生成 1`] = `114`;
exports[`EdgeGeometry生成 2`] = `360`; exports[`EdgeGeometry生成 2`] = `206`;
exports[`EdgeGeometry生成 3`] = `60`; exports[`EdgeGeometry生成 3`] = `60`;
exports[`EdgeGeometry生成2 1`] = `80`; exports[`EdgeGeometry生成2 1`] = `66`;
exports[`Geometry构建测试 1`] = `540`; exports[`Geometry构建测试 1`] = `540`;
exports[`Geometry构建测试 2`] = `408`; exports[`Geometry构建测试 2`] = `372`;
exports[`Geometry构建测试 3`] = `378`; exports[`Geometry构建测试 3`] = `378`;
exports[`Geometry构建测试 4`] = `288`; exports[`Geometry构建测试 4`] = `234`;
exports[`Geometry构建测试 5`] = `1188`; exports[`Geometry构建测试 5`] = `1188`;
exports[`Geometry构建测试 6`] = `888`; exports[`Geometry构建测试 6`] = `744`;
exports[`Geometry构建测试 7`] = `774`; exports[`Geometry构建测试 7`] = `774`;
exports[`Geometry构建测试 8`] = `552`; exports[`Geometry构建测试 8`] = `450`;
exports[`Geometry构建测试 9`] = `630`; exports[`Geometry构建测试 9`] = `630`;
exports[`Geometry构建测试 10`] = `456`; exports[`Geometry构建测试 10`] = `300`;
exports[`Geometry构建测试 11`] = `1548`; exports[`Geometry构建测试 11`] = `1548`;
exports[`Geometry构建测试 12`] = `1056`; exports[`Geometry构建测试 12`] = `570`;
exports[`Geometry构建测试 13`] = `639`; exports[`Geometry构建测试 13`] = `639`;
exports[`Geometry构建测试 14`] = `480`; exports[`Geometry构建测试 14`] = `378`;
exports[`Geometry构建测试 15`] = `1998`; exports[`Geometry构建测试 15`] = `1998`;
exports[`Geometry构建测试 16`] = `1368`; exports[`Geometry构建测试 16`] = `786`;
exports[`Geometry构建测试 17`] = `1908`; exports[`Geometry构建测试 17`] = `1908`;
exports[`Geometry构建测试 18`] = `1344`; exports[`Geometry构建测试 18`] = `744`;
exports[`Geometry构建测试 19`] = `2232`; exports[`Geometry构建测试 19`] = `2232`;
exports[`Geometry构建测试 20`] = `1584`; exports[`Geometry构建测试 20`] = `924`;
exports[`极限共线 1`] = `432`; exports[`极限共线 1`] = `432`;
exports[`极限共线 2`] = `360`; exports[`极限共线 2`] = `294`;
exports[`盖子构建失败 1`] = `180`;
exports[`盖子构建失败 2`] = `138`;

@ -10,9 +10,9 @@ const config: webpack.Configuration = merge(
output: { pathinfo: false }, output: { pathinfo: false },
devtool: "eval-source-map", devtool: "eval-source-map",
//https://www.webpackjs.com/configuration/stats/ //https://www.webpackjs.com/configuration/stats/
stats: { stats: "errors-only" || {
assets: false, assets: false,
timings: true, timings: false,
builtAt: false, builtAt: false,
cachedAssets: false, cachedAssets: false,

@ -1,4 +1,5 @@
import { app } from '../ApplicationServices/Application'; import { app } from '../ApplicationServices/Application';
import { IsDev } from '../Common/Deving';
import { GetEntity } from '../Common/Utils'; import { GetEntity } from '../Common/Utils';
import { Command } from '../Editor/CommandMachine'; import { Command } from '../Editor/CommandMachine';
import { PromptStatus } from '../Editor/PromptResult'; import { PromptStatus } from '../Editor/PromptResult';
@ -7,8 +8,22 @@ export class Command_Erase implements Command
{ {
async exec() 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 }); 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) for (let obj of ssRes.SelectSet.SelectObjectList)
{ {

@ -17,35 +17,35 @@ export class Command_TestTape
let br = enRes.Entity as Board; let br = enRes.Entity as Board;
let geo = new ExtrudeGeometryBuilder(br); let geo = new ExtrudeGeometryBuilder(br);
return; return;
let k = 0; // let k = 0;
let i = 1; // let i = 1;
let testOut = true; // let testOut = true;
for (let s of geo.shapes) // for (let s of geo)
{ // {
if (testOut) // if (testOut)
{ // {
let m = MoveMatrix(new Vector3(0, k * 20)); // let m = MoveMatrix(new Vector3(0, k * 20));
for (let t of s.contour.Tape) // for (let t of s.contourWall.Tape)
{ // {
t.Curve.ApplyMatrix(m); // t.Curve.ApplyMatrix(m);
TestDraw(t.Curve, s.depth); // TestDraw(t.Curve, s.depth);
} // }
k++; // k++;
} // }
else // else
{ // {
for (let w of s.holes) // for (let w of s.holeWalls)
{ // {
let m = MoveMatrix(new Vector3(0, k * 20)); // let m = MoveMatrix(new Vector3(0, k * 20));
for (let t of w.Tape) // for (let t of w.Tape)
{ // {
t.Curve.ApplyMatrix(m); // t.Curve.ApplyMatrix(m);
TestDraw(t.Curve, w.depth); // TestDraw(t.Curve, w.depth);
} // }
k++; // k++;
} // }
} // }
} // }
// let gs = br.Grooves; // let gs = br.Grooves;
// let brOcsInv = br.OCSInv; // let brOcsInv = br.OCSInv;
// let alm = new Matrix4; // let alm = new Matrix4;

@ -73,7 +73,7 @@ export abstract class Curve extends Entity
get Area2(): number { return 0; } get Area2(): number { return 0; }
get Length(): number { return 0; } get Length(): number { return 0; }
get IsClose(): boolean { return false; } get IsClose(): boolean { return false; }
//曲线为顺时针 /** 曲线为顺时针 */
get IsClockWise(): boolean { return this.Area2 < 0; } get IsClockWise(): boolean { return this.Area2 < 0; }
abstract get Shape(): Shape; abstract get Shape(): Shape;

@ -476,7 +476,7 @@ export class Polyline extends Curve
} }
//参数已经大于索引,证明参数在线外. //参数已经大于索引,证明参数在线外.
if (paramFloor != cuCout) if (paramFloor !== cuCout)
{ {
dist += this.GetCurveAtParam(param).GetDistAtParam(param - cuCout); dist += this.GetCurveAtParam(param).GetDistAtParam(param - cuCout);
} }

@ -23,10 +23,7 @@ export async function ExportObj(brs: Entity[])
for (let b of brs) for (let b of brs)
{ {
let o: Object3D; let o: Object3D;
if (userConfig.RenderType === RenderType.Physical) o = b.Clone().ApplyMatrix(mtx).GetDrawObjectFromRenderType(RenderType.Physical);
o = b.Clone().ApplyMatrix(mtx).GetDrawObjectFromRenderType(RenderType.Physical);
else
o = b.Clone().ApplyMatrix(mtx).GetDrawObjectFromRenderType(RenderType.Conceptual);
g.add(o); g.add(o);
} }

@ -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 { arrayRemoveDuplicateBySort, arraySortByNumber } from "../Common/ArrayExt";
import { curveLinkGroup } from "../Common/CurveUtils"; import { curveLinkGroup } from "../Common/CurveUtils";
import { clamp, FixIndex } from "../Common/Utils"; import { clamp, FixIndex } from "../Common/Utils";
@ -9,9 +9,10 @@ import { Circle } from "../DatabaseServices/Entity/Circle";
import { Curve } from "../DatabaseServices/Entity/Curve"; import { Curve } from "../DatabaseServices/Entity/Curve";
import { ExtrudeSolid, ExtureContourCurve } from "../DatabaseServices/Entity/Extrude"; import { ExtrudeSolid, ExtureContourCurve } from "../DatabaseServices/Entity/Extrude";
import { Line } from "../DatabaseServices/Entity/Line"; 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 { IntersectOption, IntersectResult } from "../GraphicsSystem/IntersectWith";
import { LinesType } from "../UI/Store/BoardInterface"; import { LinesType } from "../UI/Store/BoardInterface";
import { IntersectsBox } from "./Box";
import { AsVector2, equaln, equalv3, IdentityMtx4 } from "./GeUtils"; import { AsVector2, equaln, equalv3, IdentityMtx4 } from "./GeUtils";
import { RegionParse } from "./RegionParse"; import { RegionParse } from "./RegionParse";
@ -22,138 +23,90 @@ export enum DepthType
All = 3, 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; contourWall: ExtudeWall;//槽轮廓的墙
holes: ExtudeWall[] = []; holeWalls: ExtudeWall[] = [];//槽的网洞的墙
private lid: CurveTapeShape;//槽的盖子
box: Box3;
shape: CurveTapeShape;
constructor(contour: Contour, constructor(contour: Contour,
holes: Contour[], holes: Contour[],
public depthType: DepthType, public depthType: DepthType,
public depth: number, 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.contourWall = new ExtudeWall(contour.Curve, depthType, depth, allDepth, DirectionType.Inner);
this.box = contour.BoundingBox;
for (let h of holes) 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 groove this - groove
* @param shape
* @param [eachOther=true] * @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); this.ClipLid(groove);
shape.ClipShape(this); 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; return;
this.contour.Clip(shape, true); this.contourWall.ClipTo(groove, true);
for (let c of this.holes) for (let wall of this.holeWalls)
c.Clip(shape, true); wall.ClipTo(groove, true);
if (eachOther) if (eachOther)
{ {
shape.contour.Clip(this, false); groove.contourWall.ClipTo(this, false);
for (let c of shape.holes) for (let wall of groove.holeWalls)
c.Clip(this, false); wall.ClipTo(this, false);
} }
} }
ClipShape(shape: ExtudeWallShape) private ClipLid(groove: Groove)
{ {
if (this.depthType === DepthType.All) return; 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) if (groove.depth > this.depth)
this.shape.ClipTo(shape.shape, true); this.lid.ClipTo(groove.lid, true);
else else
{ this.lid.SplitTo(groove.lid);
this.shape.SplitTo(shape.shape);
}
} }
else else
{ {
if (this.depth + shape.depth >= this.allDepth) if (this.depth + groove.depth >= this.allDepth)
this.shape.ClipTo(shape.shape, true); this.lid.ClipTo(groove.lid, true);
else else
{ this.lid.SplitTo(groove.lid);
this.shape.SplitTo(shape.shape);
}
} }
} }
Draw(verticesArray: number[], uvArray: number[], edgeBuild: EdgeGeometryBuild, rotateUv: boolean) Draw(verticesArray: number[], uvArray: number[], edgeBuild: EdgeGeometryBuild, rotateUv: boolean)
{ {
this.contour.Draw(verticesArray, uvArray, edgeBuild); this.contourWall.Draw(verticesArray, uvArray, edgeBuild);
for (let wall of this.holes) for (let wall of this.holeWalls)
wall.Draw(verticesArray, uvArray, edgeBuild); wall.Draw(verticesArray, uvArray, edgeBuild);
if (this.depthType === DepthType.All) return; if (this.depthType === DepthType.All) return;
let isFront = this.depthType === DepthType.Front; 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 //内墙 Inner = 1 //内墙
} }
//轮廓树,用于重新确认外墙和网洞的关系 //轮廓树节点,用于重新确认外墙和网洞的关系
class ContourTree export class ContourTreeNode
{ {
parent: ContourTree; parent: ContourTreeNode;//当存在Parent时,表示它是一个洞
constructor(public contour: Contour, public children: ContourTree[] = []) constructor(public contour: Contour, public children: ContourTreeNode[] = []) { }
{
}
SetParent(s: ContourTree) SetParent(node: ContourTreeNode)
{ {
this.parent = s; this.parent = node;
s.children.push(this); node.children.push(this);
} }
Draw(verticesArray: number[], uvArray: number[], front: boolean, z: number, rotateUv: boolean)//, depth = 1 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); contourNodes.sort((c1, c2) => c1.contour.Curve.Area - c2.contour.Curve.Area);
for (let i = 0; i < contourTrees.length; i++) for (let i = 0; i < contourNodes.length; i++)
{ {
const s = contourTrees[i]; const node1 = contourNodes[i];
let p = s.contour.Curve.StartPoint; let p = node1.contour.Curve.StartPoint;
for (let j = i + 1; j < contourTrees.length; j++) for (let j = i + 1; j < contourNodes.length; j++)
{ {
const s2 = contourTrees[j]; const node2 = contourNodes[j];
if (s2.contour.BoundingBox.intersectsBox(s.contour.BoundingBox) if (node2.contour.BoundingBox.intersectsBox(node1.contour.BoundingBox)
&& s2.contour.Curve.PtInCurve(p)) && node2.contour.Curve.PtInCurve(p))
{ {
s.SetParent(s2); node1.SetParent(node2);
break; break;
} }
} }
@ -276,7 +227,7 @@ class ContourTree
class EdgeGeometryBuild class EdgeGeometryBuild
{ {
verticesArray: number[] = []; lineVerticesArray: number[] = [];
frontLines: Line[] = []; frontLines: Line[] = [];
backLines: 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) BuildLid(verticesArray: number[], uvArray: number[], rotateUv: boolean)
{ {
let arr = [this.backLines, this.frontLines]; let arr = [this.backLines, this.frontLines];
@ -320,7 +258,7 @@ class EdgeGeometryBuild
{ {
let lines = arr[index]; let lines = arr[index];
let parse = new RegionParse(lines); let parse = new RegionParse(lines);
let contours: ContourTree[] = []; let contourNodes: ContourTreeNode[] = [];
for (let routes of parse.RegionsOutline) for (let routes of parse.RegionsOutline)
{ {
let cs: Curve[] = []; let cs: Curve[] = [];
@ -328,24 +266,27 @@ class EdgeGeometryBuild
cs.push(r.curve); cs.push(r.curve);
let c = Contour.CreateContour(cs, false); let c = Contour.CreateContour(cs, false);
if (c) if (c)
contours.push(new ContourTree(c)); contourNodes.push(new ContourTreeNode(c));
else else
console.error("未能构建盖子"); console.error("未能构建盖子");
} }
ContourTree.ParseContourTree(contours); ContourTreeNode.ParseContourTree(contourNodes);
for (let j = contours.length; j--;) for (let j = contourNodes.length; j--;)
{ {
let s = contours[j]; let node = contourNodes[j];
if (s.parent) continue; 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 class Tape
{ {
constructor( constructor(
@ -405,7 +346,10 @@ class Tape
} }
} }
class CurveTapeShape /**
* ,线()
*/
export class CurveTapeShape
{ {
children: CurveTapeShape[] = []; children: CurveTapeShape[] = [];
contour: CurveTape; contour: CurveTape;
@ -439,7 +383,7 @@ class CurveTapeShape
} }
} }
//合理打断 //合理打断(以保证三维网格对齐(否则圆弧点将无法正确的对齐))
SplitTo(s: CurveTapeShape) SplitTo(s: CurveTapeShape)
{ {
for (let c of [this.contour, ...this.holes]) 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]) for (let c of [this.contour, ... this.holes])
if (c.tapes.length > 0) if (c.tapes.length > 0)
@ -494,25 +441,25 @@ class CurveTapeShape
// TestDraw(polylines, z); // TestDraw(polylines, z);
let groups = curveLinkGroup(polylines); let groups = curveLinkGroup(polylines);
let contourTrees: ContourTree[] = []; let contourNodes: ContourTreeNode[] = [];
for (let cus of groups) for (let cus of groups)
{ {
let c = Contour.CreateContour(cus, false); let c = Contour.CreateContour(cus, false);
if (c) if (c)
contourTrees.push(new ContourTree(c)); contourNodes.push(new ContourTreeNode(c));
else else
console.error("出错"); 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); // 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; return xparams;
} }
/**
* 线()
*/
class CurveTape class CurveTape
{ {
type: DirectionType;
tapes: Range[]; tapes: Range[];
splitParams: number[] = []; splitParams: number[] = [];
constructor(public contour: Contour, public wallType: DirectionType) constructor(public contour: Contour, public wallType: DirectionType)
{ {
@ -585,11 +533,9 @@ class CurveTape
let polylines: Polyline[] = []; let polylines: Polyline[] = [];
function TD(p: Vector3) function TD(p: Vector3): PolylineProps
{ {
return { return { pt: AsVector2(p), bul: 0 };
pt: AsVector2(p), bul: 0
};
} }
const addPolyline = (t: Range) => const addPolyline = (t: Range) =>
@ -621,16 +567,19 @@ class CurveTape
return polylines; 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) if (this.wallType === DirectionType.Inner)
[res1.syntropy, res1.reverse] = [res1.reverse, res1.syntropy]; [res1.syntropy, res1.reverse] = [res1.reverse, res1.syntropy];
if (res1.container.length > 0) if (res1.container.length > 0)
{ {
for (let h of s.holes) 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) if (this.wallType === DirectionType.Outer)
[res2.syntropy, res2.reverse] = [res2.reverse, res2.syntropy]; [res2.syntropy, res2.reverse] = [res2.reverse, res2.syntropy];
@ -663,8 +612,7 @@ class CurveTape
*/ */
ReverseClipTo(s: CurveTapeShape): this ReverseClipTo(s: CurveTapeShape): this
{ {
let d = this.Parse(s); this.tapes = this.Parse(s).container;
this.tapes = d.container;
return this; return this;
} }
} }
@ -672,7 +620,7 @@ class CurveTape
class ExtudeWall class ExtudeWall
{ {
//胶带(立面) //胶带(立面)
Tape: Tape[]; private Tape: Tape[];
constructor(public curve: ExtureContourCurve, constructor(public curve: ExtureContourCurve,
public depthType: DepthType, public depthType: DepthType,
public depth: number, public depth: number,
@ -685,20 +633,20 @@ class ExtudeWall
} }
/** /**
* * groove
* @param shape * @param groove this - groove
* @param [clipSyntropy=false] * @param [clipSyntropy=false]
*/ */
Clip(shape: ExtudeWallShape, clipSyntropy = false) ClipTo(groove: Groove, clipSyntropy = false)
{ {
let [res1, res2] = Parse(this.curve, shape.contour.curve); let [res1] = ParseCurveParamRangeRelation(this.curve, groove.contourWall.curve);
if (this.wallType !== shape.contour.wallType) if (this.wallType !== groove.contourWall.wallType)
[res1.syntropy, res1.reverse] = [res1.reverse, res1.syntropy]; [res1.syntropy, res1.reverse] = [res1.reverse, res1.syntropy];
if (res1.container.length > 0) 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) if (this.wallType !== h.wallType)
@ -726,14 +674,12 @@ class ExtudeWall
params.push(...res1.syntropy); params.push(...res1.syntropy);
for (let c of params) for (let c of params)
{ this.ClipFromParam(c[0], c[1], groove.depthType, groove.depth);
this.ClipFromParam(c[0], c[1], shape.depthType, shape.depth);
}
} }
ClipReverse(wall: this) ClipReverse(wall: this)
{ {
let [res1, res2] = Parse(this.curve, wall.curve); let [res1] = ParseCurveParamRangeRelation(this.curve, wall.curve);
for (let c of res1.syntropy) for (let c of res1.syntropy)
this.ClipFromParam(c[0], c[1], wall.depthType, wall.depth); this.ClipFromParam(c[0], c[1], wall.depthType, wall.depth);
} }
@ -775,66 +721,119 @@ class ExtudeWall
verticesArray.push(v.y); verticesArray.push(v.y);
verticesArray.push(v.z); verticesArray.push(v.z);
} }
let tapes: Tape[] = [];
this.Tape.sort((t1, t2) => t1.start - t2.start);
for (let tape of this.Tape) for (let tape of this.Tape)
tapes.push(...tape.Split(xparams));
for (let i = 0; i < tapes.length; i++)
{ {
let tapes = tape.Split(xparams); let preIndex = FixIndex(i - 1, tapes);
for (let t of 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 leftRanges: Range[];
let p2 = this.curve.GetPointAtParam(t.end).setZ(t.bottom); let rightRange: Range[];
let vs = [p1, p2, p2.clone().setZ(t.top), p1.clone().setZ(t.top), p1];
edgeBuild.AddLidLine(p1, p2, t.bottom); const IsInteger = (n: number) => equaln(n, Math.round(n), 1e-8);
edgeBuild.AddLidLine(p1, p2, t.top);
edgeBuild.AddEdgePolygon(vs); 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平行平行 p1.x, p1.y, tape.top,
let isXPar = (vs[0].y + vs[1].y + vs[2].y) < 0.01; p2.x, p2.y, tape.top,
);
function AddUv(p: Vector3) //左右线
for (let range of leftRanges)
{ {
if (isXPar) edgeBuild.lineVerticesArray.push(
uvArray.push((p.z - 1) * 1e-3, p.y * 1e-3); p1.x, p1.y, range[0],
else p1.x, p1.y, range[1]);
uvArray.push((p.z - 1) * 1e-3, p.x * 1e-3);
} }
if (this.wallType === DirectionType.Outer) for (let range of rightRange)
{ {
AddVertice(vs[0]); edgeBuild.lineVerticesArray.push(
AddUv(vs[0]); p2.x, p2.y, range[0],
AddVertice(vs[1]); p2.x, p2.y, range[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]);
} }
}
//#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 else
{ uvArray.push((p.z - 1) * 1e-3, p.x * 1e-3);
AddVertice(vs[0]); }
AddUv(vs[0]); if (this.wallType === DirectionType.Outer)
AddVertice(vs[2]); {
AddUv(vs[2]); AddVertice(vs[0]);
AddVertice(vs[1]); AddUv(vs[0]);
AddUv(vs[1]); AddVertice(vs[1]);
AddUv(vs[1]);
AddVertice(vs[0]); AddVertice(vs[2]);
AddUv(vs[0]); AddUv(vs[2]);
AddVertice(vs[3]);
AddUv(vs[3]); AddVertice(vs[0]);
AddVertice(vs[2]); AddUv(vs[0]);
AddUv(vs[2]); 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[];//外部 outer: Range[];//外部
container: Range[];//被包含 container: Range[];//被包含
@ -842,7 +841,7 @@ interface CurveRange
reverse: Range[];//反向 reverse: Range[];//反向
} }
function CloneCurveRange(r: CurveRange): CurveRange function CloneCurveRange(r: CurveParamRangeRelation): CurveParamRangeRelation
{ {
return { return {
outer: r.outer.slice(), outer: r.outer.slice(),
@ -878,7 +877,7 @@ function binarySearch(ar: number[], el: number): number
return -m - 1; return -m - 1;
} }
function CurveSplit(cu: Curve, range: CurveRange): CurveSegs function CurveSplit(cu: Curve, range: CurveParamRangeRelation): CurveSegs
{ {
let segs = { outer: [], container: [], syntropy: [], reverse: [] }; 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 ins = GetIntersection(cu1, cu2);
let c1Res: CurveRange = { container: [], syntropy: [], reverse: [], outer: [] }; let c1Res: CurveParamRangeRelation = { container: [], syntropy: [], reverse: [], outer: [] };
let c2Res: CurveRange = { container: [], syntropy: [], reverse: [], outer: [] }; let c2Res: CurveParamRangeRelation = { container: [], syntropy: [], reverse: [], outer: [] };
if (ins.length === 0) if (ins.length === 0)
{ {
if (cu2.PtInCurve(cu1.StartPoint)) if (cu2.PtInCurve(cu1.StartPoint))
@ -943,6 +938,9 @@ function Parse(cu1: ExtureContourCurve, cu2: ExtureContourCurve, reverseParse =
let c2Curves: CurveSeg[] = []; let c2Curves: CurveSeg[] = [];
ins.sort((a1, a2) => a1.thisParam - a2.thisParam); 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++) for (let i = 0; i < ins.length; i++)
{ {
let n1 = ins[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 }); c2Curves.push({ startParam: n1.argParam, endParam: n2.argParam, startPoint: n1.pt, endPoint: n2.pt });
} }
//分析共边关系和包含关系 //分析共边关系和包含关系
for (let c of c1Curves) for (let c of c1Curves)
{ {
@ -968,7 +965,7 @@ function Parse(cu1: ExtureContourCurve, cu2: ExtureContourCurve, reverseParse =
continue; continue;
let c2MidPoint = CenterPoint(cu2, c2.startParam, c2.endParam); let c2MidPoint = CenterPoint(cu2, c2.startParam, c2.endParam);
if (!equalv3(c1MidPoint, c2MidPoint)) if (!equalv3(c1MidPoint, c2MidPoint, 1e-4))
continue; continue;
c.used = true; c.used = true;
@ -1031,7 +1028,6 @@ function CenterPoint(cu: ExtureContourCurve, start: number, end: number)
return cu.GetPointAtDistance(lenStart + lenDiv); return cu.GetPointAtDistance(lenStart + lenDiv);
} }
//求参数并集部分,交集部分,差集部分 //求参数并集部分,交集部分,差集部分
//求 ab 和 cd 的并集部分 //求 ab 和 cd 的并集部分
@ -1136,46 +1132,38 @@ function IntersectRange(a: number, b: number, c: number, d: number, end: number)
return [c1, Math.min(b1, d1)]; return [c1, Math.min(b1, d1)];
} }
const alMatrix4 = new Matrix4;
export class ExtrudeGeometryBuilder export class ExtrudeGeometryBuilder
{ {
brOcsInv: Matrix4; verticesArray: number[] = [];//用于构建三维网格
alMatrix4: Matrix4 = new Matrix4; uvArray: number[] = [];//uv
contour: Contour;
shapes: ExtudeWallShape[] = [];
verticesArray: number[] = [];
uvArray: number[] = [];
edgeAndLidBuilder: EdgeGeometryBuild; edgeAndLidBuilder: EdgeGeometryBuild;
constructor(private br: ExtrudeSolid) constructor(private br: ExtrudeSolid)
{ {
this.edgeAndLidBuilder = new EdgeGeometryBuild(this.br.Thickness); this.edgeAndLidBuilder = new EdgeGeometryBuild(this.br.Thickness);
this.brOcsInv = br.OCSInv;
let rotateUv = (br instanceof Board && br.BoardProcessOption.lines === LinesType.Reverse); let rotateUv = (br instanceof Board && br.BoardProcessOption.lines === LinesType.Reverse);
this.contour = Contour.CreateContour(br.ContourCurve.Clone()); //计算墙(创建轮廓取出,为了得到正确的轮廓曲线(逆时针之类的))
let outerWall = new ExtudeWall(Contour.CreateContour(br.ContourCurve.Clone()).Curve, DepthType.All, br.Thickness, br.Thickness, DirectionType.Outer);
this.ParseGrooves(); let grooves = this.ParseGrooves();
for (let i = 0; i < grooves.length; i++)
//计算墙
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 s1 = this.shapes[i]; let s1 = grooves[i];
outerWall.Clip(s1, false); outerWall.ClipTo(s1, false);
s1.contour.ClipReverse(outerWall); s1.contourWall.ClipReverse(outerWall);
for (let j = i + 1; j < this.shapes.length; j++) for (let j = i + 1; j < grooves.length; j++)
{ {
let s2 = this.shapes[j]; let s2 = grooves[j];
s1.ClipWall(s2, true); s1.ClipTo(s2, true);
} }
s1.Draw(this.verticesArray, this.uvArray, this.edgeAndLidBuilder, rotateUv); s1.Draw(this.verticesArray, this.uvArray, this.edgeAndLidBuilder, rotateUv);
} }
outerWall.Draw(this.verticesArray, this.uvArray, this.edgeAndLidBuilder); outerWall.Draw(this.verticesArray, this.uvArray, this.edgeAndLidBuilder);
//这里构建盖子
this.edgeAndLidBuilder.BuildLid(this.verticesArray, this.uvArray, rotateUv); this.edgeAndLidBuilder.BuildLid(this.verticesArray, this.uvArray, rotateUv);
intCache.clear(); intCache.clear();
@ -1193,53 +1181,57 @@ export class ExtrudeGeometryBuilder
get EdgeGeometry() get EdgeGeometry()
{ {
let geo = new BufferGeometry(); let geo = new BufferGeometry();
geo.setAttribute('position', new Float32BufferAttribute(this.edgeAndLidBuilder.verticesArray, 3)); geo.setAttribute('position', new Float32BufferAttribute(this.edgeAndLidBuilder.lineVerticesArray, 3));
return geo; return geo;
} }
private ParseGrooves() private ParseGrooves()
{ {
let br = this.br; 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; let type: DepthType;
if (equaln(g.Thickness, br.Thickness)) if (equaln(groove.Thickness, br.Thickness))
type = DepthType.All; type = DepthType.All;
else else
{ {
if (equaln(g.Position.applyMatrix4(this.brOcsInv).z, 0)) if (equaln(groove.Position.applyMatrix4(brOcsInv).z, 0))
type = DepthType.Back; type = DepthType.Back;
else else
type = DepthType.Front; type = DepthType.Front;
} }
this.alMatrix4.multiplyMatrices(this.brOcsInv, g.OCSNoClone); alMatrix4.multiplyMatrices(brOcsInv, groove.OCSNoClone);
//槽轮廓 //槽轮廓
let gContour = g.ContourCurve.Clone(); let grooveContourCurve = groove.ContourCurve.Clone();
gContour.ApplyMatrix(this.alMatrix4); grooveContourCurve.ApplyMatrix(alMatrix4);
gContour.Z0(); grooveContourCurve.Z0();
if (gContour instanceof Polyline) gContour.UpdateMatrixTo(IdentityMtx4);//不可能改变这个 if (grooveContourCurve instanceof Polyline) grooveContourCurve.UpdateMatrixTo(IdentityMtx4);//不可能改变这个
let contour = Contour.CreateContour(gContour); 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(); let grooveChildContourCurve = grooveChild.ContourCurve.Clone();
this.alMatrix4.multiplyMatrices(this.brOcsInv, gg.OCSNoClone); alMatrix4.multiplyMatrices(brOcsInv, grooveChild.OCSNoClone);
c.ApplyMatrix(this.alMatrix4).Z0(); grooveChildContourCurve.ApplyMatrix(alMatrix4).Z0();
if (c instanceof Polyline) c.UpdateMatrixTo(IdentityMtx4); if (grooveChildContourCurve instanceof Polyline) grooveChildContourCurve.UpdateMatrixTo(IdentityMtx4);
let contour = Contour.CreateContour(c); let grooveChildContour = Contour.CreateContour(grooveChildContourCurve);
holes.push(contour); 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<Curve, Map<Curve, IntersectResult[]>>(); let intCache = new Map<Curve, Map<Curve, IntersectResult[]>>();
function GetIntersection(cu1: Curve, cu2: Curve) function GetIntersection(cu1: Curve, cu2: Curve): IntersectResult[]
{ {
let m = intCache.get(cu1); let m = intCache.get(cu1);
if (m) if (m)

@ -5,33 +5,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" />
<script> <script></script>
if (location.hostname !== "localhost")
fetch("ver.json").then(res => {
if (!res.ok) return;
res.json().then(v => {
if (res.status)
if (v)
{
let oldVer = sessionStorage.getItem("ver");
sessionStorage.setItem("ver", v);
if (oldVer && oldVer != v)
{
document.body.style.cssText = `
text-align: center;
display: flex;
align-items: center;
justify-content: center;
font-size: 30px;
flex-direction: column;
`;
document.body.textContent = "发现新版本,升级中!";
location.reload();
}
}
});
});
</script>
<title>WebCAD</title> <title>WebCAD</title>
<meta itemprop="description" content="云端CAD,在线衣柜设计" /> <meta itemprop="description" content="云端CAD,在线衣柜设计" />
</head> </head>

Loading…
Cancel
Save