Takeoff Formulas

The complete formula reference for table takeoffs — the four measures, filters, comparisons, deductions, and arithmetic.

Introduction

A takeoff formula answers one question about your markups: how much of this is there? You write it into a table cell, and the table recomputes it from the current markups every time it opens.

Every formula is a measure wrapped around a filter:

area(ext-wall)
│    │
│    └── the filter — which markups to include
└─────── the measure — what to read off them

That is the whole shape. Everything below is a longer answer to "what can go inside the parentheses".

What a cell accepts

A measure cell takes three kinds of entry, and it works out which one you meant:

You typeYou get
4500A pinned number. It stays put and stops following the markups.
4500 LSA lump sum with a unit — for scope you are not marking up.
area(ext-wall)A live takeoff that recomputes from your markups.

Typing a formula into a pinned cell makes it live again.


The four measures

MeasureReadsImperialMetric
count(…)Every matching markup is oneEAEA
length(…)Path length — the perimeter for closed shapesLFm
area(…)Enclosed areaSF
volume(…)Area × the markup's depth propertyCY

The measure is a lens, not a property of the markup. The same rectangle counts as 1 EA in a count cell, gives its perimeter in a length cell, and gives width × height in an area cell — you choose what to read off it.

A markup whose geometry cannot answer the question — the length of a point, the area of an open line, the volume of a markup with no depth — is left out of that cell and reported as an exclusion. It is never quietly counted as zero.

Pages with no scale set are excluded from totals for the same reason: there is no honest way to add feet to paper inches.


Filters

Everything inside the parentheses is the filter. There is no where keyword — conditions simply sit in the parentheses, joined by operators.

Naming what to measure

A bare word is a tag or a preset name:

count(receptacle)
area(ext-wall)

Preset names work by assignment: any markup wearing the Exterior Wall preset matches ext-wall, and so does a markup wearing a preset nested inside it. You never have to tag things twice.

Matching ignores case and punctuation — Exterior Wall, exterior-wall and exterior_wall are all the same name. But you cannot type a space. Write a multi-word preset with a hyphen or underscore:

area(exterior-wall)     ✅
area(exterior wall)     ❌ unexpected "wall" after filter

Combining conditions

OperatorMeansExample
&Andarea(ext-wall & rated)
, or |Orcount(window, door)
!Notcount(door & !temporary)
( )Groupingarea(wall & (rated, fire-wall))

Comparing a property

Beyond tags, you can filter on the values your markups carry:

area(window & u_value<=0.3)
count(door & element_type=A)
length(pipe & size>=4 & size<=12)

The comparisons are =, <= and >=. That is the complete list:

  • Bare < and > are rejected — write <= or >=
  • There is no != — write !key=value instead
  • There is no == — one equals sign is the comparison

A name on the left of a comparison means one of your markup's properties, with five exceptions that refer to the markup itself:

Reserved nameCompares against
typeThe tool that drew it (rectangle, polyline, measurement…)
pageThe sheet number
statusThe markup's status
labelThe markup's label
originWhere the markup came from
count(type=rectangle)
count(page>=2 & status=approved)

Anything else — element_type, u_value, finish — reads from the markup's properties. Nested properties use a dot: spec.size=3068.

tag= does not do what it looks like. tag is not one of the reserved names, so count(tag=window) looks for a property called "tag" and finds nothing. Tags are always written as bare words: count(window).

Quoting values

Values only need quotes when they contain a space or one of , & ! ( ) < > = " ':

count(door & finish=oak)              ✅ no quotes needed
count(door & finish="Type X")         ✅ quotes for the space
count(door & size="3'-0\"")           ✅ escape a quote with \

Either "double" or 'single' quotes work.


Deducting openings

Subtract one measure from another to net out openings. Both sides must be the same measure:

area(ext-wall) - area(window, door)
area(slab) - area(opening)

Deductions are tracked separately from the gross, so a cell can always show you what it took off and why.

One thing to watch: area(a) - (area(b) - area(c)) adds c back, exactly as the parentheses say. Chain your deductions instead of nesting them.


Arithmetic

Formulas take +, -, *, /, parentheses and plain numbers:

(area(ext-wall) - area(window)) * 1.1     10% waste
area(slab) / 9                            SF to SY
count(fixture) * 1.15

Adding and subtracting require the same kind of quantity. Square feet minus linear feet is a modelling mistake, not a number, and the formula will say so:

area(wall) - length(base)     ❌ cannot subtract LF from SF

Multiplying and dividing change the kind on purpose: area ÷ length gives a length, area × a number stays an area.


Column formulas

A formula column is the other kind of formula, and it works on your other columns rather than on markups:

quantity * unit_price
(material + labor) * 1.15

The names are column keys, not the headings you see — a column headed "Unit $" may be keyed unit, and the column editor tells you which key it minted. Suggestions complete as you type, and a formula naming a column that does not exist cannot be saved.

Anything blank stays blank: if a row has no unit_price, its total is empty rather than wrong. Dividing by zero gives a blank cell for the same reason.


Recipes

To getWrite
Count of a presetcount(receptacle)
Wall area net of openingsarea(ext-wall) - area(window, door)
Only one sheetcount(door & page=3)
Everything except one tagcount(door & !temporary)
Either of two tagscount(window, louver)
A numeric rangelength(pipe & size>=4 & size<=12)
Excavation volumevolume(footing) — needs a depth on each markup
Add wastearea(gwb) * 1.05
Convert SF to SYarea(paving) / 9

The warnings on a cell

A takeoff is dangerous when it is quietly wrong, so a cell says what is wrong with its formula rather than leaving you a plausible number. Three small markers can appear beside a quantity, and hovering any of them explains it.

MarkerWhat happened
name? (amber)The formula compiles, but it names a tag, a property or a value that does not exist anywhere in this version. The cell holds a real number — usually 0 — and that number is almost certainly wrong. Where the miss looks like a near-match, the tooltip suggests the name you probably meant.
formula? (red)The expression does not parse. The cell stays empty however much gets drawn. The tooltip carries the compiler's own complaint.
math? (red)Every markup resolved and the arithmetic still has no answer — a division by zero, or a result too large to hold.

The same check runs while you type, so the warning appears next to the field before you ever commit the cell.

name? is deliberately quiet in two cases, because in both of them silence is the honest answer:

  • A range. size>=4 is not checked against the sizes anyone has drawn — the endpoint of a range need not exist.
  • A key with no value list. Continuous fields (a depth, a free label) carry too many distinct values to enumerate, so any value compares clean.

When a cell comes back empty

A cell with no warning on it and an empty or zero value is nearly always one of these:

  1. A filter nothing satisfies yet. Every name is real; no markup carries all of them at once. This is a legitimate zero.
  2. A field that holds no value. A field defined as a checkbox is a flag — it marks membership but stores nothing, so field=something can never match it. Check the field's type in the console; retype it to Text, Number or Select if it should carry a value.
  3. An uncalibrated page. Set the scale and the quantities appear.
  4. A volume with no depth. volume(…) needs a depth on each markup.