Skip to content

Commit

Permalink
Added Deserialization via serde
Browse files Browse the repository at this point in the history
Also normalized traits across data structures (point, vertex, cell, tds).

Might think of hiding this behind a feature flag, but as far as I can tell there's no way to put trait bounds behind a #[cfg_attr(feature = ...)].

Also, saving data structures is very important for my research code (and was a difficult problem in C++ with HDF5), so I'd have this feature on always anyways.
  • Loading branch information
acgetchell committed Jan 3, 2024
1 parent a89ff48 commit fe3a704
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 43 deletions.
25 changes: 16 additions & 9 deletions src/delaunay_core/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use super::{point::Point, utilities::make_uuid, vertex::Vertex};
use crate::delaunay_core;
use na::{Const, OPoint};
use nalgebra as na;
use serde::Serialize;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{fmt::Debug, ops::Div};
use uuid::Uuid;

#[derive(Debug, Clone, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
/// The `Cell` struct represents a d-dimensional [simplex](https://en.wikipedia.org/wiki/Simplex)
/// with vertices, a unique identifier, optional neighbors, and optional data.
///
Expand All @@ -25,7 +25,7 @@ use uuid::Uuid;
/// storage of additional data associated with the `Cell`.
pub struct Cell<T: std::default::Default + std::marker::Copy, U, V, const D: usize>
where
[T; D]: Serialize,
[T; D]: Serialize + DeserializeOwned + Default,
{
/// The vertices of the cell.
pub vertices: Vec<Vertex<T, U, D>>,
Expand All @@ -49,7 +49,7 @@ impl<
where
for<'a> &'a T: Div<f64>,
f64: From<T>,
[T; D]: Serialize,
[T; D]: Serialize + DeserializeOwned + Default,
{
/// The function `new` creates a new `Cell`` object with the given vertices.
/// A D-dimensional cell has D + 1 vertices, so the number of vertices must be less than or equal to D + 1.
Expand Down Expand Up @@ -248,8 +248,8 @@ where
fn circumcenter(&self) -> Result<Point<f64, D>, &'static str>
where
T: Clone + Copy + PartialEq + Debug + 'static,
[T; D]: Serialize,
[f64; D]: Sized + Serialize,
[T; D]: Serialize + DeserializeOwned + Default,
[f64; D]: Sized + Serialize + DeserializeOwned + Default,
{
let dim = self.dim();
if self.vertices[0].dim() != dim {
Expand Down Expand Up @@ -303,7 +303,7 @@ where
where
T: Copy,
OPoint<T, Const<D>>: From<[f64; D]>,
[f64; D]: Serialize,
[f64; D]: Serialize + DeserializeOwned + Default,
{
let circumcenter = self.circumcenter()?;
// Change the type of vertex to match circumcenter
Expand Down Expand Up @@ -341,7 +341,7 @@ where
where
T: Copy + PartialOrd, // Add the PartialOrd trait bound
OPoint<T, Const<D>>: From<[f64; D]>,
[f64; D]: Serialize,
[f64; D]: Serialize + DeserializeOwned + Default,
{
let circumradius = self.circumradius()?;
let radius = na::distance(
Expand Down Expand Up @@ -595,7 +595,7 @@ mod tests {
}

#[test]
fn cell_serialization() {
fn cell_to_and_from_json() {
let vertex1 = Vertex::new(Point::new([0.0, 0.0, 1.0]));
let vertex2 = Vertex::new(Point::new([0.0, 1.0, 0.0]));
let vertex3 = Vertex::new(Point::new([1.0, 0.0, 0.0]));
Expand All @@ -608,5 +608,12 @@ mod tests {
assert!(serialized.contains("[0.0,1.0,0.0]"));
assert!(serialized.contains("[1.0,0.0,0.0]"));
assert!(serialized.contains("[1.0,1.0,1.0]"));

let deserialized: Cell<f64, Option<()>, Option<()>, 3> =
serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, cell);

// Human readable output for cargo test -- --nocapture
println!("Cell: {:?}", cell);
}
}
39 changes: 17 additions & 22 deletions src/delaunay_core/point.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Data and operations on d-dimensional points.
use serde::Serialize;
use serde::{de::DeserializeOwned, Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Copy, Serialize)]
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)]
/// The `Point` struct represents a point in a D-dimensional space, where the coordinates are of type
/// `T`.
///
Expand All @@ -11,29 +11,18 @@ use serde::Serialize;
/// * `coords`: `coords` is a public property of the `Point`. It is an array of type `T` with a
/// length of `D`. The type `T` is a generic type parameter, which means it can be any type. The length
/// `D` is a constant unsigned integer, which means it cannot be changed and is known at compile time.
pub struct Point<T, const D: usize>
pub struct Point<T: Default + std::marker::Copy, const D: usize>
where
[T; D]: Serialize,
[T; D]: Serialize + DeserializeOwned + Default,
{
/// The coordinates of the point.
pub coords: [T; D],
}

impl<T: std::default::Default + Copy, const D: usize> Default for Point<T, D>
where
[T; D]: Serialize,
{
fn default() -> Self {
Self {
coords: [Default::default(); D],
}
}
}

impl<T, const D: usize> From<[T; D]> for Point<f64, D>
where
[T; D]: Sized + Serialize,
[f64; D]: Sized + Serialize,
[T; D]: Sized + Serialize + DeserializeOwned + Default,
[f64; D]: Sized + Serialize + DeserializeOwned + Default,
T: Into<f64>,
{
fn from(coords: [T; D]) -> Self {
Expand All @@ -45,7 +34,7 @@ where

impl<T: Clone + std::default::Default + Copy, const D: usize> Point<T, D>
where
[T; D]: Serialize,
[T; D]: Serialize + DeserializeOwned + Default,
{
/// The function `new` creates a new instance of a `Point` with the given coordinates.
///
Expand Down Expand Up @@ -157,11 +146,11 @@ mod tests {

#[test]
fn point_serialization() {
use serde_test::{assert_ser_tokens, Token};
use serde_test::{assert_tokens, Token};

let point2: Point<f64, 3> = Point::new([1.0, 2.0, 3.0]);
assert_ser_tokens(
&point2,
let point: Point<f64, 3> = Point::new([1.0, 2.0, 3.0]);
assert_tokens(
&point,
&[
Token::Struct {
name: "Point",
Expand All @@ -176,11 +165,17 @@ mod tests {
Token::StructEnd,
],
);
}

#[test]
fn point_to_and_from_json() {
let point: Point<f64, 4> = Default::default();
let serialized = serde_json::to_string(&point).unwrap();
assert_eq!(serialized, "{\"coords\":[0.0,0.0,0.0,0.0]}");

let deserialized: Point<f64, 4> = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, point);

// Human readable output for cargo test -- --nocapture
println!("Serialized: {:?}", serialized);
}
Expand Down
34 changes: 29 additions & 5 deletions src/delaunay_core/triangulation_data_structure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ use super::utilities::find_extreme_coordinates;
use super::{cell::Cell, point::Point, vertex::Vertex};
use na::{Const, OPoint};
use nalgebra as na;
use serde::Serialize;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::cmp::{Ordering, PartialEq};
use std::ops::Div;
use std::{cmp::min, collections::HashMap};
use uuid::Uuid;

#[derive(Debug, Default, Clone, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
/// The `Tds` struct represents a triangulation data structure with vertices and cells, where the vertices
/// and cells are identified by UUIDs.
///
Expand All @@ -37,7 +37,7 @@ use uuid::Uuid;
/// In general, vertices are embedded into D-dimensional Euclidean space, and so the `Tds` is a finite simplicial complex.
pub struct Tds<T: std::default::Default + std::marker::Copy, U, V, const D: usize>
where
[T; D]: Serialize,
[T; D]: Serialize + DeserializeOwned + Default,
{
/// A HashMap that stores vertices with their corresponding UUIDs as keys.
/// Each `Vertex` has a `Point` of type T, vertex data of type U, and a constant D representing the dimension.
Expand All @@ -64,7 +64,7 @@ impl<
where
f64: From<T>,
for<'a> &'a T: Div<f64>,
[T; D]: Serialize,
[T; D]: Serialize + DeserializeOwned + Default,
{
/// The function creates a new instance of a triangulation data structure with given points, initializing the vertices and
/// cells.
Expand Down Expand Up @@ -235,7 +235,7 @@ where
T: Copy + Default + PartialOrd,
Vertex<T, U, D>: Clone, // Add the Clone trait bound for Vertex
OPoint<T, Const<D>>: From<[f64; D]>,
[f64; D]: Serialize,
[f64; D]: Serialize + DeserializeOwned + Default,
{
let mut cells: Vec<Cell<T, U, V, D>> = Vec::new();

Expand Down Expand Up @@ -423,4 +423,28 @@ mod tests {
// Human readable output for cargo test -- --nocapture
println!("{:?}", unwrapped_cells);
}

#[test]
fn tds_to_and_from_json() {
let points = vec![
Point::new([1.0, 2.0, 3.0]),
Point::new([4.0, 5.0, 6.0]),
Point::new([7.0, 8.0, 9.0]),
Point::new([10.0, 11.0, 12.0]),
];
let tds: Tds<f64, usize, usize, 3> = Tds::new(points);

let serialized = serde_json::to_string(&tds).unwrap();
// assert!(serialized.contains(r#""vertices":{},"cells":{}"#));
assert!(serialized.contains("[1.0,2.0,3.0]"));
assert!(serialized.contains("[4.0,5.0,6.0]"));
assert!(serialized.contains("[7.0,8.0,9.0]"));
assert!(serialized.contains("[10.0,11.0,12.0]"));

let deserialized: Tds<f64, usize, usize, 3> = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, tds);

// Human readable output for cargo test -- --nocapture
println!("serialized = {}", serialized);
}
}
4 changes: 2 additions & 2 deletions src/delaunay_core/utilities.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Utility functions
use super::vertex::Vertex;
use serde::Serialize;
use serde::{de::DeserializeOwned, Serialize};
use std::cmp::Ordering;
use std::collections::HashMap;
use uuid::Uuid;
Expand Down Expand Up @@ -63,7 +63,7 @@ pub fn find_extreme_coordinates<T, U, const D: usize>(
) -> [T; D]
where
T: Copy + Default + PartialOrd,
[T; D]: Sized + Serialize,
[T; D]: Sized + Serialize + DeserializeOwned + Default,
{
let mut min_coords = [Default::default(); D];

Expand Down
16 changes: 11 additions & 5 deletions src/delaunay_core/vertex.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Data and operations on d-dimensional [vertices](https://en.wikipedia.org/wiki/Vertex_(computer_graphics)).
use super::{point::Point, utilities::make_uuid};
use serde::Serialize;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::option::Option;
use uuid::Uuid;

#[derive(Debug, Clone, Default, PartialEq, Copy, Serialize)]
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)]
/// The `Vertex` struct represents a vertex in a triangulation with a `Point`, a unique
/// identifier, an optional incident cell identifier, and optional data.
///
Expand All @@ -21,7 +21,7 @@ use uuid::Uuid;
/// additional data associated with the vertex.
pub struct Vertex<T: Default + std::marker::Copy, U, const D: usize>
where
[T; D]: Serialize,
[T; D]: Serialize + DeserializeOwned + Default,
{
/// The coordinates of the vertex in a D-dimensional space.
pub point: Point<T, D>,
Expand All @@ -35,7 +35,7 @@ where

impl<T: std::default::Default + std::marker::Copy, U, const D: usize> Vertex<T, U, D>
where
[T; D]: Serialize,
[T; D]: Serialize + DeserializeOwned + Default,
{
/// The function `new_with_data` creates a new instance of a `Vertex` with the given point and data, and
/// assigns a unique identifier to it.
Expand Down Expand Up @@ -253,10 +253,16 @@ mod tests {
}

#[test]
fn vertex_serialization() {
fn vertex_to_and_from_json() {
let vertex: Vertex<f64, Option<()>, 3> = Vertex::new(Point::new([1.0, 2.0, 3.0]));
let serialized = serde_json::to_string(&vertex).unwrap();
assert!(serialized.contains("point"));
assert!(serialized.contains("[1.0,2.0,3.0]"));

let deserialized: Vertex<f64, Option<()>, 3> = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, vertex);

// Human readable output for cargo test -- --nocapture
println!("Serialized: {:?}", serialized);
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod delaunay_core {
}

/// The function `is_normal` checks that structs implement `auto` traits.
/// Traits are checked at compile time, so this function is only used for testing.
fn is_normal<T: Sized + Send + Sync + Unpin>() -> bool {
true
}
Expand Down

0 comments on commit fe3a704

Please sign in to comment.