;;; AutoCATS 리습 자료실
;; 명령: BBOX2D
;; 기능: 선·원·호·직선 경량 폴리선의 WCS XY 외곽 사각형을 그립니다.
;; 출처: https://www.autocats.co.kr/lisp/bounding-box
;; 버전: 1.0 (2026-10-23)
;; 라이선스: 자유 사용·수정, 재배포 시 출처(autocats.co.kr) 표기
;; 안전: 잠긴 레이어·외부참조 보호, 변경은 UNDO 그룹, 명령별 범위는 상세 가이드 참고

(vl-load-com)

;; Exact WCS XY extents for lines, planar circles/arcs and straight LWPolyline segments.
(defun autocats-bbox2d-points (ent / data kind center radius angles start sweep angle points)
  (setq data (entget ent) kind (cdr (assoc 0 data)))
  (cond
    ((= kind "LINE") (list (cdr (assoc 10 data)) (cdr (assoc 11 data))))
    ((not (equal '(0.0 0.0 1.0) (cdr (assoc 210 data)) 0.00000001)) nil)
    ((member kind '("CIRCLE" "ARC"))
      (setq center (cdr (assoc 10 data)) radius (cdr (assoc 40 data))
        angles (list 0.0 (/ pi 2.0) pi (* pi 1.5)))
      (if (= kind "ARC")
        (setq start (cdr (assoc 50 data)) sweep (rem (+ (* 2.0 pi) (- (cdr (assoc 51 data)) start)) (* 2.0 pi))
          angles (append (list start (cdr (assoc 51 data)))
            (vl-remove-if '(lambda (angle) (> (rem (+ (* 2.0 pi) (- angle start)) (* 2.0 pi)) sweep)) angles))))
      (foreach angle angles
        (setq points (cons (list (+ (car center) (* radius (cos angle)))
              (+ (cadr center) (* radius (sin angle))) (caddr center)) points))) points)
    ((and (= kind "LWPOLYLINE")
        (not (vl-some '(lambda (pair) (and (= 42 (car pair)) (/= 0.0 (cdr pair)))) data)))
      (foreach pair data (if (= 10 (car pair)) (setq points (cons (list (cadr pair) (caddr pair) 0.0) points)))) points)))

(defun autocats-bbox2d-bounds (ss / index points minpt maxpt point unsupported)
  (setq index 0)
  (repeat (sslength ss)
    (setq points (autocats-bbox2d-points (ssname ss index)))
    (if (null points) (setq unsupported T))
    (foreach point points
      (setq minpt (if minpt (mapcar 'min minpt point) point)
        maxpt (if maxpt (mapcar 'max maxpt point) point)))
    (setq index (1+ index)))
  (if (and minpt (not unsupported)) (list minpt maxpt)))

(defun autocats-bbox2d-draw (bounds / a b row)
  (setq row (tblsearch "LAYER" (getvar "CLAYER")))
  (if (and bounds row (> (cdr (assoc 62 row)) 0) (= 0 (logand 5 (cdr (assoc 70 row))))
      (< (caar bounds) (caadr bounds)) (< (cadar bounds) (cadadr bounds)))
    (progn
      (setq a (car bounds) b (cadr bounds))
      (entmakex (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity")
          (cons 8 (getvar "CLAYER")) '(100 . "AcDbPolyline") '(90 . 4) '(70 . 1) '(38 . 0.0)
          (cons 10 (list (car a) (cadr a))) (cons 10 (list (car b) (cadr a)))
          (cons 10 (list (car b) (cadr b))) (cons 10 (list (car a) (cadr b))))))))

(defun c:BBOX2D (/ *error* undo-open bounds selection)
  (defun *error* (msg)
    (if undo-open (progn (command-s "_.UNDO" "_End") (setq undo-open nil)))
    (if (and msg (not (wcmatch (strcase msg) "*CANCEL*,*QUIT*,*EXIT*,*취소*")))
      (princ (strcat "\n[BBOX2D] 중단: " msg)))
    (princ))

  (setq selection (ssget))
  (if selection (progn
      (setq bounds (autocats-bbox2d-bounds selection))
      (command "_.UNDO" "_Begin")
      (setq undo-open T)
      (if (autocats-bbox2d-draw bounds) (princ "\n외곽선 작성 완료") (princ "\n경계 또는 현재 레이어를 확인하세요."))
      (command "_.UNDO" "_End") (setq undo-open nil)))

  (princ))

(princ "\n[AutoCATS] BBOX2D 로드 완료")
(princ)
