CadPaste

Inventor · iLogic · 1 dzień temu · zaktualizowano dzisiaj

Oznaczenie grubości materiałów arkuszowych na rysunku

iLogic
'==============================================================================
' iLogic_GruboscWskaznik.vb - Autodesk Inventor iLogic rule (single file)
' Thickness callout (leader note) on a drawing view.
'
' WHAT THIS RULE DOES
'   On the active Drawing (.idw / .dwg):
'     1) Click a view that references a Part (.ipt)
'        -> arrow tip = click point, text = thickness "x..."
'     2) Move mouse for live preview, click again to place the text
'   Sheet-metal Part: reads SheetMetal Thickness (linked FormattedText).
'   Regular Part: tries UserParameter named "Thickness".
'   If no parameter: plain text "x" + fallback 1.5 mm (not linked to model).
'   Esc cancels and removes the preview leader.
'
' HOW TO USE
'   1) Optional: edit CfgThicknessNumberFormat below (display digits).
'   2) Inventor: External Rules -> Add Rule -> select this .vb / .iLogicVb
'      (prefer local disk, not W:\ / OneDrive - same tip as Save.iLogicVb).
'   3) Open a Drawing, run the rule, click view then text position (Esc = cancel).
'
' CONFIG (Cfg*) - change Return value only:
'   CfgThicknessNumberFormat
'
' NOTES FOR BEGINNERS
'   - iLogic = VB.NET (not VBA). Sub Main must come first.
'   - Do NOT put Dim / Const / Shared above Main - iLogic will not compile.
'     The one setting is a Function that Returns a value (same idea as Save.iLogicVb).
'   - ThisApplication is given by iLogic.
'   - Inventor database length = cm. Drawing notes / Point2d are in cm.
'     Thickness DISPLAY is in mm (cm * 10). Decimal comma for PL drawings.
'   - References: System.Windows.Forms (DoEvents loop while picking).
'   - Interaction logic lives in Class TwoPointPicker (fields OK inside Class).
'   - Class cannot call module-level Cfg* Functions (iLogic: "object reference").
'     Main reads Cfg* and assigns NumberFormat on the picker instance.
'   - All MsgBox / StatusBar text is English (ASCII-safe).
'==============================================================================

'===== ENTRY ==================================================================

Sub Main()
    ' ThisApplication = Inventor session provided by iLogic.
    Dim invApp As Inventor.Application = ThisApplication

    ' This tool works only on drawings - cast fails for Part / Assembly.
    Dim oDrawDoc As DrawingDocument = TryCast(invApp.ActiveDocument, DrawingDocument)
    If oDrawDoc Is Nothing Then
        MsgBox("Open a drawing document (.idw/.dwg).", MsgBoxStyle.Exclamation, "Thickness indicator")
        Return
    End If

    ' TwoPointPicker: interactive click-1 (view+arrow) / click-2 (text position).
    Dim picker As New TwoPointPicker()
    picker.DrawDoc = oDrawDoc
    ' Pass Cfg into Class (Class must not call module Functions directly).
    picker.NumberFormat = CfgThicknessNumberFormat()

    ' False = user cancelled (Esc) or clicks incomplete - leave drawing unchanged.
    If Not picker.Run(invApp) Then Return

    ' Refresh sheet after the final leader note is placed.
    oDrawDoc.Update()
End Sub

'----- SETTINGS (edit Return value) -------------------------------------------
' Only number display format is configurable. Everything else is fixed in code.
' Implemented as Function: iLogic forbids module-level Const/fields
' (same pattern as Save.iLogicVb / ObliczeniaBlach.ilogicVb).

Function CfgThicknessNumberFormat() As String
    ' .NET format for the thickness number (InvariantCulture first, then ",").
    ' "0.###" = up to 3 decimals, trailing zeros trimmed (e.g. 1.5 -> "1,5").
    ' Change Return, save the rule, run again.
    Return "0.###"
End Function

'===== PICKER CLASS ===========================================================
' Owns InteractionEvents + MouseEvents for the two-click workflow.
' Public fields are filled for Main / callers; Private fields are session state.

