.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

speed up code

6 REPLIES 6
Reply
Message 1 of 7
fallright
777 Views, 6 Replies

speed up code

I've made some code to find all blocks "vbredak" in a Drawing.

Version 1 "zbl" is the slowest version.

Version 2 "zbl2" is much faster. But in this version I can't find blocks in another drawing (f.i. an xref)

 

When I change in version 1

     Dim mydb As Database = myDWG.Database      Dim mydb As DatabaseServices.Database"c:\test.dwg", IO.FileShare.Read, True, "")

by 

     mydb.ReadDwgFile(

 

I can search in other drawings.

 

How can I adapt the code in version 2 so I can find blocks in ohter DWG's ?

Or

How can I speed up version 1 ?

 

Here is my code

 

    <CommandMethod("zbl")> _
    Public Sub zbl()
        Dim starttime As Double
        Dim endtime As Double
        starttime = DateAndTime.Timer
        Dim myTransMan As DatabaseServices.TransactionManager
        Dim myTrans As DatabaseServices.Transaction
        Dim myBT As DatabaseServices.BlockTable
        Dim myBTR As DatabaseServices.BlockTableRecord
        Dim myBlockDef As DatabaseServices.BlockTableRecord
        Dim mySTE As DatabaseServices.SymbolTableEnumerator
        Dim myBTE As DatabaseServices.BlockTableRecordEnumerator
        Dim myDWG As ApplicationServices.Document = ApplicationServices.Application.DocumentManager.MdiActiveDocument
        Dim mydb As Database = myDWG.Database
        Dim myEd As EditorInput.Editor = myDWG.Editor
        myTransMan = mydb.TransactionManager
        myTrans = myTransMan.StartTransaction
        myBT = myDB.BlockTableId.GetObject(DatabaseServices.OpenMode.ForRead)
        mySTE = myBT.GetEnumerator
        While mySTE.MoveNext
            myBTR = mySTE.Current.GetObject(DatabaseServices.OpenMode.ForRead)
            If myBTR.IsLayout = True Then
                myBTE = myBTR.GetEnumerator
                Dim myEnt As DatabaseServices.Entity
                While myBTE.MoveNext
                    myEnt = myBTE.Current.GetObject(DatabaseServices.OpenMode.ForRead)
                    If TypeOf myEnt Is DatabaseServices.BlockReference Then
                        Dim MyBlockRef As DatabaseServices.BlockReference
                        MyBlockRef = myEnt
                        If MyBlockRef.BlockName = "vbredak" Then myEd.WriteMessage("vbredak found")

                        If MyBlockRef.IsDynamicBlock = True Then
                            myBlockDef = MyBlockRef.DynamicBlockTableRecord.GetObject(DatabaseServices.OpenMode.ForRead)
                        Else
                            myBlockDef = MyBlockRef.BlockTableRecord.GetObject(DatabaseServices.OpenMode.ForRead)
                            If myBlockDef.Name = "vbredak" Then myEd.WriteMessage("vbredak found")

                        End If
                    End If
                End While
            End If
        End While
        myTrans.Dispose()
        myTransMan.Dispose()
        myDB.Dispose()
        endtime = DateAndTime.Timer
        MsgBox(endtime - starttime & "seconds")
    End Sub


    <CommandMethod("zbl2")> _
 Public Sub zbl2()
        Dim starttime As Double
        Dim endtime As Double
        starttime = DateAndTime.Timer
        Dim myDWG As ApplicationServices.Document
        Dim myEd As EditorInput.Editor
        Dim myPSR As EditorInput.PromptSelectionResult
        Dim mySS As EditorInput.SelectionSet
        Dim myTransMan As DatabaseServices.TransactionManager
        Dim myTrans As DatabaseServices.Transaction
        Dim myObjIds As DatabaseServices.ObjectIdCollection
        Dim myObjId As DatabaseServices.ObjectId
        Dim myBlock As DatabaseServices.BlockReference
        Dim myfilter(0) As DatabaseServices.TypedValue
        Dim mySF As New EditorInput.SelectionFilter(myfilter)
        myDWG = ApplicationServices.Application.DocumentManager.MdiActiveDocument
        myfilter(0) = New DatabaseServices.TypedValue(DatabaseServices.DxfCode.BlockName, "vbredak")
        myEd = myDWG.Editor
        myPSR = myEd.SelectAll(mySF)
        mySS = myPSR.Value
        If Not (mySS Is Nothing) Then
            myTransMan = myDWG.TransactionManager
            myTrans = myTransMan.StartTransaction
            myObjIds = New DatabaseServices.ObjectIdCollection(mySS.GetObjectIds)
            For Each myObjId In myObjIds

                myBlock = myObjId.GetObject(DatabaseServices.OpenMode.ForRead)
                If myBlock.Name = "vbredak" Then myEd.WriteMessage("vbredak found")
            Next
            myTrans.Dispose()
            myTransMan.Dispose()
        End If
        endtime = DateAndTime.Timer
        MsgBox(endtime - starttime & "seconds")
    End Sub

 

 

6 REPLIES 6
Message 2 of 7
_gile
in reply to: fallright

Hi,

 

Try the BlockTableRecord.GetBlockReferenceIds() route.

 

Here's a C# sample:

 

        private ObjectIdCollection GetAllReferences(Database db)
        {
            ObjectIdCollection result = new ObjectIdCollection();
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                foreach (ObjectId btrId in bt)
                {
                    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead);
                    if (!btr.IsLayout && !btr.IsFromExternalReference)
                        foreach (ObjectId refId in btr.GetBlockReferenceIds(true, false))
                        {
                            BlockReference br = (BlockReference)tr.GetObject(refId, OpenMode.ForRead);
                            BlockTableRecord owner = (BlockTableRecord)tr.GetObject(br.OwnerId, OpenMode.ForRead);
                            if (owner.IsLayout)
                                result.Add(br.ObjectId);
                        }
                }
                tr.Commit();
            }
            return result;
        }

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 3 of 7
_gile
in reply to: _gile

