Implement sierpinski-triangle example
This commit is contained in:
parent
7420ea7a6b
commit
79d045cfe1
5 changed files with 221 additions and 0 deletions
10
examples/sierpinski_triangle/Cargo.toml
Normal file
10
examples/sierpinski_triangle/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "sierpinski_triangle"
|
||||
version = "0.1.0"
|
||||
authors = ["xkenmon <xkenmon@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["canvas", "debug"] }
|
||||
rand = "0.8.4"
|
||||
16
examples/sierpinski_triangle/README.md
Normal file
16
examples/sierpinski_triangle/README.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
## Sierpinski Triangle Emulator
|
||||
|
||||
A simple [Sierpiński triangle](https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle) Emulator, use canvas and slider.
|
||||
|
||||
Left-click add fixed point, right-click remove fixed point.
|
||||
|
||||
<div align="center">
|
||||
<a href="https://gfycat.com/flippantrectangularechidna">
|
||||
<img src="https://thumbs.gfycat.com/FlippantRectangularEchidna-size_restricted.gif">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
You can run with cargo:
|
||||
```
|
||||
cargo run --package sierpinski_triangle
|
||||
```
|
||||
193
examples/sierpinski_triangle/src/main.rs
Normal file
193
examples/sierpinski_triangle/src/main.rs
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use iced::executor;
|
||||
use iced::widget::canvas::event::{self, Event};
|
||||
use iced::widget::canvas::{self, Canvas};
|
||||
use iced::widget::{column, row, slider, text};
|
||||
use iced::{
|
||||
Application, Color, Command, Length, Point, Rectangle, Settings, Size,
|
||||
Theme,
|
||||
};
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
fn main() -> iced::Result {
|
||||
SierpinskiEmulator::run(Settings {
|
||||
antialiasing: true,
|
||||
..Settings::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SierpinskiEmulator {
|
||||
graph: SierpinskiGraph,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
IterationSet(i32),
|
||||
PointAdded(Point),
|
||||
PointRemoved,
|
||||
}
|
||||
|
||||
impl Application for SierpinskiEmulator {
|
||||
type Executor = executor::Default;
|
||||
type Message = Message;
|
||||
type Theme = Theme;
|
||||
type Flags = ();
|
||||
|
||||
fn new(_flags: Self::Flags) -> (Self, iced::Command<Self::Message>) {
|
||||
let emulator = SierpinskiEmulator {
|
||||
graph: SierpinskiGraph::new(),
|
||||
};
|
||||
(emulator, Command::none())
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
"Sierpinski Triangle Emulator".to_string()
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Self::Message,
|
||||
) -> iced::Command<Self::Message> {
|
||||
match message {
|
||||
Message::IterationSet(cur_iter) => {
|
||||
self.graph.iteration = cur_iter;
|
||||
}
|
||||
Message::PointAdded(point) => {
|
||||
self.graph.fix_points.push(point);
|
||||
self.graph.random_points.clear();
|
||||
}
|
||||
Message::PointRemoved => {
|
||||
self.graph.fix_points.pop();
|
||||
self.graph.random_points.clear();
|
||||
}
|
||||
}
|
||||
|
||||
self.graph.redraw();
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn view(&self) -> iced::Element<'_, Self::Message> {
|
||||
column![
|
||||
Canvas::new(&self.graph)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
row![
|
||||
text(format!("Iteration: {:?}", self.graph.iteration)),
|
||||
slider(0..=10000, self.graph.iteration, Message::IterationSet)
|
||||
.width(Length::Fill)
|
||||
]
|
||||
.padding(10)
|
||||
.spacing(20),
|
||||
]
|
||||
.width(Length::Fill)
|
||||
.align_items(iced::Alignment::Center)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct SierpinskiGraph {
|
||||
iteration: i32,
|
||||
fix_points: Vec<Point>,
|
||||
random_points: Vec<Point>,
|
||||
cache: canvas::Cache,
|
||||
}
|
||||
|
||||
impl canvas::Program<Message> for SierpinskiGraph {
|
||||
type State = ();
|
||||
|
||||
fn update(
|
||||
&self,
|
||||
_state: &mut Self::State,
|
||||
event: Event,
|
||||
bounds: Rectangle,
|
||||
cursor: canvas::Cursor,
|
||||
) -> (event::Status, Option<Message>) {
|
||||
let cursor_position =
|
||||
if let Some(position) = cursor.position_in(&bounds) {
|
||||
position
|
||||
} else {
|
||||
return (event::Status::Ignored, None);
|
||||
};
|
||||
|
||||
match event {
|
||||
Event::Mouse(mouse_event) => {
|
||||
let message = match mouse_event {
|
||||
iced::mouse::Event::ButtonPressed(
|
||||
iced::mouse::Button::Left,
|
||||
) => Some(Message::PointAdded(cursor_position)),
|
||||
iced::mouse::Event::ButtonPressed(
|
||||
iced::mouse::Button::Right,
|
||||
) => Some(Message::PointRemoved),
|
||||
_ => None,
|
||||
};
|
||||
(event::Status::Captured, message)
|
||||
}
|
||||
_ => (event::Status::Ignored, None),
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Self::State,
|
||||
_theme: &Theme,
|
||||
bounds: Rectangle,
|
||||
_cursor: canvas::Cursor,
|
||||
) -> Vec<canvas::Geometry> {
|
||||
let geom = self.cache.draw(bounds.size(), |frame| {
|
||||
frame.stroke(
|
||||
&canvas::Path::rectangle(Point::ORIGIN, frame.size()),
|
||||
canvas::Stroke::default(),
|
||||
);
|
||||
|
||||
if self.fix_points.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut last = None;
|
||||
|
||||
for _ in 0..self.iteration {
|
||||
let p = self.gen_rand_point(last);
|
||||
let path = canvas::Path::rectangle(p, Size::new(1_f32, 1_f32));
|
||||
|
||||
frame.stroke(&path, canvas::Stroke::default());
|
||||
|
||||
last = Some(p);
|
||||
}
|
||||
|
||||
self.fix_points.iter().for_each(|p| {
|
||||
let path = canvas::Path::circle(*p, 5.0);
|
||||
frame.fill(&path, Color::from_rgb8(0x12, 0x93, 0xD8));
|
||||
});
|
||||
});
|
||||
|
||||
vec![geom]
|
||||
}
|
||||
}
|
||||
|
||||
impl SierpinskiGraph {
|
||||
fn new() -> SierpinskiGraph {
|
||||
SierpinskiGraph::default()
|
||||
}
|
||||
|
||||
fn redraw(&mut self) {
|
||||
self.cache.clear();
|
||||
}
|
||||
|
||||
fn gen_rand_point(&self, last: Option<Point>) -> Point {
|
||||
let dest_point_idx =
|
||||
rand::thread_rng().gen_range(0..self.fix_points.len());
|
||||
|
||||
let dest_point = self.fix_points[dest_point_idx];
|
||||
let cur_point = last.or_else(|| Some(self.fix_points[0])).unwrap();
|
||||
|
||||
Point::new(
|
||||
(dest_point.x + cur_point.x) / 2_f32,
|
||||
(dest_point.y + cur_point.y) / 2_f32,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue