Imports EgtUILib
Imports EgtWPFLib

Public Class NestPageUC
   ' Riferimento alla MainWindow
   Private m_MainWindow As MainWindow = DirectCast(Application.Current.MainWindow, MainWindow)
   Private WithEvents m_CurrProjPage As CurrentProjectPageUC
   ' Flag di pagina attiva
   Private m_bActive As Boolean = False
   ' Flag per modalità nesting standard o registrazione posizione lastra
   Friend m_bRegister As Boolean = False
   ' Flag per pezzi selezionati in tavola o fuori
   Private m_nPartPos As Integer = PART_POS.NONE_TABLE
   ' Flag per impostata per selezionare di una curva
   Friend m_bSelectCurv As Boolean = False
   ' dati per DragRettangoli (estensio/accorciamento dei rettangoli trascinando un lato)
   Private m_bIsVisibleDragRettangle As Boolean = False
   Friend m_bIsCheckedDragRettangle As Boolean = False

   ' Id della curva e del taglio selezionati per eseguire il test
   Friend m_CurrSelectedCurv As Integer = GDB_ID.NULL
   Friend m_CurrSelectedSawCurv As Integer = GDB_ID.NULL

   ' Id del rettangolo, del traglio e della curva da modificare tramite Drag
   Private m_nIdSelectedOutLoopRettangle As Integer = GDB_ID.NULL
   Private m_nIdSelectedRegionRettangle As Integer = GDB_ID.NULL
   Private m_nIdSelectedSawRettangle As Integer = GDB_ID.NULL
   Private m_nIdSelectedCurvRettangle As Integer = GDB_ID.NULL
   Private m_nIdSelectedTextRettangle As Integer = GDB_ID.NULL
   Private m_nIdSelectedTextTopRettangle As Integer = GDB_ID.NULL
   Private m_nIdSelectedFlatSurfRettangle As Integer = GDB_ID.NULL
   Private m_SideListRettangle(4) As Integer
   Private m_sTextContent As String = String.Empty
   Private m_dRettangleIsModified As Boolean = False

   Enum PART_POS As Integer
      IN_TABLE = -1
      NONE_TABLE = 0
      OUT_TABLE = 1
   End Enum
   ' Identificativi per pezzo da selezionare/deselezionare
   Private m_nIdToSel As Integer = GDB_ID.NULL
   Private m_nIdToDesel As Integer = GDB_ID.NULL
   ' Dati del grezzo
   Private m_nRawId As Integer = GDB_ID.NULL
   Private m_b3Raw As New BBox3d
   Private m_dKerf As Double
   Private m_dMinDist As Double = 0
   ' Dati movimento
   Private m_dStep As Double = 0
   Private m_dMaxStep As Double = 0
   Private m_bMaximizeMove As Boolean = False
   Private m_dAngStep As Double = 0
   Private m_bReducedCut As Boolean = False
   ' Dati per tolleranza spessore Trf
   Private m_dTrfThickTolerance As Double = 0
   ' Dati movimento registrazione grezzo
   Private m_dRegStep As Double = 0
   Private m_dRegAngStep As Double = 0
   Private m_dMaxRegAng As Double = 10
   ' Dati per Drag
   Private m_nRestRadius As Integer = 3
   Private m_bDrag As Boolean = False
   Private m_bDragToStart As Boolean = False
   Private m_bVerify As Boolean = False
   Private m_bFromParking As Boolean = False
   Private m_bDragging As Boolean = False
   Private m_locPrev As System.Drawing.Point
   Private m_ptPrev As Point3d
   Private m_vtTotMove As Vector3d
   Private m_bMagnetic As Boolean = False
   Private m_dSnapDist As Double = 0

   ' lista delle famiglie di pezzi in parcheggio
   Private m_ListOfGroupInPark As New List(Of FamilyPart)
   ' se seleniozato il layer CounterPz
   Private m_bIsCounterLy As Boolean = False
   Private m_nCurrNubrOfParts As Integer = 0

   Private Sub NestPage_Initialized(sender As Object, e As EventArgs)
      ' Imposto i messaggi letti dal file dei messaggi
      InsertPartBtn.ToolTip = EgtMsg(MSG_NESTPAGEUC + 1)            'Insert part - Inserisci pezzo
      StorePartBtn.ToolTip = EgtMsg(MSG_NESTPAGEUC + 2)             'Store part - Parcheggia pezzo
      RemovePartBtn.ToolTip = EgtMsg(MSG_NESTPAGEUC + 3)            'Remove part - Elimina pezzo
      SelectAllBtn.ToolTip = EgtMsg(MSG_NESTPAGEUC + 4)             'Select All - Seleziona Tutto
      DeselectAllBtn.ToolTip = EgtMsg(MSG_NESTPAGEUC + 5)           'Deselect All - Deseleziona Tutto

      ' gestisco la visualizzazione del comando PAN (per il drag dei rettangoli)
      m_bIsVisibleDragRettangle = (GetPrivateProfileInt(S_NEST, K_DRAG_RETTANGLE, 0, m_MainWindow.GetIniFile()) <> 0)
      If Not m_bIsVisibleDragRettangle Then
         UG1.Columns = 1
         DragRettanleBtn.Visibility = Visibility.Collapsed
      End If

   End Sub

   Private Sub NestPage_Loaded(sender As Object, e As RoutedEventArgs)
      m_bActive = True
      LoadParams()
      EgtZoom(ZM.ALL)
   End Sub

   Public Sub CalcRawPart()
      ' determino il grezzo (è il primo con fase 1)
      m_nRawId = GetCurrentRaw()
      ' recupero dimensioni del grezzo e kerf
      If Not EgtGetRawPartBBox(m_nRawId, m_b3Raw) Then
         m_nRawId = GDB_ID.NULL
         m_b3Raw.Setup()
      End If
      If Not EgtGetInfo(m_nRawId, KEY_KERF, m_dKerf) Then m_dKerf = 0
   End Sub

   Friend Sub LoadParams()
      m_CurrProjPage = m_MainWindow.m_CurrentProjectPageUC
      ' recupero dimensioni del grezzo e kerf
      CalcRawPart()
      ' calcolo valore di minima distanza (da spessore lama corrente)
      Dim dSawThick As Double = 0
      If EgtTdbSetCurrTool(m_MainWindow.m_CurrentMachine.sCurrSaw) AndAlso
         EgtTdbGetCurrToolParam(MCH_TP.THICK, dSawThick) Then
         m_dMinDist = dSawThick
      Else
         EgtOutLog("Not found current saw for nesting mindist")
         m_dMinDist = 0
      End If
      ' carico e calcolo i parametri di movimento e drag
      m_dStep = GetPrivateProfileDouble(S_NEST, K_STEP, 10, m_MainWindow.GetIniFile())
      StepMoveTxBx.Text = LenToString(m_dStep, 3)
      m_dMaxStep = Math.Max(m_b3Raw.DimX(), m_b3Raw.DimY())
      m_dAngStep = GetPrivateProfileDouble(S_NEST, K_ANGSTEP, 15, m_MainWindow.GetIniFile())
      RotationAngleTxBx.Text = DoubleToString(m_dAngStep, 2)
      m_nRestRadius = GetPrivateProfileInt(S_NEST, K_RESTRADIUS, 3, m_MainWindow.GetIniFile())
      m_dSnapDist = GetPrivateProfileDouble(S_NEST, K_SNAPDIST, 50, m_MainWindow.GetIniFile())
      m_bReducedCut = (GetPrivateProfileInt(S_MACH_NEST, K_MACH_REDUCEDCUT, 0, m_MainWindow.GetMachIniFile()) <> 0)
      ' Carico parametro tolleranza spessore per Trf
      m_dTrfThickTolerance = Math.Max(GetPrivateProfileDouble(S_NEST, K_TRFTHICKTOLERANCE, 2, m_MainWindow.GetIniFile()), EPS_SMALL)
      ' carico i parametri di registrazione grezzo
      m_dRegStep = GetPrivateProfileDouble(S_REG, K_REGSTEP, 10, m_MainWindow.GetIniFile())
      m_dRegAngStep = GetPrivateProfileDouble(S_REG, K_REGANGSTEP, 1, m_MainWindow.GetIniFile())
      m_dMaxRegAng = GetPrivateProfileDouble(S_MACH_REG, K_MACH_MAX_ROT_ANG, 10, m_MainWindow.GetMachIniFile())

      ' Garantisco visibilità pezzi parcheggiati
      ShowParkedParts()
      ' Garantisco visibilità eventuale contorno da foto
      m_CurrProjPage.ShowContour(True)
   End Sub

   Public Sub UpdateStepsOnUI()
      If Not m_bRegister Then
         StepMoveTxBx.Text = LenToString(m_dStep, 3)
         RotationAngleTxBx.Text = DoubleToString(m_dAngStep, 2)
      Else
         StepMoveTxBx.Text = LenToString(m_dRegStep, 3)
         RotationAngleTxBx.Text = DoubleToString(m_dRegAngStep, 2)
      End If
   End Sub

   ' funzione utilizzata per la selezione dei tagli quando il comando TEST è attivo
   Private Sub OnMyMouseDownSceneSelCurv(sender As Object, e As System.Windows.Forms.MouseEventArgs)
      ' Verifico di essere il gestore attivo e non in modalità registrazione
      If Not m_bActive Or m_bRegister Then Return
      ' Si può selezionare solo con il tasto sinistro e se il bottone TEST Attivo
      If e.Button <> Windows.Forms.MouseButtons.Left Or
         Not m_bSelectCurv Then
         Return
      End If
      ' Se esiste un elemento selezionato
      EgtResetMark(m_CurrSelectedSawCurv)
      ' provo a riconoscere il valore medio del segmento 
      ' Verifico se selezionato indicativo di pezzo
      EgtSetObjFilterForSelWin(False, True, False, False, False)
      Dim nSelMy As Integer
      EgtSelect(e.Location, Scene.DIM_SEL, Scene.DIM_SEL, nSelMy)
      Dim nIdMy As Integer = EgtGetFirstObjInSelWin()
      While nIdMy <> GDB_ID.NULL
         Dim sLayer As String = String.Empty
         ' layer di origine
         Dim nIdParent As Integer = EgtGetParent(nIdMy)
         ' recupero il nome del Layer
         EgtGetName(nIdParent, sLayer)
         ' solo se il nome del layer è quello associato ad un lato esterno allora procedo con l'evidenziazione
         If sLayer = NAME_OUTLOOP Then
            If EgtGetType(nIdMy) = GDB_TY.CRV_ARC OrElse EgtGetType(nIdMy) = GDB_TY.CRV_COMPO OrElse EgtGetType(nIdMy) = GDB_TY.CRV_LINE Then
               Dim ptS As Point3d
               EgtStartPoint(nIdMy, ptS)
               Dim ptE As Point3d
               EgtEndPoint(nIdMy, ptE)
               If m_CurrSelectedCurv <> GDB_ID.NULL Or m_CurrSelectedCurv = nIdMy Then
                  EgtDeselectObj(m_CurrSelectedCurv)
               End If
               ' recupero il gruppo della lavorazione associata
               Dim nIdParentPart As Integer = EgtGetParent(nIdParent)
               Dim nIdPV As Integer = EgtGetFirstNameInGroup(nIdParentPart, "PV")
               Dim nIdSaw As Integer = EgtGetFirstNameInGroup(nIdPV, "Saw" & nIdMy.ToString)
               If Not EgtGetGroupObjs(nIdSaw) Then
                  m_CurrSelectedSawCurv = nIdSaw
                  m_CurrSelectedCurv = nIdMy
                  EgtSetMark(nIdSaw)
                  EgtSelectObj(nIdMy)
                  EgtDraw()
                  Exit While
               End If

            End If
         End If
         nIdMy = EgtGetNextObjInSelWin()
      End While
      Return
   End Sub