Class TwoPointPicker
    ' Inventor Application used to create TransientGeometry / InteractionEvents.
    Private _app As Inventor.Application
    ' Interaction session (status bar text, Start/Stop, Esc -> OnTerminate).
    Private WithEvents _interact As InteractionEvents
    ' Mouse click + move while interaction is running.
    Private WithEvents _mouse As MouseEvents
    ' 1 = waiting for view/arrow click; 2 = waiting for text position click.
    Private _step As Integer
    ' True when Esc, second click, or terminate ends the DoEvents loop.
    Private _done As Boolean
    ' FormattedText built on click 1 (plain "x..." or linked <Parameter> XML).
    Private _noteText As String
    ' Temporary LeaderNote shown during move; kept as final note on click 2.
    Private _previewLeader As LeaderNote
    ' Last preview text position (cm) - used to ignore tiny mouse moves.
    Private _lastPreviewX As Double
    Private _lastPreviewY As Double
    ' False until the first preview leader exists.
    Private _hasPreviewPos As Boolean

    ' Drawing that receives the LeaderNote (set from Main).
    Public DrawDoc As DrawingDocument
    ' View chosen on click 1 (must reference a Part).
    Public SelectedView As DrawingView
    ' Text position (click 2) - sheet Point2d in cm.
    Public Point1 As Point2d
    ' Arrow tip (click 1) - sheet Point2d in cm.
    Public Point2 As Point2d
    ' Set from Main via CfgThicknessNumberFormat() - do not call Cfg* from Class.
    Public NumberFormat As String

    Public Function Run(app As Inventor.Application) As Boolean
        ' Start interactive pick. Returns True only if view + both points exist.
        _app = app
        _step = 1
        _done = False
        _hasPreviewPos = False

        ' CreateInteractionEvents: Inventor UI mode for custom mouse handling.
        _interact = _app.CommandManager.CreateInteractionEvents()
        _interact.StatusBarText = "Click 1/2: view and arrow tip (Esc = cancel)"
        _mouse = _interact.MouseEvents
        ' No snap-to-geometry inference - use raw model/sheet click coordinates.
        _mouse.PointInferenceEnabled = False
        ' Need move events for live leader preview in step 2.
        _mouse.MouseMoveEnabled = True
        _interact.Start()

        ' Keep the rule thread pumping Windows messages until _done.
        ' Without DoEvents the UI would freeze and clicks would not arrive.
        Do While Not _done
            System.Windows.Forms.Application.DoEvents()
        Loop

        _interact.Stop()
        ' Success only when all three results are set (Esc clears preview, leaves nulls).
        Return SelectedView IsNot Nothing AndAlso Point1 IsNot Nothing AndAlso Point2 IsNot Nothing
    End Function

    Private Sub _mouse_OnMouseClick(Button As MouseButtonEnum, ShiftKeys As ShiftStateEnum, ModelPosition As Inventor.Point, ViewPosition As Point2d, ByVal InvView As Inventor.View) Handles _mouse.OnMouseClick
        ' Only left button advances the workflow.
        If Button <> MouseButtonEnum.kLeftMouseButton Then Return

        ' ModelPosition is sheet space for drawing interaction (X/Y in cm).
        Dim pt As Point2d = _app.TransientGeometry.CreatePoint2d(ModelPosition.X, ModelPosition.Y)

        If _step = 1 Then
            ' ----- CLICK 1: pick view + arrow tip, build note text -----
            Dim oView As DrawingView = FindViewAtPoint(DrawDoc.ActiveSheet, pt)
            If oView Is Nothing Then
                MsgBox("Click inside a drawing view.", MsgBoxStyle.Exclamation, "Thickness indicator")
                Return
            End If

            ' View must point at a Part document (.ipt), not Assembly / Drawing.
            Dim oModel As Inventor.Document = oView.ReferencedDocumentDescriptor.ReferencedDocument
            If oModel Is Nothing OrElse oModel.DocumentType <> DocumentTypeEnum.kPartDocumentObject Then
                MsgBox("View must reference a part (.ipt).", MsgBoxStyle.Exclamation, "Thickness indicator")
                Return
            End If

            SelectedView = oView
            Point2 = pt
            _noteText = BuildThicknessFormattedText(CType(oModel, PartDocument), oView)
            _step = 2
            _interact.StatusBarText = "Click 2/2: text position (Esc = cancel)"
            ' First preview: text and arrow at the same point until mouse moves.
            ShowPreview(pt)
        Else
            ' ----- CLICK 2: lock text position; keep preview as the real note -----
            Point1 = pt
            ShowPreview(pt)
            ' Drop our reference without Delete - note stays on the sheet.
            _previewLeader = Nothing
            _done = True
        End If
    End Sub

    Private Sub _mouse_OnMouseMove(Button As MouseButtonEnum, ShiftKeys As ShiftStateEnum, ModelPosition As Inventor.Point, ViewPosition As Point2d, ByVal InvView As Inventor.View) Handles _mouse.OnMouseMove
        ' Live preview only after click 1 (arrow tip Point2 already known).
        If _step <> 2 OrElse Point2 Is Nothing Then Return
        ShowPreview(_app.TransientGeometry.CreatePoint2d(ModelPosition.X, ModelPosition.Y))
    End Sub

    Private Sub _interact_OnTerminate() Handles _interact.OnTerminate
        ' Esc / cancel / interaction ended before click 2.
        ' If we never finished, remove the temporary preview leader.
        If Not _done Then ClearPreview()
        _done = True
    End Sub

    Private Sub ShowPreview(textPt As Point2d)
        ' Rebuild LeaderNote at textPt -> Point2 (arrow).
        ' Skip moves smaller than 0.05 cm to avoid delete/add every pixel.
        If _hasPreviewPos AndAlso _
           Math.Abs(_lastPreviewX - textPt.X) < 0.05 AndAlso _
           Math.Abs(_lastPreviewY - textPt.Y) < 0.05 Then Return

        ClearPreview()

        ' LeaderNotes.Add expects: first point = text, last point = arrow tip.
        Dim oLeaderPts As ObjectCollection = _app.TransientObjects.CreateObjectCollection
        oLeaderPts.Add(textPt)
        oLeaderPts.Add(Point2)
        _previewLeader = DrawDoc.ActiveSheet.DrawingNotes.LeaderNotes.Add(oLeaderPts, "")
        ' FormattedText can be plain string or Inventor XML with <Parameter>.
        _previewLeader.FormattedText = _noteText
        ' Small dot arrowhead (less visual clutter than a filled arrow).
        _previewLeader.Leader.ArrowheadType = ArrowheadTypeEnum.kSmallDotArrowheadType

        _lastPreviewX = textPt.X
        _lastPreviewY = textPt.Y
        _hasPreviewPos = True
        DrawDoc.Update()
    End Sub

    Private Sub ClearPreview()
        ' Delete temporary leader if it still exists on the sheet.
        If _previewLeader Is Nothing Then Return
        Try
            _previewLeader.Delete()
        Catch
            ' Ignore if already deleted by the user / Inventor.
        End Try
        _previewLeader = Nothing
        _hasPreviewPos = False
    End Sub

    Private Function FindViewAtPoint(oSheet As Sheet, pt As Point2d) As DrawingView
        ' Hit-test: which DrawingView rectangle contains the sheet point (cm).
        ' Walk from last to first so the top-most (later) view wins when stacked.
        For i As Integer = oSheet.DrawingViews.Count To 1 Step -1
            Dim oView As DrawingView = oSheet.DrawingViews.Item(i)
            Dim pos As Point2d = oView.Position
            Dim halfW As Double = oView.Width / 2.0
            Dim halfH As Double = oView.Height / 2.0
            If pt.X >= pos.X - halfW AndAlso pt.X <= pos.X + halfW AndAlso _
               pt.Y >= pos.Y - halfH AndAlso pt.Y <= pos.Y + halfH Then
                Return oView
            End If
        Next
        Return Nothing
    End Function

    Private Function BuildThicknessFormattedText(oPart As PartDocument, oView As DrawingView) As String
        ' Build the LeaderNote string: "x" + value, optionally linked to Thickness.
        Dim thicknessParam As Parameter = Nothing
        ' Inventor Parameter.Value for length is cm. Fallback 1.5 mm = 0.15 cm.
        Dim thicknessInventorCm As Double = 0.15

        Dim cd As ComponentDefinition = oPart.ComponentDefinition
        If TypeOf cd Is SheetMetalComponentDefinition Then
            ' Sheet metal: Thickness is a first-class property (always present).
            thicknessParam = CType(cd, SheetMetalComponentDefinition).Thickness
            thicknessInventorCm = thicknessParam.Value
        Else
            ' Regular part: only if a UserParameter named "Thickness" exists.
            Try
                thicknessParam = oPart.ComponentDefinition.Parameters.UserParameters.Item("Thickness")
                thicknessInventorCm = thicknessParam.Value
            Catch
                ' Keep fallback 0.15 cm; thicknessParam stays Nothing -> plain text.
            End Try
        End If

        ' Display mm = cm * 10; format uses NumberFormat + decimal comma.
        Dim displayValue As String = FormatThicknessForDrawing(thicknessInventorCm * 10.0)

        If thicknessParam Is Nothing Then
            ' No model link - static text only (will not update if thickness changes).
            Return "x" & displayValue
        End If

        ' ComponentIdentifier: path Inventor uses to resolve the parameter later.
        Dim componentId As String = oPart.FullFileName
        If String.IsNullOrEmpty(componentId) Then
            ' Unsaved part - fall back to the view descriptor document name.
            componentId = oView.ReferencedDocumentDescriptor.FullDocumentName
        End If

        ' Linked FormattedText XML: Inventor refreshes the number when Thickness changes.
        ' Precision='3' = up to 3 digits after decimal in the linked display.
        Return "x<Parameter Resolved='True' ComponentIdentifier='" & EscapeXmlAttribute(componentId) & _
               "' Name='" & EscapeXmlAttribute(thicknessParam.Name) & _
               "' Precision='3'>" & displayValue & "</Parameter>"
    End Function

    Private Function EscapeXmlAttribute(value As String) As String
        ' FormattedText attributes use single quotes - escape ' inside paths/names.
        If String.IsNullOrEmpty(value) Then Return ""
        Return value.Replace("'"c, "&apos;")
    End Function

    Private Function FormatThicknessForDrawing(valueMm As Double) As String
        ' 1) Format with .NET pattern from Main (CfgThicknessNumberFormat).
        ' 2) Replace "." with "," for Polish drawing convention.
        Dim fmt As String = NumberFormat
        If String.IsNullOrEmpty(fmt) Then fmt = "0.###"
        Return valueMm.ToString(fmt, Globalization.CultureInfo.InvariantCulture).Replace("."c, ","c)
    End Function
