;;; ---------------------------------------------------------------------------
;;; QuickSpan.lsp
;;; code compiled by YZ August 2026
;;; ---------------------------------------------------------------------------
;;; Rapid aligned dimensioning at a consistent, automatic offset.
;;;
;;; Ordinary DIMALIGNED makes you place the dimension line by eye, so a sheet
;;; full of dimensions ends up with a sheet full of slightly different offsets.
;;; These two commands compute the offset instead: the dimension line is placed
;;; perpendicular to the measured run, one DIMTXT (current text height) clear
;;; of it. Every dimension placed this way sits at an identical stand-off.
;;;
;;; COMMANDS
;;;   SPANLINE  - one click: pick a LINE, it is dimensioned end to end
;;;   SPANPICK  - two clicks: pick any two points to dimension between
;;;
;;; Both are annotation commands that modify the drawing, so both open an undo
;;; group - a single U removes the dimension.
;;; ---------------------------------------------------------------------------

;; ---------------------------------------------------------------------------
;; QuickSpan:Draw
;; ---------------------------------------------------------------------------
;; Places one aligned dimension between pt1 and pt2.
;;
;; The dimension line position is derived rather than picked: travel from the
;; second point at 90 degrees to the run (angle + pi/2) by DIMTXT. Because the
;; offset is measured from the run itself, this works at any rotation.
;;
;; "_non" suppresses running object snaps so the dimension attaches to the
;; exact coordinates supplied. "_m" opens the multiline text override and the
;; following "" accepts it unchanged, which keeps the true measured value -
;; the dimension stays associative and honest.
;; ---------------------------------------------------------------------------
(defun QuickSpan:Draw ( pt1 pt2 / offset pt3 )
    (setq offset (getvar "DIMTXT")
          pt3    (polar pt2 (+ (angle pt1 pt2) (/ pi 2.0)) offset)
    )
    (command "_.dimaligned" "_non" pt1 "_non" pt2 "_m" "" "_non" pt3)
    (princ)
)

;; ---------------------------------------------------------------------------
;; QuickSpan:Restore  /  QuickSpan:Error
;; ---------------------------------------------------------------------------
;; Shared cleanup for both commands. Declared at file scope so the two command
;; definitions below stay short, but the system variable values themselves live
;; in each command's own local list, so the two can never tread on each other.
;; ---------------------------------------------------------------------------
(defun QuickSpan:Restore ( vars vals )
    (mapcar 'setvar vars vals)
    (if (= 8 (logand 8 (getvar "UNDOCTL")))
        (command "_.UNDO" "_End")
        (vl-catch-all-apply '(lambda ( ) (*pop-error-mode*)) '())
    )
    (princ)
)

;; ---------------------------------------------------------------------------
;; c:SPANLINE  -  dimension an existing line, end to end, in one pick
;; ---------------------------------------------------------------------------
(defun c:SPANLINE ( / *error* vars vals sel data pt1 pt2 )

    (setq vars '("CMDECHO")
          vals (mapcar 'getvar vars)
    )

    (defun *error* ( msg )
        (QuickSpan:Restore vars vals)
        (if (and msg (not (wcmatch (strcase msg t) "*break*,*cancel*,*exit*")))
            (princ (strcat "\n** SPANLINE error: " msg " **"))
        )
        (princ)
    )

    (setvar "CMDECHO" 0)
    ;; AutoCAD 2015 and later refuse (command) inside an *error* handler
    ;; unless the routine says up front that it will use one. Restore does,
    ;; to close this undo group. The declaring call is absent on older
    ;; releases, so it is wrapped rather than tested for.
    (vl-catch-all-apply '(lambda ( ) (*push-error-using-command*)) '())
    (command "_.UNDO" "_Begin")

    ;; -----------------------------------------------------------------------
    ;; "_:E:S" is single-pick mode restricted to one object, filtered to LINE.
    ;; DXF 10 and 11 are the line's start and end point.
    ;; -----------------------------------------------------------------------
    (princ "\nSelect a line to dimension: ")
    (if (setq sel (ssget "_:E:S" '((0 . "LINE"))))
        (progn
            (setq data (entget (ssname sel 0))
                  pt1  (cdr (assoc 10 data))
                  pt2  (cdr (assoc 11 data))
            )
            (QuickSpan:Draw pt1 pt2)
        )
        (princ "\n*Cancelled* - no line selected.")
    )

    (QuickSpan:Restore vars vals)
    (princ)
)

;; ---------------------------------------------------------------------------
;; c:SPANPICK  -  dimension between any two picked points
;; ---------------------------------------------------------------------------
(defun c:SPANPICK ( / *error* vars vals pt1 pt2 )

    (setq vars '("CMDECHO")
          vals (mapcar 'getvar vars)
    )

    (defun *error* ( msg )
        (QuickSpan:Restore vars vals)
        (if (and msg (not (wcmatch (strcase msg t) "*break*,*cancel*,*exit*")))
            (princ (strcat "\n** SPANPICK error: " msg " **"))
        )
        (princ)
    )

    (setvar "CMDECHO" 0)
    ;; AutoCAD 2015 and later refuse (command) inside an *error* handler
    ;; unless the routine says up front that it will use one. Restore does,
    ;; to close this undo group. The declaring call is absent on older
    ;; releases, so it is wrapped rather than tested for.
    (vl-catch-all-apply '(lambda ( ) (*push-error-using-command*)) '())
    (command "_.UNDO" "_Begin")

    ;; -----------------------------------------------------------------------
    ;; Both picks are validated before anything is drawn. The original passed
    ;; a nil point into polar if the user pressed Escape at the second prompt,
    ;; which raised a bad argument type error.
    ;; -----------------------------------------------------------------------
    (if (and (setq pt1 (getpoint "\nFirst dimension point: "))
             (setq pt2 (getpoint pt1 "\nSecond dimension point: "))
        )
        (QuickSpan:Draw pt1 pt2)
        (princ "\n*Cancelled* - two points are required.")
    )

    (QuickSpan:Restore vars vals)
    (princ)
)

(princ)
