;;  MirrorAcrossObject.lsp [command name = MAO]

;;  To Mirror selected object(s) across any suitable object as mirror axis.
;;  Axis object can be anything with directionality [see selection filtering],
;;    whether straight-linear or not.  [If axis object was also among objects
;;    selected to be Mirrored, removes it.]
;;  For each object to be Mirrored, finds point on axis object closest to
;;    middle of Mirrored object's bounding box; uses direction of axis
;;    object at that location to establish Mirror line.
;;  [Goes without saying, perhaps, but:]  Where axis object is not straight,
;;    and middles of Mirrored objects are not aligned on a common axis
;;    perpendicular to axis object, relationship between Mirrored objects
;;    will be different in Mirrored positions, e.g. [obvious example] objects
;;    Mirrored from outside a Circle/Arc/Ellipse to inside will be closer
;;    together on inboard side.
;;  To find closest point for Mirrored object(s) beyond end of axis object,
;;    does not virtually extend axis object, because it can't determine local
;;    direction of axis object there.  Uses end of axis object.  For axis objects
;;    that are straight at closest end [except in the case of a Polyline with a
;;    zero-length segment at that end], this will make no difference, but for
;;    curved axis objects, location of Mirrored object(s) will be different
;;    from what it/they would be if axis object extended farther.
;;  Asks whether to delete source objects once for entire selection.  Unlike
;;    native Mirror command, remembers that choice and offers as default
;;    on subsequent use [Mirror always offers No as default].
;;  Kent Cooper, 16 May 2014

(vl-load-com)
(defun C:MAO ; = Mirror Across Object
  (/ *error* svnames svvals ss mentsel ment etype inc ent mid mentnea)

  (defun *error* (errmsg)
    (if (not (wcmatch errmsg "Function cancelled,quit / exit abort,console break"))
      (princ (strcat "\nError: " errmsg))
    ); end if
    (command "_.undo" "_end")
    (mapcar 'setvar svnames svvals)
    (princ)
  ); end defun - *error*

  (prompt "\nTo Mirror object(s) Across another Object,")
  (setq
    svnames '(osmode blipmode cmdecho)
    svvals (mapcar 'getvar svnames)
    ss (ssget ":L"); select object(s) to be Mirrored
  ); setq
  (if ss ; object(s) selected
    (progn ; then
      (if ; select axis object
        (and
          (not ; T when (while) below is satisfied
            (while
              (not
                (and
                  (not ; T when (while) below is satisfied
                    (while
                      (and
                        (not (setq mentsel (entsel "\nSelect axis Object to Mirror things Across <exit>:")))
                        (= (getvar 'errno) 7)
                      ); and
                      (prompt "\nNothing selected -- try again.")
                    ); while
                  ); not
                  (if mentsel
                    (and
                      (setq ment (car mentsel) etype (cdr (assoc 0 (entget ment))))
                      (wcmatch etype "*LINE,ARC,CIRCLE,ELLIPSE,RAY")
                      (wcmatch etype "~MLINE")
                    ); and
                    T ; else - Enter/space for (entsel) above
                  ); if
                ); and
              ); not
              (prompt "\nInvalid object type for axis.")
            ); while
          ); not
          ment ; something selected - lets Enter/space end routine
        ); and
        (progn ; then -- valid axis object; proceed
          (ssdel ment ss); in case axis object was in Mirrored-objects selection, remove it
          (redraw ment 3); highlight
          (initget "Yes No")
          (setq
            *MAOdelopt ; global variable for default on subsequent use
            (cond
              ( (getkword ; nil on Enter
                  (strcat
                    "\nDelete source object(s)? [Yes/No] <"
                    (if *MAOdelopt (substr *MAOdelopt 1 1) "N"); No default on first use
                    ">: "
                  ); strcat
                ); getkword
              ); User-input condition
              (*MAOdelopt); Enter on subsequent use
              ("No"); Enter on first use
            ); cond & *MAOdelopt
          ); setq
          (mapcar 'setvar svnames '(0 0 0))
          (command "_.undo" "_begin")
          (repeat (setq inc (sslength ss))
            (setq ent (ssname ss (setq inc (1- inc))))
            (vla-getboundingbox (vlax-ename->vla-object ent) 'minpt 'maxpt)
            (setq
              mid (mapcar '/ (mapcar '+ (vlax-safearray->list minpt) (vlax-safearray->list maxpt)) '(2 2 2))
              mentnea (vlax-curve-getClosestPointTo ment mid)
            ); setq
            (command "_.mirror" ent ""
              mentnea ; first point of mirror axis
              (polar ; point in tangent direction at nearest point on ment
                mentnea
                (angle '(0 0 0) (vlax-curve-getFirstDeriv ment (vlax-curve-getParamAtPoint ment mentnea)))
                1
              ); polar
              *MAOdelopt ; delete source object Y/N
            ); command
          ); repeat
          (redraw ment 4); un-highlight
          (command "_.undo" "_end")
          (mapcar 'setvar svnames svvals); reset
        ); progn -- then
        (prompt "\nNo axis object selected."); else
      ); if [ment exists]
    ); progn -- then [outer]
    (prompt "\nNothing selected to Mirror."); else
  ); if [ss exists]
  (princ)
); defun -- C:MAO

(prompt "\nType MAO to Mirror object(s) Across another Object.")