mirror of https://github.com/yewstack/yew
Add boid example (#1540)
* It appears! * boid example works well (#1539) * remove clippy and fmt warnings (#1539) * fix typo and remove vector.add, sub, mul (#1539) * remove triangle * rename the filename * remove fmt errors * define size square function (#1540)
This commit is contained in:
parent
008577ebb4
commit
63b79bf213
|
@ -57,4 +57,5 @@ members = [
|
|||
"examples/todomvc",
|
||||
"examples/two_apps",
|
||||
"examples/webgl",
|
||||
"examples/boids",
|
||||
]
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
Cargo.lock
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "boids"
|
||||
version = "0.1.0"
|
||||
authors = ["motoki saito <stmtk13044032@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
yew = { path = "../../yew" }
|
|
@ -0,0 +1,111 @@
|
|||
use crate::vector::Vector;
|
||||
use rand::Rng;
|
||||
use std::f64::consts::PI;
|
||||
|
||||
const HEIGHT: f64 = 400.0;
|
||||
const WIDTH: f64 = 600.0;
|
||||
const VELOCITY_SIZE: f64 = 5.0;
|
||||
const ALIGNMENT_RADIUS: f64 = 100.0;
|
||||
const ALIGNMENT_WEIGHT: f64 = 3.0;
|
||||
const COHESION_RADIUS: f64 = 200.0;
|
||||
const COHESION_WEIGHT: f64 = 1.0;
|
||||
const SEPARATION_RADIUS: f64 = 50.0;
|
||||
const SEPARATION_WEIGHT: f64 = 1.0;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Boid {
|
||||
pub position: Vector,
|
||||
pub velocity: Vector,
|
||||
}
|
||||
|
||||
impl Boid {
|
||||
pub fn new(rng: &mut rand::rngs::ThreadRng) -> Boid {
|
||||
let theta = rng.gen::<f64>() * PI * 2.0;
|
||||
Boid {
|
||||
position: Vector::new(WIDTH * rng.gen::<f64>(), HEIGHT * rng.gen::<f64>()),
|
||||
velocity: Vector::new(theta.cos() * VELOCITY_SIZE, theta.sin() * VELOCITY_SIZE),
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_alignment(&self, boids: &[Boid]) -> Vector {
|
||||
let mut ret = Vector::new(0.0, 0.0);
|
||||
for other in boids {
|
||||
let mut position = other.position.clone();
|
||||
position -= self.position.clone();
|
||||
let position_size = position.size();
|
||||
if position_size == 0.0 || position_size > ALIGNMENT_RADIUS {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret += other.velocity.clone();
|
||||
}
|
||||
|
||||
ret.normalize();
|
||||
ret *= ALIGNMENT_WEIGHT;
|
||||
ret
|
||||
}
|
||||
|
||||
fn calc_cohesion(&self, boids: &[Boid]) -> Vector {
|
||||
let mut ret = Vector::new(0.0, 0.0);
|
||||
for other in boids {
|
||||
let mut position = other.position.clone();
|
||||
position -= self.position.clone();
|
||||
let position_size = position.size();
|
||||
if position_size == 0.0 || position_size > COHESION_RADIUS {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret += position;
|
||||
}
|
||||
|
||||
ret.normalize();
|
||||
ret *= COHESION_WEIGHT;
|
||||
ret
|
||||
}
|
||||
|
||||
fn calc_separation(&self, boids: &[Boid]) -> Vector {
|
||||
let mut ret = Vector::new(0.0, 0.0);
|
||||
for other in boids {
|
||||
let mut position = other.position.clone();
|
||||
position -= self.position.clone();
|
||||
let position_size = position.size();
|
||||
if position_size == 0.0 || position_size > SEPARATION_RADIUS {
|
||||
continue;
|
||||
}
|
||||
|
||||
position /= position.size_square();
|
||||
ret -= position;
|
||||
}
|
||||
|
||||
ret.normalize();
|
||||
ret *= SEPARATION_WEIGHT;
|
||||
ret
|
||||
}
|
||||
|
||||
fn move_self(&mut self) {
|
||||
self.position += self.velocity.clone();
|
||||
if self.position.x < 0.0 {
|
||||
self.position.x += WIDTH;
|
||||
} else if self.position.x > WIDTH {
|
||||
self.position.x -= WIDTH;
|
||||
}
|
||||
|
||||
if self.position.y < 0.0 {
|
||||
self.position.y += HEIGHT;
|
||||
} else if self.position.y > HEIGHT {
|
||||
self.position.y -= HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_state(&mut self, boids: &[Boid]) {
|
||||
let mut acceleration = Vector::new(0.0, 0.0);
|
||||
acceleration += self.calc_separation(boids);
|
||||
acceleration += self.calc_cohesion(boids);
|
||||
acceleration += self.calc_alignment(boids);
|
||||
self.velocity += acceleration;
|
||||
self.velocity.normalize();
|
||||
self.velocity *= VELOCITY_SIZE;
|
||||
|
||||
self.move_self();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
mod boid;
|
||||
mod vector;
|
||||
|
||||
use crate::boid::Boid;
|
||||
use crate::vector::Vector;
|
||||
use rand::prelude::thread_rng;
|
||||
use std::f64::consts::PI;
|
||||
use std::time::Duration;
|
||||
use yew::prelude::{html, Component, ComponentLink, Html, ShouldRender};
|
||||
use yew::services::{IntervalService, Task};
|
||||
|
||||
pub struct Model {
|
||||
boids: Vec<Boid>,
|
||||
#[allow(unused)]
|
||||
job: Box<dyn Task>,
|
||||
}
|
||||
|
||||
pub enum Msg {
|
||||
Tick,
|
||||
}
|
||||
|
||||
const WIDTH: u64 = 600;
|
||||
const HEIGHT: u64 = 400;
|
||||
|
||||
impl Component for Model {
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
let callback = link.callback(|_| Msg::Tick);
|
||||
let handle = IntervalService::spawn(Duration::from_millis(50), callback);
|
||||
|
||||
let mut rng = thread_rng();
|
||||
Self {
|
||||
boids: (0..100).map(|_| Boid::new(&mut rng)).collect(),
|
||||
job: Box::new(handle),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
match msg {
|
||||
Msg::Tick => {
|
||||
let boids = self.boids.clone();
|
||||
for boid in &mut self.boids {
|
||||
boid.next_state(&boids);
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn change(&mut self, _: Self::Properties) -> ShouldRender {
|
||||
false
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
let boids = (&self.boids).iter().map(|boid| boid2triangle(&boid));
|
||||
html! {
|
||||
<svg width=WIDTH height=HEIGHT viewBox={ format!("0 0 {} {}", WIDTH, HEIGHT) } xmlns={ "http://www.w3.org/2000/svg" }>
|
||||
{ for boids }
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn boid2triangle(boid: &Boid) -> Html {
|
||||
let points = get_points_str(&boid.position, &boid.velocity);
|
||||
html! {
|
||||
<polygon points=points />
|
||||
}
|
||||
}
|
||||
|
||||
fn get_points_str(position: &Vector, velocity: &Vector) -> String {
|
||||
let direction = velocity.y.atan2(velocity.x);
|
||||
let size = 10.0;
|
||||
let convert_position = |i: usize| {
|
||||
(
|
||||
position.x + size * (direction + ((i as f64) * 2.0 * PI / 3.0)).cos(),
|
||||
position.y + size * (direction + ((i as f64) * 2.0 * PI / 3.0)).sin(),
|
||||
)
|
||||
};
|
||||
(0..3)
|
||||
.map(convert_position)
|
||||
.map(|(x, y): (f64, f64)| format!("{},{}", x, y))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
yew::start_app::<boids::Model>();
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
use std::ops::{AddAssign, DivAssign, MulAssign, SubAssign};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Vector {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
}
|
||||
|
||||
impl AddAssign for Vector {
|
||||
fn add_assign(&mut self, other: Vector) {
|
||||
self.x += other.x;
|
||||
self.y += other.y;
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Vector {
|
||||
fn sub_assign(&mut self, other: Vector) {
|
||||
self.x -= other.x;
|
||||
self.y -= other.y;
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<f64> for Vector {
|
||||
fn mul_assign(&mut self, scalar: f64) {
|
||||
self.x *= scalar;
|
||||
self.y *= scalar;
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<f64> for Vector {
|
||||
fn div_assign(&mut self, scalar: f64) {
|
||||
self.x /= scalar;
|
||||
self.y /= scalar;
|
||||
}
|
||||
}
|
||||
|
||||
impl Vector {
|
||||
pub fn new(x: f64, y: f64) -> Vector {
|
||||
Vector { x, y }
|
||||
}
|
||||
|
||||
pub fn size(&self) -> f64 {
|
||||
(self.x * self.x + self.y * self.y).sqrt()
|
||||
}
|
||||
|
||||
pub fn normalize(&mut self) {
|
||||
let size = self.size();
|
||||
if size == 0.0 {
|
||||
return;
|
||||
}
|
||||
*self /= size;
|
||||
}
|
||||
|
||||
pub fn size_square(&self) -> f64 {
|
||||
self.x * self.x + self.y * self.y
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Vector {}
|
Loading…
Reference in New Issue