Refactor Position thing (again, we love hitting compiler bugs), and add P2 rendering
This commit is contained in:
parent
2b20df273b
commit
596b2a509f
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -4000,6 +4000,7 @@ dependencies = [
|
|||||||
"nalgebra 0.32.2",
|
"nalgebra 0.32.2",
|
||||||
"nd_array",
|
"nd_array",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
"parquet",
|
"parquet",
|
||||||
"polars",
|
"polars",
|
||||||
"rand",
|
"rand",
|
||||||
|
|||||||
@ -65,6 +65,7 @@ rayon = "1.7.0"
|
|||||||
walkdir = "2.3.3"
|
walkdir = "2.3.3"
|
||||||
parquet = { version = "35.0.0", features = ["serde"] }
|
parquet = { version = "35.0.0", features = ["serde"] }
|
||||||
colorous = "1.0.10"
|
colorous = "1.0.10"
|
||||||
|
num-traits = "0.2.15"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cbindgen = "0.24.3"
|
cbindgen = "0.24.3"
|
||||||
|
|||||||
BIN
dla-eg.png
Normal file
BIN
dla-eg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
@ -8,13 +8,13 @@ pub mod model;
|
|||||||
pub mod spaces;
|
pub mod spaces;
|
||||||
|
|
||||||
pub trait Position: Add<Output=Self> + Serialize + Clone {
|
pub trait Position: Add<Output=Self> + Serialize + Clone {
|
||||||
// const DIM: usize;
|
const DIM: usize;
|
||||||
type Cartesian;
|
// type Cartesian;
|
||||||
|
|
||||||
fn zero() -> Self;
|
fn zero() -> Self;
|
||||||
fn abs(&self) -> f32;
|
fn abs(&self) -> f32;
|
||||||
fn from_cartesian(cartesian: Self::Cartesian) -> Self;
|
fn from_cartesian(cartesian: &[f32]) -> Self;
|
||||||
fn to_cartesian(&self) -> Self::Cartesian;
|
fn to_cartesian(&self) -> Vec<f32>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GriddedPosition: Position {
|
pub trait GriddedPosition: Position {
|
||||||
|
|||||||
@ -2,15 +2,15 @@ use std::f32::consts::PI;
|
|||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use kiddo::distance::squared_euclidean;
|
use kiddo::distance::squared_euclidean;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use serde::Serialize;
|
use serde::{Serialize, Deserialize};
|
||||||
use crate::system::sticker::Sticker;
|
use crate::system::sticker::Sticker;
|
||||||
use crate::system::{Position, Storage};
|
use crate::system::{Position, Storage};
|
||||||
use crate::system::walker::Walker;
|
use crate::system::walker::Walker;
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct P2 {
|
pub struct P2 {
|
||||||
x: f32,
|
pub x: f32,
|
||||||
y: f32,
|
pub y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl P2 {
|
impl P2 {
|
||||||
@ -47,7 +47,7 @@ pub struct ContinuousStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Position for P2 {
|
impl Position for P2 {
|
||||||
type Cartesian = [f32; 2];
|
const DIM: usize = 2;
|
||||||
|
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
P2 { x: 0f32, y: 0f32 }
|
P2 { x: 0f32, y: 0f32 }
|
||||||
@ -57,12 +57,12 @@ impl Position for P2 {
|
|||||||
(self.x.powi(2) + self.y.powi(2)).powf(0.5)
|
(self.x.powi(2) + self.y.powi(2)).powf(0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_cartesian(cartesian: Self::Cartesian) -> Self {
|
fn from_cartesian(cartesian: &[f32]) -> Self {
|
||||||
P2 { x: cartesian[0], y: cartesian[1] }
|
P2 { x: cartesian[0], y: cartesian[1] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_cartesian(&self) -> Self::Cartesian {
|
fn to_cartesian(&self) -> Vec<f32> {
|
||||||
[self.x, self.y]
|
vec![self.x, self.y]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -51,7 +51,7 @@ pub struct ContinuousStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Position for P3 {
|
impl Position for P3 {
|
||||||
type Cartesian = [f32; 3];
|
const DIM: usize = 3;
|
||||||
|
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
P3 { x: 0f32, y: 0f32, z: 0f32 }
|
P3 { x: 0f32, y: 0f32, z: 0f32 }
|
||||||
@ -61,12 +61,12 @@ impl Position for P3 {
|
|||||||
(self.x.powi(2) + self.y.powi(2) + self.z.powi(2)).powf(0.5)
|
(self.x.powi(2) + self.y.powi(2) + self.z.powi(2)).powf(0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_cartesian(cartesian: Self::Cartesian) -> Self {
|
fn from_cartesian(cartesian: &[f32]) -> Self {
|
||||||
P3 { x: cartesian[0], y: cartesian[1], z: cartesian[3] }
|
P3 { x: cartesian[0], y: cartesian[1], z: cartesian[3] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_cartesian(&self) -> Self::Cartesian {
|
fn to_cartesian(&self) -> Vec<f32> {
|
||||||
[self.x, self.y, self.z]
|
vec![self.x, self.y, self.z]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use num_integer::Roots;
|
use num_integer::Roots;
|
||||||
|
use num_traits::Pow;
|
||||||
use crate::system::{GriddedPosition, Position};
|
use crate::system::{GriddedPosition, Position};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
@ -40,8 +41,7 @@ impl GriddedPosition for HexPosition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Position for HexPosition {
|
impl Position for HexPosition {
|
||||||
// const DIM: usize = 2;
|
const DIM: usize = 2;
|
||||||
type Cartesian = [f32; 2];
|
|
||||||
|
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
HexPosition { q: 0, r: 0 }
|
HexPosition { q: 0, r: 0 }
|
||||||
@ -51,17 +51,18 @@ impl Position for HexPosition {
|
|||||||
((self.q.pow(2) + self.r.pow(2) + self.q * self.r) as f32).sqrt()
|
((self.q.pow(2) + self.r.pow(2) + self.q * self.r) as f32).sqrt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_cartesian(cartesian: Self::Cartesian) -> Self {
|
fn from_cartesian(cartesian: &[f32]) -> Self {
|
||||||
let q = (1.0f32 / 3.0f32).sqrt() * cartesian[0] - 1.0 / 3.0 * cartesian[1];
|
let q = (1.0f32 / 3.0f32).sqrt() * cartesian[0] - 1.0 / 3.0 * cartesian[1];
|
||||||
let r = 2.0 / 3.0 * cartesian[1];
|
let r = 2.0 / 3.0 * cartesian[1];
|
||||||
|
|
||||||
Self { q: q as i32, r: r as i32 }
|
Self { q: q as i32, r: r as i32 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_cartesian(&self) -> Self::Cartesian {
|
fn to_cartesian(&self) -> Vec<f32> {
|
||||||
let q = self.q as f32;
|
let q = self.q as f32;
|
||||||
let r = self.r as f32;
|
let r = self.r as f32;
|
||||||
[
|
|
||||||
|
vec![
|
||||||
3f32.sqrt() * q + 3f32.sqrt() / 2f32 * r,
|
3f32.sqrt() * q + 3f32.sqrt() / 2f32 * r,
|
||||||
(3. / 2.) * r
|
(3. / 2.) * r
|
||||||
]
|
]
|
||||||
|
|||||||
@ -28,7 +28,7 @@ impl Add for Grid2D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Position for Grid2D {
|
impl Position for Grid2D {
|
||||||
type Cartesian = [f32; 2];
|
const DIM: usize = 2;
|
||||||
|
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
Grid2D { x: 0, y: 0 }
|
Grid2D { x: 0, y: 0 }
|
||||||
@ -38,15 +38,15 @@ impl Position for Grid2D {
|
|||||||
(((self.x * self.x) + (self.y * self.y)) as f32).powf(0.5)
|
(((self.x * self.x) + (self.y * self.y)) as f32).powf(0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_cartesian(cartesian: Self::Cartesian) -> Self {
|
fn from_cartesian(cartesian: &[f32]) -> Self {
|
||||||
Grid2D {
|
Grid2D {
|
||||||
x: cartesian[0] as i32,
|
x: cartesian[0] as i32,
|
||||||
y: cartesian[1] as i32,
|
y: cartesian[1] as i32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_cartesian(&self) -> Self::Cartesian {
|
fn to_cartesian(&self) -> Vec<f32> {
|
||||||
[self.x as f32, self.y as f32]
|
vec![self.x as f32, self.y as f32]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,17 +113,17 @@ impl Add for Grid3D {
|
|||||||
#[test]
|
#[test]
|
||||||
fn grid3_add_test() {
|
fn grid3_add_test() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Grid3D::from_cartesian([0f32, 0f32, 0f32]) + Grid3D::from_cartesian([0f32, 1f32, 0f32]),
|
Grid3D::from_cartesian(&[0f32, 0f32, 0f32]) + Grid3D::from_cartesian(&[0f32, 1f32, 0f32]),
|
||||||
Grid3D::from_cartesian([0f32, 1f32, 0f32])
|
Grid3D::from_cartesian(&[0f32, 1f32, 0f32])
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Grid3D::from_cartesian([5.0, 3.0, 1.0]) + Grid3D::from_cartesian([-2.0, 5.0, 100.0]),
|
Grid3D::from_cartesian(&[5.0, 3.0, 1.0]) + Grid3D::from_cartesian(&[-2.0, 5.0, 100.0]),
|
||||||
Grid3D::from_cartesian([3.0, 8.0, 101.0])
|
Grid3D::from_cartesian(&[3.0, 8.0, 101.0])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Position for Grid3D {
|
impl Position for Grid3D {
|
||||||
type Cartesian = [f32; 3];
|
const DIM: usize = 3;
|
||||||
|
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
Grid3D { x: 0, y: 0, z: 0 }
|
Grid3D { x: 0, y: 0, z: 0 }
|
||||||
@ -133,7 +133,7 @@ impl Position for Grid3D {
|
|||||||
(((self.x * self.x) + (self.y * self.y) + (self.z * self.z)) as f32).powf(0.5)
|
(((self.x * self.x) + (self.y * self.y) + (self.z * self.z)) as f32).powf(0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_cartesian(cartesian: Self::Cartesian) -> Self {
|
fn from_cartesian(cartesian: &[f32]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
x: cartesian[0] as i32,
|
x: cartesian[0] as i32,
|
||||||
y: cartesian[1] as i32,
|
y: cartesian[1] as i32,
|
||||||
@ -141,8 +141,8 @@ impl Position for Grid3D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_cartesian(&self) -> Self::Cartesian {
|
fn to_cartesian(&self) -> Vec<f32> {
|
||||||
[self.x as f32, self.y as f32, self.z as f32]
|
vec![self.x as f32, self.y as f32, self.z as f32]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ impl Spawner<Grid2D> for UniformSpawner {
|
|||||||
let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
|
let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
|
||||||
let (x, y) = (radius * theta.cos(), radius * theta.sin());
|
let (x, y) = (radius * theta.cos(), radius * theta.sin());
|
||||||
|
|
||||||
Grid2D::from_cartesian([x, y])
|
Grid2D::from_cartesian(&[x, y])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ impl Spawner<HexPosition> for UniformSpawner {
|
|||||||
let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
|
let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
|
||||||
let (x, y) = (radius * theta.cos(), radius * theta.sin());
|
let (x, y) = (radius * theta.cos(), radius * theta.sin());
|
||||||
|
|
||||||
HexPosition::from_cartesian([x, y])
|
HexPosition::from_cartesian(&[x, y])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ impl Spawner<Grid3D> for UniformSpawner {
|
|||||||
radius * theta.cos()
|
radius * theta.cos()
|
||||||
);
|
);
|
||||||
|
|
||||||
Grid3D::from_cartesian([x, y, z])
|
Grid3D::from_cartesian(&[x, y, z])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use polars::io::RowCount;
|
|||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
pub fn main(cli: &DataAnalysisCli) {
|
pub(crate) fn main(cli: &DataAnalysisCli) {
|
||||||
let a: Vec<_> = WalkDir::new(&cli.sp_dir)
|
let a: Vec<_> = WalkDir::new(&cli.sp_dir)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.par_bridge()
|
.par_bridge()
|
||||||
|
|||||||
@ -9,12 +9,16 @@ use crate::system::model::HistoryLine;
|
|||||||
use crate::system::spaces::square_grid::Grid2D;
|
use crate::system::spaces::square_grid::Grid2D;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use colorous::Color;
|
use colorous::Color;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use num_traits::real::Real;
|
||||||
|
use num_traits::Signed;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use svg::Node;
|
use svg::Node;
|
||||||
use svg::node::element::Rectangle;
|
use svg::node::element::{Circle, Rectangle};
|
||||||
use crate::RenderCli;
|
use crate::{RenderCli, Space};
|
||||||
use crate::system::Position;
|
use crate::system::{GriddedPosition, Position};
|
||||||
|
use crate::system::spaces::continuous_2d::P2;
|
||||||
use crate::system::spaces::hexagonal::HexPosition;
|
use crate::system::spaces::hexagonal::HexPosition;
|
||||||
use crate::tools::read;
|
use crate::tools::read;
|
||||||
|
|
||||||
@ -26,68 +30,93 @@ struct Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait ToSvg {
|
trait ToSvg {
|
||||||
fn to_svg(&self, size: i32, colour: Color) -> Box<dyn Node>;
|
fn to_svg(&self, colour: Color) -> Box<dyn Node>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToSvg for Grid2D {
|
impl ToSvg for Grid2D {
|
||||||
fn to_svg(&self, size: i32, colour: Color) -> Box<dyn Node> {
|
fn to_svg(&self, colour: Color) -> Box<dyn Node> {
|
||||||
Box::new(Rectangle::new()
|
Box::new(Rectangle::new()
|
||||||
.set("fill", format!("rgb({}, {}, {})", colour.r, colour.g, colour.b))
|
.set("fill", format!("rgb({}, {}, {})", colour.r, colour.g, colour.b))
|
||||||
.set("width", size)
|
.set("width", 1)
|
||||||
.set("height", size)
|
.set("height", 1)
|
||||||
.set("x", self.x * size)
|
.set("x", self.x)
|
||||||
.set("y", self.y * size))
|
.set("y", self.y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSvg for P2 {
|
||||||
|
fn to_svg(&self, colour: Color) -> Box<dyn Node> {
|
||||||
|
Box::new(Circle::new()
|
||||||
|
.set("fill", format!("rgb({}, {}, {})", colour.r, colour.g, colour.b))
|
||||||
|
.set("r", 1)
|
||||||
|
.set("cx", self.x)
|
||||||
|
.set("cy", self.y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToSvg for HexPosition {
|
impl ToSvg for HexPosition {
|
||||||
fn to_svg(&self, size: i32, colour: Color) -> Box<dyn Node> {
|
fn to_svg(&self, colour: Color) -> Box<dyn Node> {
|
||||||
let points = [
|
let points = [
|
||||||
[25.045, 128.0], [256.0, 0.0], [486.955, 128.0], [486.955, 384.0], [256.0, 512.0], [25.045, 384.0]
|
[25.045, 128.0], [256.0, 0.0], [486.955, 128.0], [486.955, 384.0], [256.0, 512.0], [25.045, 384.0]
|
||||||
];
|
];
|
||||||
|
|
||||||
let size = size as f32;
|
|
||||||
|
|
||||||
let b = points.map(|x| [
|
let b = points.map(|x| [
|
||||||
(x[0] / 512.0) * (size),
|
(x[0] / 512.0),
|
||||||
(x[1] / 512.0) * (size)]
|
(x[1] / 512.0)]
|
||||||
);
|
);
|
||||||
|
|
||||||
let c = b.map(|p| format!("{},{}", p[0], p[1])).join(" ");
|
let c = b.map(|p| format!("{},{}", p[0], p[1])).join(" ");
|
||||||
|
|
||||||
let [x, y] = self.to_cartesian();
|
let cartesian = self.to_cartesian();
|
||||||
Box::new(Rectangle::new()
|
Box::new(Rectangle::new()
|
||||||
.set("fill", format!("rgb({}, {}, {})", colour.r, colour.g, colour.b))
|
.set("fill", format!("rgb({}, {}, {})", colour.r, colour.g, colour.b))
|
||||||
.set("x", x * size)
|
.set("x", cartesian[0])
|
||||||
.set("y", y * size))
|
.set("y", cartesian[1]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn main(args: &RenderCli) {
|
pub fn compute_max_size<P: GriddedPosition>(positions: &[P]) -> f32 {
|
||||||
let positions: Vec<Grid2D> = read::<Grid2D>(&args.path, args.format);
|
let mut positions = positions.iter().map(P::to_cartesian);
|
||||||
|
let mut maximums: Vec<f32> = positions.next().unwrap();
|
||||||
|
|
||||||
let size: i32 = args.image_size as i32;
|
for cartesian in positions {
|
||||||
let max_x = positions.iter().max_by(|a, b| a.x.abs().cmp(&b.x.abs())).unwrap().x.abs();
|
for i in 0..maximums.len() {
|
||||||
let max_y = positions.iter().max_by(|a, b| a.y.abs().cmp(&b.y.abs())).unwrap().y.abs();
|
maximums[i] = maximums[i].abs().max(cartesian[i].abs());
|
||||||
let max_size = max_x.max(max_y) * size;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maximums.into_iter()
|
||||||
|
.fold(0f32, |r, c| r.abs().max(c.abs()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<P: Position>(args: &RenderCli) where P: DeserializeOwned + ToSvg {
|
||||||
|
let positions = read::<Grid2D>(&args.path, args.format);
|
||||||
|
let max_size = compute_max_size(&positions);
|
||||||
|
|
||||||
let mut svg = svg::Document::new()
|
let mut svg = svg::Document::new()
|
||||||
.set("width", max_size * size)
|
.set("width", max_size)
|
||||||
.set("height", max_size * size)
|
.set("height", max_size)
|
||||||
.set("viewBox", format!("{} {} {} {}", -max_size, -max_size, max_size * 2, max_size * 2));
|
.set("viewBox", format!("{} {} {} {}", -max_size, -max_size, max_size * 2.0, max_size * 2.0));
|
||||||
|
|
||||||
svg.append(Rectangle::new()
|
svg.append(Rectangle::new()
|
||||||
.set("fill", "white")
|
.set("fill", "white")
|
||||||
.set("width", max_size * 2)
|
.set("width", max_size * 2.0)
|
||||||
.set("height", max_size * 2)
|
.set("height", max_size * 2.0)
|
||||||
.set("x", -max_size)
|
.set("x", -max_size)
|
||||||
.set("y", -max_size)
|
.set("y", -max_size)
|
||||||
);
|
);
|
||||||
|
|
||||||
for (n, position) in positions.iter().enumerate() {
|
for (n, position) in positions.iter().enumerate() {
|
||||||
let colour = if args.colour { colorous::VIRIDIS.eval_rational(n, positions.len()) } else { Color::default() };
|
let colour = if args.colour { colorous::VIRIDIS.eval_rational(n, positions.len()) } else { Color::default() };
|
||||||
svg.append(position.to_svg(size, colour));
|
svg.append(position.to_svg(colour));
|
||||||
}
|
}
|
||||||
|
|
||||||
svg::write(File::create(&args.output).unwrap(), &svg).unwrap();
|
svg::write(File::create(&args.output).unwrap(), &svg).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn main(args: &RenderCli) {
|
||||||
|
match args.space {
|
||||||
|
Space::Grid2D => render::<Grid2D>(args),
|
||||||
|
Space::Continuous2D => render::<P2>(args),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#![feature(generic_const_exprs)]
|
#![feature(generic_const_exprs)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
#![feature(array_zip)]
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::ops::{Index, Mul};
|
use std::ops::{Index, Mul};
|
||||||
@ -33,6 +34,7 @@ enum ToolsCli {
|
|||||||
#[derive(clap::ValueEnum, Clone, Debug, Copy)]
|
#[derive(clap::ValueEnum, Clone, Debug, Copy)]
|
||||||
enum Space {
|
enum Space {
|
||||||
Grid2D,
|
Grid2D,
|
||||||
|
Continuous2D
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user