June’s Plugin of the Month: Dimension Patrol

Thanks again to Stephen Preston, our DevTech Americas Manager, for developing this very useful little utility. You can find an earlier version of this code – which I'd converted to C# and extended to cover text entities – in this previous post.

June's Plugin of the Month is now live: Dimension Patrol for AutoCAD. This one was kicked off by a suggestion from Shaan Hurley: a tool for designers and CAD Managers to quickly check drawings for dimensions with overridden text (which, logically enough, could mean the dimensions no longer accurately reflect their associated distance or value).

Sometimes the best applications are the simplest, and this one is certainly elegant in its simplicity. To be fair, though, it's depending on the Overrule mechanism introduced in AutoCAD 2010 - hence the requirement to use AutoCAD 2010 or above – which provides you with a great deal of control over object behaviour.

Here are the important VB.NET source files from the project (which is, of course, provided with the plugin's download from Labs).

Firstly the myPlugin.vb file:

Imports System

Imports Autodesk.AutoCAD.Runtime

Imports Autodesk.AutoCAD.ApplicationServices

Imports Autodesk.AutoCAD.DatabaseServices

Imports Autodesk.AutoCAD.Geometry

Imports Autodesk.AutoCAD.EditorInput

 

' This line is not mandatory, but improves loading performance

 

<Assembly: ExtensionApplication(GetType(DimensionPatrol.MyPlugin))>

 

Namespace DimensionPatrol

 

  ' This class is instantiated by AutoCAD once and kept alive for

  '  the duration of the session.

 

  Public Class MyPlugin

    Implements IExtensionApplication

 

    ' Declare member variables - note they are Shared

    '  (don't require class instance to access them)

 

    Private Shared mOverrule As DimPatrolDrawOverrule

    Private Shared WithEvents mDocs As DocumentCollection

    Private Shared mOldOverruleState As Boolean

 

    Private Shared ReadOnly Property DimPatrolVal() As Short

      Get

        Return MyCommands.DimPatrolVal

      End Get

    End Property

 

    Private Shared ReadOnly Property DimPatrolColVal() As Short

      Get

        Return MyCommands.DimPatrolColVal

      End Get

    End Property

 

    ' Called by AutoCAD to initialize the addin.

    ' This is where we setup our overrule.

 

    Public Sub Initialize() _

      Implements IExtensionApplication.Initialize

 

      Try

 

        ' Add demand loading info to registry

 

        DemandLoading.RegistryUpdate.RegisterForDemandLoading()

 

        ' Initialize 'withevents' variables to activate events

 

        mDocs = Application.DocumentManager

 

        ' Register overrule

 

        mOverrule = New DimPatrolDrawOverrule

        Overrule.AddOverrule(RXObject.GetClass(GetType(Dimension)), _

                            mOverrule, False)

        mOverrule.SetCustomFilter()

        mOldOverruleState = Overrule.Overruling

        Overrule.Overruling = True

 

        ' In case this app is loaded midway through a session,

        '  we need to honor values of DimPatrol and DimPatrolColor

        '  and setup the current document

 

        UpdateDoc(mDocs.MdiActiveDocument)

 

      Catch ex As Autodesk.AutoCAD.Runtime.Exception

 

        CleanUp(ex)

 

      End Try

 

    End Sub

 

    ' Our addin only terminates when AutoCAD quits -

    '  you can't dynamically unload .NET DLLs.

    ' (So one could argue that this tidy up is unnecessary).

 

    Public Sub Terminate() Implements IExtensionApplication.Terminate

 

      CleanUp(Nothing)

 

    End Sub

 

    ' Tidy up our stuff (unregister overru
