Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .github/workflows/csharp-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,18 @@ jobs:
run: |
cd src/csharp
dotnet restore
dotnet restore GeometryKit/GeometryKit.csproj
dotnet restore GeometryKit.Tests/GeometryKit.Tests.csproj

- name: Build
run: |
cd src/csharp
dotnet build --configuration Release --no-restore
dotnet build GeometryKit/GeometryKit.csproj --configuration Release --no-restore

- name: Run Tests
run: |
cd src/csharp/Tests
dotnet test --configuration Release --verbosity normal
cd ../GeometryKit.Tests
dotnet test --configuration Release --verbosity normal
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Companion implementations and notes for Keenan Crane's *Discrete Differential Ge
| C# core mesh + DDG algorithms | **Partial** | Boundary-safe parity for topology, curvature, Laplacian, Heat divergence, and boundary mapping |
| Python tooling | **Supported utility** | Dependency-free OBJ topology inspector plus optional visualizer |
| Web companion | **Interactive reference** | Responsive workbench with a JS reference engine and optional WASM acceleration |
| GeometryKit (.NET) | **Foundation** | Double-precision mesh health, spatial queries, DDG fields and fabrication-support outputs |
| Test coverage | **Partial** | Closed/open topology and Gauss–Bonnet coverage; broader parity datasets remain future work |

See `docs/status/IMPLEMENTATION_AUDIT.md` for the detailed audit.
Expand Down Expand Up @@ -67,6 +68,8 @@ engine when compiled WASM artifacts are absent.
```text
src/cpp/ C++ core mesh, algorithms, tests, examples
src/csharp/ C# core mesh, algorithms, tests, CLI examples
src/csharp/GeometryKit/ Reusable double-precision mesh geometry library
src/csharp/GeometryKit.Tests/ Executable specifications for GeometryKit workflows
src/wasm/ Emscripten bindings and wasm build config
docs/ Chapters, formulas, assignments, tutorials, status docs
examples/ Example usage notes and Python helper script
Expand All @@ -82,6 +85,7 @@ web/ Interactive scientific workbench and legacy WASM pages
- `docs/tutorials/README.md`
- `docs/status/IMPLEMENTATION_AUDIT.md`
- `docs/status/NEXT_RELEASE_PLAN.md`
- `docs/geometrykit/README.md`

## Roadmap (Realistic)

Expand Down
8 changes: 8 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Documentation Index

## GeometryKit

- [GeometryKit overview](geometrykit/README.md)
- [Architecture decisions](geometrykit/adr/)
- [Mesh health and spatial-query mathematics](geometrykit/mathematics/mesh-health-and-spatial-queries.md)
- [Discrete differential-analysis mathematics](geometrykit/mathematics/differential-analysis.md)
- [Fabrication-support mathematics](geometrykit/mathematics/fabrication-support.md)

## Core
- [Algorithm Reference](algorithms/README.md)
- [Formula Reference](formulas/index.md)
Expand Down
52 changes: 52 additions & 0 deletions docs/geometrykit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# GeometryKit

GeometryKit is the production-oriented C# track beside the DDG course companion. It focuses on
derived triangle-mesh work: health checks, spatial queries, discrete surface analysis and
fabrication-support outputs. It is **not** a B-Rep CAD kernel and it does not own a host document.

## Implemented vertical slice

| Workflow | API | Deliberate boundary |
|---|---|---|
| Mesh health and queries | `MeshHealthAnalyzer`, `MeshSpatialIndex` | Diagnostics are non-mutating; callers choose repair. |
| Surface analysis | `SurfaceAnalysis` | Curvature and graph-geodesic fields are derived from an immutable mesh snapshot. |
| Fabrication support | `MeshSectioner`, `CurvatureSizing` | Returns section segments and target edge-length fields; it does not silently edit connectivity. |

The public geometry values use `Point3d`, not the course companion's `System.Numerics.Vector3`.
That is an intentional double-precision, host-neutral boundary.

## Quick example

```csharp
using GeometryKit.Analysis;
using GeometryKit.Core;
using GeometryKit.Fabrication;
using GeometryKit.Mesh;

var mesh = new TriangleMesh(
[new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(0, 1, 0)],
[new IndexedTriangle(0, 1, 2)]);

