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
Multiplying and dividing change the kind on purpose: area ÷ length gives a length, area × a number stays an area.
Adding unlike quantities
You can add square feet to linear feet. It is usually a modelling mistake — and sometimes exactly the job:
area(facade) / 1.333 + length(facade) furring at 16" o.c., plus the trim run
What it costs is the unit, not the number. Nothing here can work out what SF + LF is measured in, so the cell computes the figure, shows an amber units? marker, and stays out of the quantity totals rather than joining the wrong one.
The same happens whenever the arithmetic lands somewhere no takeoff unit answers to, like area(a) * area(b).
Naming the unit yourself
Click the units? marker and pick one. That's the whole fix — the unit is set on that row, and the marker goes away.
You can label a cell whose formula compiles cleanly too, for the cases where the formula gets the unit wrong:
| Formula | Derives | Actually is |
|---|---|---|
area(paving) / 9 | SF | SY — a bare 9 carries no units for the formula to see |
area(facade) / 1.333 + length(facade) | nothing | LF |
area(gwb) * 1.05 | SF | SF — leave it alone |
A named unit is a label, not a conversion: the number stays exactly what it was. It belongs to the row, not the column, because a formula belongs to a row — labelling one row never relabels the row beside it. A fanned row's buckets all wear it, since they are slices of the same formula.
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
You write the headings you see, exactly as they read on the sheet — spaces and punctuation included, so Sub-total * Waste % names two columns rather than a subtraction. Case does not matter, suggestions complete as you type, and a formula naming a column that does not exist cannot be saved.
Renaming a column rewrites nothing: every formula using it simply reads back under the new heading. That is also why two columns on one table cannot share a heading — name a second one "Cost" and it saves as "Cost 2", so a formula always says which one it means.
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 — then label the cell SY |
| A field rate plus an edge run | area(f) / 1.333 + length(f) — then label it LF |
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. Four 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. |
units? (amber) | The number is exact, but nothing here can say what it is measured in — the formula adds unlike quantities, or lands outside the four measures. It carries no unit and reaches no quantity total until you say what it is. Click the marker to pick one. |
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.