MText.WidthFactor

If you’ve ever tried to edit the widthfactor of mtext through VBA or VB.NET, you’ll have realised that there isn’t a method for doing so. However, there is a built in trick which bypasses the requirement for doing this. Try the code below:

Sub main()
    Dim p As Variant
    Dim mt As AcadMText
    ThisDrawing.Utility.GetEntity mt, p, "Pick an MTEXT object..."
    mt.TextString = "\W0.5;" & a.TextString
End Sub

Preceeding the TextString property with the prefix \W0.5; tells AutoCAD® to make all the text have a width factor of 0.5. You can also edit the width factor of individual parts of the TextString, by surrounding the text you want to change in curly brackets, as shown below.

Sub main()
    Dim p As Variant
    Dim mt As AcadMText
    ThisDrawing.Utility.GetEntity mt, p, "Pick an MTEXT object..."
    mt.TextString = "{\W0.5;narrow} {\W2;wide} {\W0.5;narrow} {\W2;wide} {\W0.5;narrow} {\W2;wide}"
End Sub

Hope this helps – if it did, please do subscribe to my blog. I’ll be uploading many more tips and tricks regularly.

ThisDrawing.PaperSpace

ThisDrawing.PaperSpace is a very useful way to access the PaperSpace of your drawing through VBA or VB.NET.

When developing tools that draw entites in a drawing, we can sometimes use ThisDrawing.PaperSpace, forgetting that the tool might actually end up being used in ModelSpace. This would result in bahaviour that is not expected by the user. The ideal solution would be if there were an ActiveSpace method of the ThisDrawing object, but unfortunately this does not exist. There is a workaround however:

Function ThisSpace() As AcadBlock
    If ThisDrawing.ActiveSpace = acModelSpace Then
        Set ThisSpace = ThisDrawing.ModelSpace
    Else
        Set ThisSpace = ThisDrawing.PaperSpace
    End If
End Function

Use this function to return the block object of either ModelSpace or PaperSpace – whichever is actively open by the user. This solves the problem mentioned above, ensuring that you are always writing code that edits the space that is currently being worked on by the user. Use a call to this function in the place of ThisDrawing.PaperSpace.

I have many tutorials on my site for how to use your VBA code in VB.NET projects. I also explain what you need to get started with coding in VB.NET, and where to download the required software for free.

ThisDrawing.ModelSpace

ThisDrawing.ModelSpace is a very useful way to access the ModelSpace of your drawing through VBA or VB.NET.

When developing tools that draw entites in a drawing, we can sometimes use ThisDrawing.ModelSpace, forgetting that the tool might actually end up being used in PaperSpace. This would result in bahaviour that is not expected by the user. The ideal solution would be if there were an ActiveSpace method of the ThisDrawing object, but unfortunately this does not exist. There is a workaround however:

Function ThisSpace() As AcadBlock
    If ThisDrawing.ActiveSpace = acModelSpace Then
        Set ThisSpace = ThisDrawing.ModelSpace
    Else
        Set ThisSpace = ThisDrawing.PaperSpace
    End If
End Function

 

Use this function to return the block object of either ModelSpace or PaperSpace – whichever is actively open by the user. This solves the problem mentioned above, ensuring that you are always writing code that edits the space that is currently being worked on by the user. Use a call to this function in the place of ThisDrawing.ModelSpace.

I have many tutorials on my site for how to use your VBA code in VB.NET projects. I also explain what you need to get started with coding in VB.NET, and where to download the required software for free.

Ray Casting Algorithm: How to determine with VBA if a point is inside a polyline in AutoCAD

Someone recently asked about determining if a point resides within a polyline using VBA. There are a few ways to achieve this, some being more complex than others, each with their own advantages. However, there is one way that is particularly simple within the AutoCAD® environment, as we are able to use built in features such as the IntersectWith method.

The concept for finding out if a point is within a polyline is this – if we were to draw a line from the point in any direction to infinity, the number of intersections the line would have with the polyline would be an odd number. Think about it – if it was a square, it would cross the polyline once. For irregular shapes where the line crosses it many times, it firstly has to exit the shape – any re-entry to the shape must have a corresponding exit – so if the point is within the polyline, any line eminating from it to infinity must cross the boundary an odd number of times. And for the same reason, any point outside the boundary must cross the boundary an even number of times.

So, armed with this knowledge, we need a way to actually achieve this in AutoCAD®. And what better way of doing so is there than to simply draw a Ray in any direction, and find out how many intersections with the boundary there are? Well, that’s exactly what this code does:

Option Explicit

