Quick way to reconnect many Shapes

Started by doudou, July 16, 2014, 04:45:00 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

doudou

Quote from: wapperdude on July 17, 2014, 09:18:59 PM
Like Yacine, I only have V2007.  Unlike Yacine, my programming is not that strong, but, I think you have most of the pieces.

You need to iterate thru each shape that is not a connector. 
    For each shape, you need to go thru each connection point.
       Set a variable for number of rows and then step thru.  E.g.
           nrows = shp.RowCount(visSectionConnectionPts)
           For j = 0 To nrows - 1

               Add code to grab X, Y positions of connection point
               translate positions to page coordinates
               use those coordinates for neighbhood search for connector
               check connectivity
                    if not, then use shape IDs, and row index "j" to glue connector to connection point.
          next row.
      next shape

HTH
You'll probably do this faster than I could.
Wapperdude

Hello again wapperdude, I am sorry for my late response. I had to stop working on this project at work for another urgent project that I came up. I started playing with this last night. I can't figure out how to code "use those coordinates for neighbhood search for connector". Perhaps because I haven't used Visio in over a month :). I have below the code. Any help would be much appreciated. Thanks.


Public Sub FixConnectionsNew() 'New Version that loops through all connection points
    Dim viShapes As Visio.Shapes
    Set viShapes = ActivePage.Shapes
    Dim myShape As Visio.Shape
    Dim vsoCell1 As Visio.Cell, vsoCell2 As Visio.Cell
    Dim xCoord As Double, yCoord As Double
    Dim nrows As Long, rowNum As Long
    For Each myShape In viShapes
        nrows = myShape.RowCount(Visio.visSectionConnectionPts)
        If nrows > 0 Then ' Check for a 2D shape
            For rowNum = 0 To nrows - 1
                GetConnectionPointsCoord myShape, xCoord, yCoord, rowNum
                'use those coordinates for neighbhood search for connector
                'check connectivity
                'if not, then use shape IDs, and row index to glue connector to connection point
            Next rowNum
        End If
    Next
End Sub


Public Sub GetConnectionPointsCoord(shpObj As Visio.Shape, ByRef xCoord As Double, ByRef yCoord As Double, rowNum As Long) 'Gets the absolute coordinates for a connection point

    Dim ShapeX As Double, ShapeY As Double
   
    ShapeX = shpObj.CellsSRC(Visio.visSectionObject, visRowXFormOut, visXFormPinX).Result(Visio.visNone)
    ShapeX = ShapeX - shpObj.CellsSRC(Visio.visSectionObject, visRowXFormOut, visXFormLocPinX).Result(Visio.visNone)
    ShapeY = shpObj.CellsSRC(Visio.visSectionObject, visRowXFormOut, visXFormPinY).Result(Visio.visNone)
    ShapeY = ShapeY - shpObj.CellsSRC(Visio.visSectionObject, visRowXFormOut, visXFormLocPinY).Result(Visio.visNone)
   
    xCoord = ShapeX + shpObj.CellsSRC(Visio.visSectionConnectionPts, rowNum, visX).Result(Visio.visNone)
    yCoord = ShapeY + shpObj.CellsSRC(Visio.visSectionConnectionPts, rowNum, visY).Result(Visio.visNone)

End Sub

Function CheckConnected(myShape As Visio.Shape, con As Visio.Shape) As Boolean 'Checks if two shapes are connected
    Dim IDs() As Long
    IDs = myShape.ConnectedShapes(visConnectedShapesAllNodes, "")
    Dim i As Long
    CheckConnected = False
    For i = 0 To UBound(IDs, 1)
        If IDs(i) = con.ID Then
            CheckConnected = True
            Exit For
        End If
    Next i
End Function

wapperdude

Sorry for the delay in responding.

First, it would seem that the quickest way to get a connection point location, assuming that LocPinX and LocPinY are set to 1/2 width and 1/2 height., would be to either add or subtract appropriate amounts from the PinX and PinY values.  For example if row_1 was Width*0, Height*0.5, then, the connection point has page coordinates =PinX-width*0.5 and PinY.   Also assumes that shape is not part of a group.

Finding coordinates of the connector is matter of looking at BeginX,Y and EndX,Y.  If an end is connected, the entries will be formulas, otherwise, they are values.  Compare values to see which match.

