Works out the inside and outside size of a manhole, inlet or junction type, and writes the type as a library item in millimeters and reads it back.
How the precision is kept
Sizes are 64-bit with exact factors: 25.4 mm per inch, 12 inches per foot, and 1,200,000 ÷ 3937 mm per US survey foot written as that fraction. The library item's plan shapes, body and three size parameters are rounded to 1e-6 mm. The type's own parameters are written to the item unrounded, as text of the 64-bit value in millimeters, and divided by the same factor when read back.
Calculations under this policy
6 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.
The outside wall, inside wall and frame opening of a type as plan shapes in millimeters.
r = round₆((size × M) ÷ 2); M = 25.4 (inch) or 1 (mm)
Sizes are multiplied by the exact 25.4 and then rounded to 1e-6 mm with Math.round(v × 1e6) ÷ 1e6 before they are stored in the item.
structs.js
lines 103 to 114
CivilCraft 0.8.1
export function typePlan(k, units = 'ft') {
const M = mmPerDia(units), ents = [];
const shapeEnt = (layer, shape, w, l) => (shape === 'round'
? { type: 'circle', layer, color: null, x: 0, y: 0, r: r6((w * M) / 2) }
: { type: 'poly', layer, color: null, pts: [[-w, -l], [w, -l], [w, l], [-w, l]].map(([x, y]) => [r6((x * M) / 2), r6((y * M) / 2)]), closed: true });
// a rectangle runs along X: its length (l) is the X size, its width (w) the Y size
const W = k.shape === 'round' ? k.dia : k.l, D = k.shape === 'round' ? k.dia : k.w, wall = k.wall || 0;
ents.push(shapeEnt('STRUCTURE', k.shape, W + 2 * wall, D + 2 * wall));
if (wall > 0) ents.push(shapeEnt('STRUCTURE-DETAIL', k.shape, W, D));
if (k.frame) ents.push(shapeEnt('FRAME', k.frame.shape, k.frame.shape === 'round' ? k.frame.dia : k.frame.l, k.frame.shape === 'round' ? k.frame.dia : k.frame.w));
return { layers: [{ name: 'STRUCTURE', color: 7 }, { name: 'STRUCTURE-DETAIL', color: 8 }, { name: 'FRAME', color: 5 }], entities: ents };
}
Round outline as a polygon
64-bit
The outline of a round structure's body as points on a circle.
pᵢ = (r·cos(2π·i ÷ n), r·sin(2π·i ÷ n)), n = 48
Forty-eight points on the circle, each from one 64-bit cosine and sine of 2π·i ÷ n, so the points do not drift. The polygon lies inside the circle: its area is that of a regular 48-gon, about 0.3 % less than the circle's.
structs.js
lines 100 to 101
CivilCraft 0.8.1
// ---- the library item ----
const circlePoly = (r, n = 48) => Array.from({ length: n }, (_, i) => [r * Math.cos((2 * Math.PI * i) / n), r * Math.sin((2 * Math.PI * i) / n)]);
Library item of a type
64-bit
The library item for a type: its body from the underside of the floor slab to the rim, its sizes, and every parameter in millimeters.
H = depth × U + base × M; body = outline from −H to 0; parameters as text of (value × factor)
The body vertices and the width, depth and height parameters are rounded to 1e-6 mm. The CC-STRUCT attributes are the unrounded 64-bit products written with String(), which prints enough digits to read the same 64-bit number back.
Turns a library item's millimeter parameters back into a type in the project's units.
value = millimeters ÷ M (sizes) or ÷ U (sump, depth)
Each parameter is read with Number and divided by the same factor it was multiplied by when written. The multiply and the divide each round once in 64-bit, so a value can come back different in its last digit.
structs.js
lines 162 to 176
CivilCraft 0.8.1
/** A library item made by CivilCraft → a type in the project's units (CC-STRUCT-* attributes; mm → in or mm). */
export function typeFromItem(item, units = 'ft') {
const a = item?.source_attrs;
if (!item || typeof item !== 'object' || item.format !== 'cadcraft-library-item') return { type: null, why: t('st.lib.notitem') };
if (!a || a['CC-STRUCT-FORMAT'] !== '1') return { type: null, why: t('st.lib.notours') };
const M = mmPerDia(units), U = mmPerUnit(units), n = (k) => Number(a[k]);
const shape = a['CC-STRUCT-SHAPE'];
const o = { name: String(item.name || ''), kind: a['CC-STRUCT-KIND'], shape, wall: n('CC-STRUCT-WALL-MM') / M, base: n('CC-STRUCT-BASE-MM') / M,
sump: n('CC-STRUCT-SUMP-MM') / U, depth: n('CC-STRUCT-DEPTH-MM') / U };
if (shape === 'round') o.dia = n('CC-STRUCT-DIA-MM') / M; else { o.w = n('CC-STRUCT-W-MM') / M; o.l = n('CC-STRUCT-L-MM') / M; }
const fs = a['CC-STRUCT-FRAME-SHAPE'];
if (fs) o.frame = fs === 'round' ? { shape: fs, dia: n('CC-STRUCT-FRAME-DIA-MM') / M, h: n('CC-STRUCT-FRAME-H-MM') / M } : { shape: fs, w: n('CC-STRUCT-FRAME-W-MM') / M, l: n('CC-STRUCT-FRAME-L-MM') / M, h: n('CC-STRUCT-FRAME-H-MM') / M };
const type = cleanType(o, units);
return type ? { type, why: '' } : { type: null, why: t('st.lib.bad') };
}
In detail
Method
Outside = inside + 2 × wall; height = depth + floor slab; a round body is a prism with 48 sides.
Number types
64-bit numbers; 25.4 and 1,200,000 ÷ 3937 are exact by definition.
Tolerances
Plan coordinates, body vertices and the width, depth and height parameters are rounded to 1e-6 mm.
Limits
The body of a round structure is a 48-sided prism with its corners on the circle, so its volume and surface are those of the polygon, not the cylinder. The plan shape of a round structure is a true circle.
Approach: 64-bit, direct
Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.