HyperPics: Beyond the UI View RSS

Taking a look at software Beyond the UI (User Interface)
Hide details



Nearly 40 AU Presentations and Over a Decade in the Making (Updated Again) 20 Nov 2017 7:08 AM (7 years ago)

AU 2004 seems like a long time ago, but it was the first year in which I presented at Autodesk University and I was a nervous wreck; still even Today I would classify myself as a nervous speaker. The perfectionist in me almost did me in at AU 2004, as some know I drove to Kinko's hours before I had to present to fix a problem with my handouts. After being quoted one price, and then being told something that was well double the quoted price reality settled in and the mistake would remain; I don't even know if anyone even saw the mistake or cared it was in there. Rather than take a taxi back, I decided to hoof it back barely making it in time to get a different change in shirt as it was more than 4+ miles back (it is hard to gauge distance in Las Vegas at times).

Based on the way I was feeling up to and after the presentation at AU 2004, I figured I would be one and done. I guess I couldn't have been further wrong, as I have had the privilege to present every year since AU 2004 and impact the way people utilize the AutoCAD program. AU 2020 marked the 17th different AU I presented at and all of those have been over consecutive years. Below is a list of the sessions I have presented over the years:

Note: Session handouts and files from AU 2004 through AU 2010 can be found on the AUGI website.

If you have any areas of the AutoCAD APIs you would like to see improved in the developer documentation, covered in a future AU session, or written about in a blog article let me know.

Want to present at the next AU? Head on over to the AU website: https://www.autodesk.com/autodesk-university/

Updated 12/3/2019: Added 2018 and 2019 sessions.

Updated 11/25/2020: Added 2020 sessions and inverted the order of latest to oldest. I have now presented 56 sessions (29 unique sessions) over 17 years.

Sincerely,
  Lee

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

AutoCAD 2018 Developer Documentation - April Updates 17 Apr 2017 6:20 AM (8 years ago)

While the AutoCAD 2018 product was released only about a month ago, the first developer documentation content update has been released.  In recent years, content updates have been released once in the Summer and Fall months.  However, we are going to try and release content more frequently this year.  With updates being more frequent, the scope of the changes made will most likely be smaller but allow us to be more reactive.  The following outlines the recent changes to the AutoCAD developer documentation:

If you find something that is missing or wrong in the documentation, let me know and I can get it added to the list of changes being worked on.

Sincerely,
  Lee

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Migrating AutoLISP Programs that Previously Used the IAcadFileDependencies and IAcadFileDependency Classes 4 Apr 2017 4:57 AM (8 years ago)

Starting with AutoCAD 2018-based products, AutoLISP programs that utilize the members defined by the IAcadFileDependencies and IAcadFileDependency classes in the AutoCAD ActiveX/COM library must be updated as a result of the classes being removed. The AutoLISP functions that are affected are:

The IAcadFileDependencies and IAcadFileDependency classes were useful in obtaining information about the files attached to or referenced by a drawing file, such as xrefs, fonts, and plot styles.  However, those classes didn't always work consistently as expected and didn't support all files that could be attached to or referenced by a drawing, such as Coordination Models, weblight IES definition files, images used to define materials for rendering and more.  Even with the short comings of the two classes and their members, they were useful.

This article doesn't focus on how to go about reintroducing all of the functionality that the two classes supported, but focuses on how to go about getting the file paths to the files attached to or referenced by a drawing.  The filename and path information associated with an attached or referenced file can be found in a few different places within the drawing database, it is a matter of knowing where to look.  Filenames/paths to a file referenced by a drawing is stored in one of the following places:

Note: The code samples in this article are provided AS-IS and may need to be modified to handle conditions that were not identified during development.

The following code sample is a utility function that splits a string into individual elements based on a specified character, and returns the individual string elements as a list.  The SPLIT function is used by several of the sample function to split the path of support folders:

;; Usage: (split (getenv "PrinterConfigDir") ";")
;; Usage: (split (getenv "PrinterStyleSheetDir") ";")
(defun split (sExpression sDelimiter / len val sList)
  ; Get the length of the first item from the expression based on delimiter position
  (setq len (vl-string-position (ascii sDelimiter) sExpression))

  ; Loop until there are no more instances of the delimiter in the expression
  (while (numberp len)
    ; Get the item from the expression
    (setq val (substr sExpression 1 len))

    ; Append the item to the list
    (setq sList (cons val sList))

    ; Get the remaining part of the expression
    (setq sExpression (substr sExpression (+ 2 len)))

    ; Get the length of the next item from the string based on delimiter position
    (setq len (vl-string-position (ascii sDelimiter) sExpression))
  )

  ; Add the last value to the list
  (if (/= sExpression nil)
    (setq sList (cons sExpression sList))
  )

  ; Reverse the elements and return the list
  (reverse sList)
)

The SPLIT function was originally mention in the article "Splitting A String Into A List of Elements Based on Delimiter".

The following is yet another utility function that was written, and it attempts at trying to resolve relative paths. The RESOLVERELATIVEPATH function accepts two string arguments, the first is a file name with a path and the second is the path to try and resolve the relative path with.  The resolve path will commonly be the location of the drawing file.

;; Returns a filename based on the resolved path
;; Usage: (ResolveRelativePath "..\\grid.dwg" (getvar "dwgprefix"))
(defun ResolveRelativePath (fileNamePath dwgPath / )
  ; Check for relative paths and attempt to resolve the path to the xref
  (cond
    ; Relative path, should resolve multiple levels of relative folders; DWG file must be saved
    ((and (= (substr fileNamePath 1 2) "..")(/= (getvar "dwgprefix") ""))
      (while (= (substr fileNamePath 1 2) "..")
        (setq fileNamePath (substr fileNamePath 4)
              dwgPath (substr dwgPath 1 (vl-string-position (ascii "\\") dwgPath 0 T))
        )
      )
      (setq fileNamePath (strcat dwgPath "\\" fileNamePath))
    )
    ; Absolute paths, resolves only the top level folder; DWG file must be saved
    ((and (= (substr fileNamePath 1 1) ".")(/= (getvar "dwgprefix") ""))
      (setq fileNamePath (substr fileNamePath 3)
            fileNamePath (strcat dwgPath "\\" fileNamePath)
      )
    )
  )
 
  ; Check to see if the fileNamePath is valid, if not then attempt to locate the file in the working support path
  (if (not (findfile fileNamePath))
    (setq fileNamePath (findfile (strcat (vl-filename-base fileNamePath) (vl-filename-extension fileNamePath))))
    (if (= (vl-filename-directory fileNamePath) "")
      (setq fileNamePath (findfile fileNamePath))
    )
  )

  ; Returns the resolved path with filename, if the path couldn't be resolved
  ; then the original fileNamePath is returned
  fileNamePath
)

