ObjectArx Autocad
ObjectArx Autocad
The global functions described in this section allow your application to communicate with AutoCAD. This section discusses functions for registering commands with AutoCAD, handling user input, handling data conversions, and setting up external devices such as the tablet.
10
In this chapter
I AutoCAD Queries and
Commands
I Getting User Input I Conversions I Character Type Handling I Coordinate System
Transformations
I Display Control I Tablet Calibration I Wild-Card Matching
247
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 follows, 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 argument 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 commands 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.
248
Chapter 10
NOTE The acedCommand() and acedCmd() functions can invoke the AutoCAD
SAVE or SAVEAS command. When they do so, AutoLISP issues a kSaveMsg message to all other ObjectARX applications currently loaded, but not to the application that invoked SAVE. The comparable code is sent when these functions invoke NEW, OPEN, END, or QUIT from an application. The following sample function shows a few calls to acedCommand().
int docmd() { ads_point p1; ads_real rad; if (acedCommand(RTSTR, "circle", RTSTR, "0,0", RTSTR, "3,3", 0) != RTNORM) return BAD; if (acedCommand(RTSTR, "setvar", RTSTR, "thickness", RTSHORT, 1, 0) != RTNORM) return BAD; p1[X] = 1.0; p1[Y] = 1.0; p1[Z] = 3.0; rad = 4.5; if (acedCommand(RTSTR, "circle", RT3DPOINT, p1, RTREAL, rad, 0) != RTNORM) return BAD; 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 reference, 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.
249
The acedCmd() function also has the advantage that the command list can be modified at runtime rather than be fixed at compile time. Its disadvantage is that it takes slightly longer to execute. For more information, see the ObjectARX Reference. The following sample code fragment causes AutoCAD to perform a REDRAW on the current graphics screen (or viewport).
struct resbuf *cmdlist; cmdlist = acutBuildList(RTSTR, "redraw", 0); if (cmdlist == NULL) { acdbFail("Couldn't create list\n"); return BAD; } acedCmd(cmdlist); acutRelRb(cmdlist);
The following call starts the CIRCLE command, sets the center point as (5,5), and then pauses to let the user drag the circles radius on the screen. When the user specifies the chosen point (or enters the chosen radius), the function resumes, drawing a line from (5,5) to (7,5).
result = acedCommand(RTSTR, "circle", RTSTR, "5,5", RTSTR, PAUSE, RTSTR, "line", RTSTR, "5,5", RTSTR, "7,5", RTSTR, "", 0);
250
Chapter 10
finds the next entity in the drawing, and the acdbEntLast() function finds the last entity in the drawing.
ads_point p1; ads_name first, last; acedCommand(RTSTR, "Circle", RTSTR, "5,5", RTSTR, "2", 0); acedCommand(RTSTR, "Line", RTSTR, "1,5", RTSTR, "8,5", RTSTR, "", 0); acdbEntNext(NULL, first); // Get circle. acdbEntLast(last); // Get line. // Set pick point. p1[X] = 2.0; p1[Y] = 5.0; p1[Z] = 0.0; acedCommand(RTSTR, "Trim", RTENAME, first, RTSTR, "", RTLB, RTENAME, last, RTPOINT, p1, RTLE, RTSTR, "", 0);
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 variable. 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 buffers memory use as it does with dynamically allocated buffers. If the AutoCAD system variable is a string type, acedGetVar() allocates space for the string. The application is responsible for freeing this space. You can
251
do this by calling the standard C library function free(), as shown in the following example:
acedGetVar("TEXTSTYLE", &rb); if (rb.resval.rstring != NULL) // Release memory acquired for string: free(rb.resval.rstring);
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);
252
Chapter 10
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; rb = acutBuildList(RTSTR, "GREETINGS", 0); rc = acedPutSym("teststr", rb); acedPrompt("TESTSTR has been reset\n"); acutRelRb(rb); rb = acutBuildList(RTLB, RTSHORT, -1, RTSTR, "The combinations of the world", RTSTR, "are unstable by nature.", RTSHORT, 100, RT3DPOINT, pt1, RTLB, RTSTR, "He jests at scars", RTSTR, "that never felt a wound.", RTLE, RTLE, 0); rc = acedPutSym("longlist", rb); acedPrompt("LONGLIST has been created\n"); acutRelRb(rb);
To set an AutoLISP variable to nil, make the following assignment and function 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.
253
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;
If the call to acedFindFile() is successful, the fullpath argument is set to a fully qualified path name string, such as the following:
/home/work/ref/refc.dwg
You can also prompt users to enter a file name by means of the standard AutoCAD file dialog box. To display the file dialog box, call acedGetFileD(). The following sample code fragment uses the file dialog box to prompt users for the name of an ObjectARX application.
struct resbuf *result; int rc, flags; if (result = acutNewRb(RTSTR) == NULL) { acdbFail("Unable to allocate buffer\n"); return BAD; } result->resval.rstring=NULL; flags = 2; // Disable the "Type it" button. rc = acedGetFileD("Get ObjectARX Application", // Title "/home/work/ref/myapp", // Default pathname NULL, // The default extension: NULL means "*". flags, // The control flags result); // The path selected by the user. if (rc == RTNORM) rc = acedArxLoad(result->resval.rstring);
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);
254
Chapter 10
The following call looks for either the midpoint or endpoint of a line, or the center of an arc or circlewhichever 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.
Viewport Descriptors
The function acedVports(), like the AutoLISP function (vports), gets a list of descriptors of the current viewports and their locations. The following sample code gets the current viewport configuration and passes it back to AutoLISP for display.
struct resbuf *rb; int rc; 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 RTSHORT 1 RTPOINT 0.0 0.0 NULL RTPOINT 30.0 30.0
255
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 configuration shown in the next figure.
rb RTSHORT 5 RTPOINT 0.5 0.0 RTPOINT 1.0 0.5 RTSHORT 2 RTPOINT 0.5 0.5 RTPOINT 1.0 1.0
NULL RTSHORT 3 RTPOINT 0.0 0.5 RTPOINT 0.5 1.0 RTSHORT 4 RTPOINT 0.0 0.0 RTPOINT 0.5 0.5
The current viewports 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 information. 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 coordinates (relative to an initial point). Unlike most ObjectARX functions, these functions do not return a status value. The acdbInters() function finds the intersection of two lines; it returns RTNORM if it finds a point that matches the specification.
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 geometric utility functions.
ads_point pt1, pt2; ads_point base, endpt; ads_real rads, length; . . // Initialize pt1 and pt2. .
256
Chapter 10
// 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.
baseline
The next figure shows the point values that acedTextBox() returns for samples of vertical and aligned text. In both samples, the height of the letters
257
was entered as 1.0. (For the rotated text, this height is scaled to fit the alignment points.)
pt2 = 1.0, 0.0 origin (0,0) pt2 = 9.21954,1.38293
(10,3) (1,1) pt1 = -0.5,-20.0 pt1 = 0,0 alignment points entered where text was created
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 274.
int tbox() { ads_name tname; struct resbuf *textent, *tent; ads_point origin, lowleft, upright, p1, p2, p3, p4; ads_real rotatn; char rotatstr[15]; if (acedEntSel("\nSelect text: ", tname, p1) != RTNORM) { acdbFail("No Text entity selected\n"); return BAD; }
258
Chapter 10
textent = acdbEntGet(tname); if (textent == NULL) { acdbFail("Couldn't retrieve Text entity\n"); return BAD; } tent = entitem(textent, 10); origin[X] = tent->resval.rpoint[X]; //ECS coordinates origin[Y] = tent->resval.rpoint[Y]; tent = entitem(textent, 50); rotatn = tent->resval.rreal; // acdbAngToS() converts from radians to degrees. if (acdbAngToS(rotatn, 0, 8, rotatstr) != RTNORM) { acdbFail("Couldn't retrieve or convert angle\n"); acutRelRb(textent); return BAD; } if (acedTextBox(textent, lowleft, upright) != RTNORM) { acdbFail("Couldn't retrieve text box coordinates\n"); acutRelRb(textent); return BAD; } acutRelRb(textent); // If not currently in the WCS, at this point add // acedTrans() calls to convert the coordinates // retrieved from acedTextBox(). p1[X] = origin[X] + lowleft[X]; // UCS coordinates p1[Y] = origin[Y] + lowleft[Y]; p2[X] = origin[X] + upright[X]; p2[Y] = origin[Y] + lowleft[Y]; p3[X] = origin[X] + upright[X]; p3[Y] = origin[Y] + upright[Y]; p4[X] = origin[X] + lowleft[X]; p4[Y] = origin[Y] + upright[Y]; if (acedCommand(RTSTR, "pline", RTPOINT, p1, RTPOINT, p2, RTPOINT, p3,RTPOINT, p4, RTSTR, "c", 0) != RTNORM) { acdbFail("Problem creating polyline\n"); return BAD; } if (acedCommand(RTSTR, "rotate", RTSTR, "L", RTSTR, "", RTPOINT, origin, RTSTR, rotatstr, 0) != RTNORM) { acdbFail("Problem rotating polyline\n"); return BAD; } return GOOD; }
259
The preceding example cheats by using the AutoCAD ROTATE command to cause the rotation. A more direct way to do this is to incorporate the rotation into the calculation of the box points, as follows:
ads_real srot, crot; tent = rotatn srot = crot = entitem(textent, 50); = tent->resval.rreal; sin(rotatn); cos(rotatn); . . . p1[X] = origin[X] + (lowleft[X]*crot - lowleft[Y]*srot); p1[Y] = origin[Y] + (lowleft[X]*srot + lowleft[Y]*crot); p2[X] = origin[X] + (upright[X]*crot - lowleft[Y]*srot); p2[Y] = origin[Y] + (upright[X]*srot + lowleft[Y]*crot); p3[X] = origin[X] + (upright[X]*crot - upright[Y]*srot); p3[Y] = origin[Y] + (upright[X]*srot + upright[Y]*crot); p4[X] = origin[X] + (lowleft[X]*crot - upright[Y]*srot); p4[Y] = origin[Y] + (lowleft[X]*srot + upright[Y]*crot);
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(). The following functions behave like user-input functions: acedEntSel(),
acedNEntSelP(), acedNEntSel(), and acedDragGen().
260
Chapter 10
The following table briefly describes the user-input functions. User-input function summary
Function Name acedGetInt acedGetReal acedGetDist acedGetAngle Description Gets an integer value Gets a real value Gets a distance Gets an angle (oriented to 0 degrees as specified by the ANGBASE variable) Gets an angle (oriented to 0 degrees at the right) Gets a point Gets the corner of a rectangle Gets a keyword (see the description of keywords later in this section) Gets a string
acedGetString
With some user-input functions such as acedGetString(), the user enters a value on the AutoCAD prompt line. With others such as acedGetDist(), the user either enters a response on the prompt line or specifies the value by selecting points on the graphics screen. If the screen is used to specify a value, AutoCAD displays rubber-band lines, which are subject to application control. A prior call to acedInitGet() can cause AutoCAD to highlight the rubber-band line (or box). The acedGetKword() function retrieves a keyword. Keywords are also string values, but they contain no white space, can be abbreviated, and must be set up before the acedGetKword() call by a call to acedInitGet(). All user-input functions (except acedGetString()) can accept keyword values in addition to the values they normally return, provided acedInitGet() has been called to set up the keywords. User-input functions that accept keywords can also accept arbitrary text (with no spaces).
261
The AutoCAD user cannot respond to a user-input function by entering an AutoLISP expression. The user-input functions take advantage of the error-checking capability of AutoCAD. Trivial errors (such as entering only a single number in response to acedGetPoint()) are trapped by AutoCAD and are not returned by the user-input function. The application needs only to check for the conditions shown in the following table. Return values for user-input functions
Code RTNORM RTERROR RTCAN RTNONE RTREJ RTKWORD Description User entered a valid value The function call failed User entered ESC User entered only ENTER AutoCAD rejected the request as invalid User entered a keyword or arbitrary text
The RTCAN case enables the user to cancel the applications request by pressing ESC . This helps the application conform to the style of built-in AutoCAD commands, which always allow user cancellation. The return values RTNONE and RTKWORD are governed by the function acedInitGet(): a user-input function returns RTNONE or RTKWORD only if these values have been explicitly enabled by a prior acedInitGet() call.
262
Chapter 10
RSG_DASH
32
RSG_2D
64
RSG_OTHER
128
The following program excerpt shows the use of acedInitGet() to set up a call to the acedGetInt() function.
int age; acedInitGet(RSG_NONULL | RSG_NOZERO | RSG_NONEG, NULL); acedGetInt("How old are you? ", &age);
263
This sequence asks the users age. AutoCAD automatically displays an error message and repeats the prompt if the user tries to enter a negative or zero value, press ENTER only, or enter a keyword. (AutoCAD itself rejects attempts to enter a value that is not an integer.) The RSG_OTHER option lets the next user-input function call accept arbitrary input. If RSG_OTHER is set and the user enters an unrecognized value, the acedGetxxx() function returns RTKWORD, and the input can be retrieved by a call to acedGetInput(). Because spaces end user input just as ENTER does, the arbitrary input never contains a space. The RSG_OTHER option has the lowest priority of all the options listed in the preceding table; if the acedInitGet() call has disallowed negative numbers with RSG_NONEG, for example, AutoCAD still rejects these. The following code allows arbitrary input (the error checking is minimal).
int age, rc; char userstring[511]; acedInitGet(RSG_NONULL | RSG_NOZERO | RSG_NONEG | RSG_OTHER, "Mine Yours"); if ((rc = acedGetInt("How old are you? ", &age)) == RTKWORD) // Keyword or arbitrary input acedGetInput(userstring); }
In this example, acedGetInt() returns the values shown in the following table, depending on the users input. Arbitrary user input
User Input 41 m Result acedGetInt() returns RTNORM and sets age to 41 acedGetInt() returns RTKWORD, and acedGetInput() returns Mine acedGetInt() returns RTKWORD, and acedGetInput() returns Yours acedGetInt() returns RTKWORD, and acedGetInput() returns twenty acedGetInt() returns RTKWORD, and acedGetInput() returns what???
twenty
what???
264
Chapter 10
-34.5
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 characters, if those are specified after a comma). Regardless of how the user enters a keyword, the application has to do only one string comparison to identify it, as demonstrated in the following example. The code segment that follows shows a call to acedGetReal() preceded by a call to acedInitGet() that specifies two keywords. The application checks for these keywords and sets the input value accordingly.
int stat; ads_real x, pi = 3.14159265; char kw[20]; // Null input is not allowed. acedInitGet(RSG_NONULL, "Pi Two-pi");
265
if ((stat = acedGetReal("Pi/Two-pi/<number>: ", &x)) < 0) { if (stat == RTKWORD && acedGetInput(kw) == RTNORM) { if (strcmp(kw, "Pi") == 0) { x = pi; stat = RTNORM; } else if (strcmp(kw, "Two-pi") == 0) { x = pi * 2; stat = RTNORM; } } } if (stat != RTNORM) acutPrintf("Error on acedGetReal() input.\n"); else acutPrintf("You entered %f\n", x);
The call to acedInitGet() prevents null input and specifies two keywords: Pi and Two-pi. When acedGetReal() is called, the user responds to the prompt Pi/Two-pi/<number> by entering either a real value (stored in the local variable x) or one of the keywords. If the user enters a keyword, acedGetReal() returns RTKWORD. The application retrieves the keyword by calling acedGetInput() (note that it checks the error status of this function), and then sets the value of x to pi or 2pi, depending on which keyword was entered. In this example, the user can enter either p to select pi or t to select 2pi.
266
Chapter 10