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

VBA in AutoCAD® – Tutorial 2: Subroutines

Introduction

This tutorial assumes that you have read all previous tutorials, but have no other VBA knowledge for AutoCAD® or otherwise.

In this tutorial, I will explain the basics of subroutines.

Subroutines

In the last tutorial, I explained some basic code. The observant of you will have noticed that there were a few parts of the code that I left unexplained:


Sub QuickExample()
End Sub

This is a subroutine. The first line defines the beginning, and the second line defines the end. Similarly to variables (remember those from the last tutorial?), the name can be changed to whatever we like (certain rules apply). However, the subroutine name must end with a pair of parantheses. If they are omitted, they will be added automatically. You may notice after typing a line such as Sub QuickExample and pressing enter, that the End Sub statement is automatically inserted. If any subroutine has no corresponding End Sub, it will not run, and an error will occur.

A subroutine is a container for our code. If we had a subroutine that contains code that converts TEXT to MTEXT, it would be logical to call it something like ConvertText2MText. When we use the command VBARUN from AutoCAD®, we are presented with a dialog box that gives us the option to execute whatever subroutine we like. So in this example, we would pick the option that refers to the ConvertText2MText subroutine.

The above method using VBARUN is how to execute a subroutine directly from AutoCAD®. We can also run the subroutine from within VBA – perhaps from another subroutine. The way to do that, is to write the subroutine name in our code. So to run the subroutine in the example above, we would simply write:

ConvertText2MText

When VB reaches this line of code, it will jump to the line of code that says Sub ConvertText2Mtext(). Then, it will run the content, and when it reaches the End Sub statement, VB will return to where it was originally, and continue to process code as normal. This can be a useful way of splitting up our VBA applications into logical and understandable chunks that can be called upon whenever necessary.

So lets expand on our example. Lets say that we have 4 subroutines. I’ll put 3 of them below:


Sub ConvertText2MText()
    MsgBox "Convert Text to MText"
End Sub

Sub ConvertLines2Polylines()
    MsgBox "Convert lines to polylines"
End Sub

Sub SetupSystemVariables()
    MsgBox "Do setting up of system variables"
End Sub

I’ve omitted the actual code for these subroutines as it isn’t what I’m focusing on. Each of the above could be called directly from AutoCAD® by entering VBARUN into the command line, and then selecting the desired subroutine to run. But if we actually want to run all of them however, it might be useful to set up another subroutine to call them all:


Sub ReformatDrawing()
    MsgBox "Beginning running all subroutines..."
    ConvertText2MText
    ConvertLines2Polylines
    SetupSystemVariables
    MsgBox "Completed!"
End Sub

When you execute the ReformatDrawing subroutine, it will run the other 3 subroutines in the order that they appear. Some of you might say “You could just put all of the code into one subroutine – why not just do that?”. Well yes you could, but when you start making complicated code its not advisable because it makes it hard to follow. Also, programming in this way makes your code nicely reusable because it easy to transfer whole subroutines from one project to another.

More on Subroutines

We’ve covered the purpose, and basic usage of subroutines. Now I will show you what else subroutines can be useful for.

It is sometimes useful for the subroutine to do certain things based on given input. Say for example we wanted a subroutine that inserts some text into the drawing at a changable position. We can make it so that we can give the subroutine some parameters, and make it do things based on the information we provide. In this example, we could make it so that the subroutine will put text into our drawing based on coordinates that we supply. So I will explain how to pass parameters to a subroutine. Here we have a subroutine that inserts text into our drawing (it’s the same as the one in Tutoral 1):


Sub InsertText()

    Dim MyString As String 'Create string variable
    MyString = "Hello! This is the contents of the string variable called MyString" 'set it to something
    MsgBox MyString 'show it in a messagebox

    Dim Point(2) As Double 'This is how to create an array
    Point(0) = 10 'This is x
    Point(1) = 20 'This is y
    Point(2) = 0 'This is z

    Dim TextHeight as double 'Create double precision floating point number variable
    TextHeight = 10 'set textheight to 10

    ThisDrawing.ModelSpace.AddText MyString, Point, TextHeight  'Add text to drawing in modelspace

End Sub

This subroutine is fine, but it’s not very useful because it does the same thing every time! However, if we alter the code slightly, we can make it a bit more intuative. We can alter the subroutine so that it is anticipating additional information. If we do this, we will have to provide the additional information when we call it. To make the subroutine expect additional information, we add contents to the parantheses as follows:


Sub InsertText(MyString as String)

    MsgBox MyString 'show whatever was passed to the subroutine in a messagebox

    Dim Point(2) As Double 'This is how to create an array
    Point(0) = 10 'This is x
    Point(1) = 20 'This is y
    Point(2) = 0 'This is z

    Dim TextHeight as double 'Create double precision floating point number variable
    TextHeight = 10 'set textheight to 10

    ThisDrawing.ModelSpace.AddText MyString, Point, TextHeight  'Add text to drawing in modelspace

End Sub

Now, to call this subroutine we would use something like:
InsertText “Hello! This is some text that I am passing to the subroutine”

This would need to be contained in another subroutine. I’ll rewrite it all so that it makes some sense:


