Developing solutions can be a challenge and on this page notes are maintained - maybe they can help you!
For LISP examples, see LISP Examples.
For LISP programs, see LISP Programs.
A table of contents...
IT Architecture
Less technical, more IT Architecture based.
Xdata primer for attaching data to CAD objects Extended entity data and how they can help your organization's information flows.
Why on earth LISP? If you want to program, to do smart things with CAD.
Advanced LISP subjects
Dive deeper in LISP.
If you use AutoCAD or BricsCAD, you probably want to optimize work flows. Customizing is a packet of actions to do this and it often involves creating a user interface that fit your needs and automating drawing tasks in a smart way. If you want to dive into that, you'll have a long road ahead of you. However, that shouldn't keep you from doing so. Why? Because there is so much written on the net to help you, with relative small actions you can achieve great results and, we are here to help you.
There is much to gain but managers are in general hard to convince to put money in customizing. Keep them informed and show them how much easier and faster CAD-challenges can be solved, you may get their support. Keep them involved.
Inhoud
Knowledge Base
- Search for solutions with your favourite search engine, you are not the only one with problem / challenge "x".
- LISP directories you want offline:
- Reference works customization you want offline:
- General reference books you want offline:
- Sites with useful information:
http://www.lee-mac.com/tutorials.html < Clear code examples, techies
http://www.afralisp.net/ < Old but very clear written
https://www.fourmilab.ch/diesel/ < DIESEL overview
Documentation Tools
Manuals
Platform independent software, open-source:
Documents and create PDFs: LibreOffice https://www.libreoffice.org/
Vector drawings (also from CAD): Inkscape, https://inkscape.org/
Image processing: Gimp, http://www.gimp.org/
Screendumps: Greenshot (Windows, Linux native) http://getgreenshot.org/
Screen casts
See http://www.vanderworp.org/Screen-casting
Programming Tools
Notepad++ is useful to write code. Notepad++ recognizes the syntax and uses colors to clarify the structure. Download: https://notepad-plus-plus.org/
- Search and Replace: ctrl + h
- Indent: selecting block, tab or shift + tab
You definitely want the CAD-LISP programming environment for Notepad++. You can find it here: http://nedcad.nl/en/notepad-syntax-highlighting-for-cad-lisp/
Cygwin you mainly use to search, it's a bash command line in Windows, and therefore similar to the OS X command line. You can find it here: https://cygwin.com/.
- Some examples:
Wiebe@win7ws ~ << ~ This means that you are in your home directory (/home/Wiebe). $ cd /cygdrive/c/ << Change directory to drive C:. Wiebe@win7ws /cygdrive/c << There you are now. $ cd /cygdrive/c/ProgramData/cadchup/ << To the cadchup folder. Wiebe@win7ws /cygdrive/c/ProgramData/cadchup $ find . -iname "*adch*.dwt" << Search from current directory (.), case insensitive to (-iname) a pattern. ./cadchup.dwt < This conforms to the pattern. Wiebe@win7ws /cygdrive/c/ProgramData/cadchup $ cd /cygdrive/c/ProgramData/Autodesk/ApplicationPlugins/cadchup.bundle/ << Other folder ... Wiebe@win7ws /cygdrive/c/ProgramData/Autodesk/ApplicationPlugins/cadchup.bundle $ grep -i 'alert' * << Search all files (*) case insensitive (-i) for text "alert". grep: contents: Is a directory << That does nothing because it is a folder without files. Wiebe@win7ws /cygdrive/c/ProgramData/Autodesk/ApplicationPlugins/cadchup.bundle $ grep -i -r 'alert' * << This provides us with something, thanks to -r (including subdirectories. These are all file names and lines in files with that text and can therefore be very much. contents/abc_start_cad.lsp: (t (setq alerttxt (strcat "CADchUP is not found in " programdata locac " nor " appdata locac ". Please fix this first.")) (alert alerttxt) (exit)) ... Wiebe@win7ws /cygdrive/c/ProgramData/Autodesk/ApplicationPlugins/cadchup.bundle $ grep -i -r 'alert' * | wc -l << Send output to grep wc (via |). Word Count with -l gives the number of lines. 32 << so 32 lines with word "alert".
Moral: Cygwin is a Linux command line and a powerful tool to perform complex tasks and what is above is just a starting point.
- Some examples:
Concepts
BricsCAD and AutoCAD and dealing with AutoLoader
Read this page: BricsCAD_and_AutoCAD_and_dealing_with_AutoLoader.
Start sequence AutoCAD and BricsCAD
What happens, and in what order, when you start your CAD program? The answer - a bit outdated - can be found here: http://www.blog.cadnauseam.com/2008/09/01/what-is-loaded-at-autocad-startup-and-when/, look at the list.
LISP start files
You want your CAD program to do things at start or during opening a document.
When\Vendor |
BricsCAD |
AutoCAD |
At program start |
on_start.lsp |
acad.lsp |
At document loading |
on_doc_load.lsp |
acaddoc.lsp |
Read the previous paragraph for more options, but these work fine. If menu's are used you may find menuname.mnl also a proper construction to use. In AutoCAD, TRUSTEDPATHS can complicate things. If these files do not exist you can make them and put them in the search path. First find out if and where they live by pasting something like this on the command line:
(findfile "on_start.lsp")
If these files don't exist you can create a directory where you put them and append it to the "Support File Search Path" or put them in %appdata% like:
C:\\Users\\%username%\\AppData\\Roaming\\Bricsys\\BricsCAD\\V17x64\\en_US\\Support
Running commands at start up
Regular CAD commands should be run as a final step. Otherwise they can interfere with other things that are about to start or have been started during initializing the program. The construction for this is using (S::STARTUP).
The problem with all startup files like (S::STARTUP), acad.lsp, acaddoc.lsp is that there is only one valid file and it may be used or created by other app. A nice programming technique is checking for existence, checking if it contains the right code and - if not - append the right code. For example, CADchUP contains these measures.
As far as (S::STARTUP) is concerned you may want to take a look at http://www.cadtutor.net/forum/showthread.php?26336-S-STARTUP-Function to see how code is appended by using a defun-q construction. Alternative: https://knowledge.autodesk.com/search-result/caas/CloudHelp/cloudhelp/2016/ENU/AutoCAD-Customization/files/GUID-FDB4038D-1620-4A56-8824-D37729D42520-htm.html. An example of this, in on_doc_load.lsp (BricsCAD) or acaddoc.lsp AutoCAD):
; Your other LISP code ; Putting commands in a list, yes, this is a list. (defun-q finalstepcommands () (command "._ribbonclose") (command "._-layer" "m" "0" "") (command "._filetabclose") (command "._taskbar" "1") ) ; Appending this list to to special STARTUP list (setq S::STARTUP (append S::STARTUP finalstepcommands)) ; More LISP code ...
As an alternative you can also start your program with a /b switch. This looks like cadprogram.exe /b startup.scr, where startup.scr can contain commands, but also LISP.
vla-get vla-put
You can control much with this combo. The question is: How to use it? Reading this is not enough, paste code on the command line and study it (F2)...
If you paste:
(setq ss (vlax-get-acad-object))
... you get an object - assigned to variable ss. Next you want to know the properties of ss:
(vlax-dump-object ss)
You see the list of properties and one of the entries is "Preferences". Let's see what it offers, if you paste each line...
(setq ss (vla-get-preferences (vlax-get-acad-object))) (vlax-dump-object ss)
... you get an object files. So the next step:
(setq ss (vla-get-files (vla-get-preferences (vlax-get-acad-object)))) (vlax-dump-object ss)
Now we have all files settings. As long is there is not a (RO) text (Read Only), we can change it.
For example, we have all plotter configurations on a server drive. The current locations are:
(vla-get-printerconfigpath ss) (vla-get-printerdescpath ss) (vla-get-printerstylesheetpath ss)
We assign a different location:
(vla-put-printerconfigpath ss "x:\\cad_users\\19.1\\plotters") (vla-put-printerdescpath ss "x:\\cad_users\\19.1\\plotters\\pmpfiles") (vla-put-printerstylesheetpath ss "x:\\cad_users\\19.1\\plotters\\plotstyles")
Cool?
In summary: You start with (vlax-get-acad-object) for building a selection set, making it complexer as described, where (vlax-dump-object ss) is your friend. If you know the properties by using "get", you can change them with "put".
Easy Peacy!
UCS and trans
See trans
Calling LISP from within LISP
If you have code that you want to reuse, there is the load construction like:
(load filename [onfailure])
This evaluates filename. Another less common way is to define your command like (defun c:runthis ()...) and call it from within another LISP function like:
(princ "\nYour code to run... ") (c:runthis) (princ "\nDone. ")
You can consider this construction as an addition to error handling as described at http://www.afralisp.net/autolisp/tutorials/error-trapping.php
LISP, DRY and WET and (defun ...)
This one goes a bit deeper, but don't let it keep you from reading. It contains hard to find information on the net but it comes in really helpful at times.
Q&D, WET and DRY
I found it funny to understand the abbreviations of DRY and WET. When you start coding, you want a solution and Q&D, Quick and Dirty is no problem, "It works!"
But on the way you start noticing you are repeating yourself, "I've done that before, where was it?" Next you paste a code block, adapt it and and again you solved a challenge. This is known as WET or Write Everything Twice. The negative conotation is easy to explain: If a code block needs a change it is not easy to find all locations where you used that code block.
In time you start to understand LISP better, how every piece of code between parentheses returns a value that you can use as input for another piece of code between parentheses.
Time to work smarter.
Functions (defun ...) play a central role in this article.
(defun create-flowers () (remove-roots-and-return-flower (reap-and-return-harvested-plant (drop-seed-and-return-plant "tulip") ) ) ) (defun create-carrots () (remove-stem-and-return-root (reap-and-return-harvested-plant (drop-seed-and-return-plant "ordinary-carrot") ) ) )
How about creating a universal function, instead of copying and pasting code from file to file? (reap-and-return-harvested-plant arguments) is a good example.
This is where DRY comes in. DRY stands for Don't Repeat Yourself. Much of the example code is the same. Yet, their final output, carrots and flowers is very different. By defining (reap-and-return-harvested-plant plant) and (drop-seed-and-return-plant seed-type), where plant and seed-type are the arguments, DRY conditions are met and writing becomes easier, better and more efficient.
Functions, (defun ...) in general.
Shall we dive in the subject a little deeper? You can even automate the creation of functions, quite exiting! Do yourself a favour and paste code on the command line to better understand what happens.
(defun c:candc (/) (princ "\nCola and Chips!") (princ) )
As one paste line:
(defun c:candc (/) (princ "\nCola and Chips!") (princ))
Test by entering new command "CANDC". You get the message. Nothing new, "c:" is the reason you can use it on the command line and (princ) suppresses the previous returned values.
Now:
(type c:candc)
SUBR (subroutine) is returned, not SYM(bol), SUBR is a data type. To erase the subroutine:
(setq c:candc nil)
Clean slate, symbol c:candc seized to exist, read on, it gets interesting from this point.
Functions with variable names, meet "set" and "read"
Create a variable, symbol "function-name":
(setq function-name "candc") (type function-name)
Create a function:
(defun function-candc (/) (princ "\nCola and Chips!") (princ)) (type function-candc)
Here comes the magic:
(setq function-symbol (read (strcat "c:" function-name))) (set function-symbol function-candc)
We've created ourselves a new command "CANDC" in just a few words, all based on variables! And we did not even use (defun ...)! Read this line twice.
Analyzing with "type", from inside to outside:
- "strcat" conCATtenates a STRring of type, needless to say, STR
- "read" generates a SYM
- "function-candc" is SUBR.
Because "function-candc" is SUBR, "set" takes care that SYM "function-symbol""c:candc" becomes SUBR too.
One caveat: We need (vlax-add-cmd ...) to get it working:
(vlax-add-cmd function-name function-symbol)
Think about it, this really offers opportunities to do some smart programming.
Loading functions the smarter way
The following command draws a leader without text. It lives in "arrow.lsp"
(defun *error* (msg) (setvar "cmdecho" cmdecho-org) (princ) ) (defun arrow (/ cmdecho-org pt1 pt2) (setq cmdecho-org (getvar "cmdecho")) (setq pt1 (getpoint "\nSpecify arrow start point: ")) (setq pt2 (getpoint pt1 "\nSpecify trailing end point: ")) (setvar "cmdecho" 0) (command "._leader" "_non" pt1 "_non" pt2 "" "" "_none") (setvar "cmdecho" cmdecho-org) (princ) )
Please note that "c:" is not available, it is just a plain function that is not available on the command line - yet.
We have a bunch of these programs to make life easier in one folder, but for now, focus is on "arrow".
To keep things simple, entries are made in...
- BricsCAD: on_doc_load.lsp
- AutoCAD: acaddoc.lsp
You probably like a pointer there with (load "my-functions") in a file "my-functions.lsp", but we'll forget that for now.
The entry becomes:
(defun c:arrow (/ arrow) (load "arrow") (arrow))
A function is defined with name "c:arrow" and it claims almost no memory, it is just that single line of characters, nothing more. The opposite is what a lot of people do, just load tons of complete LISP files in memory, slowing down performance.
Loading of file "arrow.lsp" is only done when someone enters command ARROW. And unloading file "arrow.lsp", after finishing command ARROW, is taken care for by statement "(/ arrow)". Since atom "arrow" is not atom "c:arrow", the latter stays in memory, i.e. the one line of code. Proof can be found by running (atoms-family 1). After decades, it is still a charming way to treat your computer and yourself well.
You have more than one function, so your list of entries becomes something like:
(defun c:arrow (/ arrow) (load "arrow") (arrow)) (defun c:my-line (/ my-line) (load "my-line") (my-line)) (defun c:my-circle (/ my-circle) (load "my-circle") (my-circle)) (defun c:my-arc (/ my-arc) (load "my-arc") (my-arc)) (princ)
A variant that can be considered for often used commands:
(defun c:arrow () (if (not arrow) (load "arrow")) (arrow))
Once loaded from file, it remains in memory and does not have to be loaded again.
Putting it all together
How can we combine all previous knowledge into a final solution? Here is the plan:
- Read all LISP files from the LISP directory in a list.
- Use a function to transform file names into command line functions with the "set" and "read" construction.
LISP and tests
Functions like "while", "if" and "cond" require tests.
As long as the test evaluates to T or true, the test is passed and code beyond is executed.
I've seen many variants of tests, some very exotic, some not much used, some logical. The thing with LISP is that you can program with accents on readability at the cost of amount of code or with accents on efficient code at the cost of readability. This does matter, you want others to be able to understand your code while staying efficient. Conditional testing or logical testing is an example of many ways to achieve the same.
Assume:
(setq var-nil nil) (setq var-string "string") (setq var-real 0.123) (setq var-true t) (setq var-list (list 1.2 'a "A"))
Now a test:
(if (/= nil var-list) (princ "T") (princ "nil") )
This construction is very tempting and readable at least. However the most simple test is just writing down only the atom name as in:
(if var-list (princ "T") (princ "nil") )
This construction is often forgotten (at least by me) and can be combined with "and" like in:
(if (and var-list var-true) (princ "T") (princ "nil") )
Now you have written a function with arguments. Problem: The last expression is not what you want as return output for your function. Almost not documented on the net but very logical: Create a variable with the return value and, as the last statement, do (eval return-value-variable). Not limited to T or nil.
Take a look at the following list for some more examples.
All these test expressions evaluate to T:
ATOM-NAME-NOT-NIL (= var-nil nil) (/= var-nil "string") (= var-string "string") (/= var-string "hello") (= var-real 0.123) (equal var-real 0.123) (eq var-string (setq var-same-object var-string)) (= var-whatever) (= var-nil) (< 1 2) (< var-nil var-real) (< var-nil var-string) (> "b" "a") (<= 8 8.0) (< 1 2 4.0) (setq var-true t) (setq var-true (not nil)) (not (equal var-real 0.234)) (not nil) (not (not (not nil))) (not var-nil) (not var-not-defined) (and) (and (not var-nil)) (and var-string) (and var-string (not var-nil)) (and var-string (not var-nil) (setq var-new "some-value")) (eval (not nil)) (eval var-true) (listp var-list) (or var-string) (or var-string var-nil) (vl-every '/= var-list (cdr var-list)) (vl-some '/= var-list (cdr var-list)) (wcmatch var-string "str*")
Nice set of examples.
Some additional remarks:
- "equal" looks like "=" but knows an additional fuzz option, with (setvar a 0.123456 b 0.123457), (equal a b 0.000001) returns T.
LISP and groups
How can we handle named and unnamed groups?
If you want to do operations within a group you can use what is documented here: http://adndevblog.typepad.com/autocad/2012/12/how-to-add-a-group-in-a-selection-set-from-an-autolisp-function.html. A cached version:
(defun selgrp (grpname) ;; grpname is the group name, it accepts ;; unnamed groupnames, such as *A1 (setq grp (dictsearch (namedobjdict) "ACAD_GROUP")) (setq a1 (dictsearch (cdr (assoc -1 grp)) grpname)) (setq ss (ssadd)) (while (/= (assoc 340 a1) nil) (setq ent (assoc 340 a1)) (setq ss (ssadd (cdr ent) ss)) (setq a1 (subst (cons 0 "") ent a1)) ) ss )
LISP and selection sets
If you think about LISP and structures in list format, a selection set could be a list of multiple entities with their definitions. However, when multiple selection sets exist, they can overlap which, on its own, can be considered stupid and a waste. So selection sets are a bit special in a way that they are nothing more than a list of entity names. I must confess I have no proof of the latter but functions like (entget) form a strong indication. By the way, selections sets have often an abbreviation like "ss". This also means that selection sets have a kind of formatting. Consider this example:
(setq ss nil) ; ss equals nil (setq ss (ssadd)) ; ss is formatted as selection set, but contains no entities (setq ss (ssget)) ; ss can be supplied with entities. Paste examples on command line for illustration.
The concept:
- You have a selection set, made with (ssget) for example.
- You can retrieve one entity name from it using (ssname index) where index is the number of one of the entities in the selection set.
- This means you often have to use "while" or "repeat" to traverse through the selection set in order to deal with all entities in the list - unless you know the index.
- Now that you have the name of the entity, you can:
- Remove or add it from a selection set with (ssdel) or (ssadd).
- Check if entity is member of a selection set with (ssmemb).
- Get information of that entity, for testing and further actions, by using (entget).
So it is important to understand that you can not do something like adding a selection set 2 to set 1:
(setq ss-1 (+ ss-1 ss-2))
To achieve this, your construction can be:
(setq n 0) (repeat (sslength ss-2) (ssadd (ssname ss-2 n) ss-1) (setq n (1+ n)) )
In its base, this solution is okay. However, you should be aware of an important thing:
Intermezzo:
ss-1 is nothing more than a pointer to a selection set with a name. In the example above, ss-1 is changed by adding members from ss-2. If you don't want ss-1 to be changed, you are tempted to use another symbol, like (setq ss-union ss-1) and use ss-union to add entities. Oops, ss-union becomes just another pointer to the same selection set. If you change ss-union by adding entities, ss-1 will change too without any notice! Solution: Create a new selection set first: (setq ss-union (ssadd)) and next do the repeat routine for both ss-1 and ss-2. One pitfall less. This is why this statement is used directly under (defun...) in the working exmples below.
From time to time you have code adding entities and then you want to do something with only the added entities. What are those entities? Time for tricks: Before your code runs:
(setq ss-pre (ssget "x"))
All entities in the drawing are added to "ss-pre". After your code added more entities, you do:
(setq ss-post (ssget "x"))
And again there is a selection set with all entities. Think about subtracting a solid from another solid. If you do ss-post minus ss-pre, you have all newly added entities. So we need the difference in a new selection set and do post editing like grouping and moving.
You are probably aware of three Boolean operations when working with solids: Union, Subtract and Intersect. Let's take two selection sets. Entities are just a number in a list:
ss1 = (0 1 2 3) ss2 = (2 3 4 5)
And the corresponding Boolean operations:
Union: ss1 + ss2 = (0 1 2 3 4 5) Subtract: ss1 - ss2 = (0 1), ss2 - ss1 = (4 5) Intersect: ss1 ∩ ss2 = (2 3)
A bit more practical, for Boolean "Union" we have function "ssadd", for Boolean "subtract" we have "ssdel". What do we have for Boolean "Intersect"?
How about this, and Boolean again:
Intersect: ss1 ∩ ss2 = (ss1 + ss2) - (ss1 - ss2) - (ss2 - ss1)
So if you do a Union of ss1 and ss2 and next you Subtract the Subtraction of ss1 and ss2 and finally Subtract the Subtraction of ss2 and ss1, you have the intersecting entities. Are you still there? I have been developing my thoughts while writing, hope I didn't oversee anything. But it means you can do all Booleans with only (ssadd) and (ssdel).
About selection sets:
- For Union, we have a "ss-1" and a "ss-2" that will form "ss-union".
- For Subtract, we have a "ss-1" and we subtract "ss-2" from that, resulting in "ss-subtract".
- For Intersect, sets "ss-1" and "ss-2" are used, resulting in "ss-intersect".
Here are functions for "Union" and "Subtract".
File sel-union.lsp:
(defun sel-union (ss-base ss-add / ss-base ss-add n ss-union) (setq ss-union (ssadd)) (if (= 'pickset (type ss-base)) (progn (setq n 0) (repeat (sslength ss-base) (ssadd (ssname ss-base n) ss-union) (setq n (1+ n)) ) ) ) (if (= 'pickset (type ss-add)) (progn (setq n 0) (repeat (sslength ss-add) (ssadd (ssname ss-add n) ss-union) (setq n (1+ n)) ) ) ) (eval ss-union) )
And file sel-subtract.lsp:
(defun sel-subtract (ss-base ss-min / ss-base ss-min n ss-subtract) (setq ss-subtract (ssadd)) (if (= 'pickset (type ss-base)) (progn (setq n 0) (repeat (sslength ss-base) (ssadd (ssname ss-base n) ss-subtract) (setq n (1+ n)) ) ) ) (if (= 'pickset (type ss-min)) (progn (setq n 0) (repeat (sslength ss-min) (ssdel (ssname ss-min n) ss-subtract) (setq n (1+ n)) ) ) ) (eval ss-subtract) )
Where do we end with "Intersection"? We had this earlier:
Intersect: ss1 ∩ ss2 = (ss1 + ss2) - (ss1 - ss2) - (ss2 - ss1)
You can use the functions to get the intersection. However, it is a bit over complex. Better write a new function (sel-intersection ss-1 ss-2) based on using (ssmemb).
Last, but not least, file sel-intersect.lsp:
(defun sel-intersect (ss-base-1 ss-base-2 / ss-base-1 ss-base-2 n ss-base-1-new ss-intersect ent-name) (setq ss-base-1-new (ssadd) ss-intersect (ssadd)) (if (= 'pickset (type ss-base-1)) (progn (setq n 0) (repeat (sslength ss-base-1) (ssadd (ssname ss-base-1 n) ss-base-1-new) (setq n (1+ n)) ) ) ) (if (= 'pickset (type ss-base-2)) (progn (setq n 0) (repeat (sslength ss-base-2) (setq ent-name (ssmemb (ssname ss-base-2 n) ss-base-1-new)) (if (/= nil ent-name) (ssadd ent-name ss-intersect) ) (setq n (1+ n)) ) ) ) (eval ss-intersect) )
You want these functions in your toolbox! On the command line it is a bit harder but for illustrating it is fine. Files in your File Support Search Path? I use the BricsCAD prompt, which is a ":".
: (load "sel-union") < Load the Union function SEL-UNION < Return value : (setq ss1 (ssget)) < Create selection set ss1 Select entities: < Used a Crossing here Opposite Corner: Entities in set: 3 < 3 entities selected Select entities: < And an empty return <Selection set: 0000000024E454A0> : (setq ss2 (ssget)) < Same for selection set ss2 Select entities: Opposite Corner: Entities in set: 3 Select entities: <Selection set: 00000000248B6180> < And the second set : MOVE < Check with a move command Select entities to move: (sel-union ss1 ss2) <Selection set: 0000000024E454A0> < Run our function Select entities to move: < And so on... 6 found. Entities in set: 6 Select entities to move: Enter base point [Displacement] <Displacement>:0,0 Enter second point <Use base point as displacement>:0,0
There is good reading stuff:
Basic understanding: http://www.afralisp.net/autolisp/tutorials/selection-sets.php
Settings in LISP file
You probably do record settings like this:
(setq ortho_org (getvar "orthomode"))
... and more as needed. Then you code your program and change ORTHOMODE as needed:
(setvar "orthomode" 1)
However, a user should get all changed settings back after your code is finished. So you end with:
(setvar "orthomode" ortho_org)
If you do it properly, you put that line of code in your error handler too, in particular because many people press Esc too frequently.
A Consideration
For a small LISP program there is nothing wrong with this approach. However, for a big and interactive program it is not always the right way. Consider a program that interactively asks for input and finally comes up with a result. Somewhere during the proces a user switches on OSNAP. Can you imagine how user feels after finishing the command? "Hey, I thought I turned Osnap on, #@#$".
In this case we should respect the user and add code on a local base, not on a general base. To give an example:
(setq pt1 (getpoint "\nSpecify first point of line: ")) (setq pt2 (getpoint "\nSpecify last point of line: ")) (command "_.line" pt1 pt2 "")
But running object snap mode can spoil it. The last line could become:
(command "_.line" "_non" pt1 "_non" pt2 "")
Problem solved... Unless the command is an impressive list like:
(command "_.pline" pt2 "_w" "0.0" "0.0" "_a" "_d" hkd2 pt4 "_l" pt5 "_a" pt6 "_l" pt7 "_a" pt8 "_l" pt9 "_a" pt11 "_l" pt13 "_a" "_d" hkd3 pt15 "_l" pt16 "_a" pt17 "_l" pt18 "_a" pt19 "_l" pt20 "_a" pt22 "_l" "_cl")
In such a case you can consider making two functions, (commandpre) and (commandpost), where (commandpre) stores object snap mode and turns it off and (commandpost) restores it. Read on how to use boole to achieve this.
More on OSMODE
OSMODE or object snap mode. This is a number, a bitsum of many settings:
0 None 1 Endpoint 2 Midpoint 4 Center 8 Node 16 Quadrant 32 Intersection 64 Insertion 128 Perpendicular 256 Tangent 512 Nearest 1024 Quick 2048 Apparent Intersection 4096 Extension 8192 Parallel
For example, value 511 (green "P" button in CADchUP) is a bitsum of 1+2+4+8+16+32+64+128+256=511
You could use a construction as illustrated with ORTHOMODE, but you will find out it doesn't work perfectly. Why? Because OSNAP on and off is also included in the code.
16384 Object Snap on or off, F3 (on: subtract value, off: add value)
To illustrate: Do OSMODE 1. OSMODE is set to "endpoint", value 1 (check). Press F3 for turning off object snap. This is not the same as setting it to value 0, it is just a switch to turn off or turn on the setting of OSMODE (read this twice). Do OSMODE and see that it changed to 1+16384=16385.
So you want to turn osnap on and simply substract 16384? That works for 16385, but not if the value is unknown and set by the user, for example 1-16384 is a bad idea. So we need a different solution. We use a function called boole for truth tables.
Turning OSNAP off:
(setvar "osmode" (boole 7 (getvar "osmode") 16384))
Turning OSNAP on:
(setvar "osmode" (boole 2 (getvar "osmode") 16384))
Toggle OSNAP:
(setvar "osmode" (boole 6 (getvar "osmode") 16384))
Selection
Selecting objects and points. The pointer is square, dimensions (width and height) are in pixels:
- Pickbox: The square pointer when you select objects.
- Aperture: The square pointer when object snap is active for selecting a point based on a part of an entity.
Gripsize: The magnetic handles of selected objects when no command is active. In AutoCAD, the magnetic area is the same as the gripsize value. In BricsCAD, the magnetic area is set with variable AttractionDistance.
All in all, it is easy to set all values for selection squares to values using LISP. Tuning these values to users needs is preventing them from missing clicks or wrong object clicks. Importance of setting this to optimal values is often underestimated.