Sie sind auf Seite 1von 24

; CALC.LSP: A full function calculator inside acad!

; A drawing of a calculator pops up. You pick it's buttons, or use the
; keyboard, for input. Numbers are displayed and evaluated just like a
; real calculator. It supports +, -, *, /, ^ (exponent), order of
; operations and brackets. You can do as many calculations as you like,
; exiting the routine by entering a null calculation (hit '=' twice).
; Distances, angles and areas from your drawing can be entered into calc.
; Numbers can be stored in 'memory' by picking the display and inserting a
; piece of text into your drawing. The text will be deleted at calc's exit,
; unless you take the 'Permanent' option at the insertion pt prompt. These
; numbers (and other text entities which are numbers) are recalled into calc
; by picking them. The screen menu is used for user defined functions. You
; can easily add any function you like to calc. I include trig and inverse
; trig functions as examples and give instructions for creating your own.

; written by: Len Switzer


;

Upper Canada Software

222 Monteith Ave.

Stratford, Ont., Canada

N5A 2P6

73417,1024

;The help screen code on line #254 assumes calc.lsp is located in C:\ACAD\LSP\.
;If it ain't so, please change line #254.

;Entry point for routine.


(defun C:CALC( / TEMP CALCSS PTLST ENT BKTCNT INSPT SCALE
KEY OLDERR CNT STR LAST# MNULST REL10 INFUNC)

(setq OLDERR *ERROR* *ERROR* *CALC*)

;Save and set system variables.


(SYSVAR"BLIPMODE"0)
(SYSVAR"CMDECHO"0)
(SYSVAR"HIGHLIGHT"0)

(command)

;If Release 10, turn off gridmode for each vport, set UCS to view.
;Gridmode off suppresses the redraw in each vport accompanying a UCS change.
(if(>(atof(getvar"ACADVER"))9)
(progn
(setq VPCFG(vports)LST1 nil)
(foreach TEMP VPCFG
(setvar"CVPORT"(car TEMP))
(setq LST1(cons(cons(car TEMP)(getvar"GRIDMODE"))LST1))
(setvar"GRIDMODE"0))
(setvar"CVPORT"(caar VPCFG))
(if(tblsearch"UCS""$CALC$")

(command".UCS""D""$CALC$"))
(command".UCS""V"".UCS""S""$CALC$")
;Restore gridmodes.
(foreach TEMP LST1
(setvar"CVPORT"(car TEMP))
(setvar"GRIDMODE"(cdr TEMP)))
(setvar"CVPORT"(caar VPCFG))
(setq LST1 nil))
(setq VPCFG'((1 (0.0 0.0)(1.0 1.0)))))

;Display screen menu.


(setq MNULST(list" *CALC*"nil"Dist""Angle""Area"nil"Sin""Cos""Tan"
nil"Asin""Acos""Atan"nil nil nil nil nil nil nil)
CNT -1)
(foreach STR MNULST
(grtext(setq CNT(1+ CNT))(cond(STR)(" "))))

(setq TEMP(list(getvar"VIEWSIZE")(getvar"VIEWCTR"))

;This is a crude estimate of view width in dwg units.


VWIDTH(/(*(car(getvar"SCREENSIZE"))(car TEMP))
(cadr(getvar"SCREENSIZE")))

;The calculator block is 1 unit high. SCALE is used for insert cmd.
;SCALE will be the height, in dwg units, of inserted calculator.
SCALE(/(car TEMP)(-(cadr(caddar VPCFG))(car(cdadar VPCFG)))3))

(if(> SCALE(car TEMP))


(setq SCALE(*(car TEMP)0.9)))

;INSPT is the insertion point for the calculator.


(setq INSPT(list(-(+(caadr TEMP)(/ VWIDTH 2))(* SCALE 0.5))
(+(-(cadadr TEMP)(*(car TEMP)0.5))(* SCALE 0.4374)))

ENT(entlast))

;Just in case last entity has attributes or vertices.


(while(entnext ENT)
(setq ENT(entnext ENT)))

;Clear area beneath calculator.


(command".SOLID"
(list(-(car INSPT)(* SCALE 0.3091))(-(cadr INSPT)(* SCALE 0.38175)))
(list(+(car INSPT)(* SCALE 0.3091))(-(cadr INSPT)(* SCALE 0.38175)))
(list(-(car INSPT)(* SCALE 0.3091))(+(cadr INSPT)(* SCALE 0.61825)))
(list(+(car INSPT)(* SCALE 0.3091))(+(cadr INSPT)(* SCALE 0.61825)))
""
".ERASE""L"""
;Insert calculator.
".INSERT""*CALC"INSPT SCALE 0

;Start select cmd to get pickbox.


".SELECT")

(setq ENT(if ENT(entnext ENT)(entnext))


CALCSS(ssadd)
BKTCNT 0
INFUNC 0
PTLST nil)

;Fill CALCSS with calculator entities.


;CALCSS must have it's contents properly ordered.
;Don't mess with calc.dwg, it dictates the order of these entities.
(while(progn
(ssadd ENT CALCSS)
(setq ENT(entnext ENT))))

;Build PTLST with coords of each button.


;This recursive approach seems at least 20% quicker then an iterative one.
(defun TEMP(CNT)
(if(> CNT -1)
(cons(trans(cdr(assoc 11(entget(ssname CALCSS CNT))))
(ssname CALCSS CNT)1)
(TEMP(1- CNT)))))
(setq PTLST(TEMP 19))

(prompt"\n Enter calculations. Esc for help.\n ")

;Loop while calculations are performed.

(while(CALC)
(prompt"\n "))

(exit))

;Main calculation routine.


;Returns the answer (a number) or nil.
;Loops for each number/operation pair, until = or } is entered.
(defun CALC( / #LST NUMBER INPUT)
(while(and(not(member(car #LST)(list '= '} )))
(setq INPUT(GET#)))

;#LST is updated with the new number and operation.


(setq #LST(append INPUT #LST))

;Order of operation decisions.


;Compares last 2 operations to decide whether to do 2nd last operation yet.
(while(or
(and(eq(car #LST)'expt)
(eq(caddr #LST)'expt))
(and(member(car #LST)'(* /))
(not(member(caddr #LST)'(+ - nil))))
(and(member(car #LST)'(+ - = }))
(caddr #LST)))

;Evaluate one operation.


(setq #LST(cons(car #LST)
(cons(apply(caddr #LST)(list(cadddr #LST)(cadr #LST)))
(cddddr #LST)))))

(if(setq INPUT(#TRIM(cadr #LST)))


(progn
(setq NUMBER INPUT)
(if(and(eq KEY"=")(= INFUNC BKTCNT 0))
(PRINT# NUMBER))
(DISP#))
(setq #LST'(=))));If the answer is too big, exit routine.

(if INPUT(cadr #LST)));Return answer.

;The meat.
;Gets a number and an operation from user.
;Does error checking on input.
;Accepts input from calculator buttons or keyboard.
(defun GET#( / INPUT TEMP LST1 NUMBER ENT)
(setq INPUT T NUMBER" ")
(while INPUT
(setq INPUT(grread))
(cond
((or

;Calculator button pressed?


(and(eq(car INPUT)3)
;Make LST1 with distances from picked picked point to each button.
;If closest is within button radius, take it.
(setq INPUT(cadr INPUT)
LST1(mapcar'(lambda(TEMP)(distance INPUT TEMP))PTLST)
INPUT(list 3 INPUT)
TEMP(apply'min LST1))
(< TEMP(* SCALE 0.054545));within button radius?
;Set KEY to string value of button pressed.
;Because PTLST is ordered, LST1 is also ordered.
; There can be only one distance within button radius, it's unique.
; So, (length(member ... )) tells me which button was pressed.
(setq KEY(cdr(assoc 1(entget(ssname CALCSS(1-(length(member TEMP
LST1))))))))
(null(setq LST1 nil))
(if(eq KEY"%%128")
(setq KEY"P")
T))

;Keyboard input?
(and(eq(car INPUT)2)
(member(cadr INPUT);Recognised key pressed?
(list 48 49 50 51 52 53 54 55 56 57 46 94 91 93
123 125 40 41 47 42 45 43 61 80 112 8 32 13))
(setq KEY(chr
(cadr

;Set KEY to character entered, translating


; a few interchangeable char's (ie. =,CR,spc).

(cond
((assoc(cadr INPUT)
'((91 123)(93 125)(40 123)(41 125)(112 80)(13 61)(32 61))))
(INPUT)))))))

;At this point, kybd & button input are the same.
;KEY holds a single character string.
(cond

;Backspace?
((eq KEY"\010")
(_BKSPC))

;Blank the circle around button pressed.


;Since this returns nil, cond continues to test the rest.
((redraw(setq ENT(ssname CALCSS(+ 29(length(member(ascii KEY)
'(47 125 123 94 42 57 56 55 45 54 53 52 43 51 50 49 61 46 80 48)
)))))2))

;Digit or decimal pt?


((or(and(>(ascii KEY)47)(<(ascii KEY)58))(eq KEY"."))
(_DIGIT))

;+ - * / ^ } =
((member(ascii KEY)(list 43 45 42 47 94 125 61))
(_+-*/^}=))

;Left bracket?
((eq KEY"{")
(_{))

;Plus/Minus.
((eq KEY"P")(_+/-)))

(if KEY(redraw ENT 4)))

;Point picked?
((eq(car INPUT)3)
(_POINT))

;Screen menu pick?


((and(eq(car INPUT)4)MNULST
(setq TEMP(nth(cadr INPUT)MNULST))(/=(ascii TEMP)32));Function picked?
(_MENU))

;Esc key.
((equal INPUT(list 2 27))
(LSPHLP"C:\\REL\\LSP\\CALC.LSP"15);Display first 15 lines of this file.
(setq CNT -1)
(foreach STR MNULST
(grtext(setq CNT(1+ CNT))(cond(STR)(" "))))
(prompt"\n"))));End of while loop.

(if KEY
(progn
(prompt
(cond
((eq KEY"expt")"^")
((or(/= KEY"=")(= BKTCNT INFUNC 0))KEY)
("}")))
(list(read KEY)(atof NUMBER)))));Return operator and number.

;Functions used by GET#.

(defun _BKSPC()
(setq KEY nil);Flag down redraw on button, there isn't one for bkspc.
(if(eq NUMBER" ")
(BEEP)
(progn
(entmod(subst(cons 1"")(cons 1(substr NUMBER(strlen NUMBER)))
(entget(ssname CALCSS(+(strlen NUMBER)19)))))
(prompt"\010 \010")
(if(eq(setq NUMBER(substr NUMBER 1(1-(strlen NUMBER))))"")
(setq NUMBER" ")))))

(defun _DIGIT()
(if(and(eq KEY".");Decimal pt?
(setq CNT 0)

;Repeat returns T if a decimal pt is already in NUMBER.


(repeat(strlen NUMBER)
(if(eq(substr NUMBER(setq CNT(1+ CNT))1)".")
(setq CNT(1- CNT)))))
(BEEP)
(if(<(strlen NUMBER)10)
(if(eq(setq LAST#(prompt KEY)NUMBER(strcat NUMBER KEY))
(strcat" "KEY))
(DISP#)
(entmod(subst(cons 1 KEY)(cons 1"");Display digit.
(entget(ssname CALCSS(+(strlen NUMBER)19))))))
(BEEP))))

(defun _+-*/^}=()
(setq INPUT nil);A valid operation will exit GET#.
(cond
((eq KEY"^")
(setq KEY"expt"))
((eq NUMBER" ")
(cond
((and(eq KEY"=")(null #LST))
(setq KEY nil));Return a null calculation & exit C:CALC.
(LAST#
(setq NUMBER LAST#)
(PRINT# NUMBER))
((setq INPUT T)

;Grab NUMBER from last answer.

(BEEP))))))

(defun _{()
(if(eq NUMBER" ")
(progn
(prompt"{")
(setq BKTCNT(1+ BKTCNT)
NUMBER(strcat"{"(itoa BKTCNT)))
(DISP#)
(redraw ENT 4)
(setq LAST# nil NUMBER(CALC)BKTCNT(1- BKTCNT))
(if NUMBER
(progn(setq NUMBER LAST#)
(if(eq KEY"=")(setq INPUT nil)));If bracket was closed with
(setq NUMBER" ")))

;= instead of }, exit GET#.

(BEEP)))

(defun _+/-()
(if(eq NUMBER" ")
(if LAST#
(setq NUMBER LAST#))
(repeat(-(strlen NUMBER)(if(eq(ascii NUMBER)32)1 0))
(prompt"\010")))
(setq NUMBER(strcat(if(eq(ascii NUMBER)32)"-"" ")(substr NUMBER 2))
LAST# nil)
(entmod(subst(cons 1(substr NUMBER 1 1))

(cons 1(if(eq(ascii NUMBER)32)"-"" "))


(entget(ssname CALCSS 20))))
(PRINT# NUMBER)
(prompt" \010"))

(defun _POINT()
(cond
;Display picked?
((and(>=(caadr INPUT)(-(car INSPT)(* SCALE 0.2545)))
(<=(caadr INPUT)(+(car INSPT)(* SCALE 0.2545)))
(>=(cadadr INPUT)(+(cadr INSPT)(* SCALE 0.4364)))
(<=(cadadr INPUT)(+(cadr INSPT)(* SCALE 0.56565)))
(or(/= NUMBER" ")LAST#))
(_STORE))

;Memory number or number in dwg picked?


((and
(setq TEMP(ssget(cadr INPUT)))
(eq(cdr(assoc 0(setq TEMP(entget(ssname TEMP 0)))))"TEXT")
(not(zerop(setq TEMP(atof(cdr(assoc 1 TEMP)))))))
(if(eq NUMBER" ")
(if(setq TEMP(#TRIM TEMP))
(progn(setq NUMBER TEMP)
(PRINT# NUMBER)
(DISP#)))
(BEEP)))))

(defun _STORE()
;Copy current display into a piece of text.
(if(and
(progn(initget"P")
(setq TEMP(getpoint(list(car INSPT)(+(cadr INSPT)(* SCALE 0.501)))
" Permanent or <insertion pt>: ")))
(if(eq TEMP"P")
(setq MODE nil
TEMP(getpoint(list(car INSPT)(+(cadr INSPT)(* SCALE 0.501)))
"\n Insertion pt: "))
(setq MODE T)))
(progn
(command "" ".COPY"(ssname CALCSS 29)""
(cdr(assoc 11(entget(ssname CALCSS 29))))TEMP".SELECT")
(entmod(subst
(cons 1(if(/= NUMBER" ")NUMBER LAST#))
(cons 1(substr NUMBER 10 1))
(entget(entlast))))
(if MODE
(ssadd(entlast)CALCSS))))
(prompt"\n"))

(defun _MENU()
(setq INFUNC(1+ INFUNC))
(if(eq NUMBER" ")

(if(and(setq TEMP((eval(read(strcat"_"TEMP)))));Eval function.


(cond((eq(type TEMP)'STR));Distance/angle/area return a string.
((setq TEMP(#TRIM TEMP)))));User defined functions return a number.
(progn
(setq NUMBER TEMP)
(if(eq KEY"=")
(setq INPUT nil))
(DISP#)))
(BEEP))
(setq INFUNC(1- INFUNC))
(command)
(if(not(member(getvar"UCSNAME")'("$CALC$" nil)))
(command".UCS""R""$CALC$"))
(command".SELECT"))

;My standard system variable routine.


;Uses SYSLST to store values.
(defun SYSVAR(SYM MODE)
(cond
(MODE
(if(assoc SYM SYSLST)
(if(null(cdr(assoc SYM SYSLST)))
(setq SYSLST(subst(cons SYM(getvar SYM))(cons SYM nil)SYSLST)))
(setq SYSLST(cons(cons SYM(getvar SYM))SYSLST)))
(setvar SYM MODE))

(SYM
(if(and(setq MODE(assoc SYM SYSLST))(cdr MODE))
(progn(setvar SYM(cdr MODE))
(setq SYSLST(subst(cons SYM nil)MODE SYSLST)))))
(T(foreach MODE SYSLST
(if(cdr MODE)(setvar(car MODE)(cdr MODE))))
(setq SYSLST nil))))

;Insert block definition for calculator.


(if(null(tblsearch"BLOCK""CALC"))
(progn
(SYSVAR"CMDECHO"0)
(command)
(command".INSERT""C:\\ACAD\\CALC"(getvar"VIEWCTR")
(strcat"1.0E-"(itoa(getvar"LUPREC")))""0);Insert as small as possible.
(entdel(entlast))
(SYSVAR nil nil)))

;New error handler.


(defun *CALC*(STR)
(command)
(if(tblsearch"UCS""$CALC$")
(command".UCS""D""$CALC$"))
;Erase calculator stuff.
(SYSVAR"HIGHLIGHT"0)
(command".ERASE"CALCSS"")

;Redraw entities hidden by calculator.


(SYSVAR"HIGHLIGHT"1)
(command".SELECT""C"
(list(-(car INSPT)(* SCALE 0.309125))(-(cadr INSPT)(* SCALE 0.38175)))
(list(+(car INSPT)(* SCALE 0.309125))(+(cadr INSPT)(* SCALE 0.61825)))
"")
(setq *ERROR* OLDERR SS nil)
(SYSVAR nil nil)
(grtext)
(prompt(if(eq STR"quit / exit abort")
" Done."
(strcat"\n oops ... ERROR: "STR)))
(princ))

(defun PRINT#(STR)
(prompt(substr STR(if(eq(ascii STR)32)2 1))))

(defun BEEP( / FH)


(setq FH(open"CON""w"))
(princ"\007"FH)
(setq FH(close FH))
(if DEBUG(exit)))

;Displays NUMBER on calculator.


(defun DISP#( / CNT ENT)
(setq CNT 0 LAST# NUMBER)

(while(and(< CNT 11)


(apply
'(lambda(ENT STR)
(if(or(/=(cdr(assoc 1 ENT))"")(/= STR""))
(entmod(subst(cons 1 STR)(assoc 1 ENT)ENT))))
(list(entget(ssname CALCSS(+ CNT 20)))
(substr NUMBER(setq CNT(1+ CNT))1)))))
(if(eq LAST#" ")(setq LAST# nil)))

;Trims trailing zeros and possibly trailing decimal pt.


;Tests size of number for out of bounds.
(defun #TRIM(STR / CNT)
(if(>(abs STR)999999999.0)
(progn(BEEP)
(prompt"\n ERROR: Number too large."))
(progn
(setq STR(rtos STR 2 8))
(if(/=(ascii STR)45);Not negative?
(setq STR(strcat" "STR)))
(setq CNT(1+(strlen STR)))
(if(> CNT 11)(setq CNT 11))
(if(eq(substr STR
(progn(while(eq(substr STR(setq CNT(1- CNT))1)"0"))CNT)
1)
".")
(setq CNT(1- CNT)))

(substr STR 1 CNT))))

;Help screen.
;Pass it file name and # of lines to read.
(defun LSPHLP( STR CNT / FH)
(if(setq FH(open STR"r"))
(progn
(textscr)
(princ"\n")
(while(> CNT 0)
(repeat(min 24 CNT)
(princ(strcat(substr(read-line FH)2)"\n")))
(princ" Hit any key to continue:")
(if(eq(car(grread))6)
(grread));Polite thing to do, in case calling program uses grread.
(setq CNT(- CNT 24)))
(close FH))
(prompt(strcat"\n Help file "STR" not found."))))

; The following routines are the user defined ones.


; The rules for adding your own functions are as follows:
;
; - Defun your function with the name you include in MNULST and
; put an underscore before it.
; - User defined functions must return a number or nil.
; - Use (CALC) to get numbers from a user. This will use the

; calculator for input and lets a user perform calculations, or


; even another user defined function, to supply the needed number.
; - If an ACAD command is used, use (command) first to cancel the
; outstanding select cmd (which is restarted by GET# after exit).
; - Return degrees, not radians, for angles.
;
; I would like to build a more complete library of functions. If you
; make any you find useful, maybe others would too. Please pass your
; code along to me and I'll see about including it with calc.lsp.
; If I need to, I can add functions to scroll or nest MNULST.

(defun _DIST( / TEMP)


(if(and(setq TEMP(getdist"\n Distance: "))
(setq TEMP(#TRIM TEMP)))
(PRINT# TEMP))
TEMP)

(defun _ANGLE( / TEMP)


(if(and(setq TEMP(getangle"\n Angle: "))
(setq TEMP(#TRIM(*(/ TEMP pi)180))))
(PRINT# TEMP))
TEMP)

;I got lucky, acad's area cmd turned out to be easily (?) driven with Lisp.
(defun _AREA( / PT1 MODE)

(command)
(SYSVAR"CMDECHO"1)
(command".AREA")
(while(and(or MODE(null PT1))
(progn(initget"Entity Add Subtract")(setq PT1(getpoint))))
(cond
((listp PT1)
(while(progn(command PT1)(setq PT1(getpoint))))
(command""))
((eq PT1"Entity")
(while(setq PT1(entsel))
(command PT1))
(command""))
((eq PT1"Add")
(setq MODE T)
(command"Add"))
((eq PT1"Subtract")
(setq MODE T)
(command"Subtract")))
(setq PT1 T))
(SYSVAR"CMDECHO"0)
(command"")
(if(setq MODE(#TRIM(getvar"AREA")))
(PRINT# MODE))
MODE)

(defun RAD2DEG(ANG)
(*(/ ANG pi)180))
(defun DEG2RAD(ANG)
(*(/ ANG 180)pi))

(defun _SIN( / TEMP)


(prompt"Sin{")
(if(setq TEMP(CALC))
(sin(DEG2RAD TEMP))))

(defun _COS( / TEMP)


(prompt"Cos{")
(if(setq TEMP(CALC))
(cos(DEG2RAD TEMP))))

(defun _TAN( / TEMP)


(prompt"Tan{")
(if(setq TEMP(CALC))
(/(sin(DEG2RAD TEMP))(cos(DEG2RAD TEMP)))))

(defun _ASIN( / TEMP)


(prompt"ArcSin{")
(if(setq TEMP(CALC))
(RAD2DEG(atan(/ TEMP(sqrt(abs(- 1(* TEMP TEMP)))))))))

(defun _ACOS( / TEMP)

(prompt"ArcCos{")
(if(setq TEMP(CALC))
(RAD2DEG(atan(/(sqrt(abs(- 1(* TEMP TEMP))))TEMP)))))

(defun _ATAN( / TEMP)


(prompt"ArcTan{")
(if(setq TEMP(CALC))
(RAD2DEG(atan TEMP))))

(princ)

Das könnte Ihnen auch gefallen