Repository: olson-sean-k/plexus Branch: master Commit: b4e4a4b0fb09 Files: 73 Total size: 640.4 KB Directory structure: gitextract_cu48aqwt/ ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ └── continuous-integration.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches/ │ └── subdivide.rs ├── data/ │ ├── cube.ply │ ├── plexus.ply │ └── teapot.ply ├── doc/ │ └── katex-header.html ├── examples/ │ ├── sphere/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── subdivide/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── teapot/ │ ├── Cargo.toml │ └── src/ │ └── main.rs ├── pictor/ │ ├── Cargo.toml │ └── src/ │ ├── camera.rs │ ├── harness.rs │ ├── lib.rs │ ├── pipeline.rs │ ├── renderer.rs │ ├── shader.glsl.frag │ ├── shader.glsl.vert │ ├── shader.spv.frag │ └── shader.spv.vert ├── plexus/ │ ├── Cargo.toml │ └── src/ │ ├── buffer/ │ │ ├── builder.rs │ │ └── mod.rs │ ├── builder.rs │ ├── constant.rs │ ├── encoding/ │ │ ├── mod.rs │ │ └── ply.rs │ ├── entity/ │ │ ├── borrow.rs │ │ ├── dijkstra.rs │ │ ├── mod.rs │ │ ├── storage/ │ │ │ ├── hash.rs │ │ │ └── mod.rs │ │ ├── traverse.rs │ │ └── view.rs │ ├── geometry/ │ │ ├── mod.rs │ │ └── partition.rs │ ├── graph/ │ │ ├── builder.rs │ │ ├── core.rs │ │ ├── data.rs │ │ ├── edge.rs │ │ ├── face.rs │ │ ├── geometry.rs │ │ ├── mod.rs │ │ ├── mutation/ │ │ │ ├── edge.rs │ │ │ ├── face.rs │ │ │ ├── mod.rs │ │ │ ├── path.rs │ │ │ └── vertex.rs │ │ ├── path.rs │ │ └── vertex.rs │ ├── index.rs │ ├── integration/ │ │ ├── cgmath.rs │ │ ├── glam.rs │ │ ├── mint.rs │ │ ├── mod.rs │ │ ├── nalgebra.rs │ │ └── ultraviolet.rs │ ├── lib.rs │ ├── primitive/ │ │ ├── cube.rs │ │ ├── decompose.rs │ │ ├── generate.rs │ │ ├── mod.rs │ │ └── sphere.rs │ └── transact.rs ├── rustdoc.sh └── rustfmt.toml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "cargo" directory: "/" schedule: interval: "monthly" ================================================ FILE: .github/workflows/continuous-integration.yml ================================================ name: CI on: [pull_request, push] jobs: rustfmt: name: Format runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: nightly override: true components: rustfmt - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check clippy: name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true components: clippy - uses: actions-rs/cargo@v1 with: command: clippy args: --all-features --all-targets -- -D clippy::all test: name: Test needs: [clippy, rustfmt] runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [macOS-latest, ubuntu-latest, windows-latest] toolchain: - 1.81.0 # Minimum. - stable - beta - nightly steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.toolchain }} override: true - uses: actions-rs/cargo@v1 with: command: test args: --manifest-path=plexus/Cargo.toml --features geometry-nalgebra --no-default-features --verbose - uses: actions-rs/cargo@v1 with: command: test args: --manifest-path=plexus/Cargo.toml --all-features --verbose ================================================ FILE: .gitignore ================================================ .idea/ target/ **/*.rs.bk *.iml Cargo.lock ================================================ FILE: Cargo.toml ================================================ [workspace] members = [ "examples/sphere", "examples/subdivide", "examples/teapot", "pictor", "plexus", ] resolver = "2" ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================
Plexus

**Plexus** is a highly composable Rust library for polygonal mesh processing. See [the website][website] for the most recent [API documentation][rustdoc] and the [user guide][guide]. [![GitHub](https://img.shields.io/badge/GitHub-olson--sean--k/plexus-8da0cb?logo=github&style=for-the-badge)](https://github.com/olson-sean-k/plexus) [![docs.rs](https://img.shields.io/badge/docs.rs-plexus-66c2a5?logo=rust&style=for-the-badge)](https://docs.rs/plexus) [![crates.io](https://img.shields.io/crates/v/plexus.svg?logo=rust&style=for-the-badge)](https://crates.io/crates/plexus) [![Gitter](https://img.shields.io/badge/Gitter-plexus--rs-c266a5?logo=gitter&style=for-the-badge)](https://gitter.im/plexus-rs/community) ## Primitives Plexus provides primitive topological structures that can be composed using generators and iterator expressions. Iterator expressions operate over a sequence of polygons with arbitrary vertex data. These polygons can be decomposed, tessellated, indexed, and collected into mesh data structures. ```rust use decorum::R64; // See "Integrations". use nalgebra::Point3; use plexus::buffer::MeshBuffer; use plexus::index::Flat3; use plexus::prelude::*; use plexus::primitive::generate::Position; use plexus::primitive::sphere::UvSphere; use crate::render::pipeline::{Color4, Vertex}; type E3 = Point3; // Create a buffer of index and vertex data from a uv-sphere. let buffer: MeshBuffer = UvSphere::new(16, 8) .polygons::>() .map_vertices(|position| Vertex::new(position, Color4::white())) .triangulate() .collect(); ``` The above example uses a generator and iterator expression to transform the positional data of a sphere into a linear buffer for indexed drawing. See [the sphere example][example-sphere] for a rendered demonstration. ## Half-Edge Graphs The `MeshGraph` type represents polygonal meshes as an ergonomic [half-edge graph][dcel] that supports arbitrary data in vertices, arcs (half-edges), edges, and faces. Graphs can be traversed and manipulated in many ways that iterator expressions and linear buffers cannot. ```rust use plexus::graph::MeshGraph; use plexus::prelude::*; use plexus::primitive::Tetragon; use ultraviolet::vec::Vec3; type E3 = Vec3; // Create a graph of a tetragon. let mut graph = MeshGraph::::from(Tetragon::from([ (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0), (-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), ])); // Extrude the face of the tetragon. let key = graph.faces().next().unwrap().key(); let face = graph.face_mut(key).unwrap().extrude_with_offset(1.0).unwrap(); ``` Plexus avoids exposing very basic topological operations like inserting individual vertices into a graph, because they can easily be done incorrectly. Instead, graphs are typically manipulated with more abstract operations like merging and splitting. See [the user guide][guide-graphs] for more details about graphs. ## Geometric Traits Plexus provides optional traits to support spatial operations by exposing positional data in vertices. If the data exposed by the `AsPosition` trait supports these geometric traits, then geometric operations become available in primitive and mesh data structure APIs. ```rust use glam::Vec3A; use plexus::geometry::{AsPosition, Vector}; use plexus::graph::GraphData; use plexus::prelude::*; type E3 = Vec3A; #[derive(Clone, Copy, PartialEq)] pub struct Vertex { pub position: E3, pub normal: Vector, } impl GraphData for Vertex { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl AsPosition for Vertex { type Position = E3; fn as_position(&self) -> &Self::Position { &self.position } } ``` Data structures like `MeshGraph` also provide functions that allow user code to compute geometry without requiring any of these traits; the data in these structures may be arbitrary, including no data at all. ## Integrations Plexus integrates with the [`theon`] crate to provide geometric traits and support various mathematics crates in the Rust ecosystem. Any mathematics crate can be used and, if it is supported by Theon, Plexus provides geometric APIs by enabling Cargo features. | Feature | Default | Crate | |------------------------|---------|-----------------| | `geometry-cgmath` | No | [`cgmath`] | | `geometry-glam` | No | [`glam`] | | `geometry-mint` | No | [`mint`] | | `geometry-nalgebra` | No | [`nalgebra`] | | `geometry-ultraviolet` | No | [`ultraviolet`] | Enabling the corresponding feature is recommended if using one of these supported crates. Plexus also integrates with the [`decorum`] crate for floating-point representations that can be hashed for fast indexing. The `R64` type is a (totally ordered) real number with an `f64` representation that cannot be `NaN` nor infinity, for example. Geometric conversion traits are implemented for supported types to allow for implicit conversions of scalar types. ## Encodings Plexus provides support for polygonal mesh encodings. This allows mesh data structures like `MeshGraph` and `MeshBuffer` to be serialized and deserialized to and from various formats. ```rust use nalgebra::Point3; use plexus::encoding::ply::{FromPly, PositionEncoding}; use plexus::graph::MeshGraph; use plexus::prelude::*; use std::fs::File; type E3 = Point3; let ply = File::open("cube.ply").unwrap(); let encoding = PositionEncoding::::default(); let (graph, _) = MeshGraph::::from_ply(encoding, ply).unwrap(); ``` Encoding support is optional and enabled via Cargo features. | Feature | Default | Encoding | Read | Write | |----------------|---------|----------|------|-------| | `encoding-ply` | No | PLY | Yes | No | See [the teapot example][example-teapot] for a rendered demonstration of reading a mesh from the file system. [dcel]: https://en.wikipedia.org/wiki/doubly_connected_edge_list [guide]: https://plexus.rs/user-guide/getting-started [guide-graphs]: https://plexus.rs/user-guide/graphs [rustdoc]: https://plexus.rs/rustdoc/plexus [website]: https://plexus.rs [example-sphere]: https://github.com/olson-sean-k/plexus/tree/master/examples/sphere/src/main.rs [example-teapot]: https://github.com/olson-sean-k/plexus/tree/master/examples/teapot/src/main.rs [`cgmath`]: https://crates.io/crates/cgmath [`decorum`]: https://crates.io/crates/decorum [`glam`]: https://crates.io/crates/glam [`mint`]: https://crates.io/crates/mint [`nalgebra`]: https://crates.io/crates/nalgebra [`theon`]: https://crates.io/crates/theon [`ultraviolet`]: https://crates.io/crates/ultraviolet ================================================ FILE: benches/subdivide.rs ================================================ use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use nalgebra::Point3; use plexus::geometry::AsPositionMut; use plexus::graph::{EdgeMidpoint, FaceView, GraphData, MeshGraph}; use plexus::prelude::*; use plexus::primitive::Tetragon; use smallvec::SmallVec; const DEPTH: usize = 8; type E3 = Point3; pub trait Ambo { #[must_use] fn ambo(self) -> Self; } impl Ambo for FaceView<&'_ mut MeshGraph> where G: EdgeMidpoint + GraphData, G::Vertex: AsPositionMut, { // Subdivide the face with a polygon formed from vertices at the midpoints // of the edges of the face. fn ambo(self) -> Self { // Split each edge, stashing the vertex key and moving to the next arc. let arity = self.arity(); let mut arc = self.into_arc(); let mut splits = SmallVec::<[_; 4]>::with_capacity(arity); for _ in 0..arity { let vertex = arc.split_at_midpoint(); splits.push(vertex.key()); arc = vertex.into_outgoing_arc().into_next_arc(); } // Split faces along the vertices from each arc split. let mut face = arc.into_face().unwrap(); for (a, b) in splits.into_iter().perimeter() { face = face.split(a, b).unwrap().into_face().unwrap(); } // Return the face forming the similar polygon. face } } fn tetragon() -> MeshGraph { // Create a graph from a tetragon. MeshGraph::from(Tetragon::from([ (1.0, 0.0, -1.0), (-1.0, 0.0, -1.0), (-1.0, 0.0, 1.0), (1.0, 0.0, 1.0), ])) } fn subdivide(mut graph: MeshGraph) { // Get the face of the tetragon. let key = graph.faces().next().unwrap().key(); let mut face = graph.face_mut(key).unwrap(); // Subdivide and extrude the face repeatedly. for _ in 0..DEPTH { face = face.ambo().extrude_with_offset(0.5).unwrap(); } } fn benchmark(criterion: &mut Criterion) { criterion.bench_function("subdivide", move |bencher| { bencher.iter_batched(tetragon, subdivide, BatchSize::SmallInput) }); } criterion_group!(benches, benchmark); criterion_main!(benches); ================================================ FILE: data/cube.ply ================================================ ply format ascii 1.0 element vertex 8 property float x property float y property float z element face 6 property list uint8 uint32 vertex_index end_header 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 1 0 1 1 1 1 1 1 0 4 0 1 2 3 4 7 6 5 4 4 0 4 5 1 4 1 5 6 2 4 2 6 7 3 4 3 7 4 0 ================================================ FILE: data/plexus.ply ================================================ ply format ascii 1.0 comment 0 1 3 4 5 10 12 14 20 21 27 28 35 36 43 44 53 54 element vertex 55 property float x property float y property float z element face 56 property list uint8 uint32 vertex_index end_header 0 1 0 -2 0 0 0 0 0 2 0 0 2 -1 0 0 -2 0 -2 -1 0 -2 -3 0 0 -4 0 -2 -5 0 0 -6 0 -2 -7 0 0 -8 0 -2 -9 0 0 -10 0 2 -7 0 4 -6 0 2 -5 0 6 -5 0 4 -4 0 8 -4 0 6 -3 0 8 -2 0 6 -1 0 8 0 0 6 1 0 8 2 0 6 2 0 8 3 0 6 3 0 6 4 0 4 3 0 4 5 0 2 4 0 2 6 0 0 7 0 0 5 0 -2 6 0 -2 4 0 -4 5 0 -4 3 0 -6 4 0 -6 3 0 -6 2 0 -8 3 0 -6 1 0 -8 2 0 -6 -1 0 -8 0 0 -6 -3 0 -8 -2 0 -6 -5 0 -8 -4 0 -6 -7 0 -8 -6 0 3 4 2 5 3 3 2 4 3 3 0 2 3 0 1 2 3 2 1 6 3 2 6 5 3 5 6 7 3 5 7 8 3 8 7 9 3 8 9 10 3 10 9 11 3 10 11 12 3 12 11 13 3 12 13 14 3 15 10 12 3 17 10 15 3 16 17 15 3 19 17 16 3 18 19 16 3 21 19 18 3 20 21 18 3 22 21 20 3 22 23 21 3 24 23 22 3 24 25 23 3 26 25 24 3 26 27 25 3 26 29 27 3 28 29 26 3 28 30 29 3 29 31 27 3 30 31 29 3 30 32 31 3 32 33 31 3 32 34 33 3 34 36 33 3 34 35 36 3 35 37 36 3 36 37 38 3 37 39 38 3 38 39 40 3 39 41 40 3 40 41 42 3 40 42 43 3 41 44 42 3 42 44 46 3 42 46 43 3 43 46 45 3 45 46 48 3 45 48 47 3 47 48 50 3 47 50 49 3 49 50 52 3 49 52 51 3 51 52 54 3 51 54 53 ================================================ FILE: data/teapot.ply ================================================ ply format ascii 1.0 element vertex 1177 property float32 x property float32 y property float32 z element face 2256 property list uint8 uint32 vertex_index end_header 1.38137 0 2.45469 1.4 0 2.4 1.35074 -0.375926 2.4 1.33276 -0.370922 2.45469 1.38426 0 2.4875 1.33555 -0.371699 2.4875 1.40312 0 2.49844 1.35376 -0.376765 2.49844 1.43241 0 2.4875 1.38201 -0.384628 2.4875 1.46655 0 2.45469 1.41495 -0.393796 2.45469 1.5 0 2.4 1.44722 -0.402778 2.4 1.21126 -0.711407 2.4 1.19514 -0.701938 2.45469 1.19764 -0.703409 2.4875 1.21396 -0.712995 2.49844 1.2393 -0.727875 2.4875 1.26884 -0.745225 2.45469 1.29778 -0.762222 2.4 0.994 -0.994 2.4 0.98077 -0.98077 2.45469 0.982824 -0.982824 2.4875 0.996219 -0.996219 2.49844 1.01701 -1.01701 2.4875 1.04125 -1.04125 2.45469 1.065 -1.065 2.4 0.711407 -1.21126 2.4 0.701938 -1.19514 2.45469 0.703409 -1.19764 2.4875 0.712995 -1.21396 2.49844 0.727875 -1.2393 2.4875 0.745225 -1.26884 2.45469 0.762222 -1.29778 2.4 0.375926 -1.35074 2.4 0.370922 -1.33276 2.45469 0.371699 -1.33555 2.4875 0.376765 -1.35376 2.49844 0.384628 -1.38201 2.4875 0.393796 -1.41495 2.45469 0.402778 -1.44722 2.4 0 -1.4 2.4 0 -1.38137 2.45469 0 -1.38426 2.4875 0 -1.40312 2.49844 0 -1.43241 2.4875 0 -1.46655 2.45469 0 -1.5 2.4 -0.375926 -1.35074 2.4 -0.370922 -1.33276 2.45469 -0.371699 -1.33555 2.4875 -0.376765 -1.35376 2.49844 -0.384628 -1.38201 2.4875 -0.393796 -1.41495 2.45469 -0.402778 -1.44722 2.4 -0.711407 -1.21126 2.4 -0.701938 -1.19514 2.45469 -0.703409 -1.19764 2.4875 -0.712995 -1.21396 2.49844 -0.727875 -1.2393 2.4875 -0.745225 -1.26884 2.45469 -0.762222 -1.29778 2.4 -0.994 -0.994 2.4 -0.98077 -0.98077 2.45469 -0.982824 -0.982824 2.4875 -0.996219 -0.996219 2.49844 -1.01701 -1.01701 2.4875 -1.04125 -1.04125 2.45469 -1.065 -1.065 2.4 -1.21126 -0.711407 2.4 -1.19514 -0.701938 2.45469 -1.19764 -0.703409 2.4875 -1.21396 -0.712995 2.49844 -1.2393 -0.727875 2.4875 -1.26884 -0.745225 2.45469 -1.29778 -0.762222 2.4 -1.35074 -0.375926 2.4 -1.33276 -0.370922 2.45469 -1.33555 -0.371699 2.4875 -1.35376 -0.376765 2.49844 -1.38201 -0.384628 2.4875 -1.41495 -0.393796 2.45469 -1.44722 -0.402778 2.4 -1.4 0 2.4 -1.38137 0 2.45469 -1.38426 0 2.4875 -1.40312 0 2.49844 -1.43241 0 2.4875 -1.46655 0 2.45469 -1.5 0 2.4 -1.35074 0.375926 2.4 -1.33276 0.370922 2.45469 -1.33555 0.371699 2.4875 -1.35376 0.376765 2.49844 -1.38201 0.384628 2.4875 -1.41495 0.393796 2.45469 -1.44722 0.402778 2.4 -1.21126 0.711407 2.4 -1.19514 0.701938 2.45469 -1.19764 0.703409 2.4875 -1.21396 0.712995 2.49844 -1.2393 0.727875 2.4875 -1.26884 0.745225 2.45469 -1.29778 0.762222 2.4 -0.994 0.994 2.4 -0.98077 0.98077 2.45469 -0.982824 0.982824 2.4875 -0.996219 0.996219 2.49844 -1.01701 1.01701 2.4875 -1.04125 1.04125 2.45469 -1.065 1.065 2.4 -0.711407 1.21126 2.4 -0.701938 1.19514 2.45469 -0.703409 1.19764 2.4875 -0.712995 1.21396 2.49844 -0.727875 1.2393 2.4875 -0.745225 1.26884 2.45469 -0.762222 1.29778 2.4 -0.375926 1.35074 2.4 -0.370922 1.33276 2.45469 -0.371699 1.33555 2.4875 -0.376765 1.35376 2.49844 -0.384628 1.38201 2.4875 -0.393796 1.41495 2.45469 -0.402778 1.44722 2.4 0 1.4 2.4 0 1.38137 2.45469 0 1.38426 2.4875 0 1.40312 2.49844 0 1.43241 2.4875 0 1.46655 2.45469 0 1.5 2.4 0.375926 1.35074 2.4 0.370922 1.33276 2.45469 0.371699 1.33555 2.4875 0.376765 1.35376 2.49844 0.384628 1.38201 2.4875 0.393796 1.41495 2.45469 0.402778 1.44722 2.4 0.711407 1.21126 2.4 0.701938 1.19514 2.45469 0.703409 1.19764 2.4875 0.712995 1.21396 2.49844 0.727875 1.2393 2.4875 0.745225 1.26884 2.45469 0.762222 1.29778 2.4 0.994 0.994 2.4 0.98077 0.98077 2.45469 0.982824 0.982824 2.4875 0.996219 0.996219 2.49844 1.01701 1.01701 2.4875 1.04125 1.04125 2.45469 1.065 1.065 2.4 1.21126 0.711407 2.4 1.19514 0.701938 2.45469 1.19764 0.703409 2.4875 1.21396 0.712995 2.49844 1.2393 0.727875 2.4875 1.26884 0.745225 2.45469 1.29778 0.762222 2.4 1.35074 0.375926 2.4 1.33276 0.370922 2.45469 1.33555 0.371699 2.4875 1.35376 0.376765 2.49844 1.38201 0.384628 2.4875 1.41495 0.393796 2.45469 1.44722 0.402778 2.4 1.62384 0 2.13785 1.56671 -0.436032 2.13785 1.74074 0 1.87778 1.67949 -0.467421 1.87778 1.84375 0 1.62187 1.77888 -0.495081 1.62188 1.92593 0 1.37222 1.85816 -0.517147 1.37222 1.98032 0 1.1309 1.91065 -0.531754 1.1309 2 0 0.9 1.92963 -0.537037 0.9 1.40492 -0.825153 2.13785 1.50606 -0.884554 1.87778 1.59519 -0.936898 1.62188 1.66628 -0.978656 1.37222 1.71335 -1.0063 1.1309 1.73037 -1.0163 0.9 1.15293 -1.15293 2.13785 1.23593 -1.23593 1.87778 1.30906 -1.30906 1.62187 1.36741 -1.36741 1.37222 1.40603 -1.40603 1.1309 1.42 -1.42 0.9 0.825153 -1.40492 2.13785 0.884554 -1.50606 1.87778 0.936898 -1.59519 1.62188 0.978656 -1.66628 1.37222 1.0063 -1.71335 1.1309 1.0163 -1.73037 0.9 0.436032 -1.56671 2.13785 0.467421 -1.67949 1.87778 0.495081 -1.77888 1.62187 0.517147 -1.85816 1.37222 0.531754 -1.91065 1.1309 0.537037 -1.92963 0.9 0 -1.62384 2.13785 0 -1.74074 1.87778 0 -1.84375 1.62187 0 -1.92593 1.37222 0 -1.98032 1.1309 0 -2 0.9 -0.436032 -1.56671 2.13785 -0.467421 -1.67949 1.87778 -0.495081 -1.77888 1.62188 -0.517147 -1.85816 1.37222 -0.531754 -1.91065 1.1309 -0.537037 -1.92963 0.9 -0.825153 -1.40492 2.13785 -0.884554 -1.50606 1.87778 -0.936898 -1.59519 1.62188 -0.978656 -1.66628 1.37222 -1.0063 -1.71335 1.1309 -1.0163 -1.73037 0.9 -1.15293 -1.15293 2.13785 -1.23593 -1.23593 1.87778 -1.30906 -1.30906 1.62187 -1.36741 -1.36741 1.37222 -1.40603 -1.40603 1.1309 -1.42 -1.42 0.9 -1.40492 -0.825153 2.13785 -1.50606 -0.884554 1.87778 -1.59519 -0.936898 1.62188 -1.66628 -0.978656 1.37222 -1.71335 -1.0063 1.1309 -1.73037 -1.0163 0.9 -1.56671 -0.436032 2.13785 -1.67949 -0.467421 1.87778 -1.77888 -0.495081 1.62187 -1.85816 -0.517147 1.37222 -1.91065 -0.531754 1.1309 -1.92963 -0.537037 0.9 -1.62384 0 2.13785 -1.74074 0 1.87778 -1.84375 0 1.62187 -1.92593 0 1.37222 -1.98032 0 1.1309 -2 0 0.9 -1.56671 0.436032 2.13785 -1.67949 0.467421 1.87778 -1.77888 0.495081 1.62188 -1.85816 0.517147 1.37222 -1.91065 0.531754 1.1309 -1.92963 0.537037 0.9 -1.40492 0.825153 2.13785 -1.50606 0.884554 1.87778 -1.59519 0.936898 1.62188 -1.66628 0.978656 1.37222 -1.71335 1.0063 1.1309 -1.73037 1.0163 0.9 -1.15293 1.15293 2.13785 -1.23593 1.23593 1.87778 -1.30906 1.30906 1.62187 -1.36741 1.36741 1.37222 -1.40603 1.40603 1.1309 -1.42 1.42 0.9 -0.825153 1.40492 2.13785 -0.884554 1.50606 1.87778 -0.936898 1.59519 1.62188 -0.978656 1.66628 1.37222 -1.0063 1.71335 1.1309 -1.0163 1.73037 0.9 -0.436032 1.56671 2.13785 -0.467421 1.67949 1.87778 -0.495081 1.77888 1.62187 -0.517147 1.85816 1.37222 -0.531754 1.91065 1.1309 -0.537037 1.92963 0.9 0 1.62384 2.13785 0 1.74074 1.87778 0 1.84375 1.62187 0 1.92593 1.37222 0 1.98032 1.1309 0 2 0.9 0.436032 1.56671 2.13785 0.467421 1.67949 1.87778 0.495081 1.77888 1.62188 0.517147 1.85816 1.37222 0.531754 1.91065 1.1309 0.537037 1.92963 0.9 0.825153 1.40492 2.13785 0.884554 1.50606 1.87778 0.936898 1.59519 1.62188 0.978656 1.66628 1.37222 1.0063 1.71335 1.1309 1.0163 1.73037 0.9 1.15293 1.15293 2.13785 1.23593 1.23593 1.87778 1.30906 1.30906 1.62187 1.36741 1.36741 1.37222 1.40603 1.40603 1.1309 1.42 1.42 0.9 1.40492 0.825153 2.13785 1.50606 0.884554 1.87778 1.59519 0.936898 1.62188 1.66628 0.978656 1.37222 1.71335 1.0063 1.1309 1.73037 1.0163 0.9 1.56671 0.436032 2.13785 1.67949 0.467421 1.87778 1.77888 0.495081 1.62187 1.85816 0.517147 1.37222 1.91065 0.531754 1.1309 1.92963 0.537037 0.9 1.96296 0 0.693403 1.8939 -0.527092 0.693403 1.87037 0 0.522222 1.80456 -0.502229 0.522222 1.75 0 0.384375 1.68843 -0.469907 0.384375 1.62963 0 0.277778 1.57229 -0.437586 0.277778 1.53704 0 0.200347 1.48296 -0.412723 0.200347 1.5 0 0.15 1.44722 -0.402778 0.15 1.69833 -0.997476 0.693403 1.61822 -0.950425 0.522222 1.51407 -0.889259 0.384375 1.40993 -0.828093 0.277778 1.32982 -0.781043 0.200347 1.29778 -0.762222 0.15 1.3937 -1.3937 0.693403 1.32796 -1.32796 0.522222 1.2425 -1.2425 0.384375 1.15704 -1.15704 0.277778 1.0913 -1.0913 0.200347 1.065 -1.065 0.15 0.997476 -1.69833 0.693403 0.950425 -1.61822 0.522222 0.889259 -1.51407 0.384375 0.828093 -1.40993 0.277778 0.781043 -1.32982 0.200347 0.762222 -1.29778 0.15 0.527092 -1.8939 0.693403 0.502229 -1.80456 0.522222 0.469907 -1.68843 0.384375 0.437586 -1.57229 0.277778 0.412723 -1.48296 0.200347 0.402778 -1.44722 0.15 0 -1.96296 0.693403 0 -1.87037 0.522222 0 -1.75 0.384375 0 -1.62963 0.277778 0 -1.53704 0.200347 0 -1.5 0.15 -0.527092 -1.8939 0.693403 -0.502229 -1.80456 0.522222 -0.469907 -1.68843 0.384375 -0.437586 -1.57229 0.277778 -0.412723 -1.48296 0.200347 -0.402778 -1.44722 0.15 -0.997476 -1.69833 0.693403 -0.950425 -1.61822 0.522222 -0.889259 -1.51407 0.384375 -0.828093 -1.40993 0.277778 -0.781043 -1.32982 0.200347 -0.762222 -1.29778 0.15 -1.3937 -1.3937 0.693403 -1.32796 -1.32796 0.522222 -1.2425 -1.2425 0.384375 -1.15704 -1.15704 0.277778 -1.0913 -1.0913 0.200347 -1.065 -1.065 0.15 -1.69833 -0.997476 0.693403 -1.61822 -0.950425 0.522222 -1.51407 -0.889259 0.384375 -1.40993 -0.828093 0.277778 -1.32982 -0.781043 0.200347 -1.29778 -0.762222 0.15 -1.8939 -0.527092 0.693403 -1.80456 -0.502229 0.522222 -1.68843 -0.469907 0.384375 -1.57229 -0.437586 0.277778 -1.48296 -0.412723 0.200347 -1.44722 -0.402778 0.15 -1.96296 0 0.693403 -1.87037 0 0.522222 -1.75 0 0.384375 -1.62963 0 0.277778 -1.53704 0 0.200347 -1.5 0 0.15 -1.8939 0.527092 0.693403 -1.80456 0.502229 0.522222 -1.68843 0.469907 0.384375 -1.57229 0.437586 0.277778 -1.48296 0.412723 0.200347 -1.44722 0.402778 0.15 -1.69833 0.997476 0.693403 -1.61822 0.950425 0.522222 -1.51407 0.889259 0.384375 -1.40993 0.828093 0.277778 -1.32982 0.781043 0.200347 -1.29778 0.762222 0.15 -1.3937 1.3937 0.693403 -1.32796 1.32796 0.522222 -1.2425 1.2425 0.384375 -1.15704 1.15704 0.277778 -1.0913 1.0913 0.200347 -1.065 1.065 0.15 -0.997476 1.69833 0.693403 -0.950425 1.61822 0.522222 -0.889259 1.51407 0.384375 -0.828093 1.40993 0.277778 -0.781043 1.32982 0.200347 -0.762222 1.29778 0.15 -0.527092 1.8939 0.693403 -0.502229 1.80456 0.522222 -0.469907 1.68843 0.384375 -0.437586 1.57229 0.277778 -0.412723 1.48296 0.200347 -0.402778 1.44722 0.15 0 1.96296 0.693403 0 1.87037 0.522222 0 1.75 0.384375 0 1.62963 0.277778 0 1.53704 0.200347 0 1.5 0.15 0.527092 1.8939 0.693403 0.502229 1.80456 0.522222 0.469907 1.68843 0.384375 0.437586 1.57229 0.277778 0.412723 1.48296 0.200347 0.402778 1.44722 0.15 0.997476 1.69833 0.693403 0.950425 1.61822 0.522222 0.889259 1.51407 0.384375 0.828093 1.40993 0.277778 0.781043 1.32982 0.200347 0.762222 1.29778 0.15 1.3937 1.3937 0.693403 1.32796 1.32796 0.522222 1.2425 1.2425 0.384375 1.15704 1.15704 0.277778 1.0913 1.0913 0.200347 1.065 1.065 0.15 1.69833 0.997476 0.693403 1.61822 0.950425 0.522222 1.51407 0.889259 0.384375 1.40993 0.828093 0.277778 1.32982 0.781043 0.200347 1.29778 0.762222 0.15 1.8939 0.527092 0.693403 1.80456 0.502229 0.522222 1.68843 0.469907 0.384375 1.57229 0.437586 0.277778 1.48296 0.412723 0.200347 1.44722 0.402778 0.15 0.605903 0 0.005903 0 0 0 0.584584 0.162696 0.005903 1.02222 0 0.022222 0.986255 0.274486 0.022222 1.28437 0 0.046875 1.23918 0.344878 0.046875 1.42778 0 0.077778 1.37754 0.383385 0.077778 1.48785 0 0.112847 1.4355 0.399515 0.112847 0.524218 0.307888 0.005903 0.884412 0.51944 0.022222 1.11122 0.652653 0.046875 1.23529 0.725523 0.077778 1.28726 0.756047 0.112847 0.430191 0.430191 0.005903 0.725778 0.725778 0.022222 0.911906 0.911906 0.046875 1.01372 1.01372 0.077778 1.05637 1.05637 0.112847 0.307888 0.524218 0.005903 0.51944 0.884412 0.022222 0.652653 1.11122 0.046875 0.725523 1.23529 0.077778 0.756047 1.28726 0.112847 0.162696 0.584584 0.005903 0.274486 0.986255 0.022222 0.344878 1.23918 0.046875 0.383385 1.37754 0.077778 0.399515 1.4355 0.112847 0 0.605903 0.005903 0 1.02222 0.022222 0 1.28437 0.046875 0 1.42778 0.077778 0 1.48785 0.112847 -0.162696 0.584584 0.005903 -0.274486 0.986255 0.022222 -0.344878 1.23918 0.046875 -0.383385 1.37754 0.077778 -0.399515 1.4355 0.112847 -0.307888 0.524218 0.005903 -0.51944 0.884412 0.022222 -0.652653 1.11122 0.046875 -0.725523 1.23529 0.077778 -0.756047 1.28726 0.112847 -0.430191 0.430191 0.005903 -0.725778 0.725778 0.022222 -0.911906 0.911906 0.046875 -1.01372 1.01372 0.077778 -1.05637 1.05637 0.112847 -0.524218 0.307888 0.005903 -0.884412 0.51944 0.022222 -1.11122 0.652653 0.046875 -1.23529 0.725523 0.077778 -1.28726 0.756047 0.112847 -0.584584 0.162696 0.005903 -0.986255 0.274486 0.022222 -1.23918 0.344878 0.046875 -1.37754 0.383385 0.077778 -1.4355 0.399515 0.112847 -0.605903 0 0.005903 -1.02222 0 0.022222 -1.28437 0 0.046875 -1.42778 0 0.077778 -1.48785 0 0.112847 -0.584584 -0.162696 0.005903 -0.986255 -0.274486 0.022222 -1.23918 -0.344878 0.046875 -1.37754 -0.383385 0.077778 -1.4355 -0.399515 0.112847 -0.524218 -0.307888 0.005903 -0.884412 -0.51944 0.022222 -1.11122 -0.652653 0.046875 -1.23529 -0.725523 0.077778 -1.28726 -0.756047 0.112847 -0.430191 -0.430191 0.005903 -0.725778 -0.725778 0.022222 -0.911906 -0.911906 0.046875 -1.01372 -1.01372 0.077778 -1.05637 -1.05637 0.112847 -0.307888 -0.524218 0.005903 -0.51944 -0.884412 0.022222 -0.652653 -1.11122 0.046875 -0.725523 -1.23529 0.077778 -0.756047 -1.28726 0.112847 -0.162696 -0.584584 0.005903 -0.274486 -0.986255 0.022222 -0.344878 -1.23918 0.046875 -0.383385 -1.37754 0.077778 -0.399515 -1.4355 0.112847 0 -0.605903 0.005903 0 -1.02222 0.022222 0 -1.28437 0.046875 0 -1.42778 0.077778 0 -1.48785 0.112847 0.162696 -0.584584 0.005903 0.274486 -0.986255 0.022222 0.344878 -1.23918 0.046875 0.383385 -1.37754 0.077778 0.399515 -1.4355 0.112847 0.307888 -0.524218 0.005903 0.51944 -0.884412 0.022222 0.652653 -1.11122 0.046875 0.725523 -1.23529 0.077778 0.756047 -1.28726 0.112847 0.430191 -0.430191 0.005903 0.725778 -0.725778 0.022222 0.911906 -0.911906 0.046875 1.01372 -1.01372 0.077778 1.05637 -1.05637 0.112847 0.524218 -0.307888 0.005903 0.884412 -0.51944 0.022222 1.11122 -0.652653 0.046875 1.23529 -0.725523 0.077778 1.28726 -0.756047 0.112847 0.584584 -0.162696 0.005903 0.986255 -0.274486 0.022222 1.23918 -0.344878 0.046875 1.37754 -0.383385 0.077778 1.4355 -0.399515 0.112847 0.2 0 2.7 0.171296 0 2.78542 0.165279 -0.046045 2.78542 0.192963 -0.053704 2.7 0.148234 -0.087106 2.78542 0.173037 -0.10163 2.7 0.121672 -0.121672 2.78542 0.142 -0.142 2.7 0.087106 -0.148234 2.78542 0.10163 -0.173037 2.7 0.046045 -0.165279 2.78542 0.053704 -0.192963 2.7 0 -0.171296 2.78542 0 -0.2 2.7 -0.046045 -0.165279 2.78542 -0.053704 -0.192963 2.7 -0.087106 -0.148234 2.78542 -0.10163 -0.173037 2.7 -0.121672 -0.121672 2.78542 -0.142 -0.142 2.7 -0.148234 -0.087106 2.78542 -0.173037 -0.10163 2.7 -0.165279 -0.046045 2.78542 -0.192963 -0.053704 2.7 -0.171296 0 2.78542 -0.2 0 2.7 -0.165279 0.046045 2.78542 -0.192963 0.053704 2.7 -0.148234 0.087106 2.78542 -0.173037 0.10163 2.7 -0.121672 0.121672 2.78542 -0.142 0.142 2.7 -0.087106 0.148234 2.78542 -0.10163 0.173037 2.7 -0.046045 0.165279 2.78542 -0.053704 0.192963 2.7 0 0.171296 2.78542 0 0.2 2.7 0.046045 0.165279 2.78542 0.053704 0.192963 2.7 0.087106 0.148234 2.78542 0.10163 0.173037 2.7 0.121672 0.121672 2.78542 0.142 0.142 2.7 0.148234 0.087106 2.78542 0.173037 0.10163 2.7 0.165279 0.046045 2.78542 0.192963 0.053704 2.7 0.350926 0 2.63611 0.338579 -0.09423 2.63611 0.574074 0 2.58889 0.553875 -0.15415 2.58889 0.825 0 2.55 0.795972 -0.221528 2.55 1.05926 0 2.51111 1.02199 -0.284431 2.51111 1.23241 0 2.46389 1.18904 -0.330924 2.46389 1.3 0 2.4 1.25426 -0.349074 2.4 0.303616 -0.178322 2.63611 0.49668 -0.291715 2.58889 0.713778 -0.419222 2.55 0.916455 -0.538261 2.51111 1.06626 -0.626246 2.46389 1.12474 -0.660593 2.4 0.249157 -0.249157 2.63611 0.407593 -0.407593 2.58889 0.58575 -0.58575 2.55 0.752074 -0.752074 2.51111 0.875009 -0.875009 2.46389 0.923 -0.923 2.4 0.178322 -0.303616 2.63611 0.291715 -0.49668 2.58889 0.419222 -0.713778 2.55 0.538261 -0.916455 2.51111 0.626246 -1.06626 2.46389 0.660593 -1.12474 2.4 0.09423 -0.338579 2.63611 0.15415 -0.553875 2.58889 0.221528 -0.795972 2.55 0.284431 -1.02199 2.51111 0.330924 -1.18904 2.46389 0.349074 -1.25426 2.4 0 -0.350926 2.63611 0 -0.574074 2.58889 0 -0.825 2.55 0 -1.05926 2.51111 0 -1.23241 2.46389 0 -1.3 2.4 -0.09423 -0.338579 2.63611 -0.15415 -0.553875 2.58889 -0.221528 -0.795972 2.55 -0.284431 -1.02199 2.51111 -0.330924 -1.18904 2.46389 -0.349074 -1.25426 2.4 -0.178322 -0.303616 2.63611 -0.291715 -0.49668 2.58889 -0.419222 -0.713778 2.55 -0.538261 -0.916455 2.51111 -0.626246 -1.06626 2.46389 -0.660593 -1.12474 2.4 -0.249157 -0.249157 2.63611 -0.407593 -0.407593 2.58889 -0.58575 -0.58575 2.55 -0.752074 -0.752074 2.51111 -0.875009 -0.875009 2.46389 -0.923 -0.923 2.4 -0.303616 -0.178322 2.63611 -0.49668 -0.291715 2.58889 -0.713778 -0.419222 2.55 -0.916455 -0.538261 2.51111 -1.06626 -0.626246 2.46389 -1.12474 -0.660593 2.4 -0.338579 -0.09423 2.63611 -0.553875 -0.15415 2.58889 -0.795972 -0.221528 2.55 -1.02199 -0.284431 2.51111 -1.18904 -0.330924 2.46389 -1.25426 -0.349074 2.4 -0.350926 0 2.63611 -0.574074 0 2.58889 -0.825 0 2.55 -1.05926 0 2.51111 -1.23241 0 2.46389 -1.3 0 2.4 -0.338579 0.09423 2.63611 -0.553875 0.15415 2.58889 -0.795972 0.221528 2.55 -1.02199 0.284431 2.51111 -1.18904 0.330924 2.46389 -1.25426 0.349074 2.4 -0.303616 0.178322 2.63611 -0.49668 0.291715 2.58889 -0.713778 0.419222 2.55 -0.916455 0.538261 2.51111 -1.06626 0.626246 2.46389 -1.12474 0.660593 2.4 -0.249157 0.249157 2.63611 -0.407593 0.407593 2.58889 -0.58575 0.58575 2.55 -0.752074 0.752074 2.51111 -0.875009 0.875009 2.46389 -0.923 0.923 2.4 -0.178322 0.303616 2.63611 -0.291715 0.49668 2.58889 -0.419222 0.713778 2.55 -0.538261 0.916455 2.51111 -0.626246 1.06626 2.46389 -0.660593 1.12474 2.4 -0.09423 0.338579 2.63611 -0.15415 0.553875 2.58889 -0.221528 0.795972 2.55 -0.284431 1.02199 2.51111 -0.330924 1.18904 2.46389 -0.349074 1.25426 2.4 0 0.350926 2.63611 0 0.574074 2.58889 0 0.825 2.55 0 1.05926 2.51111 0 1.23241 2.46389 0 1.3 2.4 0.09423 0.338579 2.63611 0.15415 0.553875 2.58889 0.221528 0.795972 2.55 0.284431 1.02199 2.51111 0.330924 1.18904 2.46389 0.349074 1.25426 2.4 0.178322 0.303616 2.63611 0.291715 0.49668 2.58889 0.419222 0.713778 2.55 0.538261 0.916455 2.51111 0.626246 1.06626 2.46389 0.660593 1.12474 2.4 0.249157 0.249157 2.63611 0.407593 0.407593 2.58889 0.58575 0.58575 2.55 0.752074 0.752074 2.51111 0.875009 0.875009 2.46389 0.923 0.923 2.4 0.303616 0.178322 2.63611 0.49668 0.291715 2.58889 0.713778 0.419222 2.55 0.916455 0.538261 2.51111 1.06626 0.626246 2.46389 1.12474 0.660593 2.4 0.338579 0.09423 2.63611 0.553875 0.15415 2.58889 0.795972 0.221528 2.55 1.02199 0.284431 2.51111 1.18904 0.330924 2.46389 1.25426 0.349074 2.4 -1.92454 0 2.02396 -1.6 0 2.025 -1.59259 -0.125 2.04167 -1.92704 -0.125 2.04055 -2.1963 0 2.01667 -2.20645 -0.125 2.03272 -2.4125 0 1.99687 -2.42824 -0.125 2.01146 -2.57037 0 1.95833 -2.58985 -0.125 1.97006 -2.66713 0 1.89479 -2.6887 -0.125 1.90181 -2.7 0 1.8 -2.72222 -0.125 1.8 -1.57407 -0.2 2.08333 -1.9333 -0.2 2.08202 -2.23182 -0.2 2.07284 -2.46759 -0.2 2.04792 -2.63855 -0.2 1.99938 -2.74263 -0.2 1.91937 -2.77778 -0.2 1.8 -1.55 -0.225 2.1375 -1.94144 -0.225 2.13594 -2.26481 -0.225 2.125 -2.51875 -0.225 2.09531 -2.70185 -0.225 2.0375 -2.81273 -0.225 1.94219 -2.85 -0.225 1.8 -1.52593 -0.2 2.19167 -1.94957 -0.2 2.18985 -2.29781 -0.2 2.17716 -2.56991 -0.2 2.14271 -2.76516 -0.2 2.07562 -2.88284 -0.2 1.96501 -2.92222 -0.2 1.8 -1.50741 -0.125 2.23333 -1.95583 -0.125 2.23133 -2.32318 -0.125 2.21728 -2.60926 -0.125 2.17917 -2.81385 -0.125 2.10494 -2.93676 -0.125 1.98256 -2.97778 -0.125 1.8 -1.5 0 2.25 -1.95833 0 2.24792 -2.33333 0 2.23333 -2.625 0 2.19375 -2.83333 0 2.11667 -2.95833 0 1.98958 -3 0 1.8 -1.50741 0.125 2.23333 -1.95583 0.125 2.23133 -2.32318 0.125 2.21728 -2.60926 0.125 2.17917 -2.81385 0.125 2.10494 -2.93676 0.125 1.98256 -2.97778 0.125 1.8 -1.52593 0.2 2.19167 -1.94957 0.2 2.18985 -2.29781 0.2 2.17716 -2.56991 0.2 2.14271 -2.76516 0.2 2.07562 -2.88284 0.2 1.96501 -2.92222 0.2 1.8 -1.55 0.225 2.1375 -1.94144 0.225 2.13594 -2.26481 0.225 2.125 -2.51875 0.225 2.09531 -2.70185 0.225 2.0375 -2.81273 0.225 1.94219 -2.85 0.225 1.8 -1.57407 0.2 2.08333 -1.9333 0.2 2.08202 -2.23182 0.2 2.07284 -2.46759 0.2 2.04792 -2.63855 0.2 1.99938 -2.74263 0.2 1.91937 -2.77778 0.2 1.8 -1.59259 0.125 2.04167 -1.92704 0.125 2.04055 -2.20645 0.125 2.03272 -2.42824 0.125 2.01146 -2.58985 0.125 1.97006 -2.6887 0.125 1.90181 -2.72222 0.125 1.8 -2.68287 0 1.67083 -2.70418 -0.125 1.66398 -2.62963 0 1.51667 -2.64829 -0.125 1.50535 -2.5375 0 1.35 -2.55185 -0.125 1.33576 -2.4037 0 1.18333 -2.41221 -0.125 1.16687 -2.22546 0 1.02917 -2.22668 -0.125 1.01033 -1.99259 -0.125 0.877778 -2.75747 -0.2 1.64684 -2.69492 -0.2 1.47706 -2.58773 -0.2 1.30017 -2.43347 -0.2 1.12572 -2.22972 -0.2 0.963227 -1.97407 -0.2 0.822222 -2.82674 -0.225 1.62457 -2.75556 -0.225 1.44028 -2.63437 -0.225 1.25391 -2.46111 -0.225 1.07222 -2.23368 -0.225 0.901997 -1.95 -0.225 0.75 -2.896 -0.2 1.60229 -2.81619 -0.2 1.4035 -2.68102 -0.2 1.20764 -2.48875 -0.2 1.01872 -2.23764 -0.2 0.840766 -1.92593 -0.2 0.677778 -2.94929 -0.125 1.58515 -2.86283 -0.125 1.37521 -2.7169 -0.125 1.17205 -2.51001 -0.125 0.977572 -2.24068 -0.125 0.793666 -1.90741 -0.125 0.622222 -2.9706 0 1.5783 -2.88148 0 1.36389 -2.73125 0 1.15781 -2.51852 0 0.961111 -2.2419 0 0.774826 -1.9 0 0.6 -2.94929 0.125 1.58515 -2.86283 0.125 1.37521 -2.7169 0.125 1.17205 -2.51001 0.125 0.977572 -2.24068 0.125 0.793666 -1.90741 0.125 0.622222 -2.896 0.2 1.60229 -2.81619 0.2 1.4035 -2.68102 0.2 1.20764 -2.48875 0.2 1.01872 -2.23764 0.2 0.840766 -1.92593 0.2 0.677778 -2.82674 0.225 1.62457 -2.75556 0.225 1.44028 -2.63437 0.225 1.25391 -2.46111 0.225 1.07222 -2.23368 0.225 0.901997 -1.95 0.225 0.75 -2.75747 0.2 1.64684 -2.69492 0.2 1.47706 -2.58773 0.2 1.30017 -2.43347 0.2 1.12572 -2.22972 0.2 0.963227 -1.97407 0.2 0.822222 -2.70418 0.125 1.66398 -2.64829 0.125 1.50535 -2.55185 0.125 1.33576 -2.41221 0.125 1.16687 -2.22668 0.125 1.01033 -1.99259 0.125 0.877778 2.0588 0 1.47639 1.7 0 1.425 1.7 -0.275 1.36389 2.07238 -0.262346 1.42521 2.27037 0 1.61111 2.29012 -0.23071 1.57202 2.3875 0 1.8 2.40972 -0.189583 1.77361 2.46296 0 2.01389 2.48765 -0.148457 1.99928 2.54954 0 2.22361 2.5804 -0.116821 2.21831 2.7 0 2.4 2.74444 -0.104167 2.4 1.7 -0.44 1.21111 2.10633 -0.419753 1.29725 2.33951 -0.369136 1.47428 2.46528 -0.303333 1.70764 2.54938 -0.237531 1.96276 2.65756 -0.186914 2.20507 2.85556 -0.166667 2.4 1.7 -0.495 1.0125 2.15046 -0.472222 1.1309 2.4037 -0.415278 1.34722 2.5375 -0.34125 1.62187 2.62963 -0.267222 1.91528 2.75787 -0.210278 2.18785 3 -0.1875 2.4 1.7 -0.44 0.813889 2.1946 -0.419753 0.964558 2.4679 -0.369136 1.22016 2.60972 -0.303333 1.53611 2.70988 -0.237531 1.8678 2.85818 -0.186914 2.17063 3.14444 -0.166667 2.4 1.7 -0.275 0.661111 2.22855 -0.262346 0.8366 2.51728 -0.23071 1.12243 2.66528 -0.189583 1.47014 2.7716 -0.148457 1.83128 2.93534 -0.116821 2.15738 3.25556 -0.104167 2.4 1.7 0 0.6 2.24213 0 0.785417 2.53704 0 1.08333 2.6875 0 1.44375 2.7963 0 1.81667 2.9662 0 2.15208 3.3 0 2.4 1.7 0.275 0.661111 2.22855 0.262346 0.8366 2.51728 0.23071 1.12243 2.66528 0.189583 1.47014 2.7716 0.148457 1.83128 2.93534 0.116821 2.15738 3.25556 0.104167 2.4 1.7 0.44 0.813889 2.1946 0.419753 0.964558 2.4679 0.369136 1.22016 2.60972 0.303333 1.53611 2.70988 0.237531 1.8678 2.85818 0.186914 2.17063 3.14444 0.166667 2.4 1.7 0.495 1.0125 2.15046 0.472222 1.1309 2.4037 0.415278 1.34722 2.5375 0.34125 1.62187 2.62963 0.267222 1.91528 2.75787 0.210278 2.18785 3 0.1875 2.4 1.7 0.44 1.21111 2.10633 0.419753 1.29725 2.33951 0.369136 1.47428 2.46528 0.303333 1.70764 2.54938 0.237531 1.96276 2.65756 0.186914 2.20507 2.85556 0.166667 2.4 1.7 0.275 1.36389 2.07238 0.262346 1.42521 2.29012 0.23071 1.57202 2.40972 0.189583 1.77361 2.48765 0.148457 1.99928 2.5804 0.116821 2.21831 2.74444 0.104167 2.4 2.74907 0 2.43125 2.79641 -0.101032 2.43193 2.79259 0 2.45 2.83978 -0.092978 2.45123 2.825 0 2.45625 2.86968 -0.082031 2.45781 2.84074 0 2.45 2.88121 -0.070216 2.45154 2.83426 0 2.43125 2.86949 -0.059558 2.43231 2.8 0 2.4 2.82963 -0.052083 2.4 2.91474 -0.161574 2.43361 2.95775 -0.148148 2.45432 2.98137 -0.129167 2.46172 2.98237 -0.107407 2.4554 2.95756 -0.085648 2.43496 2.9037 -0.066667 2.4 3.06858 -0.181684 2.43581 3.11111 -0.165972 2.45833 3.12656 -0.142969 2.4668 3.11389 -0.115278 2.46042 3.07205 -0.085504 2.43841 3 -0.05625 2.4 3.22241 -0.16142 2.438 3.26447 -0.146914 2.46235 3.27176 -0.125 2.47187 3.2454 -0.097531 2.46543 3.18654 -0.066358 2.44186 3.0963 -0.033333 2.4 3.34075 -0.100839 2.43969 3.38244 -0.091435 2.46543 3.38345 -0.076823 2.47578 3.34657 -0.05787 2.46929 3.27461 -0.035446 2.44451 3.17037 -0.010417 2.4 3.38808 0 2.44036 3.42963 0 2.46667 3.42813 0 2.47734 3.38704 0 2.47083 3.30984 0 2.44557 3.2 0 2.4 3.34075 0.10108 2.43969 3.38244 0.093364 2.46543 3.38345 0.083333 2.47578 3.34657 0.073303 2.46929 3.27461 0.065586 2.44451 3.17037 0.0625 2.4 3.22241 0.161728 2.438 3.26447 0.149383 2.46235 3.27176 0.133333 2.47187 3.2454 0.117284 2.46543 3.18654 0.104938 2.44186 3.0963 0.1 2.4 3.06858 0.181944 2.43581 3.11111 0.168056 2.45833 3.12656 0.15 2.4668 3.11389 0.131944 2.46042 3.07205 0.118056 2.43841 3 0.1125 2.4 2.91474 0.161728 2.43361 2.95775 0.149383 2.45432 2.98137 0.133333 2.46172 2.98237 0.117284 2.4554 2.95756 0.104938 2.43496 2.9037 0.1 2.4 2.79641 0.10108 2.43193 2.83978 0.093364 2.45123 2.86968 0.083333 2.45781 2.88121 0.073303 2.45154 2.86949 0.065586 2.43231 2.82963 0.0625 2.4 0.278704 0 3.12708 0 0 3.15 0.268946 -0.075078 3.12708 0.362963 0 3.06667 0.350254 -0.097771 3.06667 0.325 0 2.98125 0.313617 -0.087529 2.98125 0.237037 0 2.88333 0.228728 -0.063803 2.88333 0.241285 -0.141931 3.12708 0.314228 -0.184834 3.06667 0.281352 -0.165481 2.98125 0.20518 -0.120647 2.88333 0.19814 -0.19814 3.12708 0.258037 -0.258037 3.06667 0.231031 -0.231031 2.98125 0.168463 -0.168463 2.88333 0.141931 -0.241285 3.12708 0.184834 -0.314228 3.06667 0.165481 -0.281352 2.98125 0.120647 -0.20518 2.88333 0.075078 -0.268946 3.12708 0.097771 -0.350254 3.06667 0.087529 -0.313617 2.98125 0.063803 -0.228728 2.88333 0 -0.278704 3.12708 0 -0.362963 3.06667 0 -0.325 2.98125 0 -0.237037 2.88333 -0.075078 -0.268946 3.12708 -0.097771 -0.350254 3.06667 -0.087529 -0.313617 2.98125 -0.063803 -0.228728 2.88333 -0.141931 -0.241285 3.12708 -0.184834 -0.314228 3.06667 -0.165481 -0.281352 2.98125 -0.120647 -0.20518 2.88333 -0.19814 -0.19814 3.12708 -0.258037 -0.258037 3.06667 -0.231031 -0.231031 2.98125 -0.168463 -0.168463 2.88333 -0.241285 -0.141931 3.12708 -0.314228 -0.184834 3.06667 -0.281352 -0.165481 2.98125 -0.20518 -0.120647 2.88333 -0.268946 -0.075078 3.12708 -0.350254 -0.097771 3.06667 -0.313617 -0.087529 2.98125 -0.228728 -0.063803 2.88333 -0.278704 0 3.12708 -0.362963 0 3.06667 -0.325 0 2.98125 -0.237037 0 2.88333 -0.268946 0.075078 3.12708 -0.350254 0.097771 3.06667 -0.313617 0.087529 2.98125 -0.228728 0.063803 2.88333 -0.241285 0.141931 3.12708 -0.314228 0.184834 3.06667 -0.281352 0.165481 2.98125 -0.20518 0.120647 2.88333 -0.19814 0.19814 3.12708 -0.258037 0.258037 3.06667 -0.231031 0.231031 2.98125 -0.168463 0.168463 2.88333 -0.141931 0.241285 3.12708 -0.184834 0.314228 3.06667 -0.165481 0.281352 2.98125 -0.120647 0.20518 2.88333 -0.075078 0.268946 3.12708 -0.097771 0.350254 3.06667 -0.087529 0.313617 2.98125 -0.063803 0.228728 2.88333 0 0.278704 3.12708 0 0.362963 3.06667 0 0.325 2.98125 0 0.237037 2.88333 0.075078 0.268946 3.12708 0.097771 0.350254 3.06667 0.087529 0.313617 2.98125 0.063803 0.228728 2.88333 0.141931 0.241285 3.12708 0.184834 0.314228 3.06667 0.165481 0.281352 2.98125 0.120647 0.20518 2.88333 0.19814 0.19814 3.12708 0.258037 0.258037 3.06667 0.231031 0.231031 2.98125 0.168463 0.168463 2.88333 0.241285 0.141931 3.12708 0.314228 0.184834 3.06667 0.281352 0.165481 2.98125 0.20518 0.120647 2.88333 0.268946 0.075078 3.12708 0.350254 0.097771 3.06667 0.313617 0.087529 2.98125 0.228728 0.063803 2.88333 3 0 1 2 3 0 2 3 3 4 0 3 3 4 3 5 3 6 4 5 3 6 5 7 3 8 6 7 3 8 7 9 3 10 8 9 3 10 9 11 3 12 10 11 3 12 11 13 3 3 2 14 3 3 14 15 3 5 3 15 3 5 15 16 3 7 5 16 3 7 16 17 3 9 7 17 3 9 17 18 3 11 9 18 3 11 18 19 3 13 11 19 3 13 19 20 3 15 14 21 3 15 21 22 3 16 15 22 3 16 22 23 3 17 16 23 3 17 23 24 3 18 17 24 3 18 24 25 3 19 18 25 3 19 25 26 3 20 19 26 3 20 26 27 3 22 21 28 3 22 28 29 3 23 22 29 3 23 29 30 3 24 23 30 3 24 30 31 3 25 24 31 3 25 31 32 3 26 25 32 3 26 32 33 3 27 26 33 3 27 33 34 3 29 28 35 3 29 35 36 3 30 29 36 3 30 36 37 3 31 30 37 3 31 37 38 3 32 31 38 3 32 38 39 3 33 32 39 3 33 39 40 3 34 33 40 3 34 40 41 3 36 35 42 3 36 42 43 3 37 36 43 3 37 43 44 3 38 37 44 3 38 44 45 3 39 38 45 3 39 45 46 3 40 39 46 3 40 46 47 3 41 40 47 3 41 47 48 3 43 42 49 3 43 49 50 3 44 43 50 3 44 50 51 3 45 44 51 3 45 51 52 3 46 45 52 3 46 52 53 3 47 46 53 3 47 53 54 3 48 47 54 3 48 54 55 3 50 49 56 3 50 56 57 3 51 50 57 3 51 57 58 3 52 51 58 3 52 58 59 3 53 52 59 3 53 59 60 3 54 53 60 3 54 60 61 3 55 54 61 3 55 61 62 3 57 56 63 3 57 63 64 3 58 57 64 3 58 64 65 3 59 58 65 3 59 65 66 3 60 59 66 3 60 66 67 3 61 60 67 3 61 67 68 3 62 61 68 3 62 68 69 3 64 63 70 3 64 70 71 3 65 64 71 3 65 71 72 3 66 65 72 3 66 72 73 3 67 66 73 3 67 73 74 3 68 67 74 3 68 74 75 3 69 68 75 3 69 75 76 3 71 70 77 3 71 77 78 3 72 71 78 3 72 78 79 3 73 72 79 3 73 79 80 3 74 73 80 3 74 80 81 3 75 74 81 3 75 81 82 3 76 75 82 3 76 82 83 3 78 77 84 3 78 84 85 3 79 78 85 3 79 85 86 3 80 79 86 3 80 86 87 3 81 80 87 3 81 87 88 3 82 81 88 3 82 88 89 3 83 82 89 3 83 89 90 3 85 84 91 3 85 91 92 3 86 85 92 3 86 92 93 3 87 86 93 3 87 93 94 3 88 87 94 3 88 94 95 3 89 88 95 3 89 95 96 3 90 89 96 3 90 96 97 3 92 91 98 3 92 98 99 3 93 92 99 3 93 99 100 3 94 93 100 3 94 100 101 3 95 94 101 3 95 101 102 3 96 95 102 3 96 102 103 3 97 96 103 3 97 103 104 3 99 98 105 3 99 105 106 3 100 99 106 3 100 106 107 3 101 100 107 3 101 107 108 3 102 101 108 3 102 108 109 3 103 102 109 3 103 109 110 3 104 103 110 3 104 110 111 3 106 105 112 3 106 112 113 3 107 106 113 3 107 113 114 3 108 107 114 3 108 114 115 3 109 108 115 3 109 115 116 3 110 109 116 3 110 116 117 3 111 110 117 3 111 117 118 3 113 112 119 3 113 119 120 3 114 113 120 3 114 120 121 3 115 114 121 3 115 121 122 3 116 115 122 3 116 122 123 3 117 116 123 3 117 123 124 3 118 117 124 3 118 124 125 3 120 119 126 3 120 126 127 3 121 120 127 3 121 127 128 3 122 121 128 3 122 128 129 3 123 122 129 3 123 129 130 3 124 123 130 3 124 130 131 3 125 124 131 3 125 131 132 3 127 126 133 3 127 133 134 3 128 127 134 3 128 134 135 3 129 128 135 3 129 135 136 3 130 129 136 3 130 136 137 3 131 130 137 3 131 137 138 3 132 131 138 3 132 138 139 3 134 133 140 3 134 140 141 3 135 134 141 3 135 141 142 3 136 135 142 3 136 142 143 3 137 136 143 3 137 143 144 3 138 137 144 3 138 144 145 3 139 138 145 3 139 145 146 3 141 140 147 3 141 147 148 3 142 141 148 3 142 148 149 3 143 142 149 3 143 149 150 3 144 143 150 3 144 150 151 3 145 144 151 3 145 151 152 3 146 145 152 3 146 152 153 3 148 147 154 3 148 154 155 3 149 148 155 3 149 155 156 3 150 149 156 3 150 156 157 3 151 150 157 3 151 157 158 3 152 151 158 3 152 158 159 3 153 152 159 3 153 159 160 3 155 154 161 3 155 161 162 3 156 155 162 3 156 162 163 3 157 156 163 3 157 163 164 3 158 157 164 3 158 164 165 3 159 158 165 3 159 165 166 3 160 159 166 3 160 166 167 3 162 161 1 3 162 1 0 3 163 162 0 3 163 0 4 3 164 163 4 3 164 4 6 3 165 164 6 3 165 6 8 3 166 165 8 3 166 8 10 3 167 166 10 3 167 10 12 3 168 12 13 3 168 13 169 3 170 168 169 3 170 169 171 3 172 170 171 3 172 171 173 3 174 172 173 3 174 173 175 3 176 174 175 3 176 175 177 3 178 176 177 3 178 177 179 3 169 13 20 3 169 20 180 3 171 169 180 3 171 180 181 3 173 171 181 3 173 181 182 3 175 173 182 3 175 182 183 3 177 175 183 3 177 183 184 3 179 177 184 3 179 184 185 3 180 20 27 3 180 27 186 3 181 180 186 3 181 186 187 3 182 181 187 3 182 187 188 3 183 182 188 3 183 188 189 3 184 183 189 3 184 189 190 3 185 184 190 3 185 190 191 3 186 27 34 3 186 34 192 3 187 186 192 3 187 192 193 3 188 187 193 3 188 193 194 3 189 188 194 3 189 194 195 3 190 189 195 3 190 195 196 3 191 190 196 3 191 196 197 3 192 34 41 3 192 41 198 3 193 192 198 3 193 198 199 3 194 193 199 3 194 199 200 3 195 194 200 3 195 200 201 3 196 195 201 3 196 201 202 3 197 196 202 3 197 202 203 3 198 41 48 3 198 48 204 3 199 198 204 3 199 204 205 3 200 199 205 3 200 205 206 3 201 200 206 3 201 206 207 3 202 201 207 3 202 207 208 3 203 202 208 3 203 208 209 3 204 48 55 3 204 55 210 3 205 204 210 3 205 210 211 3 206 205 211 3 206 211 212 3 207 206 212 3 207 212 213 3 208 207 213 3 208 213 214 3 209 208 214 3 209 214 215 3 210 55 62 3 210 62 216 3 211 210 216 3 211 216 217 3 212 211 217 3 212 217 218 3 213 212 218 3 213 218 219 3 214 213 219 3 214 219 220 3 215 214 220 3 215 220 221 3 216 62 69 3 216 69 222 3 217 216 222 3 217 222 223 3 218 217 223 3 218 223 224 3 219 218 224 3 219 224 225 3 220 219 225 3 220 225 226 3 221 220 226 3 221 226 227 3 222 69 76 3 222 76 228 3 223 222 228 3 223 228 229 3 224 223 229 3 224 229 230 3 225 224 230 3 225 230 231 3 226 225 231 3 226 231 232 3 227 226 232 3 227 232 233 3 228 76 83 3 228 83 234 3 229 228 234 3 229 234 235 3 230 229 235 3 230 235 236 3 231 230 236 3 231 236 237 3 232 231 237 3 232 237 238 3 233 232 238 3 233 238 239 3 234 83 90 3 234 90 240 3 235 234 240 3 235 240 241 3 236 235 241 3 236 241 242 3 237 236 242 3 237 242 243 3 238 237 243 3 238 243 244 3 239 238 244 3 239 244 245 3 240 90 97 3 240 97 246 3 241 240 246 3 241 246 247 3 242 241 247 3 242 247 248 3 243 242 248 3 243 248 249 3 244 243 249 3 244 249 250 3 245 244 250 3 245 250 251 3 246 97 104 3 246 104 252 3 247 246 252 3 247 252 253 3 248 247 253 3 248 253 254 3 249 248 254 3 249 254 255 3 250 249 255 3 250 255 256 3 251 250 256 3 251 256 257 3 252 104 111 3 252 111 258 3 253 252 258 3 253 258 259 3 254 253 259 3 254 259 260 3 255 254 260 3 255 260 261 3 256 255 261 3 256 261 262 3 257 256 262 3 257 262 263 3 258 111 118 3 258 118 264 3 259 258 264 3 259 264 265 3 260 259 265 3 260 265 266 3 261 260 266 3 261 266 267 3 262 261 267 3 262 267 268 3 263 262 268 3 263 268 269 3 264 118 125 3 264 125 270 3 265 264 270 3 265 270 271 3 266 265 271 3 266 271 272 3 267 266 272 3 267 272 273 3 268 267 273 3 268 273 274 3 269 268 274 3 269 274 275 3 270 125 132 3 270 132 276 3 271 270 276 3 271 276 277 3 272 271 277 3 272 277 278 3 273 272 278 3 273 278 279 3 274 273 279 3 274 279 280 3 275 274 280 3 275 280 281 3 276 132 139 3 276 139 282 3 277 276 282 3 277 282 283 3 278 277 283 3 278 283 284 3 279 278 284 3 279 284 285 3 280 279 285 3 280 285 286 3 281 280 286 3 281 286 287 3 282 139 146 3 282 146 288 3 283 282 288 3 283 288 289 3 284 283 289 3 284 289 290 3 285 284 290 3 285 290 291 3 286 285 291 3 286 291 292 3 287 286 292 3 287 292 293 3 288 146 153 3 288 153 294 3 289 288 294 3 289 294 295 3 290 289 295 3 290 295 296 3 291 290 296 3 291 296 297 3 292 291 297 3 292 297 298 3 293 292 298 3 293 298 299 3 294 153 160 3 294 160 300 3 295 294 300 3 295 300 301 3 296 295 301 3 296 301 302 3 297 296 302 3 297 302 303 3 298 297 303 3 298 303 304 3 299 298 304 3 299 304 305 3 300 160 167 3 300 167 306 3 301 300 306 3 301 306 307 3 302 301 307 3 302 307 308 3 303 302 308 3 303 308 309 3 304 303 309 3 304 309 310 3 305 304 310 3 305 310 311 3 306 167 12 3 306 12 168 3 307 306 168 3 307 168 170 3 308 307 170 3 308 170 172 3 309 308 172 3 309 172 174 3 310 309 174 3 310 174 176 3 311 310 176 3 311 176 178 3 312 178 179 3 312 179 313 3 314 312 313 3 314 313 315 3 316 314 315 3 316 315 317 3 318 316 317 3 318 317 319 3 320 318 319 3 320 319 321 3 322 320 321 3 322 321 323 3 313 179 185 3 313 185 324 3 315 313 324 3 315 324 325 3 317 315 325 3 317 325 326 3 319 317 326 3 319 326 327 3 321 319 327 3 321 327 328 3 323 321 328 3 323 328 329 3 324 185 191 3 324 191 330 3 325 324 330 3 325 330 331 3 326 325 331 3 326 331 332 3 327 326 332 3 327 332 333 3 328 327 333 3 328 333 334 3 329 328 334 3 329 334 335 3 330 191 197 3 330 197 336 3 331 330 336 3 331 336 337 3 332 331 337 3 332 337 338 3 333 332 338 3 333 338 339 3 334 333 339 3 334 339 340 3 335 334 340 3 335 340 341 3 336 197 203 3 336 203 342 3 337 336 342 3 337 342 343 3 338 337 343 3 338 343 344 3 339 338 344 3 339 344 345 3 340 339 345 3 340 345 346 3 341 340 346 3 341 346 347 3 342 203 209 3 342 209 348 3 343 342 348 3 343 348 349 3 344 343 349 3 344 349 350 3 345 344 350 3 345 350 351 3 346 345 351 3 346 351 352 3 347 346 352 3 347 352 353 3 348 209 215 3 348 215 354 3 349 348 354 3 349 354 355 3 350 349 355 3 350 355 356 3 351 350 356 3 351 356 357 3 352 351 357 3 352 357 358 3 353 352 358 3 353 358 359 3 354 215 221 3 354 221 360 3 355 354 360 3 355 360 361 3 356 355 361 3 356 361 362 3 357 356 362 3 357 362 363 3 358 357 363 3 358 363 364 3 359 358 364 3 359 364 365 3 360 221 227 3 360 227 366 3 361 360 366 3 361 366 367 3 362 361 367 3 362 367 368 3 363 362 368 3 363 368 369 3 364 363 369 3 364 369 370 3 365 364 370 3 365 370 371 3 366 227 233 3 366 233 372 3 367 366 372 3 367 372 373 3 368 367 373 3 368 373 374 3 369 368 374 3 369 374 375 3 370 369 375 3 370 375 376 3 371 370 376 3 371 376 377 3 372 233 239 3 372 239 378 3 373 372 378 3 373 378 379 3 374 373 379 3 374 379 380 3 375 374 380 3 375 380 381 3 376 375 381 3 376 381 382 3 377 376 382 3 377 382 383 3 378 239 245 3 378 245 384 3 379 378 384 3 379 384 385 3 380 379 385 3 380 385 386 3 381 380 386 3 381 386 387 3 382 381 387 3 382 387 388 3 383 382 388 3 383 388 389 3 384 245 251 3 384 251 390 3 385 384 390 3 385 390 391 3 386 385 391 3 386 391 392 3 387 386 392 3 387 392 393 3 388 387 393 3 388 393 394 3 389 388 394 3 389 394 395 3 390 251 257 3 390 257 396 3 391 390 396 3 391 396 397 3 392 391 397 3 392 397 398 3 393 392 398 3 393 398 399 3 394 393 399 3 394 399 400 3 395 394 400 3 395 400 401 3 396 257 263 3 396 263 402 3 397 396 402 3 397 402 403 3 398 397 403 3 398 403 404 3 399 398 404 3 399 404 405 3 400 399 405 3 400 405 406 3 401 400 406 3 401 406 407 3 402 263 269 3 402 269 408 3 403 402 408 3 403 408 409 3 404 403 409 3 404 409 410 3 405 404 410 3 405 410 411 3 406 405 411 3 406 411 412 3 407 406 412 3 407 412 413 3 408 269 275 3 408 275 414 3 409 408 414 3 409 414 415 3 410 409 415 3 410 415 416 3 411 410 416 3 411 416 417 3 412 411 417 3 412 417 418 3 413 412 418 3 413 418 419 3 414 275 281 3 414 281 420 3 415 414 420 3 415 420 421 3 416 415 421 3 416 421 422 3 417 416 422 3 417 422 423 3 418 417 423 3 418 423 424 3 419 418 424 3 419 424 425 3 420 281 287 3 420 287 426 3 421 420 426 3 421 426 427 3 422 421 427 3 422 427 428 3 423 422 428 3 423 428 429 3 424 423 429 3 424 429 430 3 425 424 430 3 425 430 431 3 426 287 293 3 426 293 432 3 427 426 432 3 427 432 433 3 428 427 433 3 428 433 434 3 429 428 434 3 429 434 435 3 430 429 435 3 430 435 436 3 431 430 436 3 431 436 437 3 432 293 299 3 432 299 438 3 433 432 438 3 433 438 439 3 434 433 439 3 434 439 440 3 435 434 440 3 435 440 441 3 436 435 441 3 436 441 442 3 437 436 442 3 437 442 443 3 438 299 305 3 438 305 444 3 439 438 444 3 439 444 445 3 440 439 445 3 440 445 446 3 441 440 446 3 441 446 447 3 442 441 447 3 442 447 448 3 443 442 448 3 443 448 449 3 444 305 311 3 444 311 450 3 445 444 450 3 445 450 451 3 446 445 451 3 446 451 452 3 447 446 452 3 447 452 453 3 448 447 453 3 448 453 454 3 449 448 454 3 449 454 455 3 450 311 178 3 450 178 312 3 451 450 312 3 451 312 314 3 452 451 314 3 452 314 316 3 453 452 316 3 453 316 318 3 454 453 318 3 454 318 320 3 455 454 320 3 455 320 322 3 456 457 458 3 459 456 458 3 459 458 460 3 461 459 460 3 461 460 462 3 463 461 462 3 463 462 464 3 465 463 464 3 465 464 466 3 322 465 466 3 322 466 455 3 458 457 467 3 460 458 467 3 460 467 468 3 462 460 468 3 462 468 469 3 464 462 469 3 464 469 470 3 466 464 470 3 466 470 471 3 455 466 471 3 455 471 449 3 467 457 472 3 468 467 472 3 468 472 473 3 469 468 473 3 469 473 474 3 470 469 474 3 470 474 475 3 471 470 475 3 471 475 476 3 449 471 476 3 449 476 443 3 472 457 477 3 473 472 477 3 473 477 478 3 474 473 478 3 474 478 479 3 475 474 479 3 475 479 480 3 476 475 480 3 476 480 481 3 443 476 481 3 443 481 437 3 477 457 482 3 478 477 482 3 478 482 483 3 479 478 483 3 479 483 484 3 480 479 484 3 480 484 485 3 481 480 485 3 481 485 486 3 437 481 486 3 437 486 431 3 482 457 487 3 483 482 487 3 483 487 488 3 484 483 488 3 484 488 489 3 485 484 489 3 485 489 490 3 486 485 490 3 486 490 491 3 431 486 491 3 431 491 425 3 487 457 492 3 488 487 492 3 488 492 493 3 489 488 493 3 489 493 494 3 490 489 494 3 490 494 495 3 491 490 495 3 491 495 496 3 425 491 496 3 425 496 419 3 492 457 497 3 493 492 497 3 493 497 498 3 494 493 498 3 494 498 499 3 495 494 499 3 495 499 500 3 496 495 500 3 496 500 501 3 419 496 501 3 419 501 413 3 497 457 502 3 498 497 502 3 498 502 503 3 499 498 503 3 499 503 504 3 500 499 504 3 500 504 505 3 501 500 505 3 501 505 506 3 413 501 506 3 413 506 407 3 502 457 507 3 503 502 507 3 503 507 508 3 504 503 508 3 504 508 509 3 505 504 509 3 505 509 510 3 506 505 510 3 506 510 511 3 407 506 511 3 407 511 401 3 507 457 512 3 508 507 512 3 508 512 513 3 509 508 513 3 509 513 514 3 510 509 514 3 510 514 515 3 511 510 515 3 511 515 516 3 401 511 516 3 401 516 395 3 512 457 517 3 513 512 517 3 513 517 518 3 514 513 518 3 514 518 519 3 515 514 519 3 515 519 520 3 516 515 520 3 516 520 521 3 395 516 521 3 395 521 389 3 517 457 522 3 518 517 522 3 518 522 523 3 519 518 523 3 519 523 524 3 520 519 524 3 520 524 525 3 521 520 525 3 521 525 526 3 389 521 526 3 389 526 383 3 522 457 527 3 523 522 527 3 523 527 528 3 524 523 528 3 524 528 529 3 525 524 529 3 525 529 530 3 526 525 530 3 526 530 531 3 383 526 531 3 383 531 377 3 527 457 532 3 528 527 532 3 528 532 533 3 529 528 533 3 529 533 534 3 530 529 534 3 530 534 535 3 531 530 535 3 531 535 536 3 377 531 536 3 377 536 371 3 532 457 537 3 533 532 537 3 533 537 538 3 534 533 538 3 534 538 539 3 535 534 539 3 535 539 540 3 536 535 540 3 536 540 541 3 371 536 541 3 371 541 365 3 537 457 542 3 538 537 542 3 538 542 543 3 539 538 543 3 539 543 544 3 540 539 544 3 540 544 545 3 541 540 545 3 541 545 546 3 365 541 546 3 365 546 359 3 542 457 547 3 543 542 547 3 543 547 548 3 544 543 548 3 544 548 549 3 545 544 549 3 545 549 550 3 546 545 550 3 546 550 551 3 359 546 551 3 359 551 353 3 547 457 552 3 548 547 552 3 548 552 553 3 549 548 553 3 549 553 554 3 550 549 554 3 550 554 555 3 551 550 555 3 551 555 556 3 353 551 556 3 353 556 347 3 552 457 557 3 553 552 557 3 553 557 558 3 554 553 558 3 554 558 559 3 555 554 559 3 555 559 560 3 556 555 560 3 556 560 561 3 347 556 561 3 347 561 341 3 557 457 562 3 558 557 562 3 558 562 563 3 559 558 563 3 559 563 564 3 560 559 564 3 560 564 565 3 561 560 565 3 561 565 566 3 341 561 566 3 341 566 335 3 562 457 567 3 563 562 567 3 563 567 568 3 564 563 568 3 564 568 569 3 565 564 569 3 565 569 570 3 566 565 570 3 566 570 571 3 335 566 571 3 335 571 329 3 567 457 572 3 568 567 572 3 568 572 573 3 569 568 573 3 569 573 574 3 570 569 574 3 570 574 575 3 571 570 575 3 571 575 576 3 329 571 576 3 329 576 323 3 572 457 456 3 573 572 456 3 573 456 459 3 574 573 459 3 574 459 461 3 575 574 461 3 575 461 463 3 576 575 463 3 576 463 465 3 323 576 465 3 323 465 322 3 577 578 579 3 577 579 580 3 580 579 581 3 580 581 582 3 582 581 583 3 582 583 584 3 584 583 585 3 584 585 586 3 586 585 587 3 586 587 588 3 588 587 589 3 588 589 590 3 590 589 591 3 590 591 592 3 592 591 593 3 592 593 594 3 594 593 595 3 594 595 596 3 596 595 597 3 596 597 598 3 598 597 599 3 598 599 600 3 600 599 601 3 600 601 602 3 602 601 603 3 602 603 604 3 604 603 605 3 604 605 606 3 606 605 607 3 606 607 608 3 608 607 609 3 608 609 610 3 610 609 611 3 610 611 612 3 612 611 613 3 612 613 614 3 614 613 615 3 614 615 616 3 616 615 617 3 616 617 618 3 618 617 619 3 618 619 620 3 620 619 621 3 620 621 622 3 622 621 623 3 622 623 624 3 624 623 578 3 624 578 577 3 625 577 580 3 625 580 626 3 627 625 626 3 627 626 628 3 629 627 628 3 629 628 630 3 631 629 630 3 631 630 632 3 633 631 632 3 633 632 634 3 635 633 634 3 635 634 636 3 626 580 582 3 626 582 637 3 628 626 637 3 628 637 638 3 630 628 638 3 630 638 639 3 632 630 639 3 632 639 640 3 634 632 640 3 634 640 641 3 636 634 641 3 636 641 642 3 637 582 584 3 637 584 643 3 638 637 643 3 638 643 644 3 639 638 644 3 639 644 645 3 640 639 645 3 640 645 646 3 641 640 646 3 641 646 647 3 642 641 647 3 642 647 648 3 643 584 586 3 643 586 649 3 644 643 649 3 644 649 650 3 645 644 650 3 645 650 651 3 646 645 651 3 646 651 652 3 647 646 652 3 647 652 653 3 648 647 653 3 648 653 654 3 649 586 588 3 649 588 655 3 650 649 655 3 650 655 656 3 651 650 656 3 651 656 657 3 652 651 657 3 652 657 658 3 653 652 658 3 653 658 659 3 654 653 659 3 654 659 660 3 655 588 590 3 655 590 661 3 656 655 661 3 656 661 662 3 657 656 662 3 657 662 663 3 658 657 663 3 658 663 664 3 659 658 664 3 659 664 665 3 660 659 665 3 660 665 666 3 661 590 592 3 661 592 667 3 662 661 667 3 662 667 668 3 663 662 668 3 663 668 669 3 664 663 669 3 664 669 670 3 665 664 670 3 665 670 671 3 666 665 671 3 666 671 672 3 667 592 594 3 667 594 673 3 668 667 673 3 668 673 674 3 669 668 674 3 669 674 675 3 670 669 675 3 670 675 676 3 671 670 676 3 671 676 677 3 672 671 677 3 672 677 678 3 673 594 596 3 673 596 679 3 674 673 679 3 674 679 680 3 675 674 680 3 675 680 681 3 676 675 681 3 676 681 682 3 677 676 682 3 677 682 683 3 678 677 683 3 678 683 684 3 679 596 598 3 679 598 685 3 680 679 685 3 680 685 686 3 681 680 686 3 681 686 687 3 682 681 687 3 682 687 688 3 683 682 688 3 683 688 689 3 684 683 689 3 684 689 690 3 685 598 600 3 685 600 691 3 686 685 691 3 686 691 692 3 687 686 692 3 687 692 693 3 688 687 693 3 688 693 694 3 689 688 694 3 689 694 695 3 690 689 695 3 690 695 696 3 691 600 602 3 691 602 697 3 692 691 697 3 692 697 698 3 693 692 698 3 693 698 699 3 694 693 699 3 694 699 700 3 695 694 700 3 695 700 701 3 696 695 701 3 696 701 702 3 697 602 604 3 697 604 703 3 698 697 703 3 698 703 704 3 699 698 704 3 699 704 705 3 700 699 705 3 700 705 706 3 701 700 706 3 701 706 707 3 702 701 707 3 702 707 708 3 703 604 606 3 703 606 709 3 704 703 709 3 704 709 710 3 705 704 710 3 705 710 711 3 706 705 711 3 706 711 712 3 707 706 712 3 707 712 713 3 708 707 713 3 708 713 714 3 709 606 608 3 709 608 715 3 710 709 715 3 710 715 716 3 711 710 716 3 711 716 717 3 712 711 717 3 712 717 718 3 713 712 718 3 713 718 719 3 714 713 719 3 714 719 720 3 715 608 610 3 715 610 721 3 716 715 721 3 716 721 722 3 717 716 722 3 717 722 723 3 718 717 723 3 718 723 724 3 719 718 724 3 719 724 725 3 720 719 725 3 720 725 726 3 721 610 612 3 721 612 727 3 722 721 727 3 722 727 728 3 723 722 728 3 723 728 729 3 724 723 729 3 724 729 730 3 725 724 730 3 725 730 731 3 726 725 731 3 726 731 732 3 727 612 614 3 727 614 733 3 728 727 733 3 728 733 734 3 729 728 734 3 729 734 735 3 730 729 735 3 730 735 736 3 731 730 736 3 731 736 737 3 732 731 737 3 732 737 738 3 733 614 616 3 733 616 739 3 734 733 739 3 734 739 740 3 735 734 740 3 735 740 741 3 736 735 741 3 736 741 742 3 737 736 742 3 737 742 743 3 738 737 743 3 738 743 744 3 739 616 618 3 739 618 745 3 740 739 745 3 740 745 746 3 741 740 746 3 741 746 747 3 742 741 747 3 742 747 748 3 743 742 748 3 743 748 749 3 744 743 749 3 744 749 750 3 745 618 620 3 745 620 751 3 746 745 751 3 746 751 752 3 747 746 752 3 747 752 753 3 748 747 753 3 748 753 754 3 749 748 754 3 749 754 755 3 750 749 755 3 750 755 756 3 751 620 622 3 751 622 757 3 752 751 757 3 752 757 758 3 753 752 758 3 753 758 759 3 754 753 759 3 754 759 760 3 755 754 760 3 755 760 761 3 756 755 761 3 756 761 762 3 757 622 624 3 757 624 763 3 758 757 763 3 758 763 764 3 759 758 764 3 759 764 765 3 760 759 765 3 760 765 766 3 761 760 766 3 761 766 767 3 762 761 767 3 762 767 768 3 763 624 577 3 763 577 625 3 764 763 625 3 764 625 627 3 765 764 627 3 765 627 629 3 766 765 629 3 766 629 631 3 767 766 631 3 767 631 633 3 768 767 633 3 768 633 635 3 769 770 771 3 769 771 772 3 773 769 772 3 773 772 774 3 775 773 774 3 775 774 776 3 777 775 776 3 777 776 778 3 779 777 778 3 779 778 780 3 781 779 780 3 781 780 782 3 772 771 783 3 772 783 784 3 774 772 784 3 774 784 785 3 776 774 785 3 776 785 786 3 778 776 786 3 778 786 787 3 780 778 787 3 780 787 788 3 782 780 788 3 782 788 789 3 784 783 790 3 784 790 791 3 785 784 791 3 785 791 792 3 786 785 792 3 786 792 793 3 787 786 793 3 787 793 794 3 788 787 794 3 788 794 795 3 789 788 795 3 789 795 796 3 791 790 797 3 791 797 798 3 792 791 798 3 792 798 799 3 793 792 799 3 793 799 800 3 794 793 800 3 794 800 801 3 795 794 801 3 795 801 802 3 796 795 802 3 796 802 803 3 798 797 804 3 798 804 805 3 799 798 805 3 799 805 806 3 800 799 806 3 800 806 807 3 801 800 807 3 801 807 808 3 802 801 808 3 802 808 809 3 803 802 809 3 803 809 810 3 805 804 811 3 805 811 812 3 806 805 812 3 806 812 813 3 807 806 813 3 807 813 814 3 808 807 814 3 808 814 815 3 809 808 815 3 809 815 816 3 810 809 816 3 810 816 817 3 812 811 818 3 812 818 819 3 813 812 819 3 813 819 820 3 814 813 820 3 814 820 821 3 815 814 821 3 815 821 822 3 816 815 822 3 816 822 823 3 817 816 823 3 817 823 824 3 819 818 825 3 819 825 826 3 820 819 826 3 820 826 827 3 821 820 827 3 821 827 828 3 822 821 828 3 822 828 829 3 823 822 829 3 823 829 830 3 824 823 830 3 824 830 831 3 826 825 832 3 826 832 833 3 827 826 833 3 827 833 834 3 828 827 834 3 828 834 835 3 829 828 835 3 829 835 836 3 830 829 836 3 830 836 837 3 831 830 837 3 831 837 838 3 833 832 839 3 833 839 840 3 834 833 840 3 834 840 841 3 835 834 841 3 835 841 842 3 836 835 842 3 836 842 843 3 837 836 843 3 837 843 844 3 838 837 844 3 838 844 845 3 840 839 846 3 840 846 847 3 841 840 847 3 841 847 848 3 842 841 848 3 842 848 849 3 843 842 849 3 843 849 850 3 844 843 850 3 844 850 851 3 845 844 851 3 845 851 852 3 847 846 770 3 847 770 769 3 848 847 769 3 848 769 773 3 849 848 773 3 849 773 775 3 850 849 775 3 850 775 777 3 851 850 777 3 851 777 779 3 852 851 779 3 852 779 781 3 853 781 782 3 853 782 854 3 855 853 854 3 855 854 856 3 857 855 856 3 857 856 858 3 859 857 858 3 859 858 860 3 861 859 860 3 861 860 862 3 245 861 862 3 245 862 863 3 854 782 789 3 854 789 864 3 856 854 864 3 856 864 865 3 858 856 865 3 858 865 866 3 860 858 866 3 860 866 867 3 862 860 867 3 862 867 868 3 863 862 868 3 863 868 869 3 864 789 796 3 864 796 870 3 865 864 870 3 865 870 871 3 866 865 871 3 866 871 872 3 867 866 872 3 867 872 873 3 868 867 873 3 868 873 874 3 869 868 874 3 869 874 875 3 870 796 803 3 870 803 876 3 871 870 876 3 871 876 877 3 872 871 877 3 872 877 878 3 873 872 878 3 873 878 879 3 874 873 879 3 874 879 880 3 875 874 880 3 875 880 881 3 876 803 810 3 876 810 882 3 877 876 882 3 877 882 883 3 878 877 883 3 878 883 884 3 879 878 884 3 879 884 885 3 880 879 885 3 880 885 886 3 881 880 886 3 881 886 887 3 882 810 817 3 882 817 888 3 883 882 888 3 883 888 889 3 884 883 889 3 884 889 890 3 885 884 890 3 885 890 891 3 886 885 891 3 886 891 892 3 887 886 892 3 887 892 893 3 888 817 824 3 888 824 894 3 889 888 894 3 889 894 895 3 890 889 895 3 890 895 896 3 891 890 896 3 891 896 897 3 892 891 897 3 892 897 898 3 893 892 898 3 893 898 899 3 894 824 831 3 894 831 900 3 895 894 900 3 895 900 901 3 896 895 901 3 896 901 902 3 897 896 902 3 897 902 903 3 898 897 903 3 898 903 904 3 899 898 904 3 899 904 905 3 900 831 838 3 900 838 906 3 901 900 906 3 901 906 907 3 902 901 907 3 902 907 908 3 903 902 908 3 903 908 909 3 904 903 909 3 904 909 910 3 905 904 910 3 905 910 911 3 906 838 845 3 906 845 912 3 907 906 912 3 907 912 913 3 908 907 913 3 908 913 914 3 909 908 914 3 909 914 915 3 910 909 915 3 910 915 916 3 911 910 916 3 911 916 917 3 912 845 852 3 912 852 918 3 913 912 918 3 913 918 919 3 914 913 919 3 914 919 920 3 915 914 920 3 915 920 921 3 916 915 921 3 916 921 922 3 917 916 922 3 917 922 923 3 918 852 781 3 918 781 853 3 919 918 853 3 919 853 855 3 920 919 855 3 920 855 857 3 921 920 857 3 921 857 859 3 922 921 859 3 922 859 861 3 923 922 861 3 923 861 245 3 924 925 926 3 924 926 927 3 928 924 927 3 928 927 929 3 930 928 929 3 930 929 931 3 932 930 931 3 932 931 933 3 934 932 933 3 934 933 935 3 936 934 935 3 936 935 937 3 927 926 938 3 927 938 939 3 929 927 939 3 929 939 940 3 931 929 940 3 931 940 941 3 933 931 941 3 933 941 942 3 935 933 942 3 935 942 943 3 937 935 943 3 937 943 944 3 939 938 945 3 939 945 946 3 940 939 946 3 940 946 947 3 941 940 947 3 941 947 948 3 942 941 948 3 942 948 949 3 943 942 949 3 943 949 950 3 944 943 950 3 944 950 951 3 946 945 952 3 946 952 953 3 947 946 953 3 947 953 954 3 948 947 954 3 948 954 955 3 949 948 955 3 949 955 956 3 950 949 956 3 950 956 957 3 951 950 957 3 951 957 958 3 953 952 959 3 953 959 960 3 954 953 960 3 954 960 961 3 955 954 961 3 955 961 962 3 956 955 962 3 956 962 963 3 957 956 963 3 957 963 964 3 958 957 964 3 958 964 965 3 960 959 966 3 960 966 967 3 961 960 967 3 961 967 968 3 962 961 968 3 962 968 969 3 963 962 969 3 963 969 970 3 964 963 970 3 964 970 971 3 965 964 971 3 965 971 972 3 967 966 973 3 967 973 974 3 968 967 974 3 968 974 975 3 969 968 975 3 969 975 976 3 970 969 976 3 970 976 977 3 971 970 977 3 971 977 978 3 972 971 978 3 972 978 979 3 974 973 980 3 974 980 981 3 975 974 981 3 975 981 982 3 976 975 982 3 976 982 983 3 977 976 983 3 977 983 984 3 978 977 984 3 978 984 985 3 979 978 985 3 979 985 986 3 981 980 987 3 981 987 988 3 982 981 988 3 982 988 989 3 983 982 989 3 983 989 990 3 984 983 990 3 984 990 991 3 985 984 991 3 985 991 992 3 986 985 992 3 986 992 993 3 988 987 994 3 988 994 995 3 989 988 995 3 989 995 996 3 990 989 996 3 990 996 997 3 991 990 997 3 991 997 998 3 992 991 998 3 992 998 999 3 993 992 999 3 993 999 1000 3 995 994 1001 3 995 1001 1002 3 996 995 1002 3 996 1002 1003 3 997 996 1003 3 997 1003 1004 3 998 997 1004 3 998 1004 1005 3 999 998 1005 3 999 1005 1006 3 1000 999 1006 3 1000 1006 1007 3 1002 1001 925 3 1002 925 924 3 1003 1002 924 3 1003 924 928 3 1004 1003 928 3 1004 928 930 3 1005 1004 930 3 1005 930 932 3 1006 1005 932 3 1006 932 934 3 1007 1006 934 3 1007 934 936 3 1008 936 937 3 1008 937 1009 3 1010 1008 1009 3 1010 1009 1011 3 1012 1010 1011 3 1012 1011 1013 3 1014 1012 1013 3 1014 1013 1015 3 1016 1014 1015 3 1016 1015 1017 3 1018 1016 1017 3 1018 1017 1019 3 1009 937 944 3 1009 944 1020 3 1011 1009 1020 3 1011 1020 1021 3 1013 1011 1021 3 1013 1021 1022 3 1015 1013 1022 3 1015 1022 1023 3 1017 1015 1023 3 1017 1023 1024 3 1019 1017 1024 3 1019 1024 1025 3 1020 944 951 3 1020 951 1026 3 1021 1020 1026 3 1021 1026 1027 3 1022 1021 1027 3 1022 1027 1028 3 1023 1022 1028 3 1023 1028 1029 3 1024 1023 1029 3 1024 1029 1030 3 1025 1024 1030 3 1025 1030 1031 3 1026 951 958 3 1026 958 1032 3 1027 1026 1032 3 1027 1032 1033 3 1028 1027 1033 3 1028 1033 1034 3 1029 1028 1034 3 1029 1034 1035 3 1030 1029 1035 3 1030 1035 1036 3 1031 1030 1036 3 1031 1036 1037 3 1032 958 965 3 1032 965 1038 3 1033 1032 1038 3 1033 1038 1039 3 1034 1033 1039 3 1034 1039 1040 3 1035 1034 1040 3 1035 1040 1041 3 1036 1035 1041 3 1036 1041 1042 3 1037 1036 1042 3 1037 1042 1043 3 1038 965 972 3 1038 972 1044 3 1039 1038 1044 3 1039 1044 1045 3 1040 1039 1045 3 1040 1045 1046 3 1041 1040 1046 3 1041 1046 1047 3 1042 1041 1047 3 1042 1047 1048 3 1043 1042 1048 3 1043 1048 1049 3 1044 972 979 3 1044 979 1050 3 1045 1044 1050 3 1045 1050 1051 3 1046 1045 1051 3 1046 1051 1052 3 1047 1046 1052 3 1047 1052 1053 3 1048 1047 1053 3 1048 1053 1054 3 1049 1048 1054 3 1049 1054 1055 3 1050 979 986 3 1050 986 1056 3 1051 1050 1056 3 1051 1056 1057 3 1052 1051 1057 3 1052 1057 1058 3 1053 1052 1058 3 1053 1058 1059 3 1054 1053 1059 3 1054 1059 1060 3 1055 1054 1060 3 1055 1060 1061 3 1056 986 993 3 1056 993 1062 3 1057 1056 1062 3 1057 1062 1063 3 1058 1057 1063 3 1058 1063 1064 3 1059 1058 1064 3 1059 1064 1065 3 1060 1059 1065 3 1060 1065 1066 3 1061 1060 1066 3 1061 1066 1067 3 1062 993 1000 3 1062 1000 1068 3 1063 1062 1068 3 1063 1068 1069 3 1064 1063 1069 3 1064 1069 1070 3 1065 1064 1070 3 1065 1070 1071 3 1066 1065 1071 3 1066 1071 1072 3 1067 1066 1072 3 1067 1072 1073 3 1068 1000 1007 3 1068 1007 1074 3 1069 1068 1074 3 1069 1074 1075 3 1070 1069 1075 3 1070 1075 1076 3 1071 1070 1076 3 1071 1076 1077 3 1072 1071 1077 3 1072 1077 1078 3 1073 1072 1078 3 1073 1078 1079 3 1074 1007 936 3 1074 936 1008 3 1075 1074 1008 3 1075 1008 1010 3 1076 1075 1010 3 1076 1010 1012 3 1077 1076 1012 3 1077 1012 1014 3 1078 1077 1014 3 1078 1014 1016 3 1079 1078 1016 3 1079 1016 1018 3 1080 1081 1082 3 1083 1080 1082 3 1083 1082 1084 3 1085 1083 1084 3 1085 1084 1086 3 1087 1085 1086 3 1087 1086 1088 3 578 1087 1088 3 578 1088 579 3 1082 1081 1089 3 1084 1082 1089 3 1084 1089 1090 3 1086 1084 1090 3 1086 1090 1091 3 1088 1086 1091 3 1088 1091 1092 3 579 1088 1092 3 579 1092 581 3 1089 1081 1093 3 1090 1089 1093 3 1090 1093 1094 3 1091 1090 1094 3 1091 1094 1095 3 1092 1091 1095 3 1092 1095 1096 3 581 1092 1096 3 581 1096 583 3 1093 1081 1097 3 1094 1093 1097 3 1094 1097 1098 3 1095 1094 1098 3 1095 1098 1099 3 1096 1095 1099 3 1096 1099 1100 3 583 1096 1100 3 583 1100 585 3 1097 1081 1101 3 1098 1097 1101 3 1098 1101 1102 3 1099 1098 1102 3 1099 1102 1103 3 1100 1099 1103 3 1100 1103 1104 3 585 1100 1104 3 585 1104 587 3 1101 1081 1105 3 1102 1101 1105 3 1102 1105 1106 3 1103 1102 1106 3 1103 1106 1107 3 1104 1103 1107 3 1104 1107 1108 3 587 1104 1108 3 587 1108 589 3 1105 1081 1109 3 1106 1105 1109 3 1106 1109 1110 3 1107 1106 1110 3 1107 1110 1111 3 1108 1107 1111 3 1108 1111 1112 3 589 1108 1112 3 589 1112 591 3 1109 1081 1113 3 1110 1109 1113 3 1110 1113 1114 3 1111 1110 1114 3 1111 1114 1115 3 1112 1111 1115 3 1112 1115 1116 3 591 1112 1116 3 591 1116 593 3 1113 1081 1117 3 1114 1113 1117 3 1114 1117 1118 3 1115 1114 1118 3 1115 1118 1119 3 1116 1115 1119 3 1116 1119 1120 3 593 1116 1120 3 593 1120 595 3 1117 1081 1121 3 1118 1117 1121 3 1118 1121 1122 3 1119 1118 1122 3 1119 1122 1123 3 1120 1119 1123 3 1120 1123 1124 3 595 1120 1124 3 595 1124 597 3 1121 1081 1125 3 1122 1121 1125 3 1122 1125 1126 3 1123 1122 1126 3 1123 1126 1127 3 1124 1123 1127 3 1124 1127 1128 3 597 1124 1128 3 597 1128 599 3 1125 1081 1129 3 1126 1125 1129 3 1126 1129 1130 3 1127 1126 1130 3 1127 1130 1131 3 1128 1127 1131 3 1128 1131 1132 3 599 1128 1132 3 599 1132 601 3 1129 1081 1133 3 1130 1129 1133 3 1130 1133 1134 3 1131 1130 1134 3 1131 1134 1135 3 1132 1131 1135 3 1132 1135 1136 3 601 1132 1136 3 601 1136 603 3 1133 1081 1137 3 1134 1133 1137 3 1134 1137 1138 3 1135 1134 1138 3 1135 1138 1139 3 1136 1135 1139 3 1136 1139 1140 3 603 1136 1140 3 603 1140 605 3 1137 1081 1141 3 1138 1137 1141 3 1138 1141 1142 3 1139 1138 1142 3 1139 1142 1143 3 1140 1139 1143 3 1140 1143 1144 3 605 1140 1144 3 605 1144 607 3 1141 1081 1145 3 1142 1141 1145 3 1142 1145 1146 3 1143 1142 1146 3 1143 1146 1147 3 1144 1143 1147 3 1144 1147 1148 3 607 1144 1148 3 607 1148 609 3 1145 1081 1149 3 1146 1145 1149 3 1146 1149 1150 3 1147 1146 1150 3 1147 1150 1151 3 1148 1147 1151 3 1148 1151 1152 3 609 1148 1152 3 609 1152 611 3 1149 1081 1153 3 1150 1149 1153 3 1150 1153 1154 3 1151 1150 1154 3 1151 1154 1155 3 1152 1151 1155 3 1152 1155 1156 3 611 1152 1156 3 611 1156 613 3 1153 1081 1157 3 1154 1153 1157 3 1154 1157 1158 3 1155 1154 1158 3 1155 1158 1159 3 1156 1155 1159 3 1156 1159 1160 3 613 1156 1160 3 613 1160 615 3 1157 1081 1161 3 1158 1157 1161 3 1158 1161 1162 3 1159 1158 1162 3 1159 1162 1163 3 1160 1159 1163 3 1160 1163 1164 3 615 1160 1164 3 615 1164 617 3 1161 1081 1165 3 1162 1161 1165 3 1162 1165 1166 3 1163 1162 1166 3 1163 1166 1167 3 1164 1163 1167 3 1164 1167 1168 3 617 1164 1168 3 617 1168 619 3 1165 1081 1169 3 1166 1165 1169 3 1166 1169 1170 3 1167 1166 1170 3 1167 1170 1171 3 1168 1167 1171 3 1168 1171 1172 3 619 1168 1172 3 619 1172 621 3 1169 1081 1173 3 1170 1169 1173 3 1170 1173 1174 3 1171 1170 1174 3 1171 1174 1175 3 1172 1171 1175 3 1172 1175 1176 3 621 1172 1176 3 621 1176 623 3 1173 1081 1080 3 1174 1173 1080 3 1174 1080 1083 3 1175 1174 1083 3 1175 1083 1085 3 1176 1175 1085 3 1176 1085 1087 3 623 1176 1087 3 623 1087 578 ================================================ FILE: doc/katex-header.html ================================================ ================================================ FILE: examples/sphere/Cargo.toml ================================================ [package] name = "example-sphere" version = "0.0.0" edition = "2021" authors = ["Sean Olson "] license = "MIT" publish = false [dependencies] nalgebra = "^0.33.0" theon = "^0.1.0" [dependencies.pictor] path = "../../pictor" [dependencies.plexus] path = "../../plexus" default-features = false features = [ "encoding-ply", "geometry-nalgebra", ] ================================================ FILE: examples/sphere/src/main.rs ================================================ use nalgebra::Point3; use pictor::pipeline::{self, Vertex}; use plexus::prelude::*; use plexus::primitive; use plexus::primitive::generate::{Normal, Position}; use plexus::primitive::sphere::UvSphere; use theon::space::{EuclideanSpace, VectorSpace}; type E3 = Point3; fn main() { let from = Point3::new(0.0, 0.0, 3.0); let to = Point3::origin(); pipeline::render_mesh_buffer_with(from, to, move || { let sphere = UvSphere::new(32, 16); primitive::zip_vertices(( sphere.polygons::>().triangulate(), sphere.polygons::>().triangulate(), )) .map_vertices(|(position, normal)| Vertex { position: position.into_homogeneous().into(), normal: normal.into_inner().into_homogeneous().into(), color: [1.0, 0.6, 0.2, 1.0], }) .collect() }); } ================================================ FILE: examples/subdivide/Cargo.toml ================================================ [package] name = "example-subdivide" version = "0.0.0" edition = "2021" authors = ["Sean Olson "] license = "MIT" publish = false [dependencies] nalgebra = "^0.33.0" smallvec = "^1.0.0" theon = "^0.1.0" [dependencies.pictor] path = "../../pictor" [dependencies.plexus] path = "../../plexus" default-features = false features = ["geometry-nalgebra"] ================================================ FILE: examples/subdivide/src/main.rs ================================================ use nalgebra::Point3; use pictor::pipeline::{self, Vertex}; use plexus::geometry::AsPositionMut; use plexus::graph::{ClosedView, EdgeMidpoint, FaceView, GraphData, MeshGraph}; use plexus::prelude::*; use plexus::primitive::Tetragon; use smallvec::SmallVec; use theon::space::{EuclideanSpace, VectorSpace}; type E3 = Point3; pub trait Ambo: ClosedView { #[must_use] fn ambo(self) -> Self; } impl Ambo for FaceView<&'_ mut MeshGraph> where G: EdgeMidpoint + GraphData, G::Vertex: AsPositionMut, { // Subdivide the face with a polygon formed from vertices at the midpoints // of the edges of the face. fn ambo(self) -> Self { // Split each edge, stashing the vertex key and moving to the next arc. let arity = self.arity(); let mut arc = self.into_arc(); let mut splits = SmallVec::<[_; 4]>::with_capacity(arity); for _ in 0..arity { let vertex = arc.split_at_midpoint(); splits.push(vertex.key()); arc = vertex.into_outgoing_arc().into_next_arc(); } // Split faces along the vertices from each arc split. let mut face = arc.into_face().unwrap(); for (a, b) in splits.into_iter().perimeter() { face = face.split(a, b).unwrap().into_face().unwrap(); } // Return the face forming the similar polygon. face } } fn main() { let from = Point3::new(-0.9, 3.1, 2.4); let to = Point3::new(0.0, 1.0, 0.0); pipeline::render_mesh_buffer_with(from, to, || { // Create a graph from a tetragon. let mut graph = MeshGraph::::from(Tetragon::from([ (1.0, 0.0, -1.0), (-1.0, 0.0, -1.0), (-1.0, 0.0, 1.0), (1.0, 0.0, 1.0), ])); // Get the face of the tetragon. let key = graph.faces().next().unwrap().key(); let mut face = graph.face_mut(key).unwrap(); // Subdivide and extrude the face repeatedly. for _ in 0..5 { face = face.ambo().extrude_with_offset(0.5).unwrap(); } // Convert the graph into a buffer. graph.triangulate(); graph .to_mesh_by_face_with(|face, vertex| Vertex { position: vertex.position().into_homogeneous().into(), normal: face.normal().unwrap().into_homogeneous().into(), color: [1.0, 0.6, 0.2, 1.0], }) .unwrap() }); } ================================================ FILE: examples/teapot/Cargo.toml ================================================ [package] name = "example-teapot" version = "0.0.0" edition = "2021" authors = ["Sean Olson "] license = "MIT" publish = false [dependencies] nalgebra = "^0.33.0" theon = "^0.1.0" [dependencies.pictor] path = "../../pictor" [dependencies.plexus] path = "../../plexus" default-features = false features = [ "encoding-ply", "geometry-nalgebra", ] ================================================ FILE: examples/teapot/src/main.rs ================================================ use nalgebra::Point3; use pictor::pipeline::{self, Vertex}; use plexus::encoding::ply::{FromPly, PositionEncoding}; use plexus::graph::MeshGraph; use theon::space::{EuclideanSpace, VectorSpace}; type E3 = Point3; fn main() { let from = Point3::new(0.0, -6.0, 4.0); let to = Point3::new(0.0, 0.0, 1.0); pipeline::render_mesh_buffer_with(from, to, move || { // Read PLY data into a graph. let ply: &[u8] = include_bytes!("../../../data/teapot.ply"); let encoding = PositionEncoding::::default(); let (graph, _) = MeshGraph::::from_ply(encoding, ply).expect("teapot"); // Convert the graph into a buffer. graph .to_mesh_by_vertex_with(|vertex| Vertex { position: vertex.position().into_homogeneous().into(), normal: vertex.normal().unwrap().into_homogeneous().into(), color: [1.0, 0.6, 0.2, 1.0], }) .expect("buffer") }); } ================================================ FILE: pictor/Cargo.toml ================================================ [package] name = "pictor" version = "0.0.0" edition = "2021" rust-version = "1.81.0" authors = ["Sean Olson "] license = "MIT" description = "Renderer and support library for Plexus examples." publish = false [dependencies] bytemuck = "^1.13.0" decorum = "^0.4.0" naga = "^23.0.0" nalgebra = "^0.33.0" num = "^0.4.3" rand = "^0.8.5" theon = "^0.1.0" winit = "^0.30.8" [dependencies.futures] version = "^0.3.31" default-features = false features = [ "std", "executor", ] [dependencies.plexus] path = "../plexus" default-features = false features = ["geometry-nalgebra"] [dependencies.wgpu] version = "^23.0.1" features = ["spirv"] ================================================ FILE: pictor/src/camera.rs ================================================ use nalgebra::{Isometry3, Matrix4, Orthographic3, Perspective3, Point3, Vector3}; use std::sync::LazyLock; use wgpu::SurfaceConfiguration; #[rustfmt::skip] static OPENGL_TO_WGPU_TRANSFORM: LazyLock> = LazyLock::new(|| { Matrix4::new( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 1.0, ) }); #[derive(Clone, Debug)] pub enum Projection { Perspective(Perspective3), Orthographic(Orthographic3), } impl Projection { pub fn perspective(aspect: f32, fov: f32, near: f32, far: f32) -> Self { Projection::Perspective(Perspective3::new(aspect, fov, near, far)) } pub fn orthographic(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Self { Projection::Orthographic(Orthographic3::new(left, right, bottom, top, near, far)) } } impl AsRef> for Projection { fn as_ref(&self) -> &Matrix4 { match *self { Projection::Perspective(ref perspective) => perspective.as_matrix(), Projection::Orthographic(ref orthographic) => orthographic.as_matrix(), } } } #[derive(Clone, Debug)] pub struct Camera { pub projection: Projection, view: Isometry3, } impl Camera { pub fn look_at(&mut self, from: &Point3, to: &Point3) { self.view = Isometry3::look_at_rh(from, to, &Vector3::y()); } pub fn reproject(&mut self, surface: &SurfaceConfiguration) { match self.projection { Projection::Perspective(ref mut perspective) => { perspective.set_aspect(surface.width as f32 / surface.height as f32); } Projection::Orthographic(ref mut orthographic) => { let inverse = surface.height as f32 / surface.width as f32; let radius = (orthographic.right() - orthographic.left()) * inverse * 0.5; orthographic.set_bottom_and_top(-radius, radius); } } } pub fn transform(&self) -> Matrix4 { *OPENGL_TO_WGPU_TRANSFORM * self.projection.as_ref() * self.view.to_homogeneous() } } impl From for Camera { fn from(projection: Projection) -> Self { Camera { projection, view: Isometry3::look_at_rh( &Point3::new(0.0, 0.0, 1.0), &Point3::origin(), &Vector3::y(), ), } } } ================================================ FILE: pictor/src/harness.rs ================================================ use futures::executor::{self, LocalPool, LocalSpawner}; use futures::task::LocalSpawn; use std::cmp; use std::fmt::Debug; use std::sync::Arc; use winit::application::ApplicationHandler; use winit::dpi::PhysicalSize; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::window::{Window, WindowAttributes, WindowId}; use crate::renderer::Renderer; #[derive(Debug)] struct Executor { pool: LocalPool, spawner: LocalSpawner, } impl Executor { pub fn new() -> Self { let pool = LocalPool::new(); let spawner = pool.spawner(); Executor { pool, spawner } } pub fn flush(&mut self) { self.pool.run_until_stalled() } pub fn spawner(&self) -> &impl LocalSpawn { &self.spawner } } #[derive(Debug)] struct Activity { window: Arc, renderer: Renderer<'static>, application: T, } impl Activity where T: Application, { pub fn bind_and_configure(window: Window, configuration: T::Configuration) -> Self { let window = Arc::new(window); let renderer = executor::block_on(Renderer::try_from_window(window.clone())).unwrap(); let application = T::configure(configuration, &renderer).unwrap(); Activity { window, renderer, application, } } } #[derive(Debug)] struct Harness { executor: Executor, f: F, activity: Option>, } impl Harness where T: Application, { fn redraw(&mut self) { if let Some(activity) = self.activity.as_mut() { let frame = activity.renderer.surface.get_current_texture().unwrap(); let view = frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); activity .application .render(&activity.renderer, &view, self.executor.spawner()); frame.present(); } } fn resize(&mut self, dimensions: PhysicalSize) { if let Some(activity) = self.activity.as_mut() { activity.renderer.surface_configuration.width = cmp::max(1, dimensions.width); activity.renderer.surface_configuration.height = cmp::max(1, dimensions.height); activity.application.resize(&activity.renderer); activity.renderer.surface.configure( &activity.renderer.device, &activity.renderer.surface_configuration, ); activity.window.request_redraw(); } } } impl ApplicationHandler<()> for Harness where T: Application, F: FnMut() -> (WindowAttributes, T::Configuration), { fn resumed(&mut self, reactor: &ActiveEventLoop) { let (window, configuration) = (self.f)(); self.activity.replace(Activity::bind_and_configure( reactor.create_window(window).unwrap(), configuration, )); } fn suspended(&mut self, _reactor: &ActiveEventLoop) { self.activity.take(); } fn window_event(&mut self, reactor: &ActiveEventLoop, _: WindowId, event: WindowEvent) { match event { WindowEvent::RedrawRequested => { self.redraw(); } WindowEvent::CloseRequested => { reactor.exit(); } WindowEvent::Resized(dimensions) => { self.resize(dimensions); } _ => { if let Some(activity) = self.activity.as_mut() { if let Reaction::Abort = activity.application.react(event) { reactor.exit(); } } } } self.executor.flush(); } } #[derive(Clone, Copy, Debug, Default)] pub enum Reaction { #[default] Continue, Abort, } pub trait ConfigureStage { fn device(&self) -> &wgpu::Device; fn queue(&self) -> &wgpu::Queue; fn surface_configuration(&self) -> &wgpu::SurfaceConfiguration; } pub trait RenderStage { fn device(&self) -> &wgpu::Device; fn queue(&self) -> &wgpu::Queue; } pub trait Application: 'static + Sized { type Configuration: Sized; type Error: Debug; fn configure( configuration: Self::Configuration, stage: &impl ConfigureStage, ) -> Result; fn react(&mut self, event: WindowEvent) -> Reaction { let _ = event; Reaction::Continue } fn resize(&mut self, stage: &impl ConfigureStage); fn render( &mut self, stage: &impl RenderStage, view: &wgpu::TextureView, spawn: &impl LocalSpawn, ); } pub fn run(f: F) where T: Application, F: FnMut() -> (WindowAttributes, T::Configuration), { let executor = Executor::new(); let reactor = EventLoop::new().unwrap(); reactor.set_control_flow(ControlFlow::Poll); reactor .run_app(&mut Harness:: { executor, f, activity: None, }) .unwrap(); } ================================================ FILE: pictor/src/lib.rs ================================================ mod camera; mod harness; pub mod pipeline; mod renderer; pub use crate::camera::*; pub use crate::harness::*; // TODO: Compile shaders from source code rather than statically loading binary SpirV. ================================================ FILE: pictor/src/pipeline.rs ================================================ use bytemuck::{self, Pod, Zeroable}; use decorum::cmp::CanonicalEq; use decorum::hash::CanonicalHash; use futures::task::LocalSpawn; use nalgebra::{Point3, Scalar, Vector3, Vector4}; use num::{self, One}; use plexus::buffer::MeshBuffer; use plexus::geometry::UnitGeometry; use plexus::index::Flat3; use rand::distributions::{Distribution, Standard}; use rand::{self, Rng}; use std::f32::consts::FRAC_PI_4; use std::hash::{Hash, Hasher}; use std::mem; use theon::adjunct::Extend; use wgpu::include_spirv_raw; use wgpu::util::DeviceExt as _; use winit::event::{ElementState, KeyEvent, WindowEvent}; use winit::keyboard::{Key, NamedKey}; use winit::window::Window; use crate::camera::{Camera, Projection}; use crate::harness::{self, Application, ConfigureStage, Reaction, RenderStage}; use Reaction::Abort; use Reaction::Continue; #[derive(Clone, Copy, Debug)] pub struct Color4(pub Vector4) where T: Scalar; impl Color4 where T: Scalar, { pub fn white() -> Self where T: One, { Color4(Vector4::repeat(One::one())) } pub fn random() -> Self where T: One, Standard: Distribution, Vector3: Extend, Item = T>, { let mut rng = rand::thread_rng(); Color4(Vector3::from_fn(|_, _| rng.gen()).extend(One::one())) } } impl AsRef> for Color4 where T: Scalar, { fn as_ref(&self) -> &Vector4 { &self.0 } } impl Default for Color4 where T: One + Scalar, { fn default() -> Self { Color4::white() } } impl From> for Color4 where T: Scalar, { fn from(vector: Vector4) -> Self { Color4(vector) } } impl From> for Vector4 where T: Scalar, { fn from(color: Color4) -> Self { color.0 } } impl UnitGeometry for Color4 where T: One + Scalar {} #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct Vertex { pub position: [f32; 4], pub normal: [f32; 4], pub color: [f32; 4], } impl Eq for Vertex {} impl Hash for Vertex { fn hash(&self, state: &mut H) where H: Hasher, { self.position.hash_canonical(state); self.normal.hash_canonical(state); self.color.hash_canonical(state); } } impl PartialEq for Vertex { fn eq(&self, other: &Self) -> bool { self.position.eq_canonical(&other.position) && self.normal.eq_canonical(&other.normal) && self.color.eq_canonical(&other.color) } } // SAFETY: This type is inhabited, is `repr(C)`, has no padding, has no illegal bit patterns, // contains no pointers, and has only fields that are also `Pod`. unsafe impl Pod for Vertex {} // SAFETY: This type has a zeroed inhabitant. unsafe impl Zeroable for Vertex {} #[derive(Debug)] struct RenderConfiguration { camera: Camera, from: Point3, buffer: MeshBuffer, Vertex>, } #[derive(Debug)] struct RenderApplication { camera: Camera, _viewpoint: wgpu::Buffer, transform: wgpu::Buffer, vertices: wgpu::Buffer, indices: wgpu::Buffer, depth: wgpu::TextureView, n: u32, bind_group: wgpu::BindGroup, pipeline: wgpu::RenderPipeline, } impl RenderApplication { fn configure_depth_buffer(stage: &impl ConfigureStage) -> wgpu::TextureView { stage .device() .create_texture(&wgpu::TextureDescriptor { label: None, view_formats: stage.surface_configuration().view_formats.as_slice(), size: wgpu::Extent3d { width: stage.surface_configuration().width, height: stage.surface_configuration().height, depth_or_array_layers: 1, }, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Depth32Float, usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::RENDER_ATTACHMENT, }) .create_view(&Default::default()) } } impl Application for RenderApplication { type Configuration = RenderConfiguration; type Error = (); fn configure( configuration: Self::Configuration, stage: &impl ConfigureStage, ) -> Result { let RenderConfiguration { mut camera, from, buffer, } = configuration; camera.reproject(stage.surface_configuration()); let vertices = stage .device() .create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(buffer.as_vertex_slice()), usage: wgpu::BufferUsages::VERTEX, }); let indices = stage .device() .create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(buffer.as_index_slice()), usage: wgpu::BufferUsages::INDEX, }); let transform = stage .device() .create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(camera.transform().as_slice()), usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, }); let viewpoint = stage .device() .create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(from.coords.as_slice()), usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, }); let depth = Self::configure_depth_buffer(stage); let bind_group_layout = stage .device() .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, entries: &[ wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStages::VERTEX, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: wgpu::BufferSize::new(64), }, count: None, }, wgpu::BindGroupLayoutEntry { binding: 1, visibility: wgpu::ShaderStages::VERTEX, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: wgpu::BufferSize::new(12), }, count: None, }, ], }); let bind_group = stage .device() .create_bind_group(&wgpu::BindGroupDescriptor { label: None, layout: &bind_group_layout, entries: &[ wgpu::BindGroupEntry { binding: 0, resource: transform.as_entire_binding(), }, wgpu::BindGroupEntry { binding: 1, resource: viewpoint.as_entire_binding(), }, ], }); let pipeline = stage .device() .create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, layout: Some(&stage.device().create_pipeline_layout( &wgpu::PipelineLayoutDescriptor { label: None, bind_group_layouts: &[&bind_group_layout], push_constant_ranges: &[], }, )), vertex: wgpu::VertexState { module: unsafe { &stage .device() .create_shader_module_spirv(&include_spirv_raw!("shader.spv.vert")) }, entry_point: Some("main"), compilation_options: Default::default(), buffers: &[wgpu::VertexBufferLayout { array_stride: mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, // LINT: The constants in these trivial expressions illustrate the sizing // and layout of the data more clearly than the evaluation alone. attributes: &[ #[expect(clippy::erasing_op)] wgpu::VertexAttribute { format: wgpu::VertexFormat::Float32x4, offset: 0 * 4 * 4, shader_location: 0, }, #[expect(clippy::identity_op)] wgpu::VertexAttribute { format: wgpu::VertexFormat::Float32x4, offset: 1 * 4 * 4, shader_location: 1, }, wgpu::VertexAttribute { format: wgpu::VertexFormat::Float32x4, offset: 2 * 4 * 4, shader_location: 2, }, ], }], }, fragment: Some(wgpu::FragmentState { module: unsafe { &stage .device() .create_shader_module_spirv(&include_spirv_raw!("shader.spv.frag")) }, entry_point: Some("main"), compilation_options: Default::default(), targets: &[Some(wgpu::ColorTargetState { format: stage.surface_configuration().format, blend: Some(wgpu::BlendState { color: wgpu::BlendComponent::REPLACE, alpha: wgpu::BlendComponent::REPLACE, }), write_mask: wgpu::ColorWrites::ALL, })], }), depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth32Float, depth_write_enabled: true, depth_compare: wgpu::CompareFunction::Less, stencil: Default::default(), bias: Default::default(), }), primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, front_face: wgpu::FrontFace::Ccw, cull_mode: Some(wgpu::Face::Back), ..Default::default() }, multisample: Default::default(), multiview: None, cache: None, }); Ok(RenderApplication { camera, _viewpoint: viewpoint, transform, vertices, indices, depth, n: buffer.as_index_slice().len() as u32, bind_group, pipeline, }) } fn react(&mut self, event: WindowEvent) -> Reaction { match event { WindowEvent::KeyboardInput { event: KeyEvent { logical_key: Key::Named(NamedKey::Escape), state: ElementState::Pressed, .. }, .. } => Abort, _ => Continue, } } fn resize(&mut self, stage: &impl ConfigureStage) { self.camera.reproject(stage.surface_configuration()); stage.queue().write_buffer( &self.transform, 0, bytemuck::cast_slice(self.camera.transform().as_slice()), ); self.depth = Self::configure_depth_buffer(stage); } fn render(&mut self, stage: &impl RenderStage, view: &wgpu::TextureView, _: &impl LocalSpawn) { let mut encoder = stage .device() .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[Some(wgpu::RenderPassColorAttachment { view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0, }), store: wgpu::StoreOp::Store, }, })], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { view: &self.depth, depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(1.0), store: wgpu::StoreOp::Store, }), stencil_ops: None, }), ..Default::default() }); pass.set_pipeline(&self.pipeline); pass.set_bind_group(0, &self.bind_group, &[]); pass.set_index_buffer(self.indices.slice(..), wgpu::IndexFormat::Uint32); pass.set_vertex_buffer(0, self.vertices.slice(..)); pass.draw_indexed(0..self.n, 0, 0..1); drop(pass); // Release `encoder`. stage.queue().submit(Some(encoder.finish())); } } pub fn render_mesh_buffer_with(from: Point3, to: Point3, mut f: F) where F: FnMut() -> MeshBuffer, Vertex>, { let camera = { let mut camera = Camera::from(Projection::perspective(1.0, FRAC_PI_4, 0.1, 8.0)); //let mut camera = Camera::from(Projection::orthographic(-4.0, 4.0, -4.0, 4.0, -8.0, 8.0)); camera.look_at(&from, &to); camera }; harness::run::(move || { ( Window::default_attributes().with_title("Plexus"), RenderConfiguration { camera: camera.clone(), from, buffer: f(), }, ) }) } ================================================ FILE: pictor/src/renderer.rs ================================================ use std::borrow::Borrow; use winit::window::Window; use crate::harness::{ConfigureStage, RenderStage}; #[derive(Debug)] pub struct Renderer<'window> { _instance: wgpu::Instance, _adapter: wgpu::Adapter, pub surface: wgpu::Surface<'window>, pub surface_configuration: wgpu::SurfaceConfiguration, pub device: wgpu::Device, queue: wgpu::Queue, } impl<'window> Renderer<'window> { pub async fn try_from_window(window: T) -> Result where T: Borrow + Into>, { let dimensions = window.borrow().inner_size(); let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default()); let surface = instance.create_surface(window).unwrap(); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { compatible_surface: Some(&surface), ..Default::default() }) .await .ok_or(())?; let (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { required_features: wgpu::Features::SPIRV_SHADER_PASSTHROUGH, required_limits: wgpu::Limits::downlevel_webgl2_defaults() .using_resolution(adapter.limits()), memory_hints: wgpu::MemoryHints::MemoryUsage, ..Default::default() }, None, ) .await .map_err(|_| ())?; let surface_configuration = surface .get_default_config(&adapter, dimensions.width, dimensions.height) .unwrap(); Ok(Renderer { _instance: instance, _adapter: adapter, surface, surface_configuration, device, queue, }) } } impl ConfigureStage for Renderer<'_> { fn device(&self) -> &wgpu::Device { &self.device } fn queue(&self) -> &wgpu::Queue { &self.queue } fn surface_configuration(&self) -> &wgpu::SurfaceConfiguration { &self.surface_configuration } } impl RenderStage for Renderer<'_> { fn device(&self) -> &wgpu::Device { &self.device } fn queue(&self) -> &wgpu::Queue { &self.queue } } ================================================ FILE: pictor/src/shader.glsl.frag ================================================ #version 450 layout(location = 0) in vec4 v_color; layout(location = 0) out vec4 f_target0; void main() { f_target0 = v_color; } ================================================ FILE: pictor/src/shader.glsl.vert ================================================ #version 450 layout(location = 0) in vec4 a_position; layout(location = 1) in vec4 a_normal; layout(location = 2) in vec4 a_color; layout(set = 0, binding = 0) uniform transform { mat4 u_transform; }; layout(set = 0, binding = 1) uniform viewpoint { vec3 u_viewpoint; }; layout(location = 0) out vec4 v_color; void main() { v_color = a_color * vec4(vec3(max(0.0, dot(vec3(a_normal), normalize(u_viewpoint - vec3(a_position))))), 1.0); gl_Position = u_transform * a_position; } ================================================ FILE: plexus/Cargo.toml ================================================ [package] name = "plexus" version = "0.0.11" edition = "2021" rust-version = "1.81.0" authors = ["Sean Olson "] license = "MIT" readme = "../README.md" homepage = "https://plexus.rs" repository = "https://github.com/olson-sean-k/plexus" description = "Polygonal mesh processing." keywords = [ "polygon", "mesh", "topology", "geometry", "half-edge", ] categories = [ "algorithms", "data-structures", "graphics", "rendering::data-formats", ] [package.metadata.docs.rs] # Enable all features for API documentation. all-features = true # Enable KaTeX support by injecting a header into the documentation. rustdoc-args = [ "--html-in-header", "../doc/katex-header.html", ] [[bench]] name = "subdivide" harness = false path = "../benches/subdivide.rs" [features] default = [] encoding-ply = ["dep:ply-rs"] geometry-cgmath = [ "dep:cgmath", "theon/cgmath", ] geometry-glam = [ "dep:glam", "theon/glam", ] geometry-mint = [ "dep:mint", "theon/mint", ] geometry-nalgebra = [ "dep:nalgebra", "theon/nalgebra", ] geometry-ultraviolet = [ "dep:ultraviolet", "theon/ultraviolet", ] [dependencies] approx = "^0.5.0" ahash = "^0.8.11" arrayvec = "^0.7.6" derivative = "^2.1.1" itertools = "^0.14.0" num = "^0.4.3" smallvec = "^1.0.0" thiserror = "^2.0.10" typenum = "^1.17.0" [dependencies.cgmath] version = "^0.18.0" optional = true [dependencies.decorum] version = "^0.4.0" default-features = false features = [ "approx", "serde", "std", ] [dependencies.glam] version = "^0.29.0" optional = true [dependencies.mint] version = "^0.5.0" optional = true [dependencies.nalgebra] version = "^0.33.0" optional = true [dependencies.ply-rs] version = "^0.1.2" optional = true [dependencies.theon] version = "^0.1.0" default-features = false features = ["lapack"] [dependencies.ultraviolet] version = "^0.9.0" optional = true [build-dependencies] rustversion = "^1.0.3" [dev-dependencies] criterion = "^0.5.1" # For brevity and simplicity, tests and API documentation use a direct # dependency on `nalgebra`. This approach requires that the version # specification is compatible with `theon`. nalgebra = "^0.33.0" [dev-dependencies.theon] version = "^0.1.0" default-features = false features = [ "lapack", "nalgebra", ] ================================================ FILE: plexus/src/buffer/builder.rs ================================================ use num::{Integer, NumCast, Unsigned}; use std::hash::Hash; use typenum::NonZero; use crate::buffer::{BufferError, MeshBuffer}; use crate::builder::{FacetBuilder, MeshBuilder, SurfaceBuilder}; use crate::constant::{Constant, ToType, TypeOf}; use crate::geometry::{FromGeometry, IntoGeometry}; use crate::index::{Flat, Grouping, IndexBuffer}; use crate::primitive::Topological; use crate::transact::{ClosedInput, Transact}; use crate::Arity; // TODO: It should not be possible to manufacture keys without placing // additional constraints on the type bounds of `FacetBuilder` (for // example, `FacetBuilder`). Is it important to check for // out-of-bounds indices in `insert_facet`? pub type VertexKey = ::Group> as IndexBuffer>::Index; pub struct BufferBuilder where R: Grouping, { indices: Vec, vertices: Vec, } impl Default for BufferBuilder where R: Grouping, Vec: IndexBuffer, { fn default() -> Self { BufferBuilder { indices: Default::default(), vertices: Default::default(), } } } impl ClosedInput for BufferBuilder where R: Grouping, Vec: IndexBuffer, { type Input = (); } impl FacetBuilder for BufferBuilder, G> where Constant: ToType, TypeOf: NonZero, K: Copy + Hash + Integer + Unsigned, Vec: IndexBuffer>, { type Facet = (); type Key = (); fn insert_facet(&mut self, keys: T, _: U) -> Result where Self::Facet: FromGeometry, T: AsRef<[K]>, { let keys = keys.as_ref(); if keys.len() == N { self.indices.extend(keys.iter()); Ok(()) } else { // TODO: These numbers do not necessarily represent arity (i.e., the // number of edges of each topological structure). Use a // different error variant to express this. Err(BufferError::ArityConflict { expected: N, actual: keys.len(), }) } } } impl FacetBuilder for BufferBuilder where P: Grouping + Topological, P::Vertex: Copy + Hash + Integer + Unsigned, Vec

: IndexBuffer

, { type Facet = (); type Key = (); fn insert_facet(&mut self, keys: T, _: U) -> Result where Self::Facet: FromGeometry, T: AsRef<[P::Vertex]>, { let arity = keys.as_ref().len(); P::try_from_slice(keys) .ok_or(BufferError::ArityConflict { expected: P::ARITY.into_interval().0, actual: arity, }) .map(|polygon| self.indices.push(polygon)) } } impl MeshBuilder for BufferBuilder where Self: SurfaceBuilder, R: Grouping, VertexKey: Hash, Vec: IndexBuffer, { type Builder = Self; type Vertex = G; type Facet = (); fn surface_with(&mut self, f: F) -> Result where Self::Error: From, F: FnOnce(&mut Self::Builder) -> Result, { f(self).map_err(|error| error.into()) } } impl SurfaceBuilder for BufferBuilder where Self: FacetBuilder, Facet = ()>, Self::Error: From, // TODO: Why is this necessary? R: Grouping, VertexKey: Hash + NumCast, Vec: IndexBuffer, { type Builder = Self; type Key = VertexKey; type Vertex = G; type Facet = (); fn facets_with(&mut self, f: F) -> Result where Self::Error: From, F: FnOnce(&mut Self::Builder) -> Result, { f(self).map_err(|error| error.into()) } fn insert_vertex(&mut self, data: T) -> Result where Self::Vertex: FromGeometry, { let key = as NumCast>::from(self.vertices.len()) .ok_or(BufferError::IndexOverflow)?; self.vertices.push(data.into_geometry()); Ok(key) } } impl Transact<::Input> for BufferBuilder where R: Grouping, Vec: IndexBuffer, { type Commit = MeshBuffer; type Abort = (); type Error = BufferError; fn commit(self) -> Result { let BufferBuilder { indices, vertices } = self; Ok(MeshBuffer::from_raw_buffers_unchecked(indices, vertices)) } fn abort(self) -> Self::Abort {} } ================================================ FILE: plexus/src/buffer/mod.rs ================================================ //! Linear representations of polygonal meshes. //! //! This module provides types and traits that describe polygonal meshes as //! buffers of vertex data and buffers of indices into that vertex data. These //! buffers are called the _vertex buffer_ and _index buffer_, respectively, and //! are typically used for indexed drawing. The [`MeshBuffer`] type unifies //! vertex and index buffers and maintains their consistency. //! //! Note that only _composite_ vertex buffers are supported, in which each //! element of a vertex buffer completely describes all attributes of a vertex. //! Plexus does not support _component_ buffers, in which each attribute of a //! vertex is stored in a dedicated buffer. //! //! Plexus refers to independent vertex and index buffers as _raw buffers_. For //! example, a [`Vec`] of index data is a raw buffer and can be modified without //! regard to consistency with any particular vertex buffer. The //! [`FromRawBuffers`] trait provides a way to construct mesh data structures //! from such raw buffers. //! //! Index buffers may contain either _flat_ or _structured_ data. See the //! [`index`] module for more about these buffers and how they are defined. //! //! # Examples //! //! Generating a flat [`MeshBuffer`] from a [$uv$-sphere][`UvSphere`]: //! //! ```rust //! # extern crate decorum; //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use decorum::R32; //! use nalgebra::Point3; //! use plexus::buffer::MeshBuffer3; //! use plexus::prelude::*; //! use plexus::primitive::generate::Position; //! use plexus::primitive::sphere::UvSphere; //! //! let buffer: MeshBuffer3> = UvSphere::new(16, 16) //! .polygons::>>() //! .triangulate() //! .collect(); //! let indices = buffer.as_index_slice(); //! let positions = buffer.as_vertex_slice(); //! ``` //! //! Converting a [`MeshGraph`] to a structured [`MeshBuffer`]: //! //! ```rust //! # extern crate decorum; //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use decorum::R64; //! use nalgebra::Point3; //! use plexus::buffer::MeshBufferN; //! use plexus::graph::MeshGraph; //! use plexus::prelude::*; //! use plexus::primitive::cube::Cube; //! use plexus::primitive::generate::Position; //! //! type E3 = Point3; //! //! let graph: MeshGraph = Cube::new().polygons::>().collect(); //! let buffer: MeshBufferN = graph.to_mesh_by_vertex().unwrap(); //! ``` //! //! [`Vec`]: std::vec::Vec //! [`FromRawBuffers`]: crate::buffer::FromRawBuffers //! [`MeshBuffer`]: crate::buffer::MeshBuffer //! [`MeshGraph`]: crate::graph::MeshGraph //! [`index`]: crate::index //! [`UvSphere`]: crate::primitive::sphere::UvSphere // `MeshBuffer`s must convert and sum indices into their vertex data. Some of // these conversions may fail, but others are never expected to fail, because of // invariants enforced by `MeshBuffer`. // // Index types require `Unsigned` and `Vec` capacity is limited by word size // (the width of `usize`). An overflow cannot occur in some contexts, because a // consistent `MeshBuffer` cannot index into a `Vec` with an index larger than // its maximum addressable capacity (the maximum value that `usize` can // represent). // TODO: More consistently `expect` or `ok_or` index conversions and sums. mod builder; use itertools::Itertools; use num::{Integer, NumCast, Unsigned}; use std::fmt::Debug; use std::hash::Hash; use std::iter::FromIterator; use std::vec; use theon::adjunct::Map; use thiserror::Error; use typenum::{self, NonZero, Unsigned as _, U3, U4}; use crate::buffer::builder::BufferBuilder; use crate::builder::{Buildable, MeshBuilder}; use crate::constant::{Constant, ToType, TypeOf}; use crate::encoding::{FaceDecoder, FromEncoding, VertexDecoder}; use crate::geometry::{FromGeometry, IntoGeometry}; use crate::index::{ BufferOf, Flat, Flat3, Flat4, FromIndexer, Grouping, HashIndexer, IndexBuffer, IndexOf, IndexVertices, Indexer, Push, }; use crate::primitive::decompose::IntoVertices; use crate::primitive::{ BoundedPolygon, IntoIndexed, IntoPolygons, Polygonal, Tetragon, Topological, Trigon, UnboundedPolygon, }; use crate::{Arity, DynamicArity, MeshArity, Monomorphic, StaticArity, TryFromIterator}; /// Errors concerning raw buffers and [`MeshBuffer`]s. /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer #[derive(Debug, Eq, Error, PartialEq)] pub enum BufferError { /// An index into vertex data is out of bounds. /// /// This error occurs when an index read from an index buffer is out of /// bounds of the vertex buffer it indexes. #[error("index into vertex data out of bounds")] IndexOutOfBounds, /// The computation of an index causes an overflow. /// /// This error occurs if the result of computing an index overflows the type /// used to represent indices. For example, if the elements of an index /// buffer are `u8`s and an operation results in indices greater than /// [`u8::MAX`], then this error will occur. /// /// [`u8::MAX`]: std::u8::MAX #[error("index overflow")] IndexOverflow, #[error("index buffer conflicts with arity")] /// The number of indices in a flat index buffer is incompatible with the /// arity of the buffer. /// /// This error may occur if a flat index buffer contains a number of indices /// that is indivisible by the arity of the topology it represents. For /// example, this may error occur if a triangular index buffer contains a /// number of indices that is not divisible by three. IndexUnaligned, /// The arity of a buffer or other data structure is not compatible with an /// operation. #[error("conflicting arity; expected {expected}, but got {actual}")] ArityConflict { /// The expected arity. expected: usize, /// The incompatible arity that was encountered. actual: usize, }, } /// Triangular [`MeshBuffer`]. /// /// The index buffer for this type contains [`Trigon`]s. For applications where /// a flat index buffer is necessary, consider [`IntoFlatIndex`] or the /// [`Flat3`] meta-grouping. /// /// [`IntoFlatIndex`]: crate::buffer::IntoFlatIndex /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`Flat3`]: crate::index::Flat3 /// [`Trigon`]: crate::primitive::Trigon pub type MeshBuffer3 = MeshBuffer, G>; /// Quadrilateral [`MeshBuffer`]. /// /// The index buffer for this type contains [`Tetragon`]s. For applications /// where a flat index buffer is necessary, consider [`IntoFlatIndex`] or the /// [`Flat4`] meta-grouping. /// /// [`IntoFlatIndex`]: crate::buffer::IntoFlatIndex /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`Flat4`]: crate::index::Flat4 /// [`Tetragon`]: crate::primitive::Tetragon pub type MeshBuffer4 = MeshBuffer, G>; /// [`MeshBuffer`] that supports polygons with arbitrary [arity][`Arity`]. /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`Arity`]: crate::Arity pub type MeshBufferN = MeshBuffer, G>; /// Conversion from raw buffers. pub trait FromRawBuffers: Sized { type Error: Debug; /// Creates a type from raw buffers. /// /// # Errors /// /// Returns an error if the raw buffers are inconsistent or the implementor /// cannot be constructed from the buffers. The latter typically occurs if /// the given topology is unsupported. fn from_raw_buffers(indices: I, vertices: J) -> Result where I: IntoIterator, J: IntoIterator; } /// Conversion from raw buffers that do not encode their arity. pub trait FromRawBuffersWithArity: Sized { type Error: Debug; /// Creates a type from raw buffers with the given arity. /// /// # Errors /// /// Returns an error if the raw buffers are inconsistent, the raw buffers /// are incompatible with the given arity, or the implementor cannot be /// constructed from the buffers. The latter typically occurs if the given /// topology is unsupported. fn from_raw_buffers_with_arity( indices: I, vertices: J, arity: usize, ) -> Result where I: IntoIterator, J: IntoIterator; } // TODO: Provide a similar trait for index buffers instead. `MeshBuffer` could // use such a trait to provide this API. pub trait IntoFlatIndex where Constant: ToType, TypeOf: NonZero, { type Item: Copy + Integer + Unsigned; fn into_flat_index(self) -> MeshBuffer, G>; } // TODO: Provide a similar trait for index buffers instead. `MeshBuffer` could // use such a trait to provide this API. pub trait IntoStructuredIndex where ::Vertex: Copy + Integer + Unsigned, { type Item: Polygonal; fn into_structured_index(self) -> MeshBuffer; } /// Polygonal mesh composed of vertex and index buffers. /// /// A `MeshBuffer` is a linear representation of a polygonal mesh that is /// composed of two separate but related linear buffers: an _index buffer_ and a /// _vertex buffer_. The index buffer contains ordered indices into the data in /// the vertex buffer and describes the topology of the mesh. The vertex buffer /// contains arbitrary data that describes each vertex. /// /// `MeshBuffer` only explicitly respresents vertices via the vertex buffer and /// surfaces via the index buffer. There is no explicit representation of /// structures like edges and faces. /// /// The `R` type parameter specifies the [`Grouping`] of the index buffer. See /// the [`index`] module documention for more information. /// /// [`Grouping`]: crate::index::Grouping /// [`index`]: crate::index #[derive(Debug)] pub struct MeshBuffer where R: Grouping, { indices: Vec, vertices: Vec, } impl MeshBuffer where R: Grouping, Vec: IndexBuffer, { pub(in crate::buffer) fn from_raw_buffers_unchecked( indices: Vec, vertices: Vec, ) -> Self { MeshBuffer { indices, vertices } } /// Creates an empty `MeshBuffer`. /// /// # Examples /// /// ```rust /// use plexus::buffer::MeshBuffer; /// use plexus::index::Flat3; /// /// let buffer = MeshBuffer::, (f64, f64, f64)>::new(); /// ``` pub fn new() -> Self { Self::default() } } impl MeshBuffer where R: Grouping, { /// Converts a `MeshBuffer` into its index and vertex buffers. pub fn into_raw_buffers(self) -> (Vec, Vec) { let MeshBuffer { indices, vertices } = self; (indices, vertices) } /// Maps over the vertex data in a `MeshBuffer`. /// /// # Examples /// /// Translating the position data in a buffer: /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::{Point3, Vector3}; /// use plexus::buffer::MeshBuffer3; /// use plexus::prelude::*; /// use plexus::primitive::generate::Position; /// use plexus::primitive::sphere::UvSphere; /// /// let buffer: MeshBuffer3> = UvSphere::new(16, 8) /// .polygons::>>() /// .triangulate() /// .collect(); /// // Translate the positions. /// let translation = Vector3::::x() * 2.0; /// let buffer = buffer.map_vertices(|position| position + translation); /// ``` pub fn map_vertices(self, f: F) -> MeshBuffer where F: FnMut(G) -> H, { let (indices, vertices) = self.into_raw_buffers(); MeshBuffer { indices, vertices: vertices.into_iter().map(f).collect::>(), } } /// Gets a slice over the index data. pub fn as_index_slice(&self) -> &[R::Group] { self.indices.as_slice() } /// Gets a slice over the vertex data. pub fn as_vertex_slice(&self) -> &[G] { self.vertices.as_slice() } } /// Exposes a [`MeshBuilder`] that can be used to construct a [`MeshBuffer`] /// incrementally from _surfaces_ and _facets_. /// /// Note that the facet data for [`MeshBuffer`] is always the unit type `()`. /// /// See the documentation for the [`builder`] module. /// /// # Examples /// /// Creating a [`MeshBuffer`] from a triangle: /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::buffer::MeshBuffer3; /// use plexus::builder::Buildable; /// use plexus::prelude::*; /// /// let mut builder = MeshBuffer3::>::builder(); /// let buffer = builder /// .surface_with(|builder| { /// let a = builder.insert_vertex((0.0, 0.0))?; /// let b = builder.insert_vertex((1.0, 0.0))?; /// let c = builder.insert_vertex((0.0, 1.0))?; /// builder.facets_with(|builder| builder.insert_facet(&[a, b, c], ())) /// }) /// .and_then(|_| builder.build()) /// .unwrap(); /// ``` /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`MeshBuilder`]: crate::builder::MeshBuilder /// [`builder`]: crate::builder impl Buildable for MeshBuffer where R: Grouping, Vec: IndexBuffer, BufferBuilder: MeshBuilder, { type Builder = BufferBuilder; type Error = BufferError; type Vertex = G; type Facet = (); fn builder() -> Self::Builder { BufferBuilder::default() } } impl Default for MeshBuffer where R: Grouping, Vec: IndexBuffer, { fn default() -> Self { MeshBuffer { indices: Default::default(), vertices: Default::default(), } } } impl DynamicArity for MeshBuffer, G> where Constant: ToType, TypeOf: NonZero, T: Copy + Integer + Unsigned, { type Dynamic = as StaticArity>::Static; fn arity(&self) -> Self::Dynamic { Flat::::ARITY } } impl DynamicArity for MeshBuffer where P: Grouping + Monomorphic + Polygonal, P::Vertex: Copy + Integer + Unsigned, { type Dynamic =

::Static; fn arity(&self) -> Self::Dynamic { P::ARITY } } impl DynamicArity for MeshBuffer, G> where N: Copy + Integer + Unsigned, { type Dynamic = MeshArity; fn arity(&self) -> Self::Dynamic { MeshArity::from_components::, _>(self.indices.iter()) } } impl DynamicArity for MeshBuffer, G> where N: Copy + Integer + Unsigned, { type Dynamic = MeshArity; fn arity(&self) -> Self::Dynamic { MeshArity::from_components::, _>(self.indices.iter()) } } impl Monomorphic for MeshBuffer where R: Grouping + Monomorphic {} impl StaticArity for MeshBuffer where R: Grouping, { type Static = ::Static; const ARITY: Self::Static = R::ARITY; } impl MeshBuffer, G> where Constant: ToType, TypeOf: NonZero, T: Copy + Integer + NumCast + Unsigned, { /// Appends the contents of a flat `MeshBuffer` into another `MeshBuffer`. /// The source buffer is drained. /// /// # Errors /// /// Returns an error if an index overflows. pub fn append(&mut self, buffer: &mut MeshBuffer) -> Result<(), BufferError> where G: FromGeometry, R: Grouping, R::Group: Into< as Grouping>::Group>, { let offset = T::from(self.vertices.len()).ok_or(BufferError::IndexOverflow)?; self.vertices.extend( buffer .vertices .drain(..) .map(|vertex| vertex.into_geometry()), ); self.indices .extend(buffer.indices.drain(..).map(|index| index.into() + offset)); Ok(()) } } impl MeshBuffer where P: Grouping + Polygonal, P::Vertex: Copy + Integer + NumCast + Unsigned, { /// Appends the contents of a structured `MeshBuffer` into another /// `MeshBuffer`. The source buffer is drained. /// /// # Errors /// /// Returns an error if an index overflows. pub fn append(&mut self, buffer: &mut MeshBuffer) -> Result<(), BufferError> where G: FromGeometry, R: Grouping, R::Group: Into<

::Group>,

::Group: Map::Group> + Topological, { let offset = ::from(self.vertices.len()).ok_or(BufferError::IndexOverflow)?; self.vertices.extend( buffer .vertices .drain(..) .map(|vertex| vertex.into_geometry()), ); self.indices.extend( buffer .indices .drain(..) .map(|topology| topology.into().map(|index| index + offset)), ); Ok(()) } } impl From

for MeshBuffer where P: IntoIndexed + Polygonal, Q: Clone + Map + Map + Polygonal, T: Polygonal, R: Grouping + Polygonal, N: Copy + Integer + NumCast + Unsigned, G: FromGeometry, { fn from(polygon: P) -> Self { let indexed = polygon.into_indexed(); MeshBuffer::from_raw_buffers( // It is tempting to use a range over the polygon's arity to // construct `R`, but that weakens the relationship between the // input polygon `P` and the index buffer grouping `R`. These types // must have compatible type-level arity, so this implementation // relies on the `Map` implementation from `P` to `R`. Some(indexed.clone().map(|(index, _)| index)), Map::::map(indexed, |(_, vertex)| vertex.into_geometry()), ) .expect("inconsistent index buffer") } } impl FromEncoding for MeshBuffer where E: FaceDecoder + VertexDecoder, E::Index: AsRef<[P::Vertex]>, P: Polygonal, G: FromGeometry, Self: FromRawBuffers, { type Error = >::Error; fn from_encoding( vertices: ::Output, faces: ::Output, ) -> Result { let indices: Vec<_> = faces .into_iter() .map(|(index, _)| { P::try_from_slice(index.as_ref()).ok_or(BufferError::ArityConflict { expected: P::ARITY.into_interval().0, actual: index.as_ref().len(), }) }) .collect::>()?; let vertices = vertices.into_iter().map(|vertex| vertex.into_geometry()); MeshBuffer::from_raw_buffers(indices, vertices) } } impl FromIndexer for MeshBuffer where R: Grouping, G: FromGeometry, P: Map> + Topological, P::Output: Topological>, BufferOf: Push, IndexOf: NumCast, Self: FromRawBuffers, { type Error = >::Error; fn from_indexer(input: I, indexer: M) -> Result where I: IntoIterator, M: Indexer, { let (indices, vertices) = input.into_iter().index_vertices(indexer); MeshBuffer::::from_raw_buffers( indices, vertices.into_iter().map(|vertex| vertex.into_geometry()), ) } } impl FromIterator

for MeshBuffer where R: Grouping, G: FromGeometry, P: Topological, P::Vertex: Copy + Eq + Hash, BufferOf: IndexBuffer, Self: FromIndexer, { fn from_iter(input: I) -> Self where I: IntoIterator, { Self::from_indexer(input, HashIndexer::default()).unwrap_or_else(|_| Self::default()) } } impl FromRawBuffers for MeshBuffer, G> where Constant: ToType, TypeOf: NonZero, T: Copy + Integer + NumCast + Unsigned, U: Copy + Integer + NumCast + Unsigned, G: FromGeometry, { type Error = BufferError; /// Creates a flat `MeshBuffer` from raw index and vertex buffers. /// /// # Errors /// /// Returns an error if the index data is out of bounds within the vertex /// buffer or if the number of indices disagrees with the arity of the index /// buffer. /// /// # Examples /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::buffer::MeshBuffer; /// use plexus::geometry::Vector; /// use plexus::index::{Flat3, HashIndexer}; /// use plexus::prelude::*; /// use plexus::primitive; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::{Normal, Position}; /// /// type E3 = Point3; /// type Vertex = (E3, Vector); // Position and normal. /// /// let cube = Cube::new(); /// let (indices, vertices) = primitive::zip_vertices(( /// cube.polygons::>(), /// cube.polygons::>() /// .map_vertices(|normal| normal.into_inner()), /// )) /// .triangulate() /// .index_vertices::(HashIndexer::default()); /// let buffer = MeshBuffer::::from_raw_buffers(indices, vertices).unwrap(); /// ``` fn from_raw_buffers(indices: I, vertices: J) -> Result where I: IntoIterator, J: IntoIterator, { let indices = indices .into_iter() .map(|index| ::from(index).ok_or(BufferError::IndexOverflow)) .collect::, _>>()?; if indices.len() % N != 0 { Err(BufferError::IndexUnaligned) } else { let vertices: Vec<_> = vertices .into_iter() .map(|vertex| vertex.into_geometry()) .collect(); let len = T::from(vertices.len()).unwrap(); if indices.iter().any(|index| *index >= len) { Err(BufferError::IndexOutOfBounds) } else { Ok(MeshBuffer { indices, vertices }) } } } } impl FromRawBuffers for MeshBuffer where P: From + Grouping + Polygonal, P::Vertex: Copy + Integer + NumCast + Unsigned, G: FromGeometry, { type Error = BufferError; /// Creates a structured `MeshBuffer` from raw index and vertex buffers. /// /// # Errors /// /// Returns an error if the index data is out of bounds within the vertex /// buffer. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point3; /// use plexus::buffer::MeshBufferN; /// use plexus::prelude::*; /// use plexus::primitive::generate::Position; /// use plexus::primitive::sphere::UvSphere; /// /// type E3 = Point3; /// /// let sphere = UvSphere::new(8, 8); /// let buffer = MeshBufferN::::from_raw_buffers( /// sphere.indexing_polygons::(), /// sphere.vertices::>(), /// ) /// .unwrap(); /// ``` fn from_raw_buffers(indices: I, vertices: J) -> Result where I: IntoIterator, J: IntoIterator, { let indices: Vec<_> = indices.into_iter().map(P::from).collect(); let vertices: Vec<_> = vertices .into_iter() .map(|vertex| vertex.into_geometry()) .collect(); let is_out_of_bounds = { let len = ::from(vertices.len()).ok_or(BufferError::IndexOverflow)?; indices .iter() .any(|polygon| polygon.as_ref().iter().any(|index| *index >= len)) }; if is_out_of_bounds { Err(BufferError::IndexOutOfBounds) } else { Ok(MeshBuffer { indices, vertices }) } } } impl IntoFlatIndex for MeshBuffer, G> where Constant: ToType, TypeOf: NonZero, T: Copy + Integer + Unsigned, { type Item = T; fn into_flat_index(self) -> MeshBuffer, G> { self } } impl IntoFlatIndex for MeshBuffer, G> where N: Copy + Integer + Unsigned, { type Item = N; /// Converts the index buffer of a `MeshBuffer` from structured data into /// flat data. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point3; /// use plexus::buffer::MeshBuffer; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// use plexus::primitive::Trigon; /// /// type E3 = Point3; /// /// let cube = Cube::new(); /// let buffer = MeshBuffer::, E3>::from_raw_buffers( /// cube.indexing_polygons::().triangulate(), /// cube.vertices::>(), /// ) /// .unwrap(); /// let buffer = buffer.into_flat_index(); /// for index in buffer.as_index_slice() { /// // ... /// } /// ``` fn into_flat_index(self) -> MeshBuffer, G> { let MeshBuffer { indices, vertices } = self; MeshBuffer { indices: indices .into_iter() .flat_map(|trigon| trigon.into_vertices()) .collect(), vertices, } } } impl IntoFlatIndex for MeshBuffer, G> where N: Copy + Integer + Unsigned, { type Item = N; /// Converts the index buffer of a `MeshBuffer` from flat data into /// structured data. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point3; /// use plexus::buffer::MeshBuffer; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// use plexus::primitive::Tetragon; /// /// type E3 = Point3; /// /// let cube = Cube::new(); /// let buffer = MeshBuffer::, E3>::from_raw_buffers( /// cube.indexing_polygons::(), /// cube.vertices::>(), /// ) /// .unwrap(); /// let buffer = buffer.into_flat_index(); /// for index in buffer.as_index_slice() { /// // ... /// } /// ``` fn into_flat_index(self) -> MeshBuffer, G> { let MeshBuffer { indices, vertices } = self; MeshBuffer { indices: indices .into_iter() .flat_map(|tetragon| tetragon.into_vertices()) .collect(), vertices, } } } impl IntoPolygons for MeshBuffer, G> where N: Copy + Integer + NumCast + Unsigned, G: Clone, Trigon: Grouping>, { type Output = vec::IntoIter; type Polygon = Trigon; /// Converts a triangular flat `MeshBuffer` into an iterator of [`Trigon`]s /// containing vertex data. /// /// [`Trigon`]: crate::primitive::Trigon fn into_polygons(self) -> Self::Output { let (indices, vertices) = self.into_raw_buffers(); indices .into_iter() .chunks(U3::USIZE) .into_iter() .map(|chunk| { // These conversions should never fail. Trigon::try_from_iter(chunk.map(|index| { let index = ::from(index).expect("index overflow"); vertices[index].clone() })) .expect("inconsistent index buffer") }) .collect::>() .into_iter() } } impl IntoPolygons for MeshBuffer, G> where N: Copy + Integer + NumCast + Unsigned, G: Clone, Trigon: Grouping>, { type Output = vec::IntoIter; type Polygon = Tetragon; /// Converts a quadrilateral flat `MeshBuffer` into an iterator of /// [`Tetragon`]s containing vertex data. /// /// # Examples /// /// Mapping over the polygons described by a flat buffer: /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::buffer::MeshBuffer; /// use plexus::graph::MeshGraph; /// use plexus::index::Flat4; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// type E3 = Point3; /// /// let buffer: MeshBuffer = Cube::new().polygons::>().collect(); /// let graph: MeshGraph = buffer /// .into_polygons() /// .map_vertices(|position| position * R64::assert(2.0)) /// .collect(); /// ``` /// /// [`Tetragon`]: crate::primitive::Tetragon fn into_polygons(self) -> Self::Output { let (indices, vertices) = self.into_raw_buffers(); indices .into_iter() .chunks(U4::USIZE) .into_iter() .map(|chunk| { // These conversions should never fail. Tetragon::try_from_iter(chunk.map(|index| { let index = ::from(index).expect("index overflow"); vertices[index].clone() })) .expect("inconsistent index buffer") }) .collect::>() .into_iter() } } impl IntoPolygons for MeshBuffer where P: Grouping + Polygonal, P::Group: Map + Polygonal, >::Output: Polygonal, ::Vertex: NumCast, P::Vertex: Copy + Integer + NumCast + Unsigned, G: Clone, { type Output = vec::IntoIter; type Polygon = >::Output; /// Converts a structured `MeshBuffer` into an iterator of polygons /// containing vertex data. /// /// # Examples /// /// Mapping over the polygons described by a structured buffer: /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::buffer::MeshBuffer; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::generate::Position; /// use plexus::primitive::sphere::UvSphere; /// use plexus::primitive::BoundedPolygon; /// /// type E3 = Point3; /// /// let buffer: MeshBuffer, E3> = /// UvSphere::new(8, 8).polygons::>().collect(); /// let graph: MeshGraph = buffer /// .into_polygons() /// .map_vertices(|position| position * R64::assert(2.0)) /// .triangulate() /// .collect(); /// ``` fn into_polygons(self) -> Self::Output { let (indices, vertices) = self.into_raw_buffers(); indices .into_iter() .map(|polygon| { polygon.map(|index| { // This conversion should never fail. let index = ::from(index).expect("index overflow"); vertices[index].clone() }) }) .collect::>() .into_iter() } } impl IntoStructuredIndex for MeshBuffer where P: Grouping + Polygonal, P::Vertex: Copy + Integer + Unsigned, { type Item = P; fn into_structured_index(self) -> MeshBuffer { self } } impl IntoStructuredIndex for MeshBuffer, G> where N: Copy + Integer + Unsigned, Trigon: Grouping>, { type Item = Trigon; /// Converts the index buffer of a `MeshBuffer` from flat data into /// structured data. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point3; /// use plexus::buffer::MeshBuffer; /// use plexus::index::Flat3; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// type E3 = Point3; /// /// let cube = Cube::new(); /// let buffer = MeshBuffer::::from_raw_buffers( /// cube.indexing_polygons::() /// .triangulate() /// .vertices(), /// cube.vertices::>(), /// ) /// .unwrap(); /// let buffer = buffer.into_structured_index(); /// for trigon in buffer.as_index_slice() { /// // ... /// } /// ``` fn into_structured_index(self) -> MeshBuffer { let MeshBuffer { indices, vertices } = self; let indices = indices .into_iter() .chunks(U3::USIZE) .into_iter() .map(::Group::try_from_iter) .collect::, _>>() .expect("inconsistent index buffer"); MeshBuffer { indices, vertices } } } impl IntoStructuredIndex for MeshBuffer, G> where N: Copy + Integer + Unsigned, Tetragon: Grouping>, { type Item = Tetragon; /// Converts the index buffer of a `MeshBuffer` from flat data into /// structured data. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point3; /// use plexus::buffer::MeshBuffer4; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// type E3 = Point3; /// /// let cube = Cube::new(); /// let buffer = MeshBuffer4::::from_raw_buffers( /// cube.indexing_polygons::(), /// cube.vertices::>(), /// ) /// .unwrap(); /// let buffer = buffer.into_structured_index(); /// for tetragon in buffer.as_index_slice() { /// // ... /// } /// ``` fn into_structured_index(self) -> MeshBuffer { let MeshBuffer { indices, vertices } = self; let indices = indices .into_iter() .chunks(U4::USIZE) .into_iter() .map(::Group::try_from_iter) .collect::, _>>() .expect("inconsistent index buffer"); MeshBuffer { indices, vertices } } } #[cfg(test)] mod tests { use decorum::R64; use nalgebra::Point3; use crate::buffer::{MeshBuffer, MeshBuffer4, MeshBufferN}; use crate::graph::MeshGraph; use crate::index::Flat3; use crate::prelude::*; use crate::primitive::cube::Cube; use crate::primitive::generate::Position; use crate::primitive::sphere::UvSphere; use crate::primitive::{BoundedPolygon, UnboundedPolygon}; type E3 = Point3; #[test] fn collect_into_flat_buffer() { let buffer: MeshBuffer, E3> = UvSphere::new(3, 2) .polygons::>() // 6 triangles, 18 vertices. .triangulate() .collect(); assert_eq!(18, buffer.as_index_slice().len()); assert_eq!(5, buffer.as_vertex_slice().len()); } #[test] fn collect_into_bounded_buffer() { let buffer: MeshBuffer, E3> = UvSphere::new(3, 2) .polygons::>() // 6 triangles, 18 vertices. .collect(); assert_eq!(6, buffer.as_index_slice().len()); assert_eq!(5, buffer.as_vertex_slice().len()); } #[test] fn collect_into_unbounded_buffer() { let buffer: MeshBuffer, E3> = Cube::new().polygons::>().collect(); assert_eq!(6, buffer.as_index_slice().len()); assert_eq!(8, buffer.as_vertex_slice().len()); for polygon in buffer.as_index_slice() { assert_eq!(4, polygon.arity()); } } #[test] fn append_structured_buffers() { let mut buffer: MeshBufferN = UvSphere::new(3, 2) .polygons::>() // 6 triangles, 18 vertices. .collect(); buffer .append( &mut Cube::new() .polygons::>() // 6 quadrilaterals, 24 vertices. .collect::>(), ) .unwrap(); assert_eq!(12, buffer.as_index_slice().len()); assert_eq!(13, buffer.as_vertex_slice().len()); } #[test] fn convert_mesh_to_buffer_by_vertex() { let graph: MeshGraph = UvSphere::new(3, 2) .polygons::>() // 6 triangles, 18 vertices. .collect(); let buffer: MeshBufferN = graph.to_mesh_by_vertex().unwrap(); assert_eq!(6, buffer.as_index_slice().len()); assert_eq!(5, buffer.as_vertex_slice().len()); } #[test] fn convert_mesh_to_buffer_by_face() { let graph: MeshGraph = UvSphere::new(3, 2) .polygons::>() // 6 triangles, 18 vertices. .collect(); let buffer: MeshBufferN = graph.to_mesh_by_face().unwrap(); assert_eq!(6, buffer.as_index_slice().len()); assert_eq!(18, buffer.as_vertex_slice().len()); } } ================================================ FILE: plexus/src/builder.rs ================================================ //! Incremental polygonal mesh construction. //! //! This module provides traits for incrementally constructing mesh data //! structures. This API allows for meshes to be constructed in a way that is //! agnostic to the specific data structure used to represent the mesh. //! //! [`Buildable`] is the primary trait of this API. It is implemented by mesh //! data structures and exposes various associated types for their associated //! data. [`Buildable`] exposes a builder type via its //! [`builder`][`Buildable::builder`] function. This builder type in turn //! provides additional builders that can be used to construct a mesh from //! _surfaces_ and _facets_. //! //! # Examples //! //! A function that generates a triangle from point geometry using builders: //! //! ```rust //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use nalgebra::Point2; //! use plexus::buffer::MeshBuffer3; //! use plexus::builder::Buildable; //! use plexus::geometry::FromGeometry; //! use plexus::graph::MeshGraph; //! use plexus::prelude::*; //! //! type E2 = Point2; //! //! fn trigon(points: [T; 3]) -> Result //! where //! B: Buildable, //! B::Vertex: FromGeometry, //! { //! let mut builder = B::builder(); //! builder.surface_with(|builder| { //! let [a, b, c] = points; //! let a = builder.insert_vertex(a)?; //! let b = builder.insert_vertex(b)?; //! let c = builder.insert_vertex(c)?; //! builder.facets_with(|builder| builder.insert_facet(&[a, b, c], B::Facet::default())) //! })?; //! builder.build() //! } //! //! // `MeshBuffer` and `MeshGraph` implement the `Buildable` trait. //! let graph: MeshGraph = trigon([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)]).unwrap(); //! let buffer: MeshBuffer3 = trigon([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)]).unwrap(); //! ``` //! //! [`Buildable::builder`]: crate::builder::Buildable::builder //! [`Buildable`]: crate::builder::Buildable // TODO: Is it useful to use a separate `FacetBuilder` type? // TODO: Keys are not opaque. Especially for `MeshBuffer`, it may be possible to // "forge" keys. This could be prevented by using a wrapper type that is // not exported, but would introduce a performance cost to map and collect // slices of keys. use std::fmt::Debug; use std::hash::Hash; use crate::geometry::FromGeometry; use crate::transact::ClosedInput; /// Polygonal mesh data structure that can be built incrementally. /// /// This trait is the primary entrypoint into the builder API. Types that /// implement this trait expose a [`MeshBuilder`] that can be used to construct /// an instance of the type from surfaces and facets. /// /// [`MeshBuilder`]: crate::builder::MeshBuilder pub trait Buildable: Sized { type Builder: MeshBuilder< Commit = Self, Error = Self::Error, Vertex = Self::Vertex, Facet = Self::Facet, >; type Error: Debug; /// Vertex data. /// /// This type represents the data associated with vertices in the mesh. /// This typically includes positional data, but no data is required and /// this type may be the unit type `()`. /// /// Each builder trait also exposes such an associated type which is /// constrained by the `Builder` type. type Vertex; /// Facet data. /// /// This type represents the data associated with facets in the mesh. No /// data is required and this type may be the unit type `()`. /// /// Each builder trait also exposes such an associated type which is /// constrained by the `Builder` type. type Facet: Default; fn builder() -> Self::Builder; } /// Incremental polygonal mesh builder. /// /// This trait exposes types that allow for mesh data structures to be /// constructed incrementally from _surfaces_ and _facets_. A _surface_ is a /// collection of vertices and facets connecting those vertices and typically /// describes a _manifold_. A _facet_ is the connectivity between vertices in a /// surface. Facets may also include associated data. /// /// Construction is hierarchical, beginning with a surface and its vertices and /// then facets. The association between a surface, its vertices, and facets is /// enforced by the API, which accepts functions that operate on increasingly /// specific builder types. The [`build`][`MeshBuilder::build`] function is used /// to complete the construction of a mesh. /// /// Builders may emit errors at any stage and errors depend on the /// implementation of the builder types (and by extension the details of the /// underlying data structure). /// /// [`MeshBuilder::build`]: crate::builder::MeshBuilder::build pub trait MeshBuilder: ClosedInput { type Builder: SurfaceBuilder; type Vertex; type Facet: Default; /// Constructs a surface. /// /// The given function is invoked with a [`SurfaceBuilder`], which can be /// used to insert vertices and construct facets. /// /// [`SurfaceBuilder`]: crate::builder::SurfaceBuilder fn surface_with(&mut self, f: F) -> Result where Self::Error: From, F: FnOnce(&mut Self::Builder) -> Result; /// Builds the mesh. /// /// The builder is consumed and a mesh with the constructed surfaces and /// facets is produced. /// /// # Errors /// /// Returns a latent error if the constructed surfaces and facets are /// incompatible with the underlying data structure. May return other /// errors depending on the details of the implementation. fn build(self) -> Result { self.commit().map_err(|(_, error)| error) } } pub trait SurfaceBuilder: ClosedInput { type Builder: FacetBuilder; /// Vertex key. /// /// Each vertex is associated with a key of this type. This key is used to /// reference a given vertex and is required to insert faces with a /// [`FacetBuilder`]. /// /// [`FacetBuilder`]: crate::builder::FacetBuilder type Key: Copy + Eq + Hash; type Vertex; type Facet: Default; /// Constructs facets in the surface. /// /// The given function is invoked with a [`FacetBuilder`], which can be used /// to insert facets. /// /// [`FacetBuilder`]: crate::builder::FacetBuilder fn facets_with(&mut self, f: F) -> Result where Self::Error: From, F: FnOnce(&mut Self::Builder) -> Result; /// Inserts a vertex into the surface. /// /// Returns a key that refers to the inserted vertex. This key can be used /// to insert facets with a [`FacetBuilder`]. /// /// [`FacetBuilder`]: crate::builder::FacetBuilder fn insert_vertex(&mut self, data: T) -> Result where Self::Vertex: FromGeometry; } pub trait FacetBuilder: ClosedInput where K: Eq + Hash, { /// Facet key. /// /// Each facet is associated with a key of this type. type Key: Copy + Eq + Hash; type Facet: Default; /// Inserts a facet into the associated surface. /// /// A facet is formed from connectivity between vertices represented by an /// ordered slice of vertex keys from the associated [`SurfaceBuilder`]. /// /// Returns a key that refers to the inserted facet. /// /// [`SurfaceBuilder`]: crate::builder::SurfaceBuilder fn insert_facet(&mut self, keys: T, data: U) -> Result where Self::Facet: FromGeometry, T: AsRef<[K]>; } ================================================ FILE: plexus/src/constant.rs ================================================ //! Morphisms between constant generics and numeric types. //! //! This module provides conversions between `typenum`'s unsigned integer types //! and `usize` constant generics. These conversions are necessary to perform //! static computations and comparisons, which cannot yet be done using constant //! generics alone at the time of writing (e.g., `{N >= 3}`). //! //! See discussion on the [Rust internals //! forum](https://internals.rust-lang.org/t/const-generics-where-restrictions/12742/7). // TODO: Move this into the `theon` crate as part of its public API. pub type ConstantOf = ::Output; pub type TypeOf = as ToType>::Output; pub struct Constant; pub trait ToConstant { type Output; } pub trait ToType { type Output; } macro_rules! impl_morphisms { (types => $($n:ident),*$(,)?) => ( use typenum::Unsigned; $( impl ToConstant for typenum::$n { type Output = Constant<{ typenum::$n::USIZE }>; } impl ToType for Constant<{ typenum::$n::USIZE }> { type Output = typenum::$n; } )* ); } impl_morphisms!(types => U0, U1, U2, U3, U4, U5, U6, U7, U8, U9, U10, U11, U12, U13, U14, U15, U16, U17, U18, U19, U21, U22, U23, U24, U25, U26, U27, U28, U29, U30, U31, U32, ); ================================================ FILE: plexus/src/encoding/mod.rs ================================================ //! Serialization and encodings. //! //! This module provides encoding support enabled via Cargo features. Each //! enabled encoding has a corresponding sub-module. For example, when [PLY] //! support is enabled, the `ply` module is exposed. The following table //! summarizes the encodings supported by Plexus: //! //! | Feature | Default | Encoding | Read | Write | //! |----------------|---------|----------|------|-------| //! | `encoding-ply` | No | [PLY] | Yes | No | //! //! This module provides traits used by all encodings. These traits describe the //! outputs and inputs of decoders and encoders, respectively. Generally, these //! traits should **not** be used directly. Instead, prefer the conversion //! traits exposed for specific encodings, such as `FromPly` when using [PLY]. //! //! [PLY]: https://en.wikipedia.org/wiki/ply_(file_format) pub mod ply; use std::fmt::Debug; pub trait VertexDecoder { type Output: IntoIterator; type Vertex; } pub trait FaceDecoder { type Output: IntoIterator; type Index: IntoIterator; type Face; } // TODO: This trait is a bit limiting. Consider implementing more specific // traits like `FromPly` directly. This could allow more specific // features to be supported, such as edge geometry for `MeshGraph`s. pub trait FromEncoding: Sized where E: FaceDecoder + VertexDecoder, { type Error: Debug; fn from_encoding( vertices: ::Output, faces: ::Output, ) -> Result; } ================================================ FILE: plexus/src/encoding/ply.rs ================================================ //! [PLY] encoding. //! //! This module provides support for the [PLY] format via the [`FromPly`] and //! [`ToPly`] traits. These traits can be used with a decoder and encoder to //! read and write mesh data structures to and from the [PLY] format. //! //! [PLY] support is implemented using the [`ply-rs`] crate and some of its //! types are re-exported here. //! //! # Examples //! //! Reading a [PLY] file into a [`MeshGraph`]: //! //! ```rust //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use nalgebra::Point3; //! use plexus::encoding::ply::{FromPly, PositionEncoding}; //! use plexus::graph::MeshGraph; //! use std::io::Read; //! //! type E3 = Point3; //! //! // Read from a file, network, etc. //! fn read() -> impl Read { //! // ... //! # let ply: &[u8] = include_bytes!("../../../data/cube.ply"); //! # ply //! } //! //! let encoding = PositionEncoding::::default(); //! let (graph, _) = MeshGraph::::from_ply(encoding, read()).unwrap(); //! ``` //! //! [PLY]: https://en.wikipedia.org/wiki/PLY_(file_format) //! //! [`ply-rs`]: https://crates.io/crates/ply-rs //! //! [`FromPly`]: crate::encoding::ply::FromPly //! [`ToPly`]: crate::encoding::ply::ToPly //! [`MeshGraph`]: crate::graph::MeshGraph #![cfg(feature = "encoding-ply")] use num::cast; use num::NumCast; use ply_rs::parser::Parser; use ply_rs::ply::KeyMap; use smallvec::SmallVec; use std::io::{self, Read, Write}; use std::iter::FromIterator; use std::marker::PhantomData; use theon::space::{EuclideanSpace, FiniteDimensional}; use thiserror::Error; use typenum::{NonZero, Unsigned, U2, U3}; use crate::encoding::{FaceDecoder, FromEncoding, VertexDecoder}; pub use ply_rs::ply::{ ElementDef as ElementDefinition, Property, PropertyDef as PropertyDefinition, PropertyType, }; // TODO: These traits only allow a single element to be read for each topology // (vertices, faces, etc.). It may be useful to allow code to aggregate // various elements to produce an output for a single topology. // TODO: Consider using the newtype pattern to hide underlying types and expose // a smaller and more tailored API surface. pub type Header = KeyMap; pub type Payload = KeyMap>; pub type Element = KeyMap; pub struct Ply { pub header: Header, pub payload: Payload, } impl Ply { pub fn parse(mut read: impl Read) -> io::Result { Parser::::new().read_ply(&mut read).map(|ply| Ply { header: ply.header.elements, payload: ply.payload, }) } } /// Errors concerning the [PLY] encoding. /// /// [PLY]: https://en.wikipedia.org/wiki/PLY_(file_format) #[derive(Debug, Error)] pub enum PlyError { #[error("required element not found")] ElementNotFound, #[error("required property not found")] PropertyNotFound, /// The type of a property conflicts with a decoding. #[error("conflicting property type found")] PropertyTypeConflict, /// A polygonal mesh data structure is not compatible with encoded PLY data. #[error("encoding operation failed")] EncodingIncompatible, /// An I/O operation (read or write via the `Read` and `Write` traits) /// failed. #[error("I/O operation failed")] Io(io::Error), } impl From for PlyError { fn from(error: io::Error) -> Self { PlyError::Io(error) } } pub trait ElementExt { fn scalar(&self, key: K) -> Result where K: AsRef, T: NumCast; fn list(&self, key: K) -> Result where K: AsRef, T: NumCast, I: FromIterator; } impl ElementExt for Element { fn scalar(&self, key: K) -> Result where K: AsRef, T: NumCast, { self.get(key.as_ref()) .ok_or(PlyError::PropertyNotFound)? .clone() .into_scalar() } fn list(&self, key: K) -> Result where K: AsRef, T: NumCast, I: FromIterator, { self.get(key.as_ref()) .ok_or(PlyError::PropertyNotFound)? .clone() .into_list() } } pub trait PropertyExt { fn into_scalar(self) -> Result where T: NumCast; fn into_list(self) -> Result where T: NumCast, I: FromIterator; } impl PropertyExt for Property { fn into_scalar(self) -> Result where T: NumCast, { match self { Property::Char(value) => num_cast_scalar(value), Property::UChar(value) => num_cast_scalar(value), Property::Short(value) => num_cast_scalar(value), Property::UShort(value) => num_cast_scalar(value), Property::Int(value) => num_cast_scalar(value), Property::UInt(value) => num_cast_scalar(value), Property::Float(value) => num_cast_scalar(value), Property::Double(value) => num_cast_scalar(value), _ => Err(PlyError::PropertyTypeConflict), } } fn into_list(self) -> Result where T: NumCast, I: FromIterator, { match self { Property::ListChar(values) => num_cast_list(values), Property::ListUChar(values) => num_cast_list(values), Property::ListShort(values) => num_cast_list(values), Property::ListUShort(values) => num_cast_list(values), Property::ListInt(values) => num_cast_list(values), Property::ListUInt(values) => num_cast_list(values), Property::ListFloat(values) => num_cast_list(values), Property::ListDouble(values) => num_cast_list(values), _ => Err(PlyError::PropertyTypeConflict), } } } pub trait VertexElementDecoder { fn decode_vertex_elements<'a>( &self, definitions: &'a Header, elements: &'a Payload, ) -> Result<(&'a ElementDefinition, &'a Vec), PlyError> { decode_elements(definitions, elements, "vertex") } } pub trait VertexPropertyDecoder: VertexDecoder { fn decode_vertex_properties<'a, I>( &self, definition: &'a ElementDefinition, elements: I, ) -> Result where I: IntoIterator; } pub trait FaceElementDecoder { fn decode_face_elements<'a>( &self, definitions: &'a Header, elements: &'a Payload, ) -> Result<(&'a ElementDefinition, &'a Vec), PlyError> { decode_elements(definitions, elements, "face") } } pub trait FacePropertyDecoder: FaceDecoder { fn decode_face_properties<'a, I>( &self, definition: &'a ElementDefinition, elements: I, ) -> Result where I: IntoIterator; } pub trait FromPly: Sized { fn from_ply(decoder: E, read: impl Read) -> Result<(Self, Ply), PlyError>; } impl FromPly for T where T: FromEncoding, E: FaceElementDecoder + FacePropertyDecoder + VertexPropertyDecoder + VertexElementDecoder, { fn from_ply(decoder: E, mut read: impl Read) -> Result<(Self, Ply), PlyError> { let ply = Ply::parse(&mut read)?; let mesh = T::from_encoding( decode_vertex_properties(&decoder, &ply.header, &ply.payload)?, decode_face_properties(&decoder, &ply.header, &ply.payload)?, ) .map_err(|_| PlyError::EncodingIncompatible)?; Ok((mesh, ply)) } } pub trait ToPly { fn to_ply( &self, definitions: &Header, encoder: E, write: impl Write, ) -> Result; } pub trait DecodePosition: FiniteDimensional + Sized where N: NonZero + Unsigned, { fn decode_position(element: &Element) -> Result; } impl DecodePosition for T where T: EuclideanSpace + FiniteDimensional, { fn decode_position(element: &Element) -> Result { let position = EuclideanSpace::from_xy(element.scalar("x")?, element.scalar("y")?); Ok(position) } } impl DecodePosition for T where T: EuclideanSpace + FiniteDimensional, { fn decode_position(element: &Element) -> Result { let position = EuclideanSpace::from_xyz( element.scalar("x")?, element.scalar("y")?, element.scalar("z")?, ); Ok(position) } } pub struct PositionEncoding { phantom: PhantomData T>, } impl Default for PositionEncoding { fn default() -> Self { PositionEncoding { phantom: PhantomData, } } } impl FaceDecoder for PositionEncoding { type Output = Vec<(Self::Index, Self::Face)>; type Index = SmallVec<[usize; 4]>; type Face = (); } impl FaceElementDecoder for PositionEncoding {} impl FacePropertyDecoder for PositionEncoding { fn decode_face_properties<'a, I>( &self, _: &'a ElementDefinition, elements: I, ) -> Result<::Output, PlyError> where I: IntoIterator, { elements .into_iter() .map(|element| { let indices = element.list("vertex_index")?; Ok((indices, ())) }) .collect() } } impl VertexDecoder for PositionEncoding { type Output = Vec; type Vertex = T; } impl VertexElementDecoder for PositionEncoding {} impl VertexPropertyDecoder for PositionEncoding where T: DecodePosition + FiniteDimensional, N: NonZero + Unsigned, { fn decode_vertex_properties<'a, I>( &self, _: &'a ElementDefinition, elements: I, ) -> Result<::Output, PlyError> where I: IntoIterator, { elements .into_iter() .map(|element| T::decode_position(element)) .collect() } } pub fn decode_elements<'a, K>( definitions: &'a Header, elements: &'a Payload, key: K, ) -> Result<(&'a ElementDefinition, &'a Vec), PlyError> where K: AsRef, { definitions .get(key.as_ref()) .ok_or(PlyError::ElementNotFound) .and_then(|definition| { elements .get(&definition.name) .ok_or(PlyError::ElementNotFound) .map(|elements| (definition, elements)) }) } pub fn decode_vertex_properties( decoder: &E, definitions: &Header, elements: &Payload, ) -> Result where E: VertexElementDecoder + VertexPropertyDecoder, { decoder .decode_vertex_elements(definitions, elements) .and_then(|(definition, elements)| decoder.decode_vertex_properties(definition, elements)) } pub fn decode_face_properties( decoder: &E, definitions: &Header, elements: &Payload, ) -> Result where E: FaceElementDecoder + FacePropertyDecoder, { decoder .decode_face_elements(definitions, elements) .and_then(|(definition, elements)| decoder.decode_face_properties(definition, elements)) } fn num_cast_scalar(value: T) -> Result where T: NumCast, U: NumCast, { cast::cast(value).ok_or(PlyError::PropertyTypeConflict) } fn num_cast_list(values: Vec) -> Result where T: NumCast, U: NumCast, I: FromIterator, { values .into_iter() .map(num_cast_scalar) .collect::>() } #[cfg(test)] mod tests { use nalgebra::Point3; use crate::buffer::MeshBuffer; use crate::encoding::ply::{FromPly, PositionEncoding}; use crate::graph::MeshGraph; use crate::primitive::Tetragon; type E3 = Point3; #[test] fn decode_into_buffer() { let (buffer, _) = { let ply: &[u8] = include_bytes!("../../../data/cube.ply"); MeshBuffer::, E3>::from_ply(PositionEncoding::::default(), ply) .unwrap() }; assert_eq!(8, buffer.as_vertex_slice().len()); assert_eq!(6, buffer.as_index_slice().len()); } #[test] fn decode_into_graph() { let (graph, _) = { let ply: &[u8] = include_bytes!("../../../data/cube.ply"); MeshGraph::::from_ply(PositionEncoding::::default(), ply).unwrap() }; assert_eq!(8, graph.vertex_count()); assert_eq!(12, graph.edge_count()); assert_eq!(6, graph.face_count()); } } ================================================ FILE: plexus/src/entity/borrow.rs ================================================ pub trait Reborrow { type Target; fn reborrow(&self) -> &Self::Target; } pub trait ReborrowMut: Reborrow { fn reborrow_mut(&mut self) -> &mut Self::Target; } pub trait ReborrowInto<'a>: Reborrow { fn reborrow_into(self) -> &'a Self::Target; } impl Reborrow for &'_ T { type Target = T; fn reborrow(&self) -> &Self::Target { self } } impl Reborrow for &'_ mut T { type Target = T; fn reborrow(&self) -> &Self::Target { self } } impl ReborrowMut for &'_ mut T { fn reborrow_mut(&mut self) -> &mut Self::Target { self } } impl<'a, T> ReborrowInto<'a> for &'a T { fn reborrow_into(self) -> &'a Self::Target { self } } impl<'a, T> ReborrowInto<'a> for &'a mut T { fn reborrow_into(self) -> &'a Self::Target { &*self } } ================================================ FILE: plexus/src/entity/dijkstra.rs ================================================ use derivative::Derivative; use std::cmp::Reverse; use std::collections::hash_map::Entry; use std::collections::{BinaryHeap, HashMap, HashSet}; use crate::entity::storage::{AsStorage, Enumerate, Get}; use crate::entity::traverse::Adjacency; use crate::entity::view::{Bind, Unbind}; use crate::entity::EntityError; use crate::geometry::Metric; pub type MetricTree = HashMap, Q)>; #[derive(Derivative)] #[derivative(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] struct KeyedMetric( #[derivative(Ord = "ignore", PartialEq = "ignore", PartialOrd = "ignore")] K, Q, ) where Q: Eq + Ord; pub fn metrics_with<'a, M, T, Q, F>( from: T, to: Option, f: F, ) -> Result, EntityError> where M: 'a + AsStorage, T: Adjacency + Bind<&'a M> + Copy + Unbind<&'a M>, Q: Copy + Metric, F: Fn(T, T) -> Q, { let (storage, from) = from.unbind(); let capacity = if let Some(key) = to { if !storage.as_storage().contains_key(&key) { return Err(EntityError::EntityNotFound); } 0 } else { storage.as_storage().len() }; let mut buffer = BinaryHeap::new(); let mut breadcrumbs = HashSet::with_capacity(capacity); let mut metrics = HashMap::with_capacity(capacity); metrics.insert(from, (None, Q::zero())); buffer.push(KeyedMetric(from, Reverse(Q::zero()))); while let Some(KeyedMetric(key, Reverse(metric))) = buffer.pop() { if Some(key) == to { break; } let entity = T::bind(storage, key).ok_or(EntityError::EntityNotFound)?; if breadcrumbs.insert(entity.key()) { for adjacent in entity .adjacency() .into_iter() .map(|key| T::bind(storage, key)) { let adjacent = adjacent.ok_or(EntityError::EntityNotFound)?; let summand = f(entity, adjacent); if summand < Q::zero() { return Err(EntityError::Data); } let metric = metric + summand; match metrics.entry(adjacent.key()) { Entry::Occupied(entry) => { if metric < entry.get().1 { entry.into_mut().1 = metric; } } Entry::Vacant(entry) => { entry.insert((Some(entity.key()), metric)); } } buffer.push(KeyedMetric(adjacent.key(), Reverse(metric))); } } } Ok(metrics) } #[cfg(test)] mod tests { use decorum::R64; use nalgebra::Point2; use theon::space::InnerSpace; use crate::entity::dijkstra::{self, MetricTree}; use crate::entity::EntityError; use crate::graph::MeshGraph; use crate::prelude::*; use crate::primitive::{Tetragon, Trigon}; #[test] fn decreasing_summand() { let graph = MeshGraph::>::from_raw_buffers( vec![Tetragon::new(0usize, 1, 2, 3)], vec![(-1.0, -1.0), (1.0, -1.0), (1.0, 1.0), (-1.0, 1.0)], ) .unwrap(); let vertex = graph.vertices().next().unwrap(); assert_eq!( Err(EntityError::Data), dijkstra::metrics_with(vertex, None, |_, _| -1isize) ) } #[test] fn logical_metrics() { let graph = MeshGraph::<()>::from_raw_buffers(vec![Trigon::new(0usize, 1, 2)], vec![(); 3]) .unwrap(); let vertex = graph.vertices().next().unwrap(); let metrics = dijkstra::metrics_with(vertex, None, |_, _| 1usize).unwrap(); let a = vertex.key(); let b = vertex.outgoing_arc().destination_vertex().key(); let c = vertex.outgoing_arc().next_arc().destination_vertex().key(); let aq = *metrics.get(&a).unwrap(); let bq = *metrics.get(&b).unwrap(); let cq = *metrics.get(&c).unwrap(); assert_eq!(aq, (None, 0)); assert_eq!(bq, (Some(a), 1)); assert_eq!(cq, (Some(a), 1)); } // TODO: Use approximated comparisons in assertions. #[test] fn euclidean_distance_metrics() { let graph = MeshGraph::>::from_raw_buffers( vec![Tetragon::new(0usize, 1, 2, 3)], vec![(-1.0, -1.0), (1.0, -1.0), (1.0, 1.0), (-1.0, 1.0)], ) .unwrap(); let vertex = graph.vertices().next().unwrap(); let metrics: MetricTree<_, R64> = dijkstra::metrics_with(vertex, None, |from, to| { R64::assert((to.position() - from.position()).magnitude()) }) .unwrap(); let a = vertex.key(); let b = vertex.outgoing_arc().destination_vertex().key(); let c = vertex.outgoing_arc().next_arc().destination_vertex().key(); let d = vertex .outgoing_arc() .next_arc() .next_arc() .destination_vertex() .key(); let aq = *metrics.get(&a).unwrap(); let bq = *metrics.get(&b).unwrap(); let cq = *metrics.get(&c).unwrap(); let dq = *metrics.get(&d).unwrap(); assert_eq!(aq, (None, R64::assert(0.0))); assert_eq!(bq, (Some(a), R64::assert(2.0))); assert_eq!(cq, (Some(b), R64::assert(4.0))); assert_eq!(dq, (Some(a), R64::assert(2.0))); } } ================================================ FILE: plexus/src/entity/mod.rs ================================================ pub mod borrow; pub mod dijkstra; pub mod storage; pub mod traverse; pub mod view; use thiserror::Error; use crate::entity::storage::{Dispatch, Key, Storage}; #[derive(Debug, Eq, Error, PartialEq)] pub enum EntityError { #[error("required entity not found")] EntityNotFound, #[error("data operation failed")] Data, } pub trait Entity: Sized { type Key: Key; type Storage: Default + Dispatch + Storage; } pub trait Payload: Entity { type Data; fn get(&self) -> &Self::Data; fn get_mut(&mut self) -> &mut Self::Data; } ================================================ FILE: plexus/src/entity/storage/hash.rs ================================================ use ahash::AHashMap; use std::hash::Hash; use std::marker::PhantomData; use crate::entity::storage::{ AsStorage, AsStorageMut, DependentStorage, Dispatch, Dynamic, Enumerate, Get, IncrementalKeyer, IndependentStorage, InnerKey, Insert, InsertWithKey, Key, Keyer, Mode, Remove, Static, StorageTarget, }; use crate::entity::{Entity, Payload}; // TODO: The `Keyer` parameter `R` of `HashStorage` cannot be parameterized when // implementing the `AsStorage` and `Dispatch` traits even if the // conflicting implementations use a private local type or the `Keyer` // trait is private. Instead, this is implemented more specifically for // `IncrementalKeyer`. Perhaps this will be possible in the future. // // See https://github.com/rust-lang/rust/issues/48869 pub struct HashStorage where E: Entity, R: Default, P: Mode, { inner: AHashMap::Key>, E>, keyer: R, phantom: PhantomData P>, } impl HashStorage where E: Entity, InnerKey: Eq + Hash, R: Default, P: Mode, { pub fn shrink_to_fit(&mut self) { self.inner.shrink_to_fit(); } } impl AsStorage for HashStorage where E: Entity, InnerKey: Eq + Hash, { fn as_storage(&self) -> &StorageTarget { self } } impl AsStorage for HashStorage where E: Entity, E::Key: Key, { fn as_storage(&self) -> &StorageTarget { self } } impl AsStorage for HashStorage where E: Entity, InnerKey: Eq + Hash, R: 'static + Default, { fn as_storage(&self) -> &StorageTarget { self } } impl AsStorageMut for HashStorage where E: Entity, InnerKey: Eq + Hash, { fn as_storage_mut(&mut self) -> &mut StorageTarget { self } } impl AsStorageMut for HashStorage where E: Entity, E::Key: Key, { fn as_storage_mut(&mut self) -> &mut StorageTarget { self } } impl AsStorageMut for HashStorage where E: Entity, InnerKey: Eq + Hash, R: 'static + Default, { fn as_storage_mut(&mut self) -> &mut StorageTarget { self } } impl Default for HashStorage where E: Entity, R: Default, P: Mode, { fn default() -> Self { HashStorage { inner: Default::default(), keyer: Default::default(), phantom: PhantomData, } } } #[rustfmt::skip] impl Dispatch for HashStorage where E: Entity, InnerKey: Eq + Hash, { type Target<'a> = dyn 'a + DependentStorage where E: 'a; } #[rustfmt::skip] impl Dispatch for HashStorage where E: Entity, E::Key: Key, { type Target<'a> = dyn 'a + IndependentStorage where E: 'a; } #[rustfmt::skip] impl Dispatch for HashStorage where E: Entity, InnerKey: Eq + Hash, R: 'static + Default, { type Target<'a> = Self where E: 'a; } impl Enumerate for HashStorage where E: Entity, InnerKey: Eq + Hash, R: Default, P: Mode, { fn len(&self) -> usize { self.inner.len() } fn iter(&self) -> Box> { Box::new( self.inner .iter() .map(|(key, entity)| (E::Key::from_inner(*key), entity)), ) } fn iter_mut(&mut self) -> Box> where E: Payload, { Box::new( self.inner .iter_mut() .map(|(key, entity)| (E::Key::from_inner(*key), entity.get_mut())), ) } } impl Get for HashStorage where E: Entity, InnerKey: Eq + Hash, R: Default, P: Mode, { fn get(&self, key: &E::Key) -> Option<&E> { self.inner.get(&key.into_inner()) } fn get_mut(&mut self, key: &E::Key) -> Option<&mut E> { self.inner.get_mut(&key.into_inner()) } } impl Insert for HashStorage where E: Entity, InnerKey: Eq + Hash, R: Keyer, P: Mode, { fn insert(&mut self, entity: E) -> E::Key { let key = self.keyer.next(); self.inner.insert(key, entity); Key::from_inner(key) } } impl InsertWithKey for HashStorage where E: Entity, InnerKey: Eq + Hash, P: Mode, { fn insert_with_key(&mut self, key: &E::Key, entity: E) -> Option { self.inner.insert(key.into_inner(), entity) } } impl Remove for HashStorage where E: Entity, InnerKey: Eq + Hash, R: Default, P: Mode, { fn remove(&mut self, key: &E::Key) -> Option { self.inner.remove(&key.into_inner()) } } ================================================ FILE: plexus/src/entity/storage/mod.rs ================================================ mod hash; use std::hash::Hash; use crate::entity::{Entity, Payload}; pub use crate::entity::storage::hash::HashStorage; pub mod prelude { pub use crate::entity::storage::{Enumerate, Get, Insert, InsertWithKey, Remove}; } pub type StorageTarget<'a, E> = <::Storage as Dispatch>::Target<'a>; pub type InnerKey = ::Inner; pub trait Key: Copy + Eq + Hash + Sized { type Inner: Copy + Sized; fn from_inner(key: Self::Inner) -> Self; fn into_inner(self) -> Self::Inner; } pub trait Keyer: Clone + Default where K: Key, { fn next(&mut self) -> K::Inner; } #[derive(Clone, Copy, Default)] pub struct IncrementalKeyer { key: u64, } impl Keyer for IncrementalKeyer where K: Key, { fn next(&mut self) -> K::Inner { let key = self.key; self.key = self.key.checked_add(1).expect("keyspace exhausted"); key } } #[rustfmt::skip] pub trait Dispatch where E: Entity, { type Target<'a>: 'a + ?Sized + Storage where E: 'a; } pub trait Mode {} pub enum Static {} impl Mode for Static {} pub enum Dynamic {} impl Mode for Dynamic {} // TODO: GATs cannot yet be used here while still supporting trait objects, // because the types cannot be bound in a supertrait. To support trait // objects, the associated types must be bound (likely to `Box`). // See https://github.com/rust-lang/rust/issues/67510 pub trait Enumerate where E: Entity, { fn len(&self) -> usize; fn iter(&self) -> Box>; fn iter_mut(&mut self) -> Box> where E: Payload; } pub trait Get where E: Entity, { fn get(&self, key: &E::Key) -> Option<&E>; fn get_mut(&mut self, key: &E::Key) -> Option<&mut E>; fn contains_key(&self, key: &E::Key) -> bool { self.get(key).is_some() } } pub trait Insert where E: Entity, { fn insert(&mut self, entity: E) -> E::Key; } pub trait InsertWithKey where E: Entity, { fn insert_with_key(&mut self, key: &E::Key, entity: E) -> Option; } pub trait Remove where E: Entity, { fn remove(&mut self, key: &E::Key) -> Option; } pub trait Storage: AsStorage + AsStorageMut + Enumerate + Get + Remove where E: Entity, { } impl Storage for T where T: AsStorage + AsStorageMut + Enumerate + Get + Remove, E: Entity, { } pub trait DependentStorage: InsertWithKey + Storage where E: Entity, { } impl DependentStorage for T where T: InsertWithKey + Storage, E: Entity, { } pub trait IndependentStorage: Insert + Storage where E: Entity, { } impl IndependentStorage for T where T: Insert + Storage, E: Entity, { } pub trait Fuse where M: AsStorage, T: Entity, { type Output: AsStorage; fn fuse(self, source: M) -> Self::Output; } // TODO: It may not be possible for storage wrapper types to implement this // generally for all underlying storage types when using trait objects. An // input type parameter for the wrapped storage type may be required with // an implementation for each type that can be wrapped. Is there some way // to support blanket implementations? pub trait AsStorage where E: Entity, { fn as_storage(&self) -> &StorageTarget; } impl AsStorage for &'_ T where E: Entity, T: AsStorage + ?Sized, { fn as_storage(&self) -> &StorageTarget { >::as_storage(self) } } impl AsStorage for &'_ mut T where E: Entity, T: AsStorage + ?Sized, { fn as_storage(&self) -> &StorageTarget { >::as_storage(self) } } pub trait AsStorageMut: AsStorage where E: Entity, { fn as_storage_mut(&mut self) -> &mut StorageTarget; } impl AsStorageMut for &'_ mut T where E: Entity, T: AsStorageMut + ?Sized, { fn as_storage_mut(&mut self) -> &mut StorageTarget { >::as_storage_mut(self) } } pub trait AsStorageOf { fn as_storage_of(&self) -> &StorageTarget where E: Entity, Self: AsStorage, { self.as_storage() } fn as_storage_mut_of(&mut self) -> &mut StorageTarget where E: Entity, Self: AsStorageMut, { self.as_storage_mut() } } impl AsStorageOf for T {} ================================================ FILE: plexus/src/entity/traverse.rs ================================================ use std::collections::{HashSet, VecDeque}; use std::hash::Hash; use std::marker::PhantomData; use crate::entity::borrow::Reborrow; use crate::entity::storage::{AsStorage, Enumerate}; use crate::entity::view::{Bind, ClosedView, Unbind}; pub enum Breadth {} pub enum Depth {} pub trait Buffer: Default + Extend where R: Order, { fn push(&mut self, item: T); fn pop(&mut self) -> Option; } impl Buffer for Vec { fn push(&mut self, item: T) { Vec::::push(self, item) } fn pop(&mut self) -> Option { Vec::::pop(self) } } impl Buffer for VecDeque { fn push(&mut self, item: T) { VecDeque::::push_back(self, item) } fn pop(&mut self) -> Option { VecDeque::::pop_front(self) } } /// Traversal ordering. /// /// Provides a default type implementing [`Buffer`] for the ordering described /// by `Self`. This reduces the number of required type parameters in types /// implementing traversals, as only an ordering type is needed to derive the /// buffer. Note that the item type can typically be derived from other required /// type parameters. /// /// See [`Traversal`]. /// /// [`Buffer`]: crate::entity::traverse::Buffer /// [`Traversal`]: crate::entity::traverse::Traversal pub trait Order: Sized { type Buffer: Buffer; } impl Order for Breadth { type Buffer = VecDeque; } impl Order for Depth { type Buffer = Vec; } pub trait Adjacency: ClosedView { type Output: IntoIterator; fn adjacency(&self) -> Self::Output; } #[derive(Debug)] pub struct Traversal where B: Reborrow, B::Target: AsStorage, T: Adjacency, R: Order, { storage: B, breadcrumbs: HashSet, buffer: R::Buffer, phantom: PhantomData T>, } impl Clone for Traversal where B: Clone + Reborrow, B::Target: AsStorage, T: Adjacency, R: Order, R::Buffer: Clone, { fn clone(&self) -> Self { Traversal { storage: self.storage.clone(), breadcrumbs: self.breadcrumbs.clone(), buffer: self.buffer.clone(), phantom: PhantomData, } } } impl From for Traversal where B: Reborrow, B::Target: AsStorage, T: Adjacency + Unbind, R: Order, { fn from(view: T) -> Self { let (storage, key) = view.unbind(); let capacity = storage.reborrow().as_storage().len(); let mut buffer = R::Buffer::default(); buffer.push(key); Traversal { storage, breadcrumbs: HashSet::with_capacity(capacity), buffer, phantom: PhantomData, } } } impl<'a, M, T, R> Iterator for Traversal<&'a M, T, R> where M: 'a + AsStorage, T: Adjacency + Bind<&'a M>, R: Order, { type Item = T; fn next(&mut self) -> Option { while let Some(view) = self.buffer.pop().and_then(|key| T::bind(self.storage, key)) { if self.breadcrumbs.insert(view.key()) { self.buffer.extend(view.adjacency()); return Some(view); } } None } fn size_hint(&self) -> (usize, Option) { ( 1, Some(AsStorage::::as_storage(&self.storage).len()), ) } } /// Trace of a traversal, iteration, etc. /// /// A trace caches _breadcrumbs_, which identify entities encountered during a /// traversal. pub trait Trace { /// Inserts the given breadcrumb into the trace. The breadcrumb may or may /// not be cached. /// /// A _collision_ occurs if a breadcrumb that has been cached by the trace /// is reinserted. If a collision with the trace is detected, then this /// function returns `false` and otherwise returns `true` (similarly to /// collections like [`HashSet`]). /// /// If `false` is returned, then any traversal or iteration should /// terminate. /// /// [`HashSet`]: std::collections::HashSet fn insert(&mut self, breadcrumb: T) -> bool; } /// Trace that caches and detects collisions with only the first breadcrumb that /// is inserted. /// /// A collision only occurs if the first breadcrumb is reinserted; no other /// breadcrumbs are cached. /// /// This trace should **not** be used when traversing a structure with unknown /// consistency, because it may never signal that the iteration should /// terminate. However, it requires very little space and time to operate. #[derive(Clone, Copy, Debug, Default)] pub struct TraceFirst where T: Copy, { breadcrumb: Option, } impl Trace for TraceFirst where T: Copy + Eq, { fn insert(&mut self, breadcrumb: T) -> bool { match self.breadcrumb { Some(collision) => collision != breadcrumb, None => { self.breadcrumb = Some(breadcrumb); true } } } } /// Trace that caches all inserted breadcrumbs and detects collisions with any /// such breadcrumb. /// /// This trace is very robust and reliably signals termination of a traversal, /// but requires non-trivial space to cache breadcrumbs and must hash /// breadcrumbs to detect collisions. #[derive(Clone, Debug, Default)] pub struct TraceAny where T: Copy + Eq + Hash, { breadcrumbs: HashSet, } impl Trace for TraceAny where T: Copy + Eq + Hash, { fn insert(&mut self, breadcrumb: T) -> bool { self.breadcrumbs.insert(breadcrumb) } } ================================================ FILE: plexus/src/entity/view.rs ================================================ use std::hash::{Hash, Hasher}; use std::ops::{Deref, DerefMut}; use crate::entity::borrow::{Reborrow, ReborrowInto, ReborrowMut}; use crate::entity::storage::{AsStorage, AsStorageMut, Get, Key}; use crate::entity::{Entity, Payload}; pub trait ClosedView { type Key: Key; type Entity: Entity; fn key(&self) -> Self::Key; } pub trait Bind: ClosedView + Sized where B: Reborrow, { fn bind(storage: B, key: Self::Key) -> Option; } // Note that orphan views do not gain this implementation without a // `From>` implementation. impl Bind for T where B: Reborrow, B::Target: AsStorage, T: ClosedView + From::Entity>> + Sized, { fn bind(storage: B, key: Self::Key) -> Option { View::bind(storage, key).map(Self::from) } } pub trait Rebind: ClosedView where B: Reborrow, T: ClosedView, { fn rebind(self, key: T::Key) -> Option; } impl Rebind for U where B: Reborrow, B::Target: AsStorage + AsStorage, T: ClosedView + From::Entity>>, U: ClosedView + Into::Entity>>, { fn rebind(self, key: T::Key) -> Option { self.into().rebind(key).map(T::from) } } pub trait Unbind: ClosedView where B: Reborrow, { fn unbind(self) -> (B, Self::Key); } impl Unbind for T where B: Reborrow, B::Target: AsStorage, T: ClosedView + Into::Entity>>, { fn unbind(self) -> (B, Self::Key) { self.into().unbind() } } pub struct View where B: Reborrow, B::Target: AsStorage, E: Entity, { storage: B, key: E::Key, } impl View where B: Reborrow, B::Target: AsStorage, E: Entity, { pub fn bind(storage: B, key: E::Key) -> Option { storage .reborrow() .as_storage() .contains_key(&key) .then(|| View::bind_unchecked(storage, key)) } pub fn bind_unchecked(storage: B, key: E::Key) -> Self { View { storage, key } } pub fn bind_into(storage: B, key: E::Key) -> Option where T: From, { View::bind(storage, key).map(T::from) } pub fn unbind(self) -> (B, E::Key) { let View { storage, key, .. } = self; (storage, key) } pub fn rebind(self, key: T::Key) -> Option> where B::Target: AsStorage, T: Entity, { let (storage, _) = self.unbind(); View::bind(storage, key) } pub fn rebind_into(self, key: U::Key) -> Option where B::Target: AsStorage, T: From>, U: Entity, { self.rebind(key).map(T::from) } pub fn get(&self) -> &E::Data where E: Payload, { self.as_entity().get() } pub fn key(&self) -> E::Key { self.key } pub fn to_ref(&self) -> View<&B::Target, E> { View::bind_unchecked(self.storage.reborrow(), self.key) } fn as_entity(&self) -> &E { self.storage .reborrow() .as_storage() .get(&self.key) .expect("view key invalidated") } } impl View where B: ReborrowMut, B::Target: AsStorage, E: Entity, { /// Mutably reborrows the interior of the view. /// /// It is possible to invalidate views using this function. Care must be taken to ensure that /// the originating view's key is still present in storage after the reborrowed view is /// dropped. // LINT: The "unannotated" name of this function is `to_mut`, but "unchecked" is used to // indicate that this function can be used incorrectly and corrupt views. `to_mut` does // not violate lints, but `to_mut_unchecked` does. This function and its proxies allow // this unconventional name. #[expect(clippy::wrong_self_convention)] pub fn to_mut_unchecked(&mut self) -> View<&mut B::Target, E> { View::bind_unchecked(self.storage.reborrow_mut(), self.key) } } impl View where B: ReborrowMut, B::Target: AsStorageMut, E: Entity, { pub fn get_mut(&mut self) -> &mut E::Data where E: Payload, { self.as_entity_mut().get_mut() } fn as_entity_mut(&mut self) -> &mut E { self.storage .reborrow_mut() .as_storage_mut() .get_mut(&self.key) .expect("view key invalidated") } } impl<'a, B, E> View where B: ReborrowInto<'a>, B::Target: AsStorage, E: Entity, { pub fn into_ref(self) -> View<&'a B::Target, E> { let (storage, key) = self.unbind(); View::bind_unchecked(storage.reborrow_into(), key) } } impl AsRef for View where B: Reborrow, B::Target: AsStorage, E: Entity, { fn as_ref(&self) -> &E::Key { &self.key } } impl Clone for View where B: Clone + Reborrow, B::Target: AsStorage, E: Entity, { fn clone(&self) -> Self { View { storage: self.storage.clone(), key: self.key, } } } impl ClosedView for View where B: Reborrow, B::Target: AsStorage, E: Entity, { type Key = E::Key; type Entity = E; fn key(&self) -> Self::Key { self.key } } impl Copy for View where B: Copy + Reborrow, B::Target: AsStorage, E: Entity, { } impl Deref for View where B: Reborrow, B::Target: AsStorage, E: Entity, { type Target = E; fn deref(&self) -> &Self::Target { self.as_entity() } } impl DerefMut for View where B: ReborrowMut, B::Target: AsStorageMut, E: Entity, { fn deref_mut(&mut self) -> &mut Self::Target { self.as_entity_mut() } } impl Eq for View where B: Reborrow, B::Target: AsStorage, E: Entity, { } impl Hash for View where B: Reborrow, B::Target: AsStorage, E: Entity, { fn hash(&self, state: &mut H) where H: Hasher, { self.key.hash(state); } } impl PartialEq for View where B: Reborrow, B::Target: AsStorage, E: Entity, { fn eq(&self, other: &Self) -> bool { self.key == other.key } } pub struct Orphan<'a, E> where E: Payload, { data: &'a mut E::Data, key: E::Key, } impl<'a, E> Orphan<'a, E> where E: Payload, E::Data: 'a, { pub fn bind(storage: &'a mut M, key: E::Key) -> Option where E: 'a, M: AsStorageMut, { View::bind(storage, key).map(Orphan::from) } pub fn bind_unchecked(data: &'a mut E::Data, key: E::Key) -> Self { Orphan { data, key } } pub fn bind_into(storage: &'a mut M, key: E::Key) -> Option where E: 'a, T: From, M: AsStorageMut, { Orphan::bind(storage, key).map(T::from) } pub fn get(&self) -> &E::Data { &*self.data } pub fn get_mut(&mut self) -> &mut E::Data { self.data } pub fn key(&self) -> E::Key { self.key } } impl<'a, E> AsRef for Orphan<'a, E> where E: Payload, E::Data: 'a, { fn as_ref(&self) -> &E::Key { &self.key } } impl<'a, E> ClosedView for Orphan<'a, E> where E: Payload, E::Data: 'a, { type Key = E::Key; type Entity = E; fn key(&self) -> Self::Key { self.key } } impl<'a, E, M> From> for Orphan<'a, E> where E: 'a + Payload, E::Data: 'a, M: AsStorageMut, { fn from(view: View<&'a mut M, E>) -> Self { let (storage, key) = view.unbind(); let entity = storage .as_storage_mut() .get_mut(&key) .expect("view key invalidated"); Orphan::bind_unchecked(entity.get_mut(), key) } } impl<'a, E> Eq for Orphan<'a, E> where E: Payload, E::Data: 'a, { } impl<'a, E> Hash for Orphan<'a, E> where E: Payload, E::Data: 'a, { fn hash(&self, state: &mut H) where H: Hasher, { self.key.hash(state); } } impl<'a, E> PartialEq for Orphan<'a, E> where E: Payload, E::Data: 'a, { fn eq(&self, other: &Self) -> bool { self.key == other.key } } ================================================ FILE: plexus/src/geometry/mod.rs ================================================ //! Geometric traits and computational geometry. //! //! Plexus uses the [`theon`] crate to abstract over types that represent Euclidean spaces and //! implement linear algebra. Types and traits are re-exported from [`theon`] in this module, but //! it may be necessary to import additional types from [`theon`]. //! //! [`theon`]: https://crates.io/crates/theon use num::{One, Zero}; pub mod partition; pub use theon::query::*; pub use theon::space::{Scalar, Vector}; pub use theon::{AsPosition, AsPositionMut, Position}; pub trait FromGeometry { fn from_geometry(other: T) -> Self; } impl FromGeometry for T { fn from_geometry(other: T) -> Self { other } } /// Geometry elision into `()`. impl FromGeometry for () where T: UnitGeometry, { fn from_geometry(_: T) -> Self {} } /// Geometry elision from `()`. impl FromGeometry<()> for T where T: UnitGeometry + Default, { fn from_geometry(_: ()) -> Self { T::default() } } /// Geometry elision. /// /// Geometric types that implement this trait may be elided. In particular, /// these types may be converted into and from `()` via the [`FromGeometry`] and /// [`IntoGeometry`] traits. /// /// For a geometric type `T`, the following table illustrates the elisions in /// which `T` may participate: /// /// | Bounds on `T` | From | Into | /// |--------------------------|------|------| /// | `UnitGeometry` | `T` | `()` | /// | `Default + UnitGeometry` | `()` | `T` | /// /// These conversions are useful when converting between mesh data structures /// with incompatible geometry, such as from a [`MeshGraph`] with face geometry /// to a [`MeshBuffer`] that cannot support such geometry. /// /// When geometry features are enabled, `UnitGeometry` is implemented for /// integrated foreign types. /// /// [`FromGeometry`]: crate::geometry::FromGeometry /// [`IntoGeometry`]: crate::geometry::IntoGeometry /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`MeshGraph`]: crate::graph::MeshGraph pub trait UnitGeometry {} pub trait IntoGeometry { fn into_geometry(self) -> T; } impl IntoGeometry for T where U: FromGeometry, { fn into_geometry(self) -> U { U::from_geometry(self) } } pub trait Metric: Eq + One + Ord + Zero {} impl Metric for Q where Q: Eq + One + Ord + Zero {} ================================================ FILE: plexus/src/geometry/partition.rs ================================================ use approx::abs_diff_eq; use num::traits::real::Real; use num::Zero; use std::cmp::Ordering; use theon::query::{Line, Plane}; use theon::space::{EuclideanSpace, FiniteDimensional}; use typenum::{U1, U2, U3}; // "Left" and "right" are arbitrary here and refer to the partitioned spaces // formed by a geometric entity. This is a point, line, and plane in one, two, // three dimensions, respectively. #[derive(Clone, Copy, Eq, Hash, PartialEq)] pub enum BinaryPartition { Left, Right, } pub trait PointPartition where S: EuclideanSpace, { fn partition(&self, point: S) -> Option; } impl PointPartition for S where S: EuclideanSpace + FiniteDimensional, { // TODO: Should `EmptyOrd` be used here? fn partition(&self, point: S) -> Option { let ax = self.into_x(); let px = point.into_x(); match px.partial_cmp(&ax) { Some(Ordering::Less) => Some(BinaryPartition::Left), Some(Ordering::Greater) => Some(BinaryPartition::Right), _ => None, } } } impl PointPartition for Line where S: EuclideanSpace + FiniteDimensional, { fn partition(&self, point: S) -> Option { // Compute the determinant of a matrix composed of points along the line // and the queried point. This can also be thought of as a two- // dimensional cross product. // TODO: Perhaps this should be exposed by Theon instead. let (ax, ay) = self.origin.into_xy(); let (bx, by) = (self.origin + *self.direction.get()).into_xy(); let (px, py) = point.into_xy(); let determinant = ((bx - ax) * (py - ay)) - ((by - ay) * (px - ax)); if abs_diff_eq!(determinant, Zero::zero()) { None } else { Some(if determinant.is_sign_positive() { BinaryPartition::Left } else { BinaryPartition::Right }) } } } impl PointPartition for Plane where S: EuclideanSpace + FiniteDimensional, { fn partition(&self, point: S) -> Option { let _ = point; todo!() } } ================================================ FILE: plexus/src/graph/builder.rs ================================================ use crate::builder::{FacetBuilder, MeshBuilder, SurfaceBuilder}; use crate::geometry::{FromGeometry, IntoGeometry}; use crate::graph::data::GraphData; use crate::graph::face::FaceKey; use crate::graph::mutation::face::{self, FaceInsertCache}; use crate::graph::mutation::vertex; use crate::graph::mutation::{Immediate, Mutation}; use crate::graph::vertex::VertexKey; use crate::graph::{GraphError, MeshGraph}; use crate::transact::{ClosedInput, Transact}; pub struct GraphBuilder where G: GraphData, { mutation: Mutation>>, } impl Default for GraphBuilder where G: GraphData, { fn default() -> Self { GraphBuilder { mutation: Mutation::from(MeshGraph::default()), } } } impl ClosedInput for GraphBuilder where G: GraphData, { type Input = (); } impl MeshBuilder for GraphBuilder where G: GraphData, { type Builder = Self; type Vertex = G::Vertex; type Facet = G::Face; fn surface_with(&mut self, f: F) -> Result where Self::Error: From, F: FnOnce(&mut Self::Builder) -> Result, { f(self).map_err(|error| error.into()) } } impl Transact<::Input> for GraphBuilder where G: GraphData, { type Commit = MeshGraph; type Abort = (); type Error = GraphError; fn commit(self) -> Result { let GraphBuilder { mutation } = self; mutation.commit() } fn abort(self) -> Self::Abort {} } impl SurfaceBuilder for GraphBuilder where G: GraphData, { type Builder = Self; type Key = VertexKey; type Vertex = G::Vertex; type Facet = G::Face; fn facets_with(&mut self, f: F) -> Result where Self::Error: From, F: FnOnce(&mut Self::Builder) -> Result, { f(self).map_err(|error| error.into()) } fn insert_vertex(&mut self, data: T) -> Result where Self::Vertex: FromGeometry, { Ok(vertex::insert(&mut self.mutation, data.into_geometry())) } } impl FacetBuilder for GraphBuilder where G: GraphData, { type Facet = G::Face; type Key = FaceKey; fn insert_facet(&mut self, keys: T, data: U) -> Result where Self::Facet: FromGeometry, T: AsRef<[VertexKey]>, { let cache = FaceInsertCache::from_storage(&self.mutation, keys.as_ref())?; let data = data.into_geometry(); face::insert_with(&mut self.mutation, cache, || (Default::default(), data)) } } ================================================ FILE: plexus/src/graph/core.rs ================================================ use std::marker::PhantomData; use crate::entity::storage::{AsStorage, AsStorageMut, Fuse, StorageTarget}; use crate::entity::Entity; use crate::graph::data::{GraphData, Parametric}; use crate::graph::edge::{Arc, Edge}; use crate::graph::face::Face; use crate::graph::vertex::Vertex; /// A complete core that owns all of its storage. pub type OwnedCore = Core< G, as Entity>::Storage, as Entity>::Storage, as Entity>::Storage, as Entity>::Storage, >; /// Adaptable graph representation that can incorporate arbitrary storage. /// /// Cores act as a container for storage that comprises a graph and allow /// storage to be moved (_fused_ and _unfused_) as values or references. A core /// may or may not own its storage and may or may not provide storage for all /// entities. When a core does not own its storage, it is _ephemeral_. /// /// Cores are used by the mutation API to unfuse storage and guard it behind /// per-entity APIs. Unlike `MeshGraph`, `Core` does not implement the /// `Consistent` trait. `MeshGraph` contains a core, but does not mutate it /// outside of the mutation API, which maintains consistency. /// /// A core's fields may be _unfused_ and _fused_. When a field is unfused, its /// type is `()`. An unfused field has no value and is zero-sized. A fused field /// has any type other than `()`. These fields should provide storage for their /// corresponding entity. The `Fuse` trait is used to transition from `()` to /// some other type by _fusing_ storage into a `Core`. `Fuse` implementations /// enforce storage constraints; it is not possible to fuse values that do not /// expose storage to yet unfused entities. /// /// A `Core` with no unfused fields is _complete_. pub struct Core where G: GraphData, { pub(in crate::graph) vertices: V, pub(in crate::graph) arcs: A, pub(in crate::graph) edges: E, pub(in crate::graph) faces: F, phantom: PhantomData G>, } impl Core where G: GraphData, { pub fn empty() -> Self { Core { vertices: (), arcs: (), edges: (), faces: (), phantom: PhantomData, } } } impl Core where G: GraphData, { pub fn unfuse(self) -> (V, A, E, F) { let Core { vertices, arcs, edges, faces, .. } = self; (vertices, arcs, edges, faces) } } impl AsStorage> for Core where V: AsStorage>, G: GraphData, { fn as_storage(&self) -> &StorageTarget> { self.vertices.as_storage() } } impl AsStorage> for Core where A: AsStorage>, G: GraphData, { fn as_storage(&self) -> &StorageTarget> { self.arcs.as_storage() } } impl AsStorage> for Core where E: AsStorage>, G: GraphData, { fn as_storage(&self) -> &StorageTarget> { self.edges.as_storage() } } impl AsStorage> for Core where F: AsStorage>, G: GraphData, { fn as_storage(&self) -> &StorageTarget> { self.faces.as_storage() } } impl AsStorageMut> for Core where V: AsStorageMut>, G: GraphData, { fn as_storage_mut(&mut self) -> &mut StorageTarget> { self.vertices.as_storage_mut() } } impl AsStorageMut> for Core where A: AsStorageMut>, G: GraphData, { fn as_storage_mut(&mut self) -> &mut StorageTarget> { self.arcs.as_storage_mut() } } impl AsStorageMut> for Core where E: AsStorageMut>, G: GraphData, { fn as_storage_mut(&mut self) -> &mut StorageTarget> { self.edges.as_storage_mut() } } impl AsStorageMut> for Core where F: AsStorageMut>, G: GraphData, { fn as_storage_mut(&mut self) -> &mut StorageTarget> { self.faces.as_storage_mut() } } impl Default for Core where G: GraphData, V: Default, A: Default, E: Default, F: Default, { fn default() -> Self { Core { vertices: Default::default(), arcs: Default::default(), edges: Default::default(), faces: Default::default(), phantom: PhantomData, } } } impl Fuse> for Core where V: AsStorage>, G: GraphData, { type Output = Core; fn fuse(self, vertices: V) -> Self::Output { let Core { arcs, edges, faces, .. } = self; Core { vertices, arcs, edges, faces, phantom: PhantomData, } } } impl Fuse> for Core where A: AsStorage>, G: GraphData, { type Output = Core; fn fuse(self, arcs: A) -> Self::Output { let Core { vertices, edges, faces, .. } = self; Core { vertices, arcs, edges, faces, phantom: PhantomData, } } } impl Fuse> for Core where E: AsStorage>, G: GraphData, { type Output = Core; fn fuse(self, edges: E) -> Self::Output { let Core { vertices, arcs, faces, .. } = self; Core { vertices, arcs, edges, faces, phantom: PhantomData, } } } impl Fuse> for Core where F: AsStorage>, G: GraphData, { type Output = Core; fn fuse(self, faces: F) -> Self::Output { let Core { vertices, arcs, edges, .. } = self; Core { vertices, arcs, edges, faces, phantom: PhantomData, } } } impl Parametric for Core where G: GraphData, { type Data = G; } ================================================ FILE: plexus/src/graph/data.rs ================================================ use crate::entity::borrow::Reborrow; pub type Data = ::Data; /// Graph data. /// /// Specifies the types used to represent data in vertices, arcs, edges, and /// faces in a [`MeshGraph`]. Arbitrary types can be used, including the unit /// type `()` for no data at all. /// /// Geometric operations depend on understanding the positional data in vertices /// exposed by the [`AsPosition`] trait. If the `Vertex` type implements /// [`AsPosition`], then geometric operations supported by the `Position` type /// are exposed by graph APIs. /// /// # Examples /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate num; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::{Point3, Vector4}; /// use num::Zero; /// use plexus::geometry::{AsPosition, IntoGeometry}; /// use plexus::graph::{GraphData, MeshGraph}; /// use plexus::prelude::*; /// use plexus::primitive::generate::Position; /// use plexus::primitive::sphere::UvSphere; /// /// #[derive(Clone, Copy, Eq, Hash, PartialEq)] /// pub struct Vertex { /// pub position: Point3, /// pub color: Vector4, /// } /// /// impl GraphData for Vertex { /// type Vertex = Self; /// type Arc = (); /// type Edge = (); /// type Face = (); /// } /// /// impl AsPosition for Vertex { /// type Position = Point3; /// /// fn as_position(&self) -> &Self::Position { /// &self.position /// } /// } /// /// // Create a mesh from a uv-sphere. /// let mut graph: MeshGraph = UvSphere::new(8, 8) /// .polygons::>>() /// .map_vertices(|position| Vertex { /// position, /// color: Zero::zero(), /// }) /// .collect(); /// ``` /// /// [`AsPosition`]: crate::geometry::AsPosition /// [`MeshGraph`]: crate::graph::MeshGraph pub trait GraphData: Sized { type Vertex: Clone; type Arc: Clone + Default; type Edge: Clone + Default; type Face: Clone + Default; } impl GraphData for () { type Vertex = (); type Arc = (); type Edge = (); type Face = (); } impl GraphData for (T, T) where T: Clone, { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl GraphData for (T, T, T) where T: Clone, { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl GraphData for [T; 2] where T: Clone, { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl GraphData for [T; 3] where T: Clone, { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } pub trait Parametric { type Data: GraphData; } impl Parametric for B where B: Reborrow, B::Target: Parametric, { type Data = ::Data; } ================================================ FILE: plexus/src/graph/edge.rs ================================================ use arrayvec::ArrayVec; use derivative::Derivative; use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::ops::{Deref, DerefMut}; use theon::space::{EuclideanSpace, Scalar, Vector}; use theon::{AsPosition, AsPositionMut}; use crate::entity::borrow::{Reborrow, ReborrowInto, ReborrowMut}; use crate::entity::storage::{AsStorage, AsStorageMut, HashStorage, IncrementalKeyer, Key}; use crate::entity::view::{Bind, ClosedView, Orphan, Rebind, Unbind, View}; use crate::entity::{Entity, Payload}; use crate::graph::data::{Data, GraphData, Parametric}; use crate::graph::face::{Face, FaceKey, FaceOrphan, FaceView, Ring}; use crate::graph::geometry::{ArcNormal, EdgeMidpoint, VertexPosition}; use crate::graph::mutation::edge::{ self, ArcBridgeCache, ArcExtrudeCache, EdgeRemoveCache, EdgeSplitCache, }; use crate::graph::mutation::{self, Consistent, Immediate, Mutable}; use crate::graph::path::Path; use crate::graph::vertex::{Vertex, VertexKey, VertexOrphan, VertexView}; use crate::graph::{ Circulator, GraphError, OptionExt as _, OrphanCirculator, ResultExt as _, Selector, ViewCirculator, }; use crate::transact::{BypassOrCommit, Mutate}; type Mutation = mutation::Mutation>; pub trait ToArc: Sized where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + Consistent + Parametric, { fn into_arc(self) -> ArcView; fn arc(&self) -> ArcView<&B::Target>; } // Unlike other graph structures, the vertex connectivity of an arc is immutable // and encoded within its key. This provides fast and reliable lookups even when // a graph is in an inconsistent state. However, it also complicates certain // topological mutations and sometimes requires that arcs be rekeyed. For this // reason, `Arc` has no fields representing its source and destination vertices // nor its opposite arc; such fields would be redundant. /// Arc entity. #[derive(Derivative)] #[derivative(Debug, Hash)] pub struct Arc where G: GraphData, { /// User data. #[derivative(Debug = "ignore", Hash = "ignore")] pub(crate) data: G::Arc, /// Required key into the next arc. pub(crate) next: Option, /// Required key into the previous arc. pub(crate) previous: Option, /// Required key into the edge. pub(crate) edge: Option, /// Optional key into the face. pub(crate) face: Option, } impl Arc where G: GraphData, { pub fn new(data: G::Arc) -> Self { Arc { data, next: None, previous: None, edge: None, face: None, } } } impl Entity for Arc where G: GraphData, { type Key = ArcKey; type Storage = HashStorage; } impl Payload for Arc where G: GraphData, { type Data = G::Arc; fn get(&self) -> &Self::Data { &self.data } fn get_mut(&mut self) -> &mut Self::Data { &mut self.data } } /// Arc key. #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct ArcKey(VertexKey, VertexKey); impl ArcKey { pub(in crate::graph) fn into_opposite(self) -> ArcKey { let (a, b) = self.into(); (b, a).into() } } impl From<(VertexKey, VertexKey)> for ArcKey { fn from(key: (VertexKey, VertexKey)) -> Self { ArcKey(key.0, key.1) } } impl From for (VertexKey, VertexKey) { fn from(key: ArcKey) -> Self { (key.0, key.1) } } impl Key for ArcKey { type Inner = (VertexKey, VertexKey); fn from_inner(key: Self::Inner) -> Self { ArcKey(key.0, key.1) } fn into_inner(self) -> Self::Inner { (self.0, self.1) } } /// View of an arc entity. /// /// An arc from a vertex $A$ to a vertex $B$ is notated $\overrightarrow{AB}$. /// This is shorthand for the path notation $\overrightarrow{(A,B)}$. /// /// Arcs provide the connectivity information within a [`MeshGraph`] and are the /// primary mechanism for traversing its topology. Moreover, most edge-like /// operations are exposed by arcs. /// /// See the [`graph`] module documentation for more information about views. /// /// # Examples /// /// Traversing a [`MeshGraph`] of a cube via its arcs to find an opposing face: /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::graph::MeshGraph; /// use plexus::index::HashIndexer; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// type E3 = Point3; /// /// let mut graph: MeshGraph = Cube::new() /// .polygons::>() /// .collect_with_indexer(HashIndexer::default()) /// .unwrap(); /// /// let face = graph.faces().next().unwrap(); /// let opposite = face /// .into_arc() /// .into_opposite_arc() /// .into_next_arc() /// .into_next_arc() /// .into_opposite_arc() /// .into_face() /// .unwrap(); /// ``` /// /// [`MeshGraph`]: crate::graph::MeshGraph /// [`graph`]: crate::graph pub struct ArcView where B: Reborrow, B::Target: AsStorage>> + Parametric, { inner: View>>, } impl ArcView where B: Reborrow, M: AsStorage>> + Parametric, { pub fn to_ref(&self) -> ArcView<&M> { self.inner.to_ref().into() } /// Returns `true` if this is a boundary arc. /// /// A boundary arc has no associated face. pub fn is_boundary_arc(&self) -> bool { self.face.is_none() } } impl ArcView where B: ReborrowMut, M: AsStorage>> + Parametric, { // This function is also used to implement `Ring::to_mut_unchecked`. // LINT: This convention is specifically used for interior reborrows and is consistent with // `to_ref`. #[expect(clippy::wrong_self_convention)] pub(in crate::graph) fn to_mut_unchecked(&mut self) -> ArcView<&mut M> { self.inner.to_mut_unchecked().into() } } impl<'a, B, M, G> ArcView where B: ReborrowInto<'a, Target = M>, M: AsStorage> + Parametric, G: GraphData, { // TODO: Relocate this documentation of `into_ref`. /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// /// let mut graph = MeshGraph::>::from_raw_buffers_with_arity( /// vec![0u32, 1, 2, 3], /// vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)], /// 4, /// ) /// .unwrap(); /// let key = graph /// .arcs() /// .find(|arc| arc.is_boundary_arc()) /// .unwrap() /// .key(); /// let arc = graph /// .arc_mut(key) /// .unwrap() /// .extrude_with_offset(1.0) /// .unwrap() /// .into_ref(); /// /// // This would not be possible without conversion into an immutable view. /// let _ = arc.into_next_arc().into_next_arc().into_face(); /// let _ = arc.into_opposite_arc().into_face(); /// ``` pub fn into_ref(self) -> ArcView<&'a M> { self.inner.into_ref().into() } } impl ArcView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { pub fn get<'a>(&'a self) -> &'a G::Arc where G: 'a, { self.inner.get() } } impl ArcView where B: ReborrowMut, M: AsStorageMut> + Parametric, G: GraphData, { pub fn get_mut<'a>(&'a mut self) -> &'a mut G::Arc where G: 'a, { self.inner.get_mut() } } /// Reachable API. impl ArcView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { pub(in crate::graph) fn into_reachable_boundary_arc(self) -> Option { if self.is_boundary_arc() { Some(self) } else { self.into_reachable_opposite_arc() .and_then(|opposite| opposite.is_boundary_arc().then_some(opposite)) } } pub(in crate::graph) fn into_reachable_opposite_arc(self) -> Option { let key = self.key().into_opposite(); self.rebind(key) } pub(in crate::graph) fn into_reachable_next_arc(self) -> Option { let key = self.next; key.and_then(|key| self.rebind(key)) } pub(in crate::graph) fn into_reachable_previous_arc(self) -> Option { let key = self.previous; key.and_then(|key| self.rebind(key)) } } impl ArcView where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { /// Converts the arc into its ring. pub fn into_ring(self) -> Ring { self.into() } /// Returns the arc if it is a boundary arc, otherwise `None`. pub fn into_boundary_arc(self) -> Option { self.into_reachable_boundary_arc() } /// Converts the arc into its opposite arc. // Arcs are unique in that they have one-to-one traversals over themselves. #[must_use] pub fn into_opposite_arc(self) -> Self { self.into_reachable_opposite_arc().expect_consistent() } /// Converts the arc into its next arc. // Arcs are unique in that they have one-to-one traversals over themselves. #[must_use] pub fn into_next_arc(self) -> Self { self.into_reachable_next_arc().expect_consistent() } /// Converts the arc into its previous arc. // Arcs are unique in that they have one-to-one traversals over themselves. #[must_use] pub fn into_previous_arc(self) -> Self { self.into_reachable_previous_arc().expect_consistent() } /// Gets the ring of the arc. pub fn ring(&self) -> Ring<&M> { self.to_ref().into() } /// Returns the same arc if it is a boundary arc, otherwise `None`. pub fn boundary_arc(&self) -> Option> { self.to_ref().into_boundary_arc() } /// Gets the opposite arc. pub fn opposite_arc(&self) -> ArcView<&M> { self.to_ref().into_opposite_arc() } /// Gets the next arc. pub fn next_arc(&self) -> ArcView<&M> { self.to_ref().into_next_arc() } /// Gets the previous arc. pub fn previous_arc(&self) -> ArcView<&M> { self.to_ref().into_previous_arc() } } /// Reachable API. impl ArcView where B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { pub(in crate::graph) fn into_reachable_source_vertex(self) -> Option> { let (key, _) = self.key().into(); self.rebind(key) } pub(in crate::graph) fn into_reachable_destination_vertex(self) -> Option> { let (_, key) = self.key().into(); self.rebind(key) } } impl ArcView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_path(self) -> Path<'static, B> { let (storage, ab) = self.unbind(); let (a, b) = ab.into(); Path::bind(storage, [a, b]).unwrap() } pub fn path(&self) -> Path<&M> { self.to_ref().into_path() } /// Converts the arc into its source vertex. pub fn into_source_vertex(self) -> VertexView { self.into_reachable_source_vertex().expect_consistent() } /// Converts the arc into its destination vertex. pub fn into_destination_vertex(self) -> VertexView { self.into_reachable_destination_vertex().expect_consistent() } /// Gets the source vertex of the arc. pub fn source_vertex(&self) -> VertexView<&M> { self.to_ref().into_source_vertex() } /// Gets the destination vertex of the arc. pub fn destination_vertex(&self) -> VertexView<&M> { self.to_ref().into_destination_vertex() } } /// Reachable API. impl ArcView where B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { pub(in crate::graph) fn into_reachable_edge(self) -> Option> { let key = self.edge; key.and_then(|key| self.rebind(key)) } } impl ArcView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Converts the arc into its edge. pub fn into_edge(self) -> EdgeView { self.into_reachable_edge().expect_consistent() } /// Gets the edge of the arc. pub fn edge(&self) -> EdgeView<&M> { self.to_ref().into_edge() } } /// Reachable API. impl ArcView where B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { pub(in crate::graph) fn into_reachable_face(self) -> Option> { let key = self.face; key.and_then(|key| self.rebind(key)) } } impl ArcView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Converts the arc into its face. /// /// If this is a boundary arc, then `None` is returned. pub fn into_face(self) -> Option> { self.into_reachable_face() } /// Gets the face of this arc. /// /// If this is a boundary arc, then `None` is returned. pub fn face(&self) -> Option> { self.to_ref().into_face() } } impl ArcView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn normal(&self) -> Vector> where G: ArcNormal, G::Vertex: AsPosition, { ::normal(self.to_ref()).expect_consistent() } } impl ArcView where B: Reborrow, M: AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn midpoint(&self) -> VertexPosition where G: EdgeMidpoint, G::Vertex: AsPosition, { G::midpoint(self.to_ref()).expect_consistent() } } impl<'a, B, M, G> ArcView where B: ReborrowInto<'a, Target = M>, M: 'a + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_adjacent_vertices( self, ) -> impl Clone + ExactSizeIterator> { VertexCirculator::from(self.into_ref()) } } impl ArcView where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Gets an iterator of views over the vertices connected by the arc. pub fn adjacent_vertices( &self, ) -> impl Clone + ExactSizeIterator> { self.to_ref().into_adjacent_vertices() } } impl<'a, B, M, G> ArcView where B: ReborrowInto<'a, Target = M>, M: 'a + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_adjacent_faces(self) -> impl Clone + ExactSizeIterator> { FaceCirculator::from(self.into_ref()) } } impl ArcView where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Gets an iterator of views over the faces connected to the arc. pub fn adjacent_faces(&self) -> impl Clone + ExactSizeIterator> { self.to_ref().into_adjacent_faces() } } impl<'a, M> ArcView<&'a mut M> where M: AsStorage> + AsStorageMut> + Consistent + Parametric, { pub fn into_adjacent_vertex_orphans( self, ) -> impl ExactSizeIterator> { VertexCirculator::from(self) } } impl ArcView where B: ReborrowMut, B::Target: AsStorage>> + AsStorageMut>> + Consistent + Parametric, { /// Gets an iterator of orphan views over the vertices connected by the arc. pub fn adjacent_vertex_orphans( &mut self, ) -> impl ExactSizeIterator>> { self.to_mut_unchecked().into_adjacent_vertex_orphans() } } impl<'a, M> ArcView<&'a mut M> where M: AsStorage> + AsStorageMut> + Consistent + Parametric, { pub fn into_adjacent_face_orphans( self, ) -> impl ExactSizeIterator> { FaceCirculator::from(self) } } impl ArcView where B: ReborrowMut, B::Target: AsStorage>> + AsStorageMut>> + Consistent + Parametric, { /// Gets an iterator of orphan views over the faces connected to the arc. pub fn adjacent_face_orphans(&mut self) -> impl ExactSizeIterator>> { self.to_mut_unchecked().into_adjacent_face_orphans() } } impl<'a, M, G> ArcView<&'a mut M> where M: AsStorage> + AsStorage> + AsStorage> + AsStorage> + Default + Mutable, G: GraphData, { /// Splits the composite edge of the arc into two adjacent edges that share /// a vertex. /// /// Splitting inserts a new vertex with data provided by the given function. /// Splitting an arc $\overrightarrow{AB}$ returns a vertex $M$ that /// subdivides the composite edge. The leading arc of $M$ is /// $\overrightarrow{MB}$ and is a part of the same ring as the initiating /// arc. /// /// Returns the inserted vertex. /// /// # Examples /// /// Splitting an edge in a [`MeshGraph`] with weighted vertices: /// /// ```rust /// use plexus::graph::{GraphData, MeshGraph}; /// use plexus::prelude::*; /// use plexus::primitive::NGon; /// /// pub enum Weight {} /// /// impl GraphData for Weight { /// type Vertex = f64; /// type Arc = (); /// type Edge = (); /// type Face = (); /// } /// /// let mut graph = /// MeshGraph::::from_raw_buffers(vec![NGon([0usize, 1, 2])], vec![1.0, 2.0, 0.5]) /// .unwrap(); /// let key = graph.arcs().next().unwrap().key(); /// let vertex = graph.arc_mut(key).unwrap().split_with(|| 0.1); /// ``` /// /// [`MeshGraph`]: crate::graph::MeshGraph pub fn split_with(self, f: F) -> VertexView<&'a mut M> where F: FnOnce() -> G::Vertex, { // This should never fail here. let cache = EdgeSplitCache::from_arc(self.to_ref()).expect_consistent(); let (storage, _) = self.unbind(); Mutation::take(storage) .bypass_or_commit_with(|mutation| edge::split_with(mutation, cache, f)) .map(|(storage, m)| Bind::bind(storage, m).expect_consistent()) .map_err(|(_, error)| error) .expect_consistent() } /// Splits the composite edge of the arc at its midpoint. /// /// Splitting inserts a new vertex with the data of the arc's source vertex /// but modified such that the position of the vertex is the computed /// midpoint of both of the arc's vertices. /// /// Splitting inserts a new vertex with data provided by the given function. /// Splitting an arc $\overrightarrow{AB}$ returns a vertex $M$ that /// subdivides the composite edge. The leading arc of $M$ is /// $\overrightarrow{MB}$ and is a part of the same ring as the initiating /// arc. /// /// This function is only available if a [`MeshGraph`] exposes positional /// data in its vertices and that data supports interpolation. See the /// [`EdgeMidpoint`] trait. /// /// Returns the inserted vertex. /// /// # Examples /// /// Splitting an edge in a triangle at its midpoint: /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::Trigon; /// /// let mut graph = MeshGraph::>::from_raw_buffers( /// vec![Trigon::new(0usize, 1, 2)], /// vec![(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)], /// ) /// .unwrap(); /// let key = graph.arcs().next().unwrap().key(); /// let vertex = graph.arc_mut(key).unwrap().split_at_midpoint(); /// ``` /// /// [`EdgeMidpoint`]: crate::graph::EdgeMidpoint /// [`MeshGraph`]: crate::graph::MeshGraph pub fn split_at_midpoint(self) -> VertexView<&'a mut M> where G: EdgeMidpoint, G::Vertex: AsPositionMut + Clone, { let mut data = self.source_vertex().get().clone(); let midpoint = self.midpoint(); self.split_with(move || { *data.as_position_mut() = midpoint; data }) } // TODO: What if an edge in the bridging quadrilateral is collapsed, such as // bridging arcs within a triangular ring? Document these edge cases // (no pun intended). /// Connects the arc to another arc by inserting a face. /// /// Bridging arcs inserts a new face and, as needed, new arcs and edges. /// The inserted face is always a quadrilateral. The bridged arcs must be /// boundary arcs with an orientation that allows them to form a ring. /// /// Bridging two compatible arcs $\overrightarrow{AB}$ and /// $\overrightarrow{CD}$ will result in a ring $\overrightarrow{\\{A,B, /// C,D\\}}$. /// /// Arcs can be bridged within a ring. The destination arc can be chosen by /// key or index, where an index selects the $n^\text{th}$ arc from the /// source arc within the ring. /// /// Returns the inserted face. /// /// # Errors /// /// Returns an error if the destination arc cannot be found, either arc is /// not a boundary arc, or the orientation of the destination arc is /// incompatible with the initiating arc. /// /// # Examples /// /// Bridging two disjoint quadrilaterals together: /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::geometry::FromGeometry; /// use plexus::graph::{ArcKey, GraphData, MeshGraph, VertexKey, VertexView}; /// use plexus::prelude::*; /// use plexus::primitive::NGon; /// /// fn find<'a, I, T, G>(input: I, data: T) -> Option /// where /// I: IntoIterator>>, /// G: 'a + GraphData, /// G::Vertex: FromGeometry + PartialEq, /// { /// let data = data.into_geometry(); /// input /// .into_iter() /// .find(|vertex| *vertex.get() == data) /// .map(|vertex| vertex.key()) /// } /// /// let mut graph = MeshGraph::>::from_raw_buffers( /// vec![NGon([0usize, 1, 2, 3]), NGon([4, 5, 6, 7])], /// vec![ /// (-2.0, 0.0), /// (-1.0, 0.0), // b /// (-1.0, 1.0), // a /// (-2.0, 1.0), /// (1.0, 0.0), // c /// (2.0, 0.0), /// (2.0, 1.0), /// (1.0, 1.0), // d /// ], /// ) /// .unwrap(); /// let a = find(graph.vertices(), (-1.0, 1.0)).unwrap(); /// let b = find(graph.vertices(), (-1.0, 0.0)).unwrap(); /// let c = find(graph.vertices(), (1.0, 0.0)).unwrap(); /// let d = find(graph.vertices(), (1.0, 1.0)).unwrap(); /// let face = graph /// .arc_mut(ArcKey::from((a, b))) /// .unwrap() /// .bridge(ArcKey::from((c, d))) /// .unwrap(); /// ``` pub fn bridge( self, destination: impl Into>, ) -> Result, GraphError> { let destination = destination.into().key_or_else(|index| { self.ring() .arcs() .nth(index) .ok_or(GraphError::TopologyNotFound) .map(|arc| arc.key()) })?; let cache = ArcBridgeCache::from_arc(self.to_ref(), destination)?; let (storage, _) = self.unbind(); Ok(Mutation::take(storage) .bypass_or_commit_with(|mutation| edge::bridge(mutation, cache)) .map(|(storage, face)| Bind::bind(storage, face).expect_consistent()) .map_err(|(_, error)| error) .expect_consistent()) } /// Extrudes the arc along its normal. /// /// The positions of each extruded vertex are translated along the arc's /// normal by the given offset. /// /// An arc's normal is perpendicular to the arc and also coplanar with the /// arc and one of its adjacent arcs. This is computed via a projection and /// supports both 2D and 3D geometries. /// /// Returns the extruded arc, which is in the same ring as the initiating /// arc. /// /// # Errors /// /// Returns an error if the arc is not a boundary arc. /// /// # Examples /// /// Extrude an exterior arc of a quadrilateral. /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// /// let mut graph = MeshGraph::>::from_raw_buffers_with_arity( /// vec![0usize, 1, 2, 3], /// vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)], /// 4, /// ) /// .unwrap(); /// let key = graph /// .arcs() /// .find(|arc| arc.is_boundary_arc()) /// .map(|arc| arc.key()) /// .unwrap(); /// graph /// .arc_mut(key) /// .unwrap() /// .extrude_with_offset(1.0) /// .unwrap(); /// ``` pub fn extrude_with_offset(self, offset: T) -> Result where T: Into>>, G: ArcNormal, G::Vertex: AsPositionMut, VertexPosition: EuclideanSpace, { let translation = self.normal() * offset.into(); self.extrude_with_translation(translation) } /// Extrudes the arc along a translation. /// /// The positions of each extruded vertex are translated by the given /// vector. /// /// Returns the extruded arc, which is in the same ring as the initiating /// arc. /// /// # Errors /// /// Returns an error if the arc is not a boundary arc. pub fn extrude_with_translation( self, translation: Vector>, ) -> Result where G::Vertex: AsPositionMut, VertexPosition: EuclideanSpace, { self.extrude_with(|data| data.map_position(|position| *position + translation)) } /// Extrudes the arc using the given vertex data. /// /// The data of each extruded vertex is determined by the given function, /// which maps the data of each source vertex into the data of the /// corresponding destination vertex. /// /// Returns the extruded arc, which is in the same ring as the initiating /// arc. /// /// # Errors /// /// Returns an error if the arc is not a boundary arc. pub fn extrude_with(self, f: F) -> Result where F: Fn(G::Vertex) -> G::Vertex, { let cache = ArcExtrudeCache::from_arc(self.to_ref())?; let (storage, _) = self.unbind(); Ok(Mutation::take(storage) .bypass_or_commit_with(|mutation| edge::extrude_with(mutation, cache, f)) .map(|(storage, arc)| Bind::bind(storage, arc).expect_consistent()) .map_err(|(_, error)| error) .expect_consistent()) } /// Removes the arc and its composite edge. /// /// Any and all dependent entities are also removed, such as connected /// faces, disjoint vertices, etc. /// /// Returns the source vertex of the initiating arc or `None` if that vertex /// becomes disjoint and is also removed. If an arc $\overrightarrow{AB}$ is /// removed and its source vertex is not disjoint, then $A$ is returned. pub fn remove(self) -> Option> { let a = self.source_vertex().key(); // This should never fail here. let cache = EdgeRemoveCache::from_arc(self.to_ref()).expect_consistent(); let (storage, _) = self.unbind(); Mutation::take(storage) .bypass_or_commit_with(|mutation| edge::remove(mutation, cache)) .map(|(storage, _)| Bind::bind(storage, a)) .map_err(|(_, error)| error) .expect_consistent() } } impl Borrow for ArcView where B: Reborrow, B::Target: AsStorage>> + Parametric, { fn borrow(&self) -> &ArcKey { self.inner.as_ref() } } impl Clone for ArcView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, View>: Clone, { fn clone(&self) -> Self { ArcView { inner: self.inner.clone(), } } } impl ClosedView for ArcView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Key = ArcKey; type Entity = Arc; /// Gets the key for the arc. fn key(&self) -> Self::Key { self.inner.key() } } impl Copy for ArcView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, View>: Copy, { } impl Deref for ArcView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Target = Arc; fn deref(&self) -> &Self::Target { self.inner.deref() } } impl DerefMut for ArcView where B: ReborrowMut, M: AsStorageMut> + Parametric, G: GraphData, { fn deref_mut(&mut self) -> &mut Self::Target { self.inner.deref_mut() } } impl Eq for ArcView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { } impl From> for ArcView where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { fn from(ring: Ring) -> Self { ring.into_arc() } } impl From>> for ArcView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn from(view: View>) -> Self { ArcView { inner: view } } } impl From> for View> where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn from(arc: ArcView) -> Self { let ArcView { inner, .. } = arc; inner } } impl Hash for ArcView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn hash(&self, state: &mut H) where H: Hasher, { self.inner.hash(state); } } impl PartialEq for ArcView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } impl ToArc for ArcView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { fn into_arc(self) -> ArcView { self } fn arc(&self) -> ArcView<&M> { self.to_ref() } } /// Orphan view of an arc entity. pub struct ArcOrphan<'a, G> where G: GraphData, { inner: Orphan<'a, Arc>, } impl ArcOrphan<'_, G> where G: GraphData, { pub fn get(&self) -> &G::Arc { self.inner.get() } pub fn get_mut(&mut self) -> &mut G::Arc { self.inner.get_mut() } } impl Borrow for ArcOrphan<'_, G> where G: GraphData, { fn borrow(&self) -> &ArcKey { self.inner.as_ref() } } impl ClosedView for ArcOrphan<'_, G> where G: GraphData, { type Key = ArcKey; type Entity = Arc; fn key(&self) -> Self::Key { self.inner.key() } } impl Eq for ArcOrphan<'_, G> where G: GraphData {} impl<'a, M> From> for ArcOrphan<'a, M::Data> where M: AsStorageMut> + Parametric, { fn from(arc: ArcView<&'a mut M>) -> Self { Orphan::from(arc.inner).into() } } impl<'a, G> From>> for ArcOrphan<'a, G> where G: GraphData, { fn from(inner: Orphan<'a, Arc>) -> Self { ArcOrphan { inner } } } impl<'a, M> From>> for ArcOrphan<'a, M::Data> where M: AsStorageMut> + Parametric, { fn from(view: View<&'a mut M, Arc>) -> Self { ArcOrphan { inner: view.into() } } } impl Hash for ArcOrphan<'_, G> where G: GraphData, { fn hash(&self, state: &mut H) where H: Hasher, { self.inner.hash(state); } } impl PartialEq for ArcOrphan<'_, G> where G: GraphData, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } /// Edge entity. #[derive(Derivative)] #[derivative(Debug, Hash)] pub struct Edge where G: GraphData, { /// User data. #[derivative(Debug = "ignore", Hash = "ignore")] pub(crate) data: G::Edge, /// Required key into the leading arc. pub(crate) arc: ArcKey, } impl Edge where G: GraphData, { pub fn new(arc: ArcKey, data: G::Edge) -> Self { Edge { data, arc } } } impl Entity for Edge where G: GraphData, { type Key = EdgeKey; type Storage = HashStorage; } impl Payload for Edge where G: GraphData, { type Data = G::Edge; fn get(&self) -> &Self::Data { &self.data } fn get_mut(&mut self) -> &mut Self::Data { &mut self.data } } /// Edge key. #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct EdgeKey(u64); impl Key for EdgeKey { type Inner = u64; fn from_inner(key: Self::Inner) -> Self { EdgeKey(key) } fn into_inner(self) -> Self::Inner { self.0 } } /// View of an edge entity. /// /// An edge connecting a vertex $A$ and a vertex $B$ is notated /// $\overleftrightarrow{AB}$ or $\overleftrightarrow{BA}$. /// /// See the [`graph`] module documentation for more information about views. /// /// [`graph`]: crate::graph pub struct EdgeView where B: Reborrow, B::Target: AsStorage::Data>> + Parametric, { inner: View::Data>>, } impl EdgeView where B: Reborrow, M: AsStorage>> + Parametric, { pub fn to_ref(&self) -> EdgeView<&M> { self.inner.to_ref().into() } } impl<'a, B, M, G> EdgeView where B: ReborrowInto<'a, Target = M>, M: AsStorage> + Parametric, G: GraphData, { pub fn into_ref(self) -> EdgeView<&'a M> { self.inner.into_ref().into() } } impl EdgeView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { pub fn get<'a>(&'a self) -> &'a G::Edge where G: 'a, { self.inner.get() } } impl EdgeView where B: ReborrowMut, M: AsStorageMut> + Parametric, G: GraphData, { pub fn get_mut<'a>(&'a mut self) -> &'a mut G::Edge where G: 'a, { self.inner.get_mut() } } /// Reachable API. impl EdgeView where B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { pub(in crate::graph) fn into_reachable_arc(self) -> Option> { let key = self.arc; self.rebind(key) } } impl EdgeView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_arc(self) -> ArcView { self.into_reachable_arc().expect_consistent() } pub fn arc(&self) -> ArcView<&M> { self.to_ref().into_arc() } pub fn is_boundary_edge(&self) -> bool { let arc = self.arc(); arc.is_boundary_arc() || arc.opposite_arc().is_boundary_arc() } } impl EdgeView where B: Reborrow, M: AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn midpoint(&self) -> VertexPosition where G: EdgeMidpoint, G::Vertex: AsPosition, { G::midpoint(self.to_ref()).expect_consistent() } } impl Borrow for EdgeView where B: Reborrow, B::Target: AsStorage>> + Parametric, { fn borrow(&self) -> &EdgeKey { self.inner.as_ref() } } impl Clone for EdgeView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, View>: Clone, { fn clone(&self) -> Self { EdgeView { inner: self.inner.clone(), } } } impl ClosedView for EdgeView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Key = EdgeKey; type Entity = Edge; /// Gets the key for the edge. fn key(&self) -> Self::Key { self.inner.key() } } impl Copy for EdgeView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, View>: Copy, { } impl Deref for EdgeView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Target = Edge; fn deref(&self) -> &Self::Target { self.inner.deref() } } impl DerefMut for EdgeView where B: ReborrowMut, M: AsStorageMut> + Parametric, G: GraphData, { fn deref_mut(&mut self) -> &mut Self::Target { self.inner.deref_mut() } } impl Eq for EdgeView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { } impl From>> for EdgeView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn from(view: View>) -> Self { EdgeView { inner: view } } } impl From> for View> where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn from(edge: EdgeView) -> Self { let EdgeView { inner, .. } = edge; inner } } impl Hash for EdgeView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn hash(&self, state: &mut H) where H: Hasher, { self.inner.hash(state); } } impl PartialEq for EdgeView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } impl ToArc for EdgeView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { fn into_arc(self) -> ArcView { EdgeView::into_arc(self) } fn arc(&self) -> ArcView<&M> { EdgeView::arc(self) } } /// Orphan view of an edge entity. pub struct EdgeOrphan<'a, G> where G: GraphData, { inner: Orphan<'a, Edge>, } impl EdgeOrphan<'_, G> where G: GraphData, { pub fn get(&self) -> &G::Edge { self.inner.get() } pub fn get_mut(&mut self) -> &mut G::Edge { self.inner.get_mut() } } impl Borrow for EdgeOrphan<'_, G> where G: GraphData, { fn borrow(&self) -> &EdgeKey { self.inner.as_ref() } } impl ClosedView for EdgeOrphan<'_, G> where G: GraphData, { type Key = EdgeKey; type Entity = Edge; fn key(&self) -> Self::Key { self.inner.key() } } impl Eq for EdgeOrphan<'_, G> where G: GraphData {} impl<'a, M> From> for EdgeOrphan<'a, M::Data> where M: AsStorageMut> + Parametric, { fn from(edge: EdgeView<&'a mut M>) -> Self { Orphan::from(edge.inner).into() } } impl<'a, G> From>> for EdgeOrphan<'a, G> where G: GraphData, { fn from(inner: Orphan<'a, Edge>) -> Self { EdgeOrphan { inner } } } impl<'a, M> From>> for EdgeOrphan<'a, M::Data> where M: AsStorageMut> + Parametric, { fn from(view: View<&'a mut M, Edge>) -> Self { EdgeOrphan { inner: view.into() } } } impl Hash for EdgeOrphan<'_, G> where G: GraphData, { fn hash(&self, state: &mut H) where H: Hasher, { self.inner.hash(state); } } impl PartialEq for EdgeOrphan<'_, G> where G: GraphData, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } pub struct VertexCirculator where B: Reborrow, B::Target: AsStorage>> + Parametric, { storage: B, inner: as IntoIterator>::IntoIter, } impl Circulator for VertexCirculator where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Entity = Vertex; fn next(&mut self) -> Option<::Key> { self.inner.next() } } impl From> for VertexCirculator where B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { fn from(arc: ArcView) -> Self { let (a, b) = arc.key().into(); let (storage, _) = arc.unbind(); VertexCirculator { storage, inner: ArrayVec::from([a, b]).into_iter(), } } } impl Clone for VertexCirculator where B: Clone + Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { fn clone(&self) -> Self { VertexCirculator { storage: self.storage.clone(), inner: self.inner.clone(), } } } impl ExactSizeIterator for VertexCirculator where Self: Iterator, B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { } impl<'a, M, G> Iterator for VertexCirculator<&'a M> where M: AsStorage> + Parametric, G: GraphData, { type Item = VertexView<&'a M>; fn next(&mut self) -> Option { self.bind_next_view() } fn size_hint(&self) -> (usize, Option) { (0, Some(self.inner.len())) } } impl<'a, M> Iterator for VertexCirculator<&'a mut M> where M: AsStorageMut> + Parametric, { type Item = VertexOrphan<'a, M::Data>; fn next(&mut self) -> Option { unsafe { self.bind_next_orphan() } } fn size_hint(&self) -> (usize, Option) { (0, Some(self.inner.len())) } } impl<'a, M> OrphanCirculator<'a, M> for VertexCirculator<&'a mut M> where M: AsStorageMut> + Parametric, { fn target(&mut self) -> &mut M { self.storage } } impl<'a, M> ViewCirculator<'a, M> for VertexCirculator<&'a M> where M: AsStorage> + Parametric, { fn target(&self) -> &'a M { self.storage } } pub struct FaceCirculator where B: Reborrow, B::Target: AsStorage>> + Parametric, { storage: B, inner: as IntoIterator>::IntoIter, } impl Circulator for FaceCirculator where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Entity = Face; fn next(&mut self) -> Option<::Key> { self.inner.next() } } impl Clone for FaceCirculator where B: Clone + Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn clone(&self) -> Self { FaceCirculator { storage: self.storage.clone(), inner: self.inner.clone(), } } } impl ExactSizeIterator for FaceCirculator where Self: Iterator, B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { } impl From> for FaceCirculator where B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { fn from(arc: ArcView) -> Self { let inner = arc .face .into_iter() .chain( arc.to_ref() .into_reachable_opposite_arc() .and_then(|opposite| opposite.face), ) .collect::>() .into_iter(); let (storage, _) = arc.unbind(); FaceCirculator { storage, inner } } } impl<'a, M, G> Iterator for FaceCirculator<&'a M> where M: AsStorage> + Parametric, G: GraphData, { type Item = FaceView<&'a M>; fn next(&mut self) -> Option { self.bind_next_view() } fn size_hint(&self) -> (usize, Option) { (0, Some(self.inner.len())) } } impl<'a, M> Iterator for FaceCirculator<&'a mut M> where M: AsStorageMut> + Parametric, { type Item = FaceOrphan<'a, M::Data>; fn next(&mut self) -> Option { unsafe { self.bind_next_orphan() } } fn size_hint(&self) -> (usize, Option) { (0, Some(self.inner.len())) } } impl<'a, M> OrphanCirculator<'a, M> for FaceCirculator<&'a mut M> where M: AsStorageMut> + Parametric, { fn target(&mut self) -> &mut M { self.storage } } impl<'a, M> ViewCirculator<'a, M> for FaceCirculator<&'a M> where M: AsStorage> + Parametric, { fn target(&self) -> &'a M { self.storage } } #[cfg(test)] mod tests { use decorum::R64; use nalgebra::{Point2, Point3}; use crate::geometry::FromGeometry; use crate::graph::{ArcKey, GraphData, MeshGraph}; use crate::index::HashIndexer; use crate::prelude::*; use crate::primitive::cube::Cube; use crate::primitive::generate::Position; use crate::primitive::Tetragon; type E2 = Point2; type E3 = Point3; fn find_arc(graph: &MeshGraph, data: (T, T)) -> Option where G: GraphData, G::Vertex: FromGeometry + PartialEq, { let (source, destination) = data; let source = source.into_geometry(); let destination = destination.into_geometry(); graph .vertices() .filter(|vertex| vertex.data == source) .flat_map(|source| { source .adjacent_vertices() .find(|vertex| vertex.data == destination) .map(|destination| (source.key(), destination.key())) }) .map(|(a, b)| (a, b).into()) .next() } #[test] fn extrude_arc() { let mut graph = MeshGraph::::from_raw_buffers_with_arity( vec![0u32, 1, 2, 3], vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)], 4, ) .unwrap(); let source = find_arc(&graph, ((1.0, 1.0), (1.0, 0.0))).unwrap(); graph .arc_mut(source) .unwrap() .extrude_with_offset(R64::assert(1.0)) .unwrap(); assert_eq!(14, graph.arc_count()); assert_eq!(2, graph.face_count()); } #[test] fn bridge_arcs() { // Construct a mesh with two disjoint quadrilaterals. let mut graph = MeshGraph::::from_raw_buffers_with_arity( vec![0u32, 1, 2, 3, 4, 5, 6, 7], vec![ (-2.0, 0.0, 0.0), (-1.0, 0.0, 0.0), // 1 (-1.0, 1.0, 0.0), // 2 (-2.0, 1.0, 0.0), (1.0, 0.0, 0.0), // 4 (2.0, 0.0, 0.0), (2.0, 1.0, 0.0), (1.0, 1.0, 0.0), // 7 ], 4, ) .unwrap(); let source = find_arc(&graph, ((-1.0, 1.0, 0.0), (-1.0, 0.0, 0.0))).unwrap(); let destination = find_arc(&graph, ((1.0, 0.0, 0.0), (1.0, 1.0, 0.0))).unwrap(); graph.arc_mut(source).unwrap().bridge(destination).unwrap(); assert_eq!(20, graph.arc_count()); assert_eq!(3, graph.face_count()); } #[test] fn split_edge() { let (indices, vertices) = Cube::new() .polygons::>() // 6 quadrilaterals, 24 vertices. .index_vertices::, _>(HashIndexer::default()); let mut graph = MeshGraph::::from_raw_buffers(indices, vertices).unwrap(); let key = graph.arcs().next().unwrap().key(); let vertex = graph.arc_mut(key).unwrap().split_at_midpoint().into_ref(); assert_eq!(5, vertex.into_outgoing_arc().into_face().unwrap().arity()); assert_eq!( 5, vertex .into_outgoing_arc() .into_opposite_arc() .into_face() .unwrap() .arity() ); } #[test] fn remove_edge() { // Construct a graph with two connected quadrilaterals. let mut graph = MeshGraph::::from_raw_buffers_with_arity( vec![0u32, 1, 2, 3, 0, 3, 4, 5], vec![ (0.0, 0.0), // 0 (1.0, 0.0), // 1 (1.0, 1.0), // 2 (0.0, 1.0), // 3 (-1.0, 1.0), // 4 (-1.0, 0.0), // 5 ], 4, ) .unwrap(); // The graph should begin with 2 faces. assert_eq!(2, graph.face_count()); // Remove the edge joining the quadrilaterals from the graph. let ab = find_arc(&graph, ((0.0, 0.0), (0.0, 1.0))).unwrap(); { let vertex = graph.arc_mut(ab).unwrap().remove().unwrap().into_ref(); // The ring should be formed from 6 edges. assert_eq!(6, vertex.into_outgoing_arc().into_ring().arity()); } // After the removal, the graph should have no faces. assert_eq!(0, graph.face_count()); } } ================================================ FILE: plexus/src/graph/face.rs ================================================ use derivative::Derivative; use smallvec::SmallVec; use std::borrow::Borrow; use std::cmp; use std::collections::HashSet; use std::hash::{Hash, Hasher}; use std::ops::{Deref, DerefMut}; use theon::query::{Intersection, Line, Plane}; use theon::space::{EuclideanSpace, FiniteDimensional, Scalar, Vector}; use theon::{AsPosition, AsPositionMut}; use typenum::U3; use crate::entity::borrow::{Reborrow, ReborrowInto, ReborrowMut}; use crate::entity::storage::prelude::*; use crate::entity::storage::{ AsStorage, AsStorageMut, AsStorageOf, HashStorage, IncrementalKeyer, Key, }; use crate::entity::traverse::{Adjacency, Breadth, Depth, Trace, TraceFirst, Traversal}; use crate::entity::view::{Bind, ClosedView, Orphan, Rebind, Unbind, View}; use crate::entity::{Entity, Payload}; use crate::graph::data::{Data, GraphData, Parametric}; use crate::graph::edge::{Arc, ArcKey, ArcOrphan, ArcView, Edge}; use crate::graph::geometry::{FaceCentroid, FaceNormal, FacePlane, VertexPosition}; use crate::graph::mutation::face::{ self, FaceBridgeCache, FaceExtrudeCache, FaceInsertCache, FacePokeCache, FaceRemoveCache, FaceSplitCache, }; use crate::graph::mutation::{self, Consistent, Immediate, Mutable}; use crate::graph::path::Path; use crate::graph::vertex::{Vertex, VertexKey, VertexOrphan, VertexView}; use crate::graph::{ Circulator, GraphError, MeshGraph, OptionExt as _, OrphanCirculator, ResultExt as _, Selector, ViewCirculator, }; use crate::transact::{BypassOrCommit, Mutate}; use crate::{DynamicArity, IteratorExt as _, StaticArity}; type Mutation = mutation::Mutation>; pub trait ToRing: DynamicArity + Sized where B: Reborrow, B::Target: AsStorage>> + Consistent + Parametric, { fn into_ring(self) -> Ring; fn ring(&self) -> Ring<&B::Target>; } /// Face entity. #[derive(Derivative)] #[derivative(Debug, Hash)] pub struct Face where G: GraphData, { /// User data. #[derivative(Debug = "ignore", Hash = "ignore")] pub(crate) data: G::Face, /// Required key into the leading arc. pub(crate) arc: ArcKey, } impl Face where G: GraphData, { pub fn new(arc: ArcKey, data: G::Face) -> Self { Face { data, arc } } } impl Entity for Face where G: GraphData, { type Key = FaceKey; type Storage = HashStorage; } impl Payload for Face where G: GraphData, { type Data = G::Face; fn get(&self) -> &Self::Data { &self.data } fn get_mut(&mut self) -> &mut Self::Data { &mut self.data } } /// Face key. #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct FaceKey(u64); impl Key for FaceKey { type Inner = u64; fn from_inner(key: Self::Inner) -> Self { FaceKey(key) } fn into_inner(self) -> Self::Inner { self.0 } } /// View of a face entity. /// /// Faces are notated by the path of their associated ring. A triangular face /// with a perimeter formed by vertices $A$, $B$, and $C$ is notated /// $\overrightarrow{\\{A,B,C\\}}$. While the precise ordering of vertices is /// determined by a face's leading arc, the same face may be notated using /// rotations of this set, such as $\overrightarrow{\\{B,C,A\\}}$. /// /// See the [`graph`] module documentation for more information about views. /// /// [`MeshGraph`]: crate::graph::MeshGraph /// [`graph`]: crate::graph pub struct FaceView where B: Reborrow, B::Target: AsStorage>> + Parametric, { inner: View>>, } impl FaceView where B: Reborrow, M: AsStorage>> + Parametric, { pub fn to_ref(&self) -> FaceView<&M> { self.inner.to_ref().into() } } impl FaceView where B: ReborrowMut, M: AsStorage>> + Parametric, { // LINT: This convention is specifically used for interior reborrows and is consistent with // `to_ref`. #[expect(clippy::wrong_self_convention)] fn to_mut_unchecked(&mut self) -> FaceView<&mut M> { self.inner.to_mut_unchecked().into() } } impl<'a, B, M, G> FaceView where B: ReborrowInto<'a, Target = M>, M: AsStorage> + Parametric, G: GraphData, { // TODO: Relocate this documentation of `into_ref`. /// # Examples /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// type E3 = Point3; /// /// let mut graph: MeshGraph = Cube::new().polygons::>().collect(); /// let key = graph.faces().next().unwrap().key(); /// let face = graph /// .face_mut(key) /// .unwrap() /// .extrude_with_offset(R64::assert(1.0)) /// .unwrap() /// .into_ref(); /// /// // This would not be possible without conversion into an immutable view. /// let _ = face.into_arc(); /// let _ = face.into_arc().into_next_arc(); /// ``` pub fn into_ref(self) -> FaceView<&'a M> { self.inner.into_ref().into() } } impl FaceView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { pub fn get<'a>(&'a self) -> &'a G::Face where G: 'a, { self.inner.get() } } impl FaceView where B: ReborrowMut, M: AsStorageMut> + Parametric, G: GraphData, { pub fn get_mut<'a>(&'a mut self) -> &'a mut G::Face where G: 'a, { self.inner.get_mut() } } /// Reachable API. impl FaceView where B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { pub(in crate::graph) fn into_reachable_arc(self) -> Option> { let key = self.arc; self.rebind(key) } } impl FaceView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Converts the face into its ring. pub fn into_ring(self) -> Ring { self.into_arc().into_ring() } /// Converts the face into its leading arc. pub fn into_arc(self) -> ArcView { self.into_reachable_arc().expect_consistent() } /// Gets the ring of the face. pub fn ring(&self) -> Ring<&M> { self.to_ref().into_ring() } /// Gets the leading arc of the face. pub fn arc(&self) -> ArcView<&M> { self.to_ref().into_arc() } } impl FaceView where B: Reborrow, M: AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn centroid(&self) -> VertexPosition where G: FaceCentroid, G::Vertex: AsPosition, { G::centroid(self.to_ref()).expect_consistent() } pub fn normal(&self) -> Result>, GraphError> where G: FaceNormal, G::Vertex: AsPosition, { G::normal(self.to_ref()) } pub fn plane(&self) -> Result>, GraphError> where G: FacePlane, G::Vertex: AsPosition, VertexPosition: FiniteDimensional, { G::plane(self.to_ref()) } } impl FaceView where B: ReborrowMut, M: AsStorage> + AsStorage> + AsStorageMut> + Consistent + Parametric, G: GraphData, { /// Flattens the face by translating the positions of all vertices into a /// best-fit plane. /// /// # Errors /// /// Returns an error if a best-fit plane could not be computed or positions /// could not be translated into the plane. pub fn flatten(&mut self) -> Result<(), GraphError> where G: FacePlane, G::Vertex: AsPositionMut, VertexPosition: EuclideanSpace + FiniteDimensional, { if self.arity() == 3 { return Ok(()); } let plane = self.plane()?; for mut vertex in self.adjacent_vertex_orphans() { let position = *vertex.position(); let line = Line::> { origin: position, direction: plane.normal, }; let distance = line .intersection(&plane) .expect("no line-plane intersection along normal") .into_time_of_impact() .expect("normal is parallel to plane"); let translation = *line.direction.get() * distance; *vertex.get_mut().as_position_mut() = position + translation; } Ok(()) } } impl<'a, B, M, G> FaceView where B: ReborrowInto<'a, Target = M>, M: 'a + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_adjacent_arcs(self) -> impl Clone + Iterator> { self.into_ref().into_ring().into_arcs() } pub fn into_adjacent_faces(self) -> impl Clone + Iterator> { FaceCirculator::from(ArcCirculator::from(self.into_ref().into_ring())) } } impl FaceView where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Gets an iterator of views over the arcs in the face's ring. pub fn adjacent_arcs(&self) -> impl Clone + Iterator> { self.to_ref().into_adjacent_arcs() } /// Gets an iterator of views over adjacent faces. pub fn adjacent_faces(&self) -> impl Clone + Iterator> { self.to_ref().into_adjacent_faces() } } impl<'a, B, M, G> FaceView where B: ReborrowInto<'a, Target = M>, M: 'a + AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_adjacent_vertices(self) -> impl Clone + Iterator> { self.into_ref().into_ring().into_vertices() } } impl FaceView where B: Reborrow, B::Target: AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Gets an iterator of views over the vertices that form the face. pub fn adjacent_vertices(&self) -> impl Clone + Iterator> { self.to_ref().into_adjacent_vertices() } } impl<'a, M> FaceView<&'a mut M> where M: AsStorageMut> + AsStorage> + Consistent + Parametric, { pub fn into_adjacent_arc_orphans(self) -> impl Iterator> { self.into_ring().into_arc_orphans() } } impl FaceView where B: ReborrowMut, B::Target: AsStorageMut>> + AsStorage>> + Consistent + Parametric, { /// Gets an iterator of orphan views over the arcs in the face's ring. pub fn adjacent_arc_orphans(&mut self) -> impl Iterator>> { self.to_mut_unchecked().into_adjacent_arc_orphans() } } impl<'a, M> FaceView<&'a mut M> where M: AsStorage> + AsStorageMut> + Consistent + Parametric, { pub fn into_adjacent_face_orphans(self) -> impl Iterator> { FaceCirculator::from(ArcCirculator::from(self.into_ring())) } } impl FaceView where B: ReborrowMut, B::Target: AsStorage>> + AsStorageMut>> + Consistent + Parametric, { /// Gets an iterator of orphan views over adjacent faces. pub fn adjacent_face_orphans(&mut self) -> impl Iterator>> { self.to_mut_unchecked().into_adjacent_face_orphans() } } impl<'a, M> FaceView<&'a mut M> where M: AsStorage>> + AsStorage>> + AsStorageMut>> + Consistent + Parametric, { pub fn into_adjacent_vertex_orphans(self) -> impl Iterator>> { VertexCirculator::from(ArcCirculator::from(self.into_ring())) } } impl FaceView where B: ReborrowMut, B::Target: AsStorage>> + AsStorage>> + AsStorageMut>> + Consistent + Parametric, { /// Gets an iterator of orphan views over the vertices that form the face. pub fn adjacent_vertex_orphans(&mut self) -> impl Iterator>> { self.to_mut_unchecked().into_adjacent_vertex_orphans() } } impl FaceView where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Gets an iterator that traverses adjacent faces by breadth. /// /// The traversal moves from the face to its adjacent faces and so on. If /// there are disjoint sub-graphs in the graph, then a traversal will not /// reach every face in the graph. pub fn traverse_by_breadth(&self) -> impl Clone + Iterator> { Traversal::<_, _, Breadth>::from(self.to_ref()) } /// Gets an iterator that traverses adjacent faces by depth. /// /// The traversal moves from the face to its adjacent faces and so on. If /// there are disjoint sub-graphs in the graph, then a traversal will not /// reach every face in the graph. pub fn traverse_by_depth(&self) -> impl Clone + Iterator> { Traversal::<_, _, Depth>::from(self.to_ref()) } } impl<'a, M, G> FaceView<&'a mut M> where M: AsStorage> + AsStorage> + AsStorage> + AsStorage> + Default + Mutable, G: GraphData, { /// Splits the face by bisecting it with a composite edge inserted between /// two non-adjacent vertices within the face's perimeter. /// /// The vertices can be chosen by key or index, where index selects the /// $n^\text{th}$ vertex within the face's ring. /// /// Returns the arc inserted from the source vertex to the destination /// vertex. If a face $\overrightarrow{\\{A,B, C,D\\}}$ is split from $A$ to /// $C$, then it will be decomposed into faces in the rings /// $\overrightarrow{\\{A,B,C\\}}$ and $\overrightarrow{\\{C,D,A\\}}$ and /// the arc $\overrightarrow{AC}$ will be returned. /// /// # Errors /// /// Returns an error if either of the given vertices cannot be found, are /// not within the face's perimeter, or the distance between the vertices /// along the ring is less than two. /// /// # Examples /// /// Splitting a quadrilateral face: /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::Tetragon; /// /// let mut graph = MeshGraph::>::from_raw_buffers( /// vec![Tetragon::new(0usize, 1, 2, 3)], /// vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)], /// ) /// .unwrap(); /// let key = graph.faces().next().unwrap().key(); /// let arc = graph /// .face_mut(key) /// .unwrap() /// .split(ByIndex(0), ByIndex(2)) /// .unwrap() /// .into_ref(); /// ``` pub fn split( self, source: impl Into>, destination: impl Into>, ) -> Result, GraphError> { let key_at_index = |index| { self.adjacent_vertices() .nth(index) .ok_or(GraphError::TopologyNotFound) .map(|vertex| vertex.key()) }; let source = source.into().key_or_else(key_at_index)?; let destination = destination.into().key_or_else(key_at_index)?; let cache = FaceSplitCache::from_face(self.to_ref(), source, destination)?; let (storage, _) = self.unbind(); Ok(Mutation::take(storage) .bypass_or_commit_with(|mutation| face::split(mutation, cache)) .map(|(storage, arc)| Bind::bind(storage, arc).expect_consistent()) .map_err(|(_, error)| error) .expect_consistent()) } /// Merges the face into an adjacent face over a shared edge. /// /// The adjacent face can be chosen by key or index, where index selects /// the $n^\text{th}$ adjacent face. /// /// Returns the merged face. /// /// # Errors /// /// Returns an error if the destination face cannot be found or is not /// adjacent to the initiating face. /// /// # Examples /// /// Merging two adjacent quadrilateral faces: /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::Tetragon; /// /// let mut graph = MeshGraph::>::from_raw_buffers( /// vec![Tetragon::new(0usize, 1, 2, 3), Tetragon::new(0, 3, 4, 5)], /// vec![ /// (0.0, 0.0), // 0 /// (1.0, 0.0), // 1 /// (1.0, 1.0), // 2 /// (0.0, 1.0), // 3 /// (-1.0, 1.0), // 4 /// (-1.0, 0.0), // 5 /// ], /// ) /// .unwrap(); /// /// let key = graph.faces().next().unwrap().key(); /// let face = graph /// .face_mut(key) /// .unwrap() /// .merge(ByIndex(0)) /// .unwrap() /// .into_ref(); /// ``` pub fn merge(self, destination: impl Into>) -> Result { let destination = destination.into().key_or_else(|index| { self.adjacent_faces() .nth(index) .ok_or(GraphError::TopologyNotFound) .map(|face| face.key()) })?; let ab = self .adjacent_arcs() .find(|arc| match arc.opposite_arc().face() { Some(face) => face.key() == destination, _ => false, }) .map(|arc| arc.key()) .ok_or(GraphError::TopologyNotFound)?; // TODO: `Clone` should not be needed here. Consolidate this using the // mutation API and move the necessary face data instead of // cloning it. let data = self.get().clone(); let arc: ArcView<_> = self.rebind(ab).expect_consistent(); Ok(arc .remove() // Removing an edge between faces must yield a vertex. .expect_consistent() .into_outgoing_arc() .into_ring() .get_or_insert_face_with(|| data)) } /// Connects faces with equal arity with faces inserted along their /// perimeters. /// /// The inserted faces are always quadrilateral. Both the initiating face /// and destination face are removed. /// /// # Errors /// /// Returns an error if the destination face cannot be found or the arity of /// the face and its destination are not the same. pub fn bridge(self, destination: FaceKey) -> Result<(), GraphError> { let cache = FaceBridgeCache::from_face(self.to_ref(), destination)?; let (storage, _) = self.unbind(); Mutation::take(storage) .bypass_or_commit_with(|mutation| face::bridge(mutation, cache)) .map_err(|(_, error)| error) .expect_consistent(); Ok(()) } /// Decomposes the face into triangles. Does nothing if the face is /// triangular. /// /// Returns the terminating face of the decomposition. // LINT: This function has significant side effects and can be meaningfully called for those // side effects alone. #[expect(clippy::return_self_not_must_use)] pub fn triangulate(self) -> Self { // TODO: This naive approach exhibits bad behaviors when faces are // concave, linear, collapsed, or are otherwise degenerate. // Additionally, splitting may fail under certain conditions! // Triangulation that ignores geometry is likely much less useful // than a triangulation algorithm that considers position data. // TODO: This implementation differs from `MeshGraph::triangulate`, // because it is not possible to retry `split` in this context. let mut face = self; while face.arity() > 3 { face = face .split(0, 2) .expect_consistent() // TODO: This may panic! .into_face() .expect_consistent(); } face } /// Subdivides the face about a vertex. A triangle fan is formed from each /// arc in the face's perimeter and the vertex. /// /// Poking inserts a new vertex with data provided by the given function. /// /// Returns the inserted vertex. /// /// # Examples /// /// Forming a pyramid from a triangular face: /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point3; /// use plexus::geometry::{AsPosition, AsPositionMut}; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::Trigon; /// /// let mut graph = MeshGraph::>::from_raw_buffers( /// vec![Trigon::new(0usize, 1, 2)], /// vec![(-1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 2.0, 0.0)], /// ) /// .unwrap(); /// let key = graph.faces().next().unwrap().key(); /// let mut face = graph.face_mut(key).unwrap(); /// /// // See also `poke_with_offset`, which provides this functionality. /// let position = face.centroid() + face.normal().unwrap(); /// face.poke_with(move || position); /// ``` pub fn poke_with(self, f: F) -> VertexView<&'a mut M> where F: FnOnce() -> G::Vertex, { // This should never fail here. let cache = FacePokeCache::from_face(self.to_ref()).expect_consistent(); let (storage, _) = self.unbind(); Mutation::take(storage) .bypass_or_commit_with(|mutation| face::poke_with(mutation, cache, f)) .map(|(storage, vertex)| Bind::bind(storage, vertex).expect_consistent()) .map_err(|(_, error)| error) .expect_consistent() } /// Subdivides the face about its centroid. A triangle fan is formed from /// each arc in the face's perimeter and a vertex inserted at the centroid. /// /// Returns the inserted vertex. pub fn poke_at_centroid(self) -> VertexView<&'a mut M> where G: FaceCentroid, G::Vertex: AsPositionMut, { let mut data = self.arc().source_vertex().get().clone(); let centroid = self.centroid(); self.poke_with(move || { *data.as_position_mut() = centroid; data }) } /// Subdivides the face about its centroid. A triangle fan is formed from /// each arc in the face's perimeter and a vertex inserted at the centroid. /// The inserted vertex is then translated along the initiating face's /// normal by the given offset. /// /// Returns the inserted vertex. /// /// # Errors /// /// Returns an error if the geometry could not be computed. /// /// # Examples /// /// Constructing a "spikey" sphere: /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::generate::Position; /// use plexus::primitive::sphere::UvSphere; /// /// type E3 = Point3; /// /// let mut graph: MeshGraph = UvSphere::new(16, 8).polygons::>().collect(); /// let keys = graph.faces().map(|face| face.key()).collect::>(); /// for key in keys { /// graph /// .face_mut(key) /// .unwrap() /// .poke_with_offset(R64::assert(0.5)) /// .unwrap(); /// } /// ``` pub fn poke_with_offset(self, offset: T) -> Result, GraphError> where T: Into>>, G: FaceCentroid + FaceNormal, G::Vertex: AsPositionMut, VertexPosition: EuclideanSpace, { let mut data = self.arc().source_vertex().get().clone(); let position = self.centroid() + (self.normal()? * offset.into()); Ok(self.poke_with(move || { *data.as_position_mut() = position; data })) } /// Extrudes the face along its normal. /// /// Returns the extruded face. /// /// # Errors /// /// Returns an error if the geometry could not be computed. pub fn extrude_with_offset(self, offset: T) -> Result where T: Into>>, G: FaceNormal, G::Vertex: AsPositionMut, VertexPosition: EuclideanSpace, { let translation = self.normal()? * offset.into(); Ok(self.extrude_with_translation(translation)) } /// Extrudes the face along a translation. /// /// Returns the extruded face. // LINT: This function has significant side effects and can be meaningfully called for those // side effects alone. #[expect(clippy::return_self_not_must_use)] pub fn extrude_with_translation(self, translation: Vector>) -> Self where G::Vertex: AsPositionMut, VertexPosition: EuclideanSpace, { self.extrude_with(|data| { data.clone() .map_position(|position| *position + translation) }) } /// Extrudes a face using the given vertex data. /// /// Returns the extruded face. // LINT: This function has significant side effects and can be meaningfully called for those // side effects alone. #[expect(clippy::return_self_not_must_use)] pub fn extrude_with(self, f: F) -> Self where F: Fn(&G::Vertex) -> G::Vertex, { // This should never fail here. let cache = FaceExtrudeCache::from_face(self.to_ref()).expect_consistent(); let (storage, _) = self.unbind(); Mutation::take(storage) .bypass_or_commit_with(|mutation| face::extrude_with(mutation, cache, f)) .map(|(storage, face)| Bind::bind(storage, face).expect_consistent()) .map_err(|(_, error)| error) .expect_consistent() } /// Removes the face. /// /// Returns the remaining ring of the face if it is not entirely disjoint, otherwise `None`. pub fn remove(self) -> Option> { // This should never fail here. let cache = FaceRemoveCache::from_face(self.to_ref()).expect_consistent(); let (storage, _) = self.unbind(); Mutation::take(storage) .bypass_or_commit_with(|mutation| face::remove(mutation, cache)) .map(|(storage, face)| ArcView::bind(storage, face.arc)) .map_err(|(_, error)| error) .expect_consistent() .map(|arc| arc.into_ring()) } } impl Adjacency for FaceView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { type Output = SmallVec<[Self::Key; 8]>; fn adjacency(&self) -> Self::Output { self.adjacent_faces().keys().collect() } } impl Borrow for FaceView where B: Reborrow, B::Target: AsStorage>> + Parametric, { fn borrow(&self) -> &FaceKey { self.inner.as_ref() } } impl Clone for FaceView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, View>: Clone, { fn clone(&self) -> Self { FaceView { inner: self.inner.clone(), } } } impl ClosedView for FaceView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Key = FaceKey; type Entity = Face; /// Gets the key for the face. fn key(&self) -> Self::Key { self.inner.key() } } impl Copy for FaceView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, View>: Copy, { } impl Deref for FaceView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Target = Face; fn deref(&self) -> &Self::Target { self.inner.deref() } } impl DerefMut for FaceView where B: ReborrowMut, M: AsStorageMut> + Parametric, G: GraphData, { fn deref_mut(&mut self) -> &mut Self::Target { self.inner.deref_mut() } } impl DynamicArity for FaceView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { type Dynamic = usize; /// Gets the arity of the face. This is the number of arcs that form the /// face's ring. fn arity(&self) -> Self::Dynamic { self.adjacent_arcs().count() } } impl Eq for FaceView where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { } impl From>> for FaceView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn from(view: View>) -> Self { FaceView { inner: view } } } impl From> for View> where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn from(face: FaceView) -> Self { let FaceView { inner, .. } = face; inner } } impl Hash for FaceView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn hash(&self, state: &mut H) where H: Hasher, { self.inner.hash(state); } } impl PartialEq for FaceView where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } impl StaticArity for FaceView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Static = as StaticArity>::Static; const ARITY: Self::Static = MeshGraph::::ARITY; } impl ToRing for FaceView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { fn into_ring(self) -> Ring { FaceView::into_ring(self) } fn ring(&self) -> Ring<&M> { FaceView::ring(self) } } /// Orphan view of a face entity. pub struct FaceOrphan<'a, G> where G: GraphData, { inner: Orphan<'a, Face>, } impl FaceOrphan<'_, G> where G: GraphData, { pub fn get(&self) -> &G::Face { self.inner.get() } pub fn get_mut(&mut self) -> &mut G::Face { self.inner.get_mut() } } impl ClosedView for FaceOrphan<'_, G> where G: GraphData, { type Key = FaceKey; type Entity = Face; fn key(&self) -> Self::Key { self.inner.key() } } impl Eq for FaceOrphan<'_, G> where G: GraphData {} impl<'a, M> From> for FaceOrphan<'a, M::Data> where M: AsStorageMut> + Parametric, { fn from(face: FaceView<&'a mut M>) -> Self { Orphan::from(face.inner).into() } } impl<'a, G> From>> for FaceOrphan<'a, G> where G: GraphData, { fn from(inner: Orphan<'a, Face>) -> Self { FaceOrphan { inner } } } impl<'a, M> From>> for FaceOrphan<'a, M::Data> where M: AsStorageMut> + Parametric, { fn from(view: View<&'a mut M, Face>) -> Self { FaceOrphan { inner: view.into() } } } impl PartialEq for FaceOrphan<'_, G> where G: GraphData, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } /// Closed path formed by adjacent arcs. /// /// Rings are closed paths formed by arcs and their immediately adjacent arcs. /// In a consistent graph, every arc forms such a path. Such paths may or may /// not be occupied by faces. /// /// Rings are notated by their path. A ring with a perimeter formed by vertices /// $A$, $B$, and $C$ is notated $\overrightarrow{\\{A,B,C\\}}$. Note that /// rotations of the set of vertices are equivalent, such as /// $\overrightarrow{\\{B,C,A\\}}$. pub struct Ring where B: Reborrow, B::Target: AsStorage>> + Consistent + Parametric, { arc: ArcView, } impl Ring where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { pub fn to_ref(&self) -> Ring<&M> { self.arc.to_ref().into_ring() } } impl Ring where B: ReborrowMut, M: AsStorage> + Consistent + Parametric, G: GraphData, { // LINT: This convention is specifically used for interior reborrows and is consistent with // `to_ref`. #[expect(clippy::wrong_self_convention)] fn to_mut_unchecked(&mut self) -> Ring<&mut M> { self.arc.to_mut_unchecked().into_ring() } } impl<'a, B, M, G> Ring where B: ReborrowInto<'a, Target = M>, M: AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_ref(self) -> Ring<&'a M> { self.arc.into_ref().into_ring() } } impl Ring where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { /// Converts the ring into its leading arc. pub fn into_arc(self) -> ArcView { self.arc } /// Gets the leading arc of the ring. pub fn arc(&self) -> ArcView<&M> { self.arc.to_ref() } } impl Ring where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_path(self) -> Path<'static, B> { self.into() } pub fn path(&self) -> Path<'static, &M> { self.to_ref().into_path() } /// Gets the shortest logical metric between vertices within the ring. /// /// The _logical metric_ assigns the unit weight (one) to every arc and /// effectively counts the number of arcs between vertices. In a closed path /// like a ring, there are two sub-paths between any two vertices. This /// function computes the metric of the shortest sub-path. This may be the /// null path with a zero metric. pub fn shortest_logical_metric( &self, from: impl Into>, to: impl Into>, ) -> Result { let arity = self.arity(); let index = |selector: Selector<_>| match selector { Selector::ByKey(key) => self .vertices() .keys() .enumerate() .find(|(_, a)| *a == key) .map(|(index, _)| index) .ok_or(GraphError::TopologyNotFound), Selector::ByIndex(index) => { if index >= arity { Err(GraphError::TopologyNotFound) } else { Ok(index) } } }; let from = index(from.into())?; let to = index(to.into())?; let metric = cmp::max(from, to) - cmp::min(from, to); Ok(cmp::min(metric, arity - metric)) } } impl Ring where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Converts the ring into its face. /// /// If the path has no associated face, then `None` is returned. pub fn into_face(self) -> Option> { let arc = self.into_arc(); let key = arc.face; key.map(|key| arc.rebind(key).expect_consistent()) } /// Gets the face of the ring. /// /// If the path has no associated face, then `None` is returned. pub fn face(&self) -> Option> { self.to_ref().into_face() } } impl<'a, B, M, G> Ring where B: ReborrowInto<'a, Target = M>, M: 'a + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_arcs(self) -> impl Clone + Iterator> { ArcCirculator::from(self.into_ref()) } } impl Ring where B: Reborrow, B::Target: AsStorage> + Consistent + Parametric, G: GraphData, { /// Gets an iterator of views over the arcs within the ring. pub fn arcs(&self) -> impl Clone + Iterator> { self.to_ref().into_arcs() } } impl<'a, B, M, G> Ring where B: ReborrowInto<'a, Target = M>, M: 'a + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_vertices(self) -> impl Clone + Iterator> { VertexCirculator::from(ArcCirculator::from(self.into_ref())) } } impl Ring where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Gets an iterator of views over the vertices within the ring. pub fn vertices(&self) -> impl Clone + Iterator> { self.to_ref().into_vertices() } } impl<'a, M> Ring<&'a mut M> where M: AsStorageMut> + Consistent + Parametric, { pub fn into_arc_orphans(self) -> impl Iterator> { ArcCirculator::from(self) } } impl Ring where B: ReborrowMut, B::Target: AsStorageMut>> + Consistent + Parametric, { /// Gets an iterator of orphan views over the arcs in the ring. pub fn arc_orphans(&mut self) -> impl Iterator>> { self.to_mut_unchecked().into_arc_orphans() } } impl<'a, M> Ring<&'a mut M> where M: AsStorage> + AsStorageMut> + Consistent + Parametric, { pub fn into_vertex_orphans(self) -> impl Iterator> { VertexCirculator::from(ArcCirculator::from(self)) } } impl Ring where B: ReborrowMut, B::Target: AsStorage>> + AsStorageMut>> + Consistent + Parametric, { /// Gets an iterator of views over the vertices within the ring. pub fn vertex_orphans(&mut self) -> impl Iterator>> { self.to_mut_unchecked().into_vertex_orphans() } } impl<'a, M, G> Ring<&'a mut M> where M: AsStorage> + AsStorage> + AsStorage> + Default + Mutable, G: GraphData, { /// Gets the face of the ring or inserts a face if one does not already /// exist. /// /// Returns the existing or inserted face. pub fn get_or_insert_face(self) -> FaceView<&'a mut M> { self.get_or_insert_face_with(Default::default) } /// Gets the face of the ring or inserts a face if one does not already /// exist. /// /// If a face is inserted, then the given function is used to get the data /// for the face. /// /// Returns the existing or inserted face. pub fn get_or_insert_face_with(self, f: F) -> FaceView<&'a mut M> where F: FnOnce() -> G::Face, { let key = self.arc.face; if let Some(key) = key { self.arc.rebind(key).expect_consistent() } else { // This should never fail here. let cache = FaceInsertCache::from_ring(self.to_ref()).expect_consistent(); let (storage, _) = self.arc.unbind(); Mutation::take(storage) .bypass_or_commit_with(|mutation| { face::insert_with(mutation, cache, || (Default::default(), f())) }) .map(|(storage, face)| Bind::bind(storage, face).expect_consistent()) .map_err(|(_, error)| error) .expect_consistent() } } } impl DynamicArity for Ring where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { type Dynamic = usize; /// Gets the arity of the ring. This is the number of arcs that form the /// path. fn arity(&self) -> Self::Dynamic { self.arcs().count() } } impl From> for Ring where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { fn from(arc: ArcView) -> Self { Ring { arc } } } impl PartialEq for Ring where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { fn eq(&self, other: &Self) -> bool { let keys = |ring: &Self| ring.arcs().keys().collect::>(); keys(self) == keys(other) } } impl StaticArity for Ring where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { type Static = as StaticArity>::Static; const ARITY: Self::Static = MeshGraph::::ARITY; } impl ToRing for Ring where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { fn into_ring(self) -> Ring { self } fn ring(&self) -> Ring<&M> { self.to_ref() } } pub struct VertexCirculator where B: Reborrow, B::Target: AsStorage>> + Consistent + Parametric, { inner: ArcCirculator, } impl Circulator for VertexCirculator where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { type Entity = Vertex; fn next(&mut self) -> Option<::Key> { let ab = self.inner.next(); ab.map(|ab| { let (_, b) = ab.into(); b }) } } impl Clone for VertexCirculator where B: Clone + Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { fn clone(&self) -> Self { VertexCirculator { inner: self.inner.clone(), } } } impl From> for VertexCirculator where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { fn from(inner: ArcCirculator) -> Self { VertexCirculator { inner } } } impl<'a, M, G> Iterator for VertexCirculator<&'a M> where M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { type Item = VertexView<&'a M>; fn next(&mut self) -> Option { self.bind_next_view() } fn size_hint(&self) -> (usize, Option) { // This requires consistency, because an inconsistent graph may not // produce the expected minimum of three vertices. (3, None) } } impl<'a, M> Iterator for VertexCirculator<&'a mut M> where M: AsStorage> + AsStorageMut> + Consistent + Parametric, { type Item = VertexOrphan<'a, M::Data>; fn next(&mut self) -> Option { unsafe { self.bind_next_orphan() } } fn size_hint(&self) -> (usize, Option) { // This requires consistency, because an inconsistent graph may not // produce the expected minimum of three vertices. (3, None) } } impl<'a, M> OrphanCirculator<'a, M> for VertexCirculator<&'a mut M> where M: AsStorage> + AsStorageMut> + Consistent + Parametric, { fn target(&mut self) -> &mut M { self.inner.storage } } impl<'a, M> ViewCirculator<'a, M> for VertexCirculator<&'a M> where M: AsStorage> + AsStorage> + Consistent + Parametric, { fn target(&self) -> &'a M { self.inner.storage } } pub struct ArcCirculator where B: Reborrow, B::Target: AsStorage>> + Consistent + Parametric, { storage: B, arc: Option, trace: TraceFirst, } impl Circulator for ArcCirculator where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { type Entity = Arc; fn next(&mut self) -> Option<::Key> { self.arc .and_then(|arc| self.trace.insert(arc).then_some(arc)) .inspect(|arc| { self.arc = self .storage .reborrow() .as_storage() .get(arc) .and_then(|arc| arc.next); }) } } impl Clone for ArcCirculator where B: Clone + Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { fn clone(&self) -> Self { ArcCirculator { storage: self.storage.clone(), arc: self.arc, trace: self.trace, } } } impl From> for ArcCirculator where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { fn from(ring: Ring) -> Self { let (storage, key) = ring.into_arc().unbind(); ArcCirculator { storage, arc: Some(key), trace: Default::default(), } } } impl<'a, M, G> Iterator for ArcCirculator<&'a M> where M: AsStorage> + Consistent + Parametric, G: GraphData, { type Item = ArcView<&'a M>; fn next(&mut self) -> Option { self.bind_next_view() } fn size_hint(&self) -> (usize, Option) { // This requires consistency, because an inconsistent graph may not // produce the expected minimum of three arcs. (3, None) } } impl<'a, M> Iterator for ArcCirculator<&'a mut M> where M: AsStorageMut> + Consistent + Parametric, { type Item = ArcOrphan<'a, M::Data>; fn next(&mut self) -> Option { unsafe { self.bind_next_orphan() } } fn size_hint(&self) -> (usize, Option) { // This requires consistency, because an inconsistent graph may not // produce the expected minimum of three arcs. (3, None) } } impl<'a, M> OrphanCirculator<'a, M> for ArcCirculator<&'a mut M> where M: AsStorageMut> + Consistent + Parametric, { fn target(&mut self) -> &mut M { self.storage } } impl<'a, M, G> ViewCirculator<'a, M> for ArcCirculator<&'a M> where M: AsStorage> + Consistent + Parametric, G: GraphData, { fn target(&self) -> &'a M { self.storage } } pub struct FaceCirculator where B: Reborrow, B::Target: AsStorage>> + Consistent + Parametric, { inner: ArcCirculator, } impl Circulator for FaceCirculator where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { type Entity = Face; fn next(&mut self) -> Option<::Key> { while let Some(ba) = self.inner.next().map(|ab| ab.into_opposite()) { if let Some(abc) = self .inner .storage .reborrow() .as_storage_of::>() .get(&ba) .and_then(|opposite| opposite.face) { return Some(abc); } else { // Skip arcs with no opposing face. This can occur within // non-enclosed meshes. continue; } } None } } impl Clone for FaceCirculator where B: Clone + Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { fn clone(&self) -> Self { FaceCirculator { inner: self.inner.clone(), } } } impl From> for FaceCirculator where B: Reborrow, M: AsStorage> + Consistent + Parametric, G: GraphData, { fn from(inner: ArcCirculator) -> Self { FaceCirculator { inner } } } impl<'a, M, G> Iterator for FaceCirculator<&'a M> where M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { type Item = FaceView<&'a M>; fn next(&mut self) -> Option { self.bind_next_view() } } impl<'a, M> Iterator for FaceCirculator<&'a mut M> where M: AsStorage> + AsStorageMut> + Consistent + Parametric, { type Item = FaceOrphan<'a, M::Data>; fn next(&mut self) -> Option { unsafe { self.bind_next_orphan() } } } impl<'a, M> OrphanCirculator<'a, M> for FaceCirculator<&'a mut M> where M: AsStorage> + AsStorageMut> + Consistent + Parametric, { fn target(&mut self) -> &mut M { self.inner.storage } } impl<'a, M, G> ViewCirculator<'a, M> for FaceCirculator<&'a M> where M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { fn target(&self) -> &'a M { self.inner.storage } } #[cfg(test)] mod tests { use decorum::R64; use nalgebra::{Point2, Point3}; use crate::graph::MeshGraph; use crate::index::HashIndexer; use crate::prelude::*; use crate::primitive::cube::Cube; use crate::primitive::generate::Position; use crate::primitive::sphere::UvSphere; use crate::primitive::Tetragon; type E2 = Point2; type E3 = Point3; #[test] fn circulate_over_arcs() { let graph: MeshGraph = UvSphere::new(3, 2) .polygons::>() // 6 triangles, 18 vertices. .collect(); let face = graph.faces().next().unwrap(); // All faces should be triangles and should have three edges. assert_eq!(3, face.adjacent_arcs().count()); } #[test] fn circulate_over_faces() { let graph: MeshGraph = UvSphere::new(3, 2) .polygons::>() // 6 triangles, 18 vertices. .collect(); let face = graph.faces().next().unwrap(); // No matter which face is selected, it should have three adjacent // faces. assert_eq!(3, face.adjacent_faces().count()); } #[test] fn remove_face() { let mut graph: MeshGraph = UvSphere::new(3, 2) .polygons::>() // 6 triangles, 18 vertices. .collect(); // The graph should begin with 6 faces. assert_eq!(6, graph.face_count()); // Remove a face from the graph. let abc = graph.faces().next().unwrap().key(); { let face = graph.face_mut(abc).unwrap(); assert_eq!(3, face.arity()); // The face should be triangular. let path = face.remove().unwrap().into_ref(); assert_eq!(3, path.arity()); // The path should also be triangular. } // After the removal, the graph should have only 5 faces. assert_eq!(5, graph.face_count()); } #[test] fn split_face() { let mut graph = MeshGraph::::from_raw_buffers_with_arity( vec![0u32, 1, 2, 3], vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)], 4, ) .unwrap(); let abc = graph.faces().next().unwrap().key(); let arc = graph .face_mut(abc) .unwrap() .split(ByIndex(0), ByIndex(2)) .unwrap() .into_ref(); assert!(arc.face().is_some()); assert!(arc.opposite_arc().face().is_some()); assert_eq!(4, graph.vertex_count()); assert_eq!(10, graph.arc_count()); assert_eq!(2, graph.face_count()); } #[test] fn extrude_face() { let mut graph: MeshGraph = UvSphere::new(3, 2) .polygons::>() // 6 triangles, 18 vertices. .collect(); { let key = graph.faces().next().unwrap().key(); let face = graph .face_mut(key) .unwrap() .extrude_with_offset(R64::assert(1.0)) .unwrap() .into_ref(); // The extruded face, being a triangle, should have three adjacent // faces. assert_eq!(3, face.adjacent_faces().count()); } assert_eq!(8, graph.vertex_count()); // The mesh begins with 18 arcs. The extrusion adds three quadrilaterals // with four interior arcs each, so there are `18 + (3 * 4)` arcs. assert_eq!(30, graph.arc_count()); // All faces are triangles and the mesh begins with six such faces. The // extruded face remains, in addition to three connective faces, each of // which is constructed from quadrilaterals. assert_eq!(9, graph.face_count()); } // LINT: In this test, adjacent `nth` calls are used over `0` and `1` and are arguably more // consistent and therefore a bit easier to read. #[expect(clippy::iter_nth_zero)] #[test] fn merge_faces() { // Construct a graph with two connected quadrilaterals. let mut graph = MeshGraph::::from_raw_buffers_with_arity( vec![0u32, 1, 2, 3, 0, 3, 4, 5], vec![ (0.0, 0.0), // 0 (1.0, 0.0), // 1 (1.0, 1.0), // 2 (0.0, 1.0), // 3 (-1.0, 1.0), // 4 (-1.0, 0.0), // 5 ], 4, ) .unwrap(); // The graph should begin with 2 faces. assert_eq!(2, graph.face_count()); // Get the keys for the two faces and join them. let abc = graph.faces().nth(0).unwrap().key(); let def = graph.faces().nth(1).unwrap().key(); graph.face_mut(abc).unwrap().merge(def).unwrap(); // After the removal, the graph should have 1 face. assert_eq!(1, graph.face_count()); assert_eq!(6, graph.faces().next().unwrap().arity()); } #[test] fn poke_face() { let mut graph: MeshGraph = Cube::new() .polygons::>() // 6 quadrilaterals, 24 vertices. .collect(); let key = graph.faces().next().unwrap().key(); let vertex = graph.face_mut(key).unwrap().poke_at_centroid(); // Diverging a quadrilateral yields a tetrahedron. assert_eq!(4, vertex.adjacent_faces().count()); // Traverse to one of the triangles in the tetrahedron. let face = vertex.into_outgoing_arc().into_face().unwrap(); assert_eq!(3, face.arity()); // Diverge the triangle. let vertex = face.poke_at_centroid(); assert_eq!(3, vertex.adjacent_faces().count()); } #[test] fn triangulate_mesh() { let (indices, vertices) = Cube::new() .polygons::>() // 6 quadrilaterals, 24 vertices. .index_vertices::, _>(HashIndexer::default()); let mut graph = MeshGraph::::from_raw_buffers(indices, vertices).unwrap(); graph.triangulate(); assert_eq!(8, graph.vertex_count()); assert_eq!(36, graph.arc_count()); assert_eq!(18, graph.edge_count()); // Each quadrilateral becomes 2 triangles, so 6 quadrilaterals become // 12 triangles. assert_eq!(12, graph.face_count()); } #[test] fn logical_metrics() { let graph = MeshGraph::>::from_raw_buffers_with_arity( vec![0u32, 1, 2, 3], vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)], 4, ) .unwrap(); let face = graph.faces().next().unwrap(); let keys = face .adjacent_vertices() .map(|vertex| vertex.key()) .collect::>(); let ring = face.into_ring(); assert_eq!(2, ring.shortest_logical_metric(keys[0], keys[2]).unwrap()); assert_eq!(1, ring.shortest_logical_metric(keys[0], keys[3]).unwrap()); assert_eq!(0, ring.shortest_logical_metric(keys[0], keys[0]).unwrap()); } } ================================================ FILE: plexus/src/graph/geometry.rs ================================================ //! Geometric graph traits. // Geometric traits like `FaceNormal` and `EdgeMidpoint` are defined in such a // way to reduce the contraints necessary for writing generic user code. With // few exceptions, these traits only depend on `AsPosition` being implemented by // the `Vertex` type in their definition. If a more complex implementation is // necessary, constraints are specified there so that they do not pollute user // code. use theon::ops::{Cross, Interpolate, Project}; use theon::query::Plane; use theon::space::{EuclideanSpace, FiniteDimensional, InnerSpace, Vector, VectorSpace}; use theon::{AsPosition, Position}; use typenum::U3; use crate::entity::borrow::Reborrow; use crate::entity::storage::AsStorage; use crate::graph::data::{GraphData, Parametric}; use crate::graph::edge::{Arc, ArcView, Edge, ToArc}; use crate::graph::face::{Face, ToRing}; use crate::graph::mutation::Consistent; use crate::graph::vertex::{Vertex, VertexView}; use crate::graph::{GraphError, OptionExt as _, ResultExt as _}; use crate::IteratorExt as _; pub type VertexPosition = Position<::Vertex>; pub trait VertexCentroid: GraphData where Self::Vertex: AsPosition, { fn centroid(vertex: VertexView) -> Result, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric; } impl VertexCentroid for G where G: GraphData, G::Vertex: AsPosition, { fn centroid(vertex: VertexView) -> Result, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, { Ok(VertexPosition::::centroid( vertex .adjacent_vertices() .map(|vertex| *vertex.data.as_position()), ) .expect_consistent()) } } pub trait VertexNormal: FaceNormal where Self::Vertex: AsPosition, { fn normal(vertex: VertexView) -> Result>, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric; } impl VertexNormal for G where G: FaceNormal, G::Vertex: AsPosition, { fn normal(vertex: VertexView) -> Result>, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric, { Vector::>::mean( vertex .adjacent_faces() .map(::normal) .collect::, _>>()?, ) .expect_consistent() .normalize() .ok_or(GraphError::Geometry) } } pub trait ArcNormal: GraphData where Self::Vertex: AsPosition, { fn normal(arc: ArcView) -> Result>, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric; } impl ArcNormal for G where G: GraphData, G::Vertex: AsPosition, VertexPosition: EuclideanSpace, Vector>: Project>>, { fn normal(arc: ArcView) -> Result>, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, { let (a, b) = arc .adjacent_vertices() .map(|vertex| *vertex.position()) .try_collect() .expect_consistent(); let c = *arc.next_arc().destination_vertex().position(); let ab = a - b; let cb = c - b; let p = b + ab.project(cb); (p - c).normalize().ok_or(GraphError::Geometry) } } pub trait EdgeMidpoint: GraphData where Self::Vertex: AsPosition, { fn midpoint(edge: T) -> Result, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric, T: ToArc; } impl EdgeMidpoint for G where G: GraphData, G::Vertex: AsPosition, VertexPosition: Interpolate>, { fn midpoint(edge: T) -> Result, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric, T: ToArc, { let arc = edge.into_arc(); let (a, b) = arc .adjacent_vertices() .map(|vertex| *vertex.position()) .try_collect() .expect_consistent(); Ok(a.midpoint(b)) } } pub trait FaceCentroid: GraphData where Self::Vertex: AsPosition, { fn centroid(ring: T) -> Result, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, T: ToRing; } impl FaceCentroid for G where G: GraphData, G::Vertex: AsPosition, { fn centroid(ring: T) -> Result, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, T: ToRing, { let ring = ring.into_ring(); Ok( VertexPosition::::centroid(ring.vertices().map(|vertex| *vertex.position())) .expect_consistent(), ) } } pub trait FaceNormal: GraphData where Self::Vertex: AsPosition, { fn normal(ring: T) -> Result>, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, T: ToRing; } impl FaceNormal for G where G: FaceCentroid + GraphData, G::Vertex: AsPosition, Vector>: Cross>>, VertexPosition: EuclideanSpace, { fn normal(ring: T) -> Result>, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, T: ToRing, { let ring = ring.into_ring(); let (a, b) = ring .vertices() .take(2) .map(|vertex| *vertex.position()) .try_collect() .expect_consistent(); let c = G::centroid(ring)?; let ab = a - b; let bc = b - c; ab.cross(bc).normalize().ok_or(GraphError::Geometry) } } pub trait FacePlane: GraphData where Self::Vertex: AsPosition, VertexPosition: FiniteDimensional, { fn plane(ring: T) -> Result>, GraphError> where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, T: ToRing; } // TODO: The `lapack` feature depends on `ndarray-linalg` and Intel MKL. MKL is // dynamically linked, but the linkage fails during doctests and may fail // when launching a binary. The `lapack` feature and this implementation // have been disabled until an upstream fix is available. See // https://github.com/olson-sean-k/plexus/issues/58 and // https://github.com/rust-ndarray/ndarray-linalg/issues/229 // TODO: The `lapack` feature only supports Linux. See // https://github.com/olson-sean-k/theon/issues/1 // //#[cfg(target_os = "linux")] //mod lapack { // use super::*; // // use smallvec::SmallVec; // use theon::adjunct::{FromItems, IntoItems}; // use theon::lapack::Lapack; // use theon::space::Scalar; // // impl FacePlane for G // where // G: GraphData, // G::Vertex: AsPosition, // VertexPosition: EuclideanSpace + FiniteDimensional, // Scalar>: Lapack, // Vector>: FromItems + IntoItems, // { // fn plane(ring: T) -> Result>, GraphError> // where // B: Reborrow, // B::Target: AsStorage> // + AsStorage> // + Consistent // + Parametric, // T: ToRing, // { // let ring = ring.into_ring(); // let points = ring // .vertices() // .map(|vertex| *vertex.data.as_position()) // .collect::>(); // Plane::from_points(points).ok_or(GraphError::Geometry) // } // } //} ================================================ FILE: plexus/src/graph/mod.rs ================================================ //! Half-edge graph representation of polygonal meshes. //! //! This module provides a flexible representation of polygonal meshes as a //! [half-edge graph][dcel]. Plexus refers to _Half-edges_ and _edges_ as _arcs_ //! and _edges_, respectively. Graphs can store arbitrary data associated with //! any topological entity (vertices, arcs, edges, and faces). //! //! Graph APIs support geometric operations if vertex data implements the //! [`AsPosition`] trait. //! //! See the [user guide][guide-graphs] for more details and examples. //! //! # Representation //! //! A [`MeshGraph`] is fundamentally composed of four entities: _vertices_, //! _arcs_, _edges_, and _faces_. The figure below summarizes the connectivity //! of these entities. //! //! ![Half-Edge Graph Figure](https://plexus.rs/img/heg.svg) //! //! Arcs are directed and connect vertices. An arc that is directed toward a //! vertex $A$ is an _incoming arc_ with respect to $A$. Similarly, an arc //! directed away from such a vertex is an _outgoing arc_. Every vertex is //! associated with exactly one _leading arc_, which is always an outgoing arc. //! The vertex toward which an arc is directed is the arc's _destination vertex_ //! and the other is its _source vertex_. //! //! Every arc is paired with an _opposite arc_ with an opposing direction. //! Given an arc from a vertex $A$ to a vertex $B$, that arc will have an //! opposite arc from $B$ to $A$. Such arcs are notated $\overrightarrow{AB}$ //! and $\overrightarrow{BA}$. Together, these arcs form an _edge_, which is not //! directed. An edge and its two arcs are together called a _composite edge_. //! //! Arcs are connected to their adjacent arcs, known as _next_ and _previous //! arcs_. A traversal along a series of arcs is a _path_. The path formed by //! traversing from an arc to its next arc and so on is a _ring_. When a face is //! present within an ring, the arcs will refer to that face and the face will //! refer to exactly one of the arcs in the ring (this is the leading arc of the //! face). An arc with no associated face is known as a _boundary arc_. If //! either of an edge's arcs is a boundary arc, then that edge is a _boundary //! edge_. //! //! A path that terminates is _open_ and a path that forms a loop is _closed_. //! Rings are always closed. Paths may be notated using _sequence_ or _set //! notation_ and both forms are used to describe rings and faces. //! //! Sequence notation is formed from the ordered sequence of vertices that a //! path traverses, including the source vertex of the first arc and the //! destination vertex of the last arc. Set notation is similar, but is //! implicitly closed and only includes the ordered and unique set of vertices //! traversed by the path. An open path over vertices $A$, $B$, and $C$ is //! notated as a sequence $\overrightarrow{(A,B,C)}$. A closed path over //! vertices $A$, $B$, and $C$ includes the arc $\overrightarrow{CA}$ and is //! notated as a sequence $\overrightarrow{(A,B,C,A)}$ or a set //! $\overrightarrow{\\{A,B,C\\}}$. //! //! Together with vertices and faces, the connectivity of arcs allows for //! effecient traversals. For example, it becomes trivial to find adjacent //! entities, such as the faces that share a given vertex or the adjacent faces //! of a given face. //! //! [`MeshGraph`]s store entities using associative data structures with //! strongly typed and opaque keys. These keys are used to refer entities in a //! graph. Note that paths and rings are **not** entities and are not explicitly //! stored in graphs. //! //! # Views //! //! [`MeshGraph`]s expose _views_ over their entities (vertices, arcs, edges, //! and faces). Views are a type of _smart pointer_ and bind entity storage with //! a key for a specific entity. They extend entities with rich behaviors and //! expose their associated data via `get` and `get_mut` functions. //! //! Views provide the primary API for interacting with a [`MeshGraph`]'s //! topology and data. There are three types of views summarized below: //! //! | Type | Traversal | Exclusive | Data | Topology | //! |-----------|-----------|-----------|-----------|-----------| //! | Immutable | Yes | No | Immutable | Immutable | //! | Mutable | Yes | Yes | Mutable | Mutable | //! | Orphan | No | No | Mutable | N/A | //! //! _Immutable_ and _mutable views_ behave similarly to Rust's `&` and `&mut` //! references: immutable views cannot mutate a graph and are not exclusive //! while mutable views may mutate both the data and topology of a graph but are //! exclusive. //! //! _Orphan views_ (simply referred to as _orphans_ in APIs) may mutate the data //! of a graph, but they cannot access the topology of a graph and cannot //! traverse a graph in any way. This is only useful for modifying the data in a //! graph, but unlike mutable views, orphan views are not exclusive. //! //! Views perform _interior reborrows_, which reborrow the reference to storage //! to construct other views. Immutable reborrows can be performed explicitly //! using the conversions described below: //! //! | Function | Receiver | Borrow | Output | //! |------------|-------------|--------|-----------| //! | `to_ref` | `&self` | `&_` | Immutable | //! | `into_ref` | `self` | `&*_` | Immutable | //! //! It is not possible to explicitly perform a mutable interior reborrow. Such a //! reborrow could invalidate the source view by performing a topological //! mutation. Mutable reborrows are performed beneath safe APIs, such as those //! exposing iterators over orphan views. //! //! # Geometric Traits //! //! The [`GraphData`] trait is used to specify the types of data stored in //! entities in a [`MeshGraph`]. If the `Vertex` data implements the //! [`AsPosition`] trait and the positional data implements the appropriate //! geometric traits, then geometric APIs like //! [`split_at_midpoint`][`ArcView::split_at_midpoint`] and //! [`poke_with_offset`][`FaceView::poke_with_offset`] can be used. Abstracting //! this in generic code involves various traits from [`theon`]. //! //! This module provides geometric traits that describe supported geometric //! operations without the need to express complicated relationships between //! types representing a [Euclidean space][`EuclideanSpace`]. These traits //! express the geometric capabilites of [`GraphData`]. For example, the //! following generic function requires [`EdgeMidpoint`] and subdivides faces in //! a graph by splitting edges at their midpoints: //! //! ```rust //! # extern crate plexus; //! # extern crate smallvec; //! # //! use plexus::geometry::AsPositionMut; //! use plexus::graph::{EdgeMidpoint, FaceView, GraphData, MeshGraph}; //! use plexus::prelude::*; //! use smallvec::SmallVec; //! //! // Requires `EdgeMidpoint` for `split_at_midpoint`. //! pub fn ambo(face: FaceView<&mut MeshGraph>) -> FaceView<&mut MeshGraph> //! where //! G: EdgeMidpoint + GraphData, //! G::Vertex: AsPositionMut, //! { //! let arity = face.arity(); //! let mut arc = face.into_arc(); //! let mut splits = SmallVec::<[_; 4]>::with_capacity(arity); //! for _ in 0..arity { //! let vertex = arc.split_at_midpoint(); //! splits.push(vertex.key()); //! arc = vertex.into_outgoing_arc().into_next_arc(); //! } //! let mut face = arc.into_face().unwrap(); //! for (a, b) in splits.into_iter().perimeter() { //! face = face.split(a, b).unwrap().into_face().unwrap(); //! } //! face //! } //! ``` //! //! # Examples //! //! Generating a [`MeshGraph`] from a [$uv$-sphere][`UvSphere`]: //! //! ```rust //! # extern crate decorum; //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use decorum::R64; //! use nalgebra::Point3; //! use plexus::graph::MeshGraph; //! use plexus::prelude::*; //! use plexus::primitive::generate::Position; //! use plexus::primitive::sphere::UvSphere; //! //! type E3 = Point3; //! //! let mut graph: MeshGraph = UvSphere::default().polygons::>().collect(); //! ``` //! //! Extruding a face in a [`MeshGraph`]: //! //! ```rust //! # extern crate decorum; //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use decorum::R64; //! use nalgebra::Point3; //! use plexus::graph::MeshGraph; //! use plexus::prelude::*; //! use plexus::primitive::generate::Position; //! use plexus::primitive::sphere::UvSphere; //! //! type E3 = Point3; //! //! let mut graph: MeshGraph = UvSphere::new(8, 8).polygons::>().collect(); //! // Get the key of the first face and then extrude it. //! let key = graph.faces().next().unwrap().key(); //! let face = graph //! .face_mut(key) //! .unwrap() //! .extrude_with_offset(R64::assert(1.0)) //! .unwrap(); //! ``` //! //! Traversing and circulating over a [`MeshGraph`]: //! //! ```rust //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use nalgebra::Point2; //! use plexus::graph::MeshGraph; //! use plexus::prelude::*; //! use plexus::primitive::Tetragon; //! //! let mut graph = MeshGraph::>::from_raw_buffers( //! vec![Tetragon::new(0u32, 1, 2, 3)], //! vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)], //! ) //! .unwrap(); //! graph.triangulate(); //! //! // Traverse an arc and use a circulator to get the faces of a nearby vertex. //! let key = graph.arcs().next().unwrap().key(); //! let mut vertex = graph //! .arc_mut(key) //! .unwrap() //! .into_opposite_arc() //! .into_next_arc() //! .into_destination_vertex(); //! for mut face in vertex.adjacent_face_orphans() { //! // `face.get_mut()` provides a mutable reference to face data. //! } //! ``` //! //! [dcel]: https://en.wikipedia.org/wiki/doubly_connected_edge_list //! [guide-graphs]: https://plexus.rs/user-guide/graphs //! //! [`theon`]: https://crates.io/crates/theon //! //! [`Deref`]: std::ops::Deref //! [`EuclideanSpace`]: theon::space::EuclideanSpace //! [`AsPosition`]: crate::geometry::AsPosition //! [`ArcView::split_at_midpoint`]: crate::graph::ArcView::split_at_midpoint //! [`EdgeMidpoint`]: crate::graph::EdgeMidpoint //! [`FaceView::poke_with_offset`]: crate::graph::FaceView::poke_with_offset //! [`GraphData`]: crate::graph::GraphData //! [`MeshGraph`]: crate::graph::MeshGraph //! [`UvSphere`]: crate::primitive::sphere::UvSphere mod builder; mod core; mod data; mod edge; mod face; mod geometry; mod mutation; mod path; mod vertex; use decorum::cmp::EmptyOrd; use decorum::R64; use num::{Integer, NumCast, ToPrimitive, Unsigned}; use smallvec::SmallVec; use std::borrow::Borrow; use std::collections::{HashMap, HashSet}; use std::convert::TryFrom; use std::fmt::Debug; use std::hash::Hash; use std::iter::FromIterator; use std::mem; use std::vec; use theon::adjunct::Map; use theon::query::Aabb; use theon::space::{EuclideanSpace, Scalar}; use theon::{AsPosition, AsPositionMut}; use thiserror::Error; use typenum::NonZero; use crate::buffer::{BufferError, FromRawBuffers, FromRawBuffersWithArity, MeshBuffer}; use crate::builder::{Buildable, FacetBuilder, MeshBuilder, SurfaceBuilder}; use crate::constant::{Constant, ToType, TypeOf}; use crate::encoding::{FaceDecoder, FromEncoding, VertexDecoder}; use crate::entity::borrow::Reborrow; use crate::entity::storage::prelude::*; use crate::entity::storage::{AsStorage, AsStorageMut, AsStorageOf, Key, StorageTarget}; use crate::entity::view::{Bind, Orphan, View}; use crate::entity::{Entity, EntityError, Payload}; use crate::geometry::{FromGeometry, IntoGeometry}; use crate::graph::builder::GraphBuilder; use crate::graph::core::{Core, OwnedCore}; use crate::graph::data::Parametric; use crate::graph::edge::{Arc, Edge}; use crate::graph::face::Face; use crate::graph::mutation::face::FaceInsertCache; use crate::graph::mutation::{Consistent, Immediate}; use crate::graph::vertex::Vertex; use crate::index::{Flat, FromIndexer, Grouping, HashIndexer, IndexBuffer, IndexVertices, Indexer}; use crate::primitive::decompose::IntoVertices; use crate::primitive::{IntoPolygons, Polygonal, UnboundedPolygon}; use crate::transact::Transact; use crate::{DynamicArity, MeshArity, StaticArity}; pub use crate::entity::view::{ClosedView, Rebind}; pub use crate::graph::data::GraphData; pub use crate::graph::edge::{ArcKey, ArcOrphan, ArcView, EdgeKey, EdgeOrphan, EdgeView, ToArc}; pub use crate::graph::face::{FaceKey, FaceOrphan, FaceView, Ring, ToRing}; pub use crate::graph::geometry::{ ArcNormal, EdgeMidpoint, FaceCentroid, FaceNormal, FacePlane, VertexCentroid, VertexNormal, VertexPosition, }; pub use crate::graph::path::Path; pub use crate::graph::vertex::{VertexKey, VertexOrphan, VertexView}; pub use Selector::ByIndex; pub use Selector::ByKey; type Mutation = mutation::Mutation>; /// Errors concerning [`MeshGraph`]s. /// /// [`MeshGraph`]: crate::graph::MeshGraph #[derive(Debug, Eq, Error, PartialEq)] pub enum GraphError { #[error("required topology not found")] TopologyNotFound, #[error("conflicting topology found")] TopologyConflict, #[error("topology malformed")] TopologyMalformed, #[error("topology unreachable")] TopologyUnreachable, #[error("arity is non-polygonal")] ArityNonPolygonal, /// The arity of a [`MeshGraph`] or other data structure is not compatible /// with an operation. #[error("conflicting arity; expected {expected}, but got {actual}")] ArityConflict { /// The expected arity. expected: usize, /// The incompatible arity that was encountered. actual: usize, }, /// The compound arity of a [`MeshGraph`] or other data structure is not /// uniform. /// /// This error occurs when an operation requires a uniform arity but a graph /// or other data structure is non-uniform. See [`MeshArity`]. /// /// [`MeshArity`]: crate::MeshArity #[error("arity is non-uniform")] ArityNonUniform, /// Geometry is incompatible or cannot be computed. #[error("geometric operation failed")] Geometry, /// A graph or other data structure is not compatible with an encoding. #[error("encoding operation failed")] EncodingIncompatible, } // TODO: How should buffer errors be handled? Is this sufficient? impl From for GraphError { fn from(error: BufferError) -> Self { match error { BufferError::ArityConflict { expected, actual } => { GraphError::ArityConflict { expected, actual } } _ => GraphError::EncodingIncompatible, } } } impl From for GraphError { fn from(error: EntityError) -> Self { match error { EntityError::EntityNotFound => GraphError::TopologyNotFound, EntityError::Data => GraphError::Geometry, } } } trait OptionExt { fn expect_consistent(self) -> T; } impl OptionExt for Option { fn expect_consistent(self) -> T { self.expect("internal error: graph consistency violated") } } trait ResultExt { fn expect_consistent(self) -> T where E: Debug; } impl ResultExt for Result { fn expect_consistent(self) -> T where E: Debug, { self.expect("internal error: graph consistency violated") } } trait Circulator where B: Reborrow, B::Target: AsStorage, { type Entity: Payload; fn next(&mut self) -> Option<::Key>; } trait ViewCirculator<'a, M>: Circulator<&'a M> where M: 'a + AsStorage, { fn target(&self) -> &'a M; fn bind_next_view(&mut self) -> Option where T: From>, { self.next() .and_then(|key| View::bind(self.target(), key).map(T::from)) } } trait OrphanCirculator<'a, M>: Circulator<&'a mut M> where M: 'a + AsStorageMut, { fn target(&mut self) -> &mut M; /// Advances the circulator and binds the next key into an orphan view. /// /// # Safety /// /// For the lifetime of the circulator `Self`, this function **must not** /// access a particular key in the target storage more than **once**. Such /// an access may mutably alias, which is undefined behavior. Moreover, the /// storage implementation must only expose disjoint data between distinct /// keys. Put another way, distinct keys must not allow mutable access to /// the same data. /// /// Note that the uniqueness of keys depends on the correctness of the graph /// implementation, graph consistency, and the implementation of /// [`Circulator::next`]. For example, assuming that the graph /// implementation is correct, this function is safe if the `next` /// implementation uses [`TraceAny`] to detect and reject keys previously /// seen over the lifetime of the circulator. /// /// [`Circulator::next`]: crate::graph::Circulator::next /// [`TraceAny`]: crate::entity::traverse::TraceAny unsafe fn bind_next_orphan(&mut self) -> Option where ::Data: 'a, T: 'a + From>, { self.next().and_then(|key| { let entity = self.target().as_storage_mut().get_mut(&key); entity.map(|entity| { let data = entity.get_mut(); let data = mem::transmute::< &'_ mut ::Data, &'a mut ::Data, >(data); Orphan::bind_unchecked(data, key).into() }) }) } } /// Entity selector. /// /// Identifies an entity by key or index. Keys behave as an absolute selector /// and uniquely identify a single entity within a [`MeshGraph`]. Indices behave /// as a relative selector and identify an entity relative to some other entity. /// `Selector` is used by operations that support both of these selection /// mechanisms. /// /// An index is typically used to select an adjacent entity or contained (and /// ordered) entity, such as an adjacent face. /// /// # Examples /// /// Splitting a face by index (of its contained vertices): /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// type E3 = Point3; /// /// let mut graph: MeshGraph = Cube::new().polygons::>().collect(); /// let key = graph.faces().next().unwrap().key(); /// graph /// .face_mut(key) /// .unwrap() /// .split(ByIndex(0), ByIndex(2)) /// .unwrap(); /// ``` /// /// [`MeshGraph`]: crate::graph::MeshGraph #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Selector { ByKey(K), ByIndex(usize), } impl Selector { /// Gets the selector's key or passes its index to a function to resolve /// the key. pub fn key_or_else(self, f: F) -> Result where E: Into, F: Fn(usize) -> Result, { match self { Selector::ByKey(key) => Ok(key), Selector::ByIndex(index) => f(index).map_err(|error| error.into()), } } /// Gets the selector's index or passes its key to a function to resolve /// the index. pub fn index_or_else(self, f: F) -> Result where E: Into, F: Fn(K) -> Result, { match self { Selector::ByKey(key) => f(key).map_err(|error| error.into()), Selector::ByIndex(index) => Ok(index), } } } impl From for Selector where K: Key, { fn from(key: K) -> Self { Selector::ByKey(key) } } impl From for Selector { fn from(index: usize) -> Self { Selector::ByIndex(index) } } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum GraphKey { Vertex(VertexKey), Arc(ArcKey), Edge(EdgeKey), Face(FaceKey), } impl From for GraphKey { fn from(key: VertexKey) -> Self { GraphKey::Vertex(key) } } impl From for GraphKey { fn from(key: ArcKey) -> Self { GraphKey::Arc(key) } } impl From for GraphKey { fn from(key: EdgeKey) -> Self { GraphKey::Edge(key) } } impl From for GraphKey { fn from(key: FaceKey) -> Self { GraphKey::Face(key) } } /// [Half-edge graph][dcel] representation of a polygonal mesh. /// /// `MeshGraph`s form a polygonal mesh from four interconnected entities: /// vertices, arcs, edges, and faces. These entities are exposed by view and /// orphan types as well as types that represent rings and paths in a graph. /// Entities can be associated with arbitrary data, including no data at all. /// See the [`GraphData`] trait. /// /// This flexible representation supports fast traversals and searches and can /// be used to manipulate both the data and topology of a mesh. /// /// See the [`graph`] module documentation and [user guide][guide-graphs] for /// more details. /// /// [dcel]: https://en.wikipedia.org/wiki/doubly_connected_edge_list /// [guide-graphs]: https://plexus.rs/user-guide/graphs /// /// [`GraphData`]: crate::graph::GraphData /// [`graph`]: crate::graph pub struct MeshGraph where G: GraphData, { core: OwnedCore, } impl MeshGraph where G: GraphData, { /// Creates an empty `MeshGraph`. /// /// # Examples /// /// ```rust /// use plexus::graph::MeshGraph; /// /// let mut graph = MeshGraph::<()>::new(); /// ``` pub fn new() -> Self { MeshGraph::from(Core::default()) } /// Gets the number of vertices in the graph. pub fn vertex_count(&self) -> usize { self.core.vertices.len() } /// Gets an immutable view of the vertex with the given key. pub fn vertex(&self, key: VertexKey) -> Option> { Bind::bind(self, key) } /// Gets a mutable view of the vertex with the given key. pub fn vertex_mut(&mut self, key: VertexKey) -> Option> { Bind::bind(self, key) } // TODO: Return `Clone + Iterator`. /// Gets an iterator of immutable views over the vertices in the graph. pub fn vertices(&self) -> impl Iterator> { self.core .vertices .iter() .map(|(key, _)| key) .map(move |key| View::bind_unchecked(self, key)) .map(From::from) } /// Gets an iterator of orphan views over the vertices in the graph. pub fn vertex_orphans(&mut self) -> impl Iterator> { self.core .vertices .iter_mut() .map(|(key, data)| Orphan::bind_unchecked(data, key)) .map(From::from) } /// Gets the number of arcs in the graph. pub fn arc_count(&self) -> usize { self.core.arcs.len() } /// Gets an immutable view of the arc with the given key. pub fn arc(&self, key: ArcKey) -> Option> { Bind::bind(self, key) } /// Gets a mutable view of the arc with the given key. pub fn arc_mut(&mut self, key: ArcKey) -> Option> { Bind::bind(self, key) } // TODO: Return `Clone + Iterator`. /// Gets an iterator of immutable views over the arcs in the graph. pub fn arcs(&self) -> impl Iterator> { self.core .arcs .iter() .map(|(key, _)| key) .map(move |key| View::bind_unchecked(self, key)) .map(From::from) } /// Gets an iterator of orphan views over the arcs in the graph. pub fn arc_orphans(&mut self) -> impl Iterator> { self.core .arcs .iter_mut() .map(|(key, data)| Orphan::bind_unchecked(data, key)) .map(From::from) } /// Gets the number of edges in the graph. pub fn edge_count(&self) -> usize { self.core.edges.len() } /// Gets an immutable view of the edge with the given key. pub fn edge(&self, key: EdgeKey) -> Option> { Bind::bind(self, key) } /// Gets a mutable view of the edge with the given key. pub fn edge_mut(&mut self, key: EdgeKey) -> Option> { Bind::bind(self, key) } // TODO: Return `Clone + Iterator`. /// Gets an iterator of immutable views over the edges in the graph. pub fn edges(&self) -> impl Iterator> { self.core .edges .iter() .map(|(key, _)| key) .map(move |key| View::bind_unchecked(self, key)) .map(From::from) } /// Gets an iterator of orphan views over the edges in the graph. pub fn edge_orphans(&mut self) -> impl Iterator> { self.core .edges .iter_mut() .map(|(key, data)| Orphan::bind_unchecked(data, key)) .map(From::from) } /// Gets the number of faces in the graph. pub fn face_count(&self) -> usize { self.core.faces.len() } /// Gets an immutable view of the face with the given key. pub fn face(&self, key: FaceKey) -> Option> { Bind::bind(self, key) } /// Gets a mutable view of the face with the given key. pub fn face_mut(&mut self, key: FaceKey) -> Option> { Bind::bind(self, key) } // TODO: Return `Clone + Iterator`. /// Gets an iterator of immutable views over the faces in the graph. pub fn faces(&self) -> impl Iterator> { self.core .faces .iter() .map(|(key, _)| key) .map(move |key| View::bind_unchecked(self, key)) .map(From::from) } /// Gets an iterator of orphan views over the faces in the graph. pub fn face_orphans(&mut self) -> impl Iterator> { self.core .faces .iter_mut() .map(|(key, data)| Orphan::bind_unchecked(data, key)) .map(From::from) } /// Gets an immutable path over the given sequence of vertex keys. /// /// # Errors /// /// Returns an error if a vertex is not found or the path is malformed. pub fn path(&self, keys: I) -> Result, GraphError> where I: IntoIterator, I::Item: Borrow, { Path::bind(self, keys) } /// Gets a mutable path over the given sequence of vertex keys. /// /// # Errors /// /// Returns an error if a vertex is not found or the path is malformed. pub fn path_mut(&mut self, keys: I) -> Result, GraphError> where I: IntoIterator, I::Item: Borrow, { Path::bind(self, keys) } /// Gets an axis-aligned bounding box that encloses the graph. pub fn aabb(&self) -> Aabb> where G::Vertex: AsPosition, VertexPosition: EuclideanSpace, Scalar>: EmptyOrd, { Aabb::from_points(self.vertices().map(|vertex| *vertex.position())) } // TODO: This triangulation does not consider geometry and exhibits some // bad behavior in certain situations. Triangulation needs to be // reworked and may need to expose a bit more complexity. A geometric // triangulation algorithm would be a useful addition and could // detect concave faces and provide more optimal splits. See comments // on `FaceView::triangulate`. /// Triangulates the graph, tessellating all faces into triangles. pub fn triangulate(&mut self) { // TODO: This implementation is a bit fragile and depends on the // semantics of `TopologyConflict` in this context. It also panics // if no valid split is found given all offsets or if some other // error is encountered while splitting. Can this code assume that // any of these conditions aren't possible? This should work a bit // better than using `FaceView::triangulate` until triangulation // is reworked. let keys = self .core .faces .iter() .map(|(key, _)| key) .collect::>(); for key in keys { let mut face = self.face_mut(key).unwrap(); let mut offset = 0; while face.arity() > 3 { match face.split(ByIndex(offset), ByIndex(offset + 2)) { Ok(next) => { face = next.into_face().expect_consistent(); offset = 0; } Err(GraphError::TopologyConflict) => { // Retry if the split intersected another face. See // `FaceSplitCache::from_face`. face = self.face_mut(key).unwrap(); offset += 1; if offset >= face.arity() { panic!() } } _ => panic!(), } } } } /// Smooths the positions of vertices in the graph. /// /// Each position is translated by its offset from its centroid scaled by /// the given factor. The centroid of a vertex position is the mean of the /// positions of its adjacent vertices. That is, given a factor $k$ and a /// vertex with position $P$ and centroid $Q$, its position becomes /// $P+k(Q-P)$. pub fn smooth(&mut self, factor: T) where T: Into>>, G: VertexCentroid, G::Vertex: AsPositionMut, VertexPosition: EuclideanSpace, { let factor = factor.into(); let mut positions = HashMap::with_capacity(self.vertex_count()); for vertex in self.vertices() { let position = *vertex.position(); positions.insert( vertex.key(), position + ((vertex.centroid() - position) * factor), ); } for mut vertex in self.vertex_orphans() { *vertex.get_mut().as_position_mut() = positions.remove(&vertex.key()).unwrap(); } } /// Splits the graph along a path. /// /// Splitting a graph creates boundaries along the given path and copies any /// necessary vertex, arc, and edge data. /// /// If the path bisects the graph, then splitting will result in disjointed /// sub-graphs. /// /// # Examples /// /// ```rust,no_run /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::Trigon; /// /// type E2 = Point2; /// /// // Create a graph from two triangles. /// let mut graph = MeshGraph::::from_raw_buffers( /// vec![Trigon::new(0usize, 1, 2), Trigon::new(2, 1, 3)], /// vec![(-1.0, 0.0), (0.0, -1.0), (0.0, 1.0), (1.0, 0.0)], /// ) /// .unwrap(); /// /// // Find the shared edge that bisects the triangles and then construct a path /// // along the edge and split the graph. /// let key = graph /// .edges() /// .find(|edge| !edge.is_boundary_edge()) /// .map(|edge| edge.into_arc().key()) /// .unwrap(); /// let mut path = graph.arc_mut(key).unwrap().into_path(); /// MeshGraph::split_at_path(path).unwrap(); /// ``` pub fn split_at_path(path: Path<&mut Self>) -> Result<(), GraphError> { let _ = path; unimplemented!() } /// Gets an iterator over a vertex within each disjoint sub-graph. /// /// Traverses the graph and returns an arbitrary vertex within each /// _disjoint sub-graph_. A sub-graph is _disjoint_ if it cannot be reached /// from all other topology in the graph. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::Trigon; /// /// type E2 = Point2; /// /// // Create a graph from two disjoint triangles. /// let graph = MeshGraph::::from_raw_buffers( /// vec![Trigon::new(0u32, 1, 2), Trigon::new(3, 4, 5)], /// vec![ /// (-2.0, 0.0), /// (-1.0, 0.0), /// (-1.0, 1.0), /// (1.0, 0.0), /// (2.0, 0.0), /// (1.0, 1.0), /// ], /// ) /// .unwrap(); /// /// // A vertex from each disjoint triangle is returned. /// for vertex in graph.disjoint_subgraph_vertices() { /// // ... /// } /// ``` pub fn disjoint_subgraph_vertices(&self) -> impl ExactSizeIterator> { let keys = self .core .vertices .iter() .map(|(key, _)| key) .collect::>(); let mut subkeys = HashSet::with_capacity(self.vertex_count()); let mut vertices = SmallVec::<[VertexView<_>; 4]>::new(); while let Some(key) = keys.difference(&subkeys).next() { let vertex = VertexView::from(View::bind_unchecked(self, *key)); vertices.push(vertex); subkeys.extend(vertex.traverse_by_depth().map(|vertex| vertex.key())); } vertices.into_iter() } /// Moves disjoint sub-graphs into separate graphs. pub fn into_disjoint_subgraphs(self) -> Vec { unimplemented!() } /// Shrinks the capacity of the graph's underlying storage as much as /// possible. pub fn shrink_to_fit(&mut self) { self.core.vertices.shrink_to_fit(); self.core.arcs.shrink_to_fit(); self.core.edges.shrink_to_fit(); self.core.faces.shrink_to_fit(); } /// Creates a [`Buildable`] mesh data structure from the graph. /// /// The output is created from each unique vertex in the graph. No face data /// is used, and the `Facet` type is always the unit type `()`. /// /// # Examples /// /// Creating a [`MeshBuffer`] from a [`MeshGraph`] used to modify a cube: /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::Total; /// use nalgebra::Point3; /// use plexus::buffer::MeshBufferN; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// type E3 = Point3>; /// /// let mut graph: MeshGraph = Cube::new().polygons::>().collect(); /// let key = graph.faces().next().unwrap().key(); /// graph /// .face_mut(key) /// .unwrap() /// .extrude_with_offset(1.0) /// .unwrap(); /// /// let buffer: MeshBufferN = graph.to_mesh_by_vertex().unwrap(); /// ``` /// /// # Errors /// /// Returns an error if the graph does not have constant arity that is /// compatible with the index buffer. Typically, a graph is triangulated /// before being converted to a buffer. /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`Buildable`]: crate::builder::Buildable /// [`MeshGraph`]: crate::graph::MeshGraph pub fn to_mesh_by_vertex(&self) -> Result where B: Buildable, B::Vertex: FromGeometry, { self.to_mesh_by_vertex_with(|vertex| vertex.get().clone().into_geometry()) } /// Creates a [`Buildable`] mesh data structure from the graph. /// /// The output is created from each unique vertex in the graph, which is /// converted by the given function. No face data is used, and the `Facet` /// type is always the unit type `()`. /// /// # Errors /// /// Returns an error if the vertex data cannot be inserted into the output, /// there are arity conflicts, or the output does not support topology found /// in the graph. /// /// [`Buildable`]: crate::builder::Buildable pub fn to_mesh_by_vertex_with(&self, mut f: F) -> Result where B: Buildable, F: FnMut(VertexView<&Self>) -> B::Vertex, { let mut builder = B::builder(); builder.surface_with(|builder| { let mut keys = HashMap::with_capacity(self.vertex_count()); for vertex in self.vertices() { keys.insert(vertex.key(), builder.insert_vertex(f(vertex))?); } builder.facets_with(|builder| { for face in self.faces() { let indices = face .adjacent_vertices() .map(|vertex| keys[&vertex.key()]) .collect::>(); builder.insert_facet(indices.as_slice(), ())?; } Ok(()) }) })?; builder.build() } /// Creates a [`Buildable`] mesh data structure from the graph. /// /// The output is created from each face in the graph. For each face, the /// face data and data for each of its vertices is inserted into the mesh /// via [`FromGeometry`]. This means that a vertex is inserted for each of /// its adjacent faces. /// /// # Errors /// /// Returns an error if the vertex data cannot be inserted into the output, /// there are arity conflicts, or the output does not support topology found /// in the graph. /// /// [`Buildable`]: crate::builder::Buildable /// [`FromGeometry`]: crate::geometry::FromGeometry pub fn to_mesh_by_face(&self) -> Result where B: Buildable, B::Vertex: FromGeometry, B::Facet: FromGeometry, { self.to_mesh_by_face_with(|_, vertex| vertex.get().clone().into_geometry()) } /// Creates a [`Buildable`] mesh data structure from the graph. /// /// The output is created from each face in the graph. For each face, the /// face data and data for each of its vertices is converted into the output /// vertex data by the given function. This means that a vertex is inserted /// for each of its adjacent faces. The data of each face is is inserted /// into the output via [`FromGeometry`]. /// /// # Examples /// /// Creating a [`MeshBuffer`] from a [`MeshGraph`] used to compute normals: /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::buffer::MeshBuffer; /// use plexus::geometry::Vector; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// use plexus::primitive::BoundedPolygon; /// /// type E3 = Point3; /// /// pub struct Vertex { /// pub position: E3, /// pub normal: Vector, /// } /// /// let graph: MeshGraph = Cube::new().polygons::>().collect(); /// /// let buffer: MeshBuffer, _> = graph /// .to_mesh_by_face_with(|face, vertex| Vertex { /// position: *vertex.position(), /// normal: face.normal().unwrap(), /// }) /// .unwrap(); /// ``` /// /// # Errors /// /// Returns an error if the vertex data cannot be inserted into the output, /// there are arity conflicts, or the output does not support topology found /// in the graph. /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`Buildable`]: crate::builder::Buildable /// [`FromGeometry`]: crate::geometry::FromGeometry /// [`MeshGraph`]: crate::graph::MeshGraph pub fn to_mesh_by_face_with(&self, mut f: F) -> Result where B: Buildable, B::Facet: FromGeometry, F: FnMut(FaceView<&Self>, VertexView<&Self>) -> B::Vertex, { let mut builder = B::builder(); builder.surface_with(|builder| { for face in self.faces() { let indices = face .adjacent_vertices() .map(|vertex| builder.insert_vertex(f(face, vertex))) .collect::, _>>()?; builder.facets_with(|builder| { builder.insert_facet(indices.as_slice(), face.get().clone()) })?; } Ok(()) })?; builder.build() } } impl AsStorage> for MeshGraph where G: GraphData, { fn as_storage(&self) -> &StorageTarget> { self.core.as_storage_of::>() } } impl AsStorage> for MeshGraph where G: GraphData, { fn as_storage(&self) -> &StorageTarget> { self.core.as_storage_of::>() } } impl AsStorage> for MeshGraph where G: GraphData, { fn as_storage(&self) -> &StorageTarget> { self.core.as_storage_of::>() } } impl AsStorage> for MeshGraph where G: GraphData, { fn as_storage(&self) -> &StorageTarget> { self.core.as_storage_of::>() } } impl AsStorageMut> for MeshGraph where G: GraphData, { fn as_storage_mut(&mut self) -> &mut StorageTarget> { self.core.as_storage_mut_of::>() } } impl AsStorageMut> for MeshGraph where G: GraphData, { fn as_storage_mut(&mut self) -> &mut StorageTarget> { self.core.as_storage_mut_of::>() } } impl AsStorageMut> for MeshGraph where G: GraphData, { fn as_storage_mut(&mut self) -> &mut StorageTarget> { self.core.as_storage_mut_of::>() } } impl AsStorageMut> for MeshGraph where G: GraphData, { fn as_storage_mut(&mut self) -> &mut StorageTarget> { self.core.as_storage_mut_of::>() } } /// Exposes a [`MeshBuilder`] that can be used to construct a [`MeshGraph`] /// incrementally from _surfaces_ and _facets_. /// /// See the [`builder`] module documentation for more. /// /// # Examples /// /// Creating a [`MeshGraph`] from a triangle: /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::builder::Buildable; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// /// let mut builder = MeshGraph::>::builder(); /// let graph = builder /// .surface_with(|builder| { /// let a = builder.insert_vertex((0.0, 0.0))?; /// let b = builder.insert_vertex((1.0, 0.0))?; /// let c = builder.insert_vertex((0.0, 1.0))?; /// builder.facets_with(|builder| builder.insert_facet(&[a, b, c], ())) /// }) /// .and_then(|_| builder.build()) /// .unwrap(); /// ``` /// /// [`MeshBuilder`]: crate::builder::MeshBuilder /// [`builder`]: crate::builder /// [`MeshGraph`]: crate::graph::MeshGraph impl Buildable for MeshGraph where G: GraphData, { type Builder = GraphBuilder; type Error = GraphError; type Vertex = G::Vertex; type Facet = G::Face; fn builder() -> Self::Builder { Default::default() } } impl Consistent for MeshGraph where G: GraphData {} impl Default for MeshGraph where G: GraphData, { fn default() -> Self { MeshGraph::new() } } impl DynamicArity for MeshGraph where G: GraphData, { type Dynamic = MeshArity; fn arity(&self) -> Self::Dynamic { MeshArity::from_components::, _>(self.faces()) } } impl From

for MeshGraph where P: Polygonal, G: GraphData, G::Vertex: FromGeometry, { fn from(polygon: P) -> Self { let arity = polygon.arity(); MeshGraph::from_raw_buffers_with_arity(0..arity, polygon, arity) .expect("inconsistent polygon") } } impl From> for MeshGraph where G: GraphData, { fn from(core: OwnedCore) -> Self { MeshGraph { core } } } impl From> for OwnedCore where G: GraphData, { fn from(graph: MeshGraph) -> Self { let MeshGraph { core, .. } = graph; core } } impl FromEncoding for MeshGraph where E: FaceDecoder + VertexDecoder, G: GraphData, G::Face: FromGeometry, G::Vertex: FromGeometry, { type Error = GraphError; fn from_encoding( vertices: ::Output, faces: ::Output, ) -> Result { let mut mutation = Mutation::from(MeshGraph::new()); let keys = vertices .into_iter() .map(|data| mutation::vertex::insert(&mut mutation, data.into_geometry())) .collect::>(); for (perimeter, data) in faces { let perimeter = perimeter .into_iter() .map(|index| keys[index]) .collect::>(); let cache = FaceInsertCache::from_storage(&mutation, perimeter.as_slice())?; let data = data.into_geometry(); mutation::face::insert_with(&mut mutation, cache, || (Default::default(), data))?; } mutation.commit().map_err(|(_, error)| error) } } impl FromIndexer for MeshGraph where G: GraphData, G::Vertex: FromGeometry, P: Map + Polygonal, P::Output: Grouping + IntoVertices + Polygonal, Vec: IndexBuffer, { type Error = GraphError; fn from_indexer(input: I, indexer: N) -> Result where I: IntoIterator, N: Indexer, { let mut mutation = Mutation::from(MeshGraph::new()); let (indices, vertices) = input.into_iter().index_vertices(indexer); let vertices = vertices .into_iter() .map(|vertex| mutation::vertex::insert(&mut mutation, vertex.into_geometry())) .collect::>(); for face in indices { let perimeter = face .into_vertices() .into_iter() .map(|index| vertices[index]) .collect::>(); let cache = FaceInsertCache::from_storage(&mutation, &perimeter)?; mutation::face::insert_with(&mut mutation, cache, Default::default)?; } mutation.commit().map_err(|(_, error)| error) } } impl FromIterator

for MeshGraph where G: GraphData, G::Vertex: FromGeometry, P: Polygonal, P::Vertex: Clone + Eq + Hash, Self: FromIndexer, { fn from_iter(input: I) -> Self where I: IntoIterator, { Self::from_indexer(input, HashIndexer::default()).unwrap_or_else(|_| Self::default()) } } impl FromRawBuffers for MeshGraph where P: IntoVertices + Polygonal, P::Vertex: Integer + ToPrimitive + Unsigned, G: GraphData, G::Vertex: FromGeometry, { type Error = GraphError; fn from_raw_buffers(indices: I, vertices: J) -> Result where I: IntoIterator, J: IntoIterator, { let mut mutation = Mutation::from(MeshGraph::new()); let vertices = vertices .into_iter() .map(|vertex| mutation::vertex::insert(&mut mutation, vertex.into_geometry())) .collect::>(); for face in indices { let mut perimeter = SmallVec::<[_; 4]>::with_capacity(face.arity()); for index in face.into_vertices() { let index = ::from(index).unwrap(); perimeter.push(*vertices.get(index).ok_or(GraphError::TopologyNotFound)?); } let cache = FaceInsertCache::from_storage(&mutation, &perimeter)?; mutation::face::insert_with(&mut mutation, cache, Default::default)?; } mutation.commit().map_err(|(_, error)| error) } } impl FromRawBuffersWithArity for MeshGraph where N: Integer + ToPrimitive + Unsigned, G: GraphData, G::Vertex: FromGeometry, { type Error = GraphError; /// Creates a [`MeshGraph`] from [raw buffers][`buffer`]. The arity of the /// polygons in the index buffer must be given and constant. /// /// # Errors /// /// Returns an error if the arity of the index buffer is not constant, any /// index is out of bounds, or there is an error inserting topology into the /// graph. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point3; /// use plexus::graph::MeshGraph; /// use plexus::index::{Flat3, LruIndexer}; /// use plexus::prelude::*; /// use plexus::primitive::generate::Position; /// use plexus::primitive::sphere::UvSphere; /// /// type E3 = Point3; /// /// let (indices, positions) = UvSphere::new(16, 16) /// .polygons::>() /// .triangulate() /// .index_vertices::(LruIndexer::with_capacity(256)); /// let mut graph = MeshGraph::::from_raw_buffers_with_arity(indices, positions, 3).unwrap(); /// ``` /// /// [`buffer`]: crate::buffer /// [`MeshGraph`]: crate::graph::MeshGraph fn from_raw_buffers_with_arity( indices: I, vertices: J, arity: usize, ) -> Result where I: IntoIterator, J: IntoIterator, { use itertools::Itertools; if arity < 3 { return Err(GraphError::ArityNonPolygonal); } let mut mutation = Mutation::from(MeshGraph::new()); let vertices = vertices .into_iter() .map(|vertex| mutation::vertex::insert(&mut mutation, vertex.into_geometry())) .collect::>(); for face in &indices .into_iter() .map(|index| ::from(index).unwrap()) .chunks(arity) { let face = face.collect::>(); if face.len() != arity { // Index buffer length is not a multiple of arity. return Err(GraphError::ArityConflict { expected: arity, actual: face.len(), }); } let mut perimeter = SmallVec::<[_; 4]>::with_capacity(arity); for index in face { perimeter.push(*vertices.get(index).ok_or(GraphError::TopologyNotFound)?); } let cache = FaceInsertCache::from_storage(&mutation, &perimeter)?; mutation::face::insert_with(&mut mutation, cache, Default::default)?; } mutation.commit().map_err(|(_, error)| error) } } impl IntoPolygons for MeshGraph where G: GraphData, { type Output = vec::IntoIter; type Polygon = UnboundedPolygon; fn into_polygons(self) -> Self::Output { use crate::IteratorExt as _; self.faces() .map(|face| { // The arity of a face in a graph must be polygonal (three or // higher) so this should never fail. face.adjacent_vertices() .map(|vertex| vertex.get().clone()) .try_collect() .expect_consistent() }) .collect::>() .into_iter() } } impl Parametric for MeshGraph where G: GraphData, { type Data = G; } impl StaticArity for MeshGraph where G: GraphData, { type Static = (usize, Option); const ARITY: Self::Static = (3, None); } impl TryFrom, H>> for MeshGraph where Constant: ToType, TypeOf: NonZero, T: Copy + Integer + NumCast + Unsigned, H: Clone, G: GraphData, G::Vertex: FromGeometry, { type Error = GraphError; /// Creates a [`MeshGraph`] from a flat [`MeshBuffer`]. The arity of the /// polygons in the index buffer must be known and constant. /// /// # Errors /// /// Returns an error if a [`MeshGraph`] cannot represent the topology in the /// [`MeshBuffer`]. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::buffer::MeshBuffer; /// use plexus::graph::MeshGraph; /// use plexus::index::Flat4; /// use plexus::prelude::*; /// use std::convert::TryFrom; /// /// type E2 = Point2; /// /// let buffer = MeshBuffer::::from_raw_buffers( /// vec![0u64, 1, 2, 3], /// vec![(0.0f64, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)], /// ) /// .unwrap(); /// let mut graph = MeshGraph::::try_from(buffer).unwrap(); /// ``` /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`MeshGraph`]: crate::graph::MeshGraph fn try_from(buffer: MeshBuffer, H>) -> Result { let arity = buffer.arity(); let (indices, vertices) = buffer.into_raw_buffers(); MeshGraph::from_raw_buffers_with_arity(indices, vertices, arity) } } impl TryFrom> for MeshGraph where P: Grouping + IntoVertices + Polygonal, P::Vertex: Copy + Integer + NumCast + Unsigned, H: Clone, G: GraphData, G::Vertex: FromGeometry, { type Error = GraphError; /// Creates a [`MeshGraph`] from a structured [`MeshBuffer`]. /// /// # Errors /// /// Returns an error if a [`MeshGraph`] cannot represent the topology in the /// [`MeshBuffer`]. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point2; /// use plexus::buffer::MeshBuffer; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::Tetragon; /// use std::convert::TryFrom; /// /// type E2 = Point2; /// /// let buffer = MeshBuffer::, E2>::from_raw_buffers( /// vec![Tetragon::new(0u64, 1, 2, 3)], /// vec![(0.0f64, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)], /// ) /// .unwrap(); /// let mut graph = MeshGraph::::try_from(buffer).unwrap(); /// ``` /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`MeshGraph`]: crate::graph::MeshGraph fn try_from(buffer: MeshBuffer) -> Result { let (indices, vertices) = buffer.into_raw_buffers(); MeshGraph::from_raw_buffers(indices, vertices) } } #[cfg(test)] mod tests { use decorum::R64; use nalgebra::{Point2, Point3, Vector3}; use num::Zero; use crate::buffer::MeshBuffer3; use crate::graph::{GraphData, GraphError, MeshGraph}; use crate::prelude::*; use crate::primitive::generate::Position; use crate::primitive::sphere::UvSphere; use crate::primitive::NGon; type E2 = Point2; type E3 = Point3; #[test] fn collect() { let graph: MeshGraph> = UvSphere::new(3, 2) .polygons::>() // 6 triangles, 18 vertices. .collect(); assert_eq!(5, graph.vertex_count()); assert_eq!(18, graph.arc_count()); assert_eq!(6, graph.face_count()); } #[test] fn iterate() { let mut graph: MeshGraph> = UvSphere::new(4, 2) .polygons::>() // 8 triangles, 24 vertices. .collect(); assert_eq!(6, graph.vertices().count()); assert_eq!(24, graph.arcs().count()); assert_eq!(8, graph.faces().count()); for vertex in graph.vertices() { // Every vertex is connected to 4 triangles with 4 (incoming) arcs. // Traversal of topology should be possible. assert_eq!(4, vertex.incoming_arcs().count()); } for mut vertex in graph.vertex_orphans() { // Data should be mutable. *vertex.get_mut() += Vector3::zero(); } } #[test] fn isolate_disjoint_subgraphs() { // Construct a graph from a quadrilateral. let graph = MeshGraph::::from_raw_buffers( vec![NGon([0u32, 1, 2, 3])], vec![(1.0, 0.0), (2.0, 0.0), (2.0, 1.0), (1.0, 1.0)], ) .unwrap(); assert_eq!(1, graph.disjoint_subgraph_vertices().count()); // Construct a graph with two disjoint quadrilaterals. let graph = MeshGraph::::from_raw_buffers( vec![NGon([0u32, 1, 2, 3]), NGon([4, 5, 6, 7])], vec![ (-2.0, 0.0), (-1.0, 0.0), (-1.0, 1.0), (-2.0, 1.0), (1.0, 0.0), (2.0, 0.0), (2.0, 1.0), (1.0, 1.0), ], ) .unwrap(); assert_eq!(2, graph.disjoint_subgraph_vertices().count()); } #[test] fn non_manifold_error_deferred() { let graph: MeshGraph = UvSphere::new(32, 32) .polygons::>() .triangulate() .collect(); // This conversion will join faces by a single vertex, but ultimately // creates a manifold. let _: MeshBuffer3 = graph.to_mesh_by_face().unwrap(); } #[test] fn error_on_non_manifold() { // Construct a graph with a "fan" of three triangles sharing the same // edge along the Z-axis. The edge would have three associated faces, // which should not be possible. let graph = MeshGraph::>::from_raw_buffers( vec![NGon([0u32, 1, 2]), NGon([0, 1, 3]), NGon([0, 1, 4])], vec![(0, 0, 1), (0, 0, -1), (1, 0, 0), (0, 1, 0), (1, 1, 0)], ); assert_eq!(graph.err().unwrap(), GraphError::TopologyConflict); } // This test is a sanity check for circulators over orphan views and the // unsafe transmutations used to coerce lifetimes. It is a good target for // Miri, which can detect certain memory safety issues. #[test] fn read_write_mutable_circulator() { const WEIGHT: u64 = 123_456_789; enum FaceWeight {} impl GraphData for FaceWeight { type Vertex = [i32; 2]; type Arc = (); type Edge = (); type Face = u64; } // Construct a graph resembling the following diagram. // // 0---1---2 // |\ B|C /| // | \ | / | // |A \|/ D| // 3---4---5 // |E /|\ H| // | / | \ | // |/ F|G \| // 6---7---8 let mut graph = MeshGraph::::from_raw_buffers( [ NGon([0usize, 3, 4]), NGon([4, 1, 0]), NGon([1, 4, 2]), NGon([2, 4, 5]), NGon([4, 3, 6]), NGon([6, 7, 4]), NGon([4, 7, 8]), NGon([8, 5, 4]), ], [ [0i32, 0], [1, 0], [2, 0], [0, 1], [1, 1], [2, 1], [0, 2], [1, 2], [2, 2], ], ) .unwrap(); // Get the center vertex (4). let mut vertex = { let key = graph .vertices() .find(|vertex| vertex.valence() == 8) .map(|vertex| vertex.key()) .unwrap(); graph.vertex_mut(key).unwrap() }; // Write and then read each face via circulators. for mut face in vertex.adjacent_face_orphans() { *face.get_mut() = WEIGHT; } for face in vertex.adjacent_faces() { assert_eq!(WEIGHT, *face.get()); } } } ================================================ FILE: plexus/src/graph/mutation/edge.rs ================================================ use std::ops::{Deref, DerefMut}; use crate::entity::borrow::Reborrow; use crate::entity::storage::prelude::*; use crate::entity::storage::{AsStorage, AsStorageMut, Fuse, StorageTarget}; use crate::entity::view::{Bind, ClosedView, Rebind}; use crate::graph::core::Core; use crate::graph::data::{Data, GraphData, Parametric}; use crate::graph::edge::{Arc, ArcKey, ArcView, Edge, EdgeKey}; use crate::graph::face::{Face, FaceKey}; use crate::graph::mutation::face::{self, FaceInsertCache, FaceRemoveCache}; use crate::graph::mutation::vertex::{self, VertexMutation}; use crate::graph::mutation::{Consistent, Immediate, Mode, Mutable, Mutation}; use crate::graph::vertex::{Vertex, VertexKey, VertexView}; use crate::graph::GraphError; use crate::transact::{Bypass, Transact}; use crate::IteratorExt as _; pub type CompositeEdge = (Edge, (Arc, Arc)); pub type CompositeEdgeData = ( ::Edge, (::Arc, ::Arc), ); pub type CompositeEdgeKey = (EdgeKey, (ArcKey, ArcKey)); type ModalCore

= Core< Data<

::Graph>,

::VertexStorage,

::ArcStorage,

::EdgeStorage, (), >; type RefCore<'a, G> = Core< G, &'a StorageTarget<'a, Vertex>, &'a StorageTarget<'a, Arc>, &'a StorageTarget<'a, Edge>, (), >; pub struct EdgeMutation

where P: Mode, { inner: VertexMutation

, // TODO: Split this into two fields. storage: (P::ArcStorage, P::EdgeStorage), } impl

EdgeMutation

where P: Mode, { pub fn to_ref_core(&self) -> RefCore> { self.inner .to_ref_core() .fuse(self.storage.0.as_storage()) .fuse(self.storage.1.as_storage()) } pub fn connect_adjacent_arcs(&mut self, ab: ArcKey, bc: ArcKey) -> Result<(), GraphError> { self.with_arc_mut(ab, |arc| arc.next = Some(bc))?; self.with_arc_mut(bc, |arc| arc.previous = Some(ab))?; Ok(()) } pub fn disconnect_next_arc(&mut self, ab: ArcKey) -> Result, GraphError> { let bx = self.with_arc_mut(ab, |arc| arc.next.take())?; if let Some(bx) = bx.as_ref() { self.with_arc_mut(*bx, |arc| arc.previous.take()) .map_err(|_| GraphError::TopologyMalformed)?; } Ok(bx) } pub fn disconnect_previous_arc(&mut self, ab: ArcKey) -> Result, GraphError> { let xa = self.with_arc_mut(ab, |arc| arc.previous.take())?; if let Some(xa) = xa.as_ref() { self.with_arc_mut(*xa, |arc| arc.next.take()) .map_err(|_| GraphError::TopologyMalformed)?; } Ok(xa) } pub fn connect_arc_to_edge(&mut self, ab: ArcKey, ab_ba: EdgeKey) -> Result<(), GraphError> { self.with_arc_mut(ab, |arc| arc.edge = Some(ab_ba)) } pub fn connect_arc_to_face(&mut self, ab: ArcKey, abc: FaceKey) -> Result<(), GraphError> { self.with_arc_mut(ab, |arc| arc.face = Some(abc)) } pub fn disconnect_arc_from_face(&mut self, ab: ArcKey) -> Result, GraphError> { self.with_arc_mut(ab, |arc| arc.face.take()) } fn with_arc_mut(&mut self, ab: ArcKey, mut f: F) -> Result where F: FnMut(&mut Arc>) -> T, { let arc = self .storage .0 .as_storage_mut() .get_mut(&ab) .ok_or(GraphError::TopologyNotFound)?; Ok(f(arc)) } } impl

AsStorage>> for EdgeMutation

where P: Mode, { fn as_storage(&self) -> &StorageTarget>> { self.storage.0.as_storage() } } impl

AsStorage>> for EdgeMutation

where P: Mode, { fn as_storage(&self) -> &StorageTarget>> { self.storage.1.as_storage() } } impl Bypass>> for EdgeMutation> where M: Parametric, { fn bypass(self) -> Self::Commit { let EdgeMutation { inner, storage: (arcs, edges), .. } = self; inner.bypass().fuse(arcs).fuse(edges) } } // TODO: This is a hack. Replace this with delegation. impl

Deref for EdgeMutation

where P: Mode, { type Target = VertexMutation

; fn deref(&self) -> &Self::Target { &self.inner } } impl

DerefMut for EdgeMutation

where P: Mode, { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } impl

From> for EdgeMutation

where P: Mode, { fn from(core: ModalCore

) -> Self { let (vertices, arcs, edges, ..) = core.unfuse(); EdgeMutation { inner: Core::empty().fuse(vertices).into(), storage: (arcs, edges), } } } impl Transact>> for EdgeMutation> where M: Parametric, { type Commit = ModalCore>; type Abort = (); type Error = GraphError; fn commit(self) -> Result { let EdgeMutation { inner, storage: (arcs, edges), .. } = self; // In a consistent graph, all arcs must have adjacent arcs and an // associated edge. for (_, arc) in arcs.as_storage().iter() { if arc.next.is_none() || arc.previous.is_none() || arc.edge.is_none() { return Err(((), GraphError::TopologyMalformed)); } } inner.commit().map(move |core| core.fuse(arcs).fuse(edges)) } fn abort(self) -> Self::Abort {} } struct ArcRemoveCache { ab: ArcKey, xa: Option, bx: Option, cache: Option, } impl ArcRemoveCache { pub fn from_arc(arc: ArcView) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + Consistent + Parametric, { // If the edge has no neighbors, then `xa` and `bx` will refer to the // opposite arc of `ab`. In this case, the vertices `a` and `b` should // have no leading arcs after the removal. The cache will have its `xa` // and `bx` fields set to `None` in this case. let ab = arc.key(); let ba = arc.opposite_arc().key(); let xa = arc.previous_arc().key(); let bx = arc.next_arc().key(); let cache = if let Some(face) = arc.face() { Some(FaceRemoveCache::from_face(face)?) } else { None }; Ok(ArcRemoveCache { ab, xa: if xa != ba { Some(xa) } else { None }, bx: if bx != ba { Some(bx) } else { None }, cache, }) } } pub struct EdgeRemoveCache { a: VertexKey, b: VertexKey, ab_ba: EdgeKey, arc: ArcRemoveCache, opposite: ArcRemoveCache, } impl EdgeRemoveCache { pub fn from_arc(arc: ArcView) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + AsStorage>> + Consistent + Parametric, { let a = arc.source_vertex().key(); let b = arc.destination_vertex().key(); let ab_ba = arc.edge().key(); Ok(EdgeRemoveCache { a, b, ab_ba, arc: ArcRemoveCache::from_arc(arc.to_ref())?, opposite: ArcRemoveCache::from_arc(arc.into_opposite_arc())?, }) } } pub struct EdgeSplitCache { a: VertexKey, b: VertexKey, ab: ArcKey, ba: ArcKey, ab_ba: EdgeKey, } impl EdgeSplitCache { pub fn from_arc(arc: ArcView) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + Parametric, { let opposite = arc .to_ref() .into_reachable_opposite_arc() .ok_or(GraphError::TopologyMalformed)?; let source = opposite .to_ref() .into_reachable_destination_vertex() .ok_or(GraphError::TopologyMalformed)?; let destination = arc .to_ref() .into_reachable_destination_vertex() .ok_or(GraphError::TopologyMalformed)?; let edge = arc .to_ref() .into_reachable_edge() .ok_or(GraphError::TopologyNotFound)?; Ok(EdgeSplitCache { a: source.key(), b: destination.key(), ab: arc.key(), ba: opposite.key(), ab_ba: edge.key(), }) } } pub struct ArcBridgeCache { a: VertexKey, b: VertexKey, c: VertexKey, d: VertexKey, } impl ArcBridgeCache { pub fn from_arc(arc: ArcView, destination: ArcKey) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + Parametric, { let destination: ArcView<_> = arc .to_ref() .rebind(destination) .ok_or(GraphError::TopologyNotFound)?; let a = arc .to_ref() .into_reachable_source_vertex() .ok_or(GraphError::TopologyMalformed)? .key(); let b = arc .to_ref() .into_reachable_destination_vertex() .ok_or(GraphError::TopologyMalformed)? .key(); let c = destination .to_ref() .into_reachable_source_vertex() .ok_or(GraphError::TopologyMalformed)? .key(); let d = destination .to_ref() .into_reachable_destination_vertex() .ok_or(GraphError::TopologyMalformed)? .key(); // Ensure that existing interior arcs are boundaries. for arc in [a, b, c, d] .iter() .cloned() .perimeter() .flat_map(|ab| -> Option> { arc.to_ref().rebind(ab.into()) }) { if !arc.is_boundary_arc() { return Err(GraphError::TopologyConflict); } } Ok(ArcBridgeCache { a, b, c, d }) } pub fn from_storage( storage: B, source: ArcKey, destination: ArcKey, ) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + Parametric, { ArcBridgeCache::from_arc( ArcView::bind(storage, source).ok_or(GraphError::TopologyNotFound)?, destination, ) } } pub struct ArcExtrudeCache { ab: ArcKey, } impl ArcExtrudeCache { pub fn from_arc(arc: ArcView) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + Consistent + Parametric, { if !arc.is_boundary_arc() { Err(GraphError::TopologyConflict) } else { Ok(ArcExtrudeCache { ab: arc.key() }) } } } pub fn get_or_insert_with( mut mutation: N, endpoints: (VertexKey, VertexKey), f: F, ) -> Result where N: AsMut>, P: Mode, P::Graph: Mutable, F: FnOnce() -> CompositeEdgeData>, { fn get_or_insert_arc( mut mutation: N, endpoints: (VertexKey, VertexKey), data: as GraphData>::Arc, ) -> (Option, ArcKey) where N: AsMut>, P: Mode, P::Graph: Mutable, { let (a, _) = endpoints; let ab = endpoints.into(); if let Some(arc) = mutation.as_mut().storage.0.as_storage().get(&ab) { (arc.edge, ab) } else { mutation .as_mut() .storage .0 .as_storage_mut() .insert_with_key(&ab, Arc::new(data)); let _ = mutation.as_mut().connect_outgoing_arc(a, ab); (None, ab) } } let data = f(); let (a, b) = endpoints; let (e1, ab) = get_or_insert_arc(mutation.as_mut(), (a, b), data.1 .0); let (e2, ba) = get_or_insert_arc(mutation.as_mut(), (b, a), data.1 .1); match (e1, e2) { (Some(e1), Some(e2)) if e1 == e2 => Ok((e1, (ab, ba))), (None, None) => { let ab_ba = mutation .as_mut() .storage .1 .as_storage_mut() .insert(Edge::new(ab, data.0)); mutation.as_mut().connect_arc_to_edge(ab, ab_ba)?; mutation.as_mut().connect_arc_to_edge(ba, ab_ba)?; Ok((ab_ba, (ab, ba))) } // It should not be possible to insert or remove individual arcs and // mutations should not allow arcs to be assigned to edges // independently of their opposite arcs. _ => Err(GraphError::TopologyMalformed), } } // TODO: Removing arcs must also remove disjoint vertices. More importantly, the // leading arc of vertices may be invalidated by this operation and must // be healed. This code does not handle these cases, and so can become // inconsistent. pub fn remove( mut mutation: N, cache: EdgeRemoveCache, ) -> Result>, GraphError> where N: AsMut>, P: Mode, P::Graph: Mutable, { fn remove_arc( mut mutation: N, cache: ArcRemoveCache, ) -> Result>, GraphError> where N: AsMut>, P: Mode, P::Graph: Mutable, { let ArcRemoveCache { ab, cache, .. } = cache; if let Some(cache) = cache { face::remove(mutation.as_mut(), cache)?; } mutation .as_mut() .storage .0 .as_storage_mut() .remove(&ab) .ok_or(GraphError::TopologyNotFound) } let EdgeRemoveCache { a, b, ab_ba, arc, opposite, } = cache; // Connect each vertex to a remaining outgoing edge. if let Some(ax) = opposite.bx { mutation.as_mut().connect_outgoing_arc(a, ax)?; } if let Some(bx) = arc.bx { mutation.as_mut().connect_outgoing_arc(b, bx)?; } // Connect previous and next arcs across the edge to be removed. if let (Some(xa), Some(ax)) = (arc.xa, opposite.bx) { mutation.as_mut().connect_adjacent_arcs(xa, ax)?; } if let (Some(xb), Some(bx)) = (opposite.xa, arc.bx) { mutation.as_mut().connect_adjacent_arcs(xb, bx)?; } let edge = mutation .as_mut() .storage .1 .as_storage_mut() .remove(&ab_ba) .ok_or(GraphError::TopologyNotFound)?; Ok(( edge, ( remove_arc(mutation.as_mut(), arc)?, remove_arc(mutation.as_mut(), opposite)?, ), )) } pub fn split_with( mut mutation: N, cache: EdgeSplitCache, f: F, ) -> Result where N: AsMut>, P: Mode, P::Graph: Mutable, F: FnOnce() -> as GraphData>::Vertex, { fn remove(mut mutation: N, ab: ArcKey) -> Result>, GraphError> where N: AsMut>, P: Mode, P::Graph: Mutable, { // TODO: Is is probably more correct to disconnect the source vertex // from its leading arc. However, this interacts poorly with // `split_at_vertex`, and on the second pass will orphan the // vertex B. // // let (a, _) = ab.into(); // mutation.as_mut().disconnect_outgoing_arc(a)?; let xa = mutation.as_mut().disconnect_previous_arc(ab)?; let bx = mutation.as_mut().disconnect_next_arc(ab)?; let mut arc = mutation .as_mut() .storage .0 .as_storage_mut() .remove(&ab) .unwrap(); // Restore the connectivity of the arc. The mutations will clear this // data, because it is still a part of the mesh at that point. arc.previous = xa; arc.next = bx; Ok(arc) } fn split_at_vertex( mut mutation: N, a: VertexKey, b: VertexKey, m: VertexKey, ab: ArcKey, edge_data: as GraphData>::Edge, ) -> Result<(ArcKey, ArcKey), GraphError> where N: AsMut>, P: Mode, P::Graph: Mutable, { // Remove the arc and insert two truncated arcs in its place. let Arc { next, previous, face, data: arc_data, .. } = remove(mutation.as_mut(), ab)?; let data = (edge_data, (arc_data.clone(), arc_data)); let am = get_or_insert_with(mutation.as_mut(), (a, m), || data.clone()) .map(|(_, (am, _))| am)?; let mb = get_or_insert_with(mutation.as_mut(), (m, b), || data).map(|(_, (mb, _))| mb)?; // Connect the new arcs to each other and their leading arcs. mutation.as_mut().connect_adjacent_arcs(am, mb)?; if let Some(xa) = previous { mutation.as_mut().connect_adjacent_arcs(xa, am)?; } if let Some(bx) = next { mutation.as_mut().connect_adjacent_arcs(mb, bx)?; } // Update the associated face, if any, because it may refer to the // removed arc. if let Some(abc) = face { mutation.as_mut().connect_face_to_arc(am, abc)?; mutation.as_mut().connect_arc_to_face(am, abc)?; mutation.as_mut().connect_arc_to_face(mb, abc)?; } Ok((am, mb)) } let EdgeSplitCache { a, b, ab, ba, ab_ba, } = cache; let m = vertex::insert(mutation.as_mut(), f()); // Remove the edge. let Edge { data, .. } = mutation .as_mut() .storage .1 .as_storage_mut() .remove(&ab_ba) .ok_or(GraphError::TopologyMalformed)?; // Split the arcs. split_at_vertex(mutation.as_mut(), a, b, m, ab, data.clone())?; split_at_vertex(mutation.as_mut(), b, a, m, ba, data)?; Ok(m) } pub fn bridge(mut mutation: N, cache: ArcBridgeCache) -> Result where N: AsMut>, P: Mode, P::Graph: Mutable, { let ArcBridgeCache { a, b, c, d } = cache; let cache = FaceInsertCache::from_storage(mutation.as_mut(), &[a, b, c, d])?; face::insert_with(mutation.as_mut(), cache, Default::default) } // LINT: These names follow a convention, mainly concerning vertices. #[expect(clippy::many_single_char_names)] pub fn extrude_with( mut mutation: N, cache: ArcExtrudeCache, f: F, ) -> Result where N: AsMut>, P: Mode, P::Graph: Mutable, F: Fn( as GraphData>::Vertex) -> as GraphData>::Vertex, { let ArcExtrudeCache { ab } = cache; let (c, d) = { let (a, b) = ab.into(); let c = VertexView::bind(mutation.as_mut(), b) .ok_or(GraphError::TopologyNotFound)? .get() .clone(); let d = VertexView::bind(mutation.as_mut(), a) .ok_or(GraphError::TopologyNotFound)? .get() .clone(); (f(c), f(d)) }; let c = vertex::insert(mutation.as_mut(), c); let d = vertex::insert(mutation.as_mut(), d); let cd = get_or_insert_with(mutation.as_mut(), (c, d), Default::default).map(|(_, (cd, _))| cd)?; let cache = ArcBridgeCache::from_storage(mutation.as_mut(), ab, cd)?; bridge(mutation, cache).map(|_| cd) } ================================================ FILE: plexus/src/graph/mutation/face.rs ================================================ use smallvec::SmallVec; use std::borrow::Borrow; use std::collections::{HashMap, HashSet}; use std::ops::{Deref, DerefMut}; use crate::entity::borrow::Reborrow; use crate::entity::storage::prelude::*; use crate::entity::storage::{AsStorage, AsStorageMut, Fuse, StorageTarget}; use crate::entity::view::{Bind, ClosedView, Rebind, Unbind}; use crate::graph::core::Core; use crate::graph::data::{Data, GraphData, Parametric}; use crate::graph::edge::{Arc, ArcKey, ArcView, Edge}; use crate::graph::face::{Face, FaceKey, FaceView, ToRing}; use crate::graph::mutation::edge::{self, ArcBridgeCache, EdgeMutation}; use crate::graph::mutation::{vertex, Consistent, Immediate, Mode, Mutable, Mutation}; use crate::graph::vertex::{Vertex, VertexKey, VertexView}; use crate::graph::GraphError; use crate::transact::{Bypass, Transact}; use crate::{DynamicArity, IteratorExt as _}; type ModalCore

= Core< Data<

::Graph>,

::VertexStorage,

::ArcStorage,

::EdgeStorage,

::FaceStorage, >; pub type RefCore<'a, G> = Core< G, &'a StorageTarget<'a, Vertex>, &'a StorageTarget<'a, Arc>, &'a StorageTarget<'a, Edge>, &'a StorageTarget<'a, Face>, >; pub struct FaceMutation

where P: Mode, { inner: EdgeMutation

, storage: P::FaceStorage, } impl

FaceMutation

where P: Mode, { pub fn to_ref_core(&self) -> RefCore> { self.inner.to_ref_core().fuse(self.storage.as_storage()) } // TODO: Should there be a distinction between `connect_face_to_arc` and // `connect_arc_to_face`? pub fn connect_face_to_arc(&mut self, ab: ArcKey, abc: FaceKey) -> Result<(), GraphError> { self.with_face_mut(abc, |face| face.arc = ab) } fn connect_face_interior(&mut self, arcs: &[ArcKey], face: FaceKey) -> Result<(), GraphError> { for (ab, bc) in arcs.iter().cloned().perimeter() { self.connect_adjacent_arcs(ab, bc)?; self.connect_arc_to_face(ab, face)?; } Ok(()) } fn disconnect_face_interior(&mut self, arcs: &[ArcKey]) -> Result<(), GraphError> { for ab in arcs { self.disconnect_arc_from_face(*ab)?; } Ok(()) } fn connect_face_exterior( &mut self, arcs: &[ArcKey], connectivity: ( HashMap>, HashMap>, ), ) -> Result<(), GraphError> { let (incoming, outgoing) = connectivity; for ab in arcs.iter().cloned() { let (a, b) = ab.into(); let ba = ab.into_opposite(); let adjacent = { let core = &self.to_ref_core(); if ArcView::bind(core, ba) .ok_or(GraphError::TopologyMalformed)? .is_boundary_arc() { // The next arc of BA is the outgoing arc of the destination // vertex A that is also a boundary arc or, if there is no // such outgoing arc, the next exterior arc of the face. The // previous arc is similar. let ax = outgoing[&a] .iter() .cloned() .flat_map(|ax| ArcView::bind(core, ax)) .find(|next| next.is_boundary_arc()) .or_else(|| { ArcView::bind(core, ab) .and_then(|arc| arc.into_reachable_previous_arc()) .and_then(|previous| previous.into_reachable_opposite_arc()) }) .map(|next| next.key()); let xb = incoming[&b] .iter() .cloned() .flat_map(|xb| ArcView::bind(core, xb)) .find(|previous| previous.is_boundary_arc()) .or_else(|| { ArcView::bind(core, ab) .and_then(|arc| arc.into_reachable_next_arc()) .and_then(|next| next.into_reachable_opposite_arc()) }) .map(|previous| previous.key()); ax.into_iter().zip(xb.into_iter()).next() } else { None } }; if let Some((ax, xb)) = adjacent { self.connect_adjacent_arcs(ba, ax)?; self.connect_adjacent_arcs(xb, ba)?; } } Ok(()) } fn with_face_mut(&mut self, abc: FaceKey, mut f: F) -> Result where F: FnMut(&mut Face>) -> T, { let face = self .storage .as_storage_mut() .get_mut(&abc) .ok_or(GraphError::TopologyNotFound)?; Ok(f(face)) } } impl

AsStorage>> for FaceMutation

where P: Mode, { fn as_storage(&self) -> &StorageTarget>> { self.storage.as_storage() } } impl Bypass>> for FaceMutation> where M: Parametric, { fn bypass(self) -> Self::Commit { let FaceMutation { inner, storage: faces, .. } = self; inner.bypass().fuse(faces) } } // TODO: This is a hack. Replace this with delegation. impl

Deref for FaceMutation

where P: Mode, { type Target = EdgeMutation

; fn deref(&self) -> &Self::Target { &self.inner } } impl

DerefMut for FaceMutation

where P: Mode, { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } impl

From> for FaceMutation

where P: Mode, { fn from(core: ModalCore

) -> Self { let (vertices, arcs, edges, faces) = core.unfuse(); FaceMutation { storage: faces, inner: Core::empty().fuse(vertices).fuse(arcs).fuse(edges).into(), } } } impl Transact>> for FaceMutation> where M: Parametric, { type Commit = ModalCore>; type Abort = (); type Error = GraphError; // TODO: Ensure that faces are in a consistent state. fn commit(self) -> Result { let FaceMutation { inner, storage: faces, .. } = self; inner.commit().map(move |core| core.fuse(faces)) } fn abort(self) -> Self::Abort {} } pub struct FaceInsertCache { perimeter: SmallVec<[VertexKey; 4]>, connectivity: ( HashMap>, HashMap>, ), } impl FaceInsertCache { pub fn from_ring(ring: T) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + Consistent + Parametric, T: ToRing, { let ring = ring.into_ring(); let (storage, _) = ring.arc().unbind(); FaceInsertCache::from_storage(storage, ring.vertices().keys()) } pub fn from_storage(storage: B, perimeter: K) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + Parametric, K: IntoIterator, K::Item: Borrow, { let perimeter = perimeter .into_iter() .map(|key| *key.borrow()) .collect::>(); let arity = perimeter.len(); let set = perimeter.iter().cloned().collect::>(); if set.len() != arity { // Vertex keys are not unique. return Err(GraphError::TopologyMalformed); } let storage = storage.reborrow(); let vertices = perimeter .iter() .cloned() .map(|key| VertexView::bind(storage, key).ok_or(GraphError::TopologyNotFound)) .collect::, _>>()?; for (previous, next) in perimeter .iter() .cloned() .perimeter() .map(|keys| ArcView::bind(storage, keys.into())) .perimeter() { if let Some(previous) = previous { if previous.face.is_some() { // A face already occupies an interior arc. return Err(GraphError::TopologyConflict); } // Let the previous arc be AB and the next arc be BC. The // vertices A, B, and C lie within the implied ring in order. // // If BC does not exist and AB is adjacent to some arc BX, then // X must not lie within the implied ring (the ordered set of // vertices given to this function). If X is within the path, // then BX must bisect the implied ring (because X cannot be C). if next.is_none() { if let Some(next) = previous.into_reachable_next_arc() { let (_, destination) = next.key().into(); if set.contains(&destination) { return Err(GraphError::TopologyConflict); } } } } } let mut incoming = HashMap::with_capacity(arity); let mut outgoing = HashMap::with_capacity(arity); for vertex in vertices { let key = vertex.key(); incoming.insert(key, vertex.reachable_incoming_arcs().keys().collect()); outgoing.insert(key, vertex.reachable_outgoing_arcs().keys().collect()); } Ok(FaceInsertCache { perimeter, connectivity: (incoming, outgoing), }) } } pub struct FaceRemoveCache { abc: FaceKey, arcs: Vec, } impl FaceRemoveCache { // TODO: Should this require consistency? pub fn from_face(face: FaceView) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + Consistent + Parametric, { let arcs = face.adjacent_arcs().keys().collect(); Ok(FaceRemoveCache { abc: face.key(), arcs, }) } } pub struct FaceSplitCache { cache: FaceRemoveCache, left: Vec, right: Vec, } impl FaceSplitCache { pub fn from_face( face: FaceView, source: VertexKey, destination: VertexKey, ) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + Consistent + Parametric, { let perimeter = |face: FaceView<_>| { face.adjacent_vertices() .keys() .collect::>() .into_iter() .cycle() }; // This closure determines if any arcs in the given perimeter are // connected to a face other than the face initiating the split. This // may occur if an adjacent face shares two or more edges with the // initiating face and the source and destination vertices of the split // lie along that boundary. let is_intersecting = |perimeter: &[_]| { for (a, b) in perimeter.iter().cloned().perimeter() { let ab = (a, b).into(); if let Some(arc) = Rebind::<_, ArcView<_>>::rebind(face.to_ref(), ab) { if let Some(key) = arc.face().map(|face| face.key()) { if key != face.key() { return true; } } } } false }; face.ring() .shortest_logical_metric(source, destination) .and_then(|metric| { if metric <= 1 { Err(GraphError::TopologyMalformed) } else { Ok(()) } })?; // Note that the winding of the perimeters must be relatively oriented. let left = perimeter(face.to_ref()) .perimeter() .skip_while(|(_, b)| *b != source) .take_while(|(a, _)| *a != destination) .map(|(_, b)| b) .collect::>(); let right = perimeter(face.to_ref()) .perimeter() .skip_while(|(_, b)| *b != destination) .take_while(|(a, _)| *a != source) .map(|(_, b)| b) .collect::>(); if is_intersecting(&left) || is_intersecting(&right) { return Err(GraphError::TopologyConflict); } Ok(FaceSplitCache { cache: FaceRemoveCache::from_face(face)?, left, right, }) } } pub struct FacePokeCache { vertices: Vec, cache: FaceRemoveCache, } impl FacePokeCache { pub fn from_face(face: FaceView) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + Consistent + Parametric, { let vertices = face.adjacent_vertices().keys().collect(); Ok(FacePokeCache { vertices, cache: FaceRemoveCache::from_face(face)?, }) } } pub struct FaceBridgeCache { source: SmallVec<[ArcKey; 4]>, destination: SmallVec<[ArcKey; 4]>, cache: (FaceRemoveCache, FaceRemoveCache), } impl FaceBridgeCache { pub fn from_face(face: FaceView, destination: FaceKey) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + Consistent + Parametric, { let destination: FaceView<_> = face .to_ref() .rebind(destination) .ok_or(GraphError::TopologyNotFound)?; let cache = ( FaceRemoveCache::from_face(face.to_ref())?, FaceRemoveCache::from_face(destination.to_ref())?, ); // Ensure that the opposite face exists and has the same arity. if face.arity() != destination.arity() { return Err(GraphError::ArityNonUniform); } Ok(FaceBridgeCache { source: face.adjacent_arcs().keys().collect(), destination: destination.adjacent_arcs().keys().collect(), cache, }) } } pub struct FaceExtrudeCache { sources: Vec, cache: FaceRemoveCache, } impl FaceExtrudeCache { pub fn from_face(face: FaceView) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + Consistent + Parametric, { let sources = face.adjacent_vertices().keys().collect(); let cache = FaceRemoveCache::from_face(face)?; Ok(FaceExtrudeCache { sources, cache }) } } // TODO: Should this accept arc data at all? pub fn insert_with( mut mutation: N, cache: FaceInsertCache, f: F, ) -> Result where N: AsMut>, P: Mode, P::Graph: Mutable, F: FnOnce() -> ( as GraphData>::Arc, as GraphData>::Face, ), { let FaceInsertCache { perimeter, connectivity, } = cache; let data = f(); // Insert edges and collect the interior arcs. let arcs = perimeter .iter() .cloned() .perimeter() .map(|(a, b)| { edge::get_or_insert_with(mutation.as_mut(), (a, b), || { (Default::default(), (data.0.clone(), data.0.clone())) }) .map(|(_, (ab, _))| ab) }) .collect::, _>>()?; // Insert the face. let face = mutation .as_mut() .storage .as_storage_mut() .insert(Face::new(arcs[0], data.1)); mutation.as_mut().connect_face_interior(&arcs, face)?; mutation .as_mut() .connect_face_exterior(&arcs, connectivity)?; Ok(face) } // TODO: Does this require a cache (or consistency)? // TODO: This may need to be more destructive to maintain consistency. Edges, // arcs, and vertices may also need to be removed. pub fn remove( mut mutation: N, cache: FaceRemoveCache, ) -> Result>, GraphError> where N: AsMut>, P: Mode, P::Graph: Mutable, { let FaceRemoveCache { abc, arcs } = cache; mutation.as_mut().disconnect_face_interior(&arcs)?; let face = mutation .as_mut() .storage .as_storage_mut() .remove(&abc) .ok_or(GraphError::TopologyNotFound)?; Ok(face) } pub fn split(mut mutation: N, cache: FaceSplitCache) -> Result where N: AsMut>, P: Mode, P::Graph: Mutable, { let FaceSplitCache { cache, left, right } = cache; remove(mutation.as_mut(), cache)?; let ab = (left[0], right[0]).into(); let left = FaceInsertCache::from_storage(mutation.as_mut(), left)?; let right = FaceInsertCache::from_storage(mutation.as_mut(), right)?; insert_with(mutation.as_mut(), left, Default::default)?; insert_with(mutation.as_mut(), right, Default::default)?; Ok(ab) } pub fn poke_with( mut mutation: N, cache: FacePokeCache, f: F, ) -> Result where N: AsMut>, P: Mode, P::Graph: Mutable, F: FnOnce() -> as GraphData>::Vertex, { let FacePokeCache { vertices, cache } = cache; let face = remove(mutation.as_mut(), cache)?; let c = vertex::insert(mutation.as_mut(), f()); for (a, b) in vertices.into_iter().perimeter() { let cache = FaceInsertCache::from_storage(mutation.as_mut(), &[a, b, c])?; insert_with(mutation.as_mut(), cache, || { (Default::default(), face.data.clone()) })?; } Ok(c) } pub fn bridge(mut mutation: N, cache: FaceBridgeCache) -> Result<(), GraphError> where N: AsMut>, P: Mode, P::Graph: Mutable, { let FaceBridgeCache { source, destination, cache, } = cache; // Remove the source and destination faces. Pair the topology with edge // data for the source face. remove(mutation.as_mut(), cache.0)?; remove(mutation.as_mut(), cache.1)?; // TODO: Is it always correct to reverse the order of the opposite face's // arcs? // Re-insert the arcs of the faces and bridge the mutual arcs. for (ab, cd) in source.into_iter().zip(destination.into_iter().rev()) { let cache = ArcBridgeCache::from_storage(mutation.as_mut(), ab, cd)?; edge::bridge(mutation.as_mut(), cache)?; } // TODO: Is there any reasonable entity this can return? Ok(()) } pub fn extrude_with( mut mutation: N, cache: FaceExtrudeCache, f: F, ) -> Result where N: AsMut>, P: Mode, P::Graph: Mutable, F: Fn(& as GraphData>::Vertex) -> as GraphData>::Vertex, { let FaceExtrudeCache { sources, cache } = cache; remove(mutation.as_mut(), cache)?; let destinations = { let mutation = &*mutation.as_mut(); sources .iter() .cloned() .flat_map(|a| VertexView::bind(mutation, a)) .map(|source| f(source.get())) .collect::>() }; if sources.len() != destinations.len() { return Err(GraphError::TopologyNotFound); } let destinations = destinations .into_iter() .map(|data| vertex::insert(mutation.as_mut(), data)) .collect::>(); // Use the keys for the existing vertices and the translated geometries to // construct the extruded face and its connective faces. let cache = FaceInsertCache::from_storage(mutation.as_mut(), &destinations)?; let extrusion = insert_with(mutation.as_mut(), cache, Default::default)?; for ((a, c), (b, d)) in sources .into_iter() .zip(destinations.into_iter()) .perimeter() { let cache = FaceInsertCache::from_storage(mutation.as_mut(), &[a, b, d, c])?; // TODO: Split these faces to form triangles. insert_with(mutation.as_mut(), cache, Default::default)?; } Ok(extrusion) } ================================================ FILE: plexus/src/graph/mutation/mod.rs ================================================ pub mod edge; pub mod face; pub mod path; pub mod vertex; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use crate::entity::storage::{AsStorage, AsStorageMut, StorageTarget}; use crate::entity::Entity; use crate::graph::core::OwnedCore; use crate::graph::data::{Data, Parametric}; use crate::graph::edge::{Arc, Edge}; use crate::graph::face::Face; use crate::graph::mutation::face::FaceMutation; use crate::graph::vertex::Vertex; use crate::graph::{GraphData, GraphError}; use crate::transact::{Bypass, Transact}; // TODO: The stable toolchain does not allow a type parameter `G` to be // introduced and bound to the associated type `Mode::Graph::Data`. The // compiler does not seem to consider the types equal, and requires // redundant type bounds on `Mode`'s associated storage types at each // usage. The nightly toolchain already supports this. Reintroduce a // `G: GraphData` type parameter in implementation blocks when this is // fixed. For now, this code uses `Data`. See the following // related issues: // // https://github.com/rust-lang/rust/issues/58231 // https://github.com/rust-lang/rust/issues/70703 // https://github.com/rust-lang/rust/issues/47897 /// Marker trait for graph representations that promise to be in a consistent /// state. /// /// This trait is only implemented by representations that ensure that their /// storage is only ever mutated via the mutation API (and therefore is /// consistent). Note that `Core` does not implement this trait and instead acts /// as a raw container for topological storage that can be freely manipulated. /// /// This trait allows code to make assumptions about the data it operates /// against. For example, views expose an API to user code that assumes that /// topologies are present and therefore unwraps values. pub trait Consistent {} impl Consistent for &'_ T where T: Consistent {} impl Consistent for &'_ mut T where T: Consistent {} pub trait Mode { type Graph: Parametric; type VertexStorage: AsStorageMut>>; type ArcStorage: AsStorageMut>>; type EdgeStorage: AsStorageMut>>; type FaceStorage: AsStorageMut>>; } pub struct Immediate where M: Parametric, { phantom: PhantomData M>, } impl Mode for Immediate where M: Parametric, { type Graph = M; type VertexStorage = > as Entity>::Storage; type ArcStorage = > as Entity>::Storage; type EdgeStorage = > as Entity>::Storage; type FaceStorage = > as Entity>::Storage; } /// Graph mutation. pub struct Mutation

where P: Mode, P::Graph: Consistent + From>> + Into>>, { inner: FaceMutation

, } impl

AsRef for Mutation

where P: Mode, P::Graph: Consistent + From>> + Into>>, { fn as_ref(&self) -> &Self { self } } impl

AsMut for Mutation

where P: Mode, P::Graph: Consistent + From>> + Into>>, { fn as_mut(&mut self) -> &mut Self { self } } impl

AsStorage>> for Mutation

where P: Mode, P::Graph: Consistent + From>> + Into>>, { fn as_storage(&self) -> &StorageTarget>> { self.inner.to_ref_core().unfuse().1 } } impl

AsStorage>> for Mutation

where P: Mode, P::Graph: Consistent + From>> + Into>>, { fn as_storage(&self) -> &StorageTarget>> { self.inner.to_ref_core().unfuse().2 } } impl

AsStorage>> for Mutation

where P: Mode, P::Graph: Consistent + From>> + Into>>, { fn as_storage(&self) -> &StorageTarget>> { self.inner.to_ref_core().unfuse().3 } } impl

AsStorage>> for Mutation

where P: Mode, P::Graph: Consistent + From>> + Into>>, { fn as_storage(&self) -> &StorageTarget>> { self.inner.to_ref_core().unfuse().0 } } impl Bypass for Mutation> where M: Consistent + From>> + Parametric + Into>>, { fn bypass(self) -> Self::Commit { self.inner.bypass().into() } } // TODO: This is a hack. Replace this with delegation. impl

Deref for Mutation

where P: Mode, P::Graph: Consistent + From>> + Into>>, { type Target = FaceMutation

; fn deref(&self) -> &Self::Target { &self.inner } } impl

DerefMut for Mutation

where P: Mode, P::Graph: Consistent + From>> + Into>>, { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } impl From for Mutation> where M: Consistent + From>> + Parametric + Into>>, { fn from(graph: M) -> Self { Mutation { inner: graph.into().into(), } } } impl

Parametric for Mutation

where P: Mode, P::Graph: Consistent + From>> + Into>>, { type Data = Data; } impl Transact for Mutation> where M: Consistent + From>> + Parametric + Into>>, { type Commit = M; type Abort = (); type Error = GraphError; fn commit(self) -> Result { self.inner.commit().map(|core| core.into()) } fn abort(self) -> Self::Abort {} } pub trait Mutable: Consistent + From>> + Parametric + Into>> { } impl Mutable for M where M: Consistent + From> + Parametric + Into>, G: GraphData, { } ================================================ FILE: plexus/src/graph/mutation/path.rs ================================================ use smallvec::SmallVec; use crate::entity::borrow::Reborrow; use crate::entity::storage::AsStorage; use crate::entity::view::Bind; use crate::graph::data::{Data, GraphData, Parametric}; use crate::graph::edge::Arc; use crate::graph::face::{Face, FaceKey}; use crate::graph::mutation::face::{self, FaceInsertCache}; use crate::graph::mutation::vertex; use crate::graph::mutation::{Consistent, Mode, Mutable, Mutation}; use crate::graph::path::Path; use crate::graph::vertex::{Vertex, VertexKey, VertexView}; use crate::graph::GraphError; use crate::IteratorExt as _; pub struct PathExtrudeCache { // Avoid allocations for single arc extrusions. sources: SmallVec<[VertexKey; 2]>, } impl PathExtrudeCache { pub fn from_path(path: Path) -> Result where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + AsStorage>> + Consistent + Parametric, { if path.arcs().any(|arc| !arc.is_boundary_arc()) { Err(GraphError::TopologyMalformed) } else { Ok(PathExtrudeCache { sources: path.vertices().keys().collect(), }) } } } pub fn extrude_contour_with( mut mutation: N, cache: PathExtrudeCache, f: F, ) -> Result where N: AsMut>, P: Mode, P::Graph: Mutable, F: Fn(& as GraphData>::Vertex) -> as GraphData>::Vertex, { let PathExtrudeCache { sources } = cache; let destinations: SmallVec<[_; 2]> = sources .iter() .cloned() .rev() .map(|source| -> Result<_, GraphError> { let vertex = VertexView::bind(mutation.as_mut(), source).ok_or(GraphError::TopologyNotFound)?; let data = f(vertex.get()); Ok(vertex::insert(mutation.as_mut(), data)) }) .collect::>()?; let cache = FaceInsertCache::from_storage(mutation.as_mut(), sources.into_iter().chain(destinations))?; face::insert_with(mutation.as_mut(), cache, Default::default) } ================================================ FILE: plexus/src/graph/mutation/vertex.rs ================================================ use crate::entity::borrow::Reborrow; use crate::entity::storage::prelude::*; use crate::entity::storage::{AsStorage, AsStorageMut, Fuse, StorageTarget}; use crate::graph::core::Core; use crate::graph::data::{Data, GraphData, Parametric}; use crate::graph::edge::ArcKey; use crate::graph::mutation::edge::{self, EdgeRemoveCache}; use crate::graph::mutation::{Consistent, Immediate, Mode, Mutable, Mutation}; use crate::graph::vertex::{Vertex, VertexKey, VertexView}; use crate::graph::GraphError; use crate::transact::{Bypass, Transact}; type ModalCore

= Core::Graph>,

::VertexStorage, (), (), ()>; type RefCore<'a, G> = Core>, (), (), ()>; pub struct VertexMutation

where P: Mode, { storage: P::VertexStorage, } impl

VertexMutation

where P: Mode, { pub fn to_ref_core(&self) -> RefCore> { Core::empty().fuse(self.storage.as_storage()) } pub fn connect_outgoing_arc(&mut self, a: VertexKey, ab: ArcKey) -> Result<(), GraphError> { self.with_vertex_mut(a, |vertex| vertex.arc = Some(ab)) } pub fn disconnect_outgoing_arc(&mut self, a: VertexKey) -> Result, GraphError> { self.with_vertex_mut(a, |vertex| vertex.arc.take()) } fn with_vertex_mut(&mut self, a: VertexKey, mut f: F) -> Result where F: FnMut(&mut Vertex>) -> T, { let vertex = self .storage .as_storage_mut() .get_mut(&a) .ok_or(GraphError::TopologyNotFound)?; Ok(f(vertex)) } } impl

AsStorage>> for VertexMutation

where P: Mode, { fn as_storage(&self) -> &StorageTarget>> { self.storage.as_storage() } } impl Bypass>> for VertexMutation> where M: Parametric, { fn bypass(self) -> Self::Commit { let VertexMutation { storage: vertices, .. } = self; Core::empty().fuse(vertices) } } impl

From> for VertexMutation

where P: Mode, { fn from(core: ModalCore

) -> Self { let (vertices, ..) = core.unfuse(); VertexMutation { storage: vertices } } } impl Transact>> for VertexMutation> where M: Parametric, { type Commit = ModalCore>; type Abort = (); type Error = GraphError; fn commit(self) -> Result { let VertexMutation { storage: vertices, .. } = self; // In a consistent graph, all vertices must have a leading arc. for (_, vertex) in vertices.as_storage().iter() { if vertex.arc.is_none() { return Err(((), GraphError::TopologyMalformed)); } } Ok(Core::empty().fuse(vertices)) } fn abort(self) -> Self::Abort {} } pub struct VertexRemoveCache { cache: Vec, } impl VertexRemoveCache { pub fn from_vertex(vertex: VertexView) -> Result where B: Reborrow, B::Target: AsStorage>> + Consistent + Parametric, { let _ = vertex; unimplemented!() } } pub fn insert(mut mutation: N, data: as GraphData>::Vertex) -> VertexKey where N: AsMut>, P: Mode, P::Graph: Mutable, { mutation .as_mut() .storage .as_storage_mut() .insert(Vertex::new(data)) } pub fn remove( mut mutation: N, cache: VertexRemoveCache, ) -> Result>, GraphError> where N: AsMut>, P: Mode, P::Graph: Mutable, { let VertexRemoveCache { cache } = cache; for cache in cache { edge::remove(mutation.as_mut(), cache)?; } unimplemented!() } ================================================ FILE: plexus/src/graph/path.rs ================================================ use itertools::Itertools; use std::borrow::{Borrow, Cow}; use std::cmp; use std::collections::{HashSet, VecDeque}; use crate::entity::borrow::{Reborrow, ReborrowInto}; use crate::entity::storage::AsStorage; use crate::entity::view::{Bind, ClosedView, Unbind, View}; use crate::geometry::Metric; use crate::graph::data::{Data, GraphData, Parametric}; use crate::graph::edge::{Arc, ArcKey, ArcView, Edge}; use crate::graph::face::{Face, FaceView, Ring}; use crate::graph::mutation::path::{self, PathExtrudeCache}; use crate::graph::mutation::{self, Consistent, Immediate, Mutable}; use crate::graph::vertex::{Vertex, VertexKey, VertexView}; use crate::graph::{GraphError, OptionExt as _, ResultExt as _, Selector}; use crate::transact::{BypassOrCommit, Mutate}; use crate::IteratorExt as _; type Mutation = mutation::Mutation>; /// Non-intersecting path. /// /// A path is an ordered set of vertices that are joined by arcs. Paths are /// notated as either sequences or sets. An open path over vertices $A$, $B$, /// and $C$ is notated $\overrightarrow{(A,B,C)}$ and a closed path over the /// same vertices is notated $\overrightarrow{\\{A,B,C\\}}$. /// /// `Path` represents non-intersecting paths of the form /// $\overrightarrow{(A,\cdots,B)}$, where $A$ is the _back_ of the path and $B$ /// is the _front_ of the path. Note that closed paths are always of the form /// $\overrightarrow{(A,\cdots,A)}$, where the back and front vertices are both /// $A$ (the same). /// /// `Path` maintains an ordered set of keys and uses copy-on-write semantics to /// avoid allocations and copies. #[derive(Clone)] pub struct Path<'k, B> where B: Reborrow, B::Target: AsStorage>> + AsStorage>> + Consistent + Parametric, { keys: Cow<'k, VecDeque>, storage: B, } impl Path<'static, B> where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { // Paths bind multiple keys to storage and so do not support view APIs. // This bespoke `bind` function ensures that the path is not empty and that // the topology forms a non-intersecting path. pub(in crate::graph) fn bind(storage: B, keys: I) -> Result where I: IntoIterator, I::Item: Borrow, { let mut keys = keys.into_iter().map(|key| *key.borrow()); let a = keys.next().ok_or(GraphError::TopologyMalformed)?; let b = keys.next().ok_or(GraphError::TopologyMalformed)?; let ab = (a, b).into(); ArcView::bind(storage.reborrow(), ab).ok_or(GraphError::TopologyNotFound)?; let mut path = Path { keys: Cow::Owned([ab].into_iter().collect()), storage, }; for key in keys { path.push_front(key)?; } Ok(path) } fn bind_unchecked(storage: B, keys: I) -> Self where I: IntoIterator, I::Item: Borrow, { let keys = Cow::Owned(keys.into_iter().map(|key| *key.borrow()).collect()); Path { keys, storage } } } impl Path<'_, B> where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn to_ref(&self) -> Path<&M> { Path { storage: self.storage.reborrow(), keys: Cow::Borrowed(&self.keys), } } /// Converts the path into its opposite path. pub fn into_opposite_path(self) -> Path<'static, B> { let Path { keys, storage } = self; Path::bind_unchecked( storage, keys.iter().cloned().rev().map(|ab| ab.into_opposite()), ) } /// Gets the opposite path. pub fn opposite_path(&self) -> Path<'static, &M> { self.to_ref().into_opposite_path() } /// Pushes a vertex onto the back of the path. /// /// The back of a path $\overrightarrow{(A,\cdots)}$ is the vertex $A$. /// This is the source vertex of the first arc that forms the path. /// /// The given vertex must be a source vertex of an arc formed with the back /// of the path. That is, if the given vertex is $X$, then /// $\overrightarrow{XA}$ must exist. /// /// Returns the key of the arc $\overrightarrow{XA}$ inserted into the path /// using the given source vertex $X$. /// /// # Errors /// /// Returns an error if the path is closed, the given vertex is not found, /// or the given vertex does not form an arc with the back of the path. pub fn push_back( &mut self, destination: impl Into>, ) -> Result { if self.is_closed() { return Err(GraphError::TopologyMalformed); } let back = self.back(); let xa = match destination.into() { Selector::ByKey(key) => back .incoming_arcs() .find(|arc| arc.into_source_vertex().key() == key) .ok_or(GraphError::TopologyMalformed)? .key(), Selector::ByIndex(index) => { let x = back .adjacent_vertices() .keys() .nth(index) .ok_or(GraphError::TopologyNotFound)?; (x, back.key()).into() } }; let (x, _) = xa.into(); // Do not allow intersections unless they form a loop with the first // vertex in the path (this iteration skips the vertex at the front of // the path). let is_intersecting = self .arcs() .map(|arc| arc.into_source_vertex()) .keys() .any(|key| key == x); if is_intersecting { Err(GraphError::TopologyMalformed) } else { self.keys.to_mut().push_back(xa); Ok(xa) } } /// Pushes the source vertex of the previous arc onto the back of the path. pub fn push_previous_arc(&mut self) -> Result { let key = *self.keys.to_mut().back().expect("empty path"); let key = ArcView::from(View::bind_unchecked(self.storage.reborrow(), key)) .into_previous_arc() .into_source_vertex() .key(); self.push_back(key) } /// Pops a vertex from the back of the path. pub fn pop_back(&mut self) -> Option { // Empty paths are forbidden. if self.keys.len() > 1 { self.keys.to_mut().pop_back() } else { None } } /// Pushes a vertex onto the front of the path. /// /// The front of a path $\overrightarrow{(\cdots,B)}$ is the vertex $B$. /// This is the destination vertex of the last arc that forms the path. /// /// The given vertex must be a destination vertex of an arc formed with the /// front of the path. That is, if the given vertex is $X$, then /// $\overrightarrow{BX}$ must exist. /// /// Returns the key of the arc $\overrightarrow{BX}$ inserted into the path /// using the given source vertex $X$. /// /// # Errors /// /// Returns an error if the path is closed, the given vertex is not found, /// or the given vertex does not form an arc with the front of the path. pub fn push_front( &mut self, destination: impl Into>, ) -> Result { if self.is_closed() { return Err(GraphError::TopologyMalformed); } let front = self.front(); let bx = match destination.into() { Selector::ByKey(key) => front .outgoing_arcs() .find(|arc| arc.into_destination_vertex().key() == key) .ok_or(GraphError::TopologyMalformed)? .key(), Selector::ByIndex(index) => { let x = front .adjacent_vertices() .keys() .nth(index) .ok_or(GraphError::TopologyNotFound)?; (front.key(), x).into() } }; let (_, x) = bx.into(); // Do not allow intersections unless they form a loop with the first // vertex in the path (this iteration skips the vertex at the back of // the path). let is_intersecting = self .arcs() .map(|arc| arc.into_destination_vertex()) .keys() .any(|key| key == x); if is_intersecting { Err(GraphError::TopologyMalformed) } else { self.keys.to_mut().push_front(bx); Ok(bx) } } /// Pushes the destination vertex of the next arc onto the front of the /// path. pub fn push_next_arc(&mut self) -> Result { let key = *self.keys.front().expect("empty path"); let key = ArcView::from(View::bind_unchecked(self.storage.reborrow(), key)) .into_next_arc() .into_destination_vertex() .key(); self.push_front(key) } /// Pops a vertex from the front of the path. pub fn pop_front(&mut self) -> Option { // Empty paths are forbidden. if self.keys.len() > 1 { self.keys.to_mut().pop_front() } else { None } } /// Gets the vertex at the back of the path. pub fn back(&self) -> VertexView<&M> { let (key, _) = self.terminals(); View::<_, Vertex<_>>::bind_unchecked(self.storage.reborrow(), key).into() } /// Gets the vertex at the front of the path. pub fn front(&self) -> VertexView<&M> { let (_, key) = self.terminals(); View::<_, Vertex<_>>::bind_unchecked(self.storage.reborrow(), key).into() } pub fn shortest_metric_with( &self, from: impl Into>, to: impl Into>, f: F, ) -> Result where Q: Metric, F: Fn(VertexView<&M>, VertexView<&M>) -> Q, { self.shortest_subpath_terminals(from, to).map(|(from, to)| { // A cycle is needed for closed paths. Note that if the path is // open, then the vertex keys must not wrap over the terminals here. truncate(self.arcs().cycle(), from, to).fold(Q::zero(), |metric, arc| { metric + f(arc.source_vertex(), arc.destination_vertex()) }) }) } /// Returns `true` if the path is open. /// /// An _open path_ is a path that terminates and does **not** form a loop. pub fn is_open(&self) -> bool { !self.is_closed() } /// Returns `true` if the path is closed. /// /// A _closed path_ is a path that forms a loop by starting and ending at /// the same vertex. pub fn is_closed(&self) -> bool { let (a, b) = self.terminals(); a == b } fn terminals(&self) -> (VertexKey, VertexKey) { let (a, _) = self.keys.back().cloned().expect("empty path").into(); let (_, b) = self.keys.front().cloned().expect("empty path").into(); (a, b) } fn shortest_subpath_terminals( &self, from: impl Into>, to: impl Into>, ) -> Result<(VertexKey, VertexKey), GraphError> { let index_key = |selector| { match selector { Selector::ByKey(key) => self .vertices() .find_position(|vertex| vertex.key() == key) .map(|(index, _)| (index, key)), Selector::ByIndex(index) => self .vertices() .nth(index) .map(|vertex| (index, vertex.key())), } .ok_or(GraphError::TopologyNotFound) }; let (i, from) = index_key(from.into())?; let (j, to) = index_key(to.into())?; if self.is_open() { // Reorder the vertex keys if they oppose the direction of the open // path. Ok(if i > j { (to, from) } else { (from, to) }) } else { // Reorder the vertex keys if they form the longer closed path. let n = self.keys.len() / 2; Ok(if (cmp::max(i, j) - cmp::min(i, j)) > n { (to, from) } else { (from, to) }) } } } impl<'k, 'a, B, M, G> Path<'k, B> where B: ReborrowInto<'a, Target = M>, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Converts a mutable view into an immutable view. pub fn into_ref(self) -> Path<'k, &'a M> { let Path { keys, storage, .. } = self; Path { keys, storage: storage.reborrow_into(), } } /// Splits the path into two immutable paths at the given vertex. /// /// Given a path $\overrightarrow{(A,\cdots,M,\cdots,B)}$, splitting at the /// vertex $M$ results in the paths $\overrightarrow{(A,\cdots,M)}$ and /// $\overrightarrow{(M,\cdots,B)}$. /// /// **Splitting a path does not mutate its graph in any way** (unlike /// [`ArcView::split_with`] or [`FaceView::split`], for example). To split a /// graph along a path (and thus mutate the graph) use /// [`MeshGraph::split_at_path`]. /// /// It is not possible to split a path at its back or front vertices. /// /// # Errors /// /// Returns an error if the given vertex cannot be found or the path cannot /// be split at that vertex. /// /// [`ArcView::split_with`]: crate::graph::ArcView::split_with /// [`FaceView::split`]: crate::graph::FaceView::split /// [`MeshGraph::split_at_path`]: crate::graph::MeshGraph::split_at_path pub fn split( self, at: impl Into>, ) -> Result<(Path<'static, &'a M>, Path<'static, &'a M>), GraphError> { let index = at.into().index_or_else(|key| { self.vertices() .keys() .enumerate() .find(|(_, a)| *a == key) .map(|(n, _)| n) .ok_or(GraphError::TopologyNotFound) })?; if index == 0 || index >= self.keys.len() { return Err(GraphError::TopologyMalformed); } let Path { keys, storage, .. } = self.into_ref(); let mut right = keys.into_owned(); let left = right.split_off(index); Ok(( Path { keys: Cow::Owned(left), storage, }, Path { keys: Cow::Owned(right), storage, }, )) } } impl Path<'_, B> where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Gets an iterator over the vertices in the path. pub fn vertices(&self) -> impl Clone + Iterator> { let (key, _) = self.terminals(); Some(key) .into_iter() .chain(self.keys.iter().cloned().rev().map(|key| { let (_, key) = key.into(); key })) .map(move |key| View::bind_unchecked(self.storage.reborrow(), key)) .map(From::from) } /// Gets an iterator over the arcs in the path. pub fn arcs(&self) -> impl Clone + ExactSizeIterator> { self.keys .iter() .rev() .cloned() .map(move |key| View::bind_unchecked(self.storage.reborrow(), key)) .map(From::from) } } impl<'a, M, G> Path<'_, &'a mut M> where M: AsStorage> + AsStorage> + AsStorage> + AsStorage> + Default + Mutable, G: GraphData, { /// Extrudes the contour of a boundary path. /// /// A path is a boundary path if all of its arcs are boundary arcs. /// Extruding the path transforms the vertices along the path in order using /// the given function and inserts a face between the path and its extruded /// contour. /// /// Unlike extruding individual arcs, extruding the contour of a path /// inserts a single face in which all involved arcs participate. /// /// Returns the inserted face. /// /// # Errors /// /// Returns an error if the path is not a boundary path. pub fn extrude_contour_with(self, f: F) -> Result, GraphError> where F: Fn(&G::Vertex) -> G::Vertex, { let cache = PathExtrudeCache::from_path(self.to_ref())?; let Path { storage, .. } = self; Ok(Mutation::take(storage) .bypass_or_commit_with(|mutation| path::extrude_contour_with(mutation, cache, f)) .map(|(storage, face)| Bind::bind(storage, face).expect_consistent()) .map_err(|(_, error)| error) .expect_consistent()) } /// Extrudes the surface of a closed path. pub fn extrude_surface_with(self, f: F) -> Result where F: Fn(G::Vertex) -> G::Vertex, { let _ = f; todo!() } } impl From> for Path<'static, B> where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { fn from(ring: Ring) -> Self { let keys = Cow::Owned(ring.arcs().keys().collect()); let (storage, _) = ring.into_arc().unbind(); Path { keys, storage } } } impl PartialEq for Path<'_, B> where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { fn eq(&self, other: &Self) -> bool { let keys = |path: &Self| path.keys.iter().cloned().collect::>(); keys(self) == keys(other) } } fn truncate( arcs: impl IntoIterator, from: VertexKey, to: VertexKey, ) -> impl Iterator where T: Borrow + Copy, { arcs.into_iter() .map(|arc| (arc, (*arc.borrow()).into())) .skip_while(move |(_, (a, _))| *a != from) .take_while(move |(_, (a, _))| *a != to) .map(|(arc, _)| arc) } #[cfg(test)] mod tests { use nalgebra::Point2; use crate::graph::{ClosedView, MeshGraph}; use crate::prelude::*; use crate::primitive::{Tetragon, Trigon}; use crate::IteratorExt; type E2 = Point2; #[test] fn open_close() { let graph = MeshGraph::::from_raw_buffers( vec![Trigon::from([0usize, 1, 2])], vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)], ) .unwrap(); let keys = graph .faces() .next() .unwrap() .adjacent_arcs() .map(|arc| arc.into_source_vertex()) .keys() .collect::>(); let mut path = graph.path(keys.iter()).unwrap(); assert!(path.is_open()); // TODO: Move this assertion to a distinct test. assert_eq!(path.vertices().keys().collect::>(), keys.to_vec()); path.push_front(keys[0]).unwrap(); assert!(path.is_closed()); assert_eq!(path.front().key(), path.back().key()); } #[test] fn logical_metrics() { let graph = MeshGraph::::from_raw_buffers( vec![Tetragon::from([0usize, 1, 2, 3])], vec![(1.0, 1.0), (-1.0, 1.0), (-1.0, -1.0), (1.0, -1.0)], ) .unwrap(); let path = { let key = graph.faces().next().unwrap().key(); graph.face(key).unwrap().into_ring().into_path() }; let keys: Vec<_> = path.vertices().keys().collect(); assert_eq!(0, path.shortest_metric_with(0, 0, |_, _| 1usize).unwrap()); assert_eq!( 0, path.shortest_metric_with(keys[0], keys[0], |_, _| 1usize) .unwrap() ); assert_eq!(1, path.shortest_metric_with(0, 3, |_, _| 1usize).unwrap()); assert_eq!( 2, path.shortest_metric_with(keys[3], keys[1], |_, _| 1usize) .unwrap() ); } #[test] fn split() { let graph = MeshGraph::<()>::from_raw_buffers(vec![Tetragon::from([0usize, 1, 2, 3])], vec![(); 4]) .unwrap(); let source = graph.vertices().next().unwrap(); let destination = source .into_outgoing_arc() .into_next_arc() .into_destination_vertex(); let path = source.shortest_path(destination.key()).unwrap(); assert_eq!(path.arcs().count(), 2); let (left, right) = path.split(1).unwrap(); assert_eq!(left.front().key(), right.back().key()); assert_eq!(left.arcs().count(), 1); assert_eq!(right.arcs().count(), 1); } } ================================================ FILE: plexus/src/graph/vertex.rs ================================================ use derivative::Derivative; use smallvec::SmallVec; use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::ops::{Deref, DerefMut}; use theon::space::Vector; use theon::AsPosition; use crate::entity::borrow::{Reborrow, ReborrowInto, ReborrowMut}; use crate::entity::dijkstra; use crate::entity::storage::prelude::*; use crate::entity::storage::{ AsStorage, AsStorageMut, AsStorageOf, HashStorage, IncrementalKeyer, Key, }; use crate::entity::traverse::{Adjacency, Breadth, Depth, Trace, TraceAny, TraceFirst, Traversal}; use crate::entity::view::{ClosedView, Orphan, Rebind, Unbind, View}; use crate::entity::{Entity, Payload}; use crate::geometry::Metric; use crate::graph::data::{Data, GraphData, Parametric}; use crate::graph::edge::{Arc, ArcKey, ArcOrphan, ArcView, Edge}; use crate::graph::face::{Face, FaceOrphan, FaceView}; use crate::graph::geometry::{VertexCentroid, VertexNormal, VertexPosition}; use crate::graph::mutation::vertex::{self, VertexRemoveCache}; use crate::graph::mutation::{self, Consistent, Immediate, Mutable}; use crate::graph::path::Path; use crate::graph::{ Circulator, GraphError, OptionExt as _, OrphanCirculator, ResultExt as _, ViewCirculator, }; use crate::transact::{BypassOrCommit, Mutate}; use crate::IteratorExt as _; type Mutation = mutation::Mutation>; /// Vertex entity. #[derive(Derivative)] #[derivative(Debug, Hash)] pub struct Vertex where G: GraphData, { /// User data. #[derivative(Debug = "ignore", Hash = "ignore")] pub(crate) data: G::Vertex, /// Required key into the leading arc. pub(crate) arc: Option, } impl Vertex where G: GraphData, { pub fn new(data: G::Vertex) -> Self { Vertex { data, arc: None } } } impl Entity for Vertex where G: GraphData, { type Key = VertexKey; type Storage = HashStorage; } impl Payload for Vertex where G: GraphData, { type Data = G::Vertex; fn get(&self) -> &Self::Data { &self.data } fn get_mut(&mut self) -> &mut Self::Data { &mut self.data } } /// Vertex key. #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct VertexKey(u64); impl Key for VertexKey { type Inner = u64; fn from_inner(key: Self::Inner) -> Self { VertexKey(key) } fn into_inner(self) -> Self::Inner { self.0 } } /// View of a vertex entity. /// /// See the [`graph`] module documentation for more information about views. /// /// [`graph`]: crate::graph pub struct VertexView where B: Reborrow, B::Target: AsStorage>> + Parametric, { inner: View>>, } impl VertexView where B: Reborrow, M: AsStorage>> + Parametric, { pub fn to_ref(&self) -> VertexView<&M> { self.inner.to_ref().into() } } impl VertexView where B: ReborrowMut, M: AsStorage>> + Parametric, { // LINT: This convention is specifically used for interior reborrows and is consistent with // `to_ref`. #[expect(clippy::wrong_self_convention)] fn to_mut_unchecked(&mut self) -> VertexView<&mut M> { self.inner.to_mut_unchecked().into() } } impl<'a, B, M, G> VertexView where B: ReborrowInto<'a, Target = M>, M: AsStorage> + Parametric, G: GraphData, { // TODO: Relocate this documentation of `into_ref`. /// # Examples /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// type E3 = Point3; /// /// let mut graph: MeshGraph = Cube::new().polygons::>().collect(); /// let key = graph.arcs().next().unwrap().key(); /// let vertex = graph.arc_mut(key).unwrap().split_at_midpoint().into_ref(); /// /// // This would not be possible without conversion into an immutable view. /// let _ = vertex.into_outgoing_arc().into_face().unwrap(); /// let _ = vertex /// .into_outgoing_arc() /// .into_opposite_arc() /// .into_face() /// .unwrap(); /// ``` pub fn into_ref(self) -> VertexView<&'a M> { self.inner.into_ref().into() } } impl VertexView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { pub fn get<'a>(&'a self) -> &'a G::Vertex where G: 'a, { self.inner.get() } pub fn position<'a>(&'a self) -> &'a VertexPosition where G: 'a, G::Vertex: AsPosition, { self.data.as_position() } } impl VertexView where B: ReborrowMut, M: AsStorageMut> + Parametric, G: GraphData, { pub fn get_mut<'a>(&'a mut self) -> &'a mut G::Vertex where G: 'a, { self.inner.get_mut() } } /// Reachable API. impl VertexView where B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { pub(in crate::graph) fn into_reachable_outgoing_arc(self) -> Option> { let key = self.arc; key.and_then(|key| self.rebind(key)) } } impl VertexView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Converts the vertex into its outgoing (leading) arc. pub fn into_outgoing_arc(self) -> ArcView { self.into_reachable_outgoing_arc().expect_consistent() } /// Gets the outgoing (leading) arc of the vertex. pub fn outgoing_arc(&self) -> ArcView<&M> { self.to_ref().into_outgoing_arc() } pub fn shortest_path(&self, key: VertexKey) -> Result, GraphError> { self.to_ref().into_shortest_path(key) } pub fn into_shortest_path(self, key: VertexKey) -> Result, GraphError> { self.into_shortest_path_with(key, |_, _| 1usize) } pub fn shortest_path_with( &self, key: VertexKey, f: F, ) -> Result, GraphError> where Q: Copy + Metric, F: Fn(VertexView<&M>, VertexView<&M>) -> Q, { self.to_ref().into_shortest_path_with(key, f) } pub fn into_shortest_path_with( self, mut key: VertexKey, f: F, ) -> Result, GraphError> where Q: Copy + Metric, F: Fn(VertexView<&M>, VertexView<&M>) -> Q, { let metrics = dijkstra::metrics_with(self.to_ref(), Some(key), f)?; let mut keys = vec![key]; while let Some((Some(previous), _)) = metrics.get(&key) { key = *previous; keys.push(key); } if keys.len() < 2 { return Err(GraphError::TopologyUnreachable); } let (storage, _) = self.unbind(); Path::bind(storage, keys.iter().rev()) } /// Gets the valence of the vertex. /// /// A vertex's _valence_ is the number of adjacent vertices to which it is /// connected by arcs. The valence of a vertex is the same as its _degree_, /// which is the number of edges to which the vertex is connected. pub fn valence(&self) -> usize { self.adjacent_vertices().count() } pub fn centroid(&self) -> VertexPosition where G: VertexCentroid, G::Vertex: AsPosition, { ::centroid(self.to_ref()).expect_consistent() } } impl VertexView where B: Reborrow, M: AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn normal(&self) -> Result>, GraphError> where G: VertexNormal, G::Vertex: AsPosition, { ::normal(self.to_ref()) } } /// Reachable API. impl<'a, B, M, G> VertexView where B: ReborrowInto<'a, Target = M>, M: 'a + AsStorage> + AsStorage> + Parametric, G: GraphData, { pub(in crate::graph) fn into_reachable_incoming_arcs( self, ) -> impl Clone + Iterator> { // This reachable circulator is needed for face insertions. ArcCirculator::, _>::from(self.into_ref()) } pub(in crate::graph) fn into_reachable_outgoing_arcs( self, ) -> impl Clone + Iterator> { // This reachable circulator is needed for face insertions. self.into_reachable_incoming_arcs() .flat_map(|arc| arc.into_reachable_opposite_arc()) } } /// Reachable API. impl VertexView where B: Reborrow, B::Target: AsStorage> + AsStorage> + Parametric, G: GraphData, { pub(in crate::graph) fn reachable_incoming_arcs( &self, ) -> impl Clone + Iterator> { self.to_ref().into_reachable_incoming_arcs() } pub(in crate::graph) fn reachable_outgoing_arcs( &self, ) -> impl Clone + Iterator> { self.to_ref().into_reachable_outgoing_arcs() } } impl<'a, B, M, G> VertexView where B: ReborrowInto<'a, Target = M>, M: 'a + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_adjacent_vertices(self) -> impl Clone + Iterator> { VertexCirculator::from(ArcCirculator::, _>::from(self.into_ref())) } pub fn into_incoming_arcs(self) -> impl Clone + Iterator> { ArcCirculator::, _>::from(self.into_ref()) } pub fn into_outgoing_arcs(self) -> impl Clone + Iterator> { self.into_incoming_arcs().map(|arc| arc.into_opposite_arc()) } } impl VertexView where B: Reborrow, B::Target: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn adjacent_vertices(&self) -> impl Clone + Iterator> { self.to_ref().into_adjacent_vertices() } /// Gets an iterator of views over the incoming arcs of the vertex. /// /// The ordering of arcs is deterministic and is based on the leading arc of /// the vertex. pub fn incoming_arcs(&self) -> impl Clone + Iterator> { self.to_ref().into_incoming_arcs() } /// Gets an iterator of views over the outgoing arcs of the vertex. /// /// The ordering of arcs is deterministic and is based on the leading arc of /// the vertex. pub fn outgoing_arcs(&self) -> impl Clone + Iterator> { self.to_ref().into_outgoing_arcs() } /// Gets an iterator that traverses adjacent vertices by breadth. /// /// The traversal moves from the vertex to its adjacent vertices and so on. /// If there are disjoint subgraphs in the graph, then a traversal will not /// reach every vertex in the graph. pub fn traverse_by_breadth(&self) -> impl Clone + Iterator> { Traversal::<_, _, Breadth>::from(self.to_ref()) } /// Gets an iterator that traverses adjacent vertices by depth. /// /// The traversal moves from the vertex to its adjacent vertices and so on. /// If there are disjoint subgraphs in the graph, then a traversal will not /// reach every vertex in the graph. pub fn traverse_by_depth(&self) -> impl Clone + Iterator> { Traversal::<_, _, Depth>::from(self.to_ref()) } } impl<'a, B, M, G> VertexView where B: ReborrowInto<'a, Target = M>, M: 'a + AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { pub fn into_adjacent_faces(self) -> impl Clone + Iterator> { FaceCirculator::from(ArcCirculator::, _>::from(self.into_ref())) } } impl VertexView where B: Reborrow, B::Target: AsStorage> + AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { /// Gets an iterator of views over the adjacent faces of the vertex. /// /// The ordering of faces is deterministic and is based on the leading arc /// of the vertex. pub fn adjacent_faces(&self) -> impl Clone + Iterator> { self.to_ref().into_adjacent_faces() } } impl<'a, M> VertexView<&'a mut M> where M: AsStorage> + AsStorageMut> + Consistent + Parametric, { pub fn into_adjacent_vertex_orphans(self) -> impl Iterator> { VertexCirculator::from(ArcCirculator::, _>::from(self)) } } impl VertexView where B: ReborrowMut, B::Target: AsStorage>> + AsStorageMut>> + Consistent + Parametric, { pub fn adjacent_vertex_orphans(&mut self) -> impl Iterator>> { self.to_mut_unchecked().into_adjacent_vertex_orphans() } } impl<'a, M> VertexView<&'a mut M> where M: AsStorageMut> + AsStorage> + Consistent + Parametric, { pub fn into_incoming_arc_orphans(self) -> impl Iterator> { ArcCirculator::, _>::from(self) } } impl VertexView where B: ReborrowMut, B::Target: AsStorageMut>> + AsStorage>> + Consistent + Parametric, { /// Gets an iterator of orphan views over the incoming arcs of the vertex. /// /// The ordering of arcs is deterministic and is based on the leading arc of /// the vertex. pub fn incoming_arc_orphans(&mut self) -> impl Iterator>> { self.to_mut_unchecked().into_incoming_arc_orphans() } } impl<'a, M> VertexView<&'a mut M> where M: AsStorage> + AsStorageMut> + AsStorage> + Consistent + Parametric, { pub fn into_adjacent_face_orphans(self) -> impl Iterator> { FaceCirculator::from(ArcCirculator::, _>::from(self)) } } impl VertexView where B: ReborrowMut, B::Target: AsStorage>> + AsStorageMut>> + AsStorage>> + Consistent + Parametric, { /// Gets an iterator of orphan views over the adjacent faces of the vertex. /// /// The ordering of faces is deterministic and is based on the leading arc /// of the vertex. pub fn adjacent_face_orphans(&mut self) -> impl Iterator>> { self.to_mut_unchecked().into_adjacent_face_orphans() } } impl VertexView<&'_ mut M> where M: AsStorage> + AsStorage> + AsStorage> + AsStorage> + Default + Mutable, G: GraphData, { // TODO: This is not yet implemented, so examples use `no_run`. Run these // examples in doc tests once this no longer intentionally panics. /// Removes the vertex. /// /// Any and all dependent entities are also removed, such as arcs and edges /// connected to the vertex, faces connected to such arcs, vertices with no /// remaining leading arc, etc. /// /// Vertex removal is the most destructive removal, because vertices are a /// dependency of all other entities. /// /// # Examples /// /// Removing a corner from a cube by removing its vertex: /// /// ```rust,no_run /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// type E3 = Point3; /// /// let mut graph: MeshGraph = Cube::new().polygons::>().collect(); /// let key = graph.vertices().next().unwrap().key(); /// graph.vertex_mut(key).unwrap().remove(); /// ``` pub fn remove(self) { // This should never fail here. let cache = VertexRemoveCache::from_vertex(self.to_ref()).expect_consistent(); let (storage, _) = self.unbind(); Mutation::take(storage) .bypass_or_commit_with(|mutation| vertex::remove(mutation, cache)) .map(|_| ()) .map_err(|(_, error)| error) .expect_consistent() } } impl Adjacency for VertexView where B: Reborrow, M: AsStorage> + AsStorage> + Consistent + Parametric, G: GraphData, { type Output = SmallVec<[Self::Key; 8]>; fn adjacency(&self) -> Self::Output { self.adjacent_vertices().keys().collect() } } impl Borrow for VertexView where B: Reborrow, B::Target: AsStorage>> + Parametric, { fn borrow(&self) -> &VertexKey { self.inner.as_ref() } } impl Clone for VertexView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, View>: Clone, { fn clone(&self) -> Self { VertexView { inner: self.inner.clone(), } } } impl ClosedView for VertexView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Key = VertexKey; type Entity = Vertex; fn key(&self) -> Self::Key { self.inner.key() } } impl Copy for VertexView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, View>: Copy, { } impl Deref for VertexView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Target = Vertex; fn deref(&self) -> &Self::Target { self.inner.deref() } } impl DerefMut for VertexView where B: ReborrowMut, M: AsStorageMut> + Parametric, G: GraphData, { fn deref_mut(&mut self) -> &mut Self::Target { self.inner.deref_mut() } } impl Eq for VertexView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { } impl From>> for VertexView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn from(view: View>) -> Self { VertexView { inner: view } } } impl From> for View> where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn from(vertex: VertexView) -> Self { let VertexView { inner, .. } = vertex; inner } } impl Hash for VertexView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn hash(&self, state: &mut H) where H: Hasher, { self.inner.hash(state); } } impl PartialEq for VertexView where B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } /// Orphan view of a vertex entity. pub struct VertexOrphan<'a, G> where G: GraphData, { inner: Orphan<'a, Vertex>, } impl VertexOrphan<'_, G> where G: GraphData, { pub fn position(&self) -> &VertexPosition where G::Vertex: AsPosition, { self.inner.get().as_position() } } impl VertexOrphan<'_, G> where G: GraphData, { pub fn get(&self) -> &G::Vertex { self.inner.get() } pub fn get_mut(&mut self) -> &mut G::Vertex { self.inner.get_mut() } } impl Borrow for VertexOrphan<'_, G> where G: GraphData, { fn borrow(&self) -> &VertexKey { self.inner.as_ref() } } impl ClosedView for VertexOrphan<'_, G> where G: GraphData, { type Key = VertexKey; type Entity = Vertex; fn key(&self) -> Self::Key { self.inner.key() } } impl Eq for VertexOrphan<'_, G> where G: GraphData {} impl<'a, G> From>> for VertexOrphan<'a, G> where G: GraphData, { fn from(inner: Orphan<'a, Vertex>) -> Self { VertexOrphan { inner } } } impl<'a, M> From>> for VertexOrphan<'a, M::Data> where M: AsStorageMut> + Parametric, { fn from(view: View<&'a mut M, Vertex>) -> Self { VertexOrphan { inner: view.into() } } } impl<'a, M> From> for VertexOrphan<'a, M::Data> where M: AsStorageMut> + Parametric, { fn from(vertex: VertexView<&'a mut M>) -> Self { Orphan::from(vertex.inner).into() } } impl Hash for VertexOrphan<'_, G> where G: GraphData, { fn hash(&self, state: &mut H) where H: Hasher, { self.inner.hash(state); } } impl PartialEq for VertexOrphan<'_, G> where G: GraphData, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } pub struct VertexCirculator where P: Trace, B: Reborrow, B::Target: AsStorage>> + AsStorage>> + Parametric, { inner: ArcCirculator, } impl Circulator for VertexCirculator where P: Trace, B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { type Entity = Vertex; fn next(&mut self) -> Option<::Key> { self.inner.next().map(|arc| { let (source, _) = arc.into(); source }) } } impl Clone for VertexCirculator where P: Clone + Trace, B: Clone + Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { fn clone(&self) -> Self { VertexCirculator { inner: self.inner.clone(), } } } impl From> for VertexCirculator where P: Trace, B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { fn from(inner: ArcCirculator) -> Self { VertexCirculator { inner } } } impl<'a, P, M> Iterator for VertexCirculator where P: Trace, M: AsStorage> + AsStorage> + Parametric, { type Item = VertexView<&'a M>; fn next(&mut self) -> Option { self.bind_next_view() } } impl<'a, M> Iterator for VertexCirculator, &'a mut M> where M: AsStorage> + AsStorageMut> + Parametric, { type Item = VertexOrphan<'a, M::Data>; fn next(&mut self) -> Option { unsafe { self.bind_next_orphan() } } } impl<'a, M> Iterator for VertexCirculator, &'a mut M> where M: AsStorage> + AsStorageMut> + Consistent + Parametric, { type Item = VertexOrphan<'a, M::Data>; fn next(&mut self) -> Option { unsafe { self.bind_next_orphan() } } } impl<'a, P, M> OrphanCirculator<'a, M> for VertexCirculator where P: Trace, M: AsStorage> + AsStorageMut> + Parametric, { fn target(&mut self) -> &mut M { self.inner.storage } } impl<'a, P, M> ViewCirculator<'a, M> for VertexCirculator where P: Trace, M: AsStorage> + AsStorage> + Parametric, { fn target(&self) -> &'a M { self.inner.storage } } pub struct ArcCirculator where P: Trace, B: Reborrow, B::Target: AsStorage>> + Parametric, { storage: B, outgoing: Option, trace: P, } impl Circulator for ArcCirculator where P: Trace, B: Reborrow, M: AsStorage> + Parametric, G: GraphData, { type Entity = Arc; fn next(&mut self) -> Option<::Key> { self.outgoing .and_then(|outgoing| self.trace.insert(outgoing).then_some(outgoing)) .map(|outgoing| outgoing.into_opposite()) .and_then(|incoming| { self.storage .reborrow() .as_storage() .get(&incoming) .map(|incoming| incoming.next) .map(|outgoing| (incoming, outgoing)) }) .map(|(incoming, outgoing)| { self.outgoing = outgoing; incoming }) } } impl Clone for ArcCirculator where P: Clone + Trace, B: Clone + Reborrow, M: AsStorage> + Parametric, G: GraphData, { fn clone(&self) -> Self { ArcCirculator { storage: self.storage.clone(), outgoing: self.outgoing, trace: self.trace.clone(), } } } impl From> for ArcCirculator where P: Default + Trace, B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { fn from(vertex: VertexView) -> Self { let key = vertex.arc; let (storage, _) = vertex.unbind(); ArcCirculator { storage, outgoing: key, trace: Default::default(), } } } impl<'a, P, M> Iterator for ArcCirculator where P: Trace, M: AsStorage> + Parametric, { type Item = ArcView<&'a M>; fn next(&mut self) -> Option { self.bind_next_view() } } impl<'a, M> Iterator for ArcCirculator, &'a mut M> where M: AsStorageMut> + Parametric, { type Item = ArcOrphan<'a, M::Data>; fn next(&mut self) -> Option { unsafe { self.bind_next_orphan() } } } impl<'a, M> Iterator for ArcCirculator, &'a mut M> where M: AsStorageMut> + Consistent + Parametric, { type Item = ArcOrphan<'a, M::Data>; fn next(&mut self) -> Option { unsafe { self.bind_next_orphan() } } } impl<'a, P, M> OrphanCirculator<'a, M> for ArcCirculator where P: Trace, M: AsStorageMut> + Parametric, { fn target(&mut self) -> &mut M { self.storage } } impl<'a, P, M> ViewCirculator<'a, M> for ArcCirculator where P: Trace, M: AsStorage> + Parametric, { fn target(&self) -> &'a M { self.storage } } pub struct FaceCirculator where P: Trace, B: Reborrow, B::Target: AsStorage>> + AsStorage>> + Parametric, { inner: ArcCirculator, } impl Circulator for FaceCirculator where P: Trace, B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { type Entity = Face; fn next(&mut self) -> Option<::Key> { while let Some(arc) = self.inner.next() { if let Some(face) = self .inner .storage .reborrow() .as_storage_of::>() .get(&arc) .and_then(|arc| arc.face) { return Some(face); } else { // Skip arcs with no face. This can occur within non-enclosed // meshes. continue; } } None } } impl Clone for FaceCirculator where P: Clone + Trace, B: Clone + Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { fn clone(&self) -> Self { FaceCirculator { inner: self.inner.clone(), } } } impl From> for FaceCirculator where P: Trace, B: Reborrow, M: AsStorage> + AsStorage> + Parametric, G: GraphData, { fn from(inner: ArcCirculator) -> Self { FaceCirculator { inner } } } impl<'a, P, M> Iterator for FaceCirculator where P: Trace, M: AsStorage> + AsStorage> + Parametric, { type Item = FaceView<&'a M>; fn next(&mut self) -> Option { self.bind_next_view() } } impl<'a, M> Iterator for FaceCirculator, &'a mut M> where M: AsStorage> + AsStorageMut> + Parametric, { type Item = FaceOrphan<'a, M::Data>; fn next(&mut self) -> Option { unsafe { self.bind_next_orphan() } } } impl<'a, M> Iterator for FaceCirculator, &'a mut M> where M: AsStorage> + AsStorageMut> + Consistent + Parametric, { type Item = FaceOrphan<'a, M::Data>; fn next(&mut self) -> Option { unsafe { self.bind_next_orphan() } } } impl<'a, P, M> OrphanCirculator<'a, M> for FaceCirculator where P: Trace, M: AsStorage> + AsStorageMut> + Parametric, { fn target(&mut self) -> &mut M { self.inner.storage } } impl<'a, P, M> ViewCirculator<'a, M> for FaceCirculator where P: Trace, M: AsStorage> + AsStorage> + Parametric, { fn target(&self) -> &'a M { self.inner.storage } } #[cfg(test)] mod tests { use decorum::R64; use nalgebra::{Point2, Point3}; use crate::graph::{GraphError, MeshGraph}; use crate::prelude::*; use crate::primitive::cube::Cube; use crate::primitive::generate::Position; use crate::primitive::sphere::UvSphere; use crate::primitive::Trigon; type E3 = Point3; #[test] fn circulate_over_arcs() { let graph: MeshGraph = UvSphere::new(4, 2) .polygons::>() // 6 triangles, 18 vertices. .collect(); // All faces should be triangles and all vertices should have 4 // (incoming) arcs. for vertex in graph.vertices() { assert_eq!(4, vertex.incoming_arcs().count()); } } #[test] fn reachable_shortest_path() { let graph = MeshGraph::>::from_raw_buffers( vec![Trigon::new(0usize, 1, 2)], vec![(-1.0, 0.0), (0.0, 1.0), (1.0, 0.0)], ) .unwrap(); let from = graph.vertices().next().unwrap(); let to = from.outgoing_arc().destination_vertex().key(); let path = from.shortest_path(to).unwrap(); assert_eq!(path.back().key(), from.key()); assert_eq!(path.front().key(), to); assert_eq!(path.arcs().count(), 1); } #[test] fn unreachable_shortest_path() { // Create a graph from two disjoint trigons. let graph = MeshGraph::>::from_raw_buffers( vec![Trigon::new(0usize, 1, 2), Trigon::new(3, 4, 5)], vec![ (-2.0, 0.0), (-1.0, 1.0), (-1.0, 0.0), (0.0, 0.0), (1.0, 1.0), (1.0, 0.0), ], ) .unwrap(); let mut vertices = graph.disjoint_subgraph_vertices(); let from = vertices.next().unwrap(); let to = vertices.next().unwrap().key(); assert!(matches!( from.into_shortest_path(to), Err(GraphError::TopologyUnreachable) )); } #[test] fn traverse_by_breadth() { let graph: MeshGraph = Cube::new() .polygons::>() // 6 quadrilaterals, 24 vertices. .collect(); let vertex = graph.vertices().next().unwrap(); assert_eq!(graph.vertex_count(), vertex.traverse_by_breadth().count()); } #[test] fn traverse_by_depth() { let graph: MeshGraph = Cube::new() .polygons::>() // 6 quadrilaterals, 24 vertices. .collect(); let vertex = graph.vertices().next().unwrap(); assert_eq!(graph.vertex_count(), vertex.traverse_by_depth().count()); } } ================================================ FILE: plexus/src/index.rs ================================================ //! Indexing and aggregation. //! //! This module provides types and traits that describe _index buffers_ and //! _indexers_ that disambiguate vertex data to construct minimal _index_ and //! _vertex buffers_. Plexus refers to independent vertex and index buffers as //! _raw buffers_. See the [`buffer`] module and [`MeshBuffer`] type for tools //! for working with these buffers. //! //! # Index Buffers //! //! Index buffers describe the topology of a polygonal mesh as ordered groups of //! indices into a vertex buffer. Each group of indices represents a polygon. //! The vertex buffer contains data that describes each vertex, such as //! positions or surface normals. Plexus supports _structured_ and _flat_ index //! buffers via the [`Grouping`] and [`IndexBuffer`] traits. These traits are //! implemented for [`Vec`]. //! //! Flat index buffers contain unstructured indices with an implicit grouping, //! such as `Vec`. Arity of these buffers is constant and is described by //! the [`Flat`] meta-grouping. Rendering pipelines typically expect this //! format. //! //! Structured index buffers contain elements that explicitly group indices, //! such as `Vec>`. These buffers can be formed from polygonal //! types in the [`primitive`] module. //! //! # Indexers //! //! [`Indexer`]s construct index and vertex buffers from iterators of polygonal //! types in the [`primitive`] module, such as [`NGon`] and //! [`UnboundedPolygon`]. The [`IndexVertices`] trait provides functions for //! collecting an iterator of $n$-gons into these buffers. //! //! Mesh data structures also implement the [`FromIndexer`] and [`FromIterator`] //! traits so that iterators of $n$-gons can be collected into these types //! (using a [`HashIndexer`] by default). A specific [`Indexer`] can be //! configured using the [`CollectWithIndexer`] trait. //! //! # Examples //! //! Indexing data for a cube to create raw buffers and a [`MeshBuffer`]: //! //! ```rust //! # extern crate decorum; //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use decorum::R64; //! use nalgebra::Point3; //! use plexus::buffer::MeshBuffer; //! use plexus::index::{Flat3, HashIndexer}; //! use plexus::prelude::*; //! use plexus::primitive::cube::Cube; //! use plexus::primitive::generate::Position; //! //! type E3 = Point3; //! //! let (indices, positions) = Cube::new() //! .polygons::>() //! .triangulate() //! .index_vertices::(HashIndexer::default()); //! let buffer = MeshBuffer::::from_raw_buffers(indices, positions).unwrap(); //! ``` //! //! [`FromIterator`]: std::iter::FromIterator //! [`Vec`]: std::vec::Vec //! [`MeshBuffer`]: crate::buffer::MeshBuffer //! [`buffer`]: crate::buffer //! [`MeshGraph`]: crate::graph::MeshGraph //! [`CollectWithIndexer`]: crate::index::CollectWithIndexer //! [`Flat`]: crate::index::Flat //! [`FromIndexer`]: crate::index::FromIndexer //! [`HashIndexer`]: crate::index::HashIndexer //! [`Indexer`]: crate::index::Indexer //! [`IndexVertices`]: crate::index::IndexVertices //! [`NGon`]: crate::primitive::NGon //! [`UnboundedPolygon`]: crate::primitive::UnboundedPolygon //! [`primitive`]: crate::primitive use num::{Integer, NumCast, Unsigned}; use std::cmp; use std::collections::HashMap; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; use theon::adjunct::Map; use typenum::NonZero; use crate::constant::{Constant, ToType, TypeOf}; use crate::primitive::decompose::IntoVertices; use crate::primitive::Topological; use crate::{Monomorphic, StaticArity}; pub(crate) type BufferOf = Vec<::Group>; pub(crate) type IndexOf = as IndexBuffer>::Index; // Note that it isn't possible for `IndexBuffer` types to implement // `DynamicArity`, because they are typically parameterized by `R` (see // implementations for `Vec<_>`). Instead, `DynamicArity` is implemented for // `MeshBuffer`, which can bind a `Grouping` and its implementation of // `StaticArity` with the underlying index buffer type. /// Index buffer. /// /// This trait is implemented by types that can be used as an index buffer. The /// elements in the buffer are determined by a `Grouping`. /// /// In particular, this trait is implemented by `Vec`, such as `Vec` or /// `Vec>`. pub trait IndexBuffer where R: Grouping, { /// The type of individual indices in the buffer. /// /// This type is distinct from the grouping. For example, if an index buffer /// contains [`Trigon`][`Trigon`] elements, then this type is `usize`. /// /// [`Trigon`]: crate::primitive::Trigon type Index: Copy + Integer + Unsigned; } impl IndexBuffer> for Vec where Constant: ToType, TypeOf: NonZero, T: Copy + Integer + Unsigned, { type Index = T; } impl

IndexBuffer

for Vec

where P: Topological, P::Vertex: Copy + Integer + Unsigned, { type Index = P::Vertex; } pub trait Push: IndexBuffer where R: Grouping, P: Topological, P::Vertex: Copy + Integer + Unsigned, { fn push(&mut self, index: P); } impl Push, P> for Vec where Constant: ToType, TypeOf: NonZero, T: Copy + Integer + Unsigned, P: Monomorphic + IntoVertices + Topological, { fn push(&mut self, index: P) { for index in index.into_vertices() { self.push(index); } } } impl Push for Vec

where P: From + Grouping + Topological, P::Vertex: Copy + Integer + Unsigned, Q: Topological, Self: IndexBuffer, { fn push(&mut self, index: Q) { self.push(P::from(index)); } } pub trait Grouping: StaticArity { type Group; } /// Flat index buffer meta-grouping. /// /// Describes a flat index buffer with a constant arity. The number of vertices /// in the indexed topological structures is specified using a constant /// parameter `N`, which represents the number of grouped elements in the index /// buffer. For example, `Flat<_, 3>` describes an index buffer with indices in /// implicit and contiguous groups of three. Note that this constant may be /// distinct from the arity of the indexed topological structures (i.e., if `N` /// is less than three, then arity is `N - 1` and may be zero.). /// /// Unlike structured groupings, this meta-grouping is needed to associate an /// index type with an implicit grouping and arity. For example, `Vec` /// implements both `IndexBuffer>` (a triangular buffer) and /// `IndexBuffer>` (a quadrilateral buffer). /// /// See the [`index`] module documention for more information about index /// buffers. /// /// # Examples /// /// Creating a [`MeshBuffer`] with a flat and triangular index buffer: /// /// ```rust /// use plexus::buffer::MeshBuffer; /// use plexus::index::Flat; /// use plexus::prelude::*; /// /// let mut buffer = MeshBuffer::, (f64, f64, f64)>::default(); /// ``` /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`index`]: crate::index #[derive(Debug)] pub struct Flat where Constant: ToType, TypeOf: NonZero, T: Copy + Integer + Unsigned, { phantom: PhantomData T>, } impl Grouping for Flat where Constant: ToType, TypeOf: NonZero, T: Copy + Integer + Unsigned, { /// The elements of flat index buffers are indices. These indices are /// implicitly grouped by the arity of the buffer (`N`). type Group = T; } impl Monomorphic for Flat where Constant: ToType, TypeOf: NonZero, T: Copy + Integer + Unsigned, { } impl StaticArity for Flat where Constant: ToType, TypeOf: NonZero, T: Copy + Integer + Unsigned, { type Static = usize; const ARITY: Self::Static = crate::n_arity(N); } /// Alias for a flat and triangular index buffer. pub type Flat3 = Flat; /// Alias for a flat and quadrilateral index buffer. pub type Flat4 = Flat; /// Structured index buffer grouping. /// /// Describes a structured index buffer containing [`Topological`] types with /// index data in their vertices. /// /// # Examples /// /// Creating a [`MeshBuffer`] with a structured index buffer: /// /// ```rust /// use plexus::buffer::MeshBuffer; /// use plexus::prelude::*; /// use plexus::primitive::BoundedPolygon; /// /// let mut buffer = MeshBuffer::, (f64, f64, f64)>::default(); /// ``` /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`Topological`]: crate::primitive::Topological impl

Grouping for P where P: Topological, P::Vertex: Copy + Integer + Unsigned, { /// [`Topological`] index buffers contain $n$-gons that explicitly group /// their indices. /// /// [`Topological`]: crate::primitive::Topological type Group = P; } /// Vertex indexer. /// /// Disambiguates arbitrary vertex data and emits a one-to-one mapping of /// indices to vertices. pub trait Indexer where T: Topological, { /// Indexes a vertex using a keying function. /// /// Returns a tuple containing the index and optionally vertex data. Vertex /// data is only returned if the data has not yet been indexed, otherwise /// `None` is returned. fn index(&mut self, vertex: T::Vertex, f: F) -> (usize, Option) where F: Fn(&T::Vertex) -> &K; } /// Hashing vertex indexer. /// /// This indexer hashes key data for vertices to form an index. This is fast, /// reliable, and requires no configuration. Prefer this indexer when possible. /// /// The vertex key data must implement [`Hash`]. Vertex data often includes /// floating-point values (i.e., `f32` or `f64`), which do not implement /// [`Hash`]. Types from the [`decorum`] crate can be used to allow /// floating-point data to be hashed. /// /// # Examples /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::index::{Flat3, HashIndexer}; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// let (indices, positions) = Cube::new() /// .polygons::>>() /// .triangulate() /// .index_vertices::(HashIndexer::default()); /// ``` /// /// [`decorum`]: https://crates.io/crates/decorum /// /// [`Hash`]: std::hash::Hash pub struct HashIndexer where T: Topological, K: Clone + Eq + Hash, { hash: HashMap, n: usize, phantom: PhantomData T>, } impl HashIndexer where T: Topological, K: Clone + Eq + Hash, { /// Creates a new `HashIndexer`. pub fn new() -> Self { HashIndexer { hash: HashMap::new(), n: 0, phantom: PhantomData, } } } impl Default for HashIndexer where T: Topological, K: Clone + Eq + Hash, { fn default() -> Self { HashIndexer::new() } } impl Indexer for HashIndexer where T: Topological, K: Clone + Eq + Hash, { fn index(&mut self, input: T::Vertex, f: F) -> (usize, Option) where F: Fn(&T::Vertex) -> &K, { let mut vertex = None; let mut n = self.n; let index = self.hash.entry(f(&input).clone()).or_insert_with(|| { vertex = Some(input); let m = n; n += 1; m }); self.n = n; (*index, vertex) } } /// LRU caching vertex indexer. /// /// This indexer uses a _least recently used_ (LRU) cache to form an index. To /// function correctly, an adequate cache capacity is necessary. If the capacity /// is insufficient, then redundant vertex data may be emitted. See /// [`LruIndexer::with_capacity`]. /// /// This indexer is useful if the vertex key data does not implement [`Hash`]. /// If the key data can be hashed, prefer `HashIndexer` instead. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point3; /// use plexus::index::{Flat3, LruIndexer}; /// use plexus::prelude::*; /// use plexus::primitive::generate::Position; /// use plexus::primitive::sphere::UvSphere; /// /// let (indices, positions) = UvSphere::new(8, 8) /// .polygons::>>() /// .triangulate() /// .index_vertices::(LruIndexer::with_capacity(64)); /// ``` /// /// [`Hash`]: std::hash::Hash /// [`LruIndexer::with_capacity`]: crate::index::LruIndexer::with_capacity pub struct LruIndexer where T: Topological, K: Clone + PartialEq, { lru: Vec<(K, usize)>, capacity: usize, n: usize, phantom: PhantomData T>, } impl LruIndexer where T: Topological, K: Clone + PartialEq, { /// Creates a new `LruIndexer` with a default capacity. pub fn new() -> Self { LruIndexer::with_capacity(16) } /// Creates a new `LruIndexer` with the specified capacity. /// /// The capacity of the cache must be sufficient in order to generate a /// unique set of index and vertex data. pub fn with_capacity(capacity: usize) -> Self { let capacity = cmp::max(1, capacity); LruIndexer { lru: Vec::with_capacity(capacity), capacity, n: 0, phantom: PhantomData, } } fn find(&self, key: &K) -> Option<(usize, usize)> { self.lru .iter() .enumerate() .find(|&(_, entry)| entry.0 == *key) .map(|(index, entry)| (index, entry.1)) } } impl Default for LruIndexer where T: Topological, K: Clone + PartialEq, { fn default() -> Self { LruIndexer::new() } } impl Indexer for LruIndexer where T: Topological, K: Clone + PartialEq, { fn index(&mut self, input: T::Vertex, f: F) -> (usize, Option) where F: Fn(&T::Vertex) -> &K, { let mut vertex = None; let key = f(&input).clone(); let index = if let Some(entry) = self.find(&key) { let vertex = self.lru.remove(entry.0); self.lru.push(vertex); entry.1 } else { vertex = Some(input); let m = self.n; self.n += 1; if self.lru.len() >= self.capacity { self.lru.remove(0); } self.lru.push((key, m)); m }; (index, vertex) } } /// Functions for collecting an iterator of $n$-gons into raw index and vertex /// buffers. /// /// Unlike [`IndexVertices`], this trait provides functions that are closed (not /// parameterized) with respect to [`Grouping`]. Instead, the trait is /// implemented for a particular [`Grouping`]. These functions cannot be used /// fluently as part of an iterator expression. /// /// [`Grouping`]: crate::index::Grouping /// [`IndexVertices`]: crate::index::IndexVertices pub trait GroupedIndexVertices: Sized where R: Grouping, P: Topological, { fn index_vertices_with(self, indexer: N, f: F) -> (Vec, Vec) where N: Indexer, F: Fn(&P::Vertex) -> &K; fn index_vertices(self, indexer: N) -> (Vec, Vec) where N: Indexer, { self.index_vertices_with::(indexer, |vertex| vertex) } } impl GroupedIndexVertices for I where I: Iterator, R: Grouping, P: Map> + Topological, P::Output: Topological>, BufferOf: Push, IndexOf: NumCast, { fn index_vertices_with(self, mut indexer: N, f: F) -> (Vec, Vec) where N: Indexer, F: Fn(&P::Vertex) -> &K, { let mut indices = Vec::new(); let mut vertices = Vec::new(); for topology in self { Push::push( &mut indices, topology.map(|vertex| { let (index, vertex) = indexer.index(vertex, &f); if let Some(vertex) = vertex { vertices.push(vertex); } NumCast::from(index).unwrap() }), ); } (indices, vertices) } } /// Functions for collecting an iterator of $n$-gons into raw index and vertex /// buffers. /// /// Unlike [`GroupedIndexVertices`], this trait provides functions that are /// parameterized with respect to [`Grouping`]. /// /// See [`HashIndexer`] and [`LruIndexer`]. /// /// # Examples /// /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::index::{Flat3, HashIndexer}; /// use plexus::prelude::*; /// use plexus::primitive::generate::Position; /// use plexus::primitive::sphere::UvSphere; /// /// let sphere = UvSphere::new(32, 32); /// let (indices, positions) = sphere /// .polygons::>>() /// .triangulate() /// .index_vertices::(HashIndexer::default()); /// ``` /// /// [`GroupedIndexVertices`]: crate::index::GroupedIndexVertices /// [`Grouping`]: crate::index::Grouping /// [`HashIndexer`]: crate::index::HashIndexer /// [`LruIndexer`]: crate::index::LruIndexer pub trait IndexVertices

where P: Topological, { /// Indexes an iterator of $n$-gons into raw index and vertex buffers using /// the given grouping, indexer, and keying function. fn index_vertices_with(self, indexer: N, f: F) -> (Vec, Vec) where Self: GroupedIndexVertices, R: Grouping, N: Indexer, F: Fn(&P::Vertex) -> &K, { GroupedIndexVertices::::index_vertices_with(self, indexer, f) } /// Indexes an iterator of $n$-gons into raw index and vertex buffers using /// the given grouping and indexer. /// /// # Examples /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::index::HashIndexer; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// use plexus::primitive::Trigon; /// /// // `indices` contains `Trigon`s with index data. /// let (indices, positions) = Cube::new() /// .polygons::>>() /// .subdivide() /// .triangulate() /// .index_vertices::, _>(HashIndexer::default()); /// ``` fn index_vertices(self, indexer: N) -> (Vec, Vec) where Self: GroupedIndexVertices, R: Grouping, N: Indexer, { IndexVertices::

::index_vertices_with(self, indexer, |vertex| vertex) } } impl IndexVertices

for I where I: Iterator, P: Topological, { } pub trait FromIndexer: Sized where P: Topological, Q: Topological, { type Error: Debug; fn from_indexer(input: I, indexer: N) -> Result where I: IntoIterator, N: Indexer; } /// Functions for collecting an iterator of $n$-gons into a mesh data structure. /// /// These functions can be used to collect data from an iterator into mesh data /// structures like [`MeshBuffer`] or [`MeshGraph`]. /// /// See [`HashIndexer`] and [`LruIndexer`]. /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`MeshGraph`]: crate::graph::MeshGraph /// [`HashIndexer`]: crate::index::HashIndexer /// [`LruIndexer`]: crate::index::LruIndexer pub trait CollectWithIndexer where P: Topological, Q: Topological, { /// Collects an iterator of $n$-gons into a mesh data structure using the /// given indexer. /// /// Unlike `collect`, this function allows the indexer to be specified. /// /// # Errors /// /// Returns an error defined by the implementer if the target type cannot be /// constructed from the indexed vertex data. /// /// # Examples /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// use plexus::index::HashIndexer; /// /// let graph: MeshGraph> = Cube::new() /// .polygons::>>() /// .collect_with_indexer(HashIndexer::default()) /// .unwrap(); fn collect_with_indexer(self, indexer: N) -> Result where T: FromIndexer, N: Indexer; } impl CollectWithIndexer for I where I: Iterator, P: Topological, Q: Topological, { fn collect_with_indexer(self, indexer: N) -> Result where T: FromIndexer, N: Indexer, { T::from_indexer(self, indexer) } } ================================================ FILE: plexus/src/integration/cgmath.rs ================================================ #![cfg(feature = "geometry-cgmath")] use cgmath::{Point2, Point3, Vector2, Vector3}; use decorum::{ExtendedReal, Primitive, Real, Total}; use num::{NumCast, ToPrimitive}; use crate::geometry::{FromGeometry, UnitGeometry}; use crate::graph::GraphData; impl FromGeometry<(U, U)> for Vector2 where T: NumCast, U: ToPrimitive, { fn from_geometry(other: (U, U)) -> Self { Vector2::new(T::from(other.0).unwrap(), T::from(other.1).unwrap()) } } impl FromGeometry> for (U, U) where T: ToPrimitive, U: NumCast, { fn from_geometry(other: Vector2) -> Self { (U::from(other.x).unwrap(), U::from(other.y).unwrap()) } } impl FromGeometry<(U, U, U)> for Vector3 where T: NumCast, U: ToPrimitive, { fn from_geometry(other: (U, U, U)) -> Self { Vector3::new( T::from(other.0).unwrap(), T::from(other.1).unwrap(), T::from(other.2).unwrap(), ) } } impl FromGeometry> for (U, U, U) where T: ToPrimitive, U: NumCast, { fn from_geometry(other: Vector3) -> Self { ( U::from(other.x).unwrap(), U::from(other.y).unwrap(), U::from(other.z).unwrap(), ) } } impl FromGeometry<(U, U)> for Point2 where T: NumCast, U: ToPrimitive, { fn from_geometry(other: (U, U)) -> Self { Point2::new(T::from(other.0).unwrap(), T::from(other.1).unwrap()) } } impl FromGeometry> for (U, U) where T: ToPrimitive, U: NumCast, { fn from_geometry(other: Point2) -> Self { (U::from(other.x).unwrap(), U::from(other.y).unwrap()) } } impl UnitGeometry for Point2 {} impl FromGeometry<(U, U, U)> for Point3 where T: NumCast, U: ToPrimitive, { fn from_geometry(other: (U, U, U)) -> Self { Point3::new( T::from(other.0).unwrap(), T::from(other.1).unwrap(), T::from(other.2).unwrap(), ) } } impl FromGeometry> for (U, U, U) where T: ToPrimitive, U: NumCast, { fn from_geometry(other: Point3) -> Self { ( U::from(other.x).unwrap(), U::from(other.y).unwrap(), U::from(other.z).unwrap(), ) } } impl GraphData for Point2 where Self: Copy, { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl GraphData for Point3 where Self: Copy, { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl UnitGeometry for Point3 {} macro_rules! with_constrained_scalars { ($f:ident) => { $f!(proxy => Real); $f!(proxy => ExtendedReal); $f!(proxy => Total); }; } macro_rules! with_geometric_structures { ($f:ident) => { $f!(geometry => Vector2); $f!(geometry => Vector3); $f!(geometry => Point2); $f!(geometry => Point3); }; } macro_rules! impl_from_geometry_for_constrained_scalar_structures { () => { with_constrained_scalars!(impl_from_geometry_for_constrained_scalar_structures); }; (proxy => $p:ident) => { macro_rules! impl_from_geometry_for_scalar_structure { () => { with_geometric_structures!(impl_from_geometry_for_scalar_structure); }; (geometry => $g:ident) => { impl FromGeometry<$g<$p>> for $g where T: Primitive, { fn from_geometry(other: $g<$p>) -> Self { other.map(|value| value.into_inner()) } } impl FromGeometry<$g> for $g<$p> where T: Primitive, { fn from_geometry(other: $g) -> Self { other.map($p::::assert) } } }; } impl_from_geometry_for_scalar_structure!(); }; } impl_from_geometry_for_constrained_scalar_structures!(); ================================================ FILE: plexus/src/integration/glam.rs ================================================ #![cfg(feature = "geometry-glam")] use glam::{Vec2, Vec3, Vec3A}; use crate::geometry::{FromGeometry, UnitGeometry}; use crate::graph::GraphData; impl FromGeometry<(f32, f32)> for Vec2 { fn from_geometry(other: (f32, f32)) -> Self { Self::from(other) } } impl FromGeometry<(f32, f32, f32)> for Vec3 { fn from_geometry(other: (f32, f32, f32)) -> Self { Self::from(other) } } impl FromGeometry<(f32, f32, f32)> for Vec3A { fn from_geometry(other: (f32, f32, f32)) -> Self { Self::from(other) } } impl GraphData for Vec2 { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl GraphData for Vec3 { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl GraphData for Vec3A { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl UnitGeometry for Vec2 {} impl UnitGeometry for Vec3 {} impl UnitGeometry for Vec3A {} ================================================ FILE: plexus/src/integration/mint.rs ================================================ #![cfg(feature = "geometry-mint")] use decorum::{ExtendedReal, Primitive, Real, Total}; use mint::{Point2, Point3, Vector2, Vector3}; use num::{NumCast, ToPrimitive}; use crate::geometry::{FromGeometry, UnitGeometry}; use crate::graph::GraphData; impl FromGeometry<(U, U)> for Vector2 where T: NumCast, U: ToPrimitive, { fn from_geometry(other: (U, U)) -> Self { Vector2 { x: T::from(other.0).unwrap(), y: T::from(other.1).unwrap(), } } } impl FromGeometry> for (U, U) where T: ToPrimitive, U: NumCast, { fn from_geometry(other: Vector2) -> Self { (U::from(other.x).unwrap(), U::from(other.y).unwrap()) } } impl FromGeometry<(U, U, U)> for Vector3 where T: NumCast, U: ToPrimitive, { fn from_geometry(other: (U, U, U)) -> Self { Vector3 { x: T::from(other.0).unwrap(), y: T::from(other.1).unwrap(), z: T::from(other.2).unwrap(), } } } impl FromGeometry> for (U, U, U) where T: ToPrimitive, U: NumCast, { fn from_geometry(other: Vector3) -> Self { ( U::from(other.x).unwrap(), U::from(other.y).unwrap(), U::from(other.z).unwrap(), ) } } impl FromGeometry<(U, U)> for Point2 where T: NumCast, U: ToPrimitive, { fn from_geometry(other: (U, U)) -> Self { Point2 { x: T::from(other.0).unwrap(), y: T::from(other.1).unwrap(), } } } impl FromGeometry> for (U, U) where T: ToPrimitive, U: NumCast, { fn from_geometry(other: Point2) -> Self { (U::from(other.x).unwrap(), U::from(other.y).unwrap()) } } impl UnitGeometry for Point2 {} impl FromGeometry<(U, U, U)> for Point3 where T: NumCast, U: ToPrimitive, { fn from_geometry(other: (U, U, U)) -> Self { Point3 { x: T::from(other.0).unwrap(), y: T::from(other.1).unwrap(), z: T::from(other.2).unwrap(), } } } impl FromGeometry> for (U, U, U) where T: ToPrimitive, U: NumCast, { fn from_geometry(other: Point3) -> Self { ( U::from(other.x).unwrap(), U::from(other.y).unwrap(), U::from(other.z).unwrap(), ) } } impl GraphData for Point2 where Self: Copy, { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl GraphData for Point3 where Self: Copy, { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl UnitGeometry for Point3 {} macro_rules! with_constrained_scalars { ($f:ident) => { $f!(proxy => Real); $f!(proxy => ExtendedReal); $f!(proxy => Total); }; } macro_rules! impl_from_geometry_for_constrained_scalar_structures { () => { with_constrained_scalars!(impl_from_geometry_for_constrained_scalar_structures); }; (proxy => $p:ident) => { impl FromGeometry>> for Vector2 where T: Primitive, { fn from_geometry(other: Vector2<$p>) -> Self { Vector2 { x: other.x.into_inner(), y: other.y.into_inner(), } } } impl FromGeometry> for Vector2<$p> where T: Primitive, { fn from_geometry(other: Vector2) -> Self { Vector2 { x: $p::::assert(other.x), y: $p::::assert(other.y), } } } impl FromGeometry>> for Vector3 where T: Primitive, { fn from_geometry(other: Vector3<$p>) -> Self { Vector3 { x: other.x.into_inner(), y: other.y.into_inner(), z: other.z.into_inner(), } } } impl FromGeometry> for Vector3<$p> where T: Primitive, { fn from_geometry(other: Vector3) -> Self { Vector3 { x: $p::::assert(other.x), y: $p::::assert(other.y), z: $p::::assert(other.z), } } } impl FromGeometry>> for Point2 where T: Primitive, { fn from_geometry(other: Point2<$p>) -> Self { Point2 { x: other.x.into_inner(), y: other.y.into_inner(), } } } impl FromGeometry> for Point2<$p> where T: Primitive, { fn from_geometry(other: Point2) -> Self { Point2 { x: $p::::assert(other.x), y: $p::::assert(other.y), } } } impl FromGeometry>> for Point3 where T: Primitive, { fn from_geometry(other: Point3<$p>) -> Self { Point3 { x: other.x.into_inner(), y: other.y.into_inner(), z: other.z.into_inner(), } } } impl FromGeometry> for Point3<$p> where T: Primitive, { fn from_geometry(other: Point3) -> Self { Point3 { x: $p::::assert(other.x), y: $p::::assert(other.y), z: $p::::assert(other.z), } } } }; } impl_from_geometry_for_constrained_scalar_structures!(); ================================================ FILE: plexus/src/integration/mod.rs ================================================ //! Integration of external crates and foreign types. //! //! This module provides implementations of traits in Plexus for foreign types. // TODO: Do not implement geometric traits and conversions over tuples of scalars. Prefer array // types instead. mod cgmath; mod glam; mod mint; mod nalgebra; mod ultraviolet; ================================================ FILE: plexus/src/integration/nalgebra.rs ================================================ #![cfg(feature = "geometry-nalgebra")] use decorum::{ExtendedReal, Primitive, Real, Total}; use nalgebra::base::allocator::Allocator; use nalgebra::{ DefaultAllocator, DimName, OMatrix, OPoint, Point2, Point3, Scalar, Vector2, Vector3, }; use num::{NumCast, ToPrimitive}; use crate::geometry::{FromGeometry, UnitGeometry}; use crate::graph::GraphData; impl FromGeometry<(U, U)> for Vector2 where T: NumCast + Scalar, U: ToPrimitive, { fn from_geometry(other: (U, U)) -> Self { Vector2::new(T::from(other.0).unwrap(), T::from(other.1).unwrap()) } } impl FromGeometry> for (U, U) where T: Scalar + ToPrimitive, U: NumCast, { fn from_geometry(other: Vector2) -> Self { let [x, y]: [T; 2] = other.into(); (U::from(x).unwrap(), U::from(y).unwrap()) } } impl FromGeometry<(U, U, U)> for Vector3 where T: NumCast + Scalar, U: ToPrimitive, { fn from_geometry(other: (U, U, U)) -> Self { Vector3::new( T::from(other.0).unwrap(), T::from(other.1).unwrap(), T::from(other.2).unwrap(), ) } } impl FromGeometry> for (U, U, U) where T: Scalar + ToPrimitive, U: NumCast, { fn from_geometry(other: Vector3) -> Self { let [x, y, z]: [T; 3] = other.into(); ( U::from(x).unwrap(), U::from(y).unwrap(), U::from(z).unwrap(), ) } } impl FromGeometry<(U, U)> for Point2 where T: NumCast + Scalar, U: ToPrimitive, { fn from_geometry(other: (U, U)) -> Self { Point2::new(T::from(other.0).unwrap(), T::from(other.1).unwrap()) } } impl FromGeometry> for (U, U) where T: Scalar + ToPrimitive, U: NumCast, { fn from_geometry(other: Point2) -> Self { let [x, y]: [T; 2] = other.coords.into(); (U::from(x).unwrap(), U::from(y).unwrap()) } } impl FromGeometry<(U, U, U)> for Point3 where T: NumCast + Scalar, U: ToPrimitive, { fn from_geometry(other: (U, U, U)) -> Self { Point3::new( T::from(other.0).unwrap(), T::from(other.1).unwrap(), T::from(other.2).unwrap(), ) } } impl FromGeometry> for (U, U, U) where T: Scalar + ToPrimitive, U: NumCast, { fn from_geometry(other: Point3) -> Self { let [x, y, z]: [T; 3] = other.coords.into(); ( U::from(x).unwrap(), U::from(y).unwrap(), U::from(z).unwrap(), ) } } impl GraphData for OPoint where T: Scalar, D: DimName, DefaultAllocator: Allocator, Self: Copy, { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl UnitGeometry for OPoint where T: Scalar, D: DimName, DefaultAllocator: Allocator, { } macro_rules! with_constrained_scalars { ($f:ident) => { $f!(proxy => Real); $f!(proxy => ExtendedReal); $f!(proxy => Total); }; } macro_rules! impl_from_geometry_for_constrained_scalar_structures { () => { with_constrained_scalars!(impl_from_geometry_for_constrained_scalar_structures); }; (proxy => $p:ident) => { impl FromGeometry, R, C>> for OMatrix where T: Primitive + Scalar, R: DimName, C: DimName, DefaultAllocator: Allocator, { fn from_geometry(other: OMatrix<$p, R, C>) -> Self { other.map(|value| value.into_inner()) } } impl FromGeometry> for OMatrix<$p, R, C> where T: Primitive + Scalar, R: DimName, C: DimName, DefaultAllocator: Allocator, { fn from_geometry(other: OMatrix) -> Self { other.map($p::::assert) } } impl FromGeometry, D>> for OPoint where T: Primitive + Scalar, D: DimName, DefaultAllocator: Allocator, { fn from_geometry(other: OPoint<$p, D>) -> Self { OPoint::from(other.coords.map(|value| value.into_inner())) } } impl FromGeometry> for OPoint<$p, D> where T: Primitive + Scalar, D: DimName, DefaultAllocator: Allocator, { fn from_geometry(other: OPoint) -> Self { OPoint::from(other.coords.map($p::::assert)) } } }; } impl_from_geometry_for_constrained_scalar_structures!(); ================================================ FILE: plexus/src/integration/ultraviolet.rs ================================================ #![cfg(feature = "geometry-ultraviolet")] use ultraviolet::vec::{Vec2, Vec3}; use crate::geometry::{FromGeometry, UnitGeometry}; use crate::graph::GraphData; impl FromGeometry<(f32, f32)> for Vec2 { fn from_geometry(other: (f32, f32)) -> Self { Self::from(other) } } impl FromGeometry<(f32, f32, f32)> for Vec3 { fn from_geometry(other: (f32, f32, f32)) -> Self { Self::from(other) } } impl GraphData for Vec2 { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl GraphData for Vec3 { type Vertex = Self; type Arc = (); type Edge = (); type Face = (); } impl UnitGeometry for Vec2 {} impl UnitGeometry for Vec3 {} ================================================ FILE: plexus/src/lib.rs ================================================ //! **Plexus** is a highly composable library for polygonal mesh processing. //! //! Versions of Plexus in the `0.0.*` series are experimental and unstable. //! Consider depending on the development branch of the repository. See [the //! website][website] for the latest information and documentation. //! //! [website]: https://plexus.rs #![doc(html_favicon_url = "https://plexus.rs/img/favicon.ico")] #![doc(html_logo_url = "https://plexus.rs/img/plexus.svg")] mod entity; mod integration; mod transact; pub mod buffer; pub mod builder; pub mod constant; pub mod encoding; pub mod geometry; pub mod graph; pub mod index; pub mod primitive; use arrayvec::ArrayVec; use itertools::{self, Itertools, MinMaxResult, MultiPeek}; use std::borrow::Borrow; use std::fmt::Debug; use crate::entity::view::ClosedView; pub mod prelude { //! Re-exports of commonly used types and traits. //! //! Importing the contents of this module is recommended, especially when //! working with generators and iterator expressions, as those operations //! are expressed mostly through traits. //! //! # Traits //! //! Traits from the [`primitive`] module for generating and decomposing //! iterators of topological data (e.g., [`Trigon`], [`Tetragon`], etc.) are //! re-exported so that functions in iterator expressions can be used more //! easily. //! //! Traits for (de)constructing [`MeshBuffer`]s and [`MeshGraph`]s are //! re-exported. These traits allow mesh types to be constructed from raw //! buffers and buffers to be re-indexed. //! //! Extension traits are also re-exported. //! //! # Types //! //! The [`Selector`] `enum` and its variants are re-exported for //! convenience. //! //! [`MeshBuffer`]: crate::buffer::MeshBuffer //! [`MeshGraph`]: crate::graph::MeshGraph //! [`Selector`]: crate::graph::Selector //! [`Tetragon`]: crate::primitive::Tetragon //! [`Trigon`]: crate::primitive::Trigon //! [`primitive`]: crate::primitive pub use crate::buffer::{ FromRawBuffers as _, FromRawBuffersWithArity as _, IntoFlatIndex as _, IntoStructuredIndex as _, }; pub use crate::builder::{FacetBuilder as _, MeshBuilder as _, SurfaceBuilder as _}; pub use crate::geometry::{FromGeometry as _, IntoGeometry as _}; pub use crate::graph::{ClosedView as _, Rebind as _, Selector}; pub use crate::index::{CollectWithIndexer as _, IndexVertices as _}; pub use crate::primitive::decompose::{ Edges as _, IntoEdges as _, IntoSubdivisions as _, IntoTetrahedrons as _, IntoTrigons as _, IntoVertices as _, Subdivide as _, Tetrahedrons as _, Triangulate as _, Vertices as _, }; pub use crate::primitive::generate::Generator as _; pub use crate::primitive::{ IntoPolygons as _, MapVertices as _, Polygonal as _, Topological as _, }; pub use crate::DynamicArity as _; pub use crate::IteratorExt as _; pub use Selector::ByIndex; pub use Selector::ByKey; } /// Arity of primitives and polygonal meshes. /// /// The _arity_ of a primitive topological structure (e.g., an edge, trigon, /// pentagon, etc.) is the number of edges that comprise the structure. For /// compound structures like polygonal meshes, arity describes the individual /// polygons that form the structure and may not be representable as a singular /// value. /// /// Arity is most generally described as an _open interval_ with a minimum and /// optional maximum inclusive range. This trait provides a conversion into this /// general form for all types that represent arity. See the implementations of /// this trait for more information. /// /// Types with arity implement the [`StaticArity`] and [`DynamicArity`] traits, /// which describe their type-level and value-level arity, respectively. /// /// [`DynamicArity`]: crate::DynamicArity /// [`StaticArity`]: crate::StaticArity pub trait Arity: Copy { fn into_interval(self) -> (usize, Option); } /// Singular arity. impl Arity for usize { fn into_interval(self) -> (usize, Option) { (self, Some(self)) } } /// Closed interval arity. /// /// This type represents a _closed interval_ arity with a minimum and maximum /// inclusive range. impl Arity for (usize, usize) { fn into_interval(self) -> (usize, Option) { let (min, max) = self; (min, Some(max)) } } /// Open interval arity. /// /// This type represents an _open interval_ arity with a minimum and optional /// maximum inclusive range. When there is no maximum (`None`), the maximum /// arity is unspecified. This typically means that there is no theoretical /// maximum. impl Arity for (usize, Option) { fn into_interval(self) -> (usize, Option) { self } } /// Type-level arity. /// /// This trait specifies the arity that a type supports. Values of a /// `StaticArity` type have an arity that reflects this constant, which may be /// any type or form implementing the [`Arity`] trait. /// /// [`Arity`]: crate::Arity pub trait StaticArity { type Static: Arity; const ARITY: Self::Static; } /// Value-level arity. /// /// This trait specifies the arity of a value at runtime. This is often /// distinct from the type-level arity of the [`StaticArity`] trait, which /// expresses the capabilities of a type. /// /// [`StaticArity`]: crate::StaticArity pub trait DynamicArity: StaticArity { type Dynamic: Arity; fn arity(&self) -> Self::Dynamic; } /// Topological types with fixed and singular arity. /// /// Types are _monomorphic_ if they have a fixed and singular arity as types and /// values. For example, [`Trigon`] always and only represents a trigon /// (triangle) with an arity of three. [`Trigon`] values always have an arity of /// three and types composed of only [`Trigon`]s have a compound arity of three. /// /// This contrasts _polymorphic_ types like [`BoundedPolygon`], which have an /// interval arity at the type-level and a singular but varying arity for values /// (because a [`BoundedPolygon`] value may be either a trigon or tertragon). /// /// [`BoundedPolygon`]: crate::primitive::BoundedPolygon /// [`Trigon`]: crate::primitive::Trigon pub trait Monomorphic: StaticArity {} /// Arity of a compound structure. /// /// `MeshArity` represents the arity of a compound structure, which may be /// _uniform_ or _non-uniform_. This is typically the value-level arity for /// mesh data structures like [`MeshGraph`] and [`MeshBuffer`]. /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer /// [`MeshGraph`]: crate::graph::MeshGraph #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum MeshArity { /// A compound structure has _uniform_ arity if all of its components have /// the same arity, such as a [`MeshBuffer`] composed entirely of trigons. /// /// [`MeshBuffer`]: crate::buffer::MeshBuffer Uniform(usize), /// A compound structure has _non-uniform_ arity if the arity of its /// components differ, such as a [`MeshGraph`] composed of trigons and /// tetragons. /// /// Non-uniform arity is represented as an inclusive range known as an /// _interval_. This is the minimum and maximum arity of the components, in /// that order. /// /// [`MeshGraph`]: crate::graph::MeshGraph NonUniform(usize, usize), } impl MeshArity { pub fn from_components(components: I) -> Self where T: DynamicArity, I: IntoIterator, I::Item: Borrow, { match components .into_iter() .map(|component| component.borrow().arity()) .minmax() { MinMaxResult::OneElement(exact) => MeshArity::Uniform(exact), MinMaxResult::MinMax(min, max) => MeshArity::NonUniform(min, max), _ => MeshArity::Uniform(0), } } } impl Arity for MeshArity { fn into_interval(self) -> (usize, Option) { match self { MeshArity::Uniform(exact) => (exact, Some(exact)), MeshArity::NonUniform(min, max) => (min, Some(max)), } } } pub trait TryFromIterator: Sized { type Error; fn try_from_iter(items: I) -> Result where I: Iterator; } impl TryFromIterator for [T; N] { type Error = (); fn try_from_iter(items: I) -> Result where I: Iterator, { items .has_exactly(N) .and_then(|items| items.collect::>().into_inner().ok()) .ok_or(()) } } macro_rules! count { ($x:tt $($xs:tt)*) => (1usize + count!($($xs)*)); () => (0usize); } macro_rules! substitute { ($_t:tt, $with:ty) => { $with }; } macro_rules! impl_try_from_iterator { (tuples => ($($i:ident),+)) => ( impl TryFromIterator for ($(substitute!(($i), T),)+) { type Error = (); // LINT: The `i` metavariable items are conventionally uppercase and represent type // names. Here, these names are substituted, but uppercase is used for // consistency. #[expect(non_snake_case)] fn try_from_iter(items: I) -> Result where I: Iterator, { use $crate::IteratorExt as _; items .has_exactly(count!($($i)*)) .map(|mut items| { $(let $i = items.next().unwrap();)* ($($i,)*) }) .ok_or(()) } } ); } impl_try_from_iterator!(tuples => (A, B)); impl_try_from_iterator!(tuples => (A, B, C)); impl_try_from_iterator!(tuples => (A, B, C, D)); impl_try_from_iterator!(tuples => (A, B, C, D, E)); impl_try_from_iterator!(tuples => (A, B, C, D, E, F)); /// Extension methods for types implementing [`Iterator`]. /// /// [`Iterator`]: std::iter::Iterator pub trait IteratorExt: Iterator + Sized { /// Provides an iterator over a window of duplets that includes the first /// item in the sequence at both the beginning and end of the iteration. /// /// Given a collection of ordered items $(a,b,c)$, this iterator yeilds the /// ordered items $((a,b),(b,c),(c,a))$. fn perimeter(self) -> Perimeter where Self::Item: Clone, { Perimeter::new(self) } /// Maps an iterator over [`graph`] views to the keys of those views. /// /// It is often useful to examine or collect the keys of views over a /// [`MeshGraph`]. This iterator avoids redundant use of /// [`map`][`Iterator::map`] to extract keys. /// /// # Examples /// /// Collecting keys of faces before a topological mutation in a /// [`MeshGraph`]: /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::Total; /// use nalgebra::Point3; /// use plexus::graph::MeshGraph; /// use plexus::prelude::*; /// use plexus::primitive::generate::Position; /// use plexus::primitive::sphere::UvSphere; /// /// type E3 = Point3>; /// /// let mut graph = UvSphere::new(6, 6) /// .polygons::>() /// .collect::>(); /// /// let keys = graph /// .faces() /// .filter(|face| face.arity() > 3) /// .keys() /// .collect::>(); /// for key in keys { /// graph.face_mut(key).unwrap().poke_with_offset(0.5); /// } /// ``` /// /// [`Iterator::map`]: std::iter::Iterator::map /// [`MeshGraph`]: crate::graph::MeshGraph /// [`graph`]: crate::graph fn keys(self) -> Keys where Self::Item: ClosedView, { Keys::new(self) } /// Determines if an iterator provides `n` or more items. /// /// Returns a peekable iterator if the source iterator provides at least `n` /// items, otherwise `None`. /// /// # Examples /// /// Ensuring that an iterator over vertices has an arity of at least three: /// /// ```rust /// use plexus::IteratorExt; /// /// fn is_convex(vertices: impl Iterator) -> bool { /// vertices /// .has_at_least(3) /// .and_then(|vertices| { /// for vertex in vertices { /// // ... /// } /// // ... /// # Some(0usize) /// }) /// .is_some() /// } /// ``` fn has_at_least(self, n: usize) -> Option> { peek_n(self, n).map(|mut peekable| { peekable.reset_peek(); peekable }) } fn has_exactly(self, n: usize) -> Option> { peek_n(self, n).and_then(|mut peekable| { peekable.peek().is_none().then(|| { peekable.reset_peek(); peekable }) }) } fn try_collect(self) -> Result where T: TryFromIterator, { T::try_from_iter(self) } } impl IteratorExt for I where I: Iterator {} /// Iterator that produces a window of duplets over its input. /// /// The duplets produced include the first item in the input at both the /// beginning and end of the iteration, forming a perimeter. Given a collection /// of ordered items $(a,b,c)$, this iterator yields the ordered items /// $((a,b),(b,c),(c,a))$. /// /// See [`IteratorExt::perimeter`]. /// /// [`IteratorExt::perimeter`]: crate::IteratorExt::perimeter #[derive(Clone)] pub struct Perimeter where I: Iterator, I::Item: Clone, { input: I, first: Option, previous: Option, } impl Perimeter where I: Iterator, I::Item: Clone, { fn new(mut input: I) -> Self { let first = input.next(); let previous = first.clone(); Perimeter { input, first, previous, } } } impl Iterator for Perimeter where I: Iterator, I::Item: Clone, { type Item = (I::Item, I::Item); fn next(&mut self) -> Option { let next = self.input.next(); self.previous .clone() .zip(next.or_else(|| self.first.take())) .map(|(a, b)| { self.previous = Some(b.clone()); (a, b) }) } fn size_hint(&self) -> (usize, Option) { self.input.size_hint() } } /// Iterator that maps [`graph`] views to their keys. /// /// See [`IteratorExt::keys`]. /// /// [`graph`]: crate::graph /// [`IteratorExt::keys`]: crate::IteratorExt::keys #[derive(Clone)] pub struct Keys where I: Iterator, I::Item: ClosedView, { input: I, } impl Keys where I: Iterator, I::Item: ClosedView, { fn new(input: I) -> Self { Keys { input } } } impl Iterator for Keys where I: Iterator, I::Item: ClosedView, { type Item = ::Key; fn next(&mut self) -> Option { self.input.next().map(|view| view.key()) } fn size_hint(&self) -> (usize, Option) { self.input.size_hint() } } fn peek_n(input: I, n: usize) -> Option> where I: Iterator, { let mut peekable = itertools::multipeek(input); for _ in 0..n { peekable.peek()?; } Some(peekable) } /// Computes the arity of a polygon with `n` vertices. /// /// For `n` greater than two, these values are the same and well-formed. For `n` /// less than three, the polygon is degenerate (a digon, monogon, or zerogon), /// all of which are assigned an arity of one. Note that some topological types /// do not allow `n` being one nor zero. const fn n_arity(n: usize) -> usize { if n < 3 { 1 } else { n } } ================================================ FILE: plexus/src/primitive/cube.rs ================================================ //! Cube primitives. //! //! # Examples //! //! ```rust //! # extern crate decorum; //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use decorum::R32; //! use nalgebra::Point3; //! use plexus::graph::MeshGraph; //! use plexus::prelude::*; //! use plexus::primitive::cube::Cube; //! use plexus::primitive::generate::Position; //! //! let mut graph = Cube::new() //! .polygons::>>() //! .collect::>>(); //! ``` use num::One; use theon::adjunct::{Converged, Map}; use theon::query::Unit; use theon::space::{Basis, EuclideanSpace, FiniteDimensional, InnerSpace, Scalar, Vector}; use typenum::U3; use crate::primitive::generate::{ Attribute, AttributeGenerator, AttributePolygonGenerator, AttributeVertexGenerator, Generator, IndexingPolygonGenerator, Normal, PolygonGenerator, Position, }; use crate::primitive::Tetragon; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Plane { Xy, // front Nxy, // back Yz, // right Nyz, // left Xz, // bottom Nxz, // top } impl Plane { pub fn normal(self) -> Unit where S: Basis + FiniteDimensional + InnerSpace, { match self { Plane::Xy => Unit::::z(), // front Plane::Nxy => -Unit::::z(), // back Plane::Yz => Unit::::x(), // right Plane::Nyz => -Unit::::x(), // left Plane::Xz => -Unit::::y(), // bottom Plane::Nxz => Unit::::y(), // top } } } impl Attribute for Plane {} #[derive(Clone, Copy)] pub struct Bounds where S: EuclideanSpace, { lower: Scalar, upper: Scalar, } impl Bounds where S: EuclideanSpace, { pub fn with_radius(radius: Scalar) -> Self { Bounds { lower: -radius, upper: radius, } } pub fn with_width(width: Scalar) -> Self { Self::with_radius(width / (Scalar::::one() + One::one())) } pub fn unit_radius() -> Self { Self::with_radius(One::one()) } pub fn unit_width() -> Self { Self::with_width(One::one()) } } impl Default for Bounds where S: EuclideanSpace, { fn default() -> Self { Self::unit_width() } } #[derive(Clone, Copy)] pub struct Cube; impl Cube { pub fn new() -> Self { Cube } } impl Default for Cube { fn default() -> Self { Cube::new() } } impl PolygonGenerator for Cube { fn polygon_count(&self) -> usize { 6 } } impl AttributeGenerator> for Cube where S: EuclideanSpace + FiniteDimensional, { type State = (); } impl AttributeVertexGenerator> for Cube where S: EuclideanSpace + FiniteDimensional, { type Output = Unit>; fn vertex_count(&self) -> usize { self.polygon_count() } fn vertex_from(&self, _: &Self::State, index: usize) -> Self::Output { AttributeVertexGenerator::::vertex_from(self, &(), index).normal::>() } } impl AttributePolygonGenerator> for Cube where S: EuclideanSpace + FiniteDimensional, { type Output = Tetragon>>; fn polygon_from(&self, state: &Self::State, index: usize) -> Self::Output { IndexingPolygonGenerator::>::indexing_polygon(self, index) .map(|index| AttributeVertexGenerator::>::vertex_from(self, state, index)) } } impl IndexingPolygonGenerator> for Cube { type Output = Tetragon; fn indexing_polygon(&self, index: usize) -> Self::Output { assert!(index < self.polygon_count()); Tetragon::converged(index) } } impl AttributeGenerator> for Cube where S: EuclideanSpace + FiniteDimensional, { type State = Bounds; } impl AttributeVertexGenerator> for Cube where S: EuclideanSpace + FiniteDimensional, { type Output = S; fn vertex_count(&self) -> usize { 8 } fn vertex_from(&self, state: &Self::State, index: usize) -> Self::Output { let x = if index & 0b100 == 0b100 { state.upper } else { state.lower }; let y = if index & 0b010 == 0b010 { state.upper } else { state.lower }; let z = if index & 0b001 == 0b001 { state.upper } else { state.lower }; S::from_xyz(x, y, z) } } impl AttributePolygonGenerator> for Cube where S: EuclideanSpace + FiniteDimensional, { type Output = Tetragon; fn polygon_from(&self, state: &Self::State, index: usize) -> Self::Output { IndexingPolygonGenerator::>::indexing_polygon(self, index) .map(|index| AttributeVertexGenerator::>::vertex_from(self, state, index)) } } impl IndexingPolygonGenerator> for Cube { type Output = Tetragon; fn indexing_polygon(&self, index: usize) -> Self::Output { match index { 0 => Tetragon::new(5, 7, 3, 1), // front 1 => Tetragon::new(6, 7, 5, 4), // right 2 => Tetragon::new(3, 7, 6, 2), // top 3 => Tetragon::new(0, 1, 3, 2), // left 4 => Tetragon::new(4, 5, 1, 0), // bottom 5 => Tetragon::new(0, 2, 6, 4), // back _ => panic!(), } } } impl AttributeGenerator for Cube { type State = (); } impl AttributeVertexGenerator for Cube { type Output = Plane; fn vertex_count(&self) -> usize { self.polygon_count() } fn vertex_from(&self, _: &Self::State, index: usize) -> Self::Output { match index { 0 => Plane::Xy, // front 1 => Plane::Yz, // right 2 => Plane::Nxz, // top 3 => Plane::Nyz, // left 4 => Plane::Xz, // bottom 5 => Plane::Nxy, // back _ => panic!(), } } } impl AttributePolygonGenerator for Cube { type Output = Tetragon; fn polygon_from(&self, state: &Self::State, index: usize) -> Self::Output { IndexingPolygonGenerator::::indexing_polygon(self, index) .map(|index| AttributeVertexGenerator::::vertex_from(self, state, index)) } } impl IndexingPolygonGenerator for Cube { type Output = Tetragon; fn indexing_polygon(&self, index: usize) -> Self::Output { match index { 0 => Tetragon::converged(0), // front 1 => Tetragon::converged(1), // right 2 => Tetragon::converged(2), // top 3 => Tetragon::converged(3), // left 4 => Tetragon::converged(4), // bottom 5 => Tetragon::converged(5), // back _ => panic!(), } } } impl Generator for Cube {} ================================================ FILE: plexus/src/primitive/decompose.rs ================================================ //! Decomposition and tessellation. //! //! The [`Decompose`] iterator uses various traits to decompose and tessellate //! iterators of topological structures. //! //! This module provides two kinds of decomposition traits: conversions and //! [`Iterator`] extensions. Conversion traits are implemented by topological //! types and decompose a single structure into any number of output structures. //! Extensions are implemented for iterators of topological structures and //! perform the corresponding conversion on the input items to produce flattened //! output. For example, [`IntoTrigons`] converts a [`Polygonal`] type into an //! iterator of [`Trigon`]s while [`Triangulate`] does the same to the items of //! an iterator. //! //! Many of these traits are re-exported in the [`prelude`] module. //! //! # Examples //! //! Tessellating a [`Tetragon`] into [`Trigon`]s: //! //! ```rust //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use nalgebra::Point2; //! use plexus::prelude::*; //! use plexus::primitive::Tetragon; //! //! type E2 = Point2; //! //! let square = Tetragon::from([ //! E2::new(1.0, 1.0), //! E2::new(-1.0, 1.0), //! E2::new(-1.0, -1.0), //! E2::new(1.0, -1.0), //! ]); //! let trigons = square.into_trigons(); //! ``` //! //! Tessellating an iterator of [`Tetragon`]s from a [generator][`generate`]: //! //! ```rust //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use nalgebra::Point3; //! use plexus::prelude::*; //! use plexus::primitive::cube::Cube; //! use plexus::primitive::generate::Position; //! //! type E3 = Point3; //! //! let polygons: Vec<_> = Cube::new() //! .polygons::>() //! .subdivide() //! .triangulate() //! .collect(); //! ``` //! //! [`Iterator`]: std::iter::Iterator //! [`prelude`]: crate::prelude //! [`Decompose`]: crate::primitive::decompose::Decompose //! [`IntoTrigons`]: crate::primitive::decompose::IntoTrigons //! [`Triangulate`]: crate::primitive::decompose::Triangulate //! [`generate`]: crate::primitive::generate //! [`Polygonal`]: crate::primitive::Polygonal //! [`Tetragon`]: crate::primitive::Tetragon //! [`Trigon`]: crate::primitive::Trigon use arrayvec::ArrayVec; use std::collections::VecDeque; use std::iter::IntoIterator; use theon::ops::Interpolate; use typenum::{Cmp, Greater, U1}; use crate::constant::{Constant, ToType, TypeOf}; use crate::primitive::{ BoundedPolygon, Edge, NGon, Polygonal, Tetragon, Topological, Trigon, UnboundedPolygon, }; use crate::IteratorExt as _; pub struct Decompose where R: IntoIterator, { input: I, output: VecDeque, f: fn(P) -> R, } impl Decompose where R: IntoIterator, { pub(in crate::primitive) fn new(input: I, f: fn(P) -> R) -> Self { Decompose { input, output: VecDeque::new(), f, } } } impl Decompose where I: Iterator, R: IntoIterator, { /// Reapplies a congruent decomposition. /// /// A decomposition is _congruent_ if its input and output types are the /// same. This is useful when the number of applications is somewhat large /// or variable, in which case chaining calls is impractical or impossible. /// /// # Examples /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::index::{Flat4, HashIndexer}; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// let (indices, positions) = Cube::new() /// .polygons::>>() /// .subdivide() /// .remap(7) // 8 subdivision operations are applied. /// .index_vertices::(HashIndexer::default()); /// ``` pub fn remap(self, n: usize) -> Decompose, P, P, R> { let Decompose { input, output, f } = self; Decompose::new(output.into_iter().rev().chain(remap(n, input, f)), f) } } impl Iterator for Decompose where I: Iterator, R: IntoIterator, { type Item = Q; fn next(&mut self) -> Option { loop { if let Some(ngon) = self.output.pop_front() { return Some(ngon); } if let Some(ngon) = self.input.next() { self.output.extend((self.f)(ngon)); } else { return None; } } } fn size_hint(&self) -> (usize, Option) { let (lower, _) = self.input.size_hint(); (lower, None) } } pub trait IntoVertices: Topological { type Output: IntoIterator; fn into_vertices(self) -> Self::Output; } impl IntoVertices for T where T: Topological, { type Output = ::IntoIter; fn into_vertices(self) -> Self::Output { self.into_iter() } } pub trait IntoEdges: Topological { type Output: IntoIterator>; fn into_edges(self) -> Self::Output; } pub trait IntoTrigons: Polygonal { type Output: IntoIterator>; fn into_trigons(self) -> Self::Output; } pub trait IntoSubdivisions: Polygonal { type Output: IntoIterator; fn into_subdivisions(self) -> Self::Output; } pub trait IntoTetrahedrons: Polygonal { fn into_tetrahedrons(self) -> ArrayVec, 4>; } impl IntoEdges for NGon where G: Clone, Constant: ToType, TypeOf: Cmp, { // TODO: As of Rust 1.51.1, it is not possible to constrain constant // generics nor use them in expressions. If and when this is possible, // do not implement this trait for degenerate `NGon`s and use use an // `ArrayVec, {if N == 2 { 1 } else { N }}>` as // output. type Output = Vec>; fn into_edges(self) -> Self::Output { if N == 2 { // At the time of writing, it is not possible to specialize this // case for `NGon` nor prove to the compiler that `Self` is of // type `Edge` when the condition `N == 2` is `true`. vec![self.into_iter().try_collect().unwrap()] } else { self.into_iter() .perimeter() .map(|(a, b)| Edge::new(a, b)) .collect() } } } impl IntoEdges for BoundedPolygon where T: Clone, { type Output = Vec>; fn into_edges(self) -> Self::Output { match self { BoundedPolygon::N3(trigon) => trigon.into_edges().into_iter().collect(), BoundedPolygon::N4(tetragon) => tetragon.into_edges().into_iter().collect(), } } } impl IntoEdges for UnboundedPolygon where T: Clone, { type Output = Vec>; fn into_edges(self) -> Self::Output { self.into_iter() .perimeter() .map(|(a, b)| Edge::new(a, b)) .collect() } } impl IntoTrigons for Trigon { type Output = ArrayVec, 1>; fn into_trigons(self) -> Self::Output { ArrayVec::from([self]) } } impl IntoTrigons for Tetragon where T: Clone, { type Output = ArrayVec, 2>; fn into_trigons(self) -> Self::Output { let [a, b, c, d] = self.into_array(); ArrayVec::from([Trigon::new(a.clone(), b, c.clone()), Trigon::new(c, d, a)]) } } impl IntoTrigons for BoundedPolygon where T: Clone, { type Output = Vec>; fn into_trigons(self) -> Self::Output { match self { BoundedPolygon::N3(trigon) => trigon.into_trigons().into_iter().collect(), BoundedPolygon::N4(tetragon) => tetragon.into_trigons().into_iter().collect(), } } } impl IntoSubdivisions for Trigon where T: Clone + Interpolate, { type Output = ArrayVec, 2>; fn into_subdivisions(self) -> Self::Output { let [a, b, c] = self.into_array(); let ac = a.clone().midpoint(c.clone()); ArrayVec::from([Trigon::new(b.clone(), ac.clone(), a), Trigon::new(c, ac, b)]) } } impl IntoSubdivisions for Tetragon where T: Clone + Interpolate, { type Output = ArrayVec, 4>; fn into_subdivisions(self) -> Self::Output { let [a, b, c, d] = self.into_array(); let ab = a.clone().midpoint(b.clone()); let bc = b.clone().midpoint(c.clone()); let cd = c.clone().midpoint(d.clone()); let da = d.clone().midpoint(a.clone()); let ac = a.clone().midpoint(c.clone()); // Diagonal. ArrayVec::from([ Tetragon::new(a, ab.clone(), ac.clone(), da.clone()), Tetragon::new(ab, b, bc.clone(), ac.clone()), Tetragon::new(ac.clone(), bc, c, cd.clone()), Tetragon::new(da, ac, cd, d), ]) } } impl IntoTetrahedrons for Tetragon where T: Clone + Interpolate, { fn into_tetrahedrons(self) -> ArrayVec, 4> { let [a, b, c, d] = self.into_array(); let ac = a.clone().midpoint(c.clone()); // Diagonal. ArrayVec::from([ Trigon::new(a.clone(), b.clone(), ac.clone()), Trigon::new(b, c.clone(), ac.clone()), Trigon::new(c, d.clone(), ac.clone()), Trigon::new(d, a, ac), ]) } } impl IntoSubdivisions for BoundedPolygon where T: Clone + Interpolate, { type Output = Vec; fn into_subdivisions(self) -> Self::Output { match self { BoundedPolygon::N3(trigon) => trigon .into_subdivisions() .into_iter() .map(|trigon| trigon.into()) .collect(), BoundedPolygon::N4(tetragon) => tetragon .into_subdivisions() .into_iter() .map(|tetragon| tetragon.into()) .collect(), } } } pub trait Vertices

: Sized where P: IntoVertices, { fn vertices(self) -> Decompose; } impl Vertices

for I where I: Iterator, P: IntoVertices, { fn vertices(self) -> Decompose { Decompose::new(self, P::into_vertices) } } pub trait Edges

: Sized where P: IntoEdges, { fn edges(self) -> Decompose, P::Output>; } impl Edges

for I where I: Iterator, P: IntoEdges, P::Vertex: Clone, { fn edges(self) -> Decompose, P::Output> { Decompose::new(self, P::into_edges) } } pub trait Triangulate

: Sized where P: IntoTrigons, { fn triangulate(self) -> Decompose, P::Output>; } impl Triangulate

for I where I: Iterator, P: IntoTrigons, { fn triangulate(self) -> Decompose, P::Output> { Decompose::new(self, P::into_trigons) } } pub trait Subdivide

: Sized where P: IntoSubdivisions, { fn subdivide(self) -> Decompose; } impl Subdivide

for I where I: Iterator, P: IntoSubdivisions, { fn subdivide(self) -> Decompose { Decompose::new(self, P::into_subdivisions) } } pub trait Tetrahedrons: Sized { fn tetrahedrons(self) -> Decompose, Trigon, ArrayVec, 4>>; } impl Tetrahedrons for I where I: Iterator>, T: Clone + Interpolate, { fn tetrahedrons(self) -> Decompose, Trigon, ArrayVec, 4>> { Decompose::new(self, Tetragon::into_tetrahedrons) } } fn remap(n: usize, ngons: I, f: F) -> Vec

where I: IntoIterator, R: IntoIterator, F: Fn(P) -> R, { let mut ngons: Vec<_> = ngons.into_iter().collect(); for _ in 0..n { ngons = ngons.into_iter().flat_map(&f).collect(); } ngons } ================================================ FILE: plexus/src/primitive/generate.rs ================================================ //! Polytope generation. //! //! This module provides a generic iterator and traits for generating polygons //! and vertices containing geometric attributes of polytopes like cubes and //! spheres. The [`Generate`] iterator can be used in iterator expressions. //! //! The primary API of this module is provided by the [`Generator`] trait, which //! is implemented by polytope types like [`Cube`] and [`UvSphere`]. //! //! # Examples //! //! Generating [raw buffers][`buffer`] from the positional data of a //! [$uv$-sphere][`UvSphere`]: //! //! ```rust //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use nalgebra::Point3; //! use plexus::prelude::*; //! use plexus::primitive::generate::Position; //! use plexus::primitive::sphere::UvSphere; //! //! let sphere = UvSphere::new(16, 16); //! //! // Generate the unique set of positional vertices. //! let positions = sphere //! .vertices::>>() //! .collect::>(); //! //! // Generate polygons that index the unique set of positional vertices. The //! // polygons are decomposed into triangles and then into vertices, where each //! // vertex is an index into the position data. //! let indices = sphere //! .indexing_polygons::() //! .triangulate() //! .vertices() //! .collect::>(); //! ``` //! //! [`buffer`]: crate::buffer //! [`Cube`]: crate::primitive::cube::Cube //! [`Generate`]: crate::primitive::generate::Generate //! [`Generator`]: crate::primitive::generate::Generator //! [`UvSphere`]: crate::primitive::sphere::UvSphere use std::marker::PhantomData; use std::ops::Range; use crate::primitive::Polygonal; /// Geometric attribute. /// /// Types implementing this trait can be used with [`Generator`] to query /// geometric attributes. For example, the [`Position`] type can be used to get /// positional data for cubes or spheres via [`Cube`] and [`UvSphere`]. /// /// [`Cube`]: crate::primitive::cube::Cube /// [`Generator`]: crate::primitive::generate::Generator /// [`Position`]: crate::primitive::generate::Position /// [`UvSphere`]: crate::primitive::sphere::UvSphere pub trait Attribute {} /// Meta-attribute for surface normals. /// /// Describes the surface normals of a polytope. The generated data is derived /// from the type parameter `S`, which typically requires [`EuclideanSpace`]. /// /// # Examples /// /// Generating raw buffers with normal data of a [$uv$-sphere][`UvSphere`]: /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::index::{Flat3, HashIndexer}; /// use plexus::prelude::*; /// use plexus::primitive::generate::Normal; /// use plexus::primitive::sphere::UvSphere; /// /// let (indices, normals) = UvSphere::new(8, 8) /// .polygons::>>() /// .map_vertices(|normal| normal.into_inner()) /// .triangulate() /// .index_vertices::(HashIndexer::default()); /// ``` /// /// [`EuclideanSpace`]: theon::space::EuclideanSpace /// [`UvSphere`]: crate::primitive::sphere::UvSphere pub struct Normal { phantom: PhantomData S>, } impl Attribute for Normal {} /// Meta-attribute for positions. /// /// Describes the position of vertices in a polytope. The generated data is /// derived from the type parameter `S`, which typically requires /// [`EuclideanSpace`]. /// /// # Examples /// /// Generating raw buffers with positional data of a [cube][`Cube`]: /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::index::{Flat3, HashIndexer}; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// use plexus::primitive::UnboundedPolygon; /// /// let (indices, positions) = Cube::new() /// .polygons::>>() /// .triangulate() /// .index_vertices::, _>(HashIndexer::default()); /// ``` /// /// [`EuclideanSpace`]: theon::space::EuclideanSpace /// [`Cube`]: crate::primitive::cube::Cube /// [`UvSphere`]: crate::primitive::sphere::UvSphere pub struct Position { phantom: PhantomData S>, } impl Attribute for Position {} /// Iterator that generates topology and geometric attributes. pub struct Generate<'a, G, S, P> where G: 'a, { generator: &'a G, state: S, range: Range, f: fn(&'a G, &S, usize) -> P, } impl<'a, G, S, P> Generate<'a, G, S, P> where G: 'a, { fn new(generator: &'a G, state: S, n: usize, f: fn(&'a G, &S, usize) -> P) -> Self { Generate { generator, state, range: 0..n, f, } } } impl<'a, G, S, P> Iterator for Generate<'a, G, S, P> where G: 'a, { type Item = P; fn next(&mut self) -> Option { self.range .next() .map(|index| (self.f)(self.generator, &self.state, index)) } fn size_hint(&self) -> (usize, Option) { self.range.size_hint() } } pub trait PolygonGenerator { fn polygon_count(&self) -> usize; } pub trait AttributeGenerator where A: Attribute, { type State: Default; } pub trait AttributePolygonGenerator: AttributeGenerator + PolygonGenerator where A: Attribute, { type Output: Polygonal; fn polygon_from(&self, state: &Self::State, index: usize) -> Self::Output; } pub trait AttributeVertexGenerator: AttributeGenerator where A: Attribute, { type Output; fn vertex_count(&self) -> usize; fn vertex_from(&self, state: &Self::State, index: usize) -> Self::Output; } pub trait IndexingPolygonGenerator: PolygonGenerator where A: Attribute, { type Output: Polygonal; fn indexing_polygon(&self, index: usize) -> Self::Output; } /// Functions for iterating over the topology and geometry of polytopes. pub trait Generator: Sized { /// Gets an iterator over the set of **unique** vertices with the given /// attribute data. /// /// Each geometric attribute has an independent set of unique values. For /// example, [`Cube`] generates six unique surface normals and eight unique /// positions. /// /// This can be paired with the /// [`indexing_polygons`][`Generator::indexing_polygons`] function to index /// the set of vertices. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point3; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// type E3 = Point3; /// /// let cube = Cube::new(); /// /// let positions = cube.vertices::>().collect::>(); /// let indices = cube /// .indexing_polygons::() /// .triangulate() /// .vertices() /// .collect::>(); /// ``` /// /// [`Cube`]: crate::primitive::cube::Cube /// [`Generator::indexing_polygons`]: crate::primitive::generate::Generator::indexing_polygons fn vertices( &self, ) -> Generate>::Output> where Self: AttributeVertexGenerator, A: Attribute, { self.vertices_from(Default::default()) } fn vertices_from( &self, state: Self::State, ) -> Generate>::Output> where Self: AttributeVertexGenerator, A: Attribute, { Generate::new(self, state, self.vertex_count(), Self::vertex_from) } /// Gets an iterator over the set of polygons with the given attribute data. /// /// # Examples /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R64; /// use nalgebra::Point3; /// use plexus::index::HashIndexer; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// use plexus::primitive::Tetragon; /// /// let (indices, positions) = Cube::new() /// .polygons::>>() /// .index_vertices::, _>(HashIndexer::default()); /// ``` fn polygons( &self, ) -> Generate>::Output> where Self: AttributePolygonGenerator, A: Attribute, { self.polygons_from(Default::default()) } fn polygons_from( &self, state: Self::State, ) -> Generate>::Output> where Self: AttributePolygonGenerator, A: Attribute, { Generate::new(self, state, self.polygon_count(), Self::polygon_from) } /// Gets an iterator over a set of polygons that index the unique set of /// vertices with the given attribute. /// /// Indexing differs per geometric attribute, because each attribute has an /// independent set of unique values. For example, [`Cube`] generates six /// unique surface normals and eight unique positions. /// /// When used with meta-attribute types like [`Position`], input types are /// not needed and default type parameters can be used instead. For example, /// if `Position>` is used to generate positional data, then /// `Position<()>` (or `Position`) can be used to generate indexing /// polygons. /// /// # Examples /// /// ```rust /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use nalgebra::Point3; /// use plexus::buffer::MeshBuffer4; /// use plexus::prelude::*; /// use plexus::primitive::cube::Cube; /// use plexus::primitive::generate::Position; /// /// type E3 = Point3; /// /// let cube = Cube::new(); /// let buffer = MeshBuffer4::::from_raw_buffers( /// cube.indexing_polygons::(), /// cube.vertices::>(), /// ); /// ``` /// /// [`Cube`]: crate::primitive::cube::Cube /// [`Position`]: crate::primitive::generate::Position fn indexing_polygons(&self) -> Generate where Self: IndexingPolygonGenerator, A: Attribute, { Generate::new(self, (), self.polygon_count(), |generator, _, index| { generator.indexing_polygon(index) }) } } ================================================ FILE: plexus/src/primitive/mod.rs ================================================ //! Primitive topological structures. //! //! This module provides composable primitives that describe polygonal //! structures. This includes simple $n$-gons like triangles, polytope //! [generators][`generate`], and iterator expressions that compose and //! decompose iterators of primitives. //! //! Types in this module generally describe [cycle graphs][cycle-graph] and are //! not strictly geometric. For example, [`Polygonal`] types may be //! geometrically degenerate (e.g., collinear, converged, etc.) or used to //! approximate polygons from $\Reals^2$ embedded into higher-dimensional //! spaces. These types are also used for indexing, in which case their //! representation and data are both entirely topological. //! //! Plexus uses the terms _trigon_ and _tetragon_ for its polygon types, which //! mean _triangle_ and _quadrilateral_, respectively. This is done for //! consistency with higher arity polygon names (e.g., _decagon_). In some //! contexts, the term _triangle_ is still used, such as in functions concerning //! _triangulation_. //! //! # Representations //! //! Plexus provides various topological types with different capabilities //! summarized below: //! //! | Type | Morphism | Arity | Map | Zip | Tessellate | //! |--------------------|-------------|--------------|-----|-----|------------| //! | `NGon` | Monomorphic | $1,\[3,32\]$ | Yes | Yes | Yes | //! | `BoundedPolygon` | Polymorphic | $\[3,4\]$ | Yes | No | Yes | //! | `UnboundedPolygon` | Polymorphic | $[3,\infin)$ | Yes | No | No | //! //! [`NGon`] is [monomorphic][`Monomorphic`] and supports the broadest set of //! traits and features. However, its [type-level][`StaticArity`] arity is //! somewhat limited and its [value-level][`DynamicArity`] arity is fixed. This //! means, for example, that it is not possible to have an iterator of [`NGon`]s //! represent both trigons and tetragons, because these polygons must be //! distinct [`NGon`] types. //! //! The polygon types [`BoundedPolygon`] and [`UnboundedPolygon`] are //! polymorphic and therefore support variable [value-level][`DynamicArity`] //! [arity][`Arity`]. [`BoundedPolygon`] only expresses a limited set of //! polygons by enumerating [`NGon`]s, but supports decomposition and other //! traits. [`UnboundedPolygon`] is most flexible and can represent any //! arbitrary polygon, but does not support any tessellation features. //! //! [`Edge`]s are always represented as `NGon<_, 2>`. //! //! # Examples //! //! Generating [raw buffers][`buffer`] with positional data of a [cube][`Cube`] //! using an [`Indexer`]: //! //! ```rust //! # extern crate decorum; //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use decorum::R32; //! use nalgebra::Point3; //! use plexus::index::{Flat3, HashIndexer}; //! use plexus::prelude::*; //! use plexus::primitive::cube::Cube; //! use plexus::primitive::generate::Position; //! //! let (indices, positions) = Cube::new() //! .polygons::>>() //! .triangulate() //! .index_vertices::(HashIndexer::default()); //! ``` //! //! [cycle-graph]: https://en.wikipedia.org/wiki/cycle_graph //! //! [`buffer`]: crate::buffer //! [`Indexer`]: crate::index::Indexer //! [`Cube`]: crate::primitive::cube::Cube //! [`BoundedPolygon`]: crate::primitive::BoundedPolygon //! [`NGon`]: crate::primitive::NGon //! [`UnboundedPolygon`]: crate::primitive::UnboundedPolygon //! [`Arity`]: crate::Arity //! [`DynamicArity`]: crate::DynamicArity //! [`StaticArity`]: crate::StaticArity pub mod cube; pub mod decompose; pub mod generate; pub mod sphere; use arrayvec::ArrayVec; use itertools::izip; use itertools::structs::Zip as OuterZip; // Avoid collision with `Zip`. use num::traits::real::Real; use num::traits::FloatConst; use num::{Integer, One, Unsigned, Zero}; use smallvec::{smallvec, SmallVec}; use std::array; use std::convert::TryInto; use std::fmt::{self, Debug, Formatter}; use std::marker::PhantomData; use std::ops::{Index, IndexMut}; use theon::adjunct::{Adjunct, Converged, Extend, Fold, FromItems, IntoItems, Map, ZipMap}; use theon::ops::Cross; use theon::query::{Intersection, Line, LineLine, LinePlane, Plane, Unit}; use theon::space::{EuclideanSpace, FiniteDimensional, Scalar, Vector, VectorSpace}; use theon::{AsPosition, AsPositionMut, Position}; use typenum::{Cmp, Greater, U1, U2, U3}; use crate::constant::{Constant, ToType, TypeOf}; use crate::geometry::partition::PointPartition; use crate::primitive::decompose::IntoVertices; use crate::{DynamicArity, IteratorExt as _, Monomorphic, StaticArity, TryFromIterator}; /// Topological structure. /// /// Types implementing `Topological` provide some notion of adjacency between /// vertices of their `Vertex` type. These types typically represent cycle /// graphs and polygonal structures, but may also include degenerate forms like /// monogons. pub trait Topological: Adjunct::Vertex> + AsMut<[::Vertex]> + AsRef<[::Vertex]> + DynamicArity + IntoIterator::Vertex> + Sized { type Vertex; fn try_from_slice(vertices: T) -> Option where Self::Vertex: Copy, T: AsRef<[Self::Vertex]>; /// Embeds an $n$-gon from $\Reals^2$ into $\Reals^3$. /// /// The scalar for the additional basis is normalized to the given value. /// /// # Examples /// /// Embedding a triangle into the $xy$-plane at $z=1$: /// /// ```rust /// # extern crate nalgebra; /// # extern crate theon; /// # /// use nalgebra::Point2; /// use plexus::primitive::{Topological, Trigon}; /// use theon::space::EuclideanSpace; /// /// type E2 = Point2; /// /// let trigon = Trigon::embed_into_e3_xy( /// Trigon::from([ /// E2::from_xy(-1.0, 0.0), /// E2::from_xy(0.0, 1.0), /// E2::from_xy(1.0, 0.0), /// ]), /// 1.0, /// ); /// ``` fn embed_into_e3_xy

(ngon: P, z: Scalar) -> Self where Self::Vertex: EuclideanSpace + FiniteDimensional, P: Map + Topological, P::Vertex: EuclideanSpace + FiniteDimensional + Extend, Vector: VectorSpace>, { Self::embed_into_e3_xy_with(ngon, z, |position| position) } fn embed_into_e3_xy_with(ngon: P, z: Scalar>, mut f: F) -> Self where Self::Vertex: AsPosition, Position: EuclideanSpace + FiniteDimensional, P: Map + Topological, P::Vertex: EuclideanSpace + FiniteDimensional + Extend>, Vector: VectorSpace>>, F: FnMut(Position) -> Self::Vertex, { ngon.map(move |position| f(position.extend(z))) } /// Embeds an $n$-gon from $\Reals^2$ into $\Reals^3$. /// /// The $n$-gon is rotated into the given plane about the origin. /// /// # Examples /// /// Embedding a triangle into the $xy$-plane at $z=0$: /// /// ```rust,no_run /// # extern crate nalgebra; /// # extern crate theon; /// # /// use nalgebra::{Point2, Point3}; /// use plexus::geometry::{Plane, Unit}; /// use plexus::primitive::{Topological, Trigon}; /// use theon::space::{Basis, EuclideanSpace}; /// /// type E2 = Point2; /// type E3 = Point3; /// /// let trigon = Trigon::embed_into_e3_plane( /// Trigon::from([ /// E2::from_xy(-1.0, 0.0), /// E2::from_xy(0.0, 1.0), /// E2::from_xy(1.0, 0.0), /// ]), /// Plane:: { /// origin: EuclideanSpace::origin(), /// normal: Unit::z(), /// }, /// ); /// ``` fn embed_into_e3_plane

(ngon: P, plane: Plane) -> Self where Self::Vertex: EuclideanSpace + FiniteDimensional, P: Map + Topological, P::Vertex: EuclideanSpace + FiniteDimensional + Extend, Vector: VectorSpace>, { Self::embed_into_e3_plane_with(ngon, plane, |position| position) } fn embed_into_e3_plane_with(ngon: P, _: Plane>, f: F) -> Self where Self::Vertex: AsPosition, Position: EuclideanSpace + FiniteDimensional, P: Map + Topological, P::Vertex: EuclideanSpace + FiniteDimensional + Extend>, Vector: VectorSpace>>, F: FnMut(Position) -> Self::Vertex, { // TODO: Rotate the embedded n-gon into the plane about the origin. let _ = Self::embed_into_e3_xy_with(ngon, Zero::zero(), f); unimplemented!() } /// Projects an $n$-gon into a plane. /// /// The positions in each vertex of the $n$-gon are translated along the /// normal of the plane. #[must_use] fn project_into_plane(mut self, plane: Plane>) -> Self where Self::Vertex: AsPositionMut, Position: EuclideanSpace + FiniteDimensional, as FiniteDimensional>::N: Cmp, { for vertex in self.as_mut() { let line = Line::> { origin: *vertex.as_position(), direction: plane.normal, }; // TODO: Assert that this case always occurs; the line lies along // the normal. if let Some(LinePlane::TimeOfImpact(distance)) = line.intersection(&plane) { let translation = *line.direction.get() * distance; vertex.transform(|position| *position + translation); } } self } // TODO: Once GATs are stabilized, consider using a separate trait for this // to avoid using `Vec` and allocating. This may also require a // different name to avoid collisions with the `decompose` module. See // https://github.com/rust-lang/rust/issues/44265 fn edges(&self) -> Vec> { self.as_ref() .iter() .perimeter() .map(|(a, b)| Edge::new(a, b)) .collect() } } /// Polygonal structure. /// /// `Polygonal` types form cycle graphs and extend [`Topological`] types with /// the additional constraint that all vertices have a degree and valence of /// two. This requires at least three edges and forbids degenerate structures /// like monogons. /// /// These types are topological and do not necessarily represent geometric /// concepts like polygons in the most strict sense. Polygons are only defined /// in $\Reals^2$ and cannot have converged or collinear vertices, but /// `Polygonal` types support this kind of data. However, `Polygonal` types are /// often used as a geometric approximation of polygons. Moreover, `Polygonal` /// types often contain non-geometric data, particularly index data. /// /// [`Topological`]: crate::primitive::Topological pub trait Polygonal: Topological { /// Determines if the polygon is convex. /// /// This function rejects (returns `false`) degenerate polygons, such as /// polygons with collinear or converged vertices. fn is_convex(&self) -> bool where Self::Vertex: AsPosition, Position: EuclideanSpace + FiniteDimensional, Scalar>: FloatConst, { let pi = > as FloatConst>::PI(); let mut sum = > as Zero>::zero(); for (t1, t2) in angles(self).perimeter() { if (t1 * t2) <= Zero::zero() { return false; } sum = sum + t1; } // TODO: Use an approximate comparison and do not explicitly round. (sum / (pi + pi)).round().abs() == One::one() } } pub trait IntoIndexed: Polygonal where N: Copy + Integer + Unsigned, { type Indexed: Polygonal; fn into_indexed(self) -> Self::Indexed; } impl IntoIndexed for P where P: Map<(N,

::Vertex), Output = Q> + Polygonal, Q: Polygonal, N: Copy + Integer + Unsigned, { type Indexed = Q; fn into_indexed(self) -> Self::Indexed { let mut index = Zero::zero(); self.map(|vertex| { let vertex = (index, vertex); index = index + One::one(); vertex }) } } pub trait IntoPolygons: Sized { type Output: IntoIterator; type Polygon: Polygonal; fn into_polygons(self) -> Self::Output; } pub trait Rotate { #[must_use] fn rotate(self, n: isize) -> Self; } pub trait Zip { type Output: Topological; fn zip(self) -> Self::Output; } pub trait MapVertices: Sized { fn map_vertices(self, f: F) -> InteriorMap where F: FnMut(T) -> U; } impl MapVertices for I where I: Iterator, I::Item: Map + Topological, >::Output: Topological, { fn map_vertices(self, f: F) -> InteriorMap where F: FnMut(T) -> U, { InteriorMap::new(self, f) } } pub struct InteriorMap { input: I, f: F, phantom: PhantomData T>, } impl InteriorMap { fn new(input: I, f: F) -> Self { InteriorMap { input, f, phantom: PhantomData, } } } impl Iterator for InteriorMap where I: Iterator, F: FnMut(::Vertex) -> T, I::Item: Map + Topological, >::Output: Topological, { type Item = >::Output; fn next(&mut self) -> Option { self.input.next().map(|topology| topology.map(&mut self.f)) } fn size_hint(&self) -> (usize, Option) { self.input.size_hint() } } /// Monomorphic $n$-gon. /// /// `NGon` represents a polygonal structure as an array. Each array element /// represents vertex data in order with adjacent elements being connected by an /// implicit undirected edge. For example, an `NGon` with three vertices /// (`NGon<_, 3>`) would represent a trigon (triangle). Generally these elements /// are labeled $A$, $B$, $C$, etc. Note that the constant parameter `N` /// represents the number of the `NGon`'s vertices and **not** the number of its /// edges (arity). /// /// `NGon`s with less than three vertices are a degenerate case. An `NGon` with /// two vertices (`NGon<_, 2>`) is considered a _monogon_ and is used to /// represent edges. Such an `NGon` is not considered a _digon_, as it /// represents a single undirected edge rather than two distinct (but collapsed) /// edges. Note that the polygonal types [`BoundedPolygon`] and /// [`UnboundedPolygon`] never represent edges. See the [`Edge`] type /// definition. /// /// `NGon`s with one or zero vertices are unsupported and lack various trait /// implementations. /// /// See the [module][`primitive`] documentation for more information. /// /// [`BoundedPolygon`]: crate::primitive::BoundedPolygon /// [`Edge`]: crate::primitive::Edge /// [`primitive`]: crate::primitive /// [`UnboundedPolygon`]: crate::primitive::UnboundedPolygon #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct NGon(pub [G; N]); impl NGon { pub fn into_array(self) -> [G; N] { self.0 } pub fn positions(&self) -> NGon<&Position, N> where G: AsPosition, { if let Ok(array) = self .as_ref() .iter() .map(|vertex| vertex.as_position()) .collect::>() .into_inner() { array.into() } else { panic!() } } } impl NGon<&'_ G, N> { pub fn cloned(self) -> NGon where G: Clone, { self.map(|vertex| vertex.clone()) } } impl AsRef<[G]> for NGon { fn as_ref(&self) -> &[G] { &self.0 } } impl AsMut<[G]> for NGon { fn as_mut(&mut self) -> &mut [G] { &mut self.0 } } impl Adjunct for NGon { type Item = G; } impl Converged for NGon where G: Copy, { fn converged(vertex: G) -> Self { NGon([vertex; N]) } } impl DynamicArity for NGon where Constant: ToType, TypeOf: Cmp, { type Dynamic = usize; fn arity(&self) -> Self::Dynamic { ::ARITY } } impl Fold for NGon where Self: Topological, { fn fold(self, mut seed: U, mut f: F) -> U where F: FnMut(U, Self::Item) -> U, { for vertex in self.into_vertices() { seed = f(seed, vertex); } seed } } impl From<[G; N]> for NGon { fn from(array: [G; N]) -> Self { NGon(array) } } impl FromItems for NGon { fn from_items(items: I) -> Option where I: IntoIterator, { items.into_iter().try_collect().ok() } } impl Index for NGon { type Output = G; fn index(&self, index: usize) -> &Self::Output { self.0.as_ref().index(index) } } impl IndexMut for NGon { fn index_mut(&mut self, index: usize) -> &mut Self::Output { self.0.as_mut().index_mut(index) } } impl IntoItems for NGon { type Output = ::IntoIter; fn into_items(self) -> Self::Output { self.into_iter() } } impl IntoIterator for NGon { type Item = G; type IntoIter = array::IntoIter; fn into_iter(self) -> Self::IntoIter { self.into_array().into_iter() } } impl Map for NGon { type Output = NGon; fn map(self, f: F) -> Self::Output where F: FnMut(Self::Item) -> H, { (self.into_iter().map(f)).try_collect().unwrap() } } impl Monomorphic for NGon where Constant: ToType, TypeOf: Cmp, { } impl Polygonal for NGon where // The compiler cannot deduce that the bounds on `TypeOf` are a strict // subset of the similar bounds on the `Topological` implementation, so an // explicit bound on `Self` is required. Self: Topological, Constant: ToType, TypeOf: Cmp, { } impl StaticArity for NGon where Constant: ToType, TypeOf: Cmp, { type Static = usize; const ARITY: Self::Static = crate::n_arity(N); } impl Topological for NGon where Constant: ToType, TypeOf: Cmp, { type Vertex = G; fn try_from_slice(vertices: I) -> Option where Self::Vertex: Copy, I: AsRef<[Self::Vertex]>, { vertices.as_ref().try_into().map(NGon).ok() } } impl TryFromIterator for NGon { type Error = (); fn try_from_iter(vertices: I) -> Result where I: Iterator, { vertices .collect::>() .into_inner() .map(NGon) .map_err(|_| ()) } } impl ZipMap for NGon { type Output = NGon; fn zip_map(self, other: Self, mut f: F) -> Self::Output where F: FnMut(Self::Item, Self::Item) -> H, { (self.into_iter().zip(other).map(|(a, b)| f(a, b))) .try_collect() .unwrap() } } macro_rules! impl_zip_ngon { (ngons => ($($i:ident),*)) => ( impl<$($i),*, const N: usize> Zip for ($(NGon<$i, N>),*) where Constant: ToType, TypeOf: Cmp, { type Output = NGon<($($i),*), N>; // LINT: The `i` metavariable items are conventionally uppercase and represent both // type names and local binding identifiers. #[expect(non_snake_case)] fn zip(self) -> Self::Output { let ($($i,)*) = self; (izip!($($i.into_iter()),*)).try_collect().unwrap() } } ); } impl_zip_ngon!(ngons => (A, B)); impl_zip_ngon!(ngons => (A, B, C)); impl_zip_ngon!(ngons => (A, B, C, D)); impl_zip_ngon!(ngons => (A, B, C, D, E)); impl_zip_ngon!(ngons => (A, B, C, D, E, F)); pub type Edge = NGon; impl Edge { pub fn new(a: G, b: G) -> Self { NGon([a, b]) } pub fn line(&self) -> Option>> where G: AsPosition, { let [origin, endpoint] = self.positions().cloned().into_array(); Unit::try_from_inner(endpoint - origin).map(|direction| Line { origin, direction }) } pub fn is_bisected(&self, other: &Self) -> bool where G: AsPosition, Position: FiniteDimensional, { let is_disjoint = |line: Line>, [a, b]: [Position; 2]| { line.partition(a) .zip(line.partition(b)) .map(|(pa, pb)| pa != pb) .unwrap_or(false) }; self.line() .zip(other.line()) .map(|(left, right)| { let left = is_disjoint(left, other.positions().cloned().into_array()); let right = is_disjoint(right, self.positions().cloned().into_array()); left && right }) .unwrap_or(false) } } /// Intersection of edges. #[derive(Clone, Copy, Eq, PartialEq)] pub enum EdgeEdge where S: EuclideanSpace, { Point(S), Edge(Edge), } impl EdgeEdge where S: EuclideanSpace, { pub fn into_point(self) -> Option { match self { EdgeEdge::Point(point) => Some(point), _ => None, } } pub fn into_edge(self) -> Option> { match self { EdgeEdge::Edge(edge) => Some(edge), _ => None, } } } impl Debug for EdgeEdge where S: Debug + EuclideanSpace, Vector: Debug, { fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> { match *self { EdgeEdge::Point(point) => write!(formatter, "Point({:?})", point), EdgeEdge::Edge(edge) => write!(formatter, "Edge({:?})", edge), } } } impl Intersection> for Edge where T: AsPosition, Position: FiniteDimensional, { type Output = EdgeEdge>; // TODO: This first computes a line intersection and then partitions each // edge's endpoints by the other edge's line. That's probably more // expensive than is necessary. fn intersection(&self, other: &Edge) -> Option { self.line() .zip(other.line()) .and_then(|(left, right)| match left.intersection(&right) { Some(LineLine::Point(point)) => { self.is_bisected(other).then_some(EdgeEdge::Point(point)) } Some(LineLine::Line(_)) => todo!(), _ => None, }) } } impl Rotate for Edge { fn rotate(self, n: isize) -> Self { if n % 2 != 0 { let [a, b] = self.into_array(); Edge::new(b, a) } else { self } } } /// Triangle. pub type Trigon = NGon; impl Trigon { pub fn new(a: G, b: G, c: G) -> Self { NGon([a, b, c]) } pub fn plane(&self) -> Option>> where G: AsPosition, Position: EuclideanSpace + FiniteDimensional, Vector>: Cross>>, { let [a, b, c] = self.positions().cloned().into_array(); let ab = a - b; let ac = a - c; Unit::try_from_inner(ab.cross(ac)) .map(move |normal| Plane::> { origin: a, normal }) } } impl Rotate for Trigon { fn rotate(self, n: isize) -> Self { let n = umod(n, Self::ARITY as isize); if n == 1 { let [a, b, c] = self.into_array(); Trigon::new(b, c, a) } else if n == 2 { let [a, b, c] = self.into_array(); Trigon::new(c, a, b) } else { self } } } /// Quadrilateral. pub type Tetragon = NGon; impl Tetragon { pub fn new(a: G, b: G, c: G, d: G) -> Self { NGon([a, b, c, d]) } } impl Rotate for Tetragon { // LINT: These names follow a convention, mainly concerning vertices. #[expect(clippy::many_single_char_names)] fn rotate(self, n: isize) -> Self { let n = umod(n, Self::ARITY as isize); if n == 1 { let [a, b, c, d] = self.into_array(); Tetragon::new(b, c, d, a) } else if n == 2 { let [a, b, c, d] = self.into_array(); Tetragon::new(c, d, a, b) } else if n == 3 { let [a, b, c, d] = self.into_array(); Tetragon::new(d, a, b, c) } else { self } } } /// Bounded polymorphic $n$-gon. /// /// `BoundedPolygon` represents an $n$-gon with at least three edges by /// enumerating [`NGon`]s. As such, $n$ is bounded, because the enumeration only /// supports a limited set of polygons. Only common arities used by generators /// are provided. /// /// See the [module][`primitive`] documentation for more information. /// /// [`primitive`]: crate::primitive /// [`NGon`]: crate::primitive::NGon #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum BoundedPolygon { N3(Trigon), N4(Tetragon), } impl AsRef<[G]> for BoundedPolygon { fn as_ref(&self) -> &[G] { match *self { BoundedPolygon::N3(ref trigon) => trigon.as_ref(), BoundedPolygon::N4(ref tetragon) => tetragon.as_ref(), } } } impl AsMut<[G]> for BoundedPolygon { fn as_mut(&mut self) -> &mut [G] { match *self { BoundedPolygon::N3(ref mut trigon) => trigon.as_mut(), BoundedPolygon::N4(ref mut tetragon) => tetragon.as_mut(), } } } impl Adjunct for BoundedPolygon { type Item = G; } impl DynamicArity for BoundedPolygon { type Dynamic = usize; fn arity(&self) -> Self::Dynamic { match *self { BoundedPolygon::N3(..) => Trigon::::ARITY, BoundedPolygon::N4(..) => Tetragon::::ARITY, } } } impl Fold for BoundedPolygon { fn fold(self, mut seed: U, mut f: F) -> U where F: FnMut(U, Self::Item) -> U, { for vertex in self.into_vertices() { seed = f(seed, vertex); } seed } } impl From<[G; 3]> for BoundedPolygon { fn from(array: [G; 3]) -> Self { BoundedPolygon::N3(array.into()) } } impl From<[G; 4]> for BoundedPolygon { fn from(array: [G; 4]) -> Self { BoundedPolygon::N4(array.into()) } } impl From> for BoundedPolygon { fn from(trigon: Trigon) -> Self { BoundedPolygon::N3(trigon) } } impl From> for BoundedPolygon { fn from(tetragon: Tetragon) -> Self { BoundedPolygon::N4(tetragon) } } impl Index for BoundedPolygon { type Output = G; fn index(&self, index: usize) -> &Self::Output { match *self { BoundedPolygon::N3(ref trigon) => trigon.index(index), BoundedPolygon::N4(ref tetragon) => tetragon.index(index), } } } impl IndexMut for BoundedPolygon { fn index_mut(&mut self, index: usize) -> &mut Self::Output { match *self { BoundedPolygon::N3(ref mut trigon) => trigon.index_mut(index), BoundedPolygon::N4(ref mut tetragon) => tetragon.index_mut(index), } } } impl IntoItems for BoundedPolygon { type Output = SmallVec<[G; 4]>; fn into_items(self) -> Self::Output { match self { BoundedPolygon::N3(trigon) => trigon.into_items().collect(), BoundedPolygon::N4(tetragon) => tetragon.into_items().collect(), } } } impl IntoIterator for BoundedPolygon { type Item = G; type IntoIter = <::Output as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.into_items().into_iter() } } impl Map for BoundedPolygon { type Output = BoundedPolygon; fn map(self, f: F) -> Self::Output where F: FnMut(Self::Item) -> U, { match self { BoundedPolygon::N3(trigon) => BoundedPolygon::N3(trigon.map(f)), BoundedPolygon::N4(tetragon) => BoundedPolygon::N4(tetragon.map(f)), } } } impl Polygonal for BoundedPolygon {} impl Rotate for BoundedPolygon { fn rotate(self, n: isize) -> Self { match self { BoundedPolygon::N3(trigon) => BoundedPolygon::N3(trigon.rotate(n)), BoundedPolygon::N4(tetragon) => BoundedPolygon::N4(tetragon.rotate(n)), } } } impl StaticArity for BoundedPolygon { type Static = (usize, usize); const ARITY: Self::Static = (3, 4); } impl Topological for BoundedPolygon { type Vertex = G; fn try_from_slice(vertices: I) -> Option where Self::Vertex: Copy, I: AsRef<[Self::Vertex]>, { let vertices = vertices.as_ref(); match vertices.len() { 3 => Some(BoundedPolygon::N3(NGon(vertices.try_into().unwrap()))), 4 => Some(BoundedPolygon::N4(NGon(vertices.try_into().unwrap()))), _ => None, } } } /// Unbounded polymorphic $n$-gon. /// /// `UnboundedPolygon` represents an $n$-gon with three or more edges. Unlike /// [`BoundedPolygon`], there is no limit to the arity of polygons that /// `UnboundedPolygon` may represent. /// /// It is not possible to trivially convert an [`UnboundedPolygon`] into other /// topological types like [`NGon`]. See the [module][`primitive`] documentation /// for more information. /// /// [`primitive`]: crate::primitive /// [`BoundedPolygon`]: crate::primitive::BoundedPolygon /// [`NGon`]: crate::primitive::NGon #[derive(Clone, Debug, Eq, PartialEq)] pub struct UnboundedPolygon(SmallVec<[G; 4]>); impl UnboundedPolygon { pub fn trigon(a: G, b: G, c: G) -> Self { UnboundedPolygon(smallvec![a, b, c]) } pub fn tetragon(a: G, b: G, c: G, d: G) -> Self { UnboundedPolygon(smallvec![a, b, c, d]) } pub fn positions(&self) -> UnboundedPolygon<&Position> where G: AsPosition, { UnboundedPolygon(self.0.iter().map(|vertex| vertex.as_position()).collect()) } } impl UnboundedPolygon<&'_ G> where G: Clone, { pub fn cloned(self) -> UnboundedPolygon { self.map(|vertex| vertex.clone()) } } impl Adjunct for UnboundedPolygon { type Item = G; } impl AsRef<[G]> for UnboundedPolygon { fn as_ref(&self) -> &[G] { self.0.as_ref() } } impl AsMut<[G]> for UnboundedPolygon { fn as_mut(&mut self) -> &mut [G] { self.0.as_mut() } } impl DynamicArity for UnboundedPolygon { type Dynamic = usize; fn arity(&self) -> Self::Dynamic { self.0.len() } } impl From> for UnboundedPolygon where G: Clone, { fn from(polygon: BoundedPolygon) -> Self { UnboundedPolygon(SmallVec::from(polygon.as_ref())) } } impl From> for UnboundedPolygon where Constant: ToType, TypeOf: Cmp, G: Clone, { fn from(ngon: NGon) -> Self { UnboundedPolygon(SmallVec::from(ngon.as_ref())) } } impl FromItems for UnboundedPolygon { fn from_items(items: I) -> Option where I: IntoIterator, { items.into_iter().try_collect().ok() } } impl Index for UnboundedPolygon { type Output = G; fn index(&self, index: usize) -> &Self::Output { self.0.index(index) } } impl IndexMut for UnboundedPolygon { fn index_mut(&mut self, index: usize) -> &mut Self::Output { self.0.index_mut(index) } } impl IntoItems for UnboundedPolygon { type Output = SmallVec<[G; 4]>; fn into_items(self) -> Self::Output { self.0 } } impl IntoIterator for UnboundedPolygon { type IntoIter = as IntoIterator>::IntoIter; type Item = G; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl Map for UnboundedPolygon { type Output = UnboundedPolygon; fn map(self, f: F) -> Self::Output where F: FnMut(Self::Item) -> U, { UnboundedPolygon(self.0.into_iter().map(f).collect()) } } impl Polygonal for UnboundedPolygon {} impl StaticArity for UnboundedPolygon { type Static = (usize, Option); const ARITY: Self::Static = (3, None); } impl Topological for UnboundedPolygon { type Vertex = G; fn try_from_slice(vertices: I) -> Option where Self::Vertex: Copy, I: AsRef<[Self::Vertex]>, { let vertices = vertices.as_ref(); if vertices.len() > 2 { Some(UnboundedPolygon(SmallVec::from(vertices))) } else { None } } } impl TryFromIterator for UnboundedPolygon { type Error = (); fn try_from_iter(vertices: I) -> Result where I: Iterator, { vertices .has_at_least(3) .map(|items| UnboundedPolygon(items.collect())) .ok_or(()) } } /// Zips the vertices of [`Topological`] types from multiple iterators into a /// single iterator. /// /// This is useful for zipping different geometric attributes of a /// [generator][`generate`]. For example, it can be used to combine position, /// plane, and normal data data of a [`Cube`] into a single topology iterator. /// /// # Examples /// /// Zip position, normal, and plane attributes of a [cube][`Cube`]: /// /// ```rust /// # extern crate decorum; /// # extern crate nalgebra; /// # extern crate plexus; /// # /// use decorum::R32; /// use nalgebra::Point3; /// use plexus::prelude::*; /// use plexus::primitive; /// use plexus::primitive::cube::{Cube, Plane}; /// use plexus::primitive::generate::{Normal, Position}; /// /// type E3 = Point3; /// /// let cube = Cube::new(); /// // Zip positions and texture coordinates into each vertex. /// let polygons = primitive::zip_vertices(( /// cube.polygons::>(), /// cube.polygons::>(), /// cube.polygons::(), /// )) /// .triangulate() /// .collect::>(); /// ``` /// /// [`Cube`]: crate::primitive::cube::Cube /// [`generate`]: crate::primitive::generate /// [`Topological`]: crate::primitive::Topological pub fn zip_vertices( tuple: U, ) -> impl Iterator as Iterator>::Item as Zip>::Output> where OuterZip: From + Iterator, as Iterator>::Item: Zip, { OuterZip::from(tuple).map(|item| item.zip()) } /// Gets the relative angles between adjacent edges in a [`Polygonal`] type with /// positional data in $\Reals^2$. /// /// Computes the angle between edges at each vertex in order. The angles are /// wrapped into the interval $(-\pi,\pi]$. The sign of each angle specifies the /// orientation of adjacent edges. Given a square, exactly four angles of /// $\plusmn\frac{\pi}{2}$ will be returned, with the sign depending on the /// winding of the square. /// /// Adjacent vertices with the same position are ignored, as there is no /// meaningful edge between such vertices. Because of this, it is possible that /// the number of angles returned by this function disagrees with the arity of /// the polygon. /// /// [`Polygonal`]: crate::primitive::Polygonal fn angles

(polygon: &P) -> impl '_ + Clone + Iterator>> where P: Polygonal, P::Vertex: AsPosition, Position: EuclideanSpace + FiniteDimensional, Scalar>: FloatConst, { polygon .as_ref() .iter() .map(|vertex| vertex.as_position()) .perimeter() .map(|(a, b)| *b - *a) .filter(|vector| !vector.is_zero()) // Reject like positions. .map(|vector| vector.into_xy()) .map(|(x, y)| Real::atan2(x, y)) // Absolute angle. .perimeter() .map(|(t1, t2)| t2 - t1) // Relative angle (between segments). .map(|t| { // Wrap the angle into the interval `(-pi, pi]`. let pi = > as FloatConst>::PI(); if t <= -pi { t + (pi + pi) } else if t > pi { t - (pi + pi) } else { t } }) } fn umod(n: T, m: T) -> T where T: Copy + Integer, { ((n % m) + m) % m } #[cfg(test)] mod tests { use nalgebra::Point2; use theon::adjunct::Converged; use theon::space::EuclideanSpace; use crate::primitive::{NGon, Polygonal, Tetragon, Trigon}; type E2 = Point2; #[test] fn convexity() { // Convex triangle. let trigon = Trigon::new( E2::from_xy(0.0, 1.0), E2::from_xy(1.0, 0.0), E2::from_xy(-1.0, 0.0), ); assert!(trigon.is_convex()); // Convex quadrilateral. let tetragon = Tetragon::new( E2::from_xy(1.0, 1.0), E2::from_xy(1.0, -1.0), E2::from_xy(-1.0, -1.0), E2::from_xy(-1.0, 1.0), ); assert!(tetragon.is_convex()); // Degenerate (collinear) triangle. Not convex. let trigon = Trigon::new( E2::from_xy(0.0, 0.0), E2::from_xy(0.0, 1.0), E2::from_xy(0.0, 2.0), ); assert!(!trigon.is_convex()); // Degenerate (converged) triangle. Not convex. let trigon = Trigon::converged(E2::origin()); assert!(!trigon.is_convex()); // Self-intersecting pentagon. Not convex. let pentagon = NGon::from([ E2::from_xy(1.0, 1.0), E2::from_xy(1.0, -1.0), E2::from_xy(-1.0, -1.0), E2::from_xy(-1.0, 1.0), E2::from_xy(0.0, -2.0), // Self-intersecting vertex. ]); assert!(!pentagon.is_convex()); } } ================================================ FILE: plexus/src/primitive/sphere.rs ================================================ //! Sphere primitives. //! //! # Examples //! //! Generating a graph from the positional data of a $uv$-sphere. //! //! ```rust //! # extern crate decorum; //! # extern crate nalgebra; //! # extern crate plexus; //! # //! use decorum::R64; //! use nalgebra::Point3; //! use plexus::graph::MeshGraph; //! use plexus::index::HashIndexer; //! use plexus::prelude::*; //! use plexus::primitive::generate::Position; //! use plexus::primitive::sphere::UvSphere; //! //! type E3 = Point3; //! //! let mut graph = UvSphere::new(16, 8) //! .polygons::>() //! .collect_with_indexer::, _>(HashIndexer::default()) //! .unwrap(); //! ``` use num::traits::real::Real; use num::traits::FloatConst; use num::{NumCast, One, ToPrimitive}; use std::cmp; use theon::adjunct::Map; use theon::query::Unit; use theon::space::{EuclideanSpace, FiniteDimensional, Scalar, Vector}; use typenum::U3; use crate::primitive::generate::{ AttributeGenerator, AttributePolygonGenerator, AttributeVertexGenerator, Generator, IndexingPolygonGenerator, Normal, PolygonGenerator, Position, }; use crate::primitive::{BoundedPolygon, Tetragon, Trigon}; #[derive(Clone, Copy)] pub struct Bounds where S: EuclideanSpace, { radius: Scalar, } impl Bounds where S: EuclideanSpace, { pub fn with_radius(radius: Scalar) -> Self { Bounds { radius } } pub fn with_width(width: Scalar) -> Self { Self::with_radius(width / (Scalar::::one() + One::one())) } pub fn unit_radius() -> Self { Self::with_radius(One::one()) } pub fn unit_width() -> Self { Self::with_width(One::one()) } } impl Default for Bounds where S: EuclideanSpace, { fn default() -> Self { Self::unit_radius() } } #[derive(Clone, Copy)] pub struct UvSphere { nu: usize, // Meridians. nv: usize, // Parallels. } impl UvSphere { pub fn new(nu: usize, nv: usize) -> Self { UvSphere { nu: cmp::max(3, nu), nv: cmp::max(2, nv), } } fn vertex_with_position_from( &self, state: &>>::State, u: usize, v: usize, ) -> S where Self: AttributeGenerator, State = Bounds>, S: EuclideanSpace + FiniteDimensional, Scalar: FloatConst, { let one = Scalar::::one(); let pi = FloatConst::PI(); let u = (into_scalar::<_, S>(u) / into_scalar::<_, S>(self.nu)) * pi * (one + one); let v = (into_scalar::<_, S>(v) / into_scalar::<_, S>(self.nv)) * pi; S::from_xyz( state.radius * u.cos() * v.sin(), state.radius * u.sin() * v.sin(), state.radius * v.cos(), ) } fn index_for_position(&self, u: usize, v: usize) -> usize { if v == 0 { 0 } else if v == self.nv { ((self.nv - 1) * self.nu) + 1 } else { ((v - 1) * self.nu) + (u % self.nu) + 1 } } fn map_polygon_index(&self, index: usize) -> (usize, usize) { (index % self.nu, index / self.nu) } } impl Default for UvSphere { fn default() -> Self { UvSphere::new(16, 16) } } impl PolygonGenerator for UvSphere { fn polygon_count(&self) -> usize { self.nu * self.nv } } impl AttributeGenerator> for UvSphere where S: EuclideanSpace + FiniteDimensional, { type State = (); } impl AttributeVertexGenerator> for UvSphere where S: EuclideanSpace + FiniteDimensional, Scalar: FloatConst, { type Output = Unit>; fn vertex_count(&self) -> usize { (self.nv - 1) * self.nu + 2 } fn vertex_from(&self, _: &Self::State, index: usize) -> Self::Output { let position = AttributeVertexGenerator::>::vertex_from(self, &Default::default(), index); Unit::try_from_inner(position.into_coordinates()).expect("non-zero vector") } } impl AttributePolygonGenerator> for UvSphere where S: EuclideanSpace + FiniteDimensional, Scalar: FloatConst, { type Output = BoundedPolygon>>; fn polygon_from(&self, _: &Self::State, index: usize) -> Self::Output { AttributePolygonGenerator::>::polygon_from(self, &Default::default(), index) .map(|position| { Unit::try_from_inner(position.into_coordinates()).expect("non-zero vector") }) } } impl IndexingPolygonGenerator> for UvSphere { type Output = BoundedPolygon; fn indexing_polygon(&self, index: usize) -> Self::Output { IndexingPolygonGenerator::>::indexing_polygon(self, index) } } impl AttributeGenerator> for UvSphere where S: EuclideanSpace + FiniteDimensional, { type State = Bounds; } impl AttributeVertexGenerator> for UvSphere where S: EuclideanSpace + FiniteDimensional, Scalar: FloatConst, { type Output = S; fn vertex_count(&self) -> usize { (self.nv - 1) * self.nu + 2 } fn vertex_from(&self, state: &Self::State, index: usize) -> Self::Output { let count = AttributeVertexGenerator::>::vertex_count(self); if index == 0 { self.vertex_with_position_from::(state, 0, 0) } else if index == (count - 1) { self.vertex_with_position_from::(state, 0, self.nv) } else { let index = index - 1; self.vertex_with_position_from::(state, index % self.nu, (index / self.nu) + 1) } } } impl AttributePolygonGenerator> for UvSphere where S: EuclideanSpace + FiniteDimensional, Scalar: FloatConst, { type Output = BoundedPolygon; fn polygon_from(&self, state: &Self::State, index: usize) -> Self::Output { // Prevent floating point rounding errors by wrapping the incremented // values for `(u, v)` into `(p, q)`. This is important for indexing // geometry, because small differences in the computation of spatial // vertices will produce redundant output vertices. There should be // exactly `(nv - 1) * nu + 2` unique values of `(u, v)` used to // generate positions. // // There are two important observations: // // 1. `u` must wrap, but `v` need not. There are `nu` meridians of // points and polygons, but there are `nv` parallels of polygons // and `nv + 1` parallels of points. // 2. `u`, which represents a meridian, is meaningless at the poles, // and can be normalized to zero. let (u, v) = self.map_polygon_index(index); let (p, q) = ((u + 1) % self.nu, v + 1); // Generate the vertices at the requested meridian and parallel. The // lower bound of `(u, v)` is always used, so compute that in advance // (`lower`). Emit triangles at the poles, otherwise quadrilaterals. let lower = self.vertex_with_position_from(state, u, v); if v == 0 { Trigon::new( lower, self.vertex_with_position_from(state, u, q), self.vertex_with_position_from(state, p, q), ) .into() } else if v == self.nv - 1 { Trigon::new( // Normalize `u` at the pole, using `(0, nv)` in place of // `(p, q)`. self.vertex_with_position_from(state, 0, self.nv), self.vertex_with_position_from(state, p, v), lower, ) .into() } else { Tetragon::new( lower, self.vertex_with_position_from(state, u, q), self.vertex_with_position_from(state, p, q), self.vertex_with_position_from(state, p, v), ) .into() } } } impl IndexingPolygonGenerator> for UvSphere { type Output = BoundedPolygon; fn indexing_polygon(&self, index: usize) -> Self::Output { let (u, v) = self.map_polygon_index(index); let (p, q) = (u + 1, v + 1); let low = self.index_for_position(u, v); let high = self.index_for_position(p, q); if v == 0 { Trigon::new(low, self.index_for_position(u, q), high).into() } else if v == self.nv - 1 { Trigon::new(high, self.index_for_position(p, v), low).into() } else { Tetragon::new( low, self.index_for_position(u, q), high, self.index_for_position(p, v), ) .into() } } } impl Generator for UvSphere {} fn into_scalar(value: T) -> Scalar where T: ToPrimitive, S: EuclideanSpace, { as NumCast>::from(value).unwrap() } #[cfg(test)] mod tests { use nalgebra::Point3; use std::collections::BTreeSet; use crate::prelude::*; use crate::primitive::generate::Position; use crate::primitive::sphere::UvSphere; type E3 = Point3; #[test] fn vertex_count() { assert_eq!( 5, UvSphere::new(3, 2) .vertices::>() // 5 conjoint vertices. .count() ); } #[test] fn polygon_vertex_count() { assert_eq!( 18, UvSphere::new(3, 2) .polygons::>() // 6 triangles, 18 vertices. .vertices() .count() ); } #[test] fn position_index_to_vertex_mapping() { assert_eq!( 5, UvSphere::new(3, 2) .indexing_polygons::() // 18 vertices, 5 indices. .vertices() .collect::>() .len() ) } } ================================================ FILE: plexus/src/transact.rs ================================================ use std::fmt::Debug; use std::mem; pub trait Transact: Sized { type Commit; type Abort; type Error: Debug; fn commit(self) -> Result; // LINT: This is indeed a complex type, but refactoring into a type definition cannot be done // trivially (and may not reduce complexity). #[expect(clippy::type_complexity)] fn commit_with(mut self, f: F) -> Result<(Self::Commit, U), (Self::Abort, Self::Error)> where F: FnOnce(&mut Self) -> Result, E: Into, { match f(&mut self) { Ok(value) => self.commit().map(|output| (output, value)), Err(error) => Err((self.abort(), error.into())), } } fn abort(self) -> Self::Abort; } pub trait Bypass: Transact { fn bypass(self) -> Self::Commit; } pub trait BypassOrCommit: Bypass { fn bypass_or_commit(self) -> Result; // LINT: This is indeed a complex type, but refactoring into a type definition cannot be done // trivially (and may not reduce complexity). #[expect(clippy::type_complexity)] fn bypass_or_commit_with( self, f: F, ) -> Result<(Self::Commit, X), (Self::Abort, Self::Error)> where F: FnOnce(&mut Self) -> Result, E: Into; } #[cfg(test)] impl BypassOrCommit for T where T: Bypass, { fn bypass_or_commit(self) -> Result { self.commit() } fn bypass_or_commit_with(self, f: F) -> Result<(T::Commit, X), (T::Abort, T::Error)> where F: FnOnce(&mut T) -> Result, E: Into, { self.commit_with(f) } } #[cfg(not(test))] impl BypassOrCommit for T where T: Bypass, { fn bypass_or_commit(self) -> Result { Ok(self.bypass()) } fn bypass_or_commit_with( mut self, f: F, ) -> Result<(T::Commit, X), (T::Abort, T::Error)> where F: FnOnce(&mut T) -> Result, E: Into, { match f(&mut self) { Ok(value) => Ok((self.bypass(), value)), Err(error) => Err((self.abort(), error.into())), } } } pub trait Mutate: Transact { fn replace(target: &mut T, replacement: T) -> Swapped where Self: From + Transact, { Swapped::replace(target, replacement) } fn take(target: &mut T) -> Swapped where Self: From + Transact, T: Default, { Swapped::take(target) } } impl Mutate for T where T: Transact {} pub trait ClosedInput: Transact<::Input> { type Input; } trait Drain { fn as_option_mut(&mut self) -> &mut Option; fn drain(&mut self) -> T { self.as_option_mut().take().expect("drained") } fn undrain(&mut self, value: T) { let drained = self.as_option_mut(); if drained.is_some() { panic!("undrained"); } else { *drained = Some(value); } } fn try_swap_or(&mut self, value: T, mut f: F) -> Result where F: FnMut(T) -> Result<(T, U), E>, { match f(self.drain()) { Ok((value, output)) => { self.undrain(value); Ok(output) } Err(error) => { self.undrain(value); Err(error) } } } } pub struct Swapped<'a, T, M> where M: From + Mutate, { inner: Option<(&'a mut T, M)>, } impl<'a, T, M> Swapped<'a, T, M> where M: From + Mutate, { pub fn replace(target: &'a mut T, replacement: T) -> Self { let mutant = mem::replace(target, replacement); Swapped { inner: Some((target, M::from(mutant))), } } pub fn take(target: &'a mut T) -> Self where T: Default, { Swapped::replace(target, T::default()) } fn drain_and_commit( &mut self, ) -> Result<&'a mut T, (&'a mut T, >::Error)> { let (target, inner) = self.drain(); match inner.commit() { Ok(mutant) => { *target = mutant; Ok(target) } Err((_, error)) => Err((target, error)), } } fn drain_and_abort(&mut self) -> &'a mut T { let (target, inner) = self.drain(); inner.abort(); target } } impl<'a, T, M> Swapped<'a, T, M> where M: Bypass + From + Mutate, { pub fn drain_and_bypass(&mut self) -> &'a mut T { let (target, inner) = self.drain(); *target = inner.bypass(); target } } impl AsRef for Swapped<'_, T, M> where M: From + Mutate, { fn as_ref(&self) -> &M { &self.inner.as_ref().unwrap().1 } } impl AsMut for Swapped<'_, T, M> where M: From + Mutate, { fn as_mut(&mut self) -> &mut M { &mut self.inner.as_mut().unwrap().1 } } impl<'a, T, M> Bypass<&'a mut T> for Swapped<'a, T, M> where M: Bypass + From + Mutate, { fn bypass(mut self) -> Self::Commit { let mutant = self.drain_and_bypass(); mem::forget(self); mutant } } impl<'a, T, M> Drain<(&'a mut T, M)> for Swapped<'a, T, M> where M: From + Mutate, { fn as_option_mut(&mut self) -> &mut Option<(&'a mut T, M)> { &mut self.inner } } impl Drop for Swapped<'_, T, M> where M: From + Mutate, { fn drop(&mut self) { self.drain_and_abort(); } } impl<'a, T, M> From<&'a mut T> for Swapped<'a, T, M> where T: Default, M: From + Mutate, { fn from(target: &'a mut T) -> Self { Self::replace(target, Default::default()) } } impl<'a, T, M> Transact<&'a mut T> for Swapped<'a, T, M> where M: From + Mutate, { type Commit = &'a mut T; type Abort = &'a mut T; type Error = >::Error; fn commit(mut self) -> Result { let mutant = self.drain_and_commit(); mem::forget(self); mutant } fn abort(mut self) -> Self::Abort { let mutant = self.drain_and_abort(); mem::forget(self); mutant } } ================================================ FILE: rustdoc.sh ================================================ #! /usr/bin/env bash # TODO: This script sets the `RUSTDOCFLAGS` environment variable to configure # the KaTeX header for documentation. This cannot be accomplished with # Cargo configuration yet. # # See https://github.com/rust-lang/cargo/issues/8097 set -e RUSTDOCFLAGS=--html-in-header=$(realpath ./doc/katex-header.html) \ cargo +nightly doc $@ ================================================ FILE: rustfmt.toml ================================================ control_brace_style = "ClosingNextLine" format_code_in_doc_comments = true