home *** CD-ROM | disk | FTP | other *** search
/ Current Shareware 1994 January / SHAR194.ISO / cad_util / alsps.zip / ALSP7.DOC < prev    next >
Lisp/Scheme  |  1993-11-04  |  12KB  |  365 lines

  1. This is Lesson 7 of a series of AutoLISP training exercises given
  2. on the CompuServe ADESK Forum by the Autodesk, Inc. Training
  3. Department.
  4.  
  5. WRITING NEW AUTOCAD COMMANDS (Part Three)
  6.  
  7. A DESIGN FOR A NEW AUTOCAD COMMAND
  8.  
  9. In the preceding tutorials, you have learned about function
  10. definitions, creating new AutoCAD commands, required arguments to
  11. functions, and global and local variables.
  12.  
  13. It's now time to use what you have learned (along with the topics
  14. we discuss in this tutorial) to write a new AutoCAD command.
  15.  
  16. There are some general design parameters that will apply to all
  17. new AutoCAD commands you will write in AutoLISP.
  18.  
  19. 1.  Mimic the AutoCAD command prompt interface.
  20.  
  21. 2.  Leave the drawing environment as you found it.
  22.  
  23. 3.  Avoid conflict with other applications.
  24.  
  25. The command you will write in this tutorial will prompt the user
  26. for two opposing corners of a rectangle, and draw a rectangular,
  27. closed, 2d polyline.
  28.  
  29. THE USER INTERFACE
  30.  
  31. AutoLISP-based commands should mimic existing AutoCAD command
  32. prompt sequences as much as possible.  The user will find an
  33. AutoCAD-like command easier to work with than one that invents
  34. its own interface, if only because the prompt sequence is
  35. internally consistent with the interface the user already knows.
  36.  
  37. First, the command BOX is defined.  There are no required arguments
  38. to the function, nor are any local variables declared; at least,
  39. not until the function is fully debugged.
  40.  
  41. Open a new text file named BOX.LSP and begin writing your new
  42. command.
  43.  
  44.     (defun C:BOX ()
  45.     )
  46.  
  47. Next, a prompt is generated for the user to select a corner
  48. point.  The prompt is derived from the ZOOM Window command
  49. prompt, which also prompts the user for two opposing corners of a
  50. rectangle.
  51.  
  52.     (defun C:BOX ()
  53.  
  54.           (setq pt1 (getpoint "\nFirst corner: "))
  55.  
  56.     )
  57.  
  58. This appears to be a good time to visually show the user the
  59. rectangle that would be drawn were a second point to be chosen.
  60. The GETPOINT function doesn't quite handle this case, so now
  61. would be a good time to open up the APR and browse through the
  62. function listings to see if an existing function does what you
  63. need.  It's always better to check first and see if the wheel
  64. exists before you run off to re-invent one.
  65.  
  66. Go ahead.  We'll still be here when you finish reading.
  67.  
  68. Did you find the listing in section 4.51 for GETCORNER?
  69.  
  70. As you can see, GETCORNER takes a base point argument.  In our
  71. function, that will be the first corner point chosen by the user.
  72. After prompting the user for the other corner, GETCORNER will
  73. visually drag a rectangle to the other corner point and return
  74. that point when chosen by the user.
  75.  
  76.     (defun C:BOX ()
  77.           (setq pt1 (getpoint "\nFirst corner: "))
  78.  
  79.           (setq pt2 (getcorner pt1 "\nOther corner: "))
  80.  
  81.     )
  82.  
  83. Try the new BOX command in its current state to test the user
  84. interface.
  85.  
  86.     Command: (load "box")
  87.     C:BOX
  88.  
  89.     Command: BOX
  90.     First corner: <pick>
  91.     Other corner: <drag and pick>
  92.  
  93. Aren't you glad you took the time to browse through the APR to
  94. see if an existing function handled window dragging?  What does
  95. that tell you about the APR?  How often should you refer to it
  96. when you first start writing AutoLISP programs and need specific
  97. functionality from a routine?
  98.  
  99. The interface is now working.  All that remains is to draw the
  100. polyline based on the values stored in PT1 and PT2.
  101.  
  102. The COMMAND function is called to begin drawing the polyline.
  103. The command name is prefaced with a period to ensure that the
  104. native PLINE command is accessed in case it has been UNDEFINEd in
  105. the current drawing editor session (see the UNDEFINE command in
  106. the AutoCAD Reference Manual).  PT1 is used as an argument to the
  107. "From point:" prompt of the PLINE command.
  108.  
  109.     (defun C:BOX ()
  110.           (setq pt1 (getpoint "\nFirst corner: "))
  111.           (setq pt2 (getcorner pt1 "\nOther corner: "))
  112.  
  113.           (command ".PLINE"
  114.                    pt1
  115.           )
  116.  
  117.     )
  118.  
  119. This begins the polyline at PT1.  The next step in the command
  120. algorithm will be to calculate the other two corner points based
  121. on PT1 and PT2, and send the points in the appropriate order to
  122. the PLINE command.
  123.  
  124. WORKING WITH POINTS AS LISTS OF REAL NUMBERS
  125.  
  126. AutoLISP stores a point coordinate as a list of three real
  127. numbers.  For example, this expression binds the variable PT to
  128. the list (1.0 2.0 3.0).
  129.  
  130.     Command: (setq pt (getpoint "\nPoint: "))
  131.     Point: 1,2,3
  132.     (1.0 2.0 3.0)
  133.  
  134. Note that a 3d point need not necessarily be thought of as one
  135. thing.  It's true that the list storing the coordinate values is
  136. a single object, but it is a complex object, itself consisting of
  137. a list of three elements.  Thinking about it in this way, it's
  138. easy to conceive of a point as separate X, Y, and Z values
  139. combined together in some arbitrary but very useful association.
  140.  
  141. AutoLISP has many functions to retrieve and replace elements (in
  142. this case, separate coordinate values) within an existing list.
  143. The most basic are the functions CAR and CDR (pronounced
  144. "could-er", as in "I could have had a V8").
  145.  
  146. CAR and CDR each take one required argument, which should be a
  147. list.
  148.  
  149. CAR returns the first element of a list.
  150.  
  151. CDR returns the list and everything in it EXCEPT for the first
  152. element.
  153.  
  154. If the function CAR was applied to the variable PT, what value
  155. would it return?  Which coordinate value would that equate to?
  156.  
  157.     Command: (car pt)
  158.     1.0
  159.  
  160. CAR returns the first element of PT, which is its X coordinate
  161. value.
  162.  
  163. What would the function CDR return when applied to PT?
  164.  
  165.     Command: (cdr pt)
  166.     (2.0 3.0)
  167.  
  168. At first glance, this doesn't appear particularly useful.  But
  169. what would happen if CAR was applied to the result above?  What
  170. coordinate value would the combination of CAR and CDR return?
  171.  
  172.     Command: (car (cdr pt))
  173.     2.0
  174.  
  175. CDR returns the list after the first element is removed, so it
  176. returns the list (2.0 3.0).  When CAR receives this list as its
  177. argument, it returns the first element, which is the value of the
  178. Y coordinate of the point.
  179.  
  180. (car <point>) returns the X value of a point.
  181.  
  182. (car (cdr <point>)) returns the Y value of a point.
  183.  
  184. And, yes, (car (cdr (cdr <point>))) returns the Z value of a
  185. point.
  186.  
  187. CALCULATING THE OTHER CORNERS
  188.  
  189. If PT1 and PT2 hold the values of two points that form opposing
  190. corners of a rectangle, then the other corner points can be
  191. determined by combining the X and Y values of PT1 with the Y and
  192. X values of PT2.  In our case, we ignore the Z values because
  193. we're dealing with a 2d polyline.
  194.  
  195. PT3 shares the X value with PT1 and the Y value with PT2.
  196.  
  197. PT4 shares the X value with PT2 and the Y value with PT1.
  198.  
  199.     PT3--------------------------------PT2
  200.      |                                  |
  201.      |                                  |
  202.      |                                  |
  203.      |                                  |
  204.     PT1--------------------------------PT4
  205.  
  206. How might you use the CAR and CDR functions to extract the
  207. appropriate values from PT1 and PT2?
  208.  
  209. The X value of PT1 is:        (car pt1)
  210.  
  211. The Y value of PT1 is:        (car (cdr pt1))
  212.  
  213. The X value of PT2 is:        (car pt2)
  214.  
  215. The Y value of PT2 is:        (car (cdr pt2))
  216.  
  217. CREATING NEW POINT LISTS
  218.  
  219. If you understand how to retrieve the individual coordinate values
  220. from PT1 and PT2, you might be wondering how to combine the
  221. individual values into new lists that AutoLISP will treat as
  222. points.
  223.  
  224. The LIST function takes an arbitrary number of arguments and
  225. returns a list whose elements consist of the values of its
  226. arguments.
  227.  
  228. For example, this expression returns a list of three elements.
  229.  
  230.     Command: (list 1.0 2.0 3.0)
  231.     (1.0 2.0 3.0)
  232.  
  233. LIST returns the values of its arguments.
  234.  
  235.     Command: (setq x 1.0)
  236.     1.0
  237.  
  238.     !x
  239.     1.0
  240.  
  241.     Command: (setq y 2.0)
  242.     2.0
  243.  
  244.     !y
  245.     2.0
  246.  
  247.     Command: (setq z 3.0)
  248.     3.0
  249.  
  250.     !z
  251.     3.0
  252.  
  253.     Command: (list x y z)
  254.     (1.0 2.0 3.0)
  255.  
  256.     Command: (setq pt (list x y z))
  257.     (1.0 2.0 3.0)
  258.  
  259.     !pt
  260.     (1.0 2.0 3.0)
  261.  
  262. Additional code is added to the function definition for BOX that
  263. calculates the other corner points.  The "C" option closes the
  264. polyline and completes the PLINE command.
  265.  
  266.     (defun C:BOX ()
  267.           (setq pt1 (getpoint "\nFirst corner: "))
  268.           (setq pt2 (getcorner pt1 "\nOther corner: "))
  269.           (command ".PLINE"
  270.                    pt1
  271.  
  272.                    (list (car pt2) (car (cdr pt1)))
  273.                    pt2
  274.                    (list (car pt1) (car (cdr pt2)))
  275.                    "C"
  276.  
  277.           )
  278.     )
  279.  
  280. Test the current definition of BOX in the drawing editor.
  281. Remember, AutoLISP isn't aware of changes you make to source
  282. files until you load the file after the change has been made.
  283.  
  284.     Command: (load "box")
  285.     C:BOX
  286.  
  287.     Command: BOX
  288.     First corner: <pick>
  289.     Other corner: <drag and pick>
  290.     From point:
  291.     Current line-width is 0.0000
  292.     Arc/Close/Halfwidth/Length/Undo/Width/<Endpoint of line>:
  293.     Arc/Close/Halfwidth/Length/Undo/Width/<Endpoint of line>:
  294.     Arc/Close/Halfwidth/Length/Undo/Width/<Endpoint of line>:
  295.     Arc/Close/Halfwidth/Length/Undo/Width/<Endpoint of line>: C
  296.     Command: nil
  297.  
  298. BOX should have drawn a closed, rectangular 2d polyline bounded
  299. by the opposing corners you chose.  If it didn't do this, debug
  300. the code and fix the problem before you continue.
  301.  
  302. CLEANING UP THE INTERFACE
  303.  
  304. The data acquisition part of the BOX interface works well, but
  305. there are a couple of things you can do to clean up the overall
  306. operation of the command.
  307.  
  308. 1.  There's no particular reason to echo the prompts from the
  309.     PLINE command to the command prompt area; in fact, it slows
  310.     things down.
  311.  
  312. 2.  The nil returned by BOX should be suppressed.
  313.  
  314. 3.  There's no reason to draw blips at the corners of the
  315.     polyline.
  316.  
  317. The GETVAR and SETVAR functions are used, along with SETQ, to
  318. store the values of the system variables CMDECHO and BLIPMODE as
  319. C:BOX is entered, change the values during the execution of
  320. C:BOX, and restore the original values prior to exiting C:BOX.
  321.  
  322. PRIN1 is called as the last expression in the body of C:BOX to
  323. print a null string to the terminal as C:BOX exits, suppressing
  324. the nil that would otherwise be returned.
  325.  
  326.     (defun C:BOX ()
  327.  
  328.           (setq old_cmdecho  (getvar "CMDECHO")
  329.                 old_blipmode (getvar "BLIPMODE")
  330.           )
  331.           (setvar "CMDECHO" 0)
  332.           (setvar "BLIPMODE" 0)
  333.  
  334.           (setq pt1 (getpoint "\nFirst corner: "))
  335.           (setq pt2 (getcorner pt1 "\nOther corner: "))
  336.           (command ".PLINE"
  337.                    pt1
  338.                    (list (car pt2) (car (cdr pt1)))
  339.                    pt2
  340.                    (list (car pt1) (car (cdr pt2)))
  341.                    "C"
  342.           )
  343.  
  344.           (setvar "CMDECHO"  old_cmdecho)
  345.           (setvar "BLIPMODE" old_blipmode)
  346.           (prin1)
  347.     )
  348.  
  349. Test BOX.  If everything works as expected, define the variables
  350. used by the routine to be local to the function.
  351.  
  352.     (defun C:BOX (/ pt1 pt2 old_cmdecho old_blipmode)
  353.           ...
  354.  
  355. Congratulations.  Now let your imagination run wild and think of
  356. all the different ways you can use AutoLISP to customize the
  357. AutoCAD command structure, and the new commands you can write to
  358. help make your design tasks easier!
  359.  
  360. That's your assignment for this week!
  361.  
  362. Next week:  Predicates, Logical Operators, and Conditional
  363.             Expressions, Part One
  364.  
  365.