diff --git a/examples/al-easy-access-simbad-ned.html b/examples/al-easy-access-simbad-ned.html index 4803f250..6fe95f7e 100644 --- a/examples/al-easy-access-simbad-ned.html +++ b/examples/al-easy-access-simbad-ned.html @@ -20,6 +20,7 @@ showSettingsControl: true, showStackLayerControl: true, samp: true, + showCooGrid: true, }); aladin.addCatalog(A.catalogFromSimbad('M 82', 0.1, {onClick: 'showTable'})); diff --git a/src/core/al-api/src/angle_fmt.rs b/src/core/al-api/src/angle.rs similarity index 60% rename from src/core/al-api/src/angle_fmt.rs rename to src/core/al-api/src/angle.rs index 49ff3e97..cf1d410b 100644 --- a/src/core/al-api/src/angle_fmt.rs +++ b/src/core/al-api/src/angle.rs @@ -1,8 +1,6 @@ use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; -use std::fmt; - #[wasm_bindgen(raw_module = "../../js/libs/astro/coo.js")] extern "C" { #[wasm_bindgen(js_name = Format)] @@ -28,28 +26,11 @@ extern "C" { pub fn toDecimal(num: f64, prec: u8) -> String; } -#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] +use std::cmp::Eq; +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[serde(rename_all = "camelCase")] #[wasm_bindgen] -pub enum AngleSerializeFmt { - DMM, - DD, - DMS, - HMS, -} - -impl fmt::Display for AngleSerializeFmt { - // This trait requires `fmt` with this exact signature. - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Write strictly the first element into the supplied output - // stream: `f`. Returns `fmt::Result` which indicates whether the - // operation succeeded or failed. Note that `write!` uses syntax which - // is very similar to `println!`. - let str = match self { - Self::DMM => "DMM", - Self::DD => "DD", - Self::DMS => "DMS", - Self::HMS => "HMS", - }; - write!(f, "{}", str) - } -} +pub enum Formatter { + Sexagesimal, + Decimal +} \ No newline at end of file diff --git a/src/core/al-api/src/grid.rs b/src/core/al-api/src/grid.rs index 41b65f6e..f87a0c4d 100644 --- a/src/core/al-api/src/grid.rs +++ b/src/core/al-api/src/grid.rs @@ -1,12 +1,9 @@ -use wasm_bindgen::prelude::*; - use serde::{Deserialize, Serialize}; -use crate::angle_fmt::AngleSerializeFmt; +use crate::angle::Formatter; use super::color::ColorRGB; -#[wasm_bindgen] #[derive(Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct GridCfg { @@ -22,7 +19,7 @@ pub struct GridCfg { #[serde(default = "default_enabled")] pub enabled: Option, #[serde(default = "default_fmt")] - pub fmt: Option, + pub fmt: Option, } fn default_labels() -> Option { @@ -45,6 +42,6 @@ fn default_thickness() -> Option { None } -fn default_fmt() -> Option { +fn default_fmt() -> Option { None } diff --git a/src/core/al-api/src/lib.rs b/src/core/al-api/src/lib.rs index 0c254307..7962bcae 100644 --- a/src/core/al-api/src/lib.rs +++ b/src/core/al-api/src/lib.rs @@ -13,7 +13,7 @@ pub mod resources; pub mod cell; pub mod fov; pub mod image; -pub mod angle_fmt; +pub mod angle; pub trait Abort { type Item; diff --git a/src/core/src/app.rs b/src/core/src/app.rs index baa1284a..8ed59439 100644 --- a/src/core/src/app.rs +++ b/src/core/src/app.rs @@ -20,6 +20,7 @@ use crate::{ tile_fetcher::TileFetcherQueue, time::DeltaTime, }; +use crate::math::angle::ToAngle; use al_core::image::format::ChannelType; use wcs::WCS; @@ -421,7 +422,7 @@ impl App { let v = cell.vertices(); let proj2screen = |(lon, lat): &(f64, f64)| -> Option<[f64; 2]> { // 1. convert to xyzw - let xyzw = crate::math::lonlat::radec_to_xyzw(Angle(*lon), Angle(*lat)); + let xyzw = crate::math::lonlat::radec_to_xyzw(lon.to_angle(), lat.to_angle()); // 2. get it back to the camera frame system let xyzw = crate::coosys::apply_coo_system( CooSystem::ICRS, @@ -1325,7 +1326,7 @@ impl App { Ok(()) } - pub(crate) fn get_max_fov(&self) -> f64 { + pub(crate) fn get_max_fov(&self) -> Angle { self.projection.aperture_start() } diff --git a/src/core/src/camera/mod.rs b/src/core/src/camera/mod.rs index cc37623c..f04af27e 100644 --- a/src/core/src/camera/mod.rs +++ b/src/core/src/camera/mod.rs @@ -41,7 +41,7 @@ pub fn build_fov_coverage( let hpx_idxs_iter = vertices_iter.map(|v| { let (lon, lat) = crate::math::lonlat::xyzw_to_radec(&v); - ::healpix::nested::hash(depth, lon.0, lat.0) + ::healpix::nested::hash(depth, lon.to_radians(), lat.to_radians()) }); HEALPixCoverage::from_fixed_hpx_cells(depth, hpx_idxs_iter, Some(vertices.len())) diff --git a/src/core/src/camera/viewport.rs b/src/core/src/camera/viewport.rs index 2d74e5a2..e4db7baa 100644 --- a/src/core/src/camera/viewport.rs +++ b/src/core/src/camera/viewport.rs @@ -88,7 +88,6 @@ const MAX_DPI_LIMIT: f32 = 2.0; use crate::math; use crate::time::Time; use crate::Abort; -use crate::ArcDeg; impl CameraViewPort { pub fn new( gl: &WebGlContext, @@ -97,7 +96,7 @@ impl CameraViewPort { ) -> CameraViewPort { let last_user_action = UserAction::Starting; - let aperture = Angle(projection.aperture_start()); + let aperture = projection.aperture_start(); let w2m = Matrix4::identity(); let m2w = w2m; @@ -350,12 +349,12 @@ impl CameraViewPort { _ => true, }; - let aperture_start: Angle = ArcDeg(proj.aperture_start()).into(); + let aperture_start = proj.aperture_start(); self.clip_zoom_factor = if aperture > aperture_start { //al_core::log(&format!("a: {:?}, as: {:?}", aperture, aperture_start)); if can_unzoom_more { - aperture.0 / aperture_start.0 + aperture.to_radians() / aperture_start.to_radians() } else { 1.0 } @@ -363,8 +362,8 @@ impl CameraViewPort { // Compute the new clip zoom factor let a = aperture.abs(); - let v0 = math::lonlat::radec_to_xyzw(-a / 2.0, Angle(0.0)); - let v1 = math::lonlat::radec_to_xyzw(a / 2.0, Angle(0.0)); + let v0 = math::lonlat::radec_to_xyzw(-a / 2.0, 0.0.to_angle()); + let v1 = math::lonlat::radec_to_xyzw(a / 2.0, 0.0.to_angle()); // Vertex in the WCS of the FOV if self.width < self.height { diff --git a/src/core/src/coosys.rs b/src/core/src/coosys.rs index 34f4a023..371d158d 100644 --- a/src/core/src/coosys.rs +++ b/src/core/src/coosys.rs @@ -39,8 +39,8 @@ mod tests { let gal_lonlat = super::apply_coo_system(CooSystem::ICRS, CooSystem::GAL, &lonlat.vector()).lonlat(); - let gal_lon_deg = gal_lonlat.lon().0 * 360.0 / (2.0 * std::f64::consts::PI); - let gal_lat_deg = gal_lonlat.lat().0 * 360.0 / (2.0 * std::f64::consts::PI); + let gal_lon_deg = gal_lonlat.lon().to_degrees(); + let gal_lat_deg = gal_lonlat.lat().to_degrees(); assert_delta!(gal_lon_deg, 96.33723581, 1e-3); assert_delta!(gal_lat_deg, -60.18845577, 1e-3); @@ -56,8 +56,8 @@ mod tests { let lonlat: LonLatT = LonLatT::new(ArcDeg(0.0).into(), ArcDeg(0.0).into()); let j2000_lonlat = super::apply_coo_system(CooSystem::GAL, CooSystem::ICRS, &lonlat.vector()).lonlat(); - let j2000_lon_deg = j2000_lonlat.lon().0 * 360.0 / (2.0 * std::f64::consts::PI); - let j2000_lat_deg = j2000_lonlat.lat().0 * 360.0 / (2.0 * std::f64::consts::PI); + let j2000_lon_deg = j2000_lonlat.lon().to_degrees(); + let j2000_lat_deg = j2000_lonlat.lat().to_degrees(); assert_delta!(j2000_lon_deg, 266.40506655, 1e-3); assert_delta!(j2000_lat_deg, -28.93616241, 1e-3); @@ -77,8 +77,8 @@ mod tests { let gal_lonlat = super::apply_coo_system(CooSystem::ICRS, CooSystem::GAL, &icrs_pos); - let gal_lon_deg = gal_lonlat.lon().0 * 360.0 / (2.0 * std::f64::consts::PI); - let gal_lat_deg = gal_lonlat.lat().0 * 360.0 / (2.0 * std::f64::consts::PI); + let gal_lon_deg = gal_lonlat.lon().to_degrees(); + let gal_lat_deg = gal_lonlat.lat().to_degrees(); assert_delta!(gal_lon_deg, 0.0, 1e-3); assert_delta!(gal_lat_deg, 0.0, 1e-3); diff --git a/src/core/src/healpix/coverage.rs b/src/core/src/healpix/coverage.rs index ef0ffb0e..a4002c66 100644 --- a/src/core/src/healpix/coverage.rs +++ b/src/core/src/healpix/coverage.rs @@ -27,15 +27,14 @@ impl HEALPixCoverage { let lonlat = vertices_iter .map(|vertex| { let LonLatT(lon, lat) = vertex.lonlat(); - //let (lon, lat) = math::lonlat::xyzw_to_radec(&vertex); - (lon.0, lat.0) + (lon.to_radians(), lat.to_radians()) }) .collect::>(); let LonLatT(in_lon, in_lat) = inside.lonlat(); let moc = RangeMOC::from_polygon_with_control_point( &lonlat[..], - (in_lon.0, in_lat.0), + (in_lon.to_radians(), in_lat.to_radians()), depth, CellSelection::All, ); @@ -84,11 +83,11 @@ impl HEALPixCoverage { pub fn contains_coo(&self, coo: &Vector4) -> bool { let (lon, lat) = math::lonlat::xyzw_to_radec(coo); - self.0.is_in(lon.0, lat.0) + self.0.is_in(lon.to_radians(), lat.to_radians()) } pub fn contains_lonlat(&self, lonlat: &LonLatT) -> bool { - self.0.is_in(lonlat.lon().0, lonlat.lat().0) + self.0.is_in(lonlat.lon().to_radians(), lonlat.lat().to_radians()) } // O(log2(N)) diff --git a/src/core/src/healpix/utils.rs b/src/core/src/healpix/utils.rs index 080acfd8..a5ca2a21 100644 --- a/src/core/src/healpix/utils.rs +++ b/src/core/src/healpix/utils.rs @@ -1,5 +1,6 @@ use crate::healpix::cell::HEALPixCell; -use crate::math::{angle::Angle, lonlat::LonLatT}; +use crate::math::lonlat::LonLatT; +use crate::math::angle::ToAngle; /// A simple wrapper around sore core methods /// of cdshealpix /// @@ -17,15 +18,15 @@ pub fn vertices_lonlat(cell: &HEALPixCell) -> [LonLatT; 4] { let lon = S::from(*lon).unwrap_abort(); let lat = S::from(*lat).unwrap_abort(); - (lon, lat) + (lon.to_angle(), lat.to_angle()) }) .unzip(); [ - LonLatT::new(Angle(lon[0]), Angle(lat[0])), - LonLatT::new(Angle(lon[1]), Angle(lat[1])), - LonLatT::new(Angle(lon[2]), Angle(lat[2])), - LonLatT::new(Angle(lon[3]), Angle(lat[3])), + LonLatT::new(lon[0], lat[0]), + LonLatT::new(lon[1], lat[1]), + LonLatT::new(lon[2], lat[2]), + LonLatT::new(lon[3], lat[3]), ] } use crate::Abort; @@ -40,13 +41,13 @@ pub fn grid_lonlat(cell: &HEALPixCell, n_segments_by_side: u16) -> let lon = S::from(*lon).unwrap_abort(); let lat = S::from(*lat).unwrap_abort(); - LonLatT::new(Angle(lon), Angle(lat)) + LonLatT::new(lon.to_angle(), lat.to_angle()) }) .collect() } pub fn hash_with_dxdy(depth: u8, lonlat: &LonLatT) -> (u64, f64, f64) { - healpix::nested::hash_with_dxdy(depth, lonlat.lon().0, lonlat.lat().0) + healpix::nested::hash_with_dxdy(depth, lonlat.lon().to_radians(), lonlat.lat().to_radians()) } pub const MEAN_HPX_CELL_RES: &[f64; 30] = &[ diff --git a/src/core/src/lib.rs b/src/core/src/lib.rs index fa85fd99..38309b62 100644 --- a/src/core/src/lib.rs +++ b/src/core/src/lib.rs @@ -562,7 +562,7 @@ impl WebClient { /// the sinus would be 180 degrees. #[wasm_bindgen(js_name = getMaxFieldOfView)] pub fn get_max_fov(&mut self) -> f64 { - self.app.get_max_fov() + self.app.get_max_fov().to_degrees() } /// Get the clip zoom factor of the view diff --git a/src/core/src/math/angle.rs b/src/core/src/math/angle.rs index f7ecc660..ae893e77 100644 --- a/src/core/src/math/angle.rs +++ b/src/core/src/math/angle.rs @@ -1,4 +1,5 @@ use cgmath::BaseFloat; +use crate::Abort; // ArcDeg wrapper structure #[derive(Clone, Copy)] pub struct ArcDeg(pub T); @@ -6,23 +7,6 @@ pub struct ArcDeg(pub T); //pub const TWICE_PI: f64 = 6.28318530718; pub const PI: f64 = std::f64::consts::PI; -impl ArcDeg -where - T: BaseFloat, -{ - fn get_frac_minutes(&self) -> ArcMin { - let deg = *self; - - let frac = deg.fract(); - let minutes_per_degree = T::from(60_f32).unwrap_abort(); - ArcMin(frac * minutes_per_degree) - } - - fn truncate(&mut self) { - *self = Self((*self).trunc()); - } -} - use cgmath::{Deg, Rad}; use serde::Deserialize; // Convert a Rad to an ArcDeg @@ -71,18 +55,6 @@ where #[derive(Clone, Copy)] pub struct ArcHour(pub T); -impl ArcHour -where - T: BaseFloat, -{ - fn get_frac_minutes(&self) -> ArcMin { - let hour = *self; - - let frac = hour.fract(); - let minutes_per_hour = T::from(60_f64).unwrap_abort(); - ArcMin(minutes_per_hour * frac) - } -} impl From> for ArcHour where @@ -122,19 +94,6 @@ where #[derive(Clone, Copy)] pub struct ArcMin(pub T); -impl ArcMin -where - T: BaseFloat, -{ - fn get_frac_seconds(&self) -> ArcSec { - let min: ArcMin = *self; - - let frac = min.fract(); - let seconds_per_min = T::from(60_f64).unwrap_abort(); - ArcSec(seconds_per_min * frac) - } -} - // Convert a Rad to an ArcMin impl From> for ArcMin where @@ -241,6 +200,8 @@ where } } +use al_api::angle::Format; +/* pub enum SerializeFmt { DMS, HMS, @@ -269,7 +230,7 @@ impl SerializeFmt { Self::DD => DD::to_string(angle), } } -} +}*/ /*pub trait SerializeToString { fn to_string(&self) -> String; @@ -284,6 +245,7 @@ where } }*/ +/* pub struct DMS; pub struct HMS; pub struct DMM; @@ -315,7 +277,7 @@ impl FormatType for DMM { result } } -use crate::Abort; + impl FormatType for DMS { fn to_string(angle: Angle) -> String { let angle = Rad(angle.0); @@ -361,94 +323,106 @@ impl FormatType for HMS { result } -} +}*/ + #[derive(Clone, Copy, Debug, Eq, Hash, Deserialize)] #[serde(rename_all = "camelCase")] #[repr(C)] -pub struct Angle(pub S); +pub struct Angle { + pub rad: S, + fmt: AngleFormatter, +} impl Angle where S: BaseFloat, { pub fn new>>(angle: T) -> Angle { let radians: Rad = angle.into(); - Angle(radians.0) + Angle { rad: radians.0, fmt: AngleFormatter::default() } } pub fn cos(&self) -> S { - self.0.cos() + self.rad.cos() } pub fn sin(&self) -> S { - self.0.sin() + self.rad.sin() } pub fn tan(&self) -> S { - self.0.tan() + self.rad.tan() } pub fn asin(self) -> S { - self.0.asin() + self.rad.asin() } pub fn acos(self) -> S { - self.0.acos() + self.rad.acos() } pub fn atan(self) -> S { - self.0.atan() + self.rad.atan() } pub fn atan2(self, other: Self) -> S { - self.0.atan2(other.0) + self.rad.atan2(other.rad) } pub fn floor(self) -> Self { - Angle(self.0.floor()) + self.rad.floor().to_angle() } pub fn ceil(self) -> Self { - Angle(self.0.ceil()) + self.rad.ceil().to_angle() } pub fn round(self) -> Self { - Angle(self.0.round()) + self.rad.round().to_angle() } pub fn trunc(self) -> Self { - Angle(self.0.trunc()) + self.rad.trunc().to_angle() } pub fn fract(self) -> S { - self.0.fract() + self.rad.fract() } pub fn abs(self) -> Self { - Angle(self.0.abs()) + self.rad.abs().to_angle() } pub fn max(self, other: Self) -> Self { - Angle(self.0.max(other.0)) + self.rad.max(other.rad).to_angle() } pub fn min(self, other: Self) -> Self { - Angle(self.0.min(other.0)) + self.rad.min(other.rad).to_angle() } pub fn min_value() -> Self { - Angle(S::min_value()) + S::min_value().to_angle() } pub fn max_value() -> Self { - Angle(S::max_value()) + S::max_value().to_angle() } pub fn to_radians(&self) -> S { - self.0 + self.rad } pub fn to_degrees(&self) -> S { - self.0.to_degrees() + self.rad.to_degrees() + } + + pub fn to_hours(&self) -> S { + self.to_degrees() / S::from(15.0).unwrap() + } + + pub fn set_format(&mut self, fmt: AngleFormatter) { + self.fmt = fmt; } } @@ -464,7 +438,7 @@ where S: BaseFloat, { fn to_angle(self) -> Angle { - Angle(self) + Angle { rad: self, fmt: Default::default() } } } @@ -474,7 +448,7 @@ where S: BaseFloat, { fn from(rad: Rad) -> Self { - Angle(rad.0) + rad.0.to_angle() } } impl From> for Rad @@ -482,7 +456,7 @@ where S: BaseFloat, { fn from(angle: Angle) -> Self { - Rad(angle.0) + Rad(angle.rad) } } @@ -501,7 +475,7 @@ where { fn eq(&self, other: &T) -> bool { let angle: Angle = (*other).into(); - angle.0 == self.0 + angle.rad == self.rad } } @@ -513,7 +487,7 @@ where { fn partial_cmp(&self, other: &T) -> Option { let angle: Angle = (*other).into(); - self.0.partial_cmp(&angle.0) + self.rad.partial_cmp(&angle.rad) } } @@ -524,7 +498,7 @@ where { fn from(deg: ArcDeg) -> Self { let rad: Rad = deg.into(); - Angle(rad.0) + rad.0.to_angle() } } impl From> for ArcDeg @@ -545,7 +519,7 @@ where { fn from(min: ArcMin) -> Self { let rad: Rad = min.into(); - Angle(rad.0) + rad.0.to_angle() } } // Convert from ArcSec @@ -555,28 +529,11 @@ where { fn from(sec: ArcSec) -> Self { let rad: Rad = sec.into(); - Angle(rad.0) + rad.0.to_angle() } } -/* -impl PartialEq for Angle -where - S: BaseFloat + !AngleUnit, -{ - fn eq(&self, other: &S) -> bool { - self.0 == *other - } -} -*/ + use std::cmp::Ordering; -/*impl PartialOrd for Angle -where - S: BaseFloat, -{ - fn partial_cmp(&self, other: &S) -> Option { - self.0.partial_cmp(other) - } -}*/ use std::ops::Div; impl Div for Angle @@ -586,8 +543,8 @@ where type Output = Self; fn div(self, rhs: Self) -> Self::Output { - let angle = self.0 / rhs.0; - Angle(angle) + let rad = self.rad / rhs.rad; + rad.to_angle() } } impl Div for Angle @@ -597,8 +554,8 @@ where type Output = Self; fn div(self, rhs: S) -> Self::Output { - let angle = self.0 / rhs; - Angle(angle) + let rad = self.rad / rhs; + rad.to_angle() } } @@ -610,8 +567,8 @@ where type Output = Self; fn mul(self, rhs: Self) -> Self::Output { - let angle = self.0 * rhs.0; - Angle(angle) + let angle = self.rad * rhs.rad; + angle.to_angle() } } impl Mul for Angle @@ -621,8 +578,8 @@ where type Output = Self; fn mul(self, rhs: S) -> Self::Output { - let angle = self.0 * rhs; - Angle(angle) + let angle = self.rad * rhs; + angle.to_angle() } } @@ -634,8 +591,8 @@ where type Output = Self; fn sub(self, other: Self) -> Self::Output { - let angle = self.0 - other.0; - Angle(angle) + let angle = self.rad - other.rad; + angle.to_angle() } } impl Sub for Angle @@ -645,8 +602,8 @@ where type Output = Self; fn sub(self, other: S) -> Self::Output { - let angle = self.0 - other; - Angle(angle) + let angle = self.rad - other; + angle.to_angle() } } @@ -658,8 +615,8 @@ where type Output = Self; fn add(self, other: Self) -> Self::Output { - let angle = self.0 + other.0; - Angle(angle) + let angle = self.rad + other.rad; + angle.to_angle() } } impl Add for Angle @@ -669,8 +626,8 @@ where type Output = Self; fn add(self, other: S) -> Self::Output { - let angle = self.0 + other; - Angle(angle) + let angle = self.rad + other; + angle.to_angle() } } @@ -718,8 +675,56 @@ where type Output = Self; fn rem(self, other: Self) -> Self::Output { - let angle = self.0 % other.0; - Angle(angle) + let angle = self.rad % other.rad; + angle.to_angle() + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash, Deserialize)] +pub enum AngleFormatter { + Sexagesimal { + /// Number of digit of precision for the unit value + /// (interpreted as hours or degrees depending of the hours boolean field) + prec: u8, + /// Whether a '+' is added + plus: bool, + /// HMS or DMS + hours: bool, + }, + Decimal { + /// Number of digit of precision + prec: u8, + } +} + +impl Default for AngleFormatter { + fn default() -> Self { + AngleFormatter::Decimal { prec: 8 } + } +} + +use std::fmt::Display; +impl Display for Angle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.fmt { + AngleFormatter::Sexagesimal { prec, plus, hours } => { + let unit = if hours { + self.to_hours() + } else { + self.to_degrees() + }; + // Round at a specific number of digit of precision + let pw = 10.0_f64.powi(prec as i32); + let unit = (unit * pw).round() / pw; + + // Format the unit value to sexagesimal. + // The precision 8 corresponds to the formatting: deg/hour min sec.ddd + write!(f, "{}", Format::toSexagesimal(unit, 8, plus)) + }, + AngleFormatter::Decimal { prec } => { + write!(f, "{:.1$}°", self.to_degrees(), prec as usize) + } + } } } @@ -730,18 +735,18 @@ where { type Output = Self; fn neg(self) -> Self::Output { - Angle(-self.0) + (-self.rad).to_angle() } } use al_core::{shader::UniformType, WebGlContext}; use web_sys::WebGlUniformLocation; impl UniformType for Angle { fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) { - gl.uniform1f(location, value.0); + gl.uniform1f(location, value.rad); } } impl UniformType for Angle { fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) { - gl.uniform1f(location, value.0 as f32); + gl.uniform1f(location, value.rad as f32); } } diff --git a/src/core/src/math/lonlat.rs b/src/core/src/math/lonlat.rs index 5a2f6460..19f8b0ea 100644 --- a/src/core/src/math/lonlat.rs +++ b/src/core/src/math/lonlat.rs @@ -26,8 +26,8 @@ where /// * ``lon`` - Longitude /// * ``lat`` - Latitude pub fn new(mut lon: Angle, lat: Angle) -> LonLatT { - if lon.0 < S::zero() { - lon.0 = lon.0 + S::from(TWICE_PI).unwrap_abort(); + if lon.to_radians() < S::zero() { + lon = lon + S::from(TWICE_PI).unwrap_abort(); } LonLatT(lon, lat) @@ -156,33 +156,31 @@ where #[inline] pub fn ang_between_lonlat(lonlat1: LonLatT, lonlat2: LonLatT) -> Angle { let abs_diff_lon = (lonlat1.lon() - lonlat2.lon()).abs(); - Angle( - (lonlat1.lat().sin() * lonlat2.lat().sin() - + lonlat1.lat().cos() * lonlat2.lat().cos() * abs_diff_lon.cos()) - .acos(), - ) + (lonlat1.lat().sin() * lonlat2.lat().sin() + + lonlat1.lat().cos() * lonlat2.lat().cos() * abs_diff_lon.cos()) + .acos().to_angle() } #[inline] pub fn xyz_to_radec(v: &Vector3) -> (Angle, Angle) { - let lon = Angle(v.x.atan2(v.z)); - let lat = Angle(v.y.atan2((v.x * v.x + v.z * v.z).sqrt())); + let lon = (v.x.atan2(v.z)).to_angle(); + let lat = (v.y.atan2((v.x * v.x + v.z * v.z).sqrt())).to_angle(); (lon, lat) } #[inline] pub fn xyzw_to_radec(v: &Vector4) -> (Angle, Angle) { - let lon = Angle(v.x.atan2(v.z)); - let lat = Angle(v.y.atan2((v.x * v.x + v.z * v.z).sqrt())); + let lon = (v.x.atan2(v.z)).to_angle(); + let lat = (v.y.atan2((v.x * v.x + v.z * v.z).sqrt())).to_angle(); (lon, lat) } #[inline] pub fn radec_to_xyz(theta: Angle, delta: Angle) -> Vector3 { - let (ds, dc) = delta.0.sin_cos(); - let (ts, tc) = theta.0.sin_cos(); + let (ds, dc) = delta.to_radians().sin_cos(); + let (ts, tc) = theta.to_radians().sin_cos(); Vector3::::new(dc * ts, ds, dc * tc) } diff --git a/src/core/src/math/projection/mod.rs b/src/core/src/math/projection/mod.rs index a64eae80..143af311 100644 --- a/src/core/src/math/projection/mod.rs +++ b/src/core/src/math/projection/mod.rs @@ -19,8 +19,10 @@ use cgmath::Vector2; pub mod coo_space; pub mod domain; -use domain::{basic, full::FullScreen}; +use crate::math::angle::ToAngle; +use domain::{basic, full::FullScreen}; +use crate::math::angle::Angle; /* S <-> NDC space conversion methods */ pub fn screen_to_ndc_space( pos_screen_space: &XYScreen, @@ -304,7 +306,7 @@ impl ProjectionType { } }*/ - pub fn bounds_size_ratio(&self) -> f64 { + pub const fn bounds_size_ratio(&self) -> f64 { match self { // Zenithal projections /* TAN, Gnomonic projection */ @@ -355,17 +357,17 @@ impl ProjectionType { } } - pub fn aperture_start(&self) -> f64 { + pub fn aperture_start(&self) -> Angle { match self { // Zenithal projections /* TAN, Gnomonic projection */ - ProjectionType::Tan(_) => 150.0, + ProjectionType::Tan(_) => 150.0_f64.to_radians().to_angle(), /* STG, Stereographic projection */ - ProjectionType::Stg(_) => 360.0, + ProjectionType::Stg(_) => 360.0_f64.to_radians().to_angle(), /* SIN, Orthographic */ - ProjectionType::Sin(_) => 180.0, + ProjectionType::Sin(_) => 180.0_f64.to_radians().to_angle(), /* ZEA, Equal-area */ - ProjectionType::Zea(_) => 360.0, + ProjectionType::Zea(_) => 360.0_f64.to_radians().to_angle(), /* FEYE, Fish-eyes */ //ProjectionType::Feye(_) => 190.0, /* AIR, */ @@ -379,9 +381,9 @@ impl ProjectionType { // Pseudo-cylindrical projections /* AIT, Aitoff */ - ProjectionType::Ait(_) => 360.0, + ProjectionType::Ait(_) => 360.0_f64.to_radians().to_angle(), // MOL, Mollweide */ - ProjectionType::Mol(_) => 360.0, + ProjectionType::Mol(_) => 360.0_f64.to_radians().to_angle(), // PAR, */ //ProjectionType::Par(_) => 360.0, // SFL, */ @@ -389,7 +391,7 @@ impl ProjectionType { // Cylindrical projections // MER, Mercator */ - ProjectionType::Mer(_) => 360.0, + ProjectionType::Mer(_) => 360.0_f64.to_radians().to_angle(), // CAR, */ //ProjectionType::Car(_) => 360.0, // CEA, */ diff --git a/src/core/src/math/rotation.rs b/src/core/src/math/rotation.rs index d66d9c96..c0ccd518 100644 --- a/src/core/src/math/rotation.rs +++ b/src/core/src/math/rotation.rs @@ -2,6 +2,7 @@ use crate::math; use cgmath::{BaseFloat, InnerSpace}; use cgmath::{Euler, Quaternion}; use cgmath::{Vector3, Vector4}; +use crate::math::angle::ToAngle; #[derive(Clone, Copy, Debug)] // Internal structure of a rotation, a quaternion @@ -159,7 +160,7 @@ where let a = m.x.z.atan2(m.z.z); let b = (-m.z.y).atan2((S::one() - m.z.y * m.z.y).sqrt()); let c = m.x.y.atan2(m.y.y); - (Angle(a), Angle(b), Angle(c)) + (a.to_angle(), b.to_angle(), c.to_angle()) } } diff --git a/src/core/src/math/vector.rs b/src/core/src/math/vector.rs index fb2c6cfc..df27ae13 100644 --- a/src/core/src/math/vector.rs +++ b/src/core/src/math/vector.rs @@ -1,15 +1,16 @@ use crate::math::angle::Angle; use cgmath::{BaseFloat, InnerSpace, Vector2, Vector3}; +use crate::math::angle::ToAngle; #[inline] pub fn angle2(ab: &Vector2, bc: &Vector2) -> Angle { - Angle((ab.dot(*bc)).acos()) + ((ab.dot(*bc)).acos()).to_angle() } #[inline] pub fn angle3(x: &Vector3, y: &cgmath::Vector3) -> Angle { let theta = x.cross(*y).magnitude().atan2(x.dot(*y)); - Angle(theta) + theta.to_angle() } #[inline] diff --git a/src/core/src/renderable/grid/label.rs b/src/core/src/renderable/grid/label.rs index 75804e9d..ee135be7 100644 --- a/src/core/src/renderable/grid/label.rs +++ b/src/core/src/renderable/grid/label.rs @@ -6,12 +6,13 @@ use crate::ProjectionType; use cgmath::InnerSpace; use cgmath::Vector3; -use crate::math::angle::SerializeFmt; use crate::math::lonlat::LonLat; use crate::math::projection::coo_space::XYScreen; use crate::math::TWICE_PI; use crate::math::angle::ToAngle; +use crate::math::angle::AngleFormatter; +use al_api::angle::Formatter; use cgmath::Vector2; use core::ops::Range; @@ -22,7 +23,6 @@ pub enum LabelOptions { Centered, OnSide, } - #[derive(Debug)] pub struct Label { // The position @@ -39,7 +39,8 @@ impl Label { options: LabelOptions, camera: &CameraViewPort, projection: &ProjectionType, - _fmt: &SerializeFmt, + fmt: Formatter, + grid_decimal_prec: u8 ) -> Option { let fov = camera.get_field_of_view(); let d = if fov.contains_north_pole() { @@ -76,8 +77,18 @@ impl Label { lon += TWICE_PI; } - //let content = fmt.to_string(lon.to_angle()); - let content = al_api::angle_fmt::Format::toSexagesimal(lon.to_degrees() / 15.0, 8, false); + let mut angle = lon.to_angle(); + let fmt = match fmt { + Formatter::Decimal => { + AngleFormatter::Decimal { prec: grid_decimal_prec } + }, + Formatter::Sexagesimal => { + // Sexagesimal formatting for longitudes is HMS + AngleFormatter::Sexagesimal { prec: grid_decimal_prec, plus: false, hours: true } + } + }; + angle.set_format(fmt); + let content = angle.to_string(); let position = if !fov.is_allsky() { d1 + OFF_TANGENT * dt - OFF_BI_TANGENT * db @@ -101,6 +112,8 @@ impl Label { options: LabelOptions, camera: &CameraViewPort, projection: &ProjectionType, + fmt: Formatter, + grid_decimal_prec: u8 ) -> Option { let lonlat = match options { LabelOptions::Centered => { @@ -130,8 +143,18 @@ impl Label { let dt = (d2 - d1).normalize(); let db = Vector2::new(dt.y.abs(), dt.x.abs()); - //let content = SerializeFmt::DMS.to_string(lonlat.lat()); - let content = al_api::angle_fmt::Format::toSexagesimal(lonlat.lat().to_degrees(), 7, false); + let mut angle = lat.to_angle(); + let fmt = match fmt { + Formatter::Decimal => { + AngleFormatter::Decimal { prec: grid_decimal_prec } + }, + Formatter::Sexagesimal => { + // Sexagesimal formatting for latitudes is DMS with an optional '+' character + AngleFormatter::Sexagesimal { prec: grid_decimal_prec, plus: true, hours: false } + } + }; + angle.set_format(fmt); + let content = angle.to_string(); let fov = camera.get_field_of_view(); let position = if !fov.is_allsky() && !fov.contains_pole() { diff --git a/src/core/src/renderable/grid/meridian.rs b/src/core/src/renderable/grid/meridian.rs index 12a2a7e0..fa4197c6 100644 --- a/src/core/src/renderable/grid/meridian.rs +++ b/src/core/src/renderable/grid/meridian.rs @@ -1,3 +1,5 @@ +use al_api::angle::Formatter; + use super::label::{Label, LabelOptions}; use crate::math::lonlat::LonLat; use crate::math::sph_geom::region::Intersection; @@ -7,14 +9,14 @@ use core::ops::Range; use crate::math::MINUS_HALF_PI; use crate::ProjectionType; -use super::angle::SerializeFmt; use crate::math::HALF_PI; pub fn get_intersecting_meridian( lon: f64, camera: &CameraViewPort, projection: &ProjectionType, - fmt: &SerializeFmt, + fmt: Formatter, + grid_decimal_prec: u8 ) -> Option { let fov = camera.get_field_of_view(); if fov.contains_both_poles() { @@ -25,6 +27,7 @@ pub fn get_intersecting_meridian( camera, projection, fmt, + grid_decimal_prec ); Some(meridian) } else { @@ -39,6 +42,7 @@ pub fn get_intersecting_meridian( camera, projection, fmt, + grid_decimal_prec ); Some(meridian) } @@ -56,7 +60,7 @@ pub fn get_intersecting_meridian( lat1..MINUS_HALF_PI }; - Meridian::new(lon, &lat, LabelOptions::OnSide, camera, projection, fmt) + Meridian::new(lon, &lat, LabelOptions::OnSide, camera, projection, fmt, grid_decimal_prec) } 2 => { // full intersection @@ -73,56 +77,18 @@ pub fn get_intersecting_meridian( camera, projection, fmt, + grid_decimal_prec ) } - _ => { - /*let mut vertices = vertices.into_vec(); - // One segment over two will be in the field of view - vertices.push(Vector4::new(0.0, 1.0, 0.0, 1.0)); - vertices.push(Vector4::new(0.0, -1.0, 0.0, 1.0)); - - vertices.sort_by(|i1, i2| { - i1.y.total_cmp(&i2.y) - }); - - let v1 = &vertices[0]; - let v2 = &vertices[1]; - - // meridian are part of great circles so the mean between v1 & v2 also lies on it - let vm = (v1 + v2).truncate().normalize(); - - let vertices = if !fov.contains_south_pole() { - &vertices[1..] - } else { - &vertices - }; - - let line_vertices = vertices.iter().zip(vertices.iter().skip(1)) - .step_by(2) - .map(|(i1, i2)| { - line::great_circle_arc::project( - lon, - i1.lat().to_radians(), - lon, - i2.lat().to_radians(), - camera, - projection - ) - }) - .flatten() - .collect::>(); - - let label = Label::from_meridian(&v1.lonlat(), camera, projection, fmt); - */ - Meridian::new( - lon, - &(-HALF_PI..HALF_PI), - LabelOptions::OnSide, - camera, - projection, - fmt, - ) - } + _ => Meridian::new( + lon, + &(-HALF_PI..HALF_PI), + LabelOptions::OnSide, + camera, + projection, + fmt, + grid_decimal_prec + ) }; Some(meridian) @@ -139,7 +105,6 @@ pub struct Meridian { indices: Vec>, label: Option