summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar xkenmon <xkenmon@gmail.com>2021-12-05 20:03:07 +0800
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2022-09-23 17:41:34 +0200
commit79d045cfe1c22cda9cfa6a359322360b0ca97cd8 (patch)
tree4e42b44e980bcaf55591e6553429d959b8f1a696
parent7420ea7a6b80663cad178c1238c5b756232a087f (diff)
downloadiced-79d045cfe1c22cda9cfa6a359322360b0ca97cd8.tar.gz
iced-79d045cfe1c22cda9cfa6a359322360b0ca97cd8.tar.bz2
iced-79d045cfe1c22cda9cfa6a359322360b0ca97cd8.zip
Implement sierpinski-triangle example
-rw-r--r--Cargo.toml1
-rw-r--r--examples/README.md1
-rw-r--r--examples/sierpinski_triangle/Cargo.toml10
-rw-r--r--examples/sierpinski_triangle/README.md16
-rw-r--r--examples/sierpinski_triangle/src/main.rs193
5 files changed, 221 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml
index c93e7245..39a3b5cf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -78,6 +78,7 @@ members = [
"examples/progress_bar",
"examples/qr_code",
"examples/scrollable",
+ "examples/sierpinski_triangle",
"examples/solar_system",
"examples/stopwatch",
"examples/styling",
diff --git a/examples/README.md b/examples/README.md
index 2b4919df..bb15dc2e 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -100,6 +100,7 @@ A bunch of simpler examples exist:
- [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI].
- [`progress_bar`](progress_bar), a simple progress bar that can be filled by using a slider.
- [`scrollable`](scrollable), a showcase of the various scrollbar width options.
+- [`sierpinski_triangle`](sierpinski_triangle), a [sierpiński triangle](https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle) Emulator, use `Canvas` and `Slider`.
- [`solar_system`](solar_system), an animated solar system drawn using the `Canvas` widget and showcasing how to compose different transforms.
- [`stopwatch`](stopwatch), a watch with start/stop and reset buttons showcasing how to listen to time.
- [`svg`](svg), an application that renders the [Ghostscript Tiger] by leveraging the `Svg` widget.
diff --git a/examples/sierpinski_triangle/Cargo.toml b/examples/sierpinski_triangle/Cargo.toml
new file mode 100644
index 00000000..39d45f64
--- /dev/null
+++ b/examples/sierpinski_triangle/Cargo.toml
@@ -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"
diff --git a/examples/sierpinski_triangle/README.md b/examples/sierpinski_triangle/README.md
new file mode 100644
index 00000000..9fd18257
--- /dev/null
+++ b/examples/sierpinski_triangle/README.md
@@ -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
+``` \ No newline at end of file
diff --git a/examples/sierpinski_triangle/src/main.rs b/examples/sierpinski_triangle/src/main.rs
new file mode 100644
index 00000000..1d25d171
--- /dev/null
+++ b/examples/sierpinski_triangle/src/main.rs
@@ -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,
+ )
+ }
+}