Processing multiple DXF's to determine maximum height and width

Processing multiple DXF's to determine maximum height and width

SORONW
Advocate Advocate
5,578 Views
21 Replies
Message 1 of 22

Processing multiple DXF's to determine maximum height and width

SORONW
Advocate
Advocate

I have roughly ~5000 .dxf files  containing fabrication patterns. Is there a way I could process all of them to obtain the maximum height and width of the lengths of the sides of a bounding box around each file's geometry and export that to some text or spreadsheet file?

 

I quickly put together the following code to handle a single drawing, but I'm struggling with the batch processing part.

 

(defun C:DIMEXPORT ( doc / ss fn x y)

	(setq ss (LM:ssboundingbox (ssget "X")))
	(setq fn (strcat (getvar "dwgprefix")(getvar "dwgname")))
	(setq x (- (car (cadr ss)) (car (car ss))))
	(setq y (- (cadr (cadr ss)) (cadr (car ss))))
	(if (setq file (open "c:\\temp\\DIMEXPORT" "W"))
		(progn 
			(write-line (strcat "File Name: " fn) file)
			(write-line (strcat "Horizontal Measurement: " X) file)
			(write-line (strcat "Vertical Measurement: " Y) file)
		);progn
	)
)

;; Selection Set Bounding Box  -  Lee Mac
;; Returns a list of the lower-left and upper-right WCS coordinates of a
;; rectangular frame bounding all objects in a supplied selection set.
;; sel - [sel] Selection set for which to return bounding box