Sub MainSubroutine
    InsertText "Hello! This is some text that I am passing to the subroutine"
End Sub

Sub InsertText(MyString as String)

    MsgBox MyString 'show whatever was passed to the subroutine in a messagebox

    Dim Point(2) As Double 'This is how to create an array
    Point(0) = 10 'This is x
    Point(1) = 20 'This is y
    Point(2) = 0 'This is z

    Dim TextHeight as double 'Create double precision floating point number variable
    TextHeight = 10 'set textheight to 10

    ThisDrawing.ModelSpace.AddText MyString, Point, TextHeight  'Add text to drawing in modelspace

End Sub

If we input VBARUN into the command line, and select MainSubroutine, we should get exactly the same scenario as we did to begin with. However, it happens differently. This time, we are running the subroutine called MainSubroutine, this is in turn calling the subroutine InsertText, and is providing that subroutine with some text. When VB jumps to the InsertText subroutine, it creates a variable called MyString, and automatically stores the text in here. Notice that I have removed some of the code inside the new sub – it no longer needs to create the MyString variable (with the Dim statement), or set the text in that variable, because it now happens automatically as part of the call to the subroutine.

This is a simple example using one parameter. At the moment this doesn’t really add any more functionality, but we can add more parameters as follows:


Sub MainSubroutine
    InsertText "This is the first call to the InsertText Sub", 10, 20, 0, 10
    InsertText "This is call number 2!", 10, 40, 0, 10
    InsertText "This is the third and final call", 10, 60, 0, 10
End Sub

Sub InsertText(MyString as String, XCoordinate as Double, YCoordinate as Double, ZCoordinate as Double, TextHeight as Double)

    Dim Point(2) As Double 'This is how to create an array
    Point(0) = XCoordinate 'Set the contents of Point(0) to whatever is stored in the variable XCoordinate
    Point(1) = YCoordinate 'Set the contents of Point(1) to whatever is stored in the variable YCoordinate
    Point(2) = ZCoordinate 'Set the contents of Point(2) to whatever is stored in the variable ZCoordinate

    ThisDrawing.ModelSpace.AddText MyString, Point, TextHeight  'Add text to drawing in modelspace

End Sub

Now we’ve set up the InsertText subroutine so that it accepts parameters for the contents of the text, the location, and height. This is more useful from a programming point of view because we can now insert some text into the drawing at any position, any height, and containing whatever contents we like; all only using one line of code as shown in the MainSubroutine.


If you found this useful, please do subscribe to my blog – I’ll always be adding something useful!

VBA in AutoCAD® – Tutorial 1: Introduction

Introduction

Knowing a little bit of programming can be extremely useful in the AutoCAD® environment. Before you cringe with dread at the prospect of having to learn a programming language, it sounds far more difficult than it really is. Especially since VB stands for “Visual Basic” – there’s a clue in the name… Writing simple applications for automating tasks isn’t hard – really.

This tutorial assumes no previous knowledge of VBA in AutoCAD® or otherwise. I will explain the very basics of using VBA in AutoCAD®. I will give a brief overview of the VBA IDE (Integrated Development Environment) and I will explain a small snippet of code and how it works.

What is VBA?

If I assume you know nothing about VBA, it follows that you probably don’t know what VBA stands for. VBA stands for Visual Basic for Applications. Most people reading this will probably have heard of Visual Basic, and are aware that it is a Microsoft Windows based programming language. The “for Applications” bit means that it is integrated behind the scenes in the software application you’re using, be it Word, Excel, or in our case AutoCAD.

Step One – Familiarise with the IDE

In order to begin, we need to access the VBA IDE. This is where we will be working. Input VBAIDE into the command line. This opens up a new window which is the VBA IDE. You should have something that looks like the below:

The section of the screen with the big red “1” is the project window. This shows you all the sections of your VBA project. Right click in this area and select insert/module. This will (surprisingly!) insert a module. A module is basically a container for your code. If you like, you can rename the module from “Module1” to something more descriptive of what the module does or is for.

The section of the screen represented by the number 2 is the properties window. Various aspects of your project have certain properties that can be edited. I won’t go into this right now.

The section of the screen represented by the number 3 is the editing window. If it’s grey at the moment (like in the example), you’re not editing anything. If you’ve added a module, chances are you’re looking at a white screen, and you can type stuff. When you add a module, it automatically opens it up in the editing portion of the screen.

The section of the screen represented by the number 4 is a toolbar – in particular the buttons that handle the execution of code. The run button causes VBA to start executing code from the current subroutine (i.e. the subroutine in the module you’re editing that contains the cursor). The pause button will pause execution, and take you to the debugging screen. The current position that VBA has got to will be highlighted with yellow. The stop button halts execution.

Step Two – Add some code, and run it

Make sure you’re at a state where you are editing a module. You might have at the top of the screen the words “Option Explicit”. This is fine, but also don’t worry if it’s not there. You don’t really need to know about this right now. Paste the following code into the module:

