;;; ---------------------------------------------------------------------
;;;  VTXINS-bgol.lsp
;;;  Command:  VINS
;;;
;;;  PURPOSE
;;;  Scans one or more selected alignment polylines for nearby TEXT
;;;  entities on a designated marker layer ("Extra" by default) and
;;;  inserts a new vertex into each polyline at the point closest to
;;;  each matching text label, batching what would otherwise be many
;;;  manual polyline-edit operations into a single command run.
;;;
;;;  USAGE
;;;    1. Load this file (APPLOAD or drag-drop onto the drawing window).
;;;    2. Type   VINS   at the command line and press Enter.
;;;    3. Select all polylines into which vertices should be inserted.
;;;    4. The routine gathers every TEXT entity on layer "Extra", and
;;;       for each one that lies within tolerance of a selected
;;;       polyline, inserts a new vertex there (LWPOLYLINE rebuilt via
;;;       entmod). Matched insertion points are also flagged on layer
;;;       "BGOL-VINS" for a visual audit trail.
;;;
;;;  NOTES
;;;    - Proximity tolerance defaults to 0.5 drawing units; edit
;;;      *bgol-vins-tol* below to change it.
;;;    - This is an original, independently written implementation.
;;;      It is INSPIRED by the general idea of annotation-driven,
;;;      batch vertex-insertion utilities used to prepare alignment
;;;      polylines in survey/infrastructure 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)

(setq *bgol-vins-tol* 0.5)

(defun BGOL:VINS-EnsureLayer (lname / )
  (if (not (tblsearch "LAYER" lname))
    (entmake (list '(0 . "LAYER")
                   '(100 . "AcDbSymbolTableRecord")
                   '(100 . "AcDbLayerTableRecord")
                   (cons 2 lname)
                   '(70 . 0)
                   '(62 . 6)          ; magenta
                   '(6 . "Continuous")))
  )
)

(defun BGOL:VINS-Flag (pt lname / )
  (entmake (list '(0 . "CIRCLE")
                  '(100 . "AcDbEntity")
                  (cons 8 lname)
                  '(100 . "AcDbCircle")
                  (cons 10 pt)
                  '(40 . 0.6)))
)

;; Insert a new vertex into polyline obj at point pt (closest-on-curve),
;; between the two vertices that straddle it, by rebuilding the DXF list.
(defun BGOL:VINS-InsertVertex (ent pt / edata newdata done insAfterIdx
                                 idx onPt param tol closestParam)
  (setq edata (entget ent))
  (setq tol *bgol-vins-tol*)
  (setq onPt (vlax-curve-getClosestPointTo (vlax-ename->vla-object ent) pt))
  (if (<= (distance pt onPt) tol)
    (progn
      (setq param (vlax-curve-getParamAtPoint (vlax-ename->vla-object ent) onPt))
      (setq idx (fix param))          ; segment index the point falls on
      (setq newdata '())
      (setq insAfterIdx idx)
      (setq done nil)
      (setq idx 0)
      (foreach pair edata
        (setq newdata (append newdata (list pair)))
        (if (and (= (car pair) 10) (= idx insAfterIdx) (not done))
          (progn
            (setq newdata (append newdata (list (cons 10 onPt))))
            (setq done T)
          )
        )
        (if (= (car pair) 10) (setq idx (1+ idx)))
      )
      (if done
        (progn
          (entmod newdata)
          (entupd ent)
          T
        )
        nil
      )
    )
    nil
  )
)

(defun C:VINS ( / ss n i ent txtSet tcnt j txtEnt txtData txtPt lname
                  matchCount plCount)
  (setq lname "BGOL-VINS")
  (BGOL:VINS-EnsureLayer lname)

  (setq ss (ssget '((0 . "LWPOLYLINE"))))
  (if ss
    (progn
      (setq txtSet (ssget "_X" '((0 . "TEXT") (8 . "Extra"))))
      (if txtSet
        (progn
          (setq matchCount 0)
          (setq plCount (sslength ss))
          (setq n (sslength ss))
          (setq i 0)
          (while (< i n)
            (setq ent (ssname ss i))
            (setq tcnt (sslength txtSet))
            (setq j 0)
            (while (< j tcnt)
              (setq txtEnt (ssname txtSet j))
              (setq txtData (entget txtEnt))
              (setq txtPt (cdr (assoc 10 txtData)))
              (if (BGOL:VINS-InsertVertex ent txtPt)
                (progn
                  (BGOL:VINS-Flag txtPt lname)
                  (setq matchCount (1+ matchCount))
                )
              )
              (setq j (1+ j))
            )
            (setq i (1+ i))
          )
          (princ (strcat "\nInserted " (itoa matchCount)
                          " vertex/vertices across " (itoa plCount)
                          " polyline(s), flagged on layer " lname "."))
        )
        (princ "\nNo TEXT entities found on layer \"Extra\". Nothing inserted.")
      )
    )
    (princ "\nNo polylines selected.")
  )
  (princ)
)

(princ "\nVTXINS-bgol.lsp loaded. Type VINS to batch-insert vertices at Extra-layer text locations.")
(princ "\n  -- Free & open-source, courtesy of BGol Community (https://bgol.in/) --")
(princ)
