BCad fork

Geometry introspection functions
Login

Geometry introspection functions

The geometry-introspection function family is a BCad-specific extension to the OpenSCAD language. Combined with the object-expression model (a call in expression position yields a node value — see docs/unified_callable.md), these functions operate directly on geometry values: you can "store" the result of a build into a variable and then inspect its geometric properties (face count, volume, bounding box, BRep validity). They are the standard tool for scripted tests (assert() + approximate comparison) and, with the new syntax, also handy for practical layout work — see the recipes below.

Storing geometry in a variable

Every geometry call works in expression position — no capture() needed:

c = cube([1, 2, 3]);
echo(str("Volume = ", volume(c)));

The variable holds the geometry node (an SCLContext); all diagnostic functions accept a node, a (possibly nested) list of nodes, or a raw SCLShape.

Example:

t360 = linear_extrude(height=2, twist=360)
  translate([20, 0, 0])
    rotate([90, -90, 0])
      square(2);

Diagnostic functions

All functions take a geometry argument (a node or a shape) and return a number or a vector:

Function Returns Notes
num_faces(geom) number number of faces
num_edges(geom) number number of edges
num_vertices(geom) number number of vertices
volume(geom) number volume (VolumeProperties().Mass())
surface_area(geom) number surface area
centroid(geom) [x, y, z] center of mass
bbox_min(geom) [x, y, z] bounding box min corner
bbox_max(geom) [x, y, z] bounding box max corner
is_valid(geom) true/false BRep validity (BRepCheck_Analyzer.IsValid())

Note on is_valid: BRepCheck_Analyzer validates topology strictly. Degenerate geometry (for example, extruding a profile that was rotated out of the XY plane — a "sheet" with zero thickness) is reported invalid, even though geometrically it is a legitimate 2D surface. This is expected behavior, not a bug.

Note on bbox_min/bbox_max: the box comes from OCCT's Bnd_Box, which deliberately grows every side by Precision::Confusion() (1e-7) so the box is a guaranteed container — a point may lie a hair outside its ideal boundary due to numeric rounding, and the box must still contain it. Consequently bbox_min(cube(1)) returns [-1e-07, -1e-07, -1e-07] and bbox_max returns [1.0000001, 1.0000001, 1.0000001] — expected, not a bug. When comparing bbox values use a tolerance above 1e-7 (the check_approx default eps=0.01 is fine). The gap is symmetric: the center (min+max)/2 is exact, while the size max-min is inflated by 2e-7 (negligible).

Empty geometry: when the node has no shape — x = group() { };, a failed or empty boolean operation — every function in the table above returns undef (not an error).

Example:

cyl = cylinder(h=5, r=3, $fn=12);
echo(str("faces = ", num_faces(cyl)));      // 14 (dodecagonal prism)
echo(str("valid = ", is_valid(cyl)));       // true
echo(str("bbox = ", bbox_min(cyl), " .. ", bbox_max(cyl)));

Practical recipes

With geometry stored as a value, the diagnostic functions double as layout tools:

m = cube([10, 20, 30], center=true);

// размер объекта (завышен на 2e-7 — допуск бокса, см. примечание выше)
sz = bbox_max(m) - bbox_min(m);                 // ~[10, 20, 30]

// центр по bbox — точный (допуск симметричен и сокращается)
center = (bbox_max(m) + bbox_min(m)) / 2;       // ~[0, 0, 0]

// пристыковать второй объект вплотную справа
a = cube([10, 10, 10]);
b = translate(bbox_max(a) + [0, 2, 0]) cube([10, 10, 10]);

// равномерно вписать объект в целевой размер по одному измерению
k = 20 / (bbox_max(a)[0] - bbox_min(a)[0]);     // 2 — куб 10 -> 20
s = scale(k) a;

// перенести центр объекта в заданную точку
p = [0, 0, 0];
t = translate(p - (bbox_max(a) + bbox_min(a)) / 2) a;

assert(condition, message)

Available both as a module and as a function. When the condition is false, it aborts the script with the given message (visible as a Traceback in test mode).

sq = square([2, 2]);
assert(num_faces(sq) == 1, "a square must have 1 face");

Together with the diagnostic functions it forms the standard test template (see the existing tests in tests/).

Library tests/lib/testing.scad

Included via use <lib/testing.scad>; provides an approximate-comparison helper:

function check_approx(a, b, eps=0.01) = ...

Typical test: ```scl use

m1 = cube([1, 1, 1]); assert(check_approx(bbox_min(m1), [0, 0, 0]), "bbox_min"); assert(check_approx(volume(m1), 1), "volume"); assert(is_valid(m1), "valid");

echo("--- OK ---"); ```

Running a test:

bcad-launcher --test tests/test_xxx.scad
The test is considered failed if the output contains a Traceback (from assert()).