pub fn grow(
mesh: &Triangulation,
index: &Bvh,
seeds: &[Point],
kind: FitKind,
options: &GrowOptions,
tol: Tolerances,
) -> OgeomResult<Grown>Expand description
Grow the region of mesh a surface of kind covers around seeds, and
fit it.
index is the hierarchy built over mesh. The mesh’s triangles are
read as wound counter-clockwise about the material’s outward normal,
which tells the fit the material’s side where
GrowOptions::side does not.
§Seeds
Each seed starts at the triangle nearest it. For a plane, its
neighbourhood is that triangle and the rings of triangles round it whose
normals lie within GrowOptions::normal_limit of its own, until they
hold twice the points the fit needs. For a curved kind it is the region
a plane grows from there: the part of the surface round the seed that
stays within the normal limit of one plane, over which the kind is
determined far better than over a few triangles, fitted from a consensus
start so a neighbouring surface’s points that joined it are cut off.
The first seed’s neighbourhood is fitted, keeps the triangles its fit admits, and grows. Each later seed whose neighbourhood the region has not reached joins where its neighbourhood agrees with the grown fit, and the region grows again; one that does not agree is left out, and reported with its offset and angle from the fit.
§Growth
A neighbour across an edge joins where its three vertices lie within the
gate of the model and its normal within the normal limit of the model’s
at its centroid. The gate is a prediction interval. A cutoff of k
sigmas over a fit of p parameters to n points measures the noise with
n - p degrees of freedom, so the gate is that estimate of the noise
times the Student t as rare as k sigmas of a known noise: as wide as a
few points’ noise could be, and k σ over many. It widens further at a
point by how far the fit’s own uncertainty moves the residual there, so a
model a small region determines poorly turns no triangle away for its
own error.
Where every coordinate of the mesh is a single-precision number, no gate
is below what rounding to it could do: a vertex is near within its own
coordinates’ rounding along the model’s normal, plus the cutoff’s sigmas
of the fit’s error from the region’s rounding, so a noise-free f32
mesh grows whole however its rounding fell.
The model is refitted from the last fit whenever the region has doubled since, and once more whenever no neighbour joins, every neighbour refused before asked again; growth ends when a whole pass against the latest fit admits none. So a bore stops at its end faces and a plane at its edges, however the mesh runs on across them.
§Peeling
A scanner rounds a sharp edge, and the round’s first rings lie within
the noise of the face they leave: they join, and pull the fit towards
them. GrowOptions::peel takes the region’s rim off, ring by ring,
while the rim’s normals turn away from the model’s by clearly more than
the noise turns them.
How far a rim triangle turns, across its rim edges, is to first order a
weighted sum of its vertices’ residuals: the gradient of the residuals
interpolated over the triangle, along the in-plane direction out of the
region. Summed over the rim, it is one weighted sum S = Σ cᵥ rᵥ of the
rim’s vertices’ residuals, a vertex shared by several rim triangles
weighing their weights together. Where the residuals are independent
noise of sigma σ, S is normal with standard deviation σ √Σ cᵥ². A
ring is peeled where |S| exceeds the gate times √Σ cᵥ², the gate
being k σ for Cutoff::Sigma(k) over many points: a ring of plain
noise is peeled as rarely as a point of plain noise is cut off. The
model is refitted after each ring.
§Curves
A FitKind::Circle3 or a FitKind::Line3 is traced along the
mesh’s creases rather than grown over its triangles: a crease edge is
one two triangles share across which the surface turns, between their
normals, by more than GrowOptions::crease_limit. A seed reaches the
crease edges about the smooth part of the surface it lies on, the
triangles reached from the one nearest it across edges that are not
creases. Of those within reach of the first seed, as many of its
triangle’s longest edge as the shortest chain accepted has edges, the
chain starts at the nearest whose chain holds twice the vertices the
kind’s fit needs and turns past the limit by more than the noise could
turn it, so a run of edges the noise alone turned past the limit is
passed over; a seed with no such chain about it is refused, by the
nearest chain’s fit’s own refusal where one refused and as lying on a
smooth part otherwise.
A chain turns past the limit by more than the noise where the angles
its edges turn through, less the limit, sum to more than the noise
could move that sum. Each point moved by noise of the sigma the
chain’s fit measures moves the sum, to first order, by its gradient
there; the sum’s sigma is the noise’s times the root of those
gradients’ squares, summed over the points the chain’s triangles hold.
The sum must exceed that sigma times the Student t, at the fit’s
degrees of freedom, as rare as the cutoff’s sigmas over the number of
edges within reach: of all the chains tried, one whose edges turn by
no more than the limit passes as rarely as a point of plain noise lies
that many sigmas out. A short chain measures its noise poorly and must
turn by more. With Cutoff::Distance or Cutoff::All, which count
no sigmas, every chain within reach passes.
The chain runs from the first seed’s edge both ways, a vertex at each
end in turn. Before a fit has a finite gate (with Cutoff::All,
never), it runs on through a vertex only where one crease edge leaves
it. Once one has, it runs on
along the crease edge whose far vertex lies within the gate of the fit,
the gate growth uses, its rounding floor included; where none does, or
where several do and the fit cannot tell which the crease follows, that
end stops. So a rim stops where its vertices leave the circle, and a
box’s edge at its corners, where every edge on turns past the line. The
fit is refitted whenever the chain has doubled since the last, and once
more whenever both ends stop, each asked again; tracing ends when a
whole pass against the latest fit adds no vertex, or the chain closes.
A crease that turns at a vertex no other crease meets, before a fit
over few vertices has a gate narrow enough to see it, is followed round
the turn.
A FitKind::Circle3 whose chain stops open runs on once more. A
circle over a short arc is poorly determined: its fit, run on from the
last, can settle in another minimum than one from nothing, a circle of
a hundred times the radius, and its gate is wide enough to admit both
the next vertex of the crease and the far end of a crease edge the
noise turned past the limit off its side. So where the fit lies outside
the confidence region of a fit from nothing, that one is taken, and an
end runs on only through vertices the crease runs on from. The chain
run on is kept where its fit’s noise is one the chain it ran on from
could have measured, by the ratio of their variances, and falls back to
where it stopped otherwise. Last, a circle that fits its points no
better than a line by more than the noise could make it, by the sums of
their squared residuals, has a radius they do not determine, and is
refused.
The answer’s triangles are empty, and its vertices are the chain’s.
Later seeds are reported against the fit, and do not extend the chain.
GrowOptions::normal_limit and GrowOptions::max_triangles apply
to regions only, except as below. Grown::crease is
Crease::Traced.
§Rounded edges
A scan rounds a box’s edge, so no edge of it may turn past the crease
limit by more than the noise. Where a FitKind::Line3 finds no chain
about the first seed, the line is sought instead where the planes
either side of the round meet: the sharp edge the round stands in for,
Crease::Meeting.
The round’s axis is the way the surface about the seed turns least: the rings of triangles about the seed’s grow until their normals spread one way across their mean by more than the other, by as much at least as normals spread evenly over the crease limit. The mesh is cut across that axis through the seed, and each side’s planes are grown, peeled, from the triangles the cut reaches 1, 2, 4, … triangles out. A pair meets across a round where all of these hold:
- neither plane holds the seed’s triangle, nor do they share one, and each keeps twice the points a plane needs;
- each is flat: its sigma squared over the noise its mesh shows inside
it (each inner vertex’s offset from its neighbours’ mean, taken about
the offsets’ mean so a constant bend drops out) is no larger than
the cutoff’s sigmas of chance allow, the ratio of two estimates of one
noise; or under
Cutoff::Distance, its sigma is within the distance; - they turn past the crease limit by more than their own uncertainty puts on the angle between them, at the cutoff’s sigmas;
- the cut reaches each side’s plane before the other’s, and the line where they meet lies over the round between where it enters them, by more than the planes’ uncertainty moves it;
- the way across the round along the cut is shorter than each plane is wide across the line, and they run beside each other along it.
Of those, the pair whose narrower plane is widest for the way across
the round is taken: the faces either side of the round, not a facet of
it nor faces further off. The answer’s fit is the line, located in the
middle of where both planes run beside it; its vertices are the first
plane’s and then the second’s, and its residuals each point’s from its
own plane, signed as the plane signs them. Meeting holds the planes,
their regions, the angle between them and the line’s span. A seed is
in the region where its triangle is on neither plane, within the span
and within the round’s width of the line; its angle is the planes’.
Where no pair meets across a round, or with Cutoff::All, which has
no threshold to peel by, or with anything held fixed, the seed is
refused as it is for want of a chain.
§Determinism
Everything runs on the calling thread in an order fixed by the seeds and the triangle indices, and the consensus start draws a fixed sequence: the same mesh and seeds give the same region and fit bit for bit on any thread count.
§Errors
OgeomError::Domain with no seeds, a
seed that is not finite, a normal limit or a crease limit out of range,
a cutoff whose number is not positive, peeling with Cutoff::All or
for a curve, a triangle cap of zero, or a triangle naming a vertex the
mesh lacks;
OgeomError::Dimension where the
hierarchy holds a different number of triangles from the mesh;
OgeomError::Construction where
the mesh has no triangle to be near a seed, or for a curve where no
crease within reach of the first seed holds twice the vertices the kind
needs and turns past the limit by more than the noise, or none about a
later seed, and, for a line, no two planes meet across a round about
the first, or for a circle where its crease is straight within its
noise; and the kind’s
own refusals, as its fit gives them.