diff --git a/__test__/Board/__snapshots__/Mirror.test.ts.snap b/__test__/Board/__snapshots__/Mirror.test.ts.snap index 1405b584e..002ab59be 100644 --- a/__test__/Board/__snapshots__/Mirror.test.ts.snap +++ b/__test__/Board/__snapshots__/Mirror.test.ts.snap @@ -139,7 +139,7 @@ Array [ 1, 0, 0, - 20, + 21, 0, "晨丰顶板", "未命名", @@ -193,6 +193,7 @@ Array [ 0, "", 0, + 0, ] `; @@ -359,7 +360,7 @@ Array [ 0, 0, 0, - 20, + 21, 0, "辅助条(上收口)", "", @@ -417,5 +418,6 @@ Array [ 0, "", 0, + 0, ] `; diff --git a/__test__/CFImport/__snapshots__/CFImport.test.ts.snap b/__test__/CFImport/__snapshots__/CFImport.test.ts.snap index 57fc2b2b0..b07bc83cb 100644 --- a/__test__/CFImport/__snapshots__/CFImport.test.ts.snap +++ b/__test__/CFImport/__snapshots__/CFImport.test.ts.snap @@ -2,11 +2,11 @@ exports[`晨丰导入CAD解析 1`] = `"[12,103,1,2,1,0,0,1,\\"\\",2,2,0,0,3,\\"Board\\",10,2,100,0,1,2,0,[0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,1,0,0,0,20,0,0,1],0,0,1,[1,0,0,0,0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,20,0,0,1],0,0,1,3,100,80,18,true,\\"Polyline\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,2,4,[0,0],0,[0,100],0,[80,100],0,[80,0],0,true,1,3,20,10,1,true,\\"Polyline\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,2,4,[0,0],0,[0,20],0,[10,20],0,[10,0],0,true,0,3,0,0,0,0,0,[0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,1,0,0,0,20,6.123233995736766e-16,10,1],3,0,0,0,0,0,20,1,\\"层板\\",\\"板主卧\\",\\"板标准柜\\",\\"默认板材5厘\\",\\"生态板\\",\\"经典檀木\\",0,0,\\"不排\\",2,0,\\"1.5\\",\\"1.5\\",\\"1.5\\",\\"1.5\\",\\"\\",\\"\\",\\"\\",4,\\"三合一\\",\\"三合一\\",\\"三合一\\",\\"三合一\\",true,true,2,\\"备注1\\",\\"434\\",\\"备注2\\",\\"434\\",0,0,0,0,0,0,0,true,0,0,null,0,0,\\"\\",\\"\\",\\"\\",\\"\\",0,false,0,\\"\\",0,\\"HardwareCompositeEntity\\",1,10,2,101,0,1,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,1,\\"Board\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,3,30,50,60,true,\\"Polyline\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,2,4,[0,0],0,[50,0],0,[50,30],0,[0,30],0,true,0,3,0,0,0,0,0,20,null,\\"\\",\\"\\",\\"\\",\\"\\",\\"\\",\\"\\",0,0,\\"\\",2,0,\\"1\\",\\"1\\",\\"1\\",\\"1\\",\\"\\",\\"\\",\\"\\",4,\\"不排\\",\\"不排\\",\\"不排\\",\\"不排\\",true,true,0,0,0,0,0,0,0,0,true,0,0,null,0,0,\\"\\",\\"\\",\\"\\",\\"\\",0,false,0,\\"\\",0,5,\\"五金\\",false,false,\\"五金颜色\\",\\"五金材质\\",\\"五金\\",\\"五金主卧\\",\\"五金标准柜\\",\\"L*W*H*100\\",\\"L*W*H\\",\\"X-1\\",\\"厂家晨丰\\",\\"品牌晨丰\\",\\"{L*2}\\",\\"1\\",\\"五金备注\\",\\"个\\",0,true,0,0,0,\\"HardwareTopline\\",10,2,102,0,1,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,2,\\"\\",1,\\"\\",1,0,\\"0\\",\\"\\",\\"模板主卧\\",\\"模板标准柜\\",\\"\\",\\"\\",\\"\\",\\"\\",\\"\\",\\"\\",\\"\\",0,2,4,0,0,5,0,2,3,0,0,5,0,0,0,0,0,1,0,1,\\"CommandHistoryRecord\\",1,\\"\\",1,2,3,\\"HistorycRecord\\",2,\\"RemoveObjectData\\",1,0,\\"CreateObjectData\\",1,[],\\"HistorycRecord\\",2,\\"RemoveObjectData\\",1,1,\\"CreateObjectData\\",1,[],\\"HistorycRecord\\",2,\\"RemoveObjectData\\",1,2,\\"CreateObjectData\\",1,[],2,5,0,0,0,1,2,6,0,0,0,1,2,7,0,0,1,\\"\\",2,8,0,0,0,2,9,0,0,0,1,2,10,0,0,1,\\"\\",2,11,0,0,0,0,1,2,12,0,0,5,0,0,null,2,13,0,0,0]"`; -exports[`晨丰导入五金解析 1`] = `"[1,11,2,100,0,1,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,0,1,\\"Board\\",11,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,0,3,30,50,60,true,\\"Polyline\\",11,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,0,2,4,[0,0],0,[50,0],0,[50,30],0,[0,30],0,true,0,3,0,0,0,0,0,20,null,\\"\\",\\"\\",\\"\\",\\"\\",\\"\\",\\"\\",0,0,\\"\\",2,0,\\"1\\",\\"1\\",\\"1\\",\\"1\\",\\"\\",\\"\\",\\"\\",4,\\"不排\\",\\"不排\\",\\"不排\\",\\"不排\\",true,true,0,0,0,0,0,0,0,0,true,0,0,null,0,0,\\"\\",\\"\\",\\"\\",\\"\\",0,false,0,\\"\\",0,5,\\"五金\\",false,false,\\"五金颜色\\",\\"五金材质\\",\\"五金\\",\\"五金主卧\\",\\"五金标准柜\\",\\"L*W*H*100\\",\\"L*W*H\\",\\"X-1\\",\\"厂家晨丰\\",\\"品牌晨丰\\",\\"{L*2}\\",\\"1\\",\\"五金备注\\",\\"个\\",0,true,0,0,0]"`; +exports[`晨丰导入五金解析 1`] = `"[1,10,2,100,0,1,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,1,\\"Board\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,3,30,50,60,true,\\"Polyline\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,2,4,[0,0],0,[50,0],0,[50,30],0,[0,30],0,true,0,3,0,0,0,0,0,21,null,\\"\\",\\"\\",\\"\\",\\"\\",\\"\\",\\"\\",0,0,\\"\\",2,0,\\"1\\",\\"1\\",\\"1\\",\\"1\\",\\"\\",\\"\\",\\"\\",4,\\"不排\\",\\"不排\\",\\"不排\\",\\"不排\\",true,true,0,0,0,0,0,0,0,0,true,0,0,null,0,0,\\"\\",\\"\\",\\"\\",\\"\\",0,false,0,\\"\\",0,0,5,\\"五金\\",false,false,\\"五金颜色\\",\\"五金材质\\",\\"五金\\",\\"五金主卧\\",\\"五金标准柜\\",\\"L*W*H*100\\",\\"L*W*H\\",\\"X-1\\",\\"厂家晨丰\\",\\"品牌晨丰\\",\\"{L*2}\\",\\"1\\",\\"五金备注\\",\\"个\\",0,true,0,0,0]"`; exports[`晨丰导入五金解析 2`] = `"[12,102,1,2,1,0,0,1,\\"\\",2,2,0,0,2,\\"HardwareCompositeEntity\\",1,10,2,100,0,1,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,1,\\"Board\\",10,2,101,0,1,2,0,[0,1,0,0,-1,0,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,3,100,100,100,true,\\"Polyline\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,2,4,[0,0],0,[100,0],0,[100,100],0,[0,100],0,true,0,3,0,0,0,0,0,20,0,\\"层板\\",\\"五金主卧\\",\\"五金标准柜\\",\\"\\",\\"\\",\\"\\",0,0,\\"不排\\",2,0,\\"1\\",\\"1\\",\\"1\\",\\"1\\",\\"\\",\\"\\",\\"\\",4,\\"不排\\",\\"不排\\",\\"不排\\",\\"不排\\",true,true,0,0,0,0,0,0,0,0,true,0,0,null,0,0,\\"\\",\\"\\",\\"\\",\\"\\",0,false,0,\\"\\",0,5,\\"五金\\",false,false,\\"五金颜色\\",\\"五金材质\\",\\"五金\\",\\"五金主卧\\",\\"五金标准柜\\",\\"L*W*H*100\\",\\"L*W*H\\",\\"X-1\\",\\"厂家晨丰\\",\\"品牌晨丰\\",\\"{L*2}\\",\\"1\\",\\"五金备注\\",\\"个\\",0,true,0,0,0,\\"Board\\",10,2,101,0,1,2,0,[0,1,0,0,-1,0,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,3,100,100,100,true,\\"Polyline\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,2,4,[0,0],0,[100,0],0,[100,100],0,[0,100],0,true,0,3,0,0,0,0,0,20,0,\\"层板\\",\\"五金主卧\\",\\"五金标准柜\\",\\"\\",\\"\\",\\"\\",0,0,\\"不排\\",2,0,\\"1\\",\\"1\\",\\"1\\",\\"1\\",\\"\\",\\"\\",\\"\\",4,\\"不排\\",\\"不排\\",\\"不排\\",\\"不排\\",true,true,0,0,0,0,0,0,0,0,true,0,0,null,0,0,\\"\\",\\"\\",\\"\\",\\"\\",0,false,0,\\"\\",0,2,4,0,0,5,0,2,3,0,0,5,0,0,0,0,0,1,0,1,\\"CommandHistoryRecord\\",1,\\"\\",1,2,2,\\"HistorycRecord\\",2,\\"RemoveObjectData\\",1,0,\\"CreateObjectData\\",1,[],\\"HistorycRecord\\",2,\\"RemoveObjectData\\",1,1,\\"CreateObjectData\\",1,[],2,5,0,0,0,1,2,6,0,0,0,1,2,7,0,0,1,\\"\\",2,8,0,0,0,2,9,0,0,0,1,2,10,0,0,1,\\"\\",2,11,0,0,0,0,1,2,12,0,0,5,0,0,null,2,13,0,0,0]"`; -exports[`晨丰导入板解析 1`] = `"[11,2,0,0,0,2,0,[0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,1,0,0,0,20,0,0,1],0,0,1,[1,0,0,0,0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,20,0,0,1],0,0,1,0,3,100,80,18,true,\\"Polyline\\",11,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,0,2,4,[0,0],0,[0,100],0,[80,100],0,[80,0],0,true,1,3,20,10,1,true,\\"Polyline\\",11,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,0,2,4,[0,0],0,[0,20],0,[10,20],0,[10,0],0,true,0,3,0,0,0,0,0,[0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,1,0,0,0,20,6.123233995736766e-16,10,1],3,0,0,0,0,0,20,1,\\"层板\\",\\"板主卧\\",\\"板标准柜\\",\\"默认板材5厘\\",\\"生态板\\",\\"经典檀木\\",0,0,\\"不排\\",2,0,\\"1.5\\",\\"1.5\\",\\"1.5\\",\\"1.5\\",\\"\\",\\"\\",\\"\\",4,\\"三合一\\",\\"三合一\\",\\"三合一\\",\\"三合一\\",true,true,2,\\"备注1\\",\\"434\\",\\"备注2\\",\\"434\\",0,0,0,0,0,0,0,true,0,0,null,0,0,\\"\\",\\"\\",\\"\\",\\"\\",0,false,0,\\"\\",0]"`; +exports[`晨丰导入板解析 1`] = `"[10,2,0,0,0,2,0,[0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,1,0,0,0,20,0,0,1],0,0,1,[1,0,0,0,0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,20,0,0,1],0,0,1,3,100,80,18,true,\\"Polyline\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,2,4,[0,0],0,[0,100],0,[80,100],0,[80,0],0,true,1,3,20,10,1,true,\\"Polyline\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,2,4,[0,0],0,[0,20],0,[10,20],0,[10,0],0,true,0,3,0,0,0,0,0,[0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,1,0,0,0,20,6.123233995736766e-16,10,1],3,0,0,0,0,0,21,1,\\"层板\\",\\"板主卧\\",\\"板标准柜\\",\\"默认板材5厘\\",\\"生态板\\",\\"经典檀木\\",0,0,\\"不排\\",2,0,\\"1.5\\",\\"1.5\\",\\"1.5\\",\\"1.5\\",\\"\\",\\"\\",\\"\\",4,\\"三合一\\",\\"三合一\\",\\"三合一\\",\\"三合一\\",true,true,2,\\"备注1\\",\\"434\\",\\"备注2\\",\\"434\\",0,0,0,0,0,0,0,true,0,0,null,0,0,\\"\\",\\"\\",\\"\\",\\"\\",0,false,0,\\"\\",0,0]"`; exports[`晨丰导入模板解析 1`] = `"[12,102,1,2,1,0,0,1,\\"\\",2,2,0,0,2,\\"HardwareCompositeEntity\\",1,10,2,100,0,1,7,0,[1,0,0,0,0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,400,0,0,1],0,0,1,[1,0,0,0,0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,400,0,0,1],0,0,1,1,\\"Board\\",10,2,101,0,1,2,0,[0,6.123233995736766e-17,1,0,-1,0,0,0,0,-1,6.123233995736766e-17,0,400,0,0,1],0,0,1,[1,0,0,0,0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,400,0,0,1],0,0,1,3,100,100,100,true,\\"Polyline\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,2,4,[0,0],0,[100,0],0,[100,100],0,[0,100],0,true,0,3,0,0,0,0,0,20,0,\\"层板\\",\\"模板主卧\\",\\"模板标准柜\\",\\"\\",\\"\\",\\"\\",0,0,\\"不排\\",2,0,\\"1\\",\\"1\\",\\"1\\",\\"1\\",\\"\\",\\"\\",\\"\\",4,\\"不排\\",\\"不排\\",\\"不排\\",\\"不排\\",true,true,0,0,0,0,0,0,0,0,true,0,0,null,0,0,\\"\\",\\"\\",\\"\\",\\"\\",0,false,0,\\"\\",0,5,\\"五金\\",false,false,\\"\\",\\"\\",\\"复合实体\\",\\"模板主卧\\",\\"模板标准柜\\",\\"L*W*H*100\\",\\"L*W*H*300\\",\\"X-1\\",\\"晨丰\\",\\"晨丰\\",\\"个\\",\\"1\\",\\"\\",\\"\\",0,true,0,0,0,\\"Board\\",10,2,101,0,1,2,0,[0,6.123233995736766e-17,1,0,-1,0,0,0,0,-1,6.123233995736766e-17,0,400,0,0,1],0,0,1,[1,0,0,0,0,6.123233995736766e-17,1,0,0,-1,6.123233995736766e-17,0,400,0,0,1],0,0,1,3,100,100,100,true,\\"Polyline\\",10,2,0,0,0,7,0,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],0,0,1,2,4,[0,0],0,[100,0],0,[100,100],0,[0,100],0,true,0,3,0,0,0,0,0,20,0,\\"层板\\",\\"模板主卧\\",\\"模板标准柜\\",\\"\\",\\"\\",\\"\\",0,0,\\"不排\\",2,0,\\"1\\",\\"1\\",\\"1\\",\\"1\\",\\"\\",\\"\\",\\"\\",4,\\"不排\\",\\"不排\\",\\"不排\\",\\"不排\\",true,true,0,0,0,0,0,0,0,0,true,0,0,null,0,0,\\"\\",\\"\\",\\"\\",\\"\\",0,false,0,\\"\\",0,2,4,0,0,5,0,2,3,0,0,5,0,0,0,0,0,1,0,1,\\"CommandHistoryRecord\\",1,\\"\\",1,2,2,\\"HistorycRecord\\",2,\\"RemoveObjectData\\",1,0,\\"CreateObjectData\\",1,[],\\"HistorycRecord\\",2,\\"RemoveObjectData\\",1,1,\\"CreateObjectData\\",1,[],2,5,0,0,0,1,2,6,0,0,0,1,2,7,0,0,1,\\"\\",2,8,0,0,0,2,9,0,0,0,1,2,10,0,0,1,\\"\\",2,11,0,0,0,0,1,2,12,0,0,5,0,0,null,2,13,0,0,0]"`; diff --git a/__test__/FeedingToolPath/FeedingToolPath.test.ts b/__test__/FeedingToolPath/FeedingToolPath.test.ts index fe62c6da2..e59863b54 100644 --- a/__test__/FeedingToolPath/FeedingToolPath.test.ts +++ b/__test__/FeedingToolPath/FeedingToolPath.test.ts @@ -225,7 +225,7 @@ test('矩形走刀 冗余1 优化走刀', () => let brs = LoadBoardsFromFileData(d); - let { modeling, sideModeling } = Production.GetBoardModelingData(brs[0], new Vector3, 1, []); + let { modeling } = Production.GetBoardModelingData(brs[0], new Vector3, 1, []); for (let m of modeling) { diff --git a/src/Add-on/Batch/FindModeingKnifes.tsx b/src/Add-on/Batch/FindModeingKnifes.tsx index 095a58aa9..f7faacb00 100644 --- a/src/Add-on/Batch/FindModeingKnifes.tsx +++ b/src/Add-on/Batch/FindModeingKnifes.tsx @@ -29,7 +29,7 @@ export class FindModeingKnifeRadius implements Command for (let board of boards) { let { sideModeling, modeling } = Production.GetOriginBoardModelingData(board); - for (let m of [...sideModeling, ...modeling]) + for (let m of [...sideModeling.sideModel, ...modeling]) { let bs = knifeBoardMap.get(m.knifeRadius); if (!bs) diff --git a/src/Add-on/BoardCutting/SplitBoardSideModel.ts b/src/Add-on/BoardCutting/SplitBoardSideModel.ts new file mode 100644 index 000000000..e58d7fc40 --- /dev/null +++ b/src/Add-on/BoardCutting/SplitBoardSideModel.ts @@ -0,0 +1,225 @@ +import { Matrix4, Vector3 } from "three"; +import { arrayLast } from "../../Common/ArrayExt"; +import { Board } from "../../DatabaseServices/Entity/Board"; +import { Curve } from "../../DatabaseServices/Entity/Curve"; +import { ExtrudeSolid } from "../../DatabaseServices/Entity/Extrude"; +import { Line } from "../../DatabaseServices/Entity/Line"; +import { Polyline } from "../../DatabaseServices/Entity/Polyline"; +import { Box3Ext } from "../../Geometry/Box"; +import { AsVector2, equaln } from "../../Geometry/GeUtils"; +import { GetBoardSealingCurves, SubsectionCurvesOfHightSeal } from "../../GraphicsSystem/CalcEdgeSealing"; +import { IntersectOption } from "../../GraphicsSystem/IntersectWith"; +import { FixIndex } from "../../Nest/Common/Util"; +import { SplitPolyline } from "./SplitPolyline"; + +//侧面造型分裂 +export class SplitBoardSideModelUtil +{ + //备份原始二维刀路在世界坐标系中 + private OrgBoardOCS: Matrix4 = new Matrix4(); + private CacheSideModel: Map = new Map(); + private OldSealCurves: Curve[] = []; + + constructor(br: Board) + { + this.Init(br); + } + + Init(br: Board, isSpecialShape = false) + { + this.OrgBoardOCS = br.OCS; + let curves = GetBoardSealingCurves(br); + + //取消异型操作会提前令isSpecialShape = false + if (isSpecialShape) + SubsectionCurvesOfHightSeal(curves); + + let oldSealCurves: Curve[] = []; + + for (let c of curves) + if (c instanceof Polyline) + oldSealCurves.push(...c.Explode()); + else + oldSealCurves.push(c); + + this.OldSealCurves = oldSealCurves; + + const sideModelMap: Map = new Map(); + + for (let [num, soilds] of br.SideModelingMap) + sideModelMap.set(num, soilds); + + this.CacheSideModel = sideModelMap; + } + + CheckSideModel(): boolean + { + let maxSideIndex = 0; + for (let [num, soilds] of this.CacheSideModel) + maxSideIndex = Math.max(num, maxSideIndex); + + return this.OldSealCurves.length >= maxSideIndex; + } + + SetBoardSideModel(br: Board) + { + if (!this.CacheSideModel.size) return; + + if (this.CheckSideModel()) + { + this.SpiltSideModelOfBrContour(br); + } + } + + //新轮廓切割原始轮廓 + SpiltSideModelOfBrContour(br: Board) + { + let curves = GetBoardSealingCurves(br); + + let newSealCurves: Curve[] = []; + + for (let c of curves) + if (c instanceof Polyline) + newSealCurves.push(...c.Explode()); + else + newSealCurves.push(c); + + let sideMadelMap: Map = new Map(); + for (let [nmu, soilds] of this.CacheSideModel) + { + if (soilds?.length) + { + let oldCu = this.OldSealCurves[nmu]?.Clone(); + if (!oldCu) continue; + + oldCu.ApplyMatrix(this.OrgBoardOCS); + for (let i = 0; i < newSealCurves.length; i++) + { + let newCu = newSealCurves[i].Clone().ApplyMatrix(br.OCSNoClone) as Line; + let p = newCu.GetPointAtParam(newCu.EndParam * 0.5); + + let spliteEnts: ExtrudeSolid[] = []; + + if (oldCu.PtOnCurve(p)) + { + let startX = oldCu.GetDistAtPoint(newCu.StartPoint); + let endX = oldCu.GetDistAtPoint(newCu.EndPoint); + let box = new Box3Ext(new Vector3(startX), new Vector3(endX, br.Thickness)); + + let knifePls: Polyline[] = []; + + for (let soild of soilds) + { + if (soild.Thickness <= 0) continue; + + let sCon = soild.ContourCurve.Clone().ApplyMatrix(soild.OCSNoClone) as Polyline; + let thickness = soild.Thickness; + + let newNeighborCus: Curve[] = []; + newNeighborCus.push(newSealCurves[FixIndex(i - 1, newSealCurves)].Clone().ApplyMatrix(br.OCSNoClone)); + newNeighborCus.push(newSealCurves[FixIndex(i + 1, newSealCurves)].Clone().ApplyMatrix(br.OCSNoClone)); + + const offsetCus = newCu.GetOffsetCurves(-thickness); + const xPtList: number[] = [startX, endX]; + + for (let cu of offsetCus) + { + for (let nbCu of newNeighborCus) + { + const intersectPts = cu.IntersectWith(nbCu, IntersectOption.ExtendThis); + for (let pt of intersectPts) + { + let { closestPt } = newCu.GetClosestAtPoint(pt, true); + let ptX = oldCu.GetDistAtPoint(closestPt); + xPtList.push(ptX); + } + } + } + + xPtList.sort((a, b) => a - b); + + for (let x of [xPtList[0], arrayLast(xPtList)]) + knifePls.push(new Polyline([{ pt: AsVector2({ x, y: 0 }), bul: 0 }, { pt: AsVector2({ x, y: 1 }), bul: 0 }])); + + let splitSideModelCons = SplitPolyline(sCon, knifePls); + + if (!splitSideModelCons.length) + splitSideModelCons.push(sCon); + + for (let j = 0; j < splitSideModelCons.length; j++) + { + let intersectBox = box.clone().intersect(splitSideModelCons[j].BoundingBox); + if (box.intersectsBox(splitSideModelCons[j].BoundingBox, 1) && !equaln(intersectBox.max.x, intersectBox.min.x)) + { + let soildClone = soild.Clone(); + soildClone.ContourCurve = splitSideModelCons[j].ApplyMatrix(soild.OCSInv); + soildClone.GrooveCheckAllAutoSplit(); + soildClone.ApplyMatrix(new Matrix4().setPosition(-startX, 0, 0)); + spliteEnts.push(soildClone); + } + } + } + + if (spliteEnts.length) + sideMadelMap.set(i, spliteEnts); + } + } + } + } + + br.SideModelingMap = sideMadelMap; + } + + //修改板厚度时裁剪侧面造型 + SpiltSideModelOfBrThickness(br: Board, thickness: number) + { + this.Init(br); + + let sideMadelMap: Map = new Map(); + for (let [nmu, soilds] of this.CacheSideModel) + { + if (soilds?.length) + { + let cu = this.OldSealCurves[nmu]?.Clone(); + if (!cu) continue; + cu.ApplyMatrix(this.OrgBoardOCS); + + let spliteEnts: ExtrudeSolid[] = []; + let knifePls: Polyline[] = []; + let box = new Box3Ext(new Vector3(), new Vector3(cu.Length, thickness)); + + for (let soild of soilds) + { + if (soild.Thickness <= 0) continue; + + let sCon = soild.ContourCurve.Clone().ApplyMatrix(soild.OCSNoClone) as Polyline; + + knifePls.push(new Polyline([{ pt: AsVector2({ x: 0, y: 0 }), bul: 0 }, { pt: AsVector2({ x: 1, y: 0 }), bul: 0 }])); + knifePls.push(new Polyline([{ pt: AsVector2({ x: 0, y: thickness }), bul: 0 }, { pt: AsVector2({ x: 1, y: thickness }), bul: 0 }])); + + let splitSideModelCons = SplitPolyline(sCon, knifePls); + + if (!splitSideModelCons.length) + splitSideModelCons.push(sCon); + + for (let con of splitSideModelCons) + { + let intersectBox = box.clone().intersect(con.BoundingBox); + if (box.intersectsBox(con.BoundingBox, 1) && !equaln(intersectBox.max.y, intersectBox.min.y)) + { + let soildClone = soild.Clone(); + soildClone.ContourCurve = con.ApplyMatrix(soild.OCSInv); + soildClone.GrooveCheckAllAutoSplit(); + spliteEnts.push(soildClone); + } + } + } + + if (spliteEnts.length) + sideMadelMap.set(nmu, spliteEnts); + } + } + + br.SideModelingMap = sideMadelMap; + } +} diff --git a/src/Add-on/BoardEditor/SerializeBoardData.ts b/src/Add-on/BoardEditor/SerializeBoardData.ts index 0416ba49c..070907cae 100644 --- a/src/Add-on/BoardEditor/SerializeBoardData.ts +++ b/src/Add-on/BoardEditor/SerializeBoardData.ts @@ -2,6 +2,7 @@ import { Vector3 } from "three"; import { EBoardKeyList } from "../../Common/BoardKeyList"; import { CADFiler } from "../../DatabaseServices/CADFiler"; import { I2DModeling, I3DModeling, IPathItem } from "../../DatabaseServices/Entity/Board"; +import { ExtrudeSolid } from "../../DatabaseServices/Entity/Extrude"; import { Polyline } from "../../DatabaseServices/Entity/Polyline"; import { BoardProcessOption } from "../../UI/Store/OptionInterface/BoardProcessOption"; @@ -203,3 +204,38 @@ export function DeserializationBoard3DModeingData(file: CADFiler, data: I3DModel }); } } + +export function SerializeBoardSideModeingData(file: CADFiler, sideModelingMap: Map) +{ + file.Write(sideModelingMap.size); + + for (let [index, sideModelingList] of sideModelingMap) + { + file.Write(index); + file.Write(sideModelingList.length); + + for (let data of sideModelingList) + file.WriteObject(data); + } +} + +export function DeserializationBoardSideModeingData(file: CADFiler, sideModelingMap: Map) +{ + sideModelingMap.clear(); + + const count = file.Read(); + for (let i = 0; i < count; i++) + { + let index = file.Read(); + let listCount = file.Read(); + let sideModelingList: ExtrudeSolid[] = []; + + for (let j = 0; j < listCount; j++) + { + let obj = file.ReadObject() as ExtrudeSolid; + sideModelingList.push(obj); + } + + sideModelingMap.set(index, sideModelingList); + } +} diff --git a/src/Add-on/BoardFindModify.ts b/src/Add-on/BoardFindModify.ts index 81bd5c908..c120b4cb8 100644 --- a/src/Add-on/BoardFindModify.ts +++ b/src/Add-on/BoardFindModify.ts @@ -186,7 +186,7 @@ export class BoardFindModify implements Command for (let b of br.SplitBoards) { const { sideModeling, modeling } = Production.GetOriginBoardModelingData(b); - for (let m of [...sideModeling, ...modeling]) + for (let m of [...sideModeling.sideModel, ...modeling]) { compareBrValue = m.knifeRadius; isVail = this.FilterBrSize(compareBrValue, compareValue, torValue, option.compareType.knifeRadius); diff --git a/src/Add-on/BoardInspection/Inspection.ts b/src/Add-on/BoardInspection/Inspection.ts index d9fb1df80..ebfe56717 100644 --- a/src/Add-on/BoardInspection/Inspection.ts +++ b/src/Add-on/BoardInspection/Inspection.ts @@ -41,6 +41,7 @@ export function ModelInspection(boards: Board[]) let errGrooves: ExtrudeSolid[] = []; let errHoles: ExtrudeHole[] = []; + let errorSideModel: ExtrudeSolid[] = []; for (let br of boards) { @@ -51,9 +52,10 @@ export function ModelInspection(boards: Board[]) errGrooves.push(grooves[index]); } errHoles.push(...feedingTool.CheckCustomHole(br)); + errorSideModel = feedingTool.CheckSideModeling(br); } - return [...errGrooves.map(g => [[g], "槽错误"]), ...errHoles.map(h => [[h], "孔错误"])]; + return [...errGrooves.map(g => [[g], "槽错误"]), ...errorSideModel.map(g => [[g], "侧槽错误"]), ...errHoles.map(h => [[h], "孔错误"])]; } export function SpecialBoardContourInspection(boards: Board[]) diff --git a/src/Add-on/CheckModeling.ts b/src/Add-on/CheckModeling.ts index ccca52800..30ef8b887 100644 --- a/src/Add-on/CheckModeling.ts +++ b/src/Add-on/CheckModeling.ts @@ -34,6 +34,7 @@ export class CheckModeling implements Command let errGrooves: ExtrudeSolid[] = []; let errHoles: ExtrudeHole[] = []; + let errorSideModel: ExtrudeSolid[] = []; for (let br of brs) { @@ -44,17 +45,21 @@ export class CheckModeling implements Command errGrooves.push(grooves[index]); } errHoles.push(...feedingTool.CheckCustomHole(br)); + + errorSideModel = feedingTool.CheckSideModeling(br); } - if (errGrooves.length > 0 || errHoles.length > 0) - app.Editor.Prompt(`检测到${errGrooves.length}个无法加工的造型和${errHoles.length}个无法加工的自定义排钻!旋转视图可以隐藏板件查看造型位置!`, LogType.Warning, [...errHoles, ...errGrooves]); + if (errGrooves.length > 0 || errorSideModel.length || errHoles.length > 0) + app.Editor.Prompt(`检测到${errGrooves.length + errorSideModel.length}个无法加工的造型和${errHoles.length}个无法加工的自定义排钻!旋转视图可以隐藏板件查看造型位置!`, + LogType.Warning, [...errHoles, ...errGrooves, ...errorSideModel]); else app.Editor.Prompt(`未检测到无法加工的造型!`, LogType.Info); - if (errGrooves.length > 0 || errHoles.length > 0) + if (errGrooves.length > 0 || errorSideModel.length > 0 || errHoles.length > 0) { app.Viewer.OutlinePass.selectedObjects = [...errHoles.map(h => h.DrawObject), - ...errGrooves.map(g => JigUtils.Draw(g.Clone()).DrawObject)]; + ...errGrooves.map(g => JigUtils.Draw(g.Clone()).DrawObject), + ...errorSideModel.map(soild => JigUtils.Draw(soild.Clone()).DrawObject)]; app.Editor.UpdateScreen(); AppToaster.show({ diff --git a/src/Add-on/ClearCDBrHoleModeling.ts b/src/Add-on/ClearCDBrHoleModeling.ts index dc3d58010..29e48f5a2 100644 --- a/src/Add-on/ClearCDBrHoleModeling.ts +++ b/src/Add-on/ClearCDBrHoleModeling.ts @@ -24,6 +24,7 @@ export class Command_ClearCDBrHoleModeling implements Command br.ClearAllDrillList(); br.ClearLayerNails(); br.ClearRelevance(); + br.ClearSideModeling(); } } else @@ -34,6 +35,7 @@ export class Command_ClearCDBrHoleModeling implements Command br.ClearBoardModeling(); br.ClearLayerNails(); br.ClearRelevance(); + br.ClearSideModeling(); } } } diff --git a/src/Add-on/CommandFeeding.ts b/src/Add-on/CommandFeeding.ts index 6b0ce1ea9..88577b8ca 100644 --- a/src/Add-on/CommandFeeding.ts +++ b/src/Add-on/CommandFeeding.ts @@ -5,14 +5,17 @@ import { Intent } from "../Common/Toaster"; import { DimStyleKeyCode } from "../DatabaseServices/DimStyle/DimstyleKeyCodeEnum"; import { Board } from "../DatabaseServices/Entity/Board"; import { Circle } from "../DatabaseServices/Entity/Circle"; +import { Curve } from "../DatabaseServices/Entity/Curve"; import { Line } from "../DatabaseServices/Entity/Line"; +import { Polyline } from "../DatabaseServices/Entity/Polyline"; import { Text, TextAligen } from "../DatabaseServices/Text/Text"; import { Command } from "../Editor/CommandMachine"; import { PromptStatus } from "../Editor/PromptResult"; import { userConfig } from "../Editor/UserConfig"; +import { GetSideCuFaceMtx } from "../Geometry/Board2DModelCSG/BoardSideModelCSGBuilder"; import { MoveMatrix, ZAxis, rotatePoint } from "../Geometry/GeUtils"; import { IContourData } from "../Production/Convert2PtsBul"; -import { IBoardHoleInfo, IModelingData, ISpliteOrderData, Production } from "../Production/Product"; +import { IBoardHoleInfo, IModelingData, IOriginSideModelingData, ISpliteOrderData, ModelType, Production } from "../Production/Product"; import { AppToaster } from "../UI/Components/Toaster"; import { FaceDirection } from "./DrawDrilling/DrillType"; import { DrawHoleDim } from "./DrawHoleDimUtil"; @@ -118,8 +121,12 @@ export class FeedingCommand implements Command let tMtx = MoveMatrix(pos); let contouLines = this.ParseContourData(info.originOutlin);//线段形式返回封边轮廓拆成多个线段,主要运用在3个点形成的平面 + let originContour = Production.Data2Polyline(info.originOutlin); + let sealingContour = Production.Data2Polyline(info.outline); this.DrawHole(info.holes, tMtx, info.offsetTanslation); + //侧面非圆排钻孔 + this.DrawSideHole(info.sideModeling, originContour, tMtx, info.offsetTanslation); const DimStyle: Map = new Map(); DimStyle.set(DimStyleKeyCode.DIMASZ, 2); //箭头尺寸 @@ -132,9 +139,6 @@ export class FeedingCommand implements Command //占位空间box let box = new Box3(); - let originContour = Production.Data2Polyline(info.originOutlin); - let sealingContour = Production.Data2Polyline(info.outline); - if (drawHoleDim) await DrawHoleDim(info, tMtx, contouLines, DimStyle, box, originContour.BoundingBox); @@ -163,7 +167,7 @@ export class FeedingCommand implements Command this.DrawOriginModeling(brs[i], tMtx, info); this.TestModeling(info.modeling, tMtx); - this.TestModeling(info.sideModeling, tMtx); + // this.TestModeling(info.sideModeling, tMtx); let size = originContour.BoundingBox.getSize(new Vector3); this.DateText(info, tMtx, size.x, box, pos.y); @@ -205,6 +209,41 @@ export class FeedingCommand implements Command app.Database.ModelSpace.Append(c); } } + + private DrawSideHole(sideHoleInfo: IOriginSideModelingData[], sealingContour: Polyline, tMtx: Matrix4, offset: Vector3) + { + let cus = sealingContour.Explode() as Curve[]; + const inverseZ = sealingContour.Area2 < 0; + const mt4Cache: Map = new Map(); + let offsetPs = offset.clone().multiplyScalar(-1); + + for (let info of sideHoleInfo) + { + if (info.modelType !== ModelType.drill) continue; + let cu = cus[info.dir]; + if (!cu) continue; + + let mt4 = mt4Cache.get(info.dir); + if (!mt4) + { + mt4 = GetSideCuFaceMtx(cu, inverseZ); + mt4Cache.set(info.dir, mt4); + } + + let holeOutline = Production.Data2Polyline(info.outline); + + holeOutline.ApplyMatrix(mt4); + holeOutline.Move(offsetPs); + holeOutline.ApplyMatrix(tMtx); + app.Database.ModelSpace.Append(holeOutline); + + let pos = holeOutline.BoundingBox.getCenter(new Vector3); + let l = new Line(pos, pos.clone().sub(holeOutline.Normal.multiplyScalar(info.thickness))); + l.ColorIndex = 3; + app.Database.ModelSpace.Append(l); + } + } + private TestModeling(datalist: IModelingData[], tMtx: Matrix4) { for (let data of datalist) diff --git a/src/Add-on/CommandSideModelFeediing.ts b/src/Add-on/CommandSideModelFeediing.ts new file mode 100644 index 000000000..f43fd1f41 --- /dev/null +++ b/src/Add-on/CommandSideModelFeediing.ts @@ -0,0 +1,101 @@ +import { Matrix4, Vector3 } from "three"; +import { app } from "../ApplicationServices/Application"; +import { Intent, Toaster } from "../Common/Toaster"; +import { Board } from "../DatabaseServices/Entity/Board"; +import { Entity } from "../DatabaseServices/Entity/Entity"; +import { Polyline } from "../DatabaseServices/Entity/Polyline"; +import { Text, TextAligen } from "../DatabaseServices/Text/Text"; +import { Command } from "../Editor/CommandMachine"; +import { PromptStatus } from "../Editor/PromptResult"; +import { ParseBoardSideFace } from "../Geometry/DrillParse/ParseBoardSideFace"; +import { MoveMatrix } from "../Geometry/GeUtils"; +import { FeedingToolPath } from "../GraphicsSystem/ToolPath/FeedingToolPath"; + +export class SideModelFeedingCommand implements Command +{ + async exec() + { + let enRes = await app.Editor.GetEntity({ Filter: { filterTypes: [Entity] } }); + if (enRes.Status !== PromptStatus.OK) return; + let br = enRes.Entity as Board; + + if (!br.SideModelingMap?.size) return; + + const tool = FeedingToolPath.GetInstance(); + + let faces = new ParseBoardSideFace(br); + + let ptRes = await app.Editor.GetPoint({ Msg: "点取位置" }); + + if (ptRes.Status === PromptStatus.OK) + { + let pos = ptRes.Point;; + let width = 0; + + let sideModelList = Array.from(br.SideModelingMap); + sideModelList.sort((a, b) => a[0] - b[0]); + + for (let [n, soilds] of sideModelList) + { + let tMtx = MoveMatrix(pos); + let faceContour = faces.Faces[n].Region.ShapeManager.ShapeList[0].Outline.Curve as Polyline; + + for (let soild of soilds) + { + let con = soild.ContourCurve.Clone().ApplyMatrix(soild.OCSNoClone).ApplyMatrix(tMtx); + con.ColorIndex = 8; + app.Database.ModelSpace.Append(con); + + let paths = tool.GetSideModelFeedPath(soild, faceContour);//走刀路径 + + if (paths.length) + for (let pl of paths) + { + pl.ApplyMatrix(tMtx); + pl.ColorIndex = 1; + app.Database.ModelSpace.Append(pl); + } + else + { + Toaster({ + message: "板件有侧面造型无法加工,请运行造型检测命令确认", + timeout: 5000, + intent: Intent.DANGER, + key: "造型加工错误" + }); + } + + for (let hole of soild.Shape.Holes) + { + let cu = hole.Curve.ApplyMatrix(soild.OCSNoClone).ApplyMatrix(tMtx); + cu.ColorIndex = 8; + app.Database.ModelSpace.Append(cu); + } + } + + faceContour.ApplyMatrix(tMtx); + faceContour.ColorIndex = 3; + app.Database.ModelSpace.Append(faceContour); + + width = faces.Faces[n].Length; + + this.OrderText((n + 1).toString(), tMtx, width); + + pos.add(new Vector3(width + 100)); + } + } + } + + private OrderText(order: string, tMtx: Matrix4, width: number) + { + //文本上下间隔 i + let i = -100; + + let text = new Text(new Vector3, order); + text.Height = 60; + text.TextAligen = TextAligen.Mid; + text.ApplyMatrix(tMtx); + text.Position = text.Position.add(new Vector3(width * 0.5, i)); + app.Database.ModelSpace.Append(text); + } +} diff --git a/src/Add-on/Erp/ParseData.ts b/src/Add-on/Erp/ParseData.ts index 6dc1e9248..a7895c7f7 100644 --- a/src/Add-on/Erp/ParseData.ts +++ b/src/Add-on/Erp/ParseData.ts @@ -13,7 +13,7 @@ import { TemplateRecord } from "../../DatabaseServices/Template/TemplateRecord"; import { CoordinateSystem } from "../../Geometry/CoordinateSystem"; import { GetBoxArr, IdentityMtx4, equalv2 } from "../../Geometry/GeUtils"; import { IContourData } from "../../Production/Convert2PtsBul"; -import { I2DModeling, I3DContourData, I3DModeling, IDrillingOption, IHardwareType, IModelingData, ISpliteHardwareData, ISpliteOrderData, Production } from '../../Production/Product'; +import { I2DModeling, I3DContourData, I3DModeling, IDrillingOption, IHardwareType, IModelingData, IOriginModelingData, ISpliteHardwareData, ISpliteOrderData, Production } from '../../Production/Product'; import { EMetalsType } from "../../UI/Components/RightPanel/RightPanelInterface"; import { ISealingData } from "../../UI/Store/OptionInterface/IHighSealedItem"; import { FaceDirection } from "../DrawDrilling/DrillType"; @@ -390,7 +390,8 @@ export class ErpParseData } return pointList; } - //获取造型数据 + + //获取正反面造型数据 GetModelDetail(DataArray: IModelingData[], frontOrSide: FrontOrSide, isRect: Boolean = false, add: number = 0): CadBlockModel[] { if (DataArray == null) return []; @@ -428,6 +429,32 @@ export class ErpParseData return modelList; } + //获取侧面造型数据 + GetSideModelDetail(DataArray: IOriginModelingData[]): CadBlockModel[] + { + if (DataArray == null) return []; + let sideModelList: CadBlockModel[] = []; + let modelID: number = 1; + for (let i = 0; i < DataArray.length; i++) + { + if (DataArray[i].outline) + { + let newModel = new CadBlockModel(); + newModel.ModelID = modelID; + newModel.LineID = 1; + newModel.Face = DataArray[i].dir; + newModel.KnifeName = ""; + newModel.KnifeRadius = DataArray[i].knifeRadius; + newModel.Depth = DataArray[i].thickness; + newModel.OriginModeling = DataArray[i]; + sideModelList.push(newModel); + + modelID++; + } + } + return sideModelList; + } + GetModelPointDetail(feeding: IContourData, lineID: number, deep: number): CadBlockModelPoint[] { if (feeding == null) return []; @@ -651,7 +678,7 @@ export class ErpParseData frontModelDetail.push(...this.GetOffSetModelDetail(board.modeling2D, maxID)); maxID = frontModelDetail.length + 1; frontModelDetail.push(...this.Get3DModelDetail(board.modeling3D, maxID)); - let sideModelDetail = this.GetModelDetail(board.sideModeling, FrontOrSide.侧面, board.info.isRect, add); + let sideModelDetail = this.GetSideModelDetail(board.sideModeling); let frontHoleDetail = this.GetHolesDetail(board.holes.frontBackHoles, FrontOrSide.正面反面); let sideHoleDetail = this.GetHolesDetail(board.holes.sideHoles, FrontOrSide.侧面, board.info.isRect, add); let result = new CadBlockInfo(); diff --git a/src/Add-on/Mirror.ts b/src/Add-on/Mirror.ts index 466dcc363..1f16a9792 100644 --- a/src/Add-on/Mirror.ts +++ b/src/Add-on/Mirror.ts @@ -15,10 +15,12 @@ import { Curve } from "../DatabaseServices/Entity/Curve"; import { Entity } from "../DatabaseServices/Entity/Entity"; import { EntityFbx } from "../DatabaseServices/Entity/EntityFbx"; import { EntityRef } from "../DatabaseServices/Entity/EntityRef"; +import { ExtrudeSolid } from "../DatabaseServices/Entity/Extrude"; import { HardwareCompositeEntity } from "../DatabaseServices/Hardware/HardwareCompositeEntity"; import { Command } from "../Editor/CommandMachine"; import { JigUtils } from "../Editor/JigUtils"; import { PromptStatus } from "../Editor/PromptResult"; +import { GetBoardContour } from "../GraphicsSystem/CalcEdgeSealing"; import { AppToaster } from "../UI/Components/Toaster"; import { UpdateEntityDrawTask } from "./UpdateEntityDrawTask"; @@ -139,6 +141,10 @@ export class MirrorCommand implements Command for (let en of ens) { en.ApplyMatrix(mtx); + + if (en instanceof Board && en.HasSideModel) + this.MirrorSideModel(en); + if (NEED_ENTITY.some(T => en instanceof T)) en.DeferUpdate();//立即更新 } @@ -149,4 +155,32 @@ export class MirrorCommand implements Command //延迟更新 this.UpdateTask = new UpdateEntityDrawTask(ens); } + + //侧面造型镜像 + MirrorSideModel(br: Board) + { + let newSideModelMap: Map = new Map(); + + let con = GetBoardContour(br); + let cus = con.Explode() as Curve[]; + + for (let [num, soilds] of br.SideModelingMap) + { + let index = cus.length - num - 1; + newSideModelMap.set(index, soilds); + const mirrorMtxX = MakeMirrorMtx(new Vector3(1)); + + for (let so of soilds) + { + const sign = Math.sign(new Vector3().setFromMatrixColumn(so.OCSNoClone, 0).x); + let x = sign * (cus[index].Length - so.Position.x * 2); + so.OCSNoClone.multiply(mirrorMtxX.setPosition(x, 0, 0)); + + for (let groove of so.Grooves) + groove.OCSNoClone.multiply(mirrorMtxX.setPosition(sign * (cus[index].Length - groove.Position.x * 2), 0, 0)); + } + } + + br.SideModelingMap = newSideModelMap; + } } diff --git a/src/Add-on/testEntity/TestDrawUCS.ts b/src/Add-on/testEntity/TestDrawUCS.ts new file mode 100644 index 000000000..ec25dc0d5 --- /dev/null +++ b/src/Add-on/testEntity/TestDrawUCS.ts @@ -0,0 +1,16 @@ +import { Matrix4, Vector3 } from "three"; +import { Line } from "../../DatabaseServices/Entity/Line"; +import { Point } from "../../DatabaseServices/Entity/Point"; +import { TestDraw } from "../test/TestUtil"; + +export async function TestDrawUCS(mat4: Matrix4) +{ + let pt = new Vector3().applyMatrix4(mat4); + let l1 = new Line(pt, pt.clone().add(new Vector3().setFromMatrixColumn(mat4, 0).normalize().multiplyScalar(100))); + let l2 = new Line(pt, pt.clone().add(new Vector3().setFromMatrixColumn(mat4, 1).normalize().multiplyScalar(100))); + let l3 = new Line(pt, pt.clone().add(new Vector3().setFromMatrixColumn(mat4, 2).normalize().multiplyScalar(100))); + TestDraw(new Point(pt)); + TestDraw(l1, 1); + TestDraw(l2, 3); + TestDraw(l3, 5); +} diff --git a/src/Common/CommandNames.ts b/src/Common/CommandNames.ts index 5a4232b09..486299e20 100644 --- a/src/Common/CommandNames.ts +++ b/src/Common/CommandNames.ts @@ -141,7 +141,7 @@ export enum CommandNames FZWL = "FZWL", ActicityLayerBoard = "ACTICITYLAYERBOARD", TestFb = "TESTFENGBIAN", - TestModeling = "TESTMODELING", + TestSideModeling = "TESTSIDEMODELING", Mirror = "MIRROR", Topline = "TOPLINE", Winerack = "WINERACK", diff --git a/src/Common/SerializeMaterial.ts b/src/Common/SerializeMaterial.ts index ecab3382d..780174eb9 100644 --- a/src/Common/SerializeMaterial.ts +++ b/src/Common/SerializeMaterial.ts @@ -57,7 +57,7 @@ export async function TemplateOut(template: TemplateRecord, tempDb = new Databas if (idMapRev.has(ent.Id)) { let oldEnt = idMapRev.get(ent.Id).Object as Entity; - if (oldEnt instanceof Board && oldEnt.Async2DPathing) + if (oldEnt instanceof Board && oldEnt.Async2DPathing && oldEnt.AsyncSideModeling) { async2DPathingPromis.push( oldEnt.Load2DPathIng().then((res) => diff --git a/src/DatabaseServices/Entity/Board.ts b/src/DatabaseServices/Entity/Board.ts index 4ed31c29b..eabd06d66 100644 --- a/src/DatabaseServices/Entity/Board.ts +++ b/src/DatabaseServices/Entity/Board.ts @@ -2,8 +2,9 @@ import Geom3 from '@jscad/modeling/src/geometries/geom3/type'; import { BufferAttribute, BufferGeometry, Euler, FrontSide, Frustum, Geometry, LineSegments, Matrix3, Matrix4, Mesh, Object3D, ShapeGeometry, Line as TLine, UVGenerator, Vector2, Vector3 } from 'three'; import { ArcBoardBuild } from '../../Add-on/ArcBoard/ArcBoardBuild'; import { ArcBoardOptions, ParseBoardArcFeed, defultArcBoardOption } from '../../Add-on/ArcBoard/ArcBoardFeeding'; +import { SplitBoardSideModelUtil } from '../../Add-on/BoardCutting/SplitBoardSideModel'; import { Board2Regions } from '../../Add-on/BoardEditor/Board2Regions'; -import { DeserializationBoard2DModeingData, DeserializationBoard3DModeingData, SerializeBoard2DModeingData, SerializeBoard3DModeingData, deserializationBoardData, serializeBoardData } from '../../Add-on/BoardEditor/SerializeBoardData'; +import { DeserializationBoard2DModeingData, DeserializationBoard3DModeingData, DeserializationBoardSideModeingData, SerializeBoard2DModeingData, SerializeBoard3DModeingData, SerializeBoardSideModeingData, deserializationBoardData, serializeBoardData } from '../../Add-on/BoardEditor/SerializeBoardData'; import { DrillType, FaceDirection } from "../../Add-on/DrawDrilling/DrillType"; import { CyHoleInBoard, IBoardRectHoleType, ParseBoardRectHoleType, SetBrHighHoleTypeFromRectHoleType } from '../../Add-on/DrawDrilling/HoleUtils'; import { HostApplicationServices } from '../../ApplicationServices/HostApplicationServices'; @@ -14,13 +15,14 @@ import { Geom3Res } from '../../Common/CSGIntersect'; import { ColorMaterial } from '../../Common/ColorPalette'; import { DisposeThreeObj, Object3DRemoveAll } from '../../Common/Dispose'; import { Log, LogType } from '../../Common/Log'; -import { TransformVector, tempMatrix1 } from '../../Common/Matrix4Utils'; +import { MakeMirrorMtx, TransformVector, tempMatrix1 } from '../../Common/Matrix4Utils'; import { UpdateDraw } from '../../Common/Status'; import { FixedNotZero } from '../../Common/Utils'; import { safeEval } from '../../Common/eval'; import { ObjectSnapMode } from '../../Editor/ObjectSnapMode'; import { SelectPick } from '../../Editor/SelectPick'; import { Board2DModelCSGBuilder } from '../../Geometry/Board2DModelCSG/Board2DModelCSGBuilder'; +import { BoardSideModelCSGBuilder, GetSideCuFaceMtx } from '../../Geometry/Board2DModelCSG/BoardSideModelCSGBuilder'; import { boardUVGenerator, boardUVGenerator2 } from '../../Geometry/BoardUVGenerator'; import { Box3Ext } from '../../Geometry/Box'; import { BufferGeometryUtils } from '../../Geometry/BufferGeometryUtils'; @@ -28,7 +30,7 @@ import { AddCSGSubtractTask, CSGTask, TerminateCSGTask } from '../../Geometry/CS import { EdgesGeometry } from '../../Geometry/EdgeGeometry'; import { AsVector2, AsVector3, IdentityMtx4, XAxis, XAxisN, YAxis, YAxisN, ZAxis, ZeroVec, equaln, equalv3 } from '../../Geometry/GeUtils'; import { PointShapeUtils } from '../../Geometry/PointShapeUtils'; -import { GetBoardHighSeal, GetBoardSealingCurves, GetHighBoardEdgeRemark, SetBoardEdgeRemarkData, SetBoardTopDownLeftRightSealData } from '../../GraphicsSystem/CalcEdgeSealing'; +import { GetBoardContour, GetBoardHighSeal, GetBoardSealingCurves, GetHighBoardEdgeRemark, SetBoardEdgeRemarkData, SetBoardTopDownLeftRightSealData } from '../../GraphicsSystem/CalcEdgeSealing'; import { RenderType } from '../../GraphicsSystem/RenderType'; import { BoardProcessOption } from "../../UI/Store/OptionInterface/BoardProcessOption"; import { CSG2Geometry2, Geometry2CSG2 } from '../../csg/core/Geometry2CSG'; @@ -130,6 +132,8 @@ export class Board extends ExtrudeSolid private _IsChaiDan: boolean = true; private _2DModelingList: I2DModeling[] = []; private _3DModelingList: I3DModeling[] = []; + //侧面造型 + private _SideModelingMap: Map = new Map(); private _CustomNumber: number = null;//自定义编号 private _DrillLock = false; //排钻独立锁 private _DrillAssociationLock = new Set(); //排钻关联锁 @@ -956,6 +960,30 @@ export class Board extends ExtrudeSolid this.Update(UpdateDraw.Geometry); } + //侧面造型 + get SideModelingMap() + { + return this._SideModelingMap; + } + + set SideModelingMap(sideModelingMap: Map) + { + this.WriteAllObjectRecord(); + this.ClearSideModelingCache(); + this._SideModelingMap = sideModelingMap; + this.Update(UpdateDraw.Geometry); + } + + ClearSideModeling() + { + if (!this._SideModelingMap.size) return; + + this.WriteAllObjectRecord(); + this.ClearSideModelingCache(); + this._SideModelingMap.clear(); + this.Update(UpdateDraw.Geometry); + } + ClearModeling2DList() { if (this._2DModelingList.length === 0) return; @@ -1135,6 +1163,9 @@ export class Board extends ExtrudeSolid if (this._SweepPath && !this._FixContourByArcSweepPath_Ing) this.FixArcSweepPathLength(); + if (this.HasSideModel) + this.SplitBoardSideModelUtil.SpiltSideModelOfBrContour(this); + this.Update(); } } @@ -1163,6 +1194,9 @@ export class Board extends ExtrudeSolid if (this._SweepPath && !this._FixContourByArcSweepPath_Ing) this.FixArcSweepPathLength(); + if (this.HasSideModel) + this.SplitBoardSideModelUtil.SpiltSideModelOfBrContour(this); + this.Update(); } } @@ -1173,6 +1207,9 @@ export class Board extends ExtrudeSolid { if (!equaln(thickness, this.thickness, 1e-4))//避免18.0009 无法改成 18 { + if (this.HasSideModel) + this.SplitBoardSideModelUtil.SpiltSideModelOfBrThickness(this, thickness); + super.Thickness = thickness; if (this._SweepPath && !this._FixContourByArcSweepPath_Ing) this.FixContourByArcSweepPath(); @@ -1256,6 +1293,31 @@ export class Board extends ExtrudeSolid this._Name = n; } + private UpdateSplitBoardSideModelUtil = true; + private _SplitBoardSideModelUtil: SplitBoardSideModelUtil; + + get SplitBoardSideModelUtil(): SplitBoardSideModelUtil + { + if (!this._SplitBoardSideModelUtil) + this._SplitBoardSideModelUtil = new SplitBoardSideModelUtil(this); + + return this._SplitBoardSideModelUtil; + } + + override GeneralRectContour() + { + //取消异型时,强制使用矩形轮廓 导致原始轮廓数据丢失 + if (this.HasSideModel) + { + this.UpdateSplitBoardSideModelUtil = false; + this.SplitBoardSideModelUtil.Init(this, true); + super.GeneralRectContour(); + this.UpdateSplitBoardSideModelUtil = true; + } + else + super.GeneralRectContour(); + } + /** * 板件的轮廓,在板件坐标系中的表现方式. */ @@ -1284,6 +1346,18 @@ export class Board extends ExtrudeSolid let oldHightSealDatas = GetBoardHighSeal(this, oldHightSealCurves);//旧的封边数据 let oldHighBoardEdgeRemarkDatas = GetHighBoardEdgeRemark(this, oldHightSealCurves);//旧的封边数据 + let splitSideModel = false; + + if (this.UpdateSplitBoardSideModelUtil && this.HasSideModel) + { + this.SplitBoardSideModelUtil.Init(this); //旧的侧面造型 + + //记录侧面造型后清空 防止在分裂侧面造型时带入更新mesh + this.WriteAllObjectRecord(); + this._SideModelingMap.clear(); + splitSideModel = true; + } + let oldContour = this.ContourCurve;//旧的轮廓 let defaultDrillType = this._BoardProcessOption.drillType; @@ -1357,6 +1431,10 @@ export class Board extends ExtrudeSolid this._BoardProcessOption.highBoardEdgeRemark.push(oldHighBoardEdgeRemarkDatas[closesIndex]); } } + + //分裂侧面造型 + if (splitSideModel || this.HasSideModel) + this.SplitBoardSideModelUtil.SetBoardSideModel(this); } Explode() @@ -1791,9 +1869,38 @@ export class Board extends ExtrudeSolid } //#endregion + //#region 侧面造型 + _SideModeingCsgs: Geom3[]; + private GetSideModeingCsgs(): Geom3[] + { + if (this._SideModeingCsgs) + return this._SideModeingCsgs; + + this._SideModeingCsgs = []; + + if (!this._SideModelingMap.size) + return this._SideModeingCsgs; + + this._SideModeingCsgs = BoardSideModelCSGBuilder(this); + + return this._SideModeingCsgs; + } + + //清除侧面造型Csgs的缓存 + ClearSideModelingCache() + { + this._SideModeingCsgs = undefined; + } + //#endregion 侧面造型 + + get HasSideModel() { return this._SideModelingMap.size > 0; } + private _asyncSideModelIng = false; + get AsyncSideModeling() { return this._asyncSideModelIng; } + protected get Has2DPath() { return this._2DModelingList.length > 0; } private _workerCalcedGeom: Geom3 = null;//worker计算后,暂时存入到这里 private _async2DPathIng = false; + get Async2DPathing() { return this._async2DPathIng; } override GoodBye(): void { @@ -1815,8 +1922,25 @@ export class Board extends ExtrudeSolid { if (!this._workerCalcedGeom) { - let path2dCsgs = this.Get2DPathCsgs(); - if (!path2dCsgs.length || !HostApplicationServices.show2DPathObject) + const AllSubtractCSGs: Geom3[] = []; + + this._async2DPathIng = false; + this._asyncSideModelIng = false; + + if (HostApplicationServices.show2DPathObject) + for (let csg of this.Get2DPathCsgs()) + { + AllSubtractCSGs.push(csg); + this._async2DPathIng = true; + } + + for (let csg of this.GetSideModeingCsgs()) + { + AllSubtractCSGs.push(csg); + this._asyncSideModelIng = true; + } + + if (!AllSubtractCSGs.length) { if (geo instanceof Geometry) return new BufferGeometry().fromGeometry(geo); @@ -1824,18 +1948,18 @@ export class Board extends ExtrudeSolid } let geom = Geometry2CSG2(geo); - this._async2DPathIng = true; + // 使用线程池 const task: CSGTask = { key: this, - data: [path2dCsgs, geom], + data: [AllSubtractCSGs, geom], then: (e) => { let data = e.data as { status: number, geom: Geom3; }; if (data.status || !data.geom.polygons) { - this._AsyncIngTextEntity.TextString = "二维刀路建模失败"; - Log(`板:${this.Name}二维刀路建模失败!`, LogType.Error, [this]); + this._AsyncIngTextEntity.TextString = "二维刀路或侧面造型建模失败"; + Log(`板:${this.Name}二维刀路或侧面造型建模失败!`, LogType.Error, [this]); return; } @@ -1864,6 +1988,7 @@ export class Board extends ExtrudeSolid } this._async2DPathIng = false; + this._asyncSideModelIng = false; const newGeom = this._workerCalcedGeom; this._workerCalcedGeom = undefined;//保护 @@ -2167,7 +2292,7 @@ export class Board extends ExtrudeSolid private DrawAsyncText(obj: Object3D) { - if (this._async2DPathIng) + if (this._async2DPathIng || this._asyncSideModelIng) { //#region 添加文字 if (!this._AsyncIngTextEntity) @@ -2185,7 +2310,10 @@ export class Board extends ExtrudeSolid else this._AsyncIngTextEntity.OCSNoClone.identity().setPosition(this.width * 0.5, this.height * 0.5, this.thickness * 0.5); - this._AsyncIngTextEntity.TextString = "二维刀路建模中!"; + if (this._async2DPathIng) + this._AsyncIngTextEntity.TextString = "二维刀路建模中!"; + else if (this._asyncSideModelIng) + this._AsyncIngTextEntity.TextString = "侧面造型建模中!"; let o = this._AsyncIngTextEntity.GetDrawObjectFromRenderType(RenderType.Conceptual); if (o) @@ -2289,6 +2417,8 @@ export class Board extends ExtrudeSolid else obj.add(path2d); } + + this.GetSideModeingCsgs(); } } @@ -2337,6 +2467,51 @@ export class Board extends ExtrudeSolid } } + private AddSideModelGripPoints(pts: Vector3[], dragPointType: DragPointType) + { + let con = GetBoardContour(this); + let inverseZ = con.Area2 < 0; + let cus = con.Explode() as Curve[]; + + for (let [index, soilds] of this.SideModelingMap) + { + let cu = cus[index]; + if (!cu) continue; + + let mt4 = GetSideCuFaceMtx(cus[index], inverseZ); + + for (let soild of soilds) + { + const MirrorMtxZ = MakeMirrorMtx(ZAxis); + let s = soild.Clone(); + s.ApplyMatrix(MirrorMtxZ); + s.ApplyMatrix(mt4); + s.ApplyMatrix(this.OCSNoClone); + pts.push(...s.GetGripOrStretchPoints(dragPointType)); + } + } + } + + override GetStrectchPointCountList(dragType: DragPointType): number[] + { + let counts = super.GetStrectchPointCountList(dragType); + if (this.HasSideModel) + { + for (let [num, soilds] of this.SideModelingMap) + { + for (let soild of soilds) + { + let c = soild.ContourCurve.GetDragPointCount(dragType) * 2; + for (let g of soild.Grooves) + c += g.ContourCurve.GetDragPointCount(dragType) * 2; + + counts.push(c); + } + } + } + return counts; + } + //因为圆弧板 我们重载了它 override GetGripPoints(): Vector3[] { @@ -2387,8 +2562,13 @@ export class Board extends ExtrudeSolid p.applyMatrix4(mtx); } } + + if (this.HasSideModel) + this.AddSideModelGripPoints(pts, DragPointType.Grip); + return pts; } + override MoveGripPoints(indexList: number[], vec: Vector3): void { if (indexList.length === 0) return; @@ -2429,6 +2609,7 @@ export class Board extends ExtrudeSolid } } + this.ClearSideModelingCache(); super.MoveGripPoints(indexList, vec); } @@ -2482,6 +2663,10 @@ export class Board extends ExtrudeSolid { pts.push(...m.path.GetStretchPoints().map(p => p.add(new Vector3(0, 0, m.dir === FaceDirection.Front ? this.thickness : 0)).applyMatrix4(this.OCSNoClone))); } + + if (this.HasSideModel) + this.AddSideModelGripPoints(pts, DragPointType.Stretch); + return pts; } @@ -2682,6 +2867,7 @@ export class Board extends ExtrudeSolid this.Clear2DPathCache(); this.Clear3DPathCache(); + this.ClearSideModelingCache(); this.Update(UpdateDraw.Geometry); } @@ -2996,12 +3182,16 @@ export class Board extends ExtrudeSolid if (ver > 19) this.AlignLineObject = file.ReadHardObjectId(); + + if (ver > 20) + DeserializationBoardSideModeingData(file, this._SideModelingMap); + this.ClearSideModelingCache(); } WriteFile(file: CADFiler) { super.WriteFile(file); - file.Write(20); + file.Write(21); // file.Write(this._SpaceOCS.toArray()); ver < 6 file.Write(this._BoardType); file.Write(this._Name); @@ -3098,6 +3288,9 @@ export class Board extends ExtrudeSolid //ver 20 file.WriteHardObjectId(this.AlignLineObject); + + //ver 21 + SerializeBoardSideModeingData(file, this._SideModelingMap); } } diff --git a/src/DatabaseServices/Entity/Extrude.ts b/src/DatabaseServices/Entity/Extrude.ts index 41b5c62ea..d5cb31003 100644 --- a/src/DatabaseServices/Entity/Extrude.ts +++ b/src/DatabaseServices/Entity/Extrude.ts @@ -13,6 +13,7 @@ import { MakeMirrorMtx, TransformVector, Vector2ApplyMatrix4, reviseMirrorMatrix import { Status, UpdateDraw } from "../../Common/Status"; import { ObjectSnapMode } from "../../Editor/ObjectSnapMode"; import { BSPGroupParse } from '../../Geometry/BSPGroupParse'; +import { GetSideCuFaceMtx } from '../../Geometry/Board2DModelCSG/BoardSideModelCSGBuilder'; import { boardUVGenerator } from "../../Geometry/BoardUVGenerator"; import { Box3Ext } from "../../Geometry/Box"; import { BufferGeometryUtils } from "../../Geometry/BufferGeometryUtils"; @@ -22,6 +23,7 @@ import { ExtrudeGeometryBuilder } from "../../Geometry/ExtrudeMeshGeomBuilder/Ex import { AsVector2, IdentityMtx4, MoveMatrix, XAxis, YAxis, ZAxis, ZeroVec, equaln, equalv2, equalv3, isIntersect, isParallelTo, isPerpendicularityTo } from "../../Geometry/GeUtils"; import { OBB } from "../../Geometry/OBB/obb"; import { ScaleUV, ScaleUV2 } from "../../Geometry/UVUtils"; +import { GetBoardContour } from '../../GraphicsSystem/CalcEdgeSealing'; import { RenderType } from "../../GraphicsSystem/RenderType"; import { Geometry2CSG2 } from "../../csg/core/Geometry2CSG"; import { BlockTableRecord } from "../BlockTableRecord"; @@ -35,6 +37,7 @@ import { ShapeManager } from "../ShapeManager"; import { Spline } from "../Spline"; import { Board } from "./Board"; import { Circle } from "./Circle"; +import { Curve } from './Curve'; import { DragPointType } from "./DragPointType"; import { Ellipse } from "./Ellipse"; import { Entity } from "./Entity"; @@ -371,6 +374,12 @@ export class ExtrudeSolid extends Entity return this.grooves; } + //侧面造型 + get SideModelingMap(): Map + { + return undefined; + } + /** * 返回未拷贝的轮廓曲线 */ @@ -886,7 +895,9 @@ export class ExtrudeSolid extends Entity this.WriteAllObjectRecord(); let counts = this.GetStrectchPointCountList(dragType); - if (dragType === DragPointType.Stretch && indexList.length === arraySum(counts)) + const isGrip = dragType === DragPointType.Grip; + + if (!isGrip && indexList.length === arraySum(counts)) { this.Position = this.Position.add(vec); return; @@ -897,7 +908,7 @@ export class ExtrudeSolid extends Entity let updateBak = this.AutoUpdate; this.AutoUpdate = false; - if (this.grooves.length === 0) + if (!this.grooves.length && !this.HasSideModel) { this.MoveGripOrStretchPointsOnly(indexList, vec, dragType); } @@ -908,6 +919,34 @@ export class ExtrudeSolid extends Entity let offset = 0; let grooveIndex = -1; + let sideModelIndex = -1; + + let cus: Curve[]; + let baseIndexList: Set = new Set(); + let sideModelSealCurveMtxCache: Map = new Map(); + + //获取侧面的OCS + const GetSideModelSealCurveMtx = (num: number): Matrix4 => + { + if (!cus) cus = GetBoardContour(this as unknown as Board)?.Explode() as Curve[]; + let mtx = sideModelSealCurveMtxCache.get(num); + + if (!mtx && cus?.length) + { + let cu = cus[num]; + if (!cu) return new Matrix4; + + let x = cu.GetFirstDeriv(0).normalize().applyMatrix4(this.OCS.setPosition(0, 0, 0)); + let y = this.Normal; + let z = x.clone().cross(y); + mtx = new Matrix4().getInverse(new Matrix4().makeBasis(x, y, z)); + + sideModelSealCurveMtxCache.set(num, mtx); + } + + return mtx ?? new Matrix4; + }; + for (let count of counts) { offset += count; @@ -923,11 +962,145 @@ export class ExtrudeSolid extends Entity if (ilist.length > 0) { if (grooveIndex === -1) + { + let orgCus = GetBoardContour(this as unknown as Board).Explode() as Curve[]; this.MoveGripOrStretchPointsOnly(ilist, vec, dragType); - else + + if (this.HasSideModel) + { + //修正点的索引 判断侧面造型的起点是否被移动 + let stretchCount = this.ContourCurve.GetDragPointCount(dragType); + for (let num of ilist) + { + if (num < stretchCount) + baseIndexList.add(num); + else + baseIndexList.add(num - stretchCount); + } + let isChangeThiness = this.IsStretchThickness(Array.from(baseIndexList)); + //起点被拉伸时反向移动 达到相对静止状态 + const sideModelingMap: Map = this.SideModelingMap; + + for (let [num, soilds] of sideModelingMap) + { + let firstIndex = num; + let secondIndex = (num + 1) === stretchCount ? 0 : num + 1; + + if (isGrip) + { + firstIndex = num * 2; + //拉取中点时 + secondIndex = (firstIndex - 1) < 0 ? (stretchCount - 1) : (firstIndex - 1); + } + + //Grip的时候点选中点时两边相连的 也反向移动 firstIndex - 1 + if ( + !isGrip && (baseIndexList.has(firstIndex) && !baseIndexList.has(secondIndex)) || + isGrip && (baseIndexList.has(firstIndex) || baseIndexList.has(secondIndex)) || + isChangeThiness && ilist[0] < stretchCount + ) + { + for (let s of soilds) + { + let mtx = GetSideModelSealCurveMtx(num); + let v = vec.clone().applyMatrix4(mtx).setZ(0); + if (isChangeThiness) + { + v.setX(0); + } + else + { + if (cus?.length && cus[num]) + v = new Vector3(orgCus[num].Length - cus[num].Length); + else + v.setY(0); + } + + s.ApplyMatrix(new Matrix4().setPosition(v.clone().multiplyScalar(-1))); + } + } + } + } + } + else if (grooveIndex < this.grooves.length) this.grooves[grooveIndex].MoveGripOrStretchPoints(ilist, vec, dragType); + else + { + //侧面造型拉伸 + const sideModelingMap: Map = this.SideModelingMap; + + let soildCount = 0; + const isMainChangeThiness = this.IsStretchThickness(Array.from(baseIndexList)); + + for (let [num, soilds] of sideModelingMap) + { + soildCount += soilds.length; + + if (sideModelIndex < soildCount) + { + let mtx = GetSideModelSealCurveMtx(num); + let v = vec.clone().applyMatrix4(mtx); + + if (isMainChangeThiness) v.setX(0); + + let soild = soilds[soilds.length - (soildCount - sideModelIndex)]; + const stretchPtLength = soild.ContourCurve.GetStretchPoints().length; + const firstIndex = num; + const secondIndex = (num + 1) === stretchPtLength ? 0 : num + 1; + + const mainSoildIList: number[] = []; + const grooveSoildIList: number[] = []; + + for (let k of ilist) + { + if (k < stretchPtLength * 2) + mainSoildIList.push(k); + else + grooveSoildIList.push(k); + } + + //改变侧面造型厚度 + if (soild.IsStretchThickness(mainSoildIList)) + { + if (mainSoildIList.every(i => i >= stretchPtLength)) + { + //造型 底边 + v.setZ(-v.z); + } + else if ((baseIndexList.has(firstIndex) && baseIndexList.has(secondIndex))) + { + //造型见光面 和侧面一起移动 + ilist = []; + for (let k = 0; k < stretchPtLength; k++) + ilist.push(k + stretchPtLength); + + //有选中子槽端点 默认一起改变 + if (grooveSoildIList.length) + { + const grooveStretchPtLength = soild.grooves[0].ContourCurve.GetStretchPoints().length; + + for (let k = 0; k < grooveStretchPtLength; k++) + ilist.push(k + grooveStretchPtLength + stretchPtLength * 2); + } + } + else + break; + } + else if (!isMainChangeThiness && baseIndexList.has(firstIndex) && baseIndexList.has(secondIndex)) + break; + else + v.setZ(0); + + soild.MoveGripOrStretchPoints(ilist, v, dragType); + break; + } + } + } } grooveIndex++; + + if (grooveIndex >= this.grooves.length) + sideModelIndex++; } } @@ -937,6 +1110,14 @@ export class ExtrudeSolid extends Entity let splitEntitys: this[] = []; this.GrooveCheckAll(splitEntitys); + if (this.HasSideModel) + { + let board = (this as unknown as Board); + board.SplitBoardSideModelUtil.Init(board); + board.SplitBoardSideModelUtil.SpiltSideModelOfBrContour(board); + board.SplitBoardSideModelUtil.SpiltSideModelOfBrThickness(board, board.Thickness); + } + if (splitEntitys.length > 0 && this.Owner) { let ms = this.Owner.Object as BlockTableRecord; @@ -2012,7 +2193,7 @@ export class ExtrudeSolid extends Entity } protected get Has2DPath() { return false; } - + protected get HasSideModel() { return false; } protected _EdgeGeometry: EdgesGeometry | BufferGeometry; get EdgeGeometry() { @@ -2026,7 +2207,7 @@ export class ExtrudeSolid extends Entity return this._EdgeGeometry; //这里我们超过100就用这个,为了性能 和MaxDrawGrooveCount不一致 - if (this.grooves.length > 100 || (!this.Has2DPath && (this.grooves.length === 0 || this.grooves.every(g => equaln(g.thickness, this.thickness)))) + if (this.grooves.length > 100 || (!this.Has2DPath && !this.HasSideModel && (this.grooves.length === 0 || this.grooves.every(g => equaln(g.thickness, this.thickness)))) ) { let coords = FastExtrudeEdgeGeometry(this, this.DrawColorIndex, 12, true); @@ -2141,6 +2322,11 @@ export class ExtrudeSolid extends Entity new LineSegments(this.EdgeGeometry, ColorMaterial.GetConceptualEdgeMaterial()) ); obj.add(this.GetModelGroove()); + + if (this.SideModelingMap?.size) + { + obj.add(this.GetSideModelGroove()); + } } else if (renderType === RenderType.Physical) { @@ -2175,6 +2361,7 @@ export class ExtrudeSolid extends Entity ); } } + UpdateDrawObjectMaterial(renderType: RenderType, obj: Object3D) { if (renderType === RenderType.Wireframe) @@ -2230,11 +2417,47 @@ export class ExtrudeSolid extends Entity const meshGeo = grooveClone.MeshGeometry; meshGeo.applyMatrix4(mtx); - const mesh = new Mesh(meshGeo, ColorMaterial.GetConceptualMaterial(1, 0.6)); + const mesh = new Mesh(meshGeo, ColorMaterial.GetBasicMaterialTransparent2(1, 0.6)); group.add(mesh, line); } return group; } + + private GetSideModelGroove() + { + let board = this as unknown as Board; + const group = new Group(); + + let con = GetBoardContour(board); + let inverseZ = con.Area2 < 0; + let cus = con.Explode() as Curve[]; + const mirrorMtxZ = MakeMirrorMtx(ZAxis); + + for (let [index, soilds] of board.SideModelingMap) + { + let cu = cus[index]; + if (!cu) continue; + + let mt4 = GetSideCuFaceMtx(cus[index], inverseZ); + + for (let soild of soilds) + { + const edgeGeo = soild.EdgeGeometry; + const line = new LineSegments(edgeGeo, ColorMaterial.GetLineMaterial(1, this.Freeze)); + line.applyMatrix4(mirrorMtxZ); + line.applyMatrix4(mt4.clone().multiply(soild.OCS)); + + const meshGeo = soild.MeshGeometry; + const mesh = new Mesh(meshGeo, ColorMaterial.GetBasicMaterialTransparent2(1, 0.6)); + mesh.applyMatrix4(mirrorMtxZ); + mesh.applyMatrix4(mt4.clone().multiply(soild.OCS)); + group.add(mesh, line); + } + } + + return group; + } + UpdateJigMaterial(color = 8) { diff --git a/src/Editor/CommandRegister.ts b/src/Editor/CommandRegister.ts index 6e79d544f..6b3f3941d 100644 --- a/src/Editor/CommandRegister.ts +++ b/src/Editor/CommandRegister.ts @@ -218,6 +218,7 @@ import { ImportCFData } from "../Add-on/CF/Import/CFImport"; import { ChangeColorByRoomCabinet } from "../Add-on/ChangeColorByRoomOrCabinet/ChangeColorByRoomOrCabinet"; import { Command_ClearCDBrHoleModeling } from "../Add-on/ClearCDBrHoleModeling"; import { Command_RemovePolylineRepeatPos } from "../Add-on/Cmd_RemovePolylineRepeatPos"; +import { SideModelFeedingCommand } from "../Add-on/CommandSideModelFeediing"; import { Command_Modeling } from "../Add-on/Command_Modeling"; import { Command_PickUp2DModelCsgs } from "../Add-on/Command_PickUp2DModelCsgs"; import { Command_TemplateGroup } from "../Add-on/Command_TemplateGroup"; @@ -580,6 +581,8 @@ export function registerCommand() for (let i = 0; i <= 255; i++) commandMachine.RegisterCommand(i.toString(), new ChangeColor(i)); commandMachine.RegisterCommand(CommandNames.TestM, new FeedingCommand()); //模拟走刀 + commandMachine.RegisterCommand(CommandNames.TestSideModeling, new SideModelFeedingCommand()); //模拟侧面造型走刀 + commandMachine.RegisterCommand(CommandNames.TestFb, new TestFb()); //模拟封边 commandMachine.RegisterCommand(CommandNames.Mirror, new MirrorCommand()); diff --git a/src/Geometry/Board2DModelCSG/BoardSideModelCSGBuilder.ts b/src/Geometry/Board2DModelCSG/BoardSideModelCSGBuilder.ts new file mode 100644 index 000000000..2dc0148be --- /dev/null +++ b/src/Geometry/Board2DModelCSG/BoardSideModelCSGBuilder.ts @@ -0,0 +1,52 @@ +import { Geom3, transform } from "@jscad/modeling/src/geometries/geom3"; +import { Mat4 } from "@jscad/modeling/src/maths/mat4"; +import { Matrix4 } from "three"; +import { MakeMirrorMtx } from "../../Common/Matrix4Utils"; +import { Board } from "../../DatabaseServices/Entity/Board"; +import { Curve } from "../../DatabaseServices/Entity/Curve"; +import { GetBoardContour } from "../../GraphicsSystem/CalcEdgeSealing"; +import { Geometry2CSG2 } from "../../csg/core/Geometry2CSG"; +import { ZAxis } from "../GeUtils"; + +export function BoardSideModelCSGBuilder(board: Board): Geom3[] +{ + const sideModeingCsgs: Geom3[] = []; + + if (!board.SideModelingMap.size) + return sideModeingCsgs; + + let con = GetBoardContour(board); + let inverseZ = con.Area2 < 0; + let cus = con.Explode() as Curve[]; + const mirrorMtxZ = MakeMirrorMtx(ZAxis); + + for (let [index, soilds] of board.SideModelingMap) + { + let cu = cus[index]; + if (!cu) continue; + + let mt4 = GetSideCuFaceMtx(cus[index], inverseZ); + + for (let soild of soilds) + { + let s = soild.Clone(); + let geom3 = Geometry2CSG2(s.MeshGeometry); + geom3 = transform(mirrorMtxZ.elements as Mat4, geom3); + geom3 = transform(mt4.clone().multiply(soild.OCS).elements as Mat4, geom3); + sideModeingCsgs.push(geom3); + } + } + return sideModeingCsgs; +} + +export function GetSideCuFaceMtx(cu: Curve, inverseZ = false): Matrix4 +{ + let x = cu.GetFirstDeriv(0).normalize(); + let y = ZAxis; + let z = x.clone().cross(y); + if (inverseZ) z.negate(); + + let basePt = cu.StartPoint; + + return new Matrix4().makeBasis(x, y, z).setPosition(basePt); +} diff --git a/src/Geometry/CreateWireframe.ts b/src/Geometry/CreateWireframe.ts index 01302b4a9..51159afda 100644 --- a/src/Geometry/CreateWireframe.ts +++ b/src/Geometry/CreateWireframe.ts @@ -1,6 +1,7 @@ import { BufferGeometry, Float32BufferAttribute, Geometry, Line, LineBasicMaterial, LineSegments, Matrix4, Object3D, Vector3 } from "three"; import { FaceDirection } from "../Add-on/DrawDrilling/DrillType"; import { ColorMaterial } from "../Common/ColorPalette"; +import { MakeMirrorMtx } from "../Common/Matrix4Utils"; import { FixIndex } from "../Common/Utils"; import { ExtrudeHole } from "../DatabaseServices/3DSolid/ExtrudeHole"; import { Contour } from "../DatabaseServices/Contour"; @@ -9,7 +10,9 @@ import { Curve } from "../DatabaseServices/Entity/Curve"; import { ExtrudeSolid } from "../DatabaseServices/Entity/Extrude"; import { Shape } from "../DatabaseServices/Shape"; import { Shape2 } from "../DatabaseServices/Shape2"; -import { MatrixIsIdentityCS, MoveMatrix } from "./GeUtils"; +import { GetBoardContour } from "../GraphicsSystem/CalcEdgeSealing"; +import { GetSideCuFaceMtx } from "./Board2DModelCSG/BoardSideModelCSGBuilder"; +import { MatrixIsIdentityCS, MoveMatrix, ZAxis } from "./GeUtils"; //FIXME: #IWBPB 性能缺陷和BUG. 等待废弃或者改进 export function CreateWireframe(en3D: Board | ExtrudeSolid) @@ -144,6 +147,36 @@ export function FastWireframe(br: ExtrudeSolid, color = 0, divCount = 6, optArc } } + if (br instanceof Board && br.HasSideModel) + { + let con = GetBoardContour(br); + if (con) + { + let inverseZ = con.Area2 < 0; + let cus = con.Explode() as Curve[]; + const mirrorMtxZ = MakeMirrorMtx(ZAxis); + + for (let [index, soilds] of br.SideModelingMap) + { + let cu = cus[index]; + if (!cu) continue; + + let mt4 = GetSideCuFaceMtx(cus[index], inverseZ); + for (let soild of soilds) + { + let lines = FastWireframe(soild, color, 3, false); + for (let line of lines) + { + line.applyMatrix4(mirrorMtxZ); + line.applyMatrix4(soild.OCSNoClone); + line.applyMatrix4(mt4); + result.push(line); + } + } + } + } + } + return result; } diff --git a/src/Geometry/DrillParse/ParseBoardSideFace.ts b/src/Geometry/DrillParse/ParseBoardSideFace.ts new file mode 100644 index 000000000..4030162dd --- /dev/null +++ b/src/Geometry/DrillParse/ParseBoardSideFace.ts @@ -0,0 +1,47 @@ +import { Matrix4 } from "three"; +import { Arc } from "../../DatabaseServices/Entity/Arc"; +import { Board } from "../../DatabaseServices/Entity/Board"; +import { Curve } from "../../DatabaseServices/Entity/Curve"; +import { GetBoardContour } from "../../GraphicsSystem/CalcEdgeSealing"; +import { GetSideCuFaceMtx } from "../Board2DModelCSG/BoardSideModelCSGBuilder"; +import { equaln } from "../GeUtils"; +import { BoardFaceType, BoardGetFace } from "./BoardGetFace"; +import { Face } from "./Face"; + +export class ParseBoardSideFace extends BoardGetFace +{ + constructor(public Board: Board) + { + super(Board); + } + + ParseFaces() + { + this.GetSideFaces(); + } + + GetSideFaces() + { + let con = GetBoardContour(this.Board); + let inverseZ = con.Area2 < 0; + let cus = con.Explode() as Curve[]; + for (let cu of cus) + { + let type = BoardFaceType.Side; + + let length = cu.Length; + if (equaln(length, 0) || cu instanceof Arc) + type = BoardFaceType.NoSide; + + let mtx = GetSideCuFaceMtx(cu, inverseZ); + let face = new Face({ + type, + localBoard: this.Board, + matrix4: new Matrix4().multiplyMatrices(this.Board.OCS.clone(), mtx), + length, + width: this.Board.Thickness, + }); + this.Faces.push(face); + } + } +} diff --git a/src/GraphicsSystem/CameraUpdate.ts b/src/GraphicsSystem/CameraUpdate.ts index 7e713bc51..0d16edd13 100644 --- a/src/GraphicsSystem/CameraUpdate.ts +++ b/src/GraphicsSystem/CameraUpdate.ts @@ -76,6 +76,10 @@ export class CameraUpdate } get Target() { return this._Target; } + set Target(value: Vector3) + { + this._Target = value; + } get Camera(): Camera { diff --git a/src/GraphicsSystem/ToolPath/FeedingToolPath.ts b/src/GraphicsSystem/ToolPath/FeedingToolPath.ts index bc9561d38..b9cd96e2f 100644 --- a/src/GraphicsSystem/ToolPath/FeedingToolPath.ts +++ b/src/GraphicsSystem/ToolPath/FeedingToolPath.ts @@ -4,24 +4,28 @@ import { HostApplicationServices } from "../../ApplicationServices/HostApplicati import { arrayRemoveIf } from "../../Common/ArrayExt"; import { ConverCircleToPolyline, IsRect, MergeCurvelist, equalCurve } from "../../Common/CurveUtils"; import { LogEnable } from "../../Common/Log"; +import { MakeMirrorMtx } from "../../Common/Matrix4Utils"; import { Singleton } from "../../Common/Singleton"; import { ExtrudeHole } from "../../DatabaseServices/3DSolid/ExtrudeHole"; import { Contour } from "../../DatabaseServices/Contour"; import { Board, IModeling } from "../../DatabaseServices/Entity/Board"; import { Circle } from "../../DatabaseServices/Entity/Circle"; import { Curve } from "../../DatabaseServices/Entity/Curve"; -import { ExtrudeContourCurve } from "../../DatabaseServices/Entity/Extrude"; +import { ExtrudeContourCurve, ExtrudeSolid } from "../../DatabaseServices/Entity/Extrude"; import { Line } from "../../DatabaseServices/Entity/Line"; import { Polyline } from "../../DatabaseServices/Entity/Polyline"; import { Shape } from "../../DatabaseServices/Shape"; import { ShapeManager } from "../../DatabaseServices/ShapeManager"; +import { GetSideCuFaceMtx } from "../../Geometry/Board2DModelCSG/BoardSideModelCSGBuilder"; import { Box3Ext } from "../../Geometry/Box"; import { Route } from "../../Geometry/CurveMap"; import { CanDrawHoleFuzz } from "../../Geometry/DrillParse/CanDrawHoleFuzz"; -import { AsVector2, equaln, isParallelTo } from "../../Geometry/GeUtils"; +import { ParseBoardSideFace } from "../../Geometry/DrillParse/ParseBoardSideFace"; +import { AsVector2, ZAxis, equaln, isParallelTo } from "../../Geometry/GeUtils"; import { RegionParse } from "../../Geometry/RegionParse"; import { FixIndex } from "../../Nest/Common/Util"; -import { ParseArcBoardHoles } from "../../Production/Product"; +import { ConverToPtsBul } from "../../Production/Convert2PtsBul"; +import { IOriginSideModelingData, ModelType, ParseArcBoardHoles } from "../../Production/Product"; import { BoolOpeartionType, isTargetCurInOrOnSourceCur } from "../BoolOperateUtils"; import { GetBoardContour } from "../CalcEdgeSealing"; import { GetCurveToInDir, GetOffsetCurves, OptimizeToolPath } from "./OptimizeToolPath"; @@ -211,8 +215,8 @@ export class FeedingToolPath extends Singleton TestCalcPath(br: Board, isCd = false, rk = 0) { let modelings = br.BoardModeling; - let allModeling = GetModelingFromCustomDrill(br); - modelings.push(...allModeling.modeling); + let { modeling } = GetModelingFromCustomDrill(br); + modelings.push(...modeling); if (isCd && HostApplicationServices.chaidanOption.useDefaultRad) modelings.forEach(m => m.knifeRadius = HostApplicationServices.chaidanOption.radius); if (isCd) @@ -287,6 +291,46 @@ export class FeedingToolPath extends Singleton } return cus; } + + //获取侧面造型走刀 + GetSideModelFeedPath(solid: ExtrudeSolid, faceContour: Polyline, redundancyKnif = 0): Curve[] + { + let cus: Curve[] = [];//返回走刀路径 + + let shape = solid.Shape; + let thickness = solid.Thickness; + let knifeRadius = solid.KnifeRadius; + let addLen = solid.GroovesAddLength; + let addDepth = solid.GroovesAddDepth; + let addWidth = solid.GroovesAddWidth; + + if (!knifeRadius) knifeRadius = 3; + + if (addDepth) + thickness += addDepth; + + if (thickness < 1e-5) return cus; + shape = shape.Clone().ApplyMatrix(solid.OCSNoClone); + shape.Z0(); + this.GrooveAddSize(shape, addLen, addWidth); + this.HandleThoughGroove(faceContour, shape, knifeRadius); + + //造型半径和刀半径相等,返回重合点的线 + let outline = shape.Outline.Curve; + if (outline instanceof Circle && equaln(outline.Radius, knifeRadius)) + return [new Polyline([{ pt: AsVector2(outline.Center), bul: 0 }, { pt: AsVector2(outline.Center), bul: 0 }])]; + + // { + // todo 全深槽 + // } + + let offsetCus = this.HandleShape(shape, knifeRadius, true, redundancyKnif); + if (offsetCus.length > 1) + cus.push(...OptimizeToolPath(offsetCus, shape, knifeRadius)); + + return cus; + } + private GrooveAddSize(shape: Shape, addLen: number, addWidth: number) { shape.Outline.Curve.Position = shape.Outline.Curve.Position.setZ(0); @@ -359,6 +403,7 @@ export class FeedingToolPath extends Singleton return cons; } + //检查正反面造型 CheckModeling(br: Board) { let errorIndexs: number[] = []; @@ -377,13 +422,54 @@ export class FeedingToolPath extends Singleton } return errorIndexs; } + + //检查侧面造型 + CheckSideModeling(br: Board) + { + if (!br.SideModelingMap.size) return []; + + let errorSideModel: ExtrudeSolid[] = []; + + let faces = new ParseBoardSideFace(br); + + let con = GetBoardContour(br); + let inverseZ = con.Area2 < 0; + let cus = con.Explode() as Curve[]; + const mirrorMtxZ = MakeMirrorMtx(ZAxis); + + for (let [n, solids] of br.SideModelingMap) + { + let faceContour = faces.Faces[n].Region.ShapeManager.ShapeList[0].Outline.Curve as Polyline; + let mt4 = GetSideCuFaceMtx(cus[n], inverseZ); + + for (let solid of solids) + { + // 圆造型拆成孔 + // let cu = solid.Shape.Outline.Curve; + // if (!solid.Shape.Holes.length && cu instanceof Circle && cu.Radius < HostApplicationServices.chaidanOption.modeling2HoleRad + 1e-6) + // continue; + + let paths = this.GetSideModelFeedPath(solid, faceContour);//走刀路径 + if (!paths.length) + { + let s = solid.Clone(); + s.ApplyMatrix(mirrorMtxZ); + s.ApplyMatrix(mt4); + s.ApplyMatrix(br.OCS); + errorSideModel.push(s); + } + } + } + return errorSideModel; + } + CheckCustomHole(br: Board) { - let { modeling, sideModeling } = GetModelingFromCustomDrill(br); + let { modeling } = GetModelingFromCustomDrill(br); let errHoles: ExtrudeHole[] = []; - for (let m of [...modeling, ...sideModeling]) + for (let m of modeling) { let cu = m.shape.Outline.Curve; if (m.shape.Holes.length === 0 && cu instanceof Circle && cu.Radius < HostApplicationServices.chaidanOption.modeling2HoleRad + 1e-6) @@ -539,10 +625,11 @@ export function GetModelingFromCustomDrill(br: Board) { let normal = br.Normal; let brInv = br.OCSInv; - let outline = GetBoardContour(br); + let originOutline = GetBoardContour(br) as Polyline; + let outline = originOutline; let modeling: (IModeling & { originEn: ExtrudeHole; })[] = []; - let sideModeling: (IModeling & { originEn: ExtrudeHole; })[] = []; + let sideModeling: IOriginSideModelingData[] = []; const holes: ExtrudeHole[] = []; let bbox = br.BoundingBoxInOCS; @@ -632,11 +719,10 @@ export function GetModelingFromCustomDrill(br: Board) let boxSize = faceRegionsBox.getSize(new Vector3); let extrude = Board.CreateBoard(boxSize.y, boxSize.x, 1); - outline = extrude.ContourCurve; + outline = extrude.ContourCurve as Polyline; } } - let diff = brInv.clone().multiply(hole.OCS); shape.ApplyMatrix(diff); let thickness: number; @@ -686,48 +772,68 @@ export function GetModelingFromCustomDrill(br: Board) } else { - //侧面非圆造型 - //TODO: 拆成侧面造型孔 + if (min.z <= 0 || max.z >= br.Thickness) continue; + let spt = hole.Position.applyMatrix4(brInv).setZ(0); + // 有可能Z向量朝向轮廓内部 + // if (outline.PtOnCurve(spt)) continue; - // if (min.z <= 0 || max.z >= br.Thickness) continue; - // let spt = hole.Position.applyMatrix4(brInv).setZ(0); + let line = new Line(spt, hole.Position.add(hole.Normal.multiplyScalar(hole.Height)).applyMatrix4(brInv).setZ(0)); + let pt = outline.IntersectWith(line, 0)[0]; + if (!pt) continue; - // if (outline.PtOnCurve(spt)) continue; - // let line = new Line(spt, hole.Position.add(hole.Normal.multiplyScalar(hole.Height)).applyMatrix4(brInv).setZ(0)); - // let pt = outline.IntersectWith(line, 0)[0]; - // if (!pt) continue; - - // let index = Math.floor(outline.GetParamAtPoint(pt)); - // let thickness = line.StartPoint.distanceTo(pt); - - // let shape = hole.Shape.ApplyMatrix(hole.OCS).ApplyMatrix(brInv); - // let vec = line.GetFirstDeriv(0).normalize().multiplyScalar(thickness); - // shape.Position = shape.Position.add(vec); - - // //侧面造型仅在多段线直线上 - // let cu = (outline as Polyline).GetCurveAtIndex(index); - // shape.ApplyMatrix(new Matrix4().getInverse(GetSideFaceMtx(cu))); - - // if (br.IsArcBoard) - // { - // //弧形板需要单独增加差值 - // shape.Position = shape.Position.add(addPos); - - // if (br.SweepAngle) - // { - // let ocsInv = new Matrix4().getInverse(br.ArcBuild.Rotate2OCSMtx); - // shape.ApplyMatrix(ocsInv); - // } - // } - - // sideModeling.push({ - // shape, - // thickness, - // dir: index, - // knifeRadius: hole.KnifeRadius, - // addLen: 0, - // originEn: hole, - // }); + let thickness = 0; + for (let p of [line.StartPoint, line.EndPoint]) + { + if (outline.PtInCurve(p)) + { + thickness = p.distanceTo(pt); + break; + } + } + + let index = Math.floor(outline.GetParamAtPoint(pt)); + + let vec = line.GetFirstDeriv(0).normalize().multiplyScalar(thickness); + shape.Position = shape.Position.add(vec); + + if (br.IsArcBoard) + { + //弧形板需要单独增加差值 + shape.Position = shape.Position.add(addPos); + + if (br.SweepAngle) + { + let ocsInv = new Matrix4().getInverse(br.ArcBuild.OCS2RotateMtx); + pt.applyMatrix4(ocsInv); + shape.ApplyMatrix(ocsInv); + } + } + + if (br.IsArcBoard) + { + //侧面造型仅在多段线直线上 + //因为侧面造型起点特殊性 要处理一下 + index = Math.floor(originOutline.GetParamAtPoint(pt)); + let cu = originOutline.GetCurveAtIndex(index); + shape.ApplyMatrix(new Matrix4().getInverse(GetSideCuFaceMtx(cu))); + } + else + { + let cu = outline.GetCurveAtIndex(index); + shape.ApplyMatrix(new Matrix4().getInverse(GetSideCuFaceMtx(cu))); + } + + sideModeling.push({ + outline: ConverToPtsBul(shape.Outline.Curve, false), + holes: shape.Holes.map((cu) => ConverToPtsBul(cu.Curve, false)), + thickness, + dir: index, + knifeRadius: hole.KnifeRadius, + addLen: 0, + addDepth: 0, + addWidth: 0, + modelType: ModelType.drill + }); } } diff --git a/src/Production/Product.ts b/src/Production/Product.ts index 826231cfb..45e22c034 100644 --- a/src/Production/Product.ts +++ b/src/Production/Product.ts @@ -25,6 +25,7 @@ import { HardwareCompositeEntity } from "../DatabaseServices/Hardware/HardwareCo import { HardwareTopline } from "../DatabaseServices/Hardware/HardwareTopline"; import { Shape } from "../DatabaseServices/Shape"; import { CanDrawHoleFuzz } from "../Geometry/DrillParse/CanDrawHoleFuzz"; +import { ParseBoardSideFace } from "../Geometry/DrillParse/ParseBoardSideFace"; import { AsVector2, IsBetweenA2B, MoveMatrix, XAxis, angle, angleTo, equaln, equalv2, equalv3, isIntersect2, isParallelTo, isPerpendicularityTo } from "../Geometry/GeUtils"; import { BrSealedData, GetSealedBoardContour } from "../GraphicsSystem/CalcEdgeSealing"; import { FeedingToolPath, GetModelingFromCustomDrill } from "../GraphicsSystem/ToolPath/FeedingToolPath"; @@ -115,7 +116,7 @@ export interface ISpliteOrderData modeling: IModelingData[]; //造型信息 curveBoardModeling: IModeling[]; holes: IBoardHoleInfo; //孔信息 - sideModeling: IModelingData[]; //侧面造型信息 + sideModeling: IOriginSideModelingData[]; //侧面造型信息 offsetTanslation: Vector3; originOutlin: IContourData; //不扣封边拆单原始轮廓 metalsData?: { metals: number, comp: number; }; //板件五金 @@ -151,6 +152,20 @@ export interface IOriginModelingData addDepth?: number; } +export interface IOriginSideModelingData extends IOriginModelingData +{ + chaiDanName?: string; + modelType: ModelType; +} + +export enum ModelType +{ + frontBackModel = 0, //正反面造型槽 + sideModel = 1,//绘制的侧槽造型 + drill = 2, //自定义排钻生成的非圆侧孔 + sideHoleModel = 3,//绘制的圆侧槽 +} + interface IChaiDanFeedingData extends IOriginModelingData { boardContour: IContourData; @@ -190,7 +205,11 @@ export namespace Production const curveBoardModeling = br.ArcBoardModeling; + //正反面造型 自定义不规则排钻孔 let { modeling, sideModeling } = GetBoardModelingData(br, offsetTanslation, redundancyKnif, curveBoardModeling); + //侧面造型 + let { sideModel, sideHole } = GetBoardSideModelingData(br, true); + sideModeling.push(...sideModel); let boardContour: IContourData; if (GetSpiteSize(br)) @@ -209,6 +228,9 @@ export namespace Production orgContour.Reverse(); } + let holes = GetBoardHolesData(br, offsetTanslation, orgContour); + holes.sideHoles.push(...sideHole); + return { info: GetBoardInfo(br, size), originOutlin: originOutlinePtsBul,//拼错了 未扣封边的点表 @@ -217,7 +239,7 @@ export namespace Production boardEdgeRemark: perBoardEdgeRemarkData, //每段曲线的板边备注信息 modeling, curveBoardModeling, - holes: GetBoardHolesData(br, offsetTanslation, orgContour), + holes, sideModeling, offsetTanslation, metalsData: GetBoardMetals(br), @@ -385,10 +407,8 @@ export namespace Production }; let allModeling = GetModelingFromCustomDrill(br); - let modeling = getModelings([...br.BoardModeling, ...allModeling.modeling]); - - let sideModeling = getModelings(allModeling.sideModeling); + let sideModeling = GetBoardSideModelingData(br); return { modeling, sideModeling }; } @@ -402,7 +422,7 @@ export namespace Production { const tool = FeedingToolPath.GetInstance(); const tMtx = MoveMatrix(offsetTanslation.clone().negate()); - const getModelings = (ms: IModeling[], isSide: boolean): IModelingData[] => + const getModelings = (ms: IModeling[]): IModelingData[] => { let data: IModelingData[] = []; @@ -414,8 +434,7 @@ export namespace Production if (HostApplicationServices.chaidanOption.useDefaultRad) m.knifeRadius = HostApplicationServices.chaidanOption.radius; let paths = tool.GetModelFeedPath(br, m, redundancyKnif);//走刀路径 - if (!isSide) - paths.forEach(path => path.ApplyMatrix(tMtx)); + paths.forEach(path => path.ApplyMatrix(tMtx)); //走刀的ptsbuls let feeding = paths.map((c: ExtrudeContourCurve) => ConverToPtsBul(c, false)); @@ -448,17 +467,91 @@ export namespace Production let allModeling = GetModelingFromCustomDrill(br); - let modeling = getModelings([...br.BoardModeling, ...allModeling.modeling, ...curveBoardModeling], false).filter(f => f.feeding.length > 0); + let modeling = getModelings([...br.BoardModeling, ...allModeling.modeling, ...curveBoardModeling]).filter(f => f.feeding.length > 0); + + return { modeling, sideModeling: allModeling.sideModeling }; + } - let sideModeling = getModelings(allModeling.sideModeling, true).filter(f => f.feeding.length > 0); + export function GetBoardSideModelingData(br: Board, toaster = false) + { + let sideModel: IOriginSideModelingData[] = []; + let sideHole: IDrillingOption[] = []; - return { modeling, sideModeling }; + if (!br.SideModelingMap.size) return { sideModel, sideHole }; + + const tool = FeedingToolPath.GetInstance(); + let faces = new ParseBoardSideFace(br); + + for (let [num, solids] of br.SideModelingMap) + { + let faceContour = faces.Faces[num].Region.ShapeManager.ShapeList[0].Outline.Curve as Polyline; + for (let solid of solids) + { + let cu = solid.Shape.Outline.Curve.Clone().ApplyMatrix(solid.OCSNoClone); + let modelType = ModelType.sideModel; + + // 圆造型拆成孔类型 + if (!solid.Shape.Holes.length && cu instanceof Circle && cu.Radius < HostApplicationServices.chaidanOption.modeling2HoleRad + 1e-6) + { + let mtx = br.OCSInv.multiply(faces.Faces[num].OCS); + let position = cu.Position.clone().applyMatrix4(mtx); + let endPt = cu.Position.clone().setZ(-solid.Thickness).applyMatrix4(mtx); + // cu.ApplyMatrix(mtx); + // TestDraw(cu); + // TestDraw(new Point(position)); + // TestDraw(new Point(endPt)); + sideHole.push({ + position, //排钻开始位置 + endPt, //排钻结束的位置(在板的坐标系) + radius: cu.Radius, //排钻半径 + depth: solid.Thickness, //排钻的插入深度 + face: num, //板在哪个边上 + name: "", + type: null + }); + + continue; + } + + let knifeRadius = solid.KnifeRadius; + + if (HostApplicationServices.chaidanOption.useDefaultRad) + knifeRadius = HostApplicationServices.chaidanOption.radius; + let paths = tool.GetSideModelFeedPath(solid, faceContour);//走刀路径 + + if (paths.length) + { + sideModel.push({ + thickness: solid.Thickness + (solid.GroovesAddDepth ?? 0), + dir: num, + knifeRadius, + outline: ConverToPtsBul(cu, false), + holes: solid.Shape.Holes.map((cu) => ConverToPtsBul(cu.Curve.Clone().ApplyMatrix(solid.OCSNoClone), false)), + addLen: solid.GroovesAddLength, + addWidth: solid.GroovesAddWidth, + addDepth: solid.GroovesAddDepth, + modelType + }); + } + else if (toaster) + { + Toaster({ + message: "板件有侧面造型或者自定义排钻无法加工,请运行造型检测命令确认", + timeout: 5000, + intent: Intent.DANGER, + key: "侧面造型加工错误" + }); + } + } + } + + return { sideModel, sideHole }; } //获得拆单尺寸 export function GetSpiteSize(br: Board) { - let [spHeight, spWidth, spThickness] = [br.BoardProcessOption.spliteHeight, br.BoardProcessOption.spliteWidth, br.BoardProcessOption.spliteThickness]; + let [spHeight, spWidth, spThickness] = [br.BoardProcessOption?.spliteHeight, br.BoardProcessOption?.spliteWidth, br.BoardProcessOption?.spliteThickness]; const isEffect = HostApplicationServices.chaidanOption.partialSplitValueCanTakesEffect; const param = { L: br.Height, W: br.Width, H: br.Thickness }; diff --git a/src/UI/Components/Board/BoardConfigModal.tsx b/src/UI/Components/Board/BoardConfigModal.tsx index 08b7c86da..8d12522e4 100644 --- a/src/UI/Components/Board/BoardConfigModal.tsx +++ b/src/UI/Components/Board/BoardConfigModal.tsx @@ -52,6 +52,7 @@ import { BoardOpenDirSelect, BoardTypeComponent, ItemName, SetBoardDataBlock, Se import { BoardModalType } from "./BoardModalType"; import { BoardOptionModal } from "./BoardOptionModal"; import { LastExtractBoardContour } from "./LastExtractBoardContour"; +import { SideModelingEditor } from "./SideModelingEditor"; import { DialogUserConfig } from "./UserConfigComponent"; interface BoardConfigProps @@ -61,6 +62,7 @@ interface BoardConfigProps br: Board; canDrawSpeical: IObservableValue; canModeling: IObservableValue; + canSideModeling: IObservableValue; canCurve: IObservableValue; grooveOption: IGrooveOption; uiBoardConfig: IUiOption; @@ -105,6 +107,7 @@ export class BoardConfigModal extends React.Component grooveOption, canDrawSpeical, canModeling, + canSideModeling, canCurve, arcBoardOptions, arcBoardConfig, @@ -166,18 +169,13 @@ export class BoardConfigModal extends React.Component />
canDrawSpeical.set(!canDrawSpeical.get())} /> />
-
+ canSideModeling.set(!canSideModeling.get())} + /> +
+
+
+
+
-
- 注:二维三维刀路功能目前为免费试用阶段! -
{ this.isCurveBoard && <> @@ -1168,4 +1181,37 @@ export class BoardConfigModal extends React.Component this.props.br.Modeling3D = modelingList; }, "修改三维刀路"); }; + + //编辑侧面造型 + private EditorSideModeling = async () => + { + let br = this.props.br; + if ((br.BoardProcessOption.spliteHeight || + br.BoardProcessOption.spliteHeight || + br.BoardProcessOption.spliteHeight) && !br.IsRect + ) + { + AppToaster.show({ + message: "板件有拆单值且是异形板不支持绘制侧面造型!", + timeout: 5000, + intent: Intent.WARNING, + }); + return; + } + + const rightStore = RightPanelStore.GetInstance(); + rightStore.m_IsShow = true; + rightStore.modelingStore.isNotUpdateStore = true; + let config = new DialogUserConfig(rightStore.modelingStore, BoardModalType.Zx); + await config.LoadAndInitConfig(); + + rightStore.m_TabId = RightTabId.Model; + + setTimeout(async () =>//当前命令结束后在进入编辑 + { + let Editor = new SideModelingEditor(this.props.br); + await Editor.StartEditor(); + rightStore.modelingStore.isNotUpdateStore = false; + }, 0); + }; } diff --git a/src/UI/Components/Board/BoardOptionModal.tsx b/src/UI/Components/Board/BoardOptionModal.tsx index cd5be05af..4c428e69f 100644 --- a/src/UI/Components/Board/BoardOptionModal.tsx +++ b/src/UI/Components/Board/BoardOptionModal.tsx @@ -123,6 +123,7 @@ class BoardOptionCom extends React.Component br={this.props._CurrentBoard.get()} canDrawSpeical={this.props.canDrawSpecial} canModeling={this.props.canModeling} + canSideModeling={this.props.canSideModeling} canCurve={this.props.canCurve} grooveOption={this.props._GrooveOption} uiBoardConfig={this.props.uiConfigOption} diff --git a/src/UI/Components/Board/CommonBoardOption.tsx b/src/UI/Components/Board/CommonBoardOption.tsx index 071f8b4d8..d4ddc92d2 100644 --- a/src/UI/Components/Board/CommonBoardOption.tsx +++ b/src/UI/Components/Board/CommonBoardOption.tsx @@ -33,6 +33,7 @@ export interface ICommonOptionProps drillsOption?: IBoardRectHoleType; canDrawSpecial?: IObservableValue; canModeling?: IObservableValue; + canSideModeling?: IObservableValue; canCurve: IObservableValue; _IsSpecialBoard?: IObservableValue; otherBoardData: { [key: string]: any; }; @@ -70,6 +71,7 @@ export function AddCommonBoardProps(Com: React.ComponentType public _IsSpecialBoard = observable.box(false); public canDrawSpecial = observable.box(true); public canModeling = observable.box(true); + public canSideModeling = observable.box(true); public canCurve = observable.box(true); public arcBoardOptions = observable.map(new Map()); public remarks: [string, string][] = observable(Array.from({ length: 12 }, () => ["", ""]) as [string, string][]); @@ -106,6 +108,7 @@ export function AddCommonBoardProps(Com: React.ComponentType drillsOption={this.drillsOption} canDrawSpecial={this.canDrawSpecial} canModeling={this.canModeling} + canSideModeling={this.canSideModeling} canCurve={this.canCurve} _IsSpecialBoard={this._IsSpecialBoard} otherBoardData={this.otherBoardData} @@ -123,6 +126,7 @@ export function AddCommonBoardProps(Com: React.ComponentType this._IsSpecialBoard.set(this.CurrentBoard.IsSpecialShape); this.canDrawSpecial.set(this.CurrentBoard.IsSpecialShape); this.canModeling.set(this.CurrentBoard.HasGroove); + this.canSideModeling.set(this.CurrentBoard.HasSideModel); this.canCurve.set(this.CurrentBoard.IsArcBoard); let ro = this.CurrentBoard.Rotation; @@ -318,6 +322,10 @@ export function AddCommonBoardProps(Com: React.ComponentType if (!this.canModeling.get() && board.HasGroove) board.ClearBoardModeling(); + //取消勾选侧面造型,清除侧面造型 + if (!this.canSideModeling.get() && board.HasSideModel) + board.ClearSideModeling(); + //取消勾选异形,板件回复正常形状 if (!this.canDrawSpecial.get() && !isRect) board.ConverToRectSolid(); @@ -380,6 +388,7 @@ export function AddCommonBoardProps(Com: React.ComponentType board.ClearBoardModeling(); board.ClearLayerNails(); board.ClearRelevance(); + board.ClearSideModeling(); } } else @@ -392,6 +401,7 @@ export function AddCommonBoardProps(Com: React.ComponentType board.ClearBoardModeling(); board.ClearLayerNails(); board.ClearRelevance(); + board.ClearSideModeling(); } } } diff --git a/src/UI/Components/Board/SideModelingEditor.tsx b/src/UI/Components/Board/SideModelingEditor.tsx new file mode 100644 index 000000000..d596c5b70 --- /dev/null +++ b/src/UI/Components/Board/SideModelingEditor.tsx @@ -0,0 +1,622 @@ +import { Button, Intent } from "@blueprintjs/core"; +import React from "react"; +import { Box3, LineSegments, Matrix4, Mesh, Object3D, Vector3 } from "three"; +import { FaceDirection } from "../../../Add-on/DrawDrilling/DrillType"; +import { app } from "../../../ApplicationServices/Application"; +import { ColorMaterial } from "../../../Common/ColorPalette"; +import { curveLinkGroup } from "../../../Common/CurveUtils"; +import { GroupEntitysByBox } from "../../../Common/GroupEntitysByBox"; +import { Log } from "../../../Common/Log"; +import { MakeMirrorMtx } from "../../../Common/Matrix4Utils"; +import { UpdateDraw } from "../../../Common/Status"; +import { GetEntity } from "../../../Common/Utils"; +import { CADFiler } from "../../../DatabaseServices/CADFiler"; +import { Contour } from "../../../DatabaseServices/Contour"; +import { Board, IModeling } from "../../../DatabaseServices/Entity/Board"; +import { Curve } from "../../../DatabaseServices/Entity/Curve"; +import { Ellipse } from "../../../DatabaseServices/Entity/Ellipse"; +import { ExtrudeSolid } from "../../../DatabaseServices/Entity/Extrude"; +import { Polyline } from "../../../DatabaseServices/Entity/Polyline"; +import { Region } from "../../../DatabaseServices/Entity/Region"; +import { Spline } from "../../../DatabaseServices/Spline"; +import { CommandWrap } from "../../../Editor/CommandMachine"; +import { PromptStatus } from "../../../Editor/PromptResult"; +import { TempEditor } from "../../../Editor/TempEditor"; +import { userConfig } from "../../../Editor/UserConfig"; +import { CreateContours } from "../../../Geometry/CreateContour2"; +import { BoardFaceType } from "../../../Geometry/DrillParse/BoardGetFace"; +import { Face } from "../../../Geometry/DrillParse/Face"; +import { ParseBoardSideFace } from "../../../Geometry/DrillParse/ParseBoardSideFace"; +import { ContourTreeNode } from "../../../Geometry/ExtrudeMeshGeomBuilder/ExtrudeEdgeGeometry2"; +import { ZAxis, angleTo, equalv3 } from "../../../Geometry/GeUtils"; +import { CameraUpdate } from "../../../GraphicsSystem/CameraUpdate"; +import { RenderType } from "../../../GraphicsSystem/RenderType"; +import { Board_Editor_Key } from "../../Store/RightPanelStore/BoardEdgesEditor"; +import { RightPanelStore } from "../../Store/RightPanelStore/RightPanelStore"; +import { AppConfirm } from "../Common/Confirm"; +import { AppToaster } from "../Toaster"; + +export class SideModelingEditor +{ + private Board: Board; + private SideFaces: Face[] = []; + private AroundBoardMesh: Object3D[] = []; //周围板件 + private SideModelEdgeGeom: Object3D[] = []; //已存在侧面造型边线 + private CurrentFace: Face; //选择编辑的侧面 + private CurrentFaceIndex: number; + private CurrentFaceOCS: Matrix4; + private CurrentFaceOCSInv: Matrix4; + private CurrentSideContour: Polyline; //选择编辑的侧面轮廓 + private Editoring = false; + private CameraFiler: CADFiler; + private OpacityBak: number; + private BackUCSMatrix: Matrix4; + private BackVisibleUCS: boolean; + private RenderTypeBak: RenderType; + private ShowSaveCancleButton: boolean = false; + + constructor(br: Board) + { + this.Board = br; + } + + StartEditor = async () => + { + if (this.Editoring) + throw "重复进入编辑模式!"; + + this.Editoring = true; + + this.Init(); + this.ShowToaster(); //保存或取消 + this.Start(); + await this.SelectSideFace(); //选择侧面 + if (this.Editoring) + this.AddModelingCurve(); //添加自身已存在的测槽 + }; + AddModelingCurve() + { + CommandWrap(async () => + { + for (const face of this.SideFaces) + { + let region = face.Region; + region.Erase(); + } + //添加最外圈轮廓 + this.CurrentSideContour = this.CurrentFace.Region.ShapeManager.ShapeList[0].Outline.Curve as Polyline; + app.Database.ModelSpace.Append(this.CurrentSideContour); + + //添加该面已有造型轮廓 + const SideModelingList = this.Board.SideModelingMap.get(this.CurrentFaceIndex); + + if (SideModelingList?.length) + { + const rightPanelStore = RightPanelStore.GetInstance(); + let dataColorMap = new Map();//data->color + let canUseColor: number[] = [];//可使用的颜色(无高度) + let onUseColor: number[] = [];//已经使用的颜色 + let colorIndex = 1; + for (let item of rightPanelStore.modelingStore.modelingItems) + { + let str = [item.height, item.knifeRad, item.addLen, item.addWidth, item.addDepth].join("-"); + + if (dataColorMap.has(str) || item.height === 0)//重复的相同设定一样没有意义,所以可以覆盖 + canUseColor.push(item.color);//可以被覆盖 + else + dataColorMap.set(str, item.color);//记录颜色 + } + + for (let solid of SideModelingList) + { + let str = [solid.Thickness, solid.KnifeRadius, solid.GroovesAddLength, solid.GroovesAddWidth, solid.GroovesAddDepth].join("-"); + let color: number; + if (dataColorMap.has(str)) + color = dataColorMap.get(str); + else + { + color = canUseColor[0]; + if (!color) + { + while (colorIndex < 24) + { + if (!onUseColor.includes(colorIndex)) break; + colorIndex++; + } + color = colorIndex; + } + canUseColor.shift(); + dataColorMap.set(str, color); + + const data: IModeling = { + shape: undefined, + thickness: solid.Thickness, + dir: FaceDirection.Front, + knifeRadius: solid.KnifeRadius, + addLen: solid.GroovesAddLength, + addWidth: solid.GroovesAddWidth, + addDepth: solid.GroovesAddDepth + }; + + rightPanelStore.modelingStore.ChangeModelingValue(color - 1, data); + } + onUseColor.push(color); + let cu = solid.ContourCurve.Clone().ApplyMatrix(new Matrix4().multiplyMatrices(this.CurrentFace.OCS, solid.OCS)); + cu.ColorIndex = color; + app.Database.ModelSpace.Append(cu); + + //添加子集 + for (let c of solid.Shape.Holes) + { + cu = c.Curve.Clone().ApplyMatrix(new Matrix4().multiplyMatrices(this.CurrentFace.OCS, solid.OCS)); + cu.ColorIndex = color; + app.Database.ModelSpace.Append(cu); + } + } + } + }, "选择侧面"); + } + + private Init() + { + let br = this.Board; + if ( + this.Board.BoardProcessOption.spliteHeight || + this.Board.BoardProcessOption.spliteWidth || + this.Board.BoardProcessOption.spliteThickness + ) + { + br = br.Clone(); + br.BoardProcessOption.spliteHeight = ""; + br.BoardProcessOption.spliteWidth = ""; + br.BoardProcessOption.spliteThickness = ""; + } + + let bgf = new ParseBoardSideFace(br); + this.SideFaces = bgf.Faces; + } + + private ShowToaster() + { + AppToaster.show({ + message: this.RenderToasterMessage(), + intent: Intent.PRIMARY, + timeout: 0, + onDismiss: () => this.EndEditor() + }, Board_Editor_Key); + } + + private RenderToasterMessage = () => + { + return ( +
+ 正在编辑侧面造型 + { +
+
+ } +
+ ); + }; + + //分析绘画造型曲线轮廓 + private async ParseCurveLinkGroup() + { + if (!this.ShowSaveCancleButton) return; + + let contourMap = new Map(); + let hasDrawNoHeight = false; + + await CommandWrap(async () => + { + const Curves: Set = new Set(); + + for (let obj of app.Viewer.Scene.children) + { + if (obj.visible) + { + let ent = GetEntity(obj); + if (ent && ent.Id?.Object && ent.Visible && !ent.IsErase && ent instanceof Curve) + { + if (ent instanceof Ellipse || ent instanceof Spline) + { + let temp = ent.Convert2Polyline(); + temp.__CachePolyline__ = ent; + Curves.add(temp); + } + else if (ent !== this.CurrentSideContour) + Curves.add(ent); + } + } + } + + const curGroups = curveLinkGroup(Array.from(Curves)); + const rightStore = RightPanelStore.GetInstance(); + + if (curGroups.length) + { + let box = this.CurrentSideContour.BoundingBox; + + for (let g of curGroups) + { + for (let c of g) + { + let cd = rightStore.modelingStore.modelingItems[c.ColorIndex - 1]; + if (!cd || cd.height <= 0) + { + hasDrawNoHeight = true; + break; + } + + if (box.intersectsBox(c.BoundingBox)) + { + //在创建内部造型时,如果存在封闭的轮廓或者圆,那么自动分裂将失效(需要用户自己裂开) + let contours = CreateContours(g); + + if (!contourMap.has(c.ColorIndex)) + contourMap.set(c.ColorIndex, contours); + else + contourMap.get(c.ColorIndex).push(...contours); + break; + } + } + } + } + }, "解析侧面造型"); + + if (hasDrawNoHeight) + { + let status = await AppConfirm.show({ + message: "检查到部分造型轮廓未设置高度,是否继续?", + confirmButtonText: "确定", + intent: Intent.WARNING + }); + if (!status) + return; + } + + AppToaster.dismiss(Board_Editor_Key); + await this.AddModeling(contourMap); + } + + private async AddModeling(contourMap: Map) + { + const rightStore = RightPanelStore.GetInstance(); + const SideModelingSolidList: ExtrudeSolid[] = []; + + for (let [color, contours] of contourMap) + { + for (let con of contours) + con.Curve.ApplyMatrix(this.CurrentFaceOCSInv); + + let cd = rightStore.modelingStore.modelingItems[color - 1]; + + //分析包含关系 + let contourNodes = contours.map(contour => new ContourTreeNode(contour)); + ContourTreeNode.ParseContourTree(contourNodes); + + for (let contourNode of contourNodes) + { + if (contourNode.IsHole) continue; + + let solid = new ExtrudeSolid(); + solid.Thickness = cd.height; + solid.ContourCurve = contourNode.contour.Curve; + solid.KnifeRadius = cd.knifeRad; + solid.GroovesAddLength = cd.addLen; + solid.GroovesAddWidth = cd.addWidth; + solid.GroovesAddDepth = cd.addDepth; + for (let hole of contourNode.children) + { + let holeSolid = new ExtrudeSolid(); + holeSolid.Thickness = cd.height; + holeSolid.ContourCurve = hole.contour.Curve; + solid.AppendGroove(holeSolid); + } + + SideModelingSolidList.push(solid); + } + } + + CommandWrap(() => + { + this.Board.WriteAllObjectRecord(); + + if (SideModelingSolidList.length) + this.Board.SideModelingMap.set(this.CurrentFaceIndex, SideModelingSolidList); + else + this.Board.SideModelingMap.delete(this.CurrentFaceIndex); + + this.Board.ClearSideModelingCache(); + this.Board.Update(UpdateDraw.Geometry); + }, "应用侧面造型"); + } + + private async Start() + { + app.Editor.ModalManage.ToggleShow(); + app.Editor.MaskManage.Clear(); + + TempEditor.Start(); + + //记录初始 + this.BackUCSMatrix = app.Editor.UCSMatrix; + this.BackVisibleUCS = app.Editor.UcsServices.Visible; + this.OpacityBak = userConfig.ConceptualOpacity; + this.RenderTypeBak = userConfig.RenderType; + + this.CameraFiler = new CADFiler; + app.Viewer.CameraCtrl.WriteFile(this.CameraFiler); + + //初始视角 俯视图 + // app.Viewer.CameraCtrl.LookAt(ZAxisN); + // app.Viewer.CameraCtrl.ZoomExtentsBox3(this.Board.BoundingBoxInOCS); + // app.Viewer.CameraCtrl.Zoom(1.3); + // app.Editor.UcsServices.Visible = false; + userConfig.RenderType = RenderType.Conceptual; + + //获取周围板件 + let aroundBoard = app.Database.ModelSpace.Entitys.filter((ent) => ent && ent.IsVisible && ent instanceof Board) as Board[]; + let groupEntMap = GroupEntitysByBox(aroundBoard); + + CommandWrap(() => + { + const MirrorMtxZ = MakeMirrorMtx(ZAxis); + let b = new Board(); + b.Thickness = this.Board.Thickness; + b.ContourCurve = this.Board.ContourCurve; + + let cloneBrMesh = new Mesh(b.MeshGeometry, ColorMaterial.GetBasicMaterialTransparent(this.Board.ColorIndex, 0.3)); + cloneBrMesh["freeze"] = true; + cloneBrMesh.applyMatrix4(this.Board.OCSNoClone); + TempEditor.editorScene.add(cloneBrMesh); + this.AroundBoardMesh.push(cloneBrMesh); + + for (let [index, soilds] of this.Board.SideModelingMap) + { + let face = this.SideFaces[index]; + if (!face) continue; + + for (let soild of soilds) + { + //添加原板件网格 + let line = new LineSegments(soild.EdgeGeometry, ColorMaterial.GetConceptualEdgeMaterial()); + line.applyMatrix4(MirrorMtxZ); + line.applyMatrix4(soild.OCS); + line.applyMatrix4(face.OCS); + TempEditor.editorScene.add(line); + this.SideModelEdgeGeom.push(line); + } + } + + for (let [box, brs] of groupEntMap) + { + if (brs.find((br) => br === this.Board)) + { + for (let br of brs) + { + if (br === this.Board) continue; + + let geo = br.MeshGeometry; + let aroundMesh = new Mesh(geo, ColorMaterial.GetBasicMaterialTransparent(br.ColorIndex, 0.1)); + aroundMesh["freeze"] = true; + aroundMesh.applyMatrix4(br.OCSNoClone); + TempEditor.editorScene.add(aroundMesh); + this.AroundBoardMesh.push(aroundMesh); + } + break; + } + } + + //侧面面域 + for (let face of this.SideFaces) + { + if (face.type === BoardFaceType.Side) + { + let region = face.Region; + region.ShapeApplyMatrix(face.OCS); + region.ColorIndex = this.Board.ColorIndex; + app.Database.ModelSpace.Append(region); + } + } + + }, "编辑侧面造型"); + + app.Database.hm.lockIndex++;//禁止初始化动作被撤销 + } + + private async EndEditor() + { + if (!this.Editoring) return; + + //马上标记为否,避免重入 + this.Editoring = false; + await app.Editor.ModalManage.EndExecingCmd(); + app.Editor.UcsServices.Visible = this.BackVisibleUCS; + app.Editor.UCSMatrix = this.BackUCSMatrix; + userConfig.ConceptualOpacity = this.OpacityBak; + app.Viewer.CameraCtrl.ReadFile(this.CameraFiler); + userConfig.RenderType = this.RenderTypeBak; + + TempEditor.End(); + + app.Editor.Cancel(); + this.SideFaces = []; + this.AroundBoardMesh = []; + this.SideModelEdgeGeom = []; + this.CurrentSideContour = undefined; + this.CameraFiler = undefined; + this.BackUCSMatrix = undefined; + this.ShowSaveCancleButton = false; + app.Editor.ModalManage.ToggleShow(); + app.Editor.MaskManage.OnFocusEvent(); + } + + private SelectSideFace = async () => + { + let enRes = await app.Editor.GetEntity({ + Msg: "选择该板件的一个侧面:", + Filter: { filterTypes: [Region] }, + NotNone: true, + Callback: (res) => + { + if (res.Status === PromptStatus.OK) + { + if (app.Viewer.OutlinePass.selectedObjects[0] !== res.Entity.DrawObject) + { + app.Viewer.OutlinePass.selectedObjects = [res.Entity.DrawObject]; + app.Viewer.UpdateRender(); + } + } + else + { + app.Viewer.OutlinePass.selectedObjects = []; + app.Viewer.UpdateRender(); + } + } + }); + if (enRes.Status !== PromptStatus.OK) + { + AppToaster.dismiss(Board_Editor_Key); + return; + }; + + app.Editor.SelectCtrl.Cancel(); + + const firstMesh = this.AroundBoardMesh.shift(); + + for (const Mesh of this.AroundBoardMesh) + TempEditor.editorScene.remove(Mesh); + + let index = this.SideFaces.findIndex((face) => face.Region === enRes.Entity); + if (index === -1) + { + Log("出错了,找不到选择面位置"); + return; + } + this.CurrentFaceIndex = index; + this.CurrentFace = this.SideFaces[index]; + this.CurrentFaceOCS = this.CurrentFace.OCS; + this.CurrentFaceOCSInv = new Matrix4().getInverse(this.CurrentFaceOCS); + + this.ShowSaveCancleButton = true; + + const CameraUpDir = new Vector3().setFromMatrixColumn(this.CurrentFaceOCS, 2).normalize().multiplyScalar(-1); + //旋转至选择面 + await RotateCamera(this.Board.OCS, this.Board.BoundingBox, CameraUpDir, 70, this.Board.Normal); + + //设置世界坐标系 + app.Editor.SetUCSLookAt(CameraUpDir); + app.Editor.UCSMatrix = this.CurrentFaceOCS; + + TempEditor.editorScene.remove(firstMesh); + + for (const obj of this.SideModelEdgeGeom) + TempEditor.editorScene.remove(obj); + }; +} + +//旋转至面视角 +export async function RotateCamera(UCSMatrix: Matrix4, box: Box3, lookAtDir: Vector3, totalSegments: number = 100, cameraUpDir = new Vector3(0, 0, 1)): Promise +{ + return new Promise((res) => + { + const cameraControl = app.Viewer.CameraControl; + const currentCameraUp = cameraControl.Camera.up; + const currentDir = app.Viewer.CameraControl.Direction; + const currentViewHeight = app.Viewer.CameraControl.ViewHeight; + + //最终观察视角 + const cameraControlClone = new CameraUpdate(); + cameraControlClone.SetSize(cameraControl.Width, cameraControl.Height); + cameraControlClone.LookAt(lookAtDir); + cameraControlClone.ZoomExtentsBox3(box.clone()); + cameraControlClone.Zoom(1.2); + const EndViewHeight = cameraControlClone.ViewHeight; + + // ----获取相机曲线控制点 begin------ + let controlMidPt = lookAtDir.clone(); + + const OCS = UCSMatrix.clone(); + let norZ = new Vector3().setFromMatrixColumn(OCS, 2); + controlMidPt = currentDir.clone().sub(norZ.clone().multiplyScalar(currentDir.dot(norZ))); + let an = angleTo(controlMidPt, lookAtDir, norZ); + + let normalV = controlMidPt.clone().normalize(); + + if (!normalV.length()) + { + controlMidPt = new Vector3(0, 1).applyMatrix4(app.Editor.UCSMatrix).add(lookAtDir).multiplyScalar(0.5); + if (equalv3(controlMidPt, new Vector3)) + an = Math.PI / 2; + } + + //相机朝向角度较大时 使用中点过渡 + if (Math.abs(an) >= Math.PI / 2) + controlMidPt = lookAtDir.clone().applyMatrix4(new Matrix4().makeRotationAxis(norZ, Math.sign(-an) * Math.PI / 2)); + else + controlMidPt = currentDir.clone().add(lookAtDir).multiplyScalar(0.5); + + const ControlPoints = [ + currentDir, // 起始点 + controlMidPt, // 控制中点 + lookAtDir // 结束点 + ]; + // ----获取相机曲线控制点 end------ + + let index = 0; + let time = setInterval(() => + { + // 当前分段参数 + const totalSeg = index / totalSegments; + // 计算贝塞尔曲线上对应 t 值的点 + const dirPt = EvaluateBezierCurve(ControlPoints, totalSeg); + // 相机的上方向向量 + const cameraUp = new Vector3().lerpVectors(currentCameraUp, cameraUpDir, totalSeg); + const traget = box.getCenter(new Vector3); + + cameraControl.LookAt(dirPt); + cameraControl.Camera.up = cameraUp; + cameraControl.Target = traget; + cameraControl.ViewHeight = currentViewHeight - (currentViewHeight - EndViewHeight) * totalSeg; + cameraControl.Update(); + + app.Viewer.UpdateRender(); + + if (index === totalSegments) + { + clearInterval(time); + return res(); + } + + index++; + }, 10); + }); +} + +// 贝塞尔曲线求值函数 +function EvaluateBezierCurve(controlPoints: Vector3[], t: number): Vector3 +{ + let n = controlPoints.length - 1; + let result = new Vector3(0, 0, 0); + for (let i = 0; i <= n; i++) + { + let binomialCoefficient = BinomialCoefficient(n, i); + let powerTerm = Math.pow(1 - t, n - i) * Math.pow(t, i); + result.addScaledVector(controlPoints[i], binomialCoefficient * powerTerm); + } + return result; +} + +// 计算二项式系数 +function BinomialCoefficient(n: number, k: number): number +{ + let result = 1; + for (let i = 1; i <= k; i++) + { + result *= (n - i + 1) / i; + } + return result; +} diff --git a/src/UI/Components/CommandPanel/CommandList.ts b/src/UI/Components/CommandPanel/CommandList.ts index f399d5b4f..2a044cc20 100644 --- a/src/UI/Components/CommandPanel/CommandList.ts +++ b/src/UI/Components/CommandPanel/CommandList.ts @@ -948,6 +948,15 @@ export const CommandList: ICommand[] = [ chName: "模拟造型走刀+排钻+封边", chDes: "", }, + { + typeId: "bjbj", + link: `#`, + defaultCustom: CommandNames.TestSideModeling, + command: CommandNames.TestSideModeling, + type: "模拟侧面造型加工结果", + chName: "模拟侧面造型走刀", + chDes: "", + }, { typeId: "bjbj", defaultCustom: "BBB", diff --git a/src/UI/Components/RightPanel/BoardPropsComponent.tsx b/src/UI/Components/RightPanel/BoardPropsComponent.tsx index fd41dae89..8cd3f7b6b 100644 --- a/src/UI/Components/RightPanel/BoardPropsComponent.tsx +++ b/src/UI/Components/RightPanel/BoardPropsComponent.tsx @@ -85,6 +85,7 @@ class BoardPropsCom extends React.Component br={this.props._CurrentBoard.get()} canDrawSpeical={this.props.canDrawSpecial} canModeling={this.props.canModeling} + canSideModeling={this.props.canSideModeling} canCurve={this.props.canCurve} grooveOption={this.props._GrooveOption} uiBoardConfig={this.props.uiConfigOption} diff --git a/src/csg/core/FuzzyFactory3d.ts b/src/csg/core/FuzzyFactory3d.ts index 3a3a50bb3..0b5848cf7 100644 --- a/src/csg/core/FuzzyFactory3d.ts +++ b/src/csg/core/FuzzyFactory3d.ts @@ -1,7 +1,7 @@ import { FuzzyFactory } from "./FuzzyFactory"; import { EPS } from "./constants"; -import { Polygon } from "./math/Polygon3"; import { Plane } from "./math/Plane"; +import { Polygon } from "./math/Polygon3"; import { Vertex3D } from "./math/Vertex3"; export class FuzzyCSGFactory