Skip to main content

geometry_adapt/
lib.rs

1//! # Adapting your own types
2//!
3//! Three paths get a user type into the geometry kernel, ordered from
4//! least to most indirect. Pick the first one that applies.
5//!
6//! ## Path 1 — Direct `impl Point for MyPoint`
7//!
8//! If you own the type, implement [`geometry_trait::Geometry`] and
9//! [`geometry_trait::Point`] directly (add [`geometry_trait::PointMut`]
10//! too if you need to *write* coordinates). This is the most explicit
11//! form and the one with the smallest compile-time footprint — no
12//! wrapper types, no macros.
13//!
14//! ```
15//! use geometry_cs::Cartesian;
16//! use geometry_tag::PointTag;
17//! use geometry_trait::{Geometry, Point, PointMut};
18//!
19//! #[derive(Default)]
20//! struct MyPoint { x: f64, y: f64 }
21//!
22//! impl Geometry for MyPoint {
23//!     type Kind = PointTag;
24//!     type Point = Self;
25//! }
26//!
27//! impl Point for MyPoint {
28//!     type Scalar = f64;
29//!     type Cs = Cartesian;
30//!     const DIM: usize = 2;
31//!     fn get<const D: usize>(&self) -> f64 {
32//!         if D == 0 { self.x } else { self.y }
33//!     }
34//! }
35//!
36//! impl PointMut for MyPoint {
37//!     fn set<const D: usize>(&mut self, v: f64) {
38//!         if D == 0 { self.x = v } else { self.y = v }
39//!     }
40//! }
41//! ```
42//!
43//! ## Path 2 — `#[derive(Point)]`
44//!
45//! For the common "struct with named coordinate fields" case the
46//! `Point` proc-macro generates the impl. Mirrors the C++
47//! `BOOST_GEOMETRY_REGISTER_POINT_2D` macro from
48//! `boost/geometry/geometries/register/point.hpp`. The derive lives
49//! in `geometry-derive` and is re-exported by the `geometry` facade
50//! so downstream users only need a single dependency:
51//!
52//! ```ignore
53//! use geometry::Point;             // the derive macro
54//! use geometry::prelude::*;        // brings in the `Point` trait + algorithms
55//!
56//! #[derive(Default, Point)]
57//! #[geometry(cs = "Cartesian", scalar = "f64")]
58//! struct MyPoint { x: f64, y: f64 }
59//!
60//! let d = distance(&MyPoint { x: 0.0, y: 0.0 }, &MyPoint { x: 3.0, y: 4.0 });
61//! assert_eq!(d, 5.0);
62//! ```
63//!
64//! ## Path 3 — `Adapt<T>` + optional `WithCs<T, Cs>`
65//!
66//! When you do *not* own the type (a `[T; N]`, a `(T, T)`, a foreign
67//! crate's point type), wrap it in [`Adapt<T>`]. `Adapt` is a
68//! `#[repr(transparent)]` newtype that forwards coordinate access
69//! into the foreign storage layout. Coherence forbids a blanket impl
70//! on the foreign type directly; the wrapper sidesteps the orphan
71//! rule with zero runtime cost.
72//!
73//! ```
74//! use geometry_adapt::Adapt;
75//! use geometry_trait::Point;
76//!
77//! let p = Adapt([3.0_f64, 4.0]);
78//! assert_eq!(p.get::<0>(), 3.0);
79//! assert_eq!(p.get::<1>(), 4.0);
80//! ```
81//!
82//! [`Adapt<T>`] is **shape-only** and defaults the coordinate system
83//! to [`Cartesian`](geometry_cs::Cartesian). For any other CS (latitude /
84//! longitude in degrees, radians, …) layer [`WithCs<T, Cs>`] on top.
85//! This is the orthogonality from proposal §3.7: `Adapt` answers
86//! *"how do I read coordinates out of this foreign data layout?"*,
87//! while `WithCs` answers *"what does that coordinate pair mean?"*.
88//!
89//! ```
90//! use geometry_adapt::{Adapt, WithCs};
91//! use geometry_algorithm::distance;
92//! use geometry_cs::{Degree, Geographic};
93//!
94//! // [lon, lat] degrees on WGS84.
95//! let ams = WithCs::<_, Geographic<Degree>>::new(Adapt([4.90_f64, 52.37]));
96//! let par = WithCs::<_, Geographic<Degree>>::new(Adapt([2.35_f64, 48.86]));
97//! let _ = distance(&ams, &par); // picks Andoyer for the geographic family
98//! ```
99//!
100//! Either wrapper can also re-tag a user type that already
101//! implements [`Point`](geometry_trait::Point), so a `MyPoint` from
102//! Path 1 can be reused as a geographic point by writing
103//! `WithCs::<MyPoint, Geographic<Degree>>::new(p)` instead of
104//! defining a second adapter.
105//!
106//! ## Container adaptation — the `register_*!` macros
107//!
108//! Coherence also forbids a blanket impl on foreign sequence types
109//! (`impl<P: Point, C: AsRef<[P]>> Linestring for C`). For that case,
110//! `geometry-adapt` ships declarative macros `register_linestring!`,
111//! `register_ring!`, `register_polygon!`, and the three `register_multi_*!`
112//! forms, mirroring
113//! `BOOST_GEOMETRY_REGISTER_LINESTRING` and siblings
114//! (`boost/geometry/geometries/register/linestring.hpp` and co.).
115//!
116//! ---
117//!
118//! Module layout:
119//!
120//!  * [`Adapt<T>`] — shape-only wrapper. Forwards coordinate access
121//!    into a foreign storage layout (array, tuple, third-party point).
122//!    Defaults to [`Cartesian`](geometry_cs::Cartesian); layer
123//!    [`WithCs`] on top for other systems.
124//!  * [`WithCs<T, Cs>`] — coordinate-system re-tagging wrapper.
125//!
126//! Mirrors the role of `boost/geometry/geometries/adapted/*.hpp`
127//! (`c_array.hpp`, `std_array.hpp`, `boost_tuple.hpp`,
128//! `boost_polygon.hpp`, …).
129
130#![cfg_attr(not(feature = "std"), no_std)]
131#![forbid(unsafe_code)]
132
133mod adapt;
134mod adapt_array;
135mod adapt_borrowed_array;
136mod adapt_tuple;
137mod macros;
138mod with_cs;
139
140pub use adapt::Adapt;
141pub use with_cs::WithCs;
142
143// Re-exported under `__macros` for the `register_*!` macros to expand
144// against. Not part of the public API — users never name this path.
145pub use macros::__macros;