;;; ---------------------------------------------------------------------
;;;  MNMXT-bgol.lsp
;;;  Command:  MNMX
;;;
;;;  PURPOSE
;;;  Given a polyline boundary, scans all TEXT entities located within
;;;  that boundary, identifies the minimum and maximum numeric values
;;;  among them, and marks those specific text entities with a fitted
;;;  circle -- filling the gap left by the native FIND command, which
;;;  can only locate a known value, not determine which of many is
;;;  smallest or largest.
;;;
;;;  USAGE
;;;    1. Load this file (APPLOAD or drag-drop onto the drawing window).
;;;    2. Type   MNMX   at the command line and press Enter.
;;;    3. Select a closed polyline boundary.
;;;    4. All numeric TEXT entities whose insertion point falls inside
;;;       that boundary are scanned. The minimum-value text is circled
;;;       in blue and the maximum-value text is circled in red, both on
;;;       layer "BGOL-MNMX". The values and count are reported at the
;;;       command line.
;;;
;;;  NOTES
;;;    - This is an original, independently written implementation.
;;;      It is INSPIRED by the general idea of boundary-scoped min/max
;;;      text-value finder utilities common in survey/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)

(defun BGOL:MNMX-EnsureLayer (lname / )
  (if (not (tblsearch "LAYER" lname))
    (entmake (list '(0 . "LAYER")
                   '(100 . "AcDbSymbolTableRecord")
                   '(100 . "AcDbLayerTableRecord")
                   (cons 2 lname)
                   '(70 . 0)
                   '(62 . 7)          ; white/black (default draw colour; circles colored per-entity)
                   '(6 . "Continuous")))
  )
)

;; Standard point-in-polygon (crossing number) test using a vertex list
(defun BGOL:MNMX-PointInPoly (pt verts / n i j inside xi yi xj yj x y)
  (setq x (car pt) y (cadr pt))
  (setq n (length verts))
  (setq inside nil)
  (setq i 0)
  (while (< i n)
    (setq j (if (= i 0) (1- n) (1- i)))
    (setq xi (car (nth i verts)) yi (cadr (nth i verts)))
    (setq xj (car (nth j verts)) yj (cadr (nth j verts)))
    (if (and (/= (>= yi y) (>= yj y))
             (< x (+ xi (* (/ (- xj xi) (- yj yi)) (- y yi)))))
      (setq inside (not inside))
    )
    (setq i (1+ i))
  )
  inside
)

(defun BGOL:MNMX-MarkCircle (obj colorcode lname / minpt maxpt radius center)
  (vla-getboundingbox obj 'minpt 'maxpt)
  (setq minpt (vlax-safearray->list minpt))
  (setq maxpt (vlax-safearray->list maxpt))
  (setq radius (* (/ (distance minpt maxpt) 2.0) 1.2))
  (setq center (list (/ (+ (car minpt) (car maxpt)) 2.0)
                      (/ (+ (cadr minpt) (cadr maxpt)) 2.0)
                      (/ (+ (caddr minpt) (caddr maxpt)) 2.0)))
  (entmake (list '(0 . "CIRCLE")
                 (cons 8 lname)
                 (cons 10 center)
                 (cons 40 radius)
                 (cons 62 colorcode)))
)

(defun C:MNMX ( / lname bnd bndobj verts allText ss n i ent obj pt val
                  minval maxval minent maxent)
  (setq lname "BGOL-MNMX")
  (BGOL:MNMX-EnsureLayer lname)
  (setq bnd (car (entsel "\nSelect polyline boundary: ")))
  (if (and bnd (member (cdr (assoc 0 (entget bnd))) '("LWPOLYLINE" "POLYLINE")))
    (progn
      (setq bndobj (vlax-ename->vla-object bnd))
      ;; extract vertex list (2D, WCS) for point-in-polygon test
      (setq verts (vlax-safearray->list
                    (vlax-variant-value (vla-get-Coordinates bndobj))))
      (setq verts (BGOL:MNMX-PairUp verts))
      (setq allText (ssget "_X" '((0 . "TEXT"))))
      (setq minval nil maxval nil minent nil maxent nil n 0)
      (if allText
        (progn
          (setq i 0)
          (repeat (sslength allText)
            (setq ent (ssname allText i))
            (setq obj (vlax-ename->vla-object ent))
            (setq pt (vlax-get obj 'InsertionPoint))
            (if (BGOL:MNMX-PointInPoly pt verts)
              (progn
                (setq val (atof (vlax-get obj 'TextString)))
                (setq n (1+ n))
                (if (or (null minval) (< val minval)) (setq minval val minent ent))
                (if (or (null maxval) (> val maxval)) (setq maxval val maxent ent))
              )
            )
            (setq i (1+ i))
          )
          (if (and minent maxent)
            (progn
              (BGOL:MNMX-MarkCircle (vlax-ename->vla-object minent) 5 lname) ; blue = minimum
              (BGOL:MNMX-MarkCircle (vlax-ename->vla-object maxent) 1 lname) ; red = maximum
              (princ (strcat "\nMNMX: " (itoa n) " text entit"
                             (if (= n 1) "y" "ies") " found inside boundary."))
              (princ (strcat "\nMinimum value: " (rtos minval 2 3) " (circled blue)"))
              (princ (strcat "\nMaximum value: " (rtos maxval 2 3) " (circled red)"))
            )
            (princ "\nNo numeric text found inside the selected boundary.")
          )
        )
        (princ "\nNo text entities found in the drawing.")
      )
    )
    (princ "\nNo valid polyline boundary selected.")
  )
  (princ)
)

;; helper: pair up a flat (x1 y1 x2 y2 ...) list into ((x1 y1) (x2 y2) ...)
(defun BGOL:MNMX-PairUp (flatlist / result i n)
  (setq result '())
  (setq n (length flatlist))
  (setq i 0)
  (while (< i n)
    (setq result (append result (list (list (nth i flatlist) (nth (1+ i) flatlist)))))
    (setq i (+ i 2))
  )
  result
)

(princ "\nMNMXT-bgol.lsp loaded. Type MNMX to find min/max text value inside a polyline boundary.")
(princ "\n  -- Free & open-source, courtesy of BGol Community (https://bgol.in/) --")
(princ)