Now that the two utility functions have been defined, the heart of the sample functions are used to return the filenames and paths for these types of attached or referenced files:

The functions defined by the following AutoLISP code samples are:

;; Returns a list of Xref filenames attached to the drawing
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (XrefPathsList T)
;; Usage: (XrefPathsList nil)
(defun XrefPathsList (bIncludeFullPath / ent val fullName fileName fileList)
  ; Get the first element of the Blocks symbol table
  (setq ent (tblnext "block" T))

  ; Step through the Blocks symbol table
  (while ent
    ; Check for the DXF group code 1
    (if (setq val (assoc 1 ent))
      (progn
        ; Get the xref path name from the DXF group
        (setq fullName (cdr val))

        ; Determine whether the filename is returned only or the full path
        (if bIncludeFullPath
          (progn
            ; Check for a relative path and attempt to resolve the path
            (setq fileName (ResolveRelativePath fullName (vl-string-trim "\\" (getvar "dwgprefix"))))
          )
          ; Return the filename and extension only
          (setq fileName (strcat (vl-filename-base fullName) (vl-filename-extension fullName)))
        )

        ; Append the filename to the list
        (setq fileList (cons fileName fileList))
      )
    )

    ; Return the next block in the Block symbol table
    (setq ent (tblnext "block"))
  )

  ; Return a sorted list of filenames
  (vl-sort fileList '<)
)

;; Returns a list of Raster Image filenames attached to the drawing
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (RasterImagePathsList T)
;; Usage: (RasterImagePathsList nil)
(defun RasterImagePathsList (bIncludeFullPath / imageDict cnt val fileName fullName fileList)
  ; Step through the definitions in the attached images dictionary
  (foreach val (setq imageDict (dictsearch (namedobjdict) "ACAD_IMAGE_DICT"))

    ; Check for the DXF group code 350, dictionary entry
    (if (= (car val) 350)
      (progn
        ; Get the name/path of the image
        (setq fullName (cdr (assoc 1 (entget (cdr val)))))

        ; Determine whether the filename is returned only or the full path
        (if bIncludeFullPath
          ; Get and attempt to resolve the relative path as needed
          (setq fileName (ResolveRelativePath fullName (vl-string-trim "\\" (getvar "dwgprefix"))))

          ; Get the filename and extension only
          (setq fileName (strcat (vl-filename-base fullName) (vl-filename-extension fullName)))
        )

        ; Append the filename to the list
        (setq fileList (cons fileName fileList))
      )
    )
  )

  ; Return a sorted list of filenames
  (vl-sort fileList '<)
)

;; Returns a list of Text Font filenames used by the text styles defined in the drawing
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; bIncludeXrefs = T - Include text styles from Xrefs
;; bIncludeXrefs = nil - Exclude text styles from Xrefs
;; Usage: (TextFontPathsList T nil)
;; Usage: (TextFontPathsList nil T)
(defun TextFontPathsList (bIncludeFullPath bIncludeXrefs / ent fullName fileName fileList)
  ; Get the first text syle in the Style symbol table
  (setq ent (tblnext "style" T))

  ; Step through the Style symbol table
  (while ent
    ; Determine if text styles from xrefs should be included in the file list
    (if (or (= bIncludeXrefs T)
     (and (not (wcmatch (cdr (assoc 2 ent)) "*[|]*")) (= bIncludeXrefs nil)))
      (progn
        ; Get the name/path of the font file
        (setq fullName (cdr (assoc 3 ent)))

        ; Check to see if there is a file extension, if not it is a SHX file
        (if (= (vl-filename-extension fullName) nil)
          (setq fullName (strcat fullName ".shx"))
        )

        ; Determine the current OS: Windows or Mac
        (if (wcmatch (strcase (getvar "platform")) "*WINDOWS*")
          (if (findfile (strcat (getenv "WINDIR") "\\fonts\\" fullName))
            (setq fullName (strcat (getenv "WINDIR") "\\fonts\\" fullName))
            (setq fullName (findfile fullName))
          )
          ; Mac OS currently not supported - (wcmatch (strcase (getvar "platform")) "*MAC*")
          (setq fullName "")
        )

        ; Determine whether the filename is returned only or the full path
        (if bIncludeFullPath
          ; Get the full filename
          (setq fileName fullName)

          ; Get the filename and extension only
          (setq fileName (strcat (vl-filename-base fullName) (vl-filename-extension fullName)))
        )

        ; Check to see if the entry already exists in the list
        (if (not (member fileName fileList))
          (setq fileList (cons fileName fileList))
        )
      )
    )

    ; Return the next text style in the Style symbol table
    (setq ent (tblnext "style"))
  )

  ; Return a sorted list of filenames
  (vl-sort fileList '<)
)

;; Returns a list of Weblight filenames based on the Web lights in all block definitions
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (WebLightPathsList T)
;; Usage: (WebLightPathsList nil)
(defun WebLightPathsList (bIncludeFullPath / blkName entBlkSym entName entData xDict fileList)
  ; Get the first element of the Blocks symbol table
  (setq blkName "*MODEL_SPACE")
  (setq entBlkSym (tblobjname "BLOCK" blkName)) ;;(tblnext "block" T))

  ; Step through the Blocks symbol table
  (while entBlkSym
    ; Get the entity name for the block definition and its data
    (setq entName (tblobjname "BLOCK" blkName)
          entData (entget entName))

    ; Step through each object in the block definition
    (while entName
      (if (/= entName nil)
        (progn
          ; Check to see if the entity is a Light object
          (if (= (strcase (cdr (assoc 0 entData))) "LIGHT")
            ; Check for the DXF group code 360, an extension dictionary
            (if (assoc 360 entData)
              ; Check for and get the extension dictionary related to the Photometric Light information
              (if (setq xDict (dictsearch (cdr (assoc 360 entData)) "ADSK_XREC_PHOTOMETRICLIGHTINFO"))
                (progn
                  ; Get the filename/path stored for the Weblight
                  (setq fullName (cdr (assoc 300 xDict)))

                  ; Determine whether the filename is returned only or the full path
                  (if bIncludeFullPath
                    ; Get and attempt to resolve the relative path as needed
                    (setq fileName (ResolveRelativePath fullName (vl-string-trim "\\" (getenv "IESWEB"))))

                    ; Get the filename and extension only
                    (setq fileName (strcat (vl-filename-base fullName) (vl-filename-extension fullName)))
                  )

                  ; Check to see if the file entry already exists in the list
                  (if (not (member fileName fileList))
                    (setq fileList (cons fileName fileList))
                  )
                )
              )
            )
          )

          ; Get the next entity in the block
          (setq entName (entnext entName))

          ; If there is a next entity, get its DXF entity data
          (if (/= entName nil)
            (setq entData (entget entName))
          )
        )
      )
    )
   
    ; Return the next block in the Blocks symbol table, if *Model_SPACE
    ; is the current block name then get the first symbol in the Block table
    (if (= blkName "*MODEL_SPACE")
      (setq entBlkSym (tblnext "block" T))
      (setq entBlkSym (tblnext "block"))
    )

    ; Get the name of the next block
    (setq blkName (cdr (assoc 2 entBlkSym)))
  )
 
  ; Return a sorted list of filenames
  (vl-sort fileList '<) 
)

;; Returns a list of the image files referenced by the material definitions in the drawing
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (MaterialImagePathsList T)
;; Usage: (MaterialImagePathsList nil)
(defun MaterialImagePathsList (bIncludeFullPath / materialDict cnt val fileList)
  (if (setq materialDict (dictsearch (namedobjdict) "ACAD_MATERIAL"))
    ; Step through the definitions in the Materals dictionary
    (foreach val materialDict
      ; Check for the DXF group code 350, an xrecord
      (if (= (car val) 350)
        ; Check for the DXF group code values 3 (main material image) and 8 (releif pattern image)
        (foreach dxfCode '(3 8)
          (if (setq entData (assoc dxfCode (entget (cdr val))))
            ; Proceed if the image filename is not blank
            (if (/= (setq fullName (cdr entData)) "")
              (progn
                ; Determine whether the filename is returned only or the full path
                (if bIncludeFullPath
                  ; Get and attempt to resolve the relative path as needed
                  (setq fileName (ResolveRelativePath fullName (vl-string-trim "\\" (getvar "dwgprefix"))))

                  ; Get the filename and extension only
                  (setq fileName (strcat (vl-filename-base fullName) (vl-filename-extension fullName)))
                )

                ; Check to see if the file entry already exists in the list
                (if (not (member fileName fileList))
                  (setq fileList (cons fileName fileList))
                )
              )
            )
          )
        )
      )
    )
  )
 
  ; Return a sorted list of filenames
  (vl-sort fileList '<)
)

;; Returns a list of all plot settings files referenced by layouts and named page setups
;; sPlotSettingsType = "PC3", "CTB", or "STB" - CTB and STB values return the same values
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (PlotSettingsList "PC3" T)
;; Usage: (PlotSettingsList "CTB" nil)
(defun PlotSettingsList (sPlotSettingsType bIncludeFullPath / eDict val sName fileList)
  ; Define DXF Code to return
  (cond
    ((= (strcase sPlotSettingsType) "PC3")(setq dxfCode 2 paths (split (getenv "PrinterConfigDir") ";")))
    ((= (strcase sPlotSettingsType) "CTB")(setq dxfCode 7 paths (split (getenv "PrinterStyleSheetDir") ";")))
    ((= (strcase sPlotSettingsType) "STB")(setq dxfCode 7 paths (split (getenv "PrinterStyleSheetDir") ";")))
  )
 
  ; Get the layouts and plot settings dictionaries
  (foreach eDict (list (dictsearch (namedobjdict) "ACAD_LAYOUT")
                       (dictsearch (namedobjdict) "ACAD_PLOTSETTINGS"))
      ; Step through the dictionary
      ; Change to be foreach instead of while loop
      (foreach val eDict
        ; Check to see if the value is a dictionary entry
        (if (= (car val) 350)
          (progn
            (setq sName (cdr (assoc dxfCode (entget (cdr val)))))

            ; Model tab when set to "None" returns "none_device"
            (if (= sName "none_device")(setq sName "None"))

            ; Determine whether the filename is returned only or the full path
            (if bIncludeFullPath
              ; Look through the different printer configuration paths for the correct location of the file
              (foreach path paths
                (if (findfile (strcat path "\\" sName))
                  (setq sName (strcat path "\\" sName))
                )
              )
      
              ; Get the filename and extension only
              (setq sName (strcat (vl-filename-base sName) (vl-filename-extension sName)))
            )
    
            ; Check to see if the entry already exists in the list
            (if (not (member sName fileList))
              (setq fileList (cons sName fileList))
            )
          )
        )
      )
  )

  ; Return a sorted list of filenames
  (vl-sort fileList '<)
)

;; Returns a list of all Plot Configuration (PC3) files referenced by layouts and named page setups
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (PlotConfigurationPathsList T)
;; Usage: (PlotConfigurationPathsList nil)
(defun PlotConfigurationPathsList (bIncludeFullPath / )
  (PlotSettingsList "PC3" bIncludeFullPath)
)

;; Returns a list of all Plot Style (CTB/STB) files referenced by layouts and named page setups
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (PlotStylesPathsList T)
;; Usage: (PlotStylesPathsList nil)
(defun PlotStylesPathsList (bIncludeFullPath / )
  (if (= 1 (getvar "PSTYLEMODE"))
    (PlotSettingsList "CTB" bIncludeFullPath)
    (PlotSettingsList "STB" bIncludeFullPath)   
  )
)

;; Returns a list of the specified underlay files attached to the drawing
;; sUnderlayType = "DWF", "DGN", "PDF", "RCP", "NWC", or "NWD"
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (UnderlaysList "DWF" T)
;; Usage: (UnderlaysList "DGN" nil)
;; Usage: (UnderlaysList "PDF" T)
;; Usage: (UnderlaysList "RCP" nil)
;; Usage: (UnderlaysList "NWC" nil)
(defun UnderlaysList (sUnderlayType bIncludeFullPath / underlayDict val fullName fileName fileList)
  ; Get the underlay definition dictionary
  (cond
    ((= (strcase sUnderlayType) "DWF")(setq underlayDict (dictsearch (namedobjdict) "ACAD_DWFDEFINITIONS")))
    ((= (strcase sUnderlayType) "DGN")(setq underlayDict (dictsearch (namedobjdict) "ACAD_DGNDEFINITIONS")))
    ((= (strcase sUnderlayType) "PDF")(setq underlayDict (dictsearch (namedobjdict) "ACAD_PDFDEFINITIONS")))
    ((= (strcase sUnderlayType) "RCP")(setq underlayDict (dictsearch (namedobjdict) "ACAD_POINTCLOUD_EX_DICT")))
    ((or (= (strcase sUnderlayType) "NWC")
         (= (strcase sUnderlayType) "NWD"))(setq underlayDict (dictsearch (namedobjdict) "ACAD_BIM_DEFINITIONS")))
  )

  ; Step through the DWF underlay definition dictionary
  ; Change to be foreach instead of while loop
  (foreach val underlayDict
    ; Check to see if the value is a dictionary entry
    (if (= (car val) 350)
      (progn
        (setq fullName (cdr (assoc 1 (entget (cdr val)))))

        ; Determine whether the filename is returned only or the full path
        (if bIncludeFullPath
          ; Get and attempt to resolve the relative path as needed
          (setq fileName (ResolveRelativePath fullName (vl-string-trim "\\" (getvar "dwgprefix"))))

          ; Get the filename and extension only
          (setq fileName (strcat (vl-filename-base fullName) (vl-filename-extension fullName)))
        )

        ; Check to see if the entry already exists in the file list since duplicates could exist
        (if (not (member fileName fileList))
          (setq fileList (cons fileName fileList))
        )
      )
    )
  )

  ; Return a sorted list of filenames
  (vl-sort fileList '<)
)

;; Returns a list of all DWF files attached to the drawing
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (DWFUnderlaysPathsList T)
;; Usage: (DWFUnderlaysPathsList nil)
(defun DWFUnderlaysPathsList (bIncludeFullPath / )
  (UnderlaysList "DWF" bIncludeFullPath)
)

;; Returns a list of all DGN files attached to the drawing
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (DGNUnderlaysPathsList T)
;; Usage: (DGNUnderlaysPathsList nil)
(defun DGNUnderlaysPathsList (bIncludeFullPath / )
  (UnderlaysList "DGN" bIncludeFullPath)
)

;; Returns a list of all PDF files attached to the drawing
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (PDFUnderlaysPathsList T)
;; Usage: (PDFUnderlaysPathsList nil)
(defun PDFUnderlaysPathsList (bIncludeFullPath / )
  (UnderlaysList "PDF" bIncludeFullPath)
)

;; Returns a list of all the point cloud files attached to the drawing
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (PointCloudsPathsList T)
;; Usage: (PointCloudsPathsList nil)
(defun PointCloudsPathsList (bIncludeFullPath / )
  (UnderlaysList "RCP" bIncludeFullPath)
)

;; Returns a list of all the Navisworks files attached to the drawing
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (CoordinationModelPathsList T)
;; Usage: (CoordinationModelPathsList nil)
(defun CoordinationModelPathsList (bIncludeFullPath / )
  (UnderlaysList "NWD" bIncludeFullPath)
)

;; Returns a list containing the sheet set file name associated with the drawing
;; bIncludeFullPath = T - Full file path is returned
;; bIncludeFullPath = nil - Filename and extension only are returned
;; Usage: (AssociatedDSTPathList T)
;; Usage: (AssociatedDSTPathList nil)
(defun AssociatedDSTPathList (bIncludeFullPath / shSetDict bFlag val fullName fileName fileList)
  ; Get the Sheet Set data dictionary
  (setq shSetDict (dictsearch (namedobjdict) "AcSheetSetData")
        bFlag nil
  )

  ; Step through the records in the dictionary until ShSetFileName is found
  (foreach val shSetDict
    (if (= fileList nil)
      ; Check for the DXF group code 3
      (if (= (car val) 3)
        ; If DXF group code 3 was found, check to see if the Sheet Set FileName record was found
        (if (= (cdr val) "ShSetFileName")
          (setq bFlag T)
        )

        ; If the Sheet Set FileName record was found, get the Sheet Set filename
        (if (and (= (car val) 350) (= bFlag T))
          (progn
            ; Get the DST filename from the record
            (setq fullName (cdr (assoc 1 (entget (cdr val)))))

            ; Determine whether the filename is returned only or the full path
            (if bIncludeFullPath
              ; Get the full filename
              (setq fileName fullName)

              ; Get the filename and extension only
              (setq fileName (strcat (vl-filename-base fullName) (vl-filename-extension fullName)))
            )

            ; Append the filename to the list
            (setq fileList (cons fileName fileList))
          )
        ) 
      )
    )
  )

  ; Return the file list, which should only contain one value
  fileList
)

I hope that the code samples in this article help you:

If you have any comments or thoughts on this samples in this article, let me know as it would be great to get some input.

Sincerely,
  Lee

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

What's New In the AutoCAD 2018 APIs and Developer Documentation 28 Mar 2017 1:11 PM (8 years ago)

AutoCAD 2018 marks for me the 11th product release that I worked on, it is hard to believe that I started working with the AutoCAD team about 12 years ago now (2 years as a consultant and going on 10 years as an employee). Prior to joining Autodesk, I was a developer for a fairly large company and an ADN member that lived and died by the AutoCAD developer documentation.  For me, this release marks a special milestone that I have been pushing towards for several years now and that is the merging of the AutoCAD platform developer documentation.

The AutoCAD application programming interfaces (APIs) this release has seen some changes, based on your usage of certain areas the changes might feel minor while to others they might be major. In this article, I simply highlight the changes that were introduced and don't go into any great depth; you can find out specifics about the API changes in the developer documentation itself.  I do plan on covering some specific changes in future articles though.

The AutoCAD developer documentation can be found at:
http://help.autodesk.com/view/OARX/2018/ENU/

Some of the developer documentation (AutoLISP and ActiveX), can also be found while searching in the AutoCAD product documentation:
http://help.autodesk.com/view/ACD/2018/ENU/

The following outlines the changes that were made to the AutoCAD APIs:

General Updates

AutoLISP

DCL

ActiveX/COM

Managed .NET

ObjectARX

JavaScript

Hope you found this overview of the changes to the AutoCAD APIs and developer documentation helpful.

Sincerely,
  Lee

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

PSA: windows.h is No Longer Implicitly Included with aced.h In the ObjectARX 2018 SDK 28 Mar 2017 6:28 AM (8 years ago)

Prior to the AutoCAD 2018-based products and the ObjectARX 2018 SDK, the aced.h header file implicitly included <windows.h>. When building ObjectARX applications for AutoCAD 2018-based products on Windows, you must now include <windows.h> in one of your C++ source files when aced.h is included. Failure to do so will result in a series of compiler errors that cause the project to no longer build. The following image shows an example of the compiler errors you may encounter when including aced.h and not windows.h.

Aced_windows_h compiler errors

Sincerely,
  Lee

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

AutoCAD Developer Documentation Survey (Follow-up) - Part 2 21 Mar 2017 11:51 AM (8 years ago)

Picking up a project that you are passionate about can be very easy, but it can also be a burden at the same time.  I was given the opportunity just over 4 years ago to lead the AutoCAD developer documentation, which for me was very near and dear to me as I was a consumer of the developer documentation for about 10 years prior to joining Autodesk.  Since I had experience with developing custom solutions for AutoCAD and AutoCAD-based products, I had my personal experience to lean on but as we all know everyone's experience is very different. Getting an understanding of how others work and their needs for developer documentation is why the AutoCAD Developer Documentation Survey came into existence.  The most recent survey was conducted in early 2016 (148 responded), and the first one was conducted in early 2013 (405 responded).

For a summary of the results from the most recent AutoCAD Developer Documentation Survey, see AutoCAD Developer Documentation Survey (Follow-up) - Part 1.

The results from those two surveys have helped to shape the developer documentation into what it has become today. The following outlines the many enhancements that have been influenced by the survey responses over the past 4 years along with other improvements made since AutoCAD 2012:

AutoCAD 2018

AutoCAD 2017

For more information see, AutoCAD 2017 ObjectARX and Managed .NET Documentation and What's New In the AutoCAD 2017 APIs and Developer Documentation.

AutoCAD 2016

For more information see, AutoCAD 2016 Developer Documentation Changes.

AutoCAD 2015

For more information see, AutoCAD 2015 Developer Documentation Changes.

AutoCAD 2014

For more information, see AutoCAD 2014 API and Developer Documentation Changes and AutoLISP Documentation Updates - Windows and Mac.

AutoCAD 2013

AutoCAD 2012

While many improvements have been made in recent years to the AutoCAD developer documentation, there is still much work to do.  If you have any comments or feedback about the AutoCAD developer documentation, I would like to hear from you.

Sincerely,
  Lee

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

AutoCAD Developer Documentation Survey (Follow-up) - Part 1 20 Mar 2017 2:00 PM (8 years ago)

The AutoCAD Developer Documentation survey has been posted twice over the past 4 years, but the results of those surveys and how those responses were used hasn't really been shared.  Because we value the input from our communities, I am giving you a glimpse into the survey results and a number of documentation improvements that have been made as a result of the responses in the past few years.  Of course, there is always room for improvements and we hope that you will continue providing us valuable feedback in future surveys. The most recent survey was conducted in early 2016 (148 responded), and the first one was conducted in early 2013 (405 responded).

Link to the original article related to the 2016 survey - AutoCAD Developer Documentation Survey - 2016.

Note: Keep in mind, the information in this posting is based on the voluntary responses of those that create custom solutions which extend the functionally of AutoCAD and AutoCAD-based products; the information doesn't directly represent the user base of those that use AutoCAD or AutoCAD-based products and is in no way representative of where Autodesk is investing in product development.

Assumptions: Those that responded to the survey are most likely developers, CAD administrators, and others that extend the functionality of AutoCAD and AutoCAD-based products.

What Release(s) of AutoCAD Do You Support?

When asked, a majority of the respondents indicated they were targeting the latest 2 releases while a percentage also indicated they support 2 or more previous releases along with the latest release. The following is a summary of the results of the respondents in the most recent survey:

In addition to those AutoCAD releases that respondents identified, the following are the most popular AutoCAD-based vertical products that the respondents identified they were developing for in the most recent survey (items are not listed in any specific order):

What Programming Languages Are the Most Popular?

As a developer, there are many different programming languages available and many more seem to be introduced each year.  While there are no endless options available for developing custom applications that can be loaded into AutoCAD or AutoCAD-based products, there are a number of mainstream options available.  Respondents stated these were the top programming languages that they used and their order of importance\usage:

  1. C#
  2. AutoLISP
  3. VB.NET
  4. C++
  5. VBA
  6. JavaScript
  7. F#

Do You Use the Developer Documentation in the ObjectARX SDK or Product?

The AutoCAD developer documentation is delivered as part of the AutoCAD product and ObjectARX SDK.  75+% of all respondents stated that they use the developer documentation that is part of the ObjectARX SDK which makes sense given the number of respondents that use the C#, VB.NET, and C++ programming languages to extend the functionality of AutoCAD and AutoCAD-based products.  The ObjectARX SDK contains these documentation components:

Note: The Managed .NET Developer's Guide was made available as part of the ObjectARX SDK starting with the 2017 release.

The AutoCAD product documentation contains these developer documentation components and they are used by more than 82% of all respondents:

The JavaScript Developer's Guide and JavaScript Reference Guide are linked to the AutoCAD product documentation, but not part of the product documentation set.

For the respondents, these were the most commonly accessed/used documentation components and the order of importance:

While a majority of respondents identified that they use the documentation that is part of the product and ObjectARX SDK, 30+% of the respondents identified that they were unaware of these documentation components:

How Do You Want to Access the Developer Documentation?

Respondents of the survey identified these delivery methods as the most desirable for accessing the developer documentation; methods are listed in the order of importance:

  1. CHM
  2. HTML (via product documentation)
  3. PDF
  4. Microsoft Help (via Microsoft Visual Studio)

88+% of all respondents have expressed interest in being able to access all the developer documentation from a single location online, and that 85+% have access to the Internet while they are authoring programs.

Of those that responded, 33+% of them access the ObjectARX and Managed .NET Reference documentation via the Microsoft Help feature in Microsoft Visual Studio.  However, 35% of the respondents were unaware that the ObjectARX and Managed .NET Reference documentation could be integrated into Microsoft Visual Studio.  Even though there was a vast range in those that knew or didn't know about the documentation integration with Microsoft Visual Studio, many respondents expressed interest in accessing developer documentation from within Visual Studio that wasn't just the ObjectARX and Managed .NET Reference Guides.

The documentation components that respondents want to access from Microsoft Visual Studio and their priority are:

  1. Managed .NET Developer's Guide
  2. ObjectARX Developer's Guide
  3. JavaScript Developer's Guide
  4. ObjectARX Interoperability Guide
  5. JavaScript Reference Guide
  6. ObjectARX Migration Guide
  7. Sheet Set Object Reference
  8. VBA/ActiveX Reference
  9. ObjectARX Readme
  10. VBA/ActiveX Developer’s Guide
  11. CAD Standards Plug-in Reference
  12. Connectivity Automation Object Reference
  13. Transmittal Object Reference

 The responses of both surveys have lead to a number of minor and major improvements to the AutoCAD developer documentation.  I will mention the improvements made in Part 2.

Sincerely,
  Lee

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Debugging AutoLISP Programs - Part 2: Basic Debugging 9 Mar 2017 5:04 AM (8 years ago)

The AutoLISP programming language supports some basic debugging techniques that can be utilized to help troubleshoot problems within a custom program. Displaying messages or values during the execution of a custom program can be beneficial in identifying the statement that is failing during execution. By looking at the last message or value returned, it will give you a basic or exact idea on where to start looking for the problem within the custom program based on the number of messages placed throughout your custom program.

These AutoLISP functions can be helpful in providing basic information in the AutoCAD user interface which can be used to determine where a program might have failed:

You might use the previously mentioned functions to display basic or more specific messages to help identify which code statements were recently executed. Examples of basic messages might be:

More specific messages that you might use are:

The previously mentioned functions are useful for displaying information in the AutoCAD user interface; it can also be beneficial to write messages and values out to an external log file.  In addition to the PRINC function, the following AutoLISP functions can also be used to output information to the Command prompt or an external log file:

The following code example creates a blue circle using the ActiveX/COM library and demonstrates how some of the previously mentioned functions can be used to help debug a program:

; Load COM environment
(vl-load-com)

; Create a blue circle using Visual LISP
(defun c:CreateCircle-VL ( / acadObj docObj spaceObj cenPoint circObj)
  ; Create a reference to AutoCAD
  (setq acadObj (vlax-get-acad-object))

  (princ acadObj)
  (terpri)
 
  ; Create a reference to the current drawing
  (setq docObj (vla-get-activedocument acadObj))

  (princ acadObj)
  (terpri) 
  (prompt "Determine current space")
  (terpri)
 
  ; Get a reference to the current space
  (if (= (vla-get-activespace docObj) acModelSpace)
    (setq spaceObj (vla-get-modelspace docObj))
    (setq spaceObj (vla-get-paperspace docObj))
  )

  (princ spaceObj)
  (terpri) 
  (prompt "Start object definition")
  (terpri)
 
  ; Create the center point for the circle
  (setq cenPoint (vlax-make-safearray vlax-vbdouble '(0 . 2)))
  (vlax-safearray-fill cenPoint '(5.0 5.0 0.0))

  (princ cenPoint)
  (terpri)
 
  ; Create a circle at 5,5 in a radius of 1.75
  (setq circObj (vla-addcircle spaceObj cenPoint 1.75))

  (princ circObj)
  (terpri)
 
  ; Change the color of the circle to blue
  (vla-put-color circObj 5)

  (prompt "End object definition") 
  (terpri)
 (princ)
)

When executed, the previous example displays the values of several variables using the PRINC function and messages with the PROMPT function at the Command prompt. The following is a sample of the output displayed:

Command: CREATECIRCLE-VL
#<VLA-OBJECT IAcadApplication 00000001400d6188>
#<VLA-OBJECT IAcadApplication 00000001400d6188>
Determine current space
#<VLA-OBJECT IAcadModelSpace 000000003285d808>
Start object definition
#<safearray...>
#<VLA-OBJECT IAcadCircle 0000000039fe7f88>
End object definition

Once you are done debugging, you would normally comment out or remove each statement that simply outputs the values and messages that the user wouldn't normally need to see.  The downside to this though is that there could be hundreds of statements that you might need to comment out or remove each time you debug your program, what if there was a better approach to using messages to perform basic debugging.  Over the years, I created a utility library specific for handling debug messages. This library allows you to display messages at the Command prompt or to a log file, and even disable the feature all at once without needing to comment or remove the debugging statements.

The debug utility library that I wrote consists of three global variables and five functions:

The following is what the actual function definitions look like from my debug utility library:

; Global debugging variables
; *GLOBAL-DEBUG-LOGFILE* - Log file name and location
; *GLOBAL-DEBUG-LOGGING* - Log enabled
; *GLOBAL-DEBUG-ENABLED* - Enable debugging (T or nil)

; Enable debugging - Example usage (debug-mode 2)
; 0 or nil - Disables debugging
; 1 or T   - Enables debugging at the Command prompt
; 2        - Enables debugging and outputs to a log file

(defun debug-mode (enable / )
  (cond
    ((or (= enable 0)(= enable nil))
      (if (= (type *GLOBAL-DEBUG-LOGFILE*) 'FILE)
        (progn
          (close *GLOBAL-DEBUG-LOGFILE*)
          (setq *GLOBAL-DEBUG-LOGFILE* nil)
        )
      )

      (setq *GLOBAL-DEBUG-ENABLED* nil
            *GLOBAL-DEBUG-LOGGING* nil
            *GLOBAL-DEBUG-LOGFILE* nil)
    )
    ((or (= enable 1)(= enable T))
      (setq *GLOBAL-DEBUG-ENABLED* 1 *GLOBAL-DEBUG-LOGGING* 0)
      (if (= (type *GLOBAL-DEBUG-LOGFILE*) 'FILE)
        (progn
          (close *GLOBAL-DEBUG-LOGFILE*)
          (setq *GLOBAL-DEBUG-LOGFILE* nil)
        )
      )
    )
    ((= enable 2)
      (setq *GLOBAL-DEBUG-ENABLED* 1 *GLOBAL-DEBUG-LOGGING* 1)
      (if (/= (type *GLOBAL-DEBUG-LOGFILE*) 'FILE)
        (setq *GLOBAL-DEBUG-LOGFILE* (open (strcat (getvar "tempprefix") "global-debug.log") "a"))
      ) 
    )
  )
 (princ)
)

; Get the current state of debugging
(defun debug-get-enabled ( / )
  *GLOBAL-DEBUG-ENABLED*
)

; Get the current logging state
(defun debug-get-logmode ( / )
  *GLOBAL-DEBUG-LOGGING*
)

; Get the name of the current log file
(defun debug-get-logfile ( / )
  *GLOBAL-DEBUG-LOGFILE*
)

; Debug function is a wrapper for the functionality
; of the princ and print functions. Feature is turned on or
; off with the debug-mode function.
(defun debug (value / )
  (if (debug-get-enabled)
    (if (/= (debug-get-logmode) 1)
      (progn (princ value)(terpri))
      (print value (debug-get-logfile))
    )
  )
 (princ)
)

The following is a revision of the CreateCircle-VL, shown earlier in this posting, which demonstrates the usage of the debug utility library compared to separate PRINC and PROMPT functions:

; Load COM environment
(vl-load-com)

; Enable debug mode
(debug-mode 1)

; Create a blue circle using Visual LISP
(defun c:CreateCircle-VL ( / acadObj docObj spaceObj cenPoint circObj)
  ; Create a reference to AutoCAD
  (setq acadObj (vlax-get-acad-object))

  (debug acadObj)
 
  ; Create a reference to the current drawing
  (setq docObj (vla-get-activedocument acadObj))

  (debug acadObj)
  (debug "Determine current space")
 
  ; Get a reference to the current space
  (if (= (vla-get-activespace docObj) acModelSpace)
    (setq spaceObj (vla-get-modelspace docObj))
    (setq spaceObj (vla-get-paperspace docObj))
  )

  (debug spaceObj) 
  (debug "Start object definition")
 
  ; Create the center point for the circle
  (setq cenPoint (vlax-make-safearray vlax-vbdouble '(0 . 2)))
  (vlax-safearray-fill cenPoint '(5.0 5.0 0.0))

  (debug cenPoint)
 
  ; Create a circle at 5,5 in a radius of 1.75
  (setq circObj (vla-addcircle spaceObj cenPoint 1.75))

  (debug circObj)
 
  ; Change the color of the circle to blue
  (vla-put-color circObj 5)

  (debug "End object definition")
 (princ)
)

When the revised code is loaded and executed, variable values and messages are displayed at the Command prompt.  If you execute the statement (debug-mode 0) at the Command prompt or change the (debug-mode 1) statement to (debug-mode 0) and reload the sample code, debugging is disabled and the variables and messages are no longer displayed at the Command prompt.

Hope this article makes you think differently about utilizing basic messages when debugging your custom programs.

Sincerely,
  Lee 

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Debugging AutoLISP Programs - Part 1 2 Mar 2017 11:37 AM (8 years ago)

Debugging AutoLISP programs is "A Zen like Experience," said no one ever... Although, I have tried to convince a number of people that it is very a calming and soothing activity over the years.  There are a number of different debugging techniques and tools that can be used to find and handle errors much easier, whether you are developing in AutoCAD on Windows or Mac OS.

Note: There are more debugging tools available on Windows than Mac OS at this time, which could change in the future.  If you do plan on developing a number of AutoLISP programs, you might want to consider getting access to the Windows version of AutoCAD if you are a user of Mac OS.  Just a simple suggestion, not something you must do.

The thought of debugging and error handling doesn't come naturally to new and even seasoned developers, as most are simply focused on solving the problem at hand and not all the extra baggage that often goes with well written code.  Of course, well written code is in the eye of the developer that is look at it in the current moment.  Also if it isn't broken, then why change or fix a process\approach to a solution?  After many years of writing custom programs, I have learned that debugging and error handling need to be some of the very first things to consider when writing any custom program.

While I am just as guilty for jumping in (feet, hands, and compiler first) and writing code, this approach can get you to the solution faster but can lead to some bad habits and headaches down the road.  One of the things that I have found useful in getting me to slowdown and think things through more clearly is the creation of a flowchart.  If you are not already creating one today, I recommend creating a flowchart to help you visualize all the logical paths through your programs.  Flowcharts make it easier to identify choices as well as potential failure points that need to be handled.  A flowchart can also be a great way to communicate the design of a program with other that are less technical.  Products like Microsoft Visio or Lucidchart are designed to create flowcharts, but the same can be accomplished with Microsoft Word, PowerPoint, and other word processor and presentation products.

Potential failure points can be introduced when a:

AutoLISP/Visual LISP offers a variety of built-in functions and features to help you track down problems and handle errors. You can utilize the following debugging and error handling functions and features when developing your custom programs:

Hope you find this overview topic helpful, I plan on following up with future articles that dive more into specifics about each debugging technique and tool that I mentioned.

Sincerely,
  Lee

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Calculating Elapsed Time with AutoLISP 27 Feb 2017 5:00 AM (8 years ago)

Why calculate elapsed time or differences in time you might be wonder?  There are primarily two reasons to do this:

The need to the calculate cost to make something or complete a task/project is nothing new and happens everyday across every company.  When calculating cost savings, you need to consider the following pieces of information:

Even though calculating cost savings is an important conversation, that is best left for a future article.

Until AutoCAD 2017, the date and time stored in the CDATE and DATE system variables were the best for calculating time differences in an AutoCAD-based product as they included milliseconds.  Starting with AutoCAD 2017-based products, the CDATE and DATE system variables no longer include milliseconds.  Other date related system variables, such TDCREATE were also affected by this change.  I discussed this change across several articles recently, which can be found here:

Computers can easily handle hundreds of thousands of instructions per second based on their architecture, so milliseconds is a much better measurement in time to measure compared to a full second.  Since the CDATE and DATE system variables no longer include milliseconds, the MILLISECS system variable can be used to calculate time differences in milliseconds.  As part of a custom AutoLISP program, you might store the current time of MILLISECS as the start time and then as part of the final tasks to be performed you might get the end value and subtract the differences of the two time values to get the time that has elapsed. 

Command: (setq start (getvar "MILLISECS"))
25494432

Command: (setq end (getvar "MILLISECS"))
25499471

Command: (- end start)
5039

Of course, it is always best to wrap any functionality that you might want to use across multiple programs in a utility function; making it easier to manage and maintain.  The following is an example of such a function, the Timer function wraps the functionality that stores the start time and returns the time difference in a single function.

; Global variable for the timer
(setq *g-timer-start-time* nil)

; Stores the current time when called, and then returns
; the time difference when called the next time. Time
; difference is returned in milliseconds.
; Usage: (timer)
(defun timer ( / rTimeDiff)
  ; Check to see if the function was previously called
  (if (= *g-timer-start-time* nil)
    (progn
      ; Stores the current milliseconds
      (setq *g-timer-start-time* (getvar "MILLISECS"))
      (princ)
    )
    (progn
      ; Calculates and returns the time difference
      (setq rTimeDiff (- (getvar "MILLISECS") *g-timer-start-time*)
            *g-timer-start-time* nil)
      rTimeDiff
    )
  )
)

The following shows a example of how you might use the Timer function with your custom programs and the output that is returned:

(defun c:CounterLoop ( / loop cnt cur)
  ; Store the current time
  (timer)

  ; Loop 10000 times and output a message every 100 loops
  (setq loop 0 cnt 0)
  (repeat 10000
    (setq cur (fix (/ loop 100.0)))
    (if (= cnt cur)
      (progn
        (prompt (strcat "\nLoop: " (itoa loop)))
        (setq cnt (1+ cnt)) 
      )
    )
    (setq loop (1+ loop))
  )

  (prompt (strcat "\nLoop: " (itoa loop)))

  ; Output the number of elapsed milliseconds
  (prompt (strcat "\nElapsed time (ms): " (itoa (timer))))
 (princ)
)

Partial output from the previous example:

...
Loop: 9700
Loop: 9800
Loop: 9900
Loop: 10000
Elapsed time (ms): 63

While most programs execute very quickly, some might take much longer based on the tasks they perform.  Programs that take longer to execute might complete in tens of thousands of milliseconds, which can be hard to determine just how many seconds or minutes might have elapsed.  The following functions can be used to calculate the differences in time and format milliseconds or seconds as hours, minutes, seconds, and milliseconds:

;| Takes two values in milliseconds and returns the
   difference as an integer/long or double/float

   bMilliseconds = T   - Return value in milliseconds
   bMilliseconds = nil - Return value in seconds

   Usage: (TimeDiff T1 T2 T) - Milliseconds example
   Usage: (TimeDiff T1 T2 nil) - Seconds example

   Example(s)
   (setq T1 24092498)
   (setq T2 24188267)
   (TimeDiff T1 T2 T)
   95769

   (TimeDiff T1 T2 nil)
   95.769
|;
(defun TimeDiff (nStart nEnd bReturnMilliseconds / nDiff)
 
  ;; Get the time difference
  (if (> nStart nEnd)
    (setq nDiff (- nStart nEnd))
    (setq nDiff (- nEnd nStart))
  )

  ;; Return the time difference in milliseconds or seconds
  (if (= bReturnMilliseconds nil)
    (/ nDiff 1000.0)
    nDiff
  )
)

;| Formats milliseconds or seconds into the
   HH:MM:SS:mmm (00:00:00:000) format

   bMilliseconds = T   - Time is expressed in milliseconds
   bMilliseconds = nil - Time is expressed in seconds

   Usage: (FormatTime 122.021 nil) - Seconds example
   Usage: (FormatTime 95769 T)     - Milliseconds example

   Example(s)
   (setq T1 24092498)
   (setq T2 24188267)
   (FormatTime (TimeDiff T1 T2 T) T)
   "00:01:35:769"

   (FormatTime 122.021 nil)
   "00:02:02:021"
|;
(defun FormatTime (dTime bMilliseconds / dDiff sHrs nHrs sMins
                                         nMins sSecs sMSecs nSecs)
  (if bMilliseconds
    (setq dDiff (/ dTime 1000.0))
    (setq dDiff dTime)
  )

  ; Set the default values
  (setq sHrs   "00"
        sMins  "00"
        sSecs  "00"
        sMSecs "000")
   
  ; Get the number of seconds
  (setq nSecs (fix dDiff))

  ; Get the number of milliseconds left
  (setq sMSecs (rtos (* (- dDiff nSecs) 1000.0) 2 0))

  ; Get the number of minutes
  (setq nMins (/ nSecs 60)
        sMins (itoa nMins))

  ; Get the number of hours and minutes, if minutes are greater than 60
  (if (> nMins 60)
    (setq nHrs (fix (/ nMins 60))
          sHrs (itoa nHrs)
          nMins (- nMins (* nHrs 60))
          sMins (itoa nMins))
  )

  ; Get the number of seconds
  (setq sSecs (itoa (- nSecs (* (/ nSecs 60) 60))))

  ; Format Hours to "00"
  (if (= (strlen sHrs) 1)
    (setq sHrs (strcat "0" sHrs))
  )

  ; Format Minutes to "00"
  (if (= (strlen sMins) 1)
    (setq sMins (strcat "0" sMins))
  )

  ; Format Seconds to "00"
  (if (= (strlen sSecs) 1)
    (setq sSecs (strcat "0" sSecs))
  )

  ; Format Milliseconds to "000"
  (while (< (strlen sMSecs) 3)
    (setq sMSecs (strcat "0" sMSecs))
  )

  ; Return the output in the 00:00:00:000 format
  (strcat sHrs ":" sMins ":" sSecs ":" sMSecs) 
)

Hope you found the information in this topic helpful.

Sincerely,
  Lee

 

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?