ObjectARX Developers Guide
ObjectARX Developers Guide
IN NO EVENT SHALL AUTODESK, INC. BE LIABLE TO ANYONE FOR SPECIAL, COLLATERAL, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES IN CONNECTION WITH OR ARISING OUT OF PURCHASE OR USE OF THESE MATERIALS. THE
SOLE AND EXCLUSIVE LIABILITY TO AUTODESK, INC., REGARDLESS OF THE FORM OF ACTION, SHALL NOT EXCEED THE
PURCHASE PRICE OF THE MATERIALS DESCRIBED HEREIN.
Autodesk, Inc. reserves the right to revise and improve its products as it sees fit. This publication describes the state of this product
at the time of its publication, and may not reflect the product at all times in the future.
Autodesk Trademarks
The following are registered trademarks of Autodesk, Inc., in the USA and/or other countries: 3D Plan, 3D Props, 3D Studio, 3D
Studio MAX, 3D Studio VIZ, 3D Surfer, ADE, ADI, Advanced Modeling Extension, AEC Authority (logo), AEC-X, AME, Animator
Pro, Animator Studio, ATC, AUGI, AutoCAD, AutoCAD Data Extension, AutoCAD Development System, AutoCAD LT, AutoCAD
Map, Autodesk, Autodesk Animator, Autodesk (logo), Autodesk MapGuide, Autodesk University, Autodesk View, Autodesk
WalkThrough, Autodesk World, AutoLISP, AutoShade, AutoSketch, AutoSolid, AutoSurf, AutoVision, Biped, bringing information
down to earth, CAD Overlay, Character Studio, Design Companion, Drafix, Education by Design, Generic, Generic 3D Drafting,
Generic CADD, Generic Software, Geodyssey, Heidi, HOOPS, Hyperwire, Inside Track, Kinetix, MaterialSpec, Mechanical Desktop,
Multimedia Explorer, NAAUG, Office Series, Opus, PeopleTracker, Physique, Planix, Rastation, Softdesk, Softdesk (logo), Solution
3000, Tech Talk, Texture Universe, The AEC Authority, The Auto Architect, TinkerTech, WHIP!, WHIP! (logo), Woodbourne,
WorkCenter, and World-Creating Toolkit.
The following are trademarks of Autodesk, Inc., in the USA and/or other countries: 3D on the PC, ACAD, ActiveShapes, Actrix,
Advanced User Interface, AEC Office, AME Link, Animation Partner, Animation Player, Animation Pro Player, A Studio in Every
Computer, ATLAST, Auto-Architect, AutoCAD Architectural Desktop, AutoCAD Architectural Desktop Learning Assistance,
AutoCAD DesignCenter, Learning Assistance, AutoCAD LT Learning Assistance, AutoCAD Simulator, AutoCAD SQL Extension,
AutoCAD SQL Interface, AutoCDM, Autodesk Animator Clips, Autodesk Animator Theatre, Autodesk Device Interface, Autodesk
PhotoEDIT, Autodesk Software Developer’s Kit, Autodesk View DwgX, AutoEDM, AutoFlix, AutoLathe, AutoSnap, AutoTrack, Built
with ObjectARX (logo), ClearScale, Concept Studio, Content Explorer, cornerStone Toolkit, Dancing Baby (image), Design Your
World, Design Your World (logo), Designer’s Toolkit, DWG Linking, DWG Unplugged, DXF, Exegis, FLI, FLIC, GDX Driver, Generic
3D, Heads-up Design, Home Series, Kinetix (logo), MAX DWG, ObjectARX, ObjectDBX, Ooga-Chaka, Photo Landscape,
Photoscape, Plugs and Sockets, PolarSnap, Powered with Autodesk Technology, Powered with Autodesk Technology (logo),
ProConnect, ProjectPoint, Pro Landscape, QuickCAD, RadioRay, SchoolBox, SketchTools, Suddenly Everything Clicks,
Supportdesk, The Dancing Baby, Transforms Ideas Into Reality, Visual LISP, and Volo.
Third Party Trademarks
Élan License Manager is a trademark of Élan Computer Group, Inc.
Microsoft, Visual Basic, Visual C++, and Windows are registered trademarks and Visual FoxPro and the Microsoft Visual Basic
Technology logo are trademarks of Microsoft Corporation in the United States and other countries.
All other brand names, product names or trademarks belong to their respective holders.
Third Party Software Program Credits
ACIS ® Copyright © 1994, 1997, 1999 Spatial Technology, Inc., Three-Space Ltd., and Applied Geometry Corp. All rights reserved.
Copyright © 1997 Microsoft Corporation. All rights reserved.
International CorrectSpell™ Spelling Correction System © 1995 by Lernout & Hauspie Speech Products, N.V. All rights reserved.
InstallShield™ 3.0. Copyright © 1997 InstallShield Software Corporation. All rights reserved.
Portions Copyright © 1991-1996 Arthur D. Applegate. All rights reserved.
Portions of this software are based on the work of the Independent JPEG Group.
Typefaces from the Bitstream ® typeface library copyright 1992.
Typefaces from Payne Loving Trust © 1996. All rights reserved.
The license management portion of this product is based on Élan License Manager © 1989, 1990, 1998 Élan Computer Group,
Inc. All rights reserved.
GOVERNMENT USE
Use, duplication, or disclosure by the U. S. Government is subject to restrictions as set forth in FAR 12.212 (Commercial Computer
Software-Restricted Rights) and DFAR 227.7202 (Rights in Technical Data and Computer Software), as applicable.
1 2 3 4 5 6 7 8 9 10
Contents
Chapter 1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
The ObjectARX Programming Environment . . . . . . . . . . . . . . . . . . . . . . . . 8
Accessing the AutoCAD Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Interacting with the AutoCAD Editor . . . . . . . . . . . . . . . . . . . . . . . . . 8
Creating User Interfaces with MFC . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Supporting MDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Creating Custom Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Building Complex Applications. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Interacting with Other Environments . . . . . . . . . . . . . . . . . . . . . . . . . 9
ObjectARX Class Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
AcRx Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
AcEd Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
AcDb Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
AcGi Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
AcGe Library. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
iii
System Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Installing ObjectARX. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
iv | Contents
?—List Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Load . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Unload . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Commands. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Options. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Running ObjectARX Applications from AutoLISP . . . . . . . . . . . . . . . . . . . 55
Error Handling. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Contents | v
Chapter 6 Entities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Entities Defined . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Entity Ownership . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
AutoCAD Release 12 Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Common Entity Properties. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Entity Color . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Entity Linetype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Entity Linetype Scale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Entity Visibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Entity Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Common Entity Functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Object Snap Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Transform Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
Intersecting for Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
GS Markers and Subentities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Exploding Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Creating Instances of AutoCAD Entities . . . . . . . . . . . . . . . . . . . . . . . . . 125
Creating a Simple Entity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Creating a Simple Block Table Record . . . . . . . . . . . . . . . . . . . . . . 126
Creating a Block Table Record with Attribute Definitions . . . . . . . 126
Creating a Block Reference with Attributes . . . . . . . . . . . . . . . . . . 129
Iterating through a Block Table Record . . . . . . . . . . . . . . . . . . . . . 133
Complex Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Creating a Complex Entity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Iterating through Vertices in a Polyline . . . . . . . . . . . . . . . . . . . . . 135
Coordinate System Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
Entity Coordinate System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
AcDb2dPolylineVertex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Curve Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Associating Hyperlinks with Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
AcDbHyperlink Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
AcDbHyperlinkCollection Class . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
AcDbEntityHyperlinkPE Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Hyperlink Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
vi | Contents
Layout Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Creating a Dictionary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Iterating over Dictionary Entries . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
ObjectARX Layout Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
Xrecords. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
DXF Group Codes for Xrecords . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Contents | vii
Chapter 9 Selection Set, Entity, and Symbol Table Functions . . . . . . . . . . . 199
Selection Set and Entity Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Handling Selection Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Selection Set Filter Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
Selection Set Manipulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Transformation of Selection Sets. . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Entity Name and Data Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
Entity Name Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
Entity Data Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Entity Data Functions and Graphics Screen . . . . . . . . . . . . . . . . . . 233
Notes on Extended Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Xrecord Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
Symbol Table Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
viii | Contents
Class Initialization Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Contents | ix
Chapter 13 Deriving from AcDbEntity. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
Deriving Custom Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
AcDbEntity Functions to Override . . . . . . . . . . . . . . . . . . . . . . . . . 350
AcDbEntity Functions Usually Overridden. . . . . . . . . . . . . . . . . . . 351
AcDbEntity Functions Rarely Overridden. . . . . . . . . . . . . . . . . . . . 352
Overriding Common Entity Functions . . . . . . . . . . . . . . . . . . . . . . . . . . 353
Overriding worldDraw() and viewportDraw() . . . . . . . . . . . . . . . . 353
Overriding saveAs() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
Implementing the Object Snap Point Function . . . . . . . . . . . . . . . 357
Implementing the Grip Point Functions . . . . . . . . . . . . . . . . . . . . 359
Implementing the Stretch Point Functions . . . . . . . . . . . . . . . . . . 361
Transformation Functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
Intersecting with Other Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
Intersecting a Custom Entity with Another Entity. . . . . . . . . . . . . 369
Exploding an Entity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
Extending Entity Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
Using AcEdJig . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
Deriving a New Class from AcEdJig . . . . . . . . . . . . . . . . . . . . . . . . 371
General Steps for Using AcEdJig . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
Setting Up Parameters for the Drag Sequence . . . . . . . . . . . . . . . . 372
Drag Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
Implementing the sampler(), update(), and entity() Functions . . 375
Adding the Entity to the Database . . . . . . . . . . . . . . . . . . . . . . . . . 378
Sample Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
x | Contents
Using an Editor Reactor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
Using a Database Reactor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
Using an Object Reactor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
Notification Use Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
Contents | xi
Nonreentrant Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
Making a Command Nonreentrant . . . . . . . . . . . . . . . . . . . . . . . . 432
Nonreentrant AutoCAD Commands . . . . . . . . . . . . . . . . . . . . . . . 432
Multi-Document Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
Disabling Document Switching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
Application Execution Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
Code Invoked under the Application Execution Context . . . . . . . 436
Code Differences under the Application Execution Context . . . . . 436
Other Application Execution Context Considerations. . . . . . . . . . 437
Database Undo and Transaction Management Facilities. . . . . . . . . . . . . 438
Document-Independent Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
An MDI-Aware Example Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
xii | Contents
Insert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504
Editor Reactor Notification Functions . . . . . . . . . . . . . . . . . . . . . . . 504
Contents | xiii
Input Context Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569
Input Point Filters and Monitors . . . . . . . . . . . . . . . . . . . . . . . . . . 575
Chapter 23 COM, ActiveX Automation, and the Object Property Manager 593
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
Using AutoCAD COM Objects from ObjectARX and Other
Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
Accessing COM Interfaces from ObjectARX . . . . . . . . . . . . . . . . . . 595
AutoCAD ActiveX Automation Implementation. . . . . . . . . . . . . . . . . . . 605
The Relationship between AcDbObjects and Automation Objects 605
Creating the COM Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608
Interacting with AutoCAD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611
Document Locking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
Creating a Registry File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613
Exposing Automation Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
Setting Up an ATL Project File . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
Writing a COM Wrapper. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616
Building and Registering a COM DLL . . . . . . . . . . . . . . . . . . . . . . . 621
Object Property Manager API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
AutoCAD COM Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . 623
Static OPM COM Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
ICategorizeProperties Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
IPerPropertyBrowsing Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
IOPMPropertyExtension Interface . . . . . . . . . . . . . . . . . . . . . . . . . 625
IOPMPropertyExpander Interface . . . . . . . . . . . . . . . . . . . . . . . . . . 625
Implementing Static OPM Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
Dynamic Properties and OPM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
IDynamicProperty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631
xiv | Contents
IAcDcContentFinderSite Interface . . . . . . . . . . . . . . . . . . . . . . . . . . 634
IAcDcContentFinder Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
IAcPostDrop Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
Registry Requirements for an AutoCAD DesignCenter Component . . . . 635
Applications Key . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
Extensions Key . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
CLASSID Registration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637
Implementing the Interfaces for AutoCAD DesignCenter . . . . . . . . . . . . 638
Customizing AutoCAD DesignCenter. . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
Create an ActiveX Template Library Project . . . . . . . . . . . . . . . . . . 641
Add Registry Support and a New ATL COM Object . . . . . . . . . . . . 641
Add Code to Support the New ATL COM Object . . . . . . . . . . . . . . 644
Contents | xv
Installing the ObjectDBX Libraries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
Use COMMONFILES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
Install by Version and as SHAREDFILE . . . . . . . . . . . . . . . . . . . . . . 673
Ensure the Files Are on the Path . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
Ensure Smart Pathing Updates . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674
Tips and Techniques. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
ACAD_OBJID_INLINE_INTERNAL . . . . . . . . . . . . . . . . . . . . . . . . . 676
AcDbDatabase Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
AcDbDatabase::insert() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678
Finding the Active Viewports in Model Space . . . . . . . . . . . . . . . . 678
Details About Viewports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
Always Test Your Drawings in AutoCAD 2000 . . . . . . . . . . . . . . . . 680
Using DWG Files from Earlier Releases . . . . . . . . . . . . . . . . . . . . . . 680
Extended Entity Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
Raster Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
Known Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
xvi | Contents
Clip Boundary Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724
Contents | xvii
Part VII Appendixes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 767
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809
xviii | Contents
About ObjectARX
Documentation
In This Chapter
1
The ObjectARX Documentation Set
The ObjectARX documentation set includes the following printed guides and
online documentation.
Printed Guides
Two printed manuals are provided with ObjectARX:
Online Documentation
You can access the ObjectARX online documentation from the
\objectarx\docs directory. The following online documents are provided in
Windows help file format:
NOTE The logo program guidelines contain information about how to regis-
ter your Registered Developer Symbol (RDS) with Autodesk, in addition to the
other logo requirements.
Where to Start
New users should start with the ObjectARX Developer’s Guide. Experienced
users and those upgrading from previous versions of ObjectARX should start
with the Migration Guide for Applications, and then move on to the more
detailed material on the new subjects in the ObjectARX Developer’s Guide.
Organization
The ObjectARX Developer’s Guide is organized in seven parts:
Part I: Using ObjectARX describes the fundamental concepts of ObjectARX.
Part II: User Interfaces shows how to work with ObjectARX global functions
and MFC to create and interact with user interfaces.
Part III: Defining New Classes describes how to create custom classes in
ObjectARX.
Part IV: Specialized Topics examines topics of interest to more advanced users,
such as proxy objects, notification, and protocol extension.
Part V: Interacting with Other Environments discusses working with external
programming environments such as COM and ActiveX® Automation.
Part VI: ObjectARX Libraries describes several of the ObjectARX libraries,
including the ObjectDBX™ libraries and the graphics interface library.
Part VII: Appendixes gives detailed information about migrating ADS pro-
grams to ObjectARX, and using programmable dialog boxes.
5
6
Overview
In This Chapter
1
An ObjectARX application is a dynamic link library ■ The ObjectARX Programming
Environment
(DLL) that shares the address space of AutoCAD and
■ ObjectARX Class Libraries
makes direct function calls to AutoCAD. You can add ■ Getting Started
7
The ObjectARX Programming Environment
The ObjectARX programming environment provides an object-oriented C++
application programming interface for developers to use, customize, and
extend AutoCAD. The ObjectARX libraries comprise a versatile set of tools for
application developers to take advantage of AutoCAD’s open architecture,
providing direct access to AutoCAD database structures, the graphics system,
and native command definition. In addition, these libraries are designed to
work in conjunction with Visual LISP and other application programming
interfaces so that developers can choose the programming tools best suited
to their needs and experience.
As a developer, you can use ObjectARX to accomplish the following tasks:
The next sections take a brief look at these topics. They will be discussed in
greater detail throughout the book.
8 | Chapter 1 Overview
Creating User Interfaces with MFC
ObjectARX applications can be built with a dynamically linked MFC library
that is shared with AutoCAD. You can use this library to create standard
Microsoft Windows graphical user interfaces (GUIs).
Supporting MDI
With ObjectARX, you can create applications that will support the AutoCAD
multiple document interface, and you can ensure that your applications
will interact properly with other applications in the Microsoft Windows
environment.
■ Notification
■ Transaction management
■ Deep cloning
■ Reference editing
■ Protocol extension
■ Proxy object support
The following sections take a closer look at each of the ObjectARX libraries.
For more information about specific classes and member functions, see the
ObjectARX Reference.
AcRx Library
The AcRx library provides system-level classes for DLL initialization and link-
ing and for runtime class registration and identification. The base class of this
library is AcRxObject, which provides the following facilities:
10 | Chapter 1 Overview
■ Object runtime class identification and inheritance analysis
■ Runtime addition of new protocol to an existing class (see chapter 19,
“Protocol Extension”)
■ Object equality and comparison testing
■ Object copy
The AcRx library also provides a set of C++ macros to help you create new
ObjectARX classes derived from AcRxObject (see chapter 11, “Deriving a
Custom ObjectARX Class”).
AcRxDictionary is another important class in this library. A dictionary is a
mapping from a text string to another object. The AcRx library places its
objects, classes, and service dictionaries in a global object dictionary, which
is an instance of the AcRxDictionary class. Applications can add objects to
this dictionary so that they are accessible to other applications.
The class hierarchy for the AcRx library is as follows:
AcRxClass
AcRxDictionary
AcRxDynamicLinker
AcRxEvent
AcEditor
AcRxService
AcRxKernal
AcDbServices
AcEdServices
AcadAppInfo
AcDb Library
The AcDb library provides the classes that compose the AutoCAD database.
This database stores all the information for the graphical objects, called
entities, that compose an AutoCAD drawing, as well as the nongraphical
objects (for example, layers, linetypes, and text styles) that are also part of a
drawing. You can query and manipulate existing instances of AutoCAD
entities and objects with the AcDb library, and you can create new instances
of database objects.
The AutoCAD database contains these major elements:
■ A set of nine symbol tables that own uniquely named symbol table entry
objects. These objects represent various commonly used AcDbDatabase
objects and data members.
12 | Chapter 1 Overview
■ A named object dictionary (of class AcDbDictionary), which provides the
“table of contents” for an AutoCAD drawing. Initially, this table of con-
tents contains the IDs of the four other dictionaries used by AutoCAD.
Applications you develop, however, are free to add other objects to the
dictionary.
■ A fixed set of about 200 header variables, whose values are set by
AutoCAD.
For more information on the AcDb library, see chapter 2, “Database Primer,”
chapter 4, “Database Operations,” chapter 5, “Database Objects,” chapter 6,
“Entities,” and chapter 7, “Container Objects.” For information on deriving
new classes from AcDbObject and AcDbEntity, see chapter 12, “Deriving from
AcDbObject” and chapter 13, “Deriving from AcDbEntity.”
AcGi Library
The AcGi library provides the graphics interface used for drawing AutoCAD
entities. This library is used by the AcDbEntity member functions
worldDraw(), viewportDraw(), and saveAs(), all part of the standard entity
protocol. The worldDraw() function must be defined by all custom entity
classes. The AcGiWorldDraw object provides an API through which
AcDbEntity::worldDraw() can produce its graphical representation in all
viewports simultaneously. Similarly, the AcGiViewportDraw object provides
an API through which the AcDbEntity::viewportDraw() function can pro-
duce different graphical representations for each viewport.
AcGiCommonDraw
AcGiWorldDraw
AcGiWorldDraw
AcGiContext
AcGiEdgeData
AcGiFaceData
AcGiGeometry
AcGiViewportGeometry
AcGiWorldGeometry
AcGiLinetypeEngine
AcGiSubEntityTraits
AcGiDrawableTraits
AcGiTextStyle
AcGiVertexData
AcGiViewport
AcGiDrawable
AcGiGlyph
For more information on using AcGi classes, see chapter 13, “Deriving from
AcDbEntity.”
AcGe Library
The AcGe library is used by the AcDb library and provides utility classes such
as vectors and matrices that are used to perform common 2D and 3D geomet-
ric operations. It also provides basic geometric objects such as points, curves,
and surfaces.
The AcGe library consists of two major subsets: classes for 2D geometry and
classes for 3D geometry. The major abstract base classes are AcGeEntity2d
and AcGeEntity3d. Several basic classes not derived from any other class
include AcGePoint2d, AcGeVector2d, and AcGeMatrix2d (shown at the begin-
ning of the class hierarchy). These basic classes can be used to perform many
types of common operations, such as adding a vector to a point, computing
the dot or cross product of two vectors, and computing the product of two
matrices. The higher-level classes of this library are implemented using these
basic classes. The class hierarchy for the AcGe library is as follows:
14 | Chapter 1 Overview
AcGeBoundBlock2d AcGeBoundBlock3d
AcGeClipBoundary2d AcGeCurve3d
AcGeCurve2d AcGeCircArc3de
AcGeCircArc2d AcGeCompositeCurve3d
AcGeCompositeCurve2d AcGeEllipArc3e
AcGeEllipArc2d AcGeExternalCurve3d
AcGeExternalCurve2d AcGeLinearEnt3d
AcGeLinearEnt2d AcGeLine3d
AcGeLine2d AcGeLineSeg3d
AcGeLineSeg2d AcGeRay3d
AcGeRay2d AcGeMatrix3d
AcGeOffsetCurve2d AcGeOffsetCurve3d
AcGeSplineEnt2d AcGeSplineEnt3d
AcGeCubicSplineCurve2d AcGeCubicSplineCurve3d
AcGeNurbCurve2d AcGeNurbCurve3d
AcGePolyline2d AcGePolyline3d
AcGeCurveCurveInt2d AcGeAugPolyline3d
AcGePointEnt2d AcGeCurveCurveInt3d
AcGePointOnCurve2d AcGeCurveSurfInt
AcGePosition2d AcGePointEnt3d
AcGePointOnCurve3d
AcGePointOnSurface
AcGeCurveBoundary
AcGePosition3d
AcGe
AcGeSurfSurfInt
AcGeContext
AcGeSurface
AcGeDwgIO
AcGeCone
AcGeDxfIO
AcGeCylinder
AcGeFileIO
AcGeExternalBoundedSurface
AcGeFiler
AcGeExternalSurface
AcGeInterval
AcGeNurbSurface
AcGeKnotVector
AcGeOffsetSurface
AcGeLibVersion
AcGePlanarEnt
AcGeMatrix2d
AcGeBoundedPlanet
AcGeMatrix3d
AcGePlane
AcGePoint2d
AcGeSphere
AcAxPoint2d
AcGeTorus
AcGePoint3d
AcAxPoint3d
AcGeScale2d
AcGeScale3d
AcGeTol
AcGeVector2d
AcGeVector3d
The AcGe library provides several different coordinate systems. For more
information, see chapter 27, “Using the Geometry Library.” The sample pro-
grams in this manual illustrate numerous common uses of AcGe classes.
System Requirements
Developing applications with ObjectARX requires the following software and
hardware:
Installing ObjectARX
When you install ObjectARX, a setup program guides you through the
process.
To install ObjectARX
1 Insert the CD into the CD-ROM drive.
2 If you are running Windows NT 4.0 with AutoPlay, follow the on-screen
instructions.
3 If you have turned off AutoPlay in Windows NT 4.0, from the Start menu on
the taskbar, choose Run, designate the CD-ROM drive, enter the path name,
and then enter setup.
16 | Chapter 1 Overview
classmap The classmap directory contains an AutoCAD drawing
illustrating the ObjectARX class hierarchy.
docs The docs directory contains Windows online help files for
ObjectARX developers, including the ObjectARX
Developer’s Guide, the ObjectARX Reference, the Migration
Guide for Applications, and the ObjectARX Readme file.
docsamps The docsamps directory contains subdirectories for each of
the programs from which examples were extracted for the
ObjectARX Developer’s Guide. Each subdirectory contains
the full set of source code for the application and an
explanatory Readme file.
inc The inc directory contains the ObjectARX header files.
lib The lib directory contains the ObjectARX library files.
redistrib The redistrib directory contains a set of DLLs, some of
which may be required for an ObjectARX application to
run. Developers should copy the DLLs that they need for
application development to a directory in the AutoCAD
search path, and package the necessary DLLs with their
ObjectARX applications for distribution.
samples The samples directory includes subdirectories containing
examples of ObjectARX applications. These subdirectories
include source code and Readme files. The most significant
set of sample ObjectARX applications is in the polysamp
subdirectory.
utils The utils directory contains subdirectories for applications
that are extensions to ObjectARX, including brep for
boundary representation and istorage for compound
document storage. Each application directory includes
inc, lib, and sample subdirectories.
Getting Started | 17
18
Database Primer
In This Chapter
2
The AutoCAD database stores the objects and entities ■ AutoCAD Database Overview
■ Essential Database Objects
that make up an AutoCAD drawing. This chapter
■ Creating Objects in AutoCAD
discusses the key elements of the database: entities, ■ Creating Objects in ObjectARX
the database.
19
AutoCAD Database Overview
An AutoCAD drawing is a collection of objects stored in a database. Some of
the basic database objects are entities, symbol tables, and dictionaries. Enti-
ties are a special kind of database object that have a graphical representation
within an AutoCAD drawing. Lines, circles, arcs, text, solids, regions, splines,
and ellipses are examples of entities. A user can see an entity on the screen
and can manipulate it.
Symbol tables and dictionaries are containers used to store database objects.
Both container objects map a symbol name (a text string) to a database
object. An AutoCAD database includes a fixed set of symbol tables, each of
which contains instances of a particular class of symbol table record. You
cannot add a new symbol table to the database. Examples of symbol tables
are the layer table (AcDbLayerTable), which contains layer table records, and
the block table (AcDbBlockTable), which contains block table records. All
AutoCAD entities are owned by block table records.
Dictionaries provide a more generic container for storing objects than sym-
bol tables. A dictionary can contain any object of the type AcDbObject or sub-
class thereof. The AutoCAD database creates a dictionary called the named
object dictionary when it creates a new drawing. The named object
dictionary can be viewed as the master “table of contents” for all of the dic-
tionaries associated with the database. You can create new dictionaries
within the named object dictionary and add new database objects to them.
The following figure shows the key components of the AutoCAD database.
Database
Objects
Their Symbol
Layer Table Record Block Table Record
Table Records
Entity
Multiple Databases
Multiple databases can be loaded in a single AutoCAD session. Each object in
the session has a handle and an object ID. A handle uniquely identifies the
object within the scope of a particular database, whereas an object ID
uniquely identifies the object across all databases loaded at one time. An
object ID only persists during an edit session, but a handle gets saved with
the drawing. In contrast to the object ID, an object handle is not guaranteed
to be unique when multiple databases are loaded in an AutoCAD session.
■ Create an object and append it to the database. The database then gives
the object an ID and returns it to you.
■ Use the database protocol for obtaining the object ID of the objects that
are created automatically when a database is created (such as the fixed set
of symbol tables and the named object dictionary).
■ Use class-specific protocol for obtaining object IDs. Certain classes, such
as symbol tables and dictionaries, define objects that own other objects.
These classes provide protocol for obtaining the object IDs of the owned
objects.
■ Use an iterator to step through a list or set of objects. The AcDb library
provides a number of iterators that can be used to step through various
kinds of container objects (AcDbDictionaryIterator,
AcDbObjectIterator).
■ Query a selection set. After the user has selected an object, you can ask the
selection set for the list of entity names of the selected objects, and from
the names convert to the object IDs. For more information on selection
sets, see chapter 6, “Entities.”
■ A set of nine symbol tables that includes the block table, layer table, and
linetype table. The block table initially contains three records: a record
called *MODEL_SPACE, and two paper space records called *PAPER_SPACE
and *PAPER_SPACE0. These block table records represent model space and
the two predefined paper space layouts. The layer table initially contains
one record, layer 0. The linetype table initially contains the
CONTINUOUS linetype.
■ A named object dictionary. When a database is created, this dictionary
already contains four database dictionaries: the GROUP dictionary, MLINE
style dictionary, layout dictionary, and plot style name dictionary. Within
the MLINE style dictionary, the STANDARD style is always present.
Paper Space
Block Table
Model Space
Line
When you first invoke AutoCAD and the database is in its default state, enti-
ties are added to model space, the main space in AutoCAD, which is used for
model geometry and graphics. Paper space is intended to support “documen-
tation” geometry and graphics, such as drafting sheet outlines, title blocks,
and annotational text. The entity creation commands in AutoCAD ( LINE, in
this case) cause the entity to be added to the current database as well as to
the model space block. You can ask any entity which database and which
block it belongs to.
Next, suppose the user creates a circle with this command:
circle 9,3 2
Paper Space
Block Table
Model Space
Line
Circle
AutoCAD creates a new layer table record to hold the layer and then adds it
to the layer table.
Paper Space
Block Table
Model Space
Line
Circle
Layer Table
layer 0
my layer
AutoCAD creates a new group object and adds it to the GROUP dictionary,
which is contained in the named object dictionary. The new group contains
a list of the object IDs of the objects that compose the group.
Group
Dictionary New Group
Named Object
Dictionary
MLINE Style
Dictionary
Creating Entities
The following ObjectARX code creates the line and adds it to the model space
block table record:
AcDbObjectId
createLine()
{
AcGePoint3d startPt(4.0, 2.0, 0.0);
AcGePoint3d endPt(10.0, 7.0, 0.0);
AcDbLine *pLine = new AcDbLine(startPt, endPt);
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,
AcDb::kForWrite);
pBlockTable->close();
AcDbObjectId lineId;
pBlockTableRecord->appendAcDbEntity(lineId, pLine);
pBlockTableRecord->close();
pLine->close();
return lineId;
}
The createLine() routine obtains the block table for the current drawing.
Then it opens the model space block table record for writing. After closing
the block table, it adds the entity to the block table record and then closes
the block table record and the entity.
NOTE When you are done using any ObjectARX objects, you must explicitly
close them as soon as possible.
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,
AcDb::kForWrite);
pBlockTable->close();
AcDbObjectId circleId;
pBlockTableRecord->appendAcDbEntity(circleId, pCirc);
pBlockTableRecord->close();
pCirc->close();
return circleId;
}
AcDbLayerTableRecord *pLayerTableRecord =
new AcDbLayerTableRecord;
pLayerTableRecord->setName("ASDK_MYLAYER");
pEntity->setColorIndex(newColor);
pEntity->close();
return Acad::eOk;
}
New instances of an object are considered to be open for write. Some func-
tions, such as the AcDbBlockTable::getAt() function, obtain an object ID
and open the object at the same time. An object can’t be closed until it has
been added to the database. You own the object and can freely delete it at any
time before the object is added to the database.
However, once the object has been added to the database, you cannot delete
it directly. You can call the AcDbObject::erase() function, which marks the
object as erased. Erased objects remain in the database until the database is
destroyed, but do not get saved when the drawing is saved.
WARNING! Directly deleting an object that has been added to the database
will cause AutoCAD to terminate.
AcDbObjectId pGroupId;
pGroupDict->setAt(pGroupName, pGroup, pGroupId);
pGroupDict->close();
pGroup->close();
}
In This Chapter
3
This chapter describes how to write and run an ■ Creating an ObjectARX
Application
ObjectARX application. It lists the messages passed by
■ Example Application
AutoCAD to the ObjectARX application and shows how ■ Registering New Commands
■ Loading an ObjectARX
the application typically responds to those Application
29
Creating an ObjectARX Application
An ObjectARX application is a DLL that shares AutoCAD’s address space and
makes direct function calls to AutoCAD. ObjectARX applications typically
implement commands that can be accessed from within AutoCAD. These
commands are often implemented using custom classes. Creating an
ObjectARX application involves the following general steps.
The following five tables describe the messages that AutoCAD sends to
ObjectARX applications. The first table lists messages sent to all applications.
Message Description
kLoadDwgMsg Sent once when the drawing is opened. Then, if the application
registers any functions with AutoLISP, AutoCAD sends this
message once for each drawing loaded into the editor. The
AutoCAD editor is fully initialized at this point, and all global
functions are available. However, you cannot use an
acedCommand() function from a kLoadDwgMsg.
kPreQuitMsg Sent when AutoCAD quits, but before it begins to unload all
ObjectARX applications.
Message Description
kEndMsg Sent only when the END command is entered and there are
changes that need to be saved (when dbmod != 0). kEndMsg is
not sent for a NEW or OPEN, instead, kSaveMsg and
kLoadDwgMsg are sent. For END, if dbmod = 0, then kQuitMsg
is sent instead of kEndMsg.
kQuitMsg Sent when AutoCAD quits (ends without saving) the drawing
because a QUIT command was entered. The kQuitMsg can also
be received with the END command, as noted above. If the END
command is sent and dbmod = 0, then kQuitMsg is sent.
The next table lists the messages that an application receives if it has
registered a service with ObjectARX.
Message Description
Message Description
See the rxdefs.h file where these enumeration constants are defined by the
AppMsgCode type declaration.
You will need to decide which messages your ObjectARX application will
respond to. The following table describes recommended actions upon receipt
of a given message.
Don’t release system resources that are not tied to an edit session,
or clean up AcRx classes, AcEd reactors, or commands; they remain
valid across edit sessions.
kDependencyMsg Do perform any actions that are necessary for your application
when other applications become dependent on it, such as locking
your application so that it cannot be unloaded.
kNoDependencyMsg Do perform any actions that are necessary for your application
when there are no longer any other applications dependent on
yours, such as unlocking your application so that it can be
unloaded by the user if desired.
Time
Quit kUnloadDwgMsg
kQuit
kUnloadApp
WARNING! Using kRetError for the final return value from the
acrxEntryPoint() function will cause your application to be unloaded, except
for the messages kOleUnloadAppMsg and kUnloadAppMsg. In these cases, if
kRetError is returned, the application will not be unloaded.
acedRegCmds->addCommand("ASDK_DICTIONARY_COMMANDS",
"ASDK_ITERATE", "ITERATE", ACRX_CMD_MODAL,
iterateDictionary);
AsdkMyClass::rxInit();
acrxBuildClassHierarchy();
}
Example Application | 39
Registering New Commands
This section describes adding new commands using the AcEd command
registration mechanism. For information on adding new commands using
the functions acedDefun() and acedRegFunc(), see chapter 20, “ObjectARX
Global Utility Functions.” For information on adding new commands using
the AutoLISP defun function, see the AutoCAD Customization Guide.
Command Stack
AutoCAD commands are stored in groups in the command stack, which is
defined by the AcEdCommandStack class. One instance of the command stack
is created per AutoCAD session. This stack consists of the custom commands
that you have defined. The acedRegCmds() macro gives you access to the
command stack.
When you add a command, you also assign it a group name. A good policy
is to use your registered developer prefix for the group name to avoid name
collisions with other commands. Command names within a given group
must be unique, and group names must be unique. However, multiple appli-
cations can add a command of the same name, because the group name
makes the commands unambiguous.
Lookup Order
When a command is invoked, the command stack is searched by group name,
then by command name within the group. In general, the first group registered
will be the first one searched, but you cannot always predict what this order
will be. Use the AcEdCommandStack::popGroupToTop() function to specify that
a particular group should be searched first. At the user level, the Group option
of the ARX command allows the user to specify which group to search first.
Unlocking Applications
By default, applications are locked and cannot be unloaded. To be classified
as an “unloadable” application, the application must ensure that AutoCAD
and other applications no longer refer to any objects or structures the appli-
cation has defined. Before you make an application unloadable, be very
careful that no client applications contain active pointers to any objects in
your address space. For the list of cleanup operations an application must
perform to be unloadable, see “Preparing for Unloading” on page 38.
If you want to make your application unloadable, you need to store the value
of the pkt parameter sent with the AcRx::kInitAppMsg. The pkt parameter
will be used by the unlockApplication() function. By default, an application
is locked. If you unlock an application, it can be unloaded.
Use the following two functions to lock and unlock an application:
bool
AcRxDynamicLinker::lockApplication(void* pkt) const;
bool
AcRxDynamicLinker::unlockApplication(void* pkt) const;
The following function checks whether or not an application is locked:
bool
AcRxDynamicLinker::isApplicationLocked(const char* name)
const;
Analogous global functions are also provided:
bool
acrxLockApplication(void* pkt);
bool
acrxApplicationIsLocked(const char* modulename);
Demand Loading
Demand loading is a feature of AutoCAD that automatically attempts to load
an ObjectARX application that is not resident in AutoCAD. ObjectARX appli-
cations can be designed for loading by AutoCAD under one or more of the
following circumstances:
■ When a drawing file that contains custom objects created by the absent
application is read
■ When a user or another application issues one of the absent application’s
commands
■ When AutoCAD is started
■ Limits the creation of proxy objects (see chapter 14, “Proxy Objects”)
■ Provides greater flexibility for loading ObjectARX applications
■ Conserves memory by loading applications only when their functionality
is required
Demand Loading | 45
AutoCAD, the Windows System Registry, and
ObjectARX Applications
AutoCAD uses the Windows system registry to maintain a wide range of
application information, including information that uniquely identifies dif-
ferent AutoCAD releases, language versions, and products (such as AutoCAD
Map®) that may be installed on any given computer. The registry informa-
tion that identifies different versions of AutoCAD is of particular significance
for ObjectARX developers. The installation program for an ObjectARX appli-
cation must associate information about that ObjectARX application with
information about the version(s) of AutoCAD with which it is supposed to
run.
The AutoCAD installation program creates a unique time stamp key in the
system registry immediately below the release number key (as well as adding
the same installation ID to the executable itself). This key ensures that differ-
ent versions of AutoCAD from the same release will be able to populate their
own sections of the system registry. Within this key, values are stored for the
location of AutoCAD files, the language version, and the product name, as
illustrated in this example:
\\HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R15.0\
ACAD-1:409\
...
AcadLocation:REG_SZ:f:\ACAD2000
Language:REG_SZ:English
ProductName:REG_SZ:AutoCAD Map R15.0
...
The installation program for an ObjectARX application must be able to locate
the appropriate AutoCAD release key, as well as the appropriate language and
product values.
The time stamp key is also used to identify the version of AutoCAD that is
currently loaded (or the version that was most recently loaded). This identi-
fication is necessary, because the “current” version of AutoCAD resets the
information in the global HKEY_CLASSES_ROOT section of the registry for its
own use when it is loaded.
The CurVer value in the release key section of the registry is used to identify
the current version, for example:
\\HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R15.0\
...
CurVer:REG_SZ:ACAD-1:409
When AutoCAD attempts to demand load an ObjectARX application, it looks
in the section of the registry that belongs to the latest release of AutoCAD for
information about the ObjectARX application. If it does not find the
■ Verification that the sections of the system registry for the appropriate
version of AutoCAD exist. (If the AutoCAD section of the registry does not
exist, the user should be warned that a compatible version of AutoCAD
has not been installed, and the installation should be aborted.)
■ Creation of a specific set of keys and values for the application within the
section(s) of the system registry for the appropriate version(s) of
AutoCAD.
■ Creation of a major key for the application itself, and population of that
key with another set of specific keys and values.
Demand Loading | 47
Creating AutoCAD Subkeys and Values
The ObjectARX application’s installation program must be designed to man-
age a set of keys and values for that application within the section of system
registry for each version of AutoCAD with which it is intended to run. The
following example shows the layout of the keys and values in the section of
the registry that must be created and maintained for the application:
\\HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\releaseNum\
ACAD-1:LocaleID\
Applications\
ApplicationName\
LoadCtrls:REG_DWORD:acrxAppLoadReason
RegPath:REG_SZ:RegistryPathWhereLoaderIsSpecified
The releaseNum and ACAD-1:LocaleID keys are created by the AutoCAD
installation program.
The ApplicationName key must be the logical name of the application, which
is used internally by AutoCAD to identify the program.
The acrxAppLoadReason value defines the conditions under which the appli-
cation will be loaded, using one or more logical ORs of the following hex
values listed with their associated meanings:
0x01 Load the application upon detection of proxy object.
0x02 Load the application upon AutoCAD startup.
0x04 Load the application upon invocation of a command.
0x08 Load the application upon request by the user or another
application.
0x10 Do not load the application.
The RegistryPathWhereLoaderIsSpecified value must identify the registry
path for the application’s own section of the registry.
The ObjectARX API includes the acrxRegisterApp() function, which may be
used in an ObjectARX application to enter information about the application
into the AutoCAD section of the system registry. Typically,
acrxRegisterApp() would enter this information the first time the applica-
tion is loaded, and confirm the presence of that information on subsequent
loads.
Demand Loading | 49
The legitimate values for the system variable may be used in combination.
They are defined as follows:
0 Disables demand loading of all ObjectARX applications.
1 Enables demand loading of ObjectARX applications upon
detection of proxy objects.
2 Enables demand loading of ObjectARX applications upon
command invocation.
3 Enables demand loading for both proxy objects and
command invocation (the default).
The DEMANDLOAD system variable allows the user to disable demand loading
of all ObjectARX applications that have system registry settings specifying
demand loading on command invocation, and proxy detection. It cannot
cause an application to be demand loaded if the appropriate system registry
settings do not exist.
NOTE Demand loading on detection of custom classes will only work with
classes that are derived from AcDbObject, either directly or indirectly.
Demand Loading | 51
In this example, the developer’s registered developer prefix (ASDK) is used as
the prefix for all commands to ensure that there will be no possible conflict
with commands of the same name in other applications.
The ObjectARX application must also include the appropriate calls to the
acedRegCmds macro for demand loading on command to work.
?—List Applications
Lists the currently loaded ARX applications.
Load
Loads the .arx file that you specify in the standard file dialog box. If FILEDIA
is set to 0, a dialog box is not displayed, and you enter the name of the file
to load in response to the following prompt:
Runtime extension file: Enter a name
Unload
Unloads the specified ARX program. Some applications cannot be unloaded.
See “Unloading an ObjectARX Application” on page 44 for a description of
how the programmer decides whether a program can be unloaded by the user
with this command.
Commands
Displays all command names in all command groups registered from ARX
programs.
Options
Presents developer-related ARX application options.
Options (Group/CLasses/Services): Enter an option
■ Group
Moves the specified group of commands registered from ARX applications
to be the first group searched when resolving the names of AutoCAD
commands. Other registered groups, if there are any, are subsequently
searched, in the same order as before the ARX command was executed.
Command Group Name: Enter the command group name
ARX Command | 53
The search order is important only when a command name is listed in
multiple groups. This mechanism allows different ARX applications to
define the same command names in their own separate command groups.
ARX applications that define command groups should publish the group
name in their documentation.
Group is not intended to be selected by the user directly. The user specifies
which group is searched first by interacting with a script that executes the
ARX command with the Group option. This capability is usually embed-
ded in key menu item scripts. The user selects a menu item from the script.
The key menu item script executes the Group option to establish which
group is searched first, giving commands of the same name (but probably
different functionality) from one application precedence over commands
from another.
For example, applications called ABC Construction and XYZ Interiors
define command groups ABC and XYZ, respectively. Most of ABC Con-
struction’s commands are named with construction terminology, while
most of XYZ Interiors’ commands are named with interior decorating ter-
minology, but both applications define commands named INVENTORY
and ORDERS. When working on the construction aspects of a drawing, the
user chooses a menu item defined by ABC Construction, and the follow-
ing script runs:
ARX
Group
ABC
The script pops the ABC Construction command set to give it top priority
and to resolve INVENTORY to the ABC Construction version of the com-
mand. Later, when an interior designer is working on the drawing with
the same set of applications loaded, selecting a key icon ensures that the
XYZ Interiors commands have precedence.
■ Classes
Displays a class hierarchy of C++ classes derived from objects registered in
the system, whether registered by AutoCAD or by an ARX program.
■ Services
Lists the names of all services registered by AutoCAD and by loaded ARX
programs.
Error Handling
The examples in this guide have omitted necessary error checking to simplify
the code. However, you’ll always want to check return status and take appro-
priate action. The following example shows appropriate use of error checking
for several examples shown first in chapter 2, “Database Primer.”
Acad::ErrorStatus
createCircle(AcDbObjectId& circleId)
{
circleId = AcDbObjectId::kNull;
AcDbBlockTable *pBlockTable;
Acad::ErrorStatus es =
acdbHostApplicationServices()->workingDatabase()->
getSymbolTable(pBlockTable, AcDb::kForRead);
if (es != Acad::eOk) {
delete pCirc;
return es;
}
AcDbBlockTableRecord *pBlockTableRecord;
es = pBlockTable->getAt(ACDB_MODEL_SPACE,
pBlockTableRecord, AcDb::kForWrite);
if (es != Acad::eOk) {
Acad::ErrorStatus es2 = pBlockTable->close();
if (es2 != Acad::eOk) {
acrx_abort("\nApp X failed to close Block"
" Table. Error: %d",
acadErrorStatusText(es2));
}
delete pCirc;
return es;
}
es = pBlockTable->close();
if (es != Acad::eOk) {
acrx_abort("\nApp X failed to close Block Table."
" Error: %d", acadErrorStatusText(es));
}
es = pBlockTableRecord->appendAcDbEntity(circleId,
pCirc);
if (es != Acad::eOk) {
Acad::ErrorStatus es2 = pBlockTableRecord->close();
if (es2 != Acad::eOk) {
acrx_abort("\nApp X failed to close"
" Model Space Block Record. Error: %s",
acadErrorStatusText(es2));
}
delete pCirc;
return es;
}
es = pBlockTableRecord->close();
if (es != Acad::eOk) {
acrx_abort("\nApp X failed to close"
" Model Space Block Record. Error: %d",
acadErrorStatusText(es));
}
Acad::ErrorStatus
createNewLayer()
{
AcDbLayerTableRecord *pLayerTableRecord
= new AcDbLayerTableRecord;
if (pLayerTableRecord == NULL)
return Acad::eOutOfMemory;
Acad::ErrorStatus es
= pLayerTableRecord->setName("ASDK_MYLAYER");
if (es != Acad::eOk) {
delete pLayerTableRecord;
return es;
}
AcDbLayerTable *pLayerTable;
es = acdbHostApplicationServices()->workingDatabase()->
getSymbolTable(pLayerTable, AcDb::kForWrite);
if (es != Acad::eOk) {
delete pLayerTableRecord;
return es;
}
Error Handling | 57
AcDbObjectId ltypeObjId;
es = pLinetypeTbl->getAt("CONTINUOUS", ltypeObjId);
if (es != Acad::eOk) {
delete pLayerTableRecord;
es = pLayerTable->close();
if (es != Acad::eOk) {
acrx_abort("\nApp X failed to close Layer"
" Table. Error: %d",
acadErrorStatusText(es));
}
return es;
}
pLayerTableRecord->setLinetypeObjectId(ltypeObjId);
es = pLayerTable->add(pLayerTableRecord);
if (es != Acad::eOk) {
Acad::ErrorStatus es2 = pLayerTable->close();
if (es2 != Acad::eOk) {
acrx_abort("\nApp X failed to close Layer"
" Table. Error: %d",
acadErrorStatusText(es2));
}
delete pLayerTableRecord;
return es;
}
es = pLayerTable->close();
if (es != Acad::eOk) {
acrx_abort("\nApp X failed to close Layer"
" Table. Error: %d",
acadErrorStatusText(es));
}
es = pLayerTableRecord->close();
if (es != Acad::eOk) {
acrx_abort("\nApp X failed to close Layer"
" Table Record. Error: %d",
acadErrorStatusText(es));
}
return es;
}
In This Chapter
4
This chapter describes basic database protocol including ■ Initial Database
■ Creating and Populating a
how to create a database, how to read in a drawing file,
Database
and how to save the database. The wblock and insert ■ Saving a Database
■ The wblock Operation
operations are also described here.
■ Inserting a Database
For more detailed information on the deepClone and ■ Setting Current Database Values
■ Example of Database Operations
wblock operations, see chapter 18, “Deep Cloning.”
■ Long Transactions
■ External References
■ Indexes and Filters
■ Drawing Summary Information
■ Last Saved by Autodesk Software
59
Initial Database
When an AutoCAD session begins, the database contains the following
elements:
Saving a Database
To save a database, use the AcDbDatabase::saveAs() function:
Acad::ErrorStatus
AcDbDatabase::saveAs(char* fileName);
The file name can be a path to a local file, or an Internet address.
Save format
Saving a Database | 61
Save format (continued)
■ Either you or your user may set a persistent session-wide default format for
save that will be honored by all save commands except AUTOSAVE.
■ Only the user can temporarily (not persistently between sessions) override
this setting for a particular document.
If conflicts arise when the source and target databases are being merged (for
example, if both databases have the same linetype name), AutoCAD uses the
version in the target database.
The following function is equivalent to a standard drawing INSERT
command:
Acad::ErrorStatus
AcDbDatabase::insert(AcDbObjectId& blockId,
const char* pBlockName,
AcDbDatabase* pDb);
This function copies the entities from the model space of the input database
(pDb) into the specified block table record (pBlockName) and returns the block
ID of the new block table record (blockId). The application must then create
the reference to the block table record and add it to the database.
The following function is equivalent to an AutoCAD INSERT* command:
Acad::ErrorStatus
AcDbDatabase::insert(const AcGeMatrix3d& xform,
AcDbDatabase* pDb);
This function copies the entities from the model space of the input database
(pDb) and puts them into the current space of the new database (paper space
or model space), applying the specified transformation (xform) to the
entities.
Inserting a Database | 65
Setting Current Database Values
If a data property such as color or linetype is not specified for an entity, the
database’s current value for that data is used. The following sections outline
the functions used to specify the current data values associated with the
database.
■ A linetype scale setting for the current entity, stored in the CELTSCALE
system variable.
■ A linetype scale setting for the current drawing, stored in the LTSCALE
system variable.
■ A flag that indicates whether to apply linetype scaling to the space the
entity resides in or to the entity’s appearance in paper space. This setting
is stored in the PSLTSCALE system variable.
Acad::ErrorStatus
AcDbDatabase::setCeltscale(double);
Acad::ErrorStatus
AcDbDatabase::setPsltscale(Adesk::Boolean)
AcDbBlockTable *pBtbl;
pDb->getSymbolTable(pBtbl, AcDb::kForRead);
pBtblRcd->appendAcDbEntity(pCir2);
pCir2->close();
pBtblRcd->close();
void
readDwg()
{
// Set constructor parameter to kFalse so that the
// database will be constructed empty. This way only
// what is read in will be in the database.
//
AcDbDatabase *pDb = new AcDbDatabase(Adesk::kFalse);
AcDbBlockTableRecord *pBlkTblRcd;
pBlkTbl->getAt(ACDB_MODEL_SPACE, pBlkTblRcd,
AcDb::kForRead);
pBlkTbl->close();
AcDbBlockTableRecordIterator *pBlkTblRcdItr;
pBlkTblRcd->newIterator(pBlkTblRcdItr);
delete pBlkTblRcdItr;
delete pDb;
}
Long Transactions
Long Transactions are used to support the AutoCAD Reference Editing fea-
ture and are very useful for ObjectARX applications. These classes and
functions provide a scheme for applications to check out entities for editing
and check them back in to their original location. This operation replaces the
original objects with the edited ones. There are three types of long transac-
tion check out:
■ AcDbLongTransaction class
■ AcDbLongTransWorkSetIterator class
■ AcApLongTransactionReactor class
■ AcApLongTransactionManager class
■ AcDbDatabase::wblockCloneObjects() function
AcDbLongTransaction Class
AcDbLongTransaction is the class that contains the information needed to
track a long transaction. The AcDbLongTransactionManager class takes the
responsibility for creating and appending AcDbLongTransaction objects to
the database. It then returns the AcDbObjectId of the AcDbLongTransaction
Long Transactions | 69
object. Like all other database-resident objects, its destruction is handled by
the database.
AcDbLongTransWorkSetIterator Class
AcDbLongTransWorkSetIterator provides read-only access to the objects in
the work set. During construction in
AcDbLongTransaction::newWorkSetIterator(), it can be set up to include
only the active work set, or include objects added to the work set because
they are referenced by objects in the work set (secondary objects). It can also
handle objects removed from the work set, either by
AcDbLongTransaction::removeFromWorkSet(), or by being erased.
AcApLongTransactionReactor Class
AcApLongTransactionReactor provides notification specific to long transac-
tion operations. It is designed to be used in conjunction with the deep clone
notifications that will also be sent, but will vary depending upon which type
of check out/in is being executed. To connect these notifications with the
deep clone notifications, the AcDbIdMapping object used for cloning can be
retrieved by calling the AcDbLongTransaction::activeIdMap() function.
AcApLongTransactionManager Class
AcApLongTransactionManager is the manager for starting and controlling
long transactions. There is only one for each AutoCAD session, and it is
accessed via a pointer returned by the acapLongTransactionManager object.
AcDbDatabase::wblockCloneObjects() Function
The wblockCloneObjects() function is a member of AcDbDatase. It will deep
clone objects from one database to another and follow hard references so
that all dependent objects are also cloned. The behavior of symbol table
records, when duplicates are found, is determined by the type parameter. The
Long Transactions | 71
// Get a dwg file from the user.
//
rb = acutNewRb(RTSTR);
acedGetFileD("Pick a drawing", NULL, "dwg", 0, rb);
fname = (char*)acad_malloc(strlen(rb->resval.rstring) + 1);
strcpy(fname, rb->resval.rstring);
acutRelRb(rb);
// Get the block table and then the model space record.
//
AcDbBlockTable *pBlockTable;
pDb->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pOtherMsBtr;
pBlockTable->getAt(ACDB_MODEL_SPACE, pOtherMsBtr,
AcDb::kForRead);
pBlockTable->close();
// Create an iterator
//
AcDbBlockTableRecordIterator *pIter;
pOtherMsBtr->newIterator(pIter);
AcDbObjectId id = pThisMsBtr->objectId();
pThisMsBtr->close();
// Create the long transaction. This will check all the entities
// out of the external database.
acapLongTransactionManagerPtr()->checkOut(transId,
objIdArray, id);
// Now modify the color of these entities.
//
int colorIndex;
acedGetInt("\nEnter color number to change entities to: ",
&colorIndex);
AcDbObject* pObj;
if (acdbOpenObject(pObj, transId, AcDb::kForRead) == Acad::eOk)
{
// Get a pointer to the transaction
//
AcDbLongTransaction* pLongTrans =
AcDbLongTransaction::cast(pObj);
if (pLongTrans != NULL) {
// Get a work set iterator
//
AcDbLongTransWorkSetIterator* pWorkSetIter =
pLongTrans->newWorkSetIterator();
// Iterate over the entities in the work set
// and change the color.
for (pWorkSetIter->start(); !pWorkSetIter->done();
pWorkSetIter->step()) {
AcDbEntity *pEntity;
acdbOpenAcDbEntity(pEntity, pWorkSetIter->objectId(),
AcDb::kForWrite);
pEntity->setColorIndex(colorIndex);
pEntity->close();
}
delete pWorkSetIter;
}
pObj->close();
}
Long Transactions | 73
// Pause just to see the change.
//
char str[132];
acedGetString(0, "\nNote the new colors. Press return to \
check the objects back in to the original database", str);
// Check the entities back in to the orginal database.
//
acapLongTransactionManagerPtr()->checkIn(transId);
// Save the original database, since we made changes
//
pDb->saveAs(fname);
// Close and Delete the database.
//
delete pDb;
pDb = NULL;
acad_free(fname);
}
External References
External references (xrefs) can be created and manipulated through several
global functions. These global functions mimic the AutoCAD XREF command
capabilities. The functions provided are
■ acedXrefAttach()
■ acedXrefOverlay()
■ acedXrefUnload()
■ acedXrefDetach()
■ acedXrefReload()
■ acedXrefBind()
■ acedXrefXBind()
■ acedXrefCreateBlockname()
■ acedXrefReload()
■ beginAttach()
■ otherAttach()
■ abortAttach()
■ endAttach()
■ redirected()
■ comandeered()
■ AcDbDatabase::xrefBlockId()
■ AcDbDatabase::restoreOriginalXrefSymbols()
■ AcDbDatabase::restoreForwardingXrefSymbols()
External References | 75
AcDbDatabase::xrefBlockId() Function
The xrefBlockId() function will get the AcDbObjectId of the block table
record, which refers to this database as an xref. When an xref is reloaded for
any reason (for example, XREF Reload or XREF Path commands), the former
database is kept in memory for Undo. This means that more than one data-
base may point to the same xref block table record. However only one will be
the currently active xref database for that record. The database pointer
returned by the AcDbBlockTableRecord::xrefDatabase() function on the
found record will be the active database for that xref.
AcDbDatabase::restoreOriginalXrefSymbols() Function
The restoreOriginalXrefSymbols() function restores a resolved xref data-
base to its original form, as it would be if just loaded from its file. The xref is
then in a condition where it can be modified and saved back to a file. After
calling this function, the host drawing is no longer in a valid state for regen
or for any xref command modifications or reloads. The database modifica-
tions, save back, and the restoreForwardingXrefSymbols() function must
be called before anything that might allow a regen.
AcDbDatabase::restoreForwardingXrefSymbols() Function
The restoreForwardingXrefSymbols() function restores the xref back to a
valid, attached state. Not only does it restore the original resolved symbols,
but it also seeks out newly added symbols and resolves them as well. The
restoreForwardingXrefSymbols() function cannot handle newly added,
nested xref block table records unless they already exist and are resolved in
the host drawing.
■ Updating indexes
■ Adding and removing indexes to block table records
■ Adding and removing filters to block references
■ Querying for indexes from block table records
■ Querying for filters from block references
■ Iterating through block table records and visiting only a subset of entities
■ AcDbIndexFilterManager namespace
■ AcDbIndex class
■ AcDbFilter class
■ AcDbFilteredBlockIterator class
■ AcDbCompositeFilteredBlockIterator class
AcDbIndex Class
The AcDbIndex class is the base class for all index objects. AcDbSpatialIndex
and AcDbLayerIndex derive from this class.
Keeping the index up to date is achieved through the
AcDbIndexFilterManager::updateIndexes() function calls being explicitly
invoked (either by an application or AutoCAD).
The AcDbFilteredBlockIterator will serve as the means to visit all the
AcDbObjectIds that are “hits” from the query defined by the AcDbFilter
passed to its constructor. For example, in the spatial index case, the
AcDbSpatialFilter object instance passed to the newIterator() method
will define a query region. The AcDbSpatialIndex object, through its
newIterator() method, will provide an AcDbSpatialIndexIterator that will
return object IDs that correspond to entities that fit within the query
volume.
AcDbFilter class
The AcDbFilter class is meant to define a “query.” It provides the “key” to
the AcDbCompositeFilteredBlockIterator, for which the corresponding
index is obtained through the indexClass() method.
AcDbFilteredBlockIterator Class
The AcDbFilteredBlockIterator class provides a method to process a
“query” on an index. It is used by the
AcDbCompositeFilteredBlockIterator.
AcDbCompositeFilteredBlockIterator Class
The AcDbCompositeFilteredBlockIterator class provides the alternate to
normal block iteration. By providing the filter list in the init() method, the
AcDbCompositeFilteredBlockIterator object looks for corresponding
AcDbIndex derived objects through the AcDbFilter::indexClass() method,
and creates AcDbFilteredBlockIterator objects. If the matching up-to-date
indexClass() objects are not available, it creates an
AcDbFilteredBlockIterator through the AcDbFilter::newIterator()
method. It then orders the composition of the AcDbFilteredBlockIterator
objects based on the AcDbFilteredBlockIterator::estimatedHits() and
AcDbFilteredBlockIterator::buffersForComposition() methods. The col-
AcDbDatabaseSummaryInfo Class
The AcDbDatabaseSummaryInfo class encapsulates a set of character strings
that can be used to add additional information to a DWG file. The maximum
length of these strings is 511 characters. This information is stored and
retrieved in the Summary Information object with specific methods for each
information field. The predefined fields are
■ Title
■ Subject
■ Author
■ Keywords
■ Comments
■ Last saved by
■ Revision number
■ Hyperlink base
You can create your own custom fields in addition to the predefined fields.
These custom fields are stored in a list, and you can manipulate custom fields
either by their name (or key) or position (index) in the list. Custom fields are
indexed starting at 1, and there is no limit to the number of fields you can
create.
AcDbSummaryInfoManager Class
The AcDbSummaryInfoManager class organizes the summary information reac-
tors, with methods to add and remove reactors, and to send notification that
the summary information has changed.
In This Chapter
5
This chapter describes topics that relate to all AutoCAD ■ Opening and Closing Database
Objects
database objects, including entities, symbol table
■ Deleting Objects
81
Opening and Closing Database Objects
Each AcDbObject object can be referred to in three different ways:
■ By its handle
■ By its object ID
■ By a C++ instance pointer
When AutoCAD is not running, the drawing is stored in the file system.
Objects contained in a DWG file are identified by their handles.
After the drawing is opened, the drawing information is accessible through
the AcDbDatabase object. Each object in the database has an object ID, which
persists throughout the current edit session, from creation until deletion of
the AcDbDatabase in which the object resides. The open functions take an
object ID as an argument and return a pointer to an AcDbObject object. This
pointer is valid until the object is closed, as shown in the following figure.
DWG
Handle open drawing
SAVE or
WBLOCK AcDbDatabase
command ObjectID open object
close object
C++
Pointer
pObject->getAcDbHandle(handle);
AcDbEntity*
selectEntity(AcDbObjectId& eId, AcDb::OpenMode openMode)
{
ads_name en;
ads_point pt;
acedEntSel("\nSelect an entity: ", en, pt);
AcDbEntity * pEnt;
acdbOpenObject(pEnt, eId, openMode);
return pEnt;
}
The following table shows the error codes returned when you attempt to
open an object in different modes and the object is already open.
If you are trying to open an object for write and you receive an error
eWasOpenForRead, you can use upgradeOpen() to upgrade the open status to
write if there is only one reader of the object. Then you would use
downgradeOpen() to downgrade its status to read. Similarly, if your object is
open for notify—for example, when you are receiving notification—and you
want to open it for write, you can use upgradeFromNotify() to upgrade its
open status to write. Then you would use downgradeToNotify() to down-
grade its status to notify.
For more information about how to manage complex sequences of opening
and closing objects, see “Transaction Manager” on page 451.
Deleting Objects | 85
Adding Object-Specific Data
You can use any of four mechanisms for adding instance-specific data in your
application:
Extended Data
Extended data (xdata) is created by applications written with ObjectARX or
AutoLISP and can be added to any object. Xdata consists of a linked list of
resbufs used by the application. (AutoCAD maintains the information but
doesn’t use it.) The data is associated with a DXF group code in the range of
1000 to 1071.
This mechanism is space-efficient and can be useful for adding lightweight
data to an object. However, xdata is limited to 16K and to the existing set of
DXF group codes and types.
For a more detailed description of xdata, see the
AutoCAD Customization Guide.
Use the AcDbObject::xData() function to obtain the resbuf chain contain-
ing a copy of the xdata for an object:
virtual resbuf*
AcDbObject::xData(const char* regappName = NULL) const;
Use the AcDbObject::setXData() function to specify the xdata for an object:
virtual Acad::ErrorStatus
AcDbObject::setXData(const resbuf* xdata);
The following example uses the xData() function to obtain the xdata for a
selected object and then prints the xdata to the screen. It then adds a string
(testrun) to the xdata and calls the setXdata() function to modify the
object’s xdata. This example also illustrates the use of the upgradeOpen() and
downgradeOpen() functions.
void
addXdata()
{
AcDbObject* pObj = selectObject(AcDb::kForRead);
if (!pObj) {
acutPrintf("Error selecting object\n");
return;
}
pRb = pObj->xData(appName);
if (pRb != NULL) {
// If xdata is present, then walk to the
// end of the list.
//
for (pTemp = pRb; pTemp->rbnext != NULL;
pTemp = pTemp->rbnext)
{ ; }
} else {
// If xdata is not present, register the application
// and add appName to the first resbuf in the list.
// Notice that there is no -3 group as there is in
// AutoLISP. This is ONLY the xdata so
// the -3 xdata-start marker isn’t needed.
//
acdbRegApp(appName);
pRb = acutNewRb(AcDb::kDxfRegAppName);
pTemp = pRb;
pTemp->resval.rstring
= (char*) malloc(strlen(appName) + 1);
strcpy(pTemp->resval.rstring, appName);
}
pObj->close();
acutRelRb(pRb);
}
pObj = selectObject(AcDb::kForWrite);
if (pObj == NULL) {
return;
}
printList(pRbList);
acutRelRb(pRbList);
}
Global Function Example
The following example uses global ObjectARX functions to create an xrecord
and add it to the dictionary associated with the key ASDK_REC.
int
createXrecord()
{
struct resbuf *pXrec, *pEnt, *pDict, *pTemp, *pTemp2;
ads_point dummy, testpt = {1.0, 2.0, 0.0};
ads_name xrecname, ename, extDict = {0L, 0L};
pEnt = acdbEntGet(ename);
pTemp2->rbnext = pTemp->rbnext;
pTemp->rbnext = pDict;
acdbEntMod(pEnt);
acutRelRb(pEnt);
}
pEnt = acdbEntGet(ename);
if (extDict[0] == 0L) {
acutPrintf("\nNo extension dictionary present.");
return RTNORM;
}
acedRetVoid();
return RTNORM;
}
NOTE The erase() function has different results for database objects and
entities, with consequences for unerasing them:
Container objects such as polylines and block table records usually provide
the option of skipping erased elements when iterating over their contents.
The default behavior is to skip erased elements.
Erased objects are not filed out to DWG or DXF files.
AcDbObject has two member functions for filing out: dwgOut() and
dxfOut(), and two member functions for filing in: dwgIn() and dxfIn().
These member functions are primarily called by AutoCAD; object filing is
almost never explicitly controlled by applications that use the database.
However, if your application implements new database object classes, you’ll
need a more in-depth understanding of object filing. See chapter 12,
“Deriving from AcDbObject.”
The dwg- and dxf- prefixes indicate two fundamentally different data
formats, the first typically used in writing to and from DWG files, and the
second primarily for DXF files and AutoLISP entget, entmake, and entmod
functions. The primary difference between the two formats is that for DWG
filers (an object that writes data to a file), the data is not explicitly tagged.
The DXF filers, in contrast, associate a data group code with every element of
data in a published data format (see chapter 12, “Deriving from
AcDbObject”).
Object Filing | 95
96
Entities
In This Chapter
6
This chapter describes entities—database objects with a ■ Entities Defined
■ Entity Ownership
graphical representation. It lists the properties and
■ AutoCAD Release 12 Entities
operations all entities have in common. Examples show ■ Common Entity Properties
how to create blocks, inserts, and complex entities, and ■ Common Entity Functions
■ Creating Instances of AutoCAD
how to select and highlight subentities. Entities
■ Complex Entities
■ Coordinate System Access
■ Curve Functions
■ Associating Hyperlinks with
Entities
97
Entities Defined
An entity is a database object that has a graphical representation. Examples
of entities include lines, circles, arcs, text, solids, regions, splines, and
ellipses. The AcDbEntity class is derived from AcDbObject.
With a few exceptions, entities contain all necessary information about their
geometry. A few entities contain other objects that hold their geometric
information or attributes. Complex entities include the following:
98 | Chapter 6 Entities
Entity Ownership
Entities in the database normally belong to an AcDbBlockTableRecord. The
block table in a newly created database has three predefined records,
*MODEL_SPACE, *PAPER_SPACE, and *PAPER_SPACE0, which represent
model space and the two pre-defined paper space layouts. Additional records
are added whenever the user creates new blocks (block records), typically by
issuing a BLOCK, HATCH, or DIMENSION command.
The ownership structure for database entities is as follows:
Entity Ownership | 99
AcDbDatabase
AcDbBlockTable
AcDbBlockTableRecord
AcDbxxxVertex or AcDbSequenceEnd
AcDbFaceRecord or
AcDbAttribute
■ AcDb2dPolyline
■ AcDb3dPolyline
■ AcDbPolygonMesh
■ AcDbPolyFaceMesh
■ AcDbSequenceEnd
■ AcDbBlockBegin
■ AcDbBlockEnd
■ AcDbVertex
■ AcDbFaceRecord
■ AcDb2dVertex
■ AcDb3dPolylineVertex
■ AcDbPolygonMeshVertex
■ AcDbPolyFaceMeshVertex
■ AcDbMInsertBlock
■ Color
■ Linetype
■ Linetype scale
■ Visibility
■ Layer
■ Line weight
■ Plot style name
If a property has not been explicitly specified for an entity, the database’s cur-
rent value for that property is used. See chapter 4, “Database Operations,” for
a description of the member functions used for setting and getting the cur-
rent property values associated with the database.
Entity Color
Entity color can be set and read as numeric index values ranging from 0 to
256, or by instances of AcCmColor, which is provided for future use by an
expanded color model. Currently, AutoCAD uses color indexes only. The cor-
rect color index can be obtained from an instance of AcCmColor using the
AcCmColor::getColorIndex() member function.
Color indexes 1 through 7 are used for standard colors, as shown in the fol-
lowing table:
Colors 1 to 7
1 Red
2 Yellow
3 Green
4 Cyan
5 Blue
6 Magenta
7 White or Black
Adesk::UInt16
AcDbEntity::colorIndex() const;
Entity Linetype
The linetype value points to a symbol table entry that specifies a series of dots
and dashes used for drawing lines. When an entity is instantiated, its line-
type is set to NULL. When the entity is added to the database, if a linetype has
not been specified for the entity, the linetype is set to the database’s current
linetype value. This default value is stored in the CELTYPE system variable.
Linetype can be specified by name, by a string, or by the object ID of an
AcDbLineTypeTableRecord in the entity’s target database.
virtual Acad::ErrorStatus
AcDbEntity::setLinetype(AcDbObjectId newVal);
This function returns the name of the current entity linetype:
char* AcDbEntity::linetype() const;
This function returns the object ID for the symbol table record specifying the
linetype:
AcDbObjectId AcDbEntity::linetypeId() const;
double
AcDbEntity::linetypeScale() const;
Entity Visibility
If you specify that an entity is invisible, it will be invisible regardless of other
settings in the database. Other factors can also cause an entity to be invisible.
For example, an entity will not be displayed if its layer is turned off or frozen.
The value of AcDb::Visibility can be either kInvisible or kVisible.
Acad::ErrorStatus
AcDbEntity::setVisibility(AcDb::Visibility newVal);
AcDb::Visibility
AcDbEntity::visibility() const;
Entity Layer
All entities have an associated layer. The database always contains at least
one layer (layer 0). As with linetypes, you can specify a layer for an entity. If
you don’t specify a layer, the default database layer value is used for a new
entity.
Each layer also has associated properties, which include frozen/thawed,
on/off, locked/unlocked, color, linetype, and viewport (see chapter 7,
“Container Objects”). When an entity’s color or linetype is BYLAYER, the
value of the layer property is used for the entity.
If a layer value is specified for an entity, the current database layer value is
ignored.
The following functions enable you to set the layer for an entity, either by
name or by object ID:
Acad::ErrorStatus
AcDbEntity::setLayer(const char* newVal);
Acad::ErrorStatus
AcDbEntity::setLayer(AcDbObjectId newVal);
Mode Description
kOsModeEnd Endpoint
kOsModeMid Midpoint
kOsModeCen Center
kOsModeNode Node
kOsModeQuad Quadrant
kOsModeIns Insertion
kOsModePerp Perpendicular
kOsModeTan Tangent
kOsModeNear Nearest
Transform Functions
The AcDbEntity class provides two transformation functions:
virtual Acad::ErrorStatus
AcDbEntity::transformBy(const AcGeMatrix3d& xform);
virtual Acad::ErrorStatus
AcDbEntity::getTransformedCopy(const AcGeMatrix3d& xform,
AcDbEntity*& ent) const;
The transformBy() function modifies the entity using the specified matrix.
In AutoCAD, it is called by the grip move, rotate, scale, and mirror modes. In
some cases, however, applying the transformation requires that a new entity
be created. In such cases, the getTransformedCopy() function is used so that
the resulting entity can be an instance of a different class than the original
entity.
When you explode a block reference that has been nonuniformly scaled, the
getTransformedCopy() function is called on the entities in the block refer-
ence to create the new entities (see “Exploding Entities” on page 123).
For example, suppose a drawing contains the three lines shown in the
following illustration. Line1 is “this” and line3 is the argument entity. If the
line2
virtual Acad::ErrorStatus
AcDbEntity::intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
const AcGePlane& projPlane,
AcGePoint3dArray& points,
int thisGsMarker = 0,
int otherGsMarker = 0) const;
The returned points are always on the entity (“this”). Therefore, in cases of
apparent intersection, the intersected points are projected back to the entity
before they are returned.
Both versions of the intersectWith() function allow you to supply optional
GS markers to optimize performance for this function. If the entity’s
intersectWith() function has implemented the use of GS markers, then
3
2 12
1
9 10 8 11
6
5 7
4
Subentity Path
A subentity path uniquely identifies a subentity within a particular entity in
a drawing. This path, of class AcDbFullSubentPath, consists of an array of
object IDs and a subentity ID object:
{AcDbObjectIdArray mObjectIds;
AcDbSubentId mSubentId;
}
The array contains the object IDs that specify the path to the “main” entity.
For example, a block reference (an entity that references a block table record)
might contain two boxes, each of type AcDb3dSolid. The object ID array con-
tains two entries: the ID of the block reference, followed by the ID of the
main entity [InsertID, SolidID].
The second element of an AcDbFullSubentPath is an AcDbSubentId object,
which has a subentity type (vertex, edge, or face) and the index of the sub-
entity in the list. Use the AcDbSubentId functions type() and index() to
access the member data.
Using the previous example, the second edge of the solid will have its
AcDbFullSubentPath as
{(InsertID, solid1ID)
(kEdgeSubentType, 2)};
If you have a solid only, AcDbFullSubentPath would be as follows for the first
face of the solid.
{(solidID)
(kFaceSubentType, 1)};
To highlight a subentity
1 Obtain the GS marker for the selected entity from the selection set.
2 Pass the GS marker to the entity class to be converted to a subentity path
using the getSubentPathsAtGsMarker() function. Specify the type of suben-
tity you’re interested in (vertex, edge, face).
3 Once you have the path to the selected subentity, you’re ready to call the
highlight() function, passing in the correct subentity path.
Selecting an Entity
For selection, you’ll use a combination of global functions. First, use the
acedSSGet() function to obtain the selection set. Then, use the
acedSSNameX() function to obtain a subentity GS marker for the selected
entity.
int acedSSGet(
const char *str,
const void *pt1,
const ads_point pt2,
const struct resbuf *entmask,
ads_name ss);
int acedSSNameX(
struct resbuf** rbpp,
const ads_name ss,
const longvi);
Converting GS Markers to Subentity Paths
Use the getSubentPathsAtGsMarker() function to obtain the subentity for
the GS marker returned by the acedSSNameX() function. The complete syntax
for this function is
virtual Acad::ErrorStatus
AcDbEntity::getSubentPathsAtGsMarker(
AcDb::SubentType type,
int gsMark,
const AcGePoint3d& pickPoint,
const AcGeMatrix3d& viewXform,
int& numPaths,
AcDbFullSubentPath*& subentPaths
int numInserts = 0,
AcDbObjectId* entAndInsertStack = NULL) const;
The first argument to this function is the type of subentity you’re interested
in (vertex, edge, or face). In the example code in “Highlighting the Suben-
tity,” the first call to this function specifies kEdgeSubentType because you’re
return Acad::eOk;
}
pEnt->getSubentPathsAtGsMarker(AcDb::kEdgeSubentType,
marker, pickpnt, xform, numIds, subentIds);
// At this point the subentId’s variable contains the
// address of an array of AcDbFullSubentPath objects.
// The array should be one element long, so the picked
// edge’s AcDbFullSubentPath is in subentIds[0].
//
// For objects with no edges (such as a sphere), the
// code to highlight an edge is meaningless and must
// be skipped.
//
if (numIds > 0) {
// Highlight the edge.
//
pEnt->highlight(subentIds[0]);
pEnt->getSubentPathsAtGsMarker(AcDb::kFaceSubentType,
marker, pickpnt, xform, numIds, subentIds);
Acad::ErrorStatus
addToModelSpace(AcDbObjectId &objId, AcDbEntity* pEntity)
{
AcDbBlockTable *pBlockTable;
AcDbBlockTableRecord *pSpaceRecord;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
pBlockTable->getAt(ACDB_MODEL_SPACE, pSpaceRecord,
AcDb::kForWrite);
pSpaceRecord->appendAcDbEntity(objId, pEntity);
pBlockTable->close();
pEntity->close();
pSpaceRecord->close();
return Acad::eOk;
ins1
ins2
ins3
void
createInsert()
{
// Create a nested insert and try highlighting its
// various subcomponents.
//
// There are six entities in total -- three polys and
// three boxes (solids). We’ve named them: poly1, poly2,
// poly3, and box1, box2, box3. We also have three
// inserts: ins1, ins2, ins3.
//
// ins3 is an insert of a block that contains (poly3, box3)
// ins2 is an insert of a block that contains (poly2, box2,
// ins3).
// ins1 is an insert of a block that contains (poly1, box1,
// ins2).
//
// Let's create these entities first.
//
// Polys
//
AsdkPoly *poly1, *poly2, *poly3;
AcGeVector3d norm(0, 0, 1);
if ((poly1=new AsdkPoly)==NULL){
acutPrintf("\nOut of Memory.");
return;
}
if (poly1->set(AcGePoint2d(2, 8),AcGePoint2d(4, 8), 6, norm,
"POLY1",0)!=Acad::eOk){
acutPrintf("\nCannot create object with given parameters.");
delete poly1;
return;
}
// Boxes
//
AcDb3dSolid *box1, *box2, *box3;
box1 = new AcDb3dSolid();
box2 = new AcDb3dSolid();
box3 = new AcDb3dSolid();
box1->createBox(2, 2, 2);
box2->createBox(2, 2, 2);
box3->createBox(2, 2, 2);
AcGeMatrix3d mat;
mat(0, 3) = 2; mat(1, 3) = 2;
box1->transformBy(mat);
mat(0, 3) = 7; mat(1, 3) = 2;
box2->transformBy(mat);
mat(0, 3) = 12; mat(1, 3) = 2;
box3->transformBy(mat);
postToDb(box1);
postToDb(box2);
postToDb(box3);
void
hilitInsert()
{
Adesk::Boolean interrupted = Adesk::kFalse;
acutPrintf("\nSelect an insert");
Acad::ErrorStatus es = Acad::eOk;
AcDbEntity *ent = NULL;
AcDbEntity *ent2 = NULL;
AcDbBlockReference *blRef = NULL;
AcDbObjectId objectId, blRefId;
ads_name ename, sset;
int sel_method;
ads_name subname;
short marker;
AcGePoint3d pickpnt;
AcGeVector3d pickvec;
if (!extractEntityInfo(rb,
sel_method,
ename,
subname,
marker,
pickpnt,
pickvec)) {
acutPrintf("\nextractEntityInfo failed");
acedSSFree(sset);
return;
}
acedSSFree(sset);
assert(marker != 0);
if (marker == 0) {
acutPrintf("\nmarker == 0");
return;
}
count--;
acutRelRb(insStack);
AcDbFullSubentPath subPath;
for (int i = count; i >= 0; i--) {
subPath.objectIds().append(idArray[i]);
}
es = blRef->highlight(subPath);
pressEnterToContinue();
es = blRef->unhighlight(subPath);
Exploding Entities
Some entities can be exploded, or decomposed, into a set of simpler ele-
ments. The specific behavior depends on the class. For example, boxes can be
exploded into regions, then lines. Polylines can be exploded into line seg-
ments. An mtext entity can be exploded into a separate text entity for each
line of the original object. An mline entity can be exploded into individual
lines. When you explode a block reference, AutoCAD copies all entities in the
block reference and then splits them into their components.
The explode() function creates an array of objects derived from AcDbEntity.
The following table shows what happens when you explode each entity,
when it is by itself and when it is in a block insert that is nonuniformly
scaled.
Exploding entities
Nonuniform Scaling
Entity By Itself (when in a block)
Nonuniform Scaling
Entity By Itself (when in a block)
AcDbLeader Self NA
The explode() function is a read-only function that does not modify the
original entity. It returns a set of entities for the application to handle as
desired. One potential use of this function is to explode a complex entity to
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,
AcDb::kForWrite);
pBlockTable->close();
AcDbObjectId lineId;
pBlockTableRecord->appendAcDbEntity(lineId, pLine);
pBlockTableRecord->close();
pLine->close();
return lineId;
}
pBlockTableRec->appendAcDbEntity(lineId, pLine);
pLine->close();
pBlockTableRec->close();
}
When you close the block table record, the block begin and block end objects
are added to the block automatically.
The following example creates a new block table record named
ASDK-BLOCK-WITH-ATTR and adds it to the block table. Next it creates a circle
entity and adds it to the new block table record. It creates two attribute defi-
nition entities (the second is a clone of the first) and appends them to the
same block table record.
void
defineBlockWithAttributes(
AcDbObjectId& blockId, // This is a returned value.
const AcGePoint3d& basePoint,
double textHeight,
double textAngle)
{
int retCode = 0;
AcDbBlockTable *pBlockTable = NULL;
AcDbBlockTableRecord* pBlockRecord = new AcDbBlockTableRecord;
AcDbObjectId entityId;
// Step 1: Set the block name and base point of the
// block definition.
//
pBlockRecord->setName("ASDK-BLOCK-WITH-ATTR");
pBlockRecord->setOrigin(basePoint);
// Indicate the LCS 0.0 angle, not necessarily the UCS 0.0 angle.
//
pBlkRef->setRotation(0.0);
pBlkRef->setNormal(normal);
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt(blkName, pBlockTableRecord,
AcDb::kForRead);
pBlockTable->close();
AcDbBlockTableRecordIterator *pBlockIterator;
pBlockTableRecord->newIterator(pBlockIterator);
AcDbHandle objHandle;
pEntity->getAcDbHandle(objHandle);
char handleStr[20];
objHandle.getIntoAsciiBuffer(handleStr);
const char *pCname = pEntity->isA()->name();
Complex Entities
This section provides examples showing how to create and iterate through
complex entities.
location = pVertex->position();
pVertex->close();
■ Dimensions
■ Text
■ Circles
■ Arcs
■ 2D polylines
■ Block inserts
■ Points
■ Traces
■ Solids
■ Shapes
■ Attribute definitions
■ Attributes
AcDb2dPolylineVertex
An AcDb2dPolyline has as an elevation and a series of X,Y points of class
AcDb2dPolylineVertex. The position() and setPosition() functions of
AcDb2dPolylineVertex specify 3D locations in the ECS. The Z coordinate
passed in to the setPosition() function is stored in the entity and is
returned by the position() function, but is otherwise ignored. It does not
affect the polyline’s elevation.
The AcDb2dPolyline class provides the vertexPosition() function, which
returns a World Coordinate System value for the vertex passed in. The only
way to change the elevation of a polyline is using the
AcDb2dPolyline::setElevation() function.
Curve Functions
The abstract base class AcDbCurve provides a number of functions for operat-
ing on curves, including functions for projecting, extending, and offsetting
curves, as well as a set of functions for querying curve parameters. Curves can
be defined either in parameter space or in Cartesian coordinate space. A 3D
curve is a function of one parameter (f(t)), while a 3D surface is a function of
two parameters (f(u,v)). Conversion functions allow you to convert data from
its parameter representation to points in the Cartesian coordinate system.
Splines, for example, are best represented in parameter space. To split a spline
into three equal parts, you first find the parameters that correspond to the
points of the spline and then operate on the spline in parameter space.
AcDbObjectId newCurveId;
addToModelSpace(newCurveId, pProjectedCurve);
}
AcDbObjectId newCurveId;
addToModelSpace(newCurveId, (AcDbEntity*)curves[0]);
}
AcDbHyperlink Class
An AcDbHyperlink object contains the hyperlink name (for example,
http://www.autodesk.com), a sublocation within that link, the hyperlink
description or friendly name (“Click here for Autodesk”), and a display string
for the hyperlink. For AutoCAD, a sublocation is a named view, while in a
spreadsheet application, for example, a sublocation might be a cell or group
of cells. The display string is usually the same as the hyperlink’s description.
If the description is null, the hyperlink’s name and sublocation are used
instead, in “name – sublocation” format.
Hyperlinks may also have nesting levels. Nesting level is only of interest
when dealing with hyperlink collections associated with an entity within a
block, or with collections associated with an INSERT entity.
AcDbHyperlinkCollection Class
This class is a collection of AcDbHyperlink objects, and has a variety of meth-
ods for adding and removing those objects. The AcDbHyperlinkCollection
deletes its contents when they are removed, and when the collection object
itself is deleted. Hyperlinks in the collection are numbered from zero.
AcDbEntityHyperlinkPE Class
The methods of the AcDbEntityHyperlinkPE class allow you to set, get, and
count the hyperlinks associated with an entity.
In This Chapter
7
This chapter describes the container objects used in ■ Comparison of Symbol Tables and
Dictionaries
AutoCAD database operations: symbol tables,
■ Symbol Tables
“Database Objects.”
143
Comparison of Symbol Tables and
Dictionaries
Symbol tables and dictionaries perform essentially the same function; they
contain entries that are database objects that can be looked up using a text
string key. You can add entries to these container objects, and you can use an
iterator to step through the entries and query their contents.
The AutoCAD database always contains a fixed set of nine symbol tables,
described in the following section. You cannot create or delete a symbol
table, but you can add or change the entries in a symbol table, which are
called records. Each symbol table contains only a particular type of object.
For example, the AcDbLayerTable contains only objects of type
AcDbLayerTableRecord. Symbol tables are defined in this manner mainly for
compatibility with AutoCAD Release 12 and previous releases of AutoCAD.
Dictionaries provide a similar mechanism for storing and retrieving objects
with associated name keys. The AutoCAD database creates the named object
dictionary whenever it creates a new drawing. The named object dictionary
can be viewed as the master “table of contents” for the nonentity object
structures in a drawing. This dictionary, by default, contains four
dictionaries: the GROUP dictionary, the MLINE style dictionary, the layout dic-
tionary, and the plot style name dictionary. You can create any number of
additional objects and add them to the named object dictionary. However,
the best practice is to add one object directly to the named object dictionary
and have that object in turn own the other objects associated with your
application. Typically, the owning object is a container class such as a
dictionary. Use your assigned four-letter Registered Developer Symbol for the
name of this class.
An AcDbDictionary object can contain any type of AcDbObject, including
other dictionaries. A dictionary object does not perform type checking of
entries. However, the MLINE style dictionary should contain only instances
of class AcDbMlineStyle, and the GROUP dictionary should contain only
instances of AcDbGroup. An application may require specific typing for entries
in a dictionary that it creates and maintains.
AcDbDictionary
AcDbDictionarywithDefault
Symbol Table
Dictionary
<name> Object
<class-specific fields>
Each symbol table class provides a getAt() function for looking up the
record specified by name. The signatures for overloaded forms of the getAt()
function are as follows. (##BASE_NAME## stands for any of the nine symbol
table class types.)
Acad::ErrorStatus
AcDb##BASE_NAME##Table::getAt(const char* pEntryName,
AcDb::OpenMode mode,
AcDb##BASE_NAME##TableRecord*&
pRecord,
Adesk::Boolean openErasedRecord =
Adesk::kFalse) const;
or
Acad::ErrorStatus
AcDb##BASE_NAME##Table::add(AcDbObjectId& recordId,
AcDb##BASE_NAME##TableRecord*
pRecord);
This function adds the record pointed to by pRecord to both the database
containing the table and the table itself. If the additions succeed and the
argument pId is non-NULL, it is set to the AcDbObjectId of the record in the
database.
Layer Table
The layer table contains one layer, layer 0, by default. A user adds layers to
this table with the LAYER command.
Layer Properties
The AcDbLayerTableRecord class contains member functions for specifying a
number of layer properties that affect the display of their associated entities.
All entities must refer to a valid layer table record. The AutoCAD User’s Guide
provides a detailed description of layer properties.
The following sections list the member functions for setting and querying
layer properties.
Frozen/Thawed
When a layer is frozen, graphics are not regenerated.
void AcDbLayerTableRecord::setIsFrozen(Adesk::Boolean);
Adesk::Boolean
AcDbLayerTableRecord::isFrozen() const;
Adesk::Boolean
AcDbLayerTableRecord::isOff() const;
Viewport
This setVPDFLT() function specifies whether the layer by default is visible or
invisible in new viewports.
void AcDbLayerTableRecord::setVPDFLT(Adesk::Boolean);
Adesk::Boolean
AcDbLayerTableRecord::VPDFLT() const;
Locked/Unlocked
Entities on a locked layer cannot be modified by an AutoCAD user or opened
for the write() function within a program.
void AcDbLayerTableRecord::setIsLocked(Adesk::Boolean);
Adesk::Boolean
AcDbLayerTableRecord::isLocked() const;
Color
The color set by the setColor() function is used when an entity’s color is
BYLAYER.
void AcDbLayerTableRecord::setColor(const AcCmColor &color);
AcCmColor
AcDbLayerTableRecord::color() const;
Linetype
The linetype set by the setLinetypeObjectId() function is used when an
entity’s linetype is BYLAYER.
void AcDbLayerTableRecord::setLinetypeObjectId(AcDbObjectId);
AcDbObjectId
AcDbLayerTableRecord::linetypeObjectId() const;
AcCmColor color;
color.setColorIndex(1); // set color to red
pLayerTblRcd->setColor(color);
pLayerTblRcd->setLinetypeObjectId(ltId);
pLayerTbl->add(pLayerTblRcd);
pLayerTblRcd->close();
pLayerTbl->close();
} else {
pLayerTbl->close();
acutPrintf("\nlayer already exists");
}
}
Acad::ErrorStatus
AcDb##BASE_NAME##Table::newIterator(
AcDb##BASE_NAME##TableIterator*& pIterator,
Adesk::Boolean atBeginning = Adesk::kTrue,
Adesk::Boolean skipErased = Adesk::kTrue) const;
The newIterator() function creates an object that can be used to step
through the contents of the table and sets pIterator to point to the iterator
object. If atBeginning is kTrue, the iterator starts at the beginning of the
table; if kFalse, it starts at the end of the table. If the skipErased argument
is kTrue, the iterator is positioned initially at the first (or last) unerased
record; if kFalse, it is positioned at the first (or last) record, regardless of
whether it has been erased. For a description of the functions available for
each iterator class, see the ObjectARX Reference.
When you create a new iterator, you are also responsible for deleting it. A
symbol table should not be closed until all of the iterators it has constructed
have been deleted.
In addition to the symbol tables, the block table record has an iterator that
operates on the entities it owns. The AcDbBlockTableRecord class returns an
object of class AcDbBlockTableRecordIterator when you ask it for a new
iterator. This iterator enables you to step through the entities contained in
the block table record and to seek particular entities.
Dictionaries
To create a new dictionary, you need to create an instance of AcDbDictionary,
add it to the database, and register it with its owner object. Use the setAt()
function of AcDbDictionary to add objects to the dictionary and the data-
base. The signature of this function is
Acad::ErrorStatus
AcDbDictionary::setAt(const char* pSrchKey,
AcDbObject* pNewValue,
AcDbObjectId& retObjId);
The setAt() function adds a new entry specified by newValue to the
dictionary. If the entry already exists, it is replaced by the new value. The
name of the object is specified by srchKey. The object ID of the entry is
returned in retObjId.
When you add an entry to a dictionary, the dictionary automatically attaches
a reactor to the entry. If the object is erased, the dictionary is notified and
removes it from the dictionary.
Dictionaries | 153
Use the AcDbGroup::newIterator() function to obtain an iterator and step
through the entities in the group. The AcDbGroup class also provides
functions for appending and prepending entities to the group, inserting enti-
ties at a particular index in the group, removing entities, and transferring
entities from one position in the group to another. See AcDbGroup in the
ObjectARX Reference.
You can also assign properties to all members of a group using the
setColor(), setLayer(), setLinetype(), setVisibility(), and
setHighlight() functions of the AcDbGroup class. These operations have the
same effect as opening each entity in the group and setting its property
directly.
Groups should always be stored in the GROUP dictionary, which can be
obtained as follows:
AcDbDictionary* pGrpDict =
acdbHostApplicationServices()->working Database()->
getGroupDictionary(pGroupDict, AcDb::kForWrite);
An alternative way to obtain the GROUP dictionary is to look up
“ACAD_GROUP” in the named object dictionary.
The following functions are part of an application that first prompts the user
to select some entities that are placed into a group called
“ASDK_GROUPTEST”. Then it calls the function removeAllButLines() to
iterate over the group and remove all the entities that are not lines. Finally,
it changes the remaining entities in the group to red.
void
groups()
{
AcDbGroup *pGroup = new AcDbGroup("grouptest");
AcDbDictionary *pGroupDict;
acdbHostApplicationServices()->workingDatabase()
->getGroupDictionary(pGroupDict, AcDb::kForWrite);
AcDbObjectId groupId;
pGroupDict->setAt("ASDK_GROUPTEST", pGroup, groupId);
pGroupDict->close();
pGroup->close();
makeGroup(groupId);
removeAllButLines(groupId);
}
if (err != RTNORM) {
return;
}
AcDbGroup *pGroup;
acdbOpenObject(pGroup, groupId, AcDb::kForWrite);
Dictionaries | 155
if (!pObj->isKindOf(AcDbLine::desc())) {
// AcDbGroup::remove() requires that the object
// to be removed be closed, so close it now.
//
pObj->close();
pGroup->remove(pIter->objectId());
} else {
pObj->close();
}
}
delete pIter;
<STANDARD>
<MYSTYLE>
AcDbMline::setStyle( )
<name>
Layout Dictionary
The layout dictionary is a default dictionary within the named object dictio-
nary that contains objects of class AcDbLayout. The AcDbLayout object stores
the characteristics of a paper space layout, including the plot settings. Each
AcDbLayout object also contains an object ID of an associated block table
record, which stores the entities associated with the layout.
Entity Layout
Creating a Dictionary
The following example creates a new dictionary (ASDK_DICT) and adds it to
the named object dictionary. Then it creates two new objects of the custom
class AsdkMyClass (derived from AcDbObject) and adds them to the dictio-
nary using the setAt() function.
NOTE You need to close the objects after adding them with the setAt()
function.
Dictionaries | 157
AcDbDictionary *pDict;
if (pNamedobj->getAt("ASDK_DICT", (AcDbObject*&) pDict,
AcDb::kForWrite) == Acad::eKeyNotFound)
{
pDict = new AcDbDictionary;
AcDbObjectId DictId;
pNamedobj->setAt("ASDK_DICT", pDict, DictId);
}
pNamedobj->close();
if (pDict) {
// Create new objects to add to the new dictionary,
// add them, then close them.
//
AsdkMyClass *pObj1 = new AsdkMyClass(1);
AsdkMyClass *pObj2 = new AsdkMyClass(2);
pObj1->close();
pObj2->close();
pDict->close();
}
}
Layouts
AutoCAD initially contains three layouts: a model space layout and two
paper space layouts. These layouts can be accessed by tabs at the bottom of
the drawing window in AutoCAD. The tabs are initially named Model,
Layout1, and Layout2.
The Model tab is the default tab and represents model space, in which you
generally create your drawing. The Layout1 and Layout2 tabs represent paper
space and are generally used for laying out your drawing for printing. The
paper space layouts display a paper image that shows the printable boundary
for the configured print device.
It is recommended that you use paper space layouts for preparing final draw-
ings for output, but printing can be performed from any layout, including
the model space layout. For more information on using layouts in AutoCAD,
see the AutoCAD User’s Guide.
■ AcDbLayout
■ AcDbPlotSettings
■ AcDbPlotSettingsValidator
■ AcDbLayoutManager
■ AcApLayoutManager
■ AcDbLayoutManagerReactor
Layouts | 159
layout objects and to perform other layout-related tasks. The following sec-
tions provide an overview of some of these classes. For more information, see
the ObjectARX Reference. For an example of using the ObjectARX layout
classes, see the lmgrtest.arx sample application in the ObjectARX samples
directory.
Layout Objects
Information about layouts is stored in instances of the AcDbLayout class. A
layout object contains the printing and plotting settings information needed
to print the desired portion of the drawing. For example, a layout object con-
tains the plot device, media size, plot area, and plot rotation, as well as sev-
eral other attributes that help define the area to be printed.
AcDbLayout objects are stored in the ACAD_LAYOUT dictionary within the
named object dictionary of the database. There is one AcDbLayout object per
paper space layout, as well as a single AcDbLayout for model space. Each
AcDbLayout object contains the object ID of its associated
AcDbBlockTableRecord. This makes it easy to find the block table record in
which the layout’s actual geometry resides. If an AcDbBlockTableRecord rep-
resents a layout, then it contains the object ID of its associated AcDbLayout
object.
Most of the plot information for layout objects is stored in
AcDbPlotSettings, the base class of AcDbLayout. You can create named plot
settings and use them to initialize other AcDbLayout objects. This allows you
to export and import plot settings from one layout to another. These named
plot settings are stored in instances of the AcDbPlotSettings class. There is
one AcDbPlotSettings object for each named plot setting and they are stored
in the ACAD_PLOTSETTINGS dictionary within the named object
dictionary.
■ Create layouts
■ Delete layouts
■ Rename layouts
■ Copy and clone layouts
There is one instance of a layout manager per application. The layout man-
ager always operates on the current layout.
Xrecords
Xrecords enable you to add arbitrary, application-specific data. Because they
are an alternative to defining your own object class, they are especially useful
to AutoLISP programmers. An xrecord is an instance of class AcDbxrecord,
which is a subclass of AcDbObject. Xrecord state is defined as the contents of
a resbuf chain, which is a list of data groups, each of which in turn contains
a DXF group code plus associated data. The value of the group code defines
the associated data type. Group codes for xrecords are in the range of
1 through 369. The following section describes the available DXF group
codes.
There is no inherent size limit to the amount of data you can store in an
xrecord. Xrecords can be owned by any other object, including the extension
dictionary of any object, the named object dictionary, any other dictionary,
or other xrecords.
No notification is sent when an xrecord is modified. If an application needs
to know when an object owning an xrecord has been modified, the applica-
tion will need to send its own notification.
The AcDbXrecord class provides two member functions for setting and
obtaining resbuf chains, the setfromRbChain() and rbChain() functions:
Acad::ErrorStatus
AcDbXrecord::setFromRbChain(
resbuf& pRb,
AcDbDatabase* auxDb=NULL);
Acad::ErrorStatus
AcDbXrecord::rbChain(
resbuf** ppRb,
AcDbDatabase* auxDb=NULL) const;
The AcDbXrecord::setFromRbChain() function replaces the existing resbuf
chain with the chain passed in.
Xrecords | 161
DXF Group Codes for Xrecords
The following table lists the DXF group codes that can be used in xrecords.
1 4 Text
6 9 Text
38 59 Real
60 79 16-bit integer
90 99 32-bit integer
For a description of hard and soft owners and pointers, see chapter 12,
“Deriving from AcDbObject.”
Xrecords | 163
pXrec->setFromRbChain(*pHead);
acutRelRb(pHead);
pXrec->close();
}
printList(pRbList);
acutRelRb(pRbList);
}
165
166
MFC Topics
In This Chapter
8
The Microsoft Foundation Class (MFC) library allows ■ Introduction
■ Using MFC with ObjectARX
a developer to implement standard user interfaces
Applications
ObjectARX application.
167
Introduction
ObjectARX applications can be created to take advantage of the Microsoft
Foundation Class (MFC) library. This chapter discusses how to build your
ObjectARX applications to make use of MFC and how the AutoCAD built-in
MFC system can be used to create dialogs that behave and operate like
AutoCAD.
For complete information about MFC, see the Microsoft online help and
technical notes. In particular, see notes 11 and 33 for information about
using MFC as part of a DLL, which is an important concept for ObjectARX.
CAcExtensionModule Class
The ObjectARX SDK provides two simple C++ classes that can be used to
make resource management easier. The CAcExtensionModule class serves two
purposes—it provides a placeholder for an AFX_EXTENSION_MODULE structure
(normally used to initialize or terminate an MFC extension DLL) and tracks
two resource providers for the DLL. The resource providers are the module’s
resources (which are normally the DLL itself, but may be set to some other
module) and the default resources (normally the host application, but are
actually the provider currently active when AttachInstance() is called).
CAcExtensionModule tracks these to simplify switching MFC resource lookup
between the default and the module’s. A DLL should create one instance of
this class and provide the implementation for the class.
AdUi and AcUi provide classes that implement the following features:
■ Dialog resizing
■ Dialog data persistency
■ Tabbed dialogs
Class Hierarchy
The following are the supported classes in the AdUi and AcUi libraries. There
are classes present in the header files that are not shown and are for internal
use only and are not supported for use with ObjectARX.
CAdUiTipWindow Class
CAdUiTipWindow is the basic AdUi tip window class. These objects handle
generic tip display and know when to automatically hide themselves (such
as detecting cursor movement, a brief time-out, or keyboard activity).
CAdUiTextTip Class
CAdUiTextTip specializes CAdUiTipWindow to display a TextTip.
CAdUiDrawTipText Class
CAdUiDrawTipText is used internally by the AdUi messaging system to inform
a control that a tip window needs repainting. The control has the option of
changing attributes of the tip window’s device context and drawing the text.
CAdUiBaseDialog Class
CAdUiBaseDialog provides basic support for tip windows (ToolTips and
TextTips) and the AdUi message handling system. It also supports context
CAdUiDialog Class
CAdUiDialog is a general purpose class that provides a set of member func-
tions allowing for resizable dialogs and data persistency.
CAdUiFileDialog
CAdUiFileDialog specializes CFileDialog much the same way as
CAdUiBaseDialog specializes CDialog. The class provides basic support for tip
windows (ToolTips and TextTips), context help and AdUi message handling
in a common file dialog. Unlike CAdUiBaseDialog, there is no built-in sup-
port for position and size persistency.
CAdUiTabMainDialog Class
CAdUiTabMainDialog represents the main container dialog in a tabbed dialog.
CAdUiTabMainDialog and CAdUiTabMainDialog are used in place of
CPropertySheet and CPropertyPage to construct tabbed dialogs.
CAdUiTabChildDialog Class
CAdUiTabChildDialog represents a tab in a tabbed dialog.
CAdUiTabMainDialog and CAdUiTabChildDialog are used in place of
CPropertySheet and CPropertyPage to construct tabbed dialogs. Each tab in
a tabbed dialog is a CAdUiTabChildDialog.
CAcUiDialog Class
CAcUiDialog is a general-purpose class that provides a set of member func-
tions allowing for resizable dialogs and data persistency in AutoCAD.
CAcUiTabMainDialog Class
CAcUiTabMainDialog represents the main container dialog in an AutoCAD
tabbed dialog. CAcUiTabMainDialog and CAcUiTabMainDialog are used in
place of CPropertySheet and CPropertyPage to construct tabbed dialogs in
AutoCAD.
CAcUiTabChildDialog Class
CAcUiTabChildDialog represents a tab in a tabbed dialog.
CAcUiTabMainDialog and CAcUiTabChildDialog are used in place of
CAcUiAlertDialog Class
CAdUiAlertDialog represents an alert dialog with three buttons. One button
is the CANCEL button and the other two button labels are set by the pro-
grammer. It is a general-purpose alert dialog.
CAcUiFileDialog Class
CAcUiFileDialog provides an AutoCAD-specific derivation of
CAdUiFileDialog.
CAdUiTabExtensionManager Class
CAdUiDialogManager is a class that manages adding and removing tabs from
a tabbed dialog that is extensible. If a dialog is tab extensible, an instance of
this class is found in the CAdUiTabMainDialog.
CAdUiTab Class
CAdUiTab encapsulates the MFC CTabCtrl and adds functionality to it. One
of these objects is found in the main dialog object.
CAdUiDockControlBar Class
The CAdUiDockControlBar class, part of a docking system, adds extended
capabilities to the MFC CControlBar class. The main feature provided is the
resizing of the control bars when docked. More than one control bar can be
docked together, each of them being able to be resized individually using
splitters created by the docking system. CAdUiDockControlBar also comes
with a gripper bar and a close button when docked. Control bars’ state can
be switched from docked to undocked or vice versa, by double-clicking on
the gripper when docked, or the title bar when undocked, or by dragging
them with the mouse. The docking system handles the persistency of the
control bars, preserving their position and state across sessions. Finally,
CAdUiDockControlBar provides a default context menu to control the bar
behavior, with a possibility for the developer to customize this menu.
CAdUiEdit Class
CAdUiEdit is derived from the CEdit class to provide edit box controls. This
class provides support for tip windows for truncated text items (TextTips).
This class takes bit flags to add desired validation behavior, based on the
following types of input: Numeric, String, Angular, and Symbol names. Gen-
erally you should use one of the classes derived from the AutoCAD-specific
class CAcUiComboBox, which adds a specific data type validation and persis-
tency to the control. These are CAcUiStringEdit, CAcUiSymbolEdit,
CAcUiNumericEdit, and CAcUiAngleEdit.
CAcUiEdit Class
CAcUiEdit provides an AutoCAD-specific derivation of CAdUiEdit.
CAcUiAngleEdit Class
CAcUiAngleEdit is derived from CAcUiEdit and provides a specialized con-
structor to ensure that the AC_ES_ANGLE style bit is always set in the style
mask. Objects of this class are intended for use in editing angular/rotational
data specific to AutoCAD settings.
CAcUiNumericEdit Class
CAcUiNumericEdit is derived from CAcUiEdit and provides a specialized con-
structor to ensure that the AC_ES_NUMERIC style bit is always set in the style
mask. Objects of this class are intended for use in editing numeric data (such
as distance) specific to AutoCAD settings.
CAcUiStringEdit Class
CAcUiStringEdit is derived from CAcUiEdit and provides a specialized con-
structor to ensure that the AC_ES_STRING style bit is always set in the style
mask. Any input is acceptable.
CAdUiListBox Class
CAdUiListBox specializes the MFC CListBox to provide a control that sup-
ports AdUi messaging. The class can be used anywhere a CListBox can be
used. Since it provides the additional container-side support for AdUi regis-
tered messages, it is convenient to use CAdUiBaseDialog (or a derived class)
with the CAdUiListBox (or a derived class) controls.
CAdUiListBox provides features that allow the class to be used to subclass a
list box included in a combo box. When used in concert with a
CAdUiComboBox, the list box is able to track the combo box and, in the case of
an owner-draw control, either delegate drawing to the combo box or provide
its own drawing routines.
CAdUiListCtrl Class
CAdUiListCtrl is derived from CListCtrl class to provide list controls. This
class provides support for tip windows for truncated text items (TextTips).
TextTips will appear for truncated header items for list controls in a report
view, and for individual truncated text items in columns in the body of a list
control. Owner-drawn controls are supported.
CAdUiHeaderCtrl
CAdUiHeaderCtrl specializes CHeaderCtrl. Most often, CAdUiHeaderCtrl rep-
resents the subclassed header contained in a list control (CAdUiListCtrl).
You do not need to subclass the header control to get TextTip support for col-
umn headers in a list control (provided automatically in CAdUiListCtrl).
CAdUiComboBox Class
CAdUiComboBox is derived from the CComboBox class to provide combo box
controls. This class provides support for tip windows for truncated text items
(TextTips), and data validation in the edit control. This class takes bit flags to
add desired validation behavior, based on the following types of input:
numeric, string, angular, and symbol names. Generally, you should use one
of the classes derived from the AutoCAD-specific class CAcUiComboBox, which
CAcUiAngleComboBox Class
The CAcUiAngleComboBox constructor automatically creates a
CAcUiAngleEdit to subclass the control’s edit box. This allows for validation
of angles specific to AutoCAD settings.
CAcUiNumericComboBox Class
The CAcUiAngleComboBox constructor automatically creates a
CAcUiNumericEdit to subclass the control’s edit box. This allows for valida-
tion of numbers specific to AutoCAD settings.
CAcUiStringComboBox Class
The CAcUiStringComboBox constructor automatically creates a
CAcUiStringEdit to subclass the control’s edit box. Any input is acceptable.
CAcUiSymbolComboBox Class
The CAcUiSymbolComboBox constructor automatically creates a
CAcUiSymbolEdit to subclass the control’s edit box. Valid AutoCAD symbol
names are acceptable input.
CAcUiMRUComboBox Class
CAcUiMRUComboBox inherits CAcUiComboBox and serves as the base class for
owner-draw combo boxes that implement an MRU list. Each item in the list
can contain a small image followed by some text. Each item also tracks a
CAcUiArrowHeadComboBox Class
CAcUiArrowHeadComboBox specializes CAcUiMRUComboBox for dimensioning
arrowhead selection. The control displays bitmaps representing the standard
AutoCAD dimensioning arrowhead styles, which are always present in the
list. By default no optional or additional items are present or added. The
cargo associated with each item is the AutoCAD index for the associated
stock arrowhead. When MRU items are added to the list, they are automati-
cally assigned a unique cargo value (which will be greater than the AutoCAD
index for a user-defined arrowhead style).
CAcUiColorComboBox Class
CAcUiColorComboBox specializes CAcUiMRUComboBox for color selection. The
control displays color swatches representing selections from AutoCAD’s pal-
ette. The stock items always present in the control reflect color numbers 1
through 7. Both optional items are used; Option1 displays “ByLayer” and
Option2 displays “ByBlock”. MRU items display “Color nnn,” where nnn is
the associated color number. The cargo associated with each item indicates
an AutoCAD color number (such as 1 to 255), “ByBlock” relates to 0, and
“ByLayer” corresponds to 256. The Other1 item is enabled and triggers the
AutoCAD Color Selection dialog. If Other2 is enabled it displays as
“Windows...” and by default triggers the Windows Color Selection Common
dialog. If the user selects an item from either of these dialogs the selection
appears in the MRU list and becomes the current item in the control.
CAcUiLineWeightComboBox Class
CAcUiLineWeightComboBox specializes CAcUiMRUComboBox for lineweight
selection. The control displays a small preview of the lineweights AutoCAD
supports, ranging from 0.05mm to 2.11mm, and includes “None” and
optionally “Default”. Both metric and imperial values are displayed, depend-
ing on the setting of the LWUNITS system variable. Both optional items are
used; Option1 displays “ByLayer” and Option2 displays “ByBlock”. Each
item maintains cargo that corresponds to the item’s AcDb::kLnWtxxx value.
CAcUiPlotStyleNamesComboBox Class
CAcUiPlotStyleNamesComboBox specializes CAcUiMRUComboBox for plot style
name selection. The MRU functionality of the combo is not used, and
“ByLayer”, “ByBlock”, and “Other...” items can be conditionally displayed. If
present, the “Other...” item can trigger either the Assign Plot Style dialog or
the Set Current Plot Style dialog.
CAcUiMRUListBox Class
CAcUiMRUListBox derives from CAcUiListBox. It is used by CAcUiMRUComboBox
to subclass the control’s list box (ComboLBox) and provide DrawTip support.
Advanced applications that use specialized MRU combo boxes may need to
derive special MRU list boxes to display DrawTips correctly.
CAdUiOwnerDrawButton Class
This class provides a basic owner-draw button. The class can be used any-
where a CButton can be used. When used in an AdUi-derived dialog (or a class
that supports AdUi messaging) CAdUiOwnerDrawButton automatically pro-
vides for the display of an AdUi tip window. The class also supports
drag and drop, Static and Tool Display, and PointedAt effects. In Tool Display
mode, the button appears flat and pops up when pointed at (such as when
the mouse moves over the button). Clicking the button makes it push down.
In Static Display mode, the button appears flat and behaves more like a static
control than a push button. The combination of enabling drag and drop and
Static Display is appropriate for creating sites that receive files via
drag and drop.
CAdUiBitmapButton Class
This class specializes CAdUiOwnerDrawButton to provide a button that dis-
plays a bitmap (the image is drawn transparently in the button). By default,
objects of this class automatically resize to fit the associated bitmap image.
CAdUiBitmapStatic Class
CAdUiBitmapStatic specializes CAdUiBitmapButton to provide a button that
enables Static Display by default. These controls act more like statics than
pushbuttons.
CAdUiDropSite Class
CAdUiDropSite specializes CAdUiBitmapStatic to provide a button that
enables drag and drop as well as Static Display. These controls can receive
files via drag and drop.
CAdUiToolButton Class
CAdUiToolButton specializes CAdUiBitmapButton to provide a button that
enables Tool Display by default. These controls appear more like toolbar but-
tons than regular pushbuttons.
CAcUiPickButton Class
CAcUiPickButton specializes CAcUiBitmapButton, which is a wrapper for the
class CAdUiBitmapButton. CAcUiPickButton provides a button that displays a
standard pick button bitmap.
CAcUiSelectButton Class
CAcUiSelectButton specializes CAcUiPickButton. It provides a button that
displays a standard selection button bitmap.
theArxDLL.DetachInstance();
}
// Entry point
//
extern "C" AcRx::AppRetCode acrxEntryPoint(
AcRx::AppMsgCode msg, void* appId)
{
switch( msg )
{
case AcRx::kInitAppMsg:
acrxDynamicLinker->unlockApplication(appId);
acrxDynamicLinker->registerAppMDIAware(appId);
initApp();
break;
case AcRx::kUnloadAppMsg:
unloadApp();
break;
case AcRx::kInitDialogMsg:
break;
default:
break;
}
return AcRx::kRetOK;
}
You will also need to add the ObjectARX libraries to the project file, change
the .dll extension to .arx, and modify the .def file with the proper exports.
Then you should be able to compile and load the application.
IDC_BUTTON_POINT
IDC_EDIT_XPT
IDC_EDIT_YPT
IDC_EDIT_ZPT
IDC_BUTTON_ANGLE
IDC_EDIT_ANGLE
IDC_COMBO_REGAPPS
IDC_LIST_BLOCKS
3 Make sure the resource IDs match this diagram or the remaining code will
not work.
7 Now open the AsdkAcUiDialogSample.h header file and change the derivation
of the new dialog class. It should be derived from CAcUiDialog:
class AsdkAcUiDialogSample : public CAcUiDialog
bool AsdkAcUiDialogSample::ValidatePoint()
{
if (!m_ctrlXPtEdit.Validate())
return false;
if (!m_ctrlYPtEdit.Validate())
return false;
if (!m_ctrlZPtEdit.Validate())
return false;
return true;
}
bool AsdkAcUiDialogSample::ValidateAngle()
{
if (!m_ctrlAngleEdit.Validate())
return false;
return true;
}
2 Now add some utility functions to iterate over two symbol tables and display
the names in the two different list boxes:
void AsdkAcUiDialogSample::DisplayBlocks()
{
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
void AsdkAcUiDialogSample::DisplayRegApps()
{
AcDbRegAppTable *pRegAppTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pRegAppTable, AcDb::kForRead);
// Get a point
//
void AsdkAcUiDialogSample::OnButtonAngle()
{
// Hide the dialog and give control to the editor
//
BeginEditorCommand();
double angle;
void AsdkAcUiDialogSample::OnKillfocusEditXpt()
{
// Get and update text the user typed in.
//
m_ctrlXPtEdit.Convert();
m_ctrlXPtEdit.GetWindowText(m_strXPt);
}
void AsdkAcUiDialogSample::OnKillfocusEditYpt()
{
// Get and update text the user typed in.
//
m_ctrlYPtEdit.Convert();
m_ctrlYPtEdit.GetWindowText(m_strYPt);
}
void AsdkAcUiDialogSample::OnKillfocusEditZpt()
{
// Get and update text the user typed in.
//
m_ctrlZPtEdit.Convert();
m_ctrlZPtEdit.GetWindowText(m_strZPt);
}
6 The combo box handler allows the user to type in a string and then register
this as an application name. This doesn’t really make sense for an applica-
tion, but it shows the use of a combo box:
void AsdkAcUiDialogSample::OnKillfocusComboRegapps()
{
CString strFromEdit;
m_ctrlRegAppComboBox.GetWindowText(strFromEdit);
if (m_ctrlRegAppComboBox.FindString(-1, strFromEdit) == CB_ERR)
if (acdbRegApp(strFromEdit) == RTNORM)
m_ctrlRegAppComboBox.AddString(strFromEdit);
}
if (!ValidateAngle()) {
AfxMessageBox("Sorry, Angle out of desired range.”);
m_ctrlAngleEdit.SetFocus();
return;
}
DLGCTLINFOdlgSizeInfo[]= {
{ IDC_STATIC_GROUP1, ELASTICX, 20 },
{ IDC_STATIC_GROUP1, ELASTICY, 100 },
{ IDC_EDIT_XPT,ELASTICX, 20 },
{ IDC_EDIT_YPT,ELASTICX, 20 },
{ IDC_EDIT_ZPT,ELASTICX, 20 },
{ IDC_EDIT_ANGLE, ELASTICX, 20 },
{ IDC_STATIC_GROUP2, MOVEX, 20 },
{ IDC_STATIC_GROUP2, ELASTICY, 100 },
{ IDC_STATIC_GROUP2, ELASTICX, 80 },
{ IDC_LIST_BLOCKS, MOVEX, 20 },
{ IDC_LIST_BLOCKS, ELASTICY, 100 },
{ IDC_STATIC_TEXT2,MOVEX, 20 },
{ IDC_STATIC_TEXT2,MOVEY, 100 },
{ IDC_LIST_BLOCKS, ELASTICX, 80 },
{ IDC_STATIC_TEXT2,ELASTICX, 80 },
DisplayPoint();
DisplayAngle();
DisplayBlocks();
DisplayRegApps();
return TRUE; // return TRUE unless you set the focus to a control
}
In This Chapter
9
The global functions described in this chapter handle ■ Selection Set and Entity Names
■ Handling Selection Sets
selection sets, drawing entities, and symbol tables. See
■ Entity Name and Data Functions
the AutoCAD Customization Guide for background ■ Symbol Table Access
199
Selection Set and Entity Names
Most of the ObjectARX functions that handle selection sets and entities iden-
tify a set or entity by its name, which is a pair of longs assigned and main-
tained by AutoCAD. In ObjectARX, names of selection sets and entities have
the corresponding type ads_name.
Before it can manipulate a selection set or an entity, an ObjectARX applica-
tion must obtain the current name of the set or entity by calling one of the
library functions that returns a selection set or entity name.
NOTE Selection set and entity names are volatile; they apply only while you
are working on a drawing with AutoCAD, and they are lost when exiting from
AutoCAD or switching to another drawing.
For selection sets, which also apply only to the current session, the volatility
of names poses no problem, but for entities, which are saved in the drawing
database, it does. An application that must refer at different times to the same
entities in the same drawing (or drawings), can use entity handles, described
in “Entity Handles and Their Uses” on page 216.
int
acedSSGet (
const char *str,
const void *pt1,
const void *pt2,
const struct resbuf *entmask,
ads_name ss);
:$ Prompts supplied
. User pick
:? Other callbacks
A All
B Box
C Crossing
CP Crossing Polygon
:D Duplicates OK
:E Everything in aperture
F Fence
G Groups
I Implied
:K Keyword callbacks
L Last
M Multiple
P Previous
W Window
WP Window Polygon
NOTE AutoCAD cannot have more than 128 selection sets open at once. This
limit includes the selection sets open in all concurrently running ObjectARX
and AutoLISP applications. The limit may be different on your system. If the limit
is reached, AutoCAD refuses to create more selection sets. Simultaneously
managing a large number of selection sets is not recommended. Instead, keep
a reasonable number of sets open at any given time, and call acedSSFree() to
free unused selection sets as soon as possible. Unlike AutoLISP, the ObjectARX
environment has no automatic garbage collection to free selection sets after they
have been used. An application should always free its open selection sets when
it receives a kUnloadDwgMsg, kEndMsg, or kQuitMsg message.
NOTE If only filtering is specified (“X”) but the entmask argument is NULL,
acedSSGet() selects all entities in the database.
NOTE The resval specified in each buffer must be of the appropriate type.
For example, name types are strings (resval.rstring); elevation and thickness
are double-precision floating-point values (resval.rreal); color, attributes-
follow, and flag values are short integers (resval.rint); extrusion vectors are
three-dimensional points (resval.rpoint); and so forth.
// Select all the entities within the window that are also
// on the layer FLOOR9.
acedSSGet("W", pt1, pt2, &eb1, ssname1);
NOTE The meaning of certain group codes can differ from entity to entity, and
not all group codes are present in all entities. If a particular group code is speci-
fied in a filter, entities that do not contain that group code are excluded from
the selection sets that acedSSGet() returns.
Relational Tests
Unless you specify otherwise, there is an implied “equals” test between the
entity and each item in the filter list. For numeric groups (integers, real
values, points, and vectors), you can specify other relations by including rela-
tional operators in the filter list. Relational operators are passed as a special
-4 group, whose value is a string that indicates the test to be applied to the
next group in the filter list.
The following sample code selects all circles whose radii are greater than or
equal to 2.0:
eb3.restype = 40; // Radius
eb3.resval.rreal = 2.0;
eb3.rbnext = NULL;
Conditional Filtering
The relational operators just described are binary operators. You can also test
groups by creating nested Boolean expressions that use conditional opera-
tors. The conditional operators are also specified by -4 groups, but they must
be paired.
The following sample code selects all circles in the drawing with a radius of
1.0 and all lines on the layer “ABC”.
eb1 = acutBuildList(-4, "<or",-4, "<and", RTDXF0,
"CIRCLE", 40, 1.0, -4, "and>", -4, "<and", RTDXF0,
"LINE", 8, "ABC", -4, "and>", -4, "or>", 0);
NOTE Conditional expressions that test for extended data using the -3 group
can contain only -3 groups. See “Filtering for Extended Data” on page 206.
To select all circles that have extended data registered to either “APP1” or
“APP2” but not both, you could use the following code.
eb1 = acutBuildList(-4, "<xor", -3, "APP1", -3, "APP2",
-4, "xor>", 0);
NOTE The acedSSAdd() function can also be used to create a new selection
set, as shown in the following example. As with acedSSGet(), acedSSAdd()
creates a new selection set only if it returns RTNORM.
The following sample code fragment creates a selection set that includes the
first and last entities in the current drawing.
ads_name fname, lname; // Entity names
ads_name ourset; // Selection set name
NOTE Because selection sets can be quite large, the len argument returned
by acedSSLength() must be declared as a long integer. The i argument used
as an index in calls to acedSSName() must also be a long integer. (In this con-
text, standard C compilers will correctly convert a plain integer.)
Applying this matrix scales the entities by one-half (which moves them
toward the origin) and translates their location by (20.0,5.0).
int rc, i, j;
ads_point pt1, pt2;
ads_matrix matrix;
ads_name ssname;
rc = acedXformSS(ssname, matrix);
}
When you invoke acedDragGen(), you must specify a similar function to let
users interactively control the effect of the transformation. The function’s
declaration must have the following form:
int scnf(ads_point pt, ads_matrix mt)
It should return RTNORM if it modified the matrix, RTNONE if it did not, or
RTERROR if it detects an error.
The acedDragGen() function calls the scnf function every time the user
moves the cursor. The scnf() function sets the new value of the matrix mt.
When scnf() returns with a status of RTNORM, acedDragGen() applies the new
matrix to the selection set. If there is no need to modify the matrix (for exam-
ple, if scnf() simply displays transient vectors with acedGrVecs()), scnf()
should return RTNONE. In this case, acedDragGen() ignores mt and doesn’t
transform the selection set.
In the following example, the function sets the matrix to simply move (trans-
late) the selection set without scaling or rotation.
int dragsample(usrpt, matrix)
ads_point usrpt
ads_matrix matrix;
{
ident_init(matrix); // Initialize to identity.
// Initialize translation vector.
matrix[0][T] = usrpt[X];
matrix[1][T] = usrpt[Y];
matrix[2][T] = usrpt[Z];
The acdbEntLast() function retrieves the name of the last entity in the
database. The last entity is the most recently created main entity, so
acdbEntLast() can be called to obtain the name of an entity that has just
been created by means of a call to acedCommand(), acedCmd(), or
acdbEntMake().
strcpy(handle, "5a2");
NOTE Extended data can include entity handles to save relational structures
in a drawing. In some circumstances, these handles require translation or main-
tenance. See “Using Handles in Extended Data” on page 240.
Coordinate Transformation
The first of the additional arguments returned by acedNEntSelP() is a 4x4
transformation matrix of type ads_matrix. This matrix is known as the
Model to World Transformation Matrix. It enables the application to trans-
form points in the entity’s definition data (and extended data, if that is
present) from the entity’s model coordinate system (MCS) into the World
Coordinate System (WCS). The MCS applies only to nested entities. The ori-
gin of the MCS is the insert point of the block, and its orientation is that of
the UCS that was in effect when the block was created.
NOTE To transform a vector rather than a point, do not add the translation
vector [M03 M13 M23] (from the fourth column of the transformation matrix).
The following sample code defines a function, mcs2wcs(), that performs the
transformations described by the preceding equations. It takes the transfor-
mation matrix returned by acedNEntSelP() and a single point (presumably
from the definition data of a nested entity), and returns the translated point.
If the third argument to mcs2wcs(), is_pt, is set to 0 (FALSE), the last column
of the transformation matrix—the translation vector or displacement—is not
added to the result. This enables the function to translate a vector as well as
a point.
void mcs2wcs(xform, entpt, is_pt, worldpt)
ads_matrix xform;
ads_point entpt, worldpt;
int is_pt;
ads_point ownpoint;
M M M
00 10 20
M M M
01 11 21
X' Y' Z' 1.0 = X Y Z 1.0
M M M
02 12 22
M M M
03 13 23
Although the matrix format is different, the formulas are equivalent to those
for the ads_matrix type, and the only change required to adapt mcs2wcs()
for use with acedNEntSel() is to declare the matrix argument as an array of
four points.
void mcs2wcs(xform, entpt, is_pt, worldpt);
ads_point xform[4]; // 4x3 version
ads_point entpt, worldpt;
int is_pt;
The identity form of the 4x3 matrix is as follows:
100
010
001
000
refstkres
RTENAME RTENAME
ename1 ename2
outermost (inserted)
block that contains
the selected entity
containent[0] = containers->resval.rlname[0];
containent[1] = containers->resval.rlname[1];
rb = containers;
while (rb != NULL) {
prevrb = rb;
rb = containers->rbnext;
}
// The result buffer pointed to by prevrb now contains the
// name of the outermost block.
In the following example, the current coordinate system is the WCS. Using
AutoCAD, create a block named SQUARE consisting of four lines.
Command: line
From point: 1,1
To point: 3,1
To point: 3,3
To point: 1,3
To point: c
Command: block
Block name (or ?): square
Insertion base point: 2,2
Select objects: Select the four lines you just drew
Select objects: ENTER
Then insert the block in a UCS rotated 45 degrees about the Z axis.
Command: ucs
Origin/ZAxis/3point/Entity/View/X/Y/Z/Prev/Restore/Save/Del/?/
<World>: z
Rotation angle about Z axis <0>: 45
Command: insert
Block name (or ?): square
Insertion point: 7,0
X scale factor <1> / Corner / XYZ: ENTER
Y scale factor (default=X): ENTER
Rotation angle: ENTER
acdbEntLast(ent1);
ebuf = acdbEntGet(ent1);
eb = ebuf;
int printdxf(eb)
struct resbuf *eb;
{
int rt;
if (eb == NULL)
return RTNONE;
switch (rt) {
case RTSHORT:
acutPrintf("(%d . %d)\n", eb->restype,
eb->resval.rint);
break;
case RTREAL:
acutPrintf("(%d . %0.3f)\n", eb->restype,
eb->resval.rreal);
break;
case RTSTR:
acutPrintf("(%d . \"%s\")\n", eb->restype,
eb->resval.rstring);
break;
case RT3DPOINT:
acutPrintf("(%d . %0.3f %0.3f %0.3f)\n",
eb->restype,
eb->resval.rpoint[X], eb->resval.rpoint[Y],
eb->resval.rpoint[Z]);
break;
case RTNONE:
acutPrintf("(%d . Unknown type)\n", eb->restype);
break;
case -1:
case -2:
// First block entity
acutPrintf("(%d . <Entity name: %8lx>)\n",
eb->restype, eb->resval.rlname[0]);
}
return eb->restype;
}
In the next example, the following (default) conditions apply to the current
drawing.
Then a call to getlast() would print the following (the name value will
vary).
Results from acdbEntGet() of last entity:
(-1 . <Entity name: 60000014>)
(0 . "LINE")
(8 . "0")
(10 1.0 2.0 0.0)
(11 6.0 6.0 0.0)
(210 0.0 0.0 1.0)
NOTE The printdxf() function prints the output in the format of an AutoLISP
association list, but the items are stored in a linked list of result buffers.
The result buffer at the start of the list (with a -1 sentinel code) contains the
name of the entity that this list represents. The acdbEntMod() function uses
it to identify the entity to be modified.
The codes for the components of the entity (stored in the restype field) are
those used by DXF. As with DXF, the entity header items are returned only if
they have values other than the default. Unlike DXF, optional entity defini-
tion fields are returned regardless of whether they equal their defaults. This
simplifies processing; an application can always assume that these fields are
present. Also unlike DXF, associated X, Y, and Z coordinates are returned as
a single point variable (resval.rpoint), not as separate X (10), Y (20), and Z
(30) groups. The restype value contains the group number of the X coordi-
nate (in the range 10–19).
return rchain;
}
If the DXF group code specified by the gcode argument is not present in the
list (or if gcode is not a valid DXF group), entitem() “falls off the end” and
returns NULL. Note that entitem() is equivalent to the AutoLISP function
(assoc).
The acdbEntMod() function modifies an entity. It passes a list that has the
same format as a list returned by acdbEntGet(), but with some of the entity
group values (presumably) modified by the application. This function
complements acdbEntGet(); the primary means by which an ObjectARX
application updates the database is by retrieving an entity with
acdbEntGet(), modifying its entity list, and then passing the list back to the
database with acdbEntMod().
The following code fragment retrieves the definition data of the first entity
in the drawing, and changes its layer property to MYLAYER.
ads_name en;
struct resbuf *ed, *cb;
char *nl = "MYLAYER";
strcpy(cb->resval.rstring, nl);
if (acdbEntMod(ed) != RTNORM) {
acutRelRb(ed);
return BAD; // Error
}
break; // From the for loop
}
acutRelRb(ed); // Release result buffer.
Memory management is the responsibility of an ObjectARX application.
Code in the example ensures that the string buffer is the correct size, and it
releases the result buffer returned by acdbEntGet() (and passed to
acdbEntMod()) once the operation is completed, whether or not the call to
acdbEntMod() succeeds.
An application can also add an entity to the drawing database by calling the
acdbEntMake() function. Like acdbEntMod(), the argument to
acdbEntMake() is a result-buffer list whose format is similar to that of a list
returned by acdbEntGet(). (The acdbEntMake() call ignores the entity name
field [-1] if that is present.) The new entity is appended to the drawing data-
base (it becomes the last entity in the drawing). If the entity is a complex
entity (a polyline or block), it is not appended to the database until it is
complete.
The following sample code fragment creates a circle on the layer MYLAYER.
int status;
struct resbuf *entlist;
ads_point center = {5.0, 7.0, 0.0};
char *layer = "MYLAYER";
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake buffer.
if (status == RTERROR) {
acdbFail("Unable to make circle entity\n");
return BAD;
}
Both acdbEntMod() and acdbEntMake() perform the same consistency checks
on the entity data passed to them as the AutoCAD DXFIN command performs
when reading DXF files. They fail if they cannot create valid drawing entities.
Complex Entities
A complex entity (a polyline or block) must be created by multiple calls to
acdbEntMake(), using a separate call for each subentity. When
acdbEntMake() first receives an initial component for a complex entity, it
creates a temporary file in which to gather the definition data (and extended
data, if present). Each subsequent acdbEntMake() call appends the new sub-
entity to the file. When the definition of the complex entity is complete (that
is, when acdbEntMake() receives an appropriate Seqend or Endblk subentity),
the entity is checked for consistency, and if valid, it is added to the drawing.
The file is deleted when the complex entity is complete or when its creation
is canceled.
The following example contains five calls to acdbEntMake() that create a sin-
gle complex entity, a polyline. The polyline has a linetype of DASHED and a
color of BLUE. It has three vertices located at coordinates (1,1,0), (4,6,0), and
(3,2,0). All other optional definition data assume default values.
int status;
struct resbuf *entlist, result;
ads_point newpt;
entlist = acutBuildList(
RTDXF0, "POLYLINE",// Entity type
62, 5, // Color (blue)
6, "dashed",// Linetype
66, 1, // Vertices follow.
0);
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
if (status != RTNORM) {
acutPrintf ("%d",status);
acedGetVar ("ERRNO", &result);
acutPrintf ("ERRNO == %d, result.resval.rint);
acdbFail("Unable to start polyline\n");
return BAD;
}
newpt[X] = 1.0;
newpt[Y] = 1.0;
newpt[Z] = 0.0; // The polyline is planar
entlist = acutBuildList(
RTDXF0, "VERTEX", // Entity type
62, 5, // Color (blue)
6, "dashed", // Linetype
10, newpt, // Start point
0);
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake() buffer.
if (status != RTNORM) {
acdbFail("Unable to add polyline vertex\n");
return BAD;
}
newpt[X] = 4.0;
newpt[Y] = 6.0;
entlist = acutBuildList(
RTDXF0, "VERTEX", // Entity type
62, 5, // Color (blue)
6, "dashed", // Linetype
10, newpt, // Second point
0);
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake() buffer.
newpt[X] = 3.0;
newpt[Y] = 2.0;
entlist = acutBuildList(
RTDXF0, "VERTEX", // Entity type
62, 5, // Color (blue)
6, "dashed", // Linetype
10, newpt, // Third point
0);
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake() buffer.
if (status != RTNORM) {
acdbFail("Unable to add polyline vertex\n");
return BAD;
}
entlist = acutBuildList(
RTDXF0, "SEQEND", // Sequence end
62, 5, // Color (blue)
6, "dashed", // Linetype
0);
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake() buffer.
if (status != RTNORM) {
acdbFail("Unable to complete polyline\n");
return BAD;
}
Creating a block is similar, except that when acdbEntMake() successfully
creates the Endblk entity, it returns a value of RTKWORD. You can verify the
name of the new block by a call to acedGetInput().
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake buffer.
if (status != RTNORM) {
acdbFail("Unable to start anonymous block\n");
return BAD;
}
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
if (status != RTKWORD) {
acdbFail("Unable to close anonymous block\n");
return BAD;
}
status = acedGetInput(newblkname);
if (status != RTNORM) {
acdbFail("Anonymous block not created\n");
return BAD;
}
To reference an anonymous block, create an insert entity with
acdbEntMake(). (You cannot pass an anonymous block to the INSERT
command.)
Continuing the previous example, the following code fragment inserts the
anonymous block at (0,0).
basept[X] = basept[Y] = basept[Z] = 0.0;
entlist = acutBuildList(
RTDXF0, "INSERT",
2, newblkname, // From acedGetInput
10, basept,
0 );
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake buffer.
if (status != RTNORM) {
acdbFail("Unable to insert anonymous block\n");
return BAD;
}
In the following example, the first entity in the current drawing is a polyline
with several vertices. The following code modifies the second vertex of the
polyline and then regenerates its screen image.
ads_name e1, e2;
struct resbuf *ed, *cb;
acdbEntNext(e1, e2);
head
-1 0 -3
NULL
1001 1001
"APPNAME1" "APPNAME2"
If you attempt to add a 1001 group but no other extended data to an existing
entity, the attempt is ignored. If you attempt to make an entity whose only
extended data group is a single 1001 group, the attempt fails.
Layer name 1003. Name of a layer associated with the extended data.
Database 1005. Handles of entities in the drawing database. Under
handle certain conditions, AutoCAD translates these.
3D point 1010. Three real values, contained in a point.
Real 1040. A real value.
Integer 1070. A 16-bit integer (signed or unsigned).
Long 1071. A 32-bit signed (long) integer. If the value that
appears in a 1071 group is a short integer or a real value,
it is converted to a long integer; if it is invalid (for
example, a string), it is converted to a long zero (0L).
NOTE If a 1001 group appears within a list, it is treated as a string and does
not begin a new application group.
Registering an Application
Application names are saved with the extended data of each entity that uses
them and in the APPID table. An application must register the name or names
it uses. In ObjectARX, this is done by a call to acdbRegApp(). The
acdbRegApp() function specifies a string to use as an application name. It
returns RTNORM if it can successfully add the name to APPID; otherwise, it
returns RTERROR. A result of RTERROR usually indicates that the name is
already in the symbol table. This is not an actual error condition but a
status = acdbEntMod(working_ent);
// Only extended data from registered applications still in the
// working_ent list are modified.
As the sample code shows, extended data retrieved by the acdbEntGetX()
function can be modified by a subsequent call to acdbEntMod(), just as
acdbEntMod() is used to modify normal definition data. (Extended data can
also be created by defining it in the entity list passed to acdbEntMake().)
Returning the extended data of only specifically requested applications
protects one application from damaging the data of another application. It
also controls the amount of memory that an application uses, and simplifies
the extended data processing that an application performs.
NOTE Because the strings passed with apps can include wild-card characters,
an application name of “*” will cause acdbEntGetX() to return all extended
data attached to an entity.
When an entity is placed in a block definition (by means of the BLOCK com-
mand), the entity within the block is assigned new handles. (If the original
entity is restored with OOPS, it retains its original handles.) The value of any
extended data handles remains unchanged. When a block is exploded (with
EXPLODE), extended data handles are translated, in a manner similar to the
way they are translated when drawings are combined. If the extended data
handle refers to an entity not within the block, it is unchanged; but if the
extended data handle refers to an entity within the block, it is assigned the
value of the new (exploded) entity’s handle.
WARNING! The xrecord object is designed in a way that will not offend
earlier versions of AutoCAD; however, the xrecord object will disappear when
creating a DXF file from a pre-Release 13c4 level of AutoCAD.
Xrecord objects are generic objects intended for use by ObjectARX and
AutoLISP applications. This class allows applications to create and store
arbitrary object structures of arbitrary result-buffer lists of non-graphical
information completely separate from entities. The root owner for all appli-
cation-defined objects is either the named object dictionary, which accepts
any AcDbObject type as an entry, including AcDbXrecord, or the extension
dictionary of any object.
Applications are expected to use unique entry names in the named object
dictionary. The logic of using a named object dictionary or extension dictio-
nary entry name is similar to that of a REGAPP name. In fact, REGAPP names
are perfect for use as entry names when appending application-defined
objects to the database or a particular object.
The use of xrecord objects represents a substantial streamlining with respect
to the current practice of assigning xdata to entities. Because an xrecord
object does not need to be linked with an entity, you no longer need to create
dummy entities (dummy entities were often used to provide more room for
xdata), or entities on frozen layers.
Applications are now able to do the following:
In the following example, the function getblock() retrieves the first block
(if any) in the current drawing, and calls the printdxf() function to display
that block’s contents in a list format.
void getblock()
{
struct resbuf *bl, *rb;
The first argument to acdbTblSearch() is a string that names a table, but the
second argument is a string that names a particular symbol in the table. If the
symbol is found, acdbTblSearch() returns its data. This function has a third
argument, setnext, that can be used to coordinate operations with
acdbTblNext(). If setnext is zero, the acdbTblSearch() call has no effect on
acdbTblNext(), but if setnext is nonzero, the next call to acdbTblNext()
returns the table entry that follows the entry found by acdbTblSearch().
The setnext option is especially useful when dealing with the VPORT symbol
table, because all viewports in a particular viewport configuration have the
same name (such as *ACTIVE).
Keep in mind that if the VPORT symbol table is accessed when TILEMODE is
off, changes have no visible effect until TILEMODE is turned back on.
(TILEMODE is set either by the SETVAR command or by entering its name
directly.) Do not confuse the VPORT symbol table with viewport entities.
while (v != NULL} {
for (rb = v; rb != NULL; rb = rb->rbnext)
if (rb->restype == 2)
if (strcmp(rb->resval.rstring, "4VIEW") == 0) {
.// Process the VPORT entry
.
.
acutRelRb(v);
// Get the next table entry.
v = acdbTblNext("VPORT", 0);
} else {
acutRelRb(v);
v = NULL; // Break out of the while loop.
break; // Break out of the for loop.
}
}
In This Chapter
10
The global functions described in this chapter allow ■ AutoCAD Queries and
Commands
your application to communicate with AutoCAD. This
■ Getting User Input
245
AutoCAD Queries and Commands
The functions described in this section access AutoCAD commands and
services.
General Access
The most general of the functions that access AutoCAD are acedCommand()
and acedCmd(). Like the (command) function in AutoLISP, these functions
send commands and other input directly to the AutoCAD Command
prompt.
int
acedCommand(int rtype, ...);
int
acedCmd(struct resbuf *rbp);
Unlike most other AutoCAD interaction functions, acedCommand() has a
variable-length argument list: arguments to acedCommand() are treated as
pairs except for RTLE and RTLB, which are needed to pass a pick point. The
first of each argument pair identifies the result type of the argument that fol-
lows, and the second contains the actual data. The final argument in the list
is a single argument whose value is either 0 or RTNONE. Typically, the first
argument to acedCommand() is the type code RTSTR, and the second data argu-
ment is a string that is the name of the command to invoke. Succeeding
argument pairs specify options or data that the specified command requires.
The type codes in the acedCommand() argument list are result types.
The data arguments must correspond to the data types and values expected
by that command’s prompt sequence. These can be strings, real values,
integers, points, entity names, or selection set names. Data such as angles,
distances, and points can be passed either as strings (as the user might enter
them) or as the values themselves (that is, as integer, real, or point values).
An empty string (“”) is equivalent to entering a space on the keyboard.
Because of the type identifiers, the acedCommand() argument list is not the
same as the argument list for the AutoLISP (command) routine. Be aware of
this if you convert an AutoLISP routine into an ObjectARX application.
There are restrictions on the commands that acedCommand() can invoke,
which are comparable to the restrictions on the AutoLISP (command)
function.
return GOOD;
}
Provided that AutoCAD is at the Command prompt when this function is
called, AutoCAD performs the following actions:
1 Draws a circle that passes through (3.0,3.0) and whose center is at (0.0,0.0).
2 Changes the current thickness to 1.0. Note that the first call to
acedCommand() passes the points as strings, while the second passes a short
integer. Either method is possible.
3 Draws another (extruded) circle whose center is at (1.0,1.0,3.0) and whose
radius is 4.5. This last call to acedCommand() uses a 3D point and a real
(double-precision floating-point) value. Note that points are passed by refer-
ence, because ads_point is an array type.
Using acedCmd()
The acedCmd() function is equivalent to acedCommand() but passes values to
AutoCAD in the form of a result-buffer list. This is useful in situations where
complex logic is involved in constructing a list of AutoCAD commands. The
acutBuildList() function is useful for constructing command lists.
acedCmd(cmdlist);
acutRelRb(cmdlist);
System Variables
A pair of functions, acedGetVar() and acedSetVar(), enable ObjectARX
applications to inspect and change the value of AutoCAD system variables.
These functions use a string to specify the variable name (in either uppercase
or lowercase), and a (single) result buffer for the type and value of the vari-
able. A result buffer is required in this case because the AutoCAD system
variables come in a variety of types: integers, real values, strings, 2D points,
and 3D points.
The following sample code fragment ensures that subsequent FILLET
commands use a radius of at least 1.
struct resbuf rb, rb1;
acedGetVar("FILLETRAD", &rb);
rb1.restype = RTREAL;
rb1.resval.rreal = 1.0;
if (rb.resval.rreal < 1.0)
if (acedSetVar("FILLETRAD", &rb1) != RTNORM)
return BAD; // Setvar failed.
In this example, the result buffer is allocated as an automatic variable when
it is declared in the application. The application does not have to explicitly
manage the buffer’s memory use as it does with dynamically allocated
buffers.
AutoLISP Symbols
The functions acedGetSym() and acedPutSym() let ObjectARX applications
inspect and change the value of AutoLISP variables.
In the first example, the user enters the following AutoLISP expressions:
Command: (setq testboole t)
T
Command: (setq teststr “HELLO, WORLD”)
“HELLO, WORLD”
Command: (setq sset1 (ssget))
<Selection set: 1>
Then the following sample code shows how acedGetSym() retrieves the new
values of the symbols.
struct resbuf *rb;
int rc;
long sslen;
rc = acedGetSym("testboole", &rb);
if (rc == RTNORM && rb->restype == RTT)
acutPrintf("TESTBOOLE is TRUE\n");
acutRelRb(rb);
rc = acedGetSym("teststr", &rb);
if (rc == RTNORM && rb->restype == RTSTR)
acutPrintf("TESTSTR is %s\n", rb->resval.rstring);
acutRelRb(rb);
rc = acedGetSym("sset1", &rb);
if (rc == RTNORM && rb->restype == RTPICKS) {
rc = acedSSLength(rb->resval.rlname, &sslen);
acutPrintf("SSET1 contains %lu entities\n", sslen);
}
acutRelRb(rb);
Conversely, acedPutSym() can create or change the binding of AutoLISP
symbols, as follows:
ads_point pt1;
pt1[X] = pt1[Y] = 1.4; pt1[Z] = 10.9923;
rc = acedPutSym("longlist", rb);
acedPrompt("LONGLIST has been created\n");
acutRelRb(rb);
To set an AutoLISP variable to nil, make the following assignment and func-
tion call:
rb->restype = RTNIL;
acedPutSym("var1", rb);
Users can retrieve these new values. (As shown in the example, your program
should notify users of any changes.)
TESTSTR has been reset.
LONGLIST has been created.
Command: !teststr
(“GREETINGS”)
Command: !longlist
((-1 “The combinations of the world” “are unstable by nature.” 100 (1.4 1.4
10.9923) (“He jests at scars” “that never felt a wound.”)))
File Search
The acedFindFile() function enables an application to search for a file of a
particular name. The application can specify the directory to search, or it can
use the current AutoCAD library path.
In the following sample code fragment, acedFindFile() searches for the
requested file name according to the AutoCAD library path.
char *refname = "refc.dwg";
char fullpath[100];
.
.
.
if (acedFindFile(refname, fullpath) != RTNORM) {
acutPrintf("Could not find file %s.\n", refname);
return BAD;
result->resval.rstring=NULL;
Object Snap
The acedOsnap() function finds a point by using one of the AutoCAD Object
Snap modes. The snap modes are specified in a string argument.
In the following example, the call to acedOsnap() looks for the midpoint of
a line near pt1.
acedOsnap(pt1, "midp", pt2);
The following call looks for either the midpoint or endpoint of a line, or the
center of an arc or circle—whichever is nearest pt1.
acedOsnap(pt1, "midp,endp,center", pt2);
The third argument (pt2 in the examples) is set to the snap point if one is
found. The acedOsnap() function returns RTNORM if a point is found.
rc = acedVports(&rb);
acedRetList(rb);
acutRelRb(rb);
For example, given a single-viewport configuration with TILEMODE turned
on, the preceding code may return the list shown in the following figure.
rb NULL
RTSHORT RTPOINT RTPOINT
1 0.0 30.0
0.0 30.0
Similarly, if four equal-sized viewports are located in the four corners of the
screen and TILEMODE is turned on, the preceding code may return the con-
figuration shown in the next figure.
rb
RTSHORT RTPOINT RTPOINT RTSHORT RTPOINT RTPOINT
5 0.5 1.0 2 0.5 1.0
0.0 0.5 0.5 1.0
NULL
RTSHORT RTPOINT RTPOINT RTSHORT RTPOINT RTPOINT
3 0.0 0.5 4 0.0 0.5
0.5 1.0 0.0 0.5
The current viewport’s descriptor is always first in the list. In the list shown
in the preceding figure, viewport number 5 is the current viewport.
Geometric Utilities
One group of functions enables applications to obtain geometric informa-
tion. The acutDistance() function finds the distance between two points,
acutAngle() finds the angle between a line and the X axis of the current UCS
(in the XY plane), and acutPolar() finds a point by means of polar coordi-
nates (relative to an initial point). Unlike most ObjectARX functions, these
NOTE Unlike acedOsnap(), the functions in this group simply calculate the
point, line, or angle values, and do not actually query the current drawing.
The following sample code fragment shows some simple calls to the geomet-
ric utility functions.
ads_point pt1, pt2;
ads_point base, endpt;
ads_real rads, length;
.
. // Initialize pt1 and pt2.
.
// Return the angle in the XY plane of the current UCS, in radians.
rads = acutAngle(pt1, pt2);
// Return distance in 3D space.
length = acutDistance(pt1, pt2);
base[X] = 1.0; base[Y] = 7.0; base[Z] = 0.0;
acutPolar(base, rads, length, endpt);
The call to acutPolar() sets endpt to a point that is the same distance from
(1,7) as pt1 is from pt2, and that is at the same angle from the X axis as the
angle between pt1 and pt2.
pt1
The next figure shows the point values that acedTextBox() returns for sam-
ples of vertical and aligned text. In both samples, the height of the letters was
entered as 1.0. (For the rotated text, this height is scaled to fit the alignment
points.)
pt2 = 9.21954,1.38293
(10,3)
(1,1)
pt1 pt1 = 0,0 alignment points entered where text was created
= -0.5,-20.0
Note that with vertical text styles, the points are still returned in left-to-right,
bottom-to-top order, so the first point list contains negative offsets from the
text origin.
The acedTextBox() function can also measure strings in attdef and attrib
entities. For an attdef, acedTextBox() measures the tag string (group 2); for
an attrib entity, it measures the current value (group 1).
The following function, which uses some entity handling functions, prompts
the user to select a text entity, and then draws a bounding box around the
text from the coordinates returned by acedTextBox().
NOTE The sample tbox() function works correctly only if you are currently in
the World Coordinate System (WCS). If you are not, the code should convert the
ECS points retrieved from the entity into the UCS coordinates used by
acedCommand(). See “Coordinate System Transformations” on page 271.
User-Input Functions
The user-input or acedGetxxx() functions pause for the user to enter data of
the indicated type, and return the value in a result argument. The application
can specify an optional prompt to display before the function pauses.
NOTE Several functions have similar names but are not part of the user-input
group: acedGetFunCode(), acedGetArgs(), acedGetVar(), and
acedGetInput().
Code Description
NOTE The acedDragGen() function indicates arbitrary input (if this has been
enabled by a prior acedInitGet() call) by returning RTSTR instead of RTKWORD.
Keyword Specifications
The optional kwl argument specifies a list of keywords that will be recognized
by the next user-input (acedGetxxx()) function call. The keyword value that
the user enters can be retrieved by a subsequent call to acedGetInput(). (The
keyword value will be available if the user-input function was
acedGetKword().) The meanings of the keywords and the action to perform
for each is the responsibility of the ObjectARX application.
The acedGetInput() function always returns the keyword as it appears in the
kwl argument, with the same capitalization (but not with the optional char-
acters, if those are specified after a comma). Regardless of how the user enters
User Breaks
The user-input functions and the acedCommand(), acedCmd(), acedEntSel(),
acedNEntSelP(), acedNEntSel(), acedDragGen(), and acedSSGet() func-
tions return RTCAN if the AutoCAD user responds by pressing ESC . An external
function should treat this response as a cancel request and return immedi-
ately. ObjectARX also provides a function, acedUsrBrk(), that explicitly
checks whether the user pressed ESC . This function enables ObjectARX appli-
cations to check for a user interrupt.
An application doesn’t need to call acedUsrBrk() unless it performs lengthy
computation between interactions with the user. The function acedUsrBrk()
should never be used as a substitute for checking the value returned by user-
input functions that can return RTCAN.
In some cases, an application will want to ignore the user’s cancellation
request. If this is the case, it should call acedUsrBrk() to clear the request;
otherwise, the ESC will still be outstanding and will cause the next user-input
call to fail. (If an application ignores the ESC , it should print a message to tell
the user it is doing so.) Whenever an ObjectARX application is invoked, the
ESC condition is automatically cleared.
For example, the following code fragment fails if the user enters ESC at the
prompt.
int test()
{
int i;
while (!acedUsrBrk()) {
acedGetInt("\nInput integer:", &i); // WRONG
.
.
.
}
}
for (;;) {
if (acedGetInt("\nInput integer:", &i) != RTNORM)
break;
...
}
}
The following sample changes the loop condition. This construction also
works correctly.
int test()
{
int i;
acedRetPoint A 3D point
acedRetStr A string
The following example shows the scheme of a function called when the
application receives a kInvkSubrMsg request. It returns a real value to
AutoLISP.
int dofun()
{
ads_real x
acedRetReal(x);
return GOOD;
}
NOTE An external function can make more than one call to value-return func-
tions upon a single kInvkSubrMsg request, but the AutoLISP function returns
only the value passed it by the last value-return function invoked.
Conversions
The functions described in this section are utilities for converting data types
and units.
String Conversions
The functions acdbRToS() and acdbAngToS() convert values used in
AutoCAD to string values that can be used in output or as textual data. The
acdbRToS() function converts a real value, and acdbAngToS() converts an
angle. The format of the result string is controlled by the value of AutoCAD
When the UNITMODE system variable is set to 1, which specifies that units
are displayed as entered, the string returned by acdbRToS() differs for engi-
neering (mode equals 3), architectural (mode equals 4), and fractional (mode
equals 5) units. For example, the first two lines of the preceding sample out-
put would be the same, but the last three lines would appear as follows:
Value formatted as 1′5.50″
Value formatted as 1′5-1/2″
Value formatted as 17-1/2
Conversions | 267
The acdbDisToF() function complements acdbRToS(), so the following calls,
which use the strings generated in the previous examples, all set result to
the same value, 17.5. (Again, the examples do not show error checking.)
acdbDisToF("1.7500E+01", 1, &result); // 1 = scientific
NOTE When you have a string that specifies an angle in degrees, minutes, and
seconds, you must use a backslash (\) to escape the seconds symbol (″) so that
it doesn’t appear to be the end of the string. The second of the preceding
acdbAngToF() examples demonstrates this.
Real-World Units
The file acad.unt defines a variety of conversions between real-world units
such as miles/kilometers, Fahrenheit/Celsius, and so on. The function
acutCvUnit() takes a value expressed in one system of units and returns the
equivalent value in another system. The two systems of units are specified by
strings that must match one of the definitions in acad.unt.
If the current drawing units are engineering or architectural (feet and inches),
the following fragment converts a user-specified distance into meters.
ads_real eng_len, metric_len;
char *prmpt = "Select a distance: ";
Conversions | 269
Character Type Handling
ObjectARX provides a package of character-handling functions, as shown in
the table that follows. The advantage of this package over the standard C
library package, ctype.h, is that these functions are independent of any spe-
cific character set and are not bound to ASCII. They are customized to the
current AutoCAD language configuration. In other respects, they behave like
their standard C counterparts.
The following code fragment takes a character (the value in this example is
arbitrary) and converts it to uppercase. The acutToUpper() function has no
effect if the character is already uppercase.
int cc = 0x24;
cc = acutToUpper(cc);
The following are descriptions of the AutoCAD coordinate systems that can
be specified by the from and to arguments.
WCS World Coordinate System. The “reference” coordinate
system. All other coordinate systems are defined relative
to the WCS, which never changes. Values measured
relative to the WCS are stable across changes to other
coordinate systems.
UCS User Coordinate System. The “working” coordinate system.
All points passed to AutoCAD commands, including those
returned from AutoLISP routines and external functions,
are points in the current UCS (unless the user precedes
them with a * at the Command prompt). If you want your
application to send coordinates in the WCS, ECS, or DCS
to AutoCAD commands, you must first convert them to
the UCS by calling acedTrans().
pt[X] = 1.0;
pt[Y] = 2.0;
pt[Z] = 3.0;
fromrb.restype = RTSHORT;
fromrb.resval.rint = 0; // WCS
torb.restype = RTSHORT;
torb.resval.rint = 1; // UCS
Display Control
ObjectARX has several functions for controlling the AutoCAD display,
including both text and graphics screens.
Interactive Output
The basic output functions are acedPrompt(), which displays a message on
the AutoCAD prompt line, and acutPrintf(), which displays text on the
text screen. The acutPrintf() function’s calling sequence is equivalent to
the standard C library function printf(). It is provided as a separate func-
tion, because on some platforms the standard C printf() causes the output
message to mangle the AutoCAD graphics screen. (Remember that the
acdbFail() function also displays messages on the text screen.)
The size of a string displayed by acedPrompt() should not exceed the length
of the graphics screen’s prompt line; typically this is no more than 80 char-
acters. The size of a string displayed by acutPrintf() must not exceed
132 characters, because this is the size of the string buffer used by the
acutPrintf() function (133 bytes, with the last byte reserved for the null
character).
Tablet Calibration
AutoCAD users with a digitizing tablet can calibrate the tablet by using the
TABLET command. With the acedTablet() function, applications can man-
age calibrations by setting them directly and by saving calibration settings
for future use. The function takes two arguments, list and result, each of
which is a result-buffer list. The first result buffer in the first list is an integer
code that must be 0 to retrieve the current calibration (in result), or 1 to set
the calibration according to the remaining buffers in list. Calibrations are
expressed as four 3D points (in addition to the code). The first three of these
points—row1, row2, and row3—are the three rows of the tablet’s transforma-
tion matrix. The fourth point is a vector, direction, that is normal to the
plane of the tablet’s surface (expressed in WCS).
NOTE The TABMODE system variable controls whether Tablet mode is set to
On (1) or Off (0). You can control it by using acedSetVar().
The following code sequence retrieves the current tablet calibration, and
saves it in calibr2. In this example, the user has used the TABLET command
to calibrate the matrix, and Tablet mode is on.
struct resbuf *calibr1, *calibr2;
struct resbuf varbuf, rb;
To turn the resulting vector back into a 2D point, the first two components
are divided by the third, the scale factor D' , yielding the point (X'/D',Y'/D') .
NOTE When you set a calibration, the result does not equal the list argument
if the direction in the list was not normalized; AutoCAD normalizes the direction
vector before it returns it. Also, it ensures that the third element in the third
column (row3[Z]) is equal to 1. This situation should not arise if you set the
calibration using values retrieved from AutoCAD by means of acedTablet().
However, it can happen if your program calculates the transformation itself.
Wild-Card Matching
The acutWcMatch() function enables applications to compare a string to a
wild-card pattern. This facility can be used when building a selection set (in
conjunction with acedSSGet()) and when retrieving extended entity data by
application name (in conjunction with acdbEntGetX()).
The acutWcMatch() function compares a single string to a pattern, and
returns RTNORM if the string matches the pattern, and RTERROR if it does not.
The wild-card patterns are similar to the regular expressions used by many
system and application programs. In the pattern, alphabetic characters and
numerals are treated literally; brackets can be used to specify optional char-
acters or a range of letters or digits; a question mark (?) matches a single
character, and an asterisk (*) matches a sequence of characters; certain other
special characters have meanings within the pattern. For a complete table of
characters used in wild-card strings, see the description of acutWcMatch().
In the following examples, a string variable called matchme has been declared
and initialized. The following call checks whether matchme begins with the
five characters “allof”.
if (acutWcMatch(matchme, "allof*") == RTNORM) {
.
.
.
}
281
282
Deriving a Custom
ObjectARX Class
In This Chapter
11
This chapter describes how to use the ObjectARX ■ Custom Class Derivation
■ Runtime Class Identification
macros to simplify the task of deriving a custom
■ Class Declaration Macro
ObjectARX class. These macros allow a custom class ■ Class Implementation Macros
283
Custom Class Derivation
ObjectARX provides a set of macros, declared in the rxboiler.h file, that helps
you create new classes derived from AcRxObject. You can derive new classes
from most of the classes in the ObjectARX hierarchy except the AutoCAD
Release 12 entity set (listed in chapter 6, “Entities,”) and the symbol table
classes. If you do not use the ObjectARX macros to define your new class, the
class will inherit the runtime identity of its most immediate ObjectARX-
registered parent class.
Applications can most efficiently derive new classes from the following
classes:
■ AcRxObject
■ AcRxService
■ AcDbObject
■ AcDbEntity
■ AcDbCurve
■ AcDbObjectReactor
■ AcDbDatabaseReactor
■ AcDbEntityReactor
■ AcTransactionReactor
■ AcEdJig
■ AcEditorReactor
■ AcDbAttribute
■ AcDbAttributeDefinition
■ AcDbArc
■ AcDbBlockReference
■ AcDbCircle
■ AcDbFace
■ AcDbLine
■ AcDbMInsertBlock
■ AcDbPoint
■ AcDbShape
■ AcDbSolid
■ AcDbText
■ AcDbTrace
■ All AcDbXxxDimension classes
■ AcDbViewport
■ AcDbGroup
■ All classes derived from AcDbSymbolTable
■ All classes derived from AcDbSymbolTableRecord
■ AcDbBlockBegin
■ AcDbBlockEnd
■ desc(), a static member function that returns the class descriptor object
of a particular (known) class.
■ cast(), a static member function that returns an object of the specified
type, or NULL if the object is not of the required class (or a derived class).
■ isKindOf() returns whether an object belongs to the specified class (or a
derived class).
■ isA() returns the class descriptor object of an object whose class is
unknown.
When you want to know what class an object is, use AcRxObject::isA().
This function returns the class descriptor object (an instance of AcRxClass)
for a database object. Its signature is
AcRxClass* isA() const;
if (curEntity->isKindOf(AcDbEllipse::desc())) {
// Got some kind of AcDbEllipse instance.
}
This example shows another way of looking for instances of AcDbEllipse, or
any class derived from it, using the AcDbEllipse::cast() static member
function:
AcDbEllipse* ellipseEntity = AcDbEllipse::cast(curEntity);
if (ellipseEntity != NULL) {
// Got some kind of AcDbEllipse instance.
}
The following example looks for instances of AcDbEllipse, but not instances
of classes derived from AcDbEllipse, using isA() and AcDbEllipse::desc():
if (curEntity->isA() == AcDbEllipse::desc()) {
// Got an AcDbEllipse, no more, no less.
This macro is used in the public section of the class declaration, as follows:
class myClass : public AcRxObject
{
public:
ACRX_DECLARE_MEMBERS(myClass);
...
};
For AsdkPoly, the following line expands to a single long line of code.
ACRX_DECLARE_MEMBERS(AsdkPoly);
■ ACRX_NO_CONS_DEFINE_MEMBERS(CLASS_NAME, PARENT_CLASS)
Use for abstract classes and any other classes that should not be
instantiated.
■ ACRX_CONS_DEFINE_MEMBERS(CLASS_NAME, PARENT_CLASS, VERNO)
Use for transient classes that can be instantiated but are not written to file.
■ ACRX_DXF_DEFINE_MEMBERS(CLASS_NAME, PARENT_CLASS, DWG_VERSION,\
MAINTENANCE_VERSION, PROXY_FLAGS, DXF_NAME, APP)
Use for classes that can be written to, or read from, DWG and DXF files.
Each of these macros defines the following:
For AsdkPoly, the following line expands to a very long single line of code:
ACRX_DXF_DEFINE_MEMBERS(AsdkPoly, AcDbCurve, AcDb::kDHL_CURRENT,\
AcDb::kMReleaseCurrent, 0, POLYGON, /*MSG0*/"AutoCAD");
In This Chapter
12
This chapter describes how to derive a custom class from ■ Overriding AcDbObject Virtual
Functions
AcDbObject. It provides detailed information on filers,
■ Implementing Member Functions
the four types of object references (hard and soft ■ Filing Objects to DWG and DXF
Files
owners, and hard and soft pointers), and the undo ■ Object References
291
Overriding AcDbObject Virtual Functions
If you’re subclassing from AcDbObject, there are a number of virtual func-
tions you must override, as shown in the following sections. These sections
show which other functions are usually overridden and which functions are
only rarely overridden.
virtual Acad::ErrorStatus
dwgOutFields(AcDbDwgFiler* filer) const;
virtual Acad::ErrorStatus
dxfInFields(AcDbDxfFiler* filer);
virtual Acad::ErrorStatus
dxfOutFields(AcDbDxfFiler* filer) const;
<Destructor>
virtual Acad::ErrorStatus
wblockClone(AcRxObject* pOwnerObject,
AcDbObject*& pClonedObject,
AcDbIdMapping& idMap,
Adesk::Boolean isPrimary = Adesk::kTrue) const;
virtual Acad::ErrorStatus
subHandOverTo(AcDbObject* newObject);
virtual Acad::ErrorStatus
subOpen(AcDb::OpenMode);
virtual Acad::ErrorStatus
subCancel();
virtual Acad::ErrorStatus
subSwapIdWith(AcDbObjectId otherId,
Adesk::Boolean swapXdata = Adesk::kFalse);
virtual resbuf*
xData(const char* regappName = NULL) const;
virtual Acad::ErrorStatus
setXData(const resbuf* xdata);
virtual void
addPersistentReactor(AcDbObjectId objId);
virtual Acad::ErrorStatus
removePersistentReactor(AcDbObjectId objId);
virtual void
cancelled(const AcDbObject* dbObj);
virtual void
copied(const AcDbObject* dbObj,
const AcDbObject* newObj);
virtual void
erased(const AcDbObject* dbObj,
Adesk::Boolean pErasing = Adesk::kTrue);
virtual void
goodbye(const AcDbObject* dbObj);
virtual void
modified(const AcDbObject* dbObj);
virtual void
modifyUndone(const AcDbObject* dbObj);
virtual void
modifiedXData(const AcDbObject* dbObj);
virtual void
unappended(const AcDbObject* dbObj);
virtual void
objectClosed(const AcDbObjectId objId);
virtual void
modifiedGraphics(const AcDbEntity* dbEnt);
virtual void
copyFrom(const AcRxObject* pSrc);
virtual Adesk::Boolean
isPeriodic() const;
virtual Adesk::Boolean
isPlanar() const;
virtual Acad::ErrorStatus
getPlane(AcGePlane&, AcDb::Planarity&) const;
virtual Acad::ErrorStatus
getStartParam(double&) const;
virtual Acad::ErrorStatus
getEndParam(double&) const;
virtual Acad::ErrorStatus
getStartPoint(AcGePoint3d&) const;
virtual Acad::ErrorStatus
getEndPoint(AcGePoint3d&) const;
virtual Acad::ErrorStatus
getPointAtParam(double, AcGePoint3d&) const;
virtual Acad::ErrorStatus
getParamAtPoint(const AcGePoint3d&, double&)const;
virtual Acad::ErrorStatus
getDistAtParam(double param, double& dist) const;
virtual Acad::ErrorStatus
getParamAtDist(double dist, double& param) const;
virtual Acad::ErrorStatus
getDistAtPoint(const AcGePoint3d&, double&) const;
virtual Acad::ErrorStatus
getPointAtDist(double, AcGePoint3d&) const;
virtual Acad::ErrorStatus
getFirstDeriv(
double param,
AcGeVector3d& firstDeriv) const;
virtual Acad::ErrorStatus
getFirstDeriv(
const AcGePoint3d&,
AcGeVector3d& firstDeriv) const;
virtual Acad::ErrorStatus
getSecondDeriv(
const AcGePoint3d&,
AcGeVector3d& secDeriv) const;
virtual Acad::ErrorStatus
getClosestPointTo(
const AcGePoint3d& givenPnt,
AcGePoint3d& pointOnCurve,
Adesk::Boolean extend
= Adesk::kFalse) const;
virtual Acad::ErrorStatus
getClosestPointTo(
const AcGePoint3d& givenPnt,
const AcGeVector3d& normal,
AcGePoint3d& pointOnCurve,
Adesk::Boolean extend
= Adesk::kFalse) const;
virtual Acad::ErrorStatus
getOrthoProjectedCurve(
const AcGePlane&,
AcDbCurve*& projCrv) const;
virtual Acad::ErrorStatus
getProjectedCurve(
const AcGePlane&,
const AcGeVector3d& projDir,
AcDbCurve*& projCrv) const;
virtual Acad::ErrorStatus
getOffsetCurves(
double offsetDist,
AcDbVoidPtrArray& offsetCurves) const;
virtual Acad::ErrorStatus
getSpline(AcDbSpline*& spline) const;
virtual Acad::ErrorStatus
getSplitCurves(
const AcGeDoubleArray& params,
AcDbVoidPtrArray& curveSegments) const;
virtual Acad::ErrorStatus
getSplitCurves(
const AcGePoint3dArray& points,
AcDbVoidPtrArray& curveSegments) const;
virtual Acad::ErrorStatus
extend(double newParam);
virtual Acad::ErrorStatus
getArea(double&) const;
Acad::ErrorStatus
AcDbObject::dwgIn(AcDbDwgFiler* filer);
Acad::ErrorStatus
AcDbObject::dxfOut(
AcDbDxfFiler* filer,
Adesk::Boolean allXdFlag,
Adesk::uchar* regAppTable) const);
Acad::ErrorStatus
AcDbObject::dxfIn(AcDbDxfFiler* filer);
Each function takes a pointer to a filer as its primary argument. An
AcDbObject writes data to and reads data from a filer. The FilerType enum
allows you to check the filer type. Filer types are
■ dwgOutFields()
■ dwgInFields()
■ dxfOutFields()
■ dxfInFields()
dwgIn() Function
The dwgIn() function, which calls dwgInFields(), is invoked by the follow-
ing commands and conditions:
dxfOut() Function
The dxfOut() function, which calls dxfOutFields(), is invoked by the fol-
lowing commands and functions:
■ WBLOCK
■ SAVE
■ SAVEAS
■ acdbEntGet()
dxfIn() Function
The dxfIn() function, which calls dxfInFields(), is invoked by the follow-
ing commands and functions:
■ OPEN
■ INSERT
■ acdbEntMod() or acdbEntMake()
filer->writePoint2d(mCenter);
filer->writePoint2d(mStartPoint);
filer->writeInt32(mNumSides);
filer->writeVector3d(mPlaneNormal);
filer->writeString(mpName);
// mTextStyle is a hard pointer id, so filing it out to
// the purge filer (kPurgeFiler) prevents purging of
// this object.
//
filer->writeHardPointerId(mTextStyle);
filer->writeDouble(mElevation);
return filer->filerStatus();
}
acdbWcs2Ecs(asDblArray(startPoint),asDblArray(startPoint),
asDblArray(mPlaneNormal),Adesk::kFalse);
mStartPoint.set(startPoint.x,startPoint.y);
assert(mElevation == startPoint.z);
break;
}
case 2:
filer->readPoint2d(&mCenter);
filer->readPoint2d(&mStartPoint);
filer->readInt32(&mNumSides);
filer->readVector3d(&mPlaneNormal);
acutDelString(mpName);
filer->readString(&mpName);
filer->readHardPointerId(&mTextStyle);
filer->readDouble(&mElevation);
break;
default:
assert(false);
}
return filer->filerStatus();
}
1 4 Text
6 9 Text
38 59 Real
60 79 16-bit integer
90 99 32-bit integer
Order Dependence
With DXF, at the class author’s discretion, data groups can be presented in
arbitrary order, or optionally omitted. Some classes support order indepen-
dence of data groups, while others do not. If you allow order independence,
then your dxfInFields() function must use a switch statement to choose an
action based on the group code value. Order independence is usually appro-
priate for objects with a fixed and predictable set of fields. Objects with
variable-length arrays or structures tend to be order-dependent when they
are filed out and in.
version = rb.resval.rint;
if (version > VERSION)
return Acad::eMakeMeProxy;
AcGePoint3d cen3d,sp3d;
AcGePoint2d cen2d,sp2d;
long numSides;
AcDbObjectId textStyle;
double elevation;
Adesk::UInt32 fieldsFlags = 0;
char * pName = NULL;
AcGeVector3d planeNormal;
acdbWcs2Ecs(asDblArray(sp3d),asDblArray(sp3d),
asDblArray(planeNormal),Adesk::kFalse);
mStartPoint.set(sp3d.x,sp3d.y);
assert(mElevation == sp3d.z);
} else {
mCenter = cen2d;
mStartPoint = sp2d;
mElevation = elevation;
}
return es;
}
The complete code for the AsdkPoly application-defined class can be found
in the samples directory.
if ((AcDbCurve::dxfInFields(filer) != Acad::eOk) ||
!filer->atSubclassData("AsdkPoly") )
{
return filer->filerStatus();
}
// Object Version
Adesk::Int16 version;
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfInt16)
throw AcDb::kDxfInt16;
version = rb.resval.rint;
if (version > VERSION)
return Acad::eMakeMeProxy;
if (version == 1)
{
AcGePoint3d cent,sp;
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfXCoord)
throw AcDb::kDxfXCoord
cent = asPnt3d(rb.resval.rpoint);
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfXCoord + 1)
throw AcDb::kDxfXCoord + 1;
sp = asPnt3d(rb.resval.rpoint);
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfInt32)
throw AcDb::kDxfInt32;
mNumSides = rb.resval.rlong;
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfNormalX)
throw AcDb::kDxfNormalX
mPlaneNormal = asVec3d(rb.resval.rpoint);
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfText)
throw AcDb::kDxfText;
setName(rb.resval.rstring);
filer->readItem(&rb);
if (rb.restype != kDxfHardPointerId)
throw AcDb::kDxfHardPointerId;
acdbGetObjectId(mTextStyle, rb.resval.rlname);
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfXCoord + 1)
throw AcDb::kDxfXCoord + 1;
mStartPoint = asPnt2d(rb.resval.rpoint);
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfInt32)
throw AcDb::kDxfInt32
mNumSides = rb.resval.rlong;
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfNormalX)
throw AcDb::kDxfNormalX;
mPlaneNormal = asVec3d(rb.resval.rpoint);
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfText)
throw AcDb::kDxfText
setName(rb.resval.rstring);
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfHardPointerId)
throw AcDb::kDxfHardPointerId;
acdbGetObjectId(mTextStyle, rb.resval.rlname);
filer->readItem(&rb);
if (rb.restype != AcDb::kDxfReal)
throw AcDb::kDxfReal;
mElevation = rb.resval.rreal;
}
else assert(false);
}
catch (AcDb::DxfCode code)
{
filer->pushBackItem();
filer->setError(Acad::eInvalidDxfCode,
"\nError: expected group code %d", code);
return filer->filerStatus();
}
}
Database
AcDbLine
hard owner
soft owner
hard pointer
The AcDbObject protocol always specifies the link from the owner to the
owned object and the backward link from the object to its owner.
The following code illustrates setting up the two-way ownership link
between an owner and its contents:
// Uses the OwnerDemo class defined in the next example
// (see "ObjectARX Example," below).
//
// Sets pOwner to be the owner of pOwned.
//
void
makeOwner(OwnerDemo* pOwner, AcDbObject* pOwned)
{
// First let pOwner know it is the owner. This
// establishes ownership for filing persistence.
//
pOwner->setIdData(pOwned->ojectId());
Uses of Ownership
When an object is written to a DXF or DWG file, all objects owned by this
object are also written out. The deep clone operation also recursively copies
every object owned by the cloned object. See chapter 18, “Deep Cloning.” A
hard ownership relationship protects the owned object from purge.
Types of Ownership
Owners can be either hard or soft owners of their objects.
Hard Ownership
The following are three examples of hard ownership:
Soft Ownership
A soft ownership ID (of type AcDbSoftOwnershipId) does not protect the
owned object from purge. The following are examples of soft ownership:
■ In most cases, symbol tables are soft owners of their elements (exceptions
include the block *MODEL_SPACE, *PAPER_SPACE, *PAPER_SPACE0, and
layer 0; for these elements, the symbol table maintains a hard reference).
■ Dictionaries are soft owners of their entries (but you can flag a dictionary
to be a hard owner of its entries).
ObjectARX Example
// Class declarations
//
class AsdkOwnerDemo : public AcDbObject
Adesk::Int16 intData();
Acad::ErrorStatus setIntData(const Adesk::Int16&);
AcDbHardOwnershipId idData();
Acad::ErrorStatus setIdData(const AcDbHardOwnershipId&);
ACRX_DXF_DEFINE_MEMBERS(AsdkOwnerDemo, AcDbObject,
AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent, 0,
ASDKOWNERDEMO, OWNERSHIP);
return mObjId;
}
// Sets the value of the ownership ID data member.
//
Acad::ErrorStatus
AsdkOwnerDemo::setIdData(const AcDbHardOwnershipId& ownedId)
{
if (ownedId.asOldId() == 0L) {
return Acad::eInvalidInput;
}
assertWriteEnabled();
mObjId = ownedId;
return Acad::eOk;
}
filer->readItem(&mIntval);
filer->readItem(&mObjId);
return filer->filerStatus();
}
filer->writeItem(mIntval);
filer->writeItem(mObjId);
return filer->filerStatus();
}
return filer->filerStatus();
}
filer->writeItem(AcDb::kDxfSubclass, "AsdkOwnerDemo");
filer->writeItem(AcDb::kDxfInt16, mIntval);
return filer->filerStatus();
}
AsdkOwnerDemo::rxInit();
acrxBuildClassHierarchy();
}
Pointer References
Your custom class may also contain hard or soft pointer references to other
objects in the database. A pointer is a one-way link (that is, there is no infor-
mation in the referenced object that indicates the source of the pointer). An
object can point to, or be pointed to by, any number of other objects.
Hard Pointers
A hard pointer reference protects an object from purge. For example, an
entity contains a hard pointer reference to a layer. Therefore, you can’t purge
a layer that is pointed to by one or more entities. When a new database is
written out from an existing one (for example, in a WBLOCK operation), all
hard pointers are copied into the new database.
Other examples of hard pointer references
If you use a soft pointer to refer to an object, you should check that the object
still exists before you open it.
Applications that need to prevent their objects from being included as cloned
in long transactions need to register those objects using the
AcApLongTransactionManager::addClassFilter() function.
AcDbProxyEntity and AcDbProxyObject are always filtered, so when the
application is not present, all of its objects will be filtered automatically.
Wblock cloning handles all hard pointer references, but deep cloning does
not require either type of reference to be mapped. Both of these cloning types
are used in long transactions, depending on the type of transaction it is. If an
application uses either of these types of references, or xdata handles, then its
objects will be rejected from long transactions, unless the application takes
extra steps to handle the references. This means that if the application is not
loaded, then its objects and references will automatically be prevented from
participating in long transactions and any data should be preserved in its
absence.
Use the long transaction and deep clone notifications to intercept the clon-
ing of their object and references, and to add whatever object cloning or
mapping is needed. See the deep clone notification documentation and sam-
ples for more information on this.
If an object with a soft pointer reference is cloned (or a hard pointer reference
in deep clone), the application must make sure that the reference ID is in the
Taking these steps will guarantee “transitive closure.” To ensure that a set of
objects that refer to each other can be checked out, and then checked back
in again, without breaking the object’s relationships, all associated objects
are checked out together. For example, if any boundary or the associative
hatch itself is passed into checkOut(), the hatch code adds all of the bound-
ary objects to the list of objects to be checked out. This is how it achieves
transitive closure. If this did not happen, the LTM would find the hatch’s soft
pointer IDs to its boundaries. If it found that a boundary object so referenced
was missing from the cloning, the long transaction would be aborted. If it did
not do this, even if no changes were made to the checked out hatch, the orig-
inal hatch would lose its associativity on check in.
Sometimes, there are known references that do not need to be resolved. One
situation would be an object that keeps track of all the entities that use it. For
example, block table records keep a list of all the block references that use
them. It is correct to only check out one of the references, so you must let the
long transaction mechanism know that the rest of the references do not need
to be cloned. There are several ways this can be done.
Here are some examples:
■ If the application knows about which objects are referenced but will not
be cloned—at beginWblockObjects(), beginDeepClone(), or
beginCheckOut() notification—they can add the object ID of the refer-
enced object to the IdMap for the cloning. The recommended approach is
to set the value to NULL, and the idPair as not cloned. For example
idMap.assign(idPair(id, AcDbObjectId::kNull, kFalse);
If the object needs to be cloned later, the idPair will be changed accord-
ingly.
■ The above mapping can also be done from within the object’s
wblockClone() method, if that has already been overridden.
■ If the reference is a data member of the object, which is filed out using
dwgOutFields(), then it may be possible to avoid the long transaction
validity test. The test is done by filing out the IDs using a kIdFiler type
of filer. To avoid the test, do not file out the IDs that do not need to be
cloned, during this type of filing. However, do not hold any ownership
Automatic Undo
The assertWriteEnabled() function has the following signature:
void assertWriteEnabled(
Adesk::Boolean autoUndo = Adesk::kTrue,
Adesk::Boolean recordModified = Adesk::kTrue);
When a modification function calls assertWriteEnabled(), it first checks
the value of the recordModified parameter. If recordModified is kFalse, no
undo recording is performed. If recordModified is kTrue, it next checks the
autoUndo parameter, which specifies whether an auto undo operation should
be performed.
If autoUndo is kTrue (the default), the full object state is automatically written
to the object’s undo filer. If you specify kFalse for autoUndo, no information
is recorded. AutoCAD assumes that your modification function will take care
of recording the changed object state to the object’s undo filer.
Even if you plan to implement a partial undo mechanism for your class, you
can rely on automatic undo in the first stages of development.
Partial Undo
It is up to the implementor of a new class to decide whether to implement a
partial undo mechanism for certain modification functions of the class. If
only a small portion of an object’s state is typically modified in a particular
member function, using partial undo can yield substantial performance ben-
efits. However, if your object state is small (512 bytes or less), it is probably
not worth the effort to implement your own partial undo recording and
restoring scheme.
If your modification function records a partial object state, you must imple-
ment the applyPartialUndo() function for your class so that the data can
also be restored selectively. See “Restoring State” on page 326.
Recording State
To record only part of an object’s state, specify kFalse for the autoUndo
parameter, and then use the undoFiler::writeItem() function (or another
writexxx() function) to save the relevant information in the undo file.
if (mNumSides == numSides)
return Acad::eOk;
Restoring State
If you specified kFalse for autoUndo, the object’s applyPartialUndo()
function is called when the UNDO command is invoked. The
applyPartialUndo() function is a virtual function on AcDbObject. Derived
classes can implement this function to interpret the class-specific informa-
tion stored by the undo filer and read it in. The applyPartialUndo()
function must ensure that your class performed the modification. If not, it
must super-message, as shown in the following example.
If you are implementing a partial undo mechanism, be sure to call the fol-
lowing function so that no recording happens by default.
assertWriteEnabled(kFalse, kFalse);
PolyOpCodeForPartialUndo code;
code = (PolyOpCodeForPartialUndo)shortCode;
Adesk::UInt32 value32;
switch (code) {
case kSetNumSides:
filer->readItem(&value32);
AOK(setNumSides(value32));
break;
default:
assert(Adesk::kFalse);
break;
}
return Acad::eOk;
}
Redo
When the undo operation undoes your work, it also records the current state
in preparation for a redo operation. This recording for redo requires no fur-
ther work on your part, because it uses the same filing mechanism as the
undo operation, calling the object’s dwgOutFields() function to record the
object’s state.
If you implement a partial undo for your modification function, you are
responsible for recording for redo in your undo operation. This is usually
accomplished by calling the appropriate set() functions. When your set()
function is called, assertWriteEnabled() is invoked, which records the data
for undo.
It is best not to change any state in a subsidiary function. If you must change
state, then try to change it after invoking the parent class implementation of
the same function (in case an error code is returned). If you must change state
before invoking the parent class function, then be prepared to reverse it if the
parent class returns a bad status.
The following example shows the implementation of the subErase() func-
tion that is called when an object is erased. The subErase() function checks
for hard pointer references to other objects and erases them as well.
class AsdkEllipse : public AcDbEllipse
// This class extends AcDbEllipse by adding in functionality
// to store a dynamic array of hard pointer object IDs.
//
// The subErase() member function has been overridden and
// implemented, so when an object of this class is
// erased, the objects pointed to by the hard pointer IDs
// stored within the object will also be erased.
//
ACRX_DECLARE_MEMBERS(AsdkEllipse);
AsdkEllipse() {};
AsdkEllipse(const AsdkEllipse&);
AsdkEllipse(const AcDbObjectIdArray& ellipses)
: mEllipseIds(ellipses) {};
AsdkEllipse(const AcGePoint3d& center,
const AcGeVector3d& unitNormal,
const AcGeVector3d& majorAxis,
double radiusRatio,
double startAngle = 0.0,
double endAngle = 6.28318530717958647692);
AsdkEllipse(const AcDbObjectIdArray& ellipses,
const AcGePoint3d& center,
const AcGeVector3d& unitNormal,
const AcGeVector3d& majorAxis,
double radiusRatio,
double startAngle = 0.0,
double endAngle = 6.28318530717958647692);
AcDbObjectId ellipseId(unsigned short which);
Acad::ErrorStatus setEllipseId(
const AcDbObjectId& objId, unsigned short which);
Acad::ErrorStatus setEllipseIds(
const AcDbObjectIdArray& Ids);
Acad::ErrorStatus appendId(const AcDbObjectId& objId);
Acad::ErrorStatus appendIds(
const AcDbObjectIdArray& objIds);
inline Adesk::Boolean removeId(
const AcDbObjectId& objId);
// AcDbObject overrides.
//
virtual Acad::ErrorStatus subErase(
Adesk::Boolean pErasing);
virtual Acad::ErrorStatus dwgInFields(
AcDbDwgFiler* filer);
virtual Acad::ErrorStatus dwgOutFields(
AcDbDwgFiler* filer) const;
virtual Acad::ErrorStatus dxfInFields(
AcDbDxfFiler* filer);
virtual Acad::ErrorStatus dxfOutFields(
AcDbDxfFiler* filer) const;
virtual Acad::ErrorStatus wblockClone(
AcRxObject* pOwnerObject,
AcDbObject*& pClonedObject,
AcDbIdMapping& idMap,
Adesk::Boolean isPrimary = Adesk::kTrue) const;
// AcDbEntity overrides.
//
virtual void list() const;
// AcRxObject overrides.
//
virtual AcRxObject* clone() const;
mEllipseIds = master.mEllipseIds;
}
AcDbObjectId
AsdkEllipse::ellipseId(unsigned short which)
{
assertReadEnabled();
if (which > mEllipseIds.length())
return AcDbObjectId::kNull;
return mEllipseIds[which];
}
Acad::ErrorStatus
AsdkEllipse::setEllipseId(const AcDbObjectId& objId,
unsigned short which)
{
Acad::ErrorStatus
AsdkEllipse::setEllipseIds(const AcDbObjectIdArray& objIds)
{
assertWriteEnabled();
if (objIds.length() == 0)
return Acad::eNullObjectId;
mEllipseIds = objIds;
return Acad::eOk;
}
Acad::ErrorStatus
AsdkEllipse::appendId(const AcDbObjectId& objId)
{
assertWriteEnabled();
if (objId == AcDbObjectId::kNull)
return Acad::eNullObjectId;
mEllipseIds.append(objId);
return Acad::eOk;
}
Acad::ErrorStatus
AsdkEllipse::appendIds(const AcDbObjectIdArray& objIds)
{
assertWriteEnabled();
if (objIds.length() == 0)
return Acad::eNullObjectId;
mEllipseIds.append(objIds);
return Acad::eOk;
}
inline Adesk::Boolean
AsdkEllipse::removeId(const AcDbObjectId& objId)
{
assertWriteEnabled();
return mEllipseIds.remove(objId);
}
if (mInFlux == Adesk::kFalse) {
mInFlux = Adesk::kTrue;
AsdkEllipse *pEllipse;
int es;
for (int i = 0; i < mEllipseIds.length(); i++) {
es = acdbOpenObject(pEllipse, mEllipseIds[i],
AcDb::kForWrite, Adesk::kTrue);
if (es != Acad::eOk)
continue;
pEllipse->erase(pErasing);
pEllipse->close();
}
mInFlux = Adesk::kFalse;
}
return Acad::eOk;
}
Acad::ErrorStatus
AsdkEllipse::dwgInFields(AcDbDwgFiler* filer)
{
assertWriteEnabled();
AcDbEllipse::dwgInFields(filer);
mEllipseIds.setLogicalLength(0);
int idCount;
filer->readInt32((long*)&idCount);
AcDbHardPointerId objId;
for (int i = 0; i < idCount; i++) {
filer->readItem(&objId);
mEllipseIds.append(objId);
}
return filer->filerStatus();
}
Acad::ErrorStatus
AsdkEllipse::dwgOutFields(AcDbDwgFiler* filer) const
{
assertReadEnabled();
AcDbEllipse::dwgOutFields(filer);
filer->writeInt32(mEllipseIds.length());
AcDbEllipse::dxfOutFields(filer);
filer->writeItem(AcDb::kDxfSubclass, "AsdkEllipse");
filer->writeInt32(AcDb::kDxfInt32,
mEllipseIds.length());
void
AsdkEllipse::list() const
{
assertReadEnabled();
AcDbEllipse::list();
acutPrintf("\nClass:\t%s", isA()->name());
for (int i = 0; i < mEllipseIds.length(); i++) {
acutPrintf("\nReferenceId[%d]:\t%ld", i,
(mEllipseIds[i]).asOldId());
}
}
AcDbObjectIdArray ellipseIds;
AcDbObjectId tempId;
for (i = 0; i < ellipses.length(); i++) {
pBlockTableRecord->appendAcDbEntity(tempId,
(AsdkEllipse*)ellipses[i]);
ellipseIds.append(tempId);
}
pBlockTableRecord->close();
// Set up the hard pointers and close the ellipses.
//
for (i = 0; i < ellipses.length(); i++) {
// Add in all the IDs.
//
((AsdkEllipse*)ellipses[i])
->setEllipseIds(ellipseIds);
AsdkEllipse::rxInit();
acrxBuildClassHierarchy();
}
void
unloadApp()
{
acedRegCmds->removeGroup("ASDK_ELLIPSES");
Header File
The following code shows the class declaration for the new class AsdkMyClass
derived from AcDbObject.
class AsdkMyClass : public AcDbObject
//
// This class demonstrates custom objects.
//
// To keep it simple, this class has a single integer data
// member. Get and set functions are provided for this
// data member.
//
{
public:
ACRX_DECLARE_MEMBERS(AsdkMyClass);
AsdkMyClass(): mIntval(0) {};
AsdkMyClass(const Adesk::Int16& val): mIntval(val) {};
Acad::ErrorStatus getData (Adesk::Int16&);
Acad::ErrorStatus setData (Adesk::Int16);
virtual Acad::ErrorStatus dwgInFields (AcDbDwgFiler*);
virtual Acad::ErrorStatus dwgOutFields(AcDbDwgFiler*)
const;
virtual Acad::ErrorStatus dxfInFields (AcDbDxfFiler*);
virtual Acad::ErrorStatus dxfOutFields(AcDbDxfFiler*)
const;
private:
Adesk::Int16 mIntval;
};
Source File
The following code shows the implementation for the new class
AsdkMyClass:
ACRX_DXF_DEFINE_MEMBERS(AsdkMyClass, AcDbObject,
AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent, 0,
ASDKMYCLASS, SAMP2);
Class Versioning
Beginning with AutoCAD 2000, every custom class must provide a drawing
and maintenance version number. The drawing value corresponds to the
release of AutoCAD that was current when the class was created. The main-
tenance value can be set to whatever is appropriate for your class. For
ObjectARX classes, the maintenance value will be set to zero every time the
drawing version changes due to a new AutoCAD release. The version values
are defined in the acdb.h header file. The ACRX_DXF_DEFINE_MEMBERS macro
has been modified in AutoCAD 2000 to take two new arguments,
DWG_VERSION and MAINTENANCE_VERSION:
#define ACRX_DXF_DEFINE_MEMBERS(CLASS_NAME,PARENT_CLASS,\
DWG_VERSION,MAINTENANCE_VERSION,PROXY_FLAGS,DXF_NAME,APP)
The appropriate rule should be used by the leaf class, as well as all its base
classes, to file data in and out. In the example given above, rule 2 applies (the
filer is from AutoCAD 2000, while the object is from Release 14), so we file
out using the AutoCAD 2000 version. If there was a new class introduced in
Release 14 whose data is also changing in AutoCAD 2000, and the operation
is to save as Release 13, rule 1 applies and we file out using the Release 14
(birth) version.
Two new virtual methods of AcDbObject have been introduced to implement
class versioning, one for DWG and one for DXF files:
virtual Acad::ErrorStatus
getObjectSaveVersion(
const AcDbDwgFiler* pFiler,
AcDb::AcDbDwgVersion& ver,
AcDb::MaintenanceReleaseVersion& maintVer);
virtual Acad::ErrorStatus
getObjectSaveVersion(
const AcDbDxfFiler* pFiler,
AcDb::AcDbDwgVersion& ver,
AcDb::MaintenanceReleaseVersion& maintVer);
In the filer methods, instead of calling filer->dwgVersion(), call
self()->getObjectSaveVersion(filer, ...) to let the object indicate
which version to use to dump the data out. Similarly, call that method in
dwgInFields() and dxfInFields() to find out which version the data is
coming back in.
Since not all the objects have a need to override the filer version, the ones
that do need to do so specify their intent by setting a bit on the object. This
would normally be done in the constructor of the class. The bit is used as a
quick check to determine if it’s necessary to override the filer version. Meth-
ods related to this have been added to AcDbObject:
bool
hasSaveVersionOverride();
void
setHasSaveVersionOverride(
bool bSetIt);
Class Renaming
Renaming classes for each new version is the simplest method, as it does not
involve implementing new data elements or functions to detect and respond
to different class version numbers.
Acad::ErrorStatus
AsdkPoly::dwgInFields(AcDbDwgFiler* filer)
{
...
// Object Version - must always be the first item
Adesk::Int16 version;
filer->readItem(&version);
if (version > VERSION)
return Acad::eMakeMeProxy;
...
}
Acad::ErrorStatus
AsdkPoly::dxfInFields(AcDbDxfFiler* filer)
{
...
// Object Version
case AcDb::kDxfInt16:
Adesk::Int16 version;
version = rb.resval.rint;
if (version > VERSION)
return Acad::eMakeMeProxy;
break;
...
}
Acad::ErrorStatus
AsdkPoly::dxfOutFields(AcDbDxfFiler* filer) const
{
...
// Object Version
Adesk::Int16 version = VERSION;
filer->writeItem(AcDb::kDxfInt16, version);
...
}
In This Chapter
13
This chapter describes how to derive a custom class from ■ Deriving Custom Entities
■ Overriding Common Entity
AcDbEntity, and includes specific examples of overrid-
Functions
ing virtual methods provided by the AcDbEntity class. ■ Extending Entity Functionality
■ Using AcEdJig
Overriding common entity operations, such as object
349
Deriving Custom Entities
AcDbEntity is the base class for all database objects having a graphical repre-
sentation. AcDbEntity is derived from AcDbObject. Creating a custom entity
involves the following steps.
virtual Acad::ErrorStatus
getGeomExtents(
AcDbExtents& extents) const;
virtual Acad::ErrorStatus
transformBy(
const AcGeMatrix3d& xform);
virtual Acad::ErrorStatus
getTransformedCopy(
const AcGeMatrix3d& xform,
AcDbEntity*& ent) const;
virtual Acad::ErrorStatus
moveGripPointsAt(
const AcDbIntArray& indices,
const AcGeVector3d& offset);
virtual void
list() const;
virtual Acad::ErrorStatus
intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
AcGePoint3dArray& points,
int thisGsMarker = 0,
int otherGsMarker = 0) const;
virtual Acad::ErrorStatus
intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
const AcGePlane& projPlane,
AcGePoint3dArray& points,
int thisGsMarker = 0,
int otherGsMarker = 0) const;
virtual Acad::ErrorStatus
getOsnapPoints(
AcDb::OsnapMode osnapMode,
int gsSelectionMark,
const AcGePoint3d& pickPoint,
const AcGePoint3d& lastPoint,
const AcGeMatrix3d& viewXform,
AcGePoint3dArray& snapPoints,
AcDbIntArray& geomIds) const;
virtual Acad::ErrorStatus
getStretchPoints(
AcGePoint3dArray&) const;
virtual Acad::ErrorStatus
explode(
AcDbVoidPtrArray& entitySet) const;
virtual Acad::ErrorStatus
getSubentPathsAtGsMarker(
AcDb::SubentType type,
int gsMark,
const AcGePoint3d& pickPoint,
const AcGeMatrix3d& viewXform,
int& numPaths,
AcDbFullSubentPath* & subentPaths,
int numInserts = 0,
AcDbObjectId* entAndInsertStack = NULL) const;
virtual Acad::ErrorStatus
applyPartialUndo(
AcDbDwgFiler* undoFiler,
AcRxClass* classObj);
virtual void
subSetDatabaseDefaults(
AcDbDatabase* pDb);
virtual void
saveAs(
AcGiWorldDraw* mode, AcDb::SaveType st);
virtual Acad::ErrorStatus
setColorIndex(
Adesk::UInt16 color);
virtual Acad::ErrorStatus
setLinetype(
const char* newVal);
virtual Acad::ErrorStatus
setLinetype(
AcDbObjectId newVal);
virtual Acad::ErrorStatus
getGsMarkersAtSubentPath(
const AcDbFullSubentPath& subPath,
AcDbIntArray& gsMarkers) const;
virtual Acad::ErrorStatus
highlight(
const AcDbFullSubentPath& subId = kNullSubent) const;
virtual Acad::ErrorStatus
unhighlight(
const AcDbFullSubentPath& subId = kNullSubent) const;
virtual AcDbEntity*
subentPtr(
virtual Adesk::Boolean
saveImagesByDefault() const;
virtual void
setAttributes(
AcGiSubEntityTraits* pTraits);
The following sections discuss overriding several commonly used functions.
■ AcGiWorldGeometry
■ AcGiSubEntityTraits
■ Circle
■ Circular arc
■ Polyline
■ Polygon
■ Mesh
■ Shell
■ Text
■ Xline
■ Ray
■ Color
■ Layer
■ Linetype
■ Polygon fill type
■ Selection marker
■ AcGiViewportGeometry
■ AcGiSubEntityTraits
■ AcGiViewport
The viewport geometry object provides the same list of primitives as the
world geometry object and adds to it the following primitives, which use eye-
and display-space coordinates to draw polylines and polygons:
■ polylineEye()
■ polygonEye()
■ polylineDc()
■ polygonDc()
The viewport subentity traits object is the same as that used by the world
draw object (AcGiSubEntityTraits). The viewport object provides functions
for querying the viewport’s transformation matrices and viewing parameters.
For more information about the AcGi library, see chapter 26, “The Graphics
Interface Library.”
Overriding saveAs()
You should override saveAs() if you want to save an alternate graphical rep-
resentation for saving proxy entity graphics, Release 12 DWG files, or both.
If your custom entity doesn’t override the AcDbEntity::saveAs() function,
AutoCAD will leverage your worldDraw() function to support proxy entity
■ kR13Save indicates that saveAs() was called to save proxy graphics data.
■ kR12Save indicates that saveAs() was called for saving to Release 12 DWG
files.
From within saveAs(), you may want to call the worldDraw() function for
one value of saveType and make direct AcGiWorldGeometry and
AcGiSubEntityTraits calls for the other value, or you may not want to call
the worldDraw() function at all.
In either case, before calling saveAs(), AutoCAD first replaces
AcGiWorldDraw’s geometry and traits objects with special subclasses of
AcGiWorldGeometry and AcGiSubEntityTraits. These subclasses’s geometric
primitive and property traits functions cache the data in the appropriate for-
mat rather than performing a display. After calling saveAs(), AutoCAD
writes the cached data to disk.
Neither kind of saving permits preserving any view-dependent graphics. The
viewportDraw() function is not called as part of either of the save operations.
Your custom entity may rely on its viewportDraw() function for its graphics,
so its worldDraw() function alone would not produce an appropriate image.
In that case, you’ll need to override saveAs() to produce reasonable graphics
for Release 12 and proxy objects.
For more information on proxy graphics data, see chapter 14, “Proxy
Objects.”
In Release 12 DWG files, information about the original entity is not saved
in the file. However, the first Release 12 entity will have the same handle as
the original entity, and any additional Release 12 entities will have the orig-
inal entity handle placed in their xdata. (Look under the application name
ACAD, following the string data member R13OBJECT.) This feature is provided
so that you can group the Release 12 entities into a block.
Acad::ErrorStatus
AsdkPoly::getOsnapPoints(
AcDb::OsnapMode osnapMode,
int gsSelectionMark,
const AcGePoint3d& pickPoint,
const AcGePoint3d& lastPoint,
const AcGeMatrix3d& viewXform,
AcGePoint3dArray& snapPoints,
AcDbIntArray& /*geomIds*/) const
{
assertReadEnabled();
Acad::ErrorStatus es = Acad::eOk;
if (gsSelectionMark == 0)
return Acad::eOk;
if ( osnapMode != AcDb::kOsModeEnd
&& osnapMode != AcDb::kOsModeMid
&& osnapMode != AcDb::kOsModeNear
&& osnapMode != AcDb::kOsModePerp
&& osnapMode != AcDb::kOsModeCen
&& osnapMode != AcDb::kOsModeIns)
{
return Acad::eOk;
}
return es;
}
AcGeLineSeg3d lnsg(vertexArray[startIndex],
vertexArray[startIndex + 1]);
AcGePoint3d pt;
AcGeLine3d line, perpLine;
AcGeVector3d vec;
AcGeVector3d viewDir(viewXform(Z, 0), viewXform(Z, 1),
viewXform(Z, 2));
switch (osnapMode) {
case AcDb::kOsModeEnd:
snapPoints.append(vertexArray[startIndex]);
snapPoints.append(vertexArray[startIndex + 1]);
break;
case AcDb::kOsModeMid:
pt.set(
((vertexArray[startIndex])[X]
+ (vertexArray[startIndex + 1])[X]) * 0.5,
((vertexArray[startIndex])[Y]
+ (vertexArray[startIndex + 1])[Y]) * 0.5,
((vertexArray[startIndex])[Z]
+ (vertexArray[startIndex + 1])[Z]) * 0.5);
snapPoints.append(pt);
break;
case AcDb::kOsModeNear:
pt = lnsg.projClosestPointTo(pickPoint, viewDir);
snapPoints.append(pt);
break;
case AcDb::kOsModePerp:
// Create a semi-infinite line and find a point on it.
//
vec = vertexArray[startIndex + 1]
- vertexArray[startIndex];
vec.normalize();
line.set(vertexArray[startIndex], vec);
pt = line.closestPointTo(lastPoint);
snapPoints.append(pt);
break;
virtual Acad::ErrorStatus
AcDbEntity::moveGripPointsAt(
const AcDbIntArray& indices,
const AcGeVector3d& offset);
The osnapModes and geomIds arguments of the getGripPoints() function
are not currently used.
Stretch mode in grip editing allows you to stretch an object by moving
selected grips to new locations. AutoCAD calls the moveGripPointsAt() func-
tion when the user is in stretch mode. For certain entities, however, some
grips move the object rather than stretching it. These grips include grips on
text objects, blocks, midpoints of lines, centers of circles, centers of ellipses,
and point objects. In these cases, the moveGripPointsAt() function calls
transformBy().
When the user is in grip move, rotate, scale, or mirror modes, AutoCAD calls
the transformBy() function, described in chapter 6, “Entities.”
Acad::ErrorStatus
AsdkPoly::moveGripPointsAt(
const AcDbIntArray& indices,
const AcGeVector3d& offset)
{
if (indices.length()== 0 || offset.isZeroLength())
return Acad::eOk; //that’s easy :-)
if (mDragDataFlags & kCloneMeForDraggingCalled) {
mDragDataFlags &= kUseDragCache;
//if there’s more than one hot vertex or there's one and it is
//the center then simply transform.
if (indices.length()>1 || indices[0] == mNumSides)
return transformBy(AcGeMatrix3d::translation(offset));
AcGeVector3d off(offset);
virtual Acad::ErrorStatus
AcDbEntity::moveStretchPointsAt(
const AcDbIntArray& indices,
const AcGeVector3d& offset);
You are not required to override the getStretchPoints() and
moveStretchPointsAt() functions of AcDbEntity, because they default to the
getGripPoints() and transformBy() functions.
The custom AsdkPoly class overrides these functions as shown in the exam-
ple in this section. The getStretchPoints() function returns the vertices of
the polygon, but not the center. The moveStretchPointsAt() function
checks whether all the stretch points have been selected. If they have, it
invokes the transformBy() function. Otherwise, it invokes the
moveGripPointsAt() function.
Acad::ErrorStatus
AsdkPoly::getStretchPoints(
AcGePoint3dArray& stretchPoints) const
{
assertReadEnabled();
Acad::ErrorStatus es;
if ((es = getVertices3d(stretchPoints))
!= Acad::eOk)
{
return es;
}
// Remove the duplicate point at the start and end.
//
stretchPoints.removeAt(stretchPoints.length() - 1);
return es;
}
Acad::ErrorStatus
AsdkPoly::moveStretchPointsAt(
const AcDbIntArray& indices,
const AcGeVector3d& offset)
{
return moveGripPointsAt(indices, offset);
}
AcGeMatrix2d xform2d(xform.convertToLocal(mDragPlaneNormal,
mDragElevation));
mDragCenter = xform2d * center();
mDragStartPoint = xform2d * startPoint();
mDragPlaneNormal.normalize();
} else {
assertWriteEnabled();
AcGeMatrix2d xform2d(xform.convertToLocal(mPlaneNormal,
mElevation));
mCenter.transformBy(xform2d);
mStartPoint.transformBy(xform2d);
mPlaneNormal.normalize();
}
return Acad::eOk;
}
assert(pSpline != NULL);
pSpline->setPropertiesFrom(this);
ent = pSpline;
return es;
}
virtual Acad::ErrorStatus
intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
const AcGePlane& projPlane,
AcGePoint3dArray& points,
int thisGsMarker = 0,
int otherGsMarker = 0) const;
AcDbLine *pAcadLine;
Acad::ErrorStatus
AsdkPoly::intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
const AcGePlane& projPlane,
AcGePoint3dArray& points,
int /*thisGsMarker*/,
int /*otherGsMarker*/) const
AcGePlane polyPlane;
AcDb::Planarity plnrty;
getPlane(polyPlane,plnrty);
During this process, you need to be careful about how you call the
intersectWith() function of the argument and how you interpret the points
that are the results of intersection. For example, if the intersection type was
kExtendArg, you would want to change it to kExtendThis before calling
intersectWith() on the argument. Similarly, if the intersection is an appar-
ent intersection on a plane of projection, the points returned from the
Exploding an Entity
You must override the explode() function of a custom entity for the
AutoCAD commands BHATCH and EXPLODE to work. Your explode() func-
tion should break the entity down into less complex entities. If the resulting
entities are not native entities, your function should return eExplodeAgain.
This will cause BHATCH to recursively call the explode() function on the
entities that you return, until they have been reduced to native entities. The
native entities upon which BHATCH can operate directly are AcDb2dPolyline,
AcDb3dPolyline, AcDbPolyline, AcDbText, AcDbMText, AcDbShape,
AcDbTrace, AcDbSolid, AcDbFace, AcDbViewport, AcDbFcf, AcDbDimension,
AcDbRegion, AcDbBlockReference, and AcDbHatch.
Drag Loop
After you have set the display prompt for the drag sequence, you call the
AcEdJig::drag() function, which performs the drag loop until the user
presses ENTER or the space bar, or picks with the pointing device. The follow-
ing list describes the sequence of the drag loop:
1 The drag loop receives an event.
2 It calls the AcEdJig::sampler() function. The sampler() function sets up
the keyword list (if any) with a call to the AcEdJig::setKeywordList() func-
tion, a special cursor type (if desired) with a call to the
AcEdJig::setSpecialCursorType() function, and any user input controls
with a call to the AcEdJig::setUserInputControls() function. Next, it calls
one of the acquireXXX() functions to obtain a geometric value (an angle,
AcEdJig::drag()
get an event
<myJig>::sampler()
TS=AcEdJig::acquireXXX()
Yes
TS==kNoChange
No
Do you need No
to update the
drag image?
Yes
return TS
other than KNoChange
<myJig>::update()
<myJig>::entity()->worldDraw()
Keyword List
If you have keywords that are meaningful in the drag sequence, use the fol-
lowing function to specify them:
void
AcEdJig::setKeywordList(const char* kyWdList);
The keyword list is a single string in which each keyword is separated from
the others by spaces. The required characters are capitalized, and the remain-
der of each keyword is lowercase.
For example, “Close Undo” specifies two keywords. The DragStatus enum
associates values with each keyword. The first keyword is kKW1, the second is
kKW2, and so on. When you implement your AcEdJig class, you can use these
return values in your implementations of the sampler(), update(), and
entity() functions.
Display Prompt
The display prompt is the text shown on the command line during the drag
sequence. Use the following function to set the display prompt:
void
AcEdJig::setDispPrompt(const char* prompt);
Cursor Types
If you want to set a special cursor type, use the following function:
void
AcEdJig::setSpecialCursorType(AcEdJig::CursorType);
Cursor types
Cursor Description
kEntitySelect Single entity pick box; the entity is not actually selected in
this case. Entity selection is handled with acedSSGet()
kArrow Displays the arrow cursor used for dialog boxes in AutoCAD
This step is optional. The acquirePoint() functions allow you to specify this
alternate cursor. Setting the cursor type for the acquireDist() and
acquireAngle() functions has no effect. The acquireXXX() functions will
select a cursor for you if you don’t explicitly specify one.
■ kGovernedByOrthoMode
■ kNullResponseAccepted
■ kDontEchoCancelForCtrlC
■ kDontUpdateLastPoint
■ kNoDwgLimitsChecking
■ kNoZeroResponseAccepted
■ kNoNegativeResponseAccepted
■ kAccept3dCoordinates
■ kAcceptMouseUpAsPoint
■ kAnyBlankTerminatesInput
■ kInitialBlankTerminatesInput
Once you have established the keyword list, cursor type, and user input con-
trols, your sampler() function should call one of the following functions of
AcEdJig to obtain an angle, a distance, or a point:
DragStatus
AcEdJig::acquireAngle(double &ang);
DragStatus
AcEdJig::acquireAngle(
double &ang,
const AcGePoint3d &basePt);
DragStatus
AcEdJig::acquireDist(double &dist);
DragStatus
AcEdJig::acquireDist(
double &dist,
const AcGePoint3d &basePt);
DragStatus
AcEdJig::acquirePoint(AcGePoint3d &point);
DragStatus
AcEdJig::acquirePoint(
AcGePoint3d &point,
const AcGePoint3d &basePt);
Sample Code
This example creates a class that enables the user to create an ellipse by pick-
ing its center point and then dragging to select the desired major axis and
minor axis lengths. During the drag operations, the user will be able to see
what the ellipse looks like at any time.
NOTE If the user tries to make the minor axis longer than the major axis, the
ellipse will end up as a circle because the radius ratio cannot be larger than 1.0.
void
initApp()
{
acedRegCmds->addCommand("ASDK_VISUAL_ELLIPSE",
"ASDK_VELLIPSE", "VELLIPSE", ACRX_CMD_MODAL,
createEllipse);
}
void
unloadApp()
{
acedRegCmds->removeGroup("ASDK_VISUAL_ELLIPSE");
}
385
386
Proxy Objects
In This Chapter
14
This chapter describes proxy objects and the conditions ■ Proxy Objects Defined
■ Proxy Object Life Cycle
of their creation. It also discusses user encounters with
■ User Encounters with Proxy
proxies, displaying proxy entities, and editing proxy Objects
■ Displaying Proxy Entities
entities. The effect of unloading an application on
■ Editing Proxy Entities
custom objects and entities is discussed as well. ■ Unloading an Application
387
Proxy Objects Defined
A proxy object is an object AutoCAD creates in memory as a surrogate data
holder for a custom ObjectARX object. AutoCAD automatically creates proxy
objects when the application that defines the object is not loaded.
Proxies are created for both objects and entities. AutoCAD uses proxy objects
to provide read access to data in a custom object derived from AcDbObject or
AcDbEntity. Proxy objects also provide controlled edit capabilities for that
data. The parent application determines the extent of those edit capabilities
with the PROXY_FLAGS argument of the ACRX_DXF_DEFINE_MEMBERS macro.
The proxy object class AcDbProxyObject is derived from AcDbObject, and the
proxy entity class AcDbProxyEntity is derived from AcDbEntity. Both are
abstract base classes that cannot be instantiated and are included in the
ObjectARX API.
Proxy objects convert back to the original custom object whenever the par-
ent application is loaded. For example, if proxy objects are created at the
beginning of a drawing session, and the parent application is subsequently
loaded, the proxies are restored to custom objects.
Under special circumstances, proxies are written to files, but proxy objects
usually exist only in memory.
ObjectARX developers can affect the creation and control the modification
of proxies by using the ACRX_DXF_DEFINE_MEMBERS macro and the demand
loading features in AutoCAD (see “Demand Loading” on page 45). In
addition, they can use functions of the proxy object classes in their own
applications to manage proxies that AutoCAD creates for the custom objects
of other applications.
■ If the input and output files are the same file type (both DWG or DXF), no
translation operation is necessary, and the same data that was read in is
written out. The data stored in the proxy object is written to the output
file.
■ If the input and output files differ in file type (that is, DWG in and DXF
out, or vice versa), the format cannot be translated because the translation
function defined by the parent application is not present. The entire
proxy object is therefore written to the output file. When the file is subse-
quently read by AutoCAD, the proxy will either convert to a custom object
(in the presence of the parent application), or remain a proxy in memory
(in the absence of the parent application).
Option Value
kNoOperation 0
kEraseAllowed 0x1
kTransformAllowed 0x2
kColorChangeAllowed 0x4
Option Value
kLayerChangeAllowed 0x8
kLinetypeChangeAllowed 0x10
kLinetypeScaleChangeAllowed 0x20
kVisibilityChangeAllowed 0x40
kAllAllowedBits 0x7F
Note that kNoOperation means none of the other options listed here.
You can logically OR PROXY_FLAG options to permit a combination of editing
operations.
As proxy entities only encapsulate data below the AcDbEntity base class level,
any changes made to color, layer, linetype, linetype scale, and visibility will
be written out as part of the proxy entity data. Rigid body transformations
(such as move, scale, and rotate) cannot be applied until the parent
application is present. When a transformation is applied to a proxy, the
transformation is made to the graphics metafile, and a copy of the transfor-
mation matrix is saved in a custom record in the proxy entity’s extension
dictionary. If multiple transformations are performed, the matrix is updated
to reflect the cumulative transformation. When the custom entity is returned
to memory with its parent application, AutoCAD calls the entity’s
transformBy() function, passes it the transformation matrix data, and
removes the custom data storage record from the extension dictionary. In
effect, the transformation is deferred until the parent application is present
to apply the transformation to the custom entity.
Unloading an Application
When an application is unloaded and the appropriate cleanup operations
have been performed, custom objects and entities are transformed into proxy
objects. For this to occur, all custom classes must be removed from
ObjectARX at unload time with the deleteAcRxClass() function. For a
description of the requirements for making an application “unloadable,” see
chapter 3, “ObjectARX Application Basics.”
In This Chapter
15
This chapter describes how you can create reactors that ■ Notification Overview
■ Using Reactors
respond to different event types and register the reactors
■ Notification Use Guidelines
with the appropriate objects to receive notification.
393
Notification Overview
When an event occurs in the system, certain objects, called notifiers, auto-
matically relay the event to other objects. For example, when a user copies,
erases, or modifies an object or when a user issues an UNDO or REDO com-
mand, a corresponding notification for each event is automatically triggered.
The objects receiving the events are called reactors. A reactor must be
explicitly added to a notifier’s reactor list before it can receive events from the
notifier. A given notifier can have a number of reactors in its reactor list. The
reactor’s class definition includes various notification functions. When an
event occurs, the notifier automatically invokes the corresponding notifica-
tion function of each reactor in its reactor list.
Reactor Classes
Reactor classes are derived from AcRxObject, not AcDbObject. Because these
reactors are not database objects, ownership does not apply to them and they
don’t have object IDs.
Different kinds of reactors receive different types of notification events. A
database reactor (derived from AcDbDatabaseReactor) receives events related
to the state of the database—for example, when an object is appended to the
database, modified in the database, or erased. The reactor’s notifier is the
database, so it is added to the reactor list of the AcDbDatabase. An object reac-
tor (derived from AcDbObjectReactor) responds to events at the object level,
AcApDocManagerReactor
AcApLongTransactionReactor
AcDbDatabaseReactor
AcDbObjectReactor
AcDbEntityReactor
AcDbRasterImageDefFileAccessReactor
AcDbRasterImageDefTransReactor
AcEdInputContextReactor
AcRxDLinkerReactor
AcRxEventReactor
AcEditorReactor
AcTransactionReactor
NOTE When you copy an object, any persistent reactors attached to the
object are copied as well. Transient reactor attachments are not copied when an
object is copied.
Using Reactors
To use a transient reactor, derive a new class from one of the following base
classes:
AcRxDLinkerReactor
Monitors ObjectARX application loading and unloading.
AcEditorReactor
Monitors AutoCAD-specific events such as commands
and AutoLISP evaluations.
AcDbDatabaseReactor
Monitors creation, modification, and erasure of database
objects.
AcTransactionReactor
Monitors events related to the transaction manager—
start, abort, or end of a transaction.
■ cancelled()
■ copied()
■ erased()
■ goodbye()
■ openedForModify()
■ modified()
■ subObjModified()
■ modifyUndone()
■ modifiedXData()
■ unappended()
■ reappended()
■ objectClosed()
Each of these functions requires a pointer to the notifier of the event. The
base class, AcDbObjectReactor, has NULL implementations for all of these
functions. In your derived reactor class, implement the functions corre-
sponding to the type of notifications you are interested in. Then instantiate
the reactor and add it to any number of database objects using the
AcDbObject::addReactor() function. To add or delete a transient reactor to
a notifier object, the object can be open in any state (read, write, or notify).
Adding or deleting a transient reactor is not monitored by the undo mecha-
nism. (For persistent reactors, the notifier object must be opened for write,
and adding or removing the reactors is monitored by the undo mechanism.)
Because you created the transient reactor object, you are also responsible for
deleting it.
Custom Notifications
When modifications are committed on an object, the object is closed, which
invokes the subClose() virtual function of AcDbObject. In the override of
this function in your custom class, you can notify others that you are closing
after modification. These notifications should be your custom notification in
the form of custom functions on your class. Do not use the notifications pro-
vided on AcDbObjectReactor for this purpose.
AcDbObjectReactor *pObjReactor;
AcDbObjectId persObjId;
AcDbObject *pPersReacObj;
pReactors = pEnt->reactors();
AsdkPersReactor *pTmpPers;
if ((pTmpPers =
AsdkPersReactor::cast((AcRxObject*)
pPersReacObj)) != NULL)
{
pTmpPers->custom();
}
pPersReacObj->close();
} else {
// Or is it transient?
//
pObjReactor = (AcDbObjectReactor *)
(pReactors->at(i));
acutPrintf("\n\nTransient Reactor found");
if (!pLine) {
const char* cstr = pObj->isA()->name();
acutPrintf("This is a %s.\n", cstr);
acutPrintf("I only work with lines. Sorry.\n");
return;
}
if (acdbOpenObject((AcDbObject*&)pLine2, mId,
AcDb::kForWrite) == Acad::eOk)
{
// Get length of line entity we’re being notified
// has just been modified.
//
AcGePoint3d p = pLine->startPoint();
AcGePoint3d q = pLine->endPoint();
AcGeVector3d v = q-p;
double len = v.length();
if (pNamedObj->getAt("ASDK_DICT",
(AcDbObject*&)pNameList, AcDb::kForWrite)
== Acad::eKeyNotFound)
{
pNameList = new AcDbDictionary;
AcDbObjectId DictId;
pNamedObj->setAt("ASDK_DICT", pNameList, DictId);
}
pNamedObj->close();
if ((pNameList->getAt("object_to_notify_A", objId))
== Acad::eKeyNotFound)
{
pNameList->setAt("object_to_notify_A", pObj, objId);
pObj->close();
} else {
delete pObj;
acutPrintf("object_to_notify_A already exists\n");
}
pNameList->close();
■ modified()
■ subObjModified()
■ erased()
■ modifyUndone()
■ modifiedXData()
■ unappended()
■ reappended()
■ graphicsModified()
AcTransactionReactor::transactionEnded(
int numActiveAndSuccessful);
The objectClosed() notification is sent when the object is completely closed
and the pointer is no longer valid. You can open the object again using the
ID that is passed in the argument and operate on it. Be careful not to create
infinite notification loops at this point.
In the transactionEnded() notification, you can use the
numActiveTransactions() function to query the transaction manager to see
how many transactions are active. If there are no active transactions, the
transaction has ended and all the objects in the transaction have been
committed.
Sometimes you may need to know when the outermost transaction is ending
and the commit process is beginning. Use the following notification for this
purpose:
AcTransactionReactor::endCalledOnOutermostTransaction()
When the outermost transaction ends, the commit process begins and
close() is called on each object. You might receive objectClosed() notifica-
tion as part of this close. However, it’s generally best not to act immediately.
Instead, wait until the whole transaction is finished before you perform any
operations on these objects.
In This Chapter
16
AutoCAD supports a multiple document interface ■ Overview
■ Terminology
(MDI) that allows you to have more than one drawing
■ SDI System Variable
loaded at once in a single AutoCAD session. This ■ Levels of Compatibility
chapter describes how to work with the MDI in your ■ Interacting with Multiple
Documents
ObjectARX application. ■ Document Event Notification
■ Application-Specific Document
Objects
■ Nonreentrant Commands
■ Multi-Document Commands
■ Disabling Document Switching
■ Application Execution Context
■ Database Undo and Transaction
Management Facilities
■ Document-Independent
Databases
■ An MDI-Aware Example
Application
415
Overview
AutoCAD supports a multiple document interface, and ObjectARX
applications running within AutoCAD must operate properly in the MDI
environment. Three principles must be observed for an ObjectARX
application to provide MDI support:
Data Instances
There is a separate instance of all data elements related to the database and
current command processing state for each document. This includes the
command processor, input processor, Visual LISP environment, databases,
selection sets, and (most, but not all) system variables. The current command
processing state is maintained in a heap. In addition to these built-in system
elements, all ObjectARX applications must also maintain a document-
specific state either on the stack or in structures on the heap that correspond
to each active document.
Document Locking
All documents must be locked in order to be modified. Documents may also
be locked to prevent code in other execution contexts from modifying them
temporarily. Documents do not have to be locked to perform query (read)
operations, and they are never prevented from performing query operations
on other documents. Basic document locking is handled automatically for
AutoCAD commands, ObjectARX commands, and AutoLISP functions.
Modeless dialog and toolbar code, and any commands that need to work out-
side the active document must manually perform document locking.
For more detailed information on document locking, see “Explicit Document
Locking” on page 425.
AcApDocument
The AcApDocument object contains information such as the filename, MFC
CDocument object, current database, and save format of the current drawing.
Additionally, the AcApDocument object contains functions that query the sta-
tus of document locking.
AcApDocManager
The AcApDocumentManager object contains all the document objects in an
application (there is one document object for each drawing that is open and
being edited). There is only one instance, which can be obtained using the
macro acDocManager().
AcApDocumentIterator
The AcApDocumentIterator class provides the ability to iterate over the set of
currently open AcApDocument objects.
Overview | 417
AcApDocManagerReactor
The AcApDocManagerReactor class provides a reactor that applications can
use to track modifications in documents and switches between documents.
For more information on the document management classes, see the
ObjectARX Reference. An example of using these classes is given later in this
chapter.
Terminology
The following section defines some commonly used terms to describe the
multiple document interface.
Active Document
The document that has window focus, and receives the next user input
event, unless the user is switching to (activating) another document. The
active document is always the current document when processing user input,
but programs can temporarily change the current document during some
operations.
Application
The overall running program and associated objects that are common to all
open documents, such as the MFC class CwinApp. There is one and only one
application per invocation of an executable Windows program.
Application Context
Short for “application execution context.” See “Execution Context, Applica-
tion” on page 421.
Command
Throughout this chapter, the term “command” refers to a variety of
AutoCAD constructs. A command consists of a program sequence performed
as a logical unit of work that can be requested by the user or one of the
AutoCAD scripting engines. No matter what construct is used, a command
can be undone independently of other actions performed during system
operation.
Command, Multi-Document
A set of commands that appears as one command to the user, during which
the user can change the current document and a continued logical flow of
user prompting is maintained. For example, if an active command is
prompting for user input in the current document and the user switches the
document, the application cancels the command in the old current docu-
ment, and queues up a command to commence execution in the new current
document.
Command, Nonreentrant
A command that cannot be executed in more than one document at a time.
Nonreentrancy can be used for commands that should not be available for
more than one document at a time, or when the demands of supporting mul-
tiple instantiation are too great to be worth the overhead.
Command Processor
The standard input message polling mechanism in AutoCAD that facilitates
combined keyboard and digitizer interaction. A separate command processor
exists for each open document. The state of the command processor is main-
tained as an execution context.
Terminology | 419
NOTE Commands that execute outside the context of a single document,
such as modeless dialogs and toolbars posted by AutoCAD or ObjectARX appli-
cations, execute from within the application context.
Current Document
Programmatic requests can be made to cause a document’s execution context
to become active without the user actually perceiving the document as “acti-
vated.” This is a transient state only, used primarily by ActiveX and VBA
applications.
Database
An AutoCAD drawing, specifically an instance of AcDbDatabase. Although
the database is part of a document, it is not synonymous with a document.
Document
A document consists of an MDI document window, an execution context, an
associated editor state, and a single current database, plus any number of side
databases that are opened in association with it. The current database is the
one being displayed and edited via commands. The side databases are either
used by xref or for general use. The document also includes system variables
that are associated with a given drawing such as the current viewport vari-
able. Documents are uniquely identified by their address, which is the
AcApDocument* pointer.
Drawing
Synonymous with database.
Edit Session
Usually synonymous with document, but sometimes includes its entire
history since the document was opened, as well as the current state of the
session.
MDI-Aware
ObjectARX applications (and ActiveX and COM applications, but not neces-
sarily Visual LISP applications) that meet all criteria needed to be successfully
executed in an MDI AutoCAD. These criteria are listed in the section “MDI-
Aware Level” on page 424. ObjectARX applications can register themselves as
MDI-Aware by calling
acrxDynamicLinker->registerAppMDIAware(pkt);
when receiving the kInitAppMsg within their acrxEntryPoint() function.
Per-Application
A data structure that needs to exist only once per application.
Per-Context
A data structure that needs to be instantiated and maintained for each exe-
cution context, including document execution contexts and the application
execution context. The AutoCAD command processor is an example of a per-
context instantiation.
Per-Document
Any data structure, value, or other item that needs to be instantiated and
maintained for each document.
Terminology | 421
Quiescent
When the command processor in a given edit session has no active AutoCAD
commands, ObjectARX commands, Visual LISP evaluations, ActiveX
requests, AutoCAD menu macros, or VBA macros. At this point, the
Command prompt is being displayed in the command window. Notice that
modeless dialogs and toolbars can be posted, as they do not operate through
the command processor.
Session
Synonymous with application.
Undo Stack
A repository of state recorded during an edit session, which is used to undo
the edit session command by command, when requested. Databases are
often associated with an undo stack, which is supplied by the host applica-
tion. In the case of AutoCAD, databases are typically opened under only one
document at a time, because undo stacks correspond to documents.
Value Meaning
0 MDI is enabled
NOTE This compatibility mode and the SDI variable will be removed in the
next full release of AutoCAD.
Levels of Compatibility
Your ObjectARX application can have one of four levels of compatibility with
MDI:
■ SDI-Only
■ MDI-Aware
■ MDI-Capable
■ MDI-Enhanced
SDI-Only Level
This is the basic level of compatibility and is not sufficient for most robust
applications. This level will not allow your application to run under MDI, but
it should work without failing under SDI.
MDI-Aware Level
This level of compatibility provides the minimal requirements for an
ObjectARX application to function in an MDI environment without failing.
An ObjectARX application must meet the requirements of this level of com-
patibility before being able to legitimately set the SDI system variable to 0.
The following list summarizes the minimum requirements.
Per-Document Data
Applications cannot keep per-document data in global or static variables.
This includes AcDbDatabase objects, AcDbObject objects, AcDbObjectId val-
ues, header variable values, document variable values, selection sets, and
other document-specific information. Any occurrence of per-document data
in global and static variables might be corrupted if your application is run in
multiple concurrent edit sessions.
To avoid corruption of data, you can encapsulate command behavior and
data into classes. An instance of the class can be instantiated for each call to
the command. As the command acquires document-specific data, it keeps its
own per-instance copies of that data.
Another solution is to encapsulate all of the global and static data into a
structure or class. A copy of the data is instantiated for each document. A
local pointer to the appropriate instance is set at each entry point in the
application. The local pointer is then used to access the per-document data.
■ Read only
■ Exclusive read
■ Shared write
■ Exclusive write
AutoLISP Commands
AutoLISP commands must be aware that there is a separate AutoLISP stack for
each document. This means that AutoLISP variables should be handled in
the same way as other per-document global and static data. For more infor-
mation, see “Per-Document Data” on page 424.
The AcRx::kLoadDwgMsg message in acrxEntryPoint() is sent for each
document open when an application is first loaded, and when any new doc-
uments are opened while the application is running. The messages are sent
from the corresponding document’s execution context.
MDI-Capable Level
Reaching this level of compliance involves making your code work as effi-
ciently and naturally in MDI mode as it does (or did) in SDI mode.
MDI-Enhanced Level
These additional steps will make your application integrate completely with
the MDI.
NOTE The current document is not always the active document. This is the
case during transitional states, such as when the documentToBeActivated()
reactor occurs. Do not attempt extensive processing during transitional states.
Consider using mdiActiveDocument() if you are interested in the active
document.
From the current document, you can determine the current database, the
relevant transaction manager, and your application’s associated document-
specific state, and then do whatever needs to be done before returning.
Once a command has stored the current document and associated informa-
tion on its stack, it does not need to query the current document again until
completion. Whenever a prompt for user input is made, the user can switch
When the active and current documents are different, all user input func-
tions and members concerning the document, such as the graphics screen,
will be disabled. This includes functions for both ObjectARX and ActiveX
applications.
Whenever you set the current document without also activating it, the
setCurDocument() caller should restore the current document to be the same
as the active document when finished. However, if this is not done by the
time that the next input event is processed, the current document will be
switched back to the active document.
Nonreentrant Commands
Nonreentrant commands are those commands that cannot be invoked in
one document when already in progress in a different document. This should
be regarded as a way to make your application MDI-Aware quickly, without
having to isolate all of your command-specific state information.
■ TABLET
■ NEW
■ OPEN
The TABLET command is restricted because the user is defining how the
pointing device is going to work, so that operation needs to be finished
before anything else can be done. Because NEW and OPEN have to open a
window, and then ask for a name (when FILEDIA=0), switching to another
window at that moment could be error-prone.
Multi-Document Commands
A multi-document command allows users to switch documents at selected
user prompts, while the command remains in control. The ability to have a
single command remain active across documents is very complex. At the
point of execution when a user has picked another document to switch to,
all documents are polling for input. They therefore have potentially intricate
command processor states established, including possibly nested commands,
AutoLISP expressions, scripts active, and all in arbitrary nesting order.
Also, to manage the flow of control across documents, this callback should
maintain whatever transition state the application needs. For example, a
nonreentrant variant could remember the original document, and a flag for
each document to indicate whether it is already active, and therefore not
have to invoke sendStringToExecute().
When a multi-document command completes, the controlling application
should be sure the command left no pending command states in previous
documents. The application can do this by sending an ESC or ENTER to the
documents it has traversed through, by using the bWrapUpInactiveDoc
parameter of sendStringToExecute(). If this is not done, documents may be
left in a non-quiescent state.
Coordination between the initial command and the secondary command
(and possibly multiple invocations thereof) must be managed through static
or heap-resident variables.
Both the initial and secondary commands should be registered through
acedRegCmds() or acedDefun(). The initial command should be coded to
complete successfully on its own, in case the user decides to perform the
entire command without switching documents. The second command need
not be a full command, just a portion of a command that can be invoked to
accumulate information (rooted in a static structure) from different open
documents, and apply the results. The second command may also have to be
coded such that it can be reentered in yet another document, if that is how
the command is supposed to be structured.
Remember that UNDO runs separately in each document when designing
these constructs.
■ PLOT
■ REGEN
■ RENDER
■ HIDE
■ SHADE
■ NEW
■ OPEN
■ TABLET
■ When the execution context your code is running under is not implicit in
your code structure, you can make this query to find if it is the application
execution context:
Adesk::Boolean
AcApDocManager::isApplicationContext() const;
■ All ActiveX user input members may be used, but make sure that you are
invoking them on the utility object associated with the active and current
document. As noted above, document switching will be disabled when
user input is obtained in this context. You can obtain the IAcadDocument*
instance that corresponds to the current AcApDocument via the call:
acDocManager()->curDocument()->cDoc()->GetIDispatch(
BOOL bAddRef);
■ All ObjectARX user input functions may be called, with the current active
document being implicitly used. As noted above, document switching
will be disabled when user input is obtained in this context.
■ Application code executing from the application context can use the
following member function to switch either the current and active docu-
ment, together or individually, as desired.
virtual Acad::ErrorStatus
setCurDocument(
AcApDocument* pDoc,
AcAp::DocLockMode = AcAp::kNone,
bool activate = false) = 0;
■ By alternating between prompting for user input and changing or
activating the current document, one can prompt for input from multiple
documents from a single execution context and a single sequence of code.
The drawback is that document switching by the user is disabled when
Document-Independent Databases
To participate in undo in AutoCAD, databases must be associated with a doc-
ument, because each document has an independent undo stack. However,
this feature is in direct conflict with the need to load databases whose con-
tents are intended to be shared across document edit sessions. In other
words, you must decide between the following two scenarios for your side
databases:
class AsdkDbReactor;
class AsdkPerDocData
{
friend class AsdkAppDocGlobals;
public:
AsdkPerDocData(AcApDocument* pDoc);
private:
AcApDocument* m_pDoc;
AsdkPerDocData* m_pNext;
long m_EntAcc; // Entity count
AsdkDbReactor* m_pDbr;// Pointer to database reactor
};
AsdkAppDocGlobals *gpAsdkAppDocGlobals;
AcDbHandle objHand;
char handbuf[17];
acutPrintf(
"\n (class==%s, handle==%s, id==%lx, db==%lx)",
pObj->isA()->name(), handbuf,
pObj->objectId().asOldId(), pObj->database());
}
void
AsdkDocReactor::documentCreated(AcApDocument *pDoc)
{
gpAsdkAppDocGlobals->setGlobals(pDoc);
}
void
AsdkDocReactor::documentToBeDestroyed(AcApDocument *pDoc)
{
gpAsdkAppDocGlobals->removeDocGlobals(pDoc);
}
AsdkPerDocData::AsdkPerDocData(AcApDocument *pDoc)
{
m_pDoc = pDoc;
m_pNext = NULL;
m_EntAcc = 0;
m_pDbr = NULL;
}
AsdkAppDocGlobals::AsdkAppDocGlobals(AcApDocument *pDoc)
{
m_pData = m_pHead = NULL;
m_pDocReactor = new AsdkDocReactor();
acDocManager->addReactor(m_pDocReactor);
}
long &
AsdkAppDocGlobals::entityCount()
{
return m_pData->m_EntAcc;
}
void
AsdkAppDocGlobals::incrementEntityCount()
{
m_pData->m_EntAcc++;
}
void
AsdkAppDocGlobals::decrementEntityCount()
{
m_pData->m_EntAcc--;
}
AsdkDbReactor *
AsdkAppDocGlobals::dbReactor()
{
return m_pData->m_pDbr;
}
acedRegCmds->addCommand("ASDK_NOTIFY_TEST",
"ASDK_CLEAR",
"CLEAR",
ACRX_CMD_TRANSPARENT,
clearReactors);
break;
case AcRx::kUnloadAppMsg:
if (gpAsdkAppDocGlobals != NULL)
{
gpAsdkAppDocGlobals->unload();
delete gpAsdkAppDocGlobals;
gpAsdkAppDocGlobals = NULL;
}
acedRegCmds->removeGroup("ASDK_NOTIFY_TEST");
break;
}
return AcRx::kRetOK;
}
In This Chapter
17
This chapter describes the transaction model, which can ■ Overview of Transaction
Management
be used to operate on AcDb objects. In this model, mul-
■ Transaction Manager
mechanism described in chapter 5, “Database Objects.” ■ Mixing the Transaction Model with
the Open and Close Mechanism
■ Transactions and Graphics
Generation
■ Transaction Reactors
■ Example of Nested Transactions
449
Overview of Transaction Management
The transaction model encapsulates multiple operations on multiple objects
by several clients as one atomic operation called a transaction. Inside a trans-
action boundary, clients can obtain object pointers from object IDs. Object
pointers thus obtained are valid until the transaction is ended or aborted by
the client. If the transaction is ended successfully, operations on the objects
are committed. If the transaction is aborted, operations on the objects are
canceled.
Operating on objects using this paradigm has several advantages over the
regular open and close mechanism described in chapter 5, “Database
Objects.” The open and close mechanism is suitable for simple operations on
a single object or a small group of objects. However, there are certain restric-
tions on opening objects this way. For example, if an object is open for read,
you cannot open it for write at the same time. If an object is open for write,
you cannot open it for write a second time. For a list of conflict errors asso-
ciated with the open and close mechanism, see chapter 5, “Database
Objects.” The transaction model is more lenient, and obtaining object
pointers from object IDs for a particular mode usually succeeds if the object
is associated with a transaction.
Depending upon your application, there can be other disadvantages to using
the open and close mechanism. If your application opens and closes the
same object a number of times in the course of one operation—for example,
a command—you’ll incur serious inefficiencies due to these multiple opens
and closes. A number of time-consuming operations are associated with clos-
ing an object. If you open an object for write, modify it, and then close it, the
undo records of the modification are committed to the undo file, graphics for
the object are generated, and notifications are fired. All these operations are
performed every time the object is closed. If you transact your operation and
obtain a pointer to the object using a transaction, all the activities mentioned
above happen only once, at the end of the transaction. The result is
improved efficiency and a smaller undo file, because the number of records
going into the undo file is reduced.
Also, if you have a complex network where objects refer to each other by
object ID, you want to be able to obtain a pointer to an object in any module
of your program without worrying if another module or another application
has that object opened. These activities are only possible using the transac-
tion model because transactions group operations and allow obtaining
pointers from object IDs across module boundaries.
Nesting Transactions
Transactions can be nested—that is, you can start one transaction inside
another and end or abort the recent transaction. The transaction manager
maintains transactions in a stack, with the most recent transaction at the top
of the stack. When you start a new transaction using
AcTransactionManager::startTransaction(), the new transaction is added
to the top of the stack and a pointer to it is returned (an instance of
AcTransaction). When someone calls
AcTransactionManager::endTransaction() or
AcTransactionManager::abortTransaction(), the transaction at the top of
the stack is ended or aborted.
When object pointers are obtained from object IDs, they are always
associated with the most recent transaction. You can obtain the recent trans-
action using AcTransactionManager::topTransaction(), then use
When nested transactions are started, the object pointers obtained in the
outer containing transactions are also available for operation in the inner-
most transaction. If the recent transaction is aborted, all the operations done
on all the objects (associated with either this transaction or the containing
ones) since the beginning of the recent transaction are canceled and the
objects are rolled back to the state at the beginning of the recent transaction.
The object pointers obtained in the recent transaction cease to be valid once
it’s aborted.
If the innermost transaction is ended successfully by calling
AcTransactionManager::endTransaction(), the objects whose pointers
were obtained in this transaction become associated with the containing
transaction and are available for operation. This process is continued until
the outermost (first) transaction is ended, at which time modifications on all
the objects are committed. If the outermost transaction is aborted, all the
operations on all the objects are canceled and nothing is committed.
Transaction Boundaries
Because you, not the system, are in charge of starting, ending, or aborting
transactions, it’s important to be aware of transaction boundaries. A transac-
tion boundary is the time between the start and end or abort of a transaction.
It’s recommended that you confine your boundary to the smallest possible
scope. For example, if you start a transaction in a function, try to end or abort
the transaction before you return from that function, because you may not
have knowledge of the transaction outside of the function. You need not
follow this rule if you maintain some kind of a global manager for your trans-
action activities, but you still are responsible for aborting or ending all the
transactions you start.
Multiple applications can use transaction management for their work, and
operations on objects are committed at the end of the outermost transaction.
Therefore, an AutoCAD command boundary is as far as you can stretch the
boundary of your transactions. When a command ends, there should not be
any active transactions. If there are any active transactions (the transaction
stack is not empty) when a command ends, AutoCAD will abort. As an excep-
tion, transactions can still be active when an acedCommand() or a transparent
■ ARX
■ DXFIN
■ INSERT
■ NEW
■ OPEN
■ PURGE
■ QUIT
■ RECOVER
■ REDO
■ SAVE
■ SCRIPT
■ U
■ UNDO
■ XREF
Commit-Time Guidelines
When the outermost transaction ends, the transaction manager fires an
endCalledOnOutermostTransaction() notification (see “Transaction Reac-
tors” on page 456) and begins the commit process, in which modifications
on all the objects associated with the transaction are committed to the data-
base. Each object is committed individually, one after another, until all of
them are committed. During this operation, do not modify any of the objects
involved in the commit process and do not start any new transactions. If you
do so, AutoCAD will abort with the error message eInProcessOfCommitting.
You can modify individual objects after each has been committed, but it is
recommended that you cache the IDs of the objects you want to modify and
Transaction Reactors
The transaction manager has a list of reactors through which it notifies
clients of events relevant to the transaction model. Currently, there are four
events that send notification:
virtual void
transactionStarted(
int& numTransactions);
virtual void
transactionEnded(
int& numTransactions);
virtual void
transactionAborted(
int& numTransactions);
virtual void
endCalledOnOutermostTransaction(
int& numTransactions);
The first three notifications are fired when any transaction, including nested
ones, is started, ended, or aborted. You can use these notifications in con-
junction with AcTransactionManager::numActiveTransactions() to
determine the transaction that is relevant to the notification. For example, if
a call to AcTransactionManager::numActiveTransactions() returns zero in
your override of AcTransactionReactor::transactionEnded() or
AcTransactionReactor::transactionAborted(), you know the outermost
transaction is ending or aborting.
The endCalledOnOutermostTransaction() notification signals the
beginning of the commit process of all the modifications done in all the
transactions. You can use this callback to do any necessary cleanup work
before commit begins.
The argument in all the notifications represents the number of transactions
that are active plus the ones that have completed successfully. It doesn’t
include the transactions that were started and aborted.
■ Select the polygon and obtain a pointer to it. Open it for read.
■ Create an extruded solid using the polygon.
■ Create a cylinder in the middle of the extended polygon.
3 Start Transaction 2: Subtract the cylinder from the extrusion (creates a hole
in the middle of the solid).
4 Start Transaction 3:
■ Slice the shape in half along the X/Z plane and move it along the X axis
so you can view the two pieces.
■ Abort the transaction? Answer yes.
5 Start Transaction 3 (again): Slice the shape in half along the Y/Z plane and
move it along Y.
6 End Transaction 3.
7 End Transaction 2.
NOTE If you abort at this point, Transactions 2 and 3 are both canceled. If you
abort a containing transaction, all the nested transactions are aborted, even if
they were successfully ended.
8 End Transaction 1.
// Start a transaction.
//
AcTransaction *pTrans
= actrTransactionManager->startTransaction();
assert(pTrans != NULL);
acutPrintf("\n\n###### Started transaction one."
" ######\n");
for (;;) {
switch (acedEntSel("\nSelect a polygon: ",
ename, pickpt))
{
case RTNORM:
acdbGetObjectId(objId, ename);
assert(pObj != NULL);
pPoly = AsdkPoly::cast(pObj);
if (pPoly == NULL) {
acutPrintf("\nNot a polygon. Try again");
continue;
}
break;
case RTNONE:
case RTCAN:
actrTransactionManager->abortTransaction();
return;
default:
continue;
}
break;
}
assert(pExtrusion != NULL);
assert(pCylinder != NULL);
pExtrusion->booleanOper(AcDb::kBoolSubtract, pCylinder);
pExtrusion->draw();
acutPrintf("\nSubtracted the cylinder from the"
" extrusion.");
actrTransactionManager
->addNewlyCreatedDBRObject(pOtherHalf);
pOtherHalf->draw();
pExtrusion->draw();
acutPrintf("\nSliced the resulting solid into half"
" and moved one piece.");
pExtrusion->getSlice(sectionPlane, Adesk::kTrue,
pOtherHalf);
assert(pOtherHalf != NULL);
mat.setCoordSystem(moveBy, x, y, z);
pOtherHalf->transformBy(mat);
addToDb(pOtherHalf, otherHalfId);
actrTransactionManager
->addNewlyCreatedDBRObject(pOtherHalf);
pOtherHalf->draw();
pExtrusion->draw();
// Now, give the user the option to end all the transactions.
//
yes = Adesk::kFalse;
yes = Adesk::kFalse;
if (getYOrN("\nAbort transaction one? <No> : ",
Adesk::kFalse, yes,interrupted) == Acad::eOk
&& yes == Adesk::kTrue)
{
acutPrintf("\n\n$$$$$$ Aborting transaction one."
" $$$$$$\n");
actrTransactionManager->abortTransaction();
acutPrintf("\nBack to just the Poly.");
} else {
actrTransactionManager->endTransaction();
acutPrintf("\n\n>>>>>> Ending transaction one."
" <<<<<<\n");
}
}
static Acad::ErrorStatus
createAndPostPoly()
{
int nSides = 0;
while (nSides < 3) {
acedInitGet(INP_NNEG, "");
switch (acedGetInt("\nEnter number of sides: ",
&nSides))
{
case RTNORM:
if (nSides < 3)
acutPrintf("\nNeed at least 3 sides.");
break;
default:
return Acad::eInvalidInput;
}
}
startPt[0] = center[0];
startPt[1] = center[1];
startPt[2] = center[2];
pPoly->setDatabaseDefaults(
acdbHostApplicationServices()->workingDatabase());
postToDb(pPoly);
return Acad::eOk;
}
for (;;) {
switch (acedEntSel(prompt, ename, pickpt)) {
case RTNORM:
AOK(acdbGetObjectId(objId, ename));
if (objId != checkWithThisId) {
acutPrintf("\n Select the proper"
" solid.");
continue;
}
AOK(pTransaction->getObject(pObj, objId, mode));
assert(pObj != NULL);
pSolid = AcDb3dSolid::cast(pObj);
if (pSolid == NULL) {
acutPrintf("\nNot a solid. Try again");
AOK(pObj->close());
continue;
}
break;
case RTNONE:
case RTCAN:
return Acad::eInvalidInput;
default:
continue;
}
break;
}
return Acad::eOk;
}
In This Chapter
18
This chapter describes the deep clone functions, ■ Deep Clone Basics
■ Implementing deepClone() for
which copy an object or any objects owned by the
Custom Classes
AcDbDatabase::deepCloneObjects() function, as
467
Deep Clone Basics
The deep clone functions copy an object and its ownership references. Any
pointer references are ignored. The wblock clone function copies hard own-
ers and hard pointers and ignores the soft references. In addition to copying
this hierarchy of owned objects, both the deep clone functions and the
wblock clone functions also handle the cloned object’s references, translat-
ing the references to point to new objects if necessary.
To initiate a cloning operation, use one of the following functions:
AcDbDatabase::deepCloneObjects()
AcDbDatabase::wblock()
AcDbDatabase::insert()
AcDbDatabase::deepCloneObjects() only supports cloning within a single
database. If you need to clone objects between databases, use either
wblock(), insert(), or a combination of both (such as wblock() to a tempo-
rary database, and then insert() that database into an existing destination
database).
When using AcDbDatabase::insert(), only insert to destination databases
that have already been built. You can obtain a fully built (and possibly fully
populated) destination database by using the current drawing to build a new
database with a constructor parameter of Adesk::kTrue or by creating an
empty new database using a constructor parameter of Adesk::kFalse and
then calling AcDbDatabase::readDwgFile() on it to fill it in.
In general, to use AcDbDatabase::deepCloneObjects(),
AcDbDatabase::wblock(), or AcDbDatabase::insert() functions in your
code, you do not need to be aware of how the object ID map is filled in or
exactly what happens during each stage of deep cloning. If you are creating
a new class and you want to override the AcDbObject::deepClone() or
AcDbObject::wblockClone() functions, you’ll need to be familiar with the
details of those functions, which are described in “Implementing
deepClone() for Custom Classes” on page 476.
The AcDbObject::deepClone() and AcDbObject::wblockClone() functions
should not be called directly on a custom object in application code. They
are only called as part of a chain from a higher-level cloning operation.
deep clone
if (pObj->ownerId() == ownerId)
objList.append(objId);
else if (i == 0) {
ownerId = pObj->ownerId();
objList.append(objId);
}
pObj->close();
}
acedSSFree(sset);
AcDbObjectId modelSpaceId;
pBlockTable->getAt(ACDB_MODEL_SPACE, modelSpaceId);
pBlockTable->close();
objListPS.append(objId);
acdbHostApplicationServices()->workingDatabase()->
deepCloneObjects(mslist, modelSpaceId, idMap, Adesk::kTrue);
acdbHostApplicationServices()->workingDatabase()->
deepCloneObjects(pslist, paperSpaceId, idMap);
At this point, cloning concludes and all the references are translated. The fol-
lowing code deep clones objects belonging to different owners:
void
cloneDiffOwnerObjects()
{
// Step 1: Obtain the set of objects to be cloned.
// For the two owners we’ll use model space and
// paper space, so we must perform two acedSSGet() calls.
// calls.
//
acutPrintf("\nSelect entities to be cloned to"
" Model Space");
ads_name ssetMS;
acedSSGet(NULL, NULL, NULL, NULL, ssetMS);
long lengthMS;
acedSSLength(ssetMS, &lengthMS);
acutPrintf("\nSelect entities to be cloned to"
" Paper Space");
ads_name ssetPS;
if (acedSSGet(NULL, NULL, NULL, NULL, ssetPS)
!= RTNORM && lengthMS == 0)
{
acutPrintf("\nNothing selected");
return;
}
long lengthPS;
acedSSLength(ssetPS, &lengthPS);
if (pObj->ownerId() == ownerId)
objListMS.append(objId);
else if (i == 0) {
ownerId = pObj->ownerId();
objListMS.append(objId);
}
pObj->close();
}
acedSSFree(ssetMS);
if (pObj->ownerId() == ownerId)
objListPS.append(objId);
else if (i == 0) {
ownerId = pObj->ownerId();
objListPS.append(objId);
}
pObj->close();
}
Cloning Phase
During the cloning phase, when you call deepClone() on an object,
AutoCAD checks to see if the cloned object (the primary object) owns any
other objects. If it does, it calls deepClone() on those objects as well. This
process continues until all owned objects have been cloned. Both hard and
soft ownership connections are followed.
When you call wblockClone() on an object, AutoCAD follows hard owner
and hard pointer connections and calls wblockClone() on those objects as
well.
Translation Phase
For both the deep clone and wblock clone functions, objects that are refer-
enced by the primary object are also translated. After the objects are copied,
AutoCAD translates the references as described in the following three cases.
■ Case 1: If the referenced object has been copied, the old reference is trans-
lated to refer to the copied object. In this case, it does not matter if the
copied object is in the same database as the source objects or in a new
database.
■ Case 2: This case assumes that the source object and the copied object
reside in the same database. If the referenced object has not been copied,
the reference is left in place.
■ Case 3: This case assumes that the source object and the copied object are
in different databases. If the referenced object has not been copied, the ref-
erence to it is set to NULL (because it isn’t in the destination database).
EntityA1 EntityB1
EntityA2 EntityB2
pointers
cloned objects
EntityA1 EntityB1
EntityA2 EntityB2
EntityA1 EntityB1
pointers
EntityA1 EntityB1
EntityA2
EntityA1 EntityB1
DB1
pointers DB2
EntityA1 EntityB1
DB1
DB2
EntityA2 NULL
if (es == Acad::eKeyNotFound)
return;
AcDbDictionary *pSrcDict;
acdbOpenObject(pSrcDict, dictId, AcDb::kForRead);
AcDbObject *pClone;
pDestNamedDict->setAt(kpDictionary,
pClone, dictId);
pDestNamedDict->close();
pClone->close();
break;
}
pDestNamedDict->close();
if (pObjClone == NULL)
continue;
pObjClone->close();
}
delete pIter;
pDestDict->close();
break;
default:
break;
}
pSrcDict->close();
}
During the cloning stage in this example, information about the old object
is copied to the new object using a specific type of filer to write out the object
■ If the object is an entity, its owner is a block table record and you can use
the appendAcDbEntity() function.
■ If the object is an AcDbObject, its owner is an AcDbDictionary and you can
use the setAt() function to add it to the dictionary.
If this is not a primary object, you would normally add it to the database
using addAcDbObject() and then identify its owner using setOwnerId().
To establish ownership, the owner must file out the ID of the owned
object using the appropriate ownership type.
3 Call dwgOut() on the original object, using a deep clone filer
(AcDbDeepCloneFiler) to write out the object. (Or, if you are overriding the
wblockClone() function, use an AcDbWblockCloneFiler.)
4 Rewind the filer and then call dwgIn() on the new object.
5 Call setObjectIdsInFlux() on each new object before you add its value to
the object ID map. This important step is used to indicate that the newly cre-
ated object is part of a deep clone operation and its object ID is subject to
change as part of the translation stage. This flag is automatically turned off
when translation is complete.
6 Add the new information to the idMap. The idMap contains AcDbIdPairs,
which are pairs of old (original) and new (cloned) object IDs. The constructor
for the ID pair sets the original object ID and the isPrimary flag. At this
point, you set the object ID for the cloned object, set the isCloned flag to
TRUE, and add (assign) it to the idMap.
7 Clone the owned objects. (This step is recursive.)
■ Ask the filer if there are any more owned objects. (For wblock clone, ask if
there are any more hard objects.)
■ To clone a subobject, obtain its ID and open the object for read.
■ Call deepClone() on the object. (Note that isPrimary is set to FALSE,
because this is an owned object.) The deepClone() function clones the
object and sets its owner. It also adds a record to the ID map.
■ Close the subobject if it was created at this time.
if (pBTR != NULL) {
pBTR->appendAcDbEntity(pClone);
} else {
if (isPrimary)
return Acad::eInvalidOwnerObject;
// Step 4: Rewind the filer and read the data into the clone.
//
filer.seek(0L, AcDb::kSeekFromStart);
pClone->dwgIn(&filer);
// Step 7: Using the filer list created above, find and clone
// any owned objects.
//
AcDbObjectId id;
while (filer.getNextOwnedObject(id)) {
AcDbObject *pSubObject;
AcDbObject *pClonedSubObject;
■ If you are the owner of the object, set the owner of the subobject to be the
clone of yourself.
■ If you are not the owner of the object, pass in the clone’s database as the
pOwner parameter in the wBlockClone() function call. At this time, the
object is appended to the new database, receives an object ID, and is put
into the ID map. The ID map entry for this object will specify FALSE for the
isOwnerTranslated field.
If pOwner is set to the database, wblockClone() must set the owner of the
cloned object to the same owner as that of the original object. Then, when
the references are translated by AutoCAD, it will update the owner reference
to the cloned object in the new database.
It is important to ensure that all owning objects are cloned. AutoCAD always
clones the symbol tables, named object dictionary, model space, and paper
space (for clone contexts other than AcDb::kDcXrefBind) during wblock
clone. Applications with owning objects are responsible for ensuring that
these objects are cloned if necessary. If an owning object is not cloned and
not found in the ID map, wblock clone aborts with AcDb::eOwnerNotSet.
You must pass in the database as the owner of an object when you are copy-
ing an entity that references a symbol table record. For example, suppose you
are calling wblockClone() on a sphere object. A block table record is the hard
owner of this sphere object. The sphere object contains a hard reference to
the layer table.
First, at the beginDeepClone() phase, the new database is created and set up
with the default elements. The following figure shows the model space block
table record and the layer table, because they’re relevant to this topic. The
cloning that occurs at this stage always happens during a wblock operation.
ModelSpaceBTR1 LayerTable1
ModelSpaceBTR2 LayerTable2
cloned objects
beginWblock()
soft owner
ModelSpaceBTR2 Sphere2 LayerTable2
hard owner
hard pointer
cloned objects
soft owner
hard owner
hard pointer
cloned objects
beginDeepCloneXlation()
soft owner
hard owner
hard pointer
cloned objects
* The layer table’s owner is the database itself, so this entry is meaningless.
† During translation, this setting indicates that the layer will have its owner
translated from LayerTable 1 to LayerTable2.
The wblock clone process is used for xref bind as well as wblock. The needs
of both are very similar, but there are a few differences that require special
attention when overriding the wblockClone().
Wblock clones all selected entities. However, xref bind never clones entities
that are in paper space. This leaves two things to consider when creating
objects or entities, and using AcDbHardPointerIds. First, at the beginning of
any AcDbEntity’s wblockClone(), check to see if the cloning context is
AcDb::kDcXrefBind and, if so, whether the entity is being cloned in paper
space. If it is, then no cloning should be done and wblockClone() should
return Acad::eOk.
If your custom class has any AcDbHardPointerIds that can point to entities
(such as we do with AcDbGroup), then the entities might be in paper space
and will therefore not get cloned. In that event, the AcDbHardPointerIds will
be set to NULL.
Wblock does not follow hard pointer references across databases. However,
xref bind does this all the time. For example, an entity in an xref drawing can
be on a VISRETAIN layer in the host drawing. So, if you implement your
wblockClone() with a loop to check for subobjects, and the subobject’s
if (pspace == AcDbObjectId::kNull) {
AcDbBlockTable *pTable;
database()->getSymbolTable(pTable, AcDb::kForRead);
pTable->getAt(ACDB_PAPER_SPACE, pspace);
pTable->close();
}
if ( idMap.deepCloneContext() == AcDb::kDcXrefBind
&& ownerId() == pspace)
return Acad::eOk;
if (pDb == NULL)
pDb = pOwn->database();
if (pOwn != NULL)
pBTR = AcDbBlockTableRecord::cast(pOwn);
if (pBTR != NULL) {
pBTR->appendAcDbEntity(pClone);
} else {
pDb->addAcDbObject(pClonedObject);
pClone->setOwnerId( (pOwn != NULL)
? pOwn->objectId() : ownerId());
}
// Step 4: Rewind the filer and read the data into the clone.
//
filer.seek(0L, AcDb::kSeekFromStart);
pClone->dwgIn(&filer);
// Step 7: Using the filer list created above, find and clone
// any hard references.
//
AcDbObjectId id;
while (filer.getNextHardObject(id)) {
AcDbObject *pSubObject;
AcDbObject *pClonedSubObject;
if (pSubObject->database() != database()) {
pSubObject->close();
continue;
}
pSubObject->close();
NOTE Remember that when the wblock() function is in the process of exe-
cuting, the pointer references in the destination database have not yet been
translated. The following code does not work correctly, because although it tries
to open the model space block table record of the destination database, the
model space block table record of the source database is opened instead. The
untranslated reference in the destination database’s block table is still referring
to the model space of the source database.
void
AsdkWblockReactor::otherWblock(
AcDbDatabase* pDestDb,
AcDbIdMapping& idMap,
AcDbDatabase* pSrcDb)
{
AcDbBlockTable *pDestBlockTable;
AcDbBlockTableRecord *pDestBTR;
pDestDb->getSymbolTable(pDestBlockTable, AcDb::kForRead);
pDestBlockTable->getAt(ACDB_MODEL_SPACE,
pDestBTR, AcDb::kForRead);
pDestBlockTable->close();
AcDbObjectId srcModelSpaceId;
pSrcBlockTable->getAt(ACDB_MODEL_SPACE,
srcModelSpaceId);
pSrcBlockTable->close();
AcDbIdPair idPair;
idPair.setKey(srcModelSpaceId);
idMap.compute(idPair);
AcDbBlockTableRecord *pDestBTR;
acdbOpenAcDbObject((AcDbObject*&)pDestBTR,
idPair.value(), AcDb::kForRead, Adesk::kTrue);
}
deepClone()
The AcDbHardPointerId reference problem mentioned above will not occur
in this case because deepClone() does not follow AcDbHardPointerId refer-
ences for cloning.
An application may create problems during deepClone() if it attempts to add
new entities while the AcDbObjectIds are still in flux. Therefore, never
attempt to call AcDbBlockTableRecord::appendAcDbEntity() on any cloned,
user-defined AcDbBlockTableRecords until after the
AcEditorReactor::endDeepClone() notification has been given. In contrast,
you may safely append to the model space and paper space
AcDbBlockTableRecords, because these are never cloned in deepClone().
Never try to add vertices to cloned AcDb2dPolylines, AcDb3dPolylines,
AcDbPolyFaceMeshes, or AcDbPolygonMeshes, attributes to cloned
AcDbBlockReferences, or entries to cloned dictionaries, until after the
AcEditorReactor::endDeepClone() notification.
If you must create the entities during the cloning, then you will need to keep
them in memory, along with their future owner’s ID, until after the
AcEditorReactor::endDeepClone() notification. They can be safely
appended once the deep clone is completed.
Acad::ErrorStatus
AcDbDatabase::wblock(
AcDbDatabase*& pOutputDatabase,
const AcDbObjectIdArray& pIdSet,
const AcGePoint3d& pPoint3d)
One of the main internal differences between these three versions of wblock
is their treatment of the model space and paper space
AcDbBlockTableRecords. Because the entire database is being cloned in ver-
sion one, all the entities in model space and paper space are cloned along
with their containing paper and model space AcDbBlockTableRecords. How-
ever, in versions two and three, the intent is to clone only a selected set of
entities. Although the model space and paper space AcDbBlockTableRecords
are processed, these use a “shallow clone,” which does not in turn clone all
the entities contained in model space and paper space.
Even though the model space and paper space blocks have been cloned in
versions two and three, they are empty. Therefore, it is not only acceptable
to call AcDbBlockTableRecord::AppendAcDbEntity() to place the cloned
entities into them, it is necessary to do so. (This is the exception to using
AcDbBlocKTableRecord::AppendAcDbEntity() on AcDbBlockTableRecords,
whose IDs are in flux). Also, in both versions two and three, the entities will
have isPrimary set to Adesk::kTrue when they get their wblockClone() call.
This is because the internal code individually clones the entities of the selec-
tion set, or the entities of the selected AcDbBlockTableRecord. It does not
clone the AcDbBlockTableRecord itself. (Entities in nested blocks, however,
will still have isPrimary set to Adesk::kFalse). This behavior is useful, as
will be seen in the next section, in Case 1. It saves applications from having
to know what type of WBLOCK operation is occurring.
The following two cases show how one might handle a hard reference from
a custom object to another entity. The first case is simpler, but it requires that
the referring and referenced entities always exist in the same
AcDbBlockTableRecord. The second shows what must be considered if the
two entities can exist in different records, or when the reference is in an
AcDbObject instead of an AcDbEntity.
Acad::ErrorStatus
AsdkEntity::wblockClone(AcRxObject* pOwner,
AcDbObject*& pClone,
AcDbIdMapping& idMap,
Adesk::Boolean isPrimary) const
{
// If isPrimary is kTrue, then override the default cloning
// within our own cloning, which would set it to kFalse,
// by cloning our referenced entity first.
//
if (isPrimary) {
Acad::ErrorStatus es;
AcDbEntity* pEnt;
es = acdbOpenAcDbEntity(pEnt, mRefEnt, AcDb::kForRead);
if (es != Acad::eOk)
return es;
if (pSubClone != NULL)
pSubClone->close();
pEnt->close();
if (es != Acad::eOk)
return es;
}
Insert
The insert operation is a special case of deep cloning. In the case of an insert,
the objects are not copied into the destination database; instead, they are
moved into the new database. When this occurs, the source database is no
longer valid, because it has been cannibalized when its objects were moved
into the new database. If you override the deepClone() function, your
objects will simply be cloned when an insert operation is called for. If you use
the default form of deepClone(), cheap cloning is performed internally.
When an object is copied in this way, the ID map still contains two object
IDs for each cloned object (the source ID and the destination ID), but these
IDs point temporarily to the same object. When the insert operation finishes,
the source database is deleted.
■ beginDeepClone()
■ beginDeepCloneXlation()
■ abortDeepClone()
■ endDeepClone()
The endDeepClone() function is called at the end of the cloning and transla-
tion process. The object IDs are no longer in flux. However, this call does not
mean that the entities are in their final state for whatever command is being
executed. Often the cloned entities are transformed or other operations are
performed following the cloning process. There are additional callback func-
tions that can be used to access the entities later, including commandEnded().
In addition to the previous four functions, the following notification func-
tions are provided in the wblock clone operation:
■ beginWblock()
■ otherWblock()
■ abortWblock()
■ endWblock()
These calls come in the following order with the deep clone functions:
1 beginDeepClone() This call is sent as soon as the destination AcDbDatabase
instance has been created, but it is in a “raw” state and is not ready for
appending.
2 beginWblock() The new database now has its basic elements, such as a han-
dle table, a class ID map, and model space and paper space block table
records. It is still empty. The cloning has not begun, but the new database is
now ready for appending.
3 otherWblock() and beginDeepCloneXlation() These two calls are made
back-to-back and can be used for the same purpose. The primary set of
objects has been cloned, but the reference translation has not started yet.
4 endDeepClone() The translation process has now completed, but the entities
are not yet in their final state.
5 endWblock() The entities have now been transformed, and the model space
and paper space origins have been set. The new database is complete but has
not yet been saved.
Acad::ErrorStatus
AcDbDatabase::wblock(AcDbDatabase*& pOutputDatabase)
2 WBLOCK of a user-defined block
void
AcEditorReactor::beginWblock(
AcDbDatabase* pTo,
AcDbDatabase* pFrom,
AcDbObjectId blockId)
Acad::ErrorStatus
AcDbDatabase::wblock(
AcDbDatabase*& pOutputDatabase,
AcDbObjectId nObjId)
3 WBLOCK of a selection set
void
AcEditorReactor::beginWblock(
AcDbDatabase* pTo,
AcDbDatabase* pFrom,
const AcGePoint3d& insertionPoint)
Acad::ErrorStatus
AcDbDatabase::wblock(
AcDbDatabase*& pOutputDatabase,
const AcDbObjectIdArray& pIdSet,
const AcGePoint3d& pPoint3d)
All three versions clone both the model space and paper space
AcDbBlockTableRecord before calling beginWblock(). However, for the enti-
ties within these block table records, the order of notification will appear to
come differently in the first type and last two types. In version one, entities
in model space that are being cloned will receive the call for wblockClone()
before the AcEditorReactor::beginWblock(). In versions two and three,
entities in the AcDbBlockTableRecord or the selection set will get their
wblockClone() call after the AcEditorReactor::beginWblock() notification
call.
Objects that have been cloned during a partial XBIND are automatically redi-
rected just after endDeepClone() notification. This means that their
AcDbObjectIds in the externally referenced database are forwarded to the
AcDbObjectIds of the clone objects in the host drawing, and the objects in
■ beginInsert()
■ otherInsert()
■ abortInsert()
■ endInsert()
These calls come in the following order with the deep clone functions:
1 beginInsert() and beginDeepClone() These calls come back-to-back and
can be used for the same purpose.
2 otherInsert() and beginDeepCloneXlation() These calls also come back-to-
back and can be used for the same purpose.
3 endDeepClone() The cloning and translation processes are completed. The
entities are cloned but have not been appended to a block, so they are not
graphical. You cannot use the entities in a selection set yet.
4 endInsert() The entities have now been transformed and have been
appended to a block. If this is an INSERT*, they are now in model space and
have their graphics. They can be used in selection sets. However, if this is an
INSERT, they have only been appended to a block table record; that record has
not yet been added to the block table. In this case, you must wait until
commandEnded() notification to use these entities in a selection set.
kDcSymTable XREF Attach, DXFIN, and IGESIN (only symbol table records
Merge are cloned here)
kDcSaveAs SAVEAS when VISRETAIN is set to 1 (only symbol table
records are cloned here)
kDcInsert INSERT of a drawing
kDcWblock WBLOCK
kDcObjects AcDbDatabase::deepCloneObjects()
AcDbTextStyleTable *pTsTable;
*es = pOrigDb->getSymbolTable(pTsTable,
AcDb::kForRead);
if (*es != Acad::eOk)
return;
AcDbTextStyleTableIterator *pTsIter;
*es = pTsTable->newIterator(pTsIter);
if (*es != Acad::eOk) {
pTsTable->close();
return;
}
AcDbTextStyleTableRecord *pTsRecord;
AcDbObject *pClonedObj;
if (*es != Acad::eOk) {
pTsRecord->close();
delete pTsIter;
pTsTable->close();
return;
}
*es = pTsRecord->close();
if (*es != Acad::eOk) {
delete pTsIter;
pTsTable->close();
return;
}
In This Chapter
19
All C++ class definitions are fixed at compile time. ■ Protocol Extension Defined
■ Implementing Protocol Extension
Under normal circumstances, if you write a file transla-
■ Protocol Extension for the
tor or an editing command that operates on a number MATCH Command
■ Protocol Extension Example
of existing AutoCAD classes, you have to redefine all the
511
Protocol Extension Defined
Protocol extension is a mechanism for adding functionality to existing
ObjectARX classes. This new functionality is embodied in protocol extension
classes associated with an existing ObjectARX class at runtime. The
association of an ObjectARX class with protocol extension classes is made by
way of the ObjectARX class descriptor object (described in chapter 11,
“Deriving a Custom ObjectARX Class.”) The class descriptor object describes
the class and includes an array of pointers to any objects that extend the
functionality of that class. An ObjectARX class can have any number of pro-
tocol extensions associated with it.
These steps are described in detail in the next two subsections. Additional
information about implementation issues follows these sections.
AcRxObject
EntTemperature
The first step in using protocol extension is to declare and define each of the
protocol extension classes. The base class, AsdkEntTemperature, is an abstract
base class that is defined using the ACRX_NO_CONS_DEFINE_MEMBERS() macro.
This class will eventually be registered as part of the ObjectARX class
hierarchy.
The child classes are defined using standard C++ syntax for deriving new
classes. These classes should not be registered in the ObjectARX class hierar-
chy, so you don’t need to use the ObjectARX macros for them.
For each class, you implement the functions that constitute the protocol
extension. In this example, each class has only one function,
reflectedEnergy(), which calculates a temperature for the entity.
AcRxClass
"AcRxObject"
AcRxClass
"EntTemperature"
AcRxClass
"AcDbObject"
AcRxClass
EntTemperature::desc() "AcDbEntity"
DefaultTemperature
EntTemperature::desc()
AcRxClass AcRxClass
RegionTemperature
"AcDbCurve" "AcDbRegion"
EntTemperature::desc()
AcRxClass
CircleTemperature
"AcDbCircle"
pTemp = AsdkEntTemperature::cast(
pEnt->x(AsdkEntTemperature::desc()));
double eTemp = pTemp -> reflectedEnergy (pEnt);
You can use the ACRX_X_CALL macro to simplify this code as follows:
double eTemp = ACRX_X_CALL(pEnt,
AsdkEntTemperature)->reflectedEnergy(pEnt);
ACRX_NO_CONS_DEFINE_MEMBERS(AsdkEntTemperature, AcRxObject);
{
public:
virtual double reflectedEnergy(AcDbEntity* pEnt) const;
};
double
AsdkDefaultTemperature::reflectedEnergy(
AcDbEntity* pEnt) const
{
acutPrintf(
"\nThis entity has no area, and no reflection.\n");
return -1.0;
}
double
AsdkRegionTemperature::reflectedEnergy(
AcDbEntity* pEnt) const
{
AcDbRegion *pRegion = AcDbRegion::cast(pEnt);
if (pRegion == NULL)
acutPrintf("\nThe impossible has happened!");
acdbGetObjectId(pEntId, en);
acdbOpenObject(pEnt, pEntId, AcDb::kForRead);
void
unloadApp()
{
delete acrxServiceDictionary->remove("AsdkTemperature");
acedRegCmds->removeGroup("ASDK_TEMPERATURE_APP");
In This Chapter
20
This chapter discusses some general characteristics of ■ Common Characteristics of
ObjectARX Library Functions
the ObjectARX global utility functions. For more
■ Variables, Types, and Values
Defined in ObjectARX
information on specific functions, see the
■ Lists and Other Dynamically
ObjectARX Reference. Allocated Data
■ Extended Data Exclusive Data
Types
■ Text String Globalization Issues
521
Common Characteristics of ObjectARX
Library Functions
This section describes some general characteristics of global functions in the
ObjectARX library. Most ObjectARX global functions that operate on the
database, system variables, and selection sets work on the current document.
NOTE The functions described in this chapter were known as the ADS func-
tions in previous releases of AutoCAD.
Memory Considerations
The memory requirements of an ObjectARX application are different from
those of AutoLISP. On the one hand, the data structures employed by C++
programs tend to be more compact than AutoLISP lists. On the other hand,
there is a rather large, fixed overhead for running ObjectARX applications.
Part of this consists of code that must be present in the applications them-
selves; the larger part is the ObjectARX library.
Memory Management
Some ObjectARX global functions allocate memory automatically. In most
cases, the application must explicitly release this memory as if the applica-
tion itself had allocated it. AutoLISP has automatic garbage collection, but
ObjectARX does not.
WARNING! Failure to do this slows down the system and can cause AutoCAD
to terminate.
NOTE Do not confuse a library function’s result arguments and values with its
return value. The function returns an integer status code. It places its results in
arguments passed (by reference) back to the function that calls it.
External Functions
Once an ObjectARX application has defined its external functions (with calls
to acedDefun()), the functions can be called by the AutoLISP user and by
AutoLISP programs and functions as if they were built-in or user-defined
AutoLISP functions. An external function can be passed AutoLISP values and
variables, and can return a value to the AutoLISP expression that calls it.
Some restrictions apply and are described in this section.
acedDefun("doit", 0);
The string that specifies the name of the new external function can be any
valid AutoLISP symbol name. AutoLISP converts it to all uppercase and saves
it as a symbol of the type Exsubr.
External functions are defined separately for each open document in the
MDI. The function gets defined when the document becomes active. For
more information, see chapter 16, “The Multiple Document Interface.”
NOTE The function handler must verify the number and type of arguments
passed to it, because there is no way to tell AutoLISP what the requirements are.
Function handlers that expect arguments can be written so that they prompt
the user for values if acedGetArgs() returns a NULL argument list. This tech-
nique is often applied to external functions defined as AutoCAD commands.
A group of ObjectARX functions known as value-return functions (such as
acedRetInt(), acedRetReal(), and acedRetPoint()) enable an external
function to return a value to the AutoLISP expression that invoked it.
Arguments that are passed between external functions and AutoLISP must
evaluate to one of the following types: integer, real (floating-point), string,
point (represented in AutoLISP as a list of two or three real values), an entity
name, a selection set name, the AutoLISP symbols t and nil, or a list that
contains the previous elements. AutoLISP symbols other than t and nil are
not passed to or from external functions, but an ObjectARX application can
retrieve and set the value of AutoLISP symbols by calling acedGetSym() and
acedPutSym().
This call supplies values for the function’s string, integer, and real number
arguments, which the doitagain() function handler retrieves by a call to
acedGetArgs(). For an example of retrieving arguments in this way, see the
first example in “Lists and Other Dynamically Allocated Data” on page 546.
Error Handling
The AutoCAD environment is complex and interactive, so ObjectARX appli-
cations must be robust. ObjectARX provides several error-handling facilities.
The result codes returned during “handshaking” with AutoLISP indicate
error conditions, as do the result codes library functions returned to the
application. Functions that prompt for input from the AutoCAD user employ
the built-in input-checking capabilities of AutoCAD. In addition, three func-
tions let an application notify users of an error: acdbFail(), acedAlert(),
and acrxAbort().
The acdbFail() function simply displays an error message (passed as a single
string) at the AutoCAD Command prompt. This function can be called to
identify recoverable errors such as incorrect argument values passed by the
user.
The statement in the following example calls acdbFail() from a program
named test.arx:
acdbFail("invalid osnap point\n");
The acdbFail() function displays the following:
Application test.arx ERROR: invalid osnap point
You can also warn the user about error conditions by displaying an alert box.
To display an alert box, call acedAlert(). Alert boxes are a more emphatic
way of warning the user, because the user has to choose OK before
continuing.
For fatal errors, acrxAbort() should be called. This function prompts the user
to save work in progress before exiting. The standard C++ exit() function
should not be called.
To obtain detailed information about the failure of an ObjectARX function,
inspect the AutoCAD system variable ERRNO. When certain ObjectARX func-
tion calls (or AutoLISP function calls) cause an error, ERRNO is set to a value
that the application can retrieve by a call to acedGetVar(). ObjectARX
The name of the external function, and any argument values that it requires,
is passed to acedInvoke() in the form of a linked list of result buffers. It also
returns its result in a result-buffer list; the second argument to acedInvoke()
is the address of a result-buffer pointer.
The following sample function calls acedInvoke() to invoke the factorial
function fact() in the sample program fact.cpp:
static void test()
{
int stat, x = 10;
struct resbuf *result = NULL, *list;
if (list != NULL) {
stat = acedInvoke(list, &result);
acutRelRb(list);
}
rb = acedGetArgs();
if (rb == NULL)
return RTERROR;
if (rb->restype == RTSHORT) {
x = rb->resval.rint; // Save in local variable.
} else {
acdbFail("Argument should be an integer.");
return RTERROR;
}
If a function call starts a calling sequence that causes a function in the same
application to be called with acedInvoke(), the latter function must be
registered by acedRegFunc(). If the called function isn’t registered,
acedInvoke() reports an error. The following figure illustrates this situation:
application A
A_tan() A_pi()
application B application C
B_sin() C_cos()
rc = acedInvoke(xfcnlist, &xresults);
if (rc != RTNORM) {
// Couldn't call the function—report this error (or even abort).
return BAD;
}
if (xresults == NULL) {
// Function was called but returned a bad result.
return BAD;
}
acedArxUnload("myapp");
Points
AutoCAD points are defined as the following array type:
typedef ads_real ads_point[3];
A point always includes three values. If the point is two-dimensional, the
third element of the array can be ignored; it is safest to initialize it to 0.
ObjectARX defines the following point values:
#define X 0
#define Y 1
#define Z 2
Unlike simple data types (or point lists in AutoLISP), a point cannot be
assigned with a single statement. To assign a pointer, you must copy the indi-
vidual elements of the array, as shown in the following example:
newpt[X] = oldpt[X];
newpt[Y] = oldpt[Y];
newpt[Z] = oldpt[Z];
You can also copy a point value with the ads_point_set() macro. The result
is the second argument to the macro.
The following sample code sets the point to equal to the point from:
ads_point to, from;
from[X] = from[Y] = 5.0; from[Z] = 0.0;
ads_point_set(from, to);
#include <string.h>
Because of the argument-passing conventions of the C language, points are
passed by reference without the address (indirection) operator &. (C always
Transformation Matrices
The functions acedDragGen(), acedGrVecs(), acedNEntSelP(), and
acedXformSS() multiply the input vectors by the transformation matrix
defined as a 4x4 array of real values.
typedef ads_real ads_matrix[4][4];
The first three columns of the matrix specify scaling and rotation. The fourth
column of the matrix is a translation vector. ObjectARX defines the symbol
T to represent the coordinate of this vector, as follows:
#define T 3
As these equations show, the scale factor and the last row of the matrix have
no effect and are ignored. This is known as an affine transformation.
NOTE To transform a vector rather than a point, do not add in the translation
vector M3 M13 M23 (from the fourth column of the transformation matrix).
0010 0 0 1 TZ 0 0 SZ 0 0 0 1 0
0001 0001 0 0 0 1 0 0 0 1
0 0 0 1 0 0 0 1 0 0 0 1
For uniform rotations, the 3x3 submatrix delimited by [0,0] and [2,2] is
orthonormal. That is, each row is a unit vector and is perpendicular to the
other rows; the scalar (dot) product of two rows is zero. The columns are also
unit vectors that are perpendicular to each other. The product of an
orthonormal matrix and its transpose equals the identity matrix. Two com-
plementary rotations have no net effect.
Complex transformations can be accomplished by combining (or compos-
ing) nonidentity values in a single matrix.
Useful Values
ObjectARX defines the following preprocessor directives:
#define TRUE 1
#define FALSE 0
#define EOS’\0’ // String termination character
The PAUSE symbol, a string that contains a single backslash, is defined for the
acedCommand() and acedCmd() functions, as follows:
NOTE The ObjectARX library doesn’t define the values GOOD and BAD, which
appear as return values in the code samples throughout this guide (especially in
error-handling code). You can define them if you want, or substitute a conven-
tion that you prefer.
Result-Buffer Lists
Result buffers can be combined in linked lists, described later in detail, and
are therefore suitable for handling objects whose lengths can vary and
objects that can contain a mixture of data types. Many ObjectARX functions
return or accept either single result buffers (such as acedSetVar()) or result-
buffer lists (such as acdbEntGet() and acdbTblSearch()).
struct resbuf
The following result-buffer structure, resbuf, is defined in conjunction with
a union, ads_u_val, that accommodates the various AutoCAD and
ObjectARX data types, as follows:
union ads_u_val {
ads_real rreal;
ads_real rpoint[3];
short rint; // Must be declared short, not int.
char *rstring;
long rlname[2];
long rlong;
struct ads_binary rbinary;
};
struct resbuf {
struct resbuf *rbnext; // Linked list pointer
short restype;
union ads_u_val resval;
};
NOTE The long integer field resval.rlong is like the binary data field
resval.rbinary; both hold extended entity data.
head NULL
"NAME" 5 1.41421
Code Description
RTANG Angle
RTSTR String
RTORINT Orientation
■ A unique handle that is always enabled and that persists for the lifetime
of the drawing
■ An optional xdata list
■ An optional persistent reactor set
■ An optional ownership pointer to an extension dictionary, which owns
other database objects placed in it by the application
Database objects are objects without layer, linetype, color, or any other geo-
metric or graphical properties, and entities are derived from objects and have
geometric and graphical properties.
Because DXF codes are always less than 2,000 and the result type codes are
always greater, an application can easily determine when a result-buffer list
contains result values (as returned by acedGetArgs(), for example) or con-
tains entity definition data (as returned by acdbEntGet() and other entity
functions).
The following figure shows the result-buffer format of a circle retrieved by
acdbEntGet():
result
-1 0 8
NULL
10 40 210
// Get basic C-language type from AutoCAD DXF group code (RTREAL,
// RTANG are doubles, RTPOINT double[2], RT3DPOINT double[3],
// RTENAME long[2]). The etype argument is one of the ET_
// definitions.
//
// Returns RTNONE if grpcode isn’t one of the known group codes.
// Also, sets "inxdata" argument to TRUE if DXF group is in XDATA.
*inxdata = FALSE;
if (grpcode >= 1000) { // Extended data (XDATA) groups
*inxdata = TRUE;
if (grpcode == 1071)
rbtype = RTLONG; // Special XDATA case
else
grpcode %= 1000; // All other XDATA groups match.
} // regular DXF code ranges
if (grpcode <= 49) {
if (grpcode >= 20) // 20 to 49
rbtype = RTREAL;
else if (grpcode >= 10) { // 10 to 19
if (etype == ET_VIEW) // Special table cases
rbtype = RTPOINT;
else if (etype == ET_VPORT && grpcode <= 15)
rbtype = RTPOINT;
else // Normal point
rbtype = RT3DPOINT; // 10: start point, 11: endpoint
}
else if (grpcode >= 0) // 0 to 9
rbtype = RTSTR; // Group 1004 in XDATA is binary
else if (grpcode >= -2)
Code Description
NOTE Not all ObjectARX global functions return these status codes; some
return values directly. Also, the user-input (acedGetxxx, acedEntSel,
acedEntSelP, acedNEntSel, and acedDragGen) functions can return the RTNONE
result type code, and acedDragGen() indicates arbitrary input by returning
RTSTR instead of RTKWORD.
Code Description
“Evaluating External Functions” on page 526 shows the AutoLISP calling for-
mat of an external subroutine that takes arguments of three distinct types: a
string, an integer, and a real value:
(doit pstr iarg rarg)
The following code segment shows how to implement a function with such
a calling sequence. The sample function checks that the argument list is cor-
rect and saves the values locally before operating on them (operations are not
int dofun()
{
struct resbuf *rb;
char str[64];
int ival, val;
ads_real rval;
ads_point pt;
case 0: // (doit)
if (rb->restype != RTSTR) {
acutPrintf("\nDOIT called with %d type.",
rb->restype);
acutPrintf("\nExpected a string.");
return BAD;
}
// Save the value in local string.
strcpy(str, rb->resval.rstring);
if (rb->restype != RTSHORT) {
acutPrintf("\nDOIT called with %d type.",
rb->restype);
acutPrintf("\nExpected a short integer.");
return BAD;
}
if (rb->restype != RTREAL) {
acutPrintf("\nDOIT called with %d type.",
rb->restype);
acutPrintf("\nExpected a real.");
return BAD;
}
case 1:
// Execute other functions.
. . .
}
}
WARNING! Do not write data to a dynamic location that hasn’t been allo-
cated with direct calls to malloc() or with the ObjectARX library (including
acutNewRb()). This can corrupt data in memory. Conversely, calling free() or
acutRelRb() to release data that was allocated statically—in a static or auto-
matic variable declaration—also can corrupt memory. Inserting a statically
allocated variable, such as a string, into a result-buffer list causes your program
to fail when you release the list with acutRelRb().
if ((head=acutNewRb(RT3DPOINT)) == NULL) {
acdbFail("Unable to allocate buffer\n");
return BAD;
}
head->resval.rpoint[X] = 15.0;
head->resval.rpoint[Y] = 16.0;
head->resval.rpoint[Z] = 11.18;
If the new result buffer is to contain a string, the application must explicitly
allocate memory to contain the string:
struct resbuf *head;
if ((head=acutNewRb(RTSTR)) == NULL) {
acdbFail("Unable to allocate buffer\n");
return BAD;
}
result = acutBuildList(
RTREAL, 3.5,
RTSTR, "Hello, there.",
RT3DPOINT, pt1,
0 );
If it cannot construct the list, acutBuildList() returns NULL; otherwise, it
allocates space to contain the list. This list must be released by a subsequent
call to acutRelRb():
if (result != NULL)
acutRelRb(result);
res_list = acutBuildList(
RT3DPOINT, ptarray[0],
RT3DPOINT, ptarray[1],
RT3DPOINT, ptarray[2],
RT3DPOINT, ptarray[3], 0);
if (res_list == NULL) {
acdbFail("Couldn’t create list\n");
return BAD;
}
acedRetList(res_list);
acutRelRb(res_list);
Dotted pairs and nested lists can be returned to AutoLISP by calling
acutBuildList() to build a list created with the special list-construction type
codes. These codes are needed only for complex lists. For ordinary (that is,
one-dimensional) lists, acedRetList() can be passed a simple list of result
buffers, as shown in the previous example.
NOTE A list returned to AutoLISP by acedRetList() can include only the fol-
lowing result type codes: RTREAL, RTPOINT, RTSHORT, RTANG, RTSTR, RTENAME,
RTPICKS, RTORINT, RT3DPOINT, RTLB, RTLE, RTDOTE, RTNIL, and RTT. (Although
there is an RTNIL return code, if you are returning only a nil list, you can call
acedRetNil()). It can contain result types of RTLONG if the list is being returned
to another ObjectARX application.
if (res_list == NULL) {
acdbFail("Couldn’t create list\n");
return BAD;
}
acedRetList(res_list);
acutRelRb(res_list);
The list that this example returns to AutoLISP has the following form:
((1 2 3) 4 5)
if (res_list == NULL) {
acdbFail("Couldn’t create list\n");
return BAD;
}
NOTE In AutoLISP, dotted pairs associate DXF group codes and values. In an
ObjectARX application this is unnecessary, because a single result buffer contains
both the group code (in its restype field) and the value (in its resval field).
While ObjectARX provides the list-construction type codes as a convenience,
most ObjectARX applications do not require them.
NOTE Entity definitions begin with a zero (0) group that describes the entity
type. Because lists passed to acutBuildList() are terminated with 0 (or
RTNONE), this creates a conflict. The special result type code RTDXF0 resolves the
conflict. Construct the zero group in DXF lists passed to acutBuildList() with
RTDXF0. If you attempt to substitute a literal zero for RTDXF0, acutBuildList()
truncates the list.
The following sample code fragment creates a DXF list that describes a circle
and then passes the new entity to acdbEntMake(). The circle is centered at
(4,4), has a radius of 1, and is colored red:
struct resbuf *newent;
ads_point center = {4.0, 4.0, 0.0};
newent = acutBuildList(
RTDXF0, "CIRCLE",
62, 1, // 1 == red
10, center,
40, 1.0, // Radius
0 );
if (acdbEntMake(newent) != RTNORM) {
acdbFail("Error making circle entity\n");
return BAD;
}
acutRelRb(callist);
acutRelRb(results);
Xdata can also include long integers. The ads_u_val union of the resval
field of a result buffer includes both an ads_binary and a long member for
handling extended entity data.
NOTE ObjectARX applications that make explicit assumptions about the limit
of the string length of symbol table names and TEXT entities are affected by out-
of-code-page characters.
In This Chapter
21
ObjectARX allows applications to customize input point ■ Custom Object Snap Modes
■ Input Point Management
processing. The application can associate new object
557
Custom Object Snap Modes
ObjectARX provides the ability to create custom object snap modes. These
modes allow applications to associate new object snap points and AutoSnap
alignment lines with custom and existing entities. To create a custom object
snap mode, you must do the following:
■ Keyword
The custom object snap keyword that the user types in to activate the
object snap. Both local and global keywords must be specified.
■ Protocol extension class
A pointer to the class that performs the per-entity processing of the object
snap mode.
■ Glyph
The custom glyph for the object snap mode.
■ ToolTip string
The default ToolTip string for the custom object snap mode.
For more information on setting these attributes, see the ObjectARX Reference.
A custom object snap mode is defined by registering an instance of the
AcDbCustomOsnapMode class with the custom object snap manager, described
in the previous section.
When a custom object snap mode is used, AutoCAD takes the class object
returned by AcDbCustomOsnapMode::entityOsnapClass(), looks up the cor-
responding protocol extension object for the picked entity, and invokes
AcDbCustomOsnapInfo::getOsnapInfo() to obtain the points or lines associ-
ated with that entity and object snap mode. If the final candidate point is
associated with that object snap mode, AutoCAD displays the glyph object
from the instance returned by AcDbCustomOsnapMode::glyph() and the
ToolTip string returned by AcDbCustomOsnapMode::tooltipString().
virtual Acad::ErrorStatus
getOsnapInfo(
AcDbEntity* pickedObject,
int gsSelectionMark,
const AcGePoint3d& pickPoint,
const AcGePoint3d& lastPoint,
const AcGeMatrix3d& viewXform,
AcArray<AcGePoint3d>& snapPoints,
AcArray<int>& geomIdsForPts,
AcArray<AcGeLine3d>& snapLines,
AcArray<int>& geomIdsForLines);
};
ACRX_NO_CONS_DEFINE_MEMBERS(AcmeSocketInfo, AcDbCustomOsnapInfo);
2 Initialize the base protocol extension class and add it to the runtime class
hierarchy.
For example, add the following lines to your acrxEntryPoint() function:
AcmeSocketInfo::rxInit();
acrxBuildClassHierarchy();
3 For every relevant entity class, derive a protocol extension class from the base
class.
For example, you might derive a class called AcmeSocketForLines that imple-
ments getOsnapInfo() to handle the input point processing for lines.
4 Create an instance of each protocol extension object and add the objects to
the appropriate AcRxClass descriptor objects using the addX() function.
For example:
pSocketForLine = new AcmeSocketForLine;
AcDbLine::desc()->addX(AcmeSocketInfo::desc(), pSocketForLine);
There are a few requirements for the graphics used in viewportDraw() that
must be observed. The graphics should be display-aligned, and they should
not be affected by entity orientation, the current UCS, or the current view
transform. Additionally, the graphics should be scaled to fit the current Auto-
Snap marker size, which can be determined by using the function
acdbCustomOsnapManager()->osnapGlyphSize().
NOTE If you return a NULL pointer instead of a custom glyph, AutoCAD will
not draw any glyph for the object snap mode.
virtual Acad::ErrorStatus
getOsnapInfo(
AcDbEntity* pickedObject,
int gsSelectionMark,
const AcGePoint3d& pickPoint,
const AcGePoint3d& lastPoint,
const AcGeMatrix3d& viewXform,
AcArray<AcGePoint3d>& snapPoints,
AcArray<int>& geomIdsForPts,
AcArray<AcGeCurve3d>& snapCurves,
AcArray<int>& geomIdsForLines)
};
Acad::ErrorStatus
AcmeSocketInfo::getOsnapInfo(
AcDbEntity*,
int,
const AcGePoint3d&,
const AcGePoint3d&,
const AcGePoint3d&,
AcArray<AcGePoint3d>& snapPoints,
AcArray<int>& geomIdsForPts,
AcArray<AcGeCurve3d>& snapCurves,
AcArray<int>& geomIdsForLines)
{
// Associate with AcDbEntity to define default behavior.
//
snapPoints.setLogicalLength(0);
geomIdsForPts.setLogicalLength(0);
snapLiness.setLogicalLength(0);
geomIdsForLines.setLogicalLength(0);
}
virtual Acad::ErrorStatus
getOsnapInfo(
AcDbEntity* pickedObject,
int gsSelectionMark,
const AcGePoint3d& pickPoint,
const AcGePoint3d& lastPoint,
const AcGeMatrix3d& viewXform,
AcArray<AcGePoint3d>& snapPoints,
AcArray<int>& geomIdsForPts,
AcArray<AcGeCurve3d>& snapCurves,
AcArray<int>& geomIdsForLines)
};
Acad::ErrorStatus
AcmeSocketForLine::getOsnapInfo(
AcDbEntity* pickedObject,
int,
const AcGePoint3d& pickPoint,
const AcGePoint3d&,
const AcGeMatrix3d& viewXform,
AcArray<AcGePoint3d>& snapPoints,
AcArray<int>& geomIdsForPts,
AcArray<AcGeCurve3d>& snapCurves,
AcArray<int>& geomIdsForLines)
{
// Protocol extension ensures that the following assertion
// is always true, but check in non-production versions
// just to be safe.
//
ASSERT(pickedObject->isKindOf(AcDbLine::desc()));
virtual Acad::ErrorStatus
setLocation(const AcGePoint3d& dcsPoint);
virtual void
viewportDraw(AcGiViewportDraw* vportDrawContext);
private:
AcGePoint3d mCurDcsLoc;
};
Acad::ErrorStatus
AcmeSocketGlyph::setLocation(const AcGePoint3d& dcsPoint)
{
mCurDCSLoc = dcsPoint;
}
void
AcmeSocketGlyph::viewportDraw(AcGiViewportDraw* vportDrawContext)
{
// Taking mCurDCSLoc, the pixel size, and the AutoSnap
// marker size into account, plus anything else, such as socket
// orientation, draw the glyph.
vportDrawContext->viewport()->
getNumPixelsInUnitSquare(
AcGePoint3d::kOrigin, pixelArea);
double halfGlyphSizeInDCS =
acdbCustomOsnapManager->osnapGlyphSize() * pixelArea.x / 2.0;
virtual AcGiGlyph*
glyph() const {return pSocketGlyph;);
AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void*) {
switch(msg) {
case AcRx::kInitAppMsg:
case AcRx::kUnloadAppMsg:
// Clean up.
acdbCustomOsnapManager->removeCustomOsnapMode(pSocketMode);
delete pSocketMode;
AcDbLine::desc()->delX(AcmeSocketInfo::desc());
delete pSocketForLine;
default:
// Between the initialization and termination of the
// application, all registered objects will be directly
// invoked as needed. No commands or AutoLISP
// expressions are necessary.
//
break;
}
return AcRx::kRetOK;
}
Type Description
Type Description
Empty Transient The outermost input state is exited, and a new one is about to be
entered.
When your application is loaded, you can query the value of the system vari-
able CMDACT to find out which commands are active. Input context events
can be used to note transitions between input states.
void
beginGetPoint(
const AcGePoint3d* pointIn,
const char* promptString,
int initGetFlags,
const char* pKeywords);
void
endGetPoint(
Acad::PromptStatus returnStatus,
const AcGePoint3d& pointOut,
const char*& pKeyword);
void
beginGetOrientation(
const AcGePoint3d* pointIn,
const char* promptString,
int initGetFlags,
const char* pKeywords);
void
endGetOrientation(
Acad::PromptStatus returnStatus,
double& angle,
const char*& pKeyword);
void
beginGetCorner(
const AcGePoint3d* firstCorner,
const char* promptString,
int initGetFlags,
const char* pKeywords);
void
beginSSGet(
const char* pPrompt,
int initGetFlags,
const char* pKeywords,
const char* pSSControls,
const AcArray<AcGePoint3d>& points,
const resbuf* entMask);
void
endSSGet(
Acad::PromptStatus returnStatus,
const AcArray<AcDbObjectId>& ss);
};
void
MyContextReactor::beginGetPoint(
const AcGePoint3d* pointIn,
const char* promptString,
int initGetFlags,
const char* pKeywords)
{
acutPrintf("beginGetPoint: pointIn = %.2f,%.2f,%.2f\n",
(*pointIn)[0], (*pointIn)[1], (*pointIn)[2]);
if (NULL != promptString)
acutPrintf("%s", promptString);
acutPrintf("initGetFlags: %d\n", initGetFlags);
if (NULL != pKeywords)
acutPrintf("Keywords: %s\n", pKeywords);
}
void
MyContextReactor::endGetPoint(
Acad::PromptStatus returnStatus,
const AcGePoint3d& pointOut,
const char*& pKeyword)
{
acutPrintf("endGetPoint: %d\n", returnStatus);
acutPrintf("%.2f,%.2f,%.2f\n", pointOut[0], pointOut[1],
pointOut[2]);
if (NULL != pKeyword)
acutPrintf("Keyword: %s\n", pKeyword);
}
void
MyContextReactor::endGetOrientation(
Acad::PromptStatus returnStatus,
double& angle,
const char*& pKeyword)
{
acutPrintf("endGetOrientation: %.2f\n", angle);
}
void
MyContextReactor::beginGetCorner(
const AcGePoint3d* firstCorner,
const char* promptString,
int initGetFlags,
const char* pKeywords)
{
if (NULL != firstCorner)
{
acutPrintf(
"beginGetCorner: %.2f, %.2f, %.2f\n",
(*firstCorner)[0],
(*firstCorner)[1],
(*firstCorner)[2]);
}
}
void
MyContextReactor::endGetCorner(
Acad::PromptStatus returnStatus,
AcGePoint3d& secondCorner,
const char*& pKeyword)
{
acutPrintf("endGetCorner\n");
}
void
MyContextReactor::beginSSGet(
const char* pPrompt,
int initGetFlags,
const char* pKeywords,
const char* pSSControls,
const AcArray<AcGePoint3d>& points,
const resbuf* entMask)
void
MyContextReactor::endSSGet(
Acad::PromptStatus returnStatus,
const AcArray<AcDbObjectId>& ss)
{
acutPrintf("endSSGet\n");
for (int i = 0; i < ss.length(); i++)
acutPrintf("Entity %d: <%x>\n", i, ss[i].asOldId());
}
case AcRx::kLoadDwgMsg:
// Attach a context reactor to the current document.
//
curDoc()->inputPointManager()->
addInputContextReactor(&my_icr);
break;
case AcRx::kUnloadAppMsg:
// Warning! This sample attaches a context reactor,
// but it never detaches it. A real-life application
// will need to monitor to which document it attached
// the reactor, and will need to detach it.
//
break;
}
return AcRx::kRetOK;
}
Point
Entered
NOTE It is recommended that you disable your point filter in all entity
selection contexts. Object snap mode and AutoSnaps are always disabled in
this context.
Filter Chaining
The AcEdInputPointFilter class provides a member function to support
chaining input point filters. Since only one filter can be active at a time, this
function allows you to query the current filter, embed that filter in your
custom filter, and then revoke the current filter and register your own. The
following function provides the chaining functionality:
AcEdInputFilter *
AcEdInputPointFilter::revokeFilter(AcEdInputPointFilter *);
Retrying
Input point filters and monitors can be invoked multiple times for a single
input point under the following conditions:
■ When the TAB key is pressed after a digitizer motion event, the object snap
candidate for the cursor position is cycled. As soon as the cursor motion
is detected, the object snap candidate cycling index is reset.
■ When a registered input point filter returns a Boolean indicating to retry
for a point, the system prompts for a new event, not returning the point
from the event that will be retried.
virtual Acad::ErrorStatus
monitorInputPoint(
// Output. If changedTooltipStr is kTrue, newTooltipString
// has the new ToolTip string in it.
//
bool& appendToTooltipStr,
char*& additionalTooltipString,
// Input/Output
//
AcGiViewportDraw* drawContext,
// Input parameters:
//
AcApDocument* document,
bool pointComputed,
int history,
const AcGePoint3d& lastPoint,
const AcGePoint3d& rawPoint,
const AcGePoint3d& grippedPoint,
const AcGePoint3d& cartesianSnappedPoint,
const AcGePoint3d& osnappedPoint,
AcDb::OsnapMask osnapMasks,
// const AcArray<AcDbCustomOsnapMode>& customOsnapModes
const AcArray<AcDbObjectId>& apertureEntities,
const AcArray< AcDbObjectIdArray,
AcArrayObjectCopyReallocator< AcDbObjectIdArray > >&
nestedPickedEntities,
int gsSelectionMark,
const AcArray<AcDbObjectId>& keyPointEntities,
const AcArray<AcGeCurve3d*>& alignmentPaths,
const AcGePoint3d& computedPoint,
const char* tooltipString);
};
// Input/Output
//
AcGiViewportDraw* pDrawContext,
// Input parameters:
//
AcApDocument* document,
bool pointComputed,
int history,
const AcGePoint3d& lastPoint,
const AcGePoint3d& rawPoint,
const AcGePoint3d& grippedPoint,
const AcGePoint3d& cartesianSnappedPoint,
const AcGePoint3d& osnappedPoint,
AcDb::OsnapMask osnapMasks,
// const AcArray<AcDbCustomOsnapMode>& customOsnapModes
const AcArray<AcDbObjectId>& apertureEntities,
const AcArray< AcDbObjectIdArray,
AcArrayObjectCopyReallocator< AcDbObjectIdArray > >&
nestedPickedEntities,
int gsSelectionMark,
const AcArray<AcDbObjectId>& keyPointEntities,
const AcArray<AcGeCurve3d*>& alignmentPaths,
const AcGePoint3d& computedPoint,
const char* tooltipString)
{
acutPrintf("\nhistory: %d\n", history);
if (pointComputed)
{
acutPrintf(
"rawPoint: %.2f, %.2f, %.2f\n",
rawPoint[0],
rawPoint[1],
rawPoint[2]);
OSMASK_CHECK(kOsMaskEnd);
OSMASK_CHECK(kOsMaskMid);
OSMASK_CHECK(kOsMaskCen);
OSMASK_CHECK(kOsMaskNode);
OSMASK_CHECK(kOsMaskQuad);
OSMASK_CHECK(kOsMaskInt);
OSMASK_CHECK(kOsMaskIns);
OSMASK_CHECK(kOsMaskPerp);
OSMASK_CHECK(kOsMaskTan);
OSMASK_CHECK(kOsMaskNear);
OSMASK_CHECK(kOsMaskQuick);
OSMASK_CHECK(kOsMaskApint);
OSMASK_CHECK(kOsMaskImmediate);
OSMASK_CHECK(kOsMaskAllowTan);
OSMASK_CHECK(kOsMaskDisablePerp);
OSMASK_CHECK(kOsMaskRelCartesian);
OSMASK_CHECK(kOsMaskRelPolar);
#undef OSMASK_CHECK
acutPrintf("\n");
}
}
else
acutPrintf("ViewportDraw is NULL!\n");
if (NULL != tooltipString)
{
acutPrintf("TooltipString: %s\n", tooltipString);
additionalTooltipString = ", anotherString!";
appendToTooltipStr = true;
}
if (history & Acad::eOrtho)
{
acutPrintf("Ortho found at %.2f, %.2f, %.2f\n",
computedPoint[0], computedPoint[1], computedPoint[2]);
}
return Acad::eOk;
}
Acad::ErrorStatus
processInputPoint(
bool& changedPoint,
AcGePoint3d& newPoint,
bool& changedTooltipStr,
char*& newTooltipString,
bool& retry,
AcGiViewportDraw* pDrawContext,
AcApDocument* document,
bool pointComputed,
int history,
const AcGePoint3d& lastPoint,
const AcGePoint3d& rawPoint,
const AcGePoint3d& grippedPoint,
const AcGePoint3d& cartesianSnappedPoint,
const AcGePoint3d& osnappedPoint,
AcDb::OsnapMask osnapMasks,
// const AcArray<AcDbCustomOsnapMode>& customOsnapModes
const AcArray<AcDbObjectId>& pickedEntities,
const AcArray< AcDbObjectIdArray,
AcArrayObjectCopyReallocator< AcDbObjectIdArray > >&
nestedPickedEntities,
// Of 0th element in pickedEntities.
Acad::ErrorStatus
IPF::processInputPoint(
bool& changedPoint,
AcGePoint3d& newPoint,
bool& changedTooltipStr,
char*& newTooltipString,
bool& retry,
AcGiViewportDraw* pDrawContext,
AcApDocument* document,
bool pointComputed,
int history,
const AcGePoint3d& lastPoint,
const AcGePoint3d& rawPoint,
const AcGePoint3d& grippedPoint,
const AcGePoint3d& cartesianSnappedPoint,
const AcGePoint3d& osnappedPoint,
// const AcArray<AcDbCustomOsnapMode>& customOsnapModes
const AcArray<AcDbObjectId>& pickedEntities,
const AcArray< AcDbObjectIdArray,
AcArrayObjectCopyReallocator< AcDbObjectIdArray > >&
nestedPickedEntities,
// Of 0th element in pickedEntities.
int gsSelectionMark,
// AutoSnap Info:
const AcArray<AcDbObjectId>& keyPointEntities,
const AcArray<AcGeCurve3d*>& alignmentPaths,
const AcGePoint3d& computedPoint,
const char* tooltipString)
{
// Change the computed point to an offset of (0.2, 0.2, 0.2)
// if the current computed point is an object snap point.
//
if (pointComputed && history & Acad::eOsnapped)
{
changedPoint = true;
newPoint = osnappedPoint + AcGeVector3d(0.2,0.2,0.0);
pDrawContext->geometry().circle(newPoint, 0.1,
AcGeVector3d::kZAxis);
}
return Acad::eOk;
}
case AcRx::kUnloadAppMsg:
acedRegCmds->removeGroup("mkr");
break;
}
return AcRx::kRetOK;
}
In This Chapter
22
This chapter discusses configuring your application for ■ Profile Manager
585
Profile Manager
The Profile Manager allows you to perform all the operations provided in the
Profiles tab of the Options Dialog. This API consists of two classes and several
methods used to manipulate user profiles easily.
AcApProfileManager Class
There is only one Profile Manager object per AutoCAD session. ObjectARX
provides a global function to get access to this Profile Manager object, called
acProfileManagerPtr(). This function returns a pointer to an
AcApProfileManager object, upon which the methods can then be called.
AcApProfileManagerReactor Class
The AcApProfileManagerReactor class provides a container for different
event notifications based on user profile changes.
void
AsdkProfileManagerReactor::
currentProfileWillBeReset(const char *curProfile)
{
acutPrintf("\nCurrent profile will be reset: %s", curProfile);
}
void
AsdkProfileManagerReactor::
currentProfileReset(const char *curProfile)
{
acutPrintf("\nCurrent profile has been reset: %s", curProfile);
}
void
AsdkProfileManagerReactor::
profileWillReset(const char *profName)
{
acutPrintf("\nNon-current profile will be reset: %s", profName);
}
void
aFunction()
{
acutPrintf("This is AsdkProfileSample Test Application...\n");
acProfileManagerPtr()->addReactor(pProfileRector);
// Obtain the path for the registry keys and print it out.
//
char *pstrKey;
acProfileManagerPtr()->ProfileRegistryKey(pstrKey, NULL);
if (pstrKey != NULL) {
acutPrintf("\nThe profiles registry key is: %s", pstrKey);
acutDelString(pstrKey);
}
591
592
Overview
Microsoft’s Component Object Model (COM) was originally designed to sup-
port Object Linking and Embedding (OLE); it also became the basis of
ActiveX Automation. As the emergent standard for Windows component
development, COM has relevance beyond OLE and ActiveX. Component
architecture separates interface from implementation, allowing applications
to consist of dynamically linked components rather than a single binary exe-
cutable. Developers can write programs that take advantage of existing COM
components, or they can use COM to create their own components.
ObjectARX applications can be designed as COM clients. For instance, an
ObjectARX application that needs to communicate with another program
could implement COM access to that program. Depending on the COM
interfaces that the other application provides, the ObjectARX application
could then exchange information with that application or even drive it.
An ObjectARX application can also act as an Automation server. You can
write COM wrappers to expose additional elements or custom ObjectARX
objects. New APIs, templates, classes, and support for the Microsoft Active
Template Library (ATL) make it easier than ever to add to the AutoCAD
ActiveX Automation model.
■ Hardcopy
■ Menus
■ Options
594 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
Accessing COM Interfaces from ObjectARX
Some APIs are only available as ActiveX, so in order to access them from
ObjectARX, you need to use COM from C++. This section explains the two
methods you can use to do so. The first is to use MFC and the Visual C++
ClassWizard to read the AutoCAD type library. This type library (acad.tlb)
contains the ActiveX Object model. The second method requires a bit more
work, but doesn’t require the use of MFC.
To call the ActiveX Automation interfaces using MFC and the ClassWizard Type
Library Importation system
1 The sample program will use the COM ActiveX Automation interfaces of
AutoCAD to create a circle in model space. Start Visual C++ and create a new
MFC AppWizard(dll) project called AsdkComMfcDocSamp.
2 Choose Regular DLL using shared MFC DLL.
NOTE You can actually choose any of the options, but different settings
and code will be required depending on your choice. This example will use the
Regular DLL using shared MFC DLL. See chapter 8, “MFC Topics,” for more
information on options to choose for different tasks.
Using AutoCAD COM Objects from ObjectARX and Other Environments | 595
6 Open the AsdkComMfcDocSamp.cpp source file and add the following code to
make the program ObjectARX compatible. Notice the macro call in the
acrxEntryPoint() function for
AFX_MANAGE_STATE(AfxGetStaticModuleState()):
static void initApp()
{
acedRegCmds->addCommand(
"ASDK_MFC_COM",
"AsdkMfcComCircle",
"MfcComCircle",
ACRX_CMD_MODAL,
addCircleThroughMfcCom);
}
596 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
8 Open the acad.cpp and acad.h files and explore the classes and methods that
were imported.
NOTE All the ActiveX Automation interfaces are documented in the ActiveX
and VBA Reference.
9 Open the AsdkComMfcDocSamp.cpp file and add the following function to the
file:
void addCircleThroughMfcCom()
{
}
10 Add the declarations for the three interface classes:
IAcadApplication IApp;
IAcadDocument IDoc;
IAcadModelSpace IMSpace;
11 Use the acedGetAcadWinApp to obtain the CWinApp MFC object for AutoCAD
and call the GetIDispatch method.
IDispatch *pDisp = acedGetAcadWinApp()->
GetIDispatch(TRUE);
12 Once you have the IDispatch object, attach it to the locally defined
IAcadApplication object and make sure that AutoCAD is visible:
IApp.AttachDispatch(pDisp);
IApp.SetVisible(true);
13 Obtain the active document dispatch and attach it to the locally defined
IAcadDocument object:
pDisp = IApp.GetActiveDocument();
IDoc.AttachDispatch(pDisp);
14 Query the active document for model space.
pDisp = IDoc.GetModelSpace();
IMSpace.AttachDispatch(pDisp);
15 A circle requires a center point and radius. To make this efficient and trans-
parent to different programming languages, the COM interface uses the
VARIANT type. A point is stored in a VARIANT as a SAFEARRAY. The following
code sets up a SAFEARRAY and stores it in a VARIANT:
SAFEARRAYBOUND rgsaBound;
rgsaBound.lLbound = 0L;
rgsaBound.cElements = 3;
SAFEARRAY* pStartPoint = NULL;
pStartPoint = SafeArrayCreate(VT_R8, 1, &rgsaBound);
Using AutoCAD COM Objects from ObjectARX and Other Environments | 597
// X value.
//
long i = 0;
double value = 4.0;
SafeArrayPutElement(pStartPoint, &i, &value);
// Y value.
//
i++;
value = 2.0;
SafeArrayPutElement(pStartPoint, &i, &value);
// Z value.
//
i++;
value = 0.0;
SafeArrayPutElement(pStartPoint, &i, &value);
VARIANT pt1;
VariantInit(&pt1);
V_VT(&pt1) = VT_ARRAY | VT_R8;
V_ARRAY(&pt1) = pStartPoint;
16 Call the AddCircle method from the IAcadModelSpace object:
IMSpace.AddCircle(pt1, 2.0);
The entire function should now look like
void addCircleThroughMfcCom()
{
IAcadApplication IApp;
IAcadDocument IDoc;
IAcadModelSpace IMSpace;
IDispatch *pDisp = acedGetAcadWinApp()->
GetIDispatch(FALSE);
IApp.AttachDispatch(pDisp);
IApp.SetVisible(true);
pDisp = IApp.GetActiveDocument();
IDoc.AttachDispatch(pDisp);
pDisp = IDoc.GetModelSpace();
IMSpace.AttachDispatch(pDisp);
SAFEARRAYBOUND rgsaBound;
rgsaBound.lLbound = 0L;
rgsaBound.cElements = 3;
SAFEARRAY* pStartPoint = NULL;
pStartPoint = SafeArrayCreate(VT_R8, 1, &rgsaBound);
// X value
long i = 0;
double value = 4.0;
SafeArrayPutElement(pStartPoint, &i, &value);
// Y value
i++;
value = 2.0;
SafeArrayPutElement(pStartPoint, &i, &value);
598 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
// Z value
i++;
value = 0.0;
SafeArrayPutElement(pStartPoint, &i, &value);
VARIANT pt1;
VariantInit(&pt1);
V_VT(&pt1) = VT_ARRAY | VT_R8;
V_ARRAY(&pt1) = pStartPoint;
IMSpace.AddCircle(pt1, 2.0);
}
Using AutoCAD COM Objects from ObjectARX and Other Environments | 599
void
addMenuThroughCom()
{
}
static void initApp()
{
acedRegCmds->addCommand(
"ASDK_PLAIN_COM",
"AsdkComMenu",
"ComMenu",
ACRX_CMD_MODAL,
addMenuThroughCom);
}
600 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
7 The more direct COM approach to access the Automation interfaces uses
QueryInterface. The following code returns the IUnknown for AutoCAD:
HRESULT hr = NOERROR;
CLSID clsid;
LPUNKNOWN pUnk = NULL;
LPDISPATCH pAcadDisp = NULL;
hr = ::CLSIDFromProgID(L"AutoCAD.Application", &clsid);
if (SUCCEEDED(hr))
{
if(::GetActiveObject(clsid, NULL, &pUnk) == S_OK)
{
if (pUnk->QueryInterface(IID_IDispatch,
(LPVOID*) &pAcadDisp) != S_OK)
return;
pUnk->Release();
}
}
8 Use IUnknown to get the AutoCAD application object. Also, make sure
AutoCAD is visible and get the IAcadMenuBar and IAcadMenuGroups objects.
This is shown in the following code:
if (SUCCEEDED(pAcadDisp->QueryInterface
(AutoCAD::IID_IAcadApplication,(void**)&pAcad))) {
pAcad->put_Visible(true);
}else {
acutPrintf("\nQueryInterface trouble.");
return;
}
9 With the AutoCAD application, get the menu bar and menu groups collec-
tions. Determine how many menus are current on the menu bar:
pAcad->get_MenuBar(&pMenuBar);
pAcad->get_MenuGroups(&pMenuGroups);
pAcad->Release();
long numberOfMenus;
pMenuBar->get_Count(&numberOfMenus);
pMenuBar->Release();
10 Get the first menu from the menu groups collection. This will normally be
ACAD, but could be something else. Then get the pop-up menus collection
from the first menu group:
VARIANT index;
VariantInit(&index);
V_VT(&index) = VT_I4;
V_I4(&index) = 0;
pMenuGroups->Item(index, &pMenuGroup);
pMenuGroups->Release();
pMenuGroup->get_Menus(&pPopUpMenus);
pMenuGroup->Release();
Using AutoCAD COM Objects from ObjectARX and Other Environments | 601
11 Depending on whether the menu is already created, either construct a new
pop-up menu or remove the previously created one. The following code com-
pletes the example:
WCHAR wstrMenuName[256];
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
"AsdkComAccess", -1, wstrMenuName, 256);
if (!bIsMenuLoaded) {
pPopUpMenus->Add(wstrMenuName, &pPopUpMenu);
if (pPopUpMenu != NULL) {
pPopUpMenu->put_Name(wstrMenuName);
WCHAR wstrMenuItemName[256];
MultiByteToWideChar(CP_ACP, 0,"&Add A ComCircle",
-1, wstrMenuItemName, 256);
WCHAR wstrMenuItemMacro[256];
MultiByteToWideChar(CP_ACP, 0, "AsdkComCircle ",
-1, wstrMenuItemMacro, 256);
VariantInit(&index);
V_VT(&index) = VT_I4;
V_I4(&index) = 0;
pPopUpMenu->AddMenuItem(index, wstrMenuItemName,
wstrMenuItemMacro, &pPopUpMenuItem);
VariantInit(&index);
V_VT(&index) = VT_I4;
V_I4(&index) = 1;
pPopUpMenu->AddSeparator(index,
&pPopUpMenuItem);
MultiByteToWideChar(CP_ACP, 0,
"Auto&LISP Example", -1,
wstrMenuItemName, 256);
MultiByteToWideChar(CP_ACP, 0,
"(prin1 \"Hello\") ", -1,
wstrMenuItemMacro, 256);
VariantInit(&index);
V_VT(&index) = VT_I4;
V_I4(&index) = 2;
pPopUpMenu->AddMenuItem(index, wstrMenuItemName,
wstrMenuItemMacro, &pPopUpMenuItem);
VariantInit(&index);
V_VT(&index) = VT_I4;
V_I4(&index) = numberOfMenus - 2;;
pPopUpMenu->InsertInMenuBar(index);
pPopUpMenu->Release();
pPopUpMenuItem->Release();
bIsMenuLoaded = true;
}else {
acutPrintf("\nMenu not created.");
}
}else {
VariantInit(&index);
V_VT(&index) = VT_BSTR;
V_BSTR(&index) = wstrMenuName;
pPopUpMenus->RemoveMenuFromMenuBar(index);
bIsMenuLoaded = false;
}
pPopUpMenus->Release();
602 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
The entire function should now look like
void
addMenuThroughCom()
{
AutoCAD::IAcadApplication *pAcad;
AutoCAD::IAcadMenuBar *pMenuBar;
AutoCAD::IAcadMenuGroups *pMenuGroups;
AutoCAD::IAcadMenuGroup *pMenuGroup;
AutoCAD::IAcadPopupMenus *pPopUpMenus;
AutoCAD::IAcadPopupMenu *pPopUpMenu;
AutoCAD::IAcadPopupMenuItem *pPopUpMenuItem;
HRESULT hr = NOERROR;
CLSID clsid;
LPUNKNOWN pUnk = NULL;
LPDISPATCH pAcadDisp = NULL;
hr = ::CLSIDFromProgID(L"AutoCAD.Application",
&clsid);
if (SUCCEEDED(hr))
{
if(::GetActiveObject(clsid, NULL, &pUnk) == S_OK)
{
if (pUnk->QueryInterface(IID_IDispatch,
(LPVOID*) &pAcadDisp) != S_OK)
return;
pUnk->Release();
}
}
if (SUCCEEDED(pAcadDisp->QueryInterface
(AutoCAD::IID_IAcadApplication,(void**)&pAcad))) {
pAcad->put_Visible(true);
}else {
acutPrintf("\nQueryInterface trouble.");
return;
}
pAcad->get_MenuBar(&pMenuBar);
pAcad->get_MenuGroups(&pMenuGroups);
pAcad->Release();
long numberOfMenus;
pMenuBar->get_Count(&numberOfMenus);
pMenuBar->Release();
VARIANT index;
VariantInit(&index);
V_VT(&index) = VT_I4;
V_I4(&index) = 0;
pMenuGroups->Item(index, &pMenuGroup);
pMenuGroups->Release();
pMenuGroup->get_Menus(&pPopUpMenus);
pMenuGroup->Release();
WCHAR wstrMenuName[256];
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
"AsdkComAccess", -1, wstrMenuName, 256);
Using AutoCAD COM Objects from ObjectARX and Other Environments | 603
if (!bIsMenuLoaded) {
pPopUpMenus->Add(wstrMenuName, &pPopUpMenu);
if (pPopUpMenu != NULL) {
pPopUpMenu->put_Name(wstrMenuName);
WCHAR wstrMenuItemName[256];
MultiByteToWideChar(CP_ACP, 0,"&Add A ComCircle",
-1, wstrMenuItemName, 256);
WCHAR wstrMenuItemMacro[256];
MultiByteToWideChar(CP_ACP, 0, "AsdkComCircle ",
-1, wstrMenuItemMacro, 256);
VariantInit(&index);
V_VT(&index) = VT_I4;
V_I4(&index) = 0;
pPopUpMenu->AddMenuItem(index, wstrMenuItemName,
wstrMenuItemMacro, &pPopUpMenuItem);
VariantInit(&index);
V_VT(&index) = VT_I4;
V_I4(&index) = 1;
pPopUpMenu->AddSeparator(index,
&pPopUpMenuItem);
MultiByteToWideChar(CP_ACP, 0,
"Auto&LISP Example", -1,
wstrMenuItemName, 256);
MultiByteToWideChar(CP_ACP, 0,
"(prin1 \"Hello\") ", -1,
wstrMenuItemMacro, 256);
VariantInit(&index);
V_VT(&index) = VT_I4;
V_I4(&index) = 2;
pPopUpMenu->AddMenuItem(index, wstrMenuItemName,
wstrMenuItemMacro, &pPopUpMenuItem);
VariantInit(&index);
V_VT(&index) = VT_I4;
V_I4(&index) = numberOfMenus - 2;;
pPopUpMenu->InsertInMenuBar(index);
pPopUpMenu->Release();
pPopUpMenuItem->Release();
bIsMenuLoaded = true;
} else {
acutPrintf("\nMenu not created.");
}
} else {
VariantInit(&index);
V_VT(&index) = VT_BSTR;
V_BSTR(&index) = wstrMenuName;
pPopUpMenus->RemoveMenuFromMenuBar(index);
bIsMenuLoaded = false;
}
pPopUpMenus->Release();
}
Both of these examples can be found in the ObjectARX SDK. They are located
in the docsamps\COM directory. Each sample contains code for adding a cir-
604 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
cle and a menu using either Win32 API or MFC programming techniques.
Since these methods are accessing AutoCAD through COM interfaces, these
programming techniques can be used from other C++ programs (not just
ObjectARX). Also, other languages such as Java and Visual Basic can be used.
AcDbObject-Derived
Object IAcadBaseObject COM Object
Storage of ACDbObjectId
Transient Reactor Object to AcDbObject-Derived
Object
Storage of IUnkown to AcAxOleLinkManager
COM Object
This link allows you to retrieve the existing IUnknown pointer of the COM
object given an AcDbObject pointer, as shown in the following code:
AcAxOleLinkManager* pOleLinkManager = AcAxGetOleLinkManager();
// pObject is an AcDbObject*
//
IUnknown* pUnk = pOleLinkManager->GetIUnknown(pObject);
IAcadBaseObject
IAcadBaseObject is the interface used to manage the link from a COM object
to a database-resident object. It is the COM object’s responsibility to reset the
link from the AcDbObject to the COM object when the COM object is being
destroyed. This is done using the AcAxOleLinkManager class discussed below,
usually in the destructor of the COM class:
interface DECLSPEC_UUID("5F3C54C0-49E1-11cf-93D5-0800099EB3B7")
IAcadBaseObject : public IUnknown
{
// IUnknown methods
//
STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR*
ppvObj) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
// IAcadBaseObject methods
//
STDMETHOD(SetObjectId)(THIS_ AcDbObjectId& objId,
AcDbObjectId ownerId = AcDbObjectId::kNull,
TCHAR* keyName = NULL) PURE;
STDMETHOD(GetObjectId)(THIS_ AcDbObjectId* objId) PURE;
STDMETHOD(Clone)(THIS_ AcDbObjectId ownerId,
LPUNKNOWN* pUnkClone) PURE;
STDMETHOD(GetClassID)(THIS_ CLSID& clsid) PURE;
STDMETHOD(NullObjectId)(THIS) PURE;
STDMETHOD(OnModified)(THIS) PURE;
};
SetObjectId()
This method is used to identify which database-resident object the COM
object represents. If the objId argument is equal to AcDbObjectId::kNull,
the COM object is being instructed to create a new AcDbObject-derived
object and append it to the database. The ownerId and keyName arguments
are only specified in this situation.
606 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
GetObjectId()
This method is used to retrieve the AcDbObjectId of the database-resident
object being represented.
Clone()
This method is reserved for future use.
GetClassID()
This method returns the CLSID of the COM object.
NullObjectId()
This method is used to tell the COM object that it is no longer representing
a database-resident object.
OnModified()
This method is used to tell the COM object that the AcDbObject it represents
has been modified. The COM object is then responsible for firing notification
to all its clients through established connection points.
AcAxOleLinkManager
AcAxOleLinkManager is used to manage the link from the database-resident
object to its COM object. This is done by attaching a transient reactor to the
AcDbObject. The transient reactor has one variable containing the IUnknown
of the COM object. This transient reactor is also used to call
IAcadBaseObject::OnModified() when the AcDbObject is modified.
608 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
COM objects are created via CoCreateInstance() using a CLSID, which iden-
tifies the object type. To retrieve the corresponding CLSID for a given
AcDbObject-derived object, use its getClassID() function. This function is
defined at the AcDbObject level and overridden at every other level in the
class hierarchy that has a different COM object type to represent it.
// Get corresponding COM wrapper class ID.
//
virtual Acad::ErrorStatus getClassID(CLSID* pClsid) const;
For instance, if you create a custom entity (in other words, an AcDbEntity-
derived class) and do not override getClassID(), then the CLSID returned is
the one for AcadEntity. This means your custom entities will at least have
base-level functionality even if you do not provide COM support for your
entity.
There is an additional requirement for using the following APIs to create
COM objects for your AcDbObject-derived class:
IAcadBlock::AddCustomObject(BSTR ClassName, LPDISPATCH* pObject)
IAcadModelSpace::AddCustomObject(BSTR ClassName,
LPDISPATCH* pObject)
IAcadPaperSpace::AddCustomObject(BSTR ClassName,
LPDISPATCH* pObject)
CAcadDictionary::AddObject(BSTR Keyword, BSTR ObjectName,
IAcadObject** pObject)
These functions take the actual AcDbObject-derived class name (for example,
AcDbMyObject) and create the COM object for you. After the COM object is
created, IAcadBaseObjectId::SetObjectId() will be called on it to allow the
AcDbObject-derived class to be instantiated and added to the database.
IRetrieveApplication
Used to tell the COM object what to return for the
Application property.
IAcadObject
Exposes all common properties and methods that apply to
every object in the database.
IAcadEntity
Exposes all common properties and methods that apply to
every entity in the database. (Only applicable for
AcDbEntity-derived classes.)
The following interfaces are not AutoCAD specific, but are required for
proper behavior:
IDispatch
Allows late binding. Browsers such as OPM require this
interface.
IConnectionPointContainer
Used to keep a list of connection points.
IConnectionPoint
Used to allow COM clients to ask for notification.
ISupportErrorInfo
Informs COM clients that the object supports error info.
If you are creating a COM class to represent an AcDbObject-derived class, you
will need to implement all of these interfaces.
610 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
ATL Templates
If you use ATL along with ATL-based templates from AutoCAD to create your
Automation objects, all of the interfaces listed above will be implemented
automatically. You can concentrate on the specific properties and methods
for your AcDbObject-derived class; everything else is implemented by either
Autodesk or Microsoft.
Autodesk provides the following ATL-based templates:
Template Implements
IRetrieveApplicationImpl IRetrieveApplication
IAcadEntityDispatchImpl IAcadEntity
Document Locking
Automation requests can be processed in all of the possible AutoCAD
contexts. This means you are responsible for locking a document before
modifying it. There will also be times when you will want to make a docu-
ment “current” temporarily. For example, when adding an entity to
*MODELSPACE or *PAPERSPACE you need to lock and make the document
current. Failure to lock the document in certain contexts will cause a “lock”
violation during the modification of the database. Failure to make the docu-
ment current will cause your entity to be “invisible” in the graphics display
(even after a regen).
612 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
The ObjectARX API includes functions in a document manager class to do
this. Since this is a common task, we have encapsulated the functionality
into an exported class AcAxDocLock.
For example:
STDMETHODIMP CMyEntity::Modify()
{
AcAxDocLock docLock(m_objId, AcAxDocLock::kNormal);
if(docLock.lockStatus() != Acad::eOk)
{
return E_FAIL;
}
; coclass entries
HKEY_CLASSES_ROOT\CLSID\{uuid of coclass} = ComPolygon Class
HKEY_CLASSES_ROOT\CLSID\{uuid of coclass}\InProcServer32 =
x:\some\path\to\compoly.dll
614 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
Exposing Automation Functionality
You can make functions, objects, or entities coded in ObjectARX available to
developers who are accessing an ActiveX object model through VBA or some
other programming environment.
616 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
To create an Automation wrapper for an ObjectARX application
1 Set up your project according to the steps in “Setting Up an ATL Project File.”
2 In the COM object header file, add #include "axtempl.h" (the main ActiveX
Automation template header file).
3 If you want an application property, add the following entry to the
COM_MAP:
COM_INTERFACE_ENTRY(IRetrieveApplication)
4 In the IDL file, add importlib ("c:\ACAD\acad.tlb"); after importlib
stdole32.tlb and importlib stdole2.tlb. Make sure to use the correct path
that matches your AutoCAD installation.
5 If the ObjectARX application and the COM wrapper are combined, add the
following code to your main CPP file and call it DllMain in
AcRx::kInitAppMsg and AcRx::kUnloadAppMsg with the appropriate parame-
ters. This initializes the ATL object map, among other things.
extern "C" HINSTANCE _hdllInstance;
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance,
DWORD dwReason,LPVOID /*lpReserved*/);
6 Build and register the application according to the steps in “Building and
Registering a COM DLL.”
618 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
catch(const Acad::ErrorStatus)
{
//To become more sophisticated
//
return Error(L"Failed to create square",
IID_IAsdkSquareWrapper, E_FAIL);
}
return S_OK;
}
7 In the IDL file, add importlib("c:\ACAD\acad.tlb"); after importlib
stdole32.tlb and importlib stdole2.tlb. Make sure to use the correct path
that matches your AutoCAD installation.
8 Move the acad.tlb section to the top of the IDL file and move your custom
object code so that it is within that section.
NOTE The IDL file modifications will cause the compiler to issue a warning
stating that the interface does not conform. You can ignore this message.
9 Change the derivation in the IDL file from IDispatch to IAcadObject for a
custom object or IAcadEntity for a custom entity.
10 In the section of the IDL file that corresponds to your wrapper coclass, add
[source] interface IAcadObjectEvents; after the [default] line in order
to support events. The IDL file will now appear similar to the following code:
import "oaidl.idl";
import "ocidl.idl";
[
uuid(800F70A1-6DE9-11D2-A7A6-0060B0872457),
version(1.0),
helpstring("AsdkSquareLib 1.0 Type Library")
]
library ASDKSQUARELIBLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
importlib("v:\acad\acad2000\acad.tlb");
[
object,
uuid(800F70AD-6DE9-11D2-A7A6-0060B0872457),
dual,
helpstring("IAsdkSquareWrapper Interface"),
pointer_default(unique)
]
620 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
Building and Registering a COM DLL
A registry file that describes your application, type library, and Automation
objects must be merged into the Windows system registry before the compo-
nents in your DLL will be accessible. Some additional steps are required to
link successfully a COM DLL and a separate ObjectARX application.
622 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
AutoCAD COM Implementation
The OPM is essentially a control that parses type information from COM
objects to determine their properties. When objects in the drawing are
selected, the selection set is converted into an array of IUnknown pointers rep-
resenting the COM objects that wrap all native entities in AutoCAD. These
COM object wrappers are the fundamental support for the ActiveX Automa-
tion interface and are the underlying objects that the OPM communicates
with.
These COM object wrappers implement IDispatch as well as other interfaces.
IDispatch is the COM interface the OPM uses to get and set property data. It
is also the native object representation in VB and VBA. To determine which
properties are available for an object, the OPM calls
IDispatch::GetTypeInfo(), which all AutoCAD COM wrappers implement.
This function returns the type information for the object (an object that
implements ITypeInfo). ITypeInfo is a standard Microsoft interface that
wraps a data structure describing the methods and properties available on
that object. Collections of type information used by VB and VBA to define
the ActiveX object model are called type libraries.
The OPM takes property information, and based on the type of the property
as it is defined in the IDL, constructs a property editor window appropriate
for that type of property. For example, if the property type is numeric or tex-
tual, it constructs an edit box. If it is an enum, it creates a combo box with
the enumerated value list. If it is a stock property such as Color, Layer, Line-
type, Lineweight, or other built-in properties, it constructs the standard
drop-downs for those that are the same as for the Object Property Toolbar
(OPT).
The static type information for each COM object is not the only source of
property information for the OPM. The OPM also queries the object for a few
other interfaces to control things such as property categorization, property
value names for drop-down lists, and instancing dialogs for per-property
editing (such as the ellipsis button dialogs). These will be described in detail
later in this section but will be referred to collectively as “flavoring”
interfaces.
ICategorizeProperties Interface
This interface is used by the OPM to categorize the properties shown in the
control. It is optional but strongly recommended. If the object does not
implement this interface, all properties are categorized under “General.” The
OPM does not support nesting of categories.
The OPM will use QueryInterface for this interface when it is collecting
property information. Typically this will occur when the user selects objects,
causing the pickfirst set to change. If the QueryInterface succeeds, it calls
MapPropertyToCategory for each property defined by the type information
for the object. If the category ( PROPCAT) returned is not one of the predefined
values, it calls GetCategoryName to determine which category to place the
property in. If you are only interested in categorizing using the predefined
values, you can return E_NOTIMPL from GetCategoryName. This requires that
you know the DISPID for each of your properties. The Active Template
Library (ATL) automatically assigns DISPID values to properties in the IDL
files that define your interface. These are the numbers next to the “id” key-
word in the property attribute list.
IPerPropertyBrowsing Interface
IPerPropertyBrowsing is a standard Microsoft interface. Please see the
Microsoft documentation for a detailed explanation. It is typically used by
property inspectors (such as the OPM) to display property pages for objects
that have them. It has two basic functions. The first is to associate a property
page or other dialog with a particular property via an ellipsis button on the
624 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
OPM dialog. The second purpose of IPerPropertyBrowsing is to support cus-
tom property drop-down lists in the OPM control.
IOPMPropertyExtension Interface
IOPMPropertyExtension is a collection of other flavoring functionality.
GetDisplayName is used to override the name of a property from that in the
type information. Editable is used to make properties that can be set in the
type information to read-only in the OPM. ShowProperty is used to tempo-
rarily remove a property from being displayed in the OPM.
IOPMPropertyExpander Interface
The main purpose of this class is to allow one property to be broken out into
several properties in the OPM. For example, Automation has a property
called StartPoint for AcadLine. This property gets or sets a VARIANT that con-
tains an array of doubles (technically the VARIANT contains a pointer to a
SAFEARRAY of doubles) representing the start point of the line. This is some-
what more efficient and cleaner from an API point of view than having
Automation properties called StartX, StartY, StartZ on AcadLine. However,
OPM needs to display the properties expanded out in this fashion. In addi-
tion to splitting one property into an array of properties, you can also group
the elements in that array. For example, for polyline vertices, there is one
Automation property, “Coordinates,” which returns an array of doubles,
each successive pair representing the X,Y vertices of the 2D polyline. By spec-
ifying a grouping, the OPM will automatically create a spinner control for the
property, allowing the user to enumerate and change the values of the verti-
ces. These methods are optional, since in most cases you can create separate
properties in the IDL.
To categorize properties
You may not want all your properties to show up under the “General” cate-
gory, so this next section will demonstrate how to use the built-in categories.
1 Go to Class View in the Visual C++ IDE, right-click on the custom entity
interface (such as IAsdkSquareWrapper), and choose AddProperty. Add prop-
erties for the square center and ID number.
2 Next change the derivation of the COM object to include
IOPMPropertyExtensionImpl and IOPMPropertyExpander:
public IOPMPropertyExtensionImpl<CAsdkSquareWrapper>,
public IOPMPropertyExpander
3 Add the interfaces to the COM interface map:
COM_INTERFACE_ENTRY(IOPMPropertyExtension)
COM_INTERFACE_ENTRY(ICategorizeProperties)
COM_INTERFACE_ENTRY(IPerPropertyBrowsing)
COM_INTERFACE_ENTRY(IOPMPropertyExpander)
626 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
4 Add the declaration for the IOPMPropertyExtension interface:
// IOPMPropertyExtension
//
BEGIN_OPMPROP_MAP()
OPMPROP_ENTRY(0, 0x00000001, PROPCAT_Data, \
0, 0, 0, "", 0, 1, IID_NULL, IID_NULL, "")
OPMPROP_ENTRY(0, 0x00000003, PROPCAT_Geometry, \
0, 0, 0, "", 0, 1, IID_NULL, IID_NULL, "")
END_OPMPROP_MAP()
5 Add the following two inline functions to the class:
STDMETHOD(GetCategoryName)(
THIS_
/* [in] */ PROPCAT propcat,
/* [in] */ LCID lcid,
/* [out] */ BSTR* pbstrName)
{return S_FALSE;}
STDMETHOD(GetPredefinedValue)(
/* [in] */ DISPID dispID,
/* [out] */ DWORD dwCookie,
/* [out] */ VARIANT *pVarOut);
7 Add the implementation for the function in the CPP source file. These exam-
ples are for the AsdkSquare object:
STDMETHODIMP CAsdkSquareWrapper::GetElementValue(
/* [in] */ DISPID dispID,
/* [in] */ DWORD dwCookie,
/* [out] */ VARIANT * pVarOut)
{
if (pVarOut == NULL)
return E_POINTER;
AcDbObjectPointer<AsdkSquare> pSq(m_objId, AcDb::kForRead);
if (pSq.openStatus() != Acad::eOk)
return E_ACCESSDENIED;
if (dispID == 0x03) {
AcGePoint3d acgePt;
pSq->squareCenter(acgePt);
AcAxPoint3d acaxPt(acgePt);
::VariantCopy(pVarOut,&CComVariant(acaxPt[dwCookie]));
}
return S_OK;
}
STDMETHODIMP CAsdkSquareWrapper::SetElementValue(
/* [in] */ DISPID dispID,
/* [in] */ DWORD dwCookie,
/* [in] */ VARIANT VarIn)
{
AcDbObjectPointer<AsdkSquare> pSq(m_objId, AcDb::kForRead);
if (pSq.openStatus() != Acad::eOk)
return E_ACCESSDENIED;
if (dispID == 0x03) {
AcGePoint3d acgePt;
pSq->squareCenter(acgePt);
AcAxPoint3d acaxPt(acgePt);
acaxPt[dwCookie] = V_R8(&VarIn);
pSq->upgradeOpen();
pSq->setSquareCenter(acaxPt);
}
return S_OK;
}
STDMETHODIMP CAsdkSquareWrapper::GetElementStrings(
/* [in] */ DISPID dispID,
/* [out] */ OPMLPOLESTR __RPC_FAR *pCaStringsOut,
/* [out] */ OPMDWORD __RPC_FAR *pCaCookiesOut)
628 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
{
if (dispID == 0x03)
{
long size;
size = 3;
pCaStringsOut->pElems =
(LPOLESTR *)::CoTaskMemAlloc(sizeof(LPOLESTR) * size);
pCaCookiesOut->pElems =
(DWORD *)::CoTaskMemAlloc(sizeof(DWORD) * size);
for (long i=0;i<size;i++)
pCaCookiesOut->pElems[i] = i;
pCaStringsOut->cElems = size;
pCaCookiesOut->cElems = size;
pCaStringsOut->pElems[0] = ::SysAllocString(L"Center X");
pCaStringsOut->pElems[1] = ::SysAllocString(L"Center Y");
pCaStringsOut->pElems[2] = ::SysAllocString(L"Center Z");
}
return S_OK;
}
STDMETHODIMP CAsdkSquareWrapper::GetElementGrouping(
/* [in] */ DISPID dispID,
/* [out] */ short *groupingNumber)
{
return E_NOTIMPL;
}
STDMETHODIMP CAsdkSquareWrapper::GetGroupCount(
/* [in] */ DISPID dispID,
/* [out] */ long *nGroupCnt)
{
return E_NOTIMPL;
}
STDMETHODIMP CAsdkSquareWrapper::GetPredefinedStrings(
DISPID dispID, CALPOLESTR *pCaStringsOut,
CADWORD *pCaCookiesOut)
{
return E_NOTIMPL;
}
STDMETHODIMP CAsdkSquareWrapper::GetPredefinedValue(
DISPID dispID, DWORD dwCookie, VARIANT *pVarOut)
{
return E_NOTIMPL;
}
630 | Chapter 23 COM, ActiveX Automation, and the Object Property Manager
manager and add its property classes. For modal situations, there will be a set
of predefined protocol extensions on the database that the developer can use
to retrieve the property manager for that modal situation.
IDynamicProperty
As mentioned earlier, you should implement an instance of this class for each
property that you wish to add to entities of a particular class.
In This Chapter
24
AutoCAD has features that use the COM mechanism to ■ AutoCAD DesignCenter API
■ Registry Requirements for an
query and modify objects. The AutoCAD DesignCenter
AutoCAD DesignCenter
Component
(ADC) uses the COM mechanism to provide easily
■ Implementing the Interfaces for
accessible drawing content. This chapter describes the AutoCAD DesignCenter
■ Customizing AutoCAD
COM interfaces that must be implemented by your DesignCenter
AutoCAD DesignCenter.
633
AutoCAD DesignCenter API
AutoCAD DesignCenter provides an API that can be used to provide informa-
tion about the content that it exposes. This API consists of four Component
Object Model (COM) interfaces for management of contents. Two of these
interfaces (IAcDcContentBrowser and IAcDcContentView) are designed to
enable the component provider to display their content in AutoCAD Design-
Center, and the remaining two interfaces ( IAcDcContentFinder and
IAcDcContentFinderSite) are designed to enable the component provider to
participate in the Finder mechanism of AutoCAD DesignCenter. The inter-
faces are described in the following sections. There is also another interface
(IAcPostDrop) that component providers can implement to custom handle
the right-click drag of items from AutoCAD DesignCenter.
IAcDcContentBrowser Interface
This interface is implemented in the AutoCAD DesignCenter framework and
is used by the components to communicate get and set information. A
pointer to this interface will be given to the components when their initial-
ization method is called and the components are expected to cache this
pointer to talk back to the framework.
This interface is similar to the IShellBrowser interface of the Windows
namespace extension.
IAcDcContentView Interface
This interface is implemented by the components and is used by the
AutoCAD DesignCenter framework to obtain content information from the
component. A component that has registered itself as a content provider to
AutoCAD DesignCenter would be queried for this interface at the appropriate
time and will be asked to initialize itself. Once initialized, functions in this
interface will be called at various times to get or set information in AutoCAD
DesignCenter.
This interface is similar to the IShellView interface of the Windows
namespace extension.
IAcDcContentFinderSite Interface
This interface is implemented in the AutoCAD DesignCenter framework and
is used by the components to provide search results of a content type.
IAcPostDrop Interface
This interface is implemented by components and is used at the time of a
right-click drag and drop of content entities.
Applications Key
This key is for content providers who want to register themselves and partic-
ipate in AutoCAD DesignCenter custom mode.
[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R15.0\ACAD-
x:xxx\AutodeskApps\AcadDC\Applications\Autodesk\Finder\Drawings
\Advanced Properties]
"Advanced Property1"="Block name"
"Advanced Property2"="Block and drawing description"
"Advanced Property3"="Attribute tag"
"Advanced Property4"="Attribute value"
[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R15.0\ACAD-
x:xxx\AutodeskApps\AcadDC\Applications\Autodesk\Finder\Drawings
\Properties]
"Property1"="File Name"
"Property2"="Title"
"Property3"="Subject"
"Property4"="Author"
"Property5"="Keywords"
Extensions Key
This key is for content providers who want to register themselves and
participate in AutoCAD DesignCenter desktop mode. These content
providers handle only particular types of extensions, and they are not inter-
ested in participating in the custom mode setting of AutoCAD DesignCenter.
[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R15.0\ACAD-
x:xxx\AutodeskApps\AcadDC\Extensions\.dwg]
"Default_CLSID"="{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
"Default_IconIndex"=dword:00000002
"Container"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R15.0\ACAD-
x:xxx\AutodeskApps\AcadDC\Extensions\.dwg\Blocks]
"CLSID"="{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
"IconIndex"=dword:00000000
"Prefix"="AcDc"
"LocalName"="Blocks"
CLASSID Registration
Minimum registration required by the component under
HKEY_CLASSES_ROOT is as follows:
Function Description
Function Description
Function Description
Function Description
_TCHAR szRegKey[_MAX_PATH];
_tcscpy(szRegKey, csStamp);
LPOLESTR pszId = T2OLE("AUTH");
_TCHAR szModule[_MAX_PATH];
GetModuleFileName(hInstance, szModule, _MAX_PATH);
LPCOLESTR szType = OLESTR("REGISTRY");
LPOLESTR pszModule = T2OLE(szModule);
es = pDb->getBlockTable(pBlockTable, AcDb::kForRead);
if (es != Acad::eOk) {
acedAlert("Failed to get block table!");
return Adesk::kFalse;
}
AcDbBlockTableRecord *pBlockRec;
es = pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockRec,
AcDb::kForWrite);
if (es != Acad::eOk) {
acedAlert("Failed to get block table record!");
pBlockTable->close();
return Adesk::kFalse;
}
es = pBlockRec->appendAcDbEntity(pEntity);
if (es != Acad::eOk) {
acedAlert("Failed to append entity!");
pBlockTable->close();
pBlockRec->close();
delete pEntity;
return Adesk::kFalse;
}
pBlockRec->close();
pBlockTable->close();
acDocManager->unlockDocument(pDoc);
return Adesk::kTrue;
}
STDMETHODIMP CAsdkDcContent::NavigatorNodeExpanding(
VARIANT varhNode,
BSTR bstrFullPath)
{
return S_OK;
}
STDMETHODIMP CAsdkDcContent::NavigatorNodeCollapsing(
VARIANT varhNode,
BSTR bstrFullPath)
{
return S_OK;
}
STDMETHODIMP CAsdkDcContent::NavigatorNodeClick(
VARIANT varhNode,
BSTR bstrFullPath)
{
return S_OK;
}
STDMETHODIMP CAsdkDcContent::NavigatorMouseUp(
VARIANT varhNode,
BSTR bstrFullPath,
VARIANT varX,
VARIANT varY)
{
return S_OK;
}
void CAsdkDcContent::OpenAndDisplayTextFile()
void CAsdkDcContent::OpenAndInsertTextFile()
{
DWORD length;
CString cstrBuff = OpenAndReadTextFile(length);
cstrBuff.Replace("\015\012", "\\P");
STDMETHODIMP CAsdkDcContent::PaletteItemDblClick(
BSTR bstrItemText)
{
USES_CONVERSION;
m_strSelectedItemText = OLE2T(bstrItemText);
OpenAndInsertTextFile();
return S_OK;
}
STDMETHODIMP CAsdkDcContent::PaletteColumnClick(
VARIANT varIndex)
{
return S_OK;
}
STDMETHODIMP CAsdkDcContent::PaletteMouseDown(
VARIANT varButton,
BSTR bstrFullText,
VARIANT varX, VARIANT varY)
{
return S_OK;
}
STDMETHODIMP CAsdkDcContent::RenderPreviewWindow(
BSTR bstrFullText,
VARIANT varhPreviewWindow)
{
return S_OK;
}
STDMETHODIMP CAsdkDcContent::PreviewMouseUp(
VARIANT varButton,
VARIANT varX,
VARIANT varY)
{
return S_OK;
}
STDMETHODIMP CAsdkDcContent::Refresh()
{
return S_OK;
}
STDMETHODIMP CAsdkDcContent::PaletteBeginDrag(
VARIANT varItemTexts,
VARIANT varX,
VARIANT varY)
{
return S_OK;
}
STDMETHODIMP CAsdkDcContent::ReleaseBrowser()
{
return S_OK;
}
STDMETHODIMP CAsdkDcContent::QueryContextMenu(
VARIANT varhMenu,
VARIANT varIndex,
VARIANT varCmdFirst,
VARIANT varCmdLast,
VARIANT varItemTexts)
STDMETHODIMP
CAsdkDcContent::IsExpandable(
/* [string][in] */ BSTR bstrItemText,
/* [retval][out] */ VARIANT __RPC_FAR *pvarIsExpandable)
{
pvarIsExpandable->iVal = TRUE;
return S_OK;
}
STDMETHODIMP
CAsdkDcContent::GetLargeImage(
/* [in] */ BSTR bstrFileName,
/* [out][in] */ VARIANT __RPC_FAR *pvarhLargeImage)
{
return E_NOTIMPL;
}
STDMETHODIMP
CAsdkDcContent::GetSmallImageListForContent(
BSTR bstrFileName,
VARIANT *pvarhImageList)
{
return E_NOTIMPL;
}
STDMETHODIMP
CAsdkDcContent::GetLargeImageListForContent(
BSTR bstrFileName,
VARIANT *pvarhImageList)
{
return E_NOTIMPL;
}
7 Now include the appropriate header files in the sdtafx.h file. You will also
need to add a definition to undefine _DEBUG, since the AutoCAD libraries are
non-debug. Here is what the file should look like:
#if defined(_DEBUG) && !defined(ARX_DEBUG)
#undef _DEBUG
#define ARX_DEBUG
#endif
#include <atlwin.h>
#include <adslib.h>
#include <dbmain.h>
#include <dbsymtb.h>
#include <dbmtext.h>
#include <acdocman.h>
#include <aced.h>
#include <rxregsvc.h>
#ifdef ARX_DEBUG
#undef ARX_DEBUG
#define _DEBUG
#endif
653
654
The ObjectDBX Libraries
In This Chapter
25
ObjectDBX is the successor to DWG Unplugged, and ■ Introduction
■ Using ObjectDBX
this chapter describes the changes and enhancements
■ Differences between ObjectDBX
that the ObjectDBX SDK provides, along with a and ObjectARX
■ Localization and XMX Files
description of how to implement applications using
■ Transaction Management
ObjectDBX. ■ Creating a Viewer
■ Demand Loading
■ Installing the ObjectDBX Libraries
■ Tips and Techniques
■ Known Limitations
655
Introduction
The ObjectDBX SDK is the interface among host applications, drawing (.dwg)
files, custom application (.arx) files, and custom object (.dbx) files.
Overview
ObjectDBX comprises a set of DLLs that can be used to implement custom
objects contained in an AutoCAD 2000 drawing file, and to implement appli-
cations that manipulate DWG files without the presence of AutoCAD. Part of
this capability was formerly presented in the DWG Unplugged product, but
the ObjectDBX SDK replaces and goes beyond the DWG Unplugged technol-
ogy by providing the support necessary for intelligent object systems. The
ObjectDBX SDK allows you to create non-AutoCAD host applications that
can read and write DWG files.
Host Applications
A “host application” is one that contains a main(), WinMain(), or dllMain()
function in its code, and provides the host services that an ObjectDBX or
ObjectARX program needs.
There are two types of host applications that can take advantage of the inter-
face ObjectDBX provides. One type of host application is AutoCAD 2000,
with or without associated ObjectARX applications. The second type is a
non-AutoCAD host application. A non-AutoCAD host application cannot
load an ObjectARX application, and can only take advantage of the specific
interfaces provided by the DLLs contained in ObjectDBX.
ObjectDBX Libraries
ObjectDBX libraries contain intelligence that enables custom objects (geom-
etry, components, non-graphic objects, and so on) to operate as extensions,
or custom objects, inside AutoCAD. The files that implement these objects
are given the extension .dbx, which stands for DataBase eXtension. A DBX
file is basically an ObjectARX application that has been written to work with
the ObjectDBX libraries instead of with the ObjectARX (AutoCAD) libraries.
Using ObjectDBX
Developing applications with ObjectDBX is very similar to developing appli-
cations with ObjectARX. The C++ API found in the class hierarchy of
ObjectARX is largely the same in ObjectDBX. Your principal task as a devel-
oper is to understand exactly what subset of ObjectARX is at your disposal.
C Runtime Libraries
The release DLLs for ObjectDBX are linked to the release versions of the
Microsoft C Runtime library, MSVCRT.dll. Your application should also link
with the release version and not the debug version. Mixing release and debug
or static and DLL combinations of the C Runtime libraries may cause mem-
ory allocation or deallocation errors.
AcDbDatabase
Always instantiate an AcDbDatabase before using any AcDb functions. Refer
to “Always Instantiate an AcDbDatabase” on page 676.
ObjectDBX libraries
acfirst.dll ac1st15.dll
ism.lib acISMobj15.lib
libacge.lib acge15.lib
libacgex.lib acgex15.lib
libacbr.lib acbr15.lib
When linking host applications, be sure to link acdb15.lib first, rxapi.lib sec-
ond, and any other libraries afterwards.
AcEditorReactor Class
The following AcEditorReactor notifications are valid in ObjectDBX:
■ dwgFileOpened
■ databaseToBeDestroyed
■ saveComplete
■ beginInsert
■ otherInsert
■ abortInsert
■ endInsert
■ wblockNotice
■ beginWblock
■ otherWblock
■ endWblock
■ beginDeepClone
■ beginDeepCloneXlation
■ abortDeepClone
■ endDeepClone
■ sysVarChanged
■ sysVarWillChange
AcGi API
The intended use of the AcGi layer is described in “AcGi” on page 664. The
AutoCAD-specific implementation of its AcGi layer is not part of ObjectDBX.
Instead, ObjectDBX provides its own graphics interface for displaying
AutoCAD entities.
■ You must ship the appropriate acdbLLL.xmx files along with your product.
■ You must inform ObjectDBX which acdbLLL.xmx file to load, by passing
the appropriate LCID to acdbValidateSetup().
AcTransactionManager and
AcDbTransactionManager Classes
The AcDbTransactionManager class is new in this release, and the existing
AcTransactionManager class is now derived from AcDbTransactionManager.
All the methods of AcTransactionManager, except enableGraphicsFlush()
and flushGraphics(), now belong to AcDbTransactionManager. The
enableGraphicsFlush() and flushGraphics() methods are still members of
the AcTransactionManager class. For a description of the classes and meth-
ods, please see the ObjectARX Reference. The AcTransactionManager class is
still part of the acad.lib library.
Creating a Viewer
ObjectDBX includes components that can be used to develop a viewer for
geometric models stored in an AutoCAD database. These components work
together to form a complete viewing library but may be used or replaced
independently by developers. The components interact with AcDb models
through the AcGi API, which is the same interface that the AutoCAD graph-
ics system uses to interact with AcDb.
AcGi
The AcGi API is the interface between AcDb and rendering systems used to
display AcDb models. This interface is used by the AcDbEntity member func-
tions worldDraw(), viewportDraw(), and saveAs(), which are part of the
standard entity protocol.
One method of producing an application capable of basic viewing is to
implement fully the AcGi API. Derive your own implementation classes from
the AcGi classes, such as AcGiWorldDraw and AcGiWorldGeometry. To draw a
given entity, call its worldDraw() function, then pass in a pointer to an
instance of your AcGiWorldDraw-derived class. You will then receive callbacks
into the various members of your class. The member functions are graphics
primitives such as the circle() and polyline() functions. They will be
passed all the necessary parameters needed to draw them. AcGi must be
implemented by the host application wishing to use specific graphic render-
ing logic defined by entities. The advantage of using AcGi is that the host
application need not know anything about how an entity is intended to be
rendered beyond a fixed set of geometric primitives and graphical traits, such
as color, linetype, and text font. AutoCAD has its own internal implementa-
tion of AcGi, while the AcGix library supplied with ObjectDBX breaks down
much of the complex rendering logic specified by AcGi into a relatively sim-
ple set of graphics primitives.
Some methods of AcGiWorldDraw are for query purposes (deviation() and
numberOfIsolines()) and may be used by an entity to determine the extent
NOTE For examples of using the AcGi interface, see the sample module in
samples/common/myacgi.*.
AcGix
This library is an engine that breaks up AcGi-defined geometry and traits into
a small, simple set of graphics primitives defined by the protocol of the class
AcGixVectorTaker. It operates on a registered set of viewports for which the
application must provide implementations of AcGixVectorTaker and
AcGiViewport. AcGix queries the supplied AcGiViewport for regeneration
parameters and translates the AcGi primitives it receives from entities into
calls to the supplied vectortaker. AcGix does not make any interpretation of
how the application-supplied viewports are actually displayed. This is up to
the implementor of AcGixVectorTaker.
To use AcGix with a custom graphics system rather than using SimpleView,
you must supply your own implementations of the AcGixVectorTaker and
AcGiViewport classes. The actual instances of viewport and vectortaker can
be shared between multiple viewports if this makes sense for your
application.
It is assumed that the vectortaker implementation will perform the requisite
clipping of primitives against the viewport extents. The AcGix library is sup-
plied in binary form with a set of API header files.
■ include/acgix.h
■ include/acgixcontext.h
■ include/acgixstd.h
■ include/acgixutilities.h
■ include/acgixvectortaker.h
■ include/acgixviewportset.h
■ include/acgixviewportmanager.h
SimpleView
SimpleView is a sample vectortaker. It implements a simple viewport
manager and supplies AcGixSimpleView. AcGixSimpleView aggregates imple-
mentations of AcGiViewport and AcGixVectorTaker into a single object. This
implementation uses the Windows GDI to display the results of a regen on
the screen. The full implementation of SimpleView is supplied in source
form. It can be modified by developers who wish to define a view manage-
ment system that fits their application’s needs. SimpleView is intended to
demonstrate what is required to manage a viewport layout and to work with
WhipView
The WhipView library implements AcGixView and AcGixVectorTaker on top
of the WHIP! graphics accelerator. WHIP! is a graphics accelerator with a 2D
image cache built on top of Autodesk’s HEIDI technology. It exports a single
API function, acgixAllocateWhipView(), which creates and returns an
ViewAcDb
ViewAcDb is a Multiple Document Interface (MDI) drawing file viewer that
uses SimpleView and WhipView for displaying views of DWG files.
ViewAcDb is essentially a test harness for the AcDb.dll, AcGix.dll, and the
view implementations.
To establish a view
1 The ObjectDBX application loads the DWG file into an instance of
AcDbDatabase.
2 The application creates a viewport and passes the associated AcGiViewport to
AcGix.
3 The application tells AcGix to initiate a regen of a specific block table record
or a single entity into a given set of viewports.
4 AcGix regenerates the entity(ies) into all active viewports. Essentially, each
entity is opened for read, its AcDbEntity::worldDraw() is invoked, and if the
return status indicates, its AcDbEntity::viewportDraw() member override is
invoked once for each active viewport.
5 From either of these members, each class is free to obtain the
AcGiWorld/ViewportGeometry objects and the AcGiSubentityTraits objects,
and make calls to send geometric graphics and graphics traits (attributes like
color, linetype, font) to them. These AcGi objects are implemented in AcGix,
which takes the “input” geometric primitives and traits and processes them,
reducing them to the primitives passed in to the instance of
AcGixVectorTaker associated with each active viewport.
6 The vectortaker takes the input message packets (or function calls) and con-
verts them into calls to the underlying graphics system.
■ Adapt the supplied SimpleView library source for use by the host applica-
tions, and use all three components. Study and adapt any code from the
ViewAcDb sample application as needed. This is probably the best way to
get familiar with the usage of AcGix and of the SimpleView AcGix plat-
form implementation. However, you are free to modify the SimpleView
source as desired.
■ Build a module to drive a desired graphics system through the AcGix
library. This allows much of the existing elaboration logic to be reused and
still offers considerable flexibility in actual graphics presentation and
performance. This would involve using the AcGix module but writing the
implementations of AcGiViewport and AcGixVectorTaker from scratch, in
effect replacing the SimpleView and/or WhipView libraries entirely.
■ Use the WhipView subsystem to retain elements of SimpleView needed to
support the definition of AcGixBlockView. This includes the supplied com-
bination of WHIP! and HEIDI DLLs. Direct use of the WHIP! and HEIDI
components is not supported in this release.
■ Write an entire custom implementation of the AcGi interface and do not
use any of the supplied AcGix, SimpleView, or WhipView components.
You can attain maximum performance this way with a maximum amount
of development work.
Demand Loading
The demand loading mechanism is essentially the same for an ObjectDBX
application as it is for an ObjectARX application. The only difference is in
where the information is found in the registry. For demand loading
ObjectARX applications, AutoCAD looks in the system registry under the fol-
lowing:
HKEY_LOCAL_MACHINE
Software
Autodesk
AutoCAD
R15.0
ACAD-xxxxxxx-xxxxxxxx
Applications
xxxxxxx-xxxxxxxx is a number unique to each installation.
The AutoCAD ObjectDBX shared libraries comprise a group of files that were
developed with the intent that they be used by multiple applications. This
characteristic requires that they be placed in a common location accessible
to multiple applications on the user’s workstation. Autodesk has chosen
Microsoft’s recommended implementation of installing files of this nature in
the Common Files directory. Since it is possible that your installer may be
sharing these files with other applications, you need to follow some simple,
yet critical, guidelines when installing the files.
Use COMMONFILES
Do not assume drive letters and paths when determining where to install the
Autodesk Shared files. The InstallShield system constant COMMONFILES will
return a path name that points to something like c:\Program Files\Common
Files. You must then append the Autodesk Shared name. This can be done in
a .rul script with the following line:
szSharedPath = COMMONFILES ^ "Autodesk Shared";
All files that you are redistributing from the ObjectDBX\release directory of
your SDK installation should be treated as Autodesk Shared files for installa-
tion purposes.
In addition you must also mark these files with the SHAREDFILE flag when
installing. This will ensure that you are maintaining reference counting with
other application installers that may be installing and using these libraries
and files.
The following InstallShield script is an example that installs the acge15.dll
using the version and shared file mechanism.
TARGETDIR = COMMONFILES ^ "Autodesk Shared";
nReturn = XCopyFile (
"acge15.dll", "acge15.dll",
COMP_UPDATE_SAME |
COMP_UPDATE_VERSION |
SHAREDFILE);
if (nReturn < 0 ) then
// Report failure
endif;
When updating the PATH value, no matter which operating system you are
dealing with, your installer should prompt the user to reboot so that the path
change is properly recorded after installation is complete.
Autodesk provides the InstallShield script below as an incomplete example
of smart path updating:
function AdUpdateAUTOEXEC (szSharedPath)
STRING szRootPath, szBatchName, szBatchFile,
szBackupName,szTestLine,szCheckForPathLine;
NUMBER nReturn, nvHandle;
STRING szOutput;
begin
szOutput = "SET PATH=%PATH%;" + szSharedPath;
// Obtain the filename of the system batch file.
BatchGetFileName (szBatchFile);
ParsePath(szRootPath, szBatchFile, PATH);
ACAD_OBJID_INLINE_INTERNAL
The header files acdb.h and dbidar.h contain an #ifdef section that selects a
header to #include based on the ACAD_OBJID_INLINE_INTERNAL macro.
Applications should never #define this value. This #define is intended for
Autodesk internal use only. Applications that include a #define
ACAD_OBJID_INLINE_INTERNAL macro will not compile successfully.
AcDbDatabase Notes
ObjectDBX allows you to have several instances of the AcDbDatabase class,
though you must be sure to delete them all before your application exits. It
is also important that you always have one instance of AcDbDatabase that is
the current database. These requirements are described in the following
sections.
AcDbDatabase::insert()
When inserting one database into another, the order of destruction is critical.
When using this function, the database executing the insert is the “To” data-
base, and the database used as an argument is the “From” database. Always
destroy the “From” database first, and the “To” database last; otherwise you
will cause a fatal error in the ObjectDBX DLL.
registerApplication(AcDbDatabase* pDatabase)
{
AcDbRegAppTable *pRegAppTable;
AcDbObjectId blockId;
if (pDatabase->getRegAppTable(pRegAppTable, AcDb::kForWrite)
== Acad::eOk)
{
AcDbRegAppTableRecord *pRecord = new AcDbRegAppTableRecord;
if (pRecord)
{
pRecord->setName("ACDBTEST_APP"); // For example
if (pRegAppTable->add(blockId, pRecord) == Acad::eOk)
pRecord->close();
else delete pRecord;
}
pRegAppTable->close();
}
}
EED is added to an AcDbEntity as a resbuf chain. When using resbuf types
that require pointers (like resval.rstring), be sure to allocate the pointer
with the acdbAlloc() function, and delete it with the acdbFree() function
(declared in the dbmain.h file).
Known Limitations
Please review the “Tips” section for the AcDbDimension class in the ObjectARX
Reference, which documents the behavior of AcDbDimension entities in an
external database. For the purposes of modifying or creating new
AcDbDimension entities by the API, every ObjectDBX database behaves as an
external database. Thus, a newly created or modified AcDbDimension object
will have its dimBlockId set to NULL. Calling the acdbMakeDatabaseCurrent()
function is not sufficient to change the behavior documented in the
ObjectARX Readme. This does not prevent the creation of a valid drawing, as
AutoCAD is capable of generating the correct dimBlockID for an
AcDbDimension at regen time.
In This Chapter
26
AutoCAD uses the graphics interface library (AcGi) to ■ AcGi Overview
■ Setting Entity Traits
display built-in and custom entities. This chapter dis-
■ Primitives
cusses setting entity traits and using primitives to create ■ Using Drawables in Your Object
683
AcGi Overview
The AcGi library defines a set of interfaces with which objects can render
themselves to an underlying graphics system. This chapter discusses how
AcGi works in the AutoCAD environment. However, it works in a similar way
for other systems that implement the AcGi interfaces.
The AcGi library enables entities to query for information about the regener-
ation process, and to detail a set of primitives using the geometry classes.
Access to AcGi occurs within the following three member functions of the
AcGiDrawable base class:
Adesk::Boolean
worldDraw(
AcGiWorldDraw*);
void
viewportDraw(
AcGiViewportDraw*);
Adesk::UInt32
setAttributes(
AcGiDrawableTraits*);
AcDbEntity inherits these functions from AcGiDrawable. Typically, when
implementing a custom entity, you will override these functions and provide
your own implementation.
When AutoCAD needs to regenerate the graphics to display an entity, it calls
these functions in the following manner:
AcGiDrawable *pDrawable;
pDrawable->setAttributes(pDt);
if (!pDrawable->worldDraw(pWd))
{
for each viewport
pDrawable->viewportDraw(pVd);
}
For custom entities, AutoCAD calls your setAttributes(), worldDraw(), and
viewportDraw() functions if you have overridden them. AutoCAD passes in
the appropriate AcGi objects to these functions. This enables AutoCAD to
display your custom entity just as if it were a built-in entity.
The setAttributes() function initializes attributes for the entity, such as
color, layer, and linetype. The worldDraw() function builds the portion of the
entity’s graphical representation that can be specified independent of any
particular model-space view or paper-space viewport contexts. The
viewportDraw() function then builds the view-dependent portion of the
entity’s graphics. If any of the entity’s graphics are view-dependent,
Regen Time
Model Space
Overridden Functions
AcGiContext
AcGiCommonDraw
AcGiWorldDraw
AcGiWorldDraw
AcGiContext
AcGiEdgeData
AcGiFaceData
AcGiGeometry
AcGiViewportGeometry
AcGiWorldGeometry
AcGiLinetypeEngine
AcGiSubEntityTraits
AcGiDrawableTraits
AcGiTextStyle
AcGiVertexData
AcGiViewport
AcGiDrawable
AcGiGlyph
■ AcGiWorldGeometry
■ AcGiSubEntityTraits
■ Circle
■ Circular arc
■ Polyline
■ Polygon
■ Mesh
■ Shell
■ Text
■ Xline
■ Ray
■ Draw
The draw method allows you to specify another drawable to be used as a part
of your geometry. This might be another entity or an in-memory drawable.
AcGi uses the same setAttributes(), worldDraw(), and viewportDraw()
logic on this object as it uses on your object.
■ Color
■ Layer
■ Linetype
■ Polygon fill type
■ Selection marker
■ Line weight
■ Thickness
■ Plot style name (should not be modified during worldDraw() or
viewportDraw())
■ AcGiViewportGeometry
■ AcGiSubEntityTraits
■ AcGiViewport
The viewport geometry object provides the same list of primitives as the
world geometry object and adds to it the following primitives, which use eye-
and display-space coordinates to draw polylines and polygons:
■ polylineEye()
■ polygonEye()
■ polylineDc()
■ polygonDc()
NOTE In this chapter, the term subentity is used differently than in chapter 6,
“Entities,” where the term refers to specific geometric pieces of an entity. In this
chapter, subentity is not a piece of an entity; it is just a level at which trait values
can be set and changed.
■ Subprimitive Level The mesh and shell primitive functions have optional
parameters that let you specify a rich set of traits on a per-edge and per-
face basis. (See the code samples in “Primitives” on page 696.) For any
trait, this mechanism requires that you set values for all of the edges or
faces, or for none of them. You set only the traits you want. For example,
you can set the colors of the edges of a shell or mesh without having to
set layers or linetypes, but you must specify a color for every edge. In addi-
tion to mesh and shell subprimitive traits, there is a version of the text
primitive function that has a text style parameter. Text style can be set
only at the subprimitive (per-text primitive) level. Subprimitive trait val-
ues supersede values of the corresponding traits set at the subentity and
drawable levels.
■ Color
■ Layer
■ Linetype
■ Fill type
■ GS marker
■ Line weight
■ Thickness
■ Line type scale
Color, layer, and linetype are AutoCAD entity properties, so they can also be
set at the drawable level as described in the previous section. Fill type and GS
marker are not AutoCAD entity properties.
Before each call to worldDraw() and viewportDraw(), AutoCAD calls
setAttributes() to allow the drawable to initialize the color, layer, linetype,
line weight, thickness, and line type scale subentity traits. It initializes fill
type to correspond to the regen type, and it initializes the GS marker to zero
(a zero marker signifies “no marker”).
Fill Type
The fill type enumerated value, AcGiFillType, can have one of two values:
■ kAcGiFillAlways
■ kAcGiFillNever
Primitives that can be filled are circles, polygons, shells, meshes, text, arc sec-
tors, and arc chords. Polylines and simple arcs cannot be filled.
Before AutoCAD calls worldDraw(), it sets the fill type depending on the
regen type. If the regen type is kAcGiStandardDisplay, AutoCAD sets the fill
type to kAcGiFillNever. Otherwise, AutoCAD sets the fill type to
kAcGiFillAlways. This value is reinitialized according to the regen type
before viewportDraw() is called.
If the user issues a FILL command specifying to turn Fill mode off, no objects
are filled regardless of the regen type. Similarly, if the user explicitly turns Fill
mode on, objects will be filled. If the user does not issue a FILL command, and
AcGiSubEntityTraits::setFillType() has been set, that Fill mode is used
regardless of the regen type.
// Linetype
//
static const char* const kNoLinetyping = "CONTINUOUS";
static const char* const kLinetypeByLayer = "BYLAYER";
static const char* const kLinetypeByBlock = "BYBLOCK";
// Layer
//
static const char* const kLayerZero = "0";
static Acad::ErrorStatus
getLinetypeIdFromString(const char* str, AcDbObjectId& id);
static Acad::ErrorStatus
getLayerIdFromString(const char* str, AcDbObjectId& id);
Adesk::Boolean
AsdkTraitsSamp::worldDraw(AcGiWorldDraw* pW)
{
// At this point, the current property traits are
// the entity’s property traits. If the current
// property traits are changed and you want to
// reapply the entity’s property traits, this is
// the place to save them.
//
Adesk::UInt16 entity_color
= pW->subEntityTraits().color();
AcDbObjectId entity_linetype
= pW->subEntityTraits().lineTypeId();
AcDbObjectId entity_layer
= pW->subEntityTraits().layerId();
pW->geometry().polyline(num_pts, pVerts);
err = acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pLinetypeTable, AcDb::kForRead);
if (err != Acad::eOk)
return err;
pLinetypeTable->close();
return err;
}
err = acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pLayerTable, AcDb::kForRead);
if (err != Acad::eOk)
return err;
pLayerTable->close();
return err;
}
Mesh
A mesh is an efficient way to store a parametrically rectangular grid of
vertices. The geometry for a mesh is specified as the number of rows, the
number of columns, and a list of vertices, in row-order:
virtual Adesk::Boolean
AcGiWorldGeometry::mesh(
const Adesk::UInt32 rows,
const Adesk::UInt32 columns,
const AcGePoint3d* pVertexList,
const AcGiEdgeData* pEdgeData = NULL,
const AcGiFaceData* pFaceData = NULL,
const AcGiVertexData* pVertexData = NULL) const = 0;
The mesh() function has three optional parameters for attaching property
data to edges, faces, or vertices. For edges in the mesh, you can attach color,
layer, linetype, GS marker, and visibility properties. For example, you could
use AcGiEdgeData::setColors() to attach a different color to each edge of
the mesh. In the color list, first list the colors for all the row edges, then the
colors for all the column edges. The following figure shows the ordering of
edge property data for a sample mesh:
0 1 2
12 15 18 21
3 4 5
13 16 19 22
6 7 8
14 17 20 23
9 10 11
faceInfo.setColors(pFaceColorArray);
Primitives | 697
// If the fill type is kAcGiFillAlways, then a shell,
// mesh, or polygon will be interpreted as faces;
// otherwise, they will be interpreted as edges.
//
// Output mesh as faces.
//
pW->subEntityTraits().setFillType(kAcGiFillAlways);
pW->geometry().mesh(numRows, numCols, pVerts, NULL,
&faceInfo);
return Adesk::kTrue;
}
For faces in a mesh, you can attach color, layer, GS marker, normal, and vis-
ibility traits. To assign properties to faces in a mesh, you list the values for the
faces in row-order, as indicated by the following figure:
0 1 2
3 4 5
6 7 8
Vertex data for the mesh is listed in the same order as in the vertex list. Prop-
erties that can be set with AcGiVertexData are normals and orientation.
■ kAcGiInvisible
■ kAcGiVisible
■ kAcGiSilhouette
If the surface is not curved, or the edge is not required for viewing purposes,
specify kAcGiInvisible. For hard edges of a surface or visible creases, specify
kAcGiVisible. For edges or faces that you can see from certain viewpoints,
specify kAcGiSilhouette. The silhouette visibility type is recognized only by
the HIDE command; otherwise, it is interpreted as kAcGiVisible.
For example, in the solid cylinder shown below, the edges that form the rims
of the cylinder are visible edges. The latitudinal edges are invisible edges,
since they are never used for viewing purposes. The longitudinal edges are sil-
houette edges, since they are used when the cylinder is viewed from certain
angles.
visible edges
silhouette edges
invisible edges
Primitives | 699
Shell
A shell is a list of faces that might be connected and can have holes in them.
The shell is specified by the number of unique vertices, a list of vertices
(pVertexList), the number of faces (faceListSize), and a face list, which
consists of the number of points in a given face followed by the index in the
vertex list of each vertex for that face. The signature for the shell() function
is
virtual Adesk::Boolean
AcGiWorldGeometry::shell(
const Adesk::UInt32 nbVertex,
const AcGePoint3d* pVertexList,
const Adesk::UInt32 faceListSize,
const Adesk::Int32* pFaceList,
const AcGiEdgeData* pEdgeData = NULL,
const AcGiFaceData* pFaceData = NULL,
const AcGiVertexData* pVertexData = NULL
const struct resbuf*pResBuf = NULL) const = 0;
A negative vertex count indicates a hole in the shell. Holes must be in the
same plane as the face in which they reside. The holes must not touch each
other and must be completely inside the containing face. The shell() func-
tion is a costly operation because it requires the use of a triangulator to break
the containing face and the holes down into component triangles.
AcGi polygons and shells with faces of five or more sides are also broken
down into triangles before being sent to be displayed. Having the AcGi trian-
gulate a polygon or shell face can be costly in terms of memory and speed,
so it’s recommended you use three- or four-sided faces in shells to build up
faces or polygons with five or more sides. That way, the primitive will not be
put through the slow triangulator step.
NOTE The triangulator is used only on polygons of five sides or more, shell
faces of five sides or more, shell faces with holes, and filled text.
1 3
If the same edge is used in two different faces, properties may conflict. In
such cases, you can set one of the edges to be invisible or make the properties
match for each edge.
The order of face data, if present, follows the ordering of the face list for the
shell.
The following is an example of a shell with color data attached to edges and
faces and visibility data attached to edges. The shell is composed of two tri-
angles in different planes that share a common edge. The common edge has
silhouette visibility. This means that when the HIDE command is in effect
and the AutoCAD variable DISPSILH equals 1 (display silhouettes is on), the
common edge between the faces is drawn only if both faces in the viewport
are on the same side of the common edge. In this case, one face is behind the
other, so it is not drawn:
Adesk::Boolean
AsdkShellSamp::worldDraw(AcGiWorldDraw* pW)
{
// Fill the faces with the current color.
//
pW->subEntityTraits().setFillType(kAcGiFillAlways);
// Create vertices.
//
Adesk::UInt32 numVerts = 4;
AcGePoint3d *pVerts = new AcGePoint3d[numVerts];
pVerts[0] = AcGePoint3d(0.0, 0.0, 0.0);
pVerts[1] = AcGePoint3d(0.0, 1.0, 0.0);
pVerts[2] = AcGePoint3d(1.0, 1.0, 0.0);
pVerts[3] = AcGePoint3d(1.0, 0.0, 2.0);
Primitives | 701
// Create two faces.
//
Adesk::UInt32 faceListSize = 8;
Adesk::Int32 *pFaceList
= new Adesk::Int32[faceListSize];
edgeData.setVisibility(pEdgeVisArray);
pEdgeVisArray[0] = kAcGiVisible;
pEdgeVisArray[1] = kAcGiVisible;
pEdgeVisArray[2] = kAcGiSilhouette;
pEdgeVisArray[3] = kAcGiSilhouette;
pEdgeVisArray[4] = kAcGiVisible;
pEdgeVisArray[5] = kAcGiVisible;
return Adesk::kTrue;
}
An AcGiVertexData object contains a single flag that specifies how vertices
in a shell are ordered. This flag is set and queried with the following
functions:
virtual void
AcGiVertexData::setOrientationFlag(
AcGiOrientationType oflag);
virtual AcGiOrientationType
AcGiVertexData::orientationFlag() const;
This flag is not used for meshes because the ordering of vertices specifying a
mesh is fixed. Values for the flag are
■ kAcGiClockwise
■ kAcGiCounterClockwise
■ kAcGiNoOrientation
The orientation of vertices in a shell’s face list indicates the visible side of the
face. For example, if the vertices are specified as clockwise and the vertices for
a given face are listed in clockwise order, then that face is visible. In this case,
faces with vertices in counterclockwise order are invisible.
Arc
The circularArc() function has two forms:
virtual Adesk::Boolean
AcGiWorldGeometry::circularArc(
const AcGePoint3d& center,
const double radius,
const AcGeVector3d& normal,
const AcGeVector3d& startVector,
const double sweepAngle,
const AcGiArcType arcType = kAcGiArcSimple) const = 0;
Primitives | 703
virtual Adesk::Boolean
AcGiWorldGeometry::circularArc(
const AcGePoint3d& start,
const AcGePoint3d& point,
const AcGePoint3d& end,
const AcGiArcType arcType = kAcGiArcSimple) const = 0;
The arc type variable, AcGiArcType, can have one of the following values:
Polyline
The pline() function allows a custom entity to draw graphics primitives
using an AcDbPolyline as a template:
virtual Adesk::Boolean pline(
const AcDbPolyline& lwBuf,
Adesk::UInt32 fromIndex = 0,
Adesk::UInt32 numSegs = 0) const;
AcDbPolylines are multisegmented and support straight and curved seg-
ments with or without width. Using pline() provides the ability to generate
contiguous straight and curved segments with width. None of the other AcGi
primitive functions support width, so without using pline() it would be nec-
essary to generate many parallel arc and line segments to simulate a filled arc
or line segment with width. This is inefficient, and proper display is depen-
dent upon the view (magnification).
Text
The example in this section shows use of the AcGiTextStyle class. It draws a
rectangle around a piece of AcGi text that can be oriented and located any-
where in space.
The normal and direction vectors of the text must be perpendicular to each
other. If you’re unsure of the directions, consider the direction to be along
the X axis and the normal along the Z axis in a right-handed coordinate sys-
tem. Calculate the Y axis from these. Then the cross product of the Y axis to
Primitives | 705
AcGiTextStyle style;
AcGeVector3d vec = norm;
vec = vec.crossProduct(dir);
dir = vec.crossProduct(norm);
style.setFileName("txt.shx");
style.setBigFontFileName("");
int status;
pW->geometry().polyline(5, verts);
return Adesk::kTrue;
}
Acad::ErrorStatus
toAcDbTextStyle(
const AcDbObjectId AcDbStyleId,
AcGiTextStyle& textStyle);
The following functions make use of the AcGiTextStyle and
AcDbTextStyleTableRecord names:
Acad::ErrorStatus
fromAcDbTextStyle(
AcGiTextStyle& textStyle,
const char* AcDbStyleName);
Acad::ErrorStatus
toAcDbTextStyle(
AcGiTextStyle& textStyle);
Acad::ErrorStatus
toAcDbTextStyle(
AcGiTextStyle& textStyle,
const char* AcDbStyleName);
When copying data to or from an AcDbTextStyleTableRecord that has been
specified by name, the AcGiTextStyle object’s name is set to match the name
of the AcDbTextStyleTableRecord. If no record is found when copying to an
AcDbTextStyleTableRecord specified by name, then one is created.
Primitives | 707
When copying from an AcGiTextStyle to an AcDbTextStyleTableRecord
and the name of the AcGiTextStyle is used as the name of the
AcDbTextStyleTableRecord, if the AcGiTextStyle does not have a name, a
unique name is generated and used as the name for the AcGiTextStyle and
AcDbTextStyleTableRecord objects.
The following functions are similar to the previous functions, except that
they have an AcDbObjectId argument used for the objectId of the
AcDbTextStyleTableRecord that the data has been copied into.
Acad::ErrorStatus
toAcDbTextStyle(
AcGiTextStyle& textStyle,
AcDbObjectId& AcDbStyleId);
Acad::ErrorStatus
toAcDbTextStyle(
AcGiTextStyle& textStyle,
const char* AcDbStyleName,
AcDbObjectId& AcDbStyleId);
Tessellation
Curves and curved surfaces need to be tessellated—broken up into lines and
polygons—in order to be displayed. The degree of tessellation determines
how accurate the displayed curve will be (how close it will approximate the
mathematical “true” curve) and how much performance overhead is
required to generate the graphics for a curve. A very small circle may require
only a single pixel to display it. A large circle may require hundreds of small
line segments to be calculated and displayed to create a smooth appearance.
The deviation() functions provided by the AcGiWorldDraw and
AcGiViewportDraw classes return the deviation, which is the allowable maxi-
mum difference in world space between a true mathematical surface and the
tessellated surface, as shown in the following figure:
tessellated surface
d
true curve
d = maximum deviation
Access to this value allows custom entities to tune their tessellation to the
VIEWRES command’s zoom percent option, which is set by the user. The result
is that custom entities are tessellated to relatively the same smoothness as
built-in entities.
Tessellation | 709
The deviation() function returns the suggested maximum deviation in
world space, given the type of deviation to calculate and a point in world
space for perspective scaling if required. The signature for the deviation()
function is
virtual double
AcGiWorldDraw::deviation(
AcGiDeviationType devType,
const AcGePoint3d&) const = 0;
The deviation types are
Isolines
An isoline is used to give a visual clue to the shape of an object. The
AcGiWorldDraw::isolines() function allows an entity to display the same
number of isolines per surface as specified by the user. This value is an integer
between 0 and 2047. The default number of isolines is 4. The
AcGiViewportDraw class provides an analogous function:
virtual Adesk::UInt32
AcGiWorldDraw::numberOfIsolines() const;
Transformations
The graphics pipeline can apply three possible transformations to an entity:
Model coordinates
Eye coordinates
*
Perspective
transform
Display coordinates
*Front and back clipping are performed here if specified
For the REGEN, HIDE, and SHADE commands, the entity’s world coordinates
are sent through the graphics pipeline shown in the figure above. The view
transformation specifies a particular view of the world coordinates, analo-
gous to viewing a scene with a camera. The camera has a location in world
space and a particular orientation toward the world coordinate “scene.”
When the view transformation is complete, world coordinates are trans-
formed to eye coordinates, looking down the Z axis of the camera.
If perspective is enabled, the eye coordinates are transformed to display coor-
dinates. This transformation involves division according to how far away
something is from the camera, so that objects farther away from the camera
appear smaller than objects closer to the camera.
The following sections discuss these coordinate systems in greater detail.
Transformations | 711
When converting an entity’s model coordinate geometry to world coordi-
nates, the current net block transform is used. For example, if a piece of
model coordinate geometry is in more than one block insert, then the net
effect of being in the inserts is the net block transform.
Transformation Examples
The AcGiViewport class provides functions that give you access to the
graphics pipeline, allowing you to apply each transformation explicitly and
perform the mathematics yourself. If you are manipulating entities in the
graphics pipeline yourself, you use different forms of the AcGi polygon and
polyline depending on where you are in the graphics pipeline.
Transformations | 713
// Display the eye coordinate equivalent of the
// model space polygon.
//
pV->subEntityTraits().setColor(kGreen);
pV->geometry().polygonEye(count, verts);
Acad::ErrorStatus
AsdkViewGeomSamp::transformBy(const AcGeMatrix3d &xfm)
{
assertWriteEnabled();
for (Adesk::UInt32 i = 0; i < mNumVerts; i++) {
mVerts[i].transformBy(xfm);
}
return Acad::eOk;
}
Adesk::Boolean
AsdkViewGeomSamp::worldDraw(AcGiWorldDraw* pW)
{
// Draw a pyramid.
//
// If this is the regular AutoCAD DISPLAY mode...
//
if (pW->regenType() == kAcGiStandardDisplay) {
// From each viewport’s vantage point, figure out
// which sides of the pyramid are visible,
// then make the visible ones yellow and the hidden
// ones blue.
//
// Set the extents of the pyramid here because
// AcGiViewportGeometry’s polylineEye() doesn’t
// set extents.
//
for (Adesk::UInt32 i = 0; i < mNumVerts; i++) {
AcGePoint3d pt[2];
pt[0] = mVerts[i];
pt[1] = mVerts[(i + 1) % mNumVerts];
pW->geometry().setExtents(pt);
}
return Adesk::kFalse; // Call viewport draws.
}
Transformations | 715
// Otherwise, give HIDE, SHADE, RENDER, or proxy graphics
// a pyramid with filled faces.
//
const Adesk::UInt32 faceListSize = 16;
static Adesk::Int32 faceList[faceListSize] = {
3, 0, 1, 2,
3, 0, 2, 3,
3, 0, 3, 1,
3, 1, 2, 3
};
void
AsdkViewGeomSamp::viewportDraw(AcGiViewportDraw* pV)
{
// For this viewport, draw a pyramid with yellow
// visible lines and blue hidden lines.
//
// Get this viewport’s net transform. This transform
// includes this entity’s block transforms and this
// viewport’s view transform; it does not include the
// perspective transform if we’re in perspective
// mode; that currently has to be applied separately
// when in perspective mode.
//
AcGeMatrix3d modelToEyeMat;
pV->viewport().getModelToEyeTransform(modelToEyeMat);
// AB
color = which_faces & 0x5 ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = AEye;
verts[1] = BEye;
pV->geometry().polylineEye(2, verts);
// AC
color = which_faces & 0x3 ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = AEye;
verts[1] = CEye;
pV->geometry().polylineEye(2, verts);
// AD
color = which_faces & 0x6 ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = AEye;
verts[1] = DEye;
pV->geometry().polylineEye(2, verts);
Transformations | 717
// CD
color = which_faces & 0xa ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = CEye;
verts[1] = DEye;
pV->geometry().polylineEye(2, verts);
// DB
color = which_faces & 0xc ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = DEye;
verts[1] = BEye;
pV->geometry().polylineEye(2, verts);
// BC
color = which_faces & 0x9 ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = BEye;
verts[1] = CEye;
pV->geometry().polylineEye(2, verts);
}
verts[0].x -= half_xsize;
verts[0].y += half_ysize;
verts[1].x += half_xsize;
verts[1].y += half_ysize;
verts[2].x += half_xsize;
verts[2].y -= half_ysize;
verts[3].x -= half_xsize;
verts[3].y -= half_ysize;
verts[4] = verts[0];
pV->subEntityTraits().setColor(kRed);
pV->geometry().polylineDc(num_verts, verts);
}
Transformations | 719
This is how the example calculates the necessary number of line segments in
the polyline. First, given a circle of a given radius that is centered at the origin
and located in the XY plane, and given a vertical line that intersects the X
axis at radius − 0.5 pixels, determine the angle between the X axis and a line
segment that extends from the origin to the point where the vertical line
intersects the circle. Two pi divided by this angle provides the minimum
number of segments needed by a polyline to look like a circle. The user will
not be able to differentiate the individual line segments that make up the cir-
cle because the visual differences are less than a pixel.
angle
Adesk::Boolean
AsdkTesselateSamp::worldDraw(AcGiWorldDraw *pW)
{
// Draw a red 1x1 drawing-unit square centered at the
// world coordinate origin and parallel to the XY-plane.
//
const Adesk::UInt32 num_pts = 5;
AcGePoint3d verts[num_pts];
void
AsdkTesselateSamp::viewportDraw(AcGiViewportDraw *pV)
{
static double two_pi = atan(1.0) * 8.0;
int num_segs = 8;
double angle = two_pi / num_segs;
Transformations | 721
// Given a circle centered at the origin of a
// given radius in the XY-plane, and given a
// vertical line that intersects the X-axis at
// ’radius - half a pixel’, what is the angle
// from the X-axis of a line segment from the
// origin to the point where the vertical line
// and the circle intersect? Two pi divided by
// this angle gives you a minimum number of
// segments needed by a polyline to look like
// a circle and not be able to differentiate
// the individual segments because the visual
// differences are less than the size of a
// pixel. (This is not the only way to figure
// this out but it’s sufficient.)
//
angle = acos((radius - 1.0 / (area.x / 2.0))
/ radius);
double d_num_segs = two_pi / angle;
Background
Clip boundaries are closed, non-self-intersecting, concave 2D polygons.
Optional front and back Z clipping values can be assigned. The clip boundary
is expressed in an arbitrary coordinate system relative to the objects being
clipped.
In AutoCAD, when the user defines a clipping boundary for a block, the view
direction and twist of the current view are used to define the coordinate sys-
tem for the clip boundary. This might be the same as the coordinate system
of the block reference being clipped. This is reflected in the API by the provi-
sion of a transformation to the clipping space from the block reference
system:
pGeom->pushModelTransform(myTransform());
AcGiClipBoundary cb;
cb.m_bDrawBoundary= true;
cb.m_vNormal = AcGeVector3d::kZAxis;
cb.m_ptPoint = AcGePoint3d::kOrigin;
// No Z clipping
cb.m_bClippingBack = cb.m_bClippingFront = false;
cb.m_dFrontClipZ = cb.m_dBackClipZ = 0.;
// Draw something
pGeom->circle(...);
pGeom->popModelTransform();
if(bPopClipBoundary){ pGeom->popClipBoundary(); }
return true; // world-only
}
Since this clipping is a complex operation, some AcGi implementations
might not support it fully. In this case, the AcGi implementation may return
false from pushClipBoundary(), and you should not call popClipBoundary().
In This Chapter
27
This chapter discusses the main uses of the AcGe library, ■ Overview of the AcGe Library
■ Using Basic Geometry Types
which provides a number of classes for representing 2D
■ Using the Line and Plane Classes
and 3D geometry. This library is intended for use by any ■ Parametric Geometry
725
Overview of the AcGe Library
The AcGe library includes a broad set of classes for representing commonly
used geometry, such as points, lines, curves, and surfaces. It provides a
common representation for geometry that can be used by any Autodesk
application. The library is purely mathematical; though its classes do not
deal directly with the database or with graphics, many of its classes are used
by the AcDb and AcGi libraries.
AcGeBoundBlock2d AcGeBoundBlock3d
AcGeClipBoundary2d AcGeCurve3d
AcGeCurve2d AcGeCircArc3de
AcGeCircArc2d AcGeCompositeCurve3d
AcGeCompositeCurve2d AcGeEllipArc3e
AcGeEllipArc2d AcGeExternalCurve3d
AcGeExternalCurve2d AcGeLinearEnt3d
AcGeLinearEnt2d AcGeLine3d
AcGeLine2d AcGeLineSeg3d
AcGeLineSeg2d AcGeRay3d
AcGeRay2d AcGeMatrix3d
AcGeOffsetCurve2d AcGeOffsetCurve3d
AcGeSplineEnt2d AcGeSplineEnt3d
AcGeCubicSplineCurve2d AcGeCubicSplineCurve3d
AcGeNurbCurve2d AcGeNurbCurve3d
AcGePolyline2d AcGePolyline3d
AcGeCurveCurveInt2d AcGeAugPolyline3d
AcGePointEnt2d AcGeCurveCurveInt3d
AcGePointOnCurve2d AcGeCurveSurfInt
AcGePosition2d AcGePointEnt3d
AcGePointOnCurve3d
AcGePointOnSurface
AcGeCurveBoundary
AcGePosition3d
AcGe
AcGeSurfSurfInt
AcGeContext
AcGeSurface
AcGeDwgIO
AcGeCone
AcGeDxfIO
AcGeCylinder
AcGeFileIO
AcGeExternalBoundedSurface
AcGeFiler
AcGeExternalSurface
AcGeInterval
AcGeNurbSurface
AcGeKnotVector
AcGeOffsetSurface
AcGeLibVersion
AcGePlanarEnt
AcGeMatrix2d
AcGeBoundedPlanet
AcGeMatrix3d
AcGePlane
AcGePoint2d
AcGeSphere
AcAxPoint2d
AcGeTorus
AcGePoint3d
AcAxPoint3d
AcGeScale2d
AcGeScale3d
AcGeTol
AcGeVector2d
AcGeVector3d
gepnt2d.h AcGePoint2d::kOrigin
gemat2d.h AcGeMatrix2d::kIdentity
gevec2d.h AcGeVector2d::kIdentity
AcGeVector2d::kXAxis
AcGeVector2d::kYAxis
geline2d.h AcGeLine2d::kXAxis
AcGeLine2d::kYAxis
gepnt3d.h AcGePoint3d::kOrigin
gemat3d.h AcGeMatrix3d::kIdentity
gevec3d.h AcGeVector3d::kIdentity
AcGeVector3d::kXAxis
AcGeVector3d::kYAxis
AcGeVector3d::kZAxis
geline3d.h AcGeLine3d::kXAxis
AcGeLine3d::kYAxis
AcGeLine3d::kZAxis
geplane.h AcGePlane::kXYPlane
AcGePlane::kYZPlane
AcGePlane::kXZPlane
gegbl.h AcGeContext::gOrthoVector()
Tolerances
Many methods accept a tolerance value as one of their parameters. This value
is of the AcGeTol class and always has a default value, as defined in
AcGeContext::gTol. Functions such as isClosed() and isPlanar() calculate
whether the start points and endpoints are within the defined tolerance
before returning a Boolean value. You can change the tolerance for one par-
ticular function call, or you can change the global tolerance value.
The AcGeTol class provides two functions for setting the tolerance for points
and vectors:
void
setEqualPoint(double);
void
setEqualVector(double);
The AcGeTol class also provides two functions for obtaining the tolerance for
points and vectors:
double equalPoint() const;
double equalVector() const;
NOTE These rules mean that two lines are close to each other as point sets in
the part of the modeling space of diameter diam only if the tolerance equalVector
is set tighter than equalPoint/diam.
if (line1.isOn(p1))
if (plane1.isOn(p1))
if (line1.isOn(plane1))
The following functions test if lines or planes are parallel, perpendicular, or
coincident:
if (line1.isParallelTo(line2))
if (line1.isParallelTo(plane1))
if (line1.isPerpendicularTo(line2))
if (line1.isPerpendicularTo(plane1))
if (line1.isColinearTo(line2))
if (plane1.isParallelTo(plane2))
if (plane1.isPerpendicularTo(plane2))
if (plane1.isCoplanarTo(plane2))
The following functions return the intersections of lines and planes:
if (line1.intersectWith(line2,p1))
if (line1.intersectWith(plane1,p1))
if (plane1.intersectWith(plane2,line1))
Parametric Geometry
The following sections discuss working with parametric geometry.
Curves
Curves and surfaces in the AcGe library are parametric. A curve is the result
of mapping an interval of the real line into 2D or 3D modeling space using
an evaluator function with one argument, such as f(u). Similarly, a surface is
a mapping from a 2D domain into 3D modeling space using an evaluator
function based on two arguments for example, f(u, v). Each 2D and 3D curve
class has a getInterval() function that returns the parametric interval. This
function has two forms: the first returns the interval; the second returns the
interval as well as the start point and endpoint of the curve.
NOTE If the interval is unbounded in either direction, the start points and
endpoints do not have meaning.
■ Orientation
■ Periodicity
■ Closure
■ Planarity
■ Length
Adesk::Boolean
AcGeCurve3d::isPeriodic(double& period) const;
A closed curve has start points and endpoints that are the same. Curves can
be either closed or open. Use these functions to determine whether a curve
is closed:
Adesk::Boolean
AcGeCurve2d::isClosed(
const AcGeTol&=
AcGeContext::gTol) const;
Adesk::Boolean
AcGeCurve3d::isClosed(
const AcGeTol&=
AcGeContext::gTol) const;
A 3D curve can be planar (meaning that all of its points reside in the same
plane) or nonplanar. Use this function to determine whether a 3D curve is
planar:
Adesk::Boolean
AcGeCurve3d::isPlanar(
AcGePlane&,
const AcGeTol&=AcGeContext::gTol) const;
double
AcGeCurve3d::length(
double fromParam, double toParam,
double=AcGeContext::gTol.equalPoint())
const;
You can use the AcGeCurve2d::evalPoint() and AcGeCurve3d::evalPoint()
functions to obtain the model space point that corresponds to a given
parametric value. If your application performs evaluation frequently, you’ll
probably find the AcGePointOnCurve3d and AcGePointOnCurve2d classes
more efficient (see “Special Evaluation Classes” on page 738). The curve
functions for evaluating points are as follows:
AcGePoint2d
AcGeCurve2d::evalPoint(double param) const;
AcGePoint2d
AcGeCurve2d::evalPoint(
double param, int numDeriv,
AcGeVector2dArray& derivArray) const;
AcGePoint3d
AcGeCurve3d::evalPoint(double param) const;
AcGePoint3d
AcGeCurve3d::evalPoint(
double param, int numDeriv,
AcGeVector3dArray& derivArray) const;
Adesk::Boolean
AcGeCurve3d::isDegenerate(
AcGe::EntityId& degenerateType,
const AcGeTol&=AcGeContext::gTol)
const;
Adesk::Boolean
AcGeCurve3d::isDegenerate(
AcGeEntity3d*& pConvertedEntity,
const AcGeTol&=AcGeContext::gTol)
const;
Surfaces
The orientation of a surface partially determines its evaluated normal vec-
tors. A parametric surface has two parameters, u and v, each representing the
direction of the parametric lines on the surface. If you take the cross-product
of the u tangent vector and the v tangent vector at the same point, you
obtain a vector that is normal to the surface. This vector is the natural normal
of the surface at that point. You can reverse the orientation of a surface by
calling the following function:
AcGeSurface&
AcGeSurface::reverseNormal()
if (projectedEntity->type() == AcGe::kEllipArc3d)
...
else if (projectedEntity->type() == AcGe::kCircArc3d)
...
else if (projectedEntity->type() == AcGe::kLineSeg3d)
...
The following example constructs a NURBS curve and finds the closest point
on the curve to the point p1. The closest point is returned as an
AcGePointOnCurve3d object from which the coordinates and parameter value
are obtained:
AcGeKnotVector knots;
AcGePoint3dArray cntrlPnts,
AcGePointOnCurve3d pntOnCrv;
AcGePoint3d p1(1,3,2);
knots.append (0.0);
knots.append (0.0);
knots.append (0.0);
knots.append (0.0);
knots.append (1.0);
knots.append (1.0);
knots.append (1.0);
knots.append (1.0);
cntrlPnts.append (AcGePoint3d(0,0,0));
cntrlPnts.append (AcGePoint3d(1,1,0));
cntrlPnts.append (AcGePoint3d(2,1,0));
cntrlPnts.append (AcGePoint3d(3,0,0));
AcGeNurbCurve3d nurb (3, knots, cntrlPnts);
nurb.getClosestPointTo(p1,pntOnCrv);
p2 = pntOnCrv.point();
double param = pntOnCrv.parameter();
protected:
AcDbDwgFiler* mpFiler;
};
// Inline methods.
//
inline
AcGeDwgFiler::AcGeDwgFiler(AcDbDwgFiler* filer) : mpFiler(filer)
{}
inline AcGeDwgFiler&
AcGeDwgFiler::setDwgFiler(AcDbDwgFiler* filer)
{
mpFiler = filer;
return *this;
}
inline AcDbDwgFiler*
AcGeDwgFiler::dwgFiler()
{
return mpFiler;
}
AcGePersistentXEnt ();
~AcGePersistentXEnt ();
In This Chapter
28
This chapter shows how to use the AcBr library ■ Overview
■ Domain
(libacbr.dll) to access topological, geometric, and ana-
■ Limitations
lytic data contained in certain AutoCAD entities, such ■ Class Hierarchy
as solids, bodies, and regions (that is, objects of class ■ Topological Objects
■ AcBr Class Descriptions
AcDb3dSolid, AcDbBody, and AcDbRegion), and myriad
■ Enumerated Types
751
Overview
The AcBr library can be used with the following AutoCAD entities:
The AcBr library provides read-only access to a subset of modeling data con-
tained in AutoCAD solids. These solids are not required to be database active,
and may be created in any of the following ways:
Domain | 753
Limitations
Certain operations cannot support nonuniform scaling. This includes all
functions that return an external curve or surface (including NURBS
surfaces).
The entire chain of transforms from the subentity path is cached at the time
that an AcBr object’s subentity path is set (for efficiency reasons). If a block
reference is moved, it will point to a new transform matrix but the AcBr
object will not know that its cached transform is out of date. If an insert is
changed to refer to a different AutoCAD entity, the subentity path simply no
longer has relevance and should be updated to reflect the new entity refer-
ence before being used to reinitialize all relevant AcBr objects.
Singularities (such as the apex of a cone) map to edges in AutoCAD and thus
can be used to initialize an AcBrEdge for the express purpose of querying for
the vertex, but cannot be queried for curve geometry or used to set an
AcBrLoopEdgeTraverser. They can also be accessed using an
AcBrLoopVertexTraverser, as a singularity corresponds to a single loop
boundary of a face.
Just as with AcDbObject pointers, AcBr objects cannot be used once the
AutoCAD database object has been closed in the database or goes out of
scope; they are not persistent. Any change to the database object will be
flagged as an eBrepChanged error, unless the validation level has been set to
ignore database changes. An out-of-scope or closed database object will gen-
erally cause Acad::eNotInDatabase to be returned.
AcBrEntity
AcBrBrep
AcBrComplex
AcBrEdge
AcBrFace
AcBrLoop
AcBrShell
AcBrVertex
AcBrMeshControl
AcBrMesh2dControl
AcBrMeshEntity
AcBrElement
AcBrElement2d
AcBrNode
AcBrTraverser
AcBrBrepComplexTraverser
AcBrBrepEdgeTraverser
AcBrBrepFaceTraverser
AcBrBrepShellTraverser
AcBrBrepVertexTraverser
AcBrBrepShellTraverser
AcBrBrepVertexTraverser
AcBrComplexTraverser
AcBrEdgeLoopTraverser
AcBrElement2dNodeTraverser
AcBrFaceLoopTraverser
AcBrLoopEdgeTraverser
AcBrLoopVertexTraverser
AcBrMesh2dElement2dTraverser
AcBrShellFaceTraverser
AcBrVertexEdgeTraverser
AcBrVertexLoopTraverser
AcBrHit
AcBrHitPath
AcBrMesh2dFilter
Note that AcBr objects are not derived from AcDbObject, and therefore
cannot be registered with the AutoCAD database.
Global Searches
Global traversers (such as AcBrBrepFaceTraverser and
AcBrBrepEdgeTraverser) provide the ability to traverse all of the topological
objects in the solid (complexes, shells, faces, edges, vertices).
Topological traversers
Class Objects
Mesh traversers
Class Objects
Entity Classes
Boundary representation objects are typically built using a default AcBr con-
structor and then initialized either with a set() function or with a traverser
and one of its get* functions.
■ AcBrEntity
■ AcBrBrep
■ AcBrComplex
■ AcBrShell
■ AcBrFace
■ AcBrLoop
■ AcBrEdge
■ AcBrVertex
Containment Classes
Containment objects are never built directly by the user. They are returned
by line containment queries on entities derived from AcBrEntity.
The AcBrHit class is a containment class.
Mesh Classes
Mesh objects are never built directly by the user, except where noted in the
ObjectARX Reference. They are returned by mesh traversal queries.
The mesh classes include the following:
■ AcBrMeshEntity
■ AcBrMesh
■ AcBrMesh2d
■ AcBrElement
■ AcBrElement2d
■ AcBrNode
■ AcBrMesh2dFilter
■ AcBrMeshControl
■ AcBrMesh2dControl
Traverser Classes
Traverser objects are typically built using a default AcBrTraverser* construc-
tor and then initializing with one of the set* functions. Note that the list
owner must be set before the list position can be set independently, to pro-
vide context.
All initializer functions reset the criteria for next() and done(). They fall into
the general algorithmic categories as follows:
■ AcBrTraverser
■ AcBrBrepComplexTraverser
■ AcBrBrepShellTraverser
■ AcBrBrepFaceTraverser
■ AcBrBrepEdgeTraverser
■ AcBrBrepVertexTraverser
■ AcBrComplexShellTraverser
Enumerated Types
The AcBr struct contains enumerated types that are unique to the AcBr
library and that are used as return codes or on the argument list of local class
functions. The various enum fields are described below.
Validation Level
The AcBr::ValidationLevel enum sets the level of validation for an AcBr
object. If kFullValidation (the default upon object instantiation) is speci-
fied, every function that accesses the brep topology (directly or indirectly)
first checks the associated AutoCAD database object to see if it has been mod-
ified since the AcBr object was last set. This is an expensive operation, but it
guarantees all brep data is within scope. If kNoValidation is specified, the
database is not checked unless it is critical to the completion of the function’s
tasks. This is more efficient but does not guarantee the brep data is within
scope. Setting the validation level on an object-by-object basis prevents any
collisions between applications loaded simultaneously.
LoopType
The AcBr::LoopType enum classifies a loop as interior, exterior, and so on.
Peripheral loops are kLoopExterior, and there is only one such loop per face
(by industry convention). Holes are returned as kLoopInterior, and there
may be several per face (providing there is also an exterior loop). Cones and
cylinders (whether with an elliptical or circular base) have at least two base
loops (if they are complete in both u and v), which are returned as
kLoopWinding as opposed to kLoopExterior due to the restriction of there
being just one exterior loop (along with the fact that neither base loop is a
hole). Singularities (such as the apex of a cone) are returned as
kLoopUnclassified. All loops on spheric and toric surfaces, as well as closed
periodic NURBS, return kLoopUnclassified.
Building an Application
An application that uses the AcBr library must have the library file libacbr.dll
available to link against.
More importantly, the library is needed to ensure proper registration of the
AcBr classes with ObjectARX at runtime.
Therefore it is important that libacbr.dll be explicitly loaded by the applica-
tion, if it has not already been loaded by the modeler or another application.
The best way to ensure this is to use acrxDynamicLoader() and
acrxClassDictionary() to load libacbr.dll and to check if it has been loaded.
767
768
Migrating ADS Programs to
ObjectARX
In This Appendix
A
To simplify the migration of AutoCAD Development ■ Migrating to ObjectARX
■ Loading Applications: ADS versus
System applications to the ObjectARX program envi-
ObjectARX
ronment, the ADS library was ported to the ObjectARX ■ Building ADS Applications in the
ObjectARX Program Environment
program environment. The ObjectARX version is ■ Sample ObjectARX Application
almost identical to the ADS version. This appendix pro- ■ ObjectARX-Exclusive Data Type
769
Migrating to ObjectARX
Existing ADS applications must be ported to ObjectARX. All of the library
functions that were previously available in the ADS library are included in
ObjectARX. Applications that frequently communicated with AutoCAD
through the ADS library or other calls run faster in the ObjectARX program
environment than in the ADS program environment.
// Here we declare the functions that handle the calls; at the moment
// there are two of them.
// To add more functions to this table, just put them in the list,
// after declaring the function names. Note that in standard C it’s
// all right to have a superfluous comma after the last item.
// The code from here to the end of dofun() is UNCHANGED when you
// add or delete functions.
// Get the function code and check that it’s within range.
// (It can’t fail to be, but paranoia doesn’t hurt.)
if ((val = acedGetFunCode()) < 0 || val >= ELEMENTS(func_table))
{
acdbFail(/*MSG2*/"Received nonexistent function code.");
return RTERROR;
}
if (rb == NULL)
return RTERROR;
if (rb->restype == RTSHORT) {
x = rb->resval.rint; // Save in local variable
} else {
acdbFail(/*MSG3*/"Argument should be an integer.");
return RTERROR;
}
while (n)
ans *= n--;
return ans;
}
if (rb == NULL)
return RTERROR; // A proper error msg would
// be better.
return RTNORM;
}
if (x == 0.0) {
return 0.0;
}
y = (x * 2 + .1) / (x + 1.0);
c = (y - x / y) / 2;
cl= 0.0;
In This Appendix
B
ObjectARX contains a set of functions that is ■ Overview
■ Function Sequence Outline
collectively called the Programmable Dialog Box (PDB)
■ Definitions and Declarations
package. PDB functions define dialog box controls, ■ Handling Tiles
779
Overview
Dialog box programming involves two phases:
■ Designing the dialog box Dialog boxes are defined by text files written in
dialog control language (DCL). The DCL description of a dialog box
defines how the box appears and what it contains. For more information,
see Part III, “Programmable Dialog Box Reference,” in the AutoCAD
Customization Guide.
■ Supporting the dialog box in your application The parts of a dialog box
define how it behaves; however, the use and behavior of a dialog box
depend on the application that employs it.
The examples given in this section demonstrate the typical dialog box func-
tion sequence as follows:
1 Load the DCL file with an ads_load_dialog() call.
2 Call ads_new_dialog() to display a particular dialog box on the AutoCAD
graphics screen.
Check the status of the value that ads_new_dialog() returns. Calling
ads_start_dialog() when the ads_new_dialog() call has failed can have
unpredictable results.
3 Initialize the dialog box by setting up tile values, lists, and images.
The functions typically called at this time are as follows:
The AutoCAD CMDACTIVE system variable has a bit that indicates whether a
dialog box is active. For more information on system variables, see the
AutoCAD Command Reference.
If the user must enter input based on the graphics screen rather than use the
dialog box itself (for example, to specify a point or an entity), you must hide
the dialog box. That is, you must call ads_done_dialog() to redisplay the
graphics screen, and then restart the dialog box after the user has made the
selection. The following lists show the functions that are not allowed.
AutoCAD Queries and Command Functions
The following AutoCAD functions cannot be called while a dialog box is
active:
■ acedCommand()
■ acedCmd()
■ acedHelp()
■ acedOsnap()
■ acedGetInt()
■ acedGetReal()
■ acedGetString()
■ acedGetPoint()
■ acedGetCorner()
■ acedGetDist()
■ acedGetAngle()
■ acedGetOrient()
■ acedGetKword()
■ acedGetInput()
■ acedDragGen()
■ acedPrompt()
■ acedMenuCmd()
■ acedRedraw()
■ acedGraphScr()
■ acedTextScr()
■ acedTextPage()
The functions that write text, such as acutPrintf(), are useful for displaying
information while testing a dialog box, but they should not be used in a fin-
ished product.
Low-Level Graphics Functions
The following graphics functions cannot be called while a dialog box is
active:
■ acedGrVecs()
■ acedGrDrag()
■ acedGrRead()
■ acedGrText()
■ acedGrDraw()
■ acedGrText()
■ acdbEntMod()
■ acdbEntMake()
■ acdbEntDel()
■ acedEntSel()
■ acedNEntSel()
■ acedNEntSelP()
■ acdbEntUpd()
Callback Functions
To define what action is taken when a dialog box tile is selected, associate an
ObjectARX function with that tile by calling the ads_action_tile(). Within
the callback, you often need access to attributes in the DCL file. The
ads_get_tile() and ads_get_attr() functions provide this access
(ads_get_attr() gets the value saved in DCL, while ads_get_tile() gets the
current runtime value), but the values you are most likely to use, those asso-
ciated with the selected tile, are provided automatically.
In most cases, every active tile within a dialog box generates a callback. The
callback function should do validity checking for its associated tile and
update information in the dialog box that pertains to the value of the tile.
Updating the dialog box can include issuing an error message, disabling
other tiles, and displaying the appropriate text in an edit box or list box.
Only the OK button (or its equivalent) should query the tile values to save the
settings the user finally selected. Update the variables associated with tile val-
ues within the callback for the OK button, not within the callback for an
individual tile. If permanent variables are updated within the individual tile
callbacks, there is no way to reset the values if the user chooses Cancel. If the
OK button’s callback detects an error, it should display an error message and
return focus to the tile in error; it should not exit the dialog box.
When a dialog box includes several tiles whose handling is similar, it can be
convenient to associate these tiles with a single callback function. The prin-
ciple of not committing to the user’s changes until the user specifies OK still
applies. A callback function common to several tiles can be table driven,
using user-defined attributes to provide values specific to each tile.
void
bmake_handler()
// Load dialog box and do global initialization
//
while (what_next >= DLGSTATUS) {
// Indicates custom return code
// Other initialization such as ads_new_dialog(),
// ads_action_tile(), ads_set_tile(), and
// ads_start_list() calls.
//
ads_start_dialog(hdlg, &what_next);
switch (what_next) {
case 4:
acedGetPoint(NULL, "Insertion base point: ",
pick_pt);
acdbRToS(pick_pt[X], 2, 4, x_pt);
acdbRToS(pick_pt[Y], 2, 4, y_pt);
acdbRToS(pick_pt[Z], 2, 4, z_pt);
break;
...
}
}
}
The following example hides multiple dialog boxes:
// Global variables
//
ads_point pick_pt;
void
maindlg_handler()
{
int what_next;
ads_callback_packet dummy_pkt;
Status Codes
The ads_start_dialog() function has a status argument that it sets to indi-
cate how the dialog box ended. The values for this status are shown in the
following table:
Symbol Description
Symbol Description
CBR_LOST_FOCUS For edit boxes, the user moved to another tile but did not make
a final selection.
CBR_DRAG For sliders, the user changed the value by dragging the indicator
(or equivalent) but did not make a final selection.
CBR_DOUBLE_CLICK For list boxes or image buttons, the user double-clicked to make
a final selection.
The symbols described in this section are used with the ads_mode_tile() and
ads_start_list() functions.
The function ads_start_list() begins handling a list for a list box or a pop-
up list. The symbols to use are shown in the following table:
Symbol Description
Symbol Description
Attribute names and values are passed as strings; your programs need to allo-
cate space for them. The upper limit on strings used with dialog boxes is
defined in TILE_STR_LIMIT as 255 plus one for the null terminator, EOS).
Handling Tiles
Your program has some control over the tiles in the current dialog box at
initialization time and action (callback) time. This section introduces the
tile-handling functions.
When you use ads_mode_tile() to disable a tile that has the current focus,
you must call ads_mode_tile() again to set the focus to a different tile (in
most cases, the next tab stop in the dialog box). Otherwise, the focus will
remain on a disabled tile, which is illogical and can cause errors.
An example of a tile “disabling itself” is a series of dialog box “pages” that the
user steps through by choosing a Next or Previous button. When the user
presses Next on the next-to-last page, the button is disabled. The same hap-
pens after pressing Previous on the second page. In both cases, the code must
disable the button that was pressed, and then set the focus to a different tile.
strcpy(value, cbpkt->value);
if (strcmp(value, "0") == 0) { // Cluster is disabled.
ads_mode_tile(hdlg, "group", MODE_DISABLE);
} else { // The value must equal "1".
ads_mode_tile(hdlg, "group", MODE_ENABLE);
}
}
You can inspect other attributes besides a tile’s value with the get_attr()
function. The following example retrieves the label of a button called
“pressme”:
char label_str[TILE_STR_LIMIT];
ads_get_attr(hdlg, "pressme", "label", label_str, TILE_STR_LIMIT);
If you use ads_get_attr() to retrieve a value attribute, it gets the value
attribute saved in the DCL file (the initial value of the tile). The
ads_get_tile() function, however, gets the current runtime value of the
tile. The two values are not necessarily the same.
The ads_get_attr() function returns the attribute’s value in a string argu-
ment (value). Because this function sets the value of the string, you must
allocate space for it, as shown in the preceding example.
Regardless of which list operation you are doing, you must call the three
functions in the correct sequence: ads_start_list(), then ads_add_list()
(possibly more than once), then ads_end_list().
Lists are most easily represented by linked result buffers, as shown in the fol-
lowing example:
struct resbuf *appnames, *rb;
rb = newnames;
Vectors and filled rectangles are useful for simple images, such as the color
swatches (filled rectangles) the AutoCAD Select Color dialog box uses to
display the user’s choice of color. For complicated images, slides are more
convenient. However, displaying slides can be time consuming. If you use
them, keep them simple.
The image-drawing function, ads_vector_image(), requires that you specify
absolute coordinates, while ads_fill_image() and ads_slide_image()
require a starting coordinate with a relative width and height. To do this
correctly, you must know the exact dimensions of the image tile or image
button. Because these dimensions are usually assigned when the dialog box
is laid out, the PDB package provides a function, ads_dimensions_tile(),
that returns the width and height of a particular tile. Call this function before
you begin creating an image. The origin of a tile, (0,0), is always its upper-left
corner.
Colors can be specified as AutoCAD color numbers or as one of the “logical”
color numbers shown in the following table.
...
Handling Sliders
When you handle actions and callbacks from sliders, your application should
check the reason code that it receives along with the callback.
Although you are not required to check the reason code, it is recommended
that you do so to reduce processing. The frequency of callbacks that sliders
generate depends on the platform, but some platforms generate a CBR_DRAG
callback for every mouse movement the slider detects.
Application-Specific Data
The ads_client_data_tile() function assigns application-specific data to a
tile. The data is available at callback time as the callback packet’s
client_data field. Client data is not represented in DCL; it is valid only while
your application is running. Using client data is comparable to using user-
defined attributes. The main difference is that user-defined attributes are
read-only, while client data can change at runtime. (Also, end users can
inspect user-defined attributes in the application’s DCL file, but client data is
invisible to them.)
The client data can be of whatever type you choose. It is declared as a pointer
to void.
You can express client data as a pointer to a data structure associated with the
dialog box or tile. The structure can be declared as temporary data local to
any function at or above the level of the function that calls
ads_start_dialog(). This lets you avoid declaring client data as global static
data.
Client data is a useful way to pass information to a callback function, because
no extra parameters can be added to the function.
Because your program must maintain the list displayed by a list box (or a
pop-up list), client data is good for handling this information. The previous
example of the mk_list() function already makes the list value an argument.
int
handler()
{
struct resbuf *csyshead, *usrhead;
ads_hdlg cldlg;
809
AcDbEntity class, 98, 355, 363 AcDbGroup class
acedSSGet() function, 111 newIterator() function, 154
acedSSNameX() function, 111 setColor() function, 154
colorIndex() function, 102 setLayer() function, 154
default protocol extension class and, 370 setLinetype() function, 154
deriving a custom entity class, 383 setVisibility() function, 154
deriving a custom entity class from, 349 acdbHandEnt() function, 216, 223, 240
extending entity functionality, 370 AcDbHostApplicationServices class , 658
using AcEdJig, 371, 383 AcDbHyperlink class, 139
deriving entities from, 349 AcDbHyperlinkCollection class, 139
draw() function, 105 AcDbHyperlinkPE class, 139
explode() function, 105, 123–124 AcDbIdMapping class, 508
functions rarely overridden, 352 AcDbIndex class, 78
functions usually overridden , 351 AcDbIndexFilterManager namespace, 78
getEcs() function, 136 acdbInters() function, 254
getGeomExtents() function, 105 AcDbIsPersistentReactor class, 402
getGripPoints() function, 105 acdbIsPersistentReactor() function, 402
getGsMarkersAtSubentPath() function, 106 AcDbLayerTable class, 147
getOsnapPoints() function, 105–107 AcDbLayerTableRecord class, 149
getStretchPoints() function, 105 creating and modifying, 150
getSubentPathsAtGsMarker() function, 106 AcDbLayout class, 156, 159
getTransformedCopy() function, 105, 107 AcDbLayoutManager class, 159
graphics generation and, 455 AcDbLayoutManagerReactor class, 159
Graphics System Markers, 109 AcDbLine class, creating in AutoCAD, 23
highlight() function, 106, 111 AcDbLinetypeTable class, 147
intersectWith() function, 105, 107–108 AcDbLongTransaction class, 69
layer() function, 104 AcDbLongTransWorkSetIterator class, 70
layerId() function, 105 AcDbMatchProperties class, 516
linetype() function, 103 as protocol extension class, 370
linetypeScale() function, 103 AcDbMline class, 156
list() function, 105 AcDbMlineStyle class, 156
moveGripPointsAt() function, 105 AcDbObject class, 82, 394–395
moveStretchPointsAt() function, 105 addReactor() function, 397
proxy object class derived from, 388 custom notification and, 398
setColorIndex() function, 102 deep cloning with, 467
setLayer() function, 104 deriving from, 291
setLinetype() function, 103 erase() function, 94
setLinetypeScale() function, 103 new() function, 85
setVisibility() function, 104 notification events, 398
subentPtr() function, 106, 112 overriding functions, 292
transformation functions, 363 proxy object class derived from, 388
transformBy() function, 105, 107 reactors, 395, 402
viewportDraw() function, 105 setXData() function, 86
visibility() function, 104 undo mechanism and, 455
worldDraw() function, 105 wblockClone() function, 468
AcDbEntityReactor class, 397 xData() function, 86
acdbEntLast() function, 249 AcDbObjectId class, 469
acdbEntMake() function, 149, 228–229, 232 AcDbObjectIds, translating ads_names to, 83
acdbEntMod() function, 227–228, 233 AcDbObjectReactor class, 394, 397
acdbEntNext() function, 214, 249 notification
acdbEntUpd() function, 234 events, timing of, 410
acdbFail() function, 527 functions, 397–398
AcDbFilter class, 78 transient reactors derived from, 402
AcDbFilteredBlockIterator class, 78 acdbOpenObject() function , 82
AcDbFullSubentPath class, 110 with close() function, 454
acdbGetSummaryInfo() function, 80 AcDbPlotSettings class, 159
acdbGetSummaryInfoManager() function, 80 AcDbPlotSettingsValidator class, 159
810 | Index
AcDbProxyEntity class, 388 AcEdInputPointManager class, 567
AcDbProxyObject class, 388, 391 acedInvoke() function, 528
acdbPutSummaryInfo() function, 80 AcEditorReactor class, 75, 395–396, 398, 504
acdbRegApp() function, 237–238 notification functions, 398
AcDbRegAppTable class, 147 transactions and, 453
acdbRToS() function, 266 AcEdJig class, 371, 383
acdbSaveAsR13() function, 63 adding entity to the database, 378
acdbSaveAsR14() function, 63 deriving from, 350, 371
AcDbSummaryInfoManager class, 80 drag loop, 372, 374
AcDbSummaryInfoReactor class, 80 drag() function, 372
acdbTblNext() function, 242 general steps for using, 371
acdbTblSearch() function , 242–243 implementing sampler(), update(), and
AcDbTextStyleTable class, 147 entity() functions, 375, 378
AcDbTextStyleTableRecord class, 469 cursor types (table), 375
associating with AcGiTextStyle, 707 display prompt, 375
AcDbTransactionManager class, 663 keyword list, 375
undo mechanism and, 455 user input controls (list), 377
AcDbUCSTable class, 147 sample code, 378, 383
AcDbViewportTable class, 147 setting up parameters for drag sequence,
AcDbViewTable class, 147 372
AcDbWblockCloneFiler class, 488 acedMenuCmd function(), 274
acdbXdRoom() function, 239 acedNEntSel() function, 112, 214, 217, 259
acdbXdSize() function, 239 acedNEntSelP() function, 112, 214, 259, 535
AcDbXrecord class, 161 acedOsnap() function, 252, 535
AcDbXrefFileLock class, 76 acedPrompt() function, 273
acdbXrefReload() function, 76 acedPutSym() function, 250, 526
AcEd library, 12 acedRedraw() function, 275
class hierarchy, 12 acedRegFunc() function, 529
acedAlert() function, 527 acedSetVar() function, 249
acedapi.lib library, 10 acedSSAdd() function, 209, 214
acedArxLoaded() function, 533 acedSSDel() function, 209
acedArxUnload() function, 533 acedSSFree() function, 203
acedCmd() function, 246–247 acedSSGet() function, 111, 200
acedCommand() function, 246–247 DXF group codes, 205
acedDefun() function, 524, 527 example, 201
acedDragGen() function, 212, 259, 263, 535 selection set options, 201
acedEntSel() function, 214, 216, 259 acedSSLength() function, 210
acedFindFile() function, 251 acedSSMemb() function, 210
acedGetArgs() function, 526 acedSSName() function, 210
acedGetDist() function, 259 example, 210
acedGetFileD() function, 252 acedSSNameX() function, 111
acedGetFunCode() function, 526 acedSSXform() function
acedGetInput() function, 231 example, 211
acedGetKword() function, 259 acedTablet() function, 276
acedGetPoint() function, 259 acedTextBox() function, 254
acedGetReal() function, 263 acedTextPage() function, 275
acedGetString() function, 259 acedTextScr() function, 275
acedGetSym() function, 250, 526 acedTrans() function, 271
acedGetVar() function, 249 acedUsrBrk() function, 264
acedGraphScr() function, 275 acedVports() function, 253
acedGrDraw() function, 275 acedXformSS() function, 211, 535
acedGrRead() function, 275 AcGe library, 14, 725
acedGrText() function, 275 class hierarchy, 15, 727
acedGrVecs() function, 212, 535 acge15.lib library, 10
acedInitGet() control bits, 260 AcGeFileIO class, 746
acedInitGet() function, 259–260, 263 AcGeFiler class, 746
AcEdInputContextReactor class, 569 AcGeLibVersion class, 746
Index | 811
AcGeTol class, 729 ActiveX Automation, 593
AcGi library, 13, 683 AcAxOleLinkManager class, 607
API for ObjectDBX, 664 accessing COM interfaces from ObjectARX,
class hierarchy, 14, 686 595
acgiapi.lib library, 10 adding custom objects and entities to object
AcGiDrawable class, 684 model, 617
AcGiEdgeData class, 699 adding functionality to object model, 616
AcGiFaceData class, 699 additional requirements for COM Objects,
AcGiFillType, 691 609
AcGiSubEntityTraits class, 354–355, 691 ATL templates, 611
setting attribute values, 355 ATL templates provided by Autodesk, 611
AcGiTextStyle class, 704 AutoCAD implementation, 605
associating with axtempl.h file, 611
AcDbTextStyleTableRecord, 707 building and registering a COM DLL, 621
AcGiViewport class, 355 creating a registry file, 613
AcGiViewportGeometry class, 355 creating the COM Object, 608
AcGiWorldDraw class, 354 document locking, 612
AcGiWorldGeometry class, 354 exposing Automation functionality, 615
AcGix API, 665 IAcadBaseObject interface, 606
AcGixSimpleView class, 667 implementation of Automation objects,
AcGixWhipView class, 668 610
AutoCAD features not supported, 666 interacting with AutoCAD, 611
getDatabaseMutex() function, 668 MFC access to AutoCAD ActiveX
releaseDatabaseMutex() function, 668 Automation, 595
representing TrueType fonts in 3D space, non-MFC access to AutoCAD ActiveX
666 Automation, 599
SimpleView, 667 registry layout for COM objects, 609
TrueType font elaboration, 666 relationship between AcDbObjects and
using the database mutex, 668 Automation objects, 605
ViewAcDb viewer, 669 setting up an ATL project file, 615
WhipView, 668 writing a COM wrapper, 616
acgixAllocateWhipView() function, 669 AcTransaction class, 451, 663
AcGixSimpleView class, 667 for newly created objects, 454
AcGixWhipView class, 668 for obtaining object pointers from object
acProfileManagerPtr() function, 586 IDs, 453
acquireAngle() function, 376 open and close mechanism with, 455
acquireDist() function, 376 AcTransactionManager class, 663
acquirePoint() function, 376 flushGraphics() function, 455
acquireXXX() function, 376 for newly created objects, 454
AcRx library, 10 graphics generation and, 455
class hierarchy, 11 nesting transactions with, 451
ACRX_DECLARE_MEMBERS macro, 286 obtaining, 451
ACRX_DXF_DEFINE_MEMBERS macro, 388, queueForGraphicsFlush() function, 455
390 AcTransactionReactor class, 396, 663
ACRX_NO_CONS_DEFINE_MEMBERS macro, actrTransactionManager macro, 451
287 AcUi library
ACRX_X_CALL macro, 515 button classes, 182
acrx15.lib library, 10 CAdUiPickButton class, 182
acrxAbort() function, 527 CAdUiSelectButton class, 182
AcRxDictionary class, 11 combo box controls, 178
AcRxDLinkerReactor class, 396 CAcUiAngleComboBox class, 179
acrxEntryPoint() function, 36, 184–185, 770 CAcUiNumericComboBox class, 179
AcRxObject class, 10, 394 CAcUiStringComboBox class, 179
cloning with, 468 CAcUiSymbolComboBox class, 179
function overriding, 294 control bar classes, 176
acrxProductKey() function, 635 CAcUiDockControlBar class, 177
active document, 418
812 | Index
AcUi library (continued) ads_point data type, 534
dialog classes, 175 ads_point_set() macro, 534
CAcUiAlertDialog class, 176 ads_real data type, 534
CAcUiDialog class, 175 adscodes.h file, 168, 771
CAcUiFileDialog class, 176 adsdef.h file, 771
CAcUiTabChildDialog class, 175 adsdlg.h file, 771, 792
CAcUiTabMainDialog class, 175 adslib.h file, 771
edit controls, 177 AdUi library
CAcUiAngleEdit class, 177 button classes, 181
CAcUiEdit class, 177 CAdUiBitmapButton class, 181
CAcUiHeaderCtrl class, 178 CAdUiBitmapStatic class, 182
CAcUiListBox class, 178 CAdUiDropSite class, 182
CAcUiListCtrl class, 178 CAdUiOwnerDrawButton class, 181
CAcUiNumericEdit class, 177 CAdUiToolButton class, 182
CAcUiStringEdit class, 177 combo box controls, 178
CAcUiSymbolEdit class, 178 CAdUiComboBox class, 178
hierarchy, 173 control bar classes, 176
most recently used combo boxes, 179 CAdUiDockControlBar class, 176
CAcUiArrowHeadComboBox class, dialog classes, 174
180 CAdUiDialog class, 175
CAcUiColorComboBox class, 180 CAdUiFileDialog class, 175
CAcUiLineWeightComboBox class, CAdUiTabChildDialog class, 175
180 CAdUiTabMainDialog class, 175
CAcUiMRUComboBox class, 179 edit controls, 177
CAcUiMRUListBox class, 181 CAdUiEdit class, 177
CAcUiPlotStyleNamesComboBox hierarchy, 173
class, 181 messages, 174
CAcUiPlotStyleTablesComboBox overview, 172
class, 181 tab extensibility, 176
overview, 172 CAdUiTab class, 176
acui.h file, 172 CAdUiTabExtensionManager class,
acutAngle() function, 253 176
acutBuildList() function, 202, 247, 551 tip windows, 174
acutCvUnit() function, 269 CAdUiDrawTextTip class, 174
acutDistance() function, 253 CAdUiTextTip class, 174
acutGetVar() function, 586 CAdUiTipWindow class, 174
acutPolar() function, 253 adui.h file, 172
acutPrintf() function, 273 AFX_EXTENSION_MODULE, 170
acutRelRb() function, 549 AfxSetResourceHandle() function, 170
acutWcMatch() function, 278–279 AIG. See Application Interoperability Guidelines
acutWcMatchEx() function, 279 angle conversions, 266
ADC. See AutoCAD DesignCenter API examples, 268
AddExtendedTabs() function, 183 angles, between 3D vectors, 731
adding anonymous blocks, 232
entities to the database, 378 append() function, 378
object-specific data, 86 appendAcDbEntity() function, 498
addPersistentReactor() function, 396, 402 Application Interoperability Guidelines (AIG), 2
AddTab() function, 183–184 application names
addX() function, 514 registering, 237
adesk.h file, 771 registering, example, 238
ADS functions, 522 applications
ads.h file, 771 basic example, 39
ads_binary structure, 554 communicating between, 528
ads_command() function, 452 configuration, 585
ads_matrix data type, 211 creating, 29–30
ads_name, 200 debugging with dynamic MFC, 169
translating AcDbObjectIds to, 83 execution context, 436
Index | 813
applications (continued) AutoCAD (continued)
external, 532 creating line entities in, 23
initializing, 37 creating objects in, 22
interoperability, 2 creating registry subkeys and values, 48
listing loaded, 43, 53 database overview, 20
loading, 43, 53, 772 demand loading features, for proxy objects,
MDI requirements, 423–424 388
messages sent to ObjectARX, 31 ERRNO system variable, 527
proxy objects and, 388, 391 features that use COM, 594
reactors in, 394 key components, 20
result codes, 529 proxy entity messages in, 389
running from AutoLISP, 55 Release 12 entities, 100
sequence of events in, 35 responding to messages from, 31
unloading, 38, 44, 53, 391, 515 translation phase, 477
unlocking, 44 undo in transactions, 455
using protocol extension functionality, 515 AutoCAD command functions, 246
Windows system registry entries in, 46 AutoCAD DesignCenter API, 634
applyPartialUndo() function, 325–326 applications registry branch example, 636
arc primitive, 703 applications registry key, 635
argument lists in AutoLISP and C, 522 CLASSID registration, 637
ARX command, 53 custom content example, 640
AsdkPoly class extensions registry branch example, 637
overriding intersectWith() function, 365 extensions registry key, 636
transformation functions and, 363 IAcDcContentBrowser interface, 634
with grip points, 360 IAcDcContentBrowser interface functions,
assertNotifyEnabled() function, 297 638
assertReadEnabled() function, 297, 300, 302 IAcDcContentFinder interface, 635
assertWriteEnabled() function, 297, 300, 302, IAcDcContentFinderSite interface, 634
324, 327, 411 IAcDcContentView interface, 634
assoc AutoLISP function, 227 IAcDcContentView interface functions, 639
associating IAcPostDrop interface, 635
AcDbTextStyleTableRecord with implementing interfaces, 638
AcGiTextStyle, 707 providing custom content, 640
hyperlinks with entities, 139 registry requirements, 635
association list, 226 using the acrxProductKey() function, 635
asynchronous interaction from an automation AutoCAD Release 12
client, 611 saving to DWG file, 356
ATL AutoLISP
setting up an ATL project file, 615 comparison of AutoLISP calls to ObjectARX
templates, 611 calls, 522
AttachInstance() function, 170 defining functions, 524
attribute values, 355 expressions in user input, 259
auditing extended data handles, 240 functions
AutoCAD (assoc), 227
AutoCAD DesignCenter API, 634 (command), 246
classes, protocol extension and, 511 (entget), 238
cloning phase, 477 (vports), 253
commands returning values to, 265
boundaries in transactions, 452 linked lists, 551
using deep clone and wblock clone, running applications from, 55
476 variables
using wblock clone, 476 accessing from ObjectARX, 250
creating circle entities in, 23 setting to nil, 251
creating entry points for, 36 automatic undo, 325
creating group entities in, 24 automation. See ActiveX Automation
creating instances of entities, 125 axtempl.h file, 611, 617
creating layer entities in, 24
814 | Index
B CAcUiStringComboBox class, 179
beginClose() function, 398 CAcUiStringEdit class, 177
beginDeepClone() function, 489, 504–505, 507 CAcUiSymbolComboBox class, 179
beginDeepCloneXlation() function, 491, CAcUiSymbolEdit class, 178
504–505, 507 CAcUiTabChildDialog class, 175
ID map (table), 492, 504 CAcUiTabMainDialog class, 175
notification functions, 507 CAdUiBitmapButton class, 181
beginDxfIn() function, 398 CAdUiBitmapStatic class, 182
beginInsert() function, 507 CAdUiComboBox class, 178
beginSave() function, 398 CAdUiDialog class, 175
beginWblock() function, 490, 505 CAdUiDockControlBar class, 176
BHATCH command, 370 CAdUiDrawTipText class, 174
blanking out, 275 CAdUiDropSite class, 182
BLOCK command, 149 CAdUiEdit class, 177
cloning application, 476 CAdUiFileDialog class, 175
block references with attributes, creating, 129 CAdUiHeaderCtrl class, 178
block table, 147, 149 CAdUiListBox class, 178
block table records CAdUiListCtrl class, 178
appending function to, 378 CAdUiOwnerDrawButton class, 181
creating, 126 CAdUiTab class, 176
creating, with attribute definitions, 126 CAdUiTabChildDialog class, 175
iterating through, 133 CAdUiTabExtensionManager class, 176
blocks, anonymous, 232 CAdUiTabMainDialog class, 175
boundaries of transactions, 452 CAdUiTextTip class, 174
boundary representation library. See AcBr library CAdUiTipWindow class, 174
bounding box for text, 254 CAdUiToolButton class, 182
building an ownership hierarchy, 312 calibrating tablets, 276
button classes callback functions supporting DCL, 785
AcUi, 182 callback reason values in DCL, 788
AdUi, 181 cancel() function, 328, 411, 455
cancelled() function, 397, 410–411
C CECOLOR system variable , 66
CELTSCALE system variable, 66, 103
CAcExtensionModule class, 170
CELTYPE system variable , 102
DLL initialization and termination, 170
changing callback modes and values for DCL
example, 171
tiles, 796
resource tracking, 170
character conversion and testing functions, 270
CAcModuleResourceOverride class, 171
checkIn() function, 321
example, 171
checkOut() function, 321–322
CAcUiAlertDialog class, 176
circularArc() function, 703
CAcUiAngleComboBox class, 179
class descriptor objects, 285
CAcUiAngleEdit class, 177
class hierarchy
CAcUiArrowHeadComboBox class, 180
AcBr, 755
CAcUiColorComboBox class, 180
AcDb, 13
CAcUiDialog class, 175
AcEd, 12
CAcUiDockControlBar class, 177
AcGe, 15, 727
CAcUiEdit class, 177
AcGi, 14, 686
CAcUiFileDialog class, 176
AcRx, 11
CAcUiLineWeightComboBox class, 180
AcUi, 173
CAcUiMRUComboBox class, 179
AdUi, 173
CAcUiMRUListBox class, 181
classes
CAcUiNumericComboBox class, 179
AcApDocManager, 417
CAcUiNumericEdit class, 177
AcApDocManagerReactor, 418
CAcUiPickButton class, 182
AcApDocument, 61
CAcUiPlotStyleNamesComboBox class, 181
AcApDocumentIterator, 417
CAcUiPlotStyleTablesComboBox class, 181
AcApLayoutManager, 159
CAcUiSelectButton class, 182
AcApLongTransactionManager, 70
Index | 815
classes (continued) classes (continued)
AcApLongTransactionReactor, 70 AcDbPoint, 284
AcApProfileManager, 586 AcDbPolyFaceMesh, 285
AcApProfileManagerReactor, 587 AcDbPolyFaceMeshVertex, 285
AcBr containment, 762 AcDbPolygonMesh, 285
AcBr entity, 761 AcDbPolygonMeshVertex, 285
AcBr mesh, 762 AcDbRegAppTable, 147
AcBr traverser, 762 AcDbSequenceEnd, 285
AcDb2dPolyline, 137, 285 AcDbShape, 284
AcDb2dPolylineVertex, 137, 285 AcDbSolid, 284
AcDb3dPolyline, 285 AcDbSummaryInfoManager, 80
AcDb3dPolylineVertex, 285 AcDbSummaryInfoReactor, 80
AcDbArc, 284 AcDbSymbolTable, 284
AcDbAttribute, 284 AcDbSymbolTableRecord, 284
AcDbAttributeDefinition, 284 AcDbText, 284
AcDbBlockBegin, 284 AcDbTextStyleTable, 147
AcDbBlockEnd, 284 AcDbTrace, 284
AcDbBlockReference, 284 AcDbTransactionManager, 663
AcDbBlockTable, 147 AcDbUCSTable, 147
AcDbBlockTableRecordIterator, 152 AcDbViewport, 284
AcDbCircle, 284 AcDbViewportTable, 147
AcDbCompositeFilteredBlockIterator, 78 AcDbViewTable, 147
AcDbCurve, 137, 284 AcDbXrecord, 161
AcDbDatabaseReactor, 284 AcDbXrefFileLock, 76
AcDbDatabaseSummaryInfo, 79 AcDbXxxDimension, 284
AcDbDimStyleTable, 147 AcEdInputContextReactor, 569
AcDbEntity, 98, 284, 363 AcEdInputPointManager, 567
AcDbEntityReactor, 284 AcEditorReactor, 284, 398, 504
AcDbFace, 284 AcEditorReactor class, 75
AcDbFaceRecord, 285 AcEdJig, 284, 371
AcDbFilter, 78 AcGeFileIO, 746
AcDbFilteredBlockIterator, 78 AcGeFiler, 746
AcDbFullSubentPath, 110 AcGeLibVersion, 746
AcDbGroup, 284 AcGeTol, 729
AcDbHostApplicationServices, 658 AcGiDrawable, 684
AcDbHyperlink, 139 AcGiEdgeData, 699
AcDbHyperlinkCollection, 139 AcGiFaceData, 699
AcDbIndex, 78 AcGiSubEntityTraits, 691
AcDbLayerTable, 147 AcGiTextStyle, 704
AcDbLayerTableRecord, 149 AcRxDictionary, 11
AcDbLayout, 156, 159 AcRxObject, 10, 284
AcDbLayoutManager, 159 AcRxService, 284
AcDbLayoutManagerReactor, 159 AcTransaction, 663
AcDbLine, 284 AcTransactionManager, 663
AcDbLinetypeTable, 147 AcTransactionReactor, 284, 663
AcDbLongTransaction, 69 CAcExtensionModule, 170
AcDbLongTransWorkSetIterator, 70 CAcModuleResourceOverride, 171
AcDbMatchProperties, 516 CAcUiAlertDialog, 176
AcDbMInsertBlock, 284 CAcUiAngleComboBox, 179
AcDbMline, 156 CAcUiAngleEdit, 177
AcDbMlineStyle, 156 CAcUiArrowHeadComboBox, 180
AcDbObject, 284 CAcUiColorComboBox, 180
AcDbObjectId, 469 CAcUiDialog, 175
AcDbObjectReactor, 284 CAcUiDockControlBar, 177
AcDbPlotSettings, 159 CAcUiEdit, 177
AcDbPlotSettingsValidator, 159 CAcUiFileDialog, 176
816 | Index
classes (continued) classes (continued)
CAcUiLineWeightComboBox, 180 renaming, 346
CAcUiMRUComboBox, 179 runtime class identification, 285
CAcUiMRUListBox, 181 vector, 730
CAcUiNumericComboBox, 179 version support, 343
CAcUiNumericEdit, 177 clear_reactors() function, 399
CAcUiPickButton, 182 clip boundaries in AcGi, 723
CAcUiPlotStyleNamesComboBox, 181 clone() function, 468
CAcUiPlotStyleTablesComboBox, 181 versus deepClone() function, 468
CAcUiSelectButton, 182 cloning
CAcUiStringComboBox, 179 deep, 467
CAcUiStringEdit, 177 versus deep cloning, 468
CAcUiSymbolComboBox, 179 deep clone types and duplicate record
CAcUiSymbolEdit, 178 cloning, 71
CAcUiTabChildDialog, 175 editor reactor notification functions, 504
CAcUiTabMainDialog, 175 exploding and, 477
CAdUiBitmapButton, 181 ID map, 470
CAdUiBitmapStatic, 182 inserting, 504
CAdUiComboBox, 178 key concepts, 469
CAdUiDialog, 175 objects from different owners, 472
CAdUiDockControlBar, 176 overriding the deepClone() function, 484
CAdUiDrawTipText, 174 overriding the wblockClone() function, 488
CAdUiDropSite, 182 in transformation functions, 363
CAdUiEdit, 177 translating, 470
CAdUiFileDialog, 175 using appendAcDbEntity(), 498
CAdUiHeaderCtrl, 178 close() function, 328, 411–412
CAdUiListBox, 178 for newly created objects, 454
CAdUiListCtrl, 178 object pointers and, 454
CAdUiOwnerDrawButton, 181 open and close mechanism with, 455
CAdUiTab, 176 closing ObjectARX objects, 27
CAdUiTabChildDialog, 175 CMDACT system variable, 569–570
CAdUiTabExtensionManager, 176 collections of hyperlinks, 139
CAdUiTabMainDialog, 175 colors
CAdUiTextTip, 174 entity, 101
CAdUiTipWindow, 174 values, 66
CAdUiToolButton, 182 COM
containers hierarchy, 145 accessing COM interfaces from ObjectARX,
control bar, 176 595
CPropertyPage, 175 AutoCAD features that use COM , 594
CPropertySheet, 175 building and registering a COM DLL, 621
creating custom, 31 See also ActiveX Automation
creating MFC dialog, 189 combo box controls, 178
custom class derivation, 284 command AutoLISP function, 246
custom class function declaration, 286 command line prompt, 375
declaring and defining protocol command stack, 40
extensions, 512 commandEnded() function, 453, 505
document management, 417 commands
evaluation, 737 ARX, 53
implementing deepClone() for custom AutoCAD, for cloning applications, 476
classes, 476 BLOCK, 149
initialization function, 289 boundaries (AutoCAD) in transactions, 452
line, 732 displaying names in command groups, 53
matrix, 730 EXPLODE, 124
plane, 732 global versus local names, 42
point, 730 group names, 40
reactors, 394 INSERT, 149
Index | 817
commands (continued) CPropertyPage class , 175
LAYER, 149 CPropertySheet class, 175
lookup order, 42 createObjs() function, 313
modal, 42 createXrecord() function, 163
multi-document, 432 creating
nonreentrant, 431 AcDbLayerTableRecord example, 150
not used in transactions, 453 application registry keys and values, 48
registering new, 40 applications, 29–30
transparent versus modal, 42 block table records, 126
commandWillStart() function, 453 with attribute definitions, 126
commit-time circle entities in AutoCAD, 23
guidelines, 454 class descriptor objects, 285
notification, 410 classes and controls, 189
common characteristics of global functions, 522 complex entities, 134
common entity properties, 101 custom classes, 31
communicating between applications, 528 custom entities, 350
comparison databases from existing databases, 63
ObjectARX calls and AutoLISP calls, 522 databases with entities, 64
symbol tables and dictionaries, 144 dialog handlers, 190
compatibility levels for SDI and MDI, 423 dictionaries, 157
complex entities, 98, 134, 217, 229 entities, 25
component codes for entities, 226 entry points for AutoCAD, 36
computing points on parametric curves, 738 instances of AutoCAD entities, 125
conditional filtering, 208 layer entities in AutoCAD, 24
consistency checks, 76 layer groups in AutoCAD, 24
constructing tab dialogs, 183 layers, 26
container objects, 143 line entities in AutoCAD, 23
context data, 221 MFC dialog using App Studio, 188
context events, input, 569 objects in AutoCAD, 22
context help, 174 objects in ObjectARX, 25
control bar classes, 176 objects of protocol extension classes, 514
controlling graphics and text screens, 275 ownership connection, 311
controlling long transactions, 70 proxies, 389
controlling the display, 273 registry file (.reg) for custom object COM
controls server, 613
creating, 189 selection sets, 200
user input, 377 simple entities, 125
conversions skeletons, 186
angles, 266 Windows system registry subkeys and
coordinate systems, 271 values, 48
numbers, 266 creating a database, 60
strings, 266 ctype.h file, 270
units, 269 current database, accessing, 21
coordinate systems current document
accessing, 136 accessing, 428
conversions, 271 setting, 428
descriptions, 271 current save format , 62
specifying, 271 cursor
transformation example, 273 specified types with AcEdJig, 371
transformations, 271 types (table), 375
copied() function, 397, 411 curves
COPY command, 476 AcDbCurve class, 137
copying AcGe functions, 733
arrays of entities to create databases, 64 computing points on, 738
from entity to entity, 370 functions, 137
named blocks to create databases, 64 tessellating, 709
CPROFILE system variable , 586
818 | Index
custom classes databases (continued)
creating, 31 linetype values, 66
overriding AcDbCurve functions, 292 merging, 65
overriding AcDbObject functions, 292 multiple, 21
overriding AcRxObject functions, 292 notification events, 398
custom entities objects overview, 82
adding, using AcEdJig, 371 opening objects, 82
deriving, 350 overview, 20
deriving from AcDbEntity, 349, 355, 357, ownership of objects, 85
370 ownership structure for entities, 99
extending entity functionality, 370 populating, 60
intersecting with other entities, 369 primer, 19
stretch points, 361 reactors, using, 394
transformation functions, 363 resolving conflicts while inserting, 65
custom notifications, 398 resolving conflicts while merging, 65
custom object snap modes, 558 restoring in external references, 76
custom objects saving, 61
LongTransaction issues, 321 setting color values, 66
proxy objects and, 388–389, 391 setting current values, 66
setting linetype scale values, 66
D setting linetype values, 66
data startup values, 60
application-specific DCL data, 806 wblock cloning, 488
class version numbers, 347 dbapserv.h. file, 659
class version support, 344 DCL. See Dialog Control Language
dynamically allocated, 546 DCS. See display coordinate system
instances per document, 416 debugging applications with dynamic MFC, 169
ObjectARX-exclusive data type, 778 deep cloning, 467
per-document data, 424 calls, 505
data types cloning objects from different owners, 472
acad_proxy_entity, 389 filing, 469
acad_proxy_object, 389 functions, 467
ads_matrix, 211 implementing
ads_name, 200 AutoCAD commands for, 476
runtime identification mechanism, 283 cloning phase, 477, 484
database operations, 59 filers for, 484
databases insert operation, 486
accessing with noncurrent documents, 429 translation phase, 477, 484
adding entities to, 378 in long transactions, 70
closing objects, 82 key concepts, 469
color values, 66 ownership, 468–469
components of, 20 typical operation, 470
creating, 60 using clone() versus deepClone(), 468
creating by copying arrays of entities, 64 wblock functions and, 467–468
creating by copying named blocks, 64 deepClone() function, 321, 468, 476, 484, 499
creating databases with entities, 64 AutoCAD commands using, 476
creating empty, 22 versus clone() function, 468
creating from existing databases, 63 cloning phase, 477
current, 21 overriding, 484
dependencies, finding, 402 translation phase, 477
document-independent databases, 439 deepCloneContext() function, 508
essential objects, 22 deepCloneObjects() function, 467, 470
example of operations, 67 implementing, 476
initial values, 60 default actions for DCL dialog boxes , 786
inserting, 65 default file format
layer values, 67 considerations, 62
linetype scale values, 66 settings, 61
Index | 819
definition data, retrieving, 227 Dialog Control Language (DCL) (continued)
degeneracy of entities, 736 passing arguments in callback functions,
deleteAcRxClass() function, 391 786
deleting pop-up lists, 797
extended data, 236 radio cluster handling, 803
objects, 85 setting up list boxes and pop-up lists, 797
demand loading, 45 slider handling, 804
for ObjectDBX, 671 supporting DCL dialog boxes in an
on AutoCAD start-up, 52 application, 780
on command, 51 tile handling, 795
on detection of custom objects, 50 tile mode values, 795–796
DEMANDLOAD system variable, 49 dictionaries, 144, 153
deriving adding and deleting entries, 153
custom classes, 284 comparison with symbol tables, 144
custom entities, 350 creating, example, 157
new classes from AcEdJig, 371 extension, 89
DesignCenter. See AutoCAD DesignCenter API group, 28, 153
deviation() function, 709 layout, 156
dialog boxes Mline style, 156
data persistency, 182 named object, 480
designing with DCL, 780 names, 147
naming conventions, 183 setAt() function, 153
dialog classes dimstyle table, 147
AcUi, 175 directory tree, ObjectARX, 16
AdUi, 174 disableSystemCursorGraphics() function, 568
Dialog Control Language (DCL), 780 display control, 273
ads_start_dialog() status code values , 793 display coordinate system (DCS), 712
application-specific data, 806 definition, 272
callback function definitions, 793 display prompt
callback functions, 785 for drag sequence with AcEdJig, 372
callback reason values, 788, 794 setting, 375
changing callback modes and values for displaying
tiles, 796 command names, 53
data handling, 806 entity changes, 233
declarations, 792 proxy entities, 390
default actions, 786 displaying menus
definitions, 792 example, 274
dialog box color numbers, 801 functions, 274
edit box handling, 806 displaying text
examples functions, 273
basic dialog box, 781 size limits, 273
hiding a dialog box, 790 DLLs
hiding multiple dialog boxes, 790 initializing, 170
image creation, 802 terminating, 170
multiple selection lists, 799 docking system, 176
functions not allowed while a dialog box is document management classes, 417
active, 783 documentation
handles for dialog boxes and tiles, 792 online, 2
hiding dialog boxes, 789 printed, 2
image button handling, 803 document-independent databases, 439
image creation, 801 documents
initializing modes and values for tiles , 795 accessing current, 428
list boxes, 797 disabling switching, 435
list function code values, 794 events, 430
list value handling, 799 execution context, 416
nesting dialog boxes, 789 locking, 417
820 | Index
documents (continued) enableSystemCursorGraphics() function, 568
locking for Automation requests, 612 endCalledOnOutermostTransaction() function,
object, 417 454, 456
downgradeOpen() function, 86 endDeepClone() function, 480, 504–505
drag sequences endInsert() function, 507
controlling, with AcEdJig, 371 endWblock() function, 505
drag loop, 372 entget AutoLISP function, 238
drag loop, illustrated, 374 entities
implementing, with AcEdJig, 371 AcDbBlockTableRecord, 99
limitations on, 377 adding to the database, 378
setting display prompt, 372 assigning extended data, 241
drag() function, 372 associating hyperlinks, 139
dragging, 378 AutoCAD Release 12, 100
dragging selection sets, 263 colors, 101
draw() function, 105, 455 complex, 98, 134, 217, 229
drawables, 708 component codes, 226
drawing ancillary data, 79 context, 217
drawings context data, 221
save functions, 63 coordinate transformation data, 217
summary information, 79 creating, 25
transactions and, 455 creating instances of AutoCAD entities, 125
DrawTips, 174 data functions, 223
dummy entities, 241 defined, 98
DWG files, 298 definition data, 223
from earlier releases, 680 degeneracy, 736
dwgIn() function, 299, 469 displaying changes, 233
dwgInFields() function, 300, 325, 347 dummies, 241
dwgOut() function, 299, 469 exploding, 123
dwgOutFields() function, 300, 324, 327 extending entity functionality, 370
DXF files, 298 extending functionality, 370
DXF group codes, 542 forced entity picking, 568
extended data, 236 intersecting with other entities, 369–370
handle value, 240 layer, 104
in acedSSGet(), 205 linetype, 102
ranges, 303 linetype scale, 103
sentinel codes, 226, 235 linetype scale in paper space, 104
xrecords, 162, 241 linked result buffers, 221
dxfIn() function, 299 name volatility, 200
dxfInComplete() function, 398 names, 200, 214
dxfInFields() function, 304, 347 native, 369
dxfOut() function, 299 nested entities, 221
dxfOutFields() function, 302 nested in block references, 217
dynamic properties and Object Property not derivable, 100
Manager, 630 object snap point functions, 357
dynamically allocated data, 546 ownership, 99, 242
properties, common, 101
E proxies, 387
ECS. See Eye Coordinate System purging, 223
edit controls, 177 recovering, 216
editing proxy entities, 390 regenerating, 104, 234
editor reactor, 398 restoring color, 227
notification functions, 504 restoring linetype, 227
eInProcessOfCommitting message, 411, 454 saving, 356
ellipse, creating (example), 378, 383 setting entity traits, 690
empty databases, 22 snap points, 106
enable GraphicsFlush() function, 455 stretch points, 361
Index | 821
entities (continued) examples (continued)
subentity traits, 691 acedFindFile(), 251
transformation example, 222 acedGetFileD(), 252
transformation functions, 363 acedGetInput(), 263
translation example, 218 acedGetSym(), 250
using AcEdJig, 371, 383 acedInitGet(), 261
adding the entity to the database, 378 acedOsnap, 252
deriving a new class from AcEdJig, 371 acedPutSym(), 250
drag loop, 372 acedSSGet(), 201
general steps for using AcEdJig, 371 acedSSName() usage, 210
implementing sampler(), update(), and acedSSXform() usage, 211
entity() functions, 375 acedTextBox(), 256
sample code, 378, 383 AcGe persistency, 746
setting up parameters for drag acutBuildList(), 248
sequence, 372 adding code to handlers, 191
using handles, 216 adding members to selection sets , 214
visibility, 104 angle conversions, 268
entity coordinate system anonymous blocks, 232
definition, 272 application name registration, 238
entity() function, 373 AutoCAD DesignCenter API applications
implementing, 375, 378 registry branch, 636
overriding, 371 AutoCAD DesignCenter API custom
returning points to entity with, 378 content, 640
entry points, creating for AutoCAD, 36 AutoCAD DesignCenter API extensions
enumerated types, AcBr, 764 registry branch, 637
erase() function, 94, 328 basic application, 39
erased() function, 397–398, 411 building object dependencies, 404
erasing objects, 94 CAcExtensionModule class, 171
error codes CAcModuleResourceOverride class, 171
eWasErased, 94 calling external functions, 528
when opening objects, 84 checking entities in and out of a database,
error handling, 55 71
AutoCAD system variable ERRNO , 527 class data and xdata version support, 347
filing objects to DXF and DWG files, 300 class version support, 344, 346
in selection sets, 214 clip boundary, 724
invoked functions, 532 cloning and translation, 477
ObjectARX, 527 cloning objects from different owners, 472
RTERROR, 205 COM ActiveX Automation access using
essential database objects , 22 MFC, 595
evaluating external functions, 526 COM ActiveX Automation access without
evaluation classes, 737 MFC, 599
events constructing an extensible custom tab
document, 430 dialog, 184
immediate events, 410 coordinate system transformation, 273
input context events, 569 creating a mesh, 697
notification, 394, 398 creating an application skeleton, 186
sequence of, in applications, 35 creating an MFC dialog using App Studio,
time-commit events, 410 188
eWasErased error code, 94 creating block table records, 127
eWasNotifying message, 453 creating classes and controls, 189
eWasNotOpenForWrite message, 411 creating complex entities, 134, 229
examples creating custom entities, 378
accessing AutoLISP variables, 250 creating dialog handlers, 190
AcDbLayerTableRecord creating and creating dictionaries, 157
modifying, 150 creating simple AutoCAD entities, 125
acedCmd(), 248 curve functions, 138
acedCommand(), 247 custom object classes, 338
822 | Index
examples (continued) examples (continued)
custom object header file, 338 linked lists for AutoLISP, 551
custom object snap mode, 561 linking result buffers, 546
custom object source file, 338 long transactions, 71
database operations, 67 LongTransaction, 322
database reactors, 399 matrix composition, 213
DCL basic dialog box, 781 matrix multiplication, 220
DCL data handling, 806 MDI-Aware example application, 440
DCL hiding a dialog box program, 790 message map, 168
DCL hiding multiple dialog boxes, 790 migrating ADS Programs to ObjectARX, 773
DCL image creation, 802 named object dictionary, 481
DCL multiple selection lists, 799 nested transactions, 457
deep clone operation, 470 numeric conversions, 268
deepClone() function, 499 object snap, 252
definition data retrieval, 227 overriding resources, 171
definition data retrieving and printing, 224 overriding subsidiary functions, 328
deleting extended data, 236 overriding the deepClone() function, 484
displaying entity changes, 234 overriding the wblockClone() function, 488
displaying menus, 274 ownership hierarchy, 313
dragging selected objects, 263 pausing for input in commands, 248
dwgInFields() function, 301 pick points in commands, 249
dwgOutFields() function, 300 profile manager, 588
DXF group code handling, 543 protocol extension, 516
dxfInFields() function with order protocol extension class for custom object
dependence, 307 snaps, 559
dxfInFields() with order independence, 305 reactor adding behavior to a WBLOCK
dxfOutFields() function, 304 command, 507
entity handles, 216 reading a drawing, 67
entity names, 210 registry file (.reg) for custom object COM
entity transformation, 222 server, 613
entity translation, 218 resbuf, 204–205
extending built-in tab dialogs, 185 retrieving blocks, 242
extension dictionary global functions, 91 retrieving extended data for specific
file dialog box, 252 applications, 238
file search, 251 returning a value to AutoLISP, 266
finding persistent reactors, 402 reversing damage to the graphics screen,
finding viewports, 244 276
geometric functions, 254 sample application using the AcBr library,
gripping and transforming objects, 359 766
group and group dictionary functions, 154 saving a drawing, 67
handling hard references to AcDbEntities scaling selections, 211
during wblockClone(), 502–503 searching for files, 251
highlighting a subentity, 112 searching resbufs for specific DXF codes,
highlighting nested block references, 116 227
hyperlinks, 140 selecting using conditional tests, 208
input context events, 571 selecting using extended data, 206
input options, 261 selecting using relational tests, 207
input point filter and monitor, 577 set an AutoLISP variable to nil, 251
inserting blocks with references into setting AutoLISP variables, 250
drawings, 129 setting entity names, 539
interacting with AutoCAD using ActiveX setting the Model Space viewport for
Automation, 612 ObjectDBX, 678
intersecting functions, 365 setting up an ATL project file for a COM
iterating over dictionary entries, 158 wrapper, 615
iterating through block table records, 133 shell with color and visibility, 701
iterating through vertices in a polyline, 135 simple block table records, 126
keywords comparison, 263 snap mode, 357
Index | 823
examples (continued) external references (continued)
string conversions, 267 overview, 74
system variable access , 249 pre- and post-processing, 75
tablet calibration, 276 reloading, 76
text coordinates, 256 restoring databases, 76
text with bounding box, 705 restoring resolved symbols, 76
transformation, 712 separate databases, 74
transformation functions, 363 Eye Coordinate System (ECS), 712
transforming text boxes, 257
unit conversion, 269 F
user input, 261 F1 help, 174
user input break or cancel, 264 figures
using a transient editor reactor, 508 cloning ownership connections, 469
using AcGi, 693 coordinate transformations, 711
using appendAcDbEntity() during cloning, creating circle entities in AutoCAD, 23
498 creating group entities in AutoCAD, 24
using the AcBr library, 766 creating layer entities in AutoCAD, 24
viewport descriptors, 253 creating line entities in AutoCAD, 23
wblockClone() function, 500 input point filtering and monitoring, 575
wild-card comparisons, 278 key components of AutoCAD databases, 20
xrecord functions, 163 mesh edge ordering, 696
execution contexts mesh face properties, 698
application, 436 ownership references, 307
document, 416 points returned by acedTextBox(), 255
EXPLODE command, 125, 370 regenerating a drawing, 685
cloning application, 477 sequence of events in application, 35
explode() function, 105, 123–124, 369–370 file locks
exploding entities, 123, 370 external references, 76
extended data file searching, 251
application names, 237 filers, for deep cloning, 484, 488
assigning to entities, 241 files
auditing handles, 240 adsdlg.h, 792
deleting, 236 DWG, 298, 389
DXF group codes, 236 DXF, 298, 389
filtering for, 206 formats for saving drawings, 61
handle translation, 240 locking, 76
limit in size, 239 saving objects to files, 298
managing memory use, 239 setting the default formats, 61
notes, 235 filing objects, 95
organization, 235 filing, and cloning, 469
protecting application data, 239 fill type, primitive, 691
retrieving, 238 filtering
sentinel code, 235 conditional, 208
using handles, 240 extended data, 206
versus xrecords, 242 multiple properties, 204
See also xdata relational tests, 207
extended entity data (EED or xdata). See xdata selection sets, 203
extending entity functionality, 370 wild-card patterns, 205
extension dictionaries, overview, 89 filters
external functions, 524 chaining input point filters, 576
external references input point, 575
block table records, 76 finding viewports example, 244
clipped, 77 flushGraphics() function, 455
consistency checks, 76 forced entity picking, 568
contents, 74 funcload() function, 529
file locks, 76
824 | Index
functions functions (continued)
abortDeepClone(), 504 acedGrRead(), 275
abortInsert(), 507 acedGrText(), 275
abortWblock(), 505 acedGrVecs(), 212, 535
accessing AutoLISP variables, 250 acedInitGet(), 259–260, 263
acdbAngToF(), 267 acedInvoke(), 528
acdbAngToS(), 266 acedMenuCmd(), 274
acdbDisToF(), 267 acedNEntSel(), 112, 214, 217, 259
acdbDxfOutAsR14(), 62 acedNEntSelP(), 112, 214, 259, 535
acdbEntDel(), 216, 223 acedOsnap(), 252, 535
acdbEntGet(), 223, 235 acedPrompt(), 273
acdbEntGetX(), 235, 238, 278 acedPutSym(), 250, 526
acdbEntLast(), 249 acedRedraw(), 275
acdbEntMake(), 149, 228–229, 232 acedRegFunc(), 529
acdbEntMod(), 227–228, 233 acedSetVar(), 249
acdbEntNext(), 214, 249 acedSSAdd(), 214
acdbEntUpd(), 234 acedSSAddl(), 209
acdbFail(), 527 acedSSDel(), 209
acdbGetSummaryInfo(), 80 acedSSFree(), 203
acdbGetSummaryInfoManager(), 80 acedSSGet(), 111, 200
acdbHandEnt(), 216, 223, 240 acedSSLength(), 210
acdbInters(), 254 acedSSMemb(), 210
acdbIsPersistentReactor(), 402 acedSSName(), 210
acdbOpenObject(), 82 acedSSNameX(), 111
acdbPutSummaryInfo(), 80 acedTablet(), 276
acdbRegApp(), 237–238 acedTextBox(), 254
acdbRToS(), 266 acedTextPage(), 275
acdbSaveAsR13(), 63 acedTextScr(), 275
acdbSaveAsR14(), 63 acedTrans(), 271
acdbTblNext(), 242 acedUsrBrk(), 264
acdbTblSearch(), 242–243 acedVports(), 253
acdbXdRoom(), 239 acedXformSS(), 211, 535
acdbXdSize(), 239 acgixAllocateWhipView(), 669
acdbXrefReload(), 76 acProfileManagerPtr(), 586
acedAlert(), 527 acquirePoint(), 376
acedArxLoaded(), 533 acrxAbort(), 527
acedArxUnload(), 533 acrxEntryPoint(), 36, 184–185, 770
acedCmd(), 246–247 acrxProductKey(), 635
acedCommand(), 246 acutAngle(), 253
acedDefun(), 524, 527 acutBuildList(), 202, 247, 551
acedDragGen(), 212, 259, 263, 535 acutCvUnit(), 269
acedEntSel(), 214, 216, 259 acutDistance(), 253
acedFindFile(), 251 acutGetVar(), 586
acedGetArgs(), 526 acutPolar(), 253
acedGetDist(), 259 acutPrintf(), 273
acedGetFileD(), 252 acutRelRb(), 549
acedGetFunCode(), 526 acutWcMatch(), 278–279
acedGetInput(), 231 acutWcMatchEx(), 279
acedGetKword(), 259 AddExtendedTabs(), 183
acedGetPoint(), 259 AddTab(), 183–184
acedGetReal(), 263 addX(), 514
acedGetString(), 259 AfxGetResourceHandle(), 170
acedGetSym(), 250, 526 angle conversion, 266
acedGetVar(), 249 appendAcDbEntity(), 85, 498
acedGraphScr(), 275 applyPartialUndo(), 325–326
acedGrDraw(), 275 assertNotifyEnabled(), 297
Index | 825
functions (continued) functions (continued)
assertReadEnabled(), 297, 300, 302 erase(), 94, 328
assertWriteEnabled(), 297, 300, 302, 324, executing AutoCAD commands, 246
327 explode(), 105, 123–124, 370
AttachInstance(), 170 external, 524
AutoCAD commands, 246 file search, 251
AutoCAD services, 246 flushGraphics(), 455
beginClose(), 398 for drawing primitives, 354
beginDeepClone(), 504 formatForSave(), 62
beginDeepCloneXlation(), 504 funcload(), 529
beginDxfIn(), 398 geometric utilities, 253
beginInsert(), 507 getAt(), 147
beginSave(), 398 getDatabaseMutex(), 668
beginWblock(), 505 GetDialogName(), 184
cancel(), 328 getEcs(), 136
cast(), 285 getFilerStatus(), 300
character conversion and testing, 270 getGeomExtents(), 105
checkIn(), 321 getGripPoints(), 105, 359
checkOut(), 321–322 getGsMarkersAtSubentPath(), 106
circularArc(), 703 GetObjectId(), 607
clone(), 468 getOsnapInfo(), 559
close(), 328 getOsnapPoints(), 105–107, 357
colorIndex(), 102 getStretchPoints(), 105, 361
common characteristics of library getSubentPathsAtGsMarker(), 106, 111
functions, 522 getTransformedCopy(), 105, 107, 363
conversion utilities, 266 highlight(), 106, 111
coordinate system conversion, 271 insert(), 65, 468
createObjs(), 313 intersectWith(), 105, 107–108, 364, 369
createXrecord(), 163 isA(), 285
deepClone(), 321, 468, 476, 484, 499 isKindOf(), 285
deepCloneObjects(), 468, 470 layer(), 104
deleteAcRxClass(), 391 linetype(), 103
desc(), 285 linetypeScale(), 103
deviation(), 709 list(), 105
disableSystemCursorGraphics(), 568 listXrecord(), 163
displaying menus, 274 mesh(), 696
displaying text, 273 moveGripPoints(), 359, 361
downgradeOpen(), 86 moveGripPointsAt(), 105
drag(), 372 moveStretchPointsAt(), 105
draw(), 105 new(), 85
dwgFileWasSavedByAutodeskSoftware(), 80 newIterator(), 152, 154
dwgIn(), 299, 469 not allowed while a DCL dialog box is
dwgInFields(), 300, 325, 347 active, 783
dwgOut(), 299 numeric conversion, 266
dwgOutFields(), 300, 324, 327 object snap, 252
dxfIn(), 299 ON_MESSAGE(), 168
dxfInComplete(), 398 OnInitDialog(), 183, 196
dxfInFields(), 304, 347 OnModified(), 607
dxfOut(), 299 open(), 328
dxfOutFields(), 302 otherInsert(), 507
enableSystemCursorGraphics(), 568 otherWblock(), 505
endCalledOnOutermostTransaction(), 456 overriding AcDbCurve functions, 292
endDeepClone(), 504 overriding AcDbObject functions, 292
endInsert(), 507 overriding AcRxObject functions, 292
endWblock(), 505 passing pick points, 248
entity(), 375 pausing for user input, 248
826 | Index
functions (continued) functions (continued)
pline(), 704 wblockClone(), 321, 468, 476, 488, 500,
position(), 137 502
PostNcDestroy(), 184 wblockCloneObjects(), 70
printdxf(), 224, 226 workingDatabase(), 21
purge(), 324 worldDraw(), 105, 684, 687
queueForGraphicsFlush(), 455 xData(), 86
rbChain(), 161 xrefBlockId(), 76
releaseDatabaseMutex(), 668
restoreForwardingXrefSymbols(), 76 G
restoreOriginalXrefSymbols(), 76 geometric utilities, 253
return values and results for global geometry
functions, 523 3D, 730
returning values to AutoLISP, 265 basic types, 730
rxInit(), 289 library, 725
sampler(), 372, 375 parametric, 733
saveComplete(), 398 getAt() function, 147
setAt(), 94, 153 getDatabaseMutex() function, 668
setAttributes(), 684, 686 GetDialogName() function, 184
setColor(), 154 getEcs() function, 136
setColorIndex(), 102 getFilerStatus() function, 300
setDefaultFormatForSave(), 62 getGeomExtents() function, 105
SetDialogName(), 183 getGripPoints() function, 105, 359–361
SetDirty(), 183 getGsMarkersAtSubentPath() function, 106
setElevation(), 137 getObject() function, 451, 453
setFromRbChain(), 161 for newly created objects, 454
setHighlight(), 154 in open mode, 453
setLayer(), 104, 154 open and close mechanism with, 455
setLinetype(), 103, 154 GetObjectId() function, 607
setLinetypeScale(), 103 getOsnapInfo() function, 559
SetObjectId(), 606 getOsnapPoints() function, 105–106, 357
setPosition(), 137 overriding, 357
setVisibility(), 104, 154 getStretchPoints() function, 105, 361
setXData(), 86 getSubentPathsAtGsMarker() function, 106, 111
string conversion, 266 getting user input, 258
subCancel(), 328 getTransformedCopy() function, 105, 107, 363
subClose(), 328 global functions, 245
subentPtr(), 106, 112 global utility functions, 521
subErase(), 328 glyphs, custom, 559
subOpen(), 328 goodbye() function, 397, 411
system variables, 249 graphics and text screens
text utility box, 254 controlling, 275
traits, 355 low-level access, 275
transactionAborted(), 456 reversing damage, 276
transactionEnded(), 456 graphics generation, and transactions, 455
transactionStarted(), 456 graphics interface library, 13, 683
transformation, 363 Graphics System Markers, 692
transformBy(), 105, 107, 363, 391 example diagram, 109
unit conversion, 269 graphicsModified() function, 411
update(), 375 grip points, 349
user input, 258 stretch points as subset of, 361
vertexPosition(), 137 group codes. See DXF group codes
viewport descriptors, 253 GROUP dictionary, 28, 153
viewportDraw(), 105, 684, 688 group names for commands, 40
visibility(), 104
wblock(), 63
Index | 827
groups ID maps
adding to group dictionaries, 28 cloning, 470
assign properties to all members, 154 for ddp cloning (table), 492, 504
GS markers. See Graphics System Markers use with inserting, 504
identity matrix, 220
H IdMap, 321
handlers IdPair, 321
creating dialog, 190 IDynamicProperty interface, 630–631
handles image creation for DCL, 801
for DCL dialog boxes and tiles, 792 implementing
in extended data, 240 automation objects, 610
object, 21 deepClone() for custom classes, 476
requesting object, 83 DWG filing functions, 300
translation, 240 DXF filing functions, 302
handling grip point function, 359
external applications, 532 interfaces for AutoCAD DesignCenter, 638
hard references to AcDbEntities during member functions, 297
wblockClone(), 502 protocol extension, 512
hard references in long transactions, 70 snap point functions, 357
header files, required, 771 static Object Property Manager interfaces,
help 625
context, 174 stretch point functions, 361
F1, 174 include directory, ObjectARX, 17
hiding DCL dialog boxes, 789 indexing and filtering
hierarchy block iteration, 78
container classes, 145 defining a query, 78
entity classes, 99 interfaces provided, 77
xrecords, 242 list of classes, 77
See also class hierarchy overview, 77
highlight() function, 106, 111 processing a query, 78
highlighting querying the database, 78
nested block references, 116 registering, 77
subentities, 111 updating, 77
hyperlinks initial items in a database, 60
AcDbHyperlink class, 139 initializing
associating with entities, 139 applications, 37
collections, 139 classes, 289
example, 140 modes and values for DCL tiles, 795
nested, 139 input context events, 569
objects, 139 input controls
user, 377
I values (list), 377
input options for user input functions, 260
IAcadBaseObject interface, 606
input point
Clone() function, 607
filtering, 575
GetClassId() function, 607
management, 567
GetObjectId() function, 607
manager class, 567
NullObjectId() function, 607
monitoring, 575
SetObjectId() function, 606
processing, 557
IAcadBaseObject interface interface
INSERT command, 149
OnModified() function, 607 cloning application, 476
IAcDcContentBrowser interface, 634, 638
inserting, 504
IAcDcContentFinder interface, 635
blocks with references into drawings, 129
IAcDcContentFinderSite interface, 634
databases, 65
IAcDcContentView interface, 634, 639
resolving conflicts, 65
IAcPostDrop interface, 635
installation, modifying Windows system registry
ICategorizeProperties interface, 624
at, 47
828 | Index
installing ObjectARX, 16 kUnloadAppMsg message, 31, 33
interacting kUnloadDwgMsg message, 32, 34
with multiple documents, 428 kVisibilityChangeAllowed value, 391
with other environments, 9
interactive output, 273 L
interoperability of applications, 2 last saved by Autodesk software, 80
intersecting LAYER command, 149
for points, 107 layer table, 147, 149
with other entities, 369–370 layers
with custom entities, 369 creating, 26
with native entities, 369 database values, 67
intersectWith() function, 105, 107–108, 357, entity, 104
364, 369 layout dictionary, 156
IOPMPropertyExpander interface, 625 layout manager, 160
IOPMPropertyExtension interface, 625 layouts
IPerPropertyBrowsing interface, 624 dictionary, 160
IPropertyManager interface, 630 model space, 159
isolines, 710 ObjectARX classes, 159
iterating paper space, 159
over dictionary entries, 158 lengthy operations, 264
over open documents, 417 libacbr.dll file, 751
over tables, 152 libraries
through block table records, 133 acad.lib, 10
through vertices in a polyline, 135 AcBr, 751
iterators, 152 AcDb, 12
acdb15.lib, 10
K AcEd, 12
kAllAllowedBits value, 391 acedapi.lib, 10
kCfgMsg message, 32, 35 AcGe, 14, 725
kColorChangeAllowed value, 390 acge15.lib, 10
kDependencyMsg message, 32, 34 AcGi, 13, 683
kEdgeSubentType subentity, 111 acgiapi.lib, 10
kEndMsg message, 32, 35 AcRx, 10
kEraseAllowed value, 390 acrx15.lib, 10
keyword comparison, 263 adui15.lib, 172
keyword lists, 260, 262, 375 directory, ObjectARX, 17
keyword specifications, 262 required ObjectARX, 10
keywords in user input, 259 rxapi.lib, 10
keywords, custom object snap, 559 search path, 43
kFaceSubentType subentity, 111 limit of size for extended data, 239
kForNotify mode, 84 line classes, 732
kForRead mode, 84 linear algebra operations, 732
kForWrite mode, 84 linetype
kInitAppMsg message, 31, 33 entity, 102
kInvkSubrMsg message, 32, 34 scale, 66, 103
kLayerChangeAllowed value, 391 table, 147
kLinetypeChangeAllowed value, 391 values, 66
kLinetypeScaleChangeAllowed value, 391 linked lists, 540
kLoadDwgMsg message, 31, 34 AutoLISP, 551
kNoDependencyMsg message, 32, 34 command and function invocation lists,
kNoOperation value, 390 554
kOleUnloadAppMsg message, 33–34 creating, 549
kPreQuitMsg message, 31, 34 deleting, 549
kQuitMsg message, 32, 35 dynamically allocated data, 546
kSaveDwg message, 247 entity lists with DXF codes, 553
kSaveMsg message, 32, 35 nested, 551
kTransformAllowed value, 390
Index | 829
linking MATCH command
VC++ project settings, 169 protocol extension for, 516
with MFC, 168 MATCHPROP command, 370
with ObjectARX libraries, 10 matrix
LIST command, proxy entity messages with, 389 classes, 730
list value handling in DCL, 799 composition, 213
list() function, 105 identity, 220
lists multiplication, 220
keyword, 262, 375 transformation, 535
loaded applications, 43 MCS. See model coordinate system
listXrecord() functions, 163 MDI. See multiple document interface
loading measuring text strings, 255
applications, 43, 53, 772 memory management for extended data, 239
demand, 45 memory requirements for global functions, 523
localization for ObjectDBX, 661 merging databases, 65
locking mesh primitives, 696
documents, 417 face and edge visibility, 699
documents for Automation requests, 612 traversers, 760
explicit document locking, 425 mesh() function, 696
logo compliance, 2 message map example, 168
logo program for ObjectARX, 2 messages
long transactions application reactions to AutoCAD
controlling, 70 messages, 33
deep cloning, 70 kCfgMsg, 32, 35
example, 71 kDependencyMsg, 32, 34
hard references, 70 kEndMsg, 32, 35
notifications, 70 kInitAppMsg, 31, 33
overview, 69 kInvkSubrMsg, 32, 34
read-only access, 70 kLoadDwgMsg, 31, 34
starting, 70 kNoDependencyMsg, 32, 34
tracking, 69 kOleUnloadAppMsg, 33–34
LongTransaction issues for custom objects, 321 kPreQuitMsg, 31, 34
LongTransactionManager, 321 kQuitMsg, 32, 35
lookup order, commands, 42 kSaveMsg, 32, 35, 247
LTSCALE system variable, 66 kUnloadApMsg, 33
kUnloadAppMsg, 31
M kUnloadDwgMsg, 32, 34
macros only responded to by applications using
AC_DECLARE_EXTENSION_MODULE, 171 ActiveX, 33
AC_IMPLEMENT_EXTENSION_MODULE, responding to AutoCAD, 31
171, 187 sent only if AutoLISP function registered,
ACRX_DECLARE_MEMBERS , 286 32
ACRX_DXF_DEFINE_MEMBERS, 388, 390 sequence of, 35
ACRX_NO_CONS_DEFINE_MEMBERS, 287 to applications that have registered
ACRX_X_CALL, 515 services, 32
actrTransactionManager, 451 WM_ACAD_KEEPFOCUS, 168
ads_point_set(), 534 MFC. See Microsoft Foundation Classes
cast(), 285 Microsoft Component Object Model (COM). See
class implementation, 287 COM
custom class function declaration, 286 Microsoft Foundation Classes
desc(), 285 access to AutoCAD ActiveX Automation,
isA(), 285 595
isKindof(), 285 and modeless dialog boxes, 168
managing applications with Windows system debugging ObjectARX applications, 169
registry, 52 dynamic linking, 168–169
masking. See filtering modeless dialog boxes, 168
830 | Index
Microsoft Foundation Classes (continued) multiple document interface (MDI) (continued)
resource management, 170 SDI-Only compatibility, 423
topics, 167 terminology, 418
user interface support, 172 transaction management, 438
user-interface support, 172 multiple documents, interacting, 428
Microsoft Visual C++ project settings
for dynamically linked MFC, 169 N
using AdUi and AcUi with AppWizard, 186 named object dictionary, 144, 241, 480
migrating ADS Programs to ObjectARX, 769 defaults, 22
acrxEntryPoint() function, 770 names
building ADS applications for ObjectARX, global versus local command names, 42
773 symbol table records and dictionaries, 147
example, 773 symbol tables, 243
header files, 771 nested
loading applications, 772 block reference highlighting, 116
MIRROR command, cloning application, 476 DCL dialog boxes, 789
mixing the transaction model with the open and entities, 221
close mechanism, 455 hyperlinks, 139
Mline style dictionary, 156 transactions, 451
modal commands, 42 newIterator() function, 152
model coordinate system (MCS), 217, 711 nonreentrant commands, 432
model coordinates, transforming to world notification
coordinates, 217 custom, 398
model space document events, 430
adding entities to, 23 for AcDbObject and database, 398
layouts, 159 functions, 394
versus paper space, 23 immediate, 410
modeless dialog boxes immediate versus commit-time events, 410
focus, 168 in long transactions, 70
MFC, 168 object reactors for, 402
modes obtaining ID of, 402
for opening objects, 84 types of, 395
kForNotify, 84 overview, 394
kForRead, 84 reactor classes for, 394
kForWrite, 84 reactor lists, 394
modified() function, 394, 397, 411 reactors for, 396
modifiedXData() function, 397, 411 use guidelines, 412
modifying Windows system registry at using, 394
installation, 47 numActiveTransactions() function, 412
modifyUndone() function, 397, 411 numbers, real, 534
monitoring input points, 575 numeric conversions, 266
most recently used combo boxes, 179 examples, 268
moveGripPoints() function, 359, 361
moveGripPointsAt() function, 105, 360–361 O
signature, 359
object IDs
moveStretchPointsAt() function, 105, 361
commit-time and, 454
multi-document commands, 432
for named object dictionaries, 480
multiple databases, 21
obtaining, 21
multiple document interface (MDI), 415
obtaining object pointers from, 450, 453
application-specific data, 431
ownership, for cloning, 469
compatibility levels, 423
object pointers
database undo, 438
MDI-Aware compatibility, 424 in nesting transactions, 451
obtaining in transactions, 450, 453
MDI-Capable compatibility, 427
with getObject() functions, 453
MDI-Enhanced compatibility, 427
minimum requirements, 423
Index | 831
Object Property Manager API, 593, 622 ObjectARX (continued)
adding properties for OPM, 626 types, 533
AutoCAD COM Implementation, 623 user input control bit codes, 545
categorizing properties for OPM, 626 user interface, 172
dynamic properties, 630 using MFC with applications, 168
ICategorizeProperties interface, 624 values, 533
IDynamicProperty interface, 630–631 variables, 533
implementing static OPM interfaces, 625 wizard, 30
IOPMPropertyExpander interface, 625 ObjectARX-exclusive data type , 778
IOPMPropertyExtension interface, 625 objectClosed() function, 397, 412
IPerPropertyBrowsing interface, 624 ObjectDBX, 655
IPropertyManager interface, 630 AcDbHostApplicationServices class , 658
static COM interfaces, 624 AcDbTransactionManager class, 663
object snap, 252 AcEditorReactor notifications, 660
object snap manager, 558 AcGi API, 664
object snap points, 106 AcGix API, 665
for intrinsic entity functions, 357 AcGixSimpleView class, 667
objectAppended() function, 399 AcGixWhipView class, 668
ObjectARX active viewports in Model Space, 678
application examples, 773 AcTransaction class, 663
application loading, 772 AcTransactionManager class, 663
common characteristics of global AcTransactionReactor class, 663
functions, 522 application services class, 658
communication between applications, 528 creating a viewer, 663
comparison of global function calls to demand loading, 671
AutoLISP calls, 522 differences from ObjectARX, 659
creating objects in, 25 DWG files from earlier releases, 680
creating the MFC application skeleton, 186 extended entity data (EED or xdata), 681
defining AutoLISP functions, 524 getDatabaseMutex() function, 668
deriving custom classes, 284 getting started and using, 657
directory tree, 16 host application, 656
DXF group codes, 542 insert() function, 678
dynamically allocated linked lists of result installing the libraries for your application,
buffers, 546 672
entity names, 538 libraries, 656
error handling for global functions, 527 localization, 661
extension dictionary example, 89 overview, 656
extension dictionary global functions raster images, 682
example, 91 releaseDatabaseMutex() function, 668
external application handling, 532 representing TrueType fonts in 3D space ,
global function argument lists, 522 666
global function memory requirements, 523 SimpleView, 667
global function return values, 523 tips and techniques, 676
global utility functions, 521 transaction management, 663
include directory, 17 TrueType font elaboration, 666
installing, 16 user interface and database access, 657
library directory, 17 using the database mutex, 668
logo program, 2 ViewAcDb viewer, 669
macros viewports, 679
object reactor classes and, 402 WhipVIew, 668
reactor classes and, 397 XMX files, 661
opening and closing objects, 27 objectErased() function, 399
overview, 7 objectModified() function, 399
result buffers, 540 objects
result type codes, 541 AcApDocManager, 417
samples directory, 17 AcApDocument, 417
selection set names, 538 AcDbDictionary, 144
832 | Index
objects (continued) otherWblock() function, 505
AcDbHyperlink, 139 overriding
AcDbLayout, 156 AcDbEntity functions, 350–352
AcDbObject, 82 common entity functions, 353
application-specific document objects, 431 getOsnapPoints() function, 357
closing, 82 resources, 171
container, 143 saveAs() function, 355
creating in AutoCAD, 22 viewportDraw() function, 354
creating in ObjectARX, 25 worldDraw() function, 350, 353–354
creating of protocol extension classes, 514 overview of ObjectARX, 7
deleting, 85 ownership
document, 417 and cloning, 469, 489
erasing, 94 building a hierarchy, 312
error codes during opening, 84 cloning objects from different owners, 472
exploding, cloning and, 477 entities, 99
filing, 95 hard, 312, 469, 488
handles, 21 in named object dictionaries, 480
implementation of automation, 610 objects, 85
newly created, and transactions, 454 of entities, 242
obtaining pointers to, in transactions, 453 references, 311
open modes, 84 soft, 312, 469
opening, 82 structure for database entities , 99
opening and closing process diagram, 82 types of cloning and, 469
overview, 82
ownership, 85 P
proxies, 387 paper space
references, 310 layouts, 159
relationships between, 469 linetype scale, 104
requesting handles, 83 versus model space, 23
snap modes, 106 paper space display coordinate system
snap modes, custom, 558 definition, 272
snap points, 106 parametric geometry, 733
solid, 757 partial undo, 325
using drawables in objects, 708 passing arguments in DCL callback functions, 786
version support, 343 paths
xrecord, 241 library search, 43
object-specific data subentity, 110
adding, 86 pause symbol, 248
objList space objects, 472 pausing for user input in commands, 248
obtaining object IDs, 21 PDB. See programmable dialog boxes
ol_errno.h file, 771 per-document data, 424
ON_MESSAGE() function, 168 persistency
OnInitDialog() function, 183, 196 of AcGe entities, 746
online documentation, 2 of dialog data, 182
OnModified() function, 607 pick points
open() function, 328 example in commands, 249
openedForModify() function, 397, 410–411 passing to commands, 248
opening ObjectARX objects , 27 picking
operations forced entity, 568
databases, 59 plane classes, 732
linear algebra, 732 pline() function, 704
wblock, 63 plot settings, 160
OPM. See Object Property Manager API point classes, 730
order dependence, 304 pointers
organization of extended data, 235 AcEdJig and, 371
origin point of text, 254 hard, 320, 469, 488
otherInsert() function, 507 illustrated, 490
Index | 833
pointers (continued) proxy objects (continued)
obtaining pointers to objects in entities
transactions, 453 displaying, 390
soft, 321, 469 editing, 390
points, 534 modification of, 388
polygonDc() functions, 355 object life cycle, 388
polygonEye() functions, 355 unloading application for, 391
polygons user encounters with, 389
primitives for drawing, 355 using proxy objects, 389
stretching, 360 PROXYGRAPHICS system variable, 390
polyline primitive, 704 PROXYSHOW system variable, 390
polylineDc() functions, 355 PSLTSCALE system variable, 66, 104
polylineEye() functions, 355 purge() function, 324
polylines purging entities, 223, 324
primitives for drawing, 355
scaling AsdkPoly and, 363 Q
populating databases, 60 queueForGraphicsFlush() function, 455
position() function, 137 quiescent, definition, 422
PostNcDestroy() function, 184
pOwner parameter, 489 R
primitives, 696
raster images in ObjectDBX, 682
arc, 703
rbChain() function, 161
defined, 354
RDS. See registered developer symbol
functions for drawing, 354–355
mesh, 696 reactors, 394, 456, 507
AcDbObject and database notification
polyline, 704
shell, 700 events, 398
classes, 394
text, 704
custom notifications and, 398
printdxf() function, 224, 226
database, using, 394
printed manuals, 2
document, 418
printing text on screen, 273
editor
profile manager, 586
programmable dialog boxes, 779 deep cloning and, 467
using, 394
prompt line input, 259
lists, 394
prompting for input, 258
object, 395
properties, common entity, 101, 691
building in object dependencies with
protecting application extended data, 239
protocol extension, 511 (example), 404
ending transactions, 412
class descriptor objects, 514
immediate versus commit-time
structure, 514
defining classes, 512 events, 410
modifying, 411
for custom object snaps, 559
obtaining ID of, 402
implementing, 512
persistent, 395, 402
default class for, 515
read-only state of, 411
for MATCH command, 516
transient, 395, 402
unloading the application for, 515
types of, 395
MATCH command, 516
using, 394, 402
registering classes of, 513
ObjectARX macros and, 397
using in applications, 515
obtaining the ObjectID, 402
proxy objects
persistent, 395
for entities and objects, 387
deriving, 402
creating, 389
transaction, 456
custom objects and, 388–389, 391
using, 394, 396
displaying proxy entities, 390
reading a drawing example, 67
editing proxy entities, 390
834 | Index
read-only access in long transactions, 70 retrieving blocks example, 242
real numbers, 534 retrieving data interactively, 258
reappended() function, 397–398, 411 retrieving definition data example, 227
recovering deleted entities, 216 retrieving extended data, 238
redo, 324 retrieving extended data for specific applications
redrawing the graphics screen, 275 example, 238
references retrieving user input, 258
handling hard references to AcDbEntities returning values to AutoLISP functions, 265
during wblockClone(), 502 RSERR, 529
objects, 310 RSRSLT, 529
ownership, 311 RT codes, 541
pointer, 320 RTCAN, 259
regapp table, 147 RTERROR, 259
regen type. See regeneration type RTKWORD, 259
regenerating RTNONE, 259
drawings, 104, 684 RTNORM, 259, 523, 532
entities, 234 RTREJ, 259
regeneration type, viewport, 689 running applications from AutoLISP, 55
registered developer symbol (RDS), 3, 40–41, runtime
144 class identification, 285
registering type identification mechanism, 11, 283
application names, 237 rxapi.lib library, 10
as MDI-Aware, 427 rxboiler.h file, 284
new commands, 40 rxdefs.h file, 771
protocol extension classes, 513 rxInit() function, 289
registry. See Windows system registry
relational tests in filtering, 207 S
relationship between DuplicateRecordCloning sampler() function, 372
and DeepCloneType values, 71 with drag loop, 372
relationship between symbol table and deep implementing, 375, 378
clone types, 71 obtaining angle, distance, or point with,
releaseDatabaseMutex() function, 668 377
reloading external references, 76 overriding, 371
removing Windows system registry samples. See examples
information, 49 samples directory, ObjectARX, 17
renaming classes, 346 saveAs() function
requesting, object handles, 83 overriding, 355–356
requirements proxy entities and, 390
ObjectARX libraries, 10 saveComplete() function, 398
software and hardware, 16 saving
resbuf, 540, 546 considerations, 62
example, 204–205 databases, 61
value types, 204 entities, 356
resource management file formats for drawings, 61
detaching resources, 187 global functions, 63
explicit setting, 170 saving a drawing, example, 67
overriding resources, 171 scaling
overview, 170 interactive, 212
switching resources, 170 selection sets, 211
responding to AutoCAD messages, 31 screen input, 259
restoring color or linetype, 227 low-level access, 275
restoring state, 326 scrolling tabs, 183
restype, 541 SDI system variable, 422
result buffers, 540, 546 SDI. See single drawing interface
result type codes, 541 search for files by name, 251
Index | 835
selecting extended data, 235 snap modes, 106
selection sets custom objects, 558
creating, 200 keywords, 559
deleting items, 209 snap points, 106
error handling, 214 implementing, 357
filtering, 203 solid objects, 757
freeing, 203 specifying a coordinate system , 271
graphically dragging, 263 stack, command, 40
interactive scaling, 212 stacked tabs, 183
interactive transformation, 212 starting long transactions, 70
limitations, 203 storing information in objects, 242
manipulating, 209 STRETCH command, 361
name, 200 stretch point functions, 361
name volatility, 200 stretch points, 349, 361
names, 538 in grip editing, 359
options, 201 string comparisons
scaling, 211 ignoring case, 279
transformation, 211 with wild-cards, 278
sentinel code string conversions, 266
extended data, 235 examples, 267
resbufs, 226 leading and trailing zeros, 267
sequence of events in applications, 35 precision, 267
setAt() function, 94, 153 units used, 267
setAttributes() function, 684, 686 string globalization, 555
setColor() function, 154 struct resbuf, 540
SetDialogName() function, 183 subCancel() function, 328
SetDirty() member function, 183 subClose() function, 328, 398
setDispPrompt() function, 372, 375 subentities
setFromRbChain() function, 161 highlighting, 111
setHighlight() function, 154 kEdgeSubentType, 111
setKeywordList() function, 372, 375 kFaceSubentType, 111
setLayer() function, 154 paths, 110
setLinetype() function, 154 subentPtr() function, 106, 112
SetObjectId() function, 606 subErase() function, 328
setPosition() function, 137 subObjModified() function, 397, 411
setSpecialCursorType() function, 372, 375 subOpen() function, 328
setting subsidiary functions, 328
current database values, 66 summary information for drawings, 79
current document without activating, 430 supporting DCL dialog boxes in an application ,
database color values, 66 780
database linetype scale values, 66 surfaces, AcGe functions, 733
database linetype values, 66 switching, disabling document, 435
default file format, 61 symbol tables, 144
setting up DCL list boxes and pop-up lists , 797 accessing, 242
setUserInputControls() function, 372, 377 comparison with dictionaries, 144
setVisibility() function, 154 contents at startup, 60
setXData() function, 86 defaults, 22
sheep cloning. See deep cloning getAt() function, 147
shell primitive, 700 interating
SimpleView, 667 over symbol tables, 152
single drawing interface (SDI), 422 names, 243
compatibility with MDI, 423 names in records, 147
single-screen AutoCAD installations, 275 VPORT, 243
size limits for extended data, 239 system cursor, disabling, 568
skeletons system registry. See Windows system registry
creating, 186 system requirements, 16
836 | Index
system variables tables (continued)
accessing, 249 DXF group coderanges for xrecords, 162
APERTURE, 252 entity colors, 101
AUNITS, 267 error codes, when opening objects, 84
AUPREC, 267 exploding entities, 123
CECOLOR, 66 IAcDcContentBrowser interface functions,
CELTSCALE, 66, 103 638
CELTYPE, 102 IAcDcContentView interface functions, 639
CMDACT, 569–570 ID map for overriding the wblockClone()
CPROFILE, 586 function examples, 492
DEMANDLOAD, 49 input context events, 569
DIMZIN, 267 input options set by acedInitGet(), 260
example of accessing, 249 library function result type codes, 544
freeing string space, 250 mesh traversers, 761
LTSCALE, 66 message handlers, 191
LUNITS, 267 messages sent to ObjectARX applications ,
LUPREC, 267 31–33
PROXYGRAPHICS, 390 object snap modes, 106
PROXYSHOW, 390 ObjectDBX libraries, 658
PSLTSCALE, 66, 104 proxy flags options, 390
SDI, 422 result type codes, 541
TABMODE, 276 return values for user-input functions, 259
UNITMODE, 267, 269 save format, 61
sysvars. See system variables SDI system variable values, 422
selection set options for acedSSGet(), 201
T states for opening objects, 303
tab dialogs topological traversers, 759
constructing, 183 user-input function summary, 258
extending, 183 value-return function summary, 265
extending built-in, 184 XMX file types, 661
extensibility, 176 tablet calibration, 276
scrolling tabs, 183 example, 276
stacked tabs, 183 normalization, 278
using, 183 transformation matrix, 277
tables tessellation, 709
AcApProfileManager class capabilities, 586 text bounding boxes, 254
AcApProfileManagerReactor class text box utility, 254
notifications, 587 text origin, 254
AcGe global identifiers and header files, 728 text primitive, 704
application reactions to AutoCAD text strings
messages, 33 globalization, 555
arbitrary user input, 262 measurement, 255
ATL-based templates, 611 text style table, 147
character type functions, 270 TextTips, 174
command lock types, 426 tiles in DCL, 795
CursorType values, 376 tip windows, 174
DCL ads_start_dialog() status code values, tolerances, 729
793 ToolTip string, custom object snaps, 559
DCL callback reason values, 788, 794 ToolTips windows, 174
DCL color numbers, 801 topological objects, 756
DCL list function code values, 794 brep, 756
DCL tile mode values, 795–796 complex, 756
deep clone types and duplicate record edge, 756
cloning, 71 face, 756
DXF group code ranges for object loop, 756
representation, 303 shell, 756
vertex, 756
Index | 837
topological traversers, 758 U
topTransaction() function, 451 UCS. See user coordinate system
traits UCS table, 147
entity, 690 unappended() function, 397–398, 411
subentity, 691 undo, 324
transaction management automatic, 325
for ObjectDBX, 663 in MDI environment, 438
in MDI environment, 438 partial, 325
transaction model transactions, 455
commit-time guidelines in, 454 undrawing, 275
graphics generation and, 450, 455 unit conversions, 269
mixing, with open and close mechanism, example, 269
450 unloading applications, 38, 44, 53, 391, 515
newly created objects and, 454 unlocking applications, 44
obtaining pointers to objects in, 450, 453 update() function, 373
reactors, 456 implementing, 375, 378
stack, 452 modifying entity with, 378
undo mechanism and, 450 overriding, 371
transactionAborted() function, 456 user breaks, 264
transactionEnded() function, 412, 455–456 user coordinate system (UCS), 271
transactions user input
aborting or ending, 452 arbitrary input example, 261
boundaries, 452 AutoLISP expressions, 259
graphics generation, 455 breaks, 264
handling newly created objects, 454 cancel, 264
management, 449–450 cancel example, 264
manager, 451 control bit codes, 545
model, 450 controlling function conditions, 260
nested, 451 controls, 377
obtaining pointers to objects, 453 during lengthy operations, 264
reactors, 456 from prompt line, 259
undo, 455 from the screen, 259
transactionStarted() function, 456 function return values, 259
transformation matrices, 535 functions, 258
transformation, coordinate, 710 ignoring breaks and cancels, 264
transformBy() function, 105, 107, 363, 391 input options, 260
applying transform matrix to entity, 363, keywords, 259
391 low-level access, 275
for stretch mode, 359 pausing for in commands, 248
transforming raw, 275
coordinate data, 217 restricted input example, 261
coordinate systems, 271 retrieving, 258
functions, 107, 363 user interface
interactive, 212 in ObjectARX, 172
selection sets, 211 initialization, 172
transient reactors, 395 support, 172
deriving, 402
translating V
AcDbObjectIds and ads_names, 83
value types
and cloning, 470
resbufs, 204
transparent commands, 42
values
traversers
mesh, 760 attributes, 355
cursor (table), 376
topological, 758
points, 534
TrueType font elaboration for ObjectDBX, 666
user input controls (list), 377
types
vector classes, 730
ads_point, 271
838 | Index
Veritest, 2 wild-card (continued)
version support patterns, 278
class data, 347 patterns in filters, 205
class xdata, 347 special characters, 278
classes, 343 Windows system registry
objects, 343 application entries, 46
using class versioning, 344 creating file for custom object COM server,
vertexPosition() function, 137 613
vertices in a polyline, iterating through, 135 keys and values, creating at installation, 48
view table, 147 managing applications with, 52
ViewAcDb viewer, 669 modifying at application installation, 47
viewers, 663 removing information, 49
viewport descriptors, 253 requirements for AutoCAD DesignCenter
viewport regeneration type, 689 API, 635
viewport table, 147 subkeys and values, creating in AutoCAD,
viewportDraw() function, 105, 353, 684, 688 48
proxy entities and, 390 user profiles, 182
regenerating graphics with, 354 wizard, ObjectARX, 30
saving and, 356 WM_ACAD_KEEPFOCUS, 168
view specificity with, 355 workingDatabase() function, 21
view-dependence with, 354 World Coordinate System (WCS), 217, 712
visibility entity, 104 definition, 271
visibility, mesh face and edge, 699 worldDraw() function, 105, 684, 687
Visual C++. See Microsoft Visual C++ custom entities displayed in, 353
VPORT symbol table, 243 overriding, 350, 353
vports AutoLISP function, 253 proxy entities and, 390
regenerating graphics with, 354, 373
W saving and, 356
watch_db() function, 399 supporting proxy entity graphics, 356
wblock view-independence with, 354
cloning and filing, 469 write, open and close mechanism for, 450
cloning and ownership, 469
notification functions, 505 X
operations, 63 xdata, 86
WBLOCK command, 507 ads_binary structure, 554
cloning application, 477, 480 class version numbers, 347
wblockClone() function, 321, 488, 500, 502 exclusive data types, 554
AutoCAD commands using, 476 for ObjectDBX applications, 681
deep cloning with, 467 See also extended data
pOwner parameter, 489 xData() function, 86
symbol table record and, 489 XMX files for ObjectDBX, 661
translation phase, 477 xrecords, 161
WCS. See World Coordinate System DXF group codes, 241
WhipView library, 668 example, 163
wild-card hierarchy, 242
character matching, 278 objects, 241
ignore case, 279 versus extended data, 242
matching example, 278 xrefs. See external references
Index | 839