AutoCAD – Drawings

Total the lengths in AutoCAD: three hundred lines, one figure, one pass

Total the lengths in AutoCAD in one pass, with a breakdown table to check the figure against. A free lisp to download, plus three traps that corrupt the total.

  • TS. Đỗ Quốc Hoàng
  • 15 min read
Four shapes of known length: the printed figure matches hand arithmetic exactly, with the three traps that come with it Photo: Diagram by the Institute of Information Technology in Civil Engineering

Taking off pipework quantities, measuring kerb lines, totalling cable runs, adding up the perimeter of a parcel. All four are the same shape of problem: a few hundred lines already on the drawing, and one number needed.

AutoCAD has no command for it. LIST reads one object at a time, MEASUREGEOM measures one run per pass. Adding three hundred figures by hand goes wrong, and nothing tells you it went wrong. This article publishes the Institute's lisp file to total the lengths in AutoCAD, with the source printed in full so you can read it before running it. For the whole quantity take-off workflow see our AutoCAD for Construction course.

Why a separate file is needed at all

Three things AutoCAD already offers, and where each falls short:

WayWhat it doesWhere it stops
LISTPrints every property of the selected objectsSelect 300 lines and it prints 300 blocks of text, adding nothing
MEASUREGEOMMeasures the length of one curveOne object per run
The Properties paletteShows a Length fieldSelect several objects and the field goes blank

What they share is that none of them adds up. The Properties palette is the clearest case: select one line and the Length field appears, select ten and it disappears, because the program cannot know whether you want the total or the individual figures. That palette is covered in our article on the Properties palette in AutoCAD.

So the adding has to be done by a lisp file. And such a file is only worth trusting if it prints a breakdown table rather than a bare number, because a bare number cannot be checked at all.

Technically every file that does this job relies on the same AutoLISP function. Autodesk's reference describes vlax-curve-getDistAtParam as "Returns the length of the curve's segment from the curve's beginning to the specified parameter". Ask it at the curve's end parameter and the whole length comes back. The useful part is that it works for every kind of curve, so no separate code is needed for lines, arcs, polylines or splines.

Download the Institute's file

Download DCD-IIC.lspthree commands. The source stays in the article, because running unknown code on your own machine deserves care.
CommandWhat it does
TCDselect objects, print the total with a breakdown at the command line
TCDTas above, then write the figure on the drawing as text
TCDLadd up each layer separately and print a table

All three print a count column beside the length column. That column exists for a reason, set out below.

The file linked above prompts in English. A Vietnamese version is also published, with the same three command names so a drawing and its documentation stay interchangeable.

;;; ==================================================================
;;;  DCD-IIC-EN.LSP  -  add up the length of the selected objects
;;;  Institute of Information Technology in Civil Engineering (IIC)
;;;  Hanoi University of Civil Engineering
;;;  https://iic.huce.edu.vn  -  Tel: 0989 427 809
;;; ------------------------------------------------------------------
;;;  Three commands:
;;;    TCD     select objects, print the total length at the command line
;;;    TCDT    as above, then write the figure on the drawing as text
;;;    TCDL    add up each layer separately and print a table
;;;
;;;  Why this file is needed: LIST reads one object at a time and
;;;  MEASUREGEOM measures one object per run. To know the total length
;;;  of 300 pipe runs you would have to add them up by hand, and adding
;;;  up by hand goes wrong.
;;;
;;;  How it works: vlax-curve-getDistAtParam at the curve's end
;;;  parameter. That function works for every kind of curve, so there
;;;  is no separate code for lines, arcs, polylines, ellipses or splines.
;;;
;;;  FOUR THINGS TO KNOW BEFORE RUNNING - the files circulating online
;;;  do not mention them:
;;;    1. TWO LINES LYING ON TOP OF EACH OTHER are counted TWICE. The
;;;       program has no idea they coincide. Run OVERKILL to remove
;;;       duplicate linework first, or the total comes out too high.
;;;    2. CIRCLES and ELLIPSES are measured by CIRCUMFERENCE. If you
;;;       only want the straight runs, take them out using the
;;;       breakdown table the command prints.
;;;    3. OBJECTS INSIDE BLOCKS ARE NOT COUNTED. Only what sits
;;;       directly in the drawing is added. Explode the blocks first,
;;;       or measure one and multiply by the number of insertions.
;;;    4. A POLYLINE WITH WIDTH is still measured along its CENTRELINE,
;;;       not its edges. Right for pipework, wrong for a wall drawn as
;;;       a wide polyline.
;;;  After running, the command PRINTS a breakdown by object type, so
;;;  the figure can be checked instead of taken on trust.
;;;
;;;  Units: the figure is in DRAWING UNITS. A drawing in millimetres
;;;  gives millimetres. The command also prints the figure divided by
;;;  1000, to read off metres at a glance.
;;;
;;;  Command names were checked before being chosen: the acad.pgp alias
;;;  file of the 2027 release has no TCD, TCDT or TCDL.
;;;
;;;  To load: type APPLOAD, pick this file, click Load.
;;;  Saved as UTF-8. If you edit it in Notepad, save it back as UTF-8.
;;; ==================================================================

