Coordinate cleanup

master
Mark 2023-12-21 11:26:44 -08:00
parent 6631096acf
commit 8fc23dd359
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
14 changed files with 416 additions and 235 deletions

7
Cargo.lock generated
View File

@ -38,6 +38,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "c_vec"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdd7a427adc0135366d99db65b36dae9237130997e560ed61118041fb72be6e8"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -382,6 +388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8356b2697d1ead5a34f40bcc3c5d3620205fe0c7be0a14656223bfeec0258891" checksum = "8356b2697d1ead5a34f40bcc3c5d3620205fe0c7be0a14656223bfeec0258891"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"c_vec",
"lazy_static", "lazy_static",
"libc", "libc",
"sdl2-sys", "sdl2-sys",

View File

@ -10,4 +10,4 @@ rand = "0.8.5"
[dependencies.sdl2] [dependencies.sdl2]
version = "0.36" version = "0.36"
default-features = false default-features = false
#features = ["ttf"] features = ["ttf", "gfx"]

View File

@ -1,33 +1,29 @@
use sdl2::render::Canvas; use sdl2::gfx::primitives::DrawRenderer;
use sdl2::video::Window; use sdl2::pixels::Color;
use crate::physics::PhysVec; use crate::DrawContext;
use crate::physics::Position;
use crate::Camera;
use crate::Drawable; use crate::Drawable;
use crate::SpriteAtlas; use crate::SpriteAtlas;
use crate::WorldPosition;
pub struct Doodad { pub struct Doodad {
pub sprite: String, pub sprite: String,
pub pos: Position, pub pos: WorldPosition,
pub scale: u32, pub scale: u32,
pub angle: f64, pub angle: f64,
} }
impl Drawable for Doodad { impl Drawable for Doodad {
fn position(&self) -> PhysVec { fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String> {
self.pos.to_cartesian().into() let pos = self.pos.screen_position(dc);
}
fn draw(
&self,
canvas: &mut Canvas<Window>,
sa: &SpriteAtlas,
c: &Camera,
) -> Result<(), String> {
let pos = self.screen_position(canvas, c);
let sprite = sa.get(&self.sprite); 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(()); return Ok(());
} }
} }

View File

