Shaping operations and "bogus" Symmetrical nodes

It has been noted/discussed in at least two other threads that shaping operations (Weld, Trim, Boundary, etc.) can sometime produce content that will initially have correct geometry, but that can unexpectedly become distorted when some types of node editing operations are performed.

Annoying bug when working with curvers!

Nodes convert on weld, objects distort when nodes moved

My observations are that this has to do with nodes that were Symmetrical nodes before the shaping operation, but should have been changed to Smooth nodes as a result of the shaping operation. These nodes cannot be Symmetrical and produce the correct geometry, but they are still identified as being Symmetrical. Note in this screenshot that the node is identified as being Symmetrical, but the control points are not equal distances away from the node (as they should be if a node is Symmetrical).

If certain node editing operations are performed, such nodes appear to "wake up" and assert their Symmetrical identity - which distorts the geometry.

One way to work around this is to perform the shaping operation, then manually find every such node and change it from Symmetrical to Smooth. Depending on the complexity of the shape, that might be very tedious.

As a less-tedious workaround, here is a VBA macro I wrote that checks each node in a Curve that is identified as Symmetrical. If the control points are not equal distances from the node, then that node is considered to be a "bogus" Symmetrical node, and is changed to Smooth.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Option Explicit

Sub fix_bogus_symmetrical_nodes()
Dim sr As ShapeRange
Dim s As Shape
Dim nodeThis As Node
Const dblLengthToleranceTenthMicrons As Double = 50
Dim lngNodesChangedCounter As Long
Const strMacroName As String = "Fix Bogus Symmetrical Nodes"


    On Error GoTo ErrHandler
    
    EventsEnabled = False
    Optimization = True
    ActiveDocument.BeginCommandGroup "Fix Bogus Symmetrical Nodes"
    
    Set sr = ActiveSelectionRange
    
    If sr.Count = 1 Then
        If sr(1).Type = cdrCurveShape Then
            Set s = sr(1)
            For Each nodeThis In s.Curve.Nodes
                If nodeThis.Type = cdrSymmetricalNode Then
                    If Abs(nodeThis.PrevSegment.EndingControlPointLength - nodeThis.NextSegment.StartingControlPointLength) > ActiveDocument.ToUnits(dblLengthToleranceTenthMicrons, cdrTenthMicron) Then
                        nodeThis.Type = cdrSmoothNode
                        lngNodesChangedCounter = lngNodesChangedCounter + 1
                    End If
                End If
            Next nodeThis
            MsgBox "Number of nodes changed: " & lngNodesChangedCounter, vbInformation, strMacroName
        Else
            MsgBox "Selection is not a Curve.", vbInformation, strMacroName
        End If
    Else
        MsgBox "Exactly one Curve shape must be selected.", vbInformation, strMacroName
    End If

ExitSub:
    ActiveDocument.EndCommandGroup
    Optimization = False
    EventsEnabled = True
    Exit Sub
    
ErrHandler:
    MsgBox "Error occured: " & Err.Description
    Resume ExitSub
End Sub

That same code is in the .GMS file in this.ZIP archive:

JQ_Fix_Bogus_Symmetrical_Nodes - GMS in ZIP

Here is a short video showing an example of a situation where this bug shows up, and how changing those "bogus" Symmetrical nodes to Smooth can be used to correct the problem:

VIDEO: Fix Bogus Symmetrical Nodes

.

