Compare commits
	
		
			2 Commits
		
	
	
		
			b730f7640c
			...
			7c01b355a9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7c01b355a9 | |||
| a23a328e50 | 
							
								
								
									
										6
									
								
								.cargo/config.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.cargo/config.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| # clipboard api is still unstable, so web-sys requires the below flag to be passed for copy (ctrl + c) to work | ||||
| # https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html | ||||
| # check status at https://developer.mozilla.org/en-US/docs/Web/API/Clipboard#browser_compatibility | ||||
| # we don't use `[build]` because of rust analyzer's build cache invalidation https://github.com/emilk/eframe_template/issues/93 | ||||
| [target.wasm32-unknown-unknown] | ||||
| rustflags = ["--cfg=web_sys_unstable_apis"] | ||||
							
								
								
									
										29
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,22 +1,13 @@ | ||||
| 
 | ||||
| .obsidian/* | ||||
| .vscode/* | ||||
| 
 | ||||
| # ---> Rust | ||||
| # Generated by Cargo | ||||
| # will have compiled files and executables | ||||
| debug/ | ||||
| target/ | ||||
| 
 | ||||
| # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries | ||||
| # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html | ||||
| Cargo.lock | ||||
| 
 | ||||
| # These are backup files generated by rustfmt | ||||
| **/*.rs.bk | ||||
| 
 | ||||
| # MSVC Windows builds of rustc generate these, which store debugging information | ||||
| *.pdb | ||||
| # Mac stuff: | ||||
| .DS_Store | ||||
| 
 | ||||
| # trunk output folder | ||||
| dist | ||||
| 
 | ||||
| # Rust compile target directories: | ||||
| target | ||||
| target_ra | ||||
| target_wasm | ||||
| 
 | ||||
| # https://github.com/lycheeverse/lychee | ||||
| .lycheecache | ||||
|  | ||||
							
								
								
									
										2416
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2416
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										21
									
								
								Cargo.toml
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										21
									
								
								Cargo.toml
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -1,8 +1,9 @@ | ||||
| [package] | ||||
| name = "egui-circles" | ||||
| version = "0.2.0" | ||||
| name = "egui_circles" | ||||
| version = "0.2.1" | ||||
| authors = ["Andriy Djmil <andriy@djmil.dev>"] | ||||
| edition = "2021" | ||||
| include = ["LICENSE", "**/*.rs", "Cargo.toml"] | ||||
| rust-version = "1.76" | ||||
| 
 | ||||
| [package.metadata.docs.rs] | ||||
| @ -15,13 +16,16 @@ eframe = { version = "0.28", default-features = false, features = [ | ||||
| #   "accesskit",     # Make egui compatible with screen readers. NOTE: adds a lot of dependencies. | ||||
|     "default_fonts", # Embed the default egui fonts. | ||||
|     "glow",          # Use the glow rendering backend. Alternative: "wgpu". | ||||
| #   "persistence",   # Enable restoring app state when restarting the app. | ||||
|     "persistence",   # Enable restoring app state when restarting the app. | ||||
| ] } | ||||
| log = "0.4" | ||||
| emath  = "0.28.0" | ||||
| rand = "0.8" | ||||
| getrandom = { version = "0.2", features = ["js"] } | ||||
| 
 | ||||
| # You only need serde if you want app persistence: | ||||
| serde = { version = "1", features = ["derive"] } | ||||
| 
 | ||||
| # native: | ||||
| [target.'cfg(not(target_arch = "wasm32"))'.dependencies] | ||||
| env_logger = "0.10" | ||||
| @ -40,3 +44,14 @@ opt-level = 2 # fast and small wasm | ||||
| # Optimize all dependencies even in debug builds: | ||||
| [profile.dev.package."*"] | ||||
| opt-level = 2 | ||||
| 
 | ||||
| 
 | ||||
| [patch.crates-io] | ||||
| 
 | ||||
| # If you want to use the bleeding edge version of egui and eframe: | ||||
| # egui = { git = "https://github.com/emilk/egui", branch = "master" } | ||||
| # eframe = { git = "https://github.com/emilk/egui", branch = "master" } | ||||
| 
 | ||||