#Region "DRAG RETTANGLE"

   ' funzione chiamata da OnMyMouseMoveScene: verifica che la selezione sia corretta (deve essere il lato di una figura rettangolare)
   Private Sub OnMyMouseDownSceneSelSideRettangle(sender As Object, e As System.Windows.Forms.MouseEventArgs)
      ' Verifico di essere il gestore attivo e non in modalità registrazione
      If Not m_bActive Or m_bRegister Then Return
      ' Si può selezionare solo con il tasto sinistro e se il bottone TEST Attivo
      If e.Button <> Windows.Forms.MouseButtons.Left then
         Return
      End If
      ' Verifico se selezionato indicativo di pezzo
      EgtSetObjFilterForSelWin(False, True, False, False, False)
      Dim nSelMy As Integer
      EgtSelect(e.Location, Scene.DIM_SEL, Scene.DIM_SEL, nSelMy)
      Dim nIdMy As Integer = EgtGetFirstObjInSelWin()
      While nIdMy <> GDB_ID.NULL
         Dim sLayer As String = String.Empty
         ' layer di origine
         Dim nIdParent As Integer = EgtGetParent(nIdMy)
         ' recupero il nome del Layer
         EgtGetName(nIdParent, sLayer)
         ' solo se il nome del layer è quello associato ad un lato esterno allora procedo con l'evidenziazione
         If sLayer = NAME_OUTLOOP Then
            If EgtGetType(nIdMy) = GDB_TY.CRV_LINE Then
               ' -------------------------------- INIZIO VERIFICHE --------------------------------
               m_nIdSelectedOutLoopRettangle = nIdParent
               If Not IsRettangle(m_SideListRettangle) Then
                  m_CurrProjPage.ClearMessage()
                  m_CurrProjPage.SetWarningMessage("Non è un rettangolo (0)")
                  Return
               End If
               ' recupero il gruppo della lavorazione associata
               Dim nIdParentPart As Integer = EgtGetParent(nIdParent)
               Dim nIdPV As Integer = EgtGetFirstNameInGroup(nIdParentPart, "PV")
               ' se non esiste un gruppo di lavorazione associato allora esco
               If nIdPV = GDB_ID.NULL Then
                  m_CurrProjPage.ClearMessage()
                  m_CurrProjPage.SetWarningMessage("Pezzo in parcheggio (1)")
                  Return
               End If
               ' se esiste ma è vuoto (pezzo scaricato da tavola)
               If EgtGetFirstInGroup(nIdPV) = GDB_ID.NULL Then
                  m_CurrProjPage.ClearMessage()
                  m_CurrProjPage.SetWarningMessage("Pezzo in parcheggio (2)")
                  Return
               End If
               ' Devo verificare che sia una lavorazione solo di lama?
               Dim nIdSaw As Integer = EgtGetFirstNameInGroup(nIdPV, "Saw" & nIdMy.ToString)
               ' -------------------------------- FINE VERIFICHE --------------------------------

               If Not EgtGetGroupObjs(nIdSaw) Then
                  m_nIdSelectedSawRettangle = nIdSaw
                  m_nIdSelectedCurvRettangle = nIdMy

                  m_nIdSelectedRegionRettangle = EgtGetFirstNameInGroup(nIdParentPart, NAME_REGION)
                  ' recupero i testi cintenuti nel layer Region
                  If m_nIdSelectedRegionRettangle <> GDB_ID.NULL Then
                     Dim nCurrId As Integer = EgtGetFirstInGroup(m_nIdSelectedRegionRettangle)
                     While nCurrId <> GDB_ID.NULL
                        If EgtGetType(nCurrId) = GDB_TY.EXT_TEXT Then
                           Dim sVal As String = String.Empty
                           EgtTextGetContent(nCurrId, sVal)
                           If sVal = "*TOP*" Then
                              m_nIdSelectedTextTopRettangle = nCurrId
                           Else
                              m_nIdSelectedTextRettangle = nCurrId
                              m_sTextContent = GetMyRettangleText(sVal)
                           End If
                        ElseIf EgtGetType(nCurrId) = GDB_TY.SRF_FRGN Then
                           m_nIdSelectedFlatSurfRettangle = nCurrId
                        End If
                        nCurrId = EgtGetNext(nCurrId)
                     End While
                  End If
                  ' seleziono ed evidenzio il lato selezionato
                  EgtSelectObj(nIdSaw)
                  EgtSetMark(nIdSaw)
                  EgtSelectObj(nIdMy)
                  EgtDraw()
                  Exit While
               End If
            End If
            ' se le'elemto corrente non è una linea
         End If
         ' se le'elemto corrente non appartiene al layer OutLoop passo al prossimo elemento dell'elenco
         nIdMy = EgtGetNextObjInSelWin()
      End While
   End Sub

   ' restituisce il valore del Text ripulito dalle dimensioni
   Private Function GetMyRettangleText(sTextPz As String) As String
      ' da DrawPageUC.xaml.vb/MultipleInsert
      Dim sText As String = String.Empty
      If sTextPz.Contains("<br/>") Then
         Dim nLastIndex As Integer = sTextPz.LastIndexOf(">")
         sText = sTextPz.Remove(nLastIndex + 1)
         Return sText
      End If
      If sTextPz.Contains("  ") Then
         Dim sInfo() As String = sTextPz.Split(" ")
         If sInfo.Count > 3 Then
            Return sText = sInfo(0) & "  " & sInfo(1) & "  "
         End If
      End If
      Return sText
   End Function

   ' verifico che la figura selezionata sia realmente un rettangolo
   Private Function IsRettangle(ByRef IdSideList() As Integer) As Boolean
      ' creo una lista con i lati del rettangolo
      Dim nCounter As Integer = 0
      ' recupero i lati dal leyer "OutLoop"
      Dim IdSideRettangle As Integer = EgtGetFirstInGroup(m_nIdSelectedOutLoopRettangle)
      While IdSideRettangle <> GDB_ID.NULL And nCounter < 4
         Dim sVal As String = String.Empty
         Dim dPrevAng As Double = 0
         EgtGetInfo(IdSideRettangle, "PrevAng", sVal)
         StringToDouble(sVal, dPrevAng)
         Dim dNextAng As Double = 0
         EgtGetInfo(IdSideRettangle, "NextAng", sVal)
         StringToDouble(sVal, dNextAng)
         ' se gli angoli non sono retti allora esco senza caricare il lato corrente
         If Math.Abs(dPrevAng - 90) > EPS_ANG_SMALL OrElse Math.Abs(dNextAng - 90) > EPS_ANG_SMALL Then
            Exit While
         End If
         IdSideList(nCounter) = IdSideRettangle
         nCounter += 1
         IdSideRettangle = EgtGetNext(IdSideRettangle)
      End While
      ' verifico che siano solo quattro lati
      If IdSideRettangle = GDB_ID.NULL And nCounter = 4 Then
         ' solo se la figura ha 4 lati ortogonali
         Return True
      End If

      m_nIdSelectedOutLoopRettangle = GDB_ID.NULL
      ' la figura presenta più/meno di 4 lati    
      Return False
   End Function

   ' recupero le informazioni correnti per sapere se la figura è mdificabile
   Private Function GetRettangleInfoScale(ByRef frFixed As Frame3d, ByRef MaxLen As Double, ByRef MinLen As Double) As Vector3d
      ' determino la direzione del lato selezionato
      Dim vtDirSelectedCurv As New Vector3d
      EgtStartVector(m_nIdSelectedCurvRettangle, GDB_ID.ROOT, vtDirSelectedCurv)

      ' recupero il lato successivo a quello selezionato 
      Dim Index As Integer = 0
      Dim Len As Double = 0
      For Ind As Integer = 0 To 3
         If m_SideListRettangle(Ind) = m_nIdSelectedCurvRettangle Then
            ' verifico se esiste un lato Precedente altrimenti recupero l'ultimo della lista
            If Ind = 0 Then
               Index = 3
            Else
               Index = Ind - 1
            End If
         End If
      Next
      ' recupero la dimensione del lato
      EgtCurveLength(m_SideListRettangle(Index), MaxLen)
      EgtCurveLength(m_nIdSelectedCurvRettangle, MinLen)

      ' verifico che il lato selezionato sia il maggiore
      If MinLen > MaxLen Then Return Nothing

      ' determimo il versore dal lato appena calcolato
      Dim vtDir As New Vector3d

      EgtStartVector(m_SideListRettangle(Index), GDB_ID.ROOT, vtDir)
      vtDir.Normalize()
      Dim dProjectionBaseX As Double = vtDir * Vector3d.X_AX
      Dim ptFixed As New Point3d
      EgtStartPoint(m_SideListRettangle(Index), GDB_ID.ROOT, ptFixed)

      Dim vtPerp As New Vector3d(vtDir.x, vtDir.y, vtDir.z)
      vtPerp.Rotate(Vector3d.Z_AX, 90)

      ' determino il Frame corrente
      frFixed = New Frame3d(ptFixed, vtDir, vtPerp, Vector3d.Z_AX)

      Return vtDir

   End Function

   ' funzione chiamata da OnMyMouseMoveScene: gestisce la modifica lungo una dimensione del rettangolo selezionato
   Private Sub OnMyMouseMoveSawRettangle(sender As Object, e As System.Windows.Forms.MouseEventArgs)
      ' Verifico di essere il gestore attivo
      If Not m_bActive Then Return
      ' Se drag non abilitato o già in esecuzione, esco
      If Not m_bDrag Or m_bDragging Then Return
      ' Se primo movimento di drag, verifico di aver superato la soglia di movimento in pixel
      If m_bDragToStart Then
         If Math.Abs(e.Location.X - m_locPrev.X) < m_nRestRadius And
            Math.Abs(e.Location.Y - m_locPrev.Y) < m_nRestRadius Then
            Return
         End If
         m_bDragToStart = False
      End If

      ' riferiemento che deve rimanere fisso del rettangolo 
      Dim frFixed As Frame3d = Nothing
      ' lunghezza del lato da modificare
      Dim MaxLen As Double = 0
      Dim MinLen As Double = 0
      ' recupero info sul rettangolo selezionato
      Dim vtDir As Vector3d = GetRettangleInfoScale(frFixed, MaxLen, MinLen)
      If vtDir.Equals(Vector3d.NULL) Then
         Return
      End If

      ' Inizio esecuzione di drag
      m_bDragging = True

      ' Ricavo il punto corrente in coordinate mondo
      Dim ptCurr As Point3d
      EgtUnProjectPoint(e.Location, ptCurr)
      ' Ricavo il vettore di movimento
      Dim vtMove As Vector3d = ptCurr - m_ptPrev
      ' ricavo il valore efficace di allungamento (la proiezione)
      Dim dProjection As Double = vtMove * vtDir
      vtMove = vtDir * dProjection
      ' recupero il valore di scalatura
      Dim dScalefactor = dProjection / MaxLen + 1

      ' Modifico dimensione figura
      If Math.Abs(dScalefactor) > EPS_SMALL * EPS_SMALL Then
         EgtScale(m_nIdSelectedOutLoopRettangle, frFixed, dScalefactor, 1, 1, GDB_ID.ROOT)
         EgtScale(m_nIdSelectedFlatSurfRettangle, frFixed, dScalefactor, 1, 1, GDB_ID.ROOT)
         Dim ptCen As Point3d
         EgtCenterPoint(m_nIdSelectedTextRettangle, ptCen)
         EgtModifyText(m_nIdSelectedTextRettangle, m_sTextContent & LenToString(MaxLen + dProjection, 2) & " x " & LenToString(MinLen, 2))
         EgtMove(m_nIdSelectedTextRettangle, vtMove * 0.5, GDB_ID.ROOT)
         If m_nIdSelectedTextTopRettangle <> GDB_ID.NULL Then
            EgtMove(m_nIdSelectedTextTopRettangle, vtMove * 0.5, GDB_ID.ROOT)
         End If
         ' il rettangolo è stato modifica almeno una volta
         m_dRettangleIsModified = True
      End If

      ' verifico che il il lato  maggiore rimanga semore il lato maggiore
      Dim bRettangleCorrect As Boolean = False
      If (MaxLen + dProjection - MinLen) > EPS_SMALL Then bRettangleCorrect = True

      Dim nWarn As Integer = 0
      ResetAllMachinings(nWarn)
      EnableReferenceRegion(False)
      ' se la modifica genera un errore allora ripristino la figura prima della defomrazione
      If Not EgtVerifyPart(EgtGetParent(m_nIdSelectedOutLoopRettangle), True) Or Not bRettangleCorrect Then
         EgtScale(m_nIdSelectedOutLoopRettangle, frFixed, 1 / dScalefactor, 1, 1, GDB_ID.ROOT)
         EgtScale(m_nIdSelectedFlatSurfRettangle, frFixed, 1 / dScalefactor, 1, 1, GDB_ID.ROOT)
         Dim ptCen As Point3d
         EgtCenterPoint(m_nIdSelectedTextRettangle, ptCen)
         EgtModifyText(m_nIdSelectedTextRettangle, m_sTextContent & LenToString(MaxLen, 2) & " x " & LenToString(MinLen, 2))
         EgtMove(m_nIdSelectedTextRettangle, -vtMove * 0.5, GDB_ID.ROOT)
         If m_nIdSelectedTextTopRettangle <> GDB_ID.NULL Then
            EgtMove(m_nIdSelectedTextTopRettangle, -vtMove * 0.5, GDB_ID.ROOT)
         End If
      End If

      EgtDraw()
      ' Aggiorno il punto precedente
      m_ptPrev = ptCurr
      ' Terminata esecuzione di drag
      m_bDragging = False
   End Sub

   ' funzione chiamata da OnMyMouseUp: gestisce il reset di tutti gli indici in uso 
   Public Sub ResetDragRettangleParam()
      ' deseleziono l'elemento corrente
      EgtDeselectObj(m_nIdSelectedSawRettangle)
      EgtDeselectObj(m_nIdSelectedCurvRettangle)
      EgtResetMark(m_nIdSelectedSawRettangle)
      If m_dRettangleIsModified Then
         ' elimino eventuali riferienti con la famiglia di appartenenza
         Dim nCurrPart As Integer = EgtGetParent(m_nIdSelectedRegionRettangle)
         EgtRemoveInfo(nCurrPart, INFO_REFGROUP)
         ' elimino il Layer che contiene l'informazione del numero di pezzi
         Dim nCounterLayer As Integer = EgtGetFirstNameInGroup(nCurrPart, INFO_COUNTERLY)
         EgtErase(nCounterLayer)
      End If
      m_dRettangleIsModified = False
      ' resetto gli indici
      m_nIdSelectedOutLoopRettangle = GDB_ID.NULL
      m_nIdSelectedRegionRettangle = GDB_ID.NULL
      m_nIdSelectedSawRettangle = GDB_ID.NULL
      m_nIdSelectedCurvRettangle = GDB_ID.NULL
      m_nIdSelectedTextRettangle = GDB_ID.NULL
      m_nIdSelectedTextTopRettangle = GDB_ID.NULL
      m_nIdSelectedFlatSurfRettangle = GDB_ID.NULL
      m_SideListRettangle = {GDB_ID.NULL, GDB_ID.NULL, GDB_ID.NULL, GDB_ID.NULL}
      m_sTextContent = String.Empty
      ' disattivo il drag
      m_bDrag = False
      ' aggiorno le lavorazioni
      Dim nWarn As Integer = 0
      ResetAllMachinings(nWarn)
      EgtDraw()
   End Sub