Sorry, I misunderstood the request.

 

The following sample returns all block reference ObjectIds which name is 'bName' inserted in the 'db' database spaces.

 

        private ObjectIdCollection GetReferences(Database db, string bName)
        {
            ObjectIdCollection result = new ObjectIdCollection();
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                if (bt.Has(bName))
                {
                    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[bName], OpenMode.ForRead);
                    foreach (ObjectId refId in btr.GetBlockReferenceIds(true, false))
                    {
                        BlockReference br = (BlockReference)tr.GetObject(refId, OpenMode.ForRead);
                        BlockTableRecord owner = (BlockTableRecord)tr.GetObject(br.OwnerId, OpenMode.ForRead);
                        if (owner.IsLayout)
                            result.Add(br.ObjectId);
                    }
                }
                tr.Commit();
            }
            return result;
        }

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 4 of 7
Hallex
in reply to: fallright

You might want to play with data extracton as well

Add reference to AcDx.dll

Tested on A2009 only

Follow link to get more explanation

 

Imports System
Imports System.Text
Imports System.IO
Imports System.Data
Imports System.Reflection
Imports System.Collections.Generic
Imports System.Runtime.InteropServices

Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.Colors
Imports Autodesk.AutoCAD.Runtime
Imports acApp = Autodesk.AutoCAD.ApplicationServices.Application
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.DataExtraction
Imports Autodesk.AutoCAD.Windows
'Imports System.Windows.Forms

<Assembly: CommandClass(GetType(TesterVB.ExtractData))> 
Namespace TesterVB
    Public Class ExtractData

