1

I am trying to learn Rust by creating animations using Nannou. The animation consists of circles that move according to a mathematical function, and I want to add a slider to the GUI to control the number of circles by following this code

I have created the slider and added it, and I can see that the slider value is changing when I move it. However, the problem is that the value of num_circles in my fn model is not being updated when I move the slider, and I can't link it to the slider.

In other words, I couldn't link the num_circles on the fn model but I can link it to fn update or fn veiw functions.

TLDR: I know my code is long but please don't worry about it. I have a for loop inside the fn model function that uses a constant value num_circles. I want to be able to change this value using a slider, but so far I have only been able to update it in the fn update or fn view functions. Is there a way to link the value of num_circles in fn model to the slider as well?

for i in 0..num_circles 

Here is my code:

use nannou::prelude::*;
use nannou_egui::{self, egui, Egui};
const num_circles: usize = 255;

fn main() {
    nannou::app(model).update(update).run();
}

pub struct Model {
    frequency: f32,
    amplitude: f32,
    phase: f32,
    num_circles: usize,
    num_points: usize,
    circle_points: Vec<Vec<Point2>>,
    settings: Settings,
    egui: Egui,
}

pub struct Settings {
    num_circles: usize,
}

fn model(app: &App) -> Model {
    let window_id = app
        .new_window()
        .view(view)
        .raw_event(raw_window_event)
        .build()
        .unwrap();
    let window = app.window(window_id).unwrap();
    let egui = Egui::from_window(&window);
    let frequency = 125.0;
    let amplitude = 1.4;
    let phase = 1.0;
    let num_points = 155;
    let window_rect = app.window_rect();
    let center = window_rect.xy();
    let radius = window_rect.w().min(window_rect.h()) / 2.0;
    let circle_radius = radius / (num_circles as f32);
    let mut circle_points = Vec::with_capacity(num_circles);

    for i in 0..num_circles {
        let mut points = Vec::with_capacity(num_points);
        for j in 0..num_points {
            let angle = j as f32 * 4.0 * PI / (num_points as f32);
            let x = center.x + angle.sin() * circle_radius * (i as f32 + 2.0);
            let y = center.y + angle.cos() * circle_radius * (i as f32 + 2.0);
            points.push(pt2(x, y));
        }
        circle_points.push(points);
    }
    Model {
        frequency,
        amplitude,
        phase,
        num_circles,
        num_points,
        circle_points,
        egui,
        settings: Settings { num_circles: 255 },
    }
}

fn update(_app: &App, model: &mut Model, _update: Update) {
    let egui = &mut model.egui;
    let settings = &model.settings;

    egui.set_elapsed_time(_update.since_start);
    let ctx = egui.begin_frame();
    egui::Window::new("Settings").show(&ctx, |ui| {
        ui.label("Num Circles:");
        ui.add(egui::Slider::new(&mut model.settings.num_circles, 1..=255));
    });
    for i in 0..model.num_circles {
        for j in 0..model.num_points {
            let x = model.circle_points[i][j].x
                + (-25.0 * PI * model.frequency * j as f32 / model.num_points as f32 + model.phase)
                    .sin()
                    * model.amplitude;
            let y = model.circle_points[i][j].y
                + (25.0 * PI * model.frequency * j as f32 / model.num_points as f32 + model.phase)
                    .cos()
                    * model.amplitude;
            model.circle_points[i][j] = pt2(x, y);
        }
    }
    model.phase += 0.01;
}

fn raw_window_event(_app: &App, model: &mut Model, event: &nannou::winit::event::WindowEvent) {
    model.egui.handle_raw_event(event);
}

fn view(app: &App, model: &Model, frame: Frame) {
    let settings = &model.settings;
    let draw = app.draw();
    draw.background().color(WHITE);
    for i in 0..settings.num_circles {
        let hue = i as f32 / settings.num_circles as f32;
        let color = hsla(hue, 1.0, 0.5, 1.0);
        draw.polyline()
            .weight(1.5)
            .points(model.circle_points[i].clone())
            .color(color);
    }
    draw.to_frame(app, &frame).unwrap();
    model.egui.draw_to_frame(&frame).unwrap();
}

The problem is that num_circles in fn model always stays 255 and doesn't change. How can I make the values here change with the slider?

I would appreciate any help on how to properly update the "num_circles" field in my struct Model with the slider value.

When I use model.settings.num_circles in the model function it raises an error and it's not working.

cafce25
  • 15,907
  • 4
  • 25
  • 31
AltunE
  • 375
  • 3
  • 10

1 Answers1

1

The point of the model() function is to create the inital Model. It's only called once when the program starts. Therefore it doesn't make sense to update num_circles within the model() function, because the function isn't called again.

There is one aspect of this code that is confusing; You have three distinct bindings all with the name num_circles:

  1. const num_circles: usize = 255;. Constants in Rust are uppercase by convention. If this is meant to represent the initial number of circles, a good name would be INITIAL_NUM_CIRCLES.

  2. Model -> num_circles field.

  3. Model -> settings -> num_circles field.

Perhaps you only meant to store one num_circles in your model?

As for how do you update the Model's num_circles field with the slider value: you are already doing that on this line (at least, you are updating one of your num_circles, because as I mentioned you have two of them in your Model):

ui.add(egui::Slider::new(&mut model.settings.num_circles, 1..=255));

You're providing the slider function with a mutable reference to num_circles. It will take care of writing the updated slider value into that reference.

Brian Bowman
  • 892
  • 6
  • 9
  • Thank you for your help your time and insight! I appreciate your patience and understanding with my code. I now understand that the model() function is meant to create the initial model and is only called once when the program starts. Therefore, it doesn't make sense to update the num_circles value within the model() function. I fixed my issue, thank you so much again. (note: The code I shared with you is my latest experiment so sorry for the confusion.) – AltunE Apr 05 '23 at 11:09