diff --git a/Cargo.lock b/Cargo.lock index 6503d5a..4319dc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "c_vec" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd7a427adc0135366d99db65b36dae9237130997e560ed61118041fb72be6e8" + [[package]] name = "cfg-if" version = "1.0.0" @@ -382,6 +388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8356b2697d1ead5a34f40bcc3c5d3620205fe0c7be0a14656223bfeec0258891" dependencies = [ "bitflags", + "c_vec", "lazy_static", "libc", "sdl2-sys", diff --git a/Cargo.toml b/Cargo.toml index 5dd1d7f..1137b77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,4 @@ rand = "0.8.5" [dependencies.sdl2] version = "0.36" default-features = false -#features = ["ttf"] +features = ["ttf", "gfx"] diff --git a/src/doodad.rs b/src/doodad.rs index 37983c3..a2cdd63 100644 --- a/src/doodad.rs +++ b/src/doodad.rs @@ -1,33 +1,29 @@ -use sdl2::render::Canvas; -use sdl2::video::Window; +use sdl2::gfx::primitives::DrawRenderer; +use sdl2::pixels::Color; -use crate::physics::PhysVec; -use crate::physics::Position; -use crate::Camera; +use crate::DrawContext; use crate::Drawable; use crate::SpriteAtlas; +use crate::WorldPosition; pub struct Doodad { pub sprite: String, - pub pos: Position, + pub pos: WorldPosition, pub scale: u32, pub angle: f64, } impl Drawable for Doodad { - fn position(&self) -> PhysVec { - self.pos.to_cartesian().into() - } - - fn draw( - &self, - canvas: &mut Canvas, - sa: &SpriteAtlas, - c: &Camera, - ) -> Result<(), String> { - let pos = self.screen_position(canvas, c); + fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String> { + let pos = self.pos.screen_position(dc); let sprite = sa.get(&self.sprite); - sprite.draw(canvas, pos, self.angle, 1.0)?; + sprite.draw(dc.canvas, pos, self.angle, 1.0)?; + + let p = self.pos.screen_position_real(dc); + dc.canvas + .aa_circle(p.x as i16, p.y as i16, 5, Color::RGB(255, 0, 0))?; + dc.canvas.set_draw_color(Color::RGB(0, 0, 0)); + return Ok(()); } } diff --git a/src/main.rs b/src/main.rs index bbb7aa6..fde03e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -use rand::Rng; use sdl2::{event::Event, keyboard::Keycode, render::Canvas, video::Window}; use std::{time::Duration, time::Instant}; @@ -7,112 +6,59 @@ mod inputstatus; mod physics; mod ship; mod sprite; +mod system; use crate::{ - doodad::Doodad, inputstatus::InputStatus, physics::PhysVec, physics::Position, ship::Ship, - sprite::SpriteAtlas, + doodad::Doodad, inputstatus::InputStatus, physics::Cartesian, physics::WorldPosition, + ship::Ship, ship::ShipKind, sprite::SpriteAtlas, system::System, }; trait Drawable { - /// Return the world position of the center of this object. - fn position(&self) -> PhysVec; - - /// Get the position of this drawable on the screen - /// (0, 0) is at top-left corner. - /// - /// Returned position is this object's center. - fn screen_position(&self, canvas: &mut Canvas, c: &Camera) -> PhysVec { - let win_size = PhysVec::from(canvas.window().size()); - let h = win_size / 2.0; - return self.position() - c.pos + h; - } - // Draw this item on the screen - fn draw(&self, canvas: &mut Canvas, sa: &SpriteAtlas, c: &Camera) - -> Result<(), String>; -} - -enum ShipKind { - Gypsum, -} - -impl ShipKind { - fn sprite(&self) -> &'static str { - match self { - Self::Gypsum => "gypsum.png", - } - } + fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String>; } struct Camera { - pos: PhysVec, + pos: Cartesian, } impl Camera { fn new() -> Self { Camera { - pos: PhysVec { x: 0.0, y: 0.0 }, + pos: Cartesian::new(0.0, 0.0), } } } static FTL: f64 = 1.0 / 200.0; // frame time limit -struct System { - bodies: Vec>, +struct DrawContext<'a> { + canvas: &'a mut Canvas, + camera: Camera, + + // Dimensions of the window + window_size: Cartesian, + + // Position of the top-left corner of the window, + // relative to camera position. + top_left: Cartesian, } -impl System { - fn new() -> Self { - let mut s = System { bodies: Vec::new() }; - - let mut num = rand::thread_rng(); - for _ in 0..25 { - s.bodies.push(Box::new(Doodad { - sprite: "small.png".to_owned(), - pos: Position::new_cartesian( - num.gen_range(-500.0..500.0), - num.gen_range(-500.0..500.0), - ), - scale: 1, - angle: num.gen_range(-180f64..180f64).to_radians(), - })); - } - - s.bodies.push(Box::new(Doodad { - pos: Position::new_cartesian(0.0, 0.0), - sprite: "a0.png".to_owned(), - scale: 1, - angle: 0.0, - })); - - s.bodies.push(Box::new(Doodad { - pos: Position::new_polar(PhysVec { x: 0.0, y: 0.0 }, 300.0, 31.0), - sprite: "earth.png".to_owned(), - scale: 1, - angle: (180f64).to_radians(), - })); - +impl<'a> DrawContext<'a> { + fn new(canvas: &'a mut Canvas, camera: Camera) -> Self { + let mut s = Self { + canvas, + camera, + window_size: Cartesian::new(0.0, 0.0), + top_left: Cartesian::new(0.0, 0.0), + }; + s.update(); return s; } - /// Calculate the state of this body after t seconds. - pub fn tick(&mut self, _t: f64) { - //let body = &mut self.bodies[1]; - //body.pos.angle += 0.1 * t; - //body.angle -= 0.1 * t; - } - - fn draw( - &self, - canvas: &mut Canvas, - sa: &SpriteAtlas, - c: &Camera, - ) -> Result<(), String> { - for body in &self.bodies { - body.draw(canvas, sa, c)?; - } - return Ok(()); + fn update(&mut self) { + self.window_size = Cartesian::from(self.canvas.window().size()); + self.top_left = (self.window_size / 2.0) * Cartesian::new(-1.0, 1.0); } } @@ -140,16 +86,19 @@ fn main() -> Result<(), String> { let mut system = System::new(); let mut i = InputStatus::new(); - let mut c = Camera::new(); - let mut s = Ship::new(ShipKind::Gypsum, Position::new_cartesian(0.0, 0.0)); + let mut s = Ship::new(ShipKind::Gypsum, Cartesian::new(0.0, 0.0)); //let mut last_frame_time = 0f64; let mut frame_start; let mut running = true; + + let mut dc = DrawContext::new(&mut canvas, Camera::new()); + while running { frame_start = Instant::now(); + dc.update(); for event in event_pump.poll_iter() { match event { @@ -164,13 +113,13 @@ fn main() -> Result<(), String> { } } - c.pos = s.body.pos; + dc.camera.pos = s.body.pos; // Draw - canvas.clear(); - system.draw(&mut canvas, &sa, &c)?; - s.draw(&mut canvas, &sa, &c)?; - canvas.present(); + dc.canvas.clear(); + system.draw(&mut dc, &sa)?; + s.draw(&mut dc, &sa)?; + dc.canvas.present(); let frame_time = frame_start.elapsed().as_secs_f64(); diff --git a/src/physics/body.rs b/src/physics/body.rs index e61e965..1d22628 100644 --- a/src/physics/body.rs +++ b/src/physics/body.rs @@ -1,19 +1,19 @@ -use crate::physics::PhysVec; +use crate::physics::Cartesian; use std::f64::consts::{PI, TAU}; pub struct PhysBody { - pub pos: PhysVec, - pub vel: PhysVec, + pub pos: Cartesian, + pub vel: Cartesian, pub mass: f64, pub angle: f64, // In radians } impl PhysBody { - pub fn new(pos: PhysVec) -> Self { + pub fn new(pos: Cartesian) -> Self { return PhysBody { pos, - vel: PhysVec { x: 0.0, y: 0.0 }, - mass: 1.0, + vel: Cartesian::new(0.0, 0.0), + mass: 0.3, angle: 0.0, }; } @@ -24,18 +24,14 @@ impl PhysBody { } /// Apply an instantaneous force to this object - pub fn force(&mut self, v: PhysVec) { + pub fn force(&mut self, v: Cartesian) { self.vel += v / self.mass; } /// Apply force in the direction this object is pointing. pub fn thrust(&mut self, f: f64) { - self.force( - PhysVec { - x: self.angle.sin(), - y: -self.angle.cos(), - } * f, - ); + let l = Cartesian::new(self.angle.sin(), self.angle.cos()) * f; + self.force(l); } // Rotate this object by `a` radians. diff --git a/src/physics/physvec.rs b/src/physics/cartesian.rs similarity index 58% rename from src/physics/physvec.rs rename to src/physics/cartesian.rs index 3a9e91b..ceb4aea 100644 --- a/src/physics/physvec.rs +++ b/src/physics/cartesian.rs @@ -1,21 +1,27 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; #[derive(Debug, Clone, Copy)] -pub struct PhysVec { +pub struct Cartesian { pub x: f64, pub y: f64, } -impl From<(u32, u32)> for PhysVec { +impl Cartesian { + pub fn new(x: f64, y: f64) -> Self { + Cartesian { x, y } + } +} + +impl From<(u32, u32)> for Cartesian { fn from(value: (u32, u32)) -> Self { - PhysVec { + Cartesian { x: value.0 as f64, y: value.1 as f64, } } } -impl Add for PhysVec { +impl Add for Cartesian { type Output = Self; fn add(self, other: Self) -> Self { @@ -26,14 +32,14 @@ impl Add for PhysVec { } } -impl AddAssign for PhysVec { +impl AddAssign for Cartesian { fn add_assign(&mut self, rhs: Self) { self.x += rhs.x; self.y += rhs.y; } } -impl Sub for PhysVec { +impl Sub for Cartesian { type Output = Self; fn sub(self, other: Self) -> Self { @@ -44,15 +50,33 @@ impl Sub for PhysVec { } } -impl SubAssign for PhysVec { +impl SubAssign for Cartesian { fn sub_assign(&mut self, rhs: Self) { self.x += rhs.x; self.y += rhs.y; } } -impl Mul for PhysVec { - type Output = PhysVec; +impl Mul for Cartesian { + type Output = Self; + + fn mul(self, other: Self) -> Self { + Self { + x: self.x * other.x, + y: self.y * other.y, + } + } +} + +impl MulAssign for Cartesian { + fn mul_assign(&mut self, rhs: Self) { + self.x *= rhs.x; + self.y *= rhs.y; + } +} + +impl Mul for Cartesian { + type Output = Cartesian; fn mul(self, rhs: f64) -> Self::Output { Self { x: self.x * rhs, @@ -61,15 +85,15 @@ impl Mul for PhysVec { } } -impl MulAssign for PhysVec { +impl MulAssign for Cartesian { fn mul_assign(&mut self, rhs: f64) { self.x *= rhs; self.y *= rhs; } } -impl Div for PhysVec { - type Output = PhysVec; +impl Div for Cartesian { + type Output = Cartesian; fn div(self, rhs: f64) -> Self::Output { Self { x: self.x / rhs, @@ -78,7 +102,7 @@ impl Div for PhysVec { } } -impl DivAssign for PhysVec { +impl DivAssign for Cartesian { fn div_assign(&mut self, rhs: f64) { self.x /= rhs; self.y /= rhs; diff --git a/src/physics/mod.rs b/src/physics/mod.rs index 23fb5a1..3086f2b 100644 --- a/src/physics/mod.rs +++ b/src/physics/mod.rs @@ -1,9 +1,9 @@ mod body; -mod physpol; -mod physvec; -mod position; +mod cartesian; +mod polar; +mod worldposition; pub use body::PhysBody; -pub use physpol::PhysPol; -pub use physvec::PhysVec; -pub use position::Position; +pub use cartesian::Cartesian; +pub use polar::Polar; +pub use worldposition::WorldPosition; diff --git a/src/physics/physpol.rs b/src/physics/physpol.rs deleted file mode 100644 index dab7399..0000000 --- a/src/physics/physpol.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::PhysVec; - -#[derive(Debug, Clone, Copy)] -pub struct PhysPol { - pub center: PhysVec, - pub radius: f64, - pub angle: f64, -} - -impl PhysPol { - pub fn to_cartesian(&self) -> PhysVec { - return PhysVec { - x: self.radius * self.angle.sin(), - y: self.radius * self.angle.cos(), - } + self.center; - } -} diff --git a/src/physics/polar.rs b/src/physics/polar.rs new file mode 100644 index 0000000..5a47c8e --- /dev/null +++ b/src/physics/polar.rs @@ -0,0 +1,17 @@ +use super::Cartesian; + +#[derive(Debug, Clone, Copy)] +pub struct Polar { + pub center: Cartesian, + pub radius: f64, + pub angle: f64, +} + +impl Into for Polar { + fn into(self) -> Cartesian { + return Cartesian::new( + self.radius * self.angle.sin(), + self.radius * self.angle.cos(), + ) + self.center; + } +} diff --git a/src/physics/position.rs b/src/physics/position.rs deleted file mode 100644 index cc3d185..0000000 --- a/src/physics/position.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::PhysPol; -use super::PhysVec; - -#[derive(Debug, Clone, Copy)] -pub enum Position { - Cartesian(PhysVec), - Polar(PhysPol), -} - -/// A convenient wrapper around PhysVec and PhysPol -/// that allows us to position game objects (like doodads) -/// with whatever scheme we want. -/// -/// This should ONLY be used for game objects, physics -/// calculations should always use PhysVec. -impl Position { - pub fn new_cartesian(x: f64, y: f64) -> Self { - return Self::Cartesian(PhysVec { x, y }); - } - - pub fn new_polar(center: PhysVec, radius: f64, angle: f64) -> Self { - return Self::Polar(PhysPol { - center, - radius, - angle, - }); - } - - /* - pub fn to_polar(&self) -> Self { - match self { - Self::Cartesian(pv) => Self::Polar(pv.to_polar()), - Self::Polar(_) => self.clone(), - } - } - */ - - pub fn to_cartesian(&self) -> Self { - match self { - Self::Cartesian(_) => self.clone(), - Self::Polar(pp) => Self::Cartesian(pp.to_cartesian()), - } - } -} - -impl Into for Position { - fn into(self) -> PhysVec { - let c = self.to_cartesian(); - match c { - Self::Cartesian(pv) => pv, - _ => unreachable!(), - } - } -} diff --git a/src/physics/worldposition.rs b/src/physics/worldposition.rs new file mode 100644 index 0000000..09e44e0 --- /dev/null +++ b/src/physics/worldposition.rs @@ -0,0 +1,61 @@ +use super::Cartesian; +use crate::DrawContext; + +#[derive(Debug, Clone, Copy)] + +pub struct WorldPosition { + pub pos: Cartesian, // True world position + pub par: f64, // Parallax factor +} + +impl WorldPosition { + pub fn new(pos: Cartesian, par: f64) -> Self { + WorldPosition { pos: pos, par } + } + + fn from_screen_position(dc: &DrawContext, pos: Cartesian, par: f64) -> Self { + WorldPosition { + par, + pos: ((pos * Cartesian::new(1.0, -1.0)) + dc.top_left) * par + dc.camera.pos, + } + } + + /// Get the position of this drawable on the screen + /// (0, 0) is at top-left corner. + /// + /// Returned position is this object's center. + pub fn screen_position(&self, dc: &DrawContext) -> Cartesian { + let par = self.par; + let pos: Cartesian = self.pos.into(); + return (((pos - dc.camera.pos) / par) - dc.top_left) * Cartesian::new(1.0, -1.0); + } + + /// Get the position of this drawable on the screen + /// (0, 0) is at top-left corner. + /// + /// Returned position is this object's center. + pub fn screen_position_real(&self, dc: &DrawContext) -> Cartesian { + let pos: Cartesian = self.pos.into(); + return ((pos - dc.camera.pos) - dc.top_left) * Cartesian::new(1.0, -1.0); + } + + /// Is this object on screen? + fn on_screen(&self, dc: &DrawContext) -> bool { + let pos = self.screen_position(dc); + let (width, height) = (10, 10); //TODO: actual + + // Don't draw if we're not on the screen. + // An offset is included to ensure we're completely + // off the screen. We add the whole width intentionally. + return !(pos.x < -1.0 * (width as f64) + || pos.x > dc.window_size.x + width as f64 + || pos.y < -1.0 * (height as f64) + || pos.y > dc.window_size.y + height as f64); + } +} + +impl Into for WorldPosition { + fn into(self) -> Cartesian { + self.pos.into() + } +} diff --git a/src/ship.rs b/src/ship.rs index e0fd194..14b8d39 100644 --- a/src/ship.rs +++ b/src/ship.rs @@ -1,42 +1,48 @@ -use sdl2::render::Canvas; -use sdl2::video::Window; - +use crate::physics::Cartesian; use crate::physics::PhysBody; -use crate::physics::PhysVec; -use crate::physics::Position; -use crate::Camera; +use crate::DrawContext; use crate::Drawable; -use crate::ShipKind; use crate::SpriteAtlas; +pub enum ShipKind { + Gypsum, +} + +impl ShipKind { + fn sprite(&self) -> &'static str { + match self { + Self::Gypsum => "gypsum.png", + } + } +} + pub struct Ship { pub body: PhysBody, kind: ShipKind, } impl Ship { - pub fn new(kind: ShipKind, pos: Position) -> Self { + pub fn new(kind: ShipKind, pos: Cartesian) -> Self { Ship { - body: PhysBody::new(pos.into()), + body: PhysBody::new(pos), kind, } } + + /// Get the position of this drawable on the screen + /// (0, 0) is at top-left corner. + /// + /// Returned position is this object's center. + fn screen_position(&self, dc: &DrawContext) -> Cartesian { + return ((self.body.pos - dc.camera.pos) - dc.top_left) * Cartesian::new(1.0, -1.0); + } } impl Drawable for Ship { - fn position(&self) -> PhysVec { - return self.body.pos; - } - - fn draw( - &self, - canvas: &mut Canvas, - sa: &SpriteAtlas, - c: &Camera, - ) -> Result<(), String> { - let pos = self.screen_position(canvas, c); + fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String> { + let pos = self.screen_position(dc); let sprite = sa.get(self.kind.sprite()); - sprite.draw(canvas, pos, self.body.angle.to_degrees(), 1.0)?; + sprite.draw(dc.canvas, pos, self.body.angle.to_degrees(), 1.0)?; return Ok(()); } } diff --git a/src/sprite.rs b/src/sprite.rs index d1dfa7d..073a140 100644 --- a/src/sprite.rs +++ b/src/sprite.rs @@ -7,7 +7,7 @@ use sdl2::{ use std::collections::HashMap; -use crate::physics::PhysVec; +use crate::physics::Cartesian; /// A handle for a sprite inside a SpriteAtlas pub struct Sprite<'a> { @@ -24,11 +24,11 @@ impl<'a> Sprite<'a> { pub fn draw( &self, canvas: &mut Canvas, - position: PhysVec, + position: Cartesian, angle: f64, scale: f64, ) -> Result<(), String> { - let win_size = PhysVec::from(canvas.window().size()); + let win_size = Cartesian::from(canvas.window().size()); let scale = scale * self.scale; // Post-scale dimensions on the screen @@ -60,6 +60,17 @@ impl<'a> Sprite<'a> { false, )?; + /* + canvas.set_draw_color(Color::RGB(255, 0, 0)); + canvas.aa_circle( + position.x as i16, + position.y as i16, + 5, + Color::RGB(255, 0, 0), + )?; + canvas.set_draw_color(Color::RGB(0, 0, 0)); + */ + return Ok(()); } } diff --git a/src/system.rs b/src/system.rs new file mode 100644 index 0000000..fdae5fb --- /dev/null +++ b/src/system.rs @@ -0,0 +1,185 @@ +use crate::{ + physics::Cartesian, physics::Polar, physics::WorldPosition, sprite::SpriteAtlas, Doodad, + DrawContext, Drawable, +}; +use rand::Rng; +use sdl2::{gfx::primitives::DrawRenderer, pixels::Color}; + +struct StarField { + stars: Vec, + width: f64, + height: f64, +} + +impl StarField { + fn new(width: f64, height: f64, d: f64) -> Self { + let mut s = StarField { + stars: Vec::new(), + width, + height, + }; + + let mut num = rand::thread_rng(); + let area = (width / 100.0) * (height / 100.0); + let n = (area * d) as i32; + for _ in 0..n { + s.stars.push(WorldPosition::new( + Cartesian::new( + num.gen_range(-width / 2.0..width / 2.0), + num.gen_range(-height / 2.0..height / 2.0), + ), + num.gen_range(3f64..4f64), + )) + } + + return s; + } + + fn draw_with_offset( + &self, + dc: &DrawContext, + pos_in_field: Cartesian, + offset: Cartesian, + ) -> Result<(), String> { + for wp in &self.stars { + // Coordinate of star on screen, + // with (0, 0) at top left + let p: Cartesian = wp.pos.into(); + let q = + ((p - pos_in_field + offset) / wp.par - dc.top_left) * Cartesian::new(1.0, -1.0); + + dc.canvas.filled_circle( + q.x as i16, + q.y as i16, + (5.0 - (1.0 * wp.par)) as i16, + Color::RGBA(255, 255, 255, 100), + )?; + } + return Ok(()); + } + + fn draw(&mut self, dc: &mut DrawContext, _sa: &SpriteAtlas) -> Result<(), String> { + let w = self.width; + let h = self.height; + let ww = w / 2.0; + let hh = h / 2.0; + + let pos_in_field = Cartesian::new( + dc.camera.pos.x.signum() * (((dc.camera.pos.x.abs() + ww) % w) - ww), + dc.camera.pos.y.signum() * (((dc.camera.pos.y.abs() + hh) % h) - hh), + ); + + // Field center in world coordinates + let field_center = dc.camera.pos - pos_in_field; + + // screen coordinates of top left position of starfield + let field_tl = + WorldPosition::new(field_center + Cartesian::new(-ww, hh), 3.0).screen_position(dc); + + let field_br = + WorldPosition::new(field_center + Cartesian::new(ww, -hh), 3.0).screen_position(dc); + + dc.canvas.aa_circle( + field_tl.x as i16, + field_tl.y as i16, + 5, + Color::RGB(255, 0, 0), + )?; + dc.canvas.set_draw_color(Color::RGB(0, 0, 0)); + + let north = field_tl.y > 0.0; + let south = field_br.y < dc.window_size.y; + let east = field_br.x < dc.window_size.x; + let west = field_tl.x > 0.0; + + self.draw_with_offset(dc, pos_in_field, Cartesian::new(0.0, 0.0))?; + if north { + self.draw_with_offset(dc, pos_in_field, Cartesian::new(0.0, h))?; + } + if south { + self.draw_with_offset(dc, pos_in_field, Cartesian::new(0.0, -h))?; + } + if east { + self.draw_with_offset(dc, pos_in_field, Cartesian::new(w, 0.0))?; + } + if west { + self.draw_with_offset(dc, pos_in_field, Cartesian::new(-w, 0.0))?; + } + if north && east { + self.draw_with_offset(dc, pos_in_field, Cartesian::new(w, h))?; + } + if north && west { + self.draw_with_offset(dc, pos_in_field, Cartesian::new(-w, h))?; + } + if south && east { + self.draw_with_offset(dc, pos_in_field, Cartesian::new(w, -h))?; + } + if south && west { + self.draw_with_offset(dc, pos_in_field, Cartesian::new(-w, -h))?; + } + + dc.canvas.set_draw_color(Color::RGB(0, 0, 0)); + return Ok(()); + } +} + +pub struct System { + bodies: Vec>, + starfield: StarField, +} + +impl System { + pub fn new() -> Self { + let mut s = System { + bodies: Vec::new(), + starfield: StarField::new(6000.0, 6000.0, 1.0), + }; + + s.bodies.push(Box::new(Doodad { + pos: WorldPosition::new(Cartesian::new(0.0, 0.0), 2.0), + sprite: "a0.png".to_owned(), + scale: 1, + angle: 0.0, + })); + + s.bodies.push(Box::new(Doodad { + pos: WorldPosition::new( + Polar { + center: Cartesian::new(0.0, 0.0), + radius: 300.0, + angle: 31.0, + } + .into(), + 1.5, + ), + sprite: "earth.png".to_owned(), + scale: 1, + angle: (180f64).to_radians(), + })); + + s.bodies.push(Box::new(Doodad { + pos: WorldPosition::new(Cartesian::new(1000.0, 1000.0), 2.0), + sprite: "small.png".to_owned(), + scale: 1, + angle: 0.0, + })); + + return s; + } + + /// Calculate the state of this body after t seconds. + pub fn tick(&mut self, _t: f64) { + //let body = &mut self.bodies[1]; + //body.pos.angle += 0.1 * t; + //body.angle -= 0.1 * t; + } + + pub fn draw(&mut self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String> { + self.starfield.draw(dc, sa)?; + for body in &self.bodies { + body.draw(dc, sa)?; + } + + return Ok(()); + } +}