Geez, enough with the back padding already .. every line of code is stolen from somewhere anyway! It's not a sign of clever insight - only a sign of age (and/or lack of a life)
Rugaroo, there's no better way to learn than to tear apart code and try to understand how it works. Way to go! Along with that, I think it's important also to discard and postpone things that are not about to become obvious before some other stuff is understood. For example, alot of time can be wasted on diving into a complex recursive method if the concept of recursion is not yet fully understood. So, as a beginner, grab what can be used at the moment and just skim the rest, telling yourself that in a few months you will be able to write and maybe even improve on the code that you don't quite grasp today.
Before I forget: the errors come from an extra return in the two LAYER command statements. Delete one "" in each and it should run ok.
I tried to put it all into one routine to make it simple, but this of course has a couple of reverse effects. Getting multiple values like Frontoff, Sideoff and Rearoff - or getting/offsetting entities with different prompts/distances - requires complex structures or repeated code if it has to be contained within the same defun. The natural thing to do is to write utility routines for such cases (or, as I like to call them with a misrepresenting name, subroutines).
The important thing with subroutines is that they have to live their own lifes, so to speak. A good rule of thumb is that you should be able to call a subroutine at the command line without crashes, without adding global variables (unless it is intentionally designed to do so) and without changing the environment in any way.
The subroutines in the original code are dependent on either other subroutines or on global variables. USERVAR/RESTOREUSER relied on global variables. GETSTORED could not function without GETNEW having been called. No independent variables were exchanged between either of them, and if that doesn't lead to hickups it leads to maintainance hell.
Below, I have substituted some of the code with subroutines that get called within the main defun.
Try load it and then call e.g. GetValue from the command line:
Command: (getvalue 4.5 "junk")
Specify junk yard setback <4.5>: 3.0
3.0
No dependence on the main routine, no global variables added, no changes to the environment, - it just returns a simple value.
Also try call offsetObject from the command line:
Command: (setq sset (offsetObject 12.5 (getpoint "Pick it: ") "Sir Lance" nil))
Pick it:
pick a pointSelect Sir Lance lot lines:
pick entitySelect Sir Lance lot lines:
< Selection set: 2a>
Again, no dependency .. it just returns a selection set without caring where it came from or where it'll be used.
Revised code below. Hope it's a bit simpler than the previous.
(defun getValue (val side / prmpt init inquiry)
(if (and (numberp val) (> val 0.0))
;; Value is retrieved from config - it's a number and it's
;; greater than zero, so offer it as default value
;; Set up INITGET 6 to prohibit zero and negative values
(setq prmpt (strcat "Specify " side " yard setback <" (rtos val) ">: ")
init 6
)
;; Value is not retrieved from config - or it's not a number
;; or it's not greater than zero, so don't dare to offer it
;; as default value
;; Set up INITGET 7 to prohibit zero, negative and NIL values
(setq prmpt (strcat "Specify " side " yard setback: ")
init 7
)
)
;; Perform the inquiry with the appropriate filter for input
;; If init is 6 and nil is hit then do nothing but return val
;; as it is. Otherwise, if a value is input then return it.
;; If init is 7 and nil is hit .. well, try again.
(initget init)
(if (setq inquiry (getdist prmpt))
(setq val inquiry)
)
val
)
(defun offsetObject (offdist offpt side ss / ent lastent)
;; If no selection set is supplied then create one that
;; can be returned by the function
(if (not ss)(setq ss (ssadd)))
;; Reset ERRNO as not to get caught in any previous error
;; condition
(setvar "ERRNO" 0)
;; Do yer selection and offsetting stuff
(while (and (setq ent (car (entsel (strcat "\nSelect " side " lot lines: "))))
(/= (getvar "ERRNO") 52)
)
(setq lastent (entlast))
(command ".OFFSET" offdist ent offpt "")
;; Check for new entity creation
(cond ((not (eq lastent (entlast)))
(ssadd (entlast) ss)
)
)
)
;; Return sset
(if (< 0 (sslength ss)) ss)
)
;; Main function
(defun C:SB (/ FRONTOFF SIDEOFF REAROFF USERLAY USERCMD
USEROSM USERFILL OSP tmp sset
ent lastent myPrompt *error*)
;; Local error handler. Check the comments at the end of
;; the routine.
(defun *error* (msg)
(if msg (princ (strcat "Rugaroo made an error: " msg)))
;; Reset system variables if the corresponding variables
;; contains data
(foreach var '(("clayer" USERLAY)("cmdecho" USERCMD)
("orthomode" USERORTH)("osmode" USEROSM)
("filletrad" USERFILL)
)
(if (eval (cadr var))
(setvar (car var) (eval (cadr var)))
)
)
(princ)
)
;; There are other ways to deal with this getvar/setvar stuff
;; (like the simple one shown in the local *error* function)
;; but let's just keep what you have. However! There is no
;; need to expose these variables to the environment. Keep
;; them as local variables (declared as local by appearing
;; in the 'local' list after the function name above).
(setq USERLAY (getvar "clayer")
USERCMD (getvar "cmdecho")
USERORTH (getvar "orthomode")
USEROSM (getvar "osmode")
USERFILL (getvar "filletrad")
)
(setvar "cmdecho" 0)
;; OSMODE will wait to be turned off until there's such a need.
;; The user would likely want it on when being prompted of
;; setback distances.
;; (setvar "osmode" 0)
(setvar "orthomode" 0)
(setvar "filletrad" 0)
;; Let's also keep this config stuff, but load it no matter
;; what. That way you have it and can check if it holds valid data.
;; Config data contains strings so DISTOF is a good choice to
;; try and retrieve the values. ATOF returns 0.0 from an empty
;; string, whereas DISTOF returns nil - making easier to test.
(setq FRONTOFF (distof (getcfg "AppData/Setbacks/Front"))
SIDEOFF (distof (getcfg "AppData/Setbacks/Side"))
REAROFF (distof (getcfg "AppData/Setbacks/Rear"))
)
;; For getting user inputs, see the routine GETVALUE above
(setq FRONTOFF (getValue FRONTOFF "Front")
SIDEOFF (getValue SIDEOFF "Side")
REAROFF (getValue REAROFF "Rear"))
;; User is done specifying setback distances. Now turn off OSMODE so
;; that the point specified below doesn't fly into outer space.
;; Every single line of code hereafter is dependent on the 3
;; values FRONTOFF, SIDEOFF and REAROFF. Therefore, wrap everything
;; in a condition to ensure that the routine doesn't blow up.
;; It'll be safe to assume that they hold a number or nil by now.
(cond ((and FRONTOFF SIDEOFF REAROFF
(setq OSP (getpoint "\nPick the center of the lot: "))
)
;; OSP is - like the setback distances - essential for further
;; processing. So if the user was too lazy to input a point
;; we won't proceed as it will not pass the COND test.
;; It's now safe to store the values until next time. If
;; you need to save with precision, now will be a good time
;; to check out the users settings of LUPREC and LUNITS in
;; order to determine how to call RTOS.
(setcfg "AppData/Setbacks/Front" (rtos FRONTOFF))
(setcfg "AppData/Setbacks/Side" (rtos SIDEOFF))
(setcfg "AppData/Setbacks/Rear" (rtos REAROFF))
;; Temporary layer creation dropped!
;; Call offsetObject to offset lot lines and collect newly
;; created entities. The first call will create a selection
;; set - the next calls will simply add to it.
(setq sset (offsetObject FRONTOFF OSP "Front" nil)
sset (offsetObject SIDEOFF OSP "Side" sset)
sset (offsetObject REAROFF OSP "Rear" sset))
;; Create the BPOLY. Save the last entity to check for success
(setq lastent (entlast))
(command ".BPOLY" OSP "")
(cond ((not (eq (entlast) lastent))
;; Yeehawww! BPOLY apparantly succeeded!
;; No need to set or create layer "SETBACKS" until now.
(command ".LAYER" "Make" "SETBACKS" "")
;; Now that it's set, use it:
(command ".CHPROP" (entlast) "" "Layer" "SETBACKS" "")
;; Erase temporary entities
(command ".ERASE" sset "")
;; Explode the setback boundary
(command ".EXPLODE" (entlast))
;; .. and announce the happy event
(princ "\n\nSetbacks created...")
)
(T (princ "\nNo luck today :-("))
)
)
)
(*error* nil)
;; What happened to resetting the systemvariables???!!
;; Because a local error handler was created that resets
;; the variables upon error, we might as well use it to
;; also reset the variables on normal exit. That's why
;; it's called with a simple argument of nil.
;; It even takes care of the silent-exit-princ :-)
)