Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 123 additions & 13 deletions src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct GuiController {
egui_glium: EguiGlium,
model_selection: ModelSelection,
lsystem_config: LSystemConfig,
preset_selection: PresetSelection,
shading_mode: ShadingMode,
interpolation_color_low: [f32; 3],
interpolation_color_high: [f32; 3],
Expand All @@ -22,17 +23,65 @@ pub struct LSystemConfig {
pub production_rules: Vec<(char, String)>,
pub n_iterations: u32,
pub angle: f32,
pub fractal_height: f32, // TODO move elsewhere, now leaving here for convenience with App update logic
pub fractal_height: f32,
}

impl Default for LSystemConfig {
fn default() -> Self {
Self {
axiom: "F".to_string(),
production_rules: vec![('F', "F[+F][&F][\\F]F[-F][^F][/F]F".to_string())],
n_iterations: 3,
angle: 25.0,
fractal_height: 3.0,
PresetSelection::Tree3D.to_config()
}
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum PresetSelection {
Tree3D,
Bush,
Seaweed,
TrunkTree,
Custom,
}

impl PresetSelection {
pub fn to_config(self) -> LSystemConfig {
match self {
PresetSelection::Tree3D => LSystemConfig {
axiom: "F".to_string(),
production_rules: vec![('F', "F[+F][&F][\\F]F[-F][^F][/F]F".to_string())],
n_iterations: 3,
angle: 25.0,
fractal_height: 3.0,
},
PresetSelection::Bush => LSystemConfig {
axiom: "F".to_string(),
production_rules: vec![('F', "FF[++F][-F][&F][^F]".to_string())],
n_iterations: 4,
angle: 22.5,
fractal_height: 3.0,
},
PresetSelection::Seaweed => LSystemConfig {
axiom: "F".to_string(),
production_rules: vec![('F', "F[+F]F[-F][F]".to_string())],
n_iterations: 4,
angle: 20.0,
fractal_height: 3.0,
},
PresetSelection::TrunkTree => LSystemConfig {
axiom: "FX".to_string(),
production_rules: vec![
('X', "[+FX][-FX][&FX][^FX][\\FX][/FX]".to_string()),
('F', "FF".to_string()),
],
n_iterations: 3,
angle: 28.0,
fractal_height: 4.0,
},
PresetSelection::Custom => LSystemConfig {
axiom: "F".to_string(),
production_rules: vec![('F', "F".to_string())],
n_iterations: 1,
angle: 25.0,
fractal_height: 3.0,
},
}
}
}
Expand Down Expand Up @@ -71,6 +120,7 @@ impl GuiController {
model_selection: ModelSelection::Cylinder,
shading_mode: ShadingMode::Phong,
lsystem_config: LSystemConfig::default(),
preset_selection: PresetSelection::Tree3D,
interpolation_color_low: [0.28, 0.14, 0.01],
interpolation_color_high: [0.08, 0.2, 0.01],
}
Expand Down Expand Up @@ -119,20 +169,79 @@ impl GuiController {
ui.add(egui::Slider::new(fractal_height, 0.1..=5.0).text("Fractal Height"));
}

// TODO make editable
fn ui_lsystem_config(lsystem_config: &mut LSystemConfig, ui: &mut Ui) {
fn ui_lsystem_config(
lsystem_config: &mut LSystemConfig,
preset_selection: &mut PresetSelection,
ui: &mut Ui,
) {
ui.label("LSystem Preset:");
let old_preset = *preset_selection;
egui::ComboBox::from_label("Preset")
.selected_text(format!("{preset_selection:?}"))
.show_ui(ui, |ui| {
ui.selectable_value(preset_selection, PresetSelection::Tree3D, "Tree 3D");
ui.selectable_value(preset_selection, PresetSelection::Bush, "Bush");
ui.selectable_value(preset_selection, PresetSelection::Seaweed, "Seaweed");
ui.selectable_value(preset_selection, PresetSelection::TrunkTree, "Trunk Tree");
ui.selectable_value(preset_selection, PresetSelection::Custom, "Custom");
});

if old_preset != *preset_selection && *preset_selection != PresetSelection::Custom {
*lsystem_config = preset_selection.to_config();
}

ui.separator();
ui.label("LSystem Config:");
ui.add(
egui::Slider::new(&mut lsystem_config.n_iterations, 0..=6).text("Number of Iterations"),
);
ui.add(egui::Slider::new(&mut lsystem_config.angle, 0.0..=45.0).text("Angle"));
ui.label(format!("{:?}", lsystem_config.axiom));

ui.horizontal(|ui| {
ui.label("Axiom:");
if ui.text_edit_singleline(&mut lsystem_config.axiom).changed() {
*preset_selection = PresetSelection::Custom;
}
});

ui.label("Production Rules:");
for (i, (symbol, replacement)) in lsystem_config.production_rules.iter().enumerate() {

let mut rules_changed = false;
let mut to_remove = None;

for (i, (symbol, replacement)) in lsystem_config.production_rules.iter_mut().enumerate() {
ui.horizontal(|ui| {
ui.label(format!("{i}: {symbol} -> {replacement}"));
let mut symbol_str = symbol.to_string();
ui.label(format!("{i}:"));
if ui.text_edit_singleline(&mut symbol_str).changed()
&& let Some(c) = symbol_str.chars().next()
{
*symbol = c;
rules_changed = true;
}
ui.label("->");
if ui.text_edit_singleline(replacement).changed() {
rules_changed = true;
}
if ui.button("❌").clicked() {
to_remove = Some(i);
rules_changed = true;
}
});
}

if let Some(idx) = to_remove {
lsystem_config.production_rules.remove(idx);
}

if ui.button("➕ Add Rule").clicked() {
lsystem_config.production_rules.push(('X', "X".to_string()));
rules_changed = true;
}

if rules_changed {
*preset_selection = PresetSelection::Custom;
}
}

fn ui_color_panel(
Expand All @@ -151,6 +260,7 @@ impl GuiController {
let model_selection = &mut self.model_selection;
let shading_mode = &mut self.shading_mode;
let lsystem_config = &mut self.lsystem_config;
let preset_selection = &mut self.preset_selection;
let color_low = &mut self.interpolation_color_low;
let color_high = &mut self.interpolation_color_high;

Expand All @@ -159,7 +269,7 @@ impl GuiController {
GuiController::ui_control_panel(model_selection, shading_mode, ui);
GuiController::ui_fractal_height(&mut lsystem_config.fractal_height, ui);
ui.separator();
GuiController::ui_lsystem_config(lsystem_config, ui);
GuiController::ui_lsystem_config(lsystem_config, preset_selection, ui);
ui.separator();
GuiController::ui_color_panel(color_low, color_high, ui);
});
Expand Down
4 changes: 2 additions & 2 deletions src/turtle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ impl TurtleInterpreter {
fn parse_input(input: &str) -> Vec<TurtleCommand> {
input
.chars()
.map(|c| TurtleCommand::try_from(c).unwrap())
.filter_map(|c| TurtleCommand::try_from(c).ok())
.collect()
}

Expand Down Expand Up @@ -145,7 +145,7 @@ impl TryFrom<char> for TurtleCommand {
'/' => Ok(TurtleCommand::RollRight),
'[' => Ok(TurtleCommand::PushState),
']' => Ok(TurtleCommand::PopState),
_ => Err(format!("Invalid turtle command {value}")),
_ => Err(format!("Ignoring non-drawing symbol: {value}")),
}
}
}
Expand Down
Loading