;;; ---------------------------------------------------------------------
;;;  KEEPIN-bgol.lsp
;;;  Command:  KIN
;;;
;;;  PURPOSE
;;;  Erases every entity located OUTSIDE a selected closed polyline
;;;  boundary, keeping only what falls inside it. Useful for isolating a
;;;  specific area of interest from a larger, densely-populated drawing.
;;;
;;;  USAGE
;;;    1. Load this file (APPLOAD or drag-drop onto the drawing window).
;;;    2. Type   KIN   at the command line and press Enter.
;;;    3. Select the closed polyline that defines the boundary to keep.
;;;    4. Every other entity whose reference point falls outside that
;;;       boundary is erased. The boundary polyline itself is untouched.
;;;
;;;  NOTES
;;;    - Uses a standard ray-casting point-in-polygon test against the
;;;      boundary polyline's vertices.
;;;    - Entity "location" is approximated using its insertion point
;;;      (TEXT/INSERT), start point (LINE), or center (CIRCLE); other
;;;      entity types use the first vertex of their bounding geometry.
;;;    - This is an original, independently written implementation.
;;;      It is INSPIRED by the general idea of boundary-based selective
;;;      erase utilities common in CAD drafting workflows, but the code,
;;;      command name and file name here are new and are NOT copied
;;;      from, nor identical to, any specific third-party product.
;;;
;;;  ---------------------------------------------------------------------
;;;  Developed and shared for the CAD community by:  BGol Community
;;;  Community website:                              https://bgol.in/
;;;  License:  Open-source & free to use, modify, and share for all.
;;;            Provided "as is", without warranty of any kind.
;;;  ---------------------------------------------------------------------

(vl-load-com)

;; Ray-casting point-in-polygon test. pt = (x y), poly = list of (x y).
(defun BGOL:KIN-PtInPoly (pt poly / n i j xi yi xj yj inside px py)
  (setq px (car pt))
  (setq py (cadr pt))
  (setq n (length poly))
  (setq inside nil)
  (setq i 0)
  (setq j (1- n))
  (while (< i n)
    (setq xi (car (nth i poly)))
    (setq yi (cadr (nth i poly)))
    (setq xj (car (nth j poly)))
    (setq yj (cadr (nth j poly)))
    (if (and (/= (> yi py) (> yj py))
             (< px (+ xi (* (/ (- xj xi) (- yj yi)) (- py yi)))))
      (setq inside (not inside))
    )
    (setq j i)
    (setq i (1+ i))
  )
  inside
)

;; Get a representative reference point for an entity given its DXF data.
(defun BGOL:KIN-RefPoint (edata / etype)
  (setq etype (cdr (assoc 0 edata)))
  (cond
    ((member etype '("TEXT" "MTEXT" "INSERT" "CIRCLE" "POINT"))
     (cdr (assoc 10 edata)))
    ((= etype "LINE") (cdr (assoc 10 edata)))
    ((member etype '("LWPOLYLINE" "POLYLINE"))
     (cdr (assoc 10 edata)))
    (t (cdr (assoc 10 edata)))
  )
)

(defun BGOL:KIN-PolyVerts (ent / obj n i verts pt coords)
  (setq obj (vlax-ename->vla-object ent))
  (setq verts '())
  (if (vlax-property-available-p obj 'Coordinates)
    (progn
      (setq coords (vlax-safearray->list (vlax-variant-value (vla-get-Coordinates obj))))
      (setq i 0)
      (while (< i (length coords))
        (setq verts (append verts (list (list (nth i coords) (nth (1+ i) coords)))))
        (setq i (+ i 2))
      )
    )
  )
  verts
)

(defun C:KIN ( / boundEnt boundVerts ss n i ent edata refpt erased kept)
  (setq boundEnt (car (entsel "\nSelect boundary polygon (entities OUTSIDE it will be erased): ")))
  (if boundEnt
    (progn
      (setq boundVerts (BGOL:KIN-PolyVerts boundEnt))
      (if (>= (length boundVerts) 3)
        (progn
          (setq ss (ssget "_X"))
          (if ss
            (progn
              (setq erased 0)
              (setq kept 0)
              (setq n (sslength ss))
              (setq i 0)
              (while (< i n)
                (setq ent (ssname ss i))
                (if (/= ent boundEnt)
                  (progn
                    (setq edata (entget ent))
                    (setq refpt (BGOL:KIN-RefPoint edata))
                    (if (and refpt (not (BGOL:KIN-PtInPoly refpt boundVerts)))
                      (progn
                        (entdel ent)
                        (setq erased (1+ erased))
                      )
                      (setq kept (1+ kept))
                    )
                  )
                )
                (setq i (1+ i))
              )
              (princ (strcat "\nKIN: erased " (itoa erased)
                              " entity(ies) outside the boundary. Kept "
                              (itoa kept) "."))
            )
            (princ "\nNo entities found in drawing.")
          )
        )
        (princ "\nSelected entity is not a valid closed polyline boundary.")
      )
    )
    (princ "\nNo boundary selected.")
  )
  (princ)
)

(princ "\nKEEPIN-bgol.lsp loaded. Type KIN to erase entities outside a polyline boundary.")
(princ "\n  -- Free & open-source, courtesy of BGol Community (https://bgol.in/) --")
(princ)
