Sort selected items

Sort selected items

jplujan
Advocate Advocate
3,087 Views
16 Replies
Message 1 of 17

Sort selected items

jplujan
Advocate
Advocate

Hello

I have an application in which I make an attribute numbering by order of selection of blocks, so far to make sure the order, the selection was made by border, my question is if it is possible to give this order in a more automatic way since in some case I have many blocks.

Thank you

0 Likes
Accepted solutions (1)
3,088 Views
16 Replies
Replies (16)
Message 2 of 17

grobnik
Collaborator
Collaborator

Hi @jplujan ,

Do you have a simple drawing to share with us, in order to understand better the request ?

Of course you can change automatically the attribute, based upon insertion point coordinates for example, or some others criteria, or better if you create an excel file (or text file) with attribute to be change already ordered as you wish, find a reference of set of blocks to be change, and made modification.

But criteria shall be defined before, and the same for attribute value to be change, this is the reason I'm asking a sample dwg.

 

Bye

 

0 Likes
Message 3 of 17

jplujan
Advocate
Advocate

Hello

Thanks for your dedication.

In example A the selection is made by window and the numbering does not perform it correctly since the gaps were not drawn in order

In example B what changed was the type of selection that was made by border, here the numbering does it correctly, but when there are many blocks it is a bit tedious

I don't know if there would be some way to sort the items in the selection by window or in a selection all that would be the fastest way.

 

Thanks again

0 Likes
Message 4 of 17

grobnik
Collaborator
Collaborator

Hi @jplujan , we received your file, but just some other questions, that seems to be stupid but we don't know in deep your full application:

1) do you need only to change the attribute value and ordering by ascendant value ? or do you need to move blocks into drawing ?

Seems that all blocks are the same, but I guess this is only an example, the showed circuit breaker size could change I guess...

2) Do you prefer to export attributes and blocks position into an excel file, manage it an import again into the drawing ? or do you prefer to apply modification by automatic procedure on drawing ?

3) Do you have any experience with VBA ?. At least load, run little debug...

 

Bye

0 Likes
Message 5 of 17

jplujan
Advocate
Advocate

Hello again

 

1) I only need to change the value of the attribute.
2) The size of the blqoes does not matter.
3) I prefer to apply the modification by automatic procedure.
4) If I have already programmed in both VBA and NET.

 

Thank you

0 Likes
Message 6 of 17

grobnik
Collaborator
Collaborator

Hi @jplujan could you try to run this code ? Of course the code it's very poor, but it's working.

 

 

Sub Bloques()
Dim Entity As Object
Dim ATT As Variant
Dim ATT_VALUE() As Variant
Dim BL_INS_X() As Variant
A = 1
For Each Entity In ThisDrawing.ModelSpace
    If Entity.ObjectName = "AcDbBlockReference" Then
        If Entity.Name = "BL_01" Then
            If Entity.HasAttributes = True Then
                ReDim Preserve ATT_VALUE(A)
                ReDim Preserve BL_INS_X(A)
                ATT = Entity.GetAttributes
                ATT_VALUE(A) = Val(ATT(0).TextString)
                BL_INS = Entity.InsertionPoint
                BL_INS_X(A) = BL_INS(0)
                A = A + 1
            End If
        End If
    End If
Next
SortArray ATT_VALUE()
SortArray BL_INS_X()
'A = 1
For Each Entity In ThisDrawing.ModelSpace
    If Entity.ObjectName = "AcDbBlockReference" Then
        If Entity.Name = "BL_01" Then
            BL_INS = Entity.InsertionPoint
            For QQ = LBound(BL_INS_X) To UBound(BL_INS_X)
                If BL_INS(0) = BL_INS_X(QQ) Then
                    If Entity.HasAttributes = True Then
                        ATT = Entity.GetAttributes
                        ATT(0).TextString = ATT_VALUE(QQ)
                    End If
                End If
            Next
        End If
    End If
Next
ThisDrawing.Regen acAllViewports

End Sub

Function SortArray(ArrayToSort() As Variant)
    Dim x As Long, y As Long
    Dim TempTxt1 As Variant
    Dim TempTxt2 As Variant

    For x = LBound(ArrayToSort) To UBound(ArrayToSort)
        For y = x To UBound(ArrayToSort)
            If (ArrayToSort(y)) < (ArrayToSort(x)) Then
                TempTxt1 = ArrayToSort(x)
                TempTxt2 = ArrayToSort(y)
                ArrayToSort(x) = TempTxt2
                ArrayToSort(y) = TempTxt1
            End If
        Next y
    Next x
End Function

 

 

 

What the code is doing is:

