Skip to content

Commit

Permalink
Dev/viewport (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheComamba committed Jan 1, 2024
1 parent eb3f9f1 commit 0716d02
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 67 deletions.
38 changes: 19 additions & 19 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions src/gui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,17 @@ impl Sandbox for Gui {
GuiMessage::UpdateSurfaceLatitude(latitude) => {
self.surface_view_state.surface_latitude = latitude;
}
GuiMessage::UpdateViewportOpeningAngle(angle) => {
if angle.as_degrees() < 10. {
self.surface_view_state.viewport_horizontal_opening_angle =
Angle::from_degrees(10.);
} else if angle.as_degrees() > 170. {
self.surface_view_state.viewport_horizontal_opening_angle =
Angle::from_degrees(170.);
} else {
self.surface_view_state.viewport_horizontal_opening_angle = angle;
}
}
GuiMessage::UpdateLengthScale(m_per_px) => {
self.topview_state.set_meter_per_pixel(m_per_px);
}
Expand Down Expand Up @@ -206,6 +217,7 @@ pub(super) enum GuiMessage {
UpdateTimeStep(Time),
UpdateSurfaceLongitude(Angle),
UpdateSurfaceLatitude(Angle),
UpdateViewportOpeningAngle(Angle),
UpdateLengthScale(Float),
UpdateViewLongitude(Angle),
UpdateViewLatitude(Angle),
Expand Down
93 changes: 57 additions & 36 deletions src/gui/surface_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@ use crate::model::celestial_body::CelestialBody;
use super::{Gui, GuiMessage};
use astro_utils::{
coordinates::{
cartesian::{CartesianCoordinates, ORIGIN},
direction::{Direction, X, Z},
ecliptic::EclipticCoordinates,
equatorial::EquatorialCoordinates,
cartesian::CartesianCoordinates, direction::Direction, equatorial::EquatorialCoordinates,
spherical::SphericalCoordinates,
},
surface_normal::{apparent_celestial_position, surface_normal_at_time},
surface_normal::{direction_relative_to_surface_normal, surface_normal_at_time},
units::angle::Angle,
Float,
};
Expand All @@ -20,14 +17,17 @@ use iced::{
canvas::{self, Path},
Column,
},
Alignment, Color,
Alignment, Color, Point,
};

const HUMAN_EYE_OPENING_ANGLE: Angle = Angle::from_radians(120. / 360. * 2. * PI);

pub(super) struct SurfaceViewState {
pub(super) background_cache: canvas::Cache,
pub(super) bodies_cache: canvas::Cache,
pub(super) surface_longitude: Angle,
pub(super) surface_latitude: Angle,
pub(super) viewport_horizontal_opening_angle: Angle,
}

impl SurfaceViewState {
Expand All @@ -37,6 +37,7 @@ impl SurfaceViewState {
bodies_cache: canvas::Cache::default(),
surface_longitude: Angle::from_degrees(0.0),
surface_latitude: Angle::from_degrees(0.0),
viewport_horizontal_opening_angle: HUMAN_EYE_OPENING_ANGLE,
}
}

Expand All @@ -48,26 +49,34 @@ impl SurfaceViewState {

impl Gui {
pub(super) fn surface_view_control_field(&self) -> iced::Element<'_, GuiMessage> {
const SURFACE_ANGLE_STEP: Angle = Angle::from_radians(10. * 2. * PI / 360.);
const ANGLE_STEP: Angle = Angle::from_radians(10. * 2. * PI / 360.);
let longitude = self.surface_view_state.surface_longitude;
let surface_longitude_control_field = self.control_field(
"Surface Longitude:",
format!("{}", longitude),
GuiMessage::UpdateSurfaceLongitude(longitude - SURFACE_ANGLE_STEP),
GuiMessage::UpdateSurfaceLongitude(longitude + SURFACE_ANGLE_STEP),
GuiMessage::UpdateSurfaceLongitude(longitude - ANGLE_STEP),
GuiMessage::UpdateSurfaceLongitude(longitude + ANGLE_STEP),
);
let latitude = self.surface_view_state.surface_latitude;
let surface_latitude_control_field = self.control_field(
"Surface Latitude:",
format!("{}", latitude),
GuiMessage::UpdateSurfaceLatitude(latitude - SURFACE_ANGLE_STEP),
GuiMessage::UpdateSurfaceLatitude(latitude + SURFACE_ANGLE_STEP),
GuiMessage::UpdateSurfaceLatitude(latitude - ANGLE_STEP),
GuiMessage::UpdateSurfaceLatitude(latitude + ANGLE_STEP),
);
let viewport_angle = self.surface_view_state.viewport_horizontal_opening_angle;
let viewport_angle_control_field = self.control_field(
"Horizontal Viewport Opening Angle:",
format!("{}", viewport_angle),
GuiMessage::UpdateViewportOpeningAngle(viewport_angle - ANGLE_STEP),
GuiMessage::UpdateViewportOpeningAngle(viewport_angle + ANGLE_STEP),
);
let planet_picker = self.planet_picker();
Column::new()
.push(self.time_control_fields())
.push(surface_longitude_control_field)
.push(surface_latitude_control_field)
.push(viewport_angle_control_field)
.push(planet_picker)
.width(iced::Length::Fill)
.align_items(Alignment::Center)
Expand All @@ -77,7 +86,7 @@ impl Gui {
fn observer_data(&self) -> (CartesianCoordinates, Direction) {
let body = match self.selected_body.as_ref() {
Some(body) => body,
None => return (ORIGIN, Z),
None => return (CartesianCoordinates::ORIGIN, Direction::Z),
};

let observer_equatorial_position = EquatorialCoordinates::new(
Expand All @@ -102,57 +111,69 @@ impl Gui {
(observer_position, observer_normal)
}

fn pixel_per_viewport_width(&self, canvas_width: f32) -> Float {
let opening_angle = self.surface_view_state.viewport_horizontal_opening_angle;
let viewport_width = (opening_angle / 2.).sin() * 2.; //Viewport is at unit distance
canvas_width / viewport_width
}

fn surface_view_canvas_position(
&self,
body: &CelestialBody,
observer_position: &CartesianCoordinates,
observer_normal: &Direction,
canvas_radius: f32,
) -> iced::Vector {
const PI_HALF: Float = PI / 2.;
pixel_per_viewport_width: Float,
) -> Option<iced::Vector> {
let relative_position = body.get_position() - observer_position;
let ecliptic_position = EclipticCoordinates::from_cartesian(&relative_position);
let surface_position = apparent_celestial_position(&ecliptic_position, observer_normal);
let x = surface_position.get_longitude().as_radians() / PI_HALF * canvas_radius;
let y = -surface_position.get_latitude().as_radians() / PI_HALF * canvas_radius; // y axis is inverted
iced::Vector::new(x as f32, y as f32)
let direction = Direction::from_cartesian(&relative_position);
let direction = direction_relative_to_surface_normal(&direction, observer_normal);
if direction.z() > 0.0 {
let x = direction.x() * pixel_per_viewport_width;
let y = -direction.y() * pixel_per_viewport_width; // y axis is inverted
Some(iced::Vector::new(x as f32, y as f32))
} else {
None
}
}

pub(super) fn surface_view_canvas(
&self,
renderer: &iced::Renderer,
bounds: iced::Rectangle,
) -> Vec<canvas::Geometry> {
let canvas_radius = bounds.size().width.min(bounds.size().height) / 2.;
let background =
self.surface_view_state
.background_cache
.draw(renderer, bounds.size(), |frame| {
let background = Path::circle(frame.center(), canvas_radius);
let background = Path::rectangle(Point::ORIGIN, bounds.size());
frame.fill(&background, Color::BLACK);
});

let (observer_position, observer_normal) = self.observer_data();

let pixel_per_viewport_width = self.pixel_per_viewport_width(bounds.width);
let bodies = self
.surface_view_state
.bodies_cache
.draw(renderer, bounds.size(), |frame| {
let bodies_path = Path::new(|path_builder| {
for body in self.celestial_bodies.iter() {
let pos = frame.center()
+ Self::surface_view_canvas_position(
body,
&observer_position,
&observer_normal,
canvas_radius,
);
path_builder.circle(pos, 3.0);

let mut name_widget = canvas::Text::default();
name_widget.color = Color::WHITE;
name_widget.content = body.get_name().to_string();
name_widget.position = pos;
frame.fill_text(name_widget);
let pos = self.surface_view_canvas_position(
body,
&observer_position,
&observer_normal,
pixel_per_viewport_width,
);
if let Some(pos) = pos {
let pos = frame.center() + pos;
path_builder.circle(pos, 3.0);

let mut name_widget = canvas::Text::default();
name_widget.color = Color::WHITE;
name_widget.content = body.get_name().to_string();
name_widget.position = pos;
frame.fill_text(name_widget);
}
}
});
frame.fill(&bodies_path, Color::WHITE);
Expand Down
Loading

0 comments on commit 0716d02

Please sign in to comment.