Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
775fc02
Add structural LVA domain specification
Mining-Geologist Jul 14, 2026
616f66d
Add clustered structural LVA interpolant
Mining-Geologist Jul 14, 2026
5d64ff3
Add structural interpolant field-function adapter
Mining-Geologist Jul 14, 2026
a6eb339
Expose structural LVA interpolation headers
Mining-Geologist Jul 14, 2026
6f6e15e
Add Python bindings for structural LVA interpolation
Mining-Geologist Jul 14, 2026
8c2d4f4
Build structural LVA Python extension
Mining-Geologist Jul 14, 2026
9556042
Export structural LVA Python classes
Mining-Geologist Jul 14, 2026
c33c1d3
Add structural interpolant regression tests
Mining-Geologist Jul 14, 2026
ac08aad
Register structural interpolant unit tests
Mining-Geologist Jul 14, 2026
8017abf
Use installed MSVC toolset on Windows
Mining-Geologist Jul 14, 2026
c8f37cf
Fix SdfDataGenerator Python binding compatibility
Mining-Geologist Jul 14, 2026
4b994d3
Implement SdfDataGenerator compatibility overload
Mining-Geologist Jul 14, 2026
908a702
Add automatic structural trend domain builder
Mining-Geologist Jul 14, 2026
b982bb5
Expose structural domain builder in public API
Mining-Geologist Jul 14, 2026
fee006d
Expose automatic mesh-driven structural trend builder
Mining-Geologist Jul 14, 2026
a5c1f68
Fix fit_from_meshes Python argument ordering
Mining-Geologist Jul 14, 2026
361ca70
Test automatic structural trend domain builder
Mining-Geologist Jul 14, 2026
73202fa
Document mesh-driven structural LVA API and parity limits
Mining-Geologist Jul 14, 2026
c2d23c9
Improve automatic structural domain layout and continuity
Mining-Geologist Jul 14, 2026
ec16a91
Add axial domain orientation averaging experiment
Mining-Geologist Jul 14, 2026
0ecd430
Use axial orientation averaging for Python structural domains
Mining-Geologist Jul 14, 2026
28d3cbf
Restore center-sampled domains as default after averaging test
Mining-Geologist Jul 14, 2026
a318be5
Add adaptive orientation-consistency structural domain builder
Mining-Geologist Jul 14, 2026
c306ec2
Expose adaptive structural domain builder to Python
Mining-Geologist Jul 14, 2026
2d833fd
Add orientation averaging for adaptive structural domains
Mining-Geologist Jul 15, 2026
f61fdc7
Expose orientation-averaged adaptive domain build
Mining-Geologist Jul 15, 2026
6fe6fbc
Add optional overlap offset alignment to structural interpolant
Mining-Geologist Jul 15, 2026
4fb7a81
Expose structural overlap alignment controls
Mining-Geologist Jul 15, 2026
1c73132
Refine overlap alignment near zero level
Mining-Geologist Jul 15, 2026
2f1131a
Add validated clustered structural domain builder
Mining-Geologist Jul 16, 2026
f0eee07
Expose clustered structural domain builder
Mining-Geologist Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions docs/structural_lva.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Structural LVA prototype

The `feature/structural-lva` branch contains a native Polatory structural-LVA path.

## Python API

```python
import numpy as np
import polatory
from polatory import three as p3

rbf = p3.CovSpheroidal3([10.0, 100.0])
model = p3.Model(rbf, 0)

trend_input = polatory.StructuralTrendInput3(
vertices=mesh_vertices,
faces=mesh_faces.astype(np.int64),
strength=5.0,
range=50.0,
)

structural = polatory.StructuralInterpolant3(
model,
outside_value=-1.0,
blend_power=7.0,
)

domains = structural.fit_from_meshes(
points,
values,
[trend_input],
tolerance=1e-6,
trend_type=polatory.StructuralTrendType.STRONGEST_ALONG_INPUTS,
)

predictions = structural.evaluate(query_points)
```

`fit_from_meshes` automatically:

1. calculates equal-weight incident triangle normals at mesh vertices;
2. finds the nearest mesh vertex to each structural sample location;
3. applies the recovered single-input decay law;
4. constructs determinant-one local anisotropy matrices;
5. divides the interpolation data into overlapping spatial domains;
6. assigns every data point inside each expanded domain box as local support;
7. fits and blends the local Polatory interpolants.

## Recovered single-input field

For the nearest mesh-vertex normal `n`, distance `d`, input `strength`, and input `range`:

