Grow Planar Patches
Grow Planar Patches extracts the planar surfaces exposed on a triangulated mesh — bedding planes, fracture and fault faces — as patches of triangles, each with a fitted plane, its dip and azimuth, its size and its quality. It measures the mesh first and derives its own parameters, so on most outcrops the defaults are a usable starting point.
It is the newer of VRGS's two patch tools. Auto Structural Mapping grows on orientation similarity alone; this tool fits a plane as it grows and tests candidates against both the plane's normal and their distance from it, which is what tells “the same surface” from “a parallel surface a step away”. Both write the same‑named attribute layers, so results can be compared on one mesh.
The method, stage by stage and with the formulas, is described in Grow Planar Patches — Method.
Typical Workflow
- Select the mesh in the Project Tree and open Extract → Grow Planar Patches. On a mesh it has not been run on, the dialog measures the mesh and fills every field; the read‑out says what it found.
- If the automatic Feature Radius does not suit the outcrop, set it to about the size of the smallest face you want extracted and press Suggest.
- Press Apply. When the run finishes the mesh is coloured by Patch Colour, one colour per patch, with the texture left showing on triangles in no patch, and the messages panel summarises the run.
- Judge the result on the model. If faces are broken into many small patches, press From Run and Apply again; if distinct faces have run together, lower Angle Tolerance or Distance Tolerance.
- When the patches look right, set Cluster into Sets, tick Create Objects if you want one orientation measurement per patch, and press OK.
To extract a single fracture set, see Extracting One Fracture Set.
Grow Planar Patches or Auto Structural Mapping?
| Grow Planar Patches | Auto Structural Mapping | |
|---|---|---|
| Grows on | Face normals, smoothed only as much as the measured noise needs | Per‑vertex Dip and Azimuth from Tensor Analysis, or face normals |
| A candidate is tested against | The patch's plane: its angle and its distance, both suggested from the mesh | The seed's, the current triangle's or the running mean orientation, with an optional distance |
| Boundaries | Moved afterwards to where neighbouring planes actually meet | Wherever growth stopped |
| Parameters | Measured from the mesh, refined from a run, remembered per mesh | Set by hand |
| Sets and filtering | Clusters the patches into sets; keeps a dip/azimuth range or the planes near one orientation | Dip and azimuth range filter |
| Writes | The patch layers, with Patch Set when it clusters; objects per patch or per set | The same patch layers but Patch Set; objects per patch |
Start with Grow Planar Patches. Auto Structural Mapping remains useful when you already have tensor Dip and Azimuth layers computed over a neighbourhood you trust, or want its dispersion and resultant‑length acceptance tests.
Prerequisites
- A selected
TriangulatedMeshin the Project Tree. No attribute layers are needed: the tool works from the mesh's own face normals.
Launch
- Project Tree: select a triangulated mesh, then Extract → Grow Planar Patches.
- Opening the dialog on a mesh it has not been run on measures the mesh and suggests every field. On a large mesh this takes a few seconds, and longer the first time if the triangle adjacency has to be rebuilt. On a mesh it has been run on, it restores the last parameters instead (see Remembered Parameters).
- Apply runs without closing; OK runs and closes; Suggest re‑measures the mesh and refills the growth fields; From Run refines them from the last run in the dialog. Neither touches the orientation filter, which is your choice rather than a measurement. Close closes the dialog without another run.
- The dialog stays open beside the rest of the application, so you can move the model, choose the attribute on display or select orientations between runs. A run with Create Objects adds its groups to the Interpretation tree without rebuilding it, so what is selected there stays selected. Opening the tool again on the same mesh brings its dialog forward; opening it on another mesh closes the first and opens a new one. Deleting the mesh or closing the project closes the dialog.
Progress shows in the messages panel as a planegrow progress bar, and each run ends with one summary line: patches, initial regions, merges, boundary moves, rejections, filled holes, absorbed fragments, assigned triangles and the measured normal noise. A filtered run adds a second line with the filter, the patches it kept and refused, and how many triangles face the wanted way.
Dialog Fields
Growth
- Angle Tolerance (deg): largest angle between a triangle's (smoothed) face normal and the patch normal. Also the merge angle.
- Distance Tolerance: largest distance, in model units, from any vertex of a candidate triangle to the patch plane. 0 disables the test and growth is angle‑only.
- Normal Smoothing Passes: crease‑preserving smoothing of the face normals before growth, over the ring of triangles around each vertex. Each pass widens the support by one ring. 0 grows on raw normals.
- Refinement Passes: boundary relabelling passes after merging, and fragment‑absorbing passes after rejection.
- Merge Co‑planar Patches: merge adjacent patches whose planes agree in angle and mutual distance.
- Fill holes up to: absorb unassigned islands enclosed by a single patch up to this many triangles. 0 disables.
- Feature Radius: the size of face the parameters are tuned for, in model units. It is the radius of the neighbourhoods the measurement samples. 0 lets Suggest choose: the larger of eight times the mesh resolution and half a percent of the model's diagonal.
- Read‑out: what the measurement found (see below), or after a run, what the run found.
Acceptance
- Minimum Triangles: patches with fewer triangles are rejected and their triangles left unassigned. 0 disables.
- Minimum Area: patches smaller than this area (squared model units) are rejected. 0 disables.
Orientation Filter
Limits a run to the planes you are interested in — one fracture set, only the steep faces, only bedding. Two tests, each with its own tick box; with both ticked a patch must pass both. Dips and azimuths are the ones the tool reports (the Patch Dip / Patch Azimuth layers, the per‑set lines in the messages panel, and orientation objects): azimuth is the dip direction.
- Keep only patches in a dip and azimuth range: Dip from … to … and Azimuth from … to …. The azimuth window runs clockwise from the first value to the second and may pass through north (340 to 20). Leave the azimuths at 0 to 360 for a dip‑only filter such as “everything steeper than 60”.
- Keep only patches near one orientation (a fracture set): Within (deg) of dip / azimuth. The angle is measured between the poles as axes, so a steep set keeps the planes leaning either way: 88° towards 090 and 88° towards 270 are four degrees apart, and both are within 15° of 90/090. A dip and azimuth range cannot express that in one window, so use this test for steep sets.
- From Set: fills the second test from set n of the last run in the dialog that clustered sets — the set's mean dip and azimuth, and a cone sized to hold about 95% of a set with that much scatter (between 5° and 45°).
- From Selected: fills the second test from the planes selected in the stereonet, the 3D view or the Interpretation tree, a selected orientation group counting as all its planes. The dip and azimuth are the planes' mean, taken on their poles as axes. Within (deg) keeps its current value as a minimum and widens to hold every selected plane. Lineations are left out, and the dip and azimuth range is not changed.
See Extracting One Fracture Set.
Outputs
- Cluster into Sets: number of orientation sets to cluster the patch poles into. 0 disables.
- Create Objects: one strike/dip orientation object per patch, in a “Plane Growing” group, or one group per set when clustering ran.
- Roughness Report: after each run, write the patches' roughness spectra as an analysis report and open it. See The Roughness Report. Remembered per mesh with the other fields.
Suggested Parameters
Suggest measures the mesh and fills every field. The read‑out shows the measurements:
- Resolution:
sqrt(mean triangle area), and the triangle count. - Adjacent‑normal noise: the median angle between the normals of edge‑adjacent triangles, raw and after the suggested smoothing. This is how rough the mesh is at the triangle scale.
- Within r = Feature Radius: 256 evenly spaced neighbourhoods of that radius, each fitted with a plane. The read‑out gives, for the flatter quarter of them (where a face will actually be grown): the 90th‑percentile scatter of the face normals about the fitted plane, the 90th‑percentile node offset from it, the RMS residual, and the triangle‑scale step.
From those:
- Smoothing passes = one per 6° of raw noise (1–6); the smoothing sigma is 1.5 × the raw noise, clamped to 5–45° and never below the tolerance. On a clean mesh this is one pass; on a rough photogrammetric surface several.
- Angle Tolerance = the larger of twice the smoothed noise and 1.2 × the neighbourhood normal scatter (5–30°). The first keeps a flat, noisy area from fragmenting by its own noise; the second keeps a clean but undulating face, whose adjacent normals differ by a few degrees while the face wanders tens of degrees end to end, from fragmenting into micro‑facets.
- Distance Tolerance = 1.5 × the neighbourhood node offset, never below twice the triangle‑scale step.
- Minimum Area = a quarter of the feature radius squared; Minimum Triangles 20; merge on; refinement 3; holes up to 10.
The Feature Radius is the main knob. Faces smaller than about half of it are treated as noise at that scale; faces much larger than it may still be split where they undulate. Set it to the size of the smallest surface you want extracted, press Suggest, and adjust from there.
Refining From a Run
After every run the read‑out shows what the run measured inside its patches — the median angle between adjacent smoothed normals within a patch, and the area‑weighted median RMS residual — together with the fraction of triangles assigned and what From Run would set. These measurements come from the mesh's confirmed planar areas, so unlike the sampled neighbourhoods they never straddle a crease.
From Run applies the rule:
- If less than half of the mesh was assigned, the run is taken as too tight: the angle tolerance is raised by a quarter and the distance tolerance by a half.
- Otherwise the angle becomes twice the intra‑patch noise and the distance three times the patch roughness, each damped so one step never tightens by more than a quarter.
Everything else is kept as it was. Run, look at the read‑out, press From Run, run again.
With an orientation filter the “fraction assigned” is counted against the triangles that face the wanted way rather than against the whole mesh, so extracting a set that covers a tenth of the outcrop is not mistaken for a run that was too tight.
Extracting One Fracture Set
- Run once with the filter off and Cluster into Sets set to the number of sets you expect. The messages panel lists each set's mean dip and azimuth, its area and its concentration, and the Patch Set layer shows where each one is.
- Type the number of the set you want beside From Set and press it. The second filter test is ticked and filled with that set's orientation and a cone sized from its scatter. Or select the planes of the set on the stereonet, in the 3D view or in the tree and press From Selected. Or type a dip, an azimuth and an angle of your own — from a stereonet, or from an orientation you measured.
- Set Cluster into Sets back to 0, or leave it to cluster the sets again within what is kept, and press Apply. Only the patches of that set are produced; the rest of the mesh is left unassigned. With clustering off, the run removes the Patch Set layer from step 1, whose set numbers described step 1's patches.
- Widen or narrow Within (deg) and run again. Filtered runs are quicker than full ones, because surfaces that face the wrong way are never grown.
The filter decides which patches are kept; it does not change how a patch grows. A kept patch has the same members the tolerances would give it in an unfiltered run, give or take the triangles along its edge that a neighbouring, now absent, patch would have claimed. The final decision is made on the fitted plane, so every value in Patch Dip and Patch Azimuth satisfies the filter exactly.
Remembered Parameters
The last‑used parameters, the orientation filter included, are remembered per mesh: for the session by the mesh itself, and across sessions in the application profile by mesh name. Opening the dialog on a mesh you have run before restores them and says so in the read‑out; Suggest re‑measures the mesh on demand and leaves the filter as it is.
What It Writes
Triangle attribute layers, 0 on unassigned triangles except where noted:
- Patches (int): patch id, largest patch first.
- Patch Colour (float): the id scrambled onto the colour ramp so neighbouring patches contrast. This layer is left active after a run, with its range fixed at 0 to 1 and Use Filter on over 0.05 to 1, so an unassigned triangle shows the mesh's texture or plain colour rather than the first colour of the ramp.
- Patch Size (int) and Patch Area (float).
- Patch Difference (float, degrees): angle between the triangle's raw face normal and its patch plane.
- Patch Similarity (float, 0 to 1): the cosine of Patch Difference, 1 for a triangle lying in its patch's plane and 0 for one at right angles to it. It is Auto Structural Mapping's measure, written here too so that both tools write the same layers.
- Patch CoPlanarity (float): Fernandez's M of the patch's node set, on the same scale as the per‑vertex CoPlanarity attribute (0 below four nodes).
- Patch RMS Residual (float): RMS node distance to the patch plane, model units.
- Patch Length / Patch Width (float): in‑plane extents of the fitted plane.
- Patch Dip / Patch Azimuth (float, degrees): of the fitted plane; azimuth is the dip direction. Unassigned triangles have no value in these two, rather than a 0 that would read as a horizontal plane.
- Patch Set (int): orientation set, when clustering ran.
- Patch JRC (float): the joint roughness coefficient of the patch (below). No value where a patch was too small to measure.
- Patch Hurst Dip / Patch Hurst Strike (float): the Hurst exponent of the patch's roughness, from profiles down-dip and along strike (below). No value where a patch was too small to measure that way.
- Patch Log Prefactor Dip / Patch Log Prefactor Strike (float): log10 of the spectral pre-factor C each way, C in model units cubed. The higher, the rougher.
- Patch Spectrum Spread (float, decades): the larger of the two directions' spread of log10 P(k) across the profiles, a noise flag.
Auto Structural Mapping writes all of these but Patch Set, which only a run of this tool with Cluster into Sets above 0 writes. Each run replaces the layers, and removes a patch layer it did not write rather than leave it describing an earlier run's patches: a run without clustering, or an Auto Structural Mapping run, removes the Patch Set left by a clustered run. The messages panel names any layer removed. If the stereonet's Colour by attribute was Patch Set, the mesh poles take the flat colour while the layer is gone.
JRC of each patch
Every run also measures how rough each patch is. Profiles are taken down the patch's dip — the direction a block would slide — through its nodes: up to five, spread across strike, each a strip one sampling interval wide. A nearly level patch is profiled along its longest extent instead. Each profile is resampled at an even interval, the mesh's median triangle step, and needs at least ten samples to count. After its trend is removed, the profile's Z2 (the root mean square of its slope) gives
JRC = 32.2 + 32.47 log₁₀(Z2)
(Tse & Cruden 1979, fitted at a 1.27 mm sampling interval), averaged over the profiles and kept between 0 and 20. Z2 depends on the interval, so at finer intervals the relation refitted by Yu & Vayssade (1991) is used instead: JRC = 60.32 Z2 − 4.51 at 0.25 mm, 61.79 Z2 − 3.47 at 0.5 mm and 64.22 Z2 − 2.31 at 1 mm, whichever interval is nearest to the mesh's.
- The JRC belongs to the length of the profiles: the messages panel gives the median JRC, the mean profile length and the sampling interval. JRC falls with length (Barton & Bandis 1982), so quote the length with it. No scale correction is applied.
- A mesh cannot show roughness finer than its own resolution. The profiles are sampled at the mesh's node spacing; when that is coarser than 1.27 mm, no relation is fitted there, so the messages panel warns that the JRC is a lower bound; check it against field profiles.
- With Create Objects on, each patch's orientation carries JRC and JRC Profile Length attributes, ready to inform a set's Barton-Bandis joint strength.
Roughness spectra of each patch
Every run also measures each patch's self-affine roughness from the power spectra of its profiles, after Corradetti et al. (2026): the Hurst exponent H, which says how the roughness changes with scale, and the spectral pre-factor C, which says how large it is. The two only mean something together: patches with one H can differ tenfold in amplitude. The measurement is described in full, with its validation, in Surface Roughness Spectra.
- The patch is resampled onto a grid in its fitted plane at the mesh's resolution, and every row is a profile down-dip and every column one along strike — the slip-parallel and slip-normal directions on a dip-slip fault. H and C are fitted to the mean power spectrum each way, over wavelengths from five grid cells up to half the profile length.
- A direction is measured only when the patch holds at least eight profiles of 128 samples, so at a 1 cm resolution a patch must be about 1.3 m long that way. Shorter profiles give an H that scatters by 0.3 or more from patch to patch; at 128 samples the scatter is about 0.2, at 256 about 0.1. Compare patches measured with profiles of similar length.
- H of 1 or more is not a self-affine surface: the patch holds a corrugation, a curve or a step. Patch Spectrum Spread well above 0.5 suggests a noisy mesh, which lowers H; a smoothed mesh reads H high. See Reading the numbers.
- The messages panel gives how many patches were measured and the median H each way.
- With Create Objects on, each patch's orientation carries Hurst Dip, Hurst Strike, Log Prefactor Dip and Log Prefactor Strike attributes, each way it was measured.
To map the same measurement over a whole mesh rather than per face, use the Roughness Map.
The Roughness Report
With Roughness Report ticked, each run writes an analysis report named “Patch roughness - mesh” in the project's Reports folder and opens it. It holds:
- a summary: patches measured, the median H and log10 C each way, and how many patches look noisy or read H of 1 or more;
- a figure of H against C for every patch measured, one marker per direction, which is where the paper reads surfaces apart — rough where C is high, jagged at small scales where H is low;
- a table of the patches, largest first (up to 60): area, dip and azimuth, H, log10 C, Rq and spread each way, and the JRC;
- for the four largest patches, their spectra: the mean power spectrum each way, the binned points and the fitted line over the shaded band, with the spread of log10 P at each frequency beneath against the 0.557 decades of a clean surface, and a table of the paper's three fits (all frequencies, progressive mean, progressive mean in the band) side by side;
- notes on reading the numbers, and the layers written.
When no patch is large enough to measure, no report is written and the messages panel says what size a patch would need. The report is written by Apply as well as OK, so untick the box while you are still tuning the growth.
Objects (optional): Plane_<id> (<triangles>), built from an even sample of at most 256 of the patch's nodes so a large patch does not drag thousands of vertices into the project. Groups are “Plane Growing”, or “Plane Growing Set k (dip/azimuth)” per set, and the messages panel lists each set's mean pole, area and concentration.
Patches on the Stereonet
There are two ways to get the patches onto a stereonet, depending on what one point on the net should stand for.
- One pole per patch. Tick Create Objects. Each patch becomes an orientation object, in one group per set when Cluster into Sets is on, and the stereonet plots them like any other orientation measurements. Contouring, selection on the net and the k‑means clustering in Orientations and fracture sets all apply. A small patch counts as much as a large one, so raise Minimum Area first if the small ones should not count.
- One pole per triangle, straight from the mesh. In the Stereonet window's Contour Properties, pick Patch Dip as the Dip Attribute and Patch Azimuth as the Azimuth Attribute, then set Data Source to Mesh Attributes to contour them, or switch the mesh Poles on to plot them (see Plotting a mesh's dip and azimuth). Every triangle plots with its patch's fitted plane, so a patch counts in proportion to its number of triangles, and no objects are created.
Unassigned triangles have no value in Patch Dip and Patch Azimuth, so the stereonet leaves them out: only the patches are plotted, however much of the mesh a filtered run leaves unassigned, and the Attribute Status row counts only the triangles in patches.
Picking a Patch as an Orientation Measurement
Once a mesh carries a Patches layer, the ribbon's Dip / azimuth → Attributes tool turns any patch into an orientation measurement with one click: pick the tool, click the patch on the mesh, and an orientation object named Patch_<id> is created in the active orientation group, its plane fitted through the patch's nodes (an even sample of at most 256 of them). The messages panel reports the patch id, its size and the dip and azimuth.
- Any layer can be displayed while you click; the tool reads the mesh's Patches layer, so Patch Colour is the convenient one to have on screen.
- Clicking an unassigned triangle creates nothing and says so.
- On a mesh without a Patches layer the tool falls back to its original behaviour: an orientation at the clicked point carrying the Dip and Azimuth vertex attribute values there, when those layers exist.
How It Works (Summary)
- Signal. Face normals and areas from the geometry, then bilateral (crease‑preserving) smoothing over the vertex‑ring neighbourhood, with the smoothing sigma derived from the measured noise. Normals are treated axially, so inconsistent winding does not matter.
- Growth. Seeds in order of local planarity. Best‑first growth against an area‑weighted proxy plane that refits with every accepted triangle. A candidate must be within the angle tolerance of the patch normal and, when a distance is set, have every vertex within that distance of the plane. A candidate rejected against a young plane is tested again when another of its neighbours is accepted. Every eligible triangle lands in exactly one initial patch.
- Refinement. Adjacent agreeing patches are merged; boundary triangles move to the neighbouring plane that fits them best; anything disconnected is split; patches below the size limits are rejected; enclosed holes are filled; leftover triangles that fit an adjacent patch join it.
- Outputs. A plane is fitted through each patch's distinct nodes for its statistics, patches are numbered largest first, and the layers and objects are written.
- Classification. Optionally, the patch poles are clustered into orientation sets by an area‑weighted axial k‑means.
The orientation filter, when on, acts three times, each stricter than the last. Triangles further from every passing orientation than the angle and merge tolerances together never seed a patch, which is where a filtered run saves its time. Patches whose growth plane settles outside the window are rejected along with the undersized ones, before holes are filled and fragments absorbed. And after the final plane fit a patch is kept only if the dip and azimuth it reports pass the filter literally.
Grow Planar Patches — Method gives every stage in full, with the formulas, the suggestion rules and the references.
For developers: CPlaneGrowerDlg drives CMeshPlaneGrower; the algorithm lives in PlaneGrowerCore.h, with a developer note in VRGS2020/MeshPlaneGrower.md.
Tips
- Compare with Auto Structural Mapping on the same mesh: both write the same layers (all but Patch Set), so the same layers and the same stereonet picks show either tool's patches. Each run replaces the layers of the run before.
- Display Patch Colour to see patches, Patches to read ids, Patch RMS Residual or Patch CoPlanarity to judge how planar each surface really is.
- To see the patches over the outcrop image, tick Use Filter on the displayed layer. On Patch Dip and Patch Azimuth that is all it takes: unassigned triangles have no value in those two, and a part of the mesh with no value never takes the colour ramp while the filter is on (inverted or not), so they show the mesh's texture while the patches keep their colours. (Unfiltered, those two layers paint unassigned triangles in the ramp's first colour, the colour of their lowest values.) A run sets the filter on Patch Colour itself. Every other patch layer holds 0 on unassigned triangles, so on those also raise Filter Minimum just above 0 — 1 on Patches. This is the natural way to look at a filtered run, where most of the mesh is unassigned on purpose. Narrow the window further (drag the histogram handles in the Charts panel) to pick out particular patches, or a band of dips on Patch Dip.
- On a mesh of several million triangles a run takes a minute or two; the progress bar shows which stage it is in.
- Hidden triangles are excluded from growth and never assigned. While the mesh's Use Attribute Filters is on, so are filtered triangles: those outside the window of an attribute with Use Filter ticked. Filters on the patch layers do not count, because a run replaces those layers. The filter on Patch Colour therefore cannot confine the next run to the old patches. To grow on part of the mesh, hide the rest, or filter it by another attribute, such as a Dip window for the steep faces.
Troubleshooting
- Everything fragments into tiny patches: the tolerances are too tight for the surface's undulation. Raise the Feature Radius and press Suggest, or run once and press From Run (it loosens when under half the mesh was assigned). More smoothing passes also help on rough meshes.
- Distinct faces merge into one: lower the angle tolerance, set or lower the distance tolerance, or turn Merge Co‑planar Patches off.
- Large unassigned areas: those triangles were in patches below Minimum Triangles / Minimum Area; lower the limits or raise Fill holes up to.
- Sets look wrong: clustering is area‑weighted axial k‑means with the number of sets you give it; try one more or one fewer set, and check the per‑set lines in the messages panel.
- A filtered run produces nothing: check the second summary line in the messages panel. If no triangles face the wanted way, the dip or azimuth is wrong for this mesh (remember the azimuth is the dip direction); if triangles do but no patches were kept, the patches there are below the size limits or the window is narrower than the set's scatter.
- Half of a steep set is missing: a dip and azimuth range keeps only the planes leaning one way. Use Keep only patches near one orientation instead, which treats the two sides of vertical as the neighbours they are.
- From Set does nothing: it reads the sets of the last run made in this dialog session. Run once with Cluster into Sets above 0 first.
- From Selected asks you to select planes: no plane is selected, or only lineations are, and a lineation does not define a plane filter. Select the planes first; the dialog can stay open while you do.