les etc).

    ' Called by Terminate method, and also in response to an

    '  exception to disable addin if there's a problem.

 

    Private Shared Sub CleanUp(ByVal ex As  _

                              Autodesk.AutoCAD.Runtime.Exception)

 

      ' If this is being called by an exception handler,

      '  then we politely inform user of the problem.

 

      If Not ex Is Nothing Then

        MsgBox("Dimension Patrol has encountered an error and " & _

              "is disabling itself.", MsgBoxStyle.Critical, "Oops!")

      End If

 

      ' Deactivate event handlers

 

      mDocs = Nothing

 

      ' Unregister overrule

 

      If Not mOverrule Is Nothing Then

        Overrule.RemoveOverrule( _

          RXObject.GetClass(GetType(Dimension)), mOverrule)

      End If

 

      ' We assume here that no other addin that uses overrules was

      '  loaded since our addin initialized.

 

      Overrule.Overruling = mOldOverruleState

 

    End Sub

 

 

    ' Called by  myCommands class to tell this class that value of

    '  DimPatrolColor variable has changed.

    ' (That's why its a Friend method)

 

    Friend Shared Sub DimPatColorVar_Changed()

 

      Try

 

        Dim doc As Document = _

          Application.DocumentManager.MdiActiveDocument

 

        ' Set stored value of color for this document so we know

        '  color change has been processed for this doc.

 

        Dim oldDimPatColVal As Short = _

          UpdateDocData(doc, "DimPatrolColor", DimPatrolColVal)

 

        ' Regen drawing if color has changed & DimPatrol sysvar = 1

        ' (Color changes while we're not drawing our custom graphics

        '  don't have any effect until DimPatrol is next changed and

        '  this doc is active/activated).

 

        If (DimPatrolVal) And (oldDimPatColVal <> DimPatrolColVal) _

          Then

          doc.Editor.Regen()

        End If

 

      Catch ex As Autodesk.AutoCAD.Runtime.Exception

        CleanUp(ex)

      End Try

 

    End Sub

 

    ' Called by  myCommands class to tell this class that value of

    '  DimPatrolColor variable has changed

    ' (That's why its a Friend method)

 

    Friend Shared Sub DimPatVar_Changed()

 

      Try

 

        ' Update active document with new value of DimPatrol to show

        '  we've regened it since the value was last changed

 

        Dim doc As Document = _

          Application.DocumentManager.MdiActiveDocument

        UpdateDocData(doc, "DimPatrol", DimPatrolVal)

 

        ' Whatever else happens, if we've changed the value of

        '  DimPatrol then we must regen the current document to

        '  display changed graphics.

 

        doc.Editor.Regen()

 

      Catch ex As Autodesk.AutoCAD.Runtime.Exception

        CleanUp(ex)

      End Try

 

    End
Sub

 

 

    ' When user switches document we want to respond to possible

    '  changes to DimPatrol and DimPatrolColor in that document

 

    Private Shared Sub mDocs_DocumentActivated(ByVal sender As _

      Object, ByVal e As Autodesk.AutoCAD.ApplicationServices. _

      DocumentCollectionEventArgs) Handles mDocs.DocumentActivated

 

      Try

 

        UpdateDoc(e.Document)

 

      Catch ex As Autodesk.AutoCAD.Runtime.Exception

        CleanUp(ex)

      End Try

 

    End Sub

 

    ' Updates userdata key 'varName' in document 'doc' to value 'val'

    ' Returns old value (or 0 if there wasn't an old value)

 

    Private Shared Function UpdateDocData(ByVal doc As Document, _

      ByVal varName As String, ByVal val As Short) As Short

 

      Dim oldVal As Short

 

      If doc.UserData.ContainsKey(varName) Then

 

        ' Update value of key-value pair

 

        oldVal = doc.UserData.Item(varName)

        If oldVal <> val Then

          doc.UserData.Item(varName) = val

        End If

      Else

 

        ' Add key-value pair if it didn't exist

 

        doc.UserData.Add(varName, val)

        oldVal = 0

      End If

 

      Return oldVal

 
