Skip to content

Commit 4736001

Browse files
authored
Merge pull request #15 from tamirelazar/feat/feature-triage
Gate experimental features for v0.1.0
2 parents 2dfec58 + 010d1f0 commit 4736001

15 files changed

Lines changed: 218 additions & 77 deletions

File tree

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ jobs:
102102
- name: Run cargo test
103103
run: cargo test --verbose
104104

105+
# Gated feature code (multi-species, audio, gui) is not compiled by the
106+
# default build; run lib tests with all features so refactors cannot
107+
# silently break it. Linux only: deps are installed above and the full
108+
# GUI stack is too expensive to build on the paid runners.
109+
- name: Run cargo test (all features, lib)
110+
if: runner.os == 'Linux'
111+
run: cargo test --all-features --lib
112+
105113
licenses:
106114
name: License Check
107115
needs: hygiene

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ gui = ["dep:iced", "dep:iced_term"]
2525
terminal = ["dep:crossterm", "dep:signal-hook", "dep:memory-stats", "dep:thiserror"]
2626
# audio (choir mode) opt-in: cargo build --features audio
2727
audio = ["dep:cpal"]
28+
# multi-species is experimental: cargo build --features multi-species
29+
multi-species = []
2830

2931
[dependencies]
3032
crossterm = { version = "0.27", optional = true }

README.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,6 @@ Choose from 12+ built-in palettes for different moods:
4242
|:----:|:-----:|:----:|:-----:|
4343
| ![Heat](assets/demos/heat_small.gif) | ![Ocean](assets/demos/ocean_small.gif) | ![Neon](assets/demos/neon_small.gif) | ![Slime](assets/demos/slime_small.gif) |
4444

45-
### Special Effects
46-
47-
**Multi-species mode** - Different agents with distinct colors:
48-
49-
![Species Demo](assets/demos/species.gif)
50-
5145
---
5246

5347
## 🌿 Overview
@@ -271,7 +265,6 @@ In live or screensaver mode, you can adjust simulation parameters in real-time u
271265
| `r` | Restart with same seed |
272266
| `+` / `-` | Adjust time scale (0.5x - 4.0x) |
273267
| `c` | Cycle to next palette (Shift+C for reverse) |
274-
| `d` | Toggle dithering |
275268
| `h` | Toggle help overlay (quick → options → none) |
276269
| `q` / `Esc` | Quit |
277270

@@ -586,6 +579,21 @@ cargo test --test visual_regression
586579

587580
---
588581

582+
## Experimental — help wanted
583+
584+
Features that exist but aren't ready for the default experience. Each has a
585+
tracking issue describing current state and what's needed — contributions welcome.
586+
587+
| Feature | Try it | Issue |
588+
|---|---|---|
589+
| Multi-species simulation | `cargo install tslime --features multi-species`, then `--species 'red:20k:ff0000' --species 'blue:20k:0000ff' --species-colors` | #8 |
590+
| Choir mode (audio) | `cargo install tslime --features audio`, then `--choir` | #9 |
591+
| GUI mode | `cargo build --features gui` | #10 |
592+
| WASM build | `tslime-wasm/` (standalone crate) | #11 |
593+
| Dithering | hidden flags: `--dither-mode ordered` (and `d`/`D`/`[`/`]` keys once enabled) | #12 |
594+
595+
---
596+
589597
## 🤝 Contributing
590598

591599
Contributions are welcome! Areas of interest:
@@ -596,6 +604,9 @@ Contributions are welcome! Areas of interest:
596604
- **Documentation**: Examples, tutorials, translations
597605
- **Optimization**: SIMD, parallelization, memory improvements
598606

607+
All current presets are experimental — feedback on which work, which don't,
608+
and recommendations for new ones are very welcome.
609+
599610
### Development Workflow
600611

601612
1. Fork the repository

