Update xref insertion to 0,0,0

Update xref insertion to 0,0,0

bsanada
Advocate Advocate
5,291 Views
56 Replies
Message 1 of 57

Update xref insertion to 0,0,0

bsanada
Advocate
Advocate

We have a master drawing that pulls in several hundred xrefs and most xrefs do not have a 0,0,0 insertion point.  I am looking for a lisp routine that would create a copy of each xref drawing with an updated insertion point allowing each drawing to be inserted using 0,0,0.  There may be a couple ways to skin this cat but my intitial thought would be to bind the xrefs and then wblock them back out.

0 Likes
Accepted solutions (1)
5,292 Views
56 Replies
Replies (56)
Message 41 of 57

marko_ribar
Advisor
Advisor

@bsanada 

I found this topic interested... I've changed @Moshe-A code slightly - added selecting for Xrefs and exporting master DWG along with Xrefs in that empty choosen folder for which you navigate by using Lee Mac browseforfolder sub...

Please, can you test it now with this code posted here in code tags...

 

; Save xref back to disk to 0,0,0
(defun c:xrefout ( / *error* LM:blockname ssxrf LM:browseforfolder is_acetutil_modula_exist objects_counter get_last_ent ; local functions
                     adoc ss ii blocks ucsname folder ctr pbar_ctr AcDbEntity xname rr rx bt ex cm                       ; local variables
                     AcDbBlkTblRec xrefname newxrefname s ename lst masterdwg )                                          ; local variables

  (or (not (vl-catch-all-error-p (vl-catch-all-apply (function vlax-get-acad-object) nil))) (vl-load-com))

  (defun *error* ( msg )
    (if (and msg (not (wcmatch (strcase msg t) "*break*,*cancel*,*exit*")))
      (princ (strcat "\nError: " msg))
    )
    (if (= *isAcetUtil* :vlax-true)
      (acet-ui-progress-done); close progress meter
    )
    (if ucsname
      (progn
        (if command-s
          (command-s "_.ucs" "_restore" ucsname)
          (vl-cmdf "_.ucs" "_restore" ucsname)
        )
        (if command-s
          (command-s "_.ucs" "_delete"  ucsname)
          (vl-cmdf "_.ucs" "_delete"  ucsname)
        )
      )
    )
    (if bt
      (setvar (quote bindtype) bt)
    )
    (if ex
      (setvar (quote expert) ex)
    )
    (if cm
      (setvar (quote cmdecho) cm)
    )
    (if (and blocks (= (type blocks) (quote vla-object)))
      (vlax-release-object blocks)
    )
    (if (and adoc (= (type adoc) (quote vla-object)))
      (progn
        (vla-endundomark adoc)
        (vlax-release-object adoc)
      )
    )
    (princ "\nDone.")
    (princ)
  ); *error*

  ;; Block Name  -  Lee Mac
  ;; Returns the true (effective) name of a supplied block reference

  (defun LM:blockname ( obj )
    (if (vlax-property-available-p obj (quote effectivename))
      (defun LM:blockname ( obj ) (vla-get-effectivename obj))
      (defun LM:blockname ( obj ) (vla-get-name obj))
    )
    (LM:blockname obj)
  )

  ; https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/selecting-xref-in-ssget/m-p/11865848#M446035
  (defun ssxrf ( / blocks blknam blktbl ss sx )
    (setq blocks (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))))
    (if (setq ss (ssget (list (cons 0 "INSERT"))))
      (progn
        (setq sx (ssadd)) ; initialize xref selection set
        (foreach itm (vl-remove-if (function listp) (mapcar (function cadr) (ssnamex ss)))  ; create list of all entities from selection set & cycle through
          (setq blknam (LM:blockname (vlax-ename->vla-object itm))) ; get block name
          (setq blktbl (vla-item blocks blknam))                    ; get block table
          (if (= (vla-get-isxref blktbl) :vlax-true)                ; if xref
            (ssadd itm sx) ; add to xref selection set
          ) ; if
        ) ; foreach
        (if (zerop (sslength sx)) (setq sx nil)) ; nullify when there are no selections
      ) ; progn
    ) ; if inserts
    (vlax-release-object blocks)
    sx ; return xref selection set
  ) ; defun

  ;; Browse for Folder  -  Lee Mac
  ;; Displays a dialog prompting the user to select a folder.
  ;; msg - [str] message to display at top of dialog
  ;; dir - [str] [optional] root directory (or nil)
  ;; bit - [int] bit-coded flag specifying dialog display settings
  ;; Returns: [str] Selected folder filepath, else nil.

  (defun LM:browseforfolder ( msg dir bit / err fld pth shl slf )
    (setq err
      (vl-catch-all-apply
        (function
          (lambda ( / app hwd )
            (if 
              (setq app (vlax-get-acad-object)
                    shl (vla-getinterfaceobject app "shell.application")
                    hwd (vl-catch-all-apply 'vla-get-hwnd (list app))
                    fld (vlax-invoke-method shl 'browseforfolder (if (vl-catch-all-error-p hwd) 0 hwd) msg bit dir)
              )
              (setq slf (vlax-get-property fld 'self)
                    pth (vlax-get-property slf 'path)
                    pth (vl-string-right-trim "\\" (vl-string-translate "/" "\\" pth))
              )
            )
          )
        )
      )
    )
    (if slf (vlax-release-object slf))
    (if fld (vlax-release-object fld))
    (if shl (vlax-release-object shl))
    (if (vl-catch-all-error-p err)
      (prompt (vl-catch-all-error-message err))
      pth
    )
  )

  ; check if aceutil.arx is loaded?
  (defun is_acetutil_modula_exist ()
    (if (not (vl-position "acetutil.arx" (arx)))
      (progn
        (setq *isAcetUtil* :vlax-false)
        (repeat 3
          (vlr-beep-reaction)
        )
        (prompt "\nacetutil.arx modula is not loaded.")
      )
      (setq *isAcetUtil* :vlax-true)
    )
  ); is_acetutil_modula_exist

  ; return cumulate number of objects in xrefs
  (defun objects_counter ( / counter AcDbBlkTblRec )
    (setq counter -1)
    (vlax-for AcDbBlkTblRec blocks
      (if
        (and
          (= (vla-get-isxref AcDbBlkTblRec) :vlax-true)
          ; make sure it is attached
          (ssget "_X" (list (cons 0 "INSERT") (cons 2 (vla-get-name AcDbBlkTblRec))))
        )
        ;(setq counter (+ counter (vla-get-count AcDbBlkTblRec)))
        (setq counter (1+ counter))
      )  
    ); vlax-for
    counter
  ); objects_counter

  ; make sure last entity is founded
  ; even if drawing is empty
  (defun get_last_entity ( / el ) ; rr - lexical global
    (setq el (entlast))
    (if (and el (entnext el))
      (while (setq el (entnext el))
        (setq rr el)
      )
      (if el
        (setq rr el)
        (setq rr (entmakex (list (cons 0 "POINT") (list 10 0.0 0.0 0.0))))
      )
    )
    rr
  ); get_last_entity 

  ; Here start c:xrefout
  (setq adoc (vla-get-activedocument (vlax-get-acad-object)))
  (vla-startundomark adoc)
  (setq blocks (vla-get-blocks adoc))
  (setq cm (getvar (quote cmdecho)))
  (setvar (quote cmdecho) 0)
  (setq ex (getvar (quote expert)))
  (setvar (quote expert) 5)
  (setq bt (getvar (quote bindtype)))
  (setvar (quote bindtype) 1)
  (command "_.ucs" "_save" "$xrefout")
  (setq ucsname (getvar (quote ucsname)))
  (if (= 0 (getvar (quote worlducs)))
    (command "_.ucs" "_world")
  )
  (if (setq folder (LM:browseforfolder "Select a folder" nil 0))
    (progn
      (is_acetutil_modula_exist) 
      (setq ctr (objects_counter))
      ; init progress meter
      (if (= *isAcetUtil* :vlax-true)
        (progn
          ; progress bar init requires bigger value 
          (acet-ui-progress-init "Processing" ctr)
          (setq pbar_ctr -1) ; progress bar counter
        ); progn
      ); if
      (prompt "\nSelect Xref entities to which you want to apply changing base point to WCS origin 0,0,0 point...")
      (setq ss (ssxrf))
      (repeat (setq ii (sslength ss))
        (setq AcDbEntity (vlax-ename->vla-object (ssname ss (setq ii (1- ii)))))
        (if
          (and
             (= (vla-get-objectname AcDbEntity) "AcDbBlockReference")
             (not (wcmatch (setq xname (vla-get-name AcDbEntity)) "`*U*"))  ; not anonymous block
             (not (vl-position xname lst))                                  ; not processed yet
             (setq AcDbBlkTblRec (vla-item blocks xname))                   ; get BTR
             (= (vla-get-isxref AcDbBlkTblRec) :vlax-true)                  ; is it xref?
             (setq xrefname (vla-get-path AcDbBlkTblRec))
             (setq newxrefname (strcat folder "\\" (vl-filename-base xrefname) ".dwg"))
          )
          (progn
            (vlax-release-object AcDbBlkTblRec)
            (setq AcDbBlkTblRec nil)
            ; update progress meter 
            (if (= *isAcetUtil* :vlax-true)
              (progn 
                (setq pbar_ctr (1+ pbar_ctr)) ; inc progress bar
                (acet-ui-progress-safe pbar_ctr)
              ); progn
            ); if
            (command "_.undo" "_begin")
            (setq s (ssadd) ename (get_last_entity))
            ; make sure the xref will be saved at X=1,Y=1,Z=1,R=0
            ;|
            (vla-put-XScaleFactor AcDbEntity 1.0)
            (vla-put-YScaleFactor AcDbEntity 1.0)
            (vla-put-ZScaleFactor AcDbEntity 1.0)
            (vla-put-rotation     AcDbEntity 0.0)
            |;
            (command "_.xref" "_bind" xname)
            (command "_.explode" (vlax-vla-object->ename AcDbEntity))
            ; make sure all objects selected
            (command "_.layer" "_thaw" "*" "_on" "*" "_unlock" "*" "") 
            ; collecting all objects
            (while (setq ename (entnext ename))
              (ssadd ename s)  
            ); while
            (if (and rr (= (cdr (assoc 0 (setq rx (entget rr)))) "POINT") (= (list 10 0.0 0.0 0.0) (assoc 10 rx)) (not (vlax-erased-p rr)))
              (entdel rr)
            ); get rid of last entity that is WCS 0,0,0 point
            ; write out wblock\xref
            (command "_.wblock" (strcat folder "\\" xname) "" "0,0,0" "_si" s)
            (command "_.undo" "_end")
            (command "_.u") ; restore actions
            (setq lst (cons xname lst))
          ); then progn
          (if
            (and
              AcDbBlkTblRec
              (= (type AcDbBlkTblRec) (quote vla-object))
            )
            (progn
              (vlax-release-object AcDbBlkTblRec)
              (setq AcDbBlkTblRec nil)
            ); progn
          ); else if
        ); if
      ); vlax-for

      (vlax-for AcDbEntity (vla-get-modelspace adoc)
        (if
          (and
            (= (vla-get-objectname AcDbEntity) "AcDbBlockReference")
            (not (wcmatch (setq xname (vla-get-name AcDbEntity)) "`*U*"))  ; not anonymous block
            (vl-position xname lst)                                        ; already processed
            (setq AcDbBlkTblRec (vla-item blocks xname))                   ; get BTR
            (= (vla-get-isxref AcDbBlkTblRec) :vlax-true)                  ; is it xref?
          )
          (progn
            (vla-put-insertionpoint AcDbEntity (vlax-3d-point (list 0.0 0.0 0.0)))
            (vla-put-rotation AcDbEntity 0.0)
            (vla-put-XScaleFactor AcDbEntity 1.0)
            (vla-put-YScaleFactor AcDbEntity 1.0)
            (vla-put-ZScaleFactor AcDbEntity 1.0)
          )
        )
        (vlax-release-object AcDbEntity)
      ); vlax-for

      (setq masterdwg (strcat folder "\\" (getvar (quote dwgname))))
      (vla-saveas adoc masterdwg)
      (foreach xref lst
        (command "_.-xref" "_re" xref)
      )
      (if (= *isAcetUtil* :vlax-true)
        (acet-ui-progress-done); close progress meter
      )
    ); progn
  ); if
  (*error* nil)  
); c:xrefout

 

HTH.

M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
Message 42 of 57

cadffm
Consultant
Consultant

Works well in my test,

 

now we need

correction of: rescale insert (if not scaleXYZ=1)

correction of: (setq masterdwg (strcat folder "\\" (getvar (quote dwgname)) ".dwg"))

an option to overwrite the original (instead to select one target folder)

an option to re-path xrefs to the new folder (if one new target folder selected)

😄

 

This is so nice, I think I will use it again in the future. Thanks everyone.
Again, not a single line of code written, it comes ready-made.

 
 

 

 

Sebastian

0 Likes
Message 43 of 57

marko_ribar
Advisor
Advisor

Corrected that with master dwgname... (".dwg")

I think that since OP works with 3DSOLID entities in Xrefs, he should put them in scale XYZ=1 if he wants to explode binded xrefs, or if not already set uncomment those lines with (vla-put-Xscalefactor AcDbEntity 1.0)... (Y, Z)...

This with repathing is not neccessity as OP works with relative paths... This means that new master DWG saved in new folder along with exported Xrefs can find Xrefs as it belong to the same folder along with Xrefs...

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes
Message 44 of 57

cadffm
Consultant
Consultant

Hi,

 

ununiformly scale xrefs would force a problem for 3Dsolids, of course, but not uniformly scaled als also files without 3Dsolids.

My point was to scale the xref inserts in the master file (xrefout scaled the content in external files, but it does not re-scale the inserts)

 

So, if someone like to edit for personal use:

cadffm_0-1738689976542.png

 

Absolut path - failed

Relative path - Gambling, depending the target fodler level/ position

_

 

THANK YOU

 

Sebastian

0 Likes
Message 45 of 57

bsanada
Advocate
Advocate

Moshe,

There must be some kind of variable or other routine that is causing issues with running this in my AutoCAD.  I ran this in AutoCAD Architecture (which doesn't have all of my customization) and the routing runs fine.

 

Thanks for all your help, this is fantastic!

Brad

0 Likes
Message 46 of 57

bsanada
Advocate
Advocate

CADFM and Marko Ribar - I expect your additions may be useful for others but won't be required for our specific situation.  We should not have any of our xrefs that would not be a 1:1 scale.

 

Thanks-Brad

0 Likes
Message 47 of 57

Moshe-A
Mentor
Mentor

@bsanada hi,

 

"; error: bad argument type: fixnump: 0" is thrown when a function that expect to get a number gets other data type (like string or list)

i again reviewed the code very closely and i did not find the spot where this error might arise. given it is working under AutoCAD Architecture

i think (but cannot not sure) your AutoCAD 2024 Vanila has some lisp program\function that override a built-in AutoLISP function that xrefout is using. so to eliminate such an error temporary unload any customization like acad.lsp, acaddoc.lsp appload and restart AutoCAD than run xrefout.

 

Moshe

 

0 Likes
Message 48 of 57

bsanada
Advocate
Advocate

I do not have any issues using one of the other AutoCAD verticals to run the lisp.  It will be interesting to see if it runs after the migration to 2025.

0 Likes
Message 49 of 57

Moshe-A
Mentor
Mentor

@bsanada ,

 

R2025 was released with a series bug with (entlast) \ (entnext) functions and i still do no know if this bug is fixed?!

>> see this thread from aug 2024 << 

xrefout use these functions 😫

 

moshe

 

0 Likes
Message 50 of 57

marko_ribar
Advisor
Advisor

@Moshe-A 

I have installed AutoCAD 2025 Vanilla and I just tested an issue you gave link... It seems that all works as expected :

(progn (setq e (entlast)) (while (setq e (entnext e)))) => returns nil if there is last entity present in drawing, and if there aren't any of them this snippet returns :

; error: bad argument type: lentityp nil ...

This is all normal behaviour as (entnext nil) is error...

To see how can you get last entity even if empty DWG, look how I handeled sub (get_last_entity) posted in my version of @Moshe-A LISP...

I don't know where infinity loop occur - in what version - release, but I for sure can say that I have no problems with my installed AutoCAD 2025 Vanilla...

Regards, M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes
Message 51 of 57

Moshe-A
Mentor
Mentor

@marko_ribar ,

 

don't know, i relay on @BeeKeeCZ post 🤔

 

did you test this?

 

Necessary minimum steps to reproduce:
(could be done in any ACAD version)
• attach a XREF to Model
• add a block to Layout (without atts)
• draw an object to Model (probably must be the last step)
• save, close, reopen
• execute (entnext (entlast)), possibly also (entget (entlast)) or (entget (entnext (entlast)))

0 Likes
Message 52 of 57

marko_ribar
Advisor
Advisor

I followed what you described and here is my return results with AutoCAD 2025 Vanilla...

 

Command: (entnext (entlast))
nil

Command: (entget (entlast))
((-1 . <Entity name: 21f543cb080>) (0 . "LINE") (330 . <Entity name: 21f543c51f0>) (5 . "260") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "0") (100 . "AcDbLine") (10 0.0 0.0 0.0) (11 50.0 0.0 0.0) (210 0.0 0.0 1.0))

Command: (entget (entnext (entlast)))
; error: bad argument type: lentityp nil

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes
Message 53 of 57

marko_ribar
Advisor
Advisor

BTW.

I removed (strcase xname) and just used xname in my posted version of your code, as I don't think that this is correct - better is that in variable 'lst' are stored real upper+lower letters, so that when used (command "_.-xref" "_re" xname) , AutoCAD finds exactly equal xname for reloading in master DWG values that are stored in 'lst'...

Please, correct me if I made some blunders here... I really don't know why you used (strcase) function...

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes
Message 54 of 57

Moshe-A
Mentor
Mentor

@marko_ribar  hi,

 

1. i did not use xref reload, why it was needed? @bsanada said i have a master drawing with all the xrefs already attached and positioned

    all i want is to save them back exactly as they appear in master drawing - correct me if i'm wrong?!

2. i believe AutoCAD do convert string from lowercase to uppercase when it deals with strings including commands and command options.

3. from the same reason i store strings as uppercase cause if a comparation is needed, it won't fail 🤣

 

Moshe

 

0 Likes
Message 55 of 57

marko_ribar
Advisor
Advisor

@Moshe-A 

1. I used xref reload, not to force user to reopen master dwg... That is how @cadffm pointed in his attached picture...

2. You may be right that ACAD converts strings in uppercases when dealing with strings, but we don't use commands the way ACAD won't recognize lower, or upper case strings...

3. We don't have anywhere in code comparison of strings involved except in error handler where we compare possible error message with lowercases (wcmatch (strcase msg t) "*break*,*cancel*,*exit*,*quit*")... Further more, when I avoided using (strcase xname) and used just xname, generated list of processed xrefs matches exactly adequate names, so comparison when used later stored list - variable 'lst' should be exact and if user wants to use my or @cadffm version which reloads xrefs in master dwg saved in the same folder as xrefs that are processed, everything should work fine, just as should...

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes
Message 56 of 57

Moshe-A
Mentor
Mentor

@marko_ribar wrote:

@Moshe-A 

1. I used xref reload, not to force user to reopen master dwg... That is how @cadffm pointed in his attached picture...

well, i do not like to override the origin file that's why i recommended @bsanada to pick another folder for the save so if he

discover some mistake in master drawing he could fix it.

 

2. You may be right that ACAD converts strings in uppercases when dealing with strings, but we don't use commands the way ACAD won't recognize lower, or upper case strings...

3. We don't have anywhere in code comparison of strings involved except in error handler where we compare possible error message with

yes i do have...the lst is maintained for preventing from saving duplicate xrefs.

 

(if (and
         (eq (vla-get-objectname AcDbEntity) "AcDbBlockReference")
         (not (wcmatch (setq xname (vla-get-name AcDbEntity)) "`*U*"))  ; not anonymous block
         (null (member (strcase xname) lst))                            ; not processed yet
         (setq AcDbBlkTblRec (vla-item blocks xname))                   ; get BTR
         (eq (vla-get-isXref AcDbBlkTblRec) :vlax-true)                 ; is it xref?
         (setq xrefname (vla-get-path AcDbBlkTblRec))
         (setq newxrefname (strcat folder "\\" (vl-filename-base xrefname) ".dwg"))
        )
     (progn

 

lowercases (wcmatch (strcase msg t) "*break*,*cancel*,*exit*,*quit*")... Further more, when I avoided using (strcase xname) and used just xname, generated list of processed xrefs matches exactly adequate names, so comparison when used later stored list - variable 'lst' should be exact and if user wants to use my or @cadffm version which reloads xrefs in master dwg saved in the same folder as xrefs that are processed, everything should work fine, just as should...


 

0 Likes
Message 57 of 57

marko_ribar
Advisor
Advisor

well, i do not like to override the origin file that's why i recommended @bsanada to pick another folder for the save so if he

discover some mistake in master drawing he could fix it.

This is why I posted another variation of code - it should be different than yours...

 

yes i do have...the lst is maintained for preventing from saving duplicate xrefs.

but that's not calling for ACAD evaluation through command expressions, therefore there is no need for changing already exact upper+lower lettered strings...

(null (member (strcase xname) lst))                            ; not processed yet

Already explained... IMHO, better is :

(not (vl-position xname lst))

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes