20/10 VI: Tôn vinh những người phụ nữ tuyệt vời || EN: Celebrating extraordinary women VI: C4I chúc bạn một Ngày Phụ nữ Việt Nam hạnh phúc và trọn vẹn yêu thương. || EN: C4I wishes you a joyful Vietnamese Women’s Day filled with love. WITH LOVE · C4I

Inventor iLogic – Special Plate Mass (BUCKLE / CHECKERED / PERFORATED)

Inventor iLogic - Special Plate Mass

Hình ảnh kết quả

Tiếp tục câu chuyện còn chưa xong... có chút thời gian là hoàn thiện tool ngay thôi á mà 😄

Mình thực hiện những điều này dựa theo kinh nghiệm sử dụng thực tế và AI cơ bản.

  • Ghi nhớ thuộc tính của file
    → Chạy lại lệnh sẽ có các giá trị đã được thiết lập trước đó.
  • Thêm tùy chọn ghi kết quả
    → Weight vào Custom Properties.
    → Override Physical Mass.
  • Giao diện tùy chọn đơn giản
    → Chọn Solid, chọn Plate Type, Apply và Calculate.
  • Mở rộng thêm các giá trị khác
    → BUCKLE, CHECKERED, PERFORATED, GRATING...
    → Có thể tự thêm Plate Type mới.
  • Có cảnh báo nếu lựa chọn chưa chính xác
    → Kiểm tra Thickness của Solid trước khi Apply.
  • Hỗ trợ Multi-Solid Part
    → Mỗi Solid Body có thể sử dụng một loại Plate khác nhau.
→ Nói chung với bài toán đơn giản thế này, thao tác càng ít bước thì càng dễ tiếp cận với người dùng.
Vì bản thân Inventor đã có rất nhiều lệnh rồi 😄

Có bạn nào nhờ AI để viết lại tool hôm rồi mình share chưa nhỉ???

iLogic Rule

Copy Rule bên dưới và chạy trong Inventor Part (*.ipt).

VB.NET · Autodesk Inventor iLogic
AddReference "System.Windows.Forms"
AddReference "System.Drawing"
AddReference "System.Private.Windows.Core"
Imports System
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Windows.Forms
Imports Inventor
'========================================================
' SPECIAL PLATE MASS - PROFESSIONAL MODELESS FORM
' Inventor 2027 / iLogic   |   Run only in a Part (.ipt)
'========================================================


Sub Main()
    If ThisApplication.ActiveDocument Is Nothing OrElse ThisApplication.ActiveDocument.DocumentType <> DocumentTypeEnum.kPartDocumentObject Then
        MessageBox.Show("Open a Part document (.ipt) before running this rule.", "Special Plate Mass")
        Return
    End If
    If SpecialPlateForm.Current IsNot Nothing AndAlso Not SpecialPlateForm.Current.IsDisposed Then
        SpecialPlateForm.Current.Activate()
        Return
    End If
    SpecialPlateForm.Current = New SpecialPlateForm(CType(ThisApplication.ActiveDocument, PartDocument), ThisApplication)
    SpecialPlateForm.Current.Show()  'Modeless: Inventor remains fully usable.
End Sub