(vl-load-com)

;;; ------------------------------------------------------------------
;;;  Length of ONE object. Returns nil when the object is not a curve
;;;  (text, hatch, block...).
;;; ------------------------------------------------------------------
(defun iic-len (e / p)
  (if (and e (not (vl-catch-all-error-p
                    (setq p (vl-catch-all-apply
                              'vlax-curve-getEndParam (list e))))))
    (if p
      (vl-catch-all-apply 'vlax-curve-getDistAtParam (list e p))
    )
  )
)

;;; ------------------------------------------------------------------
;;;  Collect a selection set into (type . (count . total_length))
;;; ------------------------------------------------------------------
(defun iic-collect (ss / i e kind d tbl o skip)
  (setq tbl nil skip 0 i 0)
  (while (< i (sslength ss))
    (setq e    (ssname ss i)
          kind (cdr (assoc 0 (entget e)))
          d    (iic-len e))
    (if (and d (numberp d))
      (progn
        (setq o (assoc kind tbl))
        (if o
          (setq tbl (subst (cons kind (cons (1+ (cadr o)) (+ (cddr o) d)))
                           o tbl))
          (setq tbl (cons (cons kind (cons 1 d)) tbl))
        )
      )
      (setq skip (1+ skip))
    )
    (setq i (1+ i))
  )
  (cons tbl skip)
)

;;; ------------------------------------------------------------------
;;;  Print the breakdown and the total. Returns the total length.
;;; ------------------------------------------------------------------
(defun iic-print (tbl skip / total)
  (setq total 0.0)
  (princ "\n")
  (princ "\n  Object type              Count         Length")
  (princ "\n  ---------------------------------------------")
  (foreach o (reverse tbl)
    (setq total (+ total (cddr o)))
    (princ (strcat "\n  " (iic-pad (car o) 20)
                   (iic-rpad (itoa (cadr o)) 8)
                   (iic-rpad (rtos (cddr o) 2 2) 15)))
  )
  (princ "\n  ---------------------------------------------")
  (princ (strcat "\n  TOTAL" (iic-rpad (rtos total 2 2) 38)))
  (princ (strcat "\n  Divided by 1000" (iic-rpad (rtos (/ total 1000.0) 2 3) 28)))
  (if (> skip 0)
    (princ (strcat "\n  Skipped " (itoa skip)
                   " object(s) with no length (text, hatch, block...)"))
  )
  (princ "\n")
  total
)

;;; pad on the right to width n
(defun iic-pad (s n)
  (while (< (strlen s) n) (setq s (strcat s " ")))
  s
)
;;; pad on the left to width n
(defun iic-rpad (s n)
  (while (< (strlen s) n) (setq s (strcat " " s)))
  s
)

;;; ------------------------------------------------------------------
;;;  TCD - add up and print at the command line
;;; ------------------------------------------------------------------
(defun c:TCD (/ ss r)
  (princ "\nSelect the objects to add up: ")
  (setq ss (ssget))
  (if ss
    (progn
      (setq r (iic-collect ss))
      (iic-print (car r) (cdr r))
    )
    (princ "\nNothing selected.")
  )
  (princ)
)

;;; ------------------------------------------------------------------
;;;  TCDT - add up, then write the result on the drawing
;;; ------------------------------------------------------------------
(defun c:TCDT (/ ss r total p h txt)
  (princ "\nSelect the objects to add up: ")
  (setq ss (ssget))
  (if ss
    (progn
      (setq r     (iic-collect ss)
            total (iic-print (car r) (cdr r)))
      (setq p (getpoint "\nPick where to put the text: "))
      (if p
        (progn
          (setq h (getvar "TEXTSIZE"))
          (if (or (null h) (<= h 0)) (setq h 2.5))
          (setq txt (strcat "Total length = " (rtos total 2 2)))
          (entmake (list (cons 0 "TEXT")
                         (cons 10 p)
                         (cons 40 h)
                         (cons 1 txt)))
          (princ (strcat "\nWritten: " txt))
        )
      )
    )
    (princ "\nNothing selected.")
  )
  (princ)
)

;;; ------------------------------------------------------------------
;;;  TCDL - add up layer by layer
;;; ------------------------------------------------------------------
(defun c:TCDL (/ ss i e lay d tbl o total skip)
  (princ "\nSelect the objects to add up by layer: ")
  (setq ss (ssget))
  (if ss
    (progn
      (setq tbl nil skip 0 i 0)
      (while (< i (sslength ss))
        (setq e   (ssname ss i)
              lay (cdr (assoc 8 (entget e)))
              d   (iic-len e))
        (if (and d (numberp d))
          (progn
            (setq o (assoc lay tbl))
            (if o
              (setq tbl (subst (cons lay (cons (1+ (cadr o)) (+ (cddr o) d)))
                               o tbl))
              (setq tbl (cons (cons lay (cons 1 d)) tbl))
            )
          )
          (setq skip (1+ skip))
        )
        (setq i (1+ i))
      )
      (setq total 0.0)
      (princ "\n")
      (princ "\n  Layer                    Count         Length")
      (princ "\n  ---------------------------------------------")
      (foreach o (reverse tbl)
        (setq total (+ total (cddr o)))
        (princ (strcat "\n  " (iic-pad (car o) 20)
                       (iic-rpad (itoa (cadr o)) 8)
                       (iic-rpad (rtos (cddr o) 2 2) 15)))
      )
      (princ "\n  ---------------------------------------------")
      (princ (strcat "\n  TOTAL" (iic-rpad (rtos total 2 2) 38)))
      (if (> skip 0)
        (princ (strcat "\n  Skipped " (itoa skip) " object(s) with no length"))
      )
      (princ "\n")
    )
    (princ "\nNothing selected.")
  )
  (princ)
)

(princ "\nDCD-IIC-EN loaded. Commands: TCD (print), TCDT (write as text), TCDL (by layer).")
(princ)

How to run it, step by step

StepWhat you do
1Type APPLOAD, pick DCD-IIC.lsp, click Load
2Type TCD
3Select the objects to add up, then press Enter
4Read the table printed at the command line

If only a line or two of the command line is visible, press F2 to open the history window and the whole table appears. Loading lisp files and the errors that come up while doing it are covered in our article on the appload command in AutoCAD.

One thing catches people out: a lisp function lives only in the drawing it was loaded into. Open another drawing and it has to be loaded again. To load it once and for good, add the file to the Startup Suite inside the APPLOAD dialog itself.

The printed figure matches hand arithmetic

A lisp file is only worth trusting once somebody has checked it against arithmetic. Here is that check.

Four shapes of known length, added up with TCD:

ShapeTrue length
A line1,000
A quarter arc of radius 500π × 500 / 2 = 785.3982
A circle of radius 3002π × 300 = 1,884.9556
A closed polyline 1200 × 8004,000
Added by hand7,670.3538

Running the command prints:

  Object type              Count         Length
  ---------------------------------------------
  LWPOLYLINE                   1        4000.00
  CIRCLE                       1        1884.96
  ARC                          1         785.40
  LINE                         1        1000.00
  ---------------------------------------------
  TOTAL                                 7670.35
  Divided by 1000                         7.670

A match to two decimal places, 0.00% apart. Every row agrees too: the arc at 785.40 against 785.3982 by hand, the circle at 1,884.96 against 1,884.9556.

The Divided by 1000 line is pure convenience: construction drawings are usually set up in millimetres, so dividing by a thousand gives metres without any mental arithmetic.

Totalling lengths Photo: Four shapes of known length, the printed figure matching hand arithmetic, with the three traps

Three traps that corrupt the figure silently

This is the part the files circulating online leave out, and the reason this article runs longer than a download page.

First trap: two lines on top of each other are counted twice. Draw two 1,000 lines lying exactly on one another — the eye sees a single line. Running the command prints:

  LINE                         2        2000.00

Count 2, total 2,000. The program has no idea they coincide, and on a real floor plan duplicate linework happens constantly: copied twice, an xref laid over the original, a run redrawn without the old one being deleted.

That is precisely why the table carries a count column: one line on screen with a 2 in the table means there is duplicate linework. Run OVERKILL to clear it before adding up — covered in our article on removing duplicate linework in AutoCAD.

Second trap: objects inside blocks are not counted. Put a 1,000 line inside a block, insert the block, and add a separate 500 line beside it. Select both and run the command:

  LINE                         1         500.00
  ---------------------------------------------
  TOTAL                                  500.00
  Divided by 1000                          0.500
  Skipped 1 object(s) with no length (text, hatch, block...)

Just 500. The 1,000 inside the block dropped out of the sum, and fortunately the Skipped 1 object(s) line says so. Many files online report nothing at all, so the user assumes everything was counted.

The way round it: explode the blocks first, or measure one insertion and multiply by the number placed. Counting insertions is covered in our article on counting blocks in AutoCAD.

Third trap: text and hatching inside the selection. They have no length so they are skipped — which is right, but it has to be reported. In the test, one line of text and one hatch caught in the selection produced Skipped 2 object(s). The total itself stayed clean.

Totalling by layer

Real drawings never hold only one kind of line. Supply pipes on one layer, waste on another, kerbs on a third. A single combined figure means nothing.

TCDL adds up each layer separately. The test: two lines on layer IIC-ONG of 1,000 and 1,500, and one on IIC-TUONG of 2,000.

  Layer                    Count         Length
  ---------------------------------------------
  IIC-TUONG                    1        2000.00
  IIC-ONG                      2        2500.00
  ---------------------------------------------
  TOTAL                                 4500.00

Exactly as the arithmetic says: 1,000 plus 1,500 is 2,500 for the pipe layer, and 2,000 for the other.

The tidiest way to work: window the whole drawing and run TCDL once, and out comes a quantity table by layer. Layers are covered in our article on layers in AutoCAD.

That is also the argument for keeping layers disciplined on a drawing somebody will later have to total the lengths in AutoCAD from. Linework scattered across 0, Defpoints and half a dozen leftover layers still adds up correctly, but the table it produces tells nobody anything. Half an hour spent putting things on the right layer turns a bare number into a quantity schedule.

A polyline with width: centreline or edges

This question decides whether a take-off figure is right or wrong, and no documentation answers it.

Draw a single-segment polyline with a centreline of 1,000 and set its width to 200. Running the command:

  LWPOLYLINE                   1        1000.00

Exactly 1,000, which means it measures along the centreline and adds nothing for the width. Knowing that is knowing when to use it: pipework drawn as a wide polyline gives the true pipe length, correct. A wall drawn as a wide polyline gives the length of the wall centreline, not the perimeter of its faces.

Polylines and setting their width are covered in our article on polylines in AutoCAD.

Drawing units and what the figure means

The number that comes out is in drawing units, whatever those happen to be. Nothing in the file converts anything, and nothing asks.

That matters more than it sounds, because a drawing received from elsewhere does not announce its units. A site plan drawn in metres and a detail drawn in millimetres look identical on screen. Totalling a run in one and comparing it with the other is how a quantity ends up a thousand times out.

The check takes five seconds: measure something whose real size you know — a door leaf, a standard bar diameter, a grid spacing — and see whether the figure reads 900 or 0.9. Units are covered in our article on units in AutoCAD.

Five things people say that are not true

What people sayWhat was measured
AutoCAD has a command that totals lengthsIt does not; LIST prints one by one, MEASUREGEOM measures one by one
Select several lines and Properties shows the totalThe Length field disappears once more than one object is selected
Whatever the lisp prints is the answerTwo coincident lines came out as 2,000
Selecting the whole drawing totals everythingAnything inside a block is left out
A polyline with width is measured across its widthIt is measured along the centreline: 1,000 gives 1,000

Frequently asked questions

Which kinds of object does it add up?

Lines, arcs, circles, ellipses, polylines and splines. Circles and ellipses are measured by circumference.

Why is the figure higher than I expected?

Most likely the drawing holds duplicate linework. Check the count column: one line on screen with a 2 beside it means duplicates. Run OVERKILL and add up again.

Are lines inside blocks counted?

No, and the command prints a Skipped ... object(s) line to say so. Explode the blocks, or measure one and multiply by the insertions.

What units does the result come out in?

Drawing units. A drawing in millimetres gives millimetres, and the Divided by 1000 line gives the same figure in metres.

How do I put the result on the drawing?

Use TCDT: after adding up it asks where to put the text and writes the figure there.

Can it total each layer separately?

Yes, with TCDL. It prints a table by layer with the object count for each one.

About the author

TS. Đỗ Quốc Hoàng — Vice Director of the Institute of Information Technology in Civil Engineering, Hanoi University of Civil Engineering, and a specialist in software development for construction