End Class
'==============================================================================
' iLogic_GruboscWskaznik.vb - Autodesk Inventor iLogic rule (single file)
' Thickness callout (leader note) on a drawing view.
'
' WHAT THIS RULE DOES
'   On the active Drawing (.idw / .dwg):
'     1) Click a view that references a Part (.ipt)
'        -> arrow tip = click point, text = thickness "x..."
'     2) Move mouse for live preview, click again to place the text
'   Sheet-metal Part: reads SheetMetal Thickness (linked FormattedText).
'   Regular Part: tries UserParameter named "Thickness".
'   If no parameter: plain text "x" + fallback 1.5 mm (not linked to model).
'   Esc cancels and removes the preview leader.
'
' HOW TO USE
'   1) Optional: edit CfgThicknessNumberFormat below (display digits).
'   2) Inventor: External Rules -> Add Rule -> select this .vb / .iLogicVb
'      (prefer local disk, not W:\ / OneDrive - same tip as Save.iLogicVb).
'   3) Open a Drawing, run the rule, click view then text position (Esc = cancel).
'
' CONFIG (Cfg*) - change Return value only:
'   CfgThicknessNumberFormat
'
' NOTES FOR BEGINNERS
'   - iLogic = VB.NET (not VBA). Sub Main must come first.
'   - Do NOT put Dim / Const / Shared above Main - iLogic will not compile.
'     The one setting is a Function that Returns a value (same idea as Save.iLogicVb).
'   - ThisApplication is given by iLogic.
'   - Inventor database length = cm. Drawing notes / Point2d are in cm.
'     Thickness DISPLAY is in mm (cm * 10). Decimal comma for PL drawings.
'   - References: System.Windows.Forms (DoEvents loop while picking).
'   - Interaction logic lives in Class TwoPointPicker (fields OK inside Class).
'   - Class cannot call module-level Cfg* Functions (iLogic: "object reference").
'     Main reads Cfg* and assigns NumberFormat on the picker instance.
'   - All MsgBox / StatusBar text is English (ASCII-safe).
'==============================================================================

