Most designs created with the AutoCAD® program start off with simple geometric objects, such as lines, circles, and arcs. The geometric objects are used to represent holes, bolts, motors, and even the outside of a building. As a design grows in complexity, elements often are repeated many times. For example, you might use several lines and circles to represent a bolt head or a desk with a grommet.
AutoCAD allows you to reuse geometry by creating what is known as a block. A block is a named grouping of objects that can be inserted in a drawing multiple times. Each insertion creates a block reference that displays the objects stored in the block at the insertion point. If the block is changed, each block reference based on that block is updated.
Blocks aren't the only method for reusing geometry or other data in a drawing. A drawing file can also include external references (xrefs) to geometry stored in another drawing file. External references can include blocks, raster images, and other documents. When you reference another document, such as a PDF or DWF file, it is known as an underlay. In this chapter, I explain how to use VBA to work with blocks and external referenced files.
Blocks make it possible to logically group basic geometry together with a unique name and then create instances of that geometry within a drawing. Blocks are implemented as two separate objects: block definitions and block references. Block definitions are nongraphical objects that are stored in the AcadBlocks
collection object. Each block definition is represented by an AcadBlock
object, which contains the geometry and attribute definitions that define how the block should appear and behave when it is inserted into the drawing area. A block definition can contain either static or dynamic properties.
Figure 7.1 shows the relationship between a block definition and a block reference and how the attributes of the block are used to bring the geometry into model space.
You can think of a block definition much like a cookie recipe. The recipe lists the ingredients (which determines how the cookie should taste) and provides instructions for combining those ingredients and baking the dough. What the recipe doesn't control is how much dough is placed on any particular spot on the cookie sheet before baking. The exact placement and amount of the cookie dough on the tray is determined by the baker. Similarly, an end user uses a block reference in a drawing to determine the exact placement, size, and number of geometries to be displayed. I explain how to insert and work with block references in the “Inserting and Working with Block References” section later in this chapter.
A new block definition can be added to a drawing using the Add
function of the AcadBlocks
collection object. The Add
function expects two argument values and returns an AcadBlock
object. Before adding a new block definition, you should use the Item
method with an error handler to check to see if a block definition with the specific name you want to use already exists in the drawing. The Item
method can also be used to get a specific block definition in the AcadBlocks
collection object and, as with other collections, a For
statement can be used to step through the block definitions in a drawing.
The following shows the syntax of the Add
function:
retVal = object
.Add(origin
, blockName
)
Its arguments are as follows:
retVal
The retVal
argument represents the new AcadBlock
collection object returned by the Add
function.object
The object
argument specifies the AcadBlocks
collection object that is used to add a new block definition.origin
The origin
argument is an array of three doubles that defines the origin of the new block definition; think insertion point for the block reference.blockName
The blockName
argument is a string that specifies the unique name to be assigned to the new block definition.The following code statements add a new block definition named RoomNum
:
' Defines the origin of the block
Dim dOrigin(2) As Double
dOrigin(0) = 5: dOrigin(1) = 2.5: dOrigin(2) = 0
Dim oBlkDef As AcadBlock
Set oBlkDef = ThisDrawing.Blocks.Add(dOrigin, "RoomNum")
Here is an example that checks for the existence of a block definition named RoomNum
:
On Error Resume Next
Dim oBlkDef As AcadBlock
Set oBlkDef = ThisDrawing.Blocks("RoomNum")
If Err Then
MsgBox "Block definition doesn't exist."
Else
MsgBox "Block definition exists."
End If
After an AcadBlock
object has been obtained using the Item
or Add
method of the AcadBlocks
collection object, you can step through the objects of the block using a For
statement or the Item
method of the AcadBlock
object. You can use the same functions to add new objects to a block definition as you would to add objects to model space or paper space. I explained how to add objects to and step through the objects of model space in Chapter 4, “Creating and Modifying Drawing Objects.”
The following code statements add a closed lightweight polyline to the block definition named RoomNum
:
On Error Resume Next
' Gets the RoomNum block definition
Dim oBlkDef As AcadBlock
Set oBlkDef = ThisDrawing.Blocks("RoomNum")
' If the block doesn't exist, an error is generated
If Err Then
' Defines the origin of the block
Dim dOrigin(2) As Double
dOrigin(0) = 5: dOrigin(1) = 2.5: dOrigin(2) = 0
' Adds the block definition
Set oBlkDef = ThisDrawing.Blocks.Add(dOrigin, "RoomNum")
' Defines the vertex points for the
' lightweight polyline
Dim dPts(7) As Double
dPts(0) = 0: dPts(1) = 0
dPts(2) = 10: dPts(3) = 0
dPts(4) = 10: dPts(5) = 5
dPts(6) = 0: dPts(7) = 5
' Adds a new lightweight polyline to the block
Dim oLWPoly As AcadLWPolyline
Set oLWPoly = oBlkDef.AddLightWeightPolyline(dPts)
' Closes the lightweight polyline
oLWPoly.Closed = True
' Sets the layer of the lightweight polyline to 0
oLWPoly.Layer = "0"
End If
When a block definition is no longer needed, it can be removed from the drawing using the Delete
method of an AcadBlock
object. A block definition can't be deleted if a block reference associated with the block definition is inserted into the drawing.
A block definition can contain what is known as an attribute. An attribute is similar to a text object, except its value can be changed after a block reference has been inserted into a drawing. Attributes allow you to store string values and then extract their values later. There are two types of attributes that are part of the block creation and insertion process: attribute definitions and attribute references. Attribute definitions can be added to a block definition, and attribute references are part of each block reference inserted into a drawing that is associated with a block definition that has one or more attribute definitions.
The AddAttribute
function is used to add an attribute definition to a block definition and returns an AcadAttribute
object. The following shows the syntax of the AddAttribute
function:
retVal = object
.AddAttribute(height
, mode
, prompt
, insertionPoint
, tag
, value
)
Its arguments are as follows:
retVal
The retVal
argument represents the new AcadAttribute
object returned by the AddAttribute
function.object
The object
argument specifies the AcadBlock
object to add the attribute definition.height
The height
argument is a double that represents the height of the attribute.mode
The mode
argument is an integer that represents the behavior of the attribute reference added to a block reference when the block is inserted into the drawing. Instead of using an integer value, I recommend that you use the constant values of the AcAttributeMode
enumerator. Table 7.1 lists each of the constant values of the AcAttributeMode
enumerator. You can specify more than one constant by separating each constant with a plus symbol, such as acAttributeModeInvisible + acAttributeModeNormal
.prompt
The prompt
argument is a string that represents the text that provides a hint for the value that's expected when the block reference is inserted.insertionPoint
The insertionPoint
argument is an array of three doubles that defines the insertion point of the attribute definition.tag
The tag
argument is a string that represents the text that's displayed in the drawing if the block reference is exploded after being inserted and the value used to extract the attribute's value from a block reference. A tag cannot contain spaces.value
The value
argument is a string that represents the default value of the attribute when the block reference is inserted.Table 7.1 Constant values of the
enumeratorAcAttributeMode
Constant | Description |
acAttributeModeConstant |
Indicates the value of the attribute can't be changed. |
acAttributeModeInvisible |
Attribute is marked as invisible. The attmode system variable controls the display of all invisible attributes. |
acAttributeModeLockPosition |
Position of the attribute can't be adjusted using grip editing. |
acAttributeModeMultipleLine |
Attribute supports multiple lines of text instead of the standard single line of text. |
acAttributeModeNormal |
Default display behavior of the attribute is maintained when the block is inserted using the insert command. |
acAttributeModePreset |
Value of the attribute is preset. When the block is inserted using the insert command, the user isn't prompted to enter a value for the attribute. |
acAttributeModeVerify |
User is prompted to verify the value they provide for the attribute when inserting the block reference with the insert command. |
Table 7.1 lists the constant values of the AcAttributeMode
enumerator that can be passed to the mode argument of the AddAttribute
function or assigned to the Mode
property of an AcadAttribute
object.
The following code statements add an attribute definition to the block definition assigned to the oBlkDef
variable:
' Defines the insertion point of the attribute definition
Dim dInsPt(2) As Double
dInsPt(0) = 5: dInsPt(1) = 2.5: dInsPt(2) = 0
' Adds the attribute definition to the block
Dim oAttDef As AcadAttribute
Set oAttDef = oBlkDef.AddAttribute(2.5, acAttributeModeNormal, _
"Room#", dInsPt, "Room#", "101")
' Sets the alignment for the attribute's text
oAttDef.Alignment = acAlignmentMiddleCenter
oAttDef.TextAlignmentPoint = dInsPt
After adding an attribute definition to a block definition, you can modify its appearance and behavior using the properties and methods of the AcadAttribute
object. The properties and methods of an AcadAttribute
object are similar to those of an AcadText
object. I discussed the AcadText
object in Chapter 6, “Annotating Objects.”
Table 7.2 lists the properties of the AcadAttribute
object that are unique to the object and are different from those of an AcadText
object.
Table 7.2 Properties related to an
objectAcadAttribute
Property | Description |
Constant |
Returns True if the attribute is set to the constant mode. |
Invisible |
Returns True if the attribute should be invisible when the block reference is inserted. |
LockPosition |
Returns True if the attribute can't be modified using grip editing when the block reference is inserted. |
MTextAttribute |
Returns True if the attribute should be multiline instead of single-line text. |
MTextAttributeContent |
Specifies the content for the multiline text when the MTextAttribute property is True. |
MTextBoundaryWidth |
Specifies the width of the multiline text when the MTextAttribute property is True. |
MTextDrawingDirection |
Specifies the direction in which the text should be drawn when the MTextAttribute property is True. |
Preset |
Returns True if the user shouldn't be prompted to enter a value for the attribute when the block is inserted. |
PromptString |
Specifies the prompt string that is displayed for the attribute when the block is inserted. |
TagString |
Specifies the tag for the attribute that is displayed in the drawing if the block reference is exploded after being inserted. The tag can also be useful when trying to identify which attribute's value to extract when generating a BOM. |
TextString |
Specifies the default text for the attribute to use when the block is inserted. |
Verify |
Returns True if the user should be prompted to verify the value of the attribute when the block is inserted. |
You can add new objects or modify existing objects of a block definition, much like you can in model space or paper space. The Item
method of the AcadBlock
object can be used to get a specific object or a For
statement to step through all objects in a block definition.
In addition to modifying the objects in a block definition, the origin—the insertion point of a block—can be modified using the Origin
property. The Origin
property of an AcadBlock
object allows you to get or set the origin for the block definition. The origin of a block definition is defined as a double array with three elements.
Besides modifying the objects and origin of a block, you can specify whether a block reference created from a block can be exploded, set the units that control the scaling of a block, and make other changes. Table 7.3 lists the properties that control the behavior of and provide information about an AcadBlock
object.
Table 7.3 Properties related to an
objectAcadBlock
Property | Description |
BlockScaling |
Specifies if the block can only be scaled uniformly (acUniform ) or the block can be assigned a different scale factor for each axis (acAny ). |
Comments |
Specifies a string that describes the block definition. |
Count |
Returns an integer value that contains the number of block references that have been inserted into the drawing based on the block definition. |
Explodable |
Returns True if the block reference inserted into the drawing can be exploded. |
Name |
Specifies a string that contains the name of the block definition. |
Units |
Specifies the units of measurement for the block. A constant value from the AcInsertUnits enumerator is returned by or can be assigned to this property. See the Object Browser in the VBA Editor for a list of possible values. The units specified affect the insertion scale of the block. |
After a block definition has been updated, you should use the Regen
method of the AcadApplication
object to regenerate the display of the drawing. You can also update the display of an individual block reference using the Update
method. I explained the Regen
and Update
methods in Chapter 4. If you add or remove an AcadAttribute
object in a block definition, the block references inserted into model space or paper space aren't updated to reflect the change unless the attribute being changed is defined as a constant.
To reflect the changes in the attributes between a block definition and the block references inserted into a drawing, you will need to do the following:
I discuss more about working with block references in the “Inserting and Working with Block References” section later in this chapter.
Block definitions stored in the AcadBlocks
collection object of a drawing aren't only used to insert block references. Model space and paper space are also block definitions with special names along with external references (xrefs) and layouts. You can determine a block definition's type using the properties in Table 7.4. I discuss xrefs in the “Working with Xrefs” section later in this chapter and layouts in Chapter 8, “Outputting Drawings.”
Table 7.4 Properties used to determine a block definition's type
Property | Description |
IsDynamicBlock |
Returns True if the block definition contains dynamic properties. |
IsLayout |
Returns True if the block definition is for a layout. You can use the Layout property of an AcadBlock , AcadModelSpace , or AcadPaperSpace object to get the object's associated AcadLayout object. |
IsXref |
Returns True if the block definition is for an external reference. |
A block reference is an instance—not a copy—of the geometry from a block definition; the geometry only exists as part of the block definition, with the exception of attributes. Attribute definitions that are part of a block definition are added to a block reference as attribute references unless the attribute definition is defined as a constant attribute. Constant attributes are part of the geometry that is inherited from a block definition and are not part of the block reference.
The InsertBlock
function is used to insert a reference of a block definition in model space, paper space, or another block definition and expects seven argument values that define the block definition you want to insert, as well as the placement and size of the block reference. After a block reference has been inserted, an AcadBlockReference
object is returned.
The following shows the syntax of the InsertBlock
function:
retVal = object
.InsertBlock(insertionPoint
, blockName
, xScale
, yScale
,
zScale
, rotation
[, password
])
Its arguments are as follows:
retVal
The retVal
argument represents the new AcadBlockReference
object returned by the InsertBlock
function.object
The object
argument specifies the AcadBlock
, AcadModelSpace
, or AcadPaperSpace
object where the block reference is to be inserted.insertionPoint
The insertionPoint
argument is an array of doubles that represents the insertion point of the block reference.blockName
If you wish to insert the reference into a block definition, use the blockName
argument (a string) to specify the name of that block definition. The block must already be defined in the drawing before the insertion can be executed. If you wish to insert a DWG file as a reference into a drawing, specify the full path of a DWG file. When a DWG file is specified, an AcadBlock
object based on the objects in model space of the DWG file specified is created, and then the block reference is inserted.For example, the following inserts a block named grid.dwg
from the location c:symbols
:
Set oBlkRef = ThisDrawing.ModelSpace.InsertBlock( _
insPt, "c:symbolsgrid.dwg", 1, 1, 1, 0)
xScale
, yScale
, and zScale
The xScale
, yScale
, and zScale
arguments are doubles that represent the scale factors of the block reference.rotation
The rotation
argument is a double that represents the rotation angle of the block reference. The rotation angle must be expressed in radians.password
The password
argument is an optional string that represents the password assigned to restrict the drawing file from being opened or inserted into by unapproved users. This argument is only required if you are inserting a block based on a DWG file that is password protected.The following code statements insert a block reference based on a block named RoomNum
at 15,27
. (Remember, the block you name in code like this must already be defined in your drawing before the code can be executed. Be sure to use an error handler to add the block if it doesn't exist.)
' Defines the insertion point
Dim insPt(2) As Double
insPt(0) = 15: insPt(1) = 27: insPt(2) = 0
' Defines the name of the block
Dim blkName As String
blkName = "RoomNum"
' Inserts the block reference
Dim oBlkRef As AcadBlockReference
Set oBlkRef = ThisDrawing.ModelSpace.InsertBlock( _
insPt, blkName, 1, 1, 1, 0)
Once a block reference, an AcadBlockReference
object, is inserted into a drawing, you can modify it using the methods and properties inherited from the AcadEntity
object and those specific to the AcadBlockReference
object. I explained how to use the methods and properties of the AcadEntity
object in Chapter 4. Table 7.5 lists the properties that are used to change the placement, rotation, and scale of a block reference.
Table 7.5 Properties used to affect a block reference
Property | Description |
InsertionPoint |
Specifies the insertion point of the block reference in the drawing and is an array of doubles |
InsUnits |
Returns a string that represents the insertion units saved with the block |
InsUnitsFactor |
Returns the insertion factor that is based on the insertion units of the block and those of the drawing |
Rotation |
Specifies the rotation of the block reference |
XEffectiveScaleFactor |
Specifies the effective scale factor along the X-axis for the block reference |
XScaleFactor |
Specifies the scale factor along the X-axis for the block reference |
YEffectiveScaleFactor |
Specifies the effective scale factor along the Y-axis for the block reference |
YScaleFactor |
Specifies the scale factor along the Y-axis for the block reference |
ZEffectiveScaleFactor |
Specifies the effective scale factor along the Z-axis for the block reference |
ZScaleFactor |
Specifies the scale factor along the Z-axis for the block reference |
Block references also support the ability to be exploded. The Explode
method is used to explode a block reference and it returns an array of the objects added to the drawing as a result of exploding the block reference. The objects in the array are copies of the objects from the block definition associated with the block reference. When the Explode
method is executed, the block reference isn't removed from the drawing. You must decide what to do with the block reference. Typically, the block reference is removed using the Delete
method, while the objects from the Explode
method are kept.
The following code statements explode the first block reference located in model space and then list the block definition name and the objects that make up the block definition:
Sub ExplodeFirstBlkRef()
Dim oBlkRef As AcadBlockReference
Dim oObj As Object
' Step through model space
For Each oObj In ThisDrawing.ModelSpace
' If a block reference is found, explode it
If TypeOf oObj Is AcadBlockReference Then
Set oBlkRef = oObj
' Explode the block reference
Dim vObjArray As Variant
vObjArray = oBlkRef.Explode
' List the objects that were added
ThisDrawing.Utility.Prompt vbLf & "Block exploded: " & _
oBlkRef.Name & vbLf
ThisDrawing.Utility.Prompt vbLf & "Objects added: " & _
vbLf
' Remove the block reference
oBlkRef.Delete
Dim oAcadObj As AcadObject
Dim oObjFromBlkRef As Variant
For Each oObjFromBlkRef In vObjArray
Set oAcadObj = oObjFromBlkRef
ThisDrawing.Utility.Prompt " " & oAcadObj.ObjectName & _
vbLf
Next oObjFromBlkRef
' Exit the For statement since we are interested
' in the first block reference only
Exit For
End If
Next oObj
End Sub
Here is an example of the output from the previous example code:
Block exploded: 2x4x8
Objects added:
AcDbPolyline
AcDbLine
AcDbLine
When a block reference is first inserted into a drawing, the default values of all attributes are used. The value of each nonconstant attribute of a block reference can be changed. Before you access the attributes of a block reference, you should make sure the block reference has attributes. The HasAttributes
property of an AcadBlockReference
object returns True
if a block reference has attributes, either constant or nonconstant.
The GetAttributes
and GetConstantAttributes
functions of an AcadBlockReference
object are used to access the attributes of a block reference. Neither function accepts any arguments. The GetAttributes
function returns an array of AcadAttributeReference
objects that aren't defined as constant attributes attached to a block reference, whereas the GetConstantAttributes
function returns an array of AcadAttribute
objects.
Listing 7.1 is a custom procedure that demonstrates how to get both the attributes and constant attributes attached to a block reference.
Here is an example of the output generated by the custom ListBlockAtts
procedure from Listing 7.1:
*Block Reference*
Block name: RoomNumber
*Nonconstant Attributes*
Tag: ROOM#
Value: 101
*Constant Attributes*
None
In addition to listing the values of the attributes attached to a block reference, you can modify the appearance and placement of the attribute references returned by the GetAttributes
function. If you make changes to an attribute reference, make sure to execute the Update
method and regenerate the display of the object. The AcadAttributeReference
and AcadAttribute
objects are nearly identical. However, the AcadAttributeReference
object doesn't support the Mode
, Preset
, PromptString
, or Verify
property.
Most block references display a single set of geometry, meaning that the objects that are included in the block definition are the only ones that can be shown in the drawing. Starting with AutoCAD 2006, block definitions were extended to support what are known as dynamic properties. Block definitions with dynamic properties are known as dynamic blocks. You can't create dynamic blocks with the AutoCAD Object library, but you can modify the custom properties of a dynamic block after it is inserted into a drawing. For information on how to create a dynamic block, see the topic “About Dynamic Blocks” in the AutoCAD Help system.
The IsDynamicBlock
property of the AcadBlockReference
object can be used to determine whether a block reference has dynamic properties. When the IsDynamicBlock
property returns True
, the block reference has dynamic properties that can be queried and modified.
Once you have verified that a block reference has dynamic properties, you use the GetDynamicBlockProperties
function to get an array of AcadDynamicBlockReferenceProperty
objects. The Value
property of an AcadDynamicBlockReferenceProperty
object is used to get and set the value of a dynamic property, whereas the PropertyName
property returns a string that represents the name of the dynamic property.
Listing 7.2 is a custom procedure that demonstrates how to get the custom properties and their values of a block reference named Door - Imperial
. You can insert the Door - Imperial
block reference using the block tool on the Architectural tab of the Tool Palettes window (displayed using the toolpalettes
command).
Here is an example of the output generated by the custom ListCustomProperties
procedure from Listing 7.2:
*Block Reference*
Block name: *U3
*Dynamic Properties*
Property Name: Door Size
Value: 40
Property Name: Origin
Value: 0,0
Property Name: Wall Thickness
Value: 6
Property Name: Origin
Value: 0,0
Property Name: Hinge
Value: 0
Property Name: Swing
Value: 0
Property Name: Opening Angle
Value: Open 30°
When a user manipulates a grip associated with a dynamic property, onscreen it looks like the user is manipulating the block reference through a stretching, arraying, moving, or other action. The action that is performed by the user results in the creation of a new anonymous block definition. An anonymous block is a block that can't be inserted into a drawing but that is used as a way to let AutoCAD create and manage unique blocks.
You can convert a dynamic block to an anonymous block without dynamic properties using the ConvertToAnonymousBlock
method or a new block definition using the ConvertToStaticBlock
method. The ConvertToStaticBlock
method expects a string that represents the name of the new block definition.
The appearance and custom properties of a dynamic block can be reset to their default values. To reset the appearance and custom properties of a dynamic block, you use the ResetBlock
method of an AcadBlockReference
object. The ResetBlock
method doesn't accept any argument values and doesn't return a value.
AutoCAD allows you to create what is known as an external reference. An external reference is a reference to a file that is stored outside of a drawing file. The contents in an external file can be another drawing, a raster or vector image, or even a file that supports Object Linking and Embedding (OLE). OLE allows you to embed, among other things, a Word document or an Excel spreadsheet into a drawing file. In addition to referencing a file, you can import objects into a drawing using OLE. An OLE object is represented by the AcadOle
object in the AutoCAD Object library. You can modify, but not create, an OLE object with the AutoCAD Object library. I discuss how to import objects or a file in Chapter 8.
You can see which files are externally referenced to a file by accessing the items of the AcadFileDependencies
collection object. Each file that a drawing is dependent on to correctly display objects is part of the AcadFileDependencies
collection object. I mention the AcadFileDependencies
collection object in the “Listing File Dependencies” section later in this chapter.
An external drawing file referenced into a drawing is known as an xref. Xrefs are similar to blocks because they allow for the reuse of geometry in any drawing with one distinct difference. The difference that sets blocks and xrefs apart is that any changes made to the objects in the external drawing file are reflected in any drawings that reference the file. Xrefs are frequently used in architectural and civil engineering drawings to reference a floor plan or survey drawing. An xref is represented by an AcadExternalReference
object and is similar to an AcadBlockReference
object except in the way that the object can be modified and managed.
An xref is attached to a drawing, not inserted like a block or added like other graphical objects. The AttachExternalReference
function returns an AcadExternalReference
object and expects nine argument values that define which file to attach, as well as the placement and size of the xref. When an xref is attached to a drawing, an AcadBlock
object is created. The AcadBlock
object contains the geometry that is in the referenced drawing file, but objects can't be added or modified in that AcadBlock
object. Figure 7.2 shows the flow of data that takes place when a drawing file is attached to a drawing and an xref is placed in model space.
The following shows the syntax of the AttachExternalReference
function:
retVal = object
.AttachExternalReference(fileName
, xrefName
, insertionPoint
,
xScale
, yScale
, zScale
, rotation
,
overlay
[, password
])
Its arguments are as follows:
retVal
The retVal
argument represents the new AcadExternalReference
object returned by the AttachExternalReference
function.object
The object
argument specifies the AcadBlock
, AcadModelSpace
, or AcadPaperSpace
object where you wish to attach the xref.fileName
The fileName
argument is a string that represents the name of the external DWG file you want to reference.xrefName
The xrefName
argument is a string that represents the name you want to assign to the AcadBlock
object that is added to the drawing.insertionPoint
The insertionPoint
argument is an array of doubles that represents the insertion point of the xref.xScale
, yScale
, and
zScale
The xScale
, yScale
, and zScale
arguments are doubles that represent the scale factors of the xref.rotation
The rotation
argument is a double that represent the rotation angle of the xref. The rotation angle must be expressed in radians.overlay
The overlay
argument is a Boolean that represents the reference type for the xref. There are two reference types: attachment and overlay. The reference types don't affect the current drawing unless the drawing is referenced into another drawing. An attachment reference type allows the xref to be displayed in other drawings that reference the drawing that contains the xref, whereas an overlay reference restricts the xref to be displayed only in the drawing to which it is attached. Use a value of True
to specify an overlay reference type.password
The password
argument is an optional string that represents the password assigned to restrict the drawing file from being opened or referenced by unapproved users.The following code statements add an xref based on the Ch07_Building_Plan.dwg
file at 0,0
and set the reference type to attachment:
' Defines the insertion point
Dim insPt(2) As Double
insPt(0) = 0: insPt(1) = 0: insPt(2) = 0
' Defines the path to the drawing file
Dim dwgName As String
dwgName = ThisDrawing.GetVariable("MyDocumentsPrefix") & _
"MyCustomFilesCh07_Building_Plan.dwg"
' Adds the xref
Dim oXref As AcadExternalReference
Set oXref = ThisDrawing.ModelSpace.AttachExternalReference( _
dwgName, "Building_Plan", insPt, 1, 1, 1, 0, False)
Once an xref has been attached to a drawing, you can access information about the instance of the xref. As I previously mentioned, an xref is similar to a block reference that has been inserted into a drawing, and even the AcadExternalReference
and AcadBlockReference
objects share many of the same properties and methods. For information on how to access information about and modify a block reference, see the “Modifying a Block Reference” section earlier in this chapter.
Although an AcadExternalReference
and AcadBlockReference
object have much in common, there are a few differences:
Although there are a number of differences between xrefs and block references, in the AutoCAD Object library the AcadExternalReference
and AcadBlockReference
objects are similar. The only difference between the two object types is the Path
property. The Path
property of an AcadExternalReference
object can be used to get and specify the path that AutoCAD should look in for the externally referenced drawing file. I show an example of using the Path
property in the next section.
For each externally referenced file that is attached to a drawing, AutoCAD creates an in-memory database for that file. The database is represented by an AcadDatabase
object and contains access to the nongraphical and graphical objects stored in the externally referenced file. The database of an xref can be accessed with the XRefDatabase
property of an AcadBlock
object.
Objects in the database of an xref returned by the XRefDatabase
property can't be directly modified. However, it is possible to open the externally referenced drawing file into memory with the AutoCAD/ObjectDBX Common Type library. After a drawing file is opened in memory with the AutoCAD/ObjectDBX Common Type library, the objects in the file can then be modified. Once changes have been made to the drawing, you use the Reload
method of an AcadBlock
object in the drawing to which the xref is attached to update its display. I mention the Reload
method in the next section and how to reference other object libraries in Chapter 12, “Communicating with Other Applications.”
The management of the reference between an external drawing file and an xref is handled through an AcadBlock
object. The name of the AcadBlock
object used by an xref can be obtained with the Name
property of the AcadExternalReference
object. Once the name of the block has been obtained, you can use the Item
method of the AcadBlocks
collection object to get the AcadBlock
object associated with the xref.
In addition to using the Item
method, you can use a For
statement to step through all the blocks in a drawing and see which ones are associated with an external file. While stepping through the AcadBlocks
collection object, the IsXref
property of the AcadBlock
object returns True
if the block represents an external referenced file.
The following code statements get the AcadBlock
object for a block named Building_Plan
and then use the IsXref
property to see if it is an xref. If the block is an xref, a message box with the path to the external referenced file is displayed.
' Defines the name of the xref to locate
Dim sXrefName As String
sXrefName = "Building_Plan"
' Gets the name of the block
Dim oBlkDef As AcadBlock
Set oBlkDef = ThisDrawing.Blocks(sXrefName)
' Check to see if the block is an xref
If oBlkDef.IsXRef Then
' Display information about the xref
MsgBox "Block name: " & sXrefName & _
vbLf & "Path: " & oBlkDef.Path
End If
The Path
property shown in the previous sample code is used to get the current path to the external referenced file, but it can also be used to update the default location for the externally referenced drawing file. After an AcadBlock
object containing an external reference has been obtained, you can then manage the reference between the drawing and the external referenced file stored outside of AutoCAD. Table 7.6 lists the four functions that can be used to manage an xref.
Table 7.6 Methods used to manage an xref
Method | Description |
Bind |
Removes the reference to the external file, and all xrefs attached to the drawing are converted to blocks and stored as part of the drawing. Changes made to the external file no longer affect the objects in the drawing. The method expects a Boolean value; use True if you do not want to add a prefix to the symbol names that are created from the external reference or use False to add a prefix to the symbol name. Use a value of False to maintain the appearance of objects in the xref. Specifying a value of True indicates that the objects from the xref being merged will use the nongraphical objects defined in the drawing to which the xref is attached. If the nongraphical objects don't exist in the drawing to which the xref is attached and True is specified, the nongraphical object is copied from the xref's database. |
Detach |
Removes the reference to the external referenced file, and all xrefs attached to the drawing are removed. This method doesn't accept any arguments. |
Reload |
Updates the geometry in the drawing by reading the objects from the external referenced file. This method doesn't accept any arguments. |
Unload |
Maintains the reference to the external referenced file, and all xrefs remain in the drawing. The file isn't loaded into the drawing, which results in the objects contained in the file not being displayed. This method doesn't accept any arguments. |
The following code statements reload the external reference named Building_Plan
:
' Defines the name of the xref to locate
Dim sXrefName As String
sXrefName = "Building_Plan"
' Gets the name of the block
Dim oBlkDef As AcadBlock
Set oBlkDef = ThisDrawing.Blocks(sXrefName)
' Reload the xref
oBlkDef.Reload
A raster image stored in an external file can be attached to a drawing. You might want to reference an external image file to place a company logo on a title block, display a watermark, or reference a topography map. An image file that has been added to a drawing is represented by an AcadRasterImage
object.
A raster image can be added to model space or paper space using the AddRaster
function. The AddRaster
function returns an AcadRasterImage
object and expects four argument values that specify the image file you want to add and then the placement and size of the image.
The following shows the syntax of the AddRaster
function:
retVal = object
.AddRaster(fileName
, insertionPoint
, scaleFactor
, rotation
)
Its arguments are as follows:
retVal
The retVal
argument represents the new AcadRasterImage
object returned by the AddRaster
function.object
The object
argument specifies the AcadBlock
, AcadModelSpace
, or AcadPaperSpace
object and indicates where you want to add the raster image.fileName
The fileName
argument is a string that represents the name of the image file.insertionPoint
The insertionPoint
argument is an array of doubles that represents the insertion point of the raster image.scaleFactor
The scaleFactor
argument is a double that represents the scale factor of the raster image.rotation
The rotation
argument is a double that represents the rotation angle of the raster image. The rotation angle must be expressed in radians.The following code statements add a raster image based on the acp_logo.png
filename to 5,5
and set the background of the image to transparent:
' Defines the insertion point
Dim insPt(2) As Double
insPt(0) = 5: insPt(1) = 5: insPt(2) = 0
' Defines the path to the image
Dim imageName As String
imageName = ThisDrawing.GetVariable("MyDocumentsPrefix") & _
"MyCustomFilesacp_logo.png"
' Adds the raster image
Dim oRaster As AcadRasterImage
Set oRaster = ThisDrawing.ModelSpace. _
AddRaster(imageName, insPt, 1, 0)
' Sets the background of the image to transparent
oRaster.Transparency = True
After a raster image has been added to a drawing, you can control its appearance using the object properties and methods. A raster image supports the same general properties and methods as all graphical objects in a drawing, along with additional object-specific properties. Table 7.7 lists the properties that are specific to a raster image.
Table 7.7 Raster image–related properties and methods
Property/Method | Description |
Brightness |
Specifies the brightness applied to the raster image; the valid range is 0 to 100. |
ClippingEnabled |
Returns True if the raster image is clipped. |
ClipBoundary |
Specifies the clipping boundary of the raster image. The ClipBoundary method expects an array of doubles that form a closed region. The array must contain a minimum of six elements; each element pairing specifies a 2D coordinate value. |
Contrast |
Specifies the contrast applied to the raster image; the valid range is 0 to 100. |
Fade |
Specifies the fade value applied to the raster image; the valid range is 0 to 100. The greater the value, the more transparent the object. |
Height |
Returns the height, in pixels, for the raster image. This property is read-only. |
ImageFile |
Specifies the full path to the external file for the raster image. |
ImageHeight |
Specifies the height, in pixels, for the raster image. |
ImageVisibility |
Returns True if the raster image is visible. |
ImageWidth |
Specifies the width, in pixels, for the raster image. |
Name |
Specifies the name of the raster image. |
Origin |
Specifies the insertion point of the raster image in the drawing and is an array of doubles. |
Rotation |
Specifies the rotation of the raster image. |
ScaleFactor |
Specifies the scale factor applied to the raster image. |
ShowRotation |
Returns True if the raster image is displayed at its specified rotation. |
Transparency |
Returns True if the background of the raster image is displayed as transparent. |
Width |
Returns the width, in pixels, for the underlay. This property is read-only. |
Underlays consist of geometry and annotation that is referenced into a drawing file from a drawing web (DWF/DWFx) file, MicroStation design (DGN) file, or an Adobe portable document (PDF) file. The geometry in an underlay is less accurate than that of a drawing because of the source applications that created the objects. Even though an underlay is less accurate, the accuracy might be enough for many designs created in the architectural and civil engineering industries.
When an underlay is attached to a drawing, its objects can be controlled using the layer information embedded in the underlay. As users create new objects in a drawing, they can use object snaps to snap to geometry that is part of an underlay.
The AutoCAD Object library doesn't provide support for attaching or detaching an underlay, but it does provide some support for querying and modifying an underlay that has already been attached to a drawing. If you want to attach an underlay, you can use the -dgnattach
, -dwfattach
, or -pdfattach
commands with the SendCommand
or PostCommand
method.
The following objects represent the underlays that can be attached to a drawing:
AcadDgnUnderlay
—DGN underlayAcadDwfUnderlay
—DWF/DWFx underlayAcadPdfUnderlay
—PDF underlayThe following code statements demonstrate how the ObjectName
property can be used to determine the type of an underlay object. The first two code statements get the first object in model space and expect the object to be an underlay.
Dim oEnt As AcadEntity
Set oEnt = ThisDrawing.ModelSpace(0)
Select Case oEnt.ObjectName
Case "AcDbDgnReference"
MsgBox "Underlay is a DGN file."
Case "AcDbDwfReference"
MsgBox "Underlay is a DWF file."
Case "AcDbPdfReference"
MsgBox "Underlay is a PDF file."
End Select
An underlay shares many properties in common with an AcadRaster
object. The following properties are shared between underlays and raster images:
ClippingEnabled
Contrast
Fade
Height
Rotation
ScaleFactor
Width
Table 7.8 lists the properties specific to an underlay. These properties can be used to control the display of the object and get information about the referenced file.
Table 7.8 Underlay-related properties
Property | Description |
AdjustForBackground |
Returns True if the colors in the underlay are adjusted for the current background color of the viewport. |
File |
Specifies the full path to the external file that contains the objects for the underlay. |
ItemName |
Specifies the sheet or design model name in the underlay file you want to display. A sheet or design model is one of the pages or designs stored in the underlay file. For example, a PDF file can contain several pages, and you use ItemName to specify which page you want to display. |
Monochrome |
Returns True if the colors of the underlay are displayed as monochromatic. |
Position |
Specifies the insertion point of the underlay in the drawing and is an array of doubles. |
UnderlayLayerOverrideApplied |
Specifies whether layer overrides are applied to the underlay; a constant value of acNoOverrides means no overrides are applied, whereas acApplied indicates overrides are applied. |
UnderlayName |
Specifies the name of the underlay file. |
UnderlayVisibility |
Returns True if the objects in the underlay should be visible. |
A drawing file relies on a number of support files to display objects accurately. These support files might be font files, plot styles, external referenced files, and much more. You can use the AcadFileDependencies
collection object to access a listing of the files that need to be included when sharing your files with a subcontractor or archiving your designs. Each dependency in the AcadFileDependencies
collection object is represented by an AcadFileDependency
object.
Although it is possible to directly add new entries for file dependencies to a drawing, I recommend letting the AutoCAD application and AutoCAD Object library do the work for you. Incorrectly defining a file dependency could have unexpected results on a drawing; objects might not display correctly or at all. Methods such as AttachExternalReference
and AddRaster
will add the appropriate file dependency entries to a drawing.
If you want, you can use the CreateEntry
, RemoveEntry
, and UpdateEntry
methods to manage the file dependencies of a drawing. See the AutoCAD Help system for information on the methods used to manage file dependencies. In most cases, you will simply want to query the file dependencies of a drawing to learn which files might be missing and help the user locate them if possible. Use the Item
method or a For
statement to step through the file dependencies of the AcadFileDependencies
collection object.
The following code statements display information about each file dependency at the Command prompt:
Sub ListDependencies()
Dim oFileDep As AcadFileDependency
For Each oFileDep In ThisDrawing.FileDependencies
ThisDrawing.Utility.Prompt _
vbLf & "Affects graphics: " & CStr(oFileDep.AffectsGraphics) & _
vbLf & "Feature: " & oFileDep.Feature & _
vbLf & "File name: " & oFileDep.FileName & _
vbLf & "File size (Bytes): " & CStr(oFileDep.FileSize) & _
vbLf & "Found path: " & oFileDep.FoundPath & _
vbLf
Next oFileDep
End Sub
Here is an example of the output produced for a file dependency:
Affects graphics: True
Feature: Acad:Text
File name: arial.ttf
File size (Bytes): 895200
Found path: C:WINDOWSFONTS
In this section, you will create several new procedures that create and insert room label blocks into a drawing, move the blocks to specific layers based on their names, and extract the attributes of the blocks to produce a bill of materials (BOM). Room labels and blocks with attributes are often used in architectural drawings, but the same concepts can be applied to callouts and parts in mechanical drawings.
As you insert a room label block with the custom program, a counter increments by 1 so you can place the next room label without needing to manually enter a new value. The last calculated value is stored in a custom dictionary so it can be retrieved the next time the program is started. The key concepts I cover in this exercise are:
The RoomLabel
project will contain functions and a main procedure that allow you to create and insert a room label block based on end-user input. The number applied to the block is incremented each time the block is placed in the current drawing. The following steps explain how to create a project named RoomLabel
and to save it to a file named roomlabel.dvb
:
RoomLabel
. Make sure to change the default project name (ACADProject
) to RoomLabel
in the VBA Editor.clsUtilities.cls
file in the MyCustomFiles
folder. Click Open.
The clsUtilities.cls
file contains the utility procedures that you created as part of the DrawPlate
project.
basRoomLabel
.Creating separate drawing files that your custom programs depend on has advantages and disadvantages. One advantage of creating a separate drawing file is that you can use the AutoCAD user interface to create the block file. However, AutoCAD must be able to locate the drawing file so that the custom program can use the file. If AutoCAD can't locate the file, the custom program will have problems. Creating a block definition through code allows you to avoid the need of maintaining separate files for your blocks, thus making it easier to share a custom application with your clients or subcontractors. A disadvantage of using code to create your blocks is the time it takes to write the code for all your blocks and then having to maintain the code once it has been written.
In these steps, you create a custom function named roomlabel_createblkdef
that will be used to create the block definition for the room label block if it doesn't already exist in the drawing.
basRoomLabel
component.basRoomLabel
component, type the following. (The comments are here for your information and don't need to be typed.)Private myUtilities As New clsUtilities
' Constant for the removal of the "Command: " prompt msg
Const removeCmdPrompt As String = vbBack & vbBack & vbBack & _
vbBack & vbBack & vbBack & _
vbBack & vbBack & vbBack & vbLf
Private g_nLastNumber As Integer
Private g_sLastPrefix As String
' Creates the block definition roomlabel
Private Sub RoomLabel_CreateBlkDef()
On Error Resume Next
' Check for the existence of the roomlabel block definition
Dim oBlkDef As AcadBlock
Set oBlkDef = ThisDrawing.Blocks("roomlabel")
' If an error was generated, create the block definition
If Err Then
Err.Clear
' Define the block's origin
Dim dInsPt(2) As Double
dInsPt(0) = 18: dInsPt(1) = 9: dInsPt(2) = 0
' Create the block definition
Set oBlkDef = ThisDrawing.Blocks.Add(dInsPt, "roomlabel")
' Add a rectangle to the block
Dim dPtList(7) As Double
dPtList(0) = 0: dPtList(1) = 0
dPtList(2) = 36: dPtList(3) = 0
dPtList(4) = 36: dPtList(5) = 18
dPtList(6) = 0: dPtList(7) = 18
Dim oLWPline As AcadLWPolyline
Set oLWPline = oBlkDef.AddLightWeightPolyline(dPtList)
oLWPline.Closed = True
' Add the attribute definition to the block
Dim oAttDef As AcadAttribute
Set oAttDef = oBlkDef.AddAttribute(9, acAttributeModeLockPosition, _
"ROOM#", dInsPt, "ROOM#", "L000")
oAttDef.Layer = "Plan_RoomLabel_Anno"
' Set the alignment of the attribute
oAttDef.Alignment = acAlignmentMiddleCenter
oAttDef.TextAlignmentPoint = dInsPt
End If
End Sub
Figure 7.3 shows the block definition that is created by this procedure. To see the contents of the block definition, use the bedit
command and select the RoomLabel
block. As an alternative, you can insert the RoomLabel
block into the drawing and explode it.
Once you've created the block definition and added it to the AcadBlocks
collection object, you can insert it into the drawing by using the insert
command or the InsertBlock
function in the AutoCAD Object library.
In these steps, you create two custom functions named changeattvalue
and roomlabel_insertblkref
. The changeattvalue
function allows you to revise the insertion point and value of an attribute reference attached to a block reference based on the attribute's tag. The roomlabel_insertblkref
function creates a block reference based on the RoomLabel
block definition that was created with the roomlabel_createblkdef
function.
basRoomLabel
component, scroll to the bottom of the last procedure and press Enter a few times. Then, type the following. (The comments are here for your information and don't need to be typed.)' Changes the value of an attribute reference in a block reference
Private Sub ChangeAttValue(oBlkRef As AcadBlockReference, _
vInsPt As Variant, sAttTag As String, _
sNewValue As String)
' Check to see if the block reference has attribute references
If oBlkRef.HasAttributes Then
' Get the attributes of the block reference
Dim vAtts As Variant
vAtts = oBlkRef.GetAttributes
Dim nCnt As Integer
' Step through the attributes in the block reference
Dim oAttRef As AcadAttributeReference
For nCnt = 0 To UBound(vAtts)
Set oAttRef = vAtts(nCnt)
' Compare the attributes tag with the tag
' passed to the function
If UCase(oAttRef.TagString) = UCase(sAttTag) Then
oAttRef.InsertionPoint = vInsPt
oAttRef.TextAlignmentPoint = vInsPt
oAttRef.textString = sNewValue
' Exit the For statement
Exit For
End If
Next
End If
End Sub
' Creates the block definition roomlabel
Private Sub RoomLabel_InsertBlkRef(vInsPt As Variant, _
sLabelValue As String)
' Add the layer Plan_RoomLabel_Anno
myUtilities.CreateLayer "Plan_RoomLabel_Anno", 150
' Create the "roomlabel" block definition
RoomLabel_CreateBlkDef
' Insert the block into model space
Dim oBlkRef As AcadBlockReference
Set oBlkRef = ThisDrawing.ModelSpace. _
InsertBlock(vInsPt, "roomlabel", _
1, 1, 1, 0)
' Changes the attribute value of the "ROOM#"
ChangeAttValue oBlkRef, vInsPt, "ROOM#", sLabelValue
End Sub
Now that you have defined the functions to create the block definition and inserted the block reference into a drawing, the last function creates the main procedure that will prompt the user for input. The roomlabel
procedure will allow the user to specify a point in the drawing, provide a new room number, or provide a new prefix. The roomlabel
procedure uses the default number of 101
and prefix of L
. As you use the roomlabel
procedure, it increments the counter by 1 so that you can continue placing room labels.
In these steps, you create the custom procedure named roomlabel
that uses all of the functions that you defined in this exercise to place a RoomLabel
block each time you specify a point in the drawing.
basRoomLabel
component, scroll to the bottom of the last procedure and press Enter a few times. Then, type the following. (The comments are here for your information and don't need to be typed.)' Prompts the user for an insertion point and room number
Public Sub RoomLabel()
On Error Resume Next
' Set the default values
Dim nLastNumber As Integer, sLastPrefix As String
If g_nLastNumber <> 0 Then
nLastNumber = g_nLastNumber
sLastPrefix = g_sLastPrefix
Else
nLastNumber = 101
sLastPrefix = "L"
End If
' Display current values
ThisDrawing.Utility.Prompt removeCmdPrompt & _
"Prefix: " & sLastPrefix & _
vbTab & "Number: " & CStr(nLastNumber)
Dim basePt As Variant
' Continue to ask for input until a point is provided
Do
Dim sKeyword As String
sKeyword = ""
basePt = Null
' Setup default keywords
ThisDrawing.Utility.InitializeUserInput 0, "Number Prefix"
' Prompt for a base point, number, or prefix value
basePt = ThisDrawing.Utility.GetPoint(, _
removeCmdPrompt & "Specify point for room label (" & _
sLastPrefix & CStr(nLastNumber) & _
") or change [Number/Prefix]: ")
' If an error occurs, the user entered a keyword or pressed Enter
If Err Then
Err.Clear
sKeyword = ThisDrawing.Utility.GetInput
Select Case sKeyword
Case "Number"
nLastNumber = ThisDrawing.Utility. _
GetInteger(removeCmdPrompt & _
"Enter new room number <" & _
CStr(nLastNumber) & ">: ")
Case "Prefix"
sLastPrefix = ThisDrawing.Utility. _
GetString(False, removeCmdPrompt & _
"Enter new room number prefix <" & _
sLastPrefix & ">: ")
End Select
End If
' If a base point was specified, then insert a block reference
If IsNull(basePt) = False Then
RoomLabel_InsertBlkRef basePt, sLastPrefix & CStr(nLastNumber)
' Increment number by 1
nLastNumber = nLastNumber + 1
End If
Loop Until IsNull(basePt) = True And sKeyword = ""
' Store the latest values in the global variables
g_nLastNumber = nLastNumber
g_sLastPrefix = sLastPrefix
End Sub
The roomlabel.dvb
file contains the main roomlabel
procedure and some helper functions defined in the clsUtilities.cls
file to define new layers.
The following steps explain how to use the roomlabel
procedure that is in the roomlabel.lsp
file:
Ch07_Building_Plan.dwg
. Figure 7.4 shows the plan drawing of the office building.vbarun
and press Enter.RoomLabel.dvb!basRoomLabel.RoomLabel
macro from the list and click Run.Specify point for room label (L101) or change [Number/Prefix]:
prompt, specify a point inside the room in the lower-left corner of the building.
The room label definition block and Plan_RoomLabel_Anno
layer are created the first time the roomlabel
procedure is used. The RoomLabel
block definition should look like Figure 7.5 when inserted into the drawing.
Specify point for room label (L101) or change [Number/Prefix]:
prompt, type n
and press Enter.Enter new room number <102>:
prompt, type 105
and press Enter.Specify point for room label (L105) or change [Number/Prefix]:
prompt, type p
and press Enter.Enter new room number prefix <L>:
prompt, type R
and press Enter.Specify point for room label (R105) or change [Number/Prefix]:
prompt, specify a point in the large open area in the middle of the building.roomlabel
.The FurnTools
project will contain several functions and main procedures that modify the properties and extract the attribute values of block references that have been inserted into a drawing. The following steps explain how to create a project named FurnTools
and save it to a file named furntools.dvb
:
FurnTools
. Make sure to also change the default project name (ACADProject
) to FurnTools
in the VBA Editor.clsUtilities.cls
file in the MyCustomFiles
folder. Click Open.
The clsUtilities.cls
file contains the utility procedures that you created as part of the DrawPlate
project.
basFurnTools
.Not everyone will agree on the naming conventions, plot styles, and other various aspects of layers, but there are two things drafters can agree on when it comes to layers:
Although I would like to think that all of the drawings I have ever created are perfect, I know they aren't. Rushed deadlines, changing project parameters, and other distractions impede perfection. Objects may have been placed on the wrong layer, or maybe it wasn't my fault and standards simply changed during the course of a project. With VBA and the AutoCAD Object library, you can identify potential problems in a drawing and let the user know about them so they can be fixed. You might even be able to fix the problems automatically without user input.
In these steps, you will create a custom procedure named furnlayers
that will be used to identify objects by type and value to ensure they are placed on the correct layer. This is achieved by using selection sets and entity data lists, along with looping and conditional statements.
basFurnTools
component.basFurnTools
component, type the following. (The comments are here for your information and don't need to be typed.)Private myUtilities As New clsUtilities
' Constants for PI
Const PI As Double = 3.14159265358979
' Moves objects to the correct layers based on a set of established rules
Sub FurnLayers()
On Error Resume Next
' Get the blocks to extract
Dim oSSFurn As AcadSelectionSet
Set oSSFurn = ThisDrawing.SelectionSets.Add("SSFurn")
' If an error is generated, selection set already exists
If Err Then
Err.Clear
Set oSSFurn = ThisDrawing.SelectionSets("SSFurn")
End If
' Define the selection set filter to select only blocks
Dim nDXFCodes(3) As Integer, nValue(3) As Variant
nDXFCodes(0) = -4: nValue(0) = "<OR":
nDXFCodes(1) = 0: nValue(1) = "INSERT"
nDXFCodes(2) = 0: nValue(2) = "DIMENSION"
nDXFCodes(3) = -4: nValue(3) = "OR>"
Dim vDXFCodes As Variant, vValues As Variant
vDXFCodes = nDXFCodes
vValues = nValue
' Allow the user to select objects in the drawing
oSSFurn.SelectOnScreen vDXFCodes, vValues
' Proceed if oSSFurn is greater than 0
If oSSFurn.Count > 0 Then
' Step through each object in the selection set
Dim oEnt As AcadEntity
For Each oEnt In oSSFurn
' Check to see if the object is a block reference
If oEnt.ObjectName = "AcDbBlockReference" Then
Dim oBlkRef As AcadBlockReference
Set oBlkRef = oEnt
' Get the name of the block, use EffectiveName because
' the block could be dynamic
Dim sBlkName As String
sBlkName = oBlkRef.EffectiveName
' If the block name starts with RD or CD,
' then place it on the surfaces layer
If sBlkName Like "RD*" Or _
sBlkName Like "CD*" Then
oBlkRef.Layer = "Surfaces"
' If the block name starts with PNL, PE, and PX,
' then place it on the panels layer
ElseIf sBlkName Like "PNL*" Or _
sBlkName Like "PE*" Or _
sBlkName Like "PX*" Then
oBlkRef.Layer = "Panels"
' If the block name starts with SF,
' then place it on the panels layer
ElseIf sBlkName Like "SF*" Or _
sBlkName Like "FF*" Then
oBlkRef.Layer = "Storage"
End If
ElseIf oEnt.ObjectName Like "AcDb*Dim*" Then
oEnt.Layer = "Dimensions"
End If
Next oEnt
' Remove the selection set
oSSFurn.Delete
End If
End Sub
The designs you create take time and often are a source of income or savings for your company. Based on the types of objects in a drawing, you can step through a drawing and get attribute information from blocks or even geometric values such as lengths and radii of circles. You can use the objects in a drawing to estimate the potential cost of a project or even provide information to manufacturing.
In these steps, you create four custom functions named ExtAttsFurnBOM
, SortArray
, TableFurnBOM
, and RowValuesFurnBOM
. The ExtAttsFurnBOM
function extracts the values of the attributes in the selected blocks and then uses the SortArray
function to sort the attribute values before quantifying them. The TableFurnBOM
and RowValuesFurnBOM
functions are used to create a grid of lines containing the extracted values.
basFurnTools
component, scroll to the bottom of the last procedure and press Enter a few times. Then, type the following. (The comments are here for your information and don't need to be typed.)' ExtAttsFurnBOM - Extracts, sorts, and quantifies the attribute information
Private Function ExtAttsFurnBOM(oSSFurn As AcadSelectionSet) As Variant
Dim sList() As String
Dim sPart As String, sLabel As String
' Step through each block in the selection set
Dim oBlkRef As AcadBlockReference
Dim nListCnt As Integer
nListCnt = 0
For Each oBlkRef In oSSFurn
' Step through the objects that appear after
' the block reference, looking for attributes
Dim vAtts As Variant
vAtts = oBlkRef.GetAttributes
' Check to see if the block has attributes
If oBlkRef.HasAttributes = True Then
' Get the attributes of the block reference
Dim vAttRefs As Variant
vAttRefs = oBlkRef.GetAttributes
Dim oAttRef As AcadAttributeReference
Dim nAttCnt As Integer
For nAttCnt = LBound(vAttRefs) To UBound(vAttRefs)
Set oAttRef = vAttRefs(nAttCnt)
If UCase(oAttRef.TagString) = "PART" Then
sPart = oAttRef.textString
ElseIf UCase(oAttRef.TagString) = "LABEL" Then
sLabel = oAttRef.textString
End If
Next
End If
' Resize the array
ReDim Preserve sList(nListCnt)
' Add the part and label values to the array
sList(nListCnt) = sLabel & vbTab & sPart
' Increment the counter
nListCnt = nListCnt + 1
Next oBlkRef
' Sort the array of parts and labels
Dim vFurnListSorted As Variant
vFurnListSorted = SortArray(sList)
' Quantify the list of parts and labels
' Step through each value in the sorted array
Dim sFurnList() As String
Dim vCurVal As Variant, sPreVal As String
Dim sItems As Variant
nCnt = 0: nListCnt = 0
For Each vCurVal In vFurnListSorted
' Check to see if the previous value is the same as the current value
If CStr(vCurVal) = sPreVal Or sPreVal = "" Then
' Increment the counter by 1
nCnt = nCnt + 1
' Values weren't the same, so record the quantity
Else
' Split the values of the item
sItems = Split(sPreVal, vbTab)
' Resize the array
ReDim Preserve sFurnList(nListCnt)
' Add the part and label values to the array
sFurnList(nListCnt) = CStr(nCnt) & vbTab & sItems(0) & vbTab & sItems(1)
' Increment the array counter
nListCnt = nListCnt + 1
' Reset the counter
nCnt = 1
End If
sPreVal = CStr(vCurVal)
Next vCurVal
' Append the last item
' Split the values of the item
sItems = Split(sPreVal, vbTab)
' Resize the array
ReDim Preserve sFurnList(nListCnt)
' Add the part and label values to the array
sFurnList(nListCnt) = CStr(nCnt) & vbTab & sItems(0) & vbTab & sItems(1)
' Return the sorted and quantified array
ExtAttsFurnBOM = sFurnList
End Function
' Performs a basic sort on the string values in an array,
' and returns the newly sorted array.
Private Function SortArray(vArray As Variant) As Variant
Dim nFIdx As Integer, nLIdx As Integer
nFIdx = LBound(vArray): nLIdx = UBound(vArray)
Dim nOuterCnt As Integer, nInnerCnt As Integer
Dim sTemp As String
For nOuterCnt = nFIdx To nLIdx - 1
For nInnerCnt = nOuterCnt + 1 To nLIdx
If vArray(nOuterCnt) > vArray(nInnerCnt) Then
sTemp = vArray(nInnerCnt)
vArray(nInnerCnt) = vArray(nOuterCnt)
vArray(nOuterCnt) = sTemp
End If
Next nInnerCnt
Next nOuterCnt
SortArray = vArray
End Function
' Create the bill of materials table/grid
Private Sub TableFurnBOM(vQtyList As Variant, dInsPt() As Double)
' Define the sizes of the table and grid
Dim dColWidths(3) As Double
dColWidths(0) = 0: dColWidths(1) = 15
dColWidths(2) = 45: dColWidths(3) = 50
Dim dTableWidth As Double, dTableHeight As Double
dTableWidth = 0: dTableHeight = 0
Dim nRow As Integer
nRow = 1
Dim dRowHeight As Double, dTextHeight As Double
dRowHeight = 4: dTextHeight = dRowHeight - 1
' Get the table width by adding all column widths
Dim vColWidth As Variant
For Each vColWidth In dColWidths
dTableWidth = dTableWidth + CDbl(vColWidth)
Next vColWidth
' Define the standard table headers
Dim sHeaders(2) As String
sHeaders(0) = "QTY": sHeaders(1) = "LABELS": sHeaders(2) = "PARTS"
' Create the top of the table
Dim vInsPtRight As Variant
vInsPtRight = ThisDrawing.Utility.PolarPoint( _
dInsPt, 0, dTableWidth)
Dim oLine As AcadLine
Set oLine = ThisDrawing.ModelSpace.AddLine(dInsPt, vInsPtRight)
' Get the bottom of the header row
Dim vBottomRow As Variant
vBottomRow = ThisDrawing.Utility.PolarPoint( _
dInsPt, ((PI / 2) * -1), dRowHeight)
' Add headers to the table
RowValuesFurnBOM sHeaders, vBottomRow, dColWidths, dTextHeight
' Step through each item in the list
Dim vItem As Variant
For Each vItem In vQtyList
nRow = nRow + 1
vBottomRow = ThisDrawing.Utility.PolarPoint( _
dInsPt, ((PI / 2) * -1), dRowHeight * nRow)
RowValuesFurnBOM Split(vItem, vbTab), vBottomRow, dColWidths, dTextHeight
Next vItem
' Create the vertical lines for each column
dColWidthTotal = 0
For Each vColWidth In dColWidths
' Calculate the placement of each vertical line (left to right)
dColWidthTotal = CDbl(vColWidth) + dColWidthTotal
Dim vColBasePt As Variant
vColBasePt = ThisDrawing.Utility.PolarPoint( _
dInsPt, 0, dColWidthTotal)
Dim vColBottomPt As Variant
vColBottomPt = ThisDrawing.Utility.PolarPoint( _
vColBasePt, ((PI / 2) * -1), _
myUtilities.Calc2DDistance(dInsPt(0), _
dInsPt(1), _
vBottomRow(0), _
vBottomRow(1)))
' Draw the vertical line
Set oLine = ThisDrawing.ModelSpace.AddLine(vColBasePt, vColBottomPt)
Next vColWidth
End Sub
' Create a row and populate the data for the table
Private Sub RowValuesFurnBOM(vItems As Variant, _
vBottomRow As Variant, _
vColWidths As Variant, _
dTextHeight As Double)
' Calculate the insertion point for the header text
Dim dRowText(2) As Double
dRowText(0) = 0.5 + vBottomRow(0)
dRowText(1) = 0.5 + vBottomRow(1)
dRowText(2) = vBottomRow(2)
Dim dTableWidth As Double
dTableWidth = 0
' Get the table width by adding all column widths
Dim vColWidth As Variant
For Each vColWidth In vColWidths
dTableWidth = dTableWidth + CDbl(vColWidth)
Next vColWidth
' Lay out the text in each row
Dim nCol As Integer, dColWidthTotal As Double
nCol = 0: dColWidthTotal = 0
Dim vItem As Variant
For Each vItem In vItems
' Calculate the placement of each text object (left to right)
dColWidthTotal = dColWidthTotal + vColWidths(nCol)
Dim vInsTextCol As Variant
vInsTextCol = ThisDrawing.Utility.PolarPoint( _
dRowText, 0, dColWidthTotal)
' Draw the single-line text object
Dim oText As AcadText
Set oText = ThisDrawing.ModelSpace.AddText(CStr(vItem), _
vInsTextCol, dTextHeight)
' Create the row line
Dim vBottomRowRight As Variant
vBottomRowRight = ThisDrawing.Utility.PolarPoint( _
vBottomRow, 0, dTableWidth)
Dim oLine As AcadLine
Set oLine = ThisDrawing.ModelSpace.AddLine(vBottomRow, vBottomRowRight)
' Increment the counter
nCol = nCol + 1
Next vItem
End Sub
' Extracts, aggregates, and counts attributes from the furniture blocks
Sub FurnBOM()
On Error Resume Next
' Get the blocks to extract
Dim oSSFurn As AcadSelectionSet
Set oSSFurn = ThisDrawing.SelectionSets.Add("SSFurn")
' If an error is generated, selection set already exists
If Err Then
Err.Clear
Set oSSFurn = ThisDrawing.SelectionSets("SSFurn")
End If
' Define the selection set filter to select only blocks
Dim nDXFCodes(0) As Integer, nValue(0) As Variant
nDXFCodes(0) = 0
nValue(0) = "INSERT"
Dim vDXFCodes As Variant, vValues As Variant
vDXFCodes = nDXFCodes
vValues = nValue
' Allow the user to select objects in the drawing
oSSFurn.SelectOnScreen vDXFCodes, vValues
' Use the ExtAttsFurnBOM to extract and quantify the attributes in the blocks
' If a selection set was created, then look for attributes
If oSSFurn.Count > 0 Then
' Extract and quantify the parts in the drawing
Dim vAttList As Variant
vAttList = ExtAttsFurnBOM(oSSFurn)
' Create the layer named BOM and set it current
Dim oLayer As AcadLayer
Set oLayer = myUtilities.CreateLayer("BOM", 8)
Set ThisDrawing.ActiveLayer = oLayer.Name
' Prompt the user for the point to create the BOM
Dim vInsPt As Variant
vInsPt = ThisDrawing.Utility.GetPoint(, vbLf & _
"Specify upper-left corner of BOM: ")
' Start the function that creates the table grid
Dim dInsPt(2) As Double
dInsPt(0) = vInsPt(0): dInsPt(1) = vInsPt(1): dInsPt(2) = vInsPt(2)
TableFurnBOM vAttList, dInsPt
' Remove the selection set
oSSFurn.Delete
End If
End Sub
The procedures you added to FurnTools
project leverage some of the functions defined in clsUtilities.cls
. These tools allow you to change the layers of objects in a drawing and extract information from the objects in a drawing as well. More specifically, they allow you to work with blocks that represent an office furniture layout.
Although you might be working in a civil engineering– or mechanical design–related field, these concepts can and do apply to the work you do—just in different ways. Instead of extracting information from a furniture block, you could get and set information in a title block, a callout, or even an elevation marker. Making sure hatching is placed on the correct layers along with dimensions can improve the quality of output for the designs your company creates.
The following steps explain how to use the FurnLayers
procedure:
ch07_building_plan.dwg
.vbarun
and press Enter.FurnTools.dvb!basFurnTools.FurnLayers
macro from the list and click Run.Select objects:
prompt, select all the objects in the drawing and press Enter.
The objects in the drawing are placed on the correct layers, and this can be seen as the objects were all previously placed on layer 0
and had a color of white (or black based on the background color of the drawing area).
The following steps explain how to use the FurnBom
procedure:
vbarun
and press Enter.FurnTools.dvb!basFurnTools.FurnBOM
macro from the list and click Run.Select objects:
prompt, select all the objects in the drawing. Don't press Enter yet.
Notice that the dimension objects aren't highlighted. As a result of the selection set filter being applied with the SelectOnScreen
function, the SelectOnScreen
function only allows block references (insert
object types) to be selected.
Specify upper-left corner of BOM:
prompt, specify a point to the right of the furniture layout in the drawing.
The bill of materials that represents the furniture blocks is placed in a table grid, as shown Figure 7.6.
3.143.17.27