Scattered plants, rocks and people: how many and where
Works out how many items a scatter set asks for from the area it covers and its density, and picks the points they stand on.
How the precision is kept
The target area is summed in 64-bit from the target's world triangles (a Float64Array by the file's own description), and the count is that area times the density, rounded to a whole item. Which points are used is set by a seeded random sequence, so the same set and seed give the same items. The placement matrices that result are stored in a Float32Array and only feed the renderer.
Calculations under this policy
4 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 area of the faces a scatter set covers and the number of items its density asks for.
area = Σ ½ |e₁ × e₂|; asked = min(2,000,000, round(area × density)); paint: Σ max(1, round(π r² × density))
Areas are added in triangle order into one 64-bit total, which also serves as the cumulative table in a Float64Array. The count is rounded to a whole number with Math.round. The budget is truncated with a bitwise OR, which acts on a count of instances, not on a measured value.
rt/rt-scatter.js
lines 74 to 90
BimCraft 0.8.1
const cdf = new Float64Array(nT), nrm = new Float64Array(nT * 3);
let area = 0;
for (let i = 0; i < nT; i++) {
const o = i * 9;
const ax = tris[o + 3] - tris[o], ay = tris[o + 4] - tris[o + 1], az = tris[o + 5] - tris[o + 2];
const bx = tris[o + 6] - tris[o], by = tris[o + 7] - tris[o + 1], bz = tris[o + 8] - tris[o + 2];
const cx = ay * bz - az * by, cy = az * bx - ax * bz, cz = ax * by - ay * bx, l = Math.hypot(cx, cy, cz);
area += l / 2; cdf[i] = area;
let nx = cx / (l || 1), ny = cy / (l || 1), nz = cz / (l || 1);
if (nx * U[0] + ny * U[1] + nz * U[2] < 0) { nx = -nx; ny = -ny; nz = -nz; } // faces scatter on their upper side
nrm[i * 3] = nx; nrm[i * 3 + 1] = ny; nrm[i * 3 + 2] = nz;
}
let candidates = 0;
if (S.mode === 'fill') candidates = Math.round(area * S.density);
else for (const s of S.strokes) candidates += Math.max(1, Math.round(Math.PI * s[3] * s[3] * S.density));
const wanted = Math.min(MAX_INSTANCES, candidates);
const cap = Math.min(wanted, Math.max(0, budget | 0));
A random point on the target
64-bit
Picks a triangle in proportion to its area and a point inside it.
P = A + a (B − A) + b (C − A), (a, b) reflected when a + b > 1
The triangle is found by binary search of the 64-bit cumulative areas; the index halving is a bit shift on an index. The point is a 64-bit blend of the three corners.
rt/rt-scatter.js
lines 147 to 153
BimCraft 0.8.1
const u = R() * area;
let lo = 0, hi = nT - 1;
while (lo < hi) { const mid = (lo + hi) >> 1; if (cdf[mid] < u) lo = mid + 1; else hi = mid; }
let a = R(), b = R();
if (a + b > 1) { a = 1 - a; b = 1 - b; }
const o = lo * 9;
for (let k = 0; k < 3; k++) { p[k] = tris[o + k] + (tris[o + 3 + k] - tris[o + k]) * a + (tris[o + 6 + k] - tris[o + k]) * b; n[k] = nrm[lo * 3 + k]; }
Dropping a point onto the target
64-bit
Where a vertical ray from a brush dab meets a target triangle.
t = (e₂·q) ÷ det, det = e₁·(d × e₂)
Möller–Trumbore in 64-bit. The ray starts 1,000 m above the point, so t is near 1,000 and the hit keeps correspondingly fewer digits after the decimal point. A determinant under 1e-12 is treated as parallel; the threshold is absolute, not scaled to the triangle.
Triangle area as half the length of a cross product, added in triangle order into a running total used as a cumulative table; a point by binary search of that table and two random numbers folded into the triangle; paint mode drops points onto the target by a two-sided Möller–Trumbore ray test.
Number types
Float64Array for the area table and normals; 64-bit numbers for points; Float32Array for the instance matrices; Uint16Array for item indexes.
Tolerances
Ray and triangle parallel when the determinant is under 1e-12 (absolute). Slope limit compared with cos(slope) − 1e-9. Caps: 2,000,000 instances, density 400 per m², 4,000 brush dabs.
Limits
The count placed can be lower than the count asked for: slope, height and clearance filters and the instance budget remove candidates. Instance positions are kept to 32-bit in the matrices.
Approach: 64-bit, stated limit
The arithmetic is 64-bit, but the method or the data sets the accuracy: a solver tolerance, a mesh, a step size, a datum shift, a table. The limit is stated where the result is shown.