;

    End Function

 

 

    ' Check userdata attached to document (where we store value of

    '  DimPatrol and DimPatrolColor when doc was last active).

    ' If userdata isn't there, or if it differs from current values,

    '  then we must regen the drawing.

 

    Private Shared Sub UpdateDoc(ByVal doc As Document)

 

      ' Update old DimPatrol value to show changes to this doc have

      '  been processed

 

      Dim oldDimPatVal As Short = UpdateDocData(doc, "DimPatrol", _

                                                DimPatrolVal)

 

      ' Update old DimPatrolColor value to show changes to this doc

      '  have been processed

 

      Dim oldDimPatColVal As Short = UpdateDocData(doc, _

        "DimPatrolColor", DimPatrolColVal)

 

      ' If DimPatrol sysvar has changed, or DimPatrolColor has

      '  changed when DimPatrol = 1, then we must regen the drawing

 

      If (oldDimPatVal <> DimPatrolVal) Or ((DimPatrolVal) And _

        (oldDimPatColVal <> DimPatrolColVal)) Then

 

        doc.Editor.Regen()

      End If

 

    End Sub

  End Class

 

 

    ' This is the one and only overrule used by this utility

 

  Public Class DimPatrolDrawOverrule

    Inherits Autodesk.AutoCAD.GraphicsInterface.DrawableOverrule

 

    ' Overrule WorldDraw so we can draw our additional graphics

    '  (solid color block covering the Dimension's extents)

 

    Public Overrides Function WorldDraw(ByVal drawable As  _

      Autodesk.AutoCAD.GraphicsInterface.Drawable, _

      ByVal wd As Autodesk.AutoCAD.GraphicsInterface.WorldDraw) _

      As Boolean

 

      Try

 

        ' We know this overrule is only registered for Dimensions,

        '  so we cast without checking.

 

        Dim dm As Dimension = drawable

 

        ' Now we want to draw a colored box around Dimension's

        '  extents()

 

        Dim ext As Extents3d = dm.Bounds

        Dim maxPt As Point3d = ext.MaxPoint

        Dim minPt As Point3d = ext.MinPoint

        Dim pts As New Point3dCollection

 

        ' These are the vertices of the highlight box

 

        pts.Add(New Point3d(minPt.X, minPt.Y, minPt.Z))

        pts.Add(New Point3d(minPt.X, maxPt.Y, minPt.Z))

        pts.Add(New Point3d(maxPt.X, maxPt.Y, minPt.Z))

        pts.Add(New Point3d(maxPt.X, minPt.Y, minPt.Z))

 

        ' Store current filltype and set to FillAlways

 

        Dim oldFillType As  _

          Autodesk.AutoCAD.GraphicsInterface.FillType = _

          wd.SubEntityTraits.FillType

        wd.SubEntityTraits.FillType = _

          Autodesk.AutoCAD.GraphicsInterface.FillType.FillAlways

 

        ' Store old graphics color and set to the color we want

 

        Dim oldColor As Short = wd.SubEntityTraits.Color

        wd.SubEntityTraits.Color = MyCommands.DimPatrolColVal

 

        ' Draw the filled polygon

 

        wd.Geometry.Polygon(pts)

 

        ' Restore old settings

 

        wd.SubEntityTraits.FillType = oldFillType

        wd.SubEntityTraits.Color = oldColor

 

      Catch ex As Autodesk.AutoCAD.Runtime.Exception

 

        ' Mop up any exception here so at least the Dimension can try

        '  to draw itself

 

        Application.DocumentManager.MdiActiveDocument.Editor. _

        WriteMessage( _

          vbLf & "Error highlighting changed dimension: " _

          & ex.ErrorStatus.ToString & vbLf)

      End Try

 

      ' Let the overruled Drawable draw itself.

 

      Return MyBase.WorldDraw(drawable, wd)

 

    End Function

 

    ' This function is called if we call SetCustomFilter on our

    '  overrule to check if the overrule should be applied for each

    ' entity.

    ' We add our own code to return true if the Dimension passed in

    '  is one we want to highlight.

 

    Public Overrides Function IsApplicable( _

      ByVal overruledSubject As RXObject) As Boolean

 

      Try

 

        ' If its a Dimension, we check if the text has been manually

        '  changed. But only if DimPatrol sysvar is set to 1

 

        If (MyCommands.DimPatrolVal) And _

          (TypeOf overruledSubject Is Dimension) Then

 

          Dim dm As Dimension = overruledSubject

          If dm.DimensionText <> "" Then

            Return True

          End If

        End If

 

      Catch ex As Autodesk.AutoCAD.Runtime.Exception

 

        ' Mop up any exc
eption here so at least the Dimension can try

        '  to draw itself

 

        Application.DocumentManager.MdiActiveDocument.Editor. _

        WriteMessage(vbLf & "Error testing dimension: " _

                    & ex.ErrorStatus.ToString & vbLf)

      End Try

 

      ' We only get to here if object isn't a Dimension and it didn't

      '  contain overridden text

 

      Return False

 

    End Function

 

  End Class

 

End Namespace

And secondly the myCommands.vb file:

Imports System

Imports Autodesk.AutoCAD.Runtime

Imports Autodesk.AutoCAD.ApplicationServices

Imports Autodesk.AutoCAD.DatabaseServices

Imports Autodesk.AutoCAD.Geometry

Imports Autodesk.AutoCAD.EditorInput

 

' This line is not mandatory, but improves loading performance

 

<Assembly: CommandClass(GetType(DimensionPatrol.MyCommands))>

 

