Compare commits
8
Commits
2b20df273b
...
rust
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
192017d14e | ||
|
|
10644adb54 | ||
|
|
ded1b49068 | ||
|
|
3ac782fb38 | ||
|
|
f4962e060b | ||
|
|
27529d38eb | ||
|
|
9fe0337c9d | ||
|
|
596b2a509f |
Generated
+19
-2607
File diff suppressed because it is too large
Load Diff
+2
-11
@@ -5,19 +5,10 @@ edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
name = "dla"
|
||||
crate-type = ["staticlib"]
|
||||
path = "src/clib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "model"
|
||||
path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "ui"
|
||||
path = "src/ui.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "tools"
|
||||
path = "src/tools_cli.rs"
|
||||
@@ -36,7 +27,6 @@ opt-level = 3
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.1.8", features = ["derive"] }
|
||||
bevy = { version = "0.10.0" }
|
||||
nd_array = "0.1.0"
|
||||
num-integer = "0.1.45"
|
||||
rand = { version = "0.8.5", features = ["default", "small_rng"] }
|
||||
@@ -44,7 +34,7 @@ csv = "1.1"
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = "1.0.93"
|
||||
kd-tree = { version = "0.5.1", features = ["nalgebra"] }
|
||||
nalgebra = "0.32.2"
|
||||
nalgebra = { version = "0.32.2", features = ["serde-serialize"] }
|
||||
kiddo = "0.2.5"
|
||||
anyhow = "1.0.69"
|
||||
itertools = "0.10.5"
|
||||
@@ -65,6 +55,7 @@ rayon = "1.7.0"
|
||||
walkdir = "2.3.3"
|
||||
parquet = { version = "35.0.0", features = ["serde"] }
|
||||
colorous = "1.0.10"
|
||||
num-traits = "0.2.15"
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.24.3"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# DLA Generic Model
|
||||
|
||||
A generic pluggable model for diffusion limited aggregation. Produces two executables,
|
||||
|
||||
- `./target/release/model`, built from `./src/main.rs`
|
||||
- `./target/release/tools`, built from `./src/tools_cli.rs`
|
||||
|
||||
Build with `cargo build --release`. Requires a working rust installation.
|
||||
@@ -1,16 +0,0 @@
|
||||
extern crate cbindgen;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use cbindgen::{Config, Builder};
|
||||
|
||||
fn main() {
|
||||
let crate_env = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
let crate_path = Path::new(&crate_env);
|
||||
let config = Config::from_root_or_default(crate_path);
|
||||
Builder::new().with_crate(crate_path.to_str().unwrap())
|
||||
.with_config(config)
|
||||
.generate()
|
||||
.expect("Cannot generate header file!")
|
||||
.write_to_file("libdla.h");
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
#include <new>
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool dla_rust_disabled();
|
||||
|
||||
} // extern "C"
|
||||
@@ -1,4 +0,0 @@
|
||||
#[no_mangle]
|
||||
pub extern "C" fn dla_rust_disabled() -> bool {
|
||||
true
|
||||
}
|
||||
+4
-4
@@ -8,13 +8,13 @@ pub mod model;
|
||||
pub mod spaces;
|
||||
|
||||
pub trait Position: Add<Output=Self> + Serialize + Clone {
|
||||
// const DIM: usize;
|
||||
type Cartesian;
|
||||
const DIM: usize;
|
||||
// type Cartesian;
|
||||
|
||||
fn zero() -> Self;
|
||||
fn abs(&self) -> f32;
|
||||
fn from_cartesian(cartesian: Self::Cartesian) -> Self;
|
||||
fn to_cartesian(&self) -> Self::Cartesian;
|
||||
fn from_cartesian(cartesian: &[f32]) -> Self;
|
||||
fn to_cartesian(&self) -> Vec<f32>;
|
||||
}
|
||||
|
||||
pub trait GriddedPosition: Position {
|
||||
|
||||
@@ -2,15 +2,15 @@ use std::f32::consts::PI;
|
||||
use std::ops::Add;
|
||||
use kiddo::distance::squared_euclidean;
|
||||
use rand::Rng;
|
||||
use serde::Serialize;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::system::sticker::Sticker;
|
||||
use crate::system::{Position, Storage};
|
||||
use crate::system::walker::Walker;
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct P2 {
|
||||
x: f32,
|
||||
y: f32,
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl P2 {
|
||||
@@ -47,7 +47,7 @@ pub struct ContinuousStorage {
|
||||
}
|
||||
|
||||
impl Position for P2 {
|
||||
type Cartesian = [f32; 2];
|
||||
const DIM: usize = 2;
|
||||
|
||||
fn zero() -> Self {
|
||||
P2 { x: 0f32, y: 0f32 }
|
||||
@@ -57,12 +57,12 @@ impl Position for P2 {
|
||||
(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] }
|
||||
}
|
||||
|
||||
fn to_cartesian(&self) -> Self::Cartesian {
|
||||
[self.x, self.y]
|
||||
fn to_cartesian(&self) -> Vec<f32> {
|
||||
vec![self.x, self.y]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ pub struct ContinuousStorage {
|
||||
}
|
||||
|
||||
impl Position for P3 {
|
||||
type Cartesian = [f32; 3];
|
||||
const DIM: usize = 3;
|
||||
|
||||
fn zero() -> Self {
|
||||
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)
|
||||
}
|
||||
|
||||
fn from_cartesian(cartesian: Self::Cartesian) -> Self {
|
||||
fn from_cartesian(cartesian: &[f32]) -> Self {
|
||||
P3 { x: cartesian[0], y: cartesian[1], z: cartesian[3] }
|
||||
}
|
||||
|
||||
fn to_cartesian(&self) -> Self::Cartesian {
|
||||
[self.x, self.y, self.z]
|
||||
fn to_cartesian(&self) -> Vec<f32> {
|
||||
vec![self.x, self.y, self.z]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::ops::Add;
|
||||
use num_integer::Roots;
|
||||
use num_traits::Pow;
|
||||
use crate::system::{GriddedPosition, Position};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
@@ -40,8 +41,7 @@ impl GriddedPosition for HexPosition {
|
||||
}
|
||||
|
||||
impl Position for HexPosition {
|
||||
// const DIM: usize = 2;
|
||||
type Cartesian = [f32; 2];
|
||||
const DIM: usize = 2;
|
||||
|
||||
fn zero() -> Self {
|
||||
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()
|
||||
}
|
||||
|
||||
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 r = 2.0 / 3.0 * cartesian[1];
|
||||
|
||||
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 r = self.r as f32;
|
||||
[
|
||||
|
||||
vec![
|
||||
3f32.sqrt() * q + 3f32.sqrt() / 2f32 * r,
|
||||
(3. / 2.) * r
|
||||
]
|
||||
|
||||
@@ -7,3 +7,4 @@ pub mod hexagonal;
|
||||
|
||||
pub mod continuous_3d;
|
||||
pub mod continuous_2d;
|
||||
pub mod nalg;
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
use std::ops::Add;
|
||||
use itertools::Itertools;
|
||||
use nalgebra::{EuclideanNorm, LpNorm, Matrix, Norm, OMatrix, SVector};
|
||||
use num_traits::Pow;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::system::{GriddedPosition, Position, Storage};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct Gridded<const D: usize>(SVector<i32, D>);
|
||||
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct Continuous<const D: usize>(SVector<f32, D>);
|
||||
|
||||
impl<const D: usize> Add for Continuous<D> {
|
||||
type Output = Continuous<D>;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Continuous(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> Position for Continuous<D> {
|
||||
const DIM: usize = 0;
|
||||
|
||||
fn zero() -> Self {
|
||||
Continuous(SVector::<f32, D>::zeros())
|
||||
}
|
||||
|
||||
fn abs(&self) -> f32 {
|
||||
self.0.norm()
|
||||
}
|
||||
|
||||
fn from_cartesian(cartesian: &[f32]) -> Self {
|
||||
Continuous(SVector::<f32, D>::from_fn(|i, _| cartesian[i]))
|
||||
}
|
||||
|
||||
fn to_cartesian(&self) -> Vec<f32> {
|
||||
self.0.as_slice().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> Add for Gridded<D> {
|
||||
type Output = Gridded<D>;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Gridded(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> Position for Gridded<D> {
|
||||
const DIM: usize = 0;
|
||||
|
||||
fn zero() -> Self {
|
||||
Gridded(SVector::<i32, D>::zeros())
|
||||
}
|
||||
|
||||
fn abs(&self) -> f32 {
|
||||
(self.0.fold(0, |r, c| r + c.pow(2)) as f32).sqrt()
|
||||
}
|
||||
|
||||
fn from_cartesian(cartesian: &[f32]) -> Self {
|
||||
Gridded(SVector::<i32, D>::from_fn(|i, _| cartesian[i] as i32))
|
||||
}
|
||||
|
||||
fn to_cartesian(&self) -> Vec<f32> {
|
||||
self.0.as_slice()
|
||||
.iter()
|
||||
.map(|a| *a as f32)
|
||||
.collect_vec()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KDSpace<const N: usize>(pub(crate) kiddo::KdTree<f32, (), N>);
|
||||
|
||||
impl<const D: usize> Storage<Gridded<D>> for KDSpace<D> {
|
||||
fn is_occupied(&self, position: &Gridded<D>) -> bool {
|
||||
let a = self.0.best_n_within(
|
||||
&position.0.data.0[0].map(|i| i as f32),
|
||||
0f32,
|
||||
1,
|
||||
&|a, b| {
|
||||
LpNorm(1).metric_distance(
|
||||
&SVector::<f32, D>::from_row_slice(a),
|
||||
&SVector::<f32, D>::from_row_slice(b),
|
||||
)
|
||||
},
|
||||
).unwrap();
|
||||
|
||||
!a.is_empty()
|
||||
}
|
||||
|
||||
fn deposit(&mut self, position: &Gridded<D>) {
|
||||
self.0.add(&position.0.data.0[0].map(|i| i as f32), ())
|
||||
.expect("Failed to write to space")
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> Storage<Continuous<D>> for KDSpace<D> {
|
||||
fn is_occupied(&self, position: &Continuous<D>) -> bool {
|
||||
let a = self.0.best_n_within(
|
||||
&position.0.data.0[0],
|
||||
0f32,
|
||||
1,
|
||||
&|a, b| {
|
||||
EuclideanNorm.metric_distance(
|
||||
&SVector::<f32, D>::from_row_slice(a),
|
||||
&SVector::<f32, D>::from_row_slice(b),
|
||||
)
|
||||
},
|
||||
).unwrap();
|
||||
|
||||
!a.is_empty()
|
||||
}
|
||||
|
||||
fn deposit(&mut self, position: &Continuous<D>) {
|
||||
self.0.add(&position.0.data.0[0], ())
|
||||
.expect("Failed to write to space")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VectorStorage {
|
||||
backing: Vec<bool>,
|
||||
grid_size: usize,
|
||||
}
|
||||
|
||||
impl<const D: usize> Storage<Gridded<D>> for VectorStorage {
|
||||
fn is_occupied(&self, position: &Gridded<D>) -> bool {
|
||||
let mut index: usize = 0;
|
||||
|
||||
for i in 0..D {
|
||||
index += (position.0[i] + (self.grid_size as i32 / 2)) as usize * self.grid_size * i;
|
||||
}
|
||||
|
||||
return self.backing[index];
|
||||
}
|
||||
|
||||
fn deposit(&mut self, position: &Gridded<D>) {
|
||||
let mut index: usize = 0;
|
||||
|
||||
for i in 0..D {
|
||||
index += (position.0[i] + (self.grid_size as i32 / 2)) as usize * self.grid_size * i;
|
||||
}
|
||||
|
||||
self.backing[index] = true;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ impl Add for Grid2D {
|
||||
}
|
||||
|
||||
impl Position for Grid2D {
|
||||
type Cartesian = [f32; 2];
|
||||
const DIM: usize = 2;
|
||||
|
||||
fn zero() -> Self {
|
||||
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)
|
||||
}
|
||||
|
||||
fn from_cartesian(cartesian: Self::Cartesian) -> Self {
|
||||
fn from_cartesian(cartesian: &[f32]) -> Self {
|
||||
Grid2D {
|
||||
x: cartesian[0] as i32,
|
||||
y: cartesian[1] as i32,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_cartesian(&self) -> Self::Cartesian {
|
||||
[self.x as f32, self.y as f32]
|
||||
fn to_cartesian(&self) -> Vec<f32> {
|
||||
vec![self.x as f32, self.y as f32]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,17 +113,17 @@ impl Add for Grid3D {
|
||||
#[test]
|
||||
fn grid3_add_test() {
|
||||
assert_eq!(
|
||||
Grid3D::from_cartesian([0f32, 0f32, 0f32]) + Grid3D::from_cartesian([0f32, 1f32, 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])
|
||||
);
|
||||
assert_eq!(
|
||||
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(&[5.0, 3.0, 1.0]) + Grid3D::from_cartesian(&[-2.0, 5.0, 100.0]),
|
||||
Grid3D::from_cartesian(&[3.0, 8.0, 101.0])
|
||||
);
|
||||
}
|
||||
|
||||
impl Position for Grid3D {
|
||||
type Cartesian = [f32; 3];
|
||||
const DIM: usize = 3;
|
||||
|
||||
fn zero() -> Self {
|
||||
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)
|
||||
}
|
||||
|
||||
fn from_cartesian(cartesian: Self::Cartesian) -> Self {
|
||||
fn from_cartesian(cartesian: &[f32]) -> Self {
|
||||
Self {
|
||||
x: cartesian[0] as i32,
|
||||
y: cartesian[1] as i32,
|
||||
@@ -141,8 +141,8 @@ impl Position for Grid3D {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_cartesian(&self) -> Self::Cartesian {
|
||||
[self.x as f32, self.y as f32, self.z as f32]
|
||||
fn to_cartesian(&self) -> Vec<f32> {
|
||||
vec![self.x as f32, self.y as f32, self.z as f32]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ impl VectorStorage {
|
||||
impl<P: GriddedPosition> Storage<P> for VectorStorage {
|
||||
fn is_occupied(&self, position: &P) -> bool {
|
||||
let i = position.linear_index(self.grid_size);
|
||||
println!("{i}");
|
||||
return self.backing[i];
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ impl Spawner<Grid2D> for UniformSpawner {
|
||||
let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
|
||||
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 (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()
|
||||
);
|
||||
|
||||
Grid3D::from_cartesian([x, y, z])
|
||||
Grid3D::from_cartesian(&[x, y, z])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::hash::Hash;
|
||||
use bevy::render::render_resource::encase::private::RuntimeSizedArray;
|
||||
use itertools::Itertools;
|
||||
use rand::distributions::Slice;
|
||||
use rand::prelude::Rng;
|
||||
|
||||
@@ -5,7 +5,7 @@ use polars::io::RowCount;
|
||||
use rayon::prelude::*;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub fn main(cli: &DataAnalysisCli) {
|
||||
pub(crate) fn main(cli: &DataAnalysisCli) {
|
||||
let a: Vec<_> = WalkDir::new(&cli.sp_dir)
|
||||
.into_iter()
|
||||
.par_bridge()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::fs::File;
|
||||
use std::os::unix::fs::symlink;
|
||||
use bevy::tasks::ParallelSlice;
|
||||
use crate::system::spaces::square_grid::{Grid2D, Grid3D};
|
||||
use itertools::{Itertools, MinMaxResult};
|
||||
use clap::Parser;
|
||||
|
||||
+62
-30
@@ -9,12 +9,16 @@ use crate::system::model::HistoryLine;
|
||||
use crate::system::spaces::square_grid::Grid2D;
|
||||
use clap::Parser;
|
||||
use colorous::Color;
|
||||
use itertools::Itertools;
|
||||
use num_traits::real::Real;
|
||||
use num_traits::Signed;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Deserialize;
|
||||
use svg::Node;
|
||||
use svg::node::element::Rectangle;
|
||||
use crate::RenderCli;
|
||||
use crate::system::Position;
|
||||
use svg::node::element::{Circle, Polygon, Rectangle};
|
||||
use crate::{RenderCli, Space};
|
||||
use crate::system::{GriddedPosition, Position};
|
||||
use crate::system::spaces::continuous_2d::P2;
|
||||
use crate::system::spaces::hexagonal::HexPosition;
|
||||
use crate::tools::read;
|
||||
|
||||
@@ -26,68 +30,96 @@ struct Args {
|
||||
}
|
||||
|
||||
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 {
|
||||
fn to_svg(&self, size: i32, colour: Color) -> Box<dyn Node> {
|
||||
fn to_svg(&self, colour: Color) -> Box<dyn Node> {
|
||||
Box::new(Rectangle::new()
|
||||
.set("fill", format!("rgb({}, {}, {})", colour.r, colour.g, colour.b))
|
||||
.set("width", size)
|
||||
.set("height", size)
|
||||
.set("x", self.x * size)
|
||||
.set("y", self.y * size))
|
||||
.set("width", 1)
|
||||
.set("height", 1)
|
||||
.set("x", self.x)
|
||||
.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 {
|
||||
fn to_svg(&self, size: i32, colour: Color) -> Box<dyn Node> {
|
||||
fn to_svg(&self, colour: Color) -> Box<dyn Node> {
|
||||
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]
|
||||
];
|
||||
|
||||
let size = size as f32;
|
||||
|
||||
let a = 256.0;
|
||||
// let a = 400.0;
|
||||
let b = points.map(|x| [
|
||||
(x[0] / 512.0) * (size),
|
||||
(x[1] / 512.0) * (size)]
|
||||
(x[0] / a),
|
||||
(x[1] / a)]
|
||||
);
|
||||
|
||||
let c = b.map(|p| format!("{},{}", p[0], p[1])).join(" ");
|
||||
|
||||
let [x, y] = self.to_cartesian();
|
||||
Box::new(Rectangle::new()
|
||||
let cartesian = self.to_cartesian();
|
||||
Box::new(Polygon::new()
|
||||
.set("fill", format!("rgb({}, {}, {})", colour.r, colour.g, colour.b))
|
||||
.set("x", x * size)
|
||||
.set("y", y * size))
|
||||
.set("points", c)
|
||||
.set("transform", format!("translate({}, {})", cartesian[0], cartesian[1])))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn main(args: &RenderCli) {
|
||||
let positions: Vec<Grid2D> = read::<Grid2D>(&args.path, args.format);
|
||||
pub fn compute_max_size<P: Position>(positions: &[P]) -> f32 {
|
||||
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;
|
||||
let max_x = positions.iter().max_by(|a, b| a.x.abs().cmp(&b.x.abs())).unwrap().x.abs();
|
||||
let max_y = positions.iter().max_by(|a, b| a.y.abs().cmp(&b.y.abs())).unwrap().y.abs();
|
||||
let max_size = max_x.max(max_y) * size;
|
||||
for cartesian in positions {
|
||||
for i in 0..maximums.len() {
|
||||
maximums[i] = maximums[i].abs().max(cartesian[i].abs());
|
||||
}
|
||||
}
|
||||
|
||||
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::<P>(&args.path, args.format);
|
||||
let max_size = compute_max_size(&positions) + 10.0;
|
||||
|
||||
let mut svg = svg::Document::new()
|
||||
.set("width", max_size * size)
|
||||
.set("height", max_size * size)
|
||||
.set("viewBox", format!("{} {} {} {}", -max_size, -max_size, max_size * 2, max_size * 2));
|
||||
.set("width", args.image_size)
|
||||
.set("height", args.image_size)
|
||||
.set("viewBox", format!("{} {} {} {}", -max_size, -max_size, max_size * 2.0, max_size * 2.0));
|
||||
|
||||
svg.append(Rectangle::new()
|
||||
.set("fill", "white")
|
||||
.set("width", max_size * 2)
|
||||
.set("height", max_size * 2)
|
||||
.set("width", max_size * 2.0)
|
||||
.set("height", max_size * 2.0)
|
||||
.set("x", -max_size)
|
||||
.set("y", -max_size)
|
||||
);
|
||||
|
||||
for (n, position) in positions.iter().enumerate() {
|
||||
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();
|
||||
}
|
||||
|
||||
pub(crate) fn main(args: &RenderCli) {
|
||||
match args.space {
|
||||
Space::Grid2D => render::<Grid2D>(args),
|
||||
Space::Continuous2D => render::<P2>(args),
|
||||
Space::Hex => render::<HexPosition>(args),
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,5 +1,6 @@
|
||||
#![feature(generic_const_exprs)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(array_zip)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::ops::{Index, Mul};
|
||||
@@ -33,6 +34,8 @@ enum ToolsCli {
|
||||
#[derive(clap::ValueEnum, Clone, Debug, Copy)]
|
||||
enum Space {
|
||||
Grid2D,
|
||||
Continuous2D,
|
||||
Hex,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
@@ -73,8 +76,7 @@ fn main() -> anyhow::Result<()> {
|
||||
match args {
|
||||
ToolsCli::Render(cli) => tools::render::main(&cli),
|
||||
ToolsCli::BoxCount(cli) => tools::boxcount::main(&cli),
|
||||
ToolsCli::DataAnalysis(cli) => {
|
||||
}
|
||||
ToolsCli::DataAnalysis(cli) => tools::analysis::main(&cli),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
//! A simplified implementation of the classic game "Breakout".
|
||||
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
// These constants are defined in `Transform` units.
|
||||
// Using the default 2D camera they correspond 1:1 with screen pixels.
|
||||
const GAP_BETWEEN_PADDLE_AND_FLOOR: f32 = 60.0;
|
||||
|
||||
const LEFT_WALL: f32 = -450.;
|
||||
const RIGHT_WALL: f32 = 450.;
|
||||
// y coordinates
|
||||
const BOTTOM_WALL: f32 = -300.;
|
||||
const TOP_WALL: f32 = 300.;
|
||||
|
||||
const PARTICLE_SIZE: Vec2 = Vec2::new(10., 10.);
|
||||
|
||||
// These values are exact
|
||||
const GAP_BETWEEN_PADDLE_AND_BRICKS: f32 = 270.0;
|
||||
const GAP_BETWEEN_BRICKS: f32 = 5.0;
|
||||
// These values are lower bounds, as the number of bricks is computed
|
||||
const GAP_BETWEEN_BRICKS_AND_CEILING: f32 = 20.0;
|
||||
const GAP_BETWEEN_BRICKS_AND_SIDES: f32 = 20.0;
|
||||
|
||||
const BACKGROUND_COLOR: Color = Color::rgb(0.9, 0.9, 0.9);
|
||||
const BRICK_COLOR: Color = Color::rgb(0.5, 0.5, 1.0);
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.insert_resource(ClearColor(BACKGROUND_COLOR))
|
||||
.add_startup_system(setup)
|
||||
.add_system(bevy::window::close_on_esc)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Brick;
|
||||
|
||||
// Add the game's entities to our world
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
) {
|
||||
// Camera
|
||||
commands.spawn(Camera2dBundle::default());
|
||||
|
||||
// Paddle
|
||||
let paddle_y = BOTTOM_WALL + GAP_BETWEEN_PADDLE_AND_FLOOR;
|
||||
|
||||
// Bricks
|
||||
// Negative scales result in flipped sprites / meshes,
|
||||
// which is definitely not what we want here
|
||||
assert!(PARTICLE_SIZE.x > 0.0);
|
||||
assert!(PARTICLE_SIZE.y > 0.0);
|
||||
|
||||
let total_width_of_bricks = (RIGHT_WALL - LEFT_WALL) - 2. * GAP_BETWEEN_BRICKS_AND_SIDES;
|
||||
let bottom_edge_of_bricks = paddle_y + GAP_BETWEEN_PADDLE_AND_BRICKS;
|
||||
let total_height_of_bricks = TOP_WALL - bottom_edge_of_bricks - GAP_BETWEEN_BRICKS_AND_CEILING;
|
||||
|
||||
assert!(total_width_of_bricks > 0.0);
|
||||
assert!(total_height_of_bricks > 0.0);
|
||||
|
||||
// Given the space available, compute how many rows and columns of bricks we can fit
|
||||
let n_columns = (total_width_of_bricks / (PARTICLE_SIZE.x + GAP_BETWEEN_BRICKS)).floor() as usize;
|
||||
let n_rows = (total_height_of_bricks / (PARTICLE_SIZE.y + GAP_BETWEEN_BRICKS)).floor() as usize;
|
||||
let n_vertical_gaps = n_columns - 1;
|
||||
|
||||
// Because we need to round the number of columns,
|
||||
// the space on the top and sides of the bricks only captures a lower bound, not an exact value
|
||||
let center_of_bricks = (LEFT_WALL + RIGHT_WALL) / 2.0;
|
||||
let left_edge_of_bricks = center_of_bricks
|
||||
// Space taken up by the bricks
|
||||
- (n_columns as f32 / 2.0 * PARTICLE_SIZE.x)
|
||||
// Space taken up by the gaps
|
||||
- n_vertical_gaps as f32 / 2.0 * GAP_BETWEEN_BRICKS;
|
||||
|
||||
// In Bevy, the `translation` of an entity describes the center point,
|
||||
// not its bottom-left corner
|
||||
let offset_x = left_edge_of_bricks + PARTICLE_SIZE.x / 2.;
|
||||
let offset_y = bottom_edge_of_bricks + PARTICLE_SIZE.y / 2.;
|
||||
|
||||
for row in 0..n_rows {
|
||||
for column in 0..n_columns {
|
||||
let brick_position = Vec2::new(
|
||||
offset_x + column as f32 * (PARTICLE_SIZE.x + GAP_BETWEEN_BRICKS),
|
||||
offset_y + row as f32 * (PARTICLE_SIZE.y + GAP_BETWEEN_BRICKS),
|
||||
);
|
||||
|
||||
// brick
|
||||
commands.spawn((
|
||||
SpriteBundle {
|
||||
sprite: Sprite {
|
||||
color: BRICK_COLOR,
|
||||
..default()
|
||||
},
|
||||
transform: Transform {
|
||||
translation: brick_position.extend(0.0),
|
||||
scale: Vec3::new(PARTICLE_SIZE.x, PARTICLE_SIZE.y, 1.0),
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
Brick,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user