자동 품번기호에 대한 다른 접근 방식

AutoBalloon.jpg

메모: 이 기사는 영어로 작성 및 게시되었으며 원본 여기의 번역본입니다.  

 

저는 다른 접근 방식, 즉 대화 또는 수동 조정 없이 깔끔하고 예측 가능한 결과를 생성하는 빠른 단일 클릭 동작에 초점을 맞춘 iLogic 규칙을 작성했습니다.

따라서 일상적인 작업뿐만 아니라 자동화된 프로세스에도 실용적입니다. 기본 제공 Auto Balloon 기능이 API를 통해 노출되지 않으므로, 스크립트 가능한 대체 도구가 이러한 시나리오에서 특히 유용합니다.

생성된 배치는 기본 도구와 동일한 일반적인 아이디어를 따르지만, 품번기호를 부품에 더 가깝게 유지하고 구조화된 결과를 유지하기 위해 몇 가지 작은 조정이 적용됩니다.

개별적인 개선은 미미합니다. 주요 이점은 사용성입니다. 수동 사용과 자동화 모두에 자연스럽게 적합한 신뢰할 수 있는 원클릭 워크플로우입니다.

AutoBalloon2.png

규칙 사용

  1. 코드를 외부 iLogic 규칙에 복사합니다.
  2. (선택 사항) 리본 버튼이나 바로 가기를 추가하여 실행합니다. 오토데스크 가이드:
  3. 규칙을 실행합니다.
  4. 도면 뷰를 선택하고 배치가 완료될 때까지 기다립니다.

참고: 도면 곡선이 많은 도면의 경우 규칙이 후보 모서리를 평가하여 부품별로 깔끔한 부착점을 찾기 때문에 시간이 더 오래 걸릴 수 있습니다.

Public Class ThisRule
            Private Property DistanceFromEdge As Double = 2
            Private Property minimalDistanceBetweenBaloons As Double = 1.4