Namespace DimensionPatrol

 

  ' Container class for our commands

  ' All methods and properties in this class are Shared,

  '  so this class is never instantiated by AutoCAD.

 

  Public Class MyCommands

 

    ' Member variables to store values of DIMPATROL and

    ' DIMPATROLCOLOR

 

    Private Shared mDimPatrol As Short = 0

    Private Shared mDimPatrolColor As Short = 1

 

    ' This property is also used by MyPlugin and

    ' DimPatrolDrawOverrule - hence use of 'Friend'

 

    Friend Shared Property DimPatrolVal() As Short

 

      Get

        Return mDimPatrol

      End Get

 

      Private Set(ByVal value As Short)

 

        mDimPatrol = value

 

        ' Tell MyPlugin that value of DimPatrol has changed

 

        MyPlugin.DimPatVar_Changed()

 

      End Set

 

    End Property

 

    ' This property is also used by MyPlugin and

    ' DimPatrolDrawOverrule - hence use of 'Friend'

 

    Friend Shared Property DimPatrolColVal() As Short

 

      Get

        Return mDimPatrolColor

      End Get

 

      Private Set(ByVal value As Short)

 

        mDimPatrolColor = value

 

        ' Tell MyPlugin that value of DimPatrolColor has changed

 

        MyPlugin.DimPatColorVar_Changed()

 

      End Set

 

    End Property

 

 

    ' Turn dimension patrol on (1) or off (0)

 

    <CommandMethod("ADNPLUGINS", "DIMPATROL", CommandFlags.Modal)> _

    Public Shared Sub DIMPATROL()

 

      Try

 

        ' Prompt for new value

 

        Dim ed As Editor

        ed = Application.DocumentManager.MdiActiveDocument.Editor

        Dim opts As New  _

          PromptIntegerOptions("Enter new value for DIMPATROL")

        opts.DefaultValue = DimPatrolVal

        opts.LowerLimit = 0

        opts.UpperLimit = 1

        Dim res As PromptIntegerResult = ed.GetInteger(opts)

 

        ' Only update stored value if user has entered a different

        '  value()

 

        If (res.Status = PromptStatus.OK) _

          And (res.Value <> DimPatrolVal) Then

 

          DimPatrolVal = res.Value

 

        End If

 

      Catch ex As Autodesk.AutoCAD.Runtime.Exception

        Application.DocumentManager.MdiActiveDocument. _

          Editor.WriteMessage( _

            vbLf & "Error changing DIMPATROL value: " & _

            ex.ErrorStatus.ToString & vbLf)

      End Try

 

    End Sub

 

 

    ' Allow user to set highlight color for manually edited

    ' dimensions. Color index can be set within range 1-7.

 

    <CommandMethod("ADNPLUGINS", "DIMPATROLCOLOR", _

                  CommandFlags.Modal)> _

    Public Shared Sub DIMPATROLCOLOR()

 

      Try

 

        ' Prompt for new value

 

        Dim ed As Editor

        ed = Application.DocumentManager.MdiActiveDocument.Editor

        Dim opts As New  _

          PromptIntegerOptions("Enter new value for DIMPATROLCOLOR")

        opts.DefaultValue = DimPatrolColVal

        opts.LowerLimit = 1

        opts.UpperLimit = 7

        Dim res As PromptIntegerResult = ed.GetInteger(opts)

 

        ' Only update stored value if user has entered a different

        '  value

 

        If (res.Status = PromptStatus.OK) _

          And (res.Value <> DimPatrolColVal) Then

 

          DimPatrolColVal = res.Value

 

        End If

 

      Catch ex As Autodesk.AutoCAD.Runtime.Exception

        Application.DocumentManager.MdiActiveDocument.Editor. _

          WriteMessage( _

            vbLf & "Error changing DIMPATROLCOLOR value: " _

            & ex.ErrorStatus.ToString & vbLf)

      End Try

 

    End Sub

 

 

    'Remove demand load settings from registry

 

    <CommandMethod("ADNPLUGINS", "REMOVEDP", CommandFlags.Modal)> _

    Public Shared Sub RemoveDP()

 

      DemandLoading.RegistryUpdate.UnregisterForDemandLoading()

 

      Dim ed As Editor = _

        Autodesk.AutoCAD.ApplicationServices.Application _

        .DocumentManager.MdiActiveDocument.Editor()

      ed.WriteMessage(vbCr + _

        "Dimension Patrol will not be loaded" _

        + " automatically in future editing sessions.")

 

    End Sub

 

  End Class

 