'===== ENTRY ==================================================================

Sub Main()
    ' ThisApplication = Inventor session provided by iLogic.
    Dim invApp As Inventor.Application = ThisApplication

    ' This tool works only on drawings - cast fails for Part / Assembly.
    Dim oDrawDoc As DrawingDocument = TryCast(invApp.ActiveDocument, DrawingDocument)
    If oDrawDoc Is Nothing Then
        MsgBox("Open a drawing document (.idw/.dwg).", MsgBoxStyle.Exclamation, "Thickness indicator")
        Return
    End If

    ' TwoPointPicker: interactive click-1 (view+arrow) / click-2 (text position).
    Dim picker As New TwoPointPicker()
    picker.DrawDoc = oDrawDoc
    ' Pass Cfg into Class (Class must not call module Functions directly).
    picker.NumberFormat = CfgThicknessNumberFormat()

    ' False = user cancelled (Esc) or clicks incomplete - leave drawing unchanged.
    If Not picker.Run(invApp) Then Return

    ' Refresh sheet after the final leader note is placed.
    oDrawDoc.Update()
End Sub

'----- SETTINGS (edit Return value) -------------------------------------------
' Only number display format is configurable. Everything else is fixed in code.
' Implemented as Function: iLogic forbids module-level Const/fields
' (same pattern as Save.iLogicVb / ObliczeniaBlach.ilogicVb).