```text
q = exp(-d / range)
r = 1 + (strength - 1) * q
M = r^(-1/3) * (I - n n^T) + r^(2/3) * (n n^T)
```

The equal-weight vertex normal and this matrix equation were verified against the supplied Leapfrog project.

## Trend types

The public API exposes Leapfrog-style names:

- `STRONGEST_ALONG_INPUTS`
- `BLENDING`
- `NON_DECAYING`

Multiple mesh inputs are accepted. `STRONGEST_ALONG_INPUTS` selects the input with the largest local decayed anisotropy contribution.

`BLENDING` currently uses an axial weighted-normal blend. This is an experimental implementation. The Leapfrog manual confirms that multiple inputs are blended according to individual strength and that the result decays away from the meshes, but it does not publish the exact vector/tensor combination rule.

## Parameters suitable for an RSGeo UI

User-facing structural trend settings:

- name;
- trend type;
- one or more mesh inputs;
- per-input strength;
- per-input range for decaying modes;
- optional global mean trend in a later parity update;
- compatibility mode in a later parity update.

Advanced internal settings that should normally stay hidden:

- domain size;
- domain overlap;
- minimum local support count;
- local blend power.

## Known parity limits

The following still require controlled Leapfrog A/B tests before claiming exact parity:

1. multiple-input `BLENDING` orientation and strength equations;
2. transition behaviour where two inputs have equal influence in `STRONGEST_ALONG_INPUTS`;
3. global mean trend interaction;
4. Version 1 versus Version 2 compatibility;
5. Leapfrog's internal automatic domain decomposition;
6. whether one tuned local blend power generalises across unrelated datasets.

The automatic domain builder is therefore ready for compilation and single-mesh validation, while multi-mesh blending remains explicitly experimental.
33 changes: 33 additions & 0 deletions include/polatory/isosurface/structural_rbf_field_function.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include <limits>
#include <polatory/geometry/bbox3d.hpp>
#include <polatory/geometry/point3d.hpp>
#include <polatory/isosurface/field_function.hpp>
#include <polatory/structural/interpolant.hpp>
#include <polatory/types.hpp>

namespace polatory::isosurface {

class StructuralRbfFieldFunction : public FieldFunction {
static constexpr double kInfinity = std::numeric_limits<double>::infinity();

public:
explicit StructuralRbfFieldFunction(structural::StructuralInterpolant3& interpolant,
double accuracy = kInfinity)
: interpolant_(interpolant), accuracy_(accuracy) {}

VecX operator()(const geometry::Points3& points) const override {
return interpolant_.evaluate_impl(points);
}

void set_evaluation_bbox(const geometry::Bbox3& bbox) override {
interpolant_.set_evaluation_bbox_impl(bbox, accuracy_);
}

private:
structural::StructuralInterpolant3& interpolant_;
double accuracy_;
};

} // namespace polatory::isosurface
9 changes: 7 additions & 2 deletions include/polatory/point_cloud/sdf_data_generator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@ class SdfDataGenerator {
SdfDataGenerator(const geometry::Points3& points, const geometry::Vectors3& normals,
double offset, const Mat3& aniso);

// Compatibility overload for the existing Python binding. The current C++
// implementation uses a single offset; max_distance is used as that offset.
SdfDataGenerator(const geometry::Points3& points, const geometry::Vectors3& normals,
double min_distance, double max_distance, const Mat3& aniso);

const geometry::Points3& sdf_points() const;

const VecX& sdf_values() const;

private:
static std::pair<geometry::Points3, VecX> estimate_impl(const geometry::Points3& points,
const geometry::Vectors3& normals,
double offset);
const geometry::Vectors3& normals,
double offset);

geometry::Points3 sdf_points_;
VecX sdf_values_;
Expand Down
4 changes: 4 additions & 0 deletions include/polatory/polatory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <polatory/isosurface/isosurface.hpp>
#include <polatory/isosurface/rbf_field_function.hpp>
#include <polatory/isosurface/rbf_field_function_25d.hpp>
#include <polatory/isosurface/structural_rbf_field_function.hpp>
#include <polatory/model.hpp>
#include <polatory/numeric/conv.hpp>
#include <polatory/numeric/error.hpp>
Expand All @@ -31,5 +32,8 @@
#include <polatory/rbf/polyharmonic_odd.hpp>
#include <polatory/rbf/rbf.hpp>
#include <polatory/rbf/rbf_io.hpp>
#include <polatory/structural/domain_builder.hpp>
#include <polatory/structural/domain_spec.hpp>
#include <polatory/structural/interpolant.hpp>
#include <polatory/table.hpp>
#include <polatory/types.hpp>
Loading