#Region "Data Extraction Example"
        '' based on Kean Walmsley's example from there:
        '' http://through-the-interface.typepad.com/through_the_interface/2008/04/extracting-data.html
        <CommandMethod("zb3")> _
        Public Sub zbllk()

            Dim starttime As Double
            Dim endtime As Double

            Dim sb As StringBuilder = New StringBuilder
            '' Dim fname As String = "C:\Test\bcount.dwg"''<--debug only

            Dim dialog As New System.Windows.Forms.OpenFileDialog

            With dialog
                .CheckPathExists = True
                .CheckPathExists = True
                .DefaultExt = "dxf"
                .DereferenceLinks = True
                .Multiselect = False
                .Filter = "Drawing(*.dwg)|*.dwg|All files (*.*)|*.*"
                .Title = "Select drawing"
                .FilterIndex = 1

            End With

            If dialog.ShowDialog() <> System.Windows.Forms.DialogResult.OK Then
                Return
            End If

            Dim fname As String = dialog.FileName

            starttime = DateAndTime.Timer
            Try
                Dim dataTable As System.Data.DataTable = extractNames(fname)
                Dim bname As String = "vbredak"
                Dim distinctTable As System.Data.DataTable = ExtractBlocksByName(dataTable, bname) ''<--block datatable
                endtime = DateAndTime.Timer
                For Each row As System.Data.DataRow In distinctTable.Rows
                    Dim items() As Object = row.ItemArray
                    Dim msg As String = vbTab
                    For i As Integer = 0 To items.Length - 1
                        msg = msg & distinctTable.Columns(i).ColumnName & " = " & items(i).ToString & vbCr
                    Next
                    sb.Append(msg)
                Next

                MsgBox("Time: " & endtime - starttime & " seconds;" & vbCr & "Found " & dataTable.Rows.Count & " blocks", MsgBoxStyle.OkOnly, "DataEXtraction Method")

            Catch ex As System.Exception
                MsgBox(vbCr & ex.ToString & vbCr & ex.StackTrace)
            End Try
            MsgBox(sb.ToString)

        End Sub
        ''' <summary>
        ''' 
        ''' </summary>
        ''' <param name="source"></param>
        ''' <param name="bname"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function ExtractBlocksByName(ByVal source As System.Data.DataTable, ByVal bname As String) As System.Data.DataTable
            Dim result As New System.Data.DataTable()
            Dim filter As String = String.Format("{0}='{1}'", "AcDxObjectTypeGlobalName", "BlockReferenceTypeDescriptor." + bname)

            Dim selrows As System.Data.DataRow() = source.Select(filter)

            Dim first As System.Data.DataRow = DirectCast(selrows(0), System.Data.DataRow)
            result = source.Clone()
            For Each dr As System.Data.DataRow In selrows
                result.ImportRow(dr)
            Next
            Dim tags As New List(Of String)()
            Dim isattrib As String = "BlockReferenceAttribute."

            For Each column As System.Data.DataColumn In first.Table.Columns
                Dim capture As String = column.ColumnName
                If first(capture).ToString() <> String.Empty Then
                    If capture.StartsWith(isattrib, StringComparison.InvariantCultureIgnoreCase) Then
                        Dim found As String = capture.Replace(isattrib, String.Empty)

                        tags.Add(found)

                    End If

                End If
            Next


            Dim dcolumns As New List(Of String)() From { _
  "AcDxObjectTypeName", _
  "AcDxHandleData", _
  "Layer", _
  "Position.X", _
  "Position.Y", _
  "Position.Z", _
  "Rotation", _
  "ScaleFactors.X", _
  "ScaleFactors.Y", _
  "ScaleFactors.Z" _
 }
            If tags.Count > 0 Then
                For n As Integer = 0 To tags.Count - 1
                    dcolumns.Add("BlockReferenceAttribute." + tags(n))
                Next
            End If

            Dim cnt As Integer = dcolumns.Count
            Dim columns As String() = New String(cnt - 1) {}
            dcolumns.CopyTo(columns)
            Dim dvw As DataView = result.DefaultView
            result = dvw.ToTable(False, columns)
            Dim xmlcolumns As New List(Of String)() From { _
  "BlockName", _
  "Handle", _
  "Layer", _
  "Insertion_X", _
  "Insertion_Y", _
  "Insertion_Z", _
  "Rotation", _
  "Scale_X", _
  "Scale_Y", _
  "Scale_Z" _
 }

            If tags.Count > 0 Then
                For n As Integer = 0 To tags.Count - 1

                    xmlcolumns.Add(tags(n))
                Next
            End If
            For n As Integer = 0 To xmlcolumns.Count - 1
                result.Columns(n).ColumnName = xmlcolumns(n)
            Next
            Return result
        End Function


        ''' <summary>
        ''' 
        ''' </summary>
        ''' <param name="fname"></param>
        ''' <remarks></remarks>
        Public Function extractNames(ByVal fname As String) As System.Data.DataTable
            Dim dataTable As New System.Data.DataTable()
            Dim blkTable As New System.Data.DataTable()
            Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument

            Dim ed As Editor = doc.Editor

            Dim tags As New List(Of String)()
            If Not System.IO.File.Exists(fname) Then
                ed.WriteMessage(vbLf & "Drawing file does not exist.")
                Return Nothing
                Exit Function
            End If
            Dim es As IDxExtractionSettings = New DxExtractionSettings()

            Dim de As IDxDrawingDataExtractor = es.DrawingDataExtractor

            de.Settings.ExtractFlags = ExtractFlags.None Or ExtractFlags.ModelSpaceOnly

            de.Settings.ExtractFlags = ExtractFlags.ExtractBlockOnly
            Dim fr As IDxFileReference = New DxFileReference(Path.GetDirectoryName(fname), fname)

            de.Settings.DrawingList.AddFile(fr)

            ' Scan the drawing for object types & their properties

            de.DiscoverTypesAndProperties(Path.GetDirectoryName(fname))

            Dim types As List(Of IDxTypeDescriptor) = de.DiscoveredTypesAndProperties

            ' Select all the types and properties for extraction

            ' by adding them one-by-one to these two lists

            Dim selTypes As New List(Of String)()

            Dim selProps As New List(Of String)()

            For Each type As IDxTypeDescriptor In types


                selTypes.Add(type.GlobalName)

                For Each pr As IDxPropertyDescriptor In type.Properties


                    If Not selProps.Contains(pr.GlobalName) Then


                        selProps.Add(pr.GlobalName)
                    End If

                Next
            Next

            de.Settings.SetSelectedTypesAndProperties(types, selTypes, selProps)

            ' Now perform the extraction itself

            de.ExtractData(Path.GetDirectoryName(fname))

            ' Get the results of the extraction

            dataTable = de.ExtractedData

            If dataTable.Rows.Count > 0 Then

                dataTable.TableName = "Block"

                Dim selrows As DataRow() = dataTable.Select("AcDxObjectTypeGlobalName Like 'BlockReferenceTypeDescriptor.%'")

                blkTable = dataTable.Clone()

                For Each dr As DataRow In selrows
                    blkTable.ImportRow(dr)
                Next
                '' commented lines is just for populating form listbox control
                'Dim columns As String() = New String() {"AcDxObjectTypeName"}

                'Dim dvw As DataView = blkTable.DefaultView
                'distinctTable = dvw.ToTable(False, columns)

                'Dim bnames As New List(Of String)()
                'For Each dr As System.Data.DataRow In distinctTable.Rows
                '    Dim bname As String = dr(0).ToString()
                '    If Not bnames.Contains(bname) Then
                '        bnames.Add(bname)
                '    End If
                'Next

                'Me.lst.DataSource = bnames'' <-- to fill ListBox only

                'lst.SelectedIndex = -1'' <-- set selected items to nothing
            End If
            Return blkTable
        End Function

