Mending Your Lines (mend.lsp)This month we will be looking at an AutoLISP program that will mend' broken lines. By broken lines I mean lines that have been created by breaking or trimming a single line into two lines and not a linetype. The program, MEND, will ask the user to pick two lines, check them for co-linearity, erase the second selected line and modify one endpoint of the first line to coincide with the furthest endpoint of the second. By modifying the first line instead of creating a new one, the user is able to nominate a 'dominant' line in the event that the two lines have different properties (layer, linetype, etc)The ListingAfter a few lines of comment describing the program, the DEFUN statement gives the program its name, MEND, and localizes the variable names used by the program by putting them after the forward slash inside the parentheses. Localizing variable names prevents clashes with variables of the same name used by other programs and is usually done after the program has been written and debugged. Localizing also ensures that the values of the variables are always nil when starting the program.The current values of three setvars, CMDECHO, ORTHOMODE and OSMODE are saved to variables CMD, ORTH and OSM for restoration as the program will be changing them later. The CMDECHO Setvar value is then set to zero to prevent echoing of the command sequences to screen later in the program. CMDECHO may already have been zero but to have performed a check and changed it conditional to its value would have added unnecessary code and slowed the program. The UNDO command is then called to group the program into a single UNDO item. Had this been omitted, it would have taken two UNDO commands to reverse the effects of running the program once. This is followed with the routines to select the two lines to be mended. At first glance there seems to be a lot of unnecessary code used to simply select two lines. The reasoning behind this is that it makes the code bulletproof - the user cannot do anything but select two different lines. To select the first line we enter into a WHILE loop that will keep looping until variable ENT2 is bound to a value. ENT2 is set to the list returned by ENTSEL unless a blank area of the drawing is picked in which case nil is returned to ENT2. An IF command inside the WHILE loop is used to ensure the user picks a line. This routine will run if an object has been picked and is not a line. The test statement for the IF uses quite complex nested code and combines two tests into one, linked with an AND function that will return T (True) to the IF only if both tests return T. The first part of the test will return T if ENT2 is bound to a value and the second part of the test will return Setvar if the object selected is not a line. Although AutoLISP evaluates from left to right, it is often easier for humans to evaluate nested code from the inside out. In this case (car ENT2) returns the object name to the ENTGET function which extracts the object data and returns it to the ASSOC function which extracts the object type list, passing it back to CDR which extracts the object type from the list. This is then compared to the text string 'LINE' and will return T if they do not match (is not a line). Note that the test for the existence of a value bound to ENT2 is performed first. Since the second test would cause the program to crash if ENT2 had no value, testing for a value in ENT2 first would stop further processing of tests should it return nil (no object selected). If both tests returned T, then an object that was not a line was selected. The code inside the IF will inform the user that the object was not a line and then set ENT2 to nil. This will cause the WHILE loop to run again and reprompt for an object. If the first test returned nil (no object selected), the code inside the IF would be skipped and the WHILE loop would run again since ENT2 has no value. If the second test returned nil (an object was selected and it was a line), the IF function would be skipped and the WHILE loop would exit since ENT2 has a value. Note the PROGN function immediately after the IF test statement. Since the IF function requires a test-expression, a then-expression and, optionally, an else-expression, multiple then -expressions and/ or else-expressions are grouped by a single PROGN function. Once a line has been picked, we extract the object's name from ENT2 (the list returned by ENTSEL is a list of the object name and the point by which it was picked) and set it to OBJN2. We then highlight the object to indicate to the user that the first line has been successfully selected. The code to select the second line is identical except that an additional IF function is used to check that the first line was not picked again. Variable OBJN3 is then set to the name of the second line, concluding the object selection routines. The four endpoints of the two lines are then extracted from the object using slightly complicated nested code, storing them in variables PT2 through PT5. I prefer to start with 2 for numeric suffixes for variable names so as to prevent confusion with uppercase I and lowercase l. The distances between the endpoints of the two lines are also extracted and stored in variables DIST2 through DIST5. Note that the distances are those between endpoints of different lines and not between endpoints of the same line. This ensures that the minimum distance is the gap between the two lines. The maximum and minimum distances are determined and stored in MAXD2 and MIND2 respectively. These will be needed later. The angle of each line as well as the angle between an arbitrary endpoint is then stored in variables ANG2 through to ANG4. In case two parallel lines have their start and end points in reverse, a check is made and the angle is defined in a way to make it less than p *radians (180°). Because p is an irrational number, small errors can creep into AutoLISP's calculations. Initial testing of the program showed that two parallel lines had slightly different angles, differing after 13 decimal places. To counteract this, the value of the angle is rounded to 12 decimal places. This is achieved by converting the value of the angle (a real number) to a string in decimal form to 12 decimal places. This is then reconverted to a real number. All this is done as nested code on one line using the ATOF and RTOS functions. Now for the actual co-linearity test. This is also a slightly advanced piece of code, performing quite a complex test for the IF. Essentially, if the angles of the two lines are the same and there is no gap between the lines (the distance rounded to 12 decimal places using EQUAL's 'fuzz' argument) then the lines are colinear. Also, if the angles of the two lines and the angle of the gap between them are the same, then the two lines are co-linear. These two tests are united with an OR function which will return T to the IF function if either of the tests return T (the lines are co-linear). If so, the code within the first PROGN of the IF will be executed. This code will turn off ORTHO mode and any running object snaps, erase the second line and change one endpoint of the first line to the furthest endpoint of the second line. The original ORTHO mode and any running object snaps will then be restored. If both of the test statements turn out false, the OR function will return nil to the IF function and the code within the second PROGN of the IF will be executed instead. This will result in a message on the command line informing the user that the lines are not co-linear. It will then unhighlight the first object. The program winds up by ending the UNDO group and resetting the CMDECHO
setvar to its original value. The PRINC prevents the value of CMDECHO from
being displayed on screen as the program exits.
|
©1996-2001 ZOTO Technologies