Sub main()
    Dim selPolyline As AcadLWPolyline
    Dim selPoint As AcadPoint
    Dim pnt As Variant
    Randomize 'Initialise random number generator
    ThisDrawing.Utility.GetEntity selPolyline, pnt, "Pick polyline"
    ThisDrawing.Utility.GetEntity selPoint, pnt, "Pick point"
    If isPointInPolyline(selPolyline, selPoint) Then
        ThisDrawing.Utility.Prompt "The point resides within the polyline"
        
    Else
        ThisDrawing.Utility.Prompt "The point resides outside the polyline"
    End If
End Sub

Function isPointInPolyline(pl As AcadLWPolyline, pnt As AcadPoint) As Boolean
    Dim p1 As Variant
    Dim p2 As Variant
    Dim ray As AcadRay
    Dim arr As Variant
    Dim upperbound As Long
    Dim IntersectionCount As Long
    p1 = pnt.Coordinates
    p2 = p1
    ' edit on 03/12/2010 for increased reliability
    ' horizontal ray exchanged for a ray with a random direction
    p2(0) = p2(0) + 1 - Rnd * 2 'offset x coordinate for secondary point in Ray by random amount
    p2(1) = p2(1) + 1 - Rnd * 2 'offset y coordinate for secondary point in Ray by random amount
    ' end of edit
    Set ray = thisSpace.AddRay(p1, p2)
    arr = ray.IntersectWith(pl, acExtendNone)
    upperbound = UBound(arr)
    If upperbound = -1 Then
    ' No intersections - the point must not be inside the polyline
    ' Assumes no elevation
        isPointInPolyline = False
    Else
        IntersectionCount = (upperbound + 1) / 3
        'number of elements in array is equal to the upperbound + 1 because of element zero
        'we divide by 3 to find the number of individual intersections because each has
        '3 coordinates - X, Y and Z
        
        If IntersectionCount Mod 2 = 0 Then
        'There are an even number of intersections - it cannot be inside the polyline
            isPointInPolyline = False
        Else
        'There are an odd number of intersections - it must be inside the polyline
            isPointInPolyline = True
        End If
        
    End If
    ray.Delete
End Function

Function thisSpace() As AcadBlock
    If ThisDrawing.ActiveSpace = acModelSpace Then
        Set thisSpace = ThisDrawing.ModelSpace
    Else
        Set thisSpace = ThisDrawing.PaperSpace
    End If
End Function

Do consider subscribing to my blog – I’ll always be posting code snippets like this, and always with a decent explanation. Please also feel free to leave comments.

Will

Introduction to VB.NET in AutoCAD

VBA is in the process of being phased out, and the replacement is .NET. This may disappoint some of you that have VBA applications/skills, but you will be pleased to hear that you can still use code that you have, and all you know about VBA programming with AutoCAD® can still be put to good use using VB.NET. The following tutorial sets the foundation for creating your first VB.NET project, and should serve as a good starting point for migrating your code across to VB.NET.

What you need

There is no native IDE (integrated development environment) within AutoCAD® for developing .NET projects like you could with VBA (via the command VBAIDE). Instead, you need an external software development package for writing and compiling your code. Fear not however, you can download express editions of several programming suites for free directly from Microsoft’s website. Click here to visit the page for downloading Visual Basic Express 2010.
Next, you need to download the ObjectARX programming interface from AutoDesk’s website. Click here to visit the page for downloading ObjectARX. Ensure you acquire the correct version of the ObjectARX libraries for your version of AutoCAD.

Create your first VB.NET AutoCAD® project

Open Microsoft Visual Basic Express, and select “New Project”, and then select the “Class Library” option. This creates a project that compiles to a dynamic link library (.dll file). In VBA you automatically have access to the AutoCAD® object, including objects such as ThisDrawing, but in this environment we have to create references to AutoCAD® explicitly ourselves. We do this by naming a reference to ObjectARX, which contains the AutoCAD® type libraries that we want to use.

  1. Under the Solution Explorer window, right click on your project (ClassLibrary1 if you haven’t renamed it), and select Properties.
  2. Click on the References Tab.
  3. Click on the Add dropdown, and select Reference.
  4. Click on the Browse Tab, and navigate to where the ObjectARX libraries are installed. Typically this will be C:\ObjectARX 2011\
  5. Depending on your preference, open the folder inc-win32 or inc-x64.
  6. Select the dll files Autodesk.AutoCAD.Interop.dll and Autodesk.AutoCAD.Interop.Common.dll, and click OK. This imports the AutoCAD® type libraries.
  7. Add another reference, and this time select the inc folder. Select AcMgd.dll and AcDbMgd.dll, and click OK. I’m not completely sure why these are required, but apparently they are!
  8. Finally, you’ll notice on the References Tab that the Copy Local property of the AcMgd.dll and AcDbMgd.dll references is set to True. This needs to be False so select them, and under the properties window change the Copy Local property to False.

Writing some initial code

