Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 992a115f93 | |||
| 50753dda52 | |||
| cff250ca37 | |||
| 7309ebb6a3 | |||
| c649870518 | |||
| 3d8ec03c47 | |||
| 42329d7688 |
@@ -44,8 +44,8 @@ local function GetStrategies_Egalware( Proc)
|
|||||||
Strategies = { { sStrategyId = 'HEADCUT'}}
|
Strategies = { { sStrategyId = 'HEADCUT'}}
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
-- Feature : Cut
|
-- Feature : Cut
|
||||||
elseif ID.IsSplitCut( Proc) then
|
elseif ID.IsTailCut( Proc) then
|
||||||
Strategies = { { sStrategyId = 'SPLITCUT'}}
|
Strategies = { { sStrategyId = 'TAILCUT'}}
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
-- Feature : Cut
|
-- Feature : Cut
|
||||||
elseif ID.IsCut( Proc) then
|
elseif ID.IsCut( Proc) then
|
||||||
@@ -302,8 +302,8 @@ local function GetStrategies_Essetre( Proc)
|
|||||||
Strategies = { { sStrategyId = 'HEADCUT'}}
|
Strategies = { { sStrategyId = 'HEADCUT'}}
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
-- Feature : Cut
|
-- Feature : Cut
|
||||||
elseif ID.IsSplitCut( Proc) then
|
elseif ID.IsTailCut( Proc) then
|
||||||
Strategies = { { sStrategyId = 'SPLITCUT'}}
|
Strategies = { { sStrategyId = 'TAILCUT'}}
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
-- Feature : Cut
|
-- Feature : Cut
|
||||||
elseif ID.IsCut( Proc) then
|
elseif ID.IsCut( Proc) then
|
||||||
|
|||||||
@@ -319,6 +319,66 @@ function BeamLib.Is3EdgesApprox( Proc, idFace, nAddGrpId)
|
|||||||
return bResult
|
return bResult
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
function BeamLib.GetNzLimDownUp( b3Raw, vtN, vtOrtho)
|
||||||
|
if BeamData.GetNzLimDownUp then
|
||||||
|
return BeamData.GetNzLimDownUp( b3Raw, vtN, vtOrtho)
|
||||||
|
elseif BeamData.C_SIMM then
|
||||||
|
return -0.484
|
||||||
|
elseif BeamData.TURN then
|
||||||
|
return -2
|
||||||
|
else
|
||||||
|
if vtOrtho and vtOrtho:getZ() > 0.35 then
|
||||||
|
-- N_HorAng < 15° or N_HorAng > 55°
|
||||||
|
if vtN and ( abs( vtN:getY()) < 0.259 or abs( vtN:getY()) > 0.819) then
|
||||||
|
return -0.708
|
||||||
|
-- N_HorAng > 50°
|
||||||
|
elseif vtN and abs( vtN:getY()) > 0.766 then
|
||||||
|
return -0.383
|
||||||
|
else
|
||||||
|
return EgtIf( b3Raw:getDimZ() < BeamData.MIN_DIM_HBEAM, -0.609, -0.383)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- N_HorAng > 60°
|
||||||
|
if vtN and ( abs( vtN:getY()) > 0.866) then
|
||||||
|
return -0.708
|
||||||
|
else
|
||||||
|
if b3Raw:getDimZ() < 120 then
|
||||||
|
return -0.708
|
||||||
|
elseif b3Raw:getDimZ() < 200 then
|
||||||
|
-- N_HorAng < 15° or N_HorAng > 55°
|
||||||
|
if vtN and ( abs( vtN:getY()) < 0.259 or abs( vtN:getY()) > 0.819) then
|
||||||
|
return -0.5
|
||||||
|
else
|
||||||
|
return -0.383
|
||||||
|
end
|
||||||
|
elseif b3Raw:getDimZ() < 300 then
|
||||||
|
-- N_HorAng < 10°
|
||||||
|
if vtN and ( abs( vtN:getY()) < 0.174) then
|
||||||
|
return -0.5
|
||||||
|
else
|
||||||
|
return -0.259
|
||||||
|
end
|
||||||
|
elseif b3Raw:getDimZ() < BeamData.MIN_DIM_HBEAM then
|
||||||
|
-- N_HorAng < 10°
|
||||||
|
if vtN and ( abs( vtN:getY()) < 0.174) then
|
||||||
|
return -0.342
|
||||||
|
else
|
||||||
|
return -0.259
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- N_HorAng < 10°
|
||||||
|
if vtN and ( abs( vtN:getY()) < 0.174) then
|
||||||
|
return -0.383
|
||||||
|
else
|
||||||
|
return -0.174
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
-- sovrascrivo i parametri personalizzati salvati su Proc a quelli di default dalla strategia
|
-- sovrascrivo i parametri personalizzati salvati su Proc a quelli di default dalla strategia
|
||||||
-- N.B. : I parametri personalizzati non più presenti tra i default della strategia, verranno ignorati. Quelli extra avranno valore di default
|
-- N.B. : I parametri personalizzati non più presenti tra i default della strategia, verranno ignorati. Quelli extra avranno valore di default
|
||||||
|
|||||||
@@ -0,0 +1,676 @@
|
|||||||
|
-- DiceCut.lua by Egaltech s.r.l. 2024/01/23
|
||||||
|
-- Gestione dei piano paralleli nei tagli lunghi: equidistanziamento dei piani paralleli
|
||||||
|
|
||||||
|
-- Tabella per definizione modulo
|
||||||
|
local DiceCut = {}
|
||||||
|
|
||||||
|
-- Include
|
||||||
|
require( 'EgtBase')
|
||||||
|
local BeamLib = require( 'BeamLib')
|
||||||
|
|
||||||
|
EgtOutLog( ' DiceCut started', 1)
|
||||||
|
|
||||||
|
-- Dati
|
||||||
|
local BeamData = require( 'BeamData')
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- creo i piani paralleli
|
||||||
|
-- GetParallelPlanes: restituisce un vettore con gli indici delle superfici
|
||||||
|
-- nAddGrpId: il layer
|
||||||
|
-- b3BoxToDicing: il grezzo della barra
|
||||||
|
-- ptC: il punto centrale del piano della feature
|
||||||
|
-- vtN: il versore normale del piano della feature
|
||||||
|
-- nCopyPlane: 0 => genera una copia del piano passato, 1=> non genera una copia del piano passato
|
||||||
|
-- dOffset: offset dei piani paralleli
|
||||||
|
-- nStep: numero massimo di step
|
||||||
|
-- Color: colre del fascio di piani
|
||||||
|
-- dTolerance*: distanza tra i piani paralleli e i piani di taglio (se non esistono altre superfici può essere omesso)
|
||||||
|
-- bNoTolOnFirstCut*: elimina la tolleranza per il primo piano del fascio (se non esistono altre superfici può essere omesso)
|
||||||
|
-- ptCCut*: il punto centrale della superfice limitante (se non esistono altre superfici può essere omesso)
|
||||||
|
-- vtNCut*: il punto centrale della superfice limitante (se non esistono altre superfici può essere omesso)
|
||||||
|
-- ptCCut1*: il punto centrale della superfice limitante (se non esistono altre superfici può essere omesso)
|
||||||
|
-- vtNCut1*: il punto centrale della superfice limitante (se non esistono altre superfici può essere omesso)
|
||||||
|
local function GetParallelPlanes( nAddGrpId, b3BoxToDicing, ptC, vtN, nCopyPlane, dOffset, nStep, Color, dTolerance, bNoTolOnFirstCut, ptCCut, vtNCut, ptCCut1, vtNCut1 )
|
||||||
|
local ptMyCCut
|
||||||
|
local AreaMin = 5*5
|
||||||
|
if ptCCut and vtNCut then
|
||||||
|
if dTolerance then
|
||||||
|
ptMyCCut = Point3d( ptCCut + dTolerance * vtNCut)
|
||||||
|
end
|
||||||
|
ptMyCCut = Point3d( ptCCut)
|
||||||
|
end
|
||||||
|
local ptMyCCut1
|
||||||
|
if ptCCut1 and vtNCut1 then
|
||||||
|
ptMyCCut1 = Point3d( ptCCut1 + 0 * vtNCut1)
|
||||||
|
end
|
||||||
|
local TabellaTmSurfParallel = {}
|
||||||
|
local i = nCopyPlane
|
||||||
|
while i < nStep do
|
||||||
|
local SurfId = EgtSurfTmPlaneInBBox( nAddGrpId, ptC + ( i * dOffset) * vtN, vtN, b3BoxToDicing, GDB_RT.GLOB)
|
||||||
|
local nFacet = EgtSurfTmFacetCount( SurfId or GDB_ID.NULL)
|
||||||
|
if nFacet == 0 then
|
||||||
|
-- se sono al primo taglio do una possibilità in più di girare
|
||||||
|
if i > nCopyPlane then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if nFacet > 0 and vtNCut and ptMyCCut and EgtSurfArea(SurfId) > AreaMin then
|
||||||
|
if i == nCopyPlane and bNoTolOnFirstCut then
|
||||||
|
EgtCutSurfTmPlane( SurfId, ptCCut, -vtNCut, false, GDB_RT.GLOB)
|
||||||
|
else
|
||||||
|
EgtCutSurfTmPlane( SurfId, ptMyCCut, -vtNCut, false, GDB_RT.GLOB)
|
||||||
|
end
|
||||||
|
nFacet = EgtSurfTmFacetCount( SurfId)
|
||||||
|
end
|
||||||
|
if nFacet > 0 and vtNCut1 and ptMyCCut1 and EgtSurfArea(SurfId) > AreaMin then
|
||||||
|
if i == nCopyPlane and bNoTolOnFirstCut then
|
||||||
|
EgtCutSurfTmPlane( SurfId, ptCCut1, -vtNCut1, false, GDB_RT.GLOB)
|
||||||
|
else
|
||||||
|
EgtCutSurfTmPlane( SurfId, ptMyCCut1, -vtNCut1, false, GDB_RT.GLOB)
|
||||||
|
end
|
||||||
|
nFacet = EgtSurfTmFacetCount( SurfId)
|
||||||
|
end
|
||||||
|
if nFacet > 0 and EgtSurfArea(SurfId) > AreaMin then
|
||||||
|
table.insert( TabellaTmSurfParallel, SurfId)
|
||||||
|
EgtSetColor( SurfId, Color)
|
||||||
|
else
|
||||||
|
EgtErase( SurfId)
|
||||||
|
end
|
||||||
|
if dOffset == 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
return TabellaTmSurfParallel
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- GetOrderedCutTable:
|
||||||
|
-- nAddGrpId: il layer
|
||||||
|
-- TabParallelPlanes: tabella delle superfici dei piani paralleli
|
||||||
|
-- TabOrtoPlanes: tabella delle superfici dei piani ortogonali
|
||||||
|
-- stampo l'ordine dei piani di taglio (prima tagli ortogonali piu' esterni poi taglio parallelo collettivo)
|
||||||
|
local function GetOrderedCutTable( nAddGrpId, TabParallelPlanes, TabOrtoPlanes)
|
||||||
|
local StepParallel = #TabParallelPlanes
|
||||||
|
local StepOrto = #TabOrtoPlanes
|
||||||
|
local TabellaOrderParallelCut1 = {}
|
||||||
|
|
||||||
|
for IndexTmP=1, StepParallel do
|
||||||
|
TabellaOrderParallelCut1[IndexTmP] = {}
|
||||||
|
for IndexOrto=1, StepOrto do
|
||||||
|
local ptOrtoN, vtNOrtoN = EgtSurfTmFacetCenter( TabOrtoPlanes[IndexOrto ], 0, GDB_ID.ROOT)
|
||||||
|
local Copy1Id = EgtCopySurfTmFacet( TabParallelPlanes[IndexTmP], 0, nAddGrpId)
|
||||||
|
local Copy2Id = EgtCopySurfTmFacet( TabParallelPlanes[IndexTmP], 0, nAddGrpId)
|
||||||
|
if Copy1Id and ptOrtoN then
|
||||||
|
EgtCutSurfTmPlane( Copy1Id, ptOrtoN, -vtNOrtoN, false, GDB_RT.GLOB)
|
||||||
|
EgtCutSurfTmPlane( Copy2Id, ptOrtoN, vtNOrtoN, false, GDB_RT.GLOB)
|
||||||
|
local nFacet1 = EgtSurfTmFacetCount( Copy1Id)
|
||||||
|
local nFacet2 = EgtSurfTmFacetCount( Copy2Id)
|
||||||
|
if nFacet1 < 1 then
|
||||||
|
EgtErase( Copy1Id)
|
||||||
|
EgtErase( Copy2Id)
|
||||||
|
EgtCutSurfTmPlane( TabParallelPlanes[IndexTmP], ptOrtoN, vtNOrtoN, false, GDB_RT.GLOB)
|
||||||
|
elseif nFacet2 < 1 then
|
||||||
|
EgtErase( Copy1Id)
|
||||||
|
EgtErase( Copy2Id)
|
||||||
|
break
|
||||||
|
else
|
||||||
|
table.insert( TabellaOrderParallelCut1[IndexTmP], Copy1Id)
|
||||||
|
EgtErase( Copy2Id)
|
||||||
|
EgtCutSurfTmPlane( TabParallelPlanes[IndexTmP], ptOrtoN, vtNOrtoN, false, GDB_RT.GLOB)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(TabellaOrderParallelCut1[IndexTmP], TabParallelPlanes[IndexTmP])
|
||||||
|
end
|
||||||
|
return TabellaOrderParallelCut1
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- GetOrtoCutCenter:
|
||||||
|
-- idFacet: l'id della faccia
|
||||||
|
-- ptC: il punto centrale della faccia
|
||||||
|
-- vtN: il versore normale della faccia
|
||||||
|
-- vtO: il versore dei piani ortogonali
|
||||||
|
-- dOffsetEff: offset della distanza dal punto centrale
|
||||||
|
-- Verifica se l'asse X del box costruito sopra la superficie è più grande di un certo offset
|
||||||
|
local function GetOrtoCutCenter( FacetId, ptC, vtN, vtO, dOffsetEff, dNzLimDwnUp)
|
||||||
|
local FrameLocal = Frame3d( EgtSurfTmFacetCenter( FacetId, 0, GDB_ID.ROOT))
|
||||||
|
EgtSetGridFrame(FrameLocal)
|
||||||
|
local IdAuxLocal = EgtGroup(EgtGetParent( FacetId), FrameLocal)
|
||||||
|
EgtSetName( IdAuxLocal, "AuxLocal")
|
||||||
|
local BoxLocal = EgtGetBBoxRef( FacetId, GDB_BB.STANDARD, FrameLocal)
|
||||||
|
local ptS = EgtGP( EgtSurfTmBBox( IdAuxLocal, BoxLocal, GDB_RT.GRID), GDB_ID.ROOT)
|
||||||
|
EgtErase( IdAuxLocal)
|
||||||
|
EgtSetGridFrame(Frame3d())
|
||||||
|
-- riferimento intrinseco
|
||||||
|
local asseX = Vector3d( vtO)
|
||||||
|
local asseY = vtN ^ asseX
|
||||||
|
local Frame = Frame3d( ptC, ptC + asseX, ptC + asseY)
|
||||||
|
-- ingombro della faccia secondo questo riferimento
|
||||||
|
local Box = EgtGetBBoxRef( FacetId, GDB_BB.STANDARD, Frame)
|
||||||
|
local dLen = Box:getDimX()
|
||||||
|
local dWidth = Box:getDimY()
|
||||||
|
|
||||||
|
-- se faccia non troppo lunga, con un lato piccolo e non diretta troppo verso il basso, non servono dice
|
||||||
|
local dMaxLen = BeamData.MAX_LEN_DICE or 600
|
||||||
|
if dLen < dMaxLen and dWidth < dMaxLen and
|
||||||
|
( dLen < dOffsetEff + 1.0 or dWidth < dOffsetEff + 1.0) and vtN:getZ() > dNzLimDwnUp then
|
||||||
|
return nil, nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local N = ceil( dLen / dOffsetEff)
|
||||||
|
local dOffsetRel = ( dLen / N) + 10 * GEO.EPS_SMALL
|
||||||
|
local dCopyPlane
|
||||||
|
local dCenOffs = ( Box:getMax():getX() + Box:getMin():getX()) / 2
|
||||||
|
|
||||||
|
if dLen <= dOffsetRel then
|
||||||
|
dCopyPlane = 1
|
||||||
|
elseif dLen <= 2 * dOffsetRel then
|
||||||
|
dOffsetRel = dOffsetEff
|
||||||
|
dCopyPlane = 0
|
||||||
|
else
|
||||||
|
if N % 2 == 0 then
|
||||||
|
dCopyPlane = 0
|
||||||
|
elseif N % 2 == 1 then
|
||||||
|
dCopyPlane = 0.5
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return dOffsetRel, dCopyPlane, dCenOffs, ptS
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- TableMesh:
|
||||||
|
-- TableOrto: tabella delle superfici dei piani ortogonali
|
||||||
|
-- TableParallel: tabella delle superfici dei piani paralleli
|
||||||
|
-- Forma una tabella unica delle superfici di taglio inserendo strati di tagli ortogonali alternati da strati di taglio parallelo
|
||||||
|
local function TableMesh( TableOrto, TableParallel)
|
||||||
|
local TableUnited = {}
|
||||||
|
for i=1, #TableOrto do
|
||||||
|
table.insert( TableUnited, TableOrto[i])
|
||||||
|
table.insert( TableUnited, TableParallel[i])
|
||||||
|
end
|
||||||
|
return TableUnited
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- FindValue:
|
||||||
|
-- tab: tabella da scansionare
|
||||||
|
-- val: valore da cercare
|
||||||
|
-- Verifica se tab contiene val, a supporto della funzione SortOrtoCutsByNormalMethod
|
||||||
|
local function FindValue( tab, val)
|
||||||
|
for index, value in ipairs( tab) do
|
||||||
|
if value == val then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- SortOrtoCutsByNormalMethod:
|
||||||
|
-- TabParallelPlanes: tabella delle superfici dei piani paralleli
|
||||||
|
-- TabOrtoPlanes: tabella delle superfici dei piani ortogonali
|
||||||
|
-- stampo l'ordine dei piani di taglio sfruttando il prodotto dei versori normali (prima tagli ortogonali piu' esterni poi taglio parallelo collettivo)
|
||||||
|
local function SortOrtoCutsByNormalMethod( TabParallelPlanes, TabOrtoPlanes)
|
||||||
|
-- tabella dei tagli ordinati
|
||||||
|
local TabOrderOrtoCut = {}
|
||||||
|
-- tabella ausiliaria dei dati inseriti nell'ordine
|
||||||
|
local TabAux = {}
|
||||||
|
-- ciclo di ordinamento
|
||||||
|
local StepParallel = #TabParallelPlanes
|
||||||
|
local StepOrto = #TabOrtoPlanes
|
||||||
|
for IndexTmP=1, StepParallel do
|
||||||
|
TabOrderOrtoCut[IndexTmP] = {}
|
||||||
|
local ptParalN, vtNParalN = EgtSurfTmFacetCenter( TabParallelPlanes[IndexTmP][1], 0, GDB_ID.ROOT)
|
||||||
|
for IndexOrto=1, StepOrto do
|
||||||
|
for i=1, #TabOrtoPlanes[IndexOrto] do
|
||||||
|
-- identificativo del taglio
|
||||||
|
local OrtoPlaneId = TabOrtoPlanes[IndexOrto][i]
|
||||||
|
-- controlla che l'elemento da valutare non sia gia' stato inserito nella tabella
|
||||||
|
if not FindValue( TabAux, OrtoPlaneId) then
|
||||||
|
local ptOrtoN, vtNOrtoN = EgtSurfTmFacetCenter( OrtoPlaneId, 0, GDB_ID.ROOT)
|
||||||
|
local scalarProduct = ( ptOrtoN - ptParalN) * vtNParalN
|
||||||
|
if scalarProduct > 0 then
|
||||||
|
table.insert( TabOrderOrtoCut[IndexTmP], OrtoPlaneId)
|
||||||
|
table.insert( TabAux, OrtoPlaneId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- cancello dal DB geometrico i tagli non inseriti
|
||||||
|
for IndexOrto=1, StepOrto do
|
||||||
|
for i=1, #TabOrtoPlanes[IndexOrto] do
|
||||||
|
-- identificativo del taglio
|
||||||
|
local OrtoPlaneId = TabOrtoPlanes[IndexOrto][i]
|
||||||
|
-- ricerco il taglio tra gli inseriti
|
||||||
|
if not FindValue( TabAux, OrtoPlaneId) then
|
||||||
|
EgtErase( OrtoPlaneId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return TabOrderOrtoCut
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- VerifyFirstOrthoCut :
|
||||||
|
-- CutTable: tabella dei tagli
|
||||||
|
-- OffsetP: offset della distanza dal punto centrale
|
||||||
|
-- b3BoxToDicing: il grezzo della barra
|
||||||
|
-- Verifica se l'asse X del box costruito sopra le 2 facce è più piccolo di un certo offset e quindi la faccia O è superflua
|
||||||
|
local function VerifyFirstOrthoCut( CutTable, OffsetP, b3BoxToDicing, dNzLimDwnUp)
|
||||||
|
|
||||||
|
if not CutTable[1] or not CutTable[2] then return end
|
||||||
|
|
||||||
|
local CutOId = CutTable[1][1]
|
||||||
|
local Cut1Id = CutTable[2][1]
|
||||||
|
local Cut2Id = CutTable[2][2]
|
||||||
|
|
||||||
|
if CutOId and Cut1Id and Cut2Id then
|
||||||
|
-- centri e normali delle due semifacce
|
||||||
|
local ptC1, vtN1 = EgtSurfTmFacetCenter( Cut1Id, 0, GDB_ID.ROOT)
|
||||||
|
local ptC2, vtN2 = EgtSurfTmFacetCenter( Cut2Id, 0, GDB_ID.ROOT)
|
||||||
|
-- normale alla faccia ortogonale
|
||||||
|
local _, vtO = EgtSurfTmFacetCenter( CutOId, 0, GDB_ID.ROOT)
|
||||||
|
vtO = vtO - vtO * vtN1 * vtN1 ; vtO:normalize()
|
||||||
|
local dMaxElev = EgtSurfTmFacetElevationInBBox( CutOId, 0, b3BoxToDicing, true, GDB_ID.ROOT)
|
||||||
|
-- calcolo lunghezza prima semi-faccia
|
||||||
|
local asseX1 = vtO
|
||||||
|
local asseY1 = vtN1 ^ asseX1
|
||||||
|
local Frame1 = Frame3d( ptC1, ptC1+asseX1, ptC1+asseY1)
|
||||||
|
local Box1 = EgtGetBBoxRef( Cut1Id, GDB_BB.STANDARD, Frame1)
|
||||||
|
local x1 = Box1:getDimX()
|
||||||
|
-- prendo il massimo tra la lugnhezza della faccia parallela e l'elevazione della corrispondente ortogonale
|
||||||
|
x1 = max( x1, dMaxElev)
|
||||||
|
-- calcolo lunghezza seconda semi-faccia
|
||||||
|
local asseX2 = vtO
|
||||||
|
local asseY2 = vtN2 ^ asseX2
|
||||||
|
local Frame2 = Frame3d( ptC2, ptC2+asseX2, ptC2+asseY2)
|
||||||
|
local Box2 = EgtGetBBoxRef( Cut2Id, GDB_BB.STANDARD, Frame2)
|
||||||
|
local x2 = Box2:getDimX()
|
||||||
|
-- lunghezza totale faccia
|
||||||
|
local dLongSize = x1 + x2
|
||||||
|
-- se faccia piccola e non orientata verso il basso, elimino ortogonale e unisco le due parti
|
||||||
|
if dLongSize <= OffsetP + 1.0 and vtN1:getZ() > dNzLimDwnUp then
|
||||||
|
local nAddGrpId = EgtGetParent( Cut1Id)
|
||||||
|
local SurfId = EgtSurfTmBySewing( nAddGrpId, { Cut1Id, Cut2Id})
|
||||||
|
EgtSetColor( SurfId, Color3d( FUCHSIA(), 60))
|
||||||
|
EgtErase( CutOId)
|
||||||
|
table.remove( CutTable[1], 1)
|
||||||
|
table.remove( CutTable[2], 1)
|
||||||
|
table.remove( CutTable[2], 1)
|
||||||
|
table.insert( CutTable[2], 1, SurfId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- ricavo i vertici del box
|
||||||
|
local function CalcolaPuntiEstremiBox( b3BoxToDicing)
|
||||||
|
local ptMin = b3BoxToDicing:getMin()
|
||||||
|
local ptMax = b3BoxToDicing:getMax()
|
||||||
|
local TBoxPoint = {}
|
||||||
|
table.insert( TBoxPoint, { P = Point3d( ptMin:getX(), ptMin:getY(), ptMin:getZ()), On = true})
|
||||||
|
table.insert( TBoxPoint, { P = Point3d( ptMax:getX(), ptMin:getY(), ptMin:getZ()), On = true})
|
||||||
|
table.insert( TBoxPoint, { P = Point3d( ptMin:getX(), ptMax:getY(), ptMin:getZ()), On = true})
|
||||||
|
table.insert( TBoxPoint, { P = Point3d( ptMax:getX(), ptMax:getY(), ptMin:getZ()), On = true})
|
||||||
|
table.insert( TBoxPoint, { P = Point3d( ptMin:getX(), ptMin:getY(), ptMax:getZ()), On = true})
|
||||||
|
table.insert( TBoxPoint, { P = Point3d( ptMax:getX(), ptMin:getY(), ptMax:getZ()), On = true})
|
||||||
|
table.insert( TBoxPoint, { P = Point3d( ptMin:getX(), ptMax:getY(), ptMax:getZ()), On = true})
|
||||||
|
table.insert( TBoxPoint, { P = Point3d( ptMax:getX(), ptMax:getY(), ptMax:getZ()), On = true})
|
||||||
|
return TBoxPoint
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- restituisce i punti che devono essere presi in considerazione per la costruzione del BoundingBox
|
||||||
|
local function VerificaEstremiGrezzo( ptC, vtN, TPoint)
|
||||||
|
for i = 1, #TPoint do
|
||||||
|
if ( TPoint[i].P - ptC) * vtN < 0 then
|
||||||
|
TPoint[i].On = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return TPoint
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- ricavo l'altezza del BoundingBox assegnati gli estremi del grezzo e la feature
|
||||||
|
-- le funzioni commentate permettono di vedere la creazione di BoundingBox
|
||||||
|
local function DistanzaMassima( nAddGrpId, ptC1, vtN1, ptC2, vtN2, b3BoxToDicing, TPoint)
|
||||||
|
-- calcolo il riferimento nel piano 1
|
||||||
|
local Frame1 = Frame3d( ptC1, vtN1)
|
||||||
|
-- determino l'ingombro in questo riferimento della parte di trave compresa nel o nei piani
|
||||||
|
local BB1 = BBox3d()
|
||||||
|
-- punti di vertice della trave compresi
|
||||||
|
for i = 1, #TPoint do
|
||||||
|
if TPoint[i].On then
|
||||||
|
local ptP = Point3d( TPoint[i].P)
|
||||||
|
ptP:toLoc( Frame1)
|
||||||
|
BB1:Add( ptP)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- eventuale altra faccia
|
||||||
|
if ptC2 and vtN2 then
|
||||||
|
local IdAux = EgtGroup( nAddGrpId)
|
||||||
|
local IdSurf2 = EgtSurfTmPlaneInBBox( IdAux, ptC2, vtN2, b3BoxToDicing, GDB_RT.GLOB)
|
||||||
|
EgtCutSurfTmPlane( IdSurf2, ptC1, -vtN1, false, GDB_RT.GLOB)
|
||||||
|
if IdSurf2 then
|
||||||
|
local BB2 = EgtGetBBoxRef( IdSurf2, GDB_BB.STANDARD, Frame1)
|
||||||
|
BB1:Add( BB2)
|
||||||
|
end
|
||||||
|
EgtErase( IdAux)
|
||||||
|
end
|
||||||
|
if not BB1:isEmpty() then
|
||||||
|
return ( BB1:getMax():getZ() + 0.1)
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- DiceCut.GetDice :
|
||||||
|
-- nParent: il layer
|
||||||
|
-- BBoxRawPart: il grezzo della barra
|
||||||
|
-- ptCPlanes: il punto centrale del piano della feature
|
||||||
|
-- vtNPlanes: il versore normale del piano della feature
|
||||||
|
-- bGetOrtoPlanes*: se voglio calcolare i piani ortogonali al piano passato (se non esistono altre superfici può essere omesso)
|
||||||
|
-- ptCBond*: il punto centrale della superfice limitante (se non esistono altre superfici può essere omesso)
|
||||||
|
-- vtNBond*: il versore normale della superfice limitante (se non esistono altre superfici può essere omesso)
|
||||||
|
-- dOrthoMaxDim : massima profondità taglio se faccia singola perpendicolare facce laterali trave
|
||||||
|
-- dCustMaxDimDice: dimensione massima customizzata, sostituisce il parametro BeamData.MAX_DIM_DICE (se negativa qualunque, se positiva deve stare nel MAX)
|
||||||
|
-- bDownHead : taglio con testa da sotto
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- function DiceCut.GetDice( nParent, BBoxRawPart, ptCPlanes, vtNPlanes, bGetOrtoPlanes, ptCBond, vtNBond, dOrthoMaxDim, dCustMaxDimDice, bDownHead)
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------------------------------------------
|
||||||
|
-- DiceCut.GetDice :
|
||||||
|
-- Part : pezzo
|
||||||
|
-- MainFace : lista. Contiene tutti i dati della faccia principale. Obblogatori i campi '.ptCenter' e '.vtNormal'
|
||||||
|
-- SubordinateFace : lista. Contiene tutti i dati della faccia secondaria. Se non esiste, la lista è vuota. Se esiste, obblogatori i campi '.ptCenter' e '.vtNormal'
|
||||||
|
-- OptionaParameters : eventuali parametri opzionali
|
||||||
|
---------------------------------------------------------------------------------------------------------------
|
||||||
|
function DiceCut.GetDice( Part, Face1, Face2, OptionalParameters)
|
||||||
|
|
||||||
|
local dMaxDimDice = BeamData.MAX_DIM_DICE
|
||||||
|
local dTolerance = 0 -- distanza di sicurezza per i tagli ortogonali
|
||||||
|
local OffsetP = dMaxDimDice -- distanza tra i piani paralleli
|
||||||
|
local StepP = 100 -- numero massimo di piani paralleli da generare
|
||||||
|
local OffsetO = dMaxDimDice -- distanza tra i piani ortogonali
|
||||||
|
local StepO = 100 -- numero massimo di piani ortogonali da generare
|
||||||
|
|
||||||
|
-- recupero gruppo per geometria addizionale
|
||||||
|
local nAddGrpId = BeamLib.GetAddGroup( Part.id)
|
||||||
|
-- se il box non è passato tra i parametri opzionali, faccio i calcoli sul SOLID del Part
|
||||||
|
local b3BoxToDicing = OptionalParameters.b3BoxToDicing or Part.b3Solid
|
||||||
|
-- copio dati passati su variabili locali
|
||||||
|
local ptCMainFace, vtNMainFace, ptCSubordinateFace, vtNSubordinateFace, bGetOrtoPlanes
|
||||||
|
if Face1.ptCenter and Face1.vtNormal then
|
||||||
|
ptCMainFace = Face1.ptCenter
|
||||||
|
vtNMainFace = Face1.vtNormal
|
||||||
|
end
|
||||||
|
-- se esiste faccia secondaria non esiste, forzo calcolo piani ortogonali
|
||||||
|
if Face2.ptCenter and Face2.vtNormal then
|
||||||
|
ptCSubordinateFace = Face2.ptCenter
|
||||||
|
vtNSubordinateFace = Face2.vtNormal
|
||||||
|
bGetOrtoPlanes = false
|
||||||
|
else
|
||||||
|
bGetOrtoPlanes = true
|
||||||
|
end
|
||||||
|
-- ricavo dimensione massima cubetti
|
||||||
|
if OptionalParameters.dCustMaxDimDice and OptionalParameters.dCustMaxDimDice < BeamData.MAX_DIM_DICE then
|
||||||
|
dMaxDimDice = abs( OptionalParameters.dCustMaxDimDice)
|
||||||
|
end
|
||||||
|
|
||||||
|
--Ricavo le altezze dei BoundingBox contenente feature e estremi del grezzo
|
||||||
|
local TBoxPoint = CalcolaPuntiEstremiBox( b3BoxToDicing)
|
||||||
|
TBoxPoint = VerificaEstremiGrezzo( ptCMainFace, vtNMainFace, TBoxPoint)
|
||||||
|
if ptCSubordinateFace and vtNSubordinateFace then
|
||||||
|
TBoxPoint = VerificaEstremiGrezzo( ptCSubordinateFace, vtNSubordinateFace, TBoxPoint)
|
||||||
|
end
|
||||||
|
local dElevP = DistanzaMassima( nAddGrpId, ptCMainFace, vtNMainFace, ptCSubordinateFace, vtNSubordinateFace, b3BoxToDicing, TBoxPoint)
|
||||||
|
local dElevO
|
||||||
|
if ptCSubordinateFace and vtNSubordinateFace and not AreOppositeVectorApprox( vtNSubordinateFace, vtNMainFace) then
|
||||||
|
dElevO = DistanzaMassima( nAddGrpId, ptCSubordinateFace, vtNSubordinateFace, ptCMainFace, vtNMainFace, b3BoxToDicing, TBoxPoint)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- inclinazione limite per taglio da sotto
|
||||||
|
local dNzLimDwnUp = BeamLib.GetNzLimDownUp( b3BoxToDicing)
|
||||||
|
|
||||||
|
-- se non c'è testa da sotto e normali senza componenti in Y con faccia quasi verticale e trave non alta, uso per offset i limiti dei tagli di testa e coda
|
||||||
|
if not BeamData.DOWN_HEAD and abs( vtNMainFace:getY()) < 0.1 and vtNMainFace:getZ() < 0.7071 and ( not vtNSubordinateFace or abs( vtNSubordinateFace:getY()) < 0.1) and b3BoxToDicing:getDimZ() < BeamData.MIN_DIM_HBEAM then
|
||||||
|
OffsetO = OptionalParameters.dOrthoMaxDim or BeamData.MAX_DIM_HTCUT
|
||||||
|
end
|
||||||
|
|
||||||
|
-- aggiungo piccolo extra agli offset
|
||||||
|
OffsetP = OffsetP + 10 * GEO.EPS_SMALL
|
||||||
|
OffsetO = OffsetO + 10 * GEO.EPS_SMALL
|
||||||
|
|
||||||
|
-- se piani non ortogonali, diminuisco la distanza di offset opportunamente
|
||||||
|
local originalOffsetP = OffsetP
|
||||||
|
if not bGetOrtoPlanes then
|
||||||
|
local dCoeff = ( vtNMainFace ^ vtNSubordinateFace):len()
|
||||||
|
OffsetP = OffsetP * dCoeff
|
||||||
|
OffsetO = OffsetO * dCoeff
|
||||||
|
end
|
||||||
|
|
||||||
|
local n = ceil( dElevP / OffsetP)
|
||||||
|
OffsetP = dElevP / n
|
||||||
|
if dElevO then
|
||||||
|
local m = ceil( dElevO / OffsetO)
|
||||||
|
OffsetO = dElevO / m
|
||||||
|
end
|
||||||
|
|
||||||
|
-- elenco di tutte le superfici generate dai tagli
|
||||||
|
local TabFUCHSIA = {}
|
||||||
|
local TabGREEN = {}
|
||||||
|
|
||||||
|
-- PIANI PARALLELI alla faccia di taglio
|
||||||
|
local TabellaTmSurfP = {}
|
||||||
|
local TabFromIn = GetParallelPlanes( nAddGrpId, b3BoxToDicing, ptCMainFace, vtNMainFace, 0, OffsetP, StepP, Color3d( FUCHSIA(), 60), dTolerance, true, ptCSubordinateFace, vtNSubordinateFace)
|
||||||
|
for i = #TabFromIn, 1, -1 do
|
||||||
|
table.insert( TabellaTmSurfP, TabFromIn[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- PIANI ORTOGONALI alla faccia di taglio
|
||||||
|
-- orientamento definito da seconda faccia
|
||||||
|
if not bGetOrtoPlanes then
|
||||||
|
local TabellaTmSurfOrto = {}
|
||||||
|
local TabFromIn = GetParallelPlanes( nAddGrpId, b3BoxToDicing, ptCSubordinateFace, vtNSubordinateFace, 0, OffsetO, StepO, Color3d( GREEN(), 60), dTolerance, false, ptCMainFace, vtNMainFace)
|
||||||
|
for i = #TabFromIn, 1, -1 do
|
||||||
|
table.insert( TabellaTmSurfOrto, TabFromIn[i])
|
||||||
|
end
|
||||||
|
local TabellaOrderParallelCut = GetOrderedCutTable( nAddGrpId, TabellaTmSurfP, TabellaTmSurfOrto) -- Ottiene la tabella dei tagli paralleli ordinati
|
||||||
|
local TabellaOrderOrtoCut = GetOrderedCutTable( nAddGrpId, TabellaTmSurfOrto, TabellaTmSurfP) -- Ottiene la tabella dei tagli ortogonali da riordinare per strato
|
||||||
|
|
||||||
|
TabGREEN = SortOrtoCutsByNormalMethod( TabellaOrderParallelCut, TabellaOrderOrtoCut) -- I tagli ortogonali vengono ordinati per strato
|
||||||
|
TabFUCHSIA = TabellaOrderParallelCut -- I tagli paralleli sono già ordinati
|
||||||
|
|
||||||
|
-- orientamento da definire
|
||||||
|
else
|
||||||
|
for PlnInd = 1, #TabellaTmSurfP do
|
||||||
|
-- piano interno
|
||||||
|
local ptCInner, vtNInner = EgtSurfTmFacetCenter( TabellaTmSurfP[PlnInd], 0, GDB_ID.ROOT)
|
||||||
|
-- eventuale piano esterno
|
||||||
|
local ptCOuter, vtNOuter = nil, nil
|
||||||
|
if PlnInd > 1 then
|
||||||
|
ptCOuter, vtNOuter = EgtSurfTmFacetCenter( TabellaTmSurfP[PlnInd-1], 0, GDB_ID.ROOT)
|
||||||
|
vtNOuter = -vtNOuter
|
||||||
|
end
|
||||||
|
-- calcolo la direzione dei piani ortogonali
|
||||||
|
local vtO = VectorFromUprightOrtho( vtNInner)
|
||||||
|
if vtNInner:getZ() > -0.0175 or vtNInner:getZ() < dNzLimDwnUp or abs( vtNInner:getY()) > 0.8 then
|
||||||
|
vtO:rotate( vtNInner, 90)
|
||||||
|
-- se diretto troppo ortogonalmente all'asse trave e taglio non da sotto, lo ruoto ulteriormente
|
||||||
|
if ( abs( vtO:getY()) > 4 * abs( vtO:getX()) or b3BoxToDicing:getDimZ() > 620) and vtNInner:getZ() > dNzLimDwnUp then
|
||||||
|
vtO:rotate( vtNInner, 90)
|
||||||
|
-- se faccia principale verso il basso (almeno -3deg), lo inverto per iniziare da sopra
|
||||||
|
if vtNInner:getZ() < -0.05 then
|
||||||
|
vtO = -vtO
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if vtNInner:getX() > -0.017 then
|
||||||
|
if vtO:getX() < -0.001 then vtO = -vtO end
|
||||||
|
else
|
||||||
|
if vtO:getX() > 0.001 then vtO = -vtO end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- se quasi verticale (fino a 20 gradi di distanza) su faccia inclinata in Y, ruoto di 90 gradi
|
||||||
|
if vtO:getZ() > 0.939 and abs( vtNInner:getY()) > 0.5 then
|
||||||
|
vtO:rotate( vtNInner, 90)
|
||||||
|
end
|
||||||
|
-- se taglio con testa da sotto
|
||||||
|
if OptionalParameters.bHeadFromBottom and vtO:getZ() > 0.05 then
|
||||||
|
vtO = -vtO
|
||||||
|
end
|
||||||
|
-- calcolo le dimensioni dell'offset e dove posizionare la prima faccia:
|
||||||
|
-- CopyPlane: 0 => crea la prima faccia direttamente sul punto passato
|
||||||
|
-- CopyPlane: 1 => crea la prima faccia e tutte le altre con l'offset passato
|
||||||
|
-- CopyPlane: 0.5 => crea la prima faccia a metà offset e tutte le altre con l'offest intero
|
||||||
|
local OffsetRel, CopyPlane, dCenOffs, ptCStart = GetOrtoCutCenter( TabellaTmSurfP[PlnInd], ptCInner, vtNInner, vtO, OffsetO, dNzLimDwnUp)
|
||||||
|
|
||||||
|
if OffsetRel and CopyPlane and dCenOffs then
|
||||||
|
ptCInner = ptCInner + dCenOffs * vtO
|
||||||
|
local TabRight = GetParallelPlanes( nAddGrpId, b3BoxToDicing, ptCStart, vtO, CopyPlane, -OffsetRel, StepO, Color3d( GREEN(), 60),
|
||||||
|
dTolerance, false, ptCInner, vtNInner, ptCOuter, vtNOuter)
|
||||||
|
if CopyPlane == 0 then
|
||||||
|
CopyPlane = 1
|
||||||
|
end
|
||||||
|
local TabLeft = GetParallelPlanes( nAddGrpId, b3BoxToDicing, ptCStart, vtO, CopyPlane, OffsetRel, StepO, Color3d( GREEN(), 60),
|
||||||
|
dTolerance, false, ptCInner, vtNInner, ptCOuter, vtNOuter)
|
||||||
|
-- carico la tabella con gli indici riordinati
|
||||||
|
local TempOrtoTab = {}
|
||||||
|
for i = #TabLeft, 1, -1 do
|
||||||
|
table.insert( TempOrtoTab, TabLeft[i])
|
||||||
|
end
|
||||||
|
for i = 1, #TabRight do
|
||||||
|
table.insert( TempOrtoTab, TabRight[i])
|
||||||
|
end
|
||||||
|
-- creo una tabella per ogni piano per generare i tagli sulla Inner
|
||||||
|
local TempParTab = GetOrderedCutTable( nAddGrpId, {TabellaTmSurfP[PlnInd]}, TempOrtoTab)
|
||||||
|
for i = 1, #TempParTab do
|
||||||
|
table.insert( TabFUCHSIA, TempParTab[i])
|
||||||
|
end
|
||||||
|
table.insert( TabGREEN, TempOrtoTab)
|
||||||
|
else
|
||||||
|
table.insert( TabFUCHSIA, {TabellaTmSurfP[PlnInd]})
|
||||||
|
table.insert( TabGREEN, {})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Se sono state costruite non più di una faccia parallela e una faccia perpendicolare, allora non servono e tengo le originali
|
||||||
|
if #TabGREEN == 1 and #TabGREEN[1] <= 1 and #TabFUCHSIA == 1 and #TabFUCHSIA[1] <= 1 then
|
||||||
|
if #TabGREEN[1] == 1 then
|
||||||
|
EgtErase( TabGREEN[1][1])
|
||||||
|
end
|
||||||
|
TabGREEN = {}
|
||||||
|
if #TabFUCHSIA[1] == 1 then
|
||||||
|
EgtErase( TabFUCHSIA[1][1])
|
||||||
|
end
|
||||||
|
TabFUCHSIA = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Si uniscono le tabelle dei tagli ortogonali e paralleli in una sola tabella
|
||||||
|
local UltimateTable = TableMesh( TabGREEN, TabFUCHSIA)
|
||||||
|
|
||||||
|
-- Se esiste la superficie limitante (nFacet == 2) verifica se il taglio più esterno è superfluo e quindi viene eliminato
|
||||||
|
if not bGetOrtoPlanes then
|
||||||
|
VerifyFirstOrthoCut( UltimateTable, originalOffsetP, b3BoxToDicing, dNzLimDwnUp)
|
||||||
|
-- se rimangono due sole facce, devono coincidere con le originali e quindi sono superflue
|
||||||
|
if #UltimateTable == 2 and #UltimateTable[1] == 1 and #UltimateTable[2] == 1 then
|
||||||
|
EgtErase( UltimateTable[1][1])
|
||||||
|
EgtErase( UltimateTable[2][1])
|
||||||
|
UltimateTable = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return UltimateTable
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- PrintOrderCut:
|
||||||
|
-- TabellaOrderCut: tabella delle superfici di taglio
|
||||||
|
-- stampo l'ordine dei piani di taglio ricevendo una tabella con gli strati parallelo/ortogonali già ordinati
|
||||||
|
function DiceCut.PrintOrderCut( TabellaOrderCut)
|
||||||
|
local Step = #TabellaOrderCut
|
||||||
|
if Step > 0 then
|
||||||
|
EgtOutLog( " L'ordine delle superfici da tagliare è il seguente:")
|
||||||
|
else
|
||||||
|
EgtOutLog( ' Non sono necessarie superfici aggiuntive di taglio.')
|
||||||
|
end
|
||||||
|
for i = 1, Step do
|
||||||
|
if i % 2 == 1 then
|
||||||
|
EgtOutLog( ' *** Strato di taglio ' .. EgtNumToString( ( i + 1) / 2, 0))
|
||||||
|
if #TabellaOrderCut[i] > 0 then
|
||||||
|
EgtOutLog( ' Tagli ortogonali: ')
|
||||||
|
else
|
||||||
|
EgtOutLog( ' Tagli ortogonali assenti')
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if #TabellaOrderCut[i] > 0 then
|
||||||
|
EgtOutLog( ' Tagli paralleli: ')
|
||||||
|
else
|
||||||
|
EgtOutLog( ' Tagli paralleli assenti')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for j=1, #TabellaOrderCut[i] do
|
||||||
|
EgtOutLog( ' Indice faccia ' .. (TabellaOrderCut[i][j] or 0))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
local function DiceCutTest()
|
||||||
|
|
||||||
|
-- salvo l'indice del layer Trimesh selezionato
|
||||||
|
local SelectedIndex = EgtGetFirstSelectedObj()
|
||||||
|
-- verifico che sia selezionato un elemento
|
||||||
|
if not SelectedIndex then return end
|
||||||
|
|
||||||
|
-- salvo l'indice del layer Processing
|
||||||
|
local nAddGrpId = EgtGetParent( SelectedIndex)
|
||||||
|
-- salvo l'indice del layer Part
|
||||||
|
local nRawPart = EgtGetParent( nAddGrpId)
|
||||||
|
local nBox = EgtGetFirstNameInGroup( nRawPart, 'Box')
|
||||||
|
-- carico il bounding box della trave
|
||||||
|
-- local b3BoxToDicing = EgtGetBBoxGlob( nRawPart, GDB_BB.STANDARD )
|
||||||
|
local b3BoxToDicing = EgtGetBBoxGlob( nBox, GDB_BB.STANDARD )
|
||||||
|
-- seleziono il Part e il Layer di destinazione
|
||||||
|
EgtSetCurrPartLayer( nRawPart, nAddGrpId)
|
||||||
|
|
||||||
|
-- conto il numero di facce da processare
|
||||||
|
local nFacet = EgtSurfTmFacetCount( SelectedIndex)
|
||||||
|
EgtOutLog( ' Numero facce ' .. nFacet, 1)
|
||||||
|
|
||||||
|
-- salvo
|
||||||
|
local TabPlanesFeatures = {}
|
||||||
|
for i = 1, nFacet do
|
||||||
|
local ptC, vtN = EgtSurfTmFacetCenter( SelectedIndex, i - 1, GDB_ID.ROOT)
|
||||||
|
table.insert( TabPlanesFeatures, { ptC = ptC, vtN = vtN})
|
||||||
|
end
|
||||||
|
local ptC1 = Point3d( TabPlanesFeatures[1].ptC)
|
||||||
|
local vtN1 = Vector3d( TabPlanesFeatures[1].vtN)
|
||||||
|
|
||||||
|
local CutTable = {}
|
||||||
|
if nFacet == 1 then
|
||||||
|
CutTable = DiceCut.GetDice( nAddGrpId, b3BoxToDicing, ptC1, vtN1, true)
|
||||||
|
elseif nFacet == 2 then
|
||||||
|
CutTable = DiceCut.GetDice( nAddGrpId, b3BoxToDicing, ptC1, vtN1, false, TabPlanesFeatures[2].ptC, TabPlanesFeatures[2].vtN)
|
||||||
|
end
|
||||||
|
|
||||||
|
if EgtGetDebugLevel() >= 3 then
|
||||||
|
DiceCut.PrintOrderCut( CutTable)
|
||||||
|
end
|
||||||
|
|
||||||
|
EgtDeselectAll()
|
||||||
|
|
||||||
|
EgtDraw()
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
--DiceCutTest()
|
||||||
|
|
||||||
|
return DiceCut
|
||||||
@@ -22,7 +22,7 @@ function FeatureLib.NeedTopologyFeature( Proc)
|
|||||||
return true
|
return true
|
||||||
elseif ID.IsHeadCut( Proc) then
|
elseif ID.IsHeadCut( Proc) then
|
||||||
return true
|
return true
|
||||||
elseif ID.IsSplitCut( Proc) then
|
elseif ID.IsTailCut( Proc) then
|
||||||
return true
|
return true
|
||||||
elseif ID.IsDoubleCut( Proc) then
|
elseif ID.IsDoubleCut( Proc) then
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ function Identity.IsHeadCut( Proc)
|
|||||||
return ( Proc.nGrp == 1 and Proc.nPrc == 340)
|
return ( Proc.nGrp == 1 and Proc.nPrc == 340)
|
||||||
end
|
end
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
-- Feature : Split Cut
|
-- Feature : Tail Cut
|
||||||
function Identity.IsSplitCut( Proc)
|
function Identity.IsTailCut( Proc)
|
||||||
return ( Proc.nGrp == 2 and Proc.nPrc == 350)
|
return ( Proc.nGrp == 2 and Proc.nPrc == 350)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,368 @@
|
|||||||
|
-- StrategyLib.lua by Egalware s.r.l. 2024/04/02
|
||||||
|
-- Libreria di supporto a strategie con funzioni comune a strategie diverse.
|
||||||
|
|
||||||
|
-- Tabella per definizione modulo
|
||||||
|
local StrategyLib = {}
|
||||||
|
|
||||||
|
-- Include
|
||||||
|
require( 'EgtBase')
|
||||||
|
|
||||||
|
-- Carico i dati globali
|
||||||
|
local BeamData = require( 'BeamData')
|
||||||
|
local FeatureLib = require( 'FeatureLib')
|
||||||
|
local MachiningLib = require( 'MachiningLib')
|
||||||
|
-- strategie di base
|
||||||
|
local FaceByBlade = require('FACEBYBLADE')
|
||||||
|
|
||||||
|
EgtOutLog( ' StrategyLib started', 1)
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------------
|
||||||
|
---------------------- FUNZIONI DI SPLIT ------------------------------------------
|
||||||
|
-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
StrategyLib.SplitStrategy = {}
|
||||||
|
function StrategyLib.SplitStrategy.Get( Proc, Part, OptionalParameters)
|
||||||
|
local Machining = {}
|
||||||
|
local Result = {}
|
||||||
|
-- sTypeMachining = BladeSideSingle\ BladeSideDouble\ BladeHorizontalSingle\ BladeHorizontalDouble\ ChainSawHorizontal\ ChainSawSideSingle\ ChainSawSideDouble\ ChainSawPlusBlade\ Mill\ None
|
||||||
|
Machining.sTypeMachining = 'None'
|
||||||
|
local Splitting = {}
|
||||||
|
-- imposto parametri di ricerca utensile in base a topologia
|
||||||
|
local ToolSearchParameters = {}
|
||||||
|
ToolSearchParameters.vtToolDirection = Proc.Faces[1].vtN
|
||||||
|
|
||||||
|
-- ===== RICERCA UTENSILE =====
|
||||||
|
-- cerco lama sopra
|
||||||
|
Splitting.bIsApplicable = false
|
||||||
|
ToolSearchParameters.bAllowTopHead = true
|
||||||
|
ToolSearchParameters.bAllowBottomHead = false
|
||||||
|
Splitting.ToolInfo = {}
|
||||||
|
Splitting.ToolInfo = MachiningLib.FindBlade( Proc, ToolSearchParameters)
|
||||||
|
if Splitting.ToolInfo.nToolIndex then
|
||||||
|
Splitting.bIsApplicable = true
|
||||||
|
local ParametersMRR = {}
|
||||||
|
ParametersMRR.nToolIndex = Splitting.ToolInfo.nToolIndex
|
||||||
|
Splitting.dMRR = MachiningLib.GetToolMRR( ParametersMRR)
|
||||||
|
end
|
||||||
|
table.insert( Machining, Splitting)
|
||||||
|
|
||||||
|
-- cerco lama sotto
|
||||||
|
Splitting = {}
|
||||||
|
Splitting.bIsApplicable = false
|
||||||
|
ToolSearchParameters.bAllowTopHead = false
|
||||||
|
ToolSearchParameters.bAllowBottomHead = true
|
||||||
|
Splitting.ToolInfo = {}
|
||||||
|
Splitting.ToolInfo = MachiningLib.FindBlade( Proc, ToolSearchParameters)
|
||||||
|
if Splitting.ToolInfo.nToolIndex then
|
||||||
|
Splitting.bIsApplicable = true
|
||||||
|
local ParametersMRR = {}
|
||||||
|
ParametersMRR.nToolIndex = Splitting.ToolInfo.nToolIndex
|
||||||
|
Splitting.dMRR = MachiningLib.GetToolMRR( ParametersMRR)
|
||||||
|
end
|
||||||
|
table.insert( Machining, Splitting)
|
||||||
|
|
||||||
|
-- cerco motosega
|
||||||
|
Splitting = {}
|
||||||
|
Splitting.bIsApplicable = false
|
||||||
|
table.insert( Machining, Splitting)
|
||||||
|
|
||||||
|
-- cerco fresa
|
||||||
|
Splitting = {}
|
||||||
|
Splitting.bIsApplicable = false
|
||||||
|
table.insert( Machining, Splitting)
|
||||||
|
|
||||||
|
-- ===== SCELTA LAVORAZIONI =====
|
||||||
|
-- forzature da parametri
|
||||||
|
if OptionalParameters.bForceChainSaw then
|
||||||
|
Machining[1].bIsApplicable = false
|
||||||
|
Machining[2].bIsApplicable = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- setto valori di default. Impossibile che taglio di separazione sia incompleto
|
||||||
|
Result.sStatus = 'Completed'
|
||||||
|
Result.nCompletionIndex = FeatureLib.GetFeatureCompletionIndex( 100)
|
||||||
|
Result.dMRR = 1
|
||||||
|
|
||||||
|
-- correzioni sul massimo materiale lama, considerando ingombri vari
|
||||||
|
local dMaxMatBladeSideSingle
|
||||||
|
local dMaxMatBladeSideDouble
|
||||||
|
local dMaxMatBladeHorizontalSingle
|
||||||
|
local dMaxMatBladeHorizontalDouble
|
||||||
|
if Machining[1].bIsApplicable then
|
||||||
|
local dMaxMat = TOOLS[Machining[1].ToolInfo.nToolIndex].dMaxMaterial
|
||||||
|
local dRadius = TOOLS[Machining[1].ToolInfo.nToolIndex].dDiameter / 2
|
||||||
|
local sHead = TOOLS[Machining[1].ToolInfo.nToolIndex].sHead
|
||||||
|
-- se taglio di fianco disponibile, si controlla il massimo materiale reale. Per pezzi alti, bisogna controllare anche l'ingombro asse Z nelle 4 direzioni.
|
||||||
|
if Part.dHeight < BeamData.MIN_DIM_HBEAM then
|
||||||
|
dMaxMat = min( dMaxMat, BeamData.MAX_DIM_HTCUT)
|
||||||
|
dMaxMatBladeSideSingle = dMaxMat
|
||||||
|
dMaxMatBladeSideDouble = dMaxMat * 2
|
||||||
|
else
|
||||||
|
if BeamData.GetMaxMatReductionBladeCut then
|
||||||
|
dMaxMatBladeSideSingle = min( max( dRadius - BeamData.GetMaxMatReductionBladeCut( sHead, Y_AX()), dRadius - BeamData.GetMaxMatReductionBladeCut( sHead, -Y_AX())),
|
||||||
|
TOOLS[Machining[1].ToolInfo.nToolIndex].dMaxMaterial)
|
||||||
|
dMaxMatBladeSideDouble = dRadius - BeamData.GetMaxMatReductionBladeCut( sHead, Y_AX()) + dRadius - BeamData.GetMaxMatReductionBladeCut( sHead, -Y_AX())
|
||||||
|
else
|
||||||
|
dMaxMatBladeSideSingle = abs( BeamData.MAX_DIM_HTCUT_HBEAM)
|
||||||
|
dMaxMatBladeSideDouble = abs( BeamData.MAX_DIM_HTCUT_HBEAM) * 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- se taglio orizzontale
|
||||||
|
if BeamData.GetMaxMatReductionBladeCut then
|
||||||
|
dMaxMatBladeHorizontalSingle = min( TOOLS[Machining[1].ToolInfo.nToolIndex].dMaxMaterial, dRadius - BeamData.GetMaxMatReductionBladeCut( TOOLS[Machining[1].ToolInfo.nToolIndex].sHead, -Z_AX()))
|
||||||
|
else
|
||||||
|
dMaxMatBladeHorizontalSingle = TOOLS[Machining[1].ToolInfo.nToolIndex].dMaxMaterial
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if Machining[2].bIsApplicable then
|
||||||
|
if BeamData.GetMaxMatReductionBladeCut then
|
||||||
|
local dRadius = TOOLS[Machining[2].ToolInfo.nToolIndex].dDiameter / 2
|
||||||
|
dMaxMatBladeHorizontalDouble = min( TOOLS[Machining[2].ToolInfo.nToolIndex].dMaxMaterial, dRadius - BeamData.GetMaxMatReductionBladeCut( TOOLS[Machining[2].ToolInfo.nToolIndex].sHead, Z_AX()))
|
||||||
|
else
|
||||||
|
dMaxMatBladeHorizontalDouble = TOOLS[Machining[2].ToolInfo.nToolIndex].dMaxMaterial
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO considerare di tagliare con il massimo materiale possibile per non salire troppo in Z (macchine tipo PF), oppure non scendere troppo (tipo Kairos)
|
||||||
|
-- BladeSideSingle (taglio di lama singolo di fianco)
|
||||||
|
if Machining[1].bIsApplicable and ( dMaxMatBladeSideSingle - BeamData.CUT_EXTRA) > Part.dWidth + 10 * GEO.EPS_SMALL then
|
||||||
|
Result.nQuality = FeatureLib.GetFeatureQuality( 'Blade')
|
||||||
|
Machining.sTypeMachining = 'BladeSideSingle'
|
||||||
|
Machining[2].bIsApplicable = false
|
||||||
|
Machining[3].bIsApplicable = false
|
||||||
|
Machining[4].bIsApplicable = false
|
||||||
|
return Machining, Result
|
||||||
|
-- TODO considerare di tagliare con il massimo materiale possibile per non salire troppo in Z (macchine tipo PF), oppure non scendere troppo (tipo Kairos)
|
||||||
|
-- BladeSideDouble (taglio di lama doppio di fianco)
|
||||||
|
elseif Machining[1].bIsApplicable and ( dMaxMatBladeSideDouble - BeamData.CUT_EXTRA) > Part.dWidth + 10 * GEO.EPS_SMALL then
|
||||||
|
Result.nQuality = FeatureLib.GetFeatureQuality( 'Blade')
|
||||||
|
Result.dMRR = Result.dMRR/2
|
||||||
|
Machining.sTypeMachining = 'BladeSideDouble'
|
||||||
|
Machining[2].bIsApplicable = false
|
||||||
|
Machining[3].bIsApplicable = false
|
||||||
|
Machining[4].bIsApplicable = false
|
||||||
|
return Machining, Result
|
||||||
|
-- BladeHorizontalSingle (taglio di lama singolo orizzontale)
|
||||||
|
elseif Machining[1].bIsApplicable and ( dMaxMatBladeHorizontalSingle - BeamData.CUT_EXTRA) > Part.dHeight + 10 * GEO.EPS_SMALL then
|
||||||
|
Result.nQuality = FeatureLib.GetFeatureQuality( 'Blade')
|
||||||
|
Machining.sTypeMachining = 'BladeHorizontalSingle'
|
||||||
|
Machining[2].bIsApplicable = false
|
||||||
|
Machining[3].bIsApplicable = false
|
||||||
|
Machining[4].bIsApplicable = false
|
||||||
|
return Machining, Result
|
||||||
|
-- BladeHorizontalDouble (taglio di lama doppio orizzontale)
|
||||||
|
elseif Machining[1].bIsApplicable and Machining[2].bIsApplicable and
|
||||||
|
( dMaxMatBladeHorizontalSingle + dMaxMatBladeHorizontalDouble - BeamData.CUT_EXTRA) > Part.dHeight + 10 * GEO.EPS_SMALL then
|
||||||
|
Result.nQuality = FeatureLib.GetFeatureQuality( 'Blade')
|
||||||
|
Result.dMRR = Result.dMRR/2
|
||||||
|
Machining.sTypeMachining = 'BladeHorizontalDouble'
|
||||||
|
Machining[1].ToolInfo.dMaxMatBladeFromTop = dMaxMatBladeHorizontalSingle
|
||||||
|
Machining[2].ToolInfo.dMaxMatBladeFromDown = dMaxMatBladeHorizontalDouble
|
||||||
|
Machining[3].bIsApplicable = false
|
||||||
|
Machining[4].bIsApplicable = false
|
||||||
|
return Machining, Result
|
||||||
|
|
||||||
|
-- TODO : casi con motosega da completare
|
||||||
|
-- ChainSawHorizontal (motosega)
|
||||||
|
-- ChainSawSideSingle (motosega)
|
||||||
|
-- ChainSawSideDouble (motosega)
|
||||||
|
-- ChainSawPlusBlade (motosega più lama orizzontale)
|
||||||
|
-- Mill (svuotatura)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- se non ho trovato neanche una lavorazione completa, non posso separare
|
||||||
|
if Machining.sTypeMachining == 'None' then
|
||||||
|
Result.sStatus = 'Not-Applicable'
|
||||||
|
Result.nCompletionIndex = 0
|
||||||
|
Result.dMRR = 0
|
||||||
|
Result.nQuality = 0
|
||||||
|
Result.sInfo = 'Split not possible'
|
||||||
|
end
|
||||||
|
|
||||||
|
return Machining, Result
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
function StrategyLib.SplitStrategy.GetEdgeToMachine( Proc, vtEdge)
|
||||||
|
local Edge
|
||||||
|
for i = 1, #Proc.Faces[1].Edges do
|
||||||
|
if AreSameVectorApprox( Proc.Faces[1].Edges[i].Norm, vtEdge) then
|
||||||
|
Edge = Proc.Faces[1].Edges[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return Edge
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
function StrategyLib.SplitStrategy.CalculateLeadInOut( EdgeToMachine)
|
||||||
|
|
||||||
|
local LeadIn = {}
|
||||||
|
local LeadOut = {}
|
||||||
|
LeadIn.dStartAddLength = 0
|
||||||
|
LeadOut.dEndAddLength = 0
|
||||||
|
LeadIn.nType = MCH_MILL_LI.LINEAR
|
||||||
|
LeadOut.nType = MCH_MILL_LI.LINEAR
|
||||||
|
LeadIn.dTangentDistance = 0
|
||||||
|
LeadOut.dTangentDistance = 0
|
||||||
|
-- elevazione sempre in negativo
|
||||||
|
if EdgeToMachine.Elev < 10 * GEO.EPS_SMALL then
|
||||||
|
LeadIn.dPerpDistance = BeamData.CUT_SIC - EdgeToMachine.Elev
|
||||||
|
LeadOut.dPerpDistance = BeamData.CUT_SIC - EdgeToMachine.Elev
|
||||||
|
else
|
||||||
|
LeadIn.dPerpDistance = BeamData.CUT_SIC
|
||||||
|
LeadOut.dPerpDistance = BeamData.CUT_SIC
|
||||||
|
end
|
||||||
|
LeadIn.dElevation = 0
|
||||||
|
LeadOut.dElevation = 0
|
||||||
|
LeadIn.dCompLength = 0
|
||||||
|
LeadOut.dCompLength = 0
|
||||||
|
LeadIn.dStartAddLength = BeamData.CUT_EXTRA
|
||||||
|
LeadOut.dEndAddLength = BeamData.CUT_EXTRA
|
||||||
|
|
||||||
|
return LeadIn, LeadOut
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------------
|
||||||
|
function StrategyLib.SplitStrategy.Execute( Proc, Part, Strategy)
|
||||||
|
local Machinings = {}
|
||||||
|
|
||||||
|
-- applico le lavorazioni
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
if Strategy.SplitStrategy.sTypeMachining == 'BladeSideSingle' then
|
||||||
|
local Machining = {}
|
||||||
|
Machining.Splitting = {}
|
||||||
|
Machining.AuxiliaryData = {}
|
||||||
|
local OptionalParameters = {}
|
||||||
|
OptionalParameters.nToolIndex = Strategy.SplitStrategy[1].ToolInfo.nToolIndex
|
||||||
|
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_BACK
|
||||||
|
OptionalParameters.sDepth = 0
|
||||||
|
OptionalParameters.dLongitudinalOffset = Strategy.dOffset or 0
|
||||||
|
OptionalParameters.sUserNotes = EgtIf( Strategy.bSplit, 'Split;', 'Cut;')
|
||||||
|
|
||||||
|
Machining.AuxiliaryData.bAddNewPhase = true
|
||||||
|
|
||||||
|
local EdgeToMachine = StrategyLib.SplitStrategy.GetEdgeToMachine( Proc, -Y_AX())
|
||||||
|
-- approccio e retrazione
|
||||||
|
OptionalParameters.LeadIn, OptionalParameters.LeadOut = StrategyLib.SplitStrategy.CalculateLeadInOut( EdgeToMachine)
|
||||||
|
Machining.Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
||||||
|
table.insert( Machinings, Machining)
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
elseif Strategy.SplitStrategy.sTypeMachining == 'BladeSideDouble' then
|
||||||
|
local Machining = {}
|
||||||
|
Machining.Splitting = {}
|
||||||
|
Machining.AuxiliaryData = {}
|
||||||
|
local OptionalParameters = {}
|
||||||
|
OptionalParameters.nToolIndex = Strategy.SplitStrategy[1].ToolInfo.nToolIndex
|
||||||
|
-- Taglio lato frontale
|
||||||
|
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_BACK
|
||||||
|
OptionalParameters.sDepth = 0
|
||||||
|
OptionalParameters.dLongitudinalOffset = Strategy.dOffset or 0
|
||||||
|
OptionalParameters.dRadialOffset = ( Part.dWidth - BeamData.CUT_EXTRA) / 2
|
||||||
|
OptionalParameters.sUserNotes = EgtIf( Strategy.bSplit, 'Presplit;', 'Precut;')
|
||||||
|
|
||||||
|
local EdgeToMachine = StrategyLib.SplitStrategy.GetEdgeToMachine( Proc, -Y_AX())
|
||||||
|
-- approccio e retrazione
|
||||||
|
OptionalParameters.LeadIn, OptionalParameters.LeadOut = StrategyLib.SplitStrategy.CalculateLeadInOut( EdgeToMachine)
|
||||||
|
Machining.Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
||||||
|
table.insert( Machinings, Machining)
|
||||||
|
|
||||||
|
-- Taglio lato dietro
|
||||||
|
Machining = {}
|
||||||
|
Machining.Splitting = {}
|
||||||
|
Machining.AuxiliaryData = {}
|
||||||
|
OptionalParameters = {}
|
||||||
|
OptionalParameters.nToolIndex = Strategy.SplitStrategy[1].ToolInfo.nToolIndex
|
||||||
|
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_FRONT
|
||||||
|
OptionalParameters.sDepth = 0
|
||||||
|
OptionalParameters.dLongitudinalOffset = Strategy.dOffset or 0
|
||||||
|
OptionalParameters.dRadialOffset = ( Part.dWidth - BeamData.CUT_EXTRA) / 2
|
||||||
|
OptionalParameters.sUserNotes = EgtIf( Strategy.bSplit, 'Split;', 'Cut;')
|
||||||
|
|
||||||
|
Machining.AuxiliaryData.bAddNewPhase = true
|
||||||
|
|
||||||
|
EdgeToMachine = StrategyLib.SplitStrategy.GetEdgeToMachine( Proc, Y_AX())
|
||||||
|
-- approccio e retrazione
|
||||||
|
OptionalParameters.LeadIn, OptionalParameters.LeadOut = StrategyLib.SplitStrategy.CalculateLeadInOut( EdgeToMachine)
|
||||||
|
Machining.Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
||||||
|
table.insert( Machinings, Machining)
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
elseif Strategy.SplitStrategy.sTypeMachining == 'BladeHorizontalSingle' then
|
||||||
|
local Machining = {}
|
||||||
|
Machining.Splitting = {}
|
||||||
|
Machining.AuxiliaryData = {}
|
||||||
|
local OptionalParameters = {}
|
||||||
|
OptionalParameters.nToolIndex = Strategy.SplitStrategy[1].ToolInfo.nToolIndex
|
||||||
|
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_DOWN
|
||||||
|
OptionalParameters.sDepth = 0
|
||||||
|
OptionalParameters.dLongitudinalOffset = Strategy.dOffset or 0
|
||||||
|
OptionalParameters.dRadialOffset = -BeamData.CUT_EXTRA
|
||||||
|
OptionalParameters.sUserNotes = EgtIf( Strategy.bSplit, 'Split;', 'Cut;')
|
||||||
|
|
||||||
|
Machining.AuxiliaryData.bAddNewPhase = true
|
||||||
|
|
||||||
|
local EdgeToMachine = StrategyLib.SplitStrategy.GetEdgeToMachine( Proc, Z_AX())
|
||||||
|
-- approccio e retrazione
|
||||||
|
OptionalParameters.LeadIn, OptionalParameters.LeadOut = StrategyLib.SplitStrategy.CalculateLeadInOut( EdgeToMachine)
|
||||||
|
Machining.Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
||||||
|
table.insert( Machinings, Machining)
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
elseif Strategy.SplitStrategy.sTypeMachining == 'BladeHorizontalDouble' then
|
||||||
|
local Machining = {}
|
||||||
|
Machining.Splitting = {}
|
||||||
|
Machining.AuxiliaryData = {}
|
||||||
|
local OptionalParameters = {}
|
||||||
|
OptionalParameters.nToolIndex = Strategy.SplitStrategy[1].ToolInfo.nToolIndex
|
||||||
|
-- Taglio lato frontale
|
||||||
|
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_DOWN
|
||||||
|
OptionalParameters.sDepth = 0
|
||||||
|
OptionalParameters.dLongitudinalOffset = Strategy.dOffset or 0
|
||||||
|
local dExtraMaxMat = ( Strategy.SplitStrategy[1].ToolInfo.dMaxMatBladeFromTop + Strategy.SplitStrategy[2].ToolInfo.dMaxMatBladeFromDown - Part.dHeight - BeamData.CUT_EXTRA) / 2
|
||||||
|
OptionalParameters.dRadialOffset = Part.dHeight - Strategy.SplitStrategy[1].ToolInfo.dMaxMatBladeFromTop + dExtraMaxMat
|
||||||
|
OptionalParameters.sUserNotes = EgtIf( Strategy.bSplit, 'Presplit;', 'Precut;')
|
||||||
|
|
||||||
|
|
||||||
|
local EdgeToMachine = StrategyLib.SplitStrategy.GetEdgeToMachine( Proc, Z_AX())
|
||||||
|
-- approccio e retrazione
|
||||||
|
OptionalParameters.LeadIn, OptionalParameters.LeadOut = StrategyLib.SplitStrategy.CalculateLeadInOut( EdgeToMachine)
|
||||||
|
Machining.Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
||||||
|
table.insert( Machinings, Machining)
|
||||||
|
|
||||||
|
-- Taglio lato dietro
|
||||||
|
Machining = {}
|
||||||
|
Machining.Splitting = {}
|
||||||
|
Machining.AuxiliaryData = {}
|
||||||
|
OptionalParameters = {}
|
||||||
|
OptionalParameters.nToolIndex = Strategy.SplitStrategy[2].ToolInfo.nToolIndex
|
||||||
|
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_TOP
|
||||||
|
OptionalParameters.sDepth = 0
|
||||||
|
OptionalParameters.dLongitudinalOffset = Strategy.dOffset or 0
|
||||||
|
OptionalParameters.dRadialOffset = Part.dHeight - Strategy.SplitStrategy[2].ToolInfo.dMaxMatBladeFromDown + dExtraMaxMat
|
||||||
|
OptionalParameters.sUserNotes = EgtIf( Strategy.bSplit, 'Split;', 'Cut;')
|
||||||
|
|
||||||
|
Machining.AuxiliaryData.bAddNewPhase = true
|
||||||
|
|
||||||
|
EdgeToMachine = StrategyLib.SplitStrategy.GetEdgeToMachine( Proc, -Z_AX())
|
||||||
|
-- approccio e retrazione
|
||||||
|
OptionalParameters.LeadIn, OptionalParameters.LeadOut = StrategyLib.SplitStrategy.CalculateLeadInOut( EdgeToMachine)
|
||||||
|
Machining.Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
||||||
|
table.insert( Machinings, Machining)
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
elseif Strategy.SplitStrategy.sTypeMachining == 'ChainSawHorizontal' then
|
||||||
|
; -- TODO
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
elseif Strategy.SplitStrategy.sTypeMachining == 'ChainSawSideSingle' then
|
||||||
|
; -- TODO
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
elseif Strategy.SplitStrategy.sTypeMachining == 'ChainSawSideDouble' then
|
||||||
|
; -- TODO
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
elseif Strategy.SplitStrategy.sTypeMachining == 'ChainSawPlusBlade' then
|
||||||
|
; -- TODO
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
end
|
||||||
|
return Machinings
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
return StrategyLib
|
||||||
+4
-2
@@ -42,12 +42,14 @@ _G.package.loaded.BasicCustomerStrategies = nil
|
|||||||
_G.package.loaded.FeatureLib = nil
|
_G.package.loaded.FeatureLib = nil
|
||||||
_G.package.loaded.FaceData = nil
|
_G.package.loaded.FaceData = nil
|
||||||
_G.package.loaded.MachiningLib = nil
|
_G.package.loaded.MachiningLib = nil
|
||||||
|
_G.package.loaded.DiceCut = nil
|
||||||
|
_G.package.loaded.StrategyLib = nil
|
||||||
_G.package.loaded.Logs = nil
|
_G.package.loaded.Logs = nil
|
||||||
-- strategie di base sempre presenti
|
-- strategie di base sempre presenti
|
||||||
_G.package.loaded['SPLITCUT\\SPLITCUT'] = nil
|
|
||||||
_G.package.loaded['SPLITCUT\\SPLITCUTConfig'] = nil
|
|
||||||
_G.package.loaded['HEADCUT\\HEADCUT'] = nil
|
_G.package.loaded['HEADCUT\\HEADCUT'] = nil
|
||||||
_G.package.loaded['HEADCUT\\HEADCUTConfig'] = nil
|
_G.package.loaded['HEADCUT\\HEADCUTConfig'] = nil
|
||||||
|
_G.package.loaded['TAILCUT\\TAILCUT'] = nil
|
||||||
|
_G.package.loaded['TAILCUT\\TAILCUTConfig'] = nil
|
||||||
|
|
||||||
-- TODO controllare se c'è un modo migliore per resettare librerie delle strategie caricate precedentemente
|
-- TODO controllare se c'è un modo migliore per resettare librerie delle strategie caricate precedentemente
|
||||||
-- Per ottimizzare potremmo anche ciclare solo fino al numero di strategie raggiunto per il momento.
|
-- Per ottimizzare potremmo anche ciclare solo fino al numero di strategie raggiunto per il momento.
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ function FACEBYBLADE.Make( Proc, Part, FaceToMachine, EdgeToMachine, OptionalPar
|
|||||||
local vtMachiningDirection = EdgeToMachine.Norm
|
local vtMachiningDirection = EdgeToMachine.Norm
|
||||||
local vtN = Proc.Faces[FaceToMachine+1].vtN
|
local vtN = Proc.Faces[FaceToMachine+1].vtN
|
||||||
Cutting.sDepth = OptionalParameters.sDepth or 0
|
Cutting.sDepth = OptionalParameters.sDepth or 0
|
||||||
|
Cutting.dLongitudinalOffset = OptionalParameters.dLongitudinalOffset or 0
|
||||||
Cutting.dRadialOffset = OptionalParameters.dRadialOffset or 0
|
Cutting.dRadialOffset = OptionalParameters.dRadialOffset or 0
|
||||||
|
|
||||||
Cutting.nType = MCH_MY.MILLING
|
Cutting.nType = MCH_MY.MILLING
|
||||||
|
|||||||
@@ -1,425 +0,0 @@
|
|||||||
-- Strategia: SPLITCUT
|
|
||||||
-- Descrizione
|
|
||||||
-- Taglio di separazione
|
|
||||||
-- Feature: SplitCut
|
|
||||||
|
|
||||||
-- carico librerie
|
|
||||||
local BeamLib = require( 'BeamLib')
|
|
||||||
local BeamData = require( 'BeamData')
|
|
||||||
local MachiningLib = require( 'MachiningLib')
|
|
||||||
local FeatureLib = require( 'FeatureLib')
|
|
||||||
-- strategie di base
|
|
||||||
local FaceByBlade = require('FACEBYBLADE')
|
|
||||||
|
|
||||||
-- Tabella per definizione modulo
|
|
||||||
local SPLITCUT = {}
|
|
||||||
local Strategy = {}
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
|
||||||
local function LoadStrategyParameters( CustomParameters)
|
|
||||||
local StrategyLib = {}
|
|
||||||
StrategyLib.Config = require( 'SPLITCUT\\SPLITCUTConfig')
|
|
||||||
Strategy.sName = StrategyLib.Config.sStrategyId
|
|
||||||
CustomParameters = BeamLib.GetUpdateCustomParameters( CustomParameters, StrategyLib.Config.Parameters)
|
|
||||||
Strategy.Parameters = BeamLib.LoadCustomParametersInStrategy( CustomParameters)
|
|
||||||
Strategy.Result = {}
|
|
||||||
Strategy.Machining = {}
|
|
||||||
Strategy.Result.sInfo = ''
|
|
||||||
return Strategy
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
|
||||||
local function CalculateLeadInOut( Machining, EdgeToMachine)
|
|
||||||
|
|
||||||
local LeadIn = {}
|
|
||||||
local LeadOut = {}
|
|
||||||
LeadIn.dStartAddLength = 0
|
|
||||||
LeadOut.dEndAddLength = 0
|
|
||||||
LeadIn.nType = MCH_MILL_LI.LINEAR
|
|
||||||
LeadOut.nType = MCH_MILL_LI.LINEAR
|
|
||||||
LeadIn.dTangentDistance = 0
|
|
||||||
LeadOut.dTangentDistance = 0
|
|
||||||
-- elevazione sempre in negativo
|
|
||||||
if EdgeToMachine.Elev < 10 * GEO.EPS_SMALL then
|
|
||||||
LeadIn.dPerpDistance = BeamData.CUT_SIC - EdgeToMachine.Elev
|
|
||||||
LeadOut.dPerpDistance = BeamData.CUT_SIC - EdgeToMachine.Elev
|
|
||||||
else
|
|
||||||
LeadIn.dPerpDistance = BeamData.CUT_SIC
|
|
||||||
LeadOut.dPerpDistance = BeamData.CUT_SIC
|
|
||||||
end
|
|
||||||
LeadIn.dElevation = 0
|
|
||||||
LeadOut.dElevation = 0
|
|
||||||
LeadIn.dCompLength = 0
|
|
||||||
LeadOut.dCompLength = 0
|
|
||||||
LeadIn.dStartAddLength = BeamData.CUT_EXTRA
|
|
||||||
LeadOut.dEndAddLength = BeamData.CUT_EXTRA
|
|
||||||
|
|
||||||
return LeadIn, LeadOut
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
|
||||||
local function MakeChamfer()
|
|
||||||
-- TODO funzionalità da aggiungere
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
|
||||||
local function GetEdgeToMachine( Proc, vtEdge)
|
|
||||||
local Edge
|
|
||||||
for i = 1, #Proc.Faces[1].Edges do
|
|
||||||
if AreSameVectorApprox( Proc.Faces[1].Edges[i].Norm, vtEdge) then
|
|
||||||
Edge = Proc.Faces[1].Edges[i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return Edge
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
|
||||||
local function GetSplitStrategy( Proc, Part)
|
|
||||||
-- se non sono stati caricati i parametri, si ricaricano
|
|
||||||
if not Strategy.Parameters then
|
|
||||||
Strategy = LoadStrategyParameters()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- separazione solo se esiste grezzo successivo con pezzi o scaricabile
|
|
||||||
local nNextRawId = EgtGetNextRawPart( Part.idRaw)
|
|
||||||
Strategy.bSplit = ( nNextRawId and ( EgtGetPartInRawPartCount( nNextRawId) > 0 or EgtGetRawPartBBox( nNextRawId):getDimX() >= BeamData.dMinRaw))
|
|
||||||
|
|
||||||
-- imposto paraemtri di ricerca utensile in base a topologia
|
|
||||||
local Machining = {}
|
|
||||||
-- sTypeMachining = BladeSideSingle\ BladeSideDouble\ BladeHorizontalSingle\ BladeHorizontalDouble\ ChainSawHorizontal\ ChainSawSideSingle\ ChainSawSideDouble\ ChainSawPlusBlade\ Mill\ None
|
|
||||||
Machining.sTypeMachining = 'None'
|
|
||||||
local Splitting = {}
|
|
||||||
local ToolSearchParameters = {}
|
|
||||||
ToolSearchParameters.vtToolDirection = Proc.Faces[1].vtN
|
|
||||||
|
|
||||||
|
|
||||||
-- ===== RICERCA UTENSILE =====
|
|
||||||
-- cerco lama sopra
|
|
||||||
Splitting.bIsApplicable = false
|
|
||||||
ToolSearchParameters.bAllowTopHead = true
|
|
||||||
ToolSearchParameters.bAllowBottomHead = false
|
|
||||||
Splitting.ToolInfo = {}
|
|
||||||
Splitting.ToolInfo = MachiningLib.FindBlade( Proc, ToolSearchParameters)
|
|
||||||
if Splitting.ToolInfo.nToolIndex then
|
|
||||||
Splitting.bIsApplicable = true
|
|
||||||
local ParametersMRR = {}
|
|
||||||
ParametersMRR.nToolIndex = Splitting.ToolInfo.nToolIndex
|
|
||||||
Splitting.dMRR = MachiningLib.GetToolMRR( ParametersMRR)
|
|
||||||
end
|
|
||||||
table.insert( Machining, Splitting)
|
|
||||||
|
|
||||||
-- cerco lama sotto
|
|
||||||
Splitting = {}
|
|
||||||
Splitting.bIsApplicable = false
|
|
||||||
ToolSearchParameters.bAllowTopHead = false
|
|
||||||
ToolSearchParameters.bAllowBottomHead = true
|
|
||||||
Splitting.ToolInfo = {}
|
|
||||||
Splitting.ToolInfo = MachiningLib.FindBlade( Proc, ToolSearchParameters)
|
|
||||||
if Splitting.ToolInfo.nToolIndex then
|
|
||||||
Splitting.bIsApplicable = true
|
|
||||||
local ParametersMRR = {}
|
|
||||||
ParametersMRR.nToolIndex = Splitting.ToolInfo.nToolIndex
|
|
||||||
Splitting.dMRR = MachiningLib.GetToolMRR( ParametersMRR)
|
|
||||||
end
|
|
||||||
table.insert( Machining, Splitting)
|
|
||||||
|
|
||||||
-- cerco motosega
|
|
||||||
Splitting = {}
|
|
||||||
Splitting.bIsApplicable = false
|
|
||||||
table.insert( Machining, Splitting)
|
|
||||||
|
|
||||||
-- cerco fresa
|
|
||||||
Splitting = {}
|
|
||||||
Splitting.bIsApplicable = false
|
|
||||||
table.insert( Machining, Splitting)
|
|
||||||
|
|
||||||
-- ===== SCELTA LAVORAZIONI =====
|
|
||||||
-- forzature da parametri
|
|
||||||
if Strategy.Parameters.bForceChainSaw then
|
|
||||||
Machining[1].bIsApplicable = false
|
|
||||||
Machining[2].bIsApplicable = false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- setto valori di default. Impossibile che taglio di separazione sia incompleto
|
|
||||||
Strategy.Result.sStatus = 'Completed'
|
|
||||||
Strategy.Result.nCompletionIndex = FeatureLib.GetFeatureCompletionIndex( 100)
|
|
||||||
Strategy.Result.dMRR = 1
|
|
||||||
|
|
||||||
-- correzioni sul massimo materiale lama, considerando ingombri vari
|
|
||||||
local dMaxMatBladeSideSingle
|
|
||||||
local dMaxMatBladeSideDouble
|
|
||||||
local dMaxMatBladeHorizontalSingle
|
|
||||||
local dMaxMatBladeHorizontalDouble
|
|
||||||
if Machining[1].bIsApplicable then
|
|
||||||
local dMaxMat = TOOLS[Machining[1].ToolInfo.nToolIndex].dMaxMaterial
|
|
||||||
local dRadius = TOOLS[Machining[1].ToolInfo.nToolIndex].dDiameter / 2
|
|
||||||
local sHead = TOOLS[Machining[1].ToolInfo.nToolIndex].sHead
|
|
||||||
-- se taglio di fianco disponibile, si controlla il massimo materiale reale. Per pezzi alti, bisogna controllare anche l'ingombro asse Z nelle 4 direzioni.
|
|
||||||
if Part.dHeight < BeamData.MIN_DIM_HBEAM then
|
|
||||||
dMaxMat = min( dMaxMat, BeamData.MAX_DIM_HTCUT)
|
|
||||||
dMaxMatBladeSideSingle = dMaxMat
|
|
||||||
dMaxMatBladeSideDouble = dMaxMat * 2
|
|
||||||
else
|
|
||||||
if BeamData.GetMaxMatReductionBladeCut then
|
|
||||||
dMaxMatBladeSideSingle = min( max( dRadius - BeamData.GetMaxMatReductionBladeCut( sHead, Y_AX()), dRadius - BeamData.GetMaxMatReductionBladeCut( sHead, -Y_AX())),
|
|
||||||
TOOLS[Machining[1].ToolInfo.nToolIndex].dMaxMaterial)
|
|
||||||
dMaxMatBladeSideDouble = dRadius - BeamData.GetMaxMatReductionBladeCut( sHead, Y_AX()) + dRadius - BeamData.GetMaxMatReductionBladeCut( sHead, -Y_AX())
|
|
||||||
else
|
|
||||||
dMaxMatBladeSideSingle = abs( BeamData.MAX_DIM_HTCUT_HBEAM)
|
|
||||||
dMaxMatBladeSideDouble = abs( BeamData.MAX_DIM_HTCUT_HBEAM) * 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- se taglio orizzontale
|
|
||||||
if BeamData.GetMaxMatReductionBladeCut then
|
|
||||||
dMaxMatBladeHorizontalSingle = min( TOOLS[Machining[1].ToolInfo.nToolIndex].dMaxMaterial, dRadius - BeamData.GetMaxMatReductionBladeCut( TOOLS[Machining[1].ToolInfo.nToolIndex].sHead, -Z_AX()))
|
|
||||||
else
|
|
||||||
dMaxMatBladeHorizontalSingle = TOOLS[Machining[1].ToolInfo.nToolIndex].dMaxMaterial
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if Machining[2].bIsApplicable then
|
|
||||||
if BeamData.GetMaxMatReductionBladeCut then
|
|
||||||
local dRadius = TOOLS[Machining[2].ToolInfo.nToolIndex].dDiameter / 2
|
|
||||||
dMaxMatBladeHorizontalDouble = min( TOOLS[Machining[2].ToolInfo.nToolIndex].dMaxMaterial, dRadius - BeamData.GetMaxMatReductionBladeCut( TOOLS[Machining[2].ToolInfo.nToolIndex].sHead, Z_AX()))
|
|
||||||
else
|
|
||||||
dMaxMatBladeHorizontalDouble = TOOLS[Machining[2].ToolInfo.nToolIndex].dMaxMaterial
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO considerare di tagliare con il massimo materiale possibile per non salire troppo in Z (macchine tipo PF), oppure non scendere troppo (tipo Kairos)
|
|
||||||
-- BladeSideSingle (taglio di lama singolo di fianco)
|
|
||||||
if Machining[1].bIsApplicable and ( dMaxMatBladeSideSingle - BeamData.CUT_EXTRA) > Part.dWidth + 10 * GEO.EPS_SMALL then
|
|
||||||
Strategy.Result.nQuality = FeatureLib.GetFeatureQuality( 'Blade')
|
|
||||||
Machining.sTypeMachining = 'BladeSideSingle'
|
|
||||||
Machining[2].bIsApplicable = false
|
|
||||||
Machining[3].bIsApplicable = false
|
|
||||||
Machining[4].bIsApplicable = false
|
|
||||||
return Machining
|
|
||||||
-- TODO considerare di tagliare con il massimo materiale possibile per non salire troppo in Z (macchine tipo PF), oppure non scendere troppo (tipo Kairos)
|
|
||||||
-- BladeSideDouble (taglio di lama doppio di fianco)
|
|
||||||
elseif Machining[1].bIsApplicable and ( dMaxMatBladeSideDouble - BeamData.CUT_EXTRA) > Part.dWidth + 10 * GEO.EPS_SMALL then
|
|
||||||
Strategy.Result.nQuality = FeatureLib.GetFeatureQuality( 'Blade')
|
|
||||||
Strategy.Result.dMRR = Strategy.Result.dMRR/2
|
|
||||||
Machining.sTypeMachining = 'BladeSideDouble'
|
|
||||||
Machining[2].bIsApplicable = false
|
|
||||||
Machining[3].bIsApplicable = false
|
|
||||||
Machining[4].bIsApplicable = false
|
|
||||||
return Machining
|
|
||||||
-- BladeHorizontalSingle (taglio di lama singolo orizzontale)
|
|
||||||
elseif Machining[1].bIsApplicable and ( dMaxMatBladeHorizontalSingle - BeamData.CUT_EXTRA) > Part.dHeight + 10 * GEO.EPS_SMALL then
|
|
||||||
Strategy.Result.nQuality = FeatureLib.GetFeatureQuality( 'Blade')
|
|
||||||
Machining.sTypeMachining = 'BladeHorizontalSingle'
|
|
||||||
Machining[2].bIsApplicable = false
|
|
||||||
Machining[3].bIsApplicable = false
|
|
||||||
Machining[4].bIsApplicable = false
|
|
||||||
return Machining
|
|
||||||
-- BladeHorizontalDouble (taglio di lama doppio orizzontale)
|
|
||||||
elseif Machining[1].bIsApplicable and Machining[2].bIsApplicable and
|
|
||||||
( dMaxMatBladeHorizontalSingle + dMaxMatBladeHorizontalDouble - BeamData.CUT_EXTRA) > Part.dHeight + 10 * GEO.EPS_SMALL then
|
|
||||||
Strategy.Result.nQuality = FeatureLib.GetFeatureQuality( 'Blade')
|
|
||||||
Strategy.Result.dMRR = Strategy.Result.dMRR/2
|
|
||||||
Machining.sTypeMachining = 'BladeHorizontalDouble'
|
|
||||||
Machining[1].ToolInfo.dMaxMatBladeFromTop = dMaxMatBladeHorizontalSingle
|
|
||||||
Machining[2].ToolInfo.dMaxMatBladeFromDown = dMaxMatBladeHorizontalDouble
|
|
||||||
Machining[3].bIsApplicable = false
|
|
||||||
Machining[4].bIsApplicable = false
|
|
||||||
return Machining
|
|
||||||
|
|
||||||
-- TODO : casi con motosega da completare
|
|
||||||
-- ChainSawHorizontal (motosega)
|
|
||||||
-- ChainSawSideSingle (motosega)
|
|
||||||
-- ChainSawSideDouble (motosega)
|
|
||||||
-- ChainSawPlusBlade (motosega più lama orizzontale)
|
|
||||||
-- Mill (svuotatura)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- se non ho trovato neanche una lavorazione completa, non posso separare
|
|
||||||
if Machining.sTypeMachining == 'None' then
|
|
||||||
Strategy.Result.sStatus = 'Not-Applicable'
|
|
||||||
Strategy.Result.nCompletionIndex = 0
|
|
||||||
Strategy.Result.dMRR = 0
|
|
||||||
Strategy.Result.nQuality = 0
|
|
||||||
Strategy.Result.sInfo = 'Split not possible'
|
|
||||||
end
|
|
||||||
|
|
||||||
return Machining
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
|
||||||
function SPLITCUT.Make( bAddMachining, Proc, Part, CustomParameters)
|
|
||||||
Strategy = LoadStrategyParameters( CustomParameters)
|
|
||||||
local bAreAllMachiningsAdded = true
|
|
||||||
local Splitting = {}
|
|
||||||
local AuxiliaryData = {}
|
|
||||||
|
|
||||||
Strategy.sSplitStrategy = GetSplitStrategy( Proc, Part)
|
|
||||||
if bAddMachining then
|
|
||||||
-- inserimento smussi su spigoli del taglio
|
|
||||||
if Strategy.Parameters.bMakeChamfer then
|
|
||||||
MakeChamfer()
|
|
||||||
end
|
|
||||||
|
|
||||||
local OptionalParameters = {}
|
|
||||||
-- applico le lavorazioni
|
|
||||||
----------------------------------------------------------------------------------
|
|
||||||
if Strategy.sSplitStrategy.sTypeMachining == 'BladeSideSingle' then
|
|
||||||
OptionalParameters = {}
|
|
||||||
OptionalParameters.nToolIndex = Strategy.sSplitStrategy[1].ToolInfo.nToolIndex
|
|
||||||
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_BACK
|
|
||||||
OptionalParameters.sDepth = 0
|
|
||||||
|
|
||||||
-- TODO gestire lavorazione a cubetti
|
|
||||||
|
|
||||||
if Strategy.bSplit then
|
|
||||||
OptionalParameters.sUserNotes = 'Split;'
|
|
||||||
else
|
|
||||||
OptionalParameters.sUserNotes = 'Cut;'
|
|
||||||
end
|
|
||||||
AuxiliaryData.bAddNewPhase = true
|
|
||||||
|
|
||||||
local EdgeToMachine = GetEdgeToMachine( Proc, -Y_AX())
|
|
||||||
-- approccio e retrazione
|
|
||||||
OptionalParameters.LeadIn, OptionalParameters.LeadOut = CalculateLeadInOut( Splitting, EdgeToMachine)
|
|
||||||
Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
|
||||||
MachiningLib.AddNewMachining( Proc, Splitting, AuxiliaryData)
|
|
||||||
----------------------------------------------------------------------------------
|
|
||||||
elseif Strategy.sSplitStrategy.sTypeMachining == 'BladeSideDouble' then
|
|
||||||
OptionalParameters = {}
|
|
||||||
OptionalParameters.nToolIndex = Strategy.sSplitStrategy[1].ToolInfo.nToolIndex
|
|
||||||
-- Taglio lato frontale
|
|
||||||
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_BACK
|
|
||||||
OptionalParameters.sDepth = 0
|
|
||||||
OptionalParameters.dRadialOffset = ( Part.dWidth - BeamData.CUT_EXTRA) / 2
|
|
||||||
|
|
||||||
-- TODO gestire lavorazione a cubetti
|
|
||||||
|
|
||||||
if Strategy.bSplit then
|
|
||||||
OptionalParameters.sUserNotes = 'PreSplit;'
|
|
||||||
else
|
|
||||||
OptionalParameters.sUserNotes = 'PreCut;'
|
|
||||||
end
|
|
||||||
|
|
||||||
local EdgeToMachine = GetEdgeToMachine( Proc, -Y_AX())
|
|
||||||
-- approccio e retrazione
|
|
||||||
OptionalParameters.LeadIn, OptionalParameters.LeadOut = CalculateLeadInOut( Splitting, EdgeToMachine)
|
|
||||||
Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
|
||||||
MachiningLib.AddNewMachining( Proc, Splitting, AuxiliaryData)
|
|
||||||
|
|
||||||
-- Taglio lato dietro
|
|
||||||
OptionalParameters = {}
|
|
||||||
Splitting = {}
|
|
||||||
AuxiliaryData = {}
|
|
||||||
OptionalParameters.nToolIndex = Strategy.sSplitStrategy[1].ToolInfo.nToolIndex
|
|
||||||
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_FRONT
|
|
||||||
OptionalParameters.sDepth = 0
|
|
||||||
OptionalParameters.dRadialOffset = ( Part.dWidth - BeamData.CUT_EXTRA) / 2
|
|
||||||
|
|
||||||
-- TODO gestire lavorazione a cubetti
|
|
||||||
|
|
||||||
if Strategy.bSplit then
|
|
||||||
OptionalParameters.sUserNotes = 'Split;'
|
|
||||||
else
|
|
||||||
OptionalParameters.sUserNotes = 'Cut;'
|
|
||||||
end
|
|
||||||
AuxiliaryData.bAddNewPhase = true
|
|
||||||
|
|
||||||
EdgeToMachine = GetEdgeToMachine( Proc, Y_AX())
|
|
||||||
-- approccio e retrazione
|
|
||||||
OptionalParameters.LeadIn, OptionalParameters.LeadOut = CalculateLeadInOut( Splitting, EdgeToMachine)
|
|
||||||
Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
|
||||||
MachiningLib.AddNewMachining( Proc, Splitting, AuxiliaryData)
|
|
||||||
----------------------------------------------------------------------------------
|
|
||||||
elseif Strategy.sSplitStrategy.sTypeMachining == 'BladeHorizontalSingle' then
|
|
||||||
OptionalParameters = {}
|
|
||||||
OptionalParameters.nToolIndex = Strategy.sSplitStrategy[1].ToolInfo.nToolIndex
|
|
||||||
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_DOWN
|
|
||||||
OptionalParameters.sDepth = 0
|
|
||||||
OptionalParameters.dRadialOffset = -BeamData.CUT_EXTRA
|
|
||||||
|
|
||||||
-- TODO gestire lavorazione a cubetti
|
|
||||||
|
|
||||||
if Strategy.bSplit then
|
|
||||||
OptionalParameters.sUserNotes = 'Split;'
|
|
||||||
else
|
|
||||||
OptionalParameters.sUserNotes = 'Cut;'
|
|
||||||
end
|
|
||||||
AuxiliaryData.bAddNewPhase = true
|
|
||||||
|
|
||||||
local EdgeToMachine = GetEdgeToMachine( Proc, Z_AX())
|
|
||||||
-- approccio e retrazione
|
|
||||||
OptionalParameters.LeadIn, OptionalParameters.LeadOut = CalculateLeadInOut( Splitting, EdgeToMachine)
|
|
||||||
Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
|
||||||
MachiningLib.AddNewMachining( Proc, Splitting, AuxiliaryData)
|
|
||||||
----------------------------------------------------------------------------------
|
|
||||||
elseif Strategy.sSplitStrategy.sTypeMachining == 'BladeHorizontalDouble' then
|
|
||||||
OptionalParameters = {}
|
|
||||||
OptionalParameters.nToolIndex = Strategy.sSplitStrategy[1].ToolInfo.nToolIndex
|
|
||||||
-- Taglio lato frontale
|
|
||||||
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_DOWN
|
|
||||||
OptionalParameters.sDepth = 0
|
|
||||||
local dExtraMaxMat = ( Strategy.sSplitStrategy[1].ToolInfo.dMaxMatBladeFromTop + Strategy.sSplitStrategy[2].ToolInfo.dMaxMatBladeFromDown - Part.dHeight - BeamData.CUT_EXTRA) / 2
|
|
||||||
OptionalParameters.dRadialOffset = Part.dHeight - Strategy.sSplitStrategy[1].ToolInfo.dMaxMatBladeFromTop + dExtraMaxMat
|
|
||||||
|
|
||||||
-- TODO gestire lavorazione a cubetti
|
|
||||||
|
|
||||||
if Strategy.bSplit then
|
|
||||||
OptionalParameters.sUserNotes = 'PreSplit;'
|
|
||||||
else
|
|
||||||
OptionalParameters.sUserNotes = 'PreCut;'
|
|
||||||
end
|
|
||||||
|
|
||||||
local EdgeToMachine = GetEdgeToMachine( Proc, Z_AX())
|
|
||||||
-- approccio e retrazione
|
|
||||||
OptionalParameters.LeadIn, OptionalParameters.LeadOut = CalculateLeadInOut( Splitting, EdgeToMachine)
|
|
||||||
Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
|
||||||
MachiningLib.AddNewMachining( Proc, Splitting, AuxiliaryData)
|
|
||||||
|
|
||||||
-- Taglio lato dietro
|
|
||||||
OptionalParameters = {}
|
|
||||||
Splitting = {}
|
|
||||||
AuxiliaryData = {}
|
|
||||||
OptionalParameters.nToolIndex = Strategy.sSplitStrategy[2].ToolInfo.nToolIndex
|
|
||||||
OptionalParameters.nFaceuse = MCH_MILL_FU.ORTHO_TOP
|
|
||||||
OptionalParameters.sDepth = 0
|
|
||||||
OptionalParameters.dRadialOffset = Part.dHeight - Strategy.sSplitStrategy[2].ToolInfo.dMaxMatBladeFromDown + dExtraMaxMat
|
|
||||||
|
|
||||||
-- TODO gestire lavorazione a cubetti
|
|
||||||
|
|
||||||
if Strategy.bSplit then
|
|
||||||
OptionalParameters.sUserNotes = 'Split;'
|
|
||||||
else
|
|
||||||
OptionalParameters.sUserNotes = 'Cut;'
|
|
||||||
end
|
|
||||||
AuxiliaryData.bAddNewPhase = true
|
|
||||||
|
|
||||||
EdgeToMachine = GetEdgeToMachine( Proc, -Z_AX())
|
|
||||||
-- approccio e retrazione
|
|
||||||
OptionalParameters.LeadIn, OptionalParameters.LeadOut = CalculateLeadInOut( Splitting, EdgeToMachine)
|
|
||||||
Splitting = FaceByBlade.Make( Proc, Part, Proc.Faces[1].id, EdgeToMachine, OptionalParameters)
|
|
||||||
MachiningLib.AddNewMachining( Proc, Splitting, AuxiliaryData)
|
|
||||||
----------------------------------------------------------------------------------
|
|
||||||
elseif Strategy.sSplitStrategy.sTypeMachining == 'ChainSawHorizontal' then
|
|
||||||
; -- TODO
|
|
||||||
----------------------------------------------------------------------------------
|
|
||||||
elseif Strategy.sSplitStrategy.sTypeMachining == 'ChainSawSideSingle' then
|
|
||||||
; -- TODO
|
|
||||||
----------------------------------------------------------------------------------
|
|
||||||
elseif Strategy.sSplitStrategy.sTypeMachining == 'ChainSawSideDouble' then
|
|
||||||
; -- TODO
|
|
||||||
----------------------------------------------------------------------------------
|
|
||||||
elseif Strategy.sSplitStrategy.sTypeMachining == 'ChainSawPlusBlade' then
|
|
||||||
; -- TODO
|
|
||||||
----------------------------------------------------------------------------------
|
|
||||||
elseif Strategy.sSplitStrategy.sTypeMachining == 'Mill' then
|
|
||||||
; -- TODO
|
|
||||||
----------------------------------------------------------------------------------
|
|
||||||
end
|
|
||||||
|
|
||||||
return bAreAllMachiningsAdded, Strategy.Result
|
|
||||||
else
|
|
||||||
return nil, Strategy.Result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
return SPLITCUT
|
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
-- Strategia: TAILCUT
|
||||||
|
-- Descrizione
|
||||||
|
-- Taglio di separazione
|
||||||
|
-- Feature: TailCut
|
||||||
|
|
||||||
|
-- carico librerie
|
||||||
|
local BeamLib = require( 'BeamLib')
|
||||||
|
local BeamData = require( 'BeamData')
|
||||||
|
local MachiningLib = require( 'MachiningLib')
|
||||||
|
local StrategyLib = require( 'StrategyLib')
|
||||||
|
local DiceCut = require( 'DiceCut')
|
||||||
|
|
||||||
|
-- Tabella per definizione modulo
|
||||||
|
local TAILCUT = {}
|
||||||
|
local Strategy = {}
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
local function MakeChamfer()
|
||||||
|
-- TODO funzionalità da aggiungere
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
function TAILCUT.Make( bAddMachining, Proc, Part, CustomParameters)
|
||||||
|
local StrategyDefaultParam = {}
|
||||||
|
StrategyDefaultParam.Config = require( 'TAILCUT\\TAILCUTConfig')
|
||||||
|
Strategy.sName = StrategyDefaultParam.Config.sStrategyId
|
||||||
|
CustomParameters = BeamLib.GetUpdateCustomParameters( CustomParameters, StrategyDefaultParam.Config.Parameters)
|
||||||
|
Strategy.Parameters = BeamLib.LoadCustomParametersInStrategy( CustomParameters)
|
||||||
|
Strategy.SplitStrategy = {}
|
||||||
|
Strategy.Result = {}
|
||||||
|
Strategy.Machining = {}
|
||||||
|
Strategy.Result.sInfo = ''
|
||||||
|
local bAreAllMachiningsAdded = true
|
||||||
|
|
||||||
|
-- separazione solo se esiste grezzo successivo con pezzi o scaricabile
|
||||||
|
Strategy.bSplit = not( Part.bIsLastPart) or Part.dRestLength >= BeamData.dMinRaw
|
||||||
|
|
||||||
|
-- se devo fare split perchè c'è un grezzo da scaricare o un altro pezzo
|
||||||
|
if Strategy.bSplit then
|
||||||
|
Strategy.SplitStrategy, Strategy.Result = StrategyLib.SplitStrategy.Get( Proc, Part, Strategy.Parameters)
|
||||||
|
|
||||||
|
-- se devo rimuovere tutto il restante
|
||||||
|
else
|
||||||
|
-- se abilitato, faccio tagli di PRECUT a zero (come SPLIT)
|
||||||
|
if Strategy.Parameters.bExecutePreCut then
|
||||||
|
Strategy.SplitStrategy, Strategy.Result = StrategyLib.SplitStrategy.Get( Proc, Part, Strategy.Parameters)
|
||||||
|
-- se non faccio tagli PRECUT, imposto tabella Result direttamente. Non serve verificare che riesca a rimuovere il materiale extra
|
||||||
|
else
|
||||||
|
Strategy.Result.sStatus = 'Completed'
|
||||||
|
Strategy.Result.nCompletionIndex = 5
|
||||||
|
Strategy.Result.dMRR = 1
|
||||||
|
Strategy.Result.nQuality = 5
|
||||||
|
Strategy.Result.sInfo = 'Split not possible'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- se devo applicare le lavorazioni
|
||||||
|
if bAddMachining then
|
||||||
|
|
||||||
|
-- inserimento smussi su spigoli del taglio
|
||||||
|
if Strategy.Parameters.bMakeChamfer then
|
||||||
|
MakeChamfer()
|
||||||
|
end
|
||||||
|
|
||||||
|
local MachiningsToAdd = {}
|
||||||
|
-- se devo fare split perchè c'è un grezzo da scaricare o un altro pezzo
|
||||||
|
if Strategy.bSplit then
|
||||||
|
Strategy.dOffset = 0
|
||||||
|
MachiningsToAdd = StrategyLib.SplitStrategy.Execute( Proc, Part, Strategy)
|
||||||
|
-- se devo rimuovere tutto il restante
|
||||||
|
else
|
||||||
|
-- se abilitato, faccio tagli di PRECUT a zero (come SPLIT)
|
||||||
|
if Strategy.Parameters.bExecutePreCut then
|
||||||
|
Strategy.dOffset = Part.dRestLength
|
||||||
|
MachiningsToAdd = StrategyLib.SplitStrategy.Execute( Proc, Part, Strategy)
|
||||||
|
end
|
||||||
|
-- rimuovo tutto il restante
|
||||||
|
-- table.insert( MachiningsToAdd, StrategyLib.BladeToWasteOneFace( Proc, Part))
|
||||||
|
local Faces = {}
|
||||||
|
local Face1 = {}
|
||||||
|
local Face2 = {}
|
||||||
|
local OptionalParameters = {}
|
||||||
|
Face1.ptCenter = Proc.Faces[1].ptCenter
|
||||||
|
Face1.vtNormal = Proc.Faces[1].vtN
|
||||||
|
OptionalParameters.b3BoxToDicing = Part.b3Raw
|
||||||
|
Faces = DiceCut.GetDice( Part, Face1, Face2, OptionalParameters)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- aggiungo lavorazioni trovate alla lista generale
|
||||||
|
for i = 1, #MachiningsToAdd do
|
||||||
|
MachiningLib.AddNewMachining( Proc, MachiningsToAdd[i].Splitting, MachiningsToAdd[i].AuxiliaryData)
|
||||||
|
end
|
||||||
|
|
||||||
|
return bAreAllMachiningsAdded, Strategy.Result
|
||||||
|
else
|
||||||
|
return nil, Strategy.Result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return TAILCUT
|
||||||
+5
-4
@@ -1,12 +1,13 @@
|
|||||||
-- Parametri configurabili da cliente per strategia: SPLITCUT
|
-- Parametri configurabili da cliente per strategia: TAILCUT
|
||||||
|
|
||||||
local SPLITCUTData = {
|
local TAILCUTData = {
|
||||||
sStrategyId = 'SPLITCUT',
|
sStrategyId = 'TAILCUT',
|
||||||
Parameters = {
|
Parameters = {
|
||||||
{ sName = 'bMakeChamfer', sValue = 'false', sDescriptionShort = 'Execute Chamfer', sDescriptionLong = 'Use the V-Mill to execute chamfers on cut-edges', sType = 'b', sMessageId = '', sMinUserLevel = '1'},
|
{ sName = 'bMakeChamfer', sValue = 'false', sDescriptionShort = 'Execute Chamfer', sDescriptionLong = 'Use the V-Mill to execute chamfers on cut-edges', sType = 'b', sMessageId = '', sMinUserLevel = '1'},
|
||||||
{ sName = 'bForceChainSaw', sValue = 'false', sDescriptionShort = 'Force to use chain saw', sDescriptionLong = 'Force to use chain saw', sType = 'b', sMessageId = '', sMinUserLevel = '1'},
|
{ sName = 'bForceChainSaw', sValue = 'false', sDescriptionShort = 'Force to use chain saw', sDescriptionLong = 'Force to use chain saw', sType = 'b', sMessageId = '', sMinUserLevel = '1'},
|
||||||
|
{ sName = 'bExecutePreCut', sValue = 'true', sDescriptionShort = 'Force to add PreCuts', sDescriptionLong = "Autocam will apply a machining on the theoric zero, to avoid collision if the theoric piece length doesn't correspond to the real length", sType = 'b', sMessageId = '', sMinUserLevel = '1'},
|
||||||
{ sName = 'bFinishWithMill', sValue = 'true', sDescriptionShort = 'Finish with mill', sDescriptionLong = 'Use a mill to finish the surface if split with chain saw', sType = 'b', sMessageId = '', sMinUserLevel = '1'}
|
{ sName = 'bFinishWithMill', sValue = 'true', sDescriptionShort = 'Finish with mill', sDescriptionLong = 'Use a mill to finish the surface if split with chain saw', sType = 'b', sMessageId = '', sMinUserLevel = '1'}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SPLITCUTData
|
return TAILCUTData
|
||||||
@@ -139,8 +139,8 @@ STR0003 = Topologia tipo LapJoint. Lama + motosega
|
|||||||
252,0,Feature,
|
252,0,Feature,
|
||||||
; Feature : HEADCUT
|
; Feature : HEADCUT
|
||||||
340,0,Feature,HEADCUT
|
340,0,Feature,HEADCUT
|
||||||
; Feature : SPLITCUT
|
; Feature : TAILCUT
|
||||||
350,0,Feature,SPLITCUT
|
350,0,Feature,TAILCUT
|
||||||
; Feature : Variant
|
; Feature : Variant
|
||||||
900,0,Feature,
|
900,0,Feature,
|
||||||
; Feature Decor
|
; Feature Decor
|
||||||
|
|||||||
Reference in New Issue
Block a user