Kean Walmsley

July 2009

Sun Mon Tue Wed Thu Fri Sat
      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  

Twitter Updates

    follow me on Twitter



    « Autodesk University 2007 - AU BEAT | Main | AU Handouts: There's More to .DWG Than AutoCAD® - Part 2 »

    October 17, 2007

    AU Handouts: There's More to .DWG Than AutoCAD® - Part 1

    [As mentioned in the last post, I'll be publishing chunks of the handouts for my AU classes via this blog over the coming weeks]

    Introduction

    This session focuses on downstream technologies that can be used to access DWG content outside of AutoCAD-based products. We’re going to start by creating a basic model inside AutoCAD comprised of 3D solid objects, and then look at a simple AutoCAD .NET application to access information about those solids. We will then take the same code and build a non-graphical (in terms of geometry - it does, after all, have a GUI) application around it using Autodesk RealDWG. Later on we’ll look at the same code running inside a custom-branded design application built using AutoCAD OEM.

    We will also spend time looking at DWG TrueView and the DWG HOOPS viewer, to understand how they might be used to enhance the graphical display of DWGs without the editing overhead introduced with AutoCAD-based products.

    Firstly, why are we using 3D solids in this example? The choice was somewhat arbitrary – the point is really to demonstrate the ability to access properties of objects stored in a DWG file without AutoCAD running – but it does suit our overall purpose for a few reasons:

    1. As 3D entities, 3D solids allow us to evaluate the 3D capabilities of the viewing technologies we’re looking at.
    2. They provide precise data that is of interest to us – in this case we’re going to mine their volume – and can also have data attached of a less precision-oriented nature, such as materials.
    3. Regarding both the above points, they also feed into the fact that this demo continues in another AU session, “DE401-2: Enriching Your DWF™”, which focuses on working with DWF data that is published from AutoCAD. In that session we’ll look at how to harness both 2D and 3D data to implement rich applications focused on published – rather than native – data.

    A quick word on the programming technology used in this demonstration. The code samples are all in VB.NET: regular readers of my blog (http://blogs.autodesk.com/through-the-interface) will know I have a preference for C#, having spent years working with C++, but I’m sticking with VB.NET for this demonstration: we originally developed the material using VB.NET to reach the broader audience of Visual Basic professionals.

    These handouts, along with the sample projects demonstrated within them, have been posted to my blog: http://blogs.autodesk.com/through-the-interface. Only significant portions of code will be highlighted in this document, and even those should not need to be typed back in. :-)

    [You can download the source used in this demo from here for the AutoCAD application and here for the RealDWG application.]

    Create a simple 3D model in AutoCAD and access it using .NET

    There are various 3D modeling commands available in AutoCAD, of which detailed review is outside the scope of this session. We simply want to populate a model with a number of 3D solids with materials attached. Here’s the kind of thing we’re looking for:

    3d_model

    Figure 1 – a simple set of 3D solids inside AutoCAD

    Now we’re ready to start looking at the code we’ll use inside an AutoCAD application, as well as within RealDWG and AutoCAD OEM, to access the information in this model.

    Here’s code from a file called SolidInfo.vb to represent a data structure we’ll use to collect information about our 3D solids – their type, location, ID (the AutoCAD handle) and volume.

    Imports Autodesk.AutoCAD.DatabaseServices

    Imports Autodesk.AutoCAD.Geometry

    Imports Autodesk.AutoCAD.Interop

    Imports Autodesk.AutoCAD.Interop.Common

    Imports System.Math


    ' The SolidData class encapsulates the data we

    ' want to collect and display for Solid3d objects


    Public Class SolidData


      ' We care about Type, Location, ID & Volume


      Private m_Type As String

      Private m_Location As String

      Private m_Id As String

      Private m_Volume As Double


      Public Property Type()

        Get

          Return m_Type

        End Get

        Set(ByVal value)

          m_Type = value

        End Set

      End Property

      Public Property Location()

        Get

          Return m_Location

        End Get

        Set(ByVal value)

          m_Location = value

        End Set

      End Property

      Public Property Id()

        Get

          Return m_Id

        End Get

        Set(ByVal value)

          m_Id = value

        End Set

      End Property

      Public Property Volume()

        Get

          Return m_Volume

        End Get

        Set(ByVal value)

          m_Volume = value

        End Set

      End Property

    End Class


    ' The SolidList class is simply a list of SolidData

    ' (used when defining the data a grid is bound to)


    Public Class SolidList

      Inherits System.ComponentModel.BindingList(Of SolidData)

    End Class

    Now we have a function that will take a database and iterate through its contents, collecting information about 3D solid objects and adding them to a Data-Bound Grid passed in as the second argument. This grid should be created in the UI to be bound to objects of type SolidList (defined above).

    Public Module SolidFunctions


      ' This function will get SolidData for each Solid3d

      ' in a Database and add them to a BindingSource (a grid

      ' in a form, typically). It also returns the total volume

      ' of Solid3ds in the Database


      Public Function AnalyzeDatabase _

        (ByVal db As Database, _

        ByRef sl As System.Windows.Forms.BindingSource) _

        As Double


        ' We will return the total volume of solids

        ' in the modelspace of this Database


        Dim vol As Double = 0

        sl.Clear()


        Dim tr As Transaction = _

          db.TransactionManager.StartTransaction()

        Using tr

          Try

            Dim bt As BlockTable = _

              tr.GetObject _

                (db.BlockTableId(), _

                OpenMode.ForRead)

            Dim btr As BlockTableRecord = _

              tr.GetObject _

                (bt(BlockTableRecord.ModelSpace), _

                OpenMode.ForRead)


            ' Loop for each entity in the modelspace


            For Each entId As ObjectId In btr

              Dim ent As Entity = _

                tr.GetObject(entId, OpenMode.ForRead)


              ' If it's a solid, then gather its data


              If TypeOf (ent) Is Solid3d Then


                Dim solid As Solid3d = CType(ent, Solid3d)

                Dim mp As Solid3dMassProperties = _

                  solid.MassProperties()


                Dim sd As New SolidData


                ' Use the COM API to access the type string

                ' (exposed through neither ObjectARX nor .NET)


                Dim oSolid As Acad3DSolid

                oSolid = CType(solid.AcadObject, Acad3DSolid)

                sd.Type = oSolid.SolidType


                ' Use the centroid for the location


                sd.Location = _

                  Round(mp.Centroid.X, 4).ToString() + ", " + _

                  Round(mp.Centroid.Y, 4).ToString() + ", " + _

                  Round(mp.Centroid.Y, 4).ToString()


                sd.Id = solid.Handle.ToString

                sd.Volume = Round(mp.Volume, 4)


                ' Add each solid's info to the list


                sl.Add(sd)


                ' And add its volume to the total


                vol += sd.Volume()


              End If

            Next


            ' Committing is cheaper than aborting,

            ' even if we didn't change the drawing


            tr.Commit()


          Catch ex As Exception

            MsgBox("Error: " + ex.Message)

          End Try

        End Using


        AnalyzeDatabase = Math.Round(vol, 2)


      End Function


    End Module

    We will now build the code into a complete AutoCAD .NET application which defines two commands – the first, SD, launches a dockable AutoCAD palette listing the 3D solids in our model and totaling their volume, and the second, SD2, does the same from a standard dialog:

    3d_solids_palette_2

    3d_solids_dialog

    Figure 2 - the dialogs displayed by the SD and SD2 custom commands

    These two dialogs share a common implementation: they both contain a UserControl that contains a DataGridView and a BindingNavigator to browse the contained records. There’s an optional “Analysis” button in the control, which allows the analysis operation to be launched manually. This is hidden when within the Dialog version, as it was primarily intended as a modal interface which launches the analysis automatically on load.

    Build a RealDWG application from the AutoCAD .NET code

    Now we will take the basic code from the SolidInfo.vb file and reuse it in a RealDWG application. It is also possible to use the User Control directly (defined in SolidUserControl.vb, SolidUserControl.Designer.vb and SolidUserControl.resx) but I thought it would be of more interest to demonstrate building the steps needed to build a RealDWG application and how it differs from a standard AutoCAD .NET module.

    We start by creating a new Windows Application in Visual Studio 2005:

    New_realdwg_project

    Figure 3 – creating a new project for our RealDWG application

    The first step is to copy across the SolidInfo.vb file into our project folder and add it into the project as an existing item. To get the project to build you will need to select two project references: one for the RealDWG managed assembly, and one for the COM type library:

    Add_reference_to_realdwg_managed_as

    Add_reference_to_com_type_library

    Figure 4 - adding project references to the RealDWG managed assembly & COM type-library

    We need the COM type library to work around a specific limitation in the .NET API to AutoCAD and in ObjectARX, its underlying API. These APIs do not provide access to the type of a solid – whether it’s a sphere, a cylinder or a pyramid, for instance – so we need to use COM to retrieve the information.

    New VB.NET Windows Application projects contain a default form, Form1.vb. Rename this to MainForm.vb and design a simple user interface with a TextBox for a filename (named DwgFileToAnalyze), a Button to browse to the file (named BrowseButton) and a DataGridView (called whatever you like :-). When you come to adding the DataGridView you will be prompted to define a Data Source. Select “Add a New Data Source” of type “Object”. From here you will be able to browse down to our SolidList class:

    Adding_the_datagridview

    Adding_a_data_source

    Figure 5 – adding a DataGridView and its associated Data Source

    Now just add a Label (named TotalVolumeText) at the bottom (going the whole width of the dialog, setting the AutoSize property to “False”, the Font to something nice and visible and TextAlign to “MiddleRight”). You can leave the contents blank, as we’ll be setting those programmatically.

    I always like the UI to behave well on resize, even with a sample app, so I’d suggest editing the Anchor property of the GroupBox and the TextBox to “Top, Left, Right”, of the DataGridView to “Top, Bottom, Left, Right”, and of the VolumeText to “Bottom, Left, Right”.

    I’d also change the following DataGridView properties: AllowUserToAddRows to “False”, AllowUserTo DeleteRows to “False” and AutoSizeColumnsMode to “Fill”.

    The User Interface is now done – we’re ready to flesh out the application functionality to call our AnalyzeDatabase() from SolidInfo.vb.

    First we need to implement some code to make our RealDWG application valid. We’ll add a new file called Program.vb and populate it with a base HostApplicationServices object:

    Imports Autodesk.AutoCAD.Runtime

    Imports Autodesk.AutoCAD.DatabaseServices


    '<Assembly: SecuredApplication("<Placeholder for the clear text license>", "<Placeholder for the Autodesk encrypted text license>", "<Placeholder for the client encrypted text license>", "<Placeholder for the client public key>")>

    <Assembly: SecuredApplication( _

    "THIS IS AN OBJECTDBX (TM) VERSION 2007 CLIENT LICENSE FOR THE EXCLUSIVE USE OF Kean Walmsley. YOUR USE OF OBJECTDBX(TM) IS GOVERNED BY THE SOFTWARE LICENSE INCLUDED IN THE PRODUCT. USE OF THIS SOFTWARE IN VIOLATION OF THE SOFTWARE LICENSE IS A VIOLATION OF U.S. AND/OR INTERNATIONAL COPYRIGHT LAWS AND TREATIES AND YOU MAY BE SUBJECT TO CRIMINAL PENALTIES FOR SUCH USE.", _


    ' Lines deleted - you will need to be a valid RealDWG license holder to get this text


    Public Class MyHostApplicationServices

      Inherits HostApplicationServices


      Public Overrides Function FindFile _

        (ByVal fileName As String, _

        ByVal database As Database, _

        ByVal hint As FindFileHint) _

        As String


        FindFile = Nothing


      End Function

    End Class

    I've edited the above text to delete my personal RealDWG license keys – you will need to license RealDWG to receive valid license keys of your own for this code to work. The FindFile function is needed to help RealDWG find supporting files such as fonts. As we’re only accessing information about 3D solid objects it is not important for us to implement this completely.

    Now we go to the code window for MainForm.vb – either by double-clicking the form, the browse button, or by selecting View Code from the Solution Explorer when right-clicking on the file. We replace the default implementation with the following:

    Imports Autodesk.AutoCAD.DatabaseServices

    Imports Autodesk.AutoCAD.Runtime


    Public Class MainForm


      Private Sub MainForm_Load _

        (ByVal sender As System.Object, _

        ByVal e As System.EventArgs) _

        Handles MyBase.Load


        ' Initialize RealDWG host subsystem


        RuntimeSystem.Initialize _

          (New MyHostApplicationServices(), 1033)

      End Sub


      Private Sub BrowseButton_Click _

        (ByVal sender As System.Object, _

        ByVal e As System.EventArgs) _

        Handles BrowseButton.Click


        Dim dlg As New System.Windows.Forms.OpenFileDialog()

        dlg.InitialDirectory = _

          System.Environment.CurrentDirectory

        dlg.Filter = _

          "DWG files (*.dwg)|*.dwg|All files (*.*)|*.*"


        Dim oc As Cursor = Me.Cursor


        If dlg.ShowDialog() = Windows.Forms.DialogResult.OK Then

          Me.Cursor = Cursors.WaitCursor

          DwgFileToAnalyze.Text = dlg.FileName()

          Me.Refresh()

        End If


        If DwgFileToAnalyze.Text <> "" Then


          ' Let's read the selected DWG


          Dim db As Database = _

            New Database(False, True)

          Using db


            db.ReadDwgFile _

              (DwgFileToAnalyze.Text, _

              IO.FileShare.None, _

              False, Nothing)


            HostApplicationServices.WorkingDatabase = db


            ' Analyze it and display the results


            Dim vol As Double = _

              AnalyzeDatabase(db, SolidListBindingSource)

            TotalVolumeText.Text = _

              "Total Volume = " + vol.ToString()


            Me.Cursor = oc


          End Using


        End If

      End Sub

    End Class

    Our code is now ready to build. There is, however, one additional step needed for the application to run. When you receive a license key for RealDWG, you should also receive a public license file (client.snk) that needs to be bound to your application. There’s a utility that ships in RealDWG’s Utils folder – bindmgd – which does just that:

    bindmgd -b <YourApp>.exe client.snk

    Now our application should run. All being well, when we select the DWG file we created earlier, we should now see the grid populated with data extracted from the 3D solids contained within it:

    3d_solids_realdwg_app

    Figure 6 - our RealDWG application accessing the 3D solid data

    TrackBack

    TrackBack URL for this entry:
    http://www.typepad.com/services/trackback/6a00d83452464869e200e54ef92ba68833

    Listed below are links to weblogs that reference AU Handouts: There's More to .DWG Than AutoCAD® - Part 1:

    » More to CAD-Part1-simple .NET application to access DWG solids info from CadKicks.com
    You've been kicked (a good thing) - Trackback from CadKicks.com [Read More]

    Comments

    You've been kicked (a good thing) - Trackback from CadKicks.com
    http://www.cadkicks.com/adkautocad/More_to_CAD_Part1_simple_NET_application_to_access_DWG_solids_info

    Hi Kean,
    I'm looking at writing a piece of software that will export all entities in a DWG in XML or LandXML format and import XML data into a DWG. Some of the entities would have Xdata attached to them.

    Which programming language would you recommend and do you have any tips on writing XML data

    Best regards
    Ben

    Hi Ben,

    Here are some ways you might create XML files:

    1. You can use standard ASCII output functions from your favourite programming language (this can be the simplest, but may end up taking more effort if you need to support different types of encoding).

    2. You can create a DOM (Document Object Model) object using the MSXML COM component, and populate that with elements and attributes, and ask it to save an XML file.

    3. You can use the System.Xml.XmlWriter class from .NET.

    I've personally used 1 & 2 in the past -at the time I was working with VB6 - but if I had your problem to solve today, I'd go with option 3.

    Regards,

    Kean

    Do you know why I have this message when try to
    run the project?

    The specified module could not be found. (Exception from HRESULT: 0x8007007E)

    I am new RealDWG user, I will be grateful any help or referent

    thank you
    Alberto B

    Hi Alberto,

    Please be aware of this statement from the above post:

    "I've edited the above text to delete my personal RealDWG license keys – you will need to license RealDWG to receive valid license keys of your own for this code to work."

    I suspect this is the reason for the problem, but let me know, if not.

    Regards,

    Kean

    Hi Kean,

    Great work for all your articles !

    I have a DWG file version 2004 that contains various 3dSolids (Cylinders, Torus, etc...)
    I search a way to replace each of them by a newly created object of the same kind with same attributes), but that provides a way to manipulate them under acad2008 and change their geometrical attributes (radius, length, etc...)
    Do you have an idea how to do that ?
    I'm stuck on the fact that I can't access the geometrical properties of the subtype object (like the radius of the cylinder...)

    I'm usually working with C#, but if the way to go is different, i'm ready to go with what will work ;-)

    perhaps exist some upgrade function or conversion program that provides that ?

    Thank you for your help !
    Best Regards,

    Fred

    You'll need to set RecordHistory to true, although I only know that works when creating new solids (I don't know about modifying existing ones).

    Kean

    Yes, for new solids everythings works fine.
    The goal is to "upgrade" old ones so that we would avoid "redrawing" again tons of 3Dsolids to profit of the new (and hoped) editing functions.
    I am frustrated because Acad of course knows about geometry of the underlying 3Dprimitive, he has to draw it with a good radius.
    Where to get this info ?
    The DXF file 3D datas are not readable, they seems as Binary datas.
    My hope was to use the way you do, through some API.
    I am really confused about the fact that this "upgrading" function is so hard to find.
    Do you have an advice where I have to search ? Do you guess there is a solution for my problem anywhere ?

    Thank you wery much.

    Fred

    Fred,

    I've taken a look into this... I don't think there's an easier answer, other than to re-create the solids with history enabled.

    Your assumption that AutoCAD knows about the underlying geometry isn't correct, I suspect: the SPHERE command - prior to history being added in 2007 - created a spherical ShapeManager 3dSolid. From that point on it's a 3dSolid and AutoCAD doesn't think of it as a sphere.

    This changed with 2007, of course, but 3dSolids created with older versions are not automatically understood as being of different shapes/types: I don't believe there's a way to determine a solid's creation history after the fact, unfortunately.

    Sorry if this is bad news (I'll let you know if I hear something back from my team that contradicts this information).

    Kean

    Verify your Comment

    Previewing your Comment

    This is only a preview. Your comment has not yet been posted.

    Working...
    Your comment could not be posted. Error type:
    Your comment has been posted. Post another comment

    The letters and numbers you entered did not match the image. Please try again.

    As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

    Having trouble reading this image? View an alternate.

    Working...

    Post a comment

    Feed & Share

    Search