I've attached a simple, but crude code that finds 1D shapes that have either only one or no connections.  Don't care if both ends connected.  If neither end is connected, it will not be part of the connects object, so that cannot be a search criteria.  Once an appropriate 1D shape found, there's a msgbox, then, program does a 2nd loop thru all the shapes (clunky), to find what shapes touch the connector.  Assumes there's touching going on.  Another msgbox.

At this point, need code development to (a) find end points of the 1D shape, and then (b) search thru the touching shape to see which connection point has matching X & Y coordinates.  This then provides necessary information to fill in the glueto formulas.

HTH
Wapperdude
Visio 2019 Pro

wapperdude

Updated code.  Assumes that connector ends and connections points touch.  Code will glue all connectors to coinciding connection points.

Wapperdude
Visio 2019 Pro

doudou

Quote from: wapperdude on September 07, 2014, 01:23:30 AM
Updated code.  Assumes that connector ends and connections points touch.  Code will glue all connectors to coinciding connection points.

Wapperdude

Thanks again for your invaluable help and thorough answers, this works great. However, I think it will be great if we can make a little more general, in the sense that the 1D shape and 2D shape don't just need to be touching to get glued but also if the connection point on the 2D shape is within a certain tolerance level (set by the user) from the starting or ending point on the 1D. I think we only need to modify the following conditional statement in your code with another condition.

If intReturnValue = visSpatialTouching Then


thanks again

Jean

wapperdude

I opted for the touching requirement for two reasons:
  1.  It was the easiest to implement.   ::)
  2.  For non-touching situations, there might be more than one equally possible solution.  If that were ever to occur, then it would really be better for user intervention rather than arbitrary machine decision.  It would be relatively simple to determine if the 1D shape is not touching anything at either end, and then, perhaps, change its color to make it stand out.  The user could then manually intervene.

Anyway, I think there are enough pieces in the code for you to embellish as desired.  Google Visio, vba, spatialneighbors for additional info.

Wapperdude
Visio 2019 Pro

Thomas Winkel

Hello friends,

thank you for this useful discussions and coding.
Based on this work I created the following code:


Sub reconnect()
    Dim cons As Visio.Selection
    Dim con As Visio.Shape
    Dim shp As Visio.Shape
    Dim sel As Visio.Selection
    Dim xBeg As Double, xEnd As Double, yBeg As Double, yEnd As Double
    Dim xS As Double, yS As Double, xP As Double, yP As Double
    Dim i As Integer
   
    Const dXY = 0.1
    Const tolerance = 0.001
   
    Set cons = ActivePage.CreateSelection(visSelTypeByRole, visSelModeSkipSuper, visRoleSelConnector)
   
    For Each con In cons
        If con.Connects.Count < 2 Then
            xBeg = con.CellsU("BeginX")
            yBeg = con.CellsU("BeginY")
            xEnd = con.CellsU("EndX")
            yEnd = con.CellsU("EndY")
           
            Set sel = con.SpatialNeighbors(visSpatialTouching, tolerance, 0)
           
            For Each shp In sel
                For i = 0 To shp.RowCount(visSectionConnectionPts) - 1
                    xS = shp.CellsSRC(visSectionConnectionPts, i, visCnnctX)
                    yS = shp.CellsSRC(visSectionConnectionPts, i, visCnnctY)
                    shp.XYToPage xS, yS, xP, yP
                   
                    If (Abs(xP - xBeg) < dXY) And (Abs(yP - yBeg) < dXY) Then
                        con.CellsU("BeginX").GlueTo shp.CellsSRC(visSectionConnectionPts, i, 0)
                    End If
                    If (Abs(xP - xEnd) < dXY) And (Abs(yP - yEnd) < dXY) Then
                        con.CellsU("EndX").GlueTo shp.CellsSRC(visSectionConnectionPts, i, 0)
                    End If
                Next i
            Next shp
        End If
    Next con
End Sub


Hints:

  • Use XYToPage to transform the local connection point coordinates to page coordinates, so also flipped or rotated shapes are supported.
  • Iterate only through unconnected connector shapes.
  • Connection points in sub-shapes are not considered.