(defun LM:ssboundingbox ( sel / idx llp ls1 ls2 obj urp )
    (repeat (setq idx (sslength sel))
        (setq obj (vlax-ename->vla-object (ssname sel (setq idx (1- idx)))))
        (if (and (vlax-method-applicable-p obj 'getboundingbox)
                 (not (vl-catch-all-error-p (vl-catch-all-apply 'vla-getboundingbox (list obj 'llp 'urp))))
            )
            (setq ls1 (cons (vlax-safearray->list llp) ls1)
                  ls2 (cons (vlax-safearray->list urp) ls2)
            )
        )
    )
    (if (and ls1 ls2)
        (mapcar '(lambda ( a b ) (apply 'mapcar (cons a b))) '(min max) (list ls1 ls2))
    )
)

 

 Any advice or expertise to share?

 

EDIT: I should mention that solutions that don't require admin access would be preferable if possible.😀

0 Likes
Accepted solutions (3)
5,579 Views
21 Replies
Replies (21)
Message 2 of 22

doaiena
Collaborator
Collaborator

Here is a quick test i wrote. I'm sure you can adapt it to your exact needs.

 

 

 

(defun c:test ( / files acadApp acadDocs acadDoc newDoc selections sel ents ss fn x y result)

(setq files (list "C:\\file1.dxf" "C:\\file2.dxf" "C:\\file3.dxf" "C:\\file4.dxf" "C:\\file5.dxf"))

(setq acadApp (vlax-get-acad-object))
(setq acadDocs (vlax-get-property acadApp 'Documents))
(setq acadDoc (vla-get-activedocument acadApp))

(foreach file files
(setq newDoc (vlax-invoke acadDocs 'open file))
(setq selections (vlax-get newDoc 'selectionSets))
(setq sel (vla-Add selections "newSS"))
(vlax-invoke sel 'select '5)
(setq ss (ssadd))
(vlax-for item sel (ssadd (vlax-vla-object->ename item) ss))

(setq ss (LM:ssboundingbox ss))
(setq fn (strcat (vla-get-path newDoc) "\\" (vla-get-name newDoc)))
(setq x (- (car (cadr ss)) (car (car ss))))
(setq y (- (cadr (cadr ss)) (cadr (car ss))))
(setq result (cons (list fn x y) result))

(vlax-invoke newDoc 'close ':vlax-false)
);foreach

(princ result)
(princ)
);defun

 

 

Message 3 of 22

CodeDing
Mentor
Mentor

@SORONW ,

 

...a bounding box around each file's geometry...


Do you mean like the EXTMIN and EXTMAX variables? Because those are stored and easily located in a DXF file.

Let me know.

 

Best,

~DD

0 Likes
Message 4 of 22

Sea-Haven
Mentor
Mentor
Accepted solution

Like Codeding extmin extmax, maybe use aeccoreconsole which will process the dwgs. It does not open the dwg, the other way is to use OBJDBX see Lee-mac.com

 

A good explanation how to do 

https://through-the-interface.typepad.com/through_the_interface/2012/02/the-autocad-2013-core-consol...

 

I would write a file say dwgname extmin extmax, you just need to make a text file say 1 line save it, use the append open "A" not read or write to add each dwg detail.

0 Likes
Message 5 of 22

SORONW
Advocate
Advocate

@CodeDing Yes! Thats exactly what I'm looking. I had no idea those variables even existed. learn some thing new everyday.

0 Likes
Message 6 of 22

CodeDing
Mentor
Mentor
Accepted solution

@SORONW ,

 

Thank you for clarifying. You can save this code to a lisp file (I have also attached it) and give it a go.

It will ask you to select the folder with the DXF files.. Then it will create a csv in the location of your current dwg.

 

(defun c:DXFLIMITS ( / dir dxfFiles extents fPath)
  (vl-load-com)
  (if (and (setq dir (LM:browseforfolder "Select DXF Folder" nil 0))
           (setq dxfFiles (vl-directory-files dir "*.dxf" 1))
           (setq dxfFiles (mapcar '(lambda (d) (strcat dir "\\" d)) dxfFiles))
      );and
    (progn
      (prompt (strcat "\n" (itoa (length dxfFiles)) " DXF file(s) found."))
      (prompt "\nCalculating Extents...")
      (setq extents (GetDXFExtents dxfFiles))
      (prompt "\nCreating csv...")
      (setq fPath (strcat (getvar 'DWGPREFIX) "DXF Geometry Extents.csv"))
      (CreateExtentsCSV extents fPath)
      (prompt "\nComplete.")
      (prompt (strcat "\nCSV can be found here:\n" fPath))
    );progn
  );if
  (princ)
);defun

(defun CreateExtentsCSV (ex fp / f)
  (setq f (open fp "w"))
  (write-line "File Name,Min X,Min Y,Max X,Max Y" f)
  (foreach x ex
    (write-line
      (strcat (car x) "," (caadr x) "," (cadadr x) "," (caaddr x) "," (cadadr (cdr x)))
      f
    );write-line
  );foreach
  (close f)
);defun

(defun GetDXFExtents (files / )
  (mapcar
    '(lambda (fp / f txt found eMin eMax tmp)
      (setq f (open fp "r"))
      (while (and (setq txt (strcase (read-line f)))
                  (not found)
             );and
        (if (eq "$EXTMIN" txt)
          (setq eMin (mapcar '(lambda (f) (repeat 2 (read-line f))) (list f f f)))
        );if
        (if (eq "$EXTMAX" txt)
          (setq eMax (mapcar '(lambda (f) (repeat 2 (read-line f))) (list f f f)))
        );if
        (setq found (and eMin eMax))
      );while
      (close f)
      (if found
        (list fp eMin eMax)
        (list fp
              (repeat 3 (setq tmp (cons "Not Found" tmp)))
              tmp
        );list
      );if
    );lambda
    files
  );mapcar
);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
    )
)

 

Hope it helps.

 

- - - EDIT - - -

I just updated some simple items that were missed in code. (about 15 mins after posting) Should be working fine.

 

Best,

~DD

0 Likes
Message 7 of 22

doaiena
Collaborator
Collaborator

If you don't need to filter out entities or get properties, the drawing extents will do the job, as suggested by @CodeDing . This could simplify the code a bit more.

EDIT: I posted this before seing @CodeDing 's solution, which is orders of magnitude more efficient. Still i will leave the code as it is an example of how to open/close and interact with documents.

 

(defun c:test ( / files acadApp acadDocs acadDoc newDoc ext fn result)

(setq files (list "C:\\file1.dxf" "C:\\file2.dxf" "C:\\file3.dxf" "C:\\file4.dxf" "C:\\file5.dxf"))

(setq acadApp (vlax-get-acad-object))
(setq acadDocs (vlax-get-property acadApp 'Documents))
(setq acadDoc (vla-get-activedocument acadApp))

(foreach file files
(setq newDoc (vlax-invoke acadDocs 'open file))
(setq ext (mapcar '- (vlax-invoke newDoc 'getVariable '"extmax") (vlax-invoke newDoc 'getVariable '"extmin")))
(setq fn (strcat (vla-get-path newDoc) "\\" (vla-get-name newDoc)))
(setq result (cons (list fn (car ext) (cadr ext)) result))
(vlax-invoke newDoc 'close ':vlax-false)
);foreach

(princ result)
(princ)
);defun

 

Message 8 of 22

Sea-Haven
Mentor
Mentor

Nice solution !

 

 

 

Had a quick look at Obdx at Lee-mac.com can not use as explained by Lee 

  • No access to System Variables (getvar, setvar, vla-getvariable, vla-setvariable etc)
0 Likes
Message 9 of 22

SORONW
Advocate
Advocate

@CodeDing Thank You!  That's just what I was looking for. 

 

And thanks the rest of you for the excellent reference, this should be quite useful for the future

0 Likes
Message 10 of 22

SORONW
Advocate
Advocate

Quick Followup,

 

Would you happen to know why opening the DXF and saving it again would change the value of extmax/extmin? I ran the routine twice on a small set of documents. The first time on 2 results it was well off, but after opening the file and saving again, it pulled the expected numbers. I changed nothing in the file before saving, and somehow it self corrected. The only thing I can think off would be it going from a 2013 DXF to 2018 DXF.

 

Is there some hidden math/change to extmax and extmin? The save seems to fix the values so I could put together a script to resave each file, but is there something I'm missing here?

0 Likes
Message 11 of 22

CodeDing
Mentor
Mentor

@SORONW ,

 

Whose script are you using? mine? Neither script (doaiena's or mine) saves-over the existing DXF.

When a file gets saved to DXF, the MAXIMUM precision of 15 digits is stored. This level of precision gets down into how computers operate and interpret numbers.. e.g. so if a number we see as, for example, 24.0 ...this number could ultimately be stored as 23.999999988745434. So yes, this very lengthy precision MIGHT change when an open & save occurs. You will need to execute SOME level of desired precision to probably help you get the accuracy of the numbers you are expecting.

 

My script provides the FULL precision of numbers in the csv saved. So, after the csv is created, you can use Excel formulas to round as desired (since you probably don't need 15 digit precision).

 

Does that help?

Best,

~DD

0 Likes
Message 12 of 22

SORONW
Advocate
Advocate

Sorry, I think I should have been more clear.  I used your @CodeDing  script and saw that some of the values that were pulled were too large (a difference of over ~100 when I was expecting ~8) for the geometry I knew was in the .DXF.  I opened the .dxf in a text editor, which confirmed that the values exported were at least matching what that file said, so then I opened the .dxf file in AutoCAD to make sure there wasn't any hidden geometry increasing the extmin and extmax value.

SORONW_0-1606936038994.png

SORONW_1-1606936101189.png

 

 

 

 

After I confirmed that there was nothing I could find that influenced it, I did a save-as moving it to the newest DXF format available (2018 in this case). After saving, the extmax/min changed to match the part measurement I was looking for. I changed nothing about the part other than the save. In the case shown above, when I checked the .dwg where this .dxf came from, that's where the 20.5 and 32.5 values seem to have come from. So the .dxf files geometry is right, just the values didn't update when the  .dxf was made.

 

Ultimately, my question is; what's the best way to force those extmax/min values to update? Whether it be at .dxf generation or afterward.

 

 

 

 

0 Likes
Message 13 of 22

CodeDing
Mentor
Mentor

I can not think of any reason why an Open & Save would change the EXTMIN and EXTMAX variables. Something would had to have changed to make such a drastic difference.

 

When the DXF is created, it essentially takes a snapshot of all of the relevant items in the drawing. All of them should be current at that time, then saves them to the DXF file. Then when the DXF is opened, each item is interpreted and its value is sent to each relevant location. So, if you immediately save, there is no reason for values to change, because another 'snapshot' is immediately executed.

 

Makes no sense.

0 Likes
Message 14 of 22

SORONW
Advocate
Advocate

I reproduced and attached the issue just to make sure I'm  not completely blind. Both the source .dwg and .dxf file created from it. At this point I'm thinking its definitely got to do with the .dxf generator. Something to do with inheriting those values from the larger parent drawing, which doesn't seem to track with your description of DXF generation, but at this point that seems like a good lead. 

 

 

0 Likes
Message 15 of 22

CodeDing
Mentor
Mentor

@SORONW ,

 

I see what you mean. Your dxf EXTMIN & EXTMAX do not match the entities (well.. one entity) when you open the file, then when I save (either to 2013 or 2018) the EXTMIN & EXTMAX values are corrected. 

 

How is your DXF originally created? extmin & extmax are clearly referencing the blue polyline on the "BORD" layer. What happens to this polyline before you save DXF?

0 Likes
Message 16 of 22

CodeDing
Mentor
Mentor

@SORONW ,

 

Ok so I think I have learned something here.

 

So if we look at the EXTMIN and EXTMAX documentation, we can see this snip:

Expands outward as new objects are drawn; shrinks only with ZOOM All or ZOOM Extents.

 

So what I believe is happening, is that when your DXFs were created, if items were deleted in the original document and neither Zoom call was placed before creating the DXF, then our variables will not be reflected correctly.

 

I didn't know this. So for all of your DXF documents, using the approach I provided would probably not be an effective one since we do not know if a Zoom command was placed or not before the file was created.

 

Well that stinks.

 

~DD

0 Likes
Message 17 of 22

cadffm
Consultant
Consultant

@CodeDing  Even if you don't need it: I confirm that.

 

That's right, so far your access to Extmin / Extmax was just a matter of luck ; D

 

To make the contribution meaningful, there is another addition:

[F1] >>" shrinks only with ZOOM All or ZOOM Extents"

 

it shrinks also by open the file and much more important for automation:

It also shrinks by setting the TreeDepth variable! this way you don't need a terrible Zoom action during your program or script, menumacro..

(setvar 'TREEDEPTH (getvar 'TREEDEPTH))

is enough

Sebastian

Message 18 of 22

Sea-Haven
Mentor
Mentor

My $0.05 why not add a "zoom e" as your opening the dwg, then do a extmin/max.

 

Not sure if aeccoreconsole supports Zoom E.

 

The forum is a pain can not edit post and add that 1 line to code posted.

 

 

 

0 Likes
Message 19 of 22

cadffm
Consultant
Consultant

You don't need a zoom e (yes, available in acc) 

in this case because extmin/extmax shrinks by opening the file.

Sebastian

0 Likes
Message 20 of 22

Sea-Haven
Mentor
Mentor

Thanks good to know for AECC.

0 Likes