mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2026-04-28 11:53:18 -07:00
New option to make the north pole remain up to the view
* lockNorthUp new Aladin init option. False by default. * setRotation is still allowed if lockNorthUp=true but when panning, the view resets its orientation so that the north pole is up
This commit is contained in:
committed by
Matthieu Baumann
parent
addbd555c1
commit
62f9e61978
@@ -4,7 +4,8 @@
|
||||
|
||||
### What's Changed
|
||||
|
||||
* [perf] perform CPU computations with Vec3 and Matrix3 and not 4 dimensions matrices/vectors.
|
||||
* [perf] perform CPU computations with Vec3 and Matrix3 and not 4 dimensions matrices/vectors
|
||||
* [feat] lockNorthUp Aladin object new option locking the north pole up to the view center
|
||||
|
||||
## Released
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
fullScreen: true,
|
||||
samp: true,
|
||||
realFullscreen: true,
|
||||
lockNorthUp: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -74,6 +74,7 @@ pub struct App {
|
||||
// Task executor
|
||||
//exec: Rc<RefCell<TaskExecutor>>,
|
||||
inertia: Option<Inertia>,
|
||||
north_up: bool,
|
||||
disable_inertia: Rc<RefCell<bool>>,
|
||||
dist_dragging: f32,
|
||||
time_start_dragging: Time,
|
||||
@@ -243,7 +244,7 @@ impl App {
|
||||
catalog_loaded,
|
||||
|
||||
tile_fetcher,
|
||||
|
||||
north_up: false,
|
||||
colormaps,
|
||||
projection,
|
||||
|
||||
@@ -1520,11 +1521,11 @@ impl App {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let now = Time::now();
|
||||
let dragging_duration = (now - self.time_start_dragging).as_secs();
|
||||
let dragging_vel = self.dist_dragging / dragging_duration;
|
||||
|
||||
let _dist_dragging = self.dist_dragging;
|
||||
// Detect if there has been a recent acceleration
|
||||
// It is also possible that the dragging time is too short and if it is the case, trigger the inertia
|
||||
let recent_acceleration = (Time::now() - self.time_mouse_high_vel).as_secs() < 0.1
|
||||
@@ -1545,20 +1546,20 @@ impl App {
|
||||
let ampl = delta_angle * (dragging_vel as f64) * 5e-3;
|
||||
//let ampl = (dragging_vel * 0.01) as f64;
|
||||
|
||||
self.inertia = Some(Inertia::new(ampl.to_radians(), axis))
|
||||
self.inertia = Some(Inertia::new(ampl.to_radians(), axis, self.north_up))
|
||||
}
|
||||
|
||||
pub(crate) fn set_view_center_pos_angle(&mut self, theta: ArcDeg<f64>) {
|
||||
pub(crate) fn set_position_angle(&mut self, theta: ArcDeg<f64>) {
|
||||
self.camera
|
||||
.set_center_pos_angle(theta.into(), &self.projection);
|
||||
.set_position_angle(theta.into(), &self.projection);
|
||||
// New tiles can be needed and some tiles can be removed
|
||||
self.request_for_new_tiles = true;
|
||||
|
||||
self.request_redraw = true;
|
||||
}
|
||||
|
||||
pub(crate) fn get_north_shift_angle(&self) -> Angle<f64> {
|
||||
self.camera.get_center_pos_angle()
|
||||
pub(crate) fn get_position_angle(&self) -> Angle<f64> {
|
||||
self.camera.get_position_angle()
|
||||
}
|
||||
|
||||
pub(crate) fn set_fov(&mut self, fov: f64) {
|
||||
@@ -1592,21 +1593,64 @@ impl App {
|
||||
.screen_to_model_space(&Vector2::new(s2x, s2y), &self.camera),
|
||||
) {
|
||||
let prev_pos = w1;
|
||||
//let cur_pos = w1.truncate();
|
||||
let cur_pos = w2;
|
||||
//let next_pos = w2.truncate();
|
||||
if prev_pos != cur_pos {
|
||||
/* 1. Rotate by computing the angle between the last and current position */
|
||||
let prev_cam_position = self.camera.get_center().clone();
|
||||
|
||||
// Apply the rotation to the camera to
|
||||
// go from the current pos to the next position
|
||||
let axis = prev_pos.cross(cur_pos).normalize();
|
||||
if self.north_up {
|
||||
let lonlat1 = prev_pos.lonlat();
|
||||
let lonlat2 = cur_pos.lonlat();
|
||||
|
||||
let d = math::vector::angle3(&prev_pos, &cur_pos);
|
||||
let dlon = lonlat2.lon() - lonlat1.lon();
|
||||
let dlat = lonlat2.lat() - lonlat1.lat();
|
||||
|
||||
self.camera.apply_lonlat_rotation(dlon, dlat, &self.projection);
|
||||
|
||||
self.prev_cam_position = *self.camera.get_center();
|
||||
self.camera.apply_rotation(&(-axis), d, &self.projection);
|
||||
// Detect if a pole has been crossed
|
||||
|
||||
let north_pole = Vector3::new(0.0, 1.0, 0.0);
|
||||
let south_pole = Vector3::new(0.0, -1.0, 0.0);
|
||||
let cross_north_pole = crate::math::lonlat::is_in(&prev_cam_position, &self.camera.get_center(), &north_pole);
|
||||
let cross_south_pole = crate::math::lonlat::is_in(&prev_cam_position, &self.camera.get_center(), &south_pole);
|
||||
|
||||
let cross_pole = cross_north_pole | cross_south_pole;
|
||||
|
||||
// Detect if a pole has been crossed
|
||||
let center = if cross_pole {
|
||||
&prev_cam_position
|
||||
} else {
|
||||
self.camera.get_center()
|
||||
};
|
||||
|
||||
let fov = self.camera.get_aperture();
|
||||
|
||||
let pole = if center.y >= 0.0 {
|
||||
north_pole
|
||||
} else {
|
||||
south_pole
|
||||
};
|
||||
|
||||
let c2p = crate::math::vector::angle3(center, &pole).to_radians();
|
||||
let near_pole = c2p.abs() < 5e-3 * fov;
|
||||
if near_pole || cross_pole {
|
||||
// too near to the pole
|
||||
let axis = center.cross(pole).normalize();
|
||||
use crate::math::rotation::Rotation;
|
||||
let new_center = Rotation::from_axis_angle(&axis, (-5e-3 * fov).to_angle()).rotate(&pole);
|
||||
|
||||
self.camera.set_center_xyz(&new_center, &self.projection);
|
||||
self.camera.set_position_angle(0.0.to_angle(), &self.projection);
|
||||
}
|
||||
} else {
|
||||
/* 1. Rotate by computing the angle between the last and current position */
|
||||
|
||||
let d = math::vector::angle3(&prev_pos, &cur_pos);
|
||||
let axis = prev_pos.cross(cur_pos).normalize();
|
||||
|
||||
self.camera.apply_axis_rotation(&(-axis), d, &self.projection);
|
||||
}
|
||||
|
||||
self.prev_cam_position = prev_cam_position;
|
||||
self.request_for_new_tiles = true;
|
||||
}
|
||||
} else {
|
||||
@@ -1614,6 +1658,10 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lock_north_up(&mut self) {
|
||||
self.north_up = true;
|
||||
}
|
||||
|
||||
pub(crate) fn add_cmap(&mut self, label: String, cmap: Colormap) -> Result<(), JsValue> {
|
||||
self.colormaps.add_cmap(label, cmap)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ const ID_R: &Matrix3<f64> = &Matrix3::new(
|
||||
0.0, 0.0, 1.0,
|
||||
);
|
||||
|
||||
use cgmath::{Vector3, InnerSpace};
|
||||
use super::{fov::FieldOfView, view_hpx_cells::ViewHpxCells};
|
||||
use crate::healpix::cell::HEALPixCell;
|
||||
use crate::healpix::coverage::HEALPixCoverage;
|
||||
@@ -91,7 +92,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use crate::LonLatT;
|
||||
use cgmath::{SquareMatrix, Vector3};
|
||||
use cgmath::SquareMatrix;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
const MAX_DPI_LIMIT: f32 = 2.0;
|
||||
@@ -621,7 +622,7 @@ impl CameraViewPort {
|
||||
self.texture_depth
|
||||
}
|
||||
|
||||
pub fn apply_rotation(
|
||||
pub fn apply_axis_rotation(
|
||||
&mut self,
|
||||
axis: &cgmath::Vector3<f64>,
|
||||
angle: Angle<f64>,
|
||||
@@ -634,14 +635,29 @@ impl CameraViewPort {
|
||||
self.update_rot_matrices(proj);
|
||||
}
|
||||
|
||||
pub fn apply_lonlat_rotation(
|
||||
&mut self,
|
||||
dlon: Angle<f64>,
|
||||
dlat: Angle<f64>,
|
||||
proj: &ProjectionType
|
||||
) {
|
||||
let center = self.get_center();
|
||||
let rot = Rotation::from_axis_angle(&Vector3::new(center.z, 0.0, -center.x).normalize(), dlat) * Rotation::from_axis_angle(&Vector3::unit_y(), -dlon) * Rotation::from_sky_position(¢er);
|
||||
|
||||
self.set_rotation(&rot, proj);
|
||||
}
|
||||
|
||||
/// center lonlat must be given in icrs frame
|
||||
pub fn set_center(&mut self, lonlat: &LonLatT<f64>, proj: &ProjectionType) {
|
||||
let icrs_pos: Vector3<_> = lonlat.vector();
|
||||
let icrs_pos = lonlat.vector();
|
||||
self.set_center_xyz(&icrs_pos, proj);
|
||||
}
|
||||
|
||||
let center = CooSystem::ICRS.to(self.get_coo_system()) * icrs_pos;
|
||||
pub fn set_center_xyz(&mut self, xyz: &Vector3<f64>, proj: &ProjectionType) {
|
||||
let center = CooSystem::ICRS.to(self.get_coo_system()) * xyz;
|
||||
let rot_to_center = Rotation::from_sky_position(¢er);
|
||||
|
||||
let phi = self.get_center_pos_angle();
|
||||
let phi = self.get_position_angle();
|
||||
let third_euler_rot = Rotation::from_axis_angle(¢er, phi);
|
||||
|
||||
let rot = third_euler_rot * rot_to_center;
|
||||
@@ -651,7 +667,7 @@ impl CameraViewPort {
|
||||
self.set_rotation(&rot, proj);
|
||||
}
|
||||
|
||||
pub fn set_center_pos_angle(&mut self, phi: Angle<f64>, proj: &ProjectionType) {
|
||||
pub fn set_position_angle(&mut self, phi: Angle<f64>, proj: &ProjectionType) {
|
||||
let c = self.center;
|
||||
let rot_to_center = Rotation::from_sky_position(&c);
|
||||
let third_euler_rot = Rotation::from_axis_angle(&c, phi);
|
||||
@@ -660,7 +676,7 @@ impl CameraViewPort {
|
||||
self.set_rotation(&total_rot, proj);
|
||||
}
|
||||
|
||||
fn set_rotation(&mut self, rot: &Rotation<f64>, proj: &ProjectionType) {
|
||||
pub fn set_rotation(&mut self, rot: &Rotation<f64>, proj: &ProjectionType) {
|
||||
self.w2m_rot = *rot;
|
||||
|
||||
self.update_rot_matrices(proj);
|
||||
@@ -797,7 +813,7 @@ impl CameraViewPort {
|
||||
self.coo_sys
|
||||
}
|
||||
|
||||
pub fn get_center_pos_angle(&self) -> Angle<f64> {
|
||||
pub fn get_position_angle(&self) -> Angle<f64> {
|
||||
(self.w2m.x.y).atan2(self.w2m.y.y).to_angle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use crate::camera::CameraViewPort;
|
||||
use crate::math::angle::ToAngle;
|
||||
use crate::math::projection::ProjectionType;
|
||||
use crate::time::{DeltaTime, Time};
|
||||
|
||||
/// State for inertia
|
||||
pub struct Inertia {
|
||||
// Initial angular distance
|
||||
@@ -14,15 +13,17 @@ pub struct Inertia {
|
||||
axis: Vector3<f64>,
|
||||
// The time when the inertia begins
|
||||
time_start: Time,
|
||||
north_up: bool,
|
||||
}
|
||||
|
||||
impl Inertia {
|
||||
pub fn new(ampl: f64, axis: Vector3<f64>) -> Self {
|
||||
pub fn new(ampl: f64, axis: Vector3<f64>, north_up: bool) -> Self {
|
||||
Inertia {
|
||||
time_start: Time::now(),
|
||||
ampl: ampl,
|
||||
ampl,
|
||||
speed: ampl,
|
||||
axis: axis,
|
||||
axis,
|
||||
north_up
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +36,7 @@ impl Inertia {
|
||||
// where:
|
||||
// * k is the stiffness of the ressort
|
||||
// * m is its mass
|
||||
let w0 = 10.0;
|
||||
let w0 = 7.0;
|
||||
// The angular distance goes from d0 to 0.0
|
||||
//self.speed = self.ampl * ((-w0 * t).exp());
|
||||
// The angular distance goes from d0 to 0.0
|
||||
@@ -43,7 +44,11 @@ impl Inertia {
|
||||
/*let alpha = 1_f32 + (0_f32 - 1_f32) * (10_f32 * t + 1_f32) * (-10_f32 * t).exp();
|
||||
let alpha = alpha * alpha;
|
||||
let fov = start_fov * (1_f32 - alpha) + goal_fov * alpha;*/
|
||||
camera.apply_rotation(&self.axis, self.speed.to_angle(), proj)
|
||||
camera.apply_axis_rotation(&self.axis, self.speed.to_angle(), proj);
|
||||
|
||||
if self.north_up {
|
||||
camera.set_position_angle(0.0.to_angle(), proj);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_start_ampl(&self) -> f64 {
|
||||
|
||||
@@ -534,18 +534,18 @@ impl WebClient {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `theta` - The rotation angle in degrees
|
||||
#[wasm_bindgen(js_name = setViewCenter2NorthPoleAngle)]
|
||||
pub fn set_view_center_pos_angle(&mut self, theta: f64) -> Result<(), JsValue> {
|
||||
#[wasm_bindgen(js_name = setRotation)]
|
||||
pub fn set_rotation(&mut self, theta: f64) -> Result<(), JsValue> {
|
||||
let theta = ArcDeg(theta);
|
||||
self.app.set_view_center_pos_angle(theta);
|
||||
self.app.set_position_angle(theta);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the absolute orientation angle of the view
|
||||
#[wasm_bindgen(js_name = getViewCenter2NorthPoleAngle)]
|
||||
pub fn get_north_shift_angle(&mut self) -> Result<f64, JsValue> {
|
||||
let phi = self.app.get_north_shift_angle();
|
||||
#[wasm_bindgen(js_name = getRotation)]
|
||||
pub fn get_rotation(&mut self) -> Result<f64, JsValue> {
|
||||
let phi = self.app.get_position_angle();
|
||||
Ok(phi.to_degrees())
|
||||
}
|
||||
|
||||
@@ -610,6 +610,11 @@ impl WebClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = lockNorthUp)]
|
||||
pub fn lock_north_up(&mut self) {
|
||||
self.app.lock_north_up();
|
||||
}
|
||||
|
||||
/// Get the center of the view
|
||||
///
|
||||
/// This returns a javascript array of size 2.
|
||||
|
||||
@@ -400,7 +400,7 @@ where
|
||||
S::max_value().to_angle()
|
||||
}
|
||||
|
||||
pub fn to_radians(&self) -> S {
|
||||
pub const fn to_radians(&self) -> S {
|
||||
self.rad
|
||||
}
|
||||
|
||||
|
||||
@@ -204,3 +204,10 @@ pub fn unproj_from_screen(
|
||||
.screen_to_model_space(&xy, camera)
|
||||
.map(|model_pos| model_pos.lonlat())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_in(v1: &Vector3<f64>, v2: &Vector3<f64>, v: &Vector3<f64>) -> bool {
|
||||
let theta = crate::math::vector::angle3(&v1, &v2).abs();
|
||||
|
||||
crate::math::vector::angle3(&v1, &v).abs() < theta && crate::math::vector::angle3(&v, &v2).abs() < theta
|
||||
}
|
||||
|
||||
@@ -123,7 +123,8 @@ import { Polyline } from "./shapes/Polyline";
|
||||
* @property {boolean} [showReticle=true] - Whether to show the reticle.
|
||||
* @property {boolean} [showCatalog=true] - Whether to show the catalog.
|
||||
* @property {boolean} [showCooGrid=true] - Whether the coordinates grid should be shown at startup.
|
||||
*
|
||||
* @property {boolean} [inertia=true] - Whether mouse release triggers an inertia effect.
|
||||
* @property {boolean} [lockNorthUp=false] - If true, the north pole will always be up.
|
||||
* @property {boolean} [fullScreen=false] - Whether to start in full-screen mode.
|
||||
* @property {string} [reticleColor="rgb(178, 50, 178)"] - Color of the reticle in RGB format.
|
||||
* @property {number} [reticleSize=22] - Size of the reticle.
|
||||
@@ -504,12 +505,18 @@ export let Aladin = (function () {
|
||||
this.samp = new SAMPConnector(this);
|
||||
}
|
||||
|
||||
// lockNorthUp option
|
||||
this.lockNorthUp = options.lockNorthUp || false;
|
||||
if (this.lockNorthUp) {
|
||||
this.wasm.lockNorthUp();
|
||||
}
|
||||
|
||||
if (options.inertia !== undefined) {
|
||||
this.wasm.setInertia(options.inertia);
|
||||
}
|
||||
|
||||
if (options.northPoleOrientation) {
|
||||
this.setViewCenter2NorthPoleAngle(options.northPoleOrientation);
|
||||
this.setRotation(options.northPoleOrientation);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1996,7 +2003,7 @@ export let Aladin = (function () {
|
||||
* view in the counter clockwise order (or towards the east)
|
||||
*/
|
||||
Aladin.prototype.setRotation = function (rotation) {
|
||||
this.view.setViewCenter2NorthPoleAngle(rotation);
|
||||
this.view.setRotation(rotation);
|
||||
};
|
||||
|
||||
|
||||
@@ -2009,7 +2016,7 @@ export let Aladin = (function () {
|
||||
* @returns {number} - Angle between the position center and the north pole
|
||||
*/
|
||||
Aladin.prototype.getRotation = function () {
|
||||
return this.view.wasm.getViewCenter2NorthPoleAngle();
|
||||
return this.view.wasm.getRotation();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -592,7 +592,7 @@ export let View = (function () {
|
||||
const [lon, lat] = view.aladin.pix2world(xymouse.x, xymouse.y, 'icrs');
|
||||
view.pointTo(lon, lat);
|
||||
// reset the rotation around center view
|
||||
view.setViewCenter2NorthPoleAngle(0.0);
|
||||
view.setRotation(0.0);
|
||||
}
|
||||
catch (err) {
|
||||
return;
|
||||
@@ -752,6 +752,11 @@ export let View = (function () {
|
||||
if (e.type === 'touchstart' && e.targetTouches && e.targetTouches.length >= 2) {
|
||||
view.dragging = false;
|
||||
|
||||
// Do not start the pinched rotation if the north up is locked
|
||||
if (view.aladin.lockNorthUp === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
view.pinchZoomParameters.isPinching = true;
|
||||
view.pinchZoomParameters.initialZoomFactor = view.zoomFactor;
|
||||
view.pinchZoomParameters.initialDistance = Math.sqrt(Math.pow(e.targetTouches[0].clientX - e.targetTouches[1].clientX, 2) + Math.pow(e.targetTouches[0].clientY - e.targetTouches[1].clientY, 2));
|
||||
@@ -1026,7 +1031,7 @@ export let View = (function () {
|
||||
// planetary survey case
|
||||
rotation -= fingerAngleDiff;
|
||||
}
|
||||
view.setViewCenter2NorthPoleAngle(rotation);
|
||||
view.setRotation(rotation);
|
||||
}
|
||||
|
||||
// zoom
|
||||
@@ -1540,8 +1545,8 @@ export let View = (function () {
|
||||
});
|
||||
}
|
||||
|
||||
View.prototype.setViewCenter2NorthPoleAngle = function(rotation) {
|
||||
this.wasm.setViewCenter2NorthPoleAngle(rotation);
|
||||
View.prototype.setRotation = function(rotation) {
|
||||
this.wasm.setRotation(rotation);
|
||||
}
|
||||
|
||||
View.prototype.setGridOptions = function (options) {
|
||||
|
||||
Reference in New Issue
Block a user