```
Private Enum Edge
    Top
    Bottom
    Left
    Right
End Enum

Sub Main()
    Dim doc As DrawingDocument = ThisDoc.Document
    Dim sheet As Sheet = doc.ActiveSheet
    Dim view As DrawingView = ThisApplication.CommandManager.Pick(SelectionFilterEnum.kDrawingViewFilter, "Select a drawing view.")

    Dim tagCurves = CollectClosestCurvePerFile(view)
    PlaceBalloons(doc, sheet, view, tagCurves)
End Sub

Private Function CollectClosestCurvePerFile(view As DrawingView) As List(Of TagCurve)
    Dim viewTop = view.Top
    Dim viewBottom = view.Top - view.Height
    Dim viewLeft = view.Left
    Dim viewRight = view.Left + view.Width

    Dim byFile As New Dictionary(Of String, TagCurve)

    For Each curve As DrawingCurve In view.DrawingCurves.Cast(Of DrawingCurve)()
        ' This only works with edges
        Try
   			If TypeOf curve.ModelGeometry IsNot EdgeProxy Then Continue For
        Catch
               Continue For
        End Try
                                          
        Dim tagCurve As New TagCurve(curve)
        If tagCurve.Occ.OccurrencePath.Count > 2 Then Continue For

        ' Closed curves (circles, ellipses) have no midpoint — fall back to the center.
        Dim midPoint As Point2d = curve.MidPoint
        If midPoint Is Nothing Then midPoint = curve.CenterPoint

        tagCurve.DistanceToNearestViewEdge = MinDistanceToViewEdge(midPoint, viewTop, viewBottom, viewLeft, viewRight)

        Dim existing As tagCurve = Nothing
        If Not byFile.TryGetValue(tagCurve.FileName, existing) OrElse tagCurve.DistanceToNearestViewEdge < existing.DistanceToNearestViewEdge Then
            byFile(tagCurve.FileName) = tagCurve
        End If
    Next

    Return byFile.Values.ToList()
End Function

Private Shared Function MinDistanceToViewEdge(midPoint As Point2d, top As Double, bottom As Double, left As Double, right As Double) As Double
    Return Math.Min(
        Math.Min(Math.Abs(top - midPoint.Y), Math.Abs(bottom - midPoint.Y)),
        Math.Min(Math.Abs(left - midPoint.X), Math.Abs(right - midPoint.X))
    )
End Function
       
Private Sub PlaceBalloons(doc As DrawingDocument, sheet As Sheet, view As DrawingView, tagCurves As List(Of TagCurve))
    Dim transientGeom = ThisApplication.TransientGeometry
    Dim balloonDiameter = doc.StylesManager.ActiveStandardStyle.ActiveObjectDefaults.BalloonStyle.BalloonDiameter

    Dim viewTop = view.Top + DistanceFromEdge
    Dim viewBottom = view.Top - view.Height - DistanceFromEdge
    Dim viewLeft = view.Left - DistanceFromEdge
    Dim viewRight = view.Left + view.Width + DistanceFromEdge

    ' Phase 1: assign each curve to its closest edge and compute the desired leader endpoint.
    Dim placements As New List(Of Placement)
    For Each tagCurve As TagCurve In tagCurves
        ' Closed curves (circles, ellipses) have no midpoint — fall back to the center.
        Dim midPoint As Point2d = TagCurve.Curve.MidPoint
        If midPoint Is Nothing Then midPoint = TagCurve.Curve.CenterPoint

        Dim chosenEdge = ChooseClosestEdge(midPoint, viewTop, viewBottom, viewLeft, viewRight)
        Dim leader = ComputeLeaderPoint(chosenEdge, midPoint, viewTop, viewBottom, viewLeft, viewRight, balloonDiameter)
        placements.Add(New Placement With {
            .TagCurve = TagCurve,
            .MidPoint = midPoint,
            .ChosenEdge = chosenEdge,
            .LeaderX = leader.X,
            .LeaderY = leader.Y
        })
    Next

    ' Phase 2: per edge, sort by position along that edge, sweep forward to
    ' enforce minimum spacing, then center each tight cluster so the cluster's
    ' centroid matches the centroid of its desired positions. Order along the
    ' edge is preserved either way, so leaders still don't cross.
    For Each edgeGroup In placements.GroupBy(Function(p) p.ChosenEdge)
        Dim isHorizontal = (edgeGroup.Key = Edge.Top OrElse edgeGroup.Key = Edge.Bottom)
        Dim ordered = If(isHorizontal,
                         edgeGroup.OrderBy(Function(p) p.LeaderX).ToList(),
                         edgeGroup.OrderBy(Function(p) p.LeaderY).ToList())

        Dim desired(ordered.Count - 1) As Double
        For i = 0 To ordered.Count - 1
            desired(i) = If(isHorizontal, ordered(i).LeaderX, ordered(i).LeaderY)
        Next

        Dim actual(ordered.Count - 1) As Double
        Dim lastPlaced As Double = Double.MinValue
        For i = 0 To ordered.Count - 1
            actual(i) = Math.Max(desired(i), lastPlaced + minimalDistanceBetweenBaloons)
            lastPlaced = actual(i)
        Next

        ' Walk forward and close out each cluster (consecutive balloons in tight
        ' contact) by shifting it back so sum(actual) = sum(desired) within the
        ' cluster.
        Dim clusterStart = 0
        For i = 1 To ordered.Count
            If i = ordered.Count OrElse actual(i) - actual(i - 1) > minimalDistanceBetweenBaloons + 0.001 Then
                Dim sumActual As Double = 0
                Dim sumDesired As Double = 0
                For j = clusterStart To i - 1
                    sumActual += actual(j)
                    sumDesired += desired(j)
                Next
                Dim shift = (sumActual - sumDesired) / (i - clusterStart)
                For j = clusterStart To i - 1
                    actual(j) -= shift
                Next
                clusterStart = i
            End If
        Next

        For i = 0 To ordered.Count - 1
            If isHorizontal Then
                ordered(i).LeaderX = actual(i)
            Else
                ordered(i).LeaderY = actual(i)
            End If
        Next
    Next

    ' Phase 3: create balloons at the final positions.
    For Each p In placements
        Dim leaderPoints = ThisApplication.TransientObjects.CreateObjectCollection()
        leaderPoints.Add(transientGeom.CreatePoint2d(p.LeaderX, p.LeaderY))
        leaderPoints.Add(sheet.CreateGeometryIntent(p.TagCurve.Curve, p.MidPoint))
        sheet.Balloons.Add(leaderPoints)
    Next
End Sub

Private Shared Function ChooseClosestEdge(midPoint As Point2d, top As Double, bottom As Double, left As Double, right As Double) As Edge
    Dim distTop = Math.Abs(top - midPoint.Y)
    Dim distBottom = Math.Abs(bottom - midPoint.Y)
    Dim distLeft = Math.Abs(left - midPoint.X)
    Dim distRight = Math.Abs(right - midPoint.X)

    Dim horizontalEdge As Edge = If(distTop <= distBottom, Edge.Top, Edge.Bottom)
    Dim horizontalDist = Math.Min(distTop, distBottom)
    Dim verticalEdge As Edge = If(distLeft <= distRight, Edge.Left, Edge.Right)
    Dim verticalDist = Math.Min(distLeft, distRight)

    ' Tie goes to vertical edge to preserve original behavior
    Return If(horizontalDist < verticalDist, horizontalEdge, verticalEdge)
End Function

Private Shared Function ComputeLeaderPoint(edge As Edge, midPoint As Point2d, top As Double, bottom As Double, left As Double, right As Double, balloonDiameter As Double) As (X As Double, Y As Double)
    Select Case edge
        Case edge.Top
            Return (midPoint.X + balloonDiameter / 2, top + balloonDiameter)
        Case edge.Bottom
            Return (midPoint.X + balloonDiameter / 2, bottom - balloonDiameter)
        Case edge.Left
            Return (left - balloonDiameter, midPoint.Y + balloonDiameter / 2)
        Case Else ' Edge.Right
            Return (right + balloonDiameter, midPoint.Y + balloonDiameter / 2)
    End Select
End Function

Private Class Placement
    Public Property TagCurve As TagCurve
    Public Property MidPoint As Point2d
    Public Property ChosenEdge As Edge
    Public Property LeaderX As Double
    Public Property LeaderY As Double
End Class

Public Class TagCurve

    Public Sub New(drawingCurve As DrawingCurve)
        Curve = drawingCurve

        EdgeProxy = drawingCurve.ModelGeometry
        Occ = EdgeProxy.ContainingOccurrence.OccurrencePath.Item(1)
        FileName = Occ.ReferencedFileDescriptor.FullFileName
    End Sub

    Public Property FileName As String
    Public ReadOnly Property Curve As DrawingCurve
    Public Property DistanceToNearestViewEdge As Double

    Private ReadOnly EdgeProxy As EdgeProxy
    Public Occ As ComponentOccurrence
End Class
```

End Class

추가 iLogic 기반 도면 자동화 도구:


개인 블로그: hjalte.nl - Github - LinkedIn