End Class
#End Region

 

 

~'J'~

_____________________________________
C6309D9E0751D165D0934D0621DFF27919
Tags (1)
Message 5 of 7
fallright
in reply to: _gile

Gille, your code works really good and FAST !

 

I've tried to search also for anonymous blocks. changing

 For Each refId As ObjectId In btr.GetBlockReferenceIds(True, False)

 

in

 

For Each refId As ObjectId In btr.GetAnonymousBlockIds()

didn't work. Any ideas ?

 

And second question: can you also use the code to find polylines ?

Message 6 of 7
_gile
in reply to: fallright

GetAnonymousBlockIds() returns the anonymous block table record ObjectId collection not the block reference one.

If you want to get all block references for a specific bloc name (named and anonymous), you can go this way:

 

 

        private ObjectIdCollection GetReferences(Database db, string bName)
        {
            ObjectIdCollection result = new ObjectIdCollection();
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt =
                    (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                if (bt.Has(bName))
                {
                    BlockTableRecord btr =
                        (BlockTableRecord)tr.GetObject(bt[bName], OpenMode.ForRead);
                    foreach (ObjectId refId in btr.GetBlockReferenceIds(true, false))
                    {
                        BlockReference br =
                            (BlockReference)tr.GetObject(refId, OpenMode.ForRead);
                        BlockTableRecord owner =
                            (BlockTableRecord)tr.GetObject(br.OwnerId, OpenMode.ForRead);
                        if (owner.IsLayout)
                            result.Add(br.ObjectId);
                    }
                    foreach (ObjectId id in btr.GetAnonymousBlockIds())
                    {
                        BlockTableRecord anon =
                            (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
                        foreach (ObjectId refId in anon.GetBlockReferenceIds(true, false))
                        {
                            BlockReference br =
                                (BlockReference)tr.GetObject(refId, OpenMode.ForRead);
                            BlockTableRecord owner =
                                (BlockTableRecord)tr.GetObject(br.OwnerId, OpenMode.ForRead);
                            if (owner.IsLayout)
                                result.Add(br.ObjectId);
                        }
                    }
                }
                tr.Commit();
            }
            return result;
        }

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 7 of 7
Jeffrey_H
in reply to: _gile

If you take look at this link

http://www.theswamp.org/index.php?topic=37089.0

 

For polylines

Looking at the link you can see other options, but the basic idea is to check a property of the ObjectId to see if it is the type object you want then open the object.

That way you skip a bunch of useless and sometimes expensive cycles opening and closing objects you do not need to.

 

Here this uses ObjectId.ObjectClass.Name which ObjectClass gets the RxClass of the object and checks to see if the name is "AcDbPolyline" first then opens it and prints the length


    <CommandMethod("GetPolyLinesFast")> _
        Public Sub GetPolyLinesFast()
 
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim db As Database = doc.Database
            Dim ed As Editor = doc.Editor
 
            Using trx As Transaction = db.TransactionManager.StartTransaction()
 
                Dim mdlSpaceBtr As BlockTableRecord = SymbolUtilityServices.GetBlockModelSpaceId(db).GetObject(OpenMode.ForRead)
 
                For Each objId As ObjectId In mdlSpaceBtr
 
                    If objId.ObjectClass.Name = "AcDbPolyline" Then
                        Dim plyLne As Polyline = trx.GetObject(objId, OpenMode.ForRead)
                        ed.WriteMessage(String.Format("{0} The PolyLine Length's is {1}", vbCrLf, plyLne.Length))
                    End If
 
                Next
                trx.Commit()
            End Using
        End Sub
You can also find your answers @ TheSwamp

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost