geomcore 0.37.0

A pure, standalone geometric kernel for CAD-grade curves and surfaces: parametric evaluation, transformations, and curve-on-surface parametrization.
Documentation
# Curve on surface

A curve that lies on a surface has an exact 2D image in the surface's
parameter space — the *pcurve*. `parametrize_on` computes it in closed
form where one exists (lines and circles on the five elementary
surfaces); `parametrize_numeric` samples anywhere else. Adapted from
[Parametric domain](https://avenirgeometry.com/articles/parametric-domain/).

## Why the rectangle matters

Kernels work in $(u, v)$ because the rectangle is flat: offsetting,
trimming and marching are all easier there than in space. But a uniform
grid in the rectangle is not uniform on the surface — the first
fundamental form stretches it — so every downstream algorithm must know
what the parameters measure (see [Elementary](../geometry/elementary.md#what-the-parameters-measure)).

<figure class="geom-fig">
<img src="../../assets/figures/parametric-domain/01-a-uniform-grid-drawn-on-the.svg" alt="A uniform grid drawn on the surface">
<figcaption>A uniform grid in (u, v) is not uniform on the surface: the metric stretches it.</figcaption>
</figure>

<iframe src="https://geomcore.github.io/geomcore-viewer/?scene=https://geomcore.github.io/geomcore/assets/scenes/param-grid.json" width="100%" height="480" style="border: 1px solid #333; border-radius: 8px;" allow="fullscreen" title="A uniform (u, v) grid on a sphere, one cell at the equator and one against the pole"></iframe>

## Analytic pcurves

A circle coaxial with a cylinder is a horizontal line in $(u, v)$; a
line in a plane is a line. The result is a `Line2D`/`Circle2D` (or a
`Curve2D` run-time value) that evaluates through the surface back onto
the 3D curve within `tol.confusion` — that check is what "lies on"
means, and anything farther off is `CurveNotOnSurface`.

=== "Python"

    ```python
    pc = circle.parametrize_on(cylinder)   # Line2D in (u, v)
    ```

=== "Rust"

    ```rust
    let pc = circle.parametrize_on(cylinder, tol).unwrap();
    ```

A few images across the surfaces:

<figure class="geom-fig">
<img src="../../assets/figures/parametric-domain/02-three-curves-on-a-cylinder.svg" alt="Curves on a cylinder">
<figcaption>On a cylinder: coaxial circles are horizontal lines, generators vertical ones.</figcaption>
</figure>

<iframe src="https://geomcore.github.io/geomcore-viewer/?scene=https://geomcore.github.io/geomcore/assets/scenes/param-cylinder.json" width="100%" height="480" style="border: 1px solid #333; border-radius: 8px;" allow="fullscreen" title="Three curves on a cylinder and their images in (u, v)"></iframe>

<figure class="geom-fig">
<img src="../../assets/figures/parametric-domain/04-three-curves-on-a-cone.svg" alt="Curves on a cone">
<figcaption>On a cone the same construction winds with the growing radius.</figcaption>
</figure>

<iframe src="https://geomcore.github.io/geomcore-viewer/?scene=https://geomcore.github.io/geomcore/assets/scenes/param-cone.json" width="100%" height="480" style="border: 1px solid #333; border-radius: 8px;" allow="fullscreen" title="Three curves on a cone and their images in (u, v)"></iframe>

<figure class="geom-fig">
<img src="../../assets/figures/parametric-domain/05-three-curves-on-a-sphere.svg" alt="Curves on a sphere">
<figcaption>On a sphere: parallels are horizontal, meridians vertical — and both pinch at the poles.</figcaption>
</figure>

<iframe src="https://geomcore.github.io/geomcore-viewer/?scene=https://geomcore.github.io/geomcore/assets/scenes/param-sphere.json" width="100%" height="480" style="border: 1px solid #333; border-radius: 8px;" allow="fullscreen" title="Three curves on a sphere and their images in (u, v)"></iframe>

<figure class="geom-fig">
<img src="../../assets/figures/parametric-domain/06-three-curves-on-a-torus.svg" alt="Curves on a torus">
<figcaption>On a torus both families close: the rectangle glues into the surface itself.</figcaption>
</figure>

<iframe src="https://geomcore.github.io/geomcore-viewer/?scene=https://geomcore.github.io/geomcore/assets/scenes/param-torus.json" width="100%" height="480" style="border: 1px solid #333; border-radius: 8px;" allow="fullscreen" title="Three curves on a torus and their images in (u, v)"></iframe>

## Numeric fallback

Where no closed form exists, `parametrize_numeric` inverts uniform
curve samples through surface projection and is validated against
every analytic pcurve.

=== "Python"

    ```python
    from geomcore import parametrize_numeric

    uv = parametrize_numeric(curve, surface, sample_count=64)   # [(u, v), ...]
    ```

=== "Rust"

    ```rust
    use geomcore::parametrize_numeric;

    let uv = parametrize_numeric(&curve, &surface, 64, tol);
    ```

## Viewing pcurves

The viewer draws a pcurve twice: on the surface in 3D and in its own
$(u, v)$ pane. Pass the 2D curve with the already-added surface's
handle.

=== "Python"

    ```python
    s = view.add(cylinder, label="S")
    view.add(circle, label="C")
    view.pcurve(circle.parametrize_on(cylinder), s, label="C in (u, v)")
    ```

=== "Rust"

    ```rust
    use geomcore_viewer::{Geom, Opts};

    let s = view.add(Geom::from(&cylinder), Opts::new().label("S"))?;
    view.add(&circle, Opts::new().label("C"))?;
    view.pcurve(&circle.parametrize_on(cylinder, tol)?, &s, Opts::new().label("C in (u, v)"))?;
    ```