| # If you fork https://github.com/emilk/egui you can test with: | ||||
| # egui = { path = "../egui/crates/egui" } | ||||
| # eframe = { path = "../egui/crates/eframe" } | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "eGUI Circles", | ||||
|   "short_name": "circles", | ||||
|   "short_name": "egui-circles", | ||||
|   "icons": [ | ||||
|     { | ||||
|       "src": "/icon-192.png", | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| var cacheName = 'egui-template-pwa'; | ||||
| var cacheName = 'egui_circles'; | ||||
| var filesToCache = [ | ||||
|   './', | ||||
|   './index.html', | ||||
|   './eframe_template.js', | ||||
|   './eframe_template_bg.wasm', | ||||
|   './egui_circles.js', | ||||
|   './egui_circles_bg.wasm', | ||||
| ]; | ||||
| 
 | ||||
| /* Start the service worker and cache all of the app's content */ | ||||
|  | ||||
| @ -1,4 +0,0 @@ | ||||
| #!/usr/bin/env bash | ||||
| set -euxo pipefail | ||||
| 
 | ||||
| docker build --tag egui-circles-builder:0.0.1 . | ||||
| @ -1,10 +0,0 @@ | ||||
| #!/usr/bin/env bash | ||||
| set -euo pipefail | ||||
| 
 | ||||
| PROJECT_NAME=egui-circles | ||||
| 
 | ||||
| docker run \ | ||||
| 	--volume $(pwd)/..:/$PROJECT_NAME \ | ||||
| 	--name vsc-$PROJECT_NAME          \ | ||||
| 	-p 8080:8080                      \ | ||||
| 	$PROJECT_NAME-builder:0.0.1 | ||||
							
								
								
									
										24
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								index.html
									
									
									
									
									
								
							| @ -1,9 +1,9 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <meta http-equiv="Content-Type" charset="utf-8" /> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | ||||
| 
 | ||||
| <!-- Disable zooming: --> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1.5, user-scalable=no"> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> | ||||
| 
 | ||||
| <head> | ||||
|     <!-- change this to your project name --> | ||||
| @ -56,15 +56,16 @@ | ||||
|             width: 100%; | ||||
|         } | ||||
| 
 | ||||
|         /* Position canvas in center-top: */ | ||||
|         /* Make canvas fill entire document: */ | ||||
|         canvas { | ||||
|             margin-right: auto; | ||||
|             margin-left: auto; | ||||
|             display: block; | ||||
|             position: absolute; | ||||
|             top: 0%; | ||||
|             left: 50%; | ||||
|             transform: translate(-50%, 0%); | ||||
|             top: 0; | ||||
|             left: 0; | ||||
|             width: 100%; | ||||
|             height: 100%; | ||||
|         } | ||||
| 
 | ||||
|         .centered { | ||||
| @ -110,14 +111,21 @@ | ||||
|                 transform: rotate(360deg); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     </style> | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
|     <!-- The WASM code will resize the canvas dynamically --> | ||||
|     <!-- the id is hardcoded in main.rs . so, make sure both match. --> | ||||
|     <canvas id="the_canvas_id" width="800" height="600"></canvas> | ||||
|     <canvas id="the_canvas_id"></canvas> | ||||
| 
 | ||||
|     <!-- the loading spinner will be removed in main.rs --> | ||||
|     <div class="centered" id="loading_text"> | ||||
|         <p style="font-size:16px"> | ||||
|             Loading… | ||||
|         </p> | ||||
|         <div class="lds-dual-ring"></div> | ||||
|     </div> | ||||
| 
 | ||||
|     <!--Register Service Worker. this will cache the wasm / js scripts for offline use (for PWA functionality). --> | ||||
|     <!-- Force refresh (Ctrl + F5) to load the latest files instead of cached files  --> | ||||
|  | ||||
							
								
								
									
										142
									
								
								src/app.rs
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								src/app.rs
									
									
									
									
									
								
							| @ -1,19 +1,18 @@ | ||||
| extern crate emath; | ||||
| extern crate rand; | ||||
| extern crate eframe; | ||||
| 
 | ||||
| use self::emath::Vec2; | ||||
| use self::eframe::egui; | ||||
| use self::egui::Color32; | ||||
| use self::egui::Stroke; | ||||
| use self::rand::Rng; | ||||
| use egui::{Color32, Stroke, Vec2}; | ||||
| use rand::Rng; | ||||
| 
 | ||||
| use crate::circle::Circle; | ||||
| 
 | ||||
| /// We derive Deserialize/Serialize so we can persist app state on shutdown.
 | ||||
| #[derive(serde::Deserialize, serde::Serialize)] | ||||
| #[serde(default)] // if we add new fields, give them default values when deserializing old state
 | ||||
| 
 | ||||
| pub struct Simulation { | ||||
|     circles: Vec<Circle>, | ||||
|     circles_count: usize, | ||||
| 
 | ||||
|     #[serde(skip)] // This how you opt-out of serialization of a field
 | ||||
|     circles: Vec<Circle>, | ||||
|     #[serde(skip)] | ||||
|     colors: Vec<egui::Color32>, | ||||
| } | ||||
| 
 | ||||
| @ -27,21 +26,87 @@ impl Default for Simulation { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl eframe::App for Simulation { | ||||
|     fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { | ||||
| impl Simulation { | ||||
|     /// Called once before the first frame.
 | ||||
|     pub fn new(cc: &eframe::CreationContext<'_>) -> Self { | ||||
|         // This is also where you can customize the look and feel of egui using
 | ||||
|         // `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.
 | ||||
| 
 | ||||
|         // Looks better on 4k montior
 | ||||
|         ctx.set_pixels_per_point(1.5); | ||||
|         cc.egui_ctx.set_pixels_per_point(1.5); | ||||
| 
 | ||||
|         // Load previous app state (if any).
 | ||||
|         // Note that you must enable the `persistence` feature for this to work.
 | ||||
|         if let Some(storage) = cc.storage { | ||||
|             return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default(); | ||||
|         } | ||||
| 
 | ||||
|         Default::default() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl eframe::App for Simulation { | ||||
|     /// Called by the frame work to save state before shutdown.
 | ||||
|     fn save(&mut self, storage: &mut dyn eframe::Storage) { | ||||
|         eframe::set_value(storage, eframe::APP_KEY, self); | ||||
|     } | ||||
| 
 | ||||
|     /// Called each time the UI needs repainting, which may be many times per second.
 | ||||
|     fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { | ||||
|         // Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`.
 | ||||
|         // For inspiration and more examples, go to https://emilk.github.io/egui
 | ||||
| 
 | ||||
|         egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { | ||||
|             // The top panel is often a good place for a menu bar:
 | ||||
| 
 | ||||
|             egui::menu::bar(ui, |ui| { | ||||
|                 // NOTE: no File->Quit on web pages!
 | ||||
|                 let is_web = cfg!(target_arch = "wasm32"); | ||||
|                 if !is_web { | ||||
|                     ui.menu_button("File", |ui| { | ||||
|                         if ui.button("Quit").clicked() { | ||||
|                             ctx.send_viewport_cmd(egui::ViewportCommand::Close); | ||||
|                         } | ||||
|                     }); | ||||
|                     ui.add_space(16.0); | ||||
|                 } | ||||
| 
 | ||||
|                 egui::widgets::global_dark_light_mode_buttons(ui); | ||||
| 
 | ||||
|                 ui.with_layout(egui::Layout::top_down(egui::Align::RIGHT), |ui| { | ||||
|                     ui.horizontal(|ui| { | ||||
|                         ui.spacing_mut().item_spacing.x = 4.0; | ||||
|                         ui.hyperlink_to( | ||||
|                             "source code", | ||||
|                             "https://gitea.djmil.dev/djmil/egui-circles", | ||||
|                         ); | ||||
|                         ui.label("rust"); | ||||
|                     }); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         egui::CentralPanel::default().show(ctx, |ui| { | ||||
|             if ui.button("halt").clicked() { | ||||
|                 self.circles.iter_mut().for_each(|c| (*c).v = Vec2{x: 0.0, y: 0.0}); | ||||
|             }; | ||||
|             // The central panel the region left after adding TopPanel's and SidePanel's
 | ||||
| 
 | ||||
|             if ui.button("push").clicked() { | ||||
|                 self.circles.iter_mut().for_each(|c| (*c).v = Vec2{ | ||||
|                     x: rand::thread_rng().gen_range(-2.0..2.0), | ||||
|                     y: rand::thread_rng().gen_range(-2.0..2.0)}); | ||||
|             }; | ||||
|             // Simulatiom
 | ||||
|             ui.horizontal(|ui| { | ||||
|                 ui.label("Click on circles or press"); | ||||
| 
 | ||||
|                 if ui.button("push").clicked() { | ||||
|                     self.circles.iter_mut().for_each(|c| (*c).v = Vec2{ | ||||
|                         x: rand::thread_rng().gen_range(-4.0..4.0), | ||||
|                         y: rand::thread_rng().gen_range(-4.0..4.0)}); | ||||
|                 }; | ||||
| 
 | ||||
|                 ui.label("or"); | ||||
| 
 | ||||
|                 if ui.button("halt").clicked() { | ||||
|                     self.circles.iter_mut().for_each(|c| (*c).v = Vec2{x: 0.0, y: 0.0}); | ||||
|                 }; | ||||
| 
 | ||||
|                 ui.label("buttons"); | ||||
|             }); | ||||
| 
 | ||||
|             ui.add(egui::Slider::new(&mut self.circles_count, 0..=25).text("circles count")); | ||||
| 
 | ||||
| @ -53,10 +118,10 @@ impl eframe::App for Simulation { | ||||
|                     self.circles.push(Circle::default()); | ||||
|                     self.colors.push( | ||||
|                         egui::Color32::from_rgba_premultiplied( | ||||
|                             rand::thread_rng().gen_range(0..255), | ||||
|                             rand::thread_rng().gen_range(0..255), | ||||
|                             rand::thread_rng().gen_range(0..255), | ||||
|                             64) | ||||
|                             rand::thread_rng().gen_range(64..255), | ||||
|                             rand::thread_rng().gen_range(64..255), | ||||
|                             rand::thread_rng().gen_range(64..255), | ||||
|                             128) | ||||
|                         ); | ||||
|                 } | ||||
|             } | ||||
| @ -92,9 +157,19 @@ impl eframe::App for Simulation { | ||||
|                     self.circles[i].c, 
 | ||||
|                     self.circles[i].r, 
 | ||||
|                 self.colors[i] /*Color32::TRANSPARENT*/, 
 | ||||
|                     Stroke{width: 2.0, color: Color32::from_rgb(255, 255, 255)} | ||||
|                     Stroke{width: 2.0, color: Color32::from_rgb(200, 255, 255)} | ||||
|                 ); | ||||
|             } | ||||
|             // End simultion
 | ||||
| 
 | ||||
|             ui.separator(); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|             ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| { | ||||
|                 powered_by_egui_and_eframe(ui); | ||||
|                 egui::warn_if_debug_build(ui); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         for circle in &mut self.circles { | ||||
| @ -121,9 +196,22 @@ impl eframe::App for Simulation { | ||||
| 
 | ||||
|         // This is how to go into continuous mode - uncomment this to see example of continuous mode
 | ||||
|         ctx.request_repaint(); | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn powered_by_egui_and_eframe(ui: &mut egui::Ui) { | ||||
|     ui.horizontal(|ui| { | ||||
|         ui.spacing_mut().item_spacing.x = 0.0; | ||||
|         ui.label("Powered by "); | ||||
|         ui.hyperlink_to("egui", "https://github.com/emilk/egui"); | ||||
|         ui.label(" and "); | ||||
|         ui.hyperlink_to( | ||||
|             "eframe", | ||||
|             "https://github.com/emilk/egui/tree/master/crates/eframe", | ||||
|         ); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| fn collision(c1: &Circle, c2: &Circle) -> (Vec2, Vec2) { | ||||
|     let m1 = c1.r; | ||||
| @ -168,5 +256,3 @@ fn collision(c1: &Circle, c2: &Circle) -> (Vec2, Vec2) { | ||||
|         vec2n + vec2t | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| //}
 | ||||
|  | ||||
| @ -1,10 +1,5 @@ | ||||
| extern crate emath; | ||||
| extern crate rand; | ||||
| 
 | ||||
| use self::emath::Vec2; | ||||
| use self::emath::Pos2; | ||||
| use self::emath::Rect; | ||||
| use self::rand::Rng; | ||||
| use egui::{Pos2, Rect, Vec2}; | ||||
| use rand::Rng; | ||||
| 
 | ||||
| pub static FRICTION: f32 = 0.995; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										6
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| #![warn(clippy::all, rust_2018_idioms)] | ||||
| 
 | ||||
| mod app; | ||||
| pub use app::Simulation; | ||||
| 
 | ||||
| mod circle; | ||||
							
								
								
									
										17
									
								
								src/main.rs
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										17
									
								
								src/main.rs
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -1,13 +1,13 @@ | ||||
| mod app; | ||||
| mod circle; | ||||
| #![warn(clippy::all, rust_2018_idioms)] | ||||
| #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
 | ||||
| 
 | ||||
| // When compiling natively:
 | ||||
| #[cfg(not(target_arch = "wasm32"))] | ||||
| fn main() -> eframe::Result<()> { | ||||
|     //env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
 | ||||
| fn main() -> eframe::Result { | ||||
|     env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
 | ||||
| 
 | ||||
|     let native_options = eframe::NativeOptions { | ||||
|         viewport: eframe::egui::ViewportBuilder::default() | ||||
|         viewport: egui::ViewportBuilder::default() | ||||
|             .with_inner_size([800.0, 600.0]) | ||||
|             .with_min_inner_size([300.0, 220.0]) | ||||
|             .with_icon( | ||||
| @ -19,9 +19,9 @@ fn main() -> eframe::Result<()> { | ||||
|     }; | ||||
| 
 | ||||
|     eframe::run_native( | ||||
|         "egui-circles", | ||||
|         "eGUI Circles", | ||||
|         native_options, | ||||
|         Box::new(|_| Ok(Box::<crate::app::Simulation>::default())), | ||||
|         Box::new(|cc| Ok(Box::new(egui_circles::Simulation::new(cc)))), | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| @ -38,8 +38,7 @@ fn main() { | ||||
|             .start( | ||||
|                 "the_canvas_id", | ||||
|                 web_options, | ||||
|                 //Box::new(|cc| Ok(Box::new(eframe_template::TemplateApp::new(cc)))),
 | ||||
|                 Box::new(|_| Ok(Box::<crate::app::Simulation>::default())), | ||||
|                 Box::new(|cc| Ok(Box::new(egui_circles::Simulation::new(cc)))), | ||||
|             ) | ||||
|             .await; | ||||
| 
 | ||||
|  | ||||
| @ -9,4 +9,3 @@ ENTRYPOINT ["trunk"] | ||||
| CMD ["serve"] | ||||
| 
 | ||||
| WORKDIR /egui-circles | ||||
| 
 | ||||
							
								
								
									
										40
									
								
								wasm32/build.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										40
									
								
								wasm32/build.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,40 @@ | ||||
| #!/usr/bin/env bash | ||||
| set -euo pipefail | ||||
| 
 | ||||
| NAME=egui-circles | ||||
| BUILDER=djmil/$NAME-builder:0.0.1 | ||||
| TRAGET=$NAME-wasm32 | ||||
| 
 | ||||
| has_docker_image() { | ||||
|    docker manifest inspect $1 | ||||
| } &> /dev/null | ||||
| 
 | ||||
| has_docker_container() { | ||||
| 	docker container inspect $1 | ||||
| } &> /dev/null | ||||
| 
 | ||||
| if ! has_docker_image $BUILDER; then | ||||
| 	echo "Docker $BUILDER was not found" | ||||
| 	 | ||||
| 	read -p "Would you like to create builder image (y/n)? " | ||||
| 	case "$REPLY" in  | ||||
| 	y|Y ) | ||||
| 		docker build --tag $BUILDER . ;; | ||||
|   	* ) | ||||
| 		exit 1 ;; | ||||
| 	esac | ||||
| fi | ||||
| 
 | ||||
| echo "Initiate WASM32 target build.." | ||||
| 
 | ||||
| if has_docker_container $TRAGET; then | ||||
| 	docker start \ | ||||
| 		$TRAGET \ | ||||
| 		--attach | ||||
| else | ||||
| 	docker run \ | ||||
| 		--volume $(pwd):/$NAME \ | ||||
| 		--name $TRAGET            \ | ||||
| 		$BUILDER \ | ||||
| 		build --release --public-url /$NAME --verbose | ||||
| fi | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user