var health = MeshHealthAnalyzer.Analyse(mesh);
var index = new MeshSpatialIndex(mesh);
var projected = index.ClosestPoint(new Point3d(.2, .2, 1));
var curvature = SurfaceAnalysis.GaussianCurvature(mesh);
var section = MeshSectioner.Intersect(mesh,
new Plane3d(new Point3d(0, 0, 0), new Point3d(0, 0, 1)));
```

## Running tests

```bash
dotnet test src/csharp/GeometryKit.Tests/GeometryKit.Tests.csproj
```

See [ADR](adr/) for architecture decisions and [mathematics](mathematics/) for the equations,
assumptions and known limitations behind the first algorithms.

## Next implementation increment

1. Add a `geometry3Sharp` adapter behind this API; do not expose `g3` types publicly.
2. Add an actual sparse-solver package and heat-method geodesics.
3. Stitch section segments into tolerance-aware ordered polylines.
4. Add constrained remeshing that consumes the generated sizing field.
22 changes: 22 additions & 0 deletions docs/geometrykit/adr/0001-derived-meshes-are-immutable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# ADR 0001: Derived Meshes Are Immutable Snapshots

- Status: Accepted
- Date: 2026-10-10

## Context

Rhino, Revit and an OpenCascade-based CAD application each have their own document lifecycle,
transaction model, units and object identity. A mesh calculation that silently changes a host model
cannot be retried safely by an agent, compared in tests, or presented for user approval.

## Decision

`TriangleMesh` is immutable. GeometryKit operations return values, reports and new derived data;
they do not mutate an input mesh or call a host API. Host adapters own import, identifiers,
transactions and applying results.

## Consequences

- Deterministic functions are straightforward to unit test and expose over MCP.
- A caller must explicitly rebuild a spatial index after a host edit.
- Repairs/remeshing will return a new mesh plus a change report, never modify a live document.
22 changes: 22 additions & 0 deletions docs/geometrykit/adr/0002-public-api-uses-double-precision.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# ADR 0002: Use Double Precision at the GeometryKit Boundary

- Status: Accepted
- Date: 2026-10-10

## Context

The course companion currently stores positions as `System.Numerics.Vector3`. That is fine for a
small viewer but is a poor public contract for model-unit geometry, accumulated transforms and
near-tolerance classification. Rhino and most engineering/CAD APIs already treat doubles as the
normal interchange representation.

## Decision

GeometryKit exposes its own `Point3d` value type and `GeometryTolerance` model. Conversions to
host or third-party vector types occur only in adapters.

## Consequences

- More predictable numerical behaviour and no public dependency on a UI/runtime vector type.
- Every tolerance-sensitive API can state its model-unit contract.
- GPU/upload code may down-convert to float at rendering boundaries only.
21 changes: 21 additions & 0 deletions docs/geometrykit/adr/0003-mesh-analysis-is-not-cad-authority.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ADR 0003: B-Rep Remains CAD Authority

- Status: Accepted
- Date: 2026-10-10

## Context

The companion project will be used beside an OpenCascade-based CAD application. Tessellation loses
the parametric and topological semantics of the original B-Rep; making a mesh authoritative would
make exact editing and reliable STEP workflows worse.

## Decision

GeometryKit accepts derived meshes and returns analysis fields, section curves, reports, guide
geometry or replacement meshes. It does not claim lossless mesh-to-B-Rep round-tripping.

## Consequences

- CAD retains control of solids, feature history and exchange formats.
- GeometryKit can evolve independently for scans, terrain, simulation meshes and fabrication.
- A bridge can map results back to stable CAD selections or regeneration parameters where possible.
50 changes: 50 additions & 0 deletions docs/geometrykit/mathematics/differential-analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Discrete Differential Analysis

## Gaussian curvature by angle defect

For a vertex \(i\), sum the incident triangle angles \(\theta_{if}\). The integrated Gaussian
curvature is

\[
K_i = \begin{cases}
2\pi - \sum_f \theta_{if}, & i \text{ interior},\\
\pi - \sum_f \theta_{if}, & i \text{ boundary}.
\end{cases}
\]

The first density estimate uses the barycentric area

\[
A_i = \frac13 \sum_{f\ni i} A_f, \qquad k_i = K_i/A_i.
\]

For a closed triangulated surface, \(\sum_i K_i = 2\pi\chi\). For a topological disc the
boundary-aware formula includes its boundary turning contribution, so the same total applies.

## Mean-curvature normal

For edge \((i,j)\), sum half of the cotangents opposite that edge:

\[
w_{ij}=\frac12(\cot\alpha_{ij}+\cot\beta_{ij}),\qquad
\mathbf{Hn}_i=\frac{1}{2A_i}\sum_j w_{ij}(p_i-p_j).
\]

On a boundary, only the existing incident triangle contributes. The implementation reports the
vector and its magnitude; choosing a signed scalar requires a reliable orientation convention and
is intentionally left to the caller.

## Geodesic baseline

`EdgeGeodesics` applies Dijkstra's algorithm to mesh edges weighted by Euclidean edge length. It is
exact on the 1-skeleton but not an approximation of a smooth surface geodesic across triangle
interiors. It is included as a dependable baseline and explicitly not named `HeatMethod`.

The next DDG increment will add the heat method:

\[
(M - tL)u=M\delta,\qquad X=-\frac{\nabla u}{\|\nabla u\|},\qquad
L\phi=\nabla\cdot X,
\]

using a tested sparse solver, a selectable mass matrix and an explicit null-space constraint.
30 changes: 30 additions & 0 deletions docs/geometrykit/mathematics/fabrication-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Fabrication-Support Geometry

## Triangle-plane sections

For plane point \(o\), unit normal \(n\), and segment endpoints \(p_0,p_1\), compute signed
distances \(d_0=n\cdot(p_0-o)\) and \(d_1=n\cdot(p_1-o)\). If their signs differ, the segment
crosses the plane at

\[
p(t)=p_0+t(p_1-p_0),\qquad t=\frac{d_0}{d_0-d_1}.
\]

Each triangle emits zero or one independent segment after within-triangle point deduplication.
Coplanar triangles are counted and not emitted: emitting all coplanar edges would duplicate and
topologically confuse ordinary section contours. Joining segments into ordered polylines is a
separate next step because it requires a documented endpoint clustering tolerance and branching
policy.

## Curvature-driven sizing

The initial sizing field is a recommendation for a future remesher, not remeshing itself. It uses
a small-arc sagitta relation \(e\approx \kappa h^2/8\). With curvature scale
\(\kappa\approx\sqrt{|k_i|}\), desired chord error \(e\), and hard bounds, it produces

\[
h_i=\operatorname{clamp}\left(\sqrt{\frac{8e}{\kappa}}, h_{min}, h_{max}\right).
\]

This is a heuristic. It is suitable for concentrating a mesh in high-curvature regions, but does
not guarantee manufacturing error, feature preservation, collision clearance or developability.
34 changes: 34 additions & 0 deletions docs/geometrykit/mathematics/mesh-health-and-spatial-queries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Mesh Health and Spatial Queries

## Validity model

For every indexed triangle \((i,j,k)\), the first precondition is that the three indices are in
range and distinct. Its unsigned area is

\[
A_f = \frac12 \left\|(p_j-p_i) \times (p_k-p_i)\right\|.
\]

A triangle is flagged degenerate when its doubled area falls below a tolerance scaled by its local
edge length. This is intentionally a diagnostic rather than an automatic deletion: deleting faces
can change boundaries, connected components and material assignment.

Each undirected edge is counted. One incident face is a boundary edge; two is manifold; more than
two is non-manifold. The initial spatial and differential operators reject degenerate/non-manifold
input because both closest-point tie-breaking and cotangent weights become ambiguous or unstable.

## BVH queries

The spatial index recursively partitions triangle centroids and stores an axis-aligned bounding box
at each node. A closest-point search visits nodes in increasing lower-bound distance from the
query point and discards a node once its AABB lower bound exceeds the current best triangle result.

Ray tests use the slab intersection test for BVH pruning and Möller–Trumbore for individual
triangles. A hit includes triangle index, barycentric coordinates and parameter \(t\), allowing a
host to interpolate its own attributes without GeometryKit owning them.

## Limitations

- Current BVH splitting is median-centroid, not surface-area heuristic optimised.
- Open meshes are valid for projection and ray casting; "inside" classification is deliberately
absent until closed/oriented semantics and winding-number policy are explicit.
6 changes: 6 additions & 0 deletions src/csharp/DDGCompanion.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
<Compile Remove="Tests\**" />
<EmbeddedResource Remove="Tests\**" />
<None Remove="Tests\**" />
<Compile Remove="GeometryKit\**" />
<EmbeddedResource Remove="GeometryKit\**" />
<None Remove="GeometryKit\**" />
<Compile Remove="GeometryKit.Tests\**" />
<EmbeddedResource Remove="GeometryKit.Tests\**" />
<None Remove="GeometryKit.Tests\**" />
</ItemGroup>

<ItemGroup>
Expand Down
19 changes: 19 additions & 0 deletions src/csharp/GeometryKit.Tests/GeometryKit.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GeometryKit\GeometryKit.csproj" />
</ItemGroup>
</Project>
Loading
Loading