HPGL output utility

#1
Getting HPGL output is something that’s bitten me on more than one occasion. Even finding a public domain driver can be an issue and finding something that will work with legacy hardware can be a mission impossible.
Due to this I dusted off some old code I wrote and adapted it into the quick and dirty utility below. I hope it helps a few other people.

;; (load "hpglout")(C:hpglout)
; Cycle through the drawing & save details of lines,Arcs & Circles to a HPGL format file
; Program works on a per layer baises
; Cirles still seem iffy
;; For those times when you dont have a driver or are dealing with legacy hardware
;; You may then need to send the file direct to the serial port, which will look something like these DOS commands:
;; MODE COM1 BAUD=9600 PARITY=n DATA=8
;; COPY yourfile.hpg COM1
; Tim Gathercole - 19 Oct 2019
;; GLOBALS: dScale term f5
(defun C:hpglout ( / dwgnm ss_drw) ; ll ur
(setq fln (getvar "dwgname"))
(setq hpfl (substr fln 1 (- (strlen fln) 4) ) ) ;

(setq FnPath "C:\\files\\") ; dir to save file into
(setq spno "SP7")
(setq dScale 40)
(setq term ";")
(setq f5 (open (strcat FnPath hpfl ".hpg") "w"))
(setq ll (getvar "EXTMIN")
ur (getvar "EXTMAX") ) ; Many hardware systems require a 0,0 start point. It's simplest to move bottom Left to 0,0 if necessary. Just add a move after this code.

(writeData "CREASE")
(writeData "CUT") ; No error checking yet - so there better be something on the layer
; Tail end of the HPGL file
(write-line (strcat "SP0" term) f5) ; END / Put the pen away
(close f5)
); C:hpglout
; <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
(defun writeData (laynam / ss tl n ent itm obj q xs ys xe ye)
; Save drawing details to HPGL file
(princ "\n laynam = ") (princ laynam);Erase_DV
(if (= laynam "CUT")
(setq spno "SP5") ; Cut Knife - Tool numbers will vary
(setq spno "SP7") ; ELSE Crease Wheel
) ; if
(write-line (strcat spno term) f5)
(setq ss (ssget "_X" (list (cons 8 laynam)))) ; Get all Entities
(if (null ss)
(princ "\nNo Entitys found on Layer.\n")
(progn ; ELSE
(setq n (1- (sslength ss)))
(while (>= n 0)
(setq ent (entget (setq itm (ssname ss n)))
obj (cdr (assoc 0 ent))
q (cond
((= obj "LINE") (progn
(write-line (strcat "PU" (rtos (* (cadr (assoc 10 ent)) dScale) 2 4) "," (rtos (* (caddr (assoc 10 ent)) dScale) 2 4) term) f5) ; Move to Start
(write-line (strcat "PD" (rtos (* (cadr (assoc 11 ent)) dScale) 2 4) "," (rtos (* (caddr (assoc 11 ent)) dScale) 2 4) term) f5) ; Draw to End
)) ; LINE
((= obj "ARC")
(arcout ent)
) ; ARC
((= obj "CIRCLE")
; PA uses Center / CI Radius (includes PD command)
(write-line (strcat "PU" (rtos (* (cadr (assoc 10 ent)) dScale) 2 4) "," (rtos (* (caddr (assoc 10 ent)) dScale) 2 4) term) f5) ; Move to Start / Center
(write-line (strcat "CI" (rtos (* (/ (car (assoc 40 ent)) 4) dScale) 2 4) term) f5) ; Still wondering why its /4 not 2
) ; OTHER
) ; cond
n (1- n)))
));progn IF
); writeData
; <><><><><><><><><><><><><><><><><><><><><>
; GLOBAL dScale term
(defun arcout (ent / aendx aendy Ang aStrX aStrY cen CenX CenY sAng eAng rad)
(setq cen (cdr (assoc 10 ent)) ; Centre XYZ
rad (cdr (assoc 40 ent)) ; Radius
sAng (cdr (assoc 50 ent))
eAng (cdr (assoc 51 ent))
) ; Start angle
(setq aStrX (* (+ (* rad (cos sAng)) (car cen)) dScale)
aStrY (* (+ (* rad (sin sAng)) (cadr cen)) dScale)
aEndX (* (+ (* rad (cos eAng)) (car cen)) dScale)
aEndY (* (+ (* rad (sin eAng)) (cadr cen)) dScale)
CenX (* (car cen) dScale)
CenY (* (cadr cen) dScale) ) ; end points not necessary but you never know when a version will need them
(if (> sAng eAng)
(progn
(setq eAng (+ eAng 360))
(setq Ang (dtr (- eAng (Rad2Deg sAng)) ) ) ; progs like Radians but HPGL & Humans like degrees
) ; else
(progn
(setq Ang (- eAng sAng) )
) )

(write-line (strcat "PU" (rtos aStrX 2 4) "," (rtos aStrY 2 4) term) f5) ; Move to Start / Center
(write-line (strcat "PDAA" (rtos CenX 2 4) "," (rtos CenY 2 4) "," (rtos (Rad2Deg Ang) 2 4) term) f5) ; Control points and end

) ; arcout
; <><><><><><><><><><><><><><><><><><><><><>
; <><><><><><><><><><><><><><><><><><><><><>
; Convert Degrees to Radians
(defun dtr (x)
;define degrees to radians function
(* pi (/ x 180.0))
;divide the angle by 180 then
;multiply the result by the constant PI
) ;end of function
; <><><><><><><><><><><><><><><><><><><><><>
; Convert Degrees to Radians
(DEFUN Rad2Deg (a / ret)
(setq a (read (rtos a 2 5)) ; 5 decimal places
ret (* a 57.2957795147))
ret ; returned value
) ; defun
; <><><><><><><><><><><><><><><><><><><><><>