BALLOONS (balloon.lsp)

One of the most sorely lacking features of AutoCAD is an item balloon command, essentially a leader with a circle containing a number which is referenced in a table. Some third party applications have one but if you don't need its other features, it can be an expensive way of getting one.

This month we will write a simple balloon program called, of course, BALLOON. The program will ask for a start point and a second point, create an arrowhead according to current dimvar settings and connect the arrow head and the second point with a line. It will then repeatedly prompt for more points, connecting the points with lines, until the user hits [ENTER]. The program will then prompt for a text height if this is the first time the program has been run in a session, offering the current dimension text height as the default. Subsequent calls to BALLOON will use the value supplied at the first call and the user will not be prompted for one.

The user will then be prompted for a number to place in the balloon, being offered `1' the first time the program is called and one higher than the previous number on subsequent calls. The circle will then be drawn at the end of the last line and the number placed in the centre of the circle.

The Listing

After the program description, the program name, BALLOON, is defined and variables are localized. Not all variables are localized though - TH2 and NUM2 are left global so that their values will be maintained between calls to BALLOON.

The settings of three setvars, CMDECHO, HIGHLIGHT and BLIPMODE are saved for restoration at the end of the program. The text height for the current text style is extracted with some moderately complex nested code. Although AutoLISP evaluates from left to right, we humans usually find it easier to evaluate AutoLISP code from the inside out. In this case, the name of the current textstyle is obtained, which is passed back to the TBLSEARCH function which extracts the style data. This in turn is passed back to the ASSOC function which extracts the text height list, which is passed back to the CDR function which extracts the text height value which is then stored in variable STH2. Code is often nested to avoid creating and setting variables that would only be required for a few microseconds. The current text style's height is needed to determine if it is fixed or variable as this affects certain prompts and command sequences.

We also calculate the dimension arrow length by multiplying the setvars DIMASZ and DIMSCALE, half the arrow width by dividing the arrow length by six and the dimension text height by multiplying the setvars DIMTXT and DIMSCALE. The values are then stored in variables ALEN2, AWID2 and DTH2 respectively. Command echoing is turned off and the UNDO command is called to group the program into one UNDO item. Although command echoing may already have been off, to have checked and changed it conditional to its value would have added unnecessary code and slowed the program.

An INITGET function with a `1' argument is called to ensure that the user enters a point for the following GETPOINT which is then stored in variable PA2. Another INITGET does the same for the second point which is stored in P3. This second GETPOINT has the first point (PA2) as an argument which creates the `rubber band' line from it to the cursor position. The angle between the 2 points is determined and also the angles plus and minus 90 degrees (half p ) from it. These angles, along with the arrow length and half width values, are then used to determine the other two corner points (PA3 & PA4) for the arrowhead and the start point (P2) of the line past the arrowhead.

After turning off BLIPMODE, an IF function is started which checks if the distance between the first 2 picks (PA2 & P3) is less than 1.5 times the arrow length (ALEN2). If so, the arrowhead is drawn, else the value in PA2 is copied to P2 so that the first line will be drawn from the first point and not the `blunt' end of the arrow. This approximates AutoCAD's standard practice of omitting the arrowhead from a leader if the first 2 picks are too close. The original BLIPMODE value is then restored to its previous value. A WHILE loop is started which will loop until P3 has no value. Since P3 has a value at this point, it will start and continue as long as the user picks points. For each point picked, a line will be drawn to it from P2 and then it will become the `previous' point by its value being copied to P2 and a new P3 is requested. When the user stops picking points and hits [ENTER], P3 will become nil and the loop will end.

The angle of the last drawn line is then calculated from its endpoints, again with some moderately complex nested code. This code extracts the start and end point of the last drawn line and passes them back to the ANGLE function which calculates the angle which is then stored in ANG2.

An IF is then started which tests for the text style height (STH2) being zero and variable TH2 having no value. The two tests are joined by and AND function which means both must return T (be true) for the then-expression to be processed. IF requires a test-expression, a then-expression and, optionally, an else-expression. Since there are two then-expressions, they are grouped into one by a PROGN function. The user is prompted for a text height, the value in DTH2 being offered as a default. A new IF is then started, nested in the previous IF, which will copy the value in DTH2 to TH2 if TH2 does not have a value (the default was accepted). The nested IF, the PROGN and the previous IF are then ended.

A new IF is started, this time testing for a value in TH2. If it finds one it will set variable RAD2 to 1.25 times the value in TH2 else it will set RAD2 to 1.25 times the value in STH2. RAD2 will be the radius for the balloon circle. This value is suitable for one and two digit numbers in most fonts. You can vary it if you wish.

The purpose of all these IFs is to allow both for styles that have fixed heights and styles that have `floating' heights as well as minimizing prompts. The first time BALLOON is run in an editing session, the text height will be requested by the program if the current text style height is not fixed in the current style. If the height is fixed in the style, requesting a text height would be meaningless. Subsequent calls to BALLOON would not cause it to request a text height as it would use the previously specified value. This is why the variable TH2 was not localized. Since the value in RAD2 needs to be relative to the text height, an IF is then used to get the correct variable for use in the calculations.

Still another IF is started, this time checking for a value in variable NUM2. If BALLOON had not yet been called, there will be no value and the IF's then-expression will be run. This will set NUM2 to zero. If BALLOON had been called, NUM2 will have an integer value. Being a global variable, NUM2 will retain its value between calls to BALLOON.

The next line increments the value in NUM2 by one. The user is then prompted for a number with the value of NUM2 being offered as a default. The user's response is stored in variable NUM3 which is will be nil if the user hit [RETURN] to accept the default. The IF function following will check to see if NUM3 has a value and if so, will set NUM2 to the value in NUM3.

The centre point for the balloon circle is then calculated at its radius past the end of the last line drawn and stored in variable P2.

BLIPMODE and HIGHLIGHT are then turned off and the circle is drawn. An IF is then used to determine how to insert the text - if the height is not fixed in the style (STH2 is zero), the value in TH2 is used for the text height else the text is inserted with the style's height.

To finish off, the UNDO group is terminated, the HIGHLIGHT, CMDECHO and BLIPMODE setvars are restored to their original settings and PRINC is called without any arguments to prevent the value of BLIPMODE being echoed to screen.
 

©1996-2001 ZOTO Technologies