diff --git a/__test__/FeedingToolPath/FeedingToolPath.test.ts b/__test__/FeedingToolPath/FeedingToolPath.test.ts index dabcd8a8e..a18dfea48 100644 --- a/__test__/FeedingToolPath/FeedingToolPath.test.ts +++ b/__test__/FeedingToolPath/FeedingToolPath.test.ts @@ -9,7 +9,7 @@ function testPathCount(br: Board, count?: number) { holeCount += model.shape.Holes.length; } - let cus = feedUtil.CalcPath(br); + let cus = feedUtil.TestCalcPath(br); if (count !== undefined) expect(cus.length).toBe(count); else diff --git a/src/Add-on/BoardFindModify.ts b/src/Add-on/BoardFindModify.ts index 1f905303f..db7ec4b88 100644 --- a/src/Add-on/BoardFindModify.ts +++ b/src/Add-on/BoardFindModify.ts @@ -92,7 +92,9 @@ export class BoardFindModify implements Command { if (en instanceof GangDrill) { - return (en.Type === GangDrillType.Drill && option.condition.useDrill) + return ((en.Type === GangDrillType.Pxl + || en.Type === GangDrillType.Ymj + || en.Type === GangDrillType.Ljg) && option.condition.useDrill) || (en.Type === GangDrillType.Nail && option.condition.useNail); } else if (en instanceof Board) diff --git a/src/Add-on/CommandFeeding.ts b/src/Add-on/CommandFeeding.ts index 7467ff4d1..eb8c709b6 100644 --- a/src/Add-on/CommandFeeding.ts +++ b/src/Add-on/CommandFeeding.ts @@ -27,7 +27,7 @@ export class FeedingCommand implements Command { let feedingTool = FeedingToolPath.GetInstance() as FeedingToolPath; - let retCus = brs.map(br => feedingTool.CalcPath(br)); + let retCus = brs.map(br => feedingTool.TestCalcPath(br)); let ptRes = await app.m_Editor.GetPoint({ Msg: "点取位置" }); diff --git a/src/Add-on/DrawBoard/DrawLayerBoard.ts b/src/Add-on/DrawBoard/DrawLayerBoard.ts index 9643f8ee3..5943daefd 100644 --- a/src/Add-on/DrawBoard/DrawLayerBoard.ts +++ b/src/Add-on/DrawBoard/DrawLayerBoard.ts @@ -47,10 +47,7 @@ export class DrawLayerBoard extends DrawBoardTool let type = opt.boardRelative; let spaceSize = opt.spaceSize; let frontShrink = opt.frontShrink; - if (opt.isTotalLength) - { - width -= frontShrink; - } + width -= frontShrink; let leftShrink = opt.leftShrink; let rightShrink = opt.rightShrink; let thickness = opt.thickness; @@ -115,8 +112,7 @@ export class DrawLayerBoard extends DrawBoardTool if (this.leftBoard) { - let lNail = GangDrill.CreateCylDrill(rad, nailOpt.length); - lNail.Type = GangDrillType.Nail; + let lNail = GangDrill.CreateCylDrill(rad, nailOpt.length, GangDrillType.Nail); lNail.ApplyMatrix(new Matrix4().makeRotationX(Math.PI / 2)) .ApplyMatrix(MoveMatrix(new Vector3(frontDist, br.Height + depth + leftShrink, -rad))); initNails.push({ @@ -127,8 +123,7 @@ export class DrawLayerBoard extends DrawBoardTool if (this.rightBoard) { - let rNail = GangDrill.CreateCylDrill(rad, nailOpt.length); - rNail.Type = GangDrillType.Nail; + let rNail = GangDrill.CreateCylDrill(rad, nailOpt.length, GangDrillType.Nail); rNail.ApplyMatrix(new Matrix4().makeRotationX(-Math.PI / 2)) .ApplyMatrix(MoveMatrix(new Vector3(frontDist, -depth - rightShrink, -rad))); initNails.push({ @@ -140,10 +135,11 @@ export class DrawLayerBoard extends DrawBoardTool if (this.backBoard) { - let bNail = GangDrill.CreateCylDrill(rad, nailOpt.length); - bNail.Type = GangDrillType.Nail; + let backShrink = (this.space.Size.y - layerOpt.frontShrink) - br.Width; + + let bNail = GangDrill.CreateCylDrill(rad, nailOpt.length, GangDrillType.Nail); bNail.ApplyMatrix(new Matrix4().makeRotationY(-Math.PI / 2)) - .ApplyMatrix(MoveMatrix(new Vector3(br.Width + depth, frontDist, -rad))); + .ApplyMatrix(MoveMatrix(new Vector3(br.Width + depth + backShrink, frontDist, -rad))); initNails.push({ pos: NailPos.Back, nail: bNail diff --git a/src/Add-on/DrawDrilling/DrawDrillingTool.ts b/src/Add-on/DrawDrilling/DrawDrillingTool.ts index 9618a651e..02c87e1ea 100644 --- a/src/Add-on/DrawDrilling/DrawDrillingTool.ts +++ b/src/Add-on/DrawDrilling/DrawDrillingTool.ts @@ -3,7 +3,7 @@ import { app } from "../../ApplicationServices/Application"; import { arrayLast } from "../../Common/ArrayExt"; import { Singleton } from "../../Common/Singleton"; import { operationExpReg } from "../../Common/Utils"; -import { GangDrill } from "../../DatabaseServices/3DSolid/GangDrill"; +import { GangDrill, GangDrillType } from "../../DatabaseServices/3DSolid/GangDrill"; import { Board } from "../../DatabaseServices/Board"; import { Circle } from "../../DatabaseServices/Circle"; import { ObjectId } from "../../DatabaseServices/ObjectId"; @@ -54,19 +54,19 @@ export class DrawDrillingTool extends Singleton let ljgRad = this.m_Option.ljgRad; let ljgLength = this.m_Option.ljgLength; - let pxlEnt = GangDrill.CreateCylDrill(this.m_Option.pxlRad, pxlDepth); + let pxlEnt = GangDrill.CreateCylDrill(this.m_Option.pxlRad, pxlDepth, GangDrillType.Pxl); this.drillEnts.push(pxlEnt); //将三个实体移动到相应的位置 pxlEnt.ApplyMatrix(new Matrix4().makeRotationX(Math.PI / 2)); - let ljgEnt = GangDrill.CreateCylDrill(ljgRad, ljgLength); + let ljgEnt = GangDrill.CreateCylDrill(ljgRad, ljgLength, GangDrillType.Ljg); this.drillEnts.push(ljgEnt); //如果都是侧面,不要预埋件 if (!this.m_Face.isEqualType) { - let ymjEnt = GangDrill.CreateCylDrill(this.m_Option.ymjRad, this.m_Option.ymjDepth); + let ymjEnt = GangDrill.CreateCylDrill(this.m_Option.ymjRad, this.m_Option.ymjDepth, GangDrillType.Ymj); ymjEnt.ApplyMatrix(new Matrix4().setPosition(new Vector3(0, 0, ljgLength))); this.drillEnts.push(ymjEnt); } diff --git a/src/Add-on/TestFb.ts b/src/Add-on/TestFb.ts index 08ef2b7b8..a2abf052c 100644 --- a/src/Add-on/TestFb.ts +++ b/src/Add-on/TestFb.ts @@ -1,12 +1,10 @@ import { Vector3 } from "three"; import { app } from "../ApplicationServices/Application"; -import { EBoardKeyList } from "../Common/BoardKeyList"; import { Board } from "../DatabaseServices/Board"; import { Curve } from "../DatabaseServices/Curve"; -import { Polyline } from "../DatabaseServices/Polyline"; import { Command } from "../Editor/CommandMachine"; import { PromptStatus } from "../Editor/PromptResult"; -import { calcEdgeSealing, paragraphCulist } from "../GraphicsSystem/CalcEdgeSealing"; +import { getSealedBoardContour } from "../GraphicsSystem/CalcEdgeSealing"; export class TestFb implements Command { @@ -14,10 +12,9 @@ export class TestFb implements Command { let brRes = await app.m_Editor.GetSelection({ Msg: "选择板件", - Filter: { - filterTypes: [Board] - } - }) + UseSelect: true, + Filter: { filterTypes: [Board] } + }); if (brRes.Status === PromptStatus.Cancel) return; @@ -27,46 +24,20 @@ export class TestFb implements Command for (let br of brs) { - let offsetCus: Curve[] = []; - let highSeals = br.BoardProcessOption.highSealed; - let cus = br.ContourCurve.Explode() as Curve[]; - if (!br.IsSpecialShape) + let pl = getSealedBoardContour(br); + if (pl) { - highSeals.length = 0; - let sealDown = parseFloat(br.BoardProcessOption[EBoardKeyList.DownSealed]); - let sealUp = parseFloat(br.BoardProcessOption[EBoardKeyList.UpSealed]); - let sealLeft = parseFloat(br.BoardProcessOption[EBoardKeyList.LeftSealed]); - let sealRight = parseFloat(br.BoardProcessOption[EBoardKeyList.RightSealed]); - let seals = [...new Set([sealDown, sealRight, sealUp, sealLeft])]; - [sealDown, sealRight, sealUp, sealLeft].forEach(size => highSeals.push({ size, color: seals.indexOf(size) + 1 })); + pl.ColorIndex = 1; + showCus.push(br.ContourCurve.Clone(), pl); } else - paragraphCulist(cus); - - if (highSeals.length > 0 && cus.length === highSeals.length) { - for (let i = 0; i < cus.length; i++) - { - let cs = cus[i].GetOffsetCurves(-highSeals[i].size); - for (let c of cs) - { - if (c instanceof Polyline) - offsetCus.push(...c.Explode()); - else - offsetCus.push(c); - } - } - calcEdgeSealing(offsetCus); - let pl = new Polyline(); - pl.ColorIndex = 1; - for (let c of offsetCus) - { - pl.Join(c); - } - showCus.push(br.ContourCurve.Clone(), pl); + app.m_Editor.Prompt("警告:该板件无法得到模拟封边结果!"); } } + if (showCus.length === 0) + return; let ptRes = await app.m_Editor.GetPoint({ Msg: "选择放置位置" diff --git a/src/Add-on/testEntity/test.ts b/src/Add-on/testEntity/test.ts index 1ea3ae46c..62cc8c9d5 100644 --- a/src/Add-on/testEntity/test.ts +++ b/src/Add-on/testEntity/test.ts @@ -1,10 +1,22 @@ import { HotCMD } from "../../Hot/HotCommand"; import { Command } from "../../Editor/CommandMachine"; +import { app } from "../../ApplicationServices/Application"; +import { PromptStatus } from "../../Editor/PromptResult"; +import { Board } from "../../DatabaseServices/Board"; +import { Production } from "../../Production/Product"; @HotCMD export class Test implements Command { async exec() { + let enRes = await app.m_Editor.GetEntity(); + + if (enRes.Status === PromptStatus.OK) + { + let en = enRes.Entity as Board; + + console.log(Production.GetBoardSplitOrderData(en)); + } } } diff --git a/src/Common/CurveUtils.ts b/src/Common/CurveUtils.ts index 327d5c31e..7ffb15fd5 100644 --- a/src/Common/CurveUtils.ts +++ b/src/Common/CurveUtils.ts @@ -1,19 +1,19 @@ -import { Matrix3, Vector2, Vector3, Line3 } from 'three'; +import { Box3, Line3, Matrix3, Matrix4, Vector2, Vector3 } from 'three'; import { Arc } from '../DatabaseServices/Arc'; import { Circle } from '../DatabaseServices/Circle'; import { Curve } from '../DatabaseServices/Curve'; +import { Ellipse } from '../DatabaseServices/Ellipse'; import { Line } from '../DatabaseServices/Line'; import { IsPointInBowArc } from '../DatabaseServices/PointInPolyline'; import { Polyline } from '../DatabaseServices/Polyline'; import { Count } from '../Geometry/Count'; import { CurveMap } from '../Geometry/CurveMap'; -import { equaln, equalv2, equalv3, isParallelTo } from '../Geometry/GeUtils'; -import { Stand } from '../Geometry/RegionParse'; -import { FixIndex } from './Utils'; +import { cZeroVec, equaln, equalv2, equalv3, isParallelTo } from '../Geometry/GeUtils'; import { PlaneExt } from '../Geometry/Plane'; +import { Stand } from '../Geometry/RegionParse'; import { IntersectOption } from '../GraphicsSystem/IntersectWith'; import { arrayLast, changeArrayStartIndex, equalArray } from './ArrayExt'; -import { Ellipse } from '../DatabaseServices/Ellipse'; +import { FixIndex } from './Utils'; //3点获取圆心 export function getCircleCenter(pt1: Vector3, pt2: Vector3, pt3: Vector3) @@ -364,47 +364,6 @@ function PointInPolylineArc(pl: Polyline, index: number, pt: Vector3): number return 0; } -/** - * 对多段线的圆弧进行打断,并且返回新的圆弧. - * 打断的依据是,圆弧的凸度不能大于0.5 - * - * @export - * @param {Polyline} pl - * @returns {Polyline} 返回新的多段线 - */ -export function BreakPolylineAtArc(pl: Polyline): Polyline -{ - let { pts, buls } = pl.PtsBuls; - - let npl = new Polyline(); - - for (let i = 0; i < pts.length; i++) - { - let bul = buls[i]; - npl.AddVertexAt(npl.NumberOfVertices, pts[i]); - if (i !== pts.length - 1 && Math.abs(bul) > 0.5) - { - let divCount = Math.floor(Math.abs(bul) / 0.5) + 1; - let arc = new Arc().ParseFromBul(pts[i], pts[i + 1], bul); - - let bulDiv = Math.tan(Math.atan(bul) / divCount); - npl.SetBulgeAt(npl.NumberOfVertices - 1, bulDiv); - - for (let i = 0; i < divCount - 1; i++) - { - npl.AddVertexAt(npl.NumberOfVertices, Vec3DTo2D(arc.GetPointAtParam((i + 1) / divCount))); - npl.SetBulgeAt(npl.NumberOfVertices - 1, bulDiv); - } - } - else - { - npl.SetBulgeAt(npl.NumberOfVertices - 1, 0); - } - } - - return npl; -} - export function ConverCircleToPolyline(cir: Circle): Polyline { //该写法不支持三维坐标系 @@ -592,3 +551,49 @@ export function getTanPtsOnEllipse(cu: Ellipse, lastPoint: Vector3) { return []; } + +export function isRect(cu: Curve): { isRect: boolean, size?: Vector3, box?: Box3, OCS?: Matrix4 } +{ + if (cu instanceof Polyline) + { + if (!cu.IsClose) return { isRect: false }; + + let pts = cu.GetStretchPoints(); + + if (pts.length < 4) return { isRect: false }; + + let xVec: Vector3; + let p1 = pts[0]; + for (let i = 1; i < pts.length; i++) + { + xVec = pts[i].clone().sub(p1).normalize(); + if (!equalv3(xVec, cZeroVec)) + break; + } + + if (!xVec) return { isRect: false }; + + let zVec = cu.Normal; + let yVec = zVec.clone().cross(xVec).normalize(); + + let rectOCS = new Matrix4().makeBasis(xVec, yVec, zVec); + let rectOCSInv = new Matrix4().getInverse(rectOCS); + + for (let p of pts) + p.applyMatrix4(rectOCSInv); + + let box = new Box3().setFromPoints(pts); + + let size = box.getSize(new Vector3); + if (equaln(size.x * size.y, cu.Area, 0.1)) + { + return { + isRect: true, + size, + box, + OCS: rectOCS, + }; + } + } + return { isRect: false }; +} diff --git a/src/DatabaseServices/3DSolid/GangDrill.ts b/src/DatabaseServices/3DSolid/GangDrill.ts index cad845ba8..8060a862d 100644 --- a/src/DatabaseServices/3DSolid/GangDrill.ts +++ b/src/DatabaseServices/3DSolid/GangDrill.ts @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import { BufferGeometry, ExtrudeGeometry, Group, LineSegments, MeshNormalMaterial, Object3D, Vector3 } from "three"; +import { BufferGeometry, ExtrudeGeometry, Group, LineSegments, Object3D, Vector3 } from "three"; import { ColorMaterial } from '../../Common/ColorPalette'; import { DisposeThreeObj } from '../../Common/Dispose'; import { FastDrillingEdgeGeometry } from '../../Geometry/CreateWireframe'; @@ -13,9 +13,15 @@ import { Solid3D } from "./Solid3D"; export enum GangDrillType { - Drill = 0, - Nail = 1, - Wood = 2, + /**偏心轮 */ + Pxl = 0, + /**连接杆 */ + Ljg = 1, + /**预埋件 */ + Ymj = 2, + /**层板钉 */ + Nail = 3, + Wood = 4, } @Factory @@ -24,16 +30,17 @@ export class GangDrill extends Solid3D ColorIndex = 1; private m_Shape: Shape; private m_Height: number; - private type: GangDrillType = GangDrillType.Drill; + private type: GangDrillType = GangDrillType.Pxl; constructor() { super(); } - static CreateCylDrill(rad: number, height: number) + static CreateCylDrill(rad: number, height: number, type: GangDrillType) { let drill = new GangDrill(); drill.m_Height = height; drill.m_Shape = new Shape(Contour.CreateContour([new Circle(new Vector3(), rad)])); + drill.type = type; return drill; } get Type() @@ -127,7 +134,7 @@ export class GangDrill extends Solid3D if (ver > 1) this.type = file.Read(); else - this.type = GangDrillType.Drill; + this.type = GangDrillType.Pxl; } WriteFile(file: CADFiler) { diff --git a/src/DatabaseServices/Contour.ts b/src/DatabaseServices/Contour.ts index b50b4a475..04650b251 100644 --- a/src/DatabaseServices/Contour.ts +++ b/src/DatabaseServices/Contour.ts @@ -362,6 +362,9 @@ export class Contour arrayRemoveDuplicateBySort(gclone, (cu1: Curve, cu2: Curve) => cu1.Join(cu2) === Status.True); + if (gclone[0].Join(arrayLast(gclone))) + gclone.pop(); + for (let cu of gclone) pl.Join(cu, false, tolerance); diff --git a/src/Geometry/DrillParse/BoardGetFace.ts b/src/Geometry/DrillParse/BoardGetFace.ts index 9a33da54d..a5168ac11 100644 --- a/src/Geometry/DrillParse/BoardGetFace.ts +++ b/src/Geometry/DrillParse/BoardGetFace.ts @@ -4,7 +4,7 @@ import { matrixIsCoplane } from "../../Common/Matrix4Utils"; import { Board } from "../../DatabaseServices/Board"; import { Curve } from "../../DatabaseServices/Curve"; import { Region } from "../../DatabaseServices/Region"; -import { cZAxis, equalv3, rotatePoint } from "../GeUtils"; +import { cZAxis, equalv3, rotatePoint, equaln } from "../GeUtils"; import { Face } from "./Face"; import { DrillType } from "../../UI/Store/BoardInterface"; import { Arc } from "../../DatabaseServices/Arc"; @@ -86,7 +86,9 @@ export class BoardGetFace for (let i = 0; i < cus.length; i++) { let cu = cus[i]; - if ((highDrill.length > 0 && highDrill[i] === DrillType.None) || cu instanceof Arc) + if ((highDrill.length > 0 && highDrill[i] === DrillType.None) + || equaln(cu.Length, 0) + || cu instanceof Arc) continue; let derv = cu.GetFistDeriv(0).normalize(); let len = cu.Length; diff --git a/src/GraphicsSystem/CalcEdgeSealing.ts b/src/GraphicsSystem/CalcEdgeSealing.ts index 1a4c5e91d..783fec5a7 100644 --- a/src/GraphicsSystem/CalcEdgeSealing.ts +++ b/src/GraphicsSystem/CalcEdgeSealing.ts @@ -1,10 +1,13 @@ +import { EBoardKeyList } from "../Common/BoardKeyList"; import { FixIndex } from "../Common/Utils"; +import { Board } from "../DatabaseServices/Board"; import { Curve } from "../DatabaseServices/Curve"; import { Line } from "../DatabaseServices/Line"; import { Polyline } from "../DatabaseServices/Polyline"; import { equaln, equalv3, isParallelTo } from "../Geometry/GeUtils"; import { IntersectOption } from "./IntersectWith"; import { PolyOffsetUtil } from "./OffsetPolyline"; +import { Vector3 } from "three"; /** *曲线列表分段 @@ -124,3 +127,72 @@ export function calcEdgeSealing(cus: Curve[]) laterLine.StartPoint = iPt; } } + +export function getBoardSealingData(br: Board) +{ + let highSeals = br.BoardProcessOption.highSealed.slice(); + if (!br.IsSpecialShape) + { + highSeals.length = 0; + let sealDown = parseFloat(br.BoardProcessOption[EBoardKeyList.DownSealed]); + let sealUp = parseFloat(br.BoardProcessOption[EBoardKeyList.UpSealed]); + let sealLeft = parseFloat(br.BoardProcessOption[EBoardKeyList.LeftSealed]); + let sealRight = parseFloat(br.BoardProcessOption[EBoardKeyList.RightSealed]); + let seals = [...new Set([sealDown, sealRight, sealUp, sealLeft])]; //color看上去毫无意义 + [sealDown, sealRight, sealUp, sealLeft].forEach(size => highSeals.push({ size, color: seals.indexOf(size) + 1 })); + } + return highSeals; +} + +/**获取板件封边轮廓曲线 */ +export function getBoardSealingCurves(br: Board) +{ + let cus: Curve[] = []; + if (br.IsSpecialShape) + { + cus = br.ContourCurve.Explode() as Curve[]; + paragraphCulist(br.ContourCurve.Explode() as Curve[]); + } + else + { + let cu = br.ContourCurve as Polyline; + let index = cu.GetParamAtPoint(new Vector3()); + while (cus.length < 4) + { + cus.push(cu.GetCurveAtIndex(index)); + index = FixIndex(++index, cu.EndParam); + } + } + return cus; +} + +export function getSealedBoardContour(br: Board) +{ + let offsetCus: Curve[] = []; + let highSeals = getBoardSealingData(br); + let cus = getBoardSealingCurves(br); + + let dir = Math.sign(br.ContourCurve.Area2); + + if (cus.length !== highSeals.length) + { + console.warn("请设置高级封边"); + return; + } + for (let i = 0; i < cus.length; i++) + { + let cs = cus[i].GetOffsetCurves(-highSeals[i].size * dir); + for (let c of cs) + { + if (c instanceof Polyline) + offsetCus.push(...c.Explode()); + else + offsetCus.push(c); + } + } + calcEdgeSealing(offsetCus); + let pl = new Polyline(); + for (let c of offsetCus) + pl.Join(c); + return pl; +} diff --git a/src/GraphicsSystem/ToolPath/FeedingToolPath.ts b/src/GraphicsSystem/ToolPath/FeedingToolPath.ts index eef969528..5624cfd62 100644 --- a/src/GraphicsSystem/ToolPath/FeedingToolPath.ts +++ b/src/GraphicsSystem/ToolPath/FeedingToolPath.ts @@ -1,5 +1,5 @@ import { Singleton } from "../../Common/Singleton"; -import { Board } from "../../DatabaseServices/Board"; +import { Board, IModeling } from "../../DatabaseServices/Board"; import { Circle } from "../../DatabaseServices/Circle"; import { Contour } from "../../DatabaseServices/Contour"; import { Curve } from "../../DatabaseServices/Curve"; @@ -8,7 +8,8 @@ import { Shape } from "../../DatabaseServices/Shape"; import { ShapeManager } from "../../DatabaseServices/ShapeManager"; import { BoolOpeartionType } from "../BoolOperateUtils"; import { OptimizeToolPath } from "./OptimizeToolPath"; -import { equalCurve } from "../../Common/CurveUtils"; +import { equalCurve, isRect } from "../../Common/CurveUtils"; +import { Vector3 } from "three"; /** *计算走刀工具类 @@ -28,7 +29,6 @@ export class FeedingToolPath extends Singleton let dir = this.GetCurveToInDir(outline); let offsetCus: Curve[] = [outline]; - //获得形状外孔轮廓 let holes = shape.Holes.map(h => { @@ -130,39 +130,105 @@ export class FeedingToolPath extends Singleton dir = -1; return dir; } - + /**用于测试走刀路径 */ + TestCalcPath(br: Board) + { + let cus = this.CalcPath(br); + //加入板件轮廓 + cus.push(br.ContourCurve.Clone()); + if (cus.length === 1) + return cus; + //加入造型外轮廓和洞 + for (let { shape, thickness } of br.BoardModeling) + { + let outline = shape.Outline.Curve.Clone(); + outline.Position = outline.Position.setZ(0); + outline.ColorIndex++; + cus.push(outline); + if (thickness < br.Thickness) + { + cus.push(...shape.Holes.map(h => + { + let c = h.Curve.Clone(); + c.Position = c.Position.setZ(0); + c.ColorIndex += 1; + return c; + })); + } + } + return cus; + } /** - * 开始计算走刀路径 + * 计算走刀路径 * TODO:圆孔半径===刀半径时推送ys 显示点 */ CalcPath(br: Board): Curve[] { - let cus: Curve[] = [br.ContourCurve.Clone()]; + let cus: Curve[] = []; let modelings = br.BoardModeling; - for (let { shape, thickness, knifeRadius } of modelings) + for (let m of modelings) { - if (!knifeRadius) knifeRadius = 3; - if (thickness >= br.Thickness) - { - //通孔只切一刀 - let outline = shape.Outline.Curve; - let dir = this.GetCurveToInDir(outline); - let paths: Curve[]; - if (outline instanceof Circle) - paths = outline.GetOffsetCurves(dir * knifeRadius); - else - paths = outline.GetFeedingToolPath(dir * knifeRadius); - paths.forEach(p => p.ColorIndex = outline.ColorIndex); - outline.ColorIndex++; - cus.push(outline, ...paths); - } + cus.push(...this.GetModelFeedPath(br.Thickness, m)); + } + return cus; + } + GetModelFeedPath(brThickness: number, m: IModeling): Curve[] + { + let cus: Curve[] = []; + let { shape, thickness, knifeRadius, addLen } = m; + if (!knifeRadius) knifeRadius = 3; + + shape = shape.Clone(); + + this.GrooveAddLen(shape, addLen); + + if (thickness >= brThickness) + { + //通孔只切一刀 + let outline = shape.Outline.Curve; + let dir = this.GetCurveToInDir(outline); + let paths: Curve[]; + if (outline instanceof Circle) + paths = outline.GetOffsetCurves(dir * knifeRadius); else + paths = outline.GetFeedingToolPath(dir * knifeRadius); + paths.forEach(p => p.ColorIndex = outline.ColorIndex); + outline.ColorIndex++; + cus.push(...paths); + } + else + { + let offsetCus = this.HandleShape(shape, knifeRadius); + if (offsetCus.length > 1) + cus.push(...OptimizeToolPath(offsetCus, shape, knifeRadius)); + } + return cus; + } + private GrooveAddLen(shape: Shape, addLen: number) + { + shape.Outline.Curve.Position = shape.Outline.Curve.Position.setZ(0); + //若是矩形,应用槽加长 + if (addLen > 0) + { + let curveData = isRect(shape.Outline.Curve); + if (curveData.isRect) { - let offsetCus = this.HandleShape(shape, knifeRadius); - if (offsetCus.length > 1) - cus.push(...OptimizeToolPath(offsetCus, shape, knifeRadius)); + let box = curveData.box; + let size = curveData.size; + if (size.x > size.y) + { + box.max.add(new Vector3(addLen / 2)); + box.min.add(new Vector3(-addLen / 2)); + } + else + { + box.max.add(new Vector3(0, addLen / 2)); + box.min.add(new Vector3(0, -addLen / 2)); + } + let pl = new Polyline().Create2Pt(box.min, box.max).ApplyMatrix(curveData.OCS); + pl.ColorIndex = shape.Outline.Curve.ColorIndex; + shape.Outline = Contour.CreateContour(pl); } } - return cus; } } diff --git a/src/GraphicsSystem/ToolPath/OptimizeToolPath.ts b/src/GraphicsSystem/ToolPath/OptimizeToolPath.ts index b07297b9c..da3cd18c7 100644 --- a/src/GraphicsSystem/ToolPath/OptimizeToolPath.ts +++ b/src/GraphicsSystem/ToolPath/OptimizeToolPath.ts @@ -15,18 +15,10 @@ import { arrayLast } from "../../Common/ArrayExt"; * @param rad 刀半径 * @returns tool path */ -export function OptimizeToolPath(offsetCus: Curve[], originShape: Shape, rad: number): Curve[] +export function OptimizeToolPath(offsetCus: Curve[], originShape: Shape, rad: number, isTest = false): Curve[] { // 去掉最外轮廓 let outline = offsetCus.shift(); - outline.ColorIndex += 1; - //洞轮廓 - let holesCurve = originShape.Holes.map(h => - { - let c = h.Curve.Clone(); - c.ColorIndex += 1; - return c; - }); let plList: Polyline[] = []; let noCloseCus: Curve[] = []; @@ -119,7 +111,7 @@ export function OptimizeToolPath(offsetCus: Curve[], originShape: Shape, rad: nu } } } - result.push(firstPl, outline, ...holesCurve); + result.push(firstPl); return result; } diff --git a/src/Production/Product.ts b/src/Production/Product.ts new file mode 100644 index 000000000..e97901b0e --- /dev/null +++ b/src/Production/Product.ts @@ -0,0 +1,317 @@ +import { Matrix4, Vector2, Vector3 } from "three"; +import { EBoardKeyList } from "../Common/BoardKeyList"; +import { Vec3DTo2D } from "../Common/CurveUtils"; +import { Vector2ApplyMatrix4 } from "../Common/Matrix4Utils"; +import { GangDrill, GangDrillType } from "../DatabaseServices/3DSolid/GangDrill"; +import { Arc } from "../DatabaseServices/Arc"; +import { Board } from "../DatabaseServices/Board"; +import { Circle } from "../DatabaseServices/Circle"; +import { Polyline } from "../DatabaseServices/Polyline"; +import { Vec2 } from "../Geometry/CheckIntersect"; +import { equaln, equalv3, isParallelTo } from "../Geometry/GeUtils"; +import { getBoardSealingCurves, getBoardSealingData as getBoardHighSeal, getSealedBoardContour } from "../GraphicsSystem/CalcEdgeSealing"; +import { FeedingToolPath } from "../GraphicsSystem/ToolPath/FeedingToolPath"; +import { FaceDirection, IHighSealedItem } from "../UI/Store/BoardInterface"; + +/**板件轮廓数据 */ +export interface IContourData +{ + pts: Vec2[]; + buls: number[]; +} + +interface ISealingData extends IHighSealedItem +{ + length: number; + type?: string; + shop?: string; +} +interface IModelingData +{ + curve: IContourData; + feeding: IContourData[]; + thickness: number; + dir: FaceDirection; + knifeRadius: number; + addLen: number; + +} + +interface IDrillingOption +{ + position: Vector3; + rad: number; + type: GangDrillType; + depth: number; + face: number; +} + +interface IBoardHoleInfo +{ + frontBackHoles: IDrillingOption[]; + sideHoles: IDrillingOption[]; +} + +interface IBoardProdInfo +{ + id: number; + name: string; + [EBoardKeyList.RoomName]: string; + [EBoardKeyList.CabinetName]: string; + [EBoardKeyList.Mat]: string; + [EBoardKeyList.BrMat]: string; + [EBoardKeyList.Color]: string; + [EBoardKeyList.Lines]: number; + [EBoardKeyList.DrillType]: string; + spliteHeight: string; + spliteThickness: string; + spliteWidth: string; + + isRect: boolean; + reamarks: any[]; +} + +/**拆单数据 */ +interface ISpliteOrderData +{ + info: IBoardProdInfo; //板件基本信息 + outline: IContourData; //轮廓信息 + sealing: ISealingData[]; //封边信息 + modeling: IModelingData[]; //造型信息 + holes: IBoardHoleInfo; //孔信息 + sideModeling: IModelingData[]; //侧面造型信息 +} + +export namespace Production +{ + /**获取板件拆单数据 */ + export function GetBoardSplitOrderData(br: Board): ISpliteOrderData + { + return { + info: GetBoardInfo(br), + outline: getBoardContour(br), + sealing: GetBoardSealingData(br), + modeling: GetBoardModelingData(br), + holes: GetBoardHolesData(br), + sideModeling: [], + } + } + export function GetBoardInfo(br: Board): IBoardProdInfo + { + let data = br.BoardProcessOption; + return { + id: br.Id.Index, + name: br.Name, + [EBoardKeyList.RoomName]: data[EBoardKeyList.RoomName], + [EBoardKeyList.CabinetName]: data[EBoardKeyList.CabinetName], + [EBoardKeyList.Mat]: data[EBoardKeyList.Mat], + [EBoardKeyList.BrMat]: data[EBoardKeyList.BrMat], + [EBoardKeyList.Color]: data[EBoardKeyList.Color], + [EBoardKeyList.Lines]: data[EBoardKeyList.Lines], + [EBoardKeyList.DrillType]: data[EBoardKeyList.DrillType], + spliteHeight: data.spliteHeight, + spliteThickness: data.spliteThickness, + spliteWidth: data.spliteWidth, + isRect: !br.IsSpecialShape, + reamarks: [], + }; + } + + export function getBoardContour(br: Board): IContourData + { + let curve = getSealedBoardContour(br); + return curve ? ConverToPolylineAndSplitArc(curve) : null; + } + + /** + * 转换成多段线并且将圆弧打断(大于1/4的话) + */ + function ConverToPolylineAndSplitArc(cu: Polyline | Circle): IContourData + { + let ptsBuls: { pts: Vector2[]; buls: number[]; }; + if (cu instanceof Circle) + { + let pl = ConverCircleToPolyline(cu); + ptsBuls = pl.PtsBuls; + } + else + { + ptsBuls = SplitePolylineAtArc(cu); + let ocs = cu.OCS; + if (!equaln(ocs.elements[0], 1) + || !equaln(ocs.elements[9], 0) + || !equaln(ocs.elements[10], 0) + ) + { + for (let p of ptsBuls.pts) + Vector2ApplyMatrix4(ocs, p); + } + } + return ptsBuls; + } + + export function ConverCircleToPolyline(cir: Circle): Polyline + { + let arcs = cir.GetSplitCurves([0, 0.25, 0.5, 0.75]); + let pl = new Polyline(); + for (let arc of arcs) + pl.Join(arc); + return pl; + } + + const SPLITBUL = Math.tan(Math.PI / 8); + + /** 打断多段线超过1/4圆的圆弧*/ + export function SplitePolylineAtArc(cu: Polyline): { pts: Vector2[], buls: number[] } + { + let ptsBuls = cu.PtsBuls; + let ocsInv = cu.OCSInv; + + let result: { pts: Vector2[], buls: number[] } = { pts: [], buls: [] }; + + for (let i = 0; i < ptsBuls.buls.length; i++) + { + let bul = ptsBuls.buls[i]; + if (Math.abs(bul) > SPLITBUL) + { + let allAngle = Math.atan(bul) * 4; + let splitCount = Math.floor(allAngle / (Math.PI / 2)) + 1; + let arc = cu.GetCurveAtIndex(i) as Arc; + let paramDiv = 1 / splitCount; + let newBul = Math.tan((allAngle / splitCount) / 4); + for (let i = 0; i < splitCount; i++) + { + let param = i * paramDiv; + let p = arc.GetPointAtParam(param).applyMatrix4(ocsInv); + result.pts.push(Vec3DTo2D(p)); + result.buls.push(newBul); + } + } + else + { + result.pts.push(ptsBuls.pts[i]); + result.buls.push(ptsBuls.buls[i]); + } + } + return result; + } + + /**获取封边数据 */ + export function GetBoardSealingData(br: Board): ISealingData[] + { + let highSeal = getBoardHighSeal(br); + let cus = getBoardSealingCurves(br); + if (highSeal.length === cus.length) + return highSeal.map((data, i) => Object.assign(data, { length: cus[i].Length })); + return []; + } + + export function GetBoardModelingData(br: Board): IModelingData[] + { + const tool = FeedingToolPath.GetInstance() as FeedingToolPath; + const thickness = br.Thickness; + let data: IModelingData[] = br.BoardModeling.map(m => + { + return { + curve: ConverToPolylineAndSplitArc(m.shape.Outline.Curve), + feeding: tool.GetModelFeedPath(thickness, m).map(ConverToPolylineAndSplitArc), + thickness: m.thickness, + dir: m.dir, + knifeRadius: m.knifeRadius, + addLen: m.knifeRadius, + }; + }); + return data; + } + + export function GetBoardHolesData(br: Board): IBoardHoleInfo + { + let drillList = br.DrillList; + let processData = br.BoardProcessOption; + let data: IBoardHoleInfo = { + frontBackHoles: [], + sideHoles: [] + }; + let brNormal = br.Normal; + let outline = br.ContourCurve; + + let sealedOutlin = getSealedBoardContour(br); + let moveVec = sealedOutlin ? sealedOutlin.BoundingBox.min.negate() : new Vector3(); + + let roMat = new Matrix4().extractRotation(br.OCSInv); + for (let [, driss] of drillList) + { + for (let dris of driss) + { + for (let dId of dris) + { + if (!dId || dId.IsErase) + continue; + let d = dId.Object as GangDrill; + let position = d.Position.applyMatrix4(br.OCSInv); + let holes = data.frontBackHoles; + let face: number; + let isPush = false; + if (d.Type === GangDrillType.Pxl) + { + if (isParallelTo(d.Normal, brNormal)) + { + face = processData[EBoardKeyList.BigHole]; + isPush = true; + } + } + else if (d.Type === GangDrillType.Ljg) + { + if (!isParallelTo(d.Normal, brNormal)) + { + position.add(d.Normal.multiplyScalar(d.Height).applyMatrix4(roMat)); + holes = data.sideHoles; + face = Math.floor(outline.GetParamAtPoint(position.clone().setZ(0))); + isPush = true; + } + } + else + { + if (isParallelTo(d.Normal, br.Normal)) + { + holes = data.frontBackHoles; + face = !equalv3(d.Normal, brNormal) ? 0 : 1; + isPush = true; + } + } + isPush && holes.push({ + type: d.Type, + position: position.add(moveVec), + rad: d.Radian, + depth: d.Height, + face, + }); + } + } + } + + for (let nid of br.LayerNails) + { + if (!nid || !nid.Object || nid.IsErase) + continue; + let nail = nid.Object as GangDrill; + if (isParallelTo(nail.Normal, brNormal)) + { + let nailPosition = nail.Position.applyMatrix4(br.OCSInv); + let face = equalv3(nail.Normal, brNormal) ? 0 : 1; + let depth = face === 1 ? nailPosition.z : br.Thickness - nailPosition.z; + + data.frontBackHoles.push({ + type: nail.Type, + position: nailPosition.setZ(0), + rad: nail.Radian, + depth, + face, + }) + } + } + + return data; + } + +} diff --git a/src/UI/Components/Board/BoardConfigModal.tsx b/src/UI/Components/Board/BoardConfigModal.tsx index 18c5ff686..2face3045 100644 --- a/src/UI/Components/Board/BoardConfigModal.tsx +++ b/src/UI/Components/Board/BoardConfigModal.tsx @@ -135,7 +135,8 @@ export class BoardConfigModal extends React.Component{ let groove = new ExtureSolid(); groove.Thickness = cd.height; groove.ContourCurve = shape.Outline.Curve; - + groove.KnifeRadius = cd.knifeRad; + groove.GroovesAddLength = cd.addLen; for (let hole of shape.Holes) { let holeSolid = new ExtureSolid(); @@ -195,18 +196,18 @@ export class BoardConfigModal extends React.Component{ store.InitModelingItems(); let colorIndex = 1; - let thickMap = new Map(); + let dataMap = new Map(); for (let data of br.BoardModeling) { - let thickStr = data.thickness.toString(3); + let str = [data.thickness, data.dir, data.knifeRadius, data.addLen].join("-"); let color: number; - if (thickMap.has(thickStr)) - color = thickMap.get(thickStr); + if (dataMap.has(str)) + color = dataMap.get(str); else { color = colorIndex; - thickMap.set(thickStr, color); + dataMap.set(str, color); colorIndex++; store.ChangeModelingValue(color - 1, data); diff --git a/src/UI/Components/Board/LayerBoardModal.tsx b/src/UI/Components/Board/LayerBoardModal.tsx index 109cb48d5..8f63bde6b 100644 --- a/src/UI/Components/Board/LayerBoardModal.tsx +++ b/src/UI/Components/Board/LayerBoardModal.tsx @@ -39,6 +39,9 @@ export const LayerBoardModal = uiOption['rightShrink'] = "1"; brOpt.leftShrink = 1; brOpt.rightShrink = 1; + brOpt.isTotalLength = false; + uiOption['calcHeight'] = "L-1"; + brOpt.calcHeight = "L-1"; } else { diff --git a/src/UI/Components/RightPanel/ModelingComponent.tsx b/src/UI/Components/RightPanel/ModelingComponent.tsx index deb163117..6f039f84a 100644 --- a/src/UI/Components/RightPanel/ModelingComponent.tsx +++ b/src/UI/Components/RightPanel/ModelingComponent.tsx @@ -12,8 +12,8 @@ export interface IModelingItem readonly color: number; height: number; dir: FaceDirection; - rad: number; - length: number; + knifeRad: number; + addLen: number; } @inject("store") diff --git a/src/UI/Store/BoardInterface.ts b/src/UI/Store/BoardInterface.ts index dca575332..7438d51f8 100644 --- a/src/UI/Store/BoardInterface.ts +++ b/src/UI/Store/BoardInterface.ts @@ -85,7 +85,7 @@ export interface BoardProcessOption extends BoardOption [EBoardKeyList.BigHole]?: FaceDirection;//大孔面 [EBoardKeyList.DrillType]?: string;//排钻类型 [EBoardKeyList.ComposingFace]?: ComposingType;//排版面 - [EBoardKeyList.HighSealed]?: IHighSealedItem[]; + [EBoardKeyList.HighSealed]?: IHighSealedItem[];//封边数据 [EBoardKeyList.UpSealed]?: string;//封边上下左右 [EBoardKeyList.DownSealed]?: string; [EBoardKeyList.LeftSealed]?: string; diff --git a/src/UI/Store/RightPanelStore.ts b/src/UI/Store/RightPanelStore.ts index 53ed3db04..703f24866 100644 --- a/src/UI/Store/RightPanelStore.ts +++ b/src/UI/Store/RightPanelStore.ts @@ -18,6 +18,7 @@ import { AppToaster } from "../Components/Toaster"; import { DrillType, FaceDirection, IHighSealedItem } from "./BoardInterface"; import { IConfigStore } from "./BoardStore"; import { LightStore } from "./LightStore"; +import { Modify } from "../../Common/TypeOperator"; export function toggleObject3DVisible(visible = false) { @@ -34,13 +35,20 @@ export function changeView(en: Entity) app.m_Editor.UpdateScreen(); } +type IUIModeiling = Modify; + export class RightPanelStore extends Singleton implements IConfigStore { @observable m_TabId: TabId = RightTabId.Model; @observable m_BoardTemplateTabId: TabId = "cabinet"; @observable m_IsShow = false; @observable modelingItems: IModelingItem[] = []; - @observable UIModelingItems = []; + @observable UIModelingItems: IUIModeiling[] = []; lightStore = new LightStore(); currentBoard: Board; @observable cuList: Curve[] = []; @@ -64,15 +72,15 @@ export class RightPanelStore extends Singleton implements IConfigStore color: (i + 1).toString(), height: "0", dir: FaceDirection.Front, - rad: "3", - length: "0" + knifeRad: "3", + addLen: "0" }) this.modelingItems.push({ color: i + 1, height: 0, dir: FaceDirection.Front, - rad: 3, - length: 0 + knifeRad: 3, + addLen: 0 }) } } @@ -82,6 +90,10 @@ export class RightPanelStore extends Singleton implements IConfigStore this.UIModelingItems[index]["height"] = data.thickness.toString(); this.modelingItems[index].dir = data.dir; this.UIModelingItems[index]["dir"] = data.dir; + this.modelingItems[index].knifeRad = data.knifeRadius; + this.UIModelingItems[index]["knifeRad"] = data.knifeRadius.toString(); + this.modelingItems[index].addLen = data.addLen; + this.UIModelingItems[index]["addLen"] = data.addLen.toString(); } ShowCurve() @@ -268,8 +280,8 @@ export class RightPanelStore extends Singleton implements IConfigStore for (let i = 0; i < this.modelingItems.length; i++) { this.UIModelingItems[i].height = this.modelingItems[i].height.toString(); - this.UIModelingItems[i].rad = this.modelingItems[i].rad.toString(); - this.UIModelingItems[i].length = this.modelingItems[i].length.toString(); + this.UIModelingItems[i].knifeRad = this.modelingItems[i].knifeRad.toString(); + this.UIModelingItems[i].addLen = this.modelingItems[i].addLen.toString(); } } }