@ -1,4 +1,3 @@
use rand::Rng;
use sdl2::{event::Event, keyboard::Keycode, render::Canvas, video::Window}; use sdl2::{event::Event, keyboard::Keycode, render::Canvas, video::Window};
use std::{time::Duration, time::Instant}; use std::{time::Duration, time::Instant};
@ -7,112 +6,59 @@ mod inputstatus;
mod physics; mod physics;
mod ship; mod ship;
mod sprite; mod sprite;
mod system;
use crate::{ use crate::{
doodad::Doodad, inputstatus::InputStatus, physics::PhysVec, physics::Position, ship::Ship, doodad::Doodad, inputstatus::InputStatus, physics::Cartesian, physics::WorldPosition,
sprite::SpriteAtlas, ship::Ship, ship::ShipKind, sprite::SpriteAtlas, system::System,
}; };
trait Drawable { 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<Window>, 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 // Draw this item on the screen
fn draw(&self, canvas: &mut Canvas<Window>, sa: &SpriteAtlas, c: &Camera) fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String>;
-> Result<(), String>;
}
enum ShipKind {
Gypsum,
}
impl ShipKind {
fn sprite(&self) -> &'static str {
match self {
Self::Gypsum => "gypsum.png",
}
}
} }
struct Camera { struct Camera {
pos: PhysVec, pos: Cartesian,
} }
impl Camera { impl Camera {
fn new() -> Self { fn new() -> Self {
Camera { 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 static FTL: f64 = 1.0 / 200.0; // frame time limit
struct System { struct DrawContext<'a> {
bodies: Vec<Box<dyn Drawable>>, canvas: &'a mut Canvas<Window>,
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 { impl<'a> DrawContext<'a> {
fn new() -> Self { fn new(canvas: &'a mut Canvas<Window>, camera: Camera) -> Self {
let mut s = System { bodies: Vec::new() }; let mut s = Self {
canvas,
let mut num = rand::thread_rng(); camera,
for _ in 0..25 { window_size: Cartesian::new(0.0, 0.0),
s.bodies.push(Box::new(Doodad { top_left: Cartesian::new(0.0, 0.0),
sprite: "small.png".to_owned(), };
pos: Position::new_cartesian( s.update();
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(),
}));
return s; return s;
} }
/// Calculate the state of this body after t seconds. fn update(&mut self) {
pub fn tick(&mut self, _t: f64) { self.window_size = Cartesian::from(self.canvas.window().size());
//let body = &mut self.bodies[1]; self.top_left = (self.window_size / 2.0) * Cartesian::new(-1.0, 1.0);
//body.pos.angle += 0.1 * t;
//body.angle -= 0.1 * t;
}
fn draw(
&self,
canvas: &mut Canvas<Window>,
sa: &SpriteAtlas,
c: &Camera,
) -> Result<(), String> {
for body in &self.bodies {
body.draw(canvas, sa, c)?;
}
return Ok(());
} }
} }
@ -140,16 +86,19 @@ fn main() -> Result<(), String> {
let mut system = System::new(); let mut system = System::new();
let mut i = InputStatus::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 last_frame_time = 0f64;
let mut frame_start; let mut frame_start;
let mut running = true; let mut running = true;
let mut dc = DrawContext::new(&mut canvas, Camera::new());
while running { while running {
frame_start = Instant::now(); frame_start = Instant::now();
dc.update();
for event in event_pump.poll_iter() { for event in event_pump.poll_iter() {
match event { match event {
@ -164,13 +113,13 @@ fn main() -> Result<(), String> {
} }
} }
c.pos = s.body.pos; dc.camera.pos = s.body.pos;
// Draw // Draw
canvas.clear(); dc.canvas.clear();
system.draw(&mut canvas, &sa, &c)?; system.draw(&mut dc, &sa)?;
s.draw(&mut canvas, &sa, &c)?; s.draw(&mut dc, &sa)?;
canvas.present(); dc.canvas.present();
let frame_time = frame_start.elapsed().as_secs_f64(); let frame_time = frame_start.elapsed().as_secs_f64();

View File

@ -1,19 +1,19 @@
use crate::physics::PhysVec; use crate::physics::Cartesian;
use std::f64::consts::{PI, TAU}; use std::f64::consts::{PI, TAU};
pub struct PhysBody { pub struct PhysBody {
pub pos: PhysVec, pub pos: Cartesian,
pub vel: PhysVec, pub vel: Cartesian,
pub mass: f64, pub mass: f64,
pub angle: f64, // In radians pub angle: f64, // In radians
} }
impl PhysBody { impl PhysBody {
pub fn new(pos: PhysVec) -> Self { pub fn new(pos: Cartesian) -> Self {
return PhysBody { return PhysBody {
pos, pos,
vel: PhysVec { x: 0.0, y: 0.0 }, vel: Cartesian::new(0.0, 0.0),
mass: 1.0, mass: 0.3,
angle: 0.0, angle: 0.0,
}; };
} }
@ -24,18 +24,14 @@ impl PhysBody {
} }
/// Apply an instantaneous force to this object /// 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; self.vel += v / self.mass;
} }
/// Apply force in the direction this object is pointing. /// Apply force in the direction this object is pointing.
pub fn thrust(&mut self, f: f64) { pub fn thrust(&mut self, f: f64) {
self.force( let l = Cartesian::new(self.angle.sin(), self.angle.cos()) * f;
PhysVec { self.force(l);
x: self.angle.sin(),
y: -self.angle.cos(),
} * f,
);
} }
// Rotate this object by `a` radians. // Rotate this object by `a` radians.

View File

@ -1,21 +1,27 @@
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct PhysVec { pub struct Cartesian {
pub x: f64, pub x: f64,
pub y: 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 { fn from(value: (u32, u32)) -> Self {
PhysVec { Cartesian {
x: value.0 as f64, x: value.0 as f64,
y: value.1 as f64, y: value.1 as f64,
} }
} }
} }
impl Add for PhysVec { impl Add for Cartesian {
type Output = Self; type Output = Self;
fn add(self, other: Self) -> 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) { fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x; self.x += rhs.x;
self.y += rhs.y; self.y += rhs.y;
} }
} }
impl Sub for PhysVec { impl Sub for Cartesian {
type Output = Self; type Output = Self;
fn sub(self, other: Self) -> 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) { fn sub_assign(&mut self, rhs: Self) {
self.x += rhs.x; self.x += rhs.x;
self.y += rhs.y; self.y += rhs.y;
} }
} }
impl Mul<f64> for PhysVec { impl Mul for Cartesian {
type Output = PhysVec; 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<f64> for Cartesian {
type Output = Cartesian;
fn mul(self, rhs: f64) -> Self::Output { fn mul(self, rhs: f64) -> Self::Output {
Self { Self {
x: self.x * rhs, x: self.x * rhs,
@ -61,15 +85,15 @@ impl Mul<f64> for PhysVec {
} }
} }
impl MulAssign<f64> for PhysVec { impl MulAssign<f64> for Cartesian {
fn mul_assign(&mut self, rhs: f64) { fn mul_assign(&mut self, rhs: f64) {
self.x *= rhs; self.x *= rhs;
self.y *= rhs; self.y *= rhs;
} }
} }
impl Div<f64> for PhysVec { impl Div<f64> for Cartesian {
type Output = PhysVec; type Output = Cartesian;
fn div(self, rhs: f64) -> Self::Output { fn div(self, rhs: f64) -> Self::Output {
Self { Self {
x: self.x / rhs, x: self.x / rhs,
@ -78,7 +102,7 @@ impl Div<f64> for PhysVec {
} }
} }
impl DivAssign<f64> for PhysVec { impl DivAssign<f64> for Cartesian {
fn div_assign(&mut self, rhs: f64) { fn div_assign(&mut self, rhs: f64) {
self.x /= rhs; self.x /= rhs;
self.y /= rhs; self.y /= rhs;

View File

@ -1,9 +1,9 @@
mod body; mod body;
mod physpol; mod cartesian;
mod physvec; mod polar;
mod position; mod worldposition;
pub use body::PhysBody; pub use body::PhysBody;
pub use physpol::PhysPol; pub use cartesian::Cartesian;
pub use physvec::PhysVec; pub use polar::Polar;
pub use position::Position; pub use worldposition::WorldPosition;

View File

@ -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;
}
}

17
src/physics/polar.rs Normal file
View File

@ -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<Cartesian> for Polar {
fn into(self) -> Cartesian {
return Cartesian::new(
self.radius * self.angle.sin(),
self.radius * self.angle.cos(),
) + self.center;
}
}

View File

@ -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<PhysVec> for Position {
fn into(self) -> PhysVec {
let c = self.to_cartesian();
match c {
Self::Cartesian(pv) => pv,
_ => unreachable!(),
}
}
}

View File

@ -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<Cartesian> for WorldPosition {
fn into(self) -> Cartesian {
self.pos.into()
}
}

View File

@ -1,42 +1,48 @@
use sdl2::render::Canvas; use crate::physics::Cartesian;
use sdl2::video::Window;
use crate::physics::PhysBody; use crate::physics::PhysBody;
use crate::physics::PhysVec; use crate::DrawContext;
use crate::physics::Position;
use crate::Camera;
use crate::Drawable; use crate::Drawable;
use crate::ShipKind;
use crate::SpriteAtlas; use crate::SpriteAtlas;
pub enum ShipKind {
Gypsum,
}
impl ShipKind {
fn sprite(&self) -> &'static str {
match self {
Self::Gypsum => "gypsum.png",
}
}
}
pub struct Ship { pub struct Ship {
pub body: PhysBody, pub body: PhysBody,
kind: ShipKind, kind: ShipKind,
} }
impl Ship { impl Ship {
pub fn new(kind: ShipKind, pos: Position) -> Self { pub fn new(kind: ShipKind, pos: Cartesian) -> Self {
Ship { Ship {
body: PhysBody::new(pos.into()), body: PhysBody::new(pos),
kind, 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 { impl Drawable for Ship {
fn position(&self) -> PhysVec { fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String> {
return self.body.pos; let pos = self.screen_position(dc);
}
fn draw(
&self,
canvas: &mut Canvas<Window>,
sa: &SpriteAtlas,
c: &Camera,
) -> Result<(), String> {
let pos = self.screen_position(canvas, c);
let sprite = sa.get(self.kind.sprite()); 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(()); return Ok(());
} }
} }

View File

@ -7,7 +7,7 @@ use sdl2::{
use std::collections::HashMap; use std::collections::HashMap;
use crate::physics::PhysVec; use crate::physics::Cartesian;
/// A handle for a sprite inside a SpriteAtlas /// A handle for a sprite inside a SpriteAtlas
pub struct Sprite<'a> { pub struct Sprite<'a> {
@ -24,11 +24,11 @@ impl<'a> Sprite<'a> {
pub fn draw( pub fn draw(
&self, &self,
canvas: &mut Canvas<Window>, canvas: &mut Canvas<Window>,
position: PhysVec, position: Cartesian,
angle: f64, angle: f64,
scale: f64, scale: f64,
) -> Result<(), String> { ) -> Result<(), String> {
let win_size = PhysVec::from(canvas.window().size()); let win_size = Cartesian::from(canvas.window().size());
let scale = scale * self.scale; let scale = scale * self.scale;
// Post-scale dimensions on the screen // Post-scale dimensions on the screen
@ -60,6 +60,17 @@ impl<'a> Sprite<'a> {
false, 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(()); return Ok(());
} }
} }

185
src/system.rs Normal file
View File

@ -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<WorldPosition>,
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<Box<dyn Drawable>>,
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(());
}
}