Skip to content

Instantly share code, notes, and snippets.

@fenjalien
Created August 6, 2021 17:53
Show Gist options
  • Save fenjalien/bc9fa8ca765470308ea311ea68fb499a to your computer and use it in GitHub Desktop.
Save fenjalien/bc9fa8ca765470308ea311ea68fb499a to your computer and use it in GitHub Desktop.
Generates an SVG file of a Sierpinski triangle when the mouse is pressed.
use std::path::PathBuf;
use nannou::{
draw::renderer::svg::render_and_save,
geom::Tri,
prelude::*,
rand::{prelude::SliceRandom, thread_rng},
};
fn main() {
nannou::app(model).update(update).run();
}
struct Model {
svg_render_frame: u64,
tri: Tri<Vec2>,
points: Vec<Point2>,
}
fn model(app: &App) -> Model {
app.new_window()
.mouse_pressed(mouse_pressed)
.view(view)
.build()
.unwrap();
let win_rect = app.window_rect();
Model {
svg_render_frame: 0,
tri: Tri([
win_rect.mid_top(),
win_rect.bottom_right(),
win_rect.bottom_left(),
]),
points: vec![Point2::ZERO],
}
}
fn mouse_pressed(app: &App, model: &mut Model, button: MouseButton) {
if let MouseButton::Left = button {
model.svg_render_frame = app.elapsed_frames() + 1;
}
}
fn captured_frame_path(app: &App) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!("out_{:03}.svg", app.elapsed_frames()))
}
fn update(_app: &App, model: &mut Model, _update: Update) {
// Calculate and save the next point
model.points.push(
model
.points
.last()
.unwrap()
.lerp(*model.tri.choose(&mut thread_rng()).unwrap(), 0.5),
);
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
// We only need to draw the background and bounding triangle once.
if app.elapsed_frames() == 1 {
draw.background().color(WHITE);
draw_tri(&draw, model.tri);
}
// Draw the latest calculated point
// We don't need to draw all of them as the previous are kept on the frame
draw.ellipse()
.color(BLACK)
.radius(1.0)
.xy(*model.points.last().unwrap());
if model.svg_render_frame == app.elapsed_frames() {
// Redraw bounding triangle and all points as they don't exist in the draw struct
draw_tri(&draw, model.tri);
for point in &model.points {
draw.ellipse().color(BLACK).radius(1.0).xy(*point);
}
render_and_save(app.window_rect(), &draw.clone(), captured_frame_path(app));
} else {
draw.to_frame(app, &frame).unwrap();
}
}
fn draw_tri(draw: &Draw, tri: Tri<Vec2>) {
let points: (Vec2, Vec2, Vec2) = Into::into(tri);
draw.tri()
.no_fill()
.stroke(RED)
.stroke_weight(1.0)
.points(points.0, points.1, points.2);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment