Placing a dimension break

When manually placing a dimension gap symbol (a symbol that creates a small break in a dimension line), it can be difficult to get the proper placement because the gap symbol has no visible geometry and therefore gives no useful feedback during the placement process. However, if you move the symbol after placing it (as you will likely need to), a small, temporary rectangle is drawn (overlay graphics) to show the outline of where the gap will be placed. Why the rectangle isn't shown when initially placing the symbol seems to be a bit of an oversight - one that we can correct with a bit of code.

The Code

The journal below will prompt the user to select a dimension. The linear element nearest the selection point will be selected as the target of the gap symbol. A single selection determines both the dimension and the element within that dimension to modify. Make your selection pick with this in mind.

This journal makes use of the Overlay Graphics Primitives, which we have covered in a previous tutorial. The code has been tested on NX 8.5, and 9.

'September 24, 2014
'tested on NX 8.5 & 9
'based on code from
'posted by user "rossobryan" (Clayton)
'Add gap to dimension extension line (or leader line). This version only works with linear dimension elements
'  (i.e. it won't add a break to an angular arc leader segment).
'Journal prompts user to select a dimension; the user is then prompted to select a location
'to add the break symbol. Visual feedback is given as to where the symbol will be placed
'(similar to what is seen when {Drafting} Edit -> component (move option) is used.
'If the break is no longer needed, the gap can be moved or deleted by using Edit -> component;
'select the dimension that the gap symbol is applied to, the cursor will then change to a
'"point selection" type cross, click on the location of the gap symbol and press Apply or OK.
'Alternately, the gap can be deleted by toggling the extension line off then back on. Right click the
'dimension and choose Style -> Dimensions, toggle the extension line off, press Apply, toggle the line
'on, and press Apply or OK.
'September 25, 2014
'Minor code cleanup and additional comments.
'October 7, 2014
'Removed reference to CreateRuleBaseCurveDumb(IBaseCurve()) so that code would be compatible with NX 8.
'January 16, 2015
'In Sub AdjustLineLength: added the invariant culture specifier in the "adjustLength.ToString()" command.
'The .ToString command was using the local decimal separator (aka radix) which, if it happened to be a comma, caused an error.
'send feedback and bug reports to
Option Strict Off
Imports System
Imports System.Windows.Forms
Imports System.Collections.Generic
Imports NXOpen
Imports NXOpen.UF
Module PlaceBreakSymbol
    Dim theSession As Session = Session.GetSession()
    Dim theUfSession As UFSession = UFSession.GetUFSession
    Dim ufs As UFSession = UFSession.GetUFSession
    Dim workPart As Part = theSession.Parts.Work
    Dim lw As ListingWindow = theSession.ListingWindow
    'variable to hold a reference line (placed on the dimension's linear element)
    '  closest to the user's pick point
    Dim closeLine As Line
    'direction of the line above, used to help build the rectangular
    '  visual cue when placing the dim break symbol
    Dim closeLineDir(2) As Double
    'direction 90° to above, used for the short side of the rectangle
    Dim shortLineDir(2) As Double
    'change the break symbol size to your liking
    Const inchSymbolLength As Double = 0.25
    Const mmSymbolLength As Double = 6.35
    Sub Main()
        Dim dim1ExtensionLines As New List(Of Line)
        Dim undoStack As New Stack(Of Session.UndoMarkId)
        Dim myDim1 As Annotations.Dimension = Nothing
        Dim mySelPt As Point3d
        Dim keepGoing As Boolean = True
        Dim partSymbolLength As Double
        If workPart.PartUnits = BasePart.Units.Inches Then
            partSymbolLength = inchSymbolLength
            partSymbolLength = mmSymbolLength
        End If
        While keepGoing
            Dim markId1 As Session.UndoMarkId
            Dim markText As String = "Break Dimension Line"
            markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, markText)
            'select the dimension
            If SelectDimension("Select first dimension", myDim1, mySelPt) = Selection.Response.Cancel Then
                keepGoing = False
                Dim myUndo As Session.UndoMarkId = undoStack.Pop
                theSession.UndoToMark(myUndo, markText)
                theSession.DeleteUndoMark(myUndo, markText)
            End If
                GetDimLines(myDim1, dim1ExtensionLines)
            Catch ex As Exception
                Dim myUndo As Session.UndoMarkId = undoStack.Pop
                theSession.UndoToMark(myUndo, markText)
                theSession.DeleteUndoMark(myUndo, markText)
                If ex.Message = "Dimension has no extension lines" Then
                    MessageBox.Show("The selected dimension has no extension lines, please choose new dimensions or press Cancel", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                    Continue While
                    'lw.WriteLine("Error: " & ex.Message)
                    MessageBox.Show("An unexpected error has occurred, the journal will now exit", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
                End If
            End Try
            'Find the dimension's linear element closest to the user's pick point.
            'This will be the element that the dim break symbol is added to.
            closeLine = ClosestDimLine(dim1ExtensionLines, mySelPt)
            'Shorten the 'closeLine' by the length of the symbol, this will
            'help limit where the symbol can be placed.
            AdjustLineLength(closeLine, partSymbolLength)
            Dim myBreakPoint(2) As Double
            myBreakPoint = SelectBreakPoint()
            If myBreakPoint Is Nothing Then
                Dim myUndo As Session.UndoMarkId = undoStack.Pop
                theSession.UndoToMark(myUndo, markText)
                theSession.DeleteUndoMark(myUndo, markText)
                Continue While
            End If
            Dim breakPoint As Point
            breakPoint = workPart.Points.CreatePoint(New Point3d(myBreakPoint(0), myBreakPoint(1), myBreakPoint(2)))
            'for debugging
            AdjustIntersectionPoint(breakPoint, closeLineDir, partSymbolLength)
            BreakDim(myDim1, breakPoint, partSymbolLength)
        End While
    End Sub
    Function SelectDimension(ByVal prompt As String, ByRef selObj As TaggedObject, ByRef selPoint As Point3d) As Selection.Response
        'allow user to interactively select a dimension
        Dim theUI As UI = UI.GetUI
        Dim title As String = "Select a dimension"
        Dim includeFeatures As Boolean = False
        Dim keepHighlighted As Boolean = False
        Dim selAction As Selection.SelectionAction = Selection.SelectionAction.ClearAndEnableSpecific
        'Dim cursor As Point3d
        Dim scope As Selection.SelectionScope = Selection.SelectionScope.WorkPart
        Dim selectionMask_array(0) As Selection.MaskTriple
        With selectionMask_array(0)
            .Type = UFConstants.UF_dimension_type
            .Subtype = UFConstants.UF_all_subtype
        End With
        Dim resp As Selection.Response = theUI.SelectionManager.SelectTaggedObject(prompt, _
         title, scope, selAction, _
         includeFeatures, keepHighlighted, selectionMask_array, _
         selObj, selPoint)
        If resp = Selection.Response.ObjectSelected OrElse resp = Selection.Response.ObjectSelectedByName Then
            Return Selection.Response.Ok
            Return Selection.Response.Cancel
        End If
    End Function
    Sub GetDimLines(ByVal tempDim As Annotations.Dimension, ByRef extLines As List(Of Line))
        'add the linear extension and leader elements of the given dimension to the given list of lines
        Dim cd1 As Annotations.ComponentData = workPart.Annotations.CreateComponentData(tempDim)
        Dim lc1() As Annotations.LineComponent = cd1.GetLineComponents()
        For Each thisLineComponent As Annotations.LineComponent In lc1
            If thisLineComponent.Type = Annotations.LineComponent.LineType.Extension _
                Or thisLineComponent.Type = Annotations.LineComponent.LineType.Leader Then
                Dim sp As Point3d = thisLineComponent.StartPoint
                Dim ep As Point3d = thisLineComponent.EndPoint
                Dim extLine As Line = workPart.Curves.CreateLine(sp, ep)
            End If
        If extLines.Count = 0 Then
            Throw New ApplicationException("Dimension has no extension lines")
        End If
    End Sub
    Function ClosestDimLine(ByVal dimLines As List(Of Line), ByVal pickPt As Point3d) As Line
        'return the line in the list that is closest to the given point
        If dimLines.Count = 0 Then
            Return Nothing
        End If
        Dim minDistance As Double
        minDistance = MeasurePointToObject(pickPt, dimLines.Item(0))
        Dim closestItemIndex As Integer = 0
        Dim tempDistance As Double
        For i As Integer = 1 To dimLines.Count - 1
            tempDistance = MeasurePointToObject(pickPt, dimLines.Item(i))
            If tempDistance < minDistance Then
                minDistance = tempDistance
                closestItemIndex = i
            End If
        Return dimLines.Item(closestItemIndex)
    End Function
    Function MeasurePointToObject(ByVal thePoint As Point3d, ByVal theObject As NXObject) As Double
        Dim nullNXObject As NXObject = Nothing
        Dim measureDistanceBuilder1 As MeasureDistanceBuilder
        measureDistanceBuilder1 = workPart.MeasureManager.CreateMeasureDistanceBuilder(nullNXObject)
        measureDistanceBuilder1.Mtype = MeasureDistanceBuilder.MeasureType.Minimum
        Dim unit1 As Unit = CType(workPart.UnitCollection.FindObject("Inch"), Unit)
        Dim point1 As Point
        point1 = workPart.Points.CreatePoint(thePoint)
        measureDistanceBuilder1.Object1.Value = point1
        measureDistanceBuilder1.Object2.Value = theObject
        Dim measureDistance1 As MeasureDistance
        measureDistance1 = workPart.MeasureManager.NewDistance(unit1, MeasureManager.MeasureType.Minimum, point1, theObject)
        'lw.WriteLine("distance: " & measureDistance1.Value.ToString)
        Return measureDistance1.Value
    End Function
    Sub BreakDim(ByVal theDim As Annotations.Dimension, ByVal thePoint As Point, ByVal theSymbolLength As Double)
        'This Sub does the work of creating the Gap symbol and adding it
        'to the given dimension.
        Dim myDimLinePrefs As Annotations.LineAndArrowPreferences
        myDimLinePrefs = theDim.GetLineAndArrowPreferences
        Dim breakLocation As Point3d = thePoint.Coordinates
        ' Create the symbol
        Dim symbol_data As UFDrf.SymbolCreateData
        symbol_data.length = theSymbolLength
        symbol_data.height = theSymbolLength
        symbol_data.angle = 0
        Dim anchor As Point = workPart.Points.CreatePoint(breakLocation)
        symbol_data.anchor_tag = anchor.Tag
        symbol_data.symbol_name = "GAP25"
        Dim symbol_tag As Tag
        ufs.Drf.PlaceSymbol(symbol_data, False, False, symbol_tag)
        ufs.Drf.AddSymbolToObject(symbol_data, theDim.Tag)
    End Sub
    Function SelectBreakPoint() As Double()
        'Allow user to interactively pick the point where the dimension break symbol
        'will be placed.
        'This Function needs Sub MotionCallback() to work properly.
        Dim myScreenPos(2) As Double
        Dim junk(2) As Double
        Dim theViewTag As Tag = theSession.Parts.Display.Views.WorkView.Tag
        Dim theResponse As Integer
        Dim minDist As Double
        theUfSession.Ui.SpecifyScreenPosition("pick a point", AddressOf MotionCallback, Nothing, myScreenPos, theViewTag, theResponse)
        If theResponse = UFConstants.UF_UI_PICK_RESPONSE Then
            'The AskMinimumDist function will return a point on the selected objects.
            'Calculate the minimum distance from the cursor pick point to the dimension line,
            'use the point on the line as the selected point rather than the cursor position,
            'which was probably not on the line.
            theUfSession.Modl.AskMinimumDist(Tag.Null, closeLine.Tag, 1, myScreenPos, 0, Nothing, minDist, junk, myScreenPos)
            'for debugging
            'theUfSession.Disp.DisplayTemporaryPoint(workPart.Views.WorkView.Tag, UFDisp.ViewType.UseWorkView, myScreenPos, Nothing, UFDisp.PolyMarker.Asterisk)
            Return myScreenPos
            Return Nothing
        End If
    End Function
    Sub MotionCallback(ByVal pos() As Double, _
                   ByRef motion_cb_data As UFUi.MotionCbData, _
                   ByVal client_data As System.IntPtr)
        'This sub will be called every time a cursor move is detected during the
        'SpecifyScreenPosition function.
        'The parameters motion_cb_data and client_data are not used in this implementation,
        'but the Sub must have the same signature as UFUI.MotionFnT to work properly.
        Dim point1 As Point3d = New Point3d(pos(0), pos(1), pos(2))
        Dim minDist As Double
        Dim breakPt(2) As Double
        breakPt(2) = 0
        Dim topLeft(2) As Double
        topLeft(2) = 0
        Dim bottomLeft(2) As Double
        bottomLeft(2) = 0
        Dim topRight(2) As Double
        topRight(2) = 0
        Dim bottomRight(2) As Double
        bottomRight(2) = 0
        'The AskMinimumDist function will return a point on each of the objects measured.
        'We'll use this returned point (the one on the closeLine) to ensure the visual cue
        'rectangle is drawn on the dimension line.
        theUfSession.Modl.AskMinimumDist(Tag.Null, closeLine.Tag, 1, pos, 0, Nothing, minDist, pos, breakPt)
        'Calculate points of rectangular visual cue using the vectors calculated earlier.
        'The rectangle will be drawn relative to the cursor position, but limited to lying on the dimension line.
        'Subtract the short line vector from the currently chosen point on the dim line to
        'calculate the top left corner of our rectangular visual cue.
        topLeft(0) = breakPt(0) - shortLineDir(0)
        topLeft(1) = breakPt(1) - shortLineDir(1)
        bottomLeft(0) = topLeft(0) + shortLineDir(0) * 2
        bottomLeft(1) = topLeft(1) + shortLineDir(1) * 2
        'closeLineDir is a vector parallel to the dimension line with a length of the desired break symbol.
        topRight(0) = topLeft(0) + closeLineDir(0)
        topRight(1) = topLeft(1) + closeLineDir(1)
        bottomRight(0) = bottomLeft(0) + closeLineDir(0)
        bottomRight(1) = bottomLeft(1) + closeLineDir(1)
        'draw temporary rectangle for visual cue
        theUfSession.Disp.DisplayOgpLine(theSession.Parts.Display.Views.WorkView.Tag, topLeft, bottomLeft)
        theUfSession.Disp.DisplayOgpLine(theSession.Parts.Display.Views.WorkView.Tag, topRight, bottomRight)
        theUfSession.Disp.DisplayOgpLine(theSession.Parts.Display.Views.WorkView.Tag, topLeft, topRight)
        theUfSession.Disp.DisplayOgpLine(theSession.Parts.Display.Views.WorkView.Tag, bottomLeft, bottomRight)
    End Sub
    Sub AdjustLineLength(ByVal theLine As Line, ByVal adjustLength As Double)
        'Sub to change the length of a given line by the given amount.
        'Length will be added or removed from the 'end' of the line (the line object end point will move).
        Dim markId1 As Session.UndoMarkId
        markId1 = theSession.SetUndoMark(Session.MarkVisibility.Invisible, "Curve Length")
        Dim section1 As Section
        section1 = workPart.Sections.CreateSection(0.00095, 0.001, 0.5)
        Dim curves1(0) As Curve
        curves1(0) = theLine
        Dim curveDumbRule1 As CurveDumbRule
        curveDumbRule1 = workPart.ScRuleFactory.CreateRuleCurveDumb(curves1)
        Dim rules1(0) As SelectionIntentRule
        rules1(0) = curveDumbRule1
        Dim nullNXObject As NXObject = Nothing
        Dim helpPoint1 As Point3d = theLine.StartPoint
        section1.AddToSection(rules1, theLine, nullNXObject, nullNXObject, helpPoint1, Section.Mode.Create, False)
        Dim nullFeatures_Feature As Features.Feature = Nothing
        Dim curveLengthBuilder1 As Features.CurveLengthBuilder
        curveLengthBuilder1 = workPart.Features.CreateCurvelengthBuilder(nullFeatures_Feature)
        With curveLengthBuilder1
            .Section = section1
            .DistanceTolerance = 0.001
            .CurveOptions.Associative = False
            .CurveOptions.InputCurveOption = GeometricUtilities.CurveOptions.InputCurve.Replace
            .CurvelengthData.ExtensionMethod = GeometricUtilities.ExtensionMethod.Incremental
            .CurvelengthData.ExtensionSide = GeometricUtilities.ExtensionSide.StartEnd
            .CurvelengthData.ExtensionDirection = GeometricUtilities.ExtensionDirection.Linear
            .CurvelengthData.SetEndDistance("-" & adjustLength.ToString("F12", Globalization.CultureInfo.InvariantCulture))
        End With
        Dim nXObject1 As NXObject
        nXObject1 = curveLengthBuilder1.Commit()
        Dim nErrs1 As Integer
        nErrs1 = theSession.UpdateManager.DoUpdate(markId1)
    End Sub
    Sub DeleteTempLines(ByVal extLines As List(Of Line))
        'delete the temp lines created on the dimension linear objects (extension and leader lines)
            Dim markId2 As Session.UndoMarkId
            markId2 = theSession.SetUndoMark(Session.MarkVisibility.Invisible, "Delete")
            Dim nErrs1 As Integer
            nErrs1 = theSession.UpdateManager.AddToDeleteList(extLines.ToArray)
            Dim notifyOnDelete2 As Boolean
            notifyOnDelete2 = theSession.Preferences.Modeling.NotifyOnDelete
            Dim nErrs2 As Integer
            nErrs2 = theSession.UpdateManager.DoUpdate(markId2)
            'remove the references from the list
        Catch ex As NXException
        End Try
    End Sub
    Sub CreateTempOutline(ByVal thePoint() As Double)
        'Draw temporary lines around the rectangular visual cue.
        'This sub was used for debugging to check cue size vs. that used in the Edit -> component (move) option.
        Dim topLeft(2) As Double
        topLeft(2) = 0
        Dim bottomLeft(2) As Double
        bottomLeft(2) = 0
        Dim topRight(2) As Double
        topRight(2) = 0
        Dim bottomRight(2) As Double
        bottomRight(2) = 0
        'calculate points of rectangular visual cue
        topLeft(0) = thePoint(0) - shortLineDir(0)
        topLeft(1) = thePoint(1) - shortLineDir(1)
        bottomLeft(0) = topLeft(0) + shortLineDir(0) * 2
        bottomLeft(1) = topLeft(1) + shortLineDir(1) * 2
        topRight(0) = topLeft(0) + closeLineDir(0)
        topRight(1) = topLeft(1) + closeLineDir(1)
        bottomRight(0) = bottomLeft(0) + closeLineDir(0)
        bottomRight(1) = bottomLeft(1) + closeLineDir(1)
        theUfSession.Disp.DisplayTemporaryLine(theSession.Parts.Display.Views.WorkView.Tag, UFDisp.ViewType.UseWorkView, topLeft, bottomLeft, Nothing)
        theUfSession.Disp.DisplayTemporaryLine(theSession.Parts.Display.Views.WorkView.Tag, UFDisp.ViewType.UseWorkView, topRight, bottomRight, Nothing)
        theUfSession.Disp.DisplayTemporaryLine(theSession.Parts.Display.Views.WorkView.Tag, UFDisp.ViewType.UseWorkView, topLeft, topRight, Nothing)
        theUfSession.Disp.DisplayTemporaryLine(theSession.Parts.Display.Views.WorkView.Tag, UFDisp.ViewType.UseWorkView, bottomLeft, bottomRight, Nothing)
    End Sub
    Sub AdjustIntersectionPoint(ByRef thePoint As Point, ByVal theVector() As Double, ByVal theSymbolLength As Double)
        Dim offsetPt As Point
        Dim angle As Double
        Dim offsetDirection As Integer
        Dim distScalar As Scalar
        distScalar = workPart.Scalars.CreateScalar(theSymbolLength, Scalar.DimensionalityType.Length, SmartObject.UpdateOption.WithinModeling)
        angle = Math.Atan2(theVector(1), theVector(0))
        angle = (angle * 180) / Math.PI
        'normalize the angle
        If angle < 0 Then
            angle += 360
        End If
        'gap location changes slightly based on the angle of the extension line
        'lines in quadrant 1 & 4 need to be shifted in (toward start point of extension line)
        'lines in quadrant 2 & 3 need to be shifted out (toward end point of extension line)
        If angle > 270 Or angle <= 90 Then
            'line is somewhere in quadrant 1 or 4, shift break point toward start point of line
            'offsetDirection = Sense.Reverse
            'line is somewhere in quadrant 2 or 3, shift break point toward end point of line
            offsetDirection = Sense.Forward
            'offset breakpoint by the gap width
            offsetPt = thePoint
            thePoint = workPart.Points.CreatePoint(closeLine, offsetPt, distScalar, PointCollection.AlongCurveOption.Distance, offsetDirection, SmartObject.UpdateOption.WithinModeling)
        End If
    End Sub
    Sub CalculateCueVectors(ByVal theSymbolLength As Double)
        'When placing the break symbol, a rectangular visual cue will be drawn on screen.
        'This sub will calculate the vector of the chosen dim line and use that to calculate
        'the vectors to use when drawing the rectangular visual cue.
        'Calculate the direction of the closeLine.
        theUfSession.Vec3.Sub(New Double() {closeLine.EndPoint.X, closeLine.EndPoint.Y, closeLine.EndPoint.Z}, New Double() {closeLine.StartPoint.X, closeLine.StartPoint.Y, closeLine.StartPoint.Z}, closeLineDir)
        Const vecTol As Double = 0.0001
        Dim closeLineDirMag As Double
        'unit vector in the direction of the dimension line
        theUfSession.Vec3.Unitize(closeLineDir, vecTol, closeLineDirMag, closeLineDir)
        'scale up the vector to be the same length as the desired dim break symbol
        theUfSession.Vec3.Scale(theSymbolLength, closeLineDir, closeLineDir)
        'Get vector perpendicular to the dim line to be used when drawing the short side
        'of the visual cue rectangle.
        theUfSession.Vec3.AskPerpendicular(closeLineDir, shortLineDir)
        Dim shortLineDirMag As Double
        theUfSession.Vec3.Unitize(shortLineDir, vecTol, shortLineDirMag, shortLineDir)
        'Scale the vector to be 1/4 the symbol length.
        'Consider changing this to a constant value so that the visual cue rectangle
        'isn't dependent on the chosen symbol size?
        theUfSession.Vec3.Scale(theSymbolLength / 4, shortLineDir, shortLineDir)
    End Sub
    Public Function GetUnloadOption(ByVal dummy As String) As Integer
        'Unloads the image when the NX session terminates
        GetUnloadOption = NXOpen.Session.LibraryUnloadOption.AtTermination
    End Function
End Module

Next Steps

In its current form, the code above only works on linear elements of a given dimension. With some work, it can be modified to work with circular elements, such as the arc leader lines in an angular dimension.

Hopefully, this example of overlay graphics will give you some ideas of how they can be used in your own journals.

Good luck and keep coding!


While using this code, I am getting compile error as:

Line160: 'SelectTaggedObject' is not a member of 'NXOpen.Selection'

Can some one short this out. I am using version 7.5 on temcenter

The journal is intended for NX 8.5 or above. To get the code to run on NX 7.5, one would need to rework the "SelectDimension" function to use the .SelectObject method rather than the .SelectTaggedObject method. This is the cause of the error message that you are seeing. There may be other changes required, but this would certainly need to change.