Parents
  • Thing two: my earlier approach to this does not work if the Curve contains two or more consecutive "bogus" Symmetrical nodes.

    It plays out something like this:

    1. Check node n, and find that it is a bogus Symmetrical node.
    2. Change node n to Smooth or Cusp; whichever it should have been.
    3. This causes node n+1 to "wake up" and assert its symmetrical nature, distorting the geometry.
    4. Check node n+1, and it will not report as being a bogus Symmetrical node - because it has now become a "genuine" Symmetrical node.

    So, a different, "two-pass" approach can be used:

    1. Go through the Curve, inspecting each node (first pass)
    2. If a node is found to be a bogus Symmetrical node, do not change it. Record what type of node it should be (Cusp or Smooth). For Curve segment(s) associated with the node, record the positions of the control points. Since no changes are being made, the bogus Symmetrical nodes do not "wake up" now, so the information recorded is correct for the undistorted geometry.
    3. Go through the Curve again, changing nodes as necessary (second pass).
    4. For nodes identified as bogus Symmetrical nodes in the first pass, change then to Cusp or Smooth as appropriate. For Curve segment(s) associated with the node, use the recorded information to reset the positions of the control points.

    The code looks like this now:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    Sub fix_bogus_symmetrical_nodes()
    
    Dim sr As ShapeRange
    Dim s As Shape
    Dim nodeThis As Node
    Const dblAngleTolerance As Double = 0.001
    Const dblLengthToleranceTenthMicrons As Double = 50
    Dim action_arr() As String
    Dim controlpoints_arr() As Double
    Dim lngNodeIndex_getting As Long
    Dim lngNodeIndex_setting As Long
    Dim lngNodesChangedToCuspCount As Long
    Dim lngNodesChangedToSmoothCount As Long
    Const strMacroName As String = "Fix Bogus Symmetrical Nodes"
    
        On Error GoTo ErrHandler
        
        EventsEnabled = False
        Optimization = True
        ActiveDocument.BeginCommandGroup "Fix Bogus Symmetrical Nodes"
        
        Set sr = ActiveSelectionRange
        If sr.Count = 1 Then
            If sr(1).Type = cdrCurveShape Then
                Set s = sr(1)
                ReDim action_arr(s.Curve.Nodes.Count + 1)
                ReDim controlpoints_arr(s.Curve.Nodes.Count + 1, 4)
                
                For lngNodeIndex_getting = 1 To s.Curve.Nodes.Count
                    Set nodeThis = s.Curve.Nodes(lngNodeIndex_getting)
                    If nodeThis.Type = cdrSymmetricalNode Then
                        If Abs(Abs(nodeThis.Segment.EndingControlPointAngle - nodeThis.NextSegment.StartingControlPointAngle) - 180) > dblAngleTolerance Then
                            action_arr(lngNodeIndex_getting) = "C"
                            If nodeThis.Segment.Type = cdrCurveSegment Then
                                controlpoints_arr(lngNodeIndex_getting, 1) = nodeThis.Segment.EndingControlPointX
                                controlpoints_arr(lngNodeIndex_getting, 2) = nodeThis.Segment.EndingControlPointY
                            End If
                            If nodeThis.NextSegment.Type = cdrCurveSegment Then
                                controlpoints_arr(lngNodeIndex_getting, 3) = nodeThis.NextSegment.StartingControlPointX
                                controlpoints_arr(lngNodeIndex_getting, 4) = nodeThis.NextSegment.StartingControlPointY
                            End If
                        Else
                            If Abs(nodeThis.Segment.EndingControlPointLength - nodeThis.NextSegment.StartingControlPointLength) > ActiveDocument.ToUnits(dblLengthToleranceTenthMicrons, cdrTenthMicron) Then
                                action_arr(lngNodeIndex_getting) = "S"
                                controlpoints_arr(lngNodeIndex_getting, 1) = nodeThis.Segment.EndingControlPointX
                                controlpoints_arr(lngNodeIndex_getting, 2) = nodeThis.Segment.EndingControlPointY
                                controlpoints_arr(lngNodeIndex_getting, 3) = nodeThis.NextSegment.StartingControlPointX
                                controlpoints_arr(lngNodeIndex_getting, 4) = nodeThis.NextSegment.StartingControlPointY
                            End If
                        End If
                    End If
                Next lngNodeIndex_getting
                
                For lngNodeIndex_setting = 1 To s.Curve.Nodes.Count
                    Set nodeThis = s.Curve.Nodes(lngNodeIndex_setting)
                    If action_arr(lngNodeIndex_setting) = "C" Then
                        nodeThis.Type = cdrCuspNode
                        If nodeThis.Segment.Type = cdrCurveSegment Then
                            nodeThis.Segment.EndingControlPointX = controlpoints_arr(lngNodeIndex_setting, 1)
                            nodeThis.Segment.EndingControlPointY = controlpoints_arr(lngNodeIndex_setting, 2)
                        End If
                        If nodeThis.NextSegment.Type = cdrCurveSegment Then
                            nodeThis.NextSegment.StartingControlPointX = controlpoints_arr(lngNodeIndex_setting, 3)
                            nodeThis.NextSegment.StartingControlPointY = controlpoints_arr(lngNodeIndex_setting, 4)
                        End If
                        lngNodesChangedToCuspCount = lngNodesChangedToCuspCount + 1
                    Else
                        If action_arr(lngNodeIndex_setting) = "S" Then
                            nodeThis.Type = cdrSmoothNode
                            nodeThis.Segment.EndingControlPointX = controlpoints_arr(lngNodeIndex_setting, 1)
                            nodeThis.Segment.EndingControlPointY = controlpoints_arr(lngNodeIndex_setting, 2)
                            nodeThis.NextSegment.StartingControlPointX = controlpoints_arr(lngNodeIndex_setting, 3)
                            nodeThis.NextSegment.StartingControlPointY = controlpoints_arr(lngNodeIndex_setting, 4)
                            lngNodesChangedToSmoothCount = lngNodesChangedToSmoothCount + 1
                        End If
                    End If
                Next lngNodeIndex_setting
                
                MsgBox "Number of nodes changed to Smooth: " & lngNodesChangedToSmoothCount & vbCrLf & vbCrLf & "Number of nodes changed to Cusp: " & lngNodesChangedToCuspCount, vbInformation, strMacroName
            Else
                MsgBox "Selection is not a Curve.", vbInformation, strMacroName
            End If
        Else
            MsgBox "Exactly one Curve shape must be selected.", vbInformation, strMacroName
        End If
    
    ExitSub:
        ActiveDocument.EndCommandGroup
        Optimization = False
        EventsEnabled = True
        Exit Sub
        
    ErrHandler:
        MsgBox "Error occured: " & Err.Description
        Resume ExitSub
    End Sub
    

    .

    Here is a new .GMS file:

    JQ_Fix_Bogus_Symmetrical_Nodes _2018_07_03_0808.zip.

    Here is a short video showing it working on consecutive bogus Symmetrical nodes. It's a simple but fairly dramatic example of how badly geometry can be distorted by these bogus nodes:

    VIDEO: Fix Bogus Symmetrical Nodes 2.

    .

Reply Children