Sub QuickExample()
    Dim MyString As String 'Create string variable
    MyString = "Hello! This is the contents of the string variable called MyString" 'set it to something
    MsgBox MyString 'show it in a messagebox
    Dim Point(2) As Double 'This is how to create an array
    Point(0) = 10 'This is x
    Point(1) = 20 'This is y
    Point(2) = 0 'This is z
    Dim TextHeight as double 'Create double precision floating point number variable
    TextHeight = 10 'set textheight to 10
    ThisDrawing.ModelSpace.AddText MyString, Point, TextHeight 'Add text to drawing in modelspace
End Sub

Everything should have pasted in nicely. The text should automatically change into a lovely assortment of black, dark blue, and green colours. If you get any red, then something went wrong when you copy and pasted. When editing code, different colours of text represent different things. Green text represents a comment or note – this text can be anything and is for the purpose of you making your own notes. Dark blue text represents key words that VB understands. Red text indicates anything that contains errors, or the structure of what you’ve written is wrong. However, just because it’s not red, doesn’t necessarily mean that it contains no errors. Black is anything that doesn’t fall under any of the above.

You probably guessed that to execute the code, you click the run button in the toolbar at the top. The shortcut for this button is the F5 key. As an additional point, it is sometimes useful to know how to execute your code without opening the IDE. To do this, you would type in -VBARUN into the command line, followed by you’re subroutine name. This command can conveniently be stored in a toolbar or pallette using the normal process for editing the user interface – CUI. Notice the preceeding “-” in front of the VBARUN command. You probably already know this is an AutoCAD® command modifier. The dash explicitly tells AutoCAD® to expect command line entry. If the dash is omitted, VBARUN opens a little dialog box, and you can pick your subroutine from there.

Step Three – Understanding the code

Maybe you executed the code and thought “that’s not very interesting..”. If so, I assure you this is the tip of a very large iceberg. In the code above, I’ve added comments in such a way that it explains it a little, but I’ll give a little more detail now.

Code is executed from the top down in the same manner you would read a book, unless you tell VB to do something else.

Dim MyString As String

This is the first line of code. This line creates what is known as a variable. The Keyword “Dim” is the part of the code that tells VB to create a variable. A variable is basically a memory bank with a name. The name of the “memory bank” or variable is MyString. We specified the name MyString, but it could have been called something else if we liked. The line Dim This_is_a_variable as string would be equally valid, but it’s a bit of a mouthful. Also, any reference to MyString in the code would need to be changed to This_is_a_variable in order for the code to work in the same way. The part of the code that says “As String” tells VB what type of variable it is. In this case, it is what is known as a String variable, or a Text-String. This basically means that anything that is attempted to be stored into the variable MyString should be interepretted as Text. If it is not text, VB will try to convert it to text. If it cannot, an error will occur with the message “Type Mismatch”

MyString = "Hello! This is text in a string variable!"

This line of code shows you how to set the contents of a variable. It is simply a case of stating that Variable = SOMETHING. The text after the equals sign must be enclosed inside quotation marks. If it was not enclosed in quotation marks, VB would try to execute the text as code, and an error would occur (the text would also be red). So enclosing it in quotation marks tells VB not to interpret this as code, but use it as a text value.

MsgBox MyString

This line of code causes a messagebox to be displayed. The command for doing so is MsgBox. The part following MsgBox in this instance is our variable called MyString, however we didn’t have to use a variable here. We could have explicitly put in any text we like enclosed in quotation marks, and it would be executed in the same way, displaying the inside of the quotation marks instead of the contents of the variable.

Dim Point(2) As Double

This should look quite familiar – again we’re declaring a variable. You’ll notice that it is a different type. In this instance it is a number. Specifically, a double precision floating point number, but we don’t need to know the details yet. You might also have noticed the brackets containing the number 2. The name of the variable is still just Point but this time it is an array. Think of an array as a list within that variable. This particular array has 3 elements to that list – 0, 1, and 2. We’ll probably talk more about arrays at another time.

Point(0) = 10 'This is x
Point(1) = 20 'This is y
Point(2) = 0 'This is z

Similarly to before, this is assigning values to the variable. However this time, as we’re dealing with an array, we need to specify which part of the list we want to assign our value to. Realise that all of the values are actually stored in the same variable, but just organised into a list.

Dim TextHeight As Double
TextHeight = 10

No problems here.

ThisDrawing.ModelSpace.AddText MyString, Point, TextHeight

The final line of code in this subroutine. The first part is quite self explanatory – Thisdrawing.Modelspace.Addtext. This adds some text into the modelspace of the active drawing. If there is no active drawing (i.e, they’re all closed) an error will occur. In order for AutoCAD® to be able to add text, it needs to know certain things: What text do you want to display? Where do you want to display it? What size do you want it to be? At this stage I will point out that the text we are adding is NOT MText. It is Text – what you would get by using the Text command in AutoCAD®. Conveniently, we’ve already set up some variables that contain the values we need. Pop them after Thisdrawing.Modelspace.Addtext in the right order, delimited with a comma, and you’ve now got some text in the modelspace of the current drawing.


I hope you found this a useful introduction – we haven’t don’t anything particularly useful yet, but there’s much much more great info where this came from. If you liked what you read I would encourage a subscription – you won’t regret it!