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 type | You get |
|---|---|
4500 | A pinned number. It stays put and stops following the markups. |
4500 LS | A 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
| Measure | Reads | Imperial | Metric |
|---|---|---|---|
count(…) | Every matching markup is one | EA | EA |
length(…) | Path length — the perimeter for closed shapes | LF | m |
area(…) | Enclosed area | SF | m² |
volume(…) | Area × the markup's depth property | CY | m³ |
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
| Operator | Means | Example |
|---|---|---|
& | And | area(ext-wall & rated) |
, or | | Or | count(window, door) |
! | Not | count(door & !temporary) |
( ) | Grouping | area(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=valueinstead - 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 name | Compares against |
|---|---|
type | The tool that drew it (rectangle, polyline, measurement…) |
page | The sheet number |
status | The markup's status |
label | The markup's label |
origin | Where 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.tagis not one of the reserved names, socount(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 get | Write |
|---|---|
| Count of a preset | count(receptacle) |
| Wall area net of openings | area(ext-wall) - area(window, door) |
| Only one sheet | count(door & page=3) |
| Everything except one tag | count(door & !temporary) |
| Either of two tags | count(window, louver) |
| A numeric range | length(pipe & size>=4 & size<=12) |
| Excavation volume | volume(footing) — needs a depth on each markup |
| Add waste | area(gwb) * 1.05 |
| Convert SF to SY | area(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.
| Marker | What 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>=4is 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:
- A filter nothing satisfies yet. Every name is real; no markup carries all of them at once. This is a legitimate zero.
- A field that holds no value. A field defined as a checkbox is a flag — it marks membership but stores nothing, so
field=somethingcan never match it. Check the field's type in the console; retype it to Text, Number or Select if it should carry a value. - An uncalibrated page. Set the scale and the quantities appear.
- A volume with no depth.
volume(…)needs adepthon each markup.