#End Region    ' Drag Rettangolo

   Private Sub OnMyMouseDownScene(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles m_CurrProjPage.OnMouseDownScene
      ' Verifico di essere il gestore attivo e non in modalità registrazione
      If Not m_bActive Or m_bRegister Then Return
      ' Si può selezionare solo con il tasto sinistro e se stato NULL
      If e.Button <> Windows.Forms.MouseButtons.Left Or
         Not m_CurrProjPage.CurrentProjectScene.IsStatusNull() Then
         Return
      End If

      ' se in modalità slezione tagli
      If m_bSelectCurv Then
         OnMyMouseDownSceneSelCurv(sender, e)
         Return
      End If

      m_bIsCounterLy = False
      ' se in modalità DragRettangle (deformazione delle componenti rettangolari)
      If m_bIsCheckedDragRettangle Then
         OnMyMouseDownSceneSelSideRettangle(sender, e)
         ' Dati per drag (abilito il Drag)
         m_locPrev = e.Location
         m_bDrag = EgtUnProjectPoint(e.Location, m_ptPrev) And m_nIdSelectedCurvRettangle <> GDB_ID.NULL
         m_bDragToStart = m_bDrag
         m_bVerify = m_bDrag AndAlso (Keyboard.Modifiers And ModifierKeys.Shift) > 0
         m_vtTotMove = Vector3d.NULL()
         Return
      End If

      ' Per default no drag
      m_bDrag = False
      ' Verifico se selezionato indicativo di pezzo
      EgtSetObjFilterForSelWin(True, True, True, True, True)
      Dim nSel As Integer
      EgtSelect(e.Location, Scene.DIM_SEL, Scene.DIM_SEL, nSel)
      Dim nId As Integer = EgtGetFirstObjInSelWin()
      While nId <> GDB_ID.NULL
         ' Recupero l'identificativo del pezzo cui appartiene
         Dim nPartId As Integer = EgtGetParent(EgtGetParent(nId))
         Dim sNamePartId As String = String.Empty
         EgtGetName(nPartId, sNamePartId)
         Dim bPartInTable As Boolean = (EgtGetParent(nPartId) = m_nRawId)
         If EgtIsPart(nPartId) Or bPartInTable Then
            Dim nStat As Integer = GDB_ST.ON_
            EgtGetStatus(nPartId, nStat)

            ' verifico se l'lemento selezionato è il numero di pezzi
            If EgtGetType(nId) = GDB_TY.EXT_TEXT Then
               Dim NameLayer As String = String.Empty
               EgtGetName(EgtGetParent(nId), NameLayer)
               If NameLayer = INFO_COUNTERLY Then
                  Dim sText As String = String.Empty
                  If EgtTextGetContent(nId, sText) Then
                     Dim sItems() As String = sText.Split("#")
                     If sItems.Count > 1 Then
                        m_nCurrNubrOfParts = 0
                        If StringToInt(sItems(1), m_nCurrNubrOfParts) Then
                           m_bIsCounterLy = True
                           m_nIdToSel = nPartId
                        End If
                     End If
                  End If
               End If
            End If

            If Not m_bIsCounterLy Then
               ' Se già selezionato o posizione oggetto incompatibile con flag posizione selezionati
               If nStat = GDB_ST.SEL Then
                  ' Memorizzo Id da deselezionare
                  m_nIdToDesel = nPartId
               Else
                  ' Memorizzo Id da selezionare
                  m_nIdToSel = nPartId
               End If
            End If

            ' Drag possibile
            m_bDrag = True
               Exit While
            Else
               ' verifico se l'elemento selezionato è il nome della tavola
               If EgtGetType(nId) = GDB_TY.EXT_TEXT AndAlso (sNamePartId = MAIN_TAB Or sNamePartId = SECOND_TAB) Then
               Dim nRes As Integer = m_MainWindow.m_CadCutPageUC.m_ProjectMgr.ChangeTable()
               ' se non è stata eseguita nessuna scelta esco dal ciclo 
               If nRes = 0 Then Exit While
               ' imposto l'altra tavola
               Dim sOtherTab = If(sNamePartId = MAIN_TAB, SECOND_TAB, MAIN_TAB)
               EgtChangeTable(sOtherTab, True)
               m_CurrProjPage.AdjustAdditionalTable(True)
               EgtSetMachineLook(MCH_LOOK.TAB)
               ' aggiorno le lavorazioni
               UpdateAllMachiningsToolpaths()
               ' aggiorno i movimenti pezzi tra disposizioni
               For nI As Integer = 1 To EgtGetPhaseCount()
                  EgtSetCurrPhase(nI)
                  Dim nDispId As Integer = EgtGetPhaseDisposition(nI)
                  SpecialApplyDisposition(nDispId, True)
               Next
               ' aggiorno posizionamento ventose per lavorazioni da sotto
               UpdateVacuumsForDrip()
               ' dichiaro tutto aggiornato
               m_CurrProjPage.SetOrderMachiningFlag()
               EgtSetCurrPhase(1)
               ' aggiorno posizione pezzi in parcheggio
               Dim nPPId As Integer = EgtGetFirstPart()
               While nPPId <> GDB_ID.NULL
                  PackPartInStore(nPPId)
                  nPPId = EgtGetNextPart(nPPId)
               End While
               ' se prevista rotazione
               If GetPrivateProfileInt(S_TABLE, K_CHANGETABWD, 0, m_MainWindow.GetMachIniFile()) = 2 Then
                  ' Dati tavola
                  Dim b3Tab As New BBox3d
                  EgtGetTableArea(1, b3Tab)
                  ' Box lastra
                  Dim b3OrigRaw As New BBox3d
                  GetRawBox(b3OrigRaw)
                  ' Allargo i limiti della tavola in tutte le fasi
                  Const TAB_OFFS As Double = 3000
                  For nI As Integer = 1 To EgtGetPhaseCount()
                     EgtSetCurrPhase(nI)
                     EgtSetTableAreaOffset(TAB_OFFS, TAB_OFFS, TAB_OFFS, TAB_OFFS)
                  Next
                  EgtSetCurrPhase(1)
                  ' Rotazione
                  Dim dAngRot As Double = If(sOtherTab = SECOND_TAB, 180, -180)
                  If Not RotateAllRawParts(dAngRot, False) Then
                     m_CurrProjPage.SetWarningMessage(EgtMsg(90339))        'Rotazione impossibile
                  End If
                  ' Traslazione per riportare la lastra nella stessa posizione rispetto all'angolo BL che diveta TR e viceversa
                  Dim b3Raw As New BBox3d
                  GetRawBox(b3Raw)
                  Dim vtMove As Vector3d
                  If sOtherTab = SECOND_TAB Then
                     Dim vtDiffIni As Vector3d = b3OrigRaw.Min() - b3Tab.Min()
                     Dim vtDiffFin As Vector3d = b3Tab.Max() - b3Raw.Max()
                     vtMove = vtDiffFin - vtDiffIni
                  Else
                     Dim vtDiffIni As Vector3d = b3Tab.Max() - b3OrigRaw.Max()
                     Dim vtDiffFin As Vector3d = b3Raw.Min() - b3Tab.Min()
                     vtMove = -(vtDiffFin - vtDiffIni)
                  End If
                  vtMove.z = 0
                  MoveAllRawParts(vtMove)
                  ' Ripristino i limiti della tavola in tutte le fasi
                  For nI As Integer = 1 To EgtGetPhaseCount()
                     EgtSetCurrPhase(nI)
                     EgtSetTableAreaOffset(0, 0, 0, 0)
                  Next
                  EgtSetCurrPhase(1)
                  HideAllMachinings()
               End If
               ' visualizzazione
               EgtZoom(ZM.ALL)
               Exit While
            End If
         End If
         nId = EgtGetNextObjInSelWin()
      End While
      ' Dati per drag
      m_locPrev = e.Location
      m_bDrag = m_bDrag AndAlso EgtUnProjectPoint(e.Location, m_ptPrev)
      m_bDragToStart = m_bDrag
      m_bVerify = m_bDrag AndAlso (Keyboard.Modifiers And ModifierKeys.Shift) > 0
      m_vtTotMove = Vector3d.NULL()
   End Sub

   Private Sub OnMyMouseMoveScene(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles m_CurrProjPage.OnMouseMoveScene
      ' Verifico di essere il gestore attivo
      If Not m_bActive Then Return
      ' Se drag non abilitato o già in esecuzione, esco
      If Not m_bDrag Or m_bDragging Then Return
      ' Se primo movimento di drag, verifico di aver superato la soglia di movimento in pixel
      If m_bDragToStart Then
         If Math.Abs(e.Location.X - m_locPrev.X) < m_nRestRadius And
            Math.Abs(e.Location.Y - m_locPrev.Y) < m_nRestRadius Then
            Return
         End If
         m_bDragToStart = False
      End If

      ' se devo muovere un lato del rettangolo
      If m_bIsCheckedDragRettangle Then
         OnMyMouseMoveSawRettangle(sender, e)
         Return
      End If

      ' Determino cosa muovere
      Dim nMoveId = If(m_nIdToSel <> GDB_ID.NULL, m_nIdToSel, GDB_ID.SEL)
      ' Verifico se in tavola o in parcheggio
      Dim nTestId = If(nMoveId <> GDB_ID.SEL, nMoveId, EgtGetFirstSelectedObj())
      If EgtGetParent(nTestId) <> m_nRawId Then
         ' Dal parcheggio ammesso drag di un singolo pezzo
         If nMoveId = GDB_ID.SEL Then Return
         ' Se pezzo Trf, verifico compatibilità
         If Not VerifyTrfData(nTestId) Then
            ' Pezzi con spessore, materiale o finitura non compatibili
            m_CurrProjPage.SetWarningMessage(EgtMsg(MSG_NESTPAGEUC + 7))
         End If
         ' Applico le lavorazioni al pezzo
         Dim b3Curr As New BBox3d
         EgtGetBBoxGlob(EgtGetFirstNameInGroup(nTestId, NAME_REGION), BBFLAG, b3Curr)
         If Not PreInsertOnePart(nTestId) Then Return
         Dim b3Ins As New BBox3d
         EgtGetBBoxGlob(EgtGetFirstNameInGroup(nTestId, NAME_REGION), BBFLAG, b3Ins)
         Dim vtDiff As New Vector3d(b3Curr.Min().x - b3Ins.Min().x, b3Curr.Min().y - b3Ins.Min().y, 0)
         EgtMove(nMoveId, vtDiff)

         '---------------------- COUNTER PART ----------------------
         ' aggiorno contatore pezzi
         Dim sRefGroup As String = String.Empty
         If EgtGetInfo(nMoveId, INFO_REFGROUP, sRefGroup) Then
            ' spengo il layer che contiene il contatore
            Dim nCounterLayer As Integer = EgtGetFirstNameInGroup(nMoveId, INFO_COUNTERLY)
            EgtSetStatus(nCounterLayer, GDB_ST.OFF)
            ' aggiorno il layer che indica il numero di pezzi in parcheggio
            CountPartInFamily(sRefGroup)
         End If
         '---------------------- COUNTER PART ----------------------

         ' Gestione VeinMatching
         VeinMatching.OnInsertPartInRaw(nMoveId)
         ' Imposto stato
         m_bVerify = True
         m_bFromParking = True
      End If
      ' Inizio esecuzione di drag
      m_bDragging = True
      ' Ricavo il punto corrente in coordinate mondo
      Dim ptCurr As Point3d
      EgtUnProjectPoint(e.Location, ptCurr)
      ' Ricavo il vettore di movimento
      Dim vtMove As Vector3d = ptCurr - m_ptPrev
      ' Muovo i pezzi selezionati di quanto possibile
      If vtMove.SqLen() > EPS_SMALL * EPS_SMALL Then
         ' Se movimento con sola verifica finale
         If m_bVerify Then
            EgtMove(nMoveId, vtMove)
            m_vtTotMove += vtMove
            ' altrimenti caso con verifica durante il movimento
         Else
            ' Aggiorno regioni per nesting
            UpdateNestRegions()
            EnableReferenceRegion(False)
            ' muovo il pezzo
            EgtMovePart(nMoveId, m_bReducedCut, vtMove)
            EgtSaveCollInfo()
            ' se movimento risultante nullo, provo con movimento tangente
            Dim bTgMoved As Boolean = False
            If vtMove.IsSmall() Then
               ' riprovo con movimento tangente
               Dim vtTgMove As Vector3d = ptCurr - m_ptPrev
               EgtTgMovePartOnCollision(nMoveId, m_bReducedCut, vtTgMove)
               bTgMoved = (Not vtTgMove.IsSmall())
            End If
            ' se abilitato magnetico (allineamento + snap), lo provo
            Dim bAlignMoved As Boolean = False
            Dim bSnapMoved As Boolean = False
            If m_bMagnetic Then
               If Not GetLockOnRotation(nMoveId) Then
                  EgtAlignPartOnCollision(nMoveId, m_bReducedCut, bAlignMoved)
               End If
               If m_dSnapDist > EPS_SMALL Then
                  EgtRestoreCollInfo()
                  EgtMovePartToSnapPointOnCollision(nMoveId, m_bReducedCut, m_dSnapDist, bSnapMoved)
               End If
            End If
            m_CurrProjPage.ResetOrderMachiningFlag()
         End If
         EgtDraw()
         ' Eventuale notifica al VeinMatching
         If nMoveId = GDB_ID.SEL Then
            Dim nId As Integer = EgtGetFirstSelectedObj()
            While nId <> GDB_ID.NULL
               VeinMatching.OnMovePartInRaw(nId)
               nId = EgtGetNextSelectedObj()
            End While
         Else
            VeinMatching.OnMovePartInRaw(nMoveId)
         End If
      End If
      ' Aggiorno il punto precedente
      m_ptPrev = ptCurr
      ' Terminata esecuzione di drag
      m_bDragging = False
   End Sub

   Private Sub OnMyMouseUpScene(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles m_CurrProjPage.OnMouseUpScene
      ' Verifico di essere il gestore attivo
      If Not m_bActive Then Return
      ' verifico che non sia attivo il comando Test
      If m_bSelectCurv Then Return
      ' verifico che non sia attivo il comando di Drag dei rettangoli
      If m_bIsCheckedDragRettangle Then
         ' deseleziono gli oggeti, resetto le variabili usate, aggiorno il disegno
         ResetDragRettangleParam()
         Return
      End If

      ' Se eseguito drag
      If Not m_bDragToStart Then
         ' Se movimento con sola verifica finale
         If m_bVerify Then
            ' Determino cosa verificare
            Dim nMoveId = If(m_nIdToSel <> GDB_ID.NULL, m_nIdToSel, GDB_ID.SEL)
            ' Aggiorno regioni per nesting
            UpdateNestRegions()
            EnableReferenceRegion(False)
            ' Eseguo verifica
            If VerifyTrfData(nMoveId) And EgtVerifyPart(nMoveId, m_bReducedCut) Then
               m_CurrProjPage.ResetOrderMachiningFlag()
               ' Non superata riporto alla posizione iniziale
            Else
               If m_bFromParking Then
                  m_CurrProjPage.ClearMessage()
                  PreRemoveOnePart(nMoveId)
                  VeinMatching.OnRemovePartFromRaw(nMoveId)
               Else
                  EgtMove(nMoveId, -m_vtTotMove)
                  ' Eventuale notifica al VeinMatching
                  If nMoveId = GDB_ID.SEL Then
                     Dim nId As Integer = EgtGetFirstSelectedObj()
                     While nId <> GDB_ID.NULL
                        VeinMatching.OnMovePartInRaw(nId)
                        nId = EgtGetNextSelectedObj()
                     End While
                  Else
                     VeinMatching.OnMovePartInRaw(nMoveId)
                  End If
               End If
               '---------------------- COUNTER PART ----------------------
               Dim sRefGroup As String = String.Empty
               If EgtGetInfo(nMoveId, INFO_REFGROUP, sRefGroup) Then
                  ' accendo il layer che contiene il contatore (spento in fase di Drag)
                  Dim nCounterLayer As Integer = EgtGetFirstNameInGroup(nMoveId, INFO_COUNTERLY)
                  EgtSetStatus(nCounterLayer, GDB_ST.ON_)
                  ' aggiorno il layer che indica il numero di pezzi in parcheggio
                  CountPartInFamily(sRefGroup)
               End If
               '---------------------- COUNTER PART ----------------------
            End If
            m_bFromParking = False
            ' altrimenti caso con verifica durante il movimento
         Else
            ' Basta reset alla fine
         End If

         ' se è stato selezionato il layer del contatore mostro la finsetra
      ElseIf m_bIsCounterLy Then
         '---------------------- COUNTER PART ----------------------
         ' apro una finestra di dialogo per chiedere se inserire tutti i pezzi della famiglia
         Dim SelectWD As New SelectPartFromFamilyWD(m_MainWindow, m_nCurrNubrOfParts)
         Dim sRefGroup As String = String.Empty
         EgtGetInfo(m_nIdToSel, INFO_REFGROUP, sRefGroup)
         ' deseleziono il pezzo corrente
         DeselectPartInFaimily(sRefGroup)
         Select Case SelectWD.NumberOfSelection
            Case 0    ' Annulla
               ' continuo senza eseguire niente
            Case Else
               ' seleziono il numero di pezzi indicati
               SelectPartInFaimily(sRefGroup, SelectWD.NumberOfSelection)
         End Select
         m_bIsCounterLy = False
         ' disassegno il riferimento all'oggetto appena creato
         SelectWD = Nothing

         '---------------------- COUNTER PART ----------------------

         ' Se selezione da eseguire
      ElseIf m_nIdToSel <> GDB_ID.NULL Then
         ' Determino se pezzo in tavola o in parcheggio
         Dim bPartInTable As Boolean = (EgtGetParent(m_nIdToSel) = m_nRawId)
         ' Se ci sono pezzi già selezionati nella posizione opposta, li deseleziono
         If (bPartInTable And m_nPartPos = PART_POS.OUT_TABLE) Or
            (Not bPartInTable And m_nPartPos = PART_POS.IN_TABLE) Then
            EgtDeselectAll()
            ' Aggiornamento eventuale VeinMatching
            VeinMatching.OnDeselectAll()
         End If
         ' Eseguo la selezione
         EgtSelectObj(m_nIdToSel)
         ' Eventuale aggiornamento VeinMatching
         VeinMatching.OnSelectPart(m_nIdToSel)
         ' Set flag posizione selezionati
         m_nPartPos = If(bPartInTable, PART_POS.IN_TABLE, PART_POS.OUT_TABLE)
         ' Se deselezione da eseguire
      ElseIf m_nIdToDesel <> GDB_ID.NULL Then
         EgtDeselectObj(m_nIdToDesel)
         ' Eventuale aggiornamento VeinMatching
         VeinMatching.OnDeselectPart(m_nIdToDesel)
      End If


      ' Reset
      m_bDrag = False
      m_nIdToSel = GDB_ID.NULL
      m_nIdToDesel = GDB_ID.NULL
      ' Se nessun pezzo selezionato, reset flag posizione selezionati
      If EgtGetSelectedObjCount() = 0 Then
         m_nPartPos = PART_POS.NONE_TABLE
      End If
      EgtDraw()
   End Sub

   Friend Function SelectPart(nPartId As Integer, bNotifyVM As Boolean) As Boolean
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      ' Determino se pezzo in tavola o in parcheggio
      Dim bPartInTable As Boolean = (EgtGetParent(nPartId) = m_nRawId)
      ' Se ci sono pezzi già selezionati nella posizione opposta, restituisco selezione non possibile
      If (bPartInTable And m_nPartPos = PART_POS.OUT_TABLE) Or
         (Not bPartInTable And m_nPartPos = PART_POS.IN_TABLE) Then
         Return False
      End If
      ' Eseguo la selezione
      EgtSelectObj(nPartId)
      ' Eventuale aggiornamento VeinMatching
      If bNotifyVM Then VeinMatching.OnSelectPart(nPartId)
      ' Set flag posizione selezionati
      m_nPartPos = If(bPartInTable, PART_POS.IN_TABLE, PART_POS.OUT_TABLE)
      ' Selezione effettuata
      EgtDraw()
      Return True
   End Function

   Friend Function DeselectPart(nPartId As Integer, bNotifyVM As Boolean) As Boolean
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      ' Eseguo la deselezione
      EgtDeselectObj(nPartId)
      ' Eventuale aggiornamento VeinMatching
      If bNotifyVM Then VeinMatching.OnDeselectPart(nPartId)
      ' Se nessun pezzo selezionato, reset flag posizione selezionati
      If EgtGetSelectedObjCount() = 0 Then m_nPartPos = PART_POS.NONE_TABLE
      ' Deselezione effettuata
      EgtDraw()
      Return True
   End Function

   Private Sub MaximizeMoveBtn_Click(sender As Object, e As RoutedEventArgs) Handles MaximizeMoveBtn.Click
      If MaximizeMoveBtn.IsChecked() Then
         m_bMaximizeMove = True
      Else
         m_bMaximizeMove = False
      End If
   End Sub

   Private Sub UpBtn_Click(sender As Object, e As RoutedEventArgs) Handles UpBtn.Click
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      If Not m_bRegister Then
         ' Aggiorno regioni per nesting
         UpdateNestRegions()
         EnableReferenceRegion(False)
         ' eseguo movimento
         Dim dStep As Double = If(m_bMaximizeMove, m_dMaxStep, m_dStep)
         EgtMovePart(GDB_ID.SEL, m_bReducedCut, New Vector3d(0, dStep, 0))
         EgtSaveCollInfo()
         ' se abilitato magnetico (allineamento + snap), lo provo
         Dim bAlignMoved As Boolean = False
         Dim bSnapMoved As Boolean = False
         If m_bMagnetic Then
            If Not GetLockOnRotation(GDB_ID.SEL) Then
               EgtAlignPartOnCollision(GDB_ID.SEL, m_bReducedCut, bAlignMoved)
            End If
            If m_dSnapDist > EPS_SMALL Then
               EgtRestoreCollInfo()
               EgtMovePartToSnapPointOnCollision(GDB_ID.SEL, m_bReducedCut, m_dSnapDist, bSnapMoved)
            End If
         End If
         m_CurrProjPage.ResetOrderMachiningFlag()
         EgtDraw()
         ' Eventuale notifica al VeinMatching
         Dim nId As Integer = EgtGetFirstSelectedObj()
         While nId <> GDB_ID.NULL
            VeinMatching.OnMovePartInRaw(nId)
            nId = EgtGetNextSelectedObj()
         End While
      Else
         MoveAllRawParts(New Vector3d(0, m_dRegStep, 0))
         EgtDraw()
      End If
   End Sub

   Private Sub DownBtn_Click(sender As Object, e As RoutedEventArgs) Handles DownBtn.Click
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      If Not m_bRegister Then
         ' Aggiorno regioni per nesting
         UpdateNestRegions()
         EnableReferenceRegion(False)
         ' eseguo movimento
         Dim dStep As Double = If(m_bMaximizeMove, m_dMaxStep, m_dStep)
         EgtMovePart(GDB_ID.SEL, m_bReducedCut, New Vector3d(0, -dStep, 0))
         EgtSaveCollInfo()
         ' se abilitato magnetico (allineamento + snap), lo provo
         Dim bAlignMoved As Boolean = False
         Dim bSnapMoved As Boolean = False
         If m_bMagnetic Then
            If Not GetLockOnRotation(GDB_ID.SEL) Then
               EgtAlignPartOnCollision(GDB_ID.SEL, m_bReducedCut, bAlignMoved)
            End If
            If m_dSnapDist > EPS_SMALL Then
               EgtRestoreCollInfo()
               EgtMovePartToSnapPointOnCollision(GDB_ID.SEL, m_bReducedCut, m_dSnapDist, bSnapMoved)
            End If
         End If
         m_CurrProjPage.ResetOrderMachiningFlag()
         EgtDraw()
         ' Eventuale notifica al VeinMatching
         Dim nId As Integer = EgtGetFirstSelectedObj()
         While nId <> GDB_ID.NULL
            VeinMatching.OnMovePartInRaw(nId)
            nId = EgtGetNextSelectedObj()
         End While
      Else
         MoveAllRawParts(New Vector3d(0, -m_dRegStep, 0))
         EgtDraw()
      End If
   End Sub

   Private Sub RightBtn_Click(sender As Object, e As RoutedEventArgs) Handles RightBtn.Click
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      If Not m_bRegister Then
         ' Aggiorno regioni per nesting
         UpdateNestRegions()
         EnableReferenceRegion(False)
         ' eseguo movimento
         Dim dStep As Double = If(m_bMaximizeMove, m_dMaxStep, m_dStep)
         EgtMovePart(GDB_ID.SEL, m_bReducedCut, New Vector3d(dStep, 0, 0))
         EgtSaveCollInfo()
         ' se abilitato magnetico (allineamento + snap), lo provo
         Dim bAlignMoved As Boolean = False
         Dim bSnapMoved As Boolean = False
         If m_bMagnetic Then
            If Not GetLockOnRotation(GDB_ID.SEL) Then
               EgtAlignPartOnCollision(GDB_ID.SEL, m_bReducedCut, bAlignMoved)
            End If
            If m_dSnapDist > EPS_SMALL Then
               EgtRestoreCollInfo()
               EgtMovePartToSnapPointOnCollision(GDB_ID.SEL, m_bReducedCut, m_dSnapDist, bSnapMoved)
            End If
         End If
         m_CurrProjPage.ResetOrderMachiningFlag()
         EgtDraw()
         ' Eventuale notifica al VeinMatching
         Dim nId As Integer = EgtGetFirstSelectedObj()
         While nId <> GDB_ID.NULL
            VeinMatching.OnMovePartInRaw(nId)
            nId = EgtGetNextSelectedObj()
         End While
      Else
         MoveAllRawParts(New Vector3d(m_dRegStep, 0, 0))
         EgtDraw()
      End If
   End Sub

   Private Sub LeftBtn_Click(sender As Object, e As RoutedEventArgs) Handles LeftBtn.Click
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      If Not m_bRegister Then
         ' Aggiorno regioni per nesting
         UpdateNestRegions()
         EnableReferenceRegion(False)
         ' eseguo movimento
         Dim dStep As Double = If(m_bMaximizeMove, m_dMaxStep, m_dStep)
         EgtMovePart(GDB_ID.SEL, m_bReducedCut, New Vector3d(-dStep, 0, 0))
         EgtSaveCollInfo()
         ' se abilitato magnetico (allineamento + snap), lo provo
         Dim bAlignMoved As Boolean = False
         Dim bSnapMoved As Boolean = False
         If m_bMagnetic Then
            If Not GetLockOnRotation(GDB_ID.SEL) Then
               EgtAlignPartOnCollision(GDB_ID.SEL, m_bReducedCut, bAlignMoved)
            End If
            If m_dSnapDist > EPS_SMALL Then
               EgtRestoreCollInfo()
               EgtMovePartToSnapPointOnCollision(GDB_ID.SEL, m_bReducedCut, m_dSnapDist, bSnapMoved)
            End If
         End If
         m_CurrProjPage.ResetOrderMachiningFlag()
         EgtDraw()
         ' Eventuale notifica al VeinMatching
         Dim nId As Integer = EgtGetFirstSelectedObj()
         While nId <> GDB_ID.NULL
            VeinMatching.OnMovePartInRaw(nId)
            nId = EgtGetNextSelectedObj()
         End While
      Else
         MoveAllRawParts(New Vector3d(-m_dRegStep, 0, 0))
         EgtDraw()
      End If
   End Sub

   Private Sub MoveAllRawParts(vtMove As Vector3d)
      ' Provo il movimento
      Dim bMoveOk As Boolean = True
      Dim nPhase As Integer = 1
      Dim nRawId As Integer = GDB_ID.NULL
      While nPhase <= EgtGetPhaseCount()
         EgtSetCurrPhase(nPhase)
         nRawId = EgtGetFirstRawPart()
         While nRawId <> GDB_ID.NULL
            If EgtVerifyRawPartPhase(nRawId, nPhase) Then
               If Not EgtMoveRawPart(nRawId, vtMove) Then
                  bMoveOk = False
                  Exit While
               End If
            End If
            nRawId = EgtGetNextRawPart(nRawId)
         End While
         If Not bMoveOk Then Exit While
         nPhase += 1
      End While
      ' Se movimento impossibile, ripristino posizione dei grezzi già spostati
      If Not bMoveOk Then
         Dim nRevPhase As Integer = 1
         Dim nRevRawId As Integer = GDB_ID.NULL
         While nRevPhase <= nPhase
            EgtSetCurrPhase(nRevPhase)
            nRevRawId = EgtGetFirstRawPart()
            While nRevRawId <> GDB_ID.NULL And (nRevPhase < nPhase Or nRevRawId <> nRawId)
               If EgtVerifyRawPartPhase(nRevRawId, nRevPhase) Then
                  EgtMoveRawPart(nRevRawId, -vtMove)
               End If
               nRevRawId = EgtGetNextRawPart(nRevRawId)
            End While
            nRevPhase += 1
         End While
      End If
      ' Ripristino lo stato iniziale
      EgtSetCurrPhase(1)
      HideAllMachinings()
   End Sub

   Private Sub RotateCounterClockwiseBtn_Click(sender As Object, e As RoutedEventArgs) Handles RotateCounterClockwiseBtn.Click
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      If Not m_bRegister Then
         RotateCluster(m_dAngStep)
         m_CurrProjPage.ResetOrderMachiningFlag()
         EgtDraw()
         ' Eventuale notifica al VeinMatching
         Dim nId As Integer = EgtGetFirstSelectedObj()
         While nId <> GDB_ID.NULL
            VeinMatching.OnMovePartInRaw(nId)
            nId = EgtGetNextSelectedObj()
         End While
      Else
         UserRotateAllRawParts(m_dRegAngStep)
         EgtDraw()
      End If
   End Sub

   Private Sub RotateClockwiseBtn_Click(sender As Object, e As RoutedEventArgs) Handles RotateClockwiseBtn.Click
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      If Not m_bRegister Then
         RotateCluster(-m_dAngStep)
         m_CurrProjPage.ResetOrderMachiningFlag()
         EgtDraw()
         ' Eventuale notifica al VeinMatching
         Dim nId As Integer = EgtGetFirstSelectedObj()
         While nId <> GDB_ID.NULL
            VeinMatching.OnMovePartInRaw(nId)
            nId = EgtGetNextSelectedObj()
         End While
      Else
         UserRotateAllRawParts(-m_dRegAngStep)
         EgtDraw()
      End If
   End Sub

   Private Function RotateCluster(ByVal dAngRotDeg As Double) As Boolean
      ' Se non ci sono pezzi selezionati, esco
      If EgtGetSelectedObjCount() = 0 Then
         Return True
      End If
      ' Se pezzi in parcheggio, li ruoto liberamente e li risistemo (sempre possibile) 
      If m_nPartPos <> PART_POS.IN_TABLE Then
         Dim nId As Integer = EgtGetFirstSelectedObj()
         While nId <> GDB_ID.NULL
            ' Se pezzo con rotazione libera
            If Not EgtExistsInfo(nId, INFO_PARTROT) Then
               ' Calcolo il centro di rotazione come centro del pezzo
               Dim ptCen As Point3d
               If Not EgtGetPartPartClusterCenterGlob(nId, ptCen) Then Return False
               ' Rotazione del pezzo attorno al suo centro
               EgtRotate(nId, ptCen, Vector3d.Z_AX(), dAngRotDeg, GDB_RT.GLOB)

               '---------------------- COUNTER PART ----------------------
               ' Aggiorno il Guid assegnando la nuova rotazione
               Dim sRefGroup As String = String.Empty
               If EgtGetInfo(nId, INFO_REFGROUP, sRefGroup) Then
                  Dim sCurrRefGroup As String = sRefGroup
                  If UppDateGuidCode(sRefGroup, CreatePreGuidCode(nId)) Then
                     EgtSetInfo(nId, INFO_REFGROUP, sRefGroup)
                  End If
                  ' aggiorno il layer che indica il numero di pezzi in parcheggio
                  CountPartInFamily(sCurrRefGroup)
               End If
               ' se esiste almento un pezzo del gruppo in parcheggio esco
               If Not ParkInGroupFamily(nId) Then
                  PackPartInStore(nId)
               End If
               ' ricarico l'lelenco dei pezzi in parcheggio
               GetFamilyGroupInPark()
               '---------------------- COUNTER PART ----------------------

               ' Sistemazione nel parcheggio
               'PackPartInStore(nId)
            End If
            ' Passo al successivo selezionato
            nId = EgtGetNextSelectedObj()
         End While
         Return True
         ' Altrimenti li ruoto tenendo conto delle collisioni
      Else
         ' Verifico se ci sono dei pezzi bloccati in rotazione
         Dim bLockedRot As Boolean = GetLockOnRotation(GDB_ID.SEL)
         ' Se tutti i pezzi possono ruotare
         If Not bLockedRot Then
            ' Calcolo il centro di rotazione come centro del cluster
            Dim ptCen As Point3d
            If Not EgtGetPartPartClusterCenterGlob(GDB_ID.SEL, ptCen) Then Return False
            ' Aggiorno regioni per nesting
            UpdateNestRegions()
            EnableReferenceRegion(False)
            ' Verifico rotazione complessiva
            EgtRotate(GDB_ID.SEL, ptCen, Vector3d.Z_AX(), dAngRotDeg, GDB_RT.GLOB)
            If EgtVerifyPart(GDB_ID.SEL, m_bReducedCut) Then Return True
            ' Provo rotazione parziale (dopo aver annullato la complessiva)
            EgtRotate(GDB_ID.SEL, ptCen, Vector3d.Z_AX(), -dAngRotDeg, GDB_RT.GLOB)
            Return EgtRotatePart(GDB_ID.SEL, m_bReducedCut, ptCen, dAngRotDeg)
         Else
            Return False
         End If
      End If
   End Function

   Private Sub UserRotateAllRawParts(dAngDeg As Double)
      ' Recupero rotazione totale
      Dim dRegRot As Double = GetRegistrationRotation()
      ' Verifico se nuova rotazione totale ammissibile
      m_CurrProjPage.ClearMessage()
      If Math.Abs(dRegRot + dAngDeg) > m_dMaxRegAng Then
         m_CurrProjPage.SetWarningMessage(EgtMsg(90338))        'Angolo di registrazione oltre i limiti
         Return
      End If
      Dim bMoveOk As Boolean = RotateAllRawParts(dAngDeg)
      If Not bMoveOk Then m_CurrProjPage.SetWarningMessage(EgtMsg(90339))        'Rotazione impossibile
   End Sub

   Friend Function RotateAllRawParts(dAngDeg As Double, Optional bIsReg As Boolean = True) As Boolean
      ' Recupero rotazione totale
      Dim dRegRot As Double = GetRegistrationRotation()
      ' Recupero il centro del grezzo iniziale per usarlo come centro della rotazione
      Dim ptCen As New Point3d
      GetRawCenter(ptCen)
      ' Provo la rotazione
      Dim bMoveOk As Boolean = True
      Dim nPhase As Integer = 1
      Dim nRawId As Integer = GDB_ID.NULL
      While nPhase <= EgtGetPhaseCount()
         EgtSetCurrPhase(nPhase)
         nRawId = EgtGetFirstRawPart()
         While nRawId <> GDB_ID.NULL
            If EgtVerifyRawPartPhase(nRawId, nPhase) Then
               ' Rotazione del centro come spostamento grezzo più rotazione grezzo attorno al suo centro
               Dim ptRawCen As New Point3d
               EgtGetRawPartCenter(nRawId, ptRawCen)
               Dim ptMovCen As New Point3d(ptRawCen)
               ptMovCen.Rotate(ptCen, Vector3d.Z_AX(), dAngDeg)
               If Not EgtMoveRawPart(nRawId, ptMovCen - ptRawCen) Then
                  bMoveOk = False
                  Exit While
               End If
               If Not EgtRotateRawPart(nRawId, Vector3d.Z_AX(), dAngDeg) Then
                  EgtMoveRawPart(nRawId, -(ptMovCen - ptRawCen))
                  bMoveOk = False
                  Exit While
               End If
            End If
            nRawId = EgtGetNextRawPart(nRawId)
         End While
         If Not bMoveOk Then Exit While
         nPhase += 1
      End While
      ' Se rotazione impossibile, ripristino posizione dei grezzi già spostati
      If Not bMoveOk Then
         Dim nRevPhase As Integer = 1
         Dim nRevRawId As Integer = GDB_ID.NULL
         While nRevPhase <= nPhase
            EgtSetCurrPhase(nRevPhase)
            nRevRawId = EgtGetFirstRawPart()
            While nRevRawId <> GDB_ID.NULL And (nRevPhase < nPhase Or nRevRawId <> nRawId)
               If EgtVerifyRawPartPhase(nRevRawId, nRevPhase) Then
                  ' Eseguo al contrario
                  EgtRotateRawPart(nRevRawId, Vector3d.Z_AX(), -dAngDeg)
                  Dim ptRawCen As New Point3d
                  EgtGetRawPartCenter(nRevRawId, ptRawCen)
                  Dim ptMovCen As New Point3d(ptRawCen)
                  ptMovCen.Rotate(ptCen, Vector3d.Z_AX(), -dAngDeg)
                  EgtMoveRawPart(nRevRawId, (ptMovCen - ptRawCen))
               End If
               nRevRawId = EgtGetNextRawPart(nRevRawId)
            End While
            nRevPhase += 1
         End While
         ' Altrimenti eseguo sistemazioni
      Else
         ' Origine della tavola
         Dim ptOri As Point3d
         EgtGetTableRef(1, ptOri)
         ' Annullo rotazioni dei grezzi e le rifaccio sulle parti componenti
         Dim nRevPhase As Integer = 1
         Dim nRevRawId As Integer = GDB_ID.NULL
         While nRevPhase <= EgtGetPhaseCount()
            EgtSetCurrPhase(nRevPhase)
            nRevRawId = EgtGetFirstRawPart()
            While nRevRawId <> GDB_ID.NULL
               If EgtVerifyRawPartPhase(nRevRawId, nRevPhase) Then
                  ' Eseguo al contrario
                  EgtRotateRawPart(nRevRawId, Vector3d.Z_AX(), -dAngDeg)
                  Dim ptRawCen As New Point3d
                  EgtGetRawPartCenter(nRevRawId, ptRawCen)
                  Dim ptMovCen As New Point3d(ptRawCen)
                  ptMovCen.Rotate(ptCen, Vector3d.Z_AX(), -dAngDeg)
                  EgtMoveRawPart(nRevRawId, (ptMovCen - ptRawCen))
                  ' Rifaccio sugli oggetti contenuti nel grezzo
                  Dim nId As Integer = EgtGetFirstInGroup(nRevRawId)
                  While nId <> GDB_ID.NULL
                     EgtRotate(nId, ptCen, Vector3d.Z_AX(), dAngDeg, GDB_RT.GLOB)
                     nId = EgtGetNext(nId)
                  End While
                  ' Imposto posizione esatta del grezzo dopo rotazione
                  Dim b3Raw As New BBox3d
                  EgtGetRawPartBBox(nRevRawId, b3Raw)
                  EgtMoveToCornerRawPart(nRevRawId, (b3Raw.Min() - ptOri) + Point3d.ORIG(), MCH_CR.BL)
               End If
               nRevRawId = EgtGetNextRawPart(nRevRawId)
            End While
            nRevPhase += 1
         End While
         ' Ruoto opportunamente anche i dati di movimento
         Dim nOpeId As Integer = EgtGetFirstActiveOperation()
         While nOpeId <> GDB_ID.NULL
            If EgtGetOperationType(nOpeId) = MCH_OY.DISP Then
               ' Recupero i gruppi con i dati
               Dim nRpmId As Integer = EgtGetFirstNameInGroup(nOpeId, "Rpm*")
               While nRpmId <> GDB_ID.NULL
                  ' Recupero le informazioni
                  Dim vtRawMove As New Vector3d
                  If EgtGetInfo(nRpmId, "Mv", vtRawMove) Then
                     vtRawMove.Rotate(Vector3d.Z_AX(), dAngDeg)
                     EgtSetInfo(nRpmId, "Mv", vtRawMove)
                  End If
                  Dim vtDelta As New Vector3d
                  If EgtGetInfo(nRpmId, "Dt", vtDelta) Then
                     vtDelta.Rotate(Vector3d.Z_AX(), dAngDeg)
                     EgtSetInfo(nRpmId, "Dt", vtDelta)
                  End If
                  Dim dAngRotDeg As Double = 0
                  If EgtGetInfo(nRpmId, "Ad", dAngRotDeg) Then
                     EgtSetInfo(nRpmId, "Ad", dAngRotDeg + dAngDeg)
                  End If
                  ' Cerco un altro gruppo
                  nRpmId = EgtGetNextName(nRpmId, "Rpm*")
               End While
            End If
            nOpeId = EgtGetNextActiveOperation(nOpeId)
         End While
         ' Se non è registrazione, ruoto anche l'eventuale foto della lastra
         'If Not bIsReg Then
         '   Dim nPhotoId = m_CurrProjPage.GetPhoto()
         '   If nPhotoId <> GDB_ID.NULL then EgtRotatePhoto(nPhotoId, ptCen, Vector3d.Z_AX(), dAngDeg)
         'End If
         ' Aggiorno rotazione totale
         If bIsReg Then
            SetRegistrationRotation(dRegRot + dAngDeg)
         Else
            SetRegistrationRotation(0)
         End If
         ' Dichiaro grezzo per punti
         EgtSetInfo(GetCurrentRaw(), KEY_RAWBYPOINTS, 1)
      End If
      ' Ripristino lo stato iniziale
      EgtSetCurrPhase(1)
      HideAllMachinings()
      Return bMoveOk
   End Function

   Private Sub InsertPartBtn_Click(sender As Object, e As RoutedEventArgs) Handles InsertPartBtn.Click
      ' Se inserimento automatico
      If GetPrivateProfileInt(S_NEST, K_AUTOMATICOPTIMIZE, 0, m_MainWindow.GetIniFile()) <> 0 And
         m_MainWindow.GetAutoNestingOption() Then
         AutomaticInsert()
         ' altrimenti inserimento standard
      Else
         StandardInsert()
      End If

   End Sub

   Private Sub StandardInsert()
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      Dim bWrongTrf As Boolean = False
      m_CurrProjPage.ClearMessage()
      ' Ciclo di inserimento in tavola dei pezzi selezionati
      Dim nId As Integer = EgtGetFirstSelectedObj()
      While nId <> GDB_ID.NULL
         ' Recupero successivo selezionato
         Dim nNextId = EgtGetNextSelectedObj()
         ' Se pezzo da Trf, verifico compatibilità
         If Not VerifyTrfData(nId) Then
            bWrongTrf = True
            EgtSetStatus(nId, GDB_ST.ON_)
            ' Passo al successivo selezionato
            nId = nNextId
            Continue While
         End If
         ' Lo metto in tavola, se possibile
         If InsertOnePart(nId) Then
            VeinMatching.OnInsertPartInRaw(nId)
         Else
            EgtSetStatus(nId, GDB_ST.ON_)
            VeinMatching.OnDeselectPart(nId)
         End If
         ' Costringo ad aggiornare UI
         UpdateUI()
         ' Passo al successivo selezionato
         nId = nNextId
      End While
      ' Aggiorno flag selezionati
      m_nPartPos = If(EgtGetSelectedObjCount() > 0, PART_POS.IN_TABLE, PART_POS.NONE_TABLE)
      ' Aggiorno vista
      EgtZoom(ZM.ALL)
      ' Eventuale segnalazione di pezzi Trf non adatti
      If bWrongTrf Then
         ' Pezzi con spessore, materiale o finitura non compatibili
         m_CurrProjPage.SetWarningMessage(EgtMsg(MSG_NESTPAGEUC + 7))
      End If
   End Sub

   Private Sub AutomaticInsert()
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      m_CurrProjPage.SetInfoMessage(EgtMsg(90340))       ' Nesting Automatico in corso
      Dim bWrongTrf As Boolean = False
      Dim vParts As New List(Of Integer)
      ' Ciclo di pre-inserimento in tavola dei pezzi selezionati
      Dim nId As Integer = EgtGetFirstSelectedObj()
      While nId <> GDB_ID.NULL
         ' Recupero successivo selezionato
         Dim nNextId = EgtGetNextSelectedObj()
         ' Se pezzo da Trf, verifico compatibilità
         If Not VerifyTrfData(nId) Then
            bWrongTrf = True
            EgtSetStatus(nId, GDB_ST.ON_)
            ' Passo al successivo selezionato
            nId = nNextId
            Continue While
         End If
         ' Lo preparo in tavola, se possibile
         If PreInsertOnePart(nId) Then
            vParts.Add(nId)
         Else
            EgtSetStatus(nId, GDB_ST.ON_)
            VeinMatching.OnDeselectPart(nId)
         End If
         ' Costringo ad aggiornare UI
         UpdateUI()
         ' Passo al successivo selezionato
         nId = nNextId
      End While
      ' Aggiorno le regioni per il nesting
      UpdateNestRegions()
      Dim bAligned As Boolean = (GetPrivateProfileInt(S_MACH_NEST, K_MACH_NEST_ALIGNED, 0, m_MainWindow.GetMachIniFile()) <> 0)
      EnableReferenceRegion(bAligned)
      ' Eseguo nesting automatico
      Dim bOpimizeOnX As Boolean = (GetPrivateProfileInt(S_NEST, K_AUTOMATICOPTIMIZE, 2, m_MainWindow.GetIniFile()) = 1)
      Dim bGuillotineMode As Boolean = (GetPrivateProfileInt(S_MACH_NEST, K_MACH_NEST_GHIGLIOTTINA, 0, m_MainWindow.GetMachIniFile()) <> 0)
      Dim nAutomaticMaxTime As Integer = GetPrivateProfileInt(S_NEST, K_AUTOMATICMAXTIME, 20, m_MainWindow.GetIniFile())
      Dim dPartTime As Double = If(bGuillotineMode, 1.0, 0.5)
      Dim nMaxTime As Integer = Math.Min(2.0 + dPartTime * vParts.Count(), nAutomaticMaxTime)
      EgtAutomaticPackParts(vParts, bOpimizeOnX, m_bReducedCut, bGuillotineMode, nMaxTime)
      ' Sistemo i pezzi
      For Each nPartId As Integer In vParts
         ' se pezzo inserito
         If nPartId > 0 Then
            VeinMatching.OnInsertPartInRaw(nPartId)
            ' Eventuale aggiunta solido per taglio da sotto
            UpdateSolidForDrip(nPartId)
            ' altrimenti rifiutato
         Else
            ' Ripristino lo stato originale
            PreRemoveOnePart(Math.Abs(nPartId))
            VeinMatching.OnDeselectPart(Math.Abs(nPartId))
         End If
      Next
      ' Aggiorno flag selezionati
      m_nPartPos = If(EgtGetSelectedObjCount() > 0, PART_POS.IN_TABLE, PART_POS.NONE_TABLE)
      ' Aggiorno vista
      EgtZoom(ZM.ALL)
      m_CurrProjPage.ClearMessage()
      ' Eventuale segnalazione di pezzi Trf non adatti
      If bWrongTrf Then
         ' Pezzi con spessore, materiale o finitura non compatibili
         m_CurrProjPage.SetWarningMessage(EgtMsg(MSG_NESTPAGEUC + 7))
      End If
   End Sub

   Private Function VerifyTrfData(nId As Integer) As Boolean
      ' Verifico se pezzo da Trf
      If Not EgtExistsInfo(nId, "OC") Then Return True
      ' Recupero e verifico lo spessore del pezzo
      Dim dTh As Double = 0
      EgtGetInfo(nId, "T", dTh)
      If Math.Abs(dTh - m_b3Raw.DimZ()) > m_dTrfThickTolerance Then Return False
      ' Recupero il materiale e la finitura superficiale del pezzo
      Dim sMat As String = ""
      EgtGetInfo(nId, "MT", sMat)
      Dim sSurf As String = ""
      EgtGetInfo(nId, "SRF", sSurf)
      ' Recupero materiale e finitura superficiale di pezzo già inserito (come dati di riferimento)
      Dim sRefMat As String = ""
      Dim sRefSurf As String = ""
      Dim nPartId As Integer = EgtGetFirstPartInRawPart(m_nRawId)
      While nPartId <> GDB_ID.NULL
         If EgtGetInfo(nPartId, "MT", sRefMat) And EgtGetInfo(nPartId, "SRF", sRefSurf) Then Exit While
         nPartId = EgtGetNextPartInRawPart(nPartId)
      End While
      If String.IsNullOrWhiteSpace(sRefMat) And String.IsNullOrWhiteSpace(sRefSurf) Then Return True
      ' Verifico materiale e finitura superficiale
      If sMat <> sRefMat Or sSurf <> sRefSurf Then Return False
      Return True
   End Function

   Friend Function PreInsertOnePart(nId As Integer) As Boolean
      ' Se non esiste grezzo o pezzo non in parcheggio, esco
      If m_nRawId = GDB_ID.NULL OrElse Not EgtIsPart(nId) Then Return False
      ' Sistemazioni per eventuali lati inclinati con tallone
      AdjustPartSideAngleHeel(nId, m_b3Raw.DimZ())
      ' Sistemazioni per eventuali lati esterni inclinati e/o offsettati
      EgtCalcFlatPartUpRegion(nId, True)
      EgtCalcFlatPartDownRegion(nId, m_b3Raw.DimZ())
      ' Dimensioni del pezzo
      Dim b3Part As New BBox3d
      If Not EgtGetBBoxGlob(nId, GDB_BB.IGNORE_DIM + GDB_BB.IGNORE_TEXT, b3Part) Then Return False
      ' Centro del grezzo
      Dim ptRawCenter As Point3d
      If Not EgtGetRawPartCenter(m_nRawId, ptRawCenter) Then Return False
      Dim dRawCenX = ptRawCenter.x - m_b3Raw.Min().x
      Dim dRawCenY = ptRawCenter.y - m_b3Raw.Min().y
      ' Inserisco il pezzo nel grezzo, in centro in XY e in alto in Z
      Dim ptP As New Point3d(dRawCenX - 0.5 * b3Part.DimX(), dRawCenY - 0.5 * b3Part.DimY(), m_b3Raw.DimZ() - b3Part.DimZ() + DELTAZ_REG)
      If Not EgtAddPartToRawPart(nId, ptP, m_nRawId) Then Return False
      ' Aggiungo le lavorazioni standard
      Dim nWarn As Integer = 0
      AddMachinings(nId, nWarn)
      If nWarn = 1 Then m_CurrProjPage.SetWarningMessage(EgtMsg(MSG_SPLITPAGEUC + 11)) ' Lama troppo grande per utilizzo ventosa
      Return True
   End Function

   Friend Function PreRemoveOnePart(nId As Integer) As Boolean
      ' Se non esiste il pezzo, esco
      If nId = GDB_ID.NULL Then Return False
      ' Rimuovo le lavorazioni
      EraseMachinings(nId)
      ' Elimino eventuali modifiche per lati esterni inclinati e/o offsettati
      EgtCalcFlatPartUpRegion(nId, False)
      EgtCalcFlatPartDownRegion(nId, 0)
      ' Eventuale cancellazione solido per taglio da sotto
      EraseSolidForDrip(nId)
      ' Parcheggio
      EgtRemovePartFromRawPart(nId)
      EgtSetStatus(nId, GDB_ST.ON_)
      Return True
   End Function

   Friend Function InsertOnePart(nId As Integer) As Boolean
      ' Se il pezzo non è in parcheggio, non si può inserire
      If Not EgtIsPart(nId) Then Return False
      ' Metto pezzo in centro grezzo con lavorazioni e sistemazioni varie
      Dim bFit As Boolean = False
      If PreInsertOnePart(nId) Then
         ' Eseguo nesting
         If UpdateNestRegions() Then
            Dim bAligned As Boolean = (GetPrivateProfileInt(S_MACH_NEST, K_MACH_NEST_ALIGNED, 0, m_MainWindow.GetMachIniFile()) <> 0)
            EnableReferenceRegion(bAligned)
            If Not EgtExistsInfo(m_nRawId, KEY_RAWBYPOINTS) Then
               bFit = EgtPackPartInRectangle(nId, m_bReducedCut, True)
            End If
            If Not bFit Then
               bFit = EgtPackPart(nId, m_bReducedCut, True)
            End If
         End If
      End If
      ' Gestione risultato nesting
      If bFit Then
         '---------------------- COUNTER PART ----------------------
         Dim sRefGroup As String = String.Empty
         If EgtGetInfo(nId, INFO_REFGROUP, sRefGroup) Then
            ' spengo il layer che contiene il contatore
            Dim nCounterLayer As Integer = EgtGetFirstNameInGroup(nId, INFO_COUNTERLY)
            EgtSetStatus(nCounterLayer, GDB_ST.OFF)
            ' aggiorno il layer che indica il numero di pezzi in parcheggio
            CountPartInFamily(sRefGroup)
         End If
         '---------------------- COUNTER PART ----------------------

         ' Eventuale aggiunta solido per taglio da sotto
         UpdateSolidForDrip(nId)
         Return True
      Else
         ' Ripristino lo stato originale
         PreRemoveOnePart(nId)
         Return False
      End If
   End Function

   Private Function AdjustPartSideAngleHeel(nPartId As Integer, dTh As Double) As Boolean
      ' Recupero entità del layer esterno e di quelli interni
      Dim vEnt As New List(Of Integer)
      Dim nEntId As Integer = EgtGetFirstInGroup(EgtGetFirstNameInGroup(nPartId, NAME_OUTLOOP))
      While nEntId <> GDB_ID.NULL
         vEnt.Add(nEntId)
         nEntId = EgtGetNext(nEntId)
      End While
      Dim nLayId As Integer = EgtGetFirstNameInGroup(nPartId, NAME_INLOOP)
      While nLayId <> GDB_ID.NULL
         nEntId = EgtGetFirstInGroup(nLayId)
         While nEntId <> GDB_ID.NULL
            vEnt.Add(nEntId)
            nEntId = EgtGetNext(nEntId)
         End While
         nLayId = EgtGetNextName(nLayId, NAME_INLOOP)
      End While
      ' Aggiorno le entità con tallone e quelle con angolo esterno
      Const AGG_DEPTH As Double = 0.75
      Dim bSizeOnTop As Boolean = (GetPrivateProfileInt(S_SIDES, K_SIZEALWAYSONTOP, 0, m_MainWindow.GetIniFile()) <> 0)
      For Each nEnt As Integer In vEnt
         ' Se aggiornamento vietato, vado oltre
         If EgtExistsInfo(nEnt, INFO_SIDE_FIXED) Then Continue For
         ' Recupero eventuali tallone ed angolo originale
         Dim dHeel As Double = 0
         Dim dSideAng As Double = 0
         EgtGetInfo(nEnt, INFO_HEEL, dHeel)
         EgtGetInfo(nEnt, INFO_ORIG_SIDE_ANGLE, dSideAng)
         ' Se inclinazione con tallone
         If Math.Abs(dSideAng) > EPS_ANG_SMALL And dHeel > 10 * EPS_SMALL Then
            ' Inclinazione esterna
            If dSideAng > 0 Then
               ' Se tallone inferiore a spessore
               If dHeel < dTh - 10 * EPS_SMALL Then
                  EgtSetInfo(nEnt, INFO_SIDE_ANGLE, 0.0)
                  EgtSetInfo(nEnt, INFO_SIDE_ANGLE2, dSideAng)
                  If Not bSizeOnTop Then
                     EgtSetInfo(nEnt, INFO_OFFSET, 0)
                     EgtSetInfo(nEnt, INFO_OFFSET2, -(dTh - dHeel) * Math.Tan(dSideAng * Math.PI / 180))
                  Else
                     EgtSetInfo(nEnt, INFO_OFFSET, (dTh - dHeel) * Math.Tan(dSideAng * Math.PI / 180))
                     EgtSetInfo(nEnt, INFO_OFFSET2, 0)
                  End If
                  EgtSetInfo(nEnt, INFO_DEPTH2, (dTh - dHeel) + AGG_DEPTH)
                  EgtSetInfo(nEnt, INFO_AGG2, AGG_DEPTH)
                  ' altrimenti, tallone superiore a spessore -> non c'è taglio inclinato
               Else
                  EgtSetInfo(nEnt, INFO_SIDE_ANGLE, 0.0)
                  EgtSetInfo(nEnt, INFO_SIDE_ANGLE2, 0.0)
                  EgtRemoveInfo(nEnt, INFO_OFFSET2)
                  EgtRemoveInfo(nEnt, INFO_DEPTH2)
                  EgtRemoveInfo(nEnt, INFO_AGG2)
               End If
               ' Inclinazione interna
            Else
               ' Se tallone inferiore a spessore
               If dHeel < dTh - 10 * EPS_SMALL Then
                  EgtSetInfo(nEnt, INFO_OFFSET, dHeel * Math.Tan(-dSideAng * Math.PI / 180))
                  EgtSetInfo(nEnt, INFO_SIDE_ANGLE2, 0.0)
                  EgtSetInfo(nEnt, INFO_DEPTH2, dHeel + AGG_DEPTH)
                  EgtSetInfo(nEnt, INFO_AGG2, AGG_DEPTH)
                  ' altrimenti, tallone superiore a spessore -> non c'è taglio inclinato
               Else
                  EgtSetInfo(nEnt, INFO_SIDE_ANGLE, 0.0)
                  EgtRemoveInfo(nEnt, INFO_OFFSET)
                  EgtRemoveInfo(nEnt, INFO_SIDE_ANGLE2)
                  EgtRemoveInfo(nEnt, INFO_DEPTH2)
                  EgtRemoveInfo(nEnt, INFO_AGG2)
               End If
            End If
            ' Se altrimenti inclinazione esterna
         ElseIf dSideAng > EPS_ANG_SMALL Then
            If Not bSizeOnTop Then
               EgtSetInfo(nEnt, INFO_OFFSET, -dTh * Math.Tan(dSideAng * Math.PI / 180))
            Else
               EgtSetInfo(nEnt, INFO_OFFSET, 0)
            End If
         End If
      Next
      Return True
   End Function

#Region "Manager Counter Part"

   ' creo la lista delle famiglie che popolano il parcheggio
   Public Sub GetFamilyGroupInPark()
      m_ListOfGroupInPark.Clear()
      Dim nIdInPark As Integer = EgtGetFirstPart()
      While nIdInPark <> GDB_ID.NULL
         Dim sRefGroup As String = String.Empty
         If EgtGetInfo(nIdInPark, INFO_REFGROUP, sRefGroup) Then
            Dim bExists As Boolean = False
            For Each sItem As FamilyPart In m_ListOfGroupInPark
               If sRefGroup = sItem.sRefGuid Then
                  bExists = True
                  Exit For
               End If
            Next
            If Not bExists Then
               m_ListOfGroupInPark.Add(New FamilyPart(sRefGroup, nIdInPark))
            End If
         End If
         nIdInPark = EgtGetNextPart(nIdInPark)
      End While
   End Sub

   Public Sub CountPartInFamily(sRefGuid As String)
      If String.IsNullOrEmpty(sRefGuid) Then Return
      Dim nCount As Integer = 0
      Dim nIdInPark As Integer = EgtGetFirstPart()
      ' conto il nuero di pezzi del gruppo inidcato
      While nIdInPark <> GDB_ID.NULL
         Dim sRefGroup As String = String.Empty
         EgtGetInfo(nIdInPark, INFO_REFGROUP, sRefGroup)
         If sRefGroup = sRefGuid Then
            nCount += 1
         End If
         nIdInPark = EgtGetNextPart(nIdInPark)
      End While
      ' aggiorno il contatore dei pezzi del gruppo
      nIdInPark = EgtGetFirstPart()
      While nIdInPark <> GDB_ID.NULL
         Dim sRefGroup As String = String.Empty
         EgtGetInfo(nIdInPark, INFO_REFGROUP, sRefGroup)
         If sRefGroup = sRefGuid Then
            Dim nId As Integer = EgtGetFirstNameInGroup(nIdInPark, INFO_COUNTERLY)
            If nId <> GDB_ID.NULL Then
               Dim m_IdCounterTextas As Integer = EgtGetFirstInGroup(nId)
               EgtModifyText(m_IdCounterTextas, "# " & nCount.ToString)
            End If
         End If
         nIdInPark = EgtGetNextPart(nIdInPark)
      End While
   End Sub

   Public Sub SelectPartInFaimily(sRefGuid As String, nNbrOfSelection As Integer)
      ' aggiorno il contatore dei pezzi del gruppo
      Dim nIdInPark As Integer = EgtGetFirstPart()
      Dim nCounter As Integer = 0
      While nIdInPark <> GDB_ID.NULL And nCounter < nNbrOfSelection
         Dim sRefGroup As String = String.Empty
         EgtGetInfo(nIdInPark, INFO_REFGROUP, sRefGroup)
         If sRefGroup = sRefGuid Then
            EgtSelectObj(nIdInPark)
            nCounter += 1
         End If
         nIdInPark = EgtGetNextPart(nIdInPark)
      End While
   End Sub

   Public Sub DeselectPartInFaimily(sRefGuid As String)
      ' aggiorno il contatore dei pezzi del gruppo
      Dim nIdInPark As Integer = EgtGetFirstPart()
      While nIdInPark <> GDB_ID.NULL
         Dim sRefGroup As String = String.Empty
         EgtGetInfo(nIdInPark, INFO_REFGROUP, sRefGroup)
         If sRefGroup = sRefGuid Then
            EgtDeselectObj(nIdInPark)
         End If
         nIdInPark = EgtGetNextPart(nIdInPark)
      End While
   End Sub

   Public Function ParkInGroupFamily(nId As Integer) As Boolean
      ' se pezzo appartenente ad un gruppo
      Dim sRefGroup As String = String.Empty
      Dim Element As FamilyPart
      If EgtGetInfo(nId, INFO_REFGROUP, sRefGroup) Then
         ' verifico se esiste un pezzo dello stesso gruppo in parcheggio
         Dim nNewLayer As Integer
         Dim ptMin As Point3d
         Dim ptMax As Point3d
         For Each sItem As FamilyPart In m_ListOfGroupInPark
            If sRefGroup = sItem.sRefGuid Then
               ' creo un nuovo layer per contenere l'informazione del numero di pezzi da inserire
               If EgtGetFirstNameInGroup(nId, INFO_COUNTERLY) = GDB_ID.NULL Then
                  nNewLayer = EgtCreateGroup(nId)
                  EgtSetName(nNewLayer, INFO_COUNTERLY)
                  EgtSetColor(nNewLayer, New Color3d(0, 0, 0))
                  EgtGetBBox(nId, GDB_BB.STANDARD, ptMin, ptMax)
                  EgtCreateText(nNewLayer, New Point3d((ptMin.x + ptMax.x) / 2, ptMin.y - sItem.nHText - 20, ptMin.z), "# ???", sItem.nHText, GDB_RT.LOC)
               Else
                  ' Accendo il layer che contiene il contatore
                  Dim nCounterLayer As Integer = EgtGetFirstNameInGroup(nId, INFO_COUNTERLY)
                  EgtSetStatus(nCounterLayer, GDB_ST.ON_)
               End If

               ' calcolo lo spostamento del pezzo
               Dim PtMinInRaw As Point3d
               Dim PtMaxInRaw As Point3d
               EgtGetBBoxGlob(nId, GDB_BB.STANDARD, PtMinInRaw, PtMaxInRaw)
               Dim vtMoveToStorage As New Vector3d
               vtMoveToStorage = sItem.PtStorage() - PtMinInRaw
               EgtMove(nId, vtMoveToStorage, GDB_RT.GLOB)

               ' aggiorno il layer che indica il numero di pezzi in parcheggio
               CountPartInFamily(sRefGroup)
               Return True
            End If
         Next
         ' inserisco il primo pezzo del gruppo nel parcheggio
         Element = New FamilyPart(sRefGroup, nId)
         m_ListOfGroupInPark.Add(Element)
         ' creo un nuovo layer per contenere l'informazione del numero di pezzi da inserire
         If EgtGetFirstNameInGroup(nId, INFO_COUNTERLY) = GDB_ID.NULL Then
            nNewLayer = EgtCreateGroup(nId)
            EgtSetName(nNewLayer, INFO_COUNTERLY)
            EgtSetColor(nNewLayer, New Color3d(0, 0, 0))
            EgtGetBBox(nId, GDB_BB.STANDARD, ptMin, ptMax)
            ' posso migliorarei lposizioanamento in funzione della dimensione della scritta...
            EgtCreateText(nNewLayer, New Point3d((ptMin.x + ptMax.x) / 2, ptMin.y - Element.nHText - 20, ptMin.z), "# 1", Element.nHText, GDB_RT.LOC)
         Else
            ' Accendo il layer che contiene il contatore
            Dim nCounterLayer As Integer = EgtGetFirstNameInGroup(nId, INFO_COUNTERLY)
            EgtSetStatus(nCounterLayer, GDB_ST.ON_)
            ' aggiorno il layer che indica il numero di pezzi in parcheggio
            CountPartInFamily(sRefGroup)
         End If
      End If
      Return False
   End Function

#End Region    ' Manager Counter Part
   Private Sub DragRettangle_Click() Handles DragRettanleBtn.Click
      If DragRettanleBtn.IsChecked Then
         m_bIsCheckedDragRettangle = True
      Else
         m_bIsCheckedDragRettangle = False
         ResetDragRettangleParam()
         m_CurrProjPage.ClearMessage()
         ShowParkedParts()
      End If
      EgtDraw()
   End Sub

   Private Sub StorePartBtn_Click(sender As Object, e As RoutedEventArgs) Handles StorePartBtn.Click
      ' creo la lista delle famiglie di pezzi presenti in parcheggio
      GetFamilyGroupInPark()
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      ' Ciclo di parcheggio dei pezzi selezionati
      Dim nId As Integer = EgtGetFirstSelectedObj()
      While nId <> GDB_ID.NULL
         ' Recupero indice del successivo
         Dim nNextId = EgtGetNextSelectedObj()
         ' Metto in parcheggio, se in grezzo
         StoreOnePart(nId)
         ' Eventuale notifiche al VeinMatching
         VeinMatching.OnRemovePartFromRaw(nId)
         ' Passo al successivo selezionato
         nId = nNextId
      End While
      ' Imposto flag posizione pezzi su parcheggio
      m_nPartPos = PART_POS.NONE_TABLE
      ' Aggiorno vista
      EgtZoom(ZM.ALL)
   End Sub

   Friend Function StoreOnePart(nId As Integer, Optional ByVal bForced As Boolean = False) As Boolean
      ' Se pezzo in grezzo, metto in parcheggio (sempre possibile)
      If bForced OrElse
         (m_nRawId <> GDB_ID.NULL AndAlso EgtGetParent(nId) = m_nRawId) Then
         ' Ripristino lo stato originale
         PreRemoveOnePart(nId)

         '---------------------- COUNTER PART ----------------------
         ' se esiste almento un pezzo del gruppo in parcheggio esco
         If ParkInGroupFamily(nId) Then Return True
         '---------------------- COUNTER PART ----------------------

         ' Parcheggio
         PackPartInStore(nId)
         ' Aggiusto la posizione in Z
         Dim ptPartMin, ptPartMax As Point3d
         If EgtGetBBoxGlob(nId, GDB_BB.IGNORE_DIM + GDB_BB.IGNORE_TEXT, ptPartMin, ptPartMax) And m_nRawId <> GDB_ID.NULL Then
            EgtMove(nId, New Vector3d(0, 0, m_b3Raw.Max().z - ptPartMax.z), GDB_RT.GLOB)
         End If
         Return True
      End If
      Return False
   End Function

   Friend Function PackPartInStore(nId As Integer) As Boolean
      ' Recupero box tavola
      Dim b3Tab As New BBox3d
      If Not EgtGetTableArea(1, b3Tab) Then
         b3Tab.Add(New Point3d(0, 0, 0))
         b3Tab.Add(New Point3d(-3600, -2600, 0))
      End If
      ' Parcheggio
      Const STORE_LARGH As Double = 6000
      Const STORE_DIST As Double = 200
      Const STORE_OFFS As Double = 20 + 40 + 20
      Dim dStXmin As Double = b3Tab.Min().x - 0.5 * (STORE_LARGH - b3Tab.DimX())
      Return EgtPackBox(nId, dStXmin, -INFINITO, dStXmin + STORE_LARGH, b3Tab.Min().y - STORE_DIST, STORE_OFFS, False)
   End Function

   Private Sub RemovePartBtn_Click(sender As Object, e As RoutedEventArgs) Handles RemovePartBtn.Click
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      ' Se non ci sono pezzi selezionati, esco subito
      If EgtGetFirstSelectedObj() = GDB_ID.NULL Then Return
      ' Chiedo conferma prima di procedere
      Dim RemovePartWnd As EgtMsgBox = New EgtMsgBox(m_MainWindow, "", EgtMsg(MSG_EGTMSGBOX + 21), EgtMsgBox.Buttons.OK_CANCEL, EgtMsgBox.Icons.NULL)
      If RemovePartWnd.m_nPressedBtn = 0 Then
         Return
      End If
      ' Verifico se ci sono pezzi provenienti da liste Csv
      Dim bCsvParts As Boolean = False
      Dim nId As Integer = EgtGetFirstSelectedObj()
      While nId <> GDB_ID.NULL
         If EgtExistsInfo(nId, INFO_CSV_PATH) Then
            bCsvParts = True
            Exit While
         End If
         nId = EgtGetNextSelectedObj()
      End While
      ' Chiedo se si vogliono cancellare anche questi pezzi
      Dim bEraseCsvParts As Boolean = False
      If bCsvParts Then
         ' Vuoi cancellare anche i pezzi da lista Csv ? 
         Dim EraseCsvParts As New EgtMsgBox(m_MainWindow, "", EgtMsg(MSG_EGTMSGBOX + 12),
                                            EgtMsgBox.Buttons.YES_NO_CANCEL, EgtMsgBox.Icons.NULL)
         Select Case EraseCsvParts.m_nPressedBtn
            Case 0    ' Annulla
               Return
            Case 1    ' Si
               bEraseCsvParts = True
            Case 2    ' No
               bEraseCsvParts = False
         End Select
      End If
      ' Ciclo di cancellazione dei pezzi selezionati
      Dim dEraseArea As Double = 0
      nId = EgtGetFirstSelectedObj()
      While nId <> GDB_ID.NULL
         ' Recupero indice del successivo
         Dim nNextId = EgtGetNextSelectedObj()
         ' Se abilitata cancellazione in ogni caso o pezzo non da Csv
         If bEraseCsvParts OrElse Not EgtExistsInfo(nId, INFO_CSV_PATH) Then
            ' Aggiorno l'area dei pezzi cancellati
            dEraseArea += GeomCalc.GetPartArea(nId)
            ' Se pezzo in parcheggio cancello direttamente
            If EgtIsPart(nId) Then
               '---------------------- COUNTER PART ----------------------
               Dim sRefGroup As String = String.Empty
               ' recupero l'GUID del gruppo 
               EgtGetInfo(nId, INFO_REFGROUP, sRefGroup)
               '---------------------- COUNTER PART ----------------------
               ' Rimuovo le lavorazioni
               EraseMachinings(nId)
               ' Cancello
               EgtErase(nId)
               ' aggiorno il layer che indica il numero di pezzi in parcheggio (se sRefGroup<>"")
               CountPartInFamily(sRefGroup)
               ' Altrimenti pezzo nel grezzo
            ElseIf EgtGetParent(nId) = m_nRawId Then
               If EgtRemovePartFromRawPart(nId) Then
                  ' Eventuale notifica al VeinMatching
                  VeinMatching.OnRemovePartFromRaw(nId)
                  ' Rimuovo le lavorazioni
                  EraseMachinings(nId)
                  'Cancello
                  EgtErase(nId)
               End If
            End If
         End If
         ' Passo al successivo selezionato
         nId = nNextId
      End While

      ' Imposto flag posizione pezzi
      m_nPartPos = PART_POS.NONE_TABLE
      ' Aggiorno Aree totale e da lavorare dei pezzi
      m_CurrProjPage.UpdateTotalArea(-dEraseArea)
      m_CurrProjPage.UpdateToProduceArea()
      m_CurrProjPage.ShowAreas()
      ' Aggiorno vista
      EgtZoom(ZM.ALL)
   End Sub

   Private Sub SelectAllBtn_Click(sender As Object, e As RoutedEventArgs) Handles SelectAllBtn.Click
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      ' Numero dei pezzi in tavola
      Dim nPartTab As Integer = EgtGetPartInRawPartCount(m_nRawId)
      ' Se già selezionati in tavola o non ci sono selezionati e pezzi in tavola
      If m_nPartPos = PART_POS.IN_TABLE Or
         (m_nPartPos = PART_POS.NONE_TABLE And nPartTab > 0) Then
         ' Seleziono tutti i pezzi in tavola
         Dim nPartId As Integer = EgtGetFirstGroupInGroup(m_nRawId)
         While nPartId <> GDB_ID.NULL
            EgtSelectObj(nPartId)
            VeinMatching.OnSelectPart(nPartId, False)
            nPartId = EgtGetNextGroup(nPartId)
         End While
         ' Aggiorno flag
         m_nPartPos = If(EgtGetSelectedObjCount() > 0, PART_POS.IN_TABLE, PART_POS.NONE_TABLE)
         ' Altrimenti
      Else
         ' Seleziono tutti i pezzi in parcheggio
         Dim nPartId As Integer = EgtGetFirstPart()
         While nPartId <> GDB_ID.NULL
            EgtSelectObj(nPartId)
            VeinMatching.OnSelectPart(nPartId, False)
            nPartId = EgtGetNextPart(nPartId)
         End While
         ' Aggiorno flag
         m_nPartPos = If(EgtGetSelectedObjCount() > 0, PART_POS.OUT_TABLE, PART_POS.NONE_TABLE)
      End If
      ' Aggiorno viste
      EgtDraw()
      VeinMatching.Draw()
   End Sub

   Private Sub DeselectAllBtn_Click(sender As Object, e As RoutedEventArgs) Handles DeselectAllBtn.Click
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      ' Eseguo
      EgtDeselectAll()
      ' Eventuale notifica al VeinMatching
      VeinMatching.OnDeselectAll()
      ' Imposto flag posizione pezzi
      m_nPartPos = PART_POS.NONE_TABLE
      ' Aggiorno vista
      EgtDraw()
   End Sub

   Private Sub PartRotOnBtn_Click(sender As Object, e As RoutedEventArgs) Handles PartRotOnBtn.Click
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      ' Se non ci sono pezzi selezionati o non sono in parcheggio, esco
      If EgtGetSelectedObjCount() = 0 OrElse m_nPartPos <> PART_POS.OUT_TABLE Then Return
      ' Tolgo blocco rotazione sui pezzi selezionati
      Dim nId As Integer = EgtGetFirstSelectedObj()
      While nId <> GDB_ID.NULL
         ' Se rotazione bloccata, tolgo blocco e aggiorno info
         If EgtExistsInfo(nId, INFO_PARTROT) Then
            ' Rimuovo flag di rotazione bloccata
            EgtRemoveInfo(nId, INFO_PARTROT)
            ' Tolgo da info sul pezzo Codice
            Dim nTextId As Integer = EgtGetFirstInGroup(EgtGetFirstNameInGroup(nId, NAME_REGION))
            While nTextId <> GDB_ID.NULL
               If EgtGetType(nTextId) = GDB_TY.EXT_TEXT Then
                  Dim sText As String = ""
                  EgtTextGetContent(nTextId, sText)
                  sText = sText.Replace("<br/>(X)", "")
                  EgtModifyText(nTextId, sText)
                  Exit While
               End If
               nTextId = EgtGetNext(nTextId)
            End While
         End If
         ' Passo al successivo selezionato
         nId = EgtGetNextSelectedObj()
      End While
      ' Aggiorno visualizzazione
      EgtDraw()
   End Sub

   Private Sub PartRotOffBtn_Click(sender As Object, e As RoutedEventArgs) Handles PartRotOffBtn.Click
      EgtSetCurrentContext(m_CurrProjPage.CurrentProjectScene.GetCtx())
      ' Se non ci sono pezzi selezionati o non sono in parcheggio, esco
      If EgtGetSelectedObjCount() = 0 OrElse m_nPartPos <> PART_POS.OUT_TABLE Then Return
      ' Metto blocco rotazione sui pezzi selezionati
      Dim nId As Integer = EgtGetFirstSelectedObj()
      While nId <> GDB_ID.NULL
         ' Se rotazione libera, applico blocco e aggiorno info
         If Not EgtExistsInfo(nId, INFO_PARTROT) Then
            ' Imposto flag di rotazione bloccata
            EgtSetInfo(nId, INFO_PARTROT, 0)
            ' Inserisco in info sul pezzo Codice
            Dim nTextId As Integer = EgtGetFirstInGroup(EgtGetFirstNameInGroup(nId, NAME_REGION))
            While nTextId <> GDB_ID.NULL
               If EgtGetType(nTextId) = GDB_TY.EXT_TEXT Then
                  Dim sText As String = ""
                  EgtTextGetContent(nTextId, sText)
                  sText &= "<br/>(X)"
                  EgtModifyText(nTextId, sText)
                  Exit While
               End If
               nTextId = EgtGetNext(nTextId)
            End While
         End If
         ' Passo al successivo selezionato
         nId = EgtGetNextSelectedObj()
      End While
      ' Aggiorno visualizzazione
      EgtDraw()
   End Sub

   Private Function GetLockOnRotation(nPartId As Integer) As Boolean
      ' Se singolo pezzo
      If nPartId <> GDB_ID.SEL Then
         Return EgtExistsInfo(nPartId, INFO_PARTROT)
         ' Altrimenti tutti i selezionati
      Else
         Dim bLockedRot As Boolean = False
         Dim nId As Integer = EgtGetFirstSelectedObj()
         While nId <> GDB_ID.NULL
            If EgtExistsInfo(nId, INFO_PARTROT) Then
               bLockedRot = True
               Exit While
            End If
            nId = EgtGetNextSelectedObj()
         End While
         Return bLockedRot
      End If
   End Function

   Private Sub StepMoveTxBx_EgtClosed(sender As Object, e As EventArgs) Handles StepMoveTxBx.EgtClosed
      Dim dVal As Double
      If StringToLen(StepMoveTxBx.Text, dVal) Then
         If dVal <= 0 Then
            dVal = 0
            StepMoveTxBx.Text = LenToString(dVal, 2)
         ElseIf dVal > 1000 Then
            dVal = 1000
            StepMoveTxBx.Text = LenToString(dVal, 2)
         End If
         If Not m_bRegister Then
            m_dStep = Math.Max(dVal, 2 * EPS_SMALL)
         Else
            m_dRegStep = Math.Max(dVal, 2 * EPS_SMALL)
         End If
         WritePrivateProfileString(S_NEST, K_STEP, StepMoveTxBx.Text, m_MainWindow.GetIniFile())
      End If
   End Sub

   Private Sub RotationAngleTxBx_EgtClosed(sender As Object, e As EventArgs) Handles RotationAngleTxBx.EgtClosed
      Dim dVal As Double
      If StringToDouble(RotationAngleTxBx.Text, dVal) Then
         If dVal <= 0 Then
            dVal = 0
            RotationAngleTxBx.Text = DoubleToString(dVal, 2)
         ElseIf dVal > 180 Then
            dVal = 180
            RotationAngleTxBx.Text = DoubleToString(dVal, 2)
         End If
         If Not m_bRegister Then
            m_dAngStep = Math.Max(dVal, 50 * EPS_ANG_SMALL)
         Else
            m_dRegAngStep = Math.Max(dVal, 50 * EPS_ANG_SMALL)
         End If
         WritePrivateProfileString(S_NEST, K_ANGSTEP, RotationAngleTxBx.Text, m_MainWindow.GetIniFile())
      End If
   End Sub

   Private Sub NestPage_Unloaded(sender As Object, e As RoutedEventArgs)
      m_bActive = False
      m_MainWindow.m_CadCutPageUC.m_ProjectMgr.VeinMatchingHide()
   End Sub

   Private Sub MagneticPiecesBtn_Click(sender As Object, e As RoutedEventArgs) Handles MagneticPiecesBtn.Click
      If MagneticPiecesBtn.IsChecked() Then
         m_bMagnetic = True
      Else
         m_bMagnetic = False
      End If
   End Sub

   Friend Function VerifyPartsNesting(bReducedCut As Boolean) As Boolean
      ' Aggiorno regioni per nesting
      UpdateNestRegions()
      EnableReferenceRegion(False)
      ' Ciclo su tutti i pezzi in tavola
      Dim nPartId As Integer = EgtGetFirstGroupInGroup(m_nRawId)
      While nPartId <> GDB_ID.NULL
         If Not EgtVerifyPart(nPartId, bReducedCut) Then
            Return False
         End If
         nPartId = EgtGetNextGroup(nPartId)
      End While
      Return True
   End Function

   Friend Function StoreCollisionParts(bReducedCut As Boolean) As Boolean
      ' Aggiorno regioni per nesting
      UpdateNestRegions()
      EnableReferenceRegion(False)
      ' Ciclo su tutti i pezzi in tavola (dall'ultimo)
      Dim nPartId As Integer = EgtGetLastGroupInGroup(m_nRawId)
      While nPartId <> GDB_ID.NULL
         Dim nPrevPartId As Integer = EgtGetPrevGroup(nPartId)
         If Not EgtVerifyPart(nPartId, bReducedCut) Then
            StoreOnePart(nPartId, True)
         End If
         nPartId = nPrevPartId
      End While
      Return True
   End Function

   Friend Function UpdateNestRegions() As Boolean
      m_CurrProjPage = m_MainWindow.m_CurrentProjectPageUC
      ' Se necessario, creo la regione fuori kerf
      Dim nKerfId As Integer = EgtGetFirstNameInGroup(m_CurrProjPage.m_nRawId, NAME_KERF)
      EgtCreateOutRegion(m_CurrProjPage.m_nRawId, nKerfId)
      ' Se necessario, creo la regione di riferimento
      EgtCreateReferenceRegion(m_CurrProjPage.m_nRawId, nKerfId, True)
      ' Se necessario, creo le regioni per le aree danneggiate
      Dim nId As Integer = EgtGetFirstNameInGroup(m_CurrProjPage.m_nRawId, NAME_DAMAGED)
      While nId <> GDB_ID.NULL
         EgtCreateDamagedRegion(m_CurrProjPage.m_nRawId, nId)
         nId = EgtGetNextName(nId, NAME_DAMAGED)
      End While
      Return True
   End Function

   Friend Function EnableReferenceRegion(bEnable As Boolean) As Boolean
      m_CurrProjPage = m_MainWindow.m_CurrentProjectPageUC
      ' Recupero identificativo della regione di riferimento
      Dim nRegId As Integer = EgtGetFirstNameInGroup(m_CurrProjPage.m_nRawId, NAME_REF_REG)
      If nRegId = GDB_ID.NULL Then Return False
      ' Imposto l'abilitazione voluta
      If bEnable Then
         EgtRemoveInfo(nRegId, KEY_REF_REG_OFF)
      Else
         EgtSetInfo(nRegId, KEY_REF_REG_OFF, 1)
      End If
      Return True
   End Function

End Class


Class FamilyPart

   Private PtMinStoraged As Point3d
   Private PtMaxStoraged As Point3d

   Private m_nHText As Integer = 40
   Public ReadOnly Property nHText As Double
      Get
         Return m_nHText
      End Get
   End Property

   Private m_sRefGuid As String = String.Empty
   Public ReadOnly Property sRefGuid As String
      Get
         Return m_sRefGuid
      End Get
   End Property

   Private m_nIdPart As Integer = -1
   Public ReadOnly Property nIdPart As Integer
      Get
         Return m_nIdPart
      End Get
   End Property

   Private m_IdCounterText As Integer
   Public ReadOnly Property IdCounterText As Integer
      Get
         Return m_IdCounterText
      End Get
   End Property

   Public Function PtStorage() As Point3d
      EgtGetBBoxGlob(m_nIdPart, GDB_BB.STANDARD, PtMinStoraged, PtMaxStoraged)
      Return PtMinStoraged
   End Function

   'Public Sub RefreshCounterLayer(nNbr As Integer)
   '   GetIdtextCounteLayer()
   '   EgtModifyText(m_IdCounterText, "# " & nNbr.ToString)
   'End Sub

   'Private Sub GetIdtextCounteLayer()
   '   Dim nId As Integer = EgtGetFirstNameInGroup(m_nIdPart, INFO_COUNTERLY)
   '   If nId <> GDB_ID.NULL Then
   '      m_IdCounterText = EgtGetFirstInGroup(nId)
   '   Else
   '      CreateCounterLayer()
   '   End If
   'End Sub

   Sub New(Guid As String, IdPart As Integer)
      m_sRefGuid = Guid
      m_nIdPart = IdPart
   End Sub

End Class