src/app/explanations.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,16 @@ pub fn print_parameter_explanations() {
9999
println!(" • clusters: Multiple random clusters");
100100
println!(" • food: Load from image (see --food)");
101101

102-
println!("\n --species <SPEC>");
103-
println!(" Define multiple species with different behaviors.");
104-
println!(" Format: 'name:count@sensor_angle,rotation_angle,step_size,deposit:color'");
105-
println!(
106-
" Example: --species 'red:20k@22.5,45,1.0,5.0:ff0000,blue:30k@30,60,1.5,3.0:0000ff'"
107-
);
108-
println!(" Enables multi-species simulations with distinct movement patterns.");
102+
#[cfg(feature = "multi-species")]
103+
{
104+
println!("\n --species <SPEC>");
105+
println!(" Define multiple species with different behaviors.");
106+
println!(" Format: 'name:count@sensor_angle,rotation_angle,step_size,deposit:color'");
107+
println!(
108+
" Example: --species 'red:20k@22.5,45,1.0,5.0:ff0000,blue:30k@30,60,1.5,3.0:0000ff'"
109+
);
110+
println!(" Enables multi-species simulations with distinct movement patterns.");
111+
}
109112

110113
println!("\n\nENVIRONMENTAL FORCES");
111114
println!("─────────────────────────────────────────────────────────────────────────");
@@ -167,7 +170,7 @@ pub fn print_parameter_explanations() {
167170
println!(" • Default (400×200): Good balance");
168171
println!(" • Larger (800×400): Slower, more detail");
169172

170-
println!("\n --dither-mode <MODE> (default: none)");
173+
println!("\n --dither-mode <MODE> (default: none) [dev-only]");
171174
println!(" Dithering algorithm for color quantization.");
172175
println!(" • none: No dithering");
173176
println!(" • ordered: Bayer matrix ordered dithering");
@@ -242,8 +245,11 @@ pub fn print_parameter_explanations() {
242245
println!("\n # Chaotic exploration");
243246
println!(" tslime --sensor-angle 45 --rotation-angle 60 --sensor-distance 15");
244247

245-
println!("\n # Multi-species competition");
246-
println!(" tslime --species 'red:20k:ff0000,blue:20k:0000ff' --separate-species-trails");
248+
#[cfg(feature = "multi-species")]
249+
{
250+
println!("\n # Multi-species competition");
251+
println!(" tslime --species 'red:20k:ff0000,blue:20k:0000ff' --separate-species-trails");
252+
}
247253

248254
println!("\n # Wind-driven river pattern");
249255
println!(" tslime --preset river --wind 0.3,0.0");

src/app/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ pub fn print_mode(
477477
config.max_brightness
478478
};
479479

480-
let species_rgb_colors = if args.species_colors {
480+
let species_rgb_colors = if args.species_colors_enabled() {
481481
Some(extract_species_rgb_colors(&config))
482482
} else {
483483
None
@@ -502,7 +502,7 @@ pub fn print_mode(
502502
dither_mode,
503503
&mut None,
504504
intensity_mapping.as_ref(),
505-
args.species_colors,
505+
args.species_colors_enabled(),
506506
species_rgb_colors,
507507
background_color,
508508
args.ascii_contrast,
@@ -637,7 +637,7 @@ pub fn capture_frames_mode(
637637
config.max_brightness
638638
};
639639

640-
let species_rgb_colors = if args.species_colors {
640+
let species_rgb_colors = if args.species_colors_enabled() {
641641
Some(extract_species_rgb_colors(&config))
642642
} else {
643643
None
@@ -660,7 +660,7 @@ pub fn capture_frames_mode(
660660
args.dither_mode().unwrap_or(DitherMode::None),
661661
&mut None,
662662
intensity_mapping.as_ref(),
663-
args.species_colors,
663+
args.species_colors_enabled(),
664664
species_rgb_colors,
665665
background_color,
666666
args.ascii_contrast,
@@ -829,7 +829,7 @@ pub fn export_gif_mode(
829829
config.max_brightness
830830
};
831831

832-
let species_rgb_colors = if args.species_colors {
832+
let species_rgb_colors = if args.species_colors_enabled() {
833833
Some(extract_species_rgb_colors(&config))
834834
} else {
835835
None
@@ -852,7 +852,7 @@ pub fn export_gif_mode(
852852
args.dither_mode().unwrap_or(DitherMode::None),
853853
&mut None,
854854
intensity_mapping.as_ref(),
855-
args.species_colors,
855+
args.species_colors_enabled(),
856856
species_rgb_colors,
857857
background_color,
858858
args.ascii_contrast,
@@ -957,7 +957,7 @@ pub fn export_webm_mode(
957957
config.max_brightness
958958
};
959959

960-
let species_rgb_colors = if args.species_colors {
960+
let species_rgb_colors = if args.species_colors_enabled() {
961961
Some(extract_species_rgb_colors(&config))
962962
} else {
963963
None
@@ -980,7 +980,7 @@ pub fn export_webm_mode(
980980
args.dither_mode().unwrap_or(DitherMode::None),
981981
&mut None,
982982
intensity_mapping.as_ref(),
983-
args.species_colors,
983+
args.species_colors_enabled(),
984984
species_rgb_colors,
985985
background_color,
986986
args.ascii_contrast,

src/app/runner.rs

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ pub fn run_simulation(
209209
);
210210
let dither_mode = args.dither_mode().unwrap_or(DitherMode::None);
211211
renderer.set_dither_mode(dither_mode);
212+
// Dither is dev-only for v0.1.0: runtime keys work only when it was
213+
// explicitly enabled at startup via the (hidden) CLI flags.
214+
let dither_unlocked = !matches!(dither_mode, DitherMode::None);
212215
renderer.set_ascii_contrast(args.ascii_contrast);
213216
renderer.set_window_frame(config.window_frame);
214217
let mut timer = FrameTimer::with_time_scale(args.fps, args.frame_delay, args.time_scale);
@@ -330,7 +333,7 @@ pub fn run_simulation(
330333
}
331334

332335
// let config = args.to_sim_config().unwrap(); // Already parsed above
333-
if args.species_colors {
336+
if args.species_colors_enabled() {
334337
let species_rgb_colors = extract_species_rgb_colors(&config);
335338
renderer.set_species_colors(true, species_rgb_colors);
336339
}
@@ -411,13 +414,6 @@ pub fn run_simulation(
411414
} else {
412415
None
413416
};
414-
#[cfg(not(feature = "audio"))]
415-
{
416-
if args.choir {
417-
eprintln!("--choir requires building with --features audio");
418-
}
419-
}
420-
421417
loop {
422418
if is_shutdown_requested() {
423419
break;
@@ -1113,7 +1109,7 @@ pub fn run_simulation(
11131109
});
11141110
}
11151111

1116-
if args.species_colors && sim.config().separate_species_trails {
1112+
if args.species_colors_enabled() && sim.config().separate_species_trails {
11171113
let species_trail_maps = sim.trail_maps_for_species_colors();
11181114
let species_rgb_colors = extract_species_rgb_colors(&current_config);
11191115
let combined: Vec<_> = species_trail_maps
@@ -1582,16 +1578,37 @@ pub fn run_simulation(
15821578
));
15831579
}
15841580
ControlAction::ToggleDither => {
1585-
runtime_state.toggle_dither();
1586-
renderer.set_dither_mode(runtime_state.dither_mode);
1581+
if dither_unlocked {
1582+
runtime_state.toggle_dither();
1583+
renderer.set_dither_mode(runtime_state.dither_mode);
1584+
} else {
1585+
runtime_state.show_notification(
1586+
"Dither is dev-only - see help-wanted issues on GitHub"
1587+
.to_string(),
1588+
);
1589+
}
15871590
}
15881591
ControlAction::CycleDitherMode => {
1589-
runtime_state.cycle_dither_mode();
1590-
renderer.set_dither_mode(runtime_state.dither_mode);
1592+
if dither_unlocked {
1593+
runtime_state.cycle_dither_mode();
1594+
renderer.set_dither_mode(runtime_state.dither_mode);
1595+
} else {
1596+
runtime_state.show_notification(
1597+
"Dither is dev-only - see help-wanted issues on GitHub"
1598+
.to_string(),
1599+
);
1600+
}
15911601
}
15921602
ControlAction::AdjustDitherIntensity(delta) => {
1593-
runtime_state.adjust_dither_intensity(delta);
1594-
renderer.set_dither_mode(runtime_state.dither_mode);
1603+
if dither_unlocked {
1604+
runtime_state.adjust_dither_intensity(delta);
1605+
renderer.set_dither_mode(runtime_state.dither_mode);
1606+
} else {
1607+
runtime_state.show_notification(
1608+
"Dither is dev-only - see help-wanted issues on GitHub"
1609+
.to_string(),
1610+
);
1611+
}
15951612
}
15961613
ControlAction::ToggleKeyboardHints => {
15971614
runtime_state.toggle_keyboard_hints();
@@ -2074,35 +2091,26 @@ pub fn run_simulation(
20742091
renderer.set_window_layout(None);
20752092
}
20762093
}
2094+
#[cfg(feature = "audio")]
20772095
ControlAction::ToggleChoir => {
2078-
#[cfg(feature = "audio")]
2079-
{
2080-
if choir.is_some() {
2081-
choir = None;
2082-
runtime_state.show_notification("Choir mode: off".to_string());
2083-
} else {
2084-
match crate::audio::Choir::try_new(
2085-
args.choir_volume.clamp(0.0, 1.0),
2086-
) {
2087-
Ok(c) => {
2088-
choir = Some(c);
2089-
runtime_state
2090-
.show_notification("Choir mode: on".to_string());
2091-
}
2092-
Err(e) => {
2093-
runtime_state.show_notification(format!(
2094-
"Choir init failed: {e}"
2095-
));
2096-
}
2096+
if choir.is_some() {
2097+
choir = None;
2098+
runtime_state.show_notification("Choir mode: off".to_string());
2099+
} else {
2100+
match crate::audio::Choir::try_new(
2101+
args.choir_volume.clamp(0.0, 1.0),
2102+
) {
2103+
Ok(c) => {
2104+
choir = Some(c);
2105+
runtime_state
2106+
.show_notification("Choir mode: on".to_string());
2107+
}
2108+
Err(e) => {
2109+
runtime_state
2110+
.show_notification(format!("Choir init failed: {e}"));
20972111
}
20982112
}
20992113
}
2100-
#[cfg(not(feature = "audio"))]
2101-
{
2102-
runtime_state.show_notification(
2103-
"Choir requires --features audio".to_string(),
2104-
);
2105-
}
21062114
}
21072115
ControlAction::None => {}
21082116
}
@@ -2287,7 +2295,7 @@ pub fn run_simulation(
22872295
};
22882296

22892297
// Re-render with updated pause state
2290-
if args.species_colors && sim.config().separate_species_trails {
2298+
if args.species_colors_enabled() && sim.config().separate_species_trails {
22912299
let species_trail_maps = sim.trail_maps_for_species_colors();
22922300
let species_rgb_colors = extract_species_rgb_colors(&current_config);
22932301
let combined: Vec<_> = species_trail_maps

0 commit comments

Comments
 (0)