Function CfgThicknessNumberFormat() As String
    ' .NET format for the thickness number (InvariantCulture first, then ",").
    ' "0.###" = up to 3 decimals, trailing zeros trimmed (e.g. 1.5 -> "1,5").
    ' Change Return, save the rule, run again.
    Return "0.###"
End Function

'===== PICKER CLASS ===========================================================
' Owns InteractionEvents + MouseEvents for the two-click workflow.
' Public fields are filled for Main / callers; Private fields are session state.

Class TwoPointPicker
    ' Inventor Application used to create TransientGeometry / InteractionEvents.
    Private _app As Inventor.Application
    ' Interaction session (status bar text, Start/Stop, Esc -> OnTerminate).
    Private WithEvents _interact As InteractionEvents
    ' Mouse click + move while interaction is running.
    Private WithEvents _mouse As MouseEvents
    ' 1 = waiting for view/arrow click; 2 = waiting for text position click.
    Private _step As Integer
    ' True when Esc, second click, or terminate ends the DoEvents loop.
    Private _done As Boolean
    ' FormattedText built on click 1 (plain "x..." or linked <Parameter> XML).
    Private _noteText As String
    ' Temporary LeaderNote shown during move; kept as final note on click 2.
    Private _previewLeader As LeaderNote
    ' Last preview text position (cm) - used to ignore tiny mouse moves.
    Private _lastPreviewX As Double
    Private _lastPreviewY As Double
    ' False until the first preview leader exists.
    Private _hasPreviewPos As Boolean

    ' Drawing that receives the LeaderNote (set from Main).
    Public DrawDoc As DrawingDocument
    ' View chosen on click 1 (must reference a Part).
    Public SelectedView As DrawingView
    ' Text position (click 2) - sheet Point2d in cm.
    Public Point1 As Point2d
    ' Arrow tip (click 1) - sheet Point2d in cm.
    Public Point2 As Point2d
    ' Set from Main via CfgThicknessNumberFormat() - do not call Cfg* from Class.
    Public NumberFormat As String

    Public Function Run(app As Inventor.Application) As Boolean
        ' Start interactive pick. Returns True only if view + both points exist.
        _app = app
        _step = 1
        _done = False
        _hasPreviewPos = False

        ' CreateInteractionEvents: Inventor UI mode for custom mouse handling.
        _interact = _app.CommandManager.CreateInteractionEvents()
        _interact.StatusBarText = "Click 1/2: view and arrow tip (Esc = cancel)"
        _mouse = _interact.MouseEvents
        ' No snap-to-geometry inference - use raw model/sheet click coordinates.
        _mouse.PointInferenceEnabled = False
        ' Need move events for live leader preview in step 2.
        _mouse.MouseMoveEnabled = True
        _interact.Start()

        ' Keep the rule thread pumping Windows messages until _done.
        ' Without DoEvents the UI would freeze and clicks would not arrive.
        Do While Not _done
            System.Windows.Forms.Application.DoEvents()
        Loop

        _interact.Stop()
        ' Success only when all three results are set (Esc clears preview, leaves nulls).
        Return SelectedView IsNot Nothing AndAlso Point1 IsNot Nothing AndAlso Point2 IsNot Nothing
    End Function

    Private Sub _mouse_OnMouseClick(Button As MouseButtonEnum, ShiftKeys As ShiftStateEnum, ModelPosition As Inventor.Point, ViewPosition As Point2d, ByVal InvView As Inventor.View) Handles _mouse.OnMouseClick
        ' Only left button advances the workflow.
        If Button <> MouseButtonEnum.kLeftMouseButton Then Return

        ' ModelPosition is sheet space for drawing interaction (X/Y in cm).
        Dim pt As Point2d = _app.TransientGeometry.CreatePoint2d(ModelPosition.X, ModelPosition.Y)

        If _step = 1 Then
            ' ----- CLICK 1: pick view + arrow tip, build note text -----
            Dim oView As DrawingView = FindViewAtPoint(DrawDoc.ActiveSheet, pt)
            If oView Is Nothing Then
                MsgBox("Click inside a drawing view.", MsgBoxStyle.Exclamation, "Thickness indicator")
                Return
            End If

            ' View must point at a Part document (.ipt), not Assembly / Drawing.
            Dim oModel As Inventor.Document = oView.ReferencedDocumentDescriptor.ReferencedDocument
            If oModel Is Nothing OrElse oModel.DocumentType <> DocumentTypeEnum.kPartDocumentObject Then
                MsgBox("View must reference a part (.ipt).", MsgBoxStyle.Exclamation, "Thickness indicator")
                Return
            End If

            SelectedView = oView
            Point2 = pt
            _noteText = BuildThicknessFormattedText(CType(oModel, PartDocument), oView)
            _step = 2
            _interact.StatusBarText = "Click 2/2: text position (Esc = cancel)"
            ' First preview: text and arrow at the same point until mouse moves.
            ShowPreview(pt)
        Else
            ' ----- CLICK 2: lock text position; keep preview as the real note -----
            Point1 = pt
            ShowPreview(pt)
            ' Drop our reference without Delete - note stays on the sheet.
            _previewLeader = Nothing
            _done = True
        End If
    End Sub

    Private Sub _mouse_OnMouseMove(Button As MouseButtonEnum, ShiftKeys As ShiftStateEnum, ModelPosition As Inventor.Point, ViewPosition As Point2d, ByVal InvView As Inventor.View) Handles _mouse.OnMouseMove
        ' Live preview only after click 1 (arrow tip Point2 already known).
        If _step <> 2 OrElse Point2 Is Nothing Then Return
        ShowPreview(_app.TransientGeometry.CreatePoint2d(ModelPosition.X, ModelPosition.Y))
    End Sub

    Private Sub _interact_OnTerminate() Handles _interact.OnTerminate
        ' Esc / cancel / interaction ended before click 2.
        ' If we never finished, remove the temporary preview leader.
        If Not _done Then ClearPreview()
        _done = True
    End Sub

    Private Sub ShowPreview(textPt As Point2d)
        ' Rebuild LeaderNote at textPt -> Point2 (arrow).
        ' Skip moves smaller than 0.05 cm to avoid delete/add every pixel.
        If _hasPreviewPos AndAlso _
           Math.Abs(_lastPreviewX - textPt.X) < 0.05 AndAlso _
           Math.Abs(_lastPreviewY - textPt.Y) < 0.05 Then Return

        ClearPreview()

        ' LeaderNotes.Add expects: first point = text, last point = arrow tip.
        Dim oLeaderPts As ObjectCollection = _app.TransientObjects.CreateObjectCollection
        oLeaderPts.Add(textPt)
        oLeaderPts.Add(Point2)
        _previewLeader = DrawDoc.ActiveSheet.DrawingNotes.LeaderNotes.Add(oLeaderPts, "")
        ' FormattedText can be plain string or Inventor XML with <Parameter>.
        _previewLeader.FormattedText = _noteText
        ' Small dot arrowhead (less visual clutter than a filled arrow).
        _previewLeader.Leader.ArrowheadType = ArrowheadTypeEnum.kSmallDotArrowheadType

        _lastPreviewX = textPt.X
        _lastPreviewY = textPt.Y
        _hasPreviewPos = True
        DrawDoc.Update()
    End Sub

    Private Sub ClearPreview()
        ' Delete temporary leader if it still exists on the sheet.
        If _previewLeader Is Nothing Then Return
        Try
            _previewLeader.Delete()
        Catch
            ' Ignore if already deleted by the user / Inventor.
        End Try
        _previewLeader = Nothing
        _hasPreviewPos = False
    End Sub

    Private Function FindViewAtPoint(oSheet As Sheet, pt As Point2d) As DrawingView
        ' Hit-test: which DrawingView rectangle contains the sheet point (cm).
        ' Walk from last to first so the top-most (later) view wins when stacked.
        For i As Integer = oSheet.DrawingViews.Count To 1 Step -1
            Dim oView As DrawingView = oSheet.DrawingViews.Item(i)
            Dim pos As Point2d = oView.Position
            Dim halfW As Double = oView.Width / 2.0
            Dim halfH As Double = oView.Height / 2.0
            If pt.X >= pos.X - halfW AndAlso pt.X <= pos.X + halfW AndAlso _
               pt.Y >= pos.Y - halfH AndAlso pt.Y <= pos.Y + halfH Then
                Return oView
            End If
        Next
        Return Nothing
    End Function

    Private Function BuildThicknessFormattedText(oPart As PartDocument, oView As DrawingView) As String
        ' Build the LeaderNote string: "x" + value, optionally linked to Thickness.
        Dim thicknessParam As Parameter = Nothing
        ' Inventor Parameter.Value for length is cm. Fallback 1.5 mm = 0.15 cm.
        Dim thicknessInventorCm As Double = 0.15

        Dim cd As ComponentDefinition = oPart.ComponentDefinition
        If TypeOf cd Is SheetMetalComponentDefinition Then
            ' Sheet metal: Thickness is a first-class property (always present).
            thicknessParam = CType(cd, SheetMetalComponentDefinition).Thickness
            thicknessInventorCm = thicknessParam.Value
        Else
            ' Regular part: only if a UserParameter named "Thickness" exists.
            Try
                thicknessParam = oPart.ComponentDefinition.Parameters.UserParameters.Item("Thickness")
                thicknessInventorCm = thicknessParam.Value
            Catch
                ' Keep fallback 0.15 cm; thicknessParam stays Nothing -> plain text.
            End Try
        End If

        ' Display mm = cm * 10; format uses NumberFormat + decimal comma.
        Dim displayValue As String = FormatThicknessForDrawing(thicknessInventorCm * 10.0)

        If thicknessParam Is Nothing Then
            ' No model link - static text only (will not update if thickness changes).
            Return "x" & displayValue
        End If

        ' ComponentIdentifier: path Inventor uses to resolve the parameter later.
        Dim componentId As String = oPart.FullFileName
        If String.IsNullOrEmpty(componentId) Then
            ' Unsaved part - fall back to the view descriptor document name.
            componentId = oView.ReferencedDocumentDescriptor.FullDocumentName
        End If

        ' Linked FormattedText XML: Inventor refreshes the number when Thickness changes.
        ' Precision='3' = up to 3 digits after decimal in the linked display.
        Return "x<Parameter Resolved='True' ComponentIdentifier='" & EscapeXmlAttribute(componentId) & _
               "' Name='" & EscapeXmlAttribute(thicknessParam.Name) & _
               "' Precision='3'>" & displayValue & "</Parameter>"
    End Function

    Private Function EscapeXmlAttribute(value As String) As String
        ' FormattedText attributes use single quotes - escape ' inside paths/names.
        If String.IsNullOrEmpty(value) Then Return ""
        Return value.Replace("'"c, "&apos;")
    End Function

    Private Function FormatThicknessForDrawing(valueMm As Double) As String
        ' 1) Format with .NET pattern from Main (CfgThicknessNumberFormat).
        ' 2) Replace "." with "," for Polish drawing convention.
        Dim fmt As String = NumberFormat
        If String.IsNullOrEmpty(fmt) Then fmt = "0.###"
        Return valueMm.ToString(fmt, Globalization.CultureInfo.InvariantCulture).Replace("."c, ","c)
    End Function
End Class

Wszystkie makra InventorNapisz o tym makrze