Skip to content

Commit

Permalink
Merge pull request #80 from DavJCosby/new-config-format
Browse files Browse the repository at this point in the history
New config format
  • Loading branch information
DavJCosby authored Sep 26, 2024
2 parents 182227e + 6710ab4 commit a1925f0
Show file tree
Hide file tree
Showing 15 changed files with 213 additions and 126 deletions.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ scheduler = ["spin_sleep"]
named_colors = []

[dependencies]
glam = { version = "0.29", features = ["serde"] }
glam = { version = "0.29" }
palette = { version = "0.7", default-features = false, features = [
"std",
"approx",
] }
toml = "0.8"
smallvec = "1.13"
compact_str = { version = "0.8", optional = true }
spin_sleep = { version = "1.2", optional = true }
Expand Down
50 changes: 23 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,40 +26,36 @@ To create a Sled struct, you need to create a configuration file and provide its
```rust
use sled::Sled;
fn main() -> Result<(), sled::SledError> {
let mut sled = Sled::new("/path/to/config.toml")?;
let mut sled = Sled::new("/path/to/config.yap")?;
Ok(())
}
```

A configuration file explains the layout of your LED strips in 2D space. This is used to pre-calculate some important information, speeding up complex draw calls.

Example .toml file:
```toml
center_point = [0, 0.5]
density = 30.0

[[line_segment]]
start = [-2, 0]
end = [0.5, -1]

[[line_segment]]
start = [0.5, -1]
end = [3.5, 0]

[[line_segment]]
start = [3, 0]
end = [2, 2]

[[line_segment]]
start = [2, 2]
end = [-2, 2]
[[line_segment]]
start = [-2, 2]
end = [-2, 0]
Example config file:
```
* `center_point` is a static reference point that's used to make some calculations faster. At initialization, directions, distances, etc relative to this point are pre-calculated for each Led.
* `density` represents how many LEDs per unit we can expect for the line segments below. If one or more LED strip has a different density for whatever reason, you can override this default for each `[[line_segment]]`.
* Add as many `[[line_segment]]` tags as you need to represent your scene.
center: (0.0, 0.5)
density: 30.0
--segments--
(-2, 0) --> (0.5, -1) --> (3.5, 0) -->
(2, 2) --> (-2, 2) --> (-2, 0)
```
* `center` is a 2D reference point you can use to speed up draw calls. At initialization, directions, distances, etc relative to this point are pre-calculated for each Led.
* `density` represents how many LEDs per unit we can expect for the line segments below.
* `(x, y) --> (x, y)` Indicates a line segment spanning between those two connected vertices. If you wish to introduce a break between vertices, you must replace one of the `-->` separators with a `|`. Like this:
```
--segments--
(-2, 0) --> (0.5, -1) --> (3.5, 0) |
(2, 2) --> (-2, 2) --> (-2, 0)
```
Whitespace and linebreaks are generally irrelevant in formatting segments, meaning the above is functionally equivalent to:
```
--segments--
(-2, 0) --> (0.5, -1)
--> (3.5, 0) | (2, 2)
--> (-2, 2) --> (-2, 0)
```
### Drawing
Once you have your Sled struct, you can start drawing to it right away! Here’s a taste of some of the things Sled lets you do:
Expand Down
2 changes: 1 addition & 1 deletion examples/calibration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use glam::Vec2;
use sled::{color::Rgb, Sled, SledError};

fn main() -> Result<(), SledError> {
let mut sled = Sled::new("./examples/resources/config.toml")?;
let mut sled = Sled::new("./examples/resources/config.yap")?;

let mut display = SledTerminalDisplay::start("Calibration", sled.domain());

Expand Down
2 changes: 1 addition & 1 deletion examples/comet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use resources::tui::SledTerminalDisplay;
use sled::{scheduler::Scheduler, Sled};

fn main() {
let sled = Sled::new("./examples/resources/config.toml").unwrap();
let sled = Sled::new("./examples/resources/config.yap").unwrap();
let mut display = SledTerminalDisplay::start("Comet", sled.domain());
let mut driver = comet::build_driver();
driver.mount(sled);
Expand Down
5 changes: 5 additions & 0 deletions examples/resources/config.yap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
center: (0, 0.5)
density: 30
--segments--
(-2, 0) --> (0.5, -1) --> (3.5, 0) |
(2, 2) --> (-2, 2) --> (-2, 0)
File renamed without changes.
2 changes: 1 addition & 1 deletion examples/resources/tui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl SledTerminalDisplay {
});

self.terminal.draw(|frame| {
frame.render_widget(canvas, frame.size());
frame.render_widget(canvas, frame.area());
})?;

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion examples/ripples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use resources::tui::SledTerminalDisplay;
use sled::{scheduler::Scheduler, Sled};

fn main() {
let sled = Sled::new("./examples/resources/config.toml").unwrap();
let sled = Sled::new("./examples/resources/config.yap").unwrap();
let mut display = SledTerminalDisplay::start("Ripples", sled.domain());
let mut driver = ripples::build_driver();
driver.mount(sled);
Expand Down
2 changes: 1 addition & 1 deletion examples/warpspeed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use resources::tui::SledTerminalDisplay;
use sled::{color::Rgb, scheduler::Scheduler, Sled};

fn main() {
let sled = Sled::new("./examples/resources/config.toml").unwrap();
let sled = Sled::new("./examples/resources/config.yap").unwrap();
let mut display = SledTerminalDisplay::start("Warpspeed", sled.domain());
let mut driver = warpspeed::build_driver();
driver.mount(sled);
Expand Down
164 changes: 127 additions & 37 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,145 @@
use crate::error::SledError;
use glam::Vec2;
use serde::{Deserialize, Deserializer, Serialize};
use smallvec::SmallVec;
use std::fs;
use std::{fs, str::Lines};

use std::sync::OnceLock;

static DEFAULT_DENSITY: OnceLock<f32> = OnceLock::new();

#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct Config {
pub center_point: Vec2,
#[serde(rename = "density")]
#[serde(deserialize_with = "Config::set_default_density")]
pub default_density: f32,
#[serde(rename = "line_segment")]
pub density: f32,
pub line_segments: Vec<LineSegment>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
fn extract_center_and_density_from_lines(lines: &mut Lines) -> (Option<Vec2>, Option<f32>) {
let mut center: Option<Vec2> = None;
let mut density: Option<f32> = None;
let mut segment_marker_found = false;
loop {
if let Some(top_line) = lines.next() {
let trimmed = top_line.trim();
if trimmed.starts_with("--segments--") {
segment_marker_found = true;
break;
} else if trimmed.starts_with("center:") {
center = Some(get_center_from_line(top_line));
} else if trimmed.starts_with("density:") {
density = Some(get_density_from_line(top_line));
}
} else {
break;
}
}

if !segment_marker_found {
panic!("Error parsing config file: no segment marker of form `--segments--` found.")
}

return (center, density);
}

fn get_center_from_line(line: &str) -> Vec2 {
let colon_pos = line.find(':').unwrap();
parse_string_to_vec2(&line[(colon_pos + 1)..line.len()].trim())
}

fn get_density_from_line(line: &str) -> f32 {
let colon_pos = line.find(':').unwrap();
line[(colon_pos + 1)..line.len()].trim().parse().unwrap()
}

fn parse_string_to_vec2(s: &str) -> Vec2 {
if s.starts_with('(') & s.ends_with(')') {
let sub: &str = &s[1..(s.len() - 1)];
let nums: Vec<f32> = sub
.split(',')
.map(|s| {
s.trim().parse().expect(&format!(
"Error parsing config file: malformed Vec2: `{}`",
s
))
})
.collect();
if !nums.len() == 2 {
panic!("Error parsing config file: malformed Vec2: {}", s);
}
return Vec2::new(nums[0], nums[1]);
} else {
panic!("Error parsing config file: malformed Vec2: `{}`", s);
}
}

fn lines_to_string(lines: &mut Lines) -> String {
let mut composite = String::from("");

loop {
if let Some(top_line) = lines.next() {
composite += top_line.trim();
} else {
break;
}
}

composite
}

fn extract_segments_from_string(s: &String) -> Vec<LineSegment> {
let connected: Vec<&str> = s.split("|").collect();
let mut segments: Vec<LineSegment> = vec![];
for sequence in connected {
let vertex_strings: Vec<&str> = sequence.split("-->").map(|s| s.trim()).collect();
let mut last_vertex: Option<Vec2> = None;
for vertex_string in vertex_strings {
let vertex = parse_string_to_vec2(vertex_string);
if let Some(lv) = last_vertex {
segments.push(LineSegment {
start: lv,
end: vertex,
});
}
last_vertex = Some(vertex);
}
}

return segments;
}

impl Config {
pub fn from_toml_file(path: &str) -> Result<Self, SledError> {
let as_string = fs::read_to_string(path).map_err(SledError::from_error)?;
let mut lines = as_string.lines();

let (center, density) = extract_center_and_density_from_lines(&mut lines);

if center.is_none() {
return Err(SledError::new(
"Error parsing config file: no center point descriptor found.".to_string(),
));
}
if density.is_none() {
return Err(SledError::new(
"Error parsing config file: no density descriptor found.".to_string(),
));
}

let back_to_str = lines_to_string(&mut lines);
let line_segments = extract_segments_from_string(&back_to_str);

Ok(Config {
density: density.unwrap(),
center_point: center.unwrap(),
line_segments,
})
}
}

#[derive(Debug, Clone, Copy)]
pub(crate) struct LineSegment {
pub start: Vec2,
pub end: Vec2,
#[serde(default = "Config::default_density")]
pub density: f32,
}

impl LineSegment {
pub fn num_leds(&self) -> usize {
(self.length() * self.density).round() as usize
pub fn num_leds(&self, density: f32) -> usize {
(self.length() * density).round() as usize
}

pub fn length(&self) -> f32 {
Expand Down Expand Up @@ -97,24 +208,3 @@ impl LineSegment {
(self.start + atob * t, t)
}
}

impl Config {
pub fn from_toml_file(path: &str) -> Result<Self, SledError> {
let file_contents = fs::read_to_string(path).map_err(SledError::from_error)?;
let config = toml::from_str(&file_contents).map_err(SledError::from_error)?;
Ok(config)
}

fn set_default_density<'de, D>(des: D) -> Result<f32, D::Error>
where
D: Deserializer<'de>,
{
let deserialized = f32::deserialize(des).unwrap_or(0.0);
let density = DEFAULT_DENSITY.get_or_init(|| deserialized);
Ok(*density)
}

fn default_density() -> f32 {
*DEFAULT_DENSITY.get().unwrap_or(&0.0)
}
}
Loading

0 comments on commit a1925f0

Please sign in to comment.