Public Class SpecialPlateForm
    Inherits System.Windows.Forms.Form

    Public Shared Current As SpecialPlateForm
    'Keep the previous simple-rule set name so existing Solid assignments continue to work.
    Private Const DataSetName As String = "C4I_SpecialPlate_Simple"
    Private ReadOnly doc As PartDocument
    Private ReadOnly invApp As Inventor.Application
    Private ReadOnly bodies As New List(Of SurfaceBody)
    Private ReadOnly types As New List(Of PlateType)
    Private changingRows As Boolean = False
    Private formReady As Boolean = False

    Private ReadOnly bodyView As New System.Windows.Forms.ListView
    Private ReadOnly typeBox As New System.Windows.Forms.ComboBox
    Private ReadOnly nameBox As New System.Windows.Forms.TextBox
    Private ReadOnly thicknessBox As New System.Windows.Forms.TextBox
    Private ReadOnly rateBox As New System.Windows.Forms.TextBox
    Private ReadOnly preview As New System.Windows.Forms.PictureBox
    Private ReadOnly statusLabel As New System.Windows.Forms.Label
    Private ReadOnly totalLabel As New System.Windows.Forms.Label
    Private ReadOnly detailLabel As New System.Windows.Forms.Label
    Private ReadOnly customWeightCheck As New System.Windows.Forms.CheckBox
    Private ReadOnly overrideMassCheck As New System.Windows.Forms.CheckBox
    Private ReadOnly applyButton As New System.Windows.Forms.Button

    Public Sub New(partDoc As PartDocument, app As Inventor.Application)
        doc = partDoc : invApp = app
        Text = "Special Plate Mass"
        'A compact, fixed tool window leaves the largest possible Inventor viewport free.
        ClientSize = New System.Drawing.Size(735, 500)
        StartPosition = FormStartPosition.Manual
        Dim workArea As System.Drawing.Rectangle = Screen.PrimaryScreen.WorkingArea
        Location = New System.Drawing.Point(Math.Max(workArea.Left, workArea.Right - 750), Math.Max(workArea.Top, workArea.Top + 80))
        FormBorderStyle = FormBorderStyle.FixedDialog
        MaximizeBox = False
        MinimizeBox = False
        HelpButton = True
        TopMost = True
        ShowInTaskbar = False
        BackColor = System.Drawing.Color.WhiteSmoke

        Dim title As New System.Windows.Forms.Label With {.Left = 12, .Top = 8, .Width = 700, .Height = 24, .Text = "SPECIAL PLATE MASS", .Font = New System.Drawing.Font("Segoe UI", 12, System.Drawing.FontStyle.Bold), .ForeColor = System.Drawing.Color.FromArgb(31, 78, 121)}
        Dim subtitle As New System.Windows.Forms.Label With {.Left = 13, .Top = 31, .Width = 700, .Height = 17, .Text = "Tick bodies, select a type, then apply. The panel stays visible while you work in Inventor.", .ForeColor = System.Drawing.Color.DimGray}

        Dim solidGroup As New System.Windows.Forms.GroupBox With {.Left = 12, .Top = 54, .Width = 435, .Height = 275, .Text = "Solid bodies"}
        bodyView.Left = 9 : bodyView.Top = 22 : bodyView.Width = 417 : bodyView.Height = 185
        bodyView.View = System.Windows.Forms.View.Details : bodyView.CheckBoxes = True : bodyView.FullRowSelect = True : bodyView.GridLines = True : bodyView.MultiSelect = False : bodyView.HideSelection = False
        bodyView.Columns.Add("Solid", 58) : bodyView.Columns.Add("Assigned type", 105) : bodyView.Columns.Add("Thk.", 43) : bodyView.Columns.Add("kg/m²", 48) : bodyView.Columns.Add("Mass", 68) : bodyView.Columns.Add("Check", 65)
        Dim useSelection As New System.Windows.Forms.Button With {.Left = 9, .Top = 220, .Width = 135, .Height = 26, .Text = "Use selection"}
        Dim clearSelection As New System.Windows.Forms.Button With {.Left = 153, .Top = 220, .Width = 90, .Height = 26, .Text = "Clear ticks"}
        Dim reloadButton As New System.Windows.Forms.Button With {.Left = 252, .Top = 220, .Width = 105, .Height = 26, .Text = "Refresh"}
        solidGroup.Controls.AddRange(New System.Windows.Forms.Control() {bodyView, useSelection, clearSelection, reloadButton})

        Dim materialGroup As New System.Windows.Forms.GroupBox With {.Left = 457, .Top = 54, .Width = 266, .Height = 275, .Text = "Plate configuration"}
        Dim typeLabel As New System.Windows.Forms.Label With {.Left = 11, .Top = 21, .Width = 240, .Height = 18, .Text = "Plate type"}
        typeBox.Left = 11 : typeBox.Top = 46 : typeBox.Width = 243 : typeBox.DropDownStyle = ComboBoxStyle.DropDownList
        Dim nLabel As New System.Windows.Forms.Label With {.Left = 11, .Top = 76, .Width = 240, .Height = 18, .Text = "Custom name (only when adding a type)"}
        nameBox.Left = 11 : nameBox.Top = 99 : nameBox.Width = 243
        Dim thLabel As New System.Windows.Forms.Label With {.Left = 11, .Top = 130, .Width = 110, .Height = 18, .Text = "Thickness (mm)"}
        Dim rateLabel As New System.Windows.Forms.Label With {.Left = 132, .Top = 130, .Width = 120, .Height = 18, .Text = "Weight (kg/m²)"}
        thicknessBox.Left = 11 : thicknessBox.Top = 152 : thicknessBox.Width = 110
        rateBox.Left = 132 : rateBox.Top = 152 : rateBox.Width = 122
        Dim addType As New System.Windows.Forms.Button With {.Left = 11, .Top = 182, .Width = 243, .Height = 26, .Text = "Add custom plate type"}
        preview.Left = 11 : preview.Top = 219 : preview.Width = 70 : preview.Height = 43 : preview.BackColor = System.Drawing.Color.White : preview.BorderStyle = BorderStyle.FixedSingle
        Dim previewText As New System.Windows.Forms.Label With {.Left = 91, .Top = 217, .Width = 160, .Height = 48, .Text = "Visual guide. Thickness check uses the Solid's volume and surface area.", .ForeColor = System.Drawing.Color.DimGray}
        materialGroup.Controls.AddRange(New System.Windows.Forms.Control() {typeLabel, typeBox, nLabel, nameBox, thLabel, rateLabel, thicknessBox, rateBox, addType, preview, previewText})

        statusLabel.Left = 12 : statusLabel.Top = 340 : statusLabel.Width = 711 : statusLabel.Height = 34 : statusLabel.BorderStyle = BorderStyle.FixedSingle : statusLabel.Padding = New Padding(8, 7, 8, 0)
        statusLabel.ForeColor = System.Drawing.Color.FromArgb(31, 78, 121) : statusLabel.Text = "Select bodies, then choose a plate type."

        Dim actions As New System.Windows.Forms.GroupBox With {.Left = 12, .Top = 386, .Width = 435, .Height = 101, .Text = "Actions"}
        applyButton.Left = 10 : applyButton.Top = 21 : applyButton.Width = 128 : applyButton.Height = 28 : applyButton.Text = "Apply to ticked"
        Dim removeButton As New System.Windows.Forms.Button With {.Left = 147, .Top = 21, .Width = 120, .Height = 28, .Text = "Remove setting"}
        Dim updateButton As New System.Windows.Forms.Button With {.Left = 276, .Top = 21, .Width = 148, .Height = 28, .Text = "Calculate and write"}
        customWeightCheck.Left = 10 : customWeightCheck.Top = 62 : customWeightCheck.Width = 210 : customWeightCheck.Text = "Write Custom Property: Weight"
        overrideMassCheck.Left = 225 : overrideMassCheck.Top = 62 : overrideMassCheck.Width = 195 : overrideMassCheck.Text = "Override Physical Mass"
        actions.Controls.AddRange(New System.Windows.Forms.Control() {applyButton, removeButton, updateButton, customWeightCheck, overrideMassCheck})

        Dim totalGroup As New System.Windows.Forms.GroupBox With {.Left = 457, .Top = 386, .Width = 266, .Height = 101, .Text = "FINAL WEIGHT"}
        totalLabel.Left = 8 : totalLabel.Top = 19 : totalLabel.Width = 250 : totalLabel.Height = 28 : totalLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter
        totalLabel.Font = New System.Drawing.Font("Segoe UI", 15, System.Drawing.FontStyle.Bold) : totalLabel.ForeColor = System.Drawing.Color.Crimson
        detailLabel.Left = 8 : detailLabel.Top = 52 : detailLabel.Width = 250 : detailLabel.Height = 37 : detailLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter : detailLabel.ForeColor = System.Drawing.Color.DimGray
        totalGroup.Controls.AddRange(New System.Windows.Forms.Control() {totalLabel, detailLabel})
        Controls.AddRange(New System.Windows.Forms.Control() {title, subtitle, solidGroup, materialGroup, statusLabel, actions, totalGroup})

        AddHandler bodyView.ItemChecked, AddressOf BodyChecked
        AddHandler bodyView.SelectedIndexChanged, AddressOf BodyClicked
        AddHandler typeBox.SelectedIndexChanged, AddressOf TypeChanged
        AddHandler thicknessBox.TextChanged, AddressOf ValuesChanged
        AddHandler rateBox.TextChanged, AddressOf ValuesChanged
        AddHandler useSelection.Click, AddressOf UseInventorSelection
        AddHandler clearSelection.Click, AddressOf ClearBodyTicks
        AddHandler reloadButton.Click, AddressOf ReloadBodies
        AddHandler applyButton.Click, AddressOf ApplyToBodies
        AddHandler removeButton.Click, AddressOf RemoveSettings
        AddHandler updateButton.Click, AddressOf CalculateAndWrite
        AddHandler addType.Click, AddressOf AddCustomType
        AddHandler overrideMassCheck.CheckedChanged, AddressOf OverrideChanged
        AddHandler HelpButtonClicked, AddressOf ShowAbout
        AddHandler preview.Paint, AddressOf DrawPreview
        AddHandler FormClosed, Sub(sender As Object, e As FormClosedEventArgs) Current = Nothing

        AddBuiltInTypes()
        LoadCustomTypes()
        If typeBox.Items.Count > 0 Then typeBox.SelectedIndex = 0
        customWeightCheck.Checked = ReadDoc("WriteCustom", "False") = "True"
        overrideMassCheck.Checked = ReadDoc("OverrideMass", "False") = "True"
        LoadBodies()
        formReady = True
    End Sub

    Private Sub AddBuiltInTypes()
        AddType("BUCKLE 3", 3, 25.5, False) : AddType("BUCKLE 4", 4, 33.4, False) : AddType("BUCKLE 5", 5, 41.2, False) : AddType("BUCKLE 6", 6, 49.1, False) : AddType("BUCKLE 8", 8, 64.8, False) : AddType("BUCKLE 10", 10, 80.5, False)
        AddType("CHECKERED 3", 3, 27.5, False) : AddType("CHECKERED 4", 4, 35.4, False) : AddType("CHECKERED 5", 5, 43.2, False) : AddType("CHECKERED 6", 6, 51.1, False) : AddType("CHECKERED 8", 8, 66.8, False) : AddType("CHECKERED 10", 10, 82.5, False)
        AddType("PERFORATED 3-6-10", 3, 14.5, False) : AddType("PERFORATED 3-10-17", 3, 14.5, False) : AddType("PERFORATED 3-15-24", 3, 14.8, False)
        AddType("PERFORATED 5-8-14", 5, 24, False) : AddType("PERFORATED 5-12-18", 5, 25, False) : AddType("PERFORATED 5-15-24", 5, 24, False)
        AddType("PERFORATED 8-12-20", 8, 41.5, False) : AddType("PERFORATED 8-15-24", 8, 38.5, False) : AddType("PERFORATED 8-25-32", 8, 39, False)
        AddType("GRATING 30", 30, 27.6, False)
    End Sub

    Private Sub AddType(n As String, t As Double, r As Double, isCustom As Boolean)
        Dim p As New PlateType(n, t, r, isCustom)
        types.Add(p) : typeBox.Items.Add(p)
    End Sub

    Private Sub LoadCustomTypes()
        Dim saved As String = ReadDoc("CustomTypes", "")
        If saved = "" Then Return
        For Each row As String In saved.Split(";"c)
            Dim d() As String = Row.Split("|"c)
            If d.Length = 3 Then
                Dim t As Double, r As Double
                If Double.TryParse(d(1), NumberStyles.Float, CultureInfo.InvariantCulture, t) AndAlso Double.TryParse(d(2), NumberStyles.Float, CultureInfo.InvariantCulture, r) AndAlso t > 0 AndAlso r > 0 Then AddType(d(0), t, r, True)
            End If
        Next
    End Sub

    Private Sub SaveCustomTypes()
        Dim rows As New List(Of String)
        For Each p As PlateType In types
            If p.IsCustom Then rows.Add(p.Name & "|" & p.Thickness.ToString("R", CultureInfo.InvariantCulture) & "|" & p.Rate.ToString("R", CultureInfo.InvariantCulture))
        Next
        WriteDoc("CustomTypes", String.Join(";", rows.ToArray()))
    End Sub

    Private Sub LoadBodies()
        bodies.Clear() : bodyView.Items.Clear()
        For Each body As SurfaceBody In doc.ComponentDefinition.SurfaceBodies
            If body.IsSolid Then bodies.Add(body)
        Next
        RefreshRows()
        UseInventorSelection(Nothing, Nothing)
        UpdateResult()
    End Sub

    Private Sub RefreshRows()
        Dim checkedBodies As New List(Of SurfaceBody)
        For Each row As System.Windows.Forms.ListViewItem In bodyView.Items
            If Row.Checked Then checkedBodies.Add(CType(Row.Tag, SurfaceBody))
        Next
        changingRows = True
        Try
            bodyView.Items.Clear()
            For Each body As SurfaceBody In bodies
                Dim row As New System.Windows.Forms.ListViewItem(body.Name)
                row.Tag = body
                Dim p As PlateType = ReadBody(body)
                If p Is Nothing Then
                    row.SubItems.Add("CAD mass") : row.SubItems.Add("-") : row.SubItems.Add("-") : row.SubItems.Add("-") : row.SubItems.Add("-")
                Else
                    row.SubItems.Add(p.Name) : row.SubItems.Add(p.Thickness.ToString("0.###") & " mm") : row.SubItems.Add(p.Rate.ToString("0.###")) : row.SubItems.Add(PlateMass(body, p).ToString("0.###") & " kg")
                    Dim measured As Double = 0
                    Dim checkResult As String = ThicknessOK(body, p.Thickness, measured)
                    row.SubItems.Add(checkResult)
                    If checkResult = "CHECK" Then
                        row.BackColor = System.Drawing.Color.MistyRose
                        row.ForeColor = System.Drawing.Color.Firebrick
                    End If
                End If
                row.Checked = checkedBodies.Contains(body)
                bodyView.Items.Add(row)
            Next
        Finally
            changingRows = False
        End Try
    End Sub

    Private Sub TypeChanged(sender As Object, e As EventArgs)
        If typeBox.SelectedIndex < 0 Then Return
        Dim p As PlateType = DirectCast(typeBox.SelectedItem, PlateType)
        nameBox.Text = p.Name : thicknessBox.Text = p.Thickness.ToString(CultureInfo.CurrentCulture) : rateBox.Text = p.Rate.ToString(CultureInfo.CurrentCulture)
        preview.Invalidate() : ValidateSelection()
    End Sub

    Private Sub ValuesChanged(sender As Object, e As EventArgs)
        ValidateSelection()
    End Sub

    Private Sub BodyClicked(sender As Object, e As EventArgs)
        If bodyView.SelectedItems.Count = 0 Then Return
        Dim body As SurfaceBody = CType(bodyView.SelectedItems(0).Tag, SurfaceBody)
        doc.SelectSet.Clear() : doc.SelectSet.Select(body)
        Dim p As PlateType = ReadBody(body)
        If p IsNot Nothing Then nameBox.Text = p.Name : thicknessBox.Text = p.Thickness.ToString(CultureInfo.CurrentCulture) : rateBox.Text = p.Rate.ToString(CultureInfo.CurrentCulture)
        ValidateSelection()
    End Sub

    Private Sub BodyChecked(sender As Object, e As ItemCheckedEventArgs)
        If changingRows OrElse Not IsHandleCreated Then Return
        BeginInvoke(New MethodInvoker(AddressOf HighlightTickedBodies))
    End Sub

    Private Sub HighlightTickedBodies()
        doc.SelectSet.Clear()
        For Each row As System.Windows.Forms.ListViewItem In bodyView.Items
            If Row.Checked Then doc.SelectSet.Select(CType(Row.Tag, SurfaceBody))
        Next
        ValidateSelection()
    End Sub

    Private Sub UseInventorSelection(sender As Object, e As EventArgs)
        Dim selected As New List(Of SurfaceBody)
        For Each item As Object In doc.SelectSet
            Dim body As SurfaceBody = TryCast(item, SurfaceBody)
            If body IsNot Nothing Then selected.Add(body)
        Next
        changingRows = True
        Try
            For Each row As System.Windows.Forms.ListViewItem In bodyView.Items
                Row.Checked = selected.Contains(CType(Row.Tag, SurfaceBody))
            Next
        Finally
            changingRows = False
        End Try
        ValidateSelection()
    End Sub

    Private Sub ClearBodyTicks(sender As Object, e As EventArgs)
        If CheckedRows().Count = 0 Then
            ShowStatus("Nothing was ticked. Select bodies in Inventor or tick them in the table.", System.Drawing.Color.DarkOrange)
            Return
        End If
        changingRows = True
        Try
            For Each row As System.Windows.Forms.ListViewItem In bodyView.Items : Row.Checked = False : Next
        Finally
            changingRows = False
        End Try
        doc.SelectSet.Clear()
        ShowStatus("Ticks cleared. No body settings were changed.", System.Drawing.Color.FromArgb(31, 78, 121))
    End Sub

    Private Sub ReloadBodies(sender As Object, e As EventArgs)
        LoadBodies()
        ShowStatus("Body table refreshed from the current Part.", System.Drawing.Color.FromArgb(31, 78, 121))
    End Sub

    Private Function CheckedRows() As List(Of System.Windows.Forms.ListViewItem)
        Dim result As New List(Of System.Windows.Forms.ListViewItem)
        For Each row As System.Windows.Forms.ListViewItem In bodyView.Items
            If Row.Checked Then result.Add(Row)
        Next
        Return result
    End Function

    Private Function ReadInput(ByRef t As Double, ByRef r As Double) As Boolean
        Return Double.TryParse(thicknessBox.Text.Trim().Replace(",", "."), NumberStyles.Float, CultureInfo.InvariantCulture, t) AndAlso t > 0 AndAlso Double.TryParse(rateBox.Text.Trim().Replace(",", "."), NumberStyles.Float, CultureInfo.InvariantCulture, r) AndAlso r > 0
    End Function

    Private Sub ValidateSelection()
        Dim t As Double, r As Double
        Dim rows As List(Of System.Windows.Forms.ListViewItem) = CheckedRows()
        If rows.Count = 0 Then ShowStatus("Tick one or more bodies before applying a plate setting.", System.Drawing.Color.DarkOrange) : applyButton.Enabled = False : Return
        If Not ReadInput(t, r) Then ShowStatus("Enter a positive Thickness and Weight value.", System.Drawing.Color.Crimson) : applyButton.Enabled = False : Return
        Dim warnings As New List(Of String)
        For Each row As System.Windows.Forms.ListViewItem In rows
            Dim actual As Double = 0
            If ThicknessOK(CType(Row.Tag, SurfaceBody), t, actual) = "CHECK" Then warnings.Add(CType(Row.Tag, SurfaceBody).Name & " ≈ " & actual.ToString("0.###") & " mm")
        Next
        If warnings.Count > 0 Then
            ShowStatus("Thickness review: expected " & t.ToString("0.###") & " mm; estimate: " & String.Join(", ", warnings.ToArray()) & ". You may still apply if the geometry is curved or otherwise complex.", System.Drawing.Color.Crimson)
            applyButton.Enabled = True
        Else
            ShowStatus("Ready to apply: " & rows.Count & " selected body/bodies match " & t.ToString("0.###") & " mm.", System.Drawing.Color.FromArgb(0, 110, 65))
            applyButton.Enabled = True
        End If
    End Sub

    Private Function ThicknessOK(body As SurfaceBody, expected As Double, ByRef actual As Double) As String
        'For a thin plate, 2 × Volume / SurfaceArea estimates its wall thickness.
        'Unlike a bounding box, this remains useful when the plate is curved or rotated.
        Dim area As Double = body.MassProperties.Area
        If area <= 0 Then Return "CHECK"
        actual = (2.0 * body.MassProperties.Volume / area) * 10.0   'cm -> mm
        Dim tolerance As Double = Math.Max(0.3, expected * 0.08)
        If Math.Abs(actual - expected) <= tolerance Then Return "OK"
        Return "CHECK"
    End Function

    Private Sub ApplyToBodies(sender As Object, e As EventArgs)
        Dim rows As List(Of System.Windows.Forms.ListViewItem) = CheckedRows()
        If rows.Count = 0 Then ShowStatus("Apply did nothing: tick one or more bodies first.", System.Drawing.Color.DarkOrange) : Return
        Dim t As Double, r As Double
        If Not ReadInput(t, r) Then ShowStatus("Apply did nothing: enter valid Thickness and Weight values.", System.Drawing.Color.Crimson) : Return
        ValidateSelection()
        If Not applyButton.Enabled Then Return
        Dim label As String = nameBox.Text.Trim()
        If label = "" Then label = CType(typeBox.SelectedItem, PlateType).Name
        Dim tr As Transaction = invApp.TransactionManager.StartTransaction(doc, "Apply Special Plate")
        Try
            For Each row As System.Windows.Forms.ListViewItem In rows : WriteBody(CType(Row.Tag, SurfaceBody), New PlateType(label, t, r, False)) : Next
            tr.End()
            RefreshRows() : UpdateResult()
            ShowStatus("Applied " & label & " to " & rows.Count & " body/bodies. Click Calculate and write results to update Weight.", System.Drawing.Color.FromArgb(0, 110, 65))
        Catch ex As Exception
            tr.Abort() : ShowStatus("Apply failed: " & ex.Message, System.Drawing.Color.Crimson)
        End Try
    End Sub

    Private Sub RemoveSettings(sender As Object, e As EventArgs)
        Dim rows As List(Of System.Windows.Forms.ListViewItem) = CheckedRows()
        If rows.Count = 0 Then ShowStatus("Remove did nothing: tick the bodies whose Special Plate setting you want to remove.", System.Drawing.Color.DarkOrange) : Return
        Dim tr As Transaction = invApp.TransactionManager.StartTransaction(doc, "Remove Special Plate")
        Try
            For Each row As System.Windows.Forms.ListViewItem In rows
                Dim sets As AttributeSets = CType(Row.Tag, SurfaceBody).AttributeSets
                If sets.NameIsUsed(DataSetName) Then sets.Item(DataSetName).Delete()
            Next
            tr.End()
            RefreshRows() : UpdateResult()
            ShowStatus("Removed the saved Special Plate setting from " & rows.Count & " body/bodies. Those bodies now use their actual CAD mass.", System.Drawing.Color.FromArgb(31, 78, 121))
        Catch ex As Exception
            tr.Abort() : ShowStatus("Remove failed: " & ex.Message, System.Drawing.Color.Crimson)
        End Try
    End Sub

    Private Sub CalculateAndWrite(sender As Object, e As EventArgs)
        Dim finalMass As Double, cadMass As Double, plateMass As Double
        BuildResult(finalMass, cadMass, plateMass)
        Dim savedWeight As Double = RoundWeight(finalMass)
        Dim tr As Transaction = invApp.TransactionManager.StartTransaction(doc, "Update Special Plate Weight")
        Try
            WriteDoc("WriteCustom", customWeightCheck.Checked.ToString())
            WriteDoc("OverrideMass", overrideMassCheck.Checked.ToString())
            WriteDoc("LastWeight", savedWeight.ToString("R", CultureInfo.InvariantCulture))
            If customWeightCheck.Checked Then WriteWeightProperty(savedWeight)
            If overrideMassCheck.Checked Then
                doc.ComponentDefinition.MassProperties.Mass = savedWeight
            Else
                doc.ComponentDefinition.MassProperties.MassOverridden = False
            End If
            tr.End()
            RefreshInventorPanels()
            UpdateResult()
            If overrideMassCheck.Checked Then
                ShowStatus("Updated. Physical Mass is overridden with the calculated result.", System.Drawing.Color.FromArgb(0, 110, 65))
            Else
                ShowStatus("Updated. Physical Mass override was removed, so Inventor now shows its native CAD mass.", System.Drawing.Color.FromArgb(31, 78, 121))
            End If
        Catch ex As Exception
            tr.Abort() : ShowStatus("Update failed: " & ex.Message, System.Drawing.Color.Crimson)
        End Try
    End Sub

    Private Sub OverrideChanged(sender As Object, e As EventArgs)
        If Not formReady Then Return
        If overrideMassCheck.Checked Then
            ShowStatus("Override Mass is enabled. Click Calculate and write results to apply the current calculated weight.", System.Drawing.Color.FromArgb(31, 78, 121))
            Return
        End If
        Dim tr As Transaction = invApp.TransactionManager.StartTransaction(doc, "Remove Physical Mass Override")
        Try
            doc.ComponentDefinition.MassProperties.MassOverridden = False
            WriteDoc("OverrideMass", "False")
            tr.End()
            RefreshInventorPanels()
            UpdateResult()
            ShowStatus("Physical Mass override removed. Inventor is now showing its native CAD mass.", System.Drawing.Color.FromArgb(31, 78, 121))
        Catch ex As Exception
            tr.Abort()
            ShowStatus("Could not remove Physical Mass override: " & ex.Message, System.Drawing.Color.Crimson)
        End Try
    End Sub

    Private Sub AddCustomType(sender As Object, e As EventArgs)
        Dim n As String = nameBox.Text.Trim()
        Dim t As Double, r As Double
        If n = "" OrElse n.IndexOfAny(New Char() {"|"c, ";"c}) >= 0 Then ShowStatus("Enter a custom type name. Characters | and ; are not allowed.", System.Drawing.Color.Crimson) : Return
        If Not ReadInput(t, r) Then ShowStatus("Enter valid Thickness and Weight values before adding a custom type.", System.Drawing.Color.Crimson) : Return
        For Each p As PlateType In types
            If String.Equals(p.Name, n, StringComparison.OrdinalIgnoreCase) Then ShowStatus("A plate type with this name already exists.", System.Drawing.Color.DarkOrange) : Return
        Next
        Dim tr As Transaction = invApp.TransactionManager.StartTransaction(doc, "Add Special Plate Type")
        Try
            AddType(n, t, r, True) : SaveCustomTypes() : tr.End()
            typeBox.SelectedIndex = typeBox.Items.Count - 1
            ShowStatus("Custom plate type added and stored in this Part document.", System.Drawing.Color.FromArgb(0, 110, 65))
        Catch ex As Exception
            tr.Abort() : ShowStatus("Could not add custom type: " & ex.Message, System.Drawing.Color.Crimson)
        End Try
    End Sub

    Private Sub UpdateResult()
        Dim finalMass As Double, cadMass As Double, plateMass As Double
        Dim assigned As Integer = BuildResult(finalMass, cadMass, plateMass)
        totalLabel.Text = FormatWeight(finalMass) & " kg"
        detailLabel.Text = "Assigned: " & assigned & " body/bodies" & vbCrLf & "CAD solids: " & FormatWeight(cadMass) & " kg   |   Plate: " & FormatWeight(plateMass) & " kg"
    End Sub

    Private Function BuildResult(ByRef finalMass As Double, ByRef cadMass As Double, ByRef specialMass As Double) As Integer
        Dim otherMass As Double = 0, assigned As Integer = 0
        cadMass = 0 : specialMass = 0
        For Each body As SurfaceBody In bodies
            Dim rawMass As Double = body.MassProperties.Mass  'Per-body CAD mass; never uses Part Mass override.
            cadMass += rawMass
            Dim p As PlateType = ReadBody(body)
            If p Is Nothing Then
                otherMass += rawMass
            Else
                specialMass += PlateMass(body, p) : assigned += 1
            End If
        Next
        finalMass = otherMass + specialMass
        Return assigned
    End Function

    Private Function PlateMass(body As SurfaceBody, p As PlateType) As Double
        Return (body.MassProperties.Volume / (p.Thickness * 1000.0)) * p.Rate
    End Function

    Private Function ReadBody(body As SurfaceBody) As PlateType
        Dim sets As AttributeSets = body.AttributeSets
        If Not sets.NameIsUsed(DataSetName) Then Return Nothing
        Dim oneSet As AttributeSet = sets.Item(DataSetName)
        If Not oneSet.NameIsUsed("Config") Then Return Nothing
        Dim d() As String = CStr(oneSet.Item("Config").Value).Split("|"c)
        If d.Length <> 3 Then Return Nothing
        Dim t As Double, r As Double
        If Not Double.TryParse(d(1), NumberStyles.Float, CultureInfo.InvariantCulture, t) OrElse Not Double.TryParse(d(2), NumberStyles.Float, CultureInfo.InvariantCulture, r) OrElse t <= 0 OrElse r <= 0 Then Return Nothing
        Return New PlateType(d(0), t, r, False)
    End Function

    Private Sub WriteBody(body As SurfaceBody, p As PlateType)
        Dim sets As AttributeSets = body.AttributeSets
        Dim oneSet As AttributeSet = If(sets.NameIsUsed(DataSetName), sets.Item(DataSetName), sets.Add(DataSetName))
        Dim value As String = p.Name & "|" & p.Thickness.ToString("R", CultureInfo.InvariantCulture) & "|" & p.Rate.ToString("R", CultureInfo.InvariantCulture)
        If oneSet.NameIsUsed("Config") Then oneSet.Item("Config").Value = value Else oneSet.Add("Config", ValueTypeEnum.kStringType, value)
    End Sub

    Private Function ReadDoc(key As String, fallback As String) As String
        If Not doc.AttributeSets.NameIsUsed(DataSetName) Then Return fallback
        Dim oneSet As AttributeSet = doc.AttributeSets.Item(DataSetName)
        If Not oneSet.NameIsUsed(key) Then Return fallback
        Return CStr(oneSet.Item(key).Value)
    End Function

    Private Sub WriteDoc(key As String, value As String)
        Dim sets As AttributeSets = doc.AttributeSets
        Dim oneSet As AttributeSet = If(sets.NameIsUsed(DataSetName), sets.Item(DataSetName), sets.Add(DataSetName))
        If oneSet.NameIsUsed(key) Then oneSet.Item(key).Value = value Else oneSet.Add(key, ValueTypeEnum.kStringType, value)
    End Sub

    Private Sub WriteWeightProperty(weight As Double)
        Dim props As PropertySet = doc.PropertySets.Item("{D5CDD505-2E9C-101B-9397-08002B2CF9AE}")
        For Each p As Inventor.Property In props
            If String.Equals(p.Name, "Weight", StringComparison.OrdinalIgnoreCase) Then p.Delete() : Exit For
        Next
        props.Add(weight, "Weight")
    End Sub

    Private Function RoundWeight(weight As Double) As Double
        If weight >= 100.0 Then Return Math.Round(weight, 0, MidpointRounding.AwayFromZero)
        If weight >= 10.0 Then Return Math.Round(weight, 1, MidpointRounding.AwayFromZero)
        If weight >= 1.0 Then Return Math.Round(weight, 2, MidpointRounding.AwayFromZero)
        Return Math.Round(weight, 3, MidpointRounding.AwayFromZero)
    End Function

    Private Function FormatWeight(weight As Double) As String
        If weight >= 100.0 Then Return RoundWeight(weight).ToString("0", CultureInfo.CurrentCulture)
        If weight >= 10.0 Then Return RoundWeight(weight).ToString("0.0", CultureInfo.CurrentCulture)
        If weight >= 1.0 Then Return RoundWeight(weight).ToString("0.00", CultureInfo.CurrentCulture)
        Return RoundWeight(weight).ToString("0.000", CultureInfo.CurrentCulture)
    End Function

    Private Sub RefreshInventorPanels()
        'Refresh the document and current view after iProperty/Physical Mass changes.
        'Inventor does not expose a separate API to force-refresh only the docked iProperties panel.
        Try
            doc.Update2(True)
            invApp.ActiveView.Update()
        Catch
            'The saved values remain valid even if the current view cannot be refreshed.
        End Try
    End Sub

    Private Sub ShowAbout(sender As Object, e As EventArgs)
        MessageBox.Show("Special Plate Mass" & vbCrLf & vbCrLf &
                        "Version 1.0" & vbCrLf &
                        "Developed by Le Thanh Dai" & vbCrLf &
                        "Email: dai.lethanh2019@gmail.com", _
                        "About Special Plate Mass", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

    Private Sub ShowStatus(text As String, color As System.Drawing.Color)
        statusLabel.Text = text : statusLabel.ForeColor = color
    End Sub

    Private Sub DrawPreview(sender As Object, e As PaintEventArgs)
        Dim g As System.Drawing.Graphics = e.Graphics
        g.Clear(System.Drawing.Color.White)
        g.ScaleTransform(preview.ClientSize.Width / 105.0F, preview.ClientSize.Height / 93.0F)
        Dim name As String = If(typeBox.SelectedItem Is Nothing, "", CType(typeBox.SelectedItem, PlateType).Name.ToUpperInvariant())
        Dim pen As New System.Drawing.Pen(System.Drawing.Color.FromArgb(31, 78, 121), 2)
        If name.Contains("PERFORATED") Then
            g.DrawRectangle(pen, 12, 12, 78, 68)
            For x As Integer = 24 To 76 Step 18 : For y As Integer = 24 To 66 Step 18 : g.FillEllipse(System.Drawing.Brushes.White, x, y, 8, 8) : g.DrawEllipse(pen, x, y, 8, 8) : Next : Next
        ElseIf name.Contains("GRATING") Then
            For x As Integer = 12 To 92 Step 13 : g.DrawLine(pen, x, 12, x, 80) : Next
            For y As Integer = 17 To 78 Step 15 : g.DrawLine(pen, 10, y, 94, y) : Next
        ElseIf name.Contains("CHECKERED") Then
            g.DrawRectangle(pen, 12, 12, 78, 68)
            For x As Integer = 6 To 82 Step 18 : g.DrawLine(pen, x, 79, x + 35, 13) : Next
        Else
            g.DrawRectangle(pen, 12, 12, 78, 68)
            For y As Integer = 20 To 74 Step 15 : g.DrawArc(pen, 18, y, 65, 12, 180, 180) : Next
        End If
        pen.Dispose()
    End Sub

    Private Class PlateType
        Public ReadOnly Name As String
        Public ReadOnly Thickness As Double
        Public ReadOnly Rate As Double
        Public ReadOnly IsCustom As Boolean
        Public Sub New(n As String, t As Double, r As Double, custom As Boolean)
            Name = n : Thickness = t : Rate = r : IsCustom = custom
        End Sub
        Public Overrides Function ToString() As String
            Return Name
        End Function
    End Class
End Class
#iLogic   #Addins   #VisualStudio   #Inventor   #Mechanical
VI: Chủ đề bài viết || EN: Article Topics
iLogic Rule