Search all blocks with Name "BL_01", Get attributes, and X insertion Point.

Make an ascendant sort by Attribute Value and X value from lower to higher.

Change attributes to all blocks, starting from the one which has lowest  X insertion point value.

grobnik_0-1594314842497.png

 

Let us know.

Regards

 

0 Likes
Message 7 of 17

grobnik
Collaborator
Collaborator

HI @jplujan , after further investigation the above code is running well if objects are on the same row (doesn't matter the numerical sequence it will be ordered), if it are mixed also with y insertion point modification code shall be reviewed.

If I'll have time I'll try to make some tests.

Message 8 of 17

jplujan
Advocate
Advocate

Hello

I will try what it suggests to me

Thank you

Message 9 of 17

grobnik
Collaborator
Collaborator

Hi @jplujan after some test I been able to adjust the code also for 2 lines

Here below the example as picture.

here the code

 

Function SortArray(ArrayToSort() As Variant)
    Dim x As Long, y As Long
    Dim TempTxt1 As Variant
    Dim TempTxt2 As Variant

    For x = LBound(ArrayToSort) To UBound(ArrayToSort)
        For y = x To UBound(ArrayToSort)
            If (ArrayToSort(y)) < (ArrayToSort(x)) Then
                TempTxt1 = ArrayToSort(x)
                TempTxt2 = ArrayToSort(y)
                ArrayToSort(x) = TempTxt2
                ArrayToSort(y) = TempTxt1
            End If
        Next y
    Next x
End Function

Public Sub QuickSortArray(ByRef SortArray As Variant, Optional lngMin As Long = -1, Optional lngMax As Long = -1, Optional lngColumn As Long = 0)
    On Error Resume Next

    'Sort a 2-Dimensional array

    ' SampleUsage: sort arrData by the contents of column 3
    '
    '   QuickSortArray arrData, , , 3

    '
    'Posted by Jim Rech 10/20/98 Excel.Programming

    'Modifications, Nigel Heffernan:

    '       ' Escape failed comparison with empty variant
    '       ' Defensive coding: check inputs

    Dim i As Long
    Dim j As Long
    Dim varMid As Variant
    Dim arrRowTemp As Variant
    Dim lngColTemp As Long

    If IsEmpty(SortArray) Then
        Exit Sub
    End If
    If InStr(TypeName(SortArray), "()") < 1 Then  'IsArray() is somewhat broken: Look for brackets in the type name
        Exit Sub
    End If
    If lngMin = -1 Then
        lngMin = LBound(SortArray, 1)
    End If
    If lngMax = -1 Then
        lngMax = UBound(SortArray, 1)
    End If
    If lngMin >= lngMax Then    ' no sorting required
        Exit Sub
    End If

    i = lngMin
    j = lngMax

    varMid = Empty
    varMid = SortArray((lngMin + lngMax) \ 2, lngColumn)

    ' We  send 'Empty' and invalid data items to the end of the list:
    If IsObject(varMid) Then  ' note that we don't check isObject(SortArray(n)) - varMid *might* pick up a valid default member or property
        i = lngMax
        j = lngMin
    ElseIf IsEmpty(varMid) Then
        i = lngMax
        j = lngMin
    ElseIf IsNull(varMid) Then
        i = lngMax
        j = lngMin
    ElseIf varMid = "" Then
        i = lngMax
        j = lngMin
    ElseIf VarType(varMid) = vbError Then
        i = lngMax
        j = lngMin
    ElseIf VarType(varMid) > 17 Then
        i = lngMax
        j = lngMin
    End If

    While i <= j
        While SortArray(i, lngColumn) < varMid And i < lngMax
            i = i + 1
        Wend
        While varMid < SortArray(j, lngColumn) And j > lngMin
            j = j - 1
        Wend

        If i <= j Then
            ' Swap the rows
            ReDim arrRowTemp(LBound(SortArray, 2) To UBound(SortArray, 2))
            For lngColTemp = LBound(SortArray, 2) To UBound(SortArray, 2)
                arrRowTemp(lngColTemp) = SortArray(i, lngColTemp)
                SortArray(i, lngColTemp) = SortArray(j, lngColTemp)
                SortArray(j, lngColTemp) = arrRowTemp(lngColTemp)
            Next lngColTemp
            Erase arrRowTemp

            i = i + 1
            j = j - 1
        End If
    Wend

    If (lngMin < j) Then Call QuickSortArray(SortArray, lngMin, j, lngColumn)
    If (i < lngMax) Then Call QuickSortArray(SortArray, i, lngMax, lngColumn)

End Sub


Sub Bloques_NEW()

   Dim sset As AcadSelectionSet
   Set sset = ThisDrawing.SelectionSets.Add("TestSet4")
   Dim Entity As Object
   Dim ATT As Variant
   Dim ATT_VALUE() As Variant
   Dim BL_INS_X_Y() As Variant

   Dim filterType As Variant
   Dim filterData As Variant
   'Dim p1(0 To 2) As Double
   'Dim p2(0 To 2) As Double
  
   Dim grpCode(0) As Integer
   grpCode(0) = 2
   filterType = grpCode
   Dim grpValue(0) As Variant
   grpValue(0) = "BL_01"
   filterData = grpValue
   
   sset.Select acSelectionSetAll, , , filterType, filterData
  
   'Debug.Print "Entities: " & Str(sset.Count)
   
Q = sset.Count
If Q > 0 Then
    ReDim ATT_VALUE(Q)
    ReDim BL_INS_X_Y(Q, 1)
    A = 0
    For Each Entity In sset
        If Entity.HasAttributes = True Then
            ATT = Entity.GetAttributes
            ATT_VALUE(A) = Val(ATT(0).TextString)
            BL_INS = Entity.InsertionPoint
            BL_INS_X_Y(A, 0) = BL_INS(0)
            BL_INS_X_Y(A, 1) = BL_INS(1)
            A = A + 1
        End If
    Next
    SortArray ATT_VALUE()
    Call QuickSortArray(BL_INS_X_Y, , , 0)
 
 For Each Entity In sset
    BL_INS = Entity.InsertionPoint
    For QQ = LBound(BL_INS_X_Y) To UBound(BL_INS_X_Y)
        If BL_INS(0) = BL_INS_X_Y(QQ, 0) Then
            If BL_INS(1) = BL_INS_X_Y(QQ, 1) Then
                If Entity.HasAttributes = True Then
                    ATT = Entity.GetAttributes
                    ATT(0).TextString = ATT_VALUE(QQ)
                End If
            End If
        End If
    Next
Next
        
    End If
ThisDrawing.Regen acAllViewports
  
   sset.Delete
  
End Sub

 

grobnik_0-1594406876401.png

As you can see the order of attribute will be the same from lower to highest, but it's shifted by row.

Of course code could be optimized, but it's only a test, a staring point.

I'll try to test if it's possible to order consecutive by row... I'll keep you informed.

0 Likes
Message 10 of 17

jplujan
Advocate
Advocate

Hi @grobnik

 

Hello, thank you for your dedication to this topic.

The idea is that in each format there is only one row of blocks and the drawing has many formats. I leave you an example.

 

In any case, with what you have sent me, I will continue investigating.

 

Thanks again

 

 

0 Likes
Message 11 of 17

grobnik
Collaborator
Collaborator

Hi @jplujan ,

in this case, if blocks are in the same row, and inserted with a wrong sequence only, the below code solve completely your issue.

Now I cannot open the last drawing you sent with last message reply, I opened the first one, which the code I based on.

Sub Bloques()
Dim Entity As Object
Dim ATT As Variant
Dim ATT_VALUE() As Variant
Dim BL_INS_X() As Variant
A = 1
For Each Entity In ThisDrawing.ModelSpace
    If Entity.ObjectName = "AcDbBlockReference" Then
        If Entity.Name = "BL_01" Then
            If Entity.HasAttributes = True Then
                ReDim Preserve ATT_VALUE(A)
                ReDim Preserve BL_INS_X(A)
                ATT = Entity.GetAttributes
                ATT_VALUE(A) = Val(ATT(0).TextString)
                BL_INS = Entity.InsertionPoint
                BL_INS_X(A) = BL_INS(0)
                A = A + 1
            End If
        End If
    End If
Next
SortArray ATT_VALUE()
SortArray BL_INS_X()
'A = 1
For Each Entity In ThisDrawing.ModelSpace
    If Entity.ObjectName = "AcDbBlockReference" Then
        If Entity.Name = "BL_01" Then
            BL_INS = Entity.InsertionPoint
            For QQ = LBound(BL_INS_X) To UBound(BL_INS_X)
                If BL_INS(0) = BL_INS_X(QQ) Then
                    If Entity.HasAttributes = True Then
                        ATT = Entity.GetAttributes
                        ATT(0).TextString = ATT_VALUE(QQ)
                    End If
                End If
            Next
        End If
    End If
Next
ThisDrawing.Regen acAllViewports

End Sub

Function SortArray(ArrayToSort() As Variant)
    Dim x As Long, y As Long
    Dim TempTxt1 As Variant
    Dim TempTxt2 As Variant

    For x = LBound(ArrayToSort) To UBound(ArrayToSort)
        For y = x To UBound(ArrayToSort)
            If (ArrayToSort(y)) < (ArrayToSort(x)) Then
                TempTxt1 = ArrayToSort(x)
                TempTxt2 = ArrayToSort(y)
                ArrayToSort(x) = TempTxt2
                ArrayToSort(y) = TempTxt1
            End If
        Next y
    Next x
End Function

 

Let me know.

Bye

 

0 Likes
Message 12 of 17

jplujan
Advocate
Advocate

Hello
I am testing it and it does not do the correct numbering, it does the numbering according to the order in which they were drawn.
A greeting

0 Likes
Message 13 of 17

grobnik
Collaborator
Collaborator

Hi @jplujan 

I'm seeing just now the second drawing sample, and the blocks are not in the same row, there are several "border" with inside the blocks, I don't know how this border will be represented on final drawing, if each border will be reported on paper space we can try to manage the code, if they as spreaded on model space area I don't know how you can manage the ordering.

In any case the blocks are not ordered by drawn sequence but only by X insertion point, which is different.

You said me that blocks was on the same row, of course they are on the same row, but rows are many and spreaded on model space area.

 

0 Likes
Message 14 of 17

jplujan
Advocate
Advocate

Hello

 

Sorry for not explaining me correctly, the borders do not have to be managed only by the blocks that are inside each one and in order from top left to right and continuing on the next row.

I do not want to take more of your time thank you very much, with what you have offered me I will try to continue.

 

Thank you so much for everything

0 Likes
Message 15 of 17

grobnik
Collaborator
Collaborator
Accepted solution

Hi @jplujan ,

probably I found the solution, with the below code you could have the amount of objects inside a corner user selected.

 

So the code ask you to select the border (mainly a polyline), once selected you have back ssetObj which is representing the collection of objects inside the selected polyline (border), once you have the collection you can use my first code for ordering the object inside.

You have to define before the procedure, as Global the amount the object find in the selected border because the variable it's coming from another sub.

 

Global ssetObj As AcadSelectionSet

 

I tried with the first one on last fie with multiple border you attached to last post and it's working.

In my opinion doesn't matter if block number inside the border start with 1 or 10, or something else, the procedure will order the objects from lower to higher attribute value.

 

 

Global ssetObj As AcadSelectionSet

Sub test_pl()

Dim oEnt As AcadEntity
Dim Pt(0 To 2) As Double
Dim oLWP As AcadLWPolyline
Dim oP As AcadPolyline
Dim dblNewCords As Variant
Dim ssetObj As AcadSelectionSet
On Error Resume Next
ThisDrawing.SelectionSets.Item("TEST_SSET2").Delete

ThisDrawing.Utility.GetEntity oEnt, Pt, "Select a polyline"
Set oLWP = oEnt
dblCurCords = oLWP.Coordinates
iMaxCurArr = UBound(dblCurCords)
iMaxNewArr = ((iMaxCurArr + 1) * 1.5) - 1
ReDim dblNewCords(iMaxNewArr) As Double
iCurArrIdx = 0: iCnt = 1
For iNewArrIdx = 0 To iMaxNewArr
If iCnt = 3 Then
dblNewCords(iNewArrIdx) = 0
iCnt = 1
Else
dblNewCords(iNewArrIdx) = dblCurCords(iCurArrIdx)
iCurArrIdx = iCurArrIdx + 1
iCnt = iCnt + 1
End If
Next

Set ssetObj = ThisDrawing.SelectionSets.Add("TEST_SSET2")
ssetObj.SelectByPolygon acSelectionSetCrossingPolygon, dblNewCords
MsgBox ssetObj.Count
Call Bloques(ssetObj) ' MY last procedure shall become a function.
End Sub
' --------BLOQUES PROCEDURE MODIFICATION------
'Function Bloques(ssetObj) ' To modify with Function instead Sub
'...
'...
For Each Entity In ssetObj ' To modify instead this drawing.modelspace
'...
'...
SortArray ATT_VALUE()
SortArray BL_INS_X()
'A = 1
For Each Entity In ssetObj ' To modify instead this drawing.modelspace
    If Entity.ObjectName = "AcDbBlockReference" Then 
'...

 

Let us know

 

0 Likes
Message 16 of 17

jplujan
Advocate
Advocate

Hello
I am testing and it seems to work.

Thanks for everything

0 Likes
Message 17 of 17

grobnik
Collaborator
Collaborator

Hi @jplujan your welcome,

so finally, even if specification was not so clear 🤣 your issue  have been solved.

Bye

0 Likes