End Namespace

We can set the DIMPATROL value to 1 to turn on checking for problematic dimensions. Here's are some problematic dimensions from an Xrefed drawing (which is why the red is slightly dimmed):

Dimensions being patrolled

13 responses to “June’s Plugin of the Month: Dimension Patrol”

  1. Tony Tanzillo Avatar

    "Sometimes the best applications are the simplest"

    I wholeheartedly agree with that:


    Command: ISOLATEOBJECTS
    Select Objects: (ssget "x" '((0 . "DIMENSION") (1 . "~")))
    nnn found
    Select Objects:

  2. Stephan Bartl Avatar

    That's good to know, Tony,

    and it's funny - I had to laugh - comparing it with the bulk of code above. But I found at least one big difference: The command "ISOLATEOBJECTS" does not work before AutoCAD 2011. (Although the minimum of AutoCAD 2010 for the plugin of June also is not much better.)

    For people using AutoCAD before 2010: I once saw a lisp-routine that could isolate objects playing with the visibility attribute. However I find this very dangerous if you have autosave activated.

    Regards, Stephan

  3. Kean Walmsley Avatar

    Yes, ISOLATEOBJECTS is a handy little command. That said, it doesn't work - at least as far as I'm aware - with objects inside blocks and xrefs (let alone dimensions in xrefs inside modelspace that you want to check from paperspace). Highlighting dimensions in xrefed drawings was a major use case we considered for this plugin.

    I also fully expect the Dimension Patrol plugin to be extended over time based on user feedback: for instance, I could well imagine a "fix selected dimensions, whether in Xrefs or not" command (the eventual naming will clearly need some further thought 🙂 which - while it could be attempted using a more involved LISP application - would ultimately be easier to implement using a .NET language.

    Honestly, the above VB.NET code really isn't complex considering the elegance with which it handles dimensions in blocks and xrefs.

    Kean

  4. Tony Tanzillo Avatar

    Hi Stephan, you're correct about ISOLATEOBJECTS on vanilla AutoCAD, and there are third-party LISP solutions that emulate it, but in fact, if you only need a visual indication, the highlighting that's done by standard object oelection might suffice, or you could temporarily place the objects on a new layer and lock all other layers and then just discard the drawing or undo those changes.

    Insofar as putting .NET to good use in this case, one would probably want to display a modeless dialog that shows a list of the items that need to be checked, and allows the user to zoom to one of them by selecting its entry in the list.

  5. Tony Tanzillo Avatar

    Hi Kean. Most of use generally don't consider dimensions with overridden text that include the dimension measurement embedded in the overrriden text (via the <> indicator) as potentially corrupt.

  6. Kean Walmsley Avatar

    Hi Tony,

    Interesting - thanks for that.

    So you suggest having the overrule not highlight dimensions with text that includes "<>"... anything else to watch for?

    Cheers,

    Kean

  7. Tony Tanzillo Avatar

    Hi Kean.

    The presence of <> means the dimension's measurement is included in the overriden text, and I certainly wouldn't consider that case to be suspect.

    Anything else aside from misspellings would be very discipline-specific.

  8. This looks to be very good, but as a vb noob I don't know how to use this application...

  9. Kean Walmsley Avatar

    No need to build the app: you can download the plugin from the Labs site and follow the instructions in the ReadMe to install it.

    Kean

  10. Hi Kean,

    Very useful utility (and idea).
    I tried to adapt it to highlight certain values of an AttributeReference. It works OK (I mean the highlighting part) except it crashes Acad on each attempt to modify (move, rotate, etc.) the containing BlockReference, or use grips on it. This in contrast to the DimensionOverrule which is ok for each action performed on it.
    The documentation contains precious little on the subject.
    Any tips?
    Thanks,
    alex

  11. Hi alex,

    It's hard to say what's happening here. I suggest posting some code via the AutoCAD .NET discussion group or sending it to ADN.

    Best regards,

    Kean

  12. Hi Kean,
    Really useful idea. Do you know if it is possible to limit the zone and draw the polygon just around the dimension text?
    Regards..

    1. Kean Walmsley Avatar

      Hi Yan,

      Sure - you'd need to retrieve/calculate the extents of the text, but you could do it.

      Regards,

      Kean

Leave a Reply to Joris Cancel reply

Your email address will not be published. Required fields are marked *