Our project is now set up with all the references required to link to AutoCAD®. Now we can begin writing code.
Switch back to code view (If you haven’t renamed anything, double click on Class1.vb under Solution Explorer). We need to specify in this class which libraries from our references we intend to use, so right at the top of the class (before Public Class Class1) put the following code:

    'Contains the AutoCAD® Type Library
    Imports Autodesk.AutoCAD.Interop
    'Contains the AutoCAD/ObjectDBX Type Library
    Imports Autodesk.AutoCAD.Interop.Common

Now in VBA, we had access to the ThisDrawing object. This is quite useful, so it would be useful to have the same functionality here. Now that we have access to the AutoCAD® object model, we can add a simple Get procedure to retrieve the object we want. Put the following code inside the Class (between Public Class Class1 and End Class):


    Public ReadOnly Property ThisDrawing As AcadDocument
        Get
            Return Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.AcadDocument
        End Get
    End Property

Now we can use ThisDrawing anywhere within the class as we could in VBA.

Writing our first AutoCAD® Function

Everything is in place now to start producing our own AutoCAD® commands. If you have any VBA code you want to transfer across, you should now be able to paste them into this class. Here’s one that I wrote recently as an example. The code adds a border to MTEXT, and the only difference between this code and VBA code is that I’ve made use of the .NET Try/Catch statement, which is better for error trapping than previous methods in VBA:

    Public Sub CreateMTextBorder()
        Dim ent As AcadEntity
        Dim pnt As Object
        Try
            ThisDrawing.Utility.GetEntity(ent, pnt, "Pick MTEXT to add a border to")
            Dim mt As AcadMText
            mt = ent
            Dim Min As Object, Max As Object
            Dim Coords(7) As Double
            mt.GetBoundingBox(Min, Max)
            Coords(0) = Min(0) - 1
            Coords(1) = Min(1) - 1
            Coords(2) = Max(0) + 1
            Coords(3) = Min(1) - 1
            Coords(4) = Max(0) + 1
            Coords(5) = Max(1) + 1
            Coords(6) = Min(0) - 1
            Coords(7) = Max(1) + 1
            If ThisDrawing.ActiveSpace = AcActiveSpace.acModelSpace Then
                ThisDrawing.ModelSpace.AddLightWeightPolyline(Coords).Closed = True
            Else
                ThisDrawing.PaperSpace.AddLightWeightPolyline(Coords).Closed = True
            End If
        Catch
            ThisDrawing.Utility.Prompt("Invalid Selection")
        End Try
    End Sub

Make sure that your subroutine is declared Public as opposed to Private, so that it will be visible by AutoCAD®. Also, there is one final piece of code we need to add to make this visible by AutoCAD®. We need to add some META data before the subroutine, which tells AutoCAD® that the subroutine is callable directly from AutoCAD®. This is where we assign our subroutine a Command Name for our AutoCAD® Command Line. Add the following code on the line before Public Sub CreateMTextBorder():

	<Autodesk.AutoCAD.Runtime.CommandMethod("MTEXTB")> _

Whatever we put in the brackets will be the command that we type into the AutoCAD® command line.

Our class is now complete, and is ready to be compiled into a DLL file for use with AutoCAD.

Compiling

Under the debug menu, we can select Build to create our .dll, but if we do it now the NETLOAD command in AutoCAD® will fail to load the .dll file. This is because the .dll file must be built on the same version of the .NET framework as the version of AutoCAD® you are using. AutoCAD® 2011 is built on version 3.5 of the .NET framework, so we need to tell VB to compile our class using this version.

  1. Go to project properties as we did before for adding references.
  2. Click on the Compile Tab.
  3. At the bottom, click on Advanced Compile Options.
  4. At the bottom of the Advance Compiler Settings dialog box, select the Target Framework .NET Framework 3.5.
  5. Click OK. This usually requires closing and reopening the project afterwards.

Now we can build our .dll file based on the same .NET Framework as AutoCAD®, which should make it compatable. It is useful to have the Output Window open, so that we can grab the location of the compiled .dll file once the build is complete. You can open this window by going Debug > Windows > Output. Go Debug > Build, and this will compile the .dll file. Copy the path of the .dll file from the Output window, and open AutoCAD.

Type NETLOAD into the command line. This is the command for loading .NET projects. You will be prompted to supply a path, so paste in the path to the .dll file we just created. If it worked, it should silently finish the command, i.e., you shouldn’t get any error messages. Now that the .dll is loaded you should be able to type in the command specified in the META data to call the subroutine inside the .dll, in the case if this example, MTEXTB.


I would like to take this opporunity to suggest that you subscribe to my blog – if you work with AutoCAD® I have many tips and tricks on the tip of my tongue. I guaranteee that you will find it a valuable resource.