diff --git a/CHANGELOG.md b/CHANGELOG.md index b4faae3d..bb155d21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ ## Released +### 3.9.0-beta + +* [feat] AVM tag reading from PNG extension images by [@bmatthieu3][bmatthieu3] +* [feat] Export of aladin lite view with AVM tags [@tboch][tboch] +* [fix] Switching back and forth the layer to an image by [@bmatthieu3][bmatthieu3] +* [fix] Open settings on catalog with a HiPS3D +* [fix] keep the cut values for each tile format +* [fix] Keep approx the same level of zoom when the screen is resized +* [fix] +* [fix] +* [feat] Support of SIP astrometry keywords in FITS file +* [fix] +* [feat] Progressive catalogs: first try loading metadata.xml, switch back on Metadata.xml if it fails + ### 3.8.0 diff --git a/codemeta.json b/codemeta.json index eef5160a..9f478f27 100644 --- a/codemeta.json +++ b/codemeta.json @@ -8,8 +8,8 @@ "dateModified": "2023-01-31", "issueTracker": "https://github.com/cds-astro/aladin-lite/issues", "name": "Aladin Lite", - "version": "3.8.1", - "softwareVersion": "3.8.1", + "version": "3.8.2", + "softwareVersion": "3.8.2", "description": "An astronomical HiPS visualizer in the browser.", "identifier": "10.5281/zenodo.7638833", "applicationCategory": "Astronomy, Visualization", diff --git a/examples/al-HST-outreach.html b/examples/al-HST-outreach.html index 8307645c..854c9a7b 100644 --- a/examples/al-HST-outreach.html +++ b/examples/al-HST-outreach.html @@ -107,7 +107,7 @@ if (params.has('fov')) { A.init.then(() => { aladin = A.aladin("#aladin-lite-div", {showSimbadPointerControl: true, target: target, fov: fov }); aladin.setOverlayImageLayer('https://alasky.cds.unistra.fr/HST-outreach/CDS_P_HST_EPO'); -aladin.getOverlayImageLayer().setAlpha(1.0); +aladin.getOverlayImageLayer().setAlpha(0.5); aladin.toggleFullscreen(); }); diff --git a/examples/al-cfht.html b/examples/al-cfht.html index bb83e1d0..29a93fd3 100644 --- a/examples/al-cfht.html +++ b/examples/al-cfht.html @@ -13,7 +13,7 @@ import A from '../src/js/A.js'; let aladin; A.init.then(() => { - aladin = A.aladin('#aladin-lite-div', {toolbar: {divSelector: '#toolbar'}, samp: true, survey: "data/hips/PanSTARRS_DR1_color-z-zg-g", fov:2.0, target: "22 35 58.39 +33 57 57.8", showSettingsControl: true, log: false}); + aladin = A.aladin('#aladin-lite-div', {toolbar: {divSelector: '#toolbar'}, showContextMenu: true, samp: true, survey: "data/hips/PanSTARRS_DR1_color-z-zg-g", fov:2.0, target: "22 35 58.39 +33 57 57.8", showSettingsControl: true, log: false}); aladin.setProjection('AIT'); let cfht = aladin.createImageSurvey("CFHT", "CFHT MegaCam u+g+r", "./data/hips/CFHT", "equatorial", 10, {imgFormat: 'png'}); let jwst1 = aladin.createImageSurvey("CDS/P/JWST/Stephans-Quintet/NIRCam+MIRI", "JWST NIRCam+MIRI", "data/hips/JWST_NIRCam_MIRI", null, null, {imgFormat: 'png'}); diff --git a/examples/al-multiple-instances.html b/examples/al-multiple-instances.html index eae86d44..a983ba5a 100644 --- a/examples/al-multiple-instances.html +++ b/examples/al-multiple-instances.html @@ -14,7 +14,7 @@ let al2; A.init.then(() => { // Start up Aladin Lite - al1 = A.aladin('#al1', {target: 'M51', fov: 0.3, survey: 'P/DSS2/color', fullScreen: false, showContextMenu: true, showShareControl: true}); + al1 = A.aladin('#al1', {target: 'M51', fov: 0.3, survey: 'https://alasky.cds.unistra.fr/HIPS3D/LGLBSHI-test-compression/', fullScreen: false, showContextMenu: true, showShareControl: true}); al2 = A.aladin('#al2', {target: 'M51', fov: 180, survey: 'P/PanSTARRS/DR1/z', showColorPickerControl: true, fullScreen: false, showContextMenu: true, showShareControl: true}); }); diff --git a/examples/al-zoomchanged.html b/examples/al-zoomchanged.html index 7695bf1b..decd2414 100644 --- a/examples/al-zoomchanged.html +++ b/examples/al-zoomchanged.html @@ -21,7 +21,7 @@ console.log("Rotation just changed to ", rotation); }) aladin.on("positionChanged", ({ra, dec}) => { - console.log('call to aladin', aladin.pix2world(300, 300)) + console.log('call to aladin', aladin.pix2world(300, 300, "galactic")) console.log('positionChanged in icrs', ra, dec) }) diff --git a/package.json b/package.json index c548922e..4174df5a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "homepage": "https://aladin.cds.unistra.fr/", "name": "aladin-lite", "type": "module", - "version": "3.8.2", + "version": "3.9.0-beta", "description": "An astronomical HiPS visualizer in the browser", "author": "Thomas Boch and Matthieu Baumann", "license": "GPL-3", diff --git a/src/core/Cargo.toml b/src/core/Cargo.toml index 70a3eb1d..81184d93 100644 --- a/src/core/Cargo.toml +++ b/src/core/Cargo.toml @@ -25,7 +25,8 @@ wasm-bindgen = "=0.2.92" async-channel = "1.8.0" mapproj = "0.3.0" colorgrad = "0.6.2" -fitsrs = "0.4.1" +#fitsrs = "0.4.1" +fitsrs = { git = "https://github.com/cds-astro/fitsrs", branch = "master" } [features] webgl1 = [ "al-core/webgl1", "al-api/webgl1", "web-sys/WebGlRenderingContext", "web-sys/AngleInstancedArrays", "web-sys/ExtSRgb", "web-sys/OesTextureFloat",] diff --git a/src/core/al-api/src/coo_system.rs b/src/core/al-api/src/coo_system.rs index eb623980..b3f267aa 100644 --- a/src/core/al-api/src/coo_system.rs +++ b/src/core/al-api/src/coo_system.rs @@ -31,8 +31,8 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] #[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Hash)] pub enum CooSystem { - ICRS, - GAL, + ICRS = 0, + GAL = 1, } pub const NUM_COOSYSTEM: usize = 2; diff --git a/src/core/al-api/src/hips.rs b/src/core/al-api/src/hips.rs index de1b3b75..a8e1ec2b 100644 --- a/src/core/al-api/src/hips.rs +++ b/src/core/al-api/src/hips.rs @@ -45,15 +45,15 @@ pub struct HiPSProperties { sky_fraction: Option, min_order: Option, - hips_initial_fov: Option, - hips_initial_ra: Option, - hips_initial_dec: Option, + initial_fov: Option, + initial_ra: Option, + initial_dec: Option, // HiPS cube - hips_cube_depth: Option, + cube_depth: Option, // HiPS 3D keywords - hips_order_freq: Option, - hips_tile_depth: Option, + order_freq: Option, + tile_depth: Option, /// Start of spectral coordinates (in meters) em_min: Option, @@ -77,11 +77,11 @@ pub struct HiPSProperties { impl HiPSProperties { #[inline(always)] pub fn get_hips_order_freq(&self) -> Option { - self.hips_order_freq + self.order_freq } #[inline(always)] pub fn get_hips_tile_depth(&self) -> Option { - self.hips_tile_depth + self.tile_depth } #[inline(always)] @@ -111,7 +111,7 @@ impl HiPSProperties { #[inline(always)] pub fn get_cube_depth(&self) -> Option { - self.hips_cube_depth + self.cube_depth } #[inline(always)] @@ -141,17 +141,17 @@ impl HiPSProperties { #[inline(always)] pub fn get_initial_fov(&self) -> Option { - self.hips_initial_fov + self.initial_fov } #[inline(always)] pub fn get_initial_ra(&self) -> Option { - self.hips_initial_ra + self.initial_ra } #[inline(always)] pub fn get_initial_dec(&self) -> Option { - self.hips_initial_dec + self.initial_dec } #[inline(always)] @@ -246,9 +246,13 @@ impl From for TransferFunction { } use crate::colormap::CmapLabel; -#[derive(Deserialize, Debug, Clone)] + +#[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] -pub struct HiPSColor { +#[derive(Clone)] +#[wasm_bindgen] +pub struct ImageMetadata { + /// Color config // transfer function called before evaluating the colormap pub stretch: TransferFunction, // low cut @@ -258,26 +262,17 @@ pub struct HiPSColor { // flag to tell the colormap is queried reversed pub reversed: bool, // the colormap - pub cmap_name: CmapLabel, - /// tonal color tuning factors - pub k_gamma: f32, - pub k_saturation: f32, - pub k_contrast: f32, - pub k_brightness: f32, -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -#[derive(Clone)] -#[wasm_bindgen] -pub struct ImageMetadata { - /// Color config #[wasm_bindgen(skip)] - pub color: HiPSColor, + pub colormap: CmapLabel, + /// tonal color tuning factors + pub gamma: f32, + pub saturation: f32, + pub contrast: f32, + pub brightness: f32, // Blending config #[serde(default)] - pub blend_cfg: BlendCfg, + pub blending: BlendCfg, #[serde(default = "default_opacity")] pub opacity: f32, /// the current format chosen @@ -289,90 +284,6 @@ fn default_opacity() -> f32 { } use crate::Abort; -#[wasm_bindgen] -impl ImageMetadata { - #[wasm_bindgen(setter = color)] - pub fn set_color(&mut self, color: JsValue) -> std::result::Result<(), JsValue> { - self.color = serde_wasm_bindgen::from_value(color)?; - - Ok(()) - } - - #[wasm_bindgen(getter = color)] - pub fn color(&self) -> JsValue { - let js_color_obj = js_sys::Object::new(); - - let HiPSColor { - stretch, - min_cut, - max_cut, - reversed, - cmap_name, - k_gamma, - k_saturation, - k_brightness, - k_contrast, - } = &self.color; - - js_sys::Reflect::set( - &js_color_obj, - &"stretch".into(), - &serde_wasm_bindgen::to_value(&stretch).unwrap_abort(), - ) - .unwrap_abort(); - js_sys::Reflect::set( - &js_color_obj, - &"minCut".into(), - &serde_wasm_bindgen::to_value(&min_cut).unwrap_abort(), - ) - .unwrap_abort(); - js_sys::Reflect::set( - &js_color_obj, - &"maxCut".into(), - &serde_wasm_bindgen::to_value(&max_cut).unwrap_abort(), - ) - .unwrap_abort(); - js_sys::Reflect::set( - &js_color_obj, - &"kGamma".into(), - &serde_wasm_bindgen::to_value(&k_gamma).unwrap_abort(), - ) - .unwrap_abort(); - js_sys::Reflect::set( - &js_color_obj, - &"kSaturation".into(), - &serde_wasm_bindgen::to_value(&k_saturation).unwrap_abort(), - ) - .unwrap_abort(); - js_sys::Reflect::set( - &js_color_obj, - &"kBrightness".into(), - &serde_wasm_bindgen::to_value(&k_brightness).unwrap_abort(), - ) - .unwrap_abort(); - js_sys::Reflect::set( - &js_color_obj, - &"kContrast".into(), - &serde_wasm_bindgen::to_value(&k_contrast).unwrap_abort(), - ) - .unwrap_abort(); - js_sys::Reflect::set( - &js_color_obj, - &"reversed".into(), - &JsValue::from_bool(*reversed), - ) - .unwrap_abort(); - js_sys::Reflect::set( - &js_color_obj, - &"colormap".into(), - &serde_wasm_bindgen::to_value(&cmap_name).unwrap_abort(), - ) - .unwrap_abort(); - - js_color_obj.into() - } -} - impl ImageMetadata { pub fn visible(&self) -> bool { self.opacity > 0.0 diff --git a/src/core/al-core/Cargo.toml b/src/core/al-core/Cargo.toml index 5f65f281..d9d5b62c 100644 --- a/src/core/al-core/Cargo.toml +++ b/src/core/al-core/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] js-sys = "0.3.47" cgmath = "*" -fitsrs = "0.4.1" +fitsrs = { git = "https://github.com/cds-astro/fitsrs", branch = "master" } al-api = { path = "../al-api" } serde = { version = "^1.0.59", features = ["derive"] } serde_json = "1.0" diff --git a/src/core/al-core/src/shader.rs b/src/core/al-core/src/shader.rs index 4e57d058..d258b276 100644 --- a/src/core/al-core/src/shader.rs +++ b/src/core/al-core/src/shader.rs @@ -289,20 +289,9 @@ impl UniformType for TransferFunction { } } -use al_api::hips::HiPSColor; use al_api::hips::ImageMetadata; impl SendUniforms for ImageMetadata { - fn attach_uniforms<'a>(&self, shader: &'a ShaderBound<'a>) -> &'a ShaderBound<'a> { - shader - .attach_uniforms_from(&self.color) - .attach_uniform("opacity", &self.opacity); - - shader - } -} - -impl SendUniforms for HiPSColor { fn attach_uniforms<'a>(&self, shader: &'a ShaderBound<'a>) -> &'a ShaderBound<'a> { let reversed = self.reversed as u8 as f32; @@ -310,17 +299,18 @@ impl SendUniforms for HiPSColor { .attach_uniform("H", &self.stretch) .attach_uniform("min_value", &self.min_cut.unwrap_or(0.0)) .attach_uniform("max_value", &self.max_cut.unwrap_or(1.0)) - .attach_uniform("k_gamma", &self.k_gamma) - .attach_uniform("k_saturation", &self.k_saturation) - .attach_uniform("k_brightness", &self.k_brightness) - .attach_uniform("k_contrast", &self.k_contrast) - .attach_uniform("reversed", &reversed); + .attach_uniform("k_gamma", &self.gamma) + .attach_uniform("k_saturation", &self.saturation) + .attach_uniform("k_brightness", &self.brightness) + .attach_uniform("k_contrast", &self.contrast) + .attach_uniform("reversed", &reversed) + .attach_uniform("opacity", &self.opacity); shader } } -impl SendUniformsWithParams for HiPSColor { +impl SendUniformsWithParams for ImageMetadata { fn attach_uniforms_with_params<'a>( &self, shader: &'a ShaderBound<'a>, @@ -328,18 +318,19 @@ impl SendUniformsWithParams for HiPSColor { ) -> &'a ShaderBound<'a> { let reversed = self.reversed as u8 as f32; - let cmap = cmaps.get(self.cmap_name.as_ref()); + let cmap = cmaps.get(self.colormap.as_ref()); shader .attach_uniforms_from(cmaps) .attach_uniforms_with_params_from(cmap, cmaps) .attach_uniform("H", &self.stretch) .attach_uniform("min_value", &self.min_cut.unwrap_or(0.0)) .attach_uniform("max_value", &self.max_cut.unwrap_or(1.0)) - .attach_uniform("k_gamma", &self.k_gamma) - .attach_uniform("k_saturation", &self.k_saturation) - .attach_uniform("k_brightness", &self.k_brightness) - .attach_uniform("k_contrast", &self.k_contrast) - .attach_uniform("reversed", &reversed); + .attach_uniform("k_gamma", &self.gamma) + .attach_uniform("k_saturation", &self.saturation) + .attach_uniform("k_brightness", &self.brightness) + .attach_uniform("k_contrast", &self.contrast) + .attach_uniform("reversed", &reversed) + .attach_uniform("opacity", &self.opacity); shader } diff --git a/src/core/src/app.rs b/src/core/src/app.rs index 9c6d392c..236bb713 100644 --- a/src/core/src/app.rs +++ b/src/core/src/app.rs @@ -1452,8 +1452,8 @@ impl App { pub(crate) fn resize(&mut self, width: f32, height: f32) { self.camera.set_screen_size(width, height, &self.projection); - self.camera - .set_zoom_factor(self.camera.get_zoom_factor(), &self.projection); + //self.camera + // .set_zoom_factor(self.camera.get_zoom_factor(), &self.projection); // resize the view fbo //let screen_size = self.camera.get_screen_size(); @@ -1853,8 +1853,8 @@ impl App { self.request_redraw = true; } - pub(crate) fn get_fov(&self) -> f64 { - self.camera.get_aperture().to_degrees() + pub(crate) fn get_fov(&self) -> [f64; 2] { + [self.camera.get_aperture().to_degrees(), self.camera.get_aperture_y().to_degrees()] } pub(crate) fn get_colormaps(&self) -> &Colormaps { diff --git a/src/core/src/camera/viewport.rs b/src/core/src/camera/viewport.rs index 7661480b..78723b9f 100644 --- a/src/core/src/camera/viewport.rs +++ b/src/core/src/camera/viewport.rs @@ -24,6 +24,7 @@ const ZOOM_FACTOR_UPPER_LIMIT: f64 = 2.0; pub struct CameraViewPort { // The field of view angle aperture: f64, + aperture_y: f64, // The rotation of the camera center: Vector3, w2m_rot: Rotation, @@ -78,6 +79,9 @@ pub struct CameraViewPort { pub(crate) min_fov: Option, // an optional max field of view pub(crate) max_fov: Option, + + scissor_w: f64, + scissor_h: f64, } use al_api::coo_system::CooSystem; use al_core::WebGlContext; @@ -104,6 +108,7 @@ impl CameraViewPort { let last_user_action = UserAction::Starting; let aperture = projection.aperture_start().to_radians(); + let aperture_y = aperture; let w2m = Matrix3::identity(); let m2w = w2m; @@ -127,6 +132,9 @@ impl CameraViewPort { let width = width * dpi; let height = height * dpi; + let scissor_w = width as f64; + let scissor_h = height as f64; + let aspect = height / width; let ndc_to_clip = Vector2::new(1.0, (height as f64) / (width as f64)); let zoom_factor = 1.0; @@ -144,6 +152,8 @@ impl CameraViewPort { CameraViewPort { // The field of view angle aperture, + aperture_y, + center, // The rotation of the cameraq w2m_rot, @@ -187,6 +197,9 @@ impl CameraViewPort { min_fov: None, max_fov: None, + + scissor_w, + scissor_h } } @@ -246,7 +259,7 @@ impl CameraViewPort { } } - fn recompute_scissor(&self) { + fn recompute_scissor(&mut self) { // Clear all the screen before updating the scissor //self.gl.scissor(0, 0, self.width as i32, self.height as i32); //self.gl.clear(web_sys::WebGl2RenderingContext::COLOR_BUFFER_BIT); @@ -274,6 +287,9 @@ impl CameraViewPort { let w = (tr_s.x - tl_s.x).min(self.width as f64); let h = (br_s.y - tr_s.y).min(self.height as f64); + self.scissor_w = w; + self.scissor_h = h; + // Specify a scissor here self.gl.scissor( (tl_s.x as i32).max(0), @@ -284,6 +300,8 @@ impl CameraViewPort { } pub fn set_screen_size(&mut self, width: f32, height: f32, projection: &ProjectionType) { + let old_w = self.width; + let old_h = self.height; self.width = width * self.dpi; self.height = height * self.dpi; @@ -291,8 +309,9 @@ impl CameraViewPort { // Compute the new clip zoom factor self.compute_ndc_to_clip_factor(projection); - self.fov - .set_aperture(&self.ndc_to_clip, self.zoom_factor, &self.w2m, projection); + self.set_zoom_factor(self.zoom_factor * ((self.width / old_w) as f64), projection); + + //self.set_aperture(self.aperture * ((self.width / old_w) as f64), projection); let proj_area = projection.get_area(); self.is_allsky = !proj_area.is_in(&math::projection::ndc_to_clip_space( @@ -445,6 +464,12 @@ impl CameraViewPort { // Limit later the aperture to aperture_start self.aperture = aperture.min(aperture_start); + if self.scissor_h < (self.height - 1.0).into() { + self.aperture_y = aperture_start; + } else { + self.aperture_y = self.aperture / (self.aspect as f64); + } + // Project this vertex into the screen self.moved = true; self.zoomed = true; @@ -539,6 +564,12 @@ impl CameraViewPort { self.aperture = aperture; + if self.scissor_h < (self.height - 1.0).into() { + self.aperture_y = aperture_start; + } else { + self.aperture_y = self.aperture / (self.aspect as f64); + } + // Project this vertex into the screen self.moved = true; self.zoomed = true; @@ -610,7 +641,6 @@ impl CameraViewPort { Err(idx) => idx, }; - //al_core::log(&format!("{:?}", depth_pixel)); const DEPTH_OFFSET_TEXTURE: usize = 9; self.texture_depth = if DEPTH_OFFSET_TEXTURE > depth_pixel { 0_u8 @@ -799,6 +829,11 @@ impl CameraViewPort { self.aperture } + #[inline] + pub fn get_aperture_y(&self) -> f64 { + self.aperture_y + } + #[inline] pub fn get_center(&self) -> &Vector3 { &self.center diff --git a/src/core/src/lib.rs b/src/core/src/lib.rs index 8946969c..ad4dc860 100644 --- a/src/core/src/lib.rs +++ b/src/core/src/lib.rs @@ -465,9 +465,9 @@ impl WebClient { /// Get the field of the view in degrees #[wasm_bindgen(js_name = getFieldOfView)] - pub fn get_fov(&self) -> Result { + pub fn get_fov(&self) -> Result, JsValue> { let fov = self.app.get_fov(); - Ok(fov) + Ok(fov.into()) } /// Get the max aperture of a projection (in degrees) diff --git a/src/core/src/renderable/hips/d2/mod.rs b/src/core/src/renderable/hips/d2/mod.rs index 90bd5e4a..6a7bcddb 100644 --- a/src/core/src/renderable/hips/d2/mod.rs +++ b/src/core/src/renderable/hips/d2/mod.rs @@ -796,28 +796,27 @@ impl HiPS2D { let config = self.get_config(); let ImageMetadata { - color, opacity, - blend_cfg, + blending, + colormap, .. } = cfg; // Get the colormap from the color - let cmap = colormaps.get(color.cmap_name.as_ref()); + let colormap = colormaps.get(colormap.as_ref()); - blend_cfg.enable(&self.gl, || { + blending.enable(&self.gl, || { if draw_allsky { let w2v = c * (*camera.get_w2m()); - let shader = get_raytracer_shader(cmap, &self.gl, shaders, config)?; + let shader = get_raytracer_shader(colormap, &self.gl, shaders, config)?; let shader = shader.bind(&self.gl); shader .attach_uniforms_from(camera) .attach_uniforms_from(&self.buffer) // send the cmap appart from the color config - .attach_uniforms_with_params_from(cmap, colormaps) - .attach_uniforms_from(color) + .attach_uniforms_with_params_from(cfg, colormaps) .attach_uniform("model", &w2v) .attach_uniform("current_time", &utils::get_current_time()) .attach_uniform( @@ -851,13 +850,13 @@ impl HiPS2D { // - The UVs are changed if: // * new cells are added/removed (because new cells are added) // * there are new available tiles for the GPU - let shader = get_raster_shader(cmap, &self.gl, shaders, config)?.bind(&self.gl); + let shader = get_raster_shader(colormap, &self.gl, shaders, config)?.bind(&self.gl); shader .attach_uniforms_from(&self.buffer) // send the cmap appart from the color config - .attach_uniforms_with_params_from(cmap, colormaps) - .attach_uniforms_from(color) + .attach_uniforms_with_params_from(colormap, colormaps) + .attach_uniforms_from(cfg) .attach_uniforms_from(camera) .attach_uniform("inv_model", &v2w) .attach_uniform("current_time", &utils::get_current_time()) diff --git a/src/core/src/renderable/hips/d3/mod.rs b/src/core/src/renderable/hips/d3/mod.rs index 7e1760b4..cfacbc2c 100644 --- a/src/core/src/renderable/hips/d3/mod.rs +++ b/src/core/src/renderable/hips/d3/mod.rs @@ -1064,13 +1064,13 @@ impl HiPS3D { } let ImageMetadata { - color, + colormap, opacity, - blend_cfg, + blending, .. } = cfg; - let cmap = colormaps.get(color.cmap_name.as_ref()); + let colormap = colormaps.get(colormap.as_ref()); let v2w = (*camera.get_m2w()) * c.transpose(); @@ -1090,7 +1090,7 @@ impl HiPS3D { let shader = get_raster_shader(&self.gl, shaders, hips_cfg)?; for (cell, num_indices) in self.cells.iter().zip(self.num_indices.iter()) { - blend_cfg.enable(&self.gl, || { + blending.enable(&self.gl, || { // Bind the shader at each draw of a cell to not exceed the max number of tex image units bindable // to a shader. It is 32 in my case let shaderbound = shader.bind(&self.gl); @@ -1098,8 +1098,8 @@ impl HiPS3D { shaderbound .attach_uniform("tex", &self.buffer.get(cell).unwrap_abort().texture) .attach_uniforms_from(&self.buffer) - .attach_uniforms_with_params_from(cmap, colormaps) - .attach_uniforms_from(color) + .attach_uniforms_with_params_from(colormap, colormaps) + .attach_uniforms_from(cfg) .attach_uniforms_from(camera) .attach_uniform("inv_model", &v2w) .attach_uniform("opacity", opacity) diff --git a/src/core/src/renderable/image/mod.rs b/src/core/src/renderable/image/mod.rs index a6ee5dee..fbd3b330 100644 --- a/src/core/src/renderable/image/mod.rs +++ b/src/core/src/renderable/image/mod.rs @@ -722,9 +722,8 @@ impl Image { //self.gl.enable(WebGl2RenderingContext::BLEND); let ImageMetadata { - color, opacity, - blend_cfg, + blending, .. } = cfg; @@ -758,7 +757,7 @@ impl Image { //self.gl.disable(WebGl2RenderingContext::CULL_FACE); // 2. Draw it if its opacity is not null - blend_cfg.enable(&self.gl, || { + blending.enable(&self.gl, || { let mut off_indices = 0; for &idx_tex in self.idx_tex.iter() { let texture = &self.textures[idx_tex]; @@ -767,7 +766,7 @@ impl Image { let shader_bound = shader.bind(&self.gl); shader_bound - .attach_uniforms_with_params_from(color, colormaps) + .attach_uniforms_with_params_from(cfg, colormaps) .attach_uniform("opacity", opacity) .attach_uniform("tex", texture) .attach_uniform("scale", &self.bscale) diff --git a/src/core/src/shaders.rs b/src/core/src/shaders.rs index 41cf5a90..e8b954ae 100644 --- a/src/core/src/shaders.rs +++ b/src/core/src/shaders.rs @@ -3,19 +3,717 @@ use std::collections::HashMap; pub fn get_all() -> HashMap<&'static str, &'static str> { let mut out = HashMap::new(); out.insert( - r"colormaps_colormap.vert", + r"catalogs_ortho.frag", r#"#version 300 es precision lowp float; -precision lowp sampler2D; -layout (location = 0) in vec2 position; +in vec2 out_uv; +in vec3 out_p; + +out vec4 color; + +uniform sampler2D kernel_texture; +uniform float max_density; // max number of sources in a kernel sized HEALPix cell at the current depth +uniform float fov; +uniform float strength; +void main() { + if (out_p.z < 0.f) { + discard; + } + + color = texture(kernel_texture, out_uv) / max(log2(fov*100.0), 1.0); + color.r *= strength; +}"#, + ); + out.insert( + r"catalogs_mollweide.vert", + r#"#version 300 es +precision lowp float; +layout (location = 0) in vec2 offset; layout (location = 1) in vec2 uv; +layout (location = 2) in vec3 center; + +uniform float current_time; +uniform mat3 inv_model; + +uniform vec2 ndc_to_clip; +uniform float czf; +uniform vec2 kernel_size; out vec2 out_uv; +out vec3 out_p; + +const float PI = 3.141592653589793; +const float SQRT_2 = 1.41421356237309504880168872420969808; + +vec2 w2c_sin(vec3 p) { + vec2 q = vec2(-p.x, p.y); + return p.z >= 0.f ? q : normalize(q); +} + +vec2 w2c_sin_no_backface(vec3 p) { + return vec2(-p.x, p.y); +} + +vec2 w2c_ait(vec3 p) { + float r = length(p.zx); + float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) + w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma + float y2d = p.y / w; + + float x2d = 0.0; + if (abs(p.x) < 5e-3) { + float x_over_r = p.x/r; + x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; + } else { + w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) + x2d = sign(-p.x) * w; + } + + return vec2(x2d * 0.5, y2d) / SQRT_2; +} +const float eps = 1.25e-8; +const int n_iter = 100; + +float newton_solve(float z) { + float cte = PI * z; + float x = 2.0 * asin(z); + float f = x + sin(x) - cte; + int i = 0; + while (abs(f) > eps && i < n_iter) { + x -= f / (1.0 + cos(x)); + f = x + sin(x) - cte; + i += 1; + } + + return 0.5 * x; +} + +vec2 w2c_mol(vec3 p) { + float g = newton_solve(p.y); + + float sg = sin(g); + float cg = cos(g); + return vec2((atan(-p.x, p.z) * cg) / PI, sg); +} +vec2 w2c_tan(vec3 p) { + p.z = max(p.z, 1e-2); + return vec2(-p.x, p.y) / (p.z*PI); +} +vec2 w2c_stg(vec3 p) { + float w = (1.0 + p.z) * 0.5; + return vec2(-p.x, p.y) / (PI * w); +} +vec2 w2c_zea(vec3 p) { + float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] + return vec2(-p.x, p.y) * 0.5 / w; +} +vec2 w2c_mer(vec3 p) { + return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; +} + +vec3 lonlat2xyz(vec2 lonlat) { + float t = lonlat.x; + float tc = cos(t); + float ts = sin(t); + + float d = lonlat.y; + float dc = cos(d); + float ds = sin(d); + + return vec3(dc * ts, ds, dc * tc); +} + +uniform int u_proj; + +vec2 proj(vec3 p) { + if (u_proj == 0) { + return w2c_tan(p); + } else if (u_proj == 1) { + return w2c_stg(p); + } else if (u_proj == 2) { + return w2c_sin(p); + } else if (u_proj == 3) { + return w2c_zea(p); + } else if (u_proj == 4) { + return w2c_ait(p); + } else if (u_proj == 5) { + return w2c_mol(p); + } else { + return w2c_mer(p); + } +} + void main() { - gl_Position = vec4(position, 0.f, 1.f); + vec3 p = inv_model * center; + + vec2 center_pos_clip_space = world2clip_mollweide(p); + + vec2 pos_clip_space = center_pos_clip_space; + gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); + out_uv = uv; + out_p = p; +}"#, + ); + out.insert( + r"hips_raytracer_backcolor.frag", + r#"#version 300 es +precision lowp float; +precision mediump int; + +out vec4 out_frag_color; + +uniform vec3 color; + +void main() { + out_frag_color = vec4(color, 1.0); +}"#, + ); + out.insert( + r"catalogs_healpix.vert", + r#"#version 300 es +precision lowp float; +layout (location = 0) in vec2 offset; +layout (location = 1) in vec2 uv; +layout (location = 2) in vec3 center; + +uniform float current_time; +uniform mat3 inv_model; + +uniform vec2 ndc_to_clip; +uniform float czf; +uniform vec2 kernel_size; + +out vec2 out_uv; +out vec3 out_p; + +const float PI = 3.141592653589793; +const float SQRT_2 = 1.41421356237309504880168872420969808; + +vec2 w2c_sin(vec3 p) { + vec2 q = vec2(-p.x, p.y); + return p.z >= 0.f ? q : normalize(q); +} + +vec2 w2c_sin_no_backface(vec3 p) { + return vec2(-p.x, p.y); +} + +vec2 w2c_ait(vec3 p) { + float r = length(p.zx); + float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) + w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma + float y2d = p.y / w; + + float x2d = 0.0; + if (abs(p.x) < 5e-3) { + float x_over_r = p.x/r; + x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; + } else { + w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) + x2d = sign(-p.x) * w; + } + + return vec2(x2d * 0.5, y2d) / SQRT_2; +} +const float eps = 1.25e-8; +const int n_iter = 100; + +float newton_solve(float z) { + float cte = PI * z; + float x = 2.0 * asin(z); + float f = x + sin(x) - cte; + int i = 0; + while (abs(f) > eps && i < n_iter) { + x -= f / (1.0 + cos(x)); + f = x + sin(x) - cte; + i += 1; + } + + return 0.5 * x; +} + +vec2 w2c_mol(vec3 p) { + float g = newton_solve(p.y); + + float sg = sin(g); + float cg = cos(g); + return vec2((atan(-p.x, p.z) * cg) / PI, sg); +} +vec2 w2c_tan(vec3 p) { + p.z = max(p.z, 1e-2); + return vec2(-p.x, p.y) / (p.z*PI); +} +vec2 w2c_stg(vec3 p) { + float w = (1.0 + p.z) * 0.5; + return vec2(-p.x, p.y) / (PI * w); +} +vec2 w2c_zea(vec3 p) { + float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] + return vec2(-p.x, p.y) * 0.5 / w; +} +vec2 w2c_mer(vec3 p) { + return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; +} + +vec3 lonlat2xyz(vec2 lonlat) { + float t = lonlat.x; + float tc = cos(t); + float ts = sin(t); + + float d = lonlat.y; + float dc = cos(d); + float ds = sin(d); + + return vec3(dc * ts, ds, dc * tc); +} + +uniform int u_proj; + +vec2 proj(vec3 p) { + if (u_proj == 0) { + return w2c_tan(p); + } else if (u_proj == 1) { + return w2c_stg(p); + } else if (u_proj == 2) { + return w2c_sin(p); + } else if (u_proj == 3) { + return w2c_zea(p); + } else if (u_proj == 4) { + return w2c_ait(p); + } else if (u_proj == 5) { + return w2c_mol(p); + } else { + return w2c_mer(p); + } +} + + +void main() { + vec3 p = inv_model * center; + + vec2 center_pos_clip_space = world2clip_healpix(p); + + vec2 pos_clip_space = center_pos_clip_space; + gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); + + out_uv = uv; + out_p = p; +}"#, + ); + out.insert( + r"line_base.vert", + r#"#version 300 es +precision highp float; +layout (location = 0) in vec2 ndc_pos; + +out float l; + +void main() { + gl_Position = vec4( + ndc_pos, + 0.0, + 1.0 + ); +}"#, + ); + out.insert( + r"hips_rasterizer_i16.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler2DArray; + +uniform sampler2DArray tex; + +in vec3 frag_uv_start; +in vec3 frag_uv_end; +in float frag_blending_factor; + +out vec4 out_frag_color; + +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + +vec4 uvw2c_r(vec3 uv) { + vec2 va = texture(tex, uv).ra; + + va.x = transfer_func(H, va.x, min_value, max_value); + + va.x = mix(va.x, 1.0 - va.x, reversed); + + vec4 c = colormap_f(va.x); + return apply_tonal(c); +} + +vec4 uvw2c_rgba(vec3 uv) { + vec4 c = texture(tex, uv).rgba; + + c.r = transfer_func(H, c.r, min_value, max_value); + c.g = transfer_func(H, c.g, min_value, max_value); + c.b = transfer_func(H, c.b, min_value, max_value); + + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 uvw2c_ra(vec3 uv) { + vec2 c = texture(tex, uv).rg; + + c.r = transfer_func(H, c.r, min_value, max_value); + + c.r = mix(c.r, 1.0 - c.r, reversed); + + vec3 color = colormap_f(c.r).rgb; + + return apply_tonal(vec4(color, c.g)); +} + +vec4 uvw2cmap_rgba(vec3 uv) { + float v = texture(tex, uv).r; + v = transfer_func(H, v, min_value, max_value); + vec4 c = colormap_f(v); + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uvw2c_f32(vec3 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uvw2c_i32(vec3 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_u8(vec3 uv) { + float val = float(decode_u8(texture(tex, uv).r)); + return val2c(val); +} + +uniform float opacity; + +void main() { + vec3 uv0 = frag_uv_start; + vec3 uv1 = frag_uv_end; + uv0.y = 1.0 - uv0.y; + uv1.y = 1.0 - uv1.y; + + vec4 color_start = uvw2c_i16(uv0); + vec4 color_end = uvw2c_i16(uv1); + + out_frag_color = mix(color_start, color_end, frag_blending_factor); + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"hips3d_raster.vert", + r#"#version 300 es +precision lowp float; + +layout (location = 0) in vec2 lonlat; +layout (location = 1) in vec3 uv; + +out vec3 frag_uv; + +uniform mat3 inv_model; +uniform vec2 ndc_to_clip; +uniform float czf; + +const float PI = 3.141592653589793; +const float SQRT_2 = 1.41421356237309504880168872420969808; + +vec2 w2c_sin(vec3 p) { + vec2 q = vec2(-p.x, p.y); + return p.z >= 0.f ? q : normalize(q); +} + +vec2 w2c_sin_no_backface(vec3 p) { + return vec2(-p.x, p.y); +} + +vec2 w2c_ait(vec3 p) { + float r = length(p.zx); + float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) + w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma + float y2d = p.y / w; + + float x2d = 0.0; + if (abs(p.x) < 5e-3) { + float x_over_r = p.x/r; + x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; + } else { + w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) + x2d = sign(-p.x) * w; + } + + return vec2(x2d * 0.5, y2d) / SQRT_2; +} +const float eps = 1.25e-8; +const int n_iter = 100; + +float newton_solve(float z) { + float cte = PI * z; + float x = 2.0 * asin(z); + float f = x + sin(x) - cte; + int i = 0; + while (abs(f) > eps && i < n_iter) { + x -= f / (1.0 + cos(x)); + f = x + sin(x) - cte; + i += 1; + } + + return 0.5 * x; +} + +vec2 w2c_mol(vec3 p) { + float g = newton_solve(p.y); + + float sg = sin(g); + float cg = cos(g); + return vec2((atan(-p.x, p.z) * cg) / PI, sg); +} +vec2 w2c_tan(vec3 p) { + p.z = max(p.z, 1e-2); + return vec2(-p.x, p.y) / (p.z*PI); +} +vec2 w2c_stg(vec3 p) { + float w = (1.0 + p.z) * 0.5; + return vec2(-p.x, p.y) / (PI * w); +} +vec2 w2c_zea(vec3 p) { + float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] + return vec2(-p.x, p.y) * 0.5 / w; +} +vec2 w2c_mer(vec3 p) { + return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; +} + +vec3 lonlat2xyz(vec2 lonlat) { + float t = lonlat.x; + float tc = cos(t); + float ts = sin(t); + + float d = lonlat.y; + float dc = cos(d); + float ds = sin(d); + + return vec3(dc * ts, ds, dc * tc); +} + +uniform int u_proj; + +vec2 proj(vec3 p) { + if (u_proj == 0) { + return w2c_tan(p); + } else if (u_proj == 1) { + return w2c_stg(p); + } else if (u_proj == 2) { + return w2c_sin(p); + } else if (u_proj == 3) { + return w2c_zea(p); + } else if (u_proj == 4) { + return w2c_ait(p); + } else if (u_proj == 5) { + return w2c_mol(p); + } else { + return w2c_mer(p); + } +} + +void main() { + vec3 p_xyz = lonlat2xyz(lonlat); + vec3 p_w = inv_model * p_xyz; + vec2 p_clip = proj(p_w.xyz); + + vec2 p_ndc = p_clip / (ndc_to_clip * czf); + gl_Position = vec4(p_ndc, 0.0, 1.0); + + frag_uv = uv; }"#, ); out.insert( @@ -412,6 +1110,1406 @@ void main() { out_frag_color = c; out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"catalogs_aitoff.vert", + r#"#version 300 es +precision lowp float; +layout (location = 0) in vec2 offset; +layout (location = 1) in vec2 uv; +layout (location = 2) in vec3 center; + +uniform float current_time; +uniform mat3 inv_model; + +uniform vec2 ndc_to_clip; +uniform float czf; +uniform vec2 kernel_size; + +out vec2 out_uv; +out vec3 out_p; + +const float PI = 3.141592653589793; +const float SQRT_2 = 1.41421356237309504880168872420969808; + +vec2 w2c_sin(vec3 p) { + vec2 q = vec2(-p.x, p.y); + return p.z >= 0.f ? q : normalize(q); +} + +vec2 w2c_sin_no_backface(vec3 p) { + return vec2(-p.x, p.y); +} + +vec2 w2c_ait(vec3 p) { + float r = length(p.zx); + float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) + w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma + float y2d = p.y / w; + + float x2d = 0.0; + if (abs(p.x) < 5e-3) { + float x_over_r = p.x/r; + x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; + } else { + w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) + x2d = sign(-p.x) * w; + } + + return vec2(x2d * 0.5, y2d) / SQRT_2; +} +const float eps = 1.25e-8; +const int n_iter = 100; + +float newton_solve(float z) { + float cte = PI * z; + float x = 2.0 * asin(z); + float f = x + sin(x) - cte; + int i = 0; + while (abs(f) > eps && i < n_iter) { + x -= f / (1.0 + cos(x)); + f = x + sin(x) - cte; + i += 1; + } + + return 0.5 * x; +} + +vec2 w2c_mol(vec3 p) { + float g = newton_solve(p.y); + + float sg = sin(g); + float cg = cos(g); + return vec2((atan(-p.x, p.z) * cg) / PI, sg); +} +vec2 w2c_tan(vec3 p) { + p.z = max(p.z, 1e-2); + return vec2(-p.x, p.y) / (p.z*PI); +} +vec2 w2c_stg(vec3 p) { + float w = (1.0 + p.z) * 0.5; + return vec2(-p.x, p.y) / (PI * w); +} +vec2 w2c_zea(vec3 p) { + float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] + return vec2(-p.x, p.y) * 0.5 / w; +} +vec2 w2c_mer(vec3 p) { + return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; +} + +vec3 lonlat2xyz(vec2 lonlat) { + float t = lonlat.x; + float tc = cos(t); + float ts = sin(t); + + float d = lonlat.y; + float dc = cos(d); + float ds = sin(d); + + return vec3(dc * ts, ds, dc * tc); +} + +uniform int u_proj; + +vec2 proj(vec3 p) { + if (u_proj == 0) { + return w2c_tan(p); + } else if (u_proj == 1) { + return w2c_stg(p); + } else if (u_proj == 2) { + return w2c_sin(p); + } else if (u_proj == 3) { + return w2c_zea(p); + } else if (u_proj == 4) { + return w2c_ait(p); + } else if (u_proj == 5) { + return w2c_mol(p); + } else { + return w2c_mer(p); + } +} + +void main() { + vec3 p = inv_model * center; + + vec2 center_pos_clip_space = world2clip_aitoff(p); + + vec2 pos_clip_space = center_pos_clip_space; + gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); + + out_uv = uv; + out_p = p; +}"#, + ); + out.insert( + r"colormaps_colormap.vert", + r#"#version 300 es +precision lowp float; +precision lowp sampler2D; + +layout (location = 0) in vec2 position; +layout (location = 1) in vec2 uv; + +out vec2 out_uv; + +void main() { + gl_Position = vec4(position, 0.f, 1.f); + out_uv = uv; +}"#, + ); + out.insert( + r"fits_i16.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler2D; +precision mediump int; + +out vec4 out_frag_color; +in vec2 frag_uv; + +uniform sampler2D tex; +uniform float opacity; + +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uv2c_f32(vec2 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uv2c_i32(vec2 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return val2c(val); +} + +vec4 uv2c_i16(vec2 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return val2c(val); +} + +vec4 uv2c_u8(vec2 uv) { + float val = float(decode_u8(texture(tex, uv).r)); + return val2c(val); +} + +void main() { + vec2 uv = frag_uv; + uv.y = 1.0 - uv.y; + + out_frag_color = uv2c_i16(frag_uv); + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"fits_f32.frag", + r#"#version 300 es +precision highp float; +precision highp sampler2D; +precision highp int; + +out vec4 out_frag_color; +in vec2 frag_uv; + +uniform sampler2D tex; +uniform float opacity; + +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uv2c_f32(vec2 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uv2c_i32(vec2 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return val2c(val); +} + +vec4 uv2c_i16(vec2 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return val2c(val); +} + +vec4 uv2c_u8(vec2 uv) { + float val = float(decode_u8(texture(tex, uv).r)); + return val2c(val); +} + +void main() { + vec2 uv = frag_uv; + uv.y = 1.0 - uv.y; + + out_frag_color = uv2c_f32(frag_uv); + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"hips_rasterizer_rgba.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler2DArray; + +uniform sampler2DArray tex; + +in vec3 frag_uv_start; +in vec3 frag_uv_end; +in float frag_blending_factor; + +out vec4 out_frag_color; +uniform float opacity; + +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + +vec4 uvw2c_r(vec3 uv) { + vec2 va = texture(tex, uv).ra; + + va.x = transfer_func(H, va.x, min_value, max_value); + + va.x = mix(va.x, 1.0 - va.x, reversed); + + vec4 c = colormap_f(va.x); + return apply_tonal(c); +} + +vec4 uvw2c_rgba(vec3 uv) { + vec4 c = texture(tex, uv).rgba; + + c.r = transfer_func(H, c.r, min_value, max_value); + c.g = transfer_func(H, c.g, min_value, max_value); + c.b = transfer_func(H, c.b, min_value, max_value); + + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 uvw2c_ra(vec3 uv) { + vec2 c = texture(tex, uv).rg; + + c.r = transfer_func(H, c.r, min_value, max_value); + + c.r = mix(c.r, 1.0 - c.r, reversed); + + vec3 color = colormap_f(c.r).rgb; + + return apply_tonal(vec4(color, c.g)); +} + +vec4 uvw2cmap_rgba(vec3 uv) { + float v = texture(tex, uv).r; + v = transfer_func(H, v, min_value, max_value); + vec4 c = colormap_f(v); + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uvw2c_f32(vec3 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uvw2c_i32(vec3 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_u8(vec3 uv) { + float val = float(decode_u8(texture(tex, uv).r)); + return val2c(val); +} + +void main() { + vec4 color_start = uvw2c_rgba(frag_uv_start); + vec4 color_end = uvw2c_rgba(frag_uv_end); + + out_frag_color = mix(color_start, color_end, frag_blending_factor); + out_frag_color.a = opacity * out_frag_color.a; +}"#, + ); + out.insert( + r"hips_raytracer_raytracer.vert", + r#"#version 300 es +precision lowp float; +precision mediump int; + +layout (location = 0) in vec2 pos_clip_space; +layout (location = 1) in vec3 pos_world_space; + +out vec2 out_clip_pos; +out vec3 frag_pos; + +uniform vec2 ndc_to_clip; +uniform float czf; +uniform mat3 model; + +void main() { + vec2 uv = pos_clip_space * 0.5 + 0.5; + + frag_pos = model * pos_world_space; + + gl_Position = vec4(pos_clip_space / (ndc_to_clip * czf), 0.0, 1.0); + out_clip_pos = pos_clip_space; +}"#, + ); + out.insert( + r"moc_base.frag", + r#"#version 300 es + +precision lowp float; +out vec4 color; + +uniform vec4 u_color; + +void main() { + color = u_color; +}"#, + ); + out.insert( + r"catalogs_catalog.frag", + r#"#version 300 es +precision lowp float; + +in vec2 out_uv; +in vec3 out_p; + +out vec4 color; + +uniform sampler2D kernel_texture; +uniform float max_density; // max number of sources in a kernel sized HEALPix cell at the current depth +uniform float fov; +uniform float strength; +void main() { + color = texture(kernel_texture, out_uv) / max(log2(fov*100.0), 1.0); + color.r *= strength; +}"#, + ); + out.insert( + r"catalogs_arc.vert", + r#"#version 300 es +precision lowp float; +layout (location = 0) in vec2 offset; +layout (location = 1) in vec2 uv; +layout (location = 2) in vec3 center; + +uniform float current_time; +uniform mat3 inv_model; + +uniform vec2 ndc_to_clip; +uniform float czf; +uniform vec2 kernel_size; + +out vec2 out_uv; +out vec3 out_p; + +const float PI = 3.141592653589793; +const float SQRT_2 = 1.41421356237309504880168872420969808; + +vec2 w2c_sin(vec3 p) { + vec2 q = vec2(-p.x, p.y); + return p.z >= 0.f ? q : normalize(q); +} + +vec2 w2c_sin_no_backface(vec3 p) { + return vec2(-p.x, p.y); +} + +vec2 w2c_ait(vec3 p) { + float r = length(p.zx); + float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) + w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma + float y2d = p.y / w; + + float x2d = 0.0; + if (abs(p.x) < 5e-3) { + float x_over_r = p.x/r; + x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; + } else { + w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) + x2d = sign(-p.x) * w; + } + + return vec2(x2d * 0.5, y2d) / SQRT_2; +} +const float eps = 1.25e-8; +const int n_iter = 100; + +float newton_solve(float z) { + float cte = PI * z; + float x = 2.0 * asin(z); + float f = x + sin(x) - cte; + int i = 0; + while (abs(f) > eps && i < n_iter) { + x -= f / (1.0 + cos(x)); + f = x + sin(x) - cte; + i += 1; + } + + return 0.5 * x; +} + +vec2 w2c_mol(vec3 p) { + float g = newton_solve(p.y); + + float sg = sin(g); + float cg = cos(g); + return vec2((atan(-p.x, p.z) * cg) / PI, sg); +} +vec2 w2c_tan(vec3 p) { + p.z = max(p.z, 1e-2); + return vec2(-p.x, p.y) / (p.z*PI); +} +vec2 w2c_stg(vec3 p) { + float w = (1.0 + p.z) * 0.5; + return vec2(-p.x, p.y) / (PI * w); +} +vec2 w2c_zea(vec3 p) { + float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] + return vec2(-p.x, p.y) * 0.5 / w; +} +vec2 w2c_mer(vec3 p) { + return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; +} + +vec3 lonlat2xyz(vec2 lonlat) { + float t = lonlat.x; + float tc = cos(t); + float ts = sin(t); + + float d = lonlat.y; + float dc = cos(d); + float ds = sin(d); + + return vec3(dc * ts, ds, dc * tc); +} + +uniform int u_proj; + +vec2 proj(vec3 p) { + if (u_proj == 0) { + return w2c_tan(p); + } else if (u_proj == 1) { + return w2c_stg(p); + } else if (u_proj == 2) { + return w2c_sin(p); + } else if (u_proj == 3) { + return w2c_zea(p); + } else if (u_proj == 4) { + return w2c_ait(p); + } else if (u_proj == 5) { + return w2c_mol(p); + } else { + return w2c_mer(p); + } +} + +void main() { + vec3 p = inv_model * center; + + vec2 center_pos_clip_space = world2clip_arc(p); + + vec2 pos_clip_space = center_pos_clip_space; + gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); + + out_uv = uv; + out_p = p; +}"#, + ); + out.insert( + r"fits_i32.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler2D; +precision mediump int; + +out vec4 out_frag_color; +in vec2 frag_uv; + +uniform sampler2D tex; +uniform float opacity; + +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uv2c_f32(vec2 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uv2c_i32(vec2 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return val2c(val); +} + +vec4 uv2c_i16(vec2 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return val2c(val); +} + +vec4 uv2c_u8(vec2 uv) { + float val = float(decode_u8(texture(tex, uv).r)); + return val2c(val); +} + +void main() { + vec2 uv = frag_uv; + uv.y = 1.0 - uv.y; + + out_frag_color = uv2c_i32(frag_uv); + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"line_base.frag", + r#"#version 300 es + +precision highp float; + +out vec4 color; +in vec2 l; + +uniform vec4 u_color; +uniform float u_thickness; +uniform float u_width; +uniform float u_height; + +void main() { + if (l.x > 0.05) { + discard; + } else { + color = u_color; + + float dist = abs((u_thickness + 2.0) * l.y); + + float half_thickness = (u_thickness + 2.0) * 0.5; + color.a = color.a * (1.0 - smoothstep(half_thickness - 1.0, half_thickness, dist)); + } +}"#, + ); + out.insert( + r"catalogs_mercator.vert", + r#"#version 300 es +precision lowp float; +layout (location = 0) in vec2 offset; +layout (location = 1) in vec2 uv; +layout (location = 2) in vec3 center; + +uniform float current_time; +uniform mat3 inv_model; + +uniform vec2 ndc_to_clip; +uniform float czf; +uniform vec2 kernel_size; + +out vec2 out_uv; +out vec3 out_p; + +const float PI = 3.141592653589793; +const float SQRT_2 = 1.41421356237309504880168872420969808; + +vec2 w2c_sin(vec3 p) { + vec2 q = vec2(-p.x, p.y); + return p.z >= 0.f ? q : normalize(q); +} + +vec2 w2c_sin_no_backface(vec3 p) { + return vec2(-p.x, p.y); +} + +vec2 w2c_ait(vec3 p) { + float r = length(p.zx); + float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) + w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma + float y2d = p.y / w; + + float x2d = 0.0; + if (abs(p.x) < 5e-3) { + float x_over_r = p.x/r; + x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; + } else { + w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) + x2d = sign(-p.x) * w; + } + + return vec2(x2d * 0.5, y2d) / SQRT_2; +} +const float eps = 1.25e-8; +const int n_iter = 100; + +float newton_solve(float z) { + float cte = PI * z; + float x = 2.0 * asin(z); + float f = x + sin(x) - cte; + int i = 0; + while (abs(f) > eps && i < n_iter) { + x -= f / (1.0 + cos(x)); + f = x + sin(x) - cte; + i += 1; + } + + return 0.5 * x; +} + +vec2 w2c_mol(vec3 p) { + float g = newton_solve(p.y); + + float sg = sin(g); + float cg = cos(g); + return vec2((atan(-p.x, p.z) * cg) / PI, sg); +} +vec2 w2c_tan(vec3 p) { + p.z = max(p.z, 1e-2); + return vec2(-p.x, p.y) / (p.z*PI); +} +vec2 w2c_stg(vec3 p) { + float w = (1.0 + p.z) * 0.5; + return vec2(-p.x, p.y) / (PI * w); +} +vec2 w2c_zea(vec3 p) { + float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] + return vec2(-p.x, p.y) * 0.5 / w; +} +vec2 w2c_mer(vec3 p) { + return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; +} + +vec3 lonlat2xyz(vec2 lonlat) { + float t = lonlat.x; + float tc = cos(t); + float ts = sin(t); + + float d = lonlat.y; + float dc = cos(d); + float ds = sin(d); + + return vec3(dc * ts, ds, dc * tc); +} + +uniform int u_proj; + +vec2 proj(vec3 p) { + if (u_proj == 0) { + return w2c_tan(p); + } else if (u_proj == 1) { + return w2c_stg(p); + } else if (u_proj == 2) { + return w2c_sin(p); + } else if (u_proj == 3) { + return w2c_zea(p); + } else if (u_proj == 4) { + return w2c_ait(p); + } else if (u_proj == 5) { + return w2c_mol(p); + } else { + return w2c_mer(p); + } +} + + +void main() { + vec3 p = inv_model * center; + + vec2 center_pos_clip_space = world2clip_mercator(p); + + vec2 pos_clip_space = center_pos_clip_space; + gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); + + out_uv = uv; + out_p = p; +}"#, + ); + out.insert( + r"passes_post_fragment_100es.frag", + r#"#version 300 es +precision mediump float; + +in vec2 v_tc; +out vec4 color; + +uniform sampler2D fbo_tex; + +vec3 srgb_from_linear(vec3 rgb) { + bvec3 cutoff = lessThan(rgb, vec3(0.0031308)); + vec3 lower = rgb * vec3(3294.6); + vec3 higher = vec3(269.025) * pow(rgb, vec3(1.0 / 2.4)) - vec3(14.025); + return mix(higher, lower, vec3(cutoff)); +} + +vec4 srgba_from_linear(vec4 rgba) { + return vec4(srgb_from_linear(rgba.rgb), 255.0 * rgba.a); +} + +void main() { + color = texture(fbo_tex, v_tc); + }"#, ); out.insert( @@ -430,19 +2528,148 @@ void main() { }"#, ); out.insert( - r"fits_base.vert", + r"line_inst_lonlat.vert", r#"#version 300 es precision highp float; -precision highp int; +layout (location = 0) in vec2 p_a_lonlat; +layout (location = 1) in vec2 p_b_lonlat; +layout (location = 2) in vec2 vertex; -layout (location = 0) in vec2 ndc_pos; -layout (location = 1) in vec2 uv; +uniform mat3 u_2world; +uniform vec2 ndc_to_clip; +uniform float czf; +uniform float u_width; +uniform float u_height; +uniform float u_thickness; -out vec2 frag_uv; +out vec2 l; + +const float PI = 3.141592653589793; +const float SQRT_2 = 1.41421356237309504880168872420969808; + +vec2 w2c_sin(vec3 p) { + vec2 q = vec2(-p.x, p.y); + return p.z >= 0.f ? q : normalize(q); +} + +vec2 w2c_sin_no_backface(vec3 p) { + return vec2(-p.x, p.y); +} + +vec2 w2c_ait(vec3 p) { + float r = length(p.zx); + float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) + w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma + float y2d = p.y / w; + + float x2d = 0.0; + if (abs(p.x) < 5e-3) { + float x_over_r = p.x/r; + x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; + } else { + w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) + x2d = sign(-p.x) * w; + } + + return vec2(x2d * 0.5, y2d) / SQRT_2; +} +const float eps = 1.25e-8; +const int n_iter = 100; + +float newton_solve(float z) { + float cte = PI * z; + float x = 2.0 * asin(z); + float f = x + sin(x) - cte; + int i = 0; + while (abs(f) > eps && i < n_iter) { + x -= f / (1.0 + cos(x)); + f = x + sin(x) - cte; + i += 1; + } + + return 0.5 * x; +} + +vec2 w2c_mol(vec3 p) { + float g = newton_solve(p.y); + + float sg = sin(g); + float cg = cos(g); + return vec2((atan(-p.x, p.z) * cg) / PI, sg); +} +vec2 w2c_tan(vec3 p) { + p.z = max(p.z, 1e-2); + return vec2(-p.x, p.y) / (p.z*PI); +} +vec2 w2c_stg(vec3 p) { + float w = (1.0 + p.z) * 0.5; + return vec2(-p.x, p.y) / (PI * w); +} +vec2 w2c_zea(vec3 p) { + float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] + return vec2(-p.x, p.y) * 0.5 / w; +} +vec2 w2c_mer(vec3 p) { + return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; +} + +vec3 lonlat2xyz(vec2 lonlat) { + float t = lonlat.x; + float tc = cos(t); + float ts = sin(t); + + float d = lonlat.y; + float dc = cos(d); + float ds = sin(d); + + return vec3(dc * ts, ds, dc * tc); +} + +uniform int u_proj; + +vec2 proj(vec3 p) { + if (u_proj == 0) { + return w2c_tan(p); + } else if (u_proj == 1) { + return w2c_stg(p); + } else if (u_proj == 2) { + return w2c_sin(p); + } else if (u_proj == 3) { + return w2c_zea(p); + } else if (u_proj == 4) { + return w2c_ait(p); + } else if (u_proj == 5) { + return w2c_mol(p); + } else { + return w2c_mer(p); + } +} void main() { - gl_Position = vec4(ndc_pos, 0.0, 1.0); - frag_uv = uv; + vec3 p_a_xyz = lonlat2xyz(p_a_lonlat); + vec3 p_b_xyz = lonlat2xyz(p_b_lonlat); + vec3 p_a_w = u_2world * p_a_xyz; + vec3 p_b_w = u_2world * p_b_xyz; + vec2 p_a_clip = proj(p_a_w); + vec2 p_b_clip = proj(p_b_w); + + vec2 da = p_a_clip - p_b_clip; + + vec2 p_a_ndc = p_a_clip / (ndc_to_clip * czf); + vec2 p_b_ndc = p_b_clip / (ndc_to_clip * czf); + + vec2 x_b = p_b_ndc - p_a_ndc; + vec2 y_b = normalize(vec2(-x_b.y, x_b.x)); + + float ndc2pix = 2.0 / u_width; + + vec2 p_ndc_x = x_b * vertex.x; + vec2 p_ndc_y = (u_thickness + 2.0) * y_b * vertex.y * vec2(1.0, u_width/u_height) * ndc2pix; + + vec2 p_ndc = p_a_ndc + p_ndc_x + p_ndc_y; + gl_Position = vec4(p_ndc, 0.f, 1.f); + + l = vec2(dot(da, da), vertex.y); }"#, ); out.insert( @@ -709,3700 +2936,6 @@ void main() { out_frag_color = color; out_frag_color.a = out_frag_color.a * opacity; -}"#, - ); - out.insert( - r"hips_rasterizer_u8.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler2DArray; - -uniform sampler2DArray tex; - -in vec3 frag_uv_start; -in vec3 frag_uv_end; -in float frag_blending_factor; - -out vec4 out_frag_color; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -vec3 rgb2hsv(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - -vec4 uvw2c_r(vec3 uv) { - vec2 va = texture(tex, uv).ra; - - va.x = transfer_func(H, va.x, min_value, max_value); - - va.x = mix(va.x, 1.0 - va.x, reversed); - - vec4 c = colormap_f(va.x); - return apply_tonal(c); -} - -vec4 uvw2c_rgba(vec3 uv) { - vec4 c = texture(tex, uv).rgba; - - c.r = transfer_func(H, c.r, min_value, max_value); - c.g = transfer_func(H, c.g, min_value, max_value); - c.b = transfer_func(H, c.b, min_value, max_value); - - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 uvw2c_ra(vec3 uv) { - vec2 c = texture(tex, uv).rg; - - c.r = transfer_func(H, c.r, min_value, max_value); - - c.r = mix(c.r, 1.0 - c.r, reversed); - - vec3 color = colormap_f(c.r).rgb; - - return apply_tonal(vec4(color, c.g)); -} - -vec4 uvw2cmap_rgba(vec3 uv) { - float v = texture(tex, uv).r; - v = transfer_func(H, v, min_value, max_value); - vec4 c = colormap_f(v); - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uvw2c_f32(vec3 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uvw2c_i32(vec3 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_u8(vec3 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} - -uniform float opacity; - -void main() { - vec3 uv0 = frag_uv_start; - vec3 uv1 = frag_uv_end; - uv0.y = 1.0 - uv0.y; - uv1.y = 1.0 - uv1.y; - - vec4 color_start = uvw2c_u8(uv0); - vec4 color_end = uvw2c_u8(uv1); - - out_frag_color = mix(color_start, color_end, frag_blending_factor); - out_frag_color.a = out_frag_color.a * opacity; -}"#, - ); - out.insert( - r"hips3d_i16.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler3D; -precision lowp isampler3D; -precision lowp usampler3D; - -uniform sampler3D tex; - -in vec3 frag_uv; - -out vec4 out_frag_color; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -vec3 rgb2hsv(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - -vec4 uvw2c_r(vec3 uv) { - vec2 va = texture(tex, uv).ra; - - va.x = transfer_func(H, va.x, min_value, max_value); - - va.x = mix(va.x, 1.0 - va.x, reversed); - - vec4 c = colormap_f(va.x); - return apply_tonal(c); -} - -vec4 uvw2c_rgba(vec3 uv) { - vec4 c = texture(tex, uv).rgba; - - c.r = transfer_func(H, c.r, min_value, max_value); - c.g = transfer_func(H, c.g, min_value, max_value); - c.b = transfer_func(H, c.b, min_value, max_value); - - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 uvw2c_ra(vec3 uv) { - vec2 c = texture(tex, uv).rg; - - c.r = transfer_func(H, c.r, min_value, max_value); - - c.r = mix(c.r, 1.0 - c.r, reversed); - - vec3 color = colormap_f(c.r).rgb; - - return apply_tonal(vec4(color, c.g)); -} - -vec4 uvw2cmap_rgba(vec3 uv) { - float v = texture(tex, uv).r; - v = transfer_func(H, v, min_value, max_value); - vec4 c = colormap_f(v); - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uvw2c_f32(vec3 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uvw2c_i32(vec3 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_u8(vec3 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} - -uniform float opacity; - -void main() { - vec3 uv = vec3(frag_uv.xyz); - uv.y = 1.0 - uv.y; - - vec4 color = uvw2c_i16(uv); - - out_frag_color = color; - out_frag_color.a = out_frag_color.a * opacity; -}"#, - ); - out.insert( - r"hips_raytracer_raytracer.vert", - r#"#version 300 es -precision lowp float; -precision mediump int; - -layout (location = 0) in vec2 pos_clip_space; -layout (location = 1) in vec3 pos_world_space; - -out vec2 out_clip_pos; -out vec3 frag_pos; - -uniform vec2 ndc_to_clip; -uniform float czf; -uniform mat3 model; - -void main() { - vec2 uv = pos_clip_space * 0.5 + 0.5; - - frag_pos = model * pos_world_space; - - gl_Position = vec4(pos_clip_space / (ndc_to_clip * czf), 0.0, 1.0); - out_clip_pos = pos_clip_space; -}"#, - ); - out.insert( - r"catalogs_mollweide.vert", - r#"#version 300 es -precision lowp float; -layout (location = 0) in vec2 offset; -layout (location = 1) in vec2 uv; -layout (location = 2) in vec3 center; - -uniform float current_time; -uniform mat3 inv_model; - -uniform vec2 ndc_to_clip; -uniform float czf; -uniform vec2 kernel_size; - -out vec2 out_uv; -out vec3 out_p; - -const float PI = 3.141592653589793; -const float SQRT_2 = 1.41421356237309504880168872420969808; - -vec2 w2c_sin(vec3 p) { - vec2 q = vec2(-p.x, p.y); - return p.z >= 0.f ? q : normalize(q); -} - -vec2 w2c_sin_no_backface(vec3 p) { - return vec2(-p.x, p.y); -} - -vec2 w2c_ait(vec3 p) { - float r = length(p.zx); - float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) - w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma - float y2d = p.y / w; - - float x2d = 0.0; - if (abs(p.x) < 5e-3) { - float x_over_r = p.x/r; - x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; - } else { - w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) - x2d = sign(-p.x) * w; - } - - return vec2(x2d * 0.5, y2d) / SQRT_2; -} -const float eps = 1.25e-8; -const int n_iter = 100; - -float newton_solve(float z) { - float cte = PI * z; - float x = 2.0 * asin(z); - float f = x + sin(x) - cte; - int i = 0; - while (abs(f) > eps && i < n_iter) { - x -= f / (1.0 + cos(x)); - f = x + sin(x) - cte; - i += 1; - } - - return 0.5 * x; -} - -vec2 w2c_mol(vec3 p) { - float g = newton_solve(p.y); - - float sg = sin(g); - float cg = cos(g); - return vec2((atan(-p.x, p.z) * cg) / PI, sg); -} -vec2 w2c_tan(vec3 p) { - p.z = max(p.z, 1e-2); - return vec2(-p.x, p.y) / (p.z*PI); -} -vec2 w2c_stg(vec3 p) { - float w = (1.0 + p.z) * 0.5; - return vec2(-p.x, p.y) / (PI * w); -} -vec2 w2c_zea(vec3 p) { - float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] - return vec2(-p.x, p.y) * 0.5 / w; -} -vec2 w2c_mer(vec3 p) { - return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; -} - -vec3 lonlat2xyz(vec2 lonlat) { - float t = lonlat.x; - float tc = cos(t); - float ts = sin(t); - - float d = lonlat.y; - float dc = cos(d); - float ds = sin(d); - - return vec3(dc * ts, ds, dc * tc); -} - -uniform int u_proj; - -vec2 proj(vec3 p) { - if (u_proj == 0) { - return w2c_tan(p); - } else if (u_proj == 1) { - return w2c_stg(p); - } else if (u_proj == 2) { - return w2c_sin(p); - } else if (u_proj == 3) { - return w2c_zea(p); - } else if (u_proj == 4) { - return w2c_ait(p); - } else if (u_proj == 5) { - return w2c_mol(p); - } else { - return w2c_mer(p); - } -} - - -void main() { - vec3 p = inv_model * center; - - vec2 center_pos_clip_space = world2clip_mollweide(p); - - vec2 pos_clip_space = center_pos_clip_space; - gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); - - out_uv = uv; - out_p = p; -}"#, - ); - out.insert( - r"catalogs_aitoff.vert", - r#"#version 300 es -precision lowp float; -layout (location = 0) in vec2 offset; -layout (location = 1) in vec2 uv; -layout (location = 2) in vec3 center; - -uniform float current_time; -uniform mat3 inv_model; - -uniform vec2 ndc_to_clip; -uniform float czf; -uniform vec2 kernel_size; - -out vec2 out_uv; -out vec3 out_p; - -const float PI = 3.141592653589793; -const float SQRT_2 = 1.41421356237309504880168872420969808; - -vec2 w2c_sin(vec3 p) { - vec2 q = vec2(-p.x, p.y); - return p.z >= 0.f ? q : normalize(q); -} - -vec2 w2c_sin_no_backface(vec3 p) { - return vec2(-p.x, p.y); -} - -vec2 w2c_ait(vec3 p) { - float r = length(p.zx); - float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) - w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma - float y2d = p.y / w; - - float x2d = 0.0; - if (abs(p.x) < 5e-3) { - float x_over_r = p.x/r; - x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; - } else { - w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) - x2d = sign(-p.x) * w; - } - - return vec2(x2d * 0.5, y2d) / SQRT_2; -} -const float eps = 1.25e-8; -const int n_iter = 100; - -float newton_solve(float z) { - float cte = PI * z; - float x = 2.0 * asin(z); - float f = x + sin(x) - cte; - int i = 0; - while (abs(f) > eps && i < n_iter) { - x -= f / (1.0 + cos(x)); - f = x + sin(x) - cte; - i += 1; - } - - return 0.5 * x; -} - -vec2 w2c_mol(vec3 p) { - float g = newton_solve(p.y); - - float sg = sin(g); - float cg = cos(g); - return vec2((atan(-p.x, p.z) * cg) / PI, sg); -} -vec2 w2c_tan(vec3 p) { - p.z = max(p.z, 1e-2); - return vec2(-p.x, p.y) / (p.z*PI); -} -vec2 w2c_stg(vec3 p) { - float w = (1.0 + p.z) * 0.5; - return vec2(-p.x, p.y) / (PI * w); -} -vec2 w2c_zea(vec3 p) { - float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] - return vec2(-p.x, p.y) * 0.5 / w; -} -vec2 w2c_mer(vec3 p) { - return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; -} - -vec3 lonlat2xyz(vec2 lonlat) { - float t = lonlat.x; - float tc = cos(t); - float ts = sin(t); - - float d = lonlat.y; - float dc = cos(d); - float ds = sin(d); - - return vec3(dc * ts, ds, dc * tc); -} - -uniform int u_proj; - -vec2 proj(vec3 p) { - if (u_proj == 0) { - return w2c_tan(p); - } else if (u_proj == 1) { - return w2c_stg(p); - } else if (u_proj == 2) { - return w2c_sin(p); - } else if (u_proj == 3) { - return w2c_zea(p); - } else if (u_proj == 4) { - return w2c_ait(p); - } else if (u_proj == 5) { - return w2c_mol(p); - } else { - return w2c_mer(p); - } -} - -void main() { - vec3 p = inv_model * center; - - vec2 center_pos_clip_space = world2clip_aitoff(p); - - vec2 pos_clip_space = center_pos_clip_space; - gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); - - out_uv = uv; - out_p = p; -}"#, - ); - out.insert( - r"hips_raytracer_rgba.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler2DArray; -precision mediump int; - -uniform sampler2DArray tex; - -in vec2 out_clip_pos; -in vec3 frag_pos; -out vec4 out_frag_color; - -struct Tile { - int uniq; // Healpix cell - int texture_idx; // Index in the texture buffer - float start_time; // Absolute time that the load has been done in ms - float empty; -}; - -uniform Tile textures_tiles[12]; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -vec3 rgb2hsv(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - -vec4 uvw2c_r(vec3 uv) { - vec2 va = texture(tex, uv).ra; - - va.x = transfer_func(H, va.x, min_value, max_value); - - va.x = mix(va.x, 1.0 - va.x, reversed); - - vec4 c = colormap_f(va.x); - return apply_tonal(c); -} - -vec4 uvw2c_rgba(vec3 uv) { - vec4 c = texture(tex, uv).rgba; - - c.r = transfer_func(H, c.r, min_value, max_value); - c.g = transfer_func(H, c.g, min_value, max_value); - c.b = transfer_func(H, c.b, min_value, max_value); - - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 uvw2c_ra(vec3 uv) { - vec2 c = texture(tex, uv).rg; - - c.r = transfer_func(H, c.r, min_value, max_value); - - c.r = mix(c.r, 1.0 - c.r, reversed); - - vec3 color = colormap_f(c.r).rgb; - - return apply_tonal(vec4(color, c.g)); -} - -vec4 uvw2cmap_rgba(vec3 uv) { - float v = texture(tex, uv).r; - v = transfer_func(H, v, min_value, max_value); - vec4 c = colormap_f(v); - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uvw2c_f32(vec3 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uvw2c_i32(vec3 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_u8(vec3 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} -const float TWICE_PI = 6.28318530718; -const float PI = 3.141592653589793; -const float FOUR_OVER_PI = 1.27323954474; -const float TRANSITION_Z = 0.66666666666; -const float TRANSITION_Z_INV = 1.5; - -int quarter(vec2 p) { - int x_neg = int(p.x < 0.0); - int y_neg = int(p.y < 0.0); - int q = (x_neg + y_neg) | (y_neg << 1); - return q; -} - -float xpm1(vec2 p) { - bool x_neg = (p.x < 0.0); - bool y_neg = (p.y < 0.0); - float lon = atan(abs(p.y), abs(p.x)); - float x02 = lon * FOUR_OVER_PI; - if (x_neg != y_neg) { // Could be replaced by a sign copy from (x_neg ^ y_neg) << 32 - return 1.0 - x02; - } else { - return x02 - 1.0; - } -} - -float one_minus_z_pos(vec3 p) { - float d2 = dot(p.xy, p.xy); // z = sqrt(1 - d2) AND sqrt(1 - x) = 1 - x / 2 - x^2 / 8 - x^3 / 16 - 5 x^4/128 - 7 * x^5/256 - - if (d2 < 1e-1) { // <=> dec > 84.27 deg - return d2 * (0.5 + d2 * (0.125 + d2 * (0.0625 + d2 * (0.0390625 + d2 * 0.02734375)))); - } - return 1.0f - p.z; -} - -float one_minus_z_neg(vec3 p) { - float d2 = dot(p.xy, p.xy); // z = sqrt(1 - d2) AND sqrt(1 - x) = 1 - x / 2 - x^2 / 8 - x^3 / 16 - 5 x^4/128 - 7 * x^5/256 - if (d2 < 1e-1f) { // <=> dec < -84.27 deg - return d2 * (0.5 + d2 * (0.125 + d2 * (0.0625 + d2 * (0.0390625 + d2 * 0.02734375)))); - } - return p.z + 1.0; -} - -int ij2z(int i, int j) { - int i4 = i | (j << 2); - - int j4 = (i4 ^ (i4 >> 1)) & 0x22222222; - int i5 = i4 ^ j4 ^ (j4 << 1); - - return i5; -} - -struct HashDxDy { - int idx; - float dx; - float dy; -}; - -uniform sampler2D ang2pixd; -HashDxDy hash_with_dxdy2(vec2 radec) { - vec2 aa = vec2(radec.x/TWICE_PI + 1.0, (radec.y/PI) + 0.5); - vec3 v = texture(ang2pixd, aa).rgb; - return HashDxDy( - int(v.x * 255.0), - v.y, - v.z - ); -} -HashDxDy hash_with_dxdy(int depth, vec3 p) { - - int nside = 1 << depth; - float half_nside = float(nside) * 0.5; - - float x_pm1 = xpm1(p.xy); - int q = quarter(p.xy); - - int d0h = 0; - vec2 p_proj = vec2(0.0); - if (p.z > TRANSITION_Z) { - float sqrt_3_one_min_z = sqrt(3.0 * one_minus_z_pos(p)); - p_proj = vec2(x_pm1 * sqrt_3_one_min_z, 2.0 - sqrt_3_one_min_z); - d0h = q; - } else if (p.z < -TRANSITION_Z) { - float sqrt_3_one_min_z = sqrt(3.0 * one_minus_z_neg(p)); - p_proj = vec2(x_pm1 * sqrt_3_one_min_z, sqrt_3_one_min_z); - d0h = q + 8; - } else { - float y_pm1 = p.z * TRANSITION_Z_INV; - int q01 = int(x_pm1 > y_pm1); // 0/1 - int q12 = int(x_pm1 >= -y_pm1); // 0\1 - int q03 = 1 - q12; // 1\0 - int q1 = q01 & q12; // = 1 if q1, 0 else - p_proj = vec2( - x_pm1 - float(q01 + q12 - 1), - y_pm1 + float(q01 + q03) - ); - d0h = ((q01 + q03) << 2) + ((q + q1) & 3); - } - - float x = (half_nside * (p_proj.x + p_proj.y)); - float y = (half_nside * (p_proj.y - p_proj.x)); - int i = int(x); - int j = int(y); - - return HashDxDy( - (d0h << (depth << 1)) + ij2z(i, j), - x - float(i), - y - float(j) - ); -} -vec3 xyz2uv(vec3 xyz) { - HashDxDy result = hash_with_dxdy(0, xyz.zxy); - - int idx = result.idx; - vec2 offset = vec2(result.dy, result.dx); - Tile tile = textures_tiles[idx]; - - return vec3(offset, float(tile.texture_idx)); -} - -uniform float opacity; -uniform vec4 no_tile_color; - -void main() { - vec3 uv = xyz2uv(normalize(frag_pos)); - vec4 c = uvw2c_rgba(uv); - - out_frag_color = c; - out_frag_color = vec4(c.rgb, opacity * c.a); -}"#, - ); - out.insert( - r"moc_base.vert", - r#"#version 300 es -precision highp float; -layout (location = 0) in vec2 lonlat; - -uniform mat3 u_2world; -uniform vec2 ndc_to_clip; -uniform float czf; - -const float PI = 3.141592653589793; -const float SQRT_2 = 1.41421356237309504880168872420969808; - -vec2 w2c_sin(vec3 p) { - vec2 q = vec2(-p.x, p.y); - return p.z >= 0.f ? q : normalize(q); -} - -vec2 w2c_sin_no_backface(vec3 p) { - return vec2(-p.x, p.y); -} - -vec2 w2c_ait(vec3 p) { - float r = length(p.zx); - float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) - w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma - float y2d = p.y / w; - - float x2d = 0.0; - if (abs(p.x) < 5e-3) { - float x_over_r = p.x/r; - x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; - } else { - w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) - x2d = sign(-p.x) * w; - } - - return vec2(x2d * 0.5, y2d) / SQRT_2; -} -const float eps = 1.25e-8; -const int n_iter = 100; - -float newton_solve(float z) { - float cte = PI * z; - float x = 2.0 * asin(z); - float f = x + sin(x) - cte; - int i = 0; - while (abs(f) > eps && i < n_iter) { - x -= f / (1.0 + cos(x)); - f = x + sin(x) - cte; - i += 1; - } - - return 0.5 * x; -} - -vec2 w2c_mol(vec3 p) { - float g = newton_solve(p.y); - - float sg = sin(g); - float cg = cos(g); - return vec2((atan(-p.x, p.z) * cg) / PI, sg); -} -vec2 w2c_tan(vec3 p) { - p.z = max(p.z, 1e-2); - return vec2(-p.x, p.y) / (p.z*PI); -} -vec2 w2c_stg(vec3 p) { - float w = (1.0 + p.z) * 0.5; - return vec2(-p.x, p.y) / (PI * w); -} -vec2 w2c_zea(vec3 p) { - float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] - return vec2(-p.x, p.y) * 0.5 / w; -} -vec2 w2c_mer(vec3 p) { - return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; -} - -vec3 lonlat2xyz(vec2 lonlat) { - float t = lonlat.x; - float tc = cos(t); - float ts = sin(t); - - float d = lonlat.y; - float dc = cos(d); - float ds = sin(d); - - return vec3(dc * ts, ds, dc * tc); -} - -uniform int u_proj; - -vec2 proj(vec3 p) { - if (u_proj == 0) { - return w2c_tan(p); - } else if (u_proj == 1) { - return w2c_stg(p); - } else if (u_proj == 2) { - return w2c_sin(p); - } else if (u_proj == 3) { - return w2c_zea(p); - } else if (u_proj == 4) { - return w2c_ait(p); - } else if (u_proj == 5) { - return w2c_mol(p); - } else { - return w2c_mer(p); - } -} - -void main() { - vec3 p_xyz = lonlat2xyz(lonlat); - vec3 p_w = u_2world * p_xyz; - vec2 p_clip = proj(p_w); - - vec2 p_ndc = p_clip / (ndc_to_clip * czf); - gl_Position = vec4(p_ndc, 0.f, 1.f); -}"#, - ); - out.insert( - r"moc_base.frag", - r#"#version 300 es - -precision lowp float; -out vec4 color; - -uniform vec4 u_color; - -void main() { - color = u_color; -}"#, - ); - out.insert( - r"catalogs_tan.vert", - r#"#version 300 es -precision lowp float; - -layout (location = 0) in vec2 offset; -layout (location = 1) in vec2 uv; -layout (location = 2) in vec3 center; - -uniform float current_time; -uniform mat3 inv_model; - -uniform vec2 ndc_to_clip; -uniform float czf; -uniform vec2 kernel_size; - -out vec2 out_uv; -out vec3 out_p; - -const float PI = 3.141592653589793; -const float SQRT_2 = 1.41421356237309504880168872420969808; - -vec2 w2c_sin(vec3 p) { - vec2 q = vec2(-p.x, p.y); - return p.z >= 0.f ? q : normalize(q); -} - -vec2 w2c_sin_no_backface(vec3 p) { - return vec2(-p.x, p.y); -} - -vec2 w2c_ait(vec3 p) { - float r = length(p.zx); - float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) - w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma - float y2d = p.y / w; - - float x2d = 0.0; - if (abs(p.x) < 5e-3) { - float x_over_r = p.x/r; - x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; - } else { - w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) - x2d = sign(-p.x) * w; - } - - return vec2(x2d * 0.5, y2d) / SQRT_2; -} -const float eps = 1.25e-8; -const int n_iter = 100; - -float newton_solve(float z) { - float cte = PI * z; - float x = 2.0 * asin(z); - float f = x + sin(x) - cte; - int i = 0; - while (abs(f) > eps && i < n_iter) { - x -= f / (1.0 + cos(x)); - f = x + sin(x) - cte; - i += 1; - } - - return 0.5 * x; -} - -vec2 w2c_mol(vec3 p) { - float g = newton_solve(p.y); - - float sg = sin(g); - float cg = cos(g); - return vec2((atan(-p.x, p.z) * cg) / PI, sg); -} -vec2 w2c_tan(vec3 p) { - p.z = max(p.z, 1e-2); - return vec2(-p.x, p.y) / (p.z*PI); -} -vec2 w2c_stg(vec3 p) { - float w = (1.0 + p.z) * 0.5; - return vec2(-p.x, p.y) / (PI * w); -} -vec2 w2c_zea(vec3 p) { - float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] - return vec2(-p.x, p.y) * 0.5 / w; -} -vec2 w2c_mer(vec3 p) { - return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; -} - -vec3 lonlat2xyz(vec2 lonlat) { - float t = lonlat.x; - float tc = cos(t); - float ts = sin(t); - - float d = lonlat.y; - float dc = cos(d); - float ds = sin(d); - - return vec3(dc * ts, ds, dc * tc); -} - -uniform int u_proj; - -vec2 proj(vec3 p) { - if (u_proj == 0) { - return w2c_tan(p); - } else if (u_proj == 1) { - return w2c_stg(p); - } else if (u_proj == 2) { - return w2c_sin(p); - } else if (u_proj == 3) { - return w2c_zea(p); - } else if (u_proj == 4) { - return w2c_ait(p); - } else if (u_proj == 5) { - return w2c_mol(p); - } else { - return w2c_mer(p); - } -} - - -void main() { - vec3 p = inv_model * center; - - vec2 center_pos_clip_space = world2clip_gnomonic(p); - - vec2 pos_clip_space = center_pos_clip_space; - gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); - - out_uv = uv; - out_p = p; -}"#, - ); - out.insert( - r"line_base.vert", - r#"#version 300 es -precision highp float; -layout (location = 0) in vec2 ndc_pos; - -out float l; - -void main() { - gl_Position = vec4( - ndc_pos, - 0.0, - 1.0 - ); -}"#, - ); - out.insert( - r"catalogs_catalog.frag", - r#"#version 300 es -precision lowp float; - -in vec2 out_uv; -in vec3 out_p; - -out vec4 color; - -uniform sampler2D kernel_texture; -uniform float max_density; // max number of sources in a kernel sized HEALPix cell at the current depth -uniform float fov; -uniform float strength; -void main() { - color = texture(kernel_texture, out_uv) / max(log2(fov*100.0), 1.0); - color.r *= strength; -}"#, - ); - out.insert( - r"hips3d_u8.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler3D; - -uniform sampler3D tex; - -in vec3 frag_uv; - -out vec4 out_frag_color; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -vec3 rgb2hsv(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - -vec4 uvw2c_r(vec3 uv) { - vec2 va = texture(tex, uv).ra; - - va.x = transfer_func(H, va.x, min_value, max_value); - - va.x = mix(va.x, 1.0 - va.x, reversed); - - vec4 c = colormap_f(va.x); - return apply_tonal(c); -} - -vec4 uvw2c_rgba(vec3 uv) { - vec4 c = texture(tex, uv).rgba; - - c.r = transfer_func(H, c.r, min_value, max_value); - c.g = transfer_func(H, c.g, min_value, max_value); - c.b = transfer_func(H, c.b, min_value, max_value); - - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 uvw2c_ra(vec3 uv) { - vec2 c = texture(tex, uv).rg; - - c.r = transfer_func(H, c.r, min_value, max_value); - - c.r = mix(c.r, 1.0 - c.r, reversed); - - vec3 color = colormap_f(c.r).rgb; - - return apply_tonal(vec4(color, c.g)); -} - -vec4 uvw2cmap_rgba(vec3 uv) { - float v = texture(tex, uv).r; - v = transfer_func(H, v, min_value, max_value); - vec4 c = colormap_f(v); - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uvw2c_f32(vec3 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uvw2c_i32(vec3 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_u8(vec3 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} - -uniform float opacity; - -void main() { - vec3 uv = vec3(frag_uv.xyz); - uv.y = 1.0 - uv.y; - - vec4 color = uvw2c_u8(uv); - - out_frag_color = color; - out_frag_color.a = out_frag_color.a * opacity; -}"#, - ); - out.insert( - r"catalogs_arc.vert", - r#"#version 300 es -precision lowp float; -layout (location = 0) in vec2 offset; -layout (location = 1) in vec2 uv; -layout (location = 2) in vec3 center; - -uniform float current_time; -uniform mat3 inv_model; - -uniform vec2 ndc_to_clip; -uniform float czf; -uniform vec2 kernel_size; - -out vec2 out_uv; -out vec3 out_p; - -const float PI = 3.141592653589793; -const float SQRT_2 = 1.41421356237309504880168872420969808; - -vec2 w2c_sin(vec3 p) { - vec2 q = vec2(-p.x, p.y); - return p.z >= 0.f ? q : normalize(q); -} - -vec2 w2c_sin_no_backface(vec3 p) { - return vec2(-p.x, p.y); -} - -vec2 w2c_ait(vec3 p) { - float r = length(p.zx); - float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) - w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma - float y2d = p.y / w; - - float x2d = 0.0; - if (abs(p.x) < 5e-3) { - float x_over_r = p.x/r; - x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; - } else { - w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) - x2d = sign(-p.x) * w; - } - - return vec2(x2d * 0.5, y2d) / SQRT_2; -} -const float eps = 1.25e-8; -const int n_iter = 100; - -float newton_solve(float z) { - float cte = PI * z; - float x = 2.0 * asin(z); - float f = x + sin(x) - cte; - int i = 0; - while (abs(f) > eps && i < n_iter) { - x -= f / (1.0 + cos(x)); - f = x + sin(x) - cte; - i += 1; - } - - return 0.5 * x; -} - -vec2 w2c_mol(vec3 p) { - float g = newton_solve(p.y); - - float sg = sin(g); - float cg = cos(g); - return vec2((atan(-p.x, p.z) * cg) / PI, sg); -} -vec2 w2c_tan(vec3 p) { - p.z = max(p.z, 1e-2); - return vec2(-p.x, p.y) / (p.z*PI); -} -vec2 w2c_stg(vec3 p) { - float w = (1.0 + p.z) * 0.5; - return vec2(-p.x, p.y) / (PI * w); -} -vec2 w2c_zea(vec3 p) { - float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] - return vec2(-p.x, p.y) * 0.5 / w; -} -vec2 w2c_mer(vec3 p) { - return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; -} - -vec3 lonlat2xyz(vec2 lonlat) { - float t = lonlat.x; - float tc = cos(t); - float ts = sin(t); - - float d = lonlat.y; - float dc = cos(d); - float ds = sin(d); - - return vec3(dc * ts, ds, dc * tc); -} - -uniform int u_proj; - -vec2 proj(vec3 p) { - if (u_proj == 0) { - return w2c_tan(p); - } else if (u_proj == 1) { - return w2c_stg(p); - } else if (u_proj == 2) { - return w2c_sin(p); - } else if (u_proj == 3) { - return w2c_zea(p); - } else if (u_proj == 4) { - return w2c_ait(p); - } else if (u_proj == 5) { - return w2c_mol(p); - } else { - return w2c_mer(p); - } -} - -void main() { - vec3 p = inv_model * center; - - vec2 center_pos_clip_space = world2clip_arc(p); - - vec2 pos_clip_space = center_pos_clip_space; - gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); - - out_uv = uv; - out_p = p; -}"#, - ); - out.insert( - r"hips_rasterizer_rgba2cmap.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler2DArray; - -uniform sampler2DArray tex; - -in vec3 frag_uv_start; -in vec3 frag_uv_end; -in float frag_blending_factor; - -out vec4 out_frag_color; -uniform float opacity; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -vec3 rgb2hsv(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - -vec4 uvw2c_r(vec3 uv) { - vec2 va = texture(tex, uv).ra; - - va.x = transfer_func(H, va.x, min_value, max_value); - - va.x = mix(va.x, 1.0 - va.x, reversed); - - vec4 c = colormap_f(va.x); - return apply_tonal(c); -} - -vec4 uvw2c_rgba(vec3 uv) { - vec4 c = texture(tex, uv).rgba; - - c.r = transfer_func(H, c.r, min_value, max_value); - c.g = transfer_func(H, c.g, min_value, max_value); - c.b = transfer_func(H, c.b, min_value, max_value); - - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 uvw2c_ra(vec3 uv) { - vec2 c = texture(tex, uv).rg; - - c.r = transfer_func(H, c.r, min_value, max_value); - - c.r = mix(c.r, 1.0 - c.r, reversed); - - vec3 color = colormap_f(c.r).rgb; - - return apply_tonal(vec4(color, c.g)); -} - -vec4 uvw2cmap_rgba(vec3 uv) { - float v = texture(tex, uv).r; - v = transfer_func(H, v, min_value, max_value); - vec4 c = colormap_f(v); - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uvw2c_f32(vec3 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uvw2c_i32(vec3 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_u8(vec3 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} - -void main() { - vec4 color_start = uvw2cmap_rgba(frag_uv_start); - vec4 color_end = uvw2cmap_rgba(frag_uv_end); - - out_frag_color = mix(color_start, color_end, frag_blending_factor); - out_frag_color.a = opacity * out_frag_color.a; -}"#, - ); - out.insert( - r"line_inst_lonlat.vert", - r#"#version 300 es -precision highp float; -layout (location = 0) in vec2 p_a_lonlat; -layout (location = 1) in vec2 p_b_lonlat; -layout (location = 2) in vec2 vertex; - -uniform mat3 u_2world; -uniform vec2 ndc_to_clip; -uniform float czf; -uniform float u_width; -uniform float u_height; -uniform float u_thickness; - -out vec2 l; - -const float PI = 3.141592653589793; -const float SQRT_2 = 1.41421356237309504880168872420969808; - -vec2 w2c_sin(vec3 p) { - vec2 q = vec2(-p.x, p.y); - return p.z >= 0.f ? q : normalize(q); -} - -vec2 w2c_sin_no_backface(vec3 p) { - return vec2(-p.x, p.y); -} - -vec2 w2c_ait(vec3 p) { - float r = length(p.zx); - float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) - w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma - float y2d = p.y / w; - - float x2d = 0.0; - if (abs(p.x) < 5e-3) { - float x_over_r = p.x/r; - x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; - } else { - w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) - x2d = sign(-p.x) * w; - } - - return vec2(x2d * 0.5, y2d) / SQRT_2; -} -const float eps = 1.25e-8; -const int n_iter = 100; - -float newton_solve(float z) { - float cte = PI * z; - float x = 2.0 * asin(z); - float f = x + sin(x) - cte; - int i = 0; - while (abs(f) > eps && i < n_iter) { - x -= f / (1.0 + cos(x)); - f = x + sin(x) - cte; - i += 1; - } - - return 0.5 * x; -} - -vec2 w2c_mol(vec3 p) { - float g = newton_solve(p.y); - - float sg = sin(g); - float cg = cos(g); - return vec2((atan(-p.x, p.z) * cg) / PI, sg); -} -vec2 w2c_tan(vec3 p) { - p.z = max(p.z, 1e-2); - return vec2(-p.x, p.y) / (p.z*PI); -} -vec2 w2c_stg(vec3 p) { - float w = (1.0 + p.z) * 0.5; - return vec2(-p.x, p.y) / (PI * w); -} -vec2 w2c_zea(vec3 p) { - float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] - return vec2(-p.x, p.y) * 0.5 / w; -} -vec2 w2c_mer(vec3 p) { - return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; -} - -vec3 lonlat2xyz(vec2 lonlat) { - float t = lonlat.x; - float tc = cos(t); - float ts = sin(t); - - float d = lonlat.y; - float dc = cos(d); - float ds = sin(d); - - return vec3(dc * ts, ds, dc * tc); -} - -uniform int u_proj; - -vec2 proj(vec3 p) { - if (u_proj == 0) { - return w2c_tan(p); - } else if (u_proj == 1) { - return w2c_stg(p); - } else if (u_proj == 2) { - return w2c_sin(p); - } else if (u_proj == 3) { - return w2c_zea(p); - } else if (u_proj == 4) { - return w2c_ait(p); - } else if (u_proj == 5) { - return w2c_mol(p); - } else { - return w2c_mer(p); - } -} - -void main() { - vec3 p_a_xyz = lonlat2xyz(p_a_lonlat); - vec3 p_b_xyz = lonlat2xyz(p_b_lonlat); - vec3 p_a_w = u_2world * p_a_xyz; - vec3 p_b_w = u_2world * p_b_xyz; - vec2 p_a_clip = proj(p_a_w); - vec2 p_b_clip = proj(p_b_w); - - vec2 da = p_a_clip - p_b_clip; - - vec2 p_a_ndc = p_a_clip / (ndc_to_clip * czf); - vec2 p_b_ndc = p_b_clip / (ndc_to_clip * czf); - - vec2 x_b = p_b_ndc - p_a_ndc; - vec2 y_b = normalize(vec2(-x_b.y, x_b.x)); - - float ndc2pix = 2.0 / u_width; - - vec2 p_ndc_x = x_b * vertex.x; - vec2 p_ndc_y = (u_thickness + 2.0) * y_b * vertex.y * vec2(1.0, u_width/u_height) * ndc2pix; - - vec2 p_ndc = p_a_ndc + p_ndc_x + p_ndc_y; - gl_Position = vec4(p_ndc, 0.f, 1.f); - - l = vec2(dot(da, da), vertex.y); -}"#, - ); - out.insert( - r"hips_rasterizer_i16.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler2DArray; - -uniform sampler2DArray tex; - -in vec3 frag_uv_start; -in vec3 frag_uv_end; -in float frag_blending_factor; - -out vec4 out_frag_color; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -vec3 rgb2hsv(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - -vec4 uvw2c_r(vec3 uv) { - vec2 va = texture(tex, uv).ra; - - va.x = transfer_func(H, va.x, min_value, max_value); - - va.x = mix(va.x, 1.0 - va.x, reversed); - - vec4 c = colormap_f(va.x); - return apply_tonal(c); -} - -vec4 uvw2c_rgba(vec3 uv) { - vec4 c = texture(tex, uv).rgba; - - c.r = transfer_func(H, c.r, min_value, max_value); - c.g = transfer_func(H, c.g, min_value, max_value); - c.b = transfer_func(H, c.b, min_value, max_value); - - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 uvw2c_ra(vec3 uv) { - vec2 c = texture(tex, uv).rg; - - c.r = transfer_func(H, c.r, min_value, max_value); - - c.r = mix(c.r, 1.0 - c.r, reversed); - - vec3 color = colormap_f(c.r).rgb; - - return apply_tonal(vec4(color, c.g)); -} - -vec4 uvw2cmap_rgba(vec3 uv) { - float v = texture(tex, uv).r; - v = transfer_func(H, v, min_value, max_value); - vec4 c = colormap_f(v); - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uvw2c_f32(vec3 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uvw2c_i32(vec3 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_u8(vec3 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} - -uniform float opacity; - -void main() { - vec3 uv0 = frag_uv_start; - vec3 uv1 = frag_uv_end; - uv0.y = 1.0 - uv0.y; - uv1.y = 1.0 - uv1.y; - - vec4 color_start = uvw2c_i16(uv0); - vec4 color_end = uvw2c_i16(uv1); - - out_frag_color = mix(color_start, color_end, frag_blending_factor); - out_frag_color.a = out_frag_color.a * opacity; -}"#, - ); - out.insert( - r"passes_post_vertex_100es.vert", - r#"#version 300 es -precision mediump float; - -layout (location = 0) in vec2 a_pos; -out vec2 v_tc; - -void main() { - gl_Position = vec4(a_pos * 2. - 1., 0.0, 1.0); - v_tc = a_pos; -}"#, - ); - out.insert( - r"line_inst_ndc.vert", - r#"#version 300 es -precision highp float; -layout (location = 0) in vec2 p_a; -layout (location = 1) in vec2 p_b; -layout (location = 2) in vec2 vertex; - -out vec2 l; - -uniform float u_width; -uniform float u_height; -uniform float u_thickness; - -void main() { - vec2 x_b = p_b - p_a; - vec2 y_b = normalize(vec2(-x_b.y, x_b.x)); - - float ndc2pix = 2.0 / u_width; - - vec2 p = p_a + x_b * vertex.x + (u_thickness + 2.0) * y_b * vertex.y * vec2(1.0, u_width/u_height) * ndc2pix; - gl_Position = vec4(p, 0.f, 1.f); - l = vec2(0.0, vertex.y); -}"#, - ); - out.insert( - r"line_base.frag", - r#"#version 300 es - -precision highp float; - -out vec4 color; -in vec2 l; - -uniform vec4 u_color; -uniform float u_thickness; -uniform float u_width; -uniform float u_height; - -void main() { - if (l.x > 0.05) { - discard; - } else { - color = u_color; - - float dist = abs((u_thickness + 2.0) * l.y); - - float half_thickness = (u_thickness + 2.0) * 0.5; - color.a = color.a * (1.0 - smoothstep(half_thickness - 1.0, half_thickness, dist)); - } -}"#, - ); - out.insert( - r"hips_rasterizer_i32.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler2DArray; - -uniform sampler2DArray tex; - -in vec3 frag_uv_start; -in vec3 frag_uv_end; -in float frag_blending_factor; - -out vec4 out_frag_color; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -vec3 rgb2hsv(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - -vec4 uvw2c_r(vec3 uv) { - vec2 va = texture(tex, uv).ra; - - va.x = transfer_func(H, va.x, min_value, max_value); - - va.x = mix(va.x, 1.0 - va.x, reversed); - - vec4 c = colormap_f(va.x); - return apply_tonal(c); -} - -vec4 uvw2c_rgba(vec3 uv) { - vec4 c = texture(tex, uv).rgba; - - c.r = transfer_func(H, c.r, min_value, max_value); - c.g = transfer_func(H, c.g, min_value, max_value); - c.b = transfer_func(H, c.b, min_value, max_value); - - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 uvw2c_ra(vec3 uv) { - vec2 c = texture(tex, uv).rg; - - c.r = transfer_func(H, c.r, min_value, max_value); - - c.r = mix(c.r, 1.0 - c.r, reversed); - - vec3 color = colormap_f(c.r).rgb; - - return apply_tonal(vec4(color, c.g)); -} - -vec4 uvw2cmap_rgba(vec3 uv) { - float v = texture(tex, uv).r; - v = transfer_func(H, v, min_value, max_value); - vec4 c = colormap_f(v); - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uvw2c_f32(vec3 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uvw2c_i32(vec3 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_u8(vec3 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} - -uniform float opacity; - -void main() { - vec3 uv0 = frag_uv_start; - vec3 uv1 = frag_uv_end; - uv0.y = 1.0 - uv0.y; - uv1.y = 1.0 - uv1.y; - - vec4 color_start = uvw2c_i32(uv0); - vec4 color_end = uvw2c_i32(uv1); - - out_frag_color = mix(color_start, color_end, frag_blending_factor); - out_frag_color.a = out_frag_color.a * opacity; -}"#, - ); - out.insert( - r"fits_i32.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler2D; -precision mediump int; - -out vec4 out_frag_color; -in vec2 frag_uv; - -uniform sampler2D tex; -uniform float opacity; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uv2c_f32(vec2 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uv2c_i32(vec2 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return val2c(val); -} - -vec4 uv2c_i16(vec2 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return val2c(val); -} - -vec4 uv2c_u8(vec2 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} - -void main() { - vec2 uv = frag_uv; - uv.y = 1.0 - uv.y; - - out_frag_color = uv2c_i32(frag_uv); - out_frag_color.a = out_frag_color.a * opacity; -}"#, - ); - out.insert( - r"hips_raytracer_backcolor.vert", - r#"#version 300 es -precision lowp float; -precision mediump int; - -layout (location = 0) in vec2 pos_clip_space; - -uniform vec2 ndc_to_clip; -uniform float czf; - -void main() { - gl_Position = vec4(pos_clip_space / (ndc_to_clip * czf), 0.0, 1.0); -}"#, - ); - out.insert( - r"hips_rasterizer_raster.vert", - r#"#version 300 es -precision highp float; - -layout (location = 0) in vec3 xyz; -layout (location = 1) in vec3 uv_start; -layout (location = 2) in vec3 uv_end; -layout (location = 3) in float time_tile_received; - -out vec3 frag_uv_start; -out vec3 frag_uv_end; -out float frag_blending_factor; - -uniform mat3 inv_model; -uniform vec2 ndc_to_clip; -uniform float czf; -uniform float current_time; - -const float PI = 3.141592653589793; -const float SQRT_2 = 1.41421356237309504880168872420969808; - -vec2 w2c_sin(vec3 p) { - vec2 q = vec2(-p.x, p.y); - return p.z >= 0.f ? q : normalize(q); -} - -vec2 w2c_sin_no_backface(vec3 p) { - return vec2(-p.x, p.y); -} - -vec2 w2c_ait(vec3 p) { - float r = length(p.zx); - float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) - w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma - float y2d = p.y / w; - - float x2d = 0.0; - if (abs(p.x) < 5e-3) { - float x_over_r = p.x/r; - x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; - } else { - w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) - x2d = sign(-p.x) * w; - } - - return vec2(x2d * 0.5, y2d) / SQRT_2; -} -const float eps = 1.25e-8; -const int n_iter = 100; - -float newton_solve(float z) { - float cte = PI * z; - float x = 2.0 * asin(z); - float f = x + sin(x) - cte; - int i = 0; - while (abs(f) > eps && i < n_iter) { - x -= f / (1.0 + cos(x)); - f = x + sin(x) - cte; - i += 1; - } - - return 0.5 * x; -} - -vec2 w2c_mol(vec3 p) { - float g = newton_solve(p.y); - - float sg = sin(g); - float cg = cos(g); - return vec2((atan(-p.x, p.z) * cg) / PI, sg); -} -vec2 w2c_tan(vec3 p) { - p.z = max(p.z, 1e-2); - return vec2(-p.x, p.y) / (p.z*PI); -} -vec2 w2c_stg(vec3 p) { - float w = (1.0 + p.z) * 0.5; - return vec2(-p.x, p.y) / (PI * w); -} -vec2 w2c_zea(vec3 p) { - float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] - return vec2(-p.x, p.y) * 0.5 / w; -} -vec2 w2c_mer(vec3 p) { - return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; -} - -vec3 lonlat2xyz(vec2 lonlat) { - float t = lonlat.x; - float tc = cos(t); - float ts = sin(t); - - float d = lonlat.y; - float dc = cos(d); - float ds = sin(d); - - return vec3(dc * ts, ds, dc * tc); -} - -uniform int u_proj; - -vec2 proj(vec3 p) { - if (u_proj == 0) { - return w2c_tan(p); - } else if (u_proj == 1) { - return w2c_stg(p); - } else if (u_proj == 2) { - return w2c_sin(p); - } else if (u_proj == 3) { - return w2c_zea(p); - } else if (u_proj == 4) { - return w2c_ait(p); - } else if (u_proj == 5) { - return w2c_mol(p); - } else { - return w2c_mer(p); - } -} - -void main() { - vec3 p_w = inv_model * xyz; - vec2 p_clip = proj(p_w); - - vec2 p_ndc = p_clip / (ndc_to_clip * czf); - gl_Position = vec4(p_ndc, 0.0, 1.0); - - frag_uv_start = uv_start; - frag_uv_end = uv_end; - frag_blending_factor = min((current_time - time_tile_received) / 200.0, 1.0); -}"#, - ); - out.insert( - r"hips_raytracer_rgba2cmap.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler2DArray; -precision lowp sampler2DArray; -precision lowp isampler2DArray; -precision mediump int; - -in vec3 frag_pos; -in vec2 out_clip_pos; -out vec4 out_frag_color; - -struct Tile { - int uniq; // Healpix cell - int texture_idx; // Index in the texture buffer - float start_time; // Absolute time that the load has been done in ms - float empty; -}; - -uniform Tile textures_tiles[12]; - -uniform float opacity; -uniform sampler2DArray tex; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -vec3 rgb2hsv(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - -vec4 uvw2c_r(vec3 uv) { - vec2 va = texture(tex, uv).ra; - - va.x = transfer_func(H, va.x, min_value, max_value); - - va.x = mix(va.x, 1.0 - va.x, reversed); - - vec4 c = colormap_f(va.x); - return apply_tonal(c); -} - -vec4 uvw2c_rgba(vec3 uv) { - vec4 c = texture(tex, uv).rgba; - - c.r = transfer_func(H, c.r, min_value, max_value); - c.g = transfer_func(H, c.g, min_value, max_value); - c.b = transfer_func(H, c.b, min_value, max_value); - - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 uvw2c_ra(vec3 uv) { - vec2 c = texture(tex, uv).rg; - - c.r = transfer_func(H, c.r, min_value, max_value); - - c.r = mix(c.r, 1.0 - c.r, reversed); - - vec3 color = colormap_f(c.r).rgb; - - return apply_tonal(vec4(color, c.g)); -} - -vec4 uvw2cmap_rgba(vec3 uv) { - float v = texture(tex, uv).r; - v = transfer_func(H, v, min_value, max_value); - vec4 c = colormap_f(v); - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uvw2c_f32(vec3 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uvw2c_i32(vec3 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_u8(vec3 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} -const float TWICE_PI = 6.28318530718; -const float PI = 3.141592653589793; -const float FOUR_OVER_PI = 1.27323954474; -const float TRANSITION_Z = 0.66666666666; -const float TRANSITION_Z_INV = 1.5; - -int quarter(vec2 p) { - int x_neg = int(p.x < 0.0); - int y_neg = int(p.y < 0.0); - int q = (x_neg + y_neg) | (y_neg << 1); - return q; -} - -float xpm1(vec2 p) { - bool x_neg = (p.x < 0.0); - bool y_neg = (p.y < 0.0); - float lon = atan(abs(p.y), abs(p.x)); - float x02 = lon * FOUR_OVER_PI; - if (x_neg != y_neg) { // Could be replaced by a sign copy from (x_neg ^ y_neg) << 32 - return 1.0 - x02; - } else { - return x02 - 1.0; - } -} - -float one_minus_z_pos(vec3 p) { - float d2 = dot(p.xy, p.xy); // z = sqrt(1 - d2) AND sqrt(1 - x) = 1 - x / 2 - x^2 / 8 - x^3 / 16 - 5 x^4/128 - 7 * x^5/256 - - if (d2 < 1e-1) { // <=> dec > 84.27 deg - return d2 * (0.5 + d2 * (0.125 + d2 * (0.0625 + d2 * (0.0390625 + d2 * 0.02734375)))); - } - return 1.0f - p.z; -} - -float one_minus_z_neg(vec3 p) { - float d2 = dot(p.xy, p.xy); // z = sqrt(1 - d2) AND sqrt(1 - x) = 1 - x / 2 - x^2 / 8 - x^3 / 16 - 5 x^4/128 - 7 * x^5/256 - if (d2 < 1e-1f) { // <=> dec < -84.27 deg - return d2 * (0.5 + d2 * (0.125 + d2 * (0.0625 + d2 * (0.0390625 + d2 * 0.02734375)))); - } - return p.z + 1.0; -} - -int ij2z(int i, int j) { - int i4 = i | (j << 2); - - int j4 = (i4 ^ (i4 >> 1)) & 0x22222222; - int i5 = i4 ^ j4 ^ (j4 << 1); - - return i5; -} - -struct HashDxDy { - int idx; - float dx; - float dy; -}; - -uniform sampler2D ang2pixd; -HashDxDy hash_with_dxdy2(vec2 radec) { - vec2 aa = vec2(radec.x/TWICE_PI + 1.0, (radec.y/PI) + 0.5); - vec3 v = texture(ang2pixd, aa).rgb; - return HashDxDy( - int(v.x * 255.0), - v.y, - v.z - ); -} -HashDxDy hash_with_dxdy(int depth, vec3 p) { - - int nside = 1 << depth; - float half_nside = float(nside) * 0.5; - - float x_pm1 = xpm1(p.xy); - int q = quarter(p.xy); - - int d0h = 0; - vec2 p_proj = vec2(0.0); - if (p.z > TRANSITION_Z) { - float sqrt_3_one_min_z = sqrt(3.0 * one_minus_z_pos(p)); - p_proj = vec2(x_pm1 * sqrt_3_one_min_z, 2.0 - sqrt_3_one_min_z); - d0h = q; - } else if (p.z < -TRANSITION_Z) { - float sqrt_3_one_min_z = sqrt(3.0 * one_minus_z_neg(p)); - p_proj = vec2(x_pm1 * sqrt_3_one_min_z, sqrt_3_one_min_z); - d0h = q + 8; - } else { - float y_pm1 = p.z * TRANSITION_Z_INV; - int q01 = int(x_pm1 > y_pm1); // 0/1 - int q12 = int(x_pm1 >= -y_pm1); // 0\1 - int q03 = 1 - q12; // 1\0 - int q1 = q01 & q12; // = 1 if q1, 0 else - p_proj = vec2( - x_pm1 - float(q01 + q12 - 1), - y_pm1 + float(q01 + q03) - ); - d0h = ((q01 + q03) << 2) + ((q + q1) & 3); - } - - float x = (half_nside * (p_proj.x + p_proj.y)); - float y = (half_nside * (p_proj.y - p_proj.x)); - int i = int(x); - int j = int(y); - - return HashDxDy( - (d0h << (depth << 1)) + ij2z(i, j), - x - float(i), - y - float(j) - ); -} -vec3 xyz2uv(vec3 xyz) { - HashDxDy result = hash_with_dxdy(0, xyz.zxy); - - int idx = result.idx; - vec2 offset = vec2(result.dy, result.dx); - Tile tile = textures_tiles[idx]; - - return vec3(offset, float(tile.texture_idx)); -} - -void main() { - vec3 uv = xyz2uv(normalize(frag_pos)); - vec4 c = uvw2cmap_rgba(uv); - - out_frag_color = c; - out_frag_color.a = out_frag_color.a * opacity; -}"#, - ); - out.insert( - r"hips_raytracer_backcolor.frag", - r#"#version 300 es -precision lowp float; -precision mediump int; - -out vec4 out_frag_color; - -uniform vec3 color; - -void main() { - out_frag_color = vec4(color, 1.0); }"#, ); out.insert( @@ -4803,22 +3336,14 @@ void main() { }"#, ); out.insert( - r"catalogs_ortho.vert", + r"moc_base.vert", r#"#version 300 es -precision lowp float; -layout (location = 0) in vec2 offset; -layout (location = 1) in vec2 uv; -layout (location = 2) in vec3 center; - -uniform float current_time; -uniform mat3 inv_model; +precision highp float; +layout (location = 0) in vec2 lonlat; +uniform mat3 u_2world; uniform vec2 ndc_to_clip; uniform float czf; -uniform vec2 kernel_size; - -out vec2 out_uv; -out vec3 out_p; const float PI = 3.141592653589793; const float SQRT_2 = 1.41421356237309504880168872420969808; @@ -4921,30 +3446,37 @@ vec2 proj(vec3 p) { } } - void main() { - vec3 p = inv_model * center; + vec3 p_xyz = lonlat2xyz(lonlat); + vec3 p_w = u_2world * p_xyz; + vec2 p_clip = proj(p_w); - vec2 center_pos_clip_space = world2clip_orthographic(p); - - vec2 pos_clip_space = center_pos_clip_space; - gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); - - out_uv = uv; - out_p = p; + vec2 p_ndc = p_clip / (ndc_to_clip * czf); + gl_Position = vec4(p_ndc, 0.f, 1.f); }"#, ); out.insert( - r"fits_i16.frag", + r"hips_raytracer_i16.frag", r#"#version 300 es precision lowp float; -precision lowp sampler2D; +precision lowp sampler2DArray; precision mediump int; -out vec4 out_frag_color; -in vec2 frag_uv; +uniform sampler2DArray tex; + +in vec3 frag_pos; +in vec2 out_clip_pos; +out vec4 out_frag_color; + +struct Tile { + int uniq; // Healpix cell + int texture_idx; // Index in the texture buffer + float start_time; // Absolute time that the load has been done in ms + float empty; +}; + +uniform Tile textures_tiles[12]; -uniform sampler2D tex; uniform float opacity; uniform float scale; @@ -5047,6 +3579,21 @@ vec4 apply_tonal(vec4 color) { k_gamma ); } +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} highp float decode_f32(highp vec4 rgba) { highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; @@ -5090,6 +3637,49 @@ uint decode_u8(float r) { +vec4 uvw2c_r(vec3 uv) { + vec2 va = texture(tex, uv).ra; + + va.x = transfer_func(H, va.x, min_value, max_value); + + va.x = mix(va.x, 1.0 - va.x, reversed); + + vec4 c = colormap_f(va.x); + return apply_tonal(c); +} + +vec4 uvw2c_rgba(vec3 uv) { + vec4 c = texture(tex, uv).rgba; + + c.r = transfer_func(H, c.r, min_value, max_value); + c.g = transfer_func(H, c.g, min_value, max_value); + c.b = transfer_func(H, c.b, min_value, max_value); + + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 uvw2c_ra(vec3 uv) { + vec2 c = texture(tex, uv).rg; + + c.r = transfer_func(H, c.r, min_value, max_value); + + c.r = mix(c.r, 1.0 - c.r, reversed); + + vec3 color = colormap_f(c.r).rgb; + + return apply_tonal(vec4(color, c.g)); +} + +vec4 uvw2cmap_rgba(vec3 uv) { + float v = texture(tex, uv).r; + v = transfer_func(H, v, min_value, max_value); + vec4 c = colormap_f(v); + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} vec4 val2c_f32(float x) { float alpha = x * scale + offset; @@ -5111,32 +3701,1864 @@ vec4 val2c(float x) { return apply_tonal(new_color); } -vec4 uv2c_f32(vec2 uv) { +vec4 uvw2c_f32(vec3 uv) { float val = decode_f32(texture(tex, uv).rgba*255.0); return val2c_f32(val); } -vec4 uv2c_i32(vec2 uv) { +vec4 uvw2c_i32(vec3 uv) { float val = float(decode_i32(texture(tex, uv).rgba)); - return val2c(val); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); } -vec4 uv2c_i16(vec2 uv) { +vec4 uvw2c_i16(vec3 uv) { float val = float(decode_i16(texture(tex, uv).rg)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_u8(vec3 uv) { + float val = float(decode_u8(texture(tex, uv).r)); + return val2c(val); +} +const float TWICE_PI = 6.28318530718; +const float PI = 3.141592653589793; +const float FOUR_OVER_PI = 1.27323954474; +const float TRANSITION_Z = 0.66666666666; +const float TRANSITION_Z_INV = 1.5; + +int quarter(vec2 p) { + int x_neg = int(p.x < 0.0); + int y_neg = int(p.y < 0.0); + int q = (x_neg + y_neg) | (y_neg << 1); + return q; +} + +float xpm1(vec2 p) { + bool x_neg = (p.x < 0.0); + bool y_neg = (p.y < 0.0); + float lon = atan(abs(p.y), abs(p.x)); + float x02 = lon * FOUR_OVER_PI; + if (x_neg != y_neg) { // Could be replaced by a sign copy from (x_neg ^ y_neg) << 32 + return 1.0 - x02; + } else { + return x02 - 1.0; + } +} + +float one_minus_z_pos(vec3 p) { + float d2 = dot(p.xy, p.xy); // z = sqrt(1 - d2) AND sqrt(1 - x) = 1 - x / 2 - x^2 / 8 - x^3 / 16 - 5 x^4/128 - 7 * x^5/256 + + if (d2 < 1e-1) { // <=> dec > 84.27 deg + return d2 * (0.5 + d2 * (0.125 + d2 * (0.0625 + d2 * (0.0390625 + d2 * 0.02734375)))); + } + return 1.0f - p.z; +} + +float one_minus_z_neg(vec3 p) { + float d2 = dot(p.xy, p.xy); // z = sqrt(1 - d2) AND sqrt(1 - x) = 1 - x / 2 - x^2 / 8 - x^3 / 16 - 5 x^4/128 - 7 * x^5/256 + if (d2 < 1e-1f) { // <=> dec < -84.27 deg + return d2 * (0.5 + d2 * (0.125 + d2 * (0.0625 + d2 * (0.0390625 + d2 * 0.02734375)))); + } + return p.z + 1.0; +} + +int ij2z(int i, int j) { + int i4 = i | (j << 2); + + int j4 = (i4 ^ (i4 >> 1)) & 0x22222222; + int i5 = i4 ^ j4 ^ (j4 << 1); + + return i5; +} + +struct HashDxDy { + int idx; + float dx; + float dy; +}; + +uniform sampler2D ang2pixd; +HashDxDy hash_with_dxdy2(vec2 radec) { + vec2 aa = vec2(radec.x/TWICE_PI + 1.0, (radec.y/PI) + 0.5); + vec3 v = texture(ang2pixd, aa).rgb; + return HashDxDy( + int(v.x * 255.0), + v.y, + v.z + ); +} +HashDxDy hash_with_dxdy(int depth, vec3 p) { + + int nside = 1 << depth; + float half_nside = float(nside) * 0.5; + + float x_pm1 = xpm1(p.xy); + int q = quarter(p.xy); + + int d0h = 0; + vec2 p_proj = vec2(0.0); + if (p.z > TRANSITION_Z) { + float sqrt_3_one_min_z = sqrt(3.0 * one_minus_z_pos(p)); + p_proj = vec2(x_pm1 * sqrt_3_one_min_z, 2.0 - sqrt_3_one_min_z); + d0h = q; + } else if (p.z < -TRANSITION_Z) { + float sqrt_3_one_min_z = sqrt(3.0 * one_minus_z_neg(p)); + p_proj = vec2(x_pm1 * sqrt_3_one_min_z, sqrt_3_one_min_z); + d0h = q + 8; + } else { + float y_pm1 = p.z * TRANSITION_Z_INV; + int q01 = int(x_pm1 > y_pm1); // 0/1 + int q12 = int(x_pm1 >= -y_pm1); // 0\1 + int q03 = 1 - q12; // 1\0 + int q1 = q01 & q12; // = 1 if q1, 0 else + p_proj = vec2( + x_pm1 - float(q01 + q12 - 1), + y_pm1 + float(q01 + q03) + ); + d0h = ((q01 + q03) << 2) + ((q + q1) & 3); + } + + float x = (half_nside * (p_proj.x + p_proj.y)); + float y = (half_nside * (p_proj.y - p_proj.x)); + int i = int(x); + int j = int(y); + + return HashDxDy( + (d0h << (depth << 1)) + ij2z(i, j), + x - float(i), + y - float(j) + ); +} +vec3 xyz2uv(vec3 xyz) { + HashDxDy result = hash_with_dxdy(0, xyz.zxy); + + int idx = result.idx; + vec2 offset = vec2(result.dy, result.dx); + Tile tile = textures_tiles[idx]; + + return vec3(offset, float(tile.texture_idx)); +} + +void main() { + vec3 uv = xyz2uv(normalize(frag_pos)); + + uv.y = 1.0 - uv.y; + vec4 c = uvw2c_i16(uv); + + + out_frag_color = c; + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"hips3d_u8.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler3D; + +uniform sampler3D tex; + +in vec3 frag_uv; + +out vec4 out_frag_color; + +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + +vec4 uvw2c_r(vec3 uv) { + vec2 va = texture(tex, uv).ra; + + va.x = transfer_func(H, va.x, min_value, max_value); + + va.x = mix(va.x, 1.0 - va.x, reversed); + + vec4 c = colormap_f(va.x); + return apply_tonal(c); +} + +vec4 uvw2c_rgba(vec3 uv) { + vec4 c = texture(tex, uv).rgba; + + c.r = transfer_func(H, c.r, min_value, max_value); + c.g = transfer_func(H, c.g, min_value, max_value); + c.b = transfer_func(H, c.b, min_value, max_value); + + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 uvw2c_ra(vec3 uv) { + vec2 c = texture(tex, uv).rg; + + c.r = transfer_func(H, c.r, min_value, max_value); + + c.r = mix(c.r, 1.0 - c.r, reversed); + + vec3 color = colormap_f(c.r).rgb; + + return apply_tonal(vec4(color, c.g)); +} + +vec4 uvw2cmap_rgba(vec3 uv) { + float v = texture(tex, uv).r; + v = transfer_func(H, v, min_value, max_value); + vec4 c = colormap_f(v); + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uvw2c_f32(vec3 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uvw2c_i32(vec3 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_u8(vec3 uv) { + float val = float(decode_u8(texture(tex, uv).r)); return val2c(val); } -vec4 uv2c_u8(vec2 uv) { +uniform float opacity; + +void main() { + vec3 uv = vec3(frag_uv.xyz); + uv.y = 1.0 - uv.y; + + vec4 color = uvw2c_u8(uv); + + out_frag_color = color; + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"image_sampler.frag", + r#"#version 300 es +precision highp float; +precision highp sampler2D; + +out vec4 out_frag_color; +in vec2 frag_uv; + +uniform sampler2D tex; +uniform float opacity; + +void main() { + out_frag_color = texture(tex, frag_uv); + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"hips3d_red.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler3D; +precision lowp isampler3D; +precision lowp usampler3D; + +uniform sampler3D tex; + +in vec3 frag_uv; + +out vec4 out_frag_color; +uniform float opacity; + +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + +vec4 uvw2c_r(vec3 uv) { + vec2 va = texture(tex, uv).ra; + + va.x = transfer_func(H, va.x, min_value, max_value); + + va.x = mix(va.x, 1.0 - va.x, reversed); + + vec4 c = colormap_f(va.x); + return apply_tonal(c); +} + +vec4 uvw2c_rgba(vec3 uv) { + vec4 c = texture(tex, uv).rgba; + + c.r = transfer_func(H, c.r, min_value, max_value); + c.g = transfer_func(H, c.g, min_value, max_value); + c.b = transfer_func(H, c.b, min_value, max_value); + + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 uvw2c_ra(vec3 uv) { + vec2 c = texture(tex, uv).rg; + + c.r = transfer_func(H, c.r, min_value, max_value); + + c.r = mix(c.r, 1.0 - c.r, reversed); + + vec3 color = colormap_f(c.r).rgb; + + return apply_tonal(vec4(color, c.g)); +} + +vec4 uvw2cmap_rgba(vec3 uv) { + float v = texture(tex, uv).r; + v = transfer_func(H, v, min_value, max_value); + vec4 c = colormap_f(v); + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uvw2c_f32(vec3 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uvw2c_i32(vec3 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_u8(vec3 uv) { float val = float(decode_u8(texture(tex, uv).r)); return val2c(val); } void main() { - vec2 uv = frag_uv; + vec3 uv = vec3(frag_uv.xyz); + vec4 color = uvw2c_ra(uv); + + out_frag_color = color; + out_frag_color.a = opacity * out_frag_color.a; +}"#, + ); + out.insert( + r"hips_raytracer_rgba2cmap.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler2DArray; +precision lowp sampler2DArray; +precision lowp isampler2DArray; +precision mediump int; + +in vec3 frag_pos; +in vec2 out_clip_pos; +out vec4 out_frag_color; + +struct Tile { + int uniq; // Healpix cell + int texture_idx; // Index in the texture buffer + float start_time; // Absolute time that the load has been done in ms + float empty; +}; + +uniform Tile textures_tiles[12]; + +uniform float opacity; +uniform sampler2DArray tex; + +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + +vec4 uvw2c_r(vec3 uv) { + vec2 va = texture(tex, uv).ra; + + va.x = transfer_func(H, va.x, min_value, max_value); + + va.x = mix(va.x, 1.0 - va.x, reversed); + + vec4 c = colormap_f(va.x); + return apply_tonal(c); +} + +vec4 uvw2c_rgba(vec3 uv) { + vec4 c = texture(tex, uv).rgba; + + c.r = transfer_func(H, c.r, min_value, max_value); + c.g = transfer_func(H, c.g, min_value, max_value); + c.b = transfer_func(H, c.b, min_value, max_value); + + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 uvw2c_ra(vec3 uv) { + vec2 c = texture(tex, uv).rg; + + c.r = transfer_func(H, c.r, min_value, max_value); + + c.r = mix(c.r, 1.0 - c.r, reversed); + + vec3 color = colormap_f(c.r).rgb; + + return apply_tonal(vec4(color, c.g)); +} + +vec4 uvw2cmap_rgba(vec3 uv) { + float v = texture(tex, uv).r; + v = transfer_func(H, v, min_value, max_value); + vec4 c = colormap_f(v); + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uvw2c_f32(vec3 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uvw2c_i32(vec3 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_u8(vec3 uv) { + float val = float(decode_u8(texture(tex, uv).r)); + return val2c(val); +} +const float TWICE_PI = 6.28318530718; +const float PI = 3.141592653589793; +const float FOUR_OVER_PI = 1.27323954474; +const float TRANSITION_Z = 0.66666666666; +const float TRANSITION_Z_INV = 1.5; + +int quarter(vec2 p) { + int x_neg = int(p.x < 0.0); + int y_neg = int(p.y < 0.0); + int q = (x_neg + y_neg) | (y_neg << 1); + return q; +} + +float xpm1(vec2 p) { + bool x_neg = (p.x < 0.0); + bool y_neg = (p.y < 0.0); + float lon = atan(abs(p.y), abs(p.x)); + float x02 = lon * FOUR_OVER_PI; + if (x_neg != y_neg) { // Could be replaced by a sign copy from (x_neg ^ y_neg) << 32 + return 1.0 - x02; + } else { + return x02 - 1.0; + } +} + +float one_minus_z_pos(vec3 p) { + float d2 = dot(p.xy, p.xy); // z = sqrt(1 - d2) AND sqrt(1 - x) = 1 - x / 2 - x^2 / 8 - x^3 / 16 - 5 x^4/128 - 7 * x^5/256 + + if (d2 < 1e-1) { // <=> dec > 84.27 deg + return d2 * (0.5 + d2 * (0.125 + d2 * (0.0625 + d2 * (0.0390625 + d2 * 0.02734375)))); + } + return 1.0f - p.z; +} + +float one_minus_z_neg(vec3 p) { + float d2 = dot(p.xy, p.xy); // z = sqrt(1 - d2) AND sqrt(1 - x) = 1 - x / 2 - x^2 / 8 - x^3 / 16 - 5 x^4/128 - 7 * x^5/256 + if (d2 < 1e-1f) { // <=> dec < -84.27 deg + return d2 * (0.5 + d2 * (0.125 + d2 * (0.0625 + d2 * (0.0390625 + d2 * 0.02734375)))); + } + return p.z + 1.0; +} + +int ij2z(int i, int j) { + int i4 = i | (j << 2); + + int j4 = (i4 ^ (i4 >> 1)) & 0x22222222; + int i5 = i4 ^ j4 ^ (j4 << 1); + + return i5; +} + +struct HashDxDy { + int idx; + float dx; + float dy; +}; + +uniform sampler2D ang2pixd; +HashDxDy hash_with_dxdy2(vec2 radec) { + vec2 aa = vec2(radec.x/TWICE_PI + 1.0, (radec.y/PI) + 0.5); + vec3 v = texture(ang2pixd, aa).rgb; + return HashDxDy( + int(v.x * 255.0), + v.y, + v.z + ); +} +HashDxDy hash_with_dxdy(int depth, vec3 p) { + + int nside = 1 << depth; + float half_nside = float(nside) * 0.5; + + float x_pm1 = xpm1(p.xy); + int q = quarter(p.xy); + + int d0h = 0; + vec2 p_proj = vec2(0.0); + if (p.z > TRANSITION_Z) { + float sqrt_3_one_min_z = sqrt(3.0 * one_minus_z_pos(p)); + p_proj = vec2(x_pm1 * sqrt_3_one_min_z, 2.0 - sqrt_3_one_min_z); + d0h = q; + } else if (p.z < -TRANSITION_Z) { + float sqrt_3_one_min_z = sqrt(3.0 * one_minus_z_neg(p)); + p_proj = vec2(x_pm1 * sqrt_3_one_min_z, sqrt_3_one_min_z); + d0h = q + 8; + } else { + float y_pm1 = p.z * TRANSITION_Z_INV; + int q01 = int(x_pm1 > y_pm1); // 0/1 + int q12 = int(x_pm1 >= -y_pm1); // 0\1 + int q03 = 1 - q12; // 1\0 + int q1 = q01 & q12; // = 1 if q1, 0 else + p_proj = vec2( + x_pm1 - float(q01 + q12 - 1), + y_pm1 + float(q01 + q03) + ); + d0h = ((q01 + q03) << 2) + ((q + q1) & 3); + } + + float x = (half_nside * (p_proj.x + p_proj.y)); + float y = (half_nside * (p_proj.y - p_proj.x)); + int i = int(x); + int j = int(y); + + return HashDxDy( + (d0h << (depth << 1)) + ij2z(i, j), + x - float(i), + y - float(j) + ); +} +vec3 xyz2uv(vec3 xyz) { + HashDxDy result = hash_with_dxdy(0, xyz.zxy); + + int idx = result.idx; + vec2 offset = vec2(result.dy, result.dx); + Tile tile = textures_tiles[idx]; + + return vec3(offset, float(tile.texture_idx)); +} + +void main() { + vec3 uv = xyz2uv(normalize(frag_pos)); + vec4 c = uvw2cmap_rgba(uv); + + out_frag_color = c; + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"hips3d_i32.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler3D; +precision lowp isampler3D; +precision lowp usampler3D; + +uniform sampler3D tex; + +in vec3 frag_uv; + +out vec4 out_frag_color; + +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + +vec4 uvw2c_r(vec3 uv) { + vec2 va = texture(tex, uv).ra; + + va.x = transfer_func(H, va.x, min_value, max_value); + + va.x = mix(va.x, 1.0 - va.x, reversed); + + vec4 c = colormap_f(va.x); + return apply_tonal(c); +} + +vec4 uvw2c_rgba(vec3 uv) { + vec4 c = texture(tex, uv).rgba; + + c.r = transfer_func(H, c.r, min_value, max_value); + c.g = transfer_func(H, c.g, min_value, max_value); + c.b = transfer_func(H, c.b, min_value, max_value); + + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 uvw2c_ra(vec3 uv) { + vec2 c = texture(tex, uv).rg; + + c.r = transfer_func(H, c.r, min_value, max_value); + + c.r = mix(c.r, 1.0 - c.r, reversed); + + vec3 color = colormap_f(c.r).rgb; + + return apply_tonal(vec4(color, c.g)); +} + +vec4 uvw2cmap_rgba(vec3 uv) { + float v = texture(tex, uv).r; + v = transfer_func(H, v, min_value, max_value); + vec4 c = colormap_f(v); + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uvw2c_f32(vec3 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uvw2c_i32(vec3 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_u8(vec3 uv) { + float val = float(decode_u8(texture(tex, uv).r)); + return val2c(val); +} + +uniform float opacity; + +void main() { + vec3 uv = vec3(frag_uv.xyz); uv.y = 1.0 - uv.y; - out_frag_color = uv2c_i16(frag_uv); + vec4 color = uvw2c_i32(uv); + + out_frag_color = color; out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"hips_rasterizer_i32.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler2DArray; + +uniform sampler2DArray tex; + +in vec3 frag_uv_start; +in vec3 frag_uv_end; +in float frag_blending_factor; + +out vec4 out_frag_color; + +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + +vec4 uvw2c_r(vec3 uv) { + vec2 va = texture(tex, uv).ra; + + va.x = transfer_func(H, va.x, min_value, max_value); + + va.x = mix(va.x, 1.0 - va.x, reversed); + + vec4 c = colormap_f(va.x); + return apply_tonal(c); +} + +vec4 uvw2c_rgba(vec3 uv) { + vec4 c = texture(tex, uv).rgba; + + c.r = transfer_func(H, c.r, min_value, max_value); + c.g = transfer_func(H, c.g, min_value, max_value); + c.b = transfer_func(H, c.b, min_value, max_value); + + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 uvw2c_ra(vec3 uv) { + vec2 c = texture(tex, uv).rg; + + c.r = transfer_func(H, c.r, min_value, max_value); + + c.r = mix(c.r, 1.0 - c.r, reversed); + + vec3 color = colormap_f(c.r).rgb; + + return apply_tonal(vec4(color, c.g)); +} + +vec4 uvw2cmap_rgba(vec3 uv) { + float v = texture(tex, uv).r; + v = transfer_func(H, v, min_value, max_value); + vec4 c = colormap_f(v); + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uvw2c_f32(vec3 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uvw2c_i32(vec3 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_u8(vec3 uv) { + float val = float(decode_u8(texture(tex, uv).r)); + return val2c(val); +} + +uniform float opacity; + +void main() { + vec3 uv0 = frag_uv_start; + vec3 uv1 = frag_uv_end; + uv0.y = 1.0 - uv0.y; + uv1.y = 1.0 - uv1.y; + + vec4 color_start = uvw2c_i32(uv0); + vec4 color_end = uvw2c_i32(uv1); + + out_frag_color = mix(color_start, color_end, frag_blending_factor); + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"fits_base.vert", + r#"#version 300 es +precision highp float; +precision highp int; + +layout (location = 0) in vec2 ndc_pos; +layout (location = 1) in vec2 uv; + +out vec2 frag_uv; + +void main() { + gl_Position = vec4(ndc_pos, 0.0, 1.0); + frag_uv = uv; +}"#, + ); + out.insert( + r"line_inst_ndc.vert", + r#"#version 300 es +precision highp float; +layout (location = 0) in vec2 p_a; +layout (location = 1) in vec2 p_b; +layout (location = 2) in vec2 vertex; + +out vec2 l; + +uniform float u_width; +uniform float u_height; +uniform float u_thickness; + +void main() { + vec2 x_b = p_b - p_a; + vec2 y_b = normalize(vec2(-x_b.y, x_b.x)); + + float ndc2pix = 2.0 / u_width; + + vec2 p = p_a + x_b * vertex.x + (u_thickness + 2.0) * y_b * vertex.y * vec2(1.0, u_width/u_height) * ndc2pix; + gl_Position = vec4(p, 0.f, 1.f); + l = vec2(0.0, vertex.y); +}"#, + ); + out.insert( + r"passes_post_vertex_100es.vert", + r#"#version 300 es +precision mediump float; + +layout (location = 0) in vec2 a_pos; +out vec2 v_tc; + +void main() { + gl_Position = vec4(a_pos * 2. - 1., 0.0, 1.0); + v_tc = a_pos; +}"#, + ); + out.insert( + r"colormaps_colormap.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler2D; + +in vec2 out_uv; +out vec4 color; + +uniform sampler2D texture_fbo; +uniform float alpha; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} + +void main() { + float opacity = texture(texture_fbo, out_uv).r; + + float o = smoothstep(0.0, 0.1, opacity); + + color = colormap_f(opacity); + color.a = o * alpha; +}"#, + ); + out.insert( + r"hips_raytracer_backcolor.vert", + r#"#version 300 es +precision lowp float; +precision mediump int; + +layout (location = 0) in vec2 pos_clip_space; + +uniform vec2 ndc_to_clip; +uniform float czf; + +void main() { + gl_Position = vec4(pos_clip_space / (ndc_to_clip * czf), 0.0, 1.0); +}"#, + ); + out.insert( + r"hips_rasterizer_raster.vert", + r#"#version 300 es +precision highp float; + +layout (location = 0) in vec3 xyz; +layout (location = 1) in vec3 uv_start; +layout (location = 2) in vec3 uv_end; +layout (location = 3) in float time_tile_received; + +out vec3 frag_uv_start; +out vec3 frag_uv_end; +out float frag_blending_factor; + +uniform mat3 inv_model; +uniform vec2 ndc_to_clip; +uniform float czf; +uniform float current_time; + +const float PI = 3.141592653589793; +const float SQRT_2 = 1.41421356237309504880168872420969808; + +vec2 w2c_sin(vec3 p) { + vec2 q = vec2(-p.x, p.y); + return p.z >= 0.f ? q : normalize(q); +} + +vec2 w2c_sin_no_backface(vec3 p) { + return vec2(-p.x, p.y); +} + +vec2 w2c_ait(vec3 p) { + float r = length(p.zx); + float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) + w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma + float y2d = p.y / w; + + float x2d = 0.0; + if (abs(p.x) < 5e-3) { + float x_over_r = p.x/r; + x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; + } else { + w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) + x2d = sign(-p.x) * w; + } + + return vec2(x2d * 0.5, y2d) / SQRT_2; +} +const float eps = 1.25e-8; +const int n_iter = 100; + +float newton_solve(float z) { + float cte = PI * z; + float x = 2.0 * asin(z); + float f = x + sin(x) - cte; + int i = 0; + while (abs(f) > eps && i < n_iter) { + x -= f / (1.0 + cos(x)); + f = x + sin(x) - cte; + i += 1; + } + + return 0.5 * x; +} + +vec2 w2c_mol(vec3 p) { + float g = newton_solve(p.y); + + float sg = sin(g); + float cg = cos(g); + return vec2((atan(-p.x, p.z) * cg) / PI, sg); +} +vec2 w2c_tan(vec3 p) { + p.z = max(p.z, 1e-2); + return vec2(-p.x, p.y) / (p.z*PI); +} +vec2 w2c_stg(vec3 p) { + float w = (1.0 + p.z) * 0.5; + return vec2(-p.x, p.y) / (PI * w); +} +vec2 w2c_zea(vec3 p) { + float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] + return vec2(-p.x, p.y) * 0.5 / w; +} +vec2 w2c_mer(vec3 p) { + return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; +} + +vec3 lonlat2xyz(vec2 lonlat) { + float t = lonlat.x; + float tc = cos(t); + float ts = sin(t); + + float d = lonlat.y; + float dc = cos(d); + float ds = sin(d); + + return vec3(dc * ts, ds, dc * tc); +} + +uniform int u_proj; + +vec2 proj(vec3 p) { + if (u_proj == 0) { + return w2c_tan(p); + } else if (u_proj == 1) { + return w2c_stg(p); + } else if (u_proj == 2) { + return w2c_sin(p); + } else if (u_proj == 3) { + return w2c_zea(p); + } else if (u_proj == 4) { + return w2c_ait(p); + } else if (u_proj == 5) { + return w2c_mol(p); + } else { + return w2c_mer(p); + } +} + +void main() { + vec3 p_w = inv_model * xyz; + vec2 p_clip = proj(p_w); + + vec2 p_ndc = p_clip / (ndc_to_clip * czf); + gl_Position = vec4(p_ndc, 0.0, 1.0); + + frag_uv_start = uv_start; + frag_uv_end = uv_end; + frag_blending_factor = min((current_time - time_tile_received) / 200.0, 1.0); }"#, ); out.insert( @@ -5345,20 +5767,268 @@ void main() { }"#, ); out.insert( - r"image_sampler.frag", + r"hips_rasterizer_rgba2cmap.frag", r#"#version 300 es -precision highp float; -precision highp sampler2D; +precision lowp float; +precision lowp sampler2DArray; + +uniform sampler2DArray tex; + +in vec3 frag_uv_start; +in vec3 frag_uv_end; +in float frag_blending_factor; out vec4 out_frag_color; -in vec2 frag_uv; - -uniform sampler2D tex; uniform float opacity; +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + +vec4 uvw2c_r(vec3 uv) { + vec2 va = texture(tex, uv).ra; + + va.x = transfer_func(H, va.x, min_value, max_value); + + va.x = mix(va.x, 1.0 - va.x, reversed); + + vec4 c = colormap_f(va.x); + return apply_tonal(c); +} + +vec4 uvw2c_rgba(vec3 uv) { + vec4 c = texture(tex, uv).rgba; + + c.r = transfer_func(H, c.r, min_value, max_value); + c.g = transfer_func(H, c.g, min_value, max_value); + c.b = transfer_func(H, c.b, min_value, max_value); + + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 uvw2c_ra(vec3 uv) { + vec2 c = texture(tex, uv).rg; + + c.r = transfer_func(H, c.r, min_value, max_value); + + c.r = mix(c.r, 1.0 - c.r, reversed); + + vec3 color = colormap_f(c.r).rgb; + + return apply_tonal(vec4(color, c.g)); +} + +vec4 uvw2cmap_rgba(vec3 uv) { + float v = texture(tex, uv).r; + v = transfer_func(H, v, min_value, max_value); + vec4 c = colormap_f(v); + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uvw2c_f32(vec3 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uvw2c_i32(vec3 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_u8(vec3 uv) { + float val = float(decode_u8(texture(tex, uv).r)); + return val2c(val); +} + void main() { - out_frag_color = texture(tex, frag_uv); - out_frag_color.a = out_frag_color.a * opacity; + vec4 color_start = uvw2cmap_rgba(frag_uv_start); + vec4 color_end = uvw2cmap_rgba(frag_uv_end); + + out_frag_color = mix(color_start, color_end, frag_blending_factor); + out_frag_color.a = opacity * out_frag_color.a; }"#, ); out.insert( @@ -5762,946 +6432,7 @@ void main() { }"#, ); out.insert( - r"catalogs_mercator.vert", - r#"#version 300 es -precision lowp float; -layout (location = 0) in vec2 offset; -layout (location = 1) in vec2 uv; -layout (location = 2) in vec3 center; - -uniform float current_time; -uniform mat3 inv_model; - -uniform vec2 ndc_to_clip; -uniform float czf; -uniform vec2 kernel_size; - -out vec2 out_uv; -out vec3 out_p; - -const float PI = 3.141592653589793; -const float SQRT_2 = 1.41421356237309504880168872420969808; - -vec2 w2c_sin(vec3 p) { - vec2 q = vec2(-p.x, p.y); - return p.z >= 0.f ? q : normalize(q); -} - -vec2 w2c_sin_no_backface(vec3 p) { - return vec2(-p.x, p.y); -} - -vec2 w2c_ait(vec3 p) { - float r = length(p.zx); - float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) - w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma - float y2d = p.y / w; - - float x2d = 0.0; - if (abs(p.x) < 5e-3) { - float x_over_r = p.x/r; - x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; - } else { - w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) - x2d = sign(-p.x) * w; - } - - return vec2(x2d * 0.5, y2d) / SQRT_2; -} -const float eps = 1.25e-8; -const int n_iter = 100; - -float newton_solve(float z) { - float cte = PI * z; - float x = 2.0 * asin(z); - float f = x + sin(x) - cte; - int i = 0; - while (abs(f) > eps && i < n_iter) { - x -= f / (1.0 + cos(x)); - f = x + sin(x) - cte; - i += 1; - } - - return 0.5 * x; -} - -vec2 w2c_mol(vec3 p) { - float g = newton_solve(p.y); - - float sg = sin(g); - float cg = cos(g); - return vec2((atan(-p.x, p.z) * cg) / PI, sg); -} -vec2 w2c_tan(vec3 p) { - p.z = max(p.z, 1e-2); - return vec2(-p.x, p.y) / (p.z*PI); -} -vec2 w2c_stg(vec3 p) { - float w = (1.0 + p.z) * 0.5; - return vec2(-p.x, p.y) / (PI * w); -} -vec2 w2c_zea(vec3 p) { - float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] - return vec2(-p.x, p.y) * 0.5 / w; -} -vec2 w2c_mer(vec3 p) { - return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; -} - -vec3 lonlat2xyz(vec2 lonlat) { - float t = lonlat.x; - float tc = cos(t); - float ts = sin(t); - - float d = lonlat.y; - float dc = cos(d); - float ds = sin(d); - - return vec3(dc * ts, ds, dc * tc); -} - -uniform int u_proj; - -vec2 proj(vec3 p) { - if (u_proj == 0) { - return w2c_tan(p); - } else if (u_proj == 1) { - return w2c_stg(p); - } else if (u_proj == 2) { - return w2c_sin(p); - } else if (u_proj == 3) { - return w2c_zea(p); - } else if (u_proj == 4) { - return w2c_ait(p); - } else if (u_proj == 5) { - return w2c_mol(p); - } else { - return w2c_mer(p); - } -} - - -void main() { - vec3 p = inv_model * center; - - vec2 center_pos_clip_space = world2clip_mercator(p); - - vec2 pos_clip_space = center_pos_clip_space; - gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); - - out_uv = uv; - out_p = p; -}"#, - ); - out.insert( - r"hips_rasterizer_rgba.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler2DArray; - -uniform sampler2DArray tex; - -in vec3 frag_uv_start; -in vec3 frag_uv_end; -in float frag_blending_factor; - -out vec4 out_frag_color; -uniform float opacity; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -vec3 rgb2hsv(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - -vec4 uvw2c_r(vec3 uv) { - vec2 va = texture(tex, uv).ra; - - va.x = transfer_func(H, va.x, min_value, max_value); - - va.x = mix(va.x, 1.0 - va.x, reversed); - - vec4 c = colormap_f(va.x); - return apply_tonal(c); -} - -vec4 uvw2c_rgba(vec3 uv) { - vec4 c = texture(tex, uv).rgba; - - c.r = transfer_func(H, c.r, min_value, max_value); - c.g = transfer_func(H, c.g, min_value, max_value); - c.b = transfer_func(H, c.b, min_value, max_value); - - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 uvw2c_ra(vec3 uv) { - vec2 c = texture(tex, uv).rg; - - c.r = transfer_func(H, c.r, min_value, max_value); - - c.r = mix(c.r, 1.0 - c.r, reversed); - - vec3 color = colormap_f(c.r).rgb; - - return apply_tonal(vec4(color, c.g)); -} - -vec4 uvw2cmap_rgba(vec3 uv) { - float v = texture(tex, uv).r; - v = transfer_func(H, v, min_value, max_value); - vec4 c = colormap_f(v); - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uvw2c_f32(vec3 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uvw2c_i32(vec3 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_u8(vec3 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} - -void main() { - vec4 color_start = uvw2c_rgba(frag_uv_start); - vec4 color_end = uvw2c_rgba(frag_uv_end); - - out_frag_color = mix(color_start, color_end, frag_blending_factor); - out_frag_color.a = opacity * out_frag_color.a; -}"#, - ); - out.insert( - r"catalogs_ortho.frag", - r#"#version 300 es -precision lowp float; - -in vec2 out_uv; -in vec3 out_p; - -out vec4 color; - -uniform sampler2D kernel_texture; -uniform float max_density; // max number of sources in a kernel sized HEALPix cell at the current depth -uniform float fov; -uniform float strength; -void main() { - if (out_p.z < 0.f) { - discard; - } - - color = texture(kernel_texture, out_uv) / max(log2(fov*100.0), 1.0); - color.r *= strength; -}"#, - ); - out.insert( - r"hips3d_raster.vert", - r#"#version 300 es -precision lowp float; - -layout (location = 0) in vec2 lonlat; -layout (location = 1) in vec3 uv; - -out vec3 frag_uv; - -uniform mat3 inv_model; -uniform vec2 ndc_to_clip; -uniform float czf; - -const float PI = 3.141592653589793; -const float SQRT_2 = 1.41421356237309504880168872420969808; - -vec2 w2c_sin(vec3 p) { - vec2 q = vec2(-p.x, p.y); - return p.z >= 0.f ? q : normalize(q); -} - -vec2 w2c_sin_no_backface(vec3 p) { - return vec2(-p.x, p.y); -} - -vec2 w2c_ait(vec3 p) { - float r = length(p.zx); - float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) - w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma - float y2d = p.y / w; - - float x2d = 0.0; - if (abs(p.x) < 5e-3) { - float x_over_r = p.x/r; - x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; - } else { - w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) - x2d = sign(-p.x) * w; - } - - return vec2(x2d * 0.5, y2d) / SQRT_2; -} -const float eps = 1.25e-8; -const int n_iter = 100; - -float newton_solve(float z) { - float cte = PI * z; - float x = 2.0 * asin(z); - float f = x + sin(x) - cte; - int i = 0; - while (abs(f) > eps && i < n_iter) { - x -= f / (1.0 + cos(x)); - f = x + sin(x) - cte; - i += 1; - } - - return 0.5 * x; -} - -vec2 w2c_mol(vec3 p) { - float g = newton_solve(p.y); - - float sg = sin(g); - float cg = cos(g); - return vec2((atan(-p.x, p.z) * cg) / PI, sg); -} -vec2 w2c_tan(vec3 p) { - p.z = max(p.z, 1e-2); - return vec2(-p.x, p.y) / (p.z*PI); -} -vec2 w2c_stg(vec3 p) { - float w = (1.0 + p.z) * 0.5; - return vec2(-p.x, p.y) / (PI * w); -} -vec2 w2c_zea(vec3 p) { - float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] - return vec2(-p.x, p.y) * 0.5 / w; -} -vec2 w2c_mer(vec3 p) { - return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; -} - -vec3 lonlat2xyz(vec2 lonlat) { - float t = lonlat.x; - float tc = cos(t); - float ts = sin(t); - - float d = lonlat.y; - float dc = cos(d); - float ds = sin(d); - - return vec3(dc * ts, ds, dc * tc); -} - -uniform int u_proj; - -vec2 proj(vec3 p) { - if (u_proj == 0) { - return w2c_tan(p); - } else if (u_proj == 1) { - return w2c_stg(p); - } else if (u_proj == 2) { - return w2c_sin(p); - } else if (u_proj == 3) { - return w2c_zea(p); - } else if (u_proj == 4) { - return w2c_ait(p); - } else if (u_proj == 5) { - return w2c_mol(p); - } else { - return w2c_mer(p); - } -} - -void main() { - vec3 p_xyz = lonlat2xyz(lonlat); - vec3 p_w = inv_model * p_xyz; - vec2 p_clip = proj(p_w.xyz); - - vec2 p_ndc = p_clip / (ndc_to_clip * czf); - gl_Position = vec4(p_ndc, 0.0, 1.0); - - frag_uv = uv; -}"#, - ); - out.insert( - r"colormaps_colormap.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler2D; - -in vec2 out_uv; -out vec4 color; - -uniform sampler2D texture_fbo; -uniform float alpha; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} - -void main() { - float opacity = texture(texture_fbo, out_uv).r; - - float o = smoothstep(0.0, 0.1, opacity); - - color = colormap_f(opacity); - color.a = o * alpha; -}"#, - ); - out.insert( - r"catalogs_healpix.vert", - r#"#version 300 es -precision lowp float; -layout (location = 0) in vec2 offset; -layout (location = 1) in vec2 uv; -layout (location = 2) in vec3 center; - -uniform float current_time; -uniform mat3 inv_model; - -uniform vec2 ndc_to_clip; -uniform float czf; -uniform vec2 kernel_size; - -out vec2 out_uv; -out vec3 out_p; - -const float PI = 3.141592653589793; -const float SQRT_2 = 1.41421356237309504880168872420969808; - -vec2 w2c_sin(vec3 p) { - vec2 q = vec2(-p.x, p.y); - return p.z >= 0.f ? q : normalize(q); -} - -vec2 w2c_sin_no_backface(vec3 p) { - return vec2(-p.x, p.y); -} - -vec2 w2c_ait(vec3 p) { - float r = length(p.zx); - float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) - w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma - float y2d = p.y / w; - - float x2d = 0.0; - if (abs(p.x) < 5e-3) { - float x_over_r = p.x/r; - x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; - } else { - w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) - x2d = sign(-p.x) * w; - } - - return vec2(x2d * 0.5, y2d) / SQRT_2; -} -const float eps = 1.25e-8; -const int n_iter = 100; - -float newton_solve(float z) { - float cte = PI * z; - float x = 2.0 * asin(z); - float f = x + sin(x) - cte; - int i = 0; - while (abs(f) > eps && i < n_iter) { - x -= f / (1.0 + cos(x)); - f = x + sin(x) - cte; - i += 1; - } - - return 0.5 * x; -} - -vec2 w2c_mol(vec3 p) { - float g = newton_solve(p.y); - - float sg = sin(g); - float cg = cos(g); - return vec2((atan(-p.x, p.z) * cg) / PI, sg); -} -vec2 w2c_tan(vec3 p) { - p.z = max(p.z, 1e-2); - return vec2(-p.x, p.y) / (p.z*PI); -} -vec2 w2c_stg(vec3 p) { - float w = (1.0 + p.z) * 0.5; - return vec2(-p.x, p.y) / (PI * w); -} -vec2 w2c_zea(vec3 p) { - float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] - return vec2(-p.x, p.y) * 0.5 / w; -} -vec2 w2c_mer(vec3 p) { - return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; -} - -vec3 lonlat2xyz(vec2 lonlat) { - float t = lonlat.x; - float tc = cos(t); - float ts = sin(t); - - float d = lonlat.y; - float dc = cos(d); - float ds = sin(d); - - return vec3(dc * ts, ds, dc * tc); -} - -uniform int u_proj; - -vec2 proj(vec3 p) { - if (u_proj == 0) { - return w2c_tan(p); - } else if (u_proj == 1) { - return w2c_stg(p); - } else if (u_proj == 2) { - return w2c_sin(p); - } else if (u_proj == 3) { - return w2c_zea(p); - } else if (u_proj == 4) { - return w2c_ait(p); - } else if (u_proj == 5) { - return w2c_mol(p); - } else { - return w2c_mer(p); - } -} - - -void main() { - vec3 p = inv_model * center; - - vec2 center_pos_clip_space = world2clip_healpix(p); - - vec2 pos_clip_space = center_pos_clip_space; - gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); - - out_uv = uv; - out_p = p; -}"#, - ); - out.insert( - r"fits_f32.frag", - r#"#version 300 es -precision highp float; -precision highp sampler2D; -precision highp int; - -out vec4 out_frag_color; -in vec2 frag_uv; - -uniform sampler2D tex; -uniform float opacity; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uv2c_f32(vec2 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uv2c_i32(vec2 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return val2c(val); -} - -vec4 uv2c_i16(vec2 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return val2c(val); -} - -vec4 uv2c_u8(vec2 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} - -void main() { - vec2 uv = frag_uv; - uv.y = 1.0 - uv.y; - - out_frag_color = uv2c_f32(frag_uv); - out_frag_color.a = out_frag_color.a * opacity; -}"#, - ); - out.insert( - r"passes_post_fragment_100es.frag", - r#"#version 300 es -precision mediump float; - -in vec2 v_tc; -out vec4 color; - -uniform sampler2D fbo_tex; - -vec3 srgb_from_linear(vec3 rgb) { - bvec3 cutoff = lessThan(rgb, vec3(0.0031308)); - vec3 lower = rgb * vec3(3294.6); - vec3 higher = vec3(269.025) * pow(rgb, vec3(1.0 / 2.4)) - vec3(14.025); - return mix(higher, lower, vec3(cutoff)); -} - -vec4 srgba_from_linear(vec4 rgba) { - return vec4(srgb_from_linear(rgba.rgb), 255.0 * rgba.a); -} - -void main() { - color = texture(fbo_tex, v_tc); - -}"#, - ); - out.insert( - r"hips3d_i32.frag", + r"hips3d_i16.frag", r#"#version 300 es precision lowp float; precision lowp sampler3D; @@ -6962,275 +6693,10 @@ void main() { vec3 uv = vec3(frag_uv.xyz); uv.y = 1.0 - uv.y; - vec4 color = uvw2c_i32(uv); + vec4 color = uvw2c_i16(uv); out_frag_color = color; out_frag_color.a = out_frag_color.a * opacity; -}"#, - ); - out.insert( - r"hips3d_red.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler3D; -precision lowp isampler3D; -precision lowp usampler3D; - -uniform sampler3D tex; - -in vec3 frag_uv; - -out vec4 out_frag_color; -uniform float opacity; - -uniform float scale; -uniform float offset; -uniform float blank; -uniform float min_value; -uniform float max_value; -uniform int H; -uniform float reversed; - -uniform sampler2D colormaps; -uniform float num_colormaps; -uniform float colormap_id; - -vec4 colormap_f(float x) { - float id = (colormap_id + 0.5) / num_colormaps; - return texture(colormaps, vec2(x, id)); -} -float linear_f(float x, float min_value, float max_value) { - return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); -} - -float sqrt_f(float x, float min_value, float max_value) { - float a = linear_f(x, min_value, max_value); - return sqrt(a); -} - -float log_f(float x, float min_value, float max_value) { - float y = linear_f(x, min_value, max_value); - float a = 1000.0; - return log(a*y + 1.0)/log(a); -} - -float asinh_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return asinh(10.0*d)/3.0; -} - -float pow2_f(float x, float min_value, float max_value) { - float d = linear_f(x, min_value, max_value); - return d*d; -} - -float transfer_func(int H, float x, float min_value, float max_value) { - if (H == 0) { - return linear_f(x, min_value, max_value); - } else if (H == 1) { - return sqrt_f(x, min_value, max_value); - } else if (H == 2) { - return log_f(x, min_value, max_value); - } else if (H == 3) { - return asinh_f(x, min_value, max_value); - } else { - return pow2_f(x, min_value, max_value); - } -} - -uniform float k_gamma; -uniform float k_saturation; -uniform float k_contrast; -uniform float k_brightness; -uniform float k_exposure; - -vec4 apply_gamma(vec4 ic, float g) { - float new_r = pow(ic.r, g); - float new_g = pow(ic.g, g); - float new_b = pow(ic.b, g); - - return vec4(new_r, new_g, new_b, ic.a); -} - -vec4 apply_saturation(vec4 color, float value) { - const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); - vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); - - return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); -} - -vec4 apply_contrast(vec4 color, float value) { - return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); -} - -vec4 apply_brightness(vec4 color, float value) { - return vec4(color.rgb + value, color.a); -} - -vec4 apply_exposure(vec4 color, float value) { - return vec4((1.0 + value) * color.rgb, color.a); -} - -vec4 apply_tonal(vec4 color) { - return apply_gamma( - apply_saturation( - apply_contrast( - apply_brightness(color, k_brightness), - k_contrast - ), - k_saturation - ), - k_gamma - ); -} -vec3 rgb2hsv(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} -highp float decode_f32(highp vec4 rgba) { - highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; - highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; - if (abs(Exponent + 127.0) < 1e-3) { - return 0.0; - } - highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); - return Result; -} - -int decode_i32(vec4 rgba) { - int r = int(rgba.r * 255.0 + 0.5); - int g = int(rgba.g * 255.0 + 0.5); - int b = int(rgba.b * 255.0 + 0.5); - int a = int(rgba.a * 255.0 + 0.5); - - int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer - - return value; -} - -int decode_i16(vec2 rg) { - int r = int(rg.r * 255.0 + 0.5); - int g = int(rg.g * 255.0 + 0.5); - - int value = (r << 8) | g; // Combine into a 16-bit integer - - if (value >= 32768) { - value -= 65536; - } - - return value; -} - -uint decode_u8(float r) { - uint value = uint(r * 255.0 + 0.5); - return value; -} - - - - -vec4 uvw2c_r(vec3 uv) { - vec2 va = texture(tex, uv).ra; - - va.x = transfer_func(H, va.x, min_value, max_value); - - va.x = mix(va.x, 1.0 - va.x, reversed); - - vec4 c = colormap_f(va.x); - return apply_tonal(c); -} - -vec4 uvw2c_rgba(vec3 uv) { - vec4 c = texture(tex, uv).rgba; - - c.r = transfer_func(H, c.r, min_value, max_value); - c.g = transfer_func(H, c.g, min_value, max_value); - c.b = transfer_func(H, c.b, min_value, max_value); - - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 uvw2c_ra(vec3 uv) { - vec2 c = texture(tex, uv).rg; - - c.r = transfer_func(H, c.r, min_value, max_value); - - c.r = mix(c.r, 1.0 - c.r, reversed); - - vec3 color = colormap_f(c.r).rgb; - - return apply_tonal(vec4(color, c.g)); -} - -vec4 uvw2cmap_rgba(vec3 uv) { - float v = texture(tex, uv).r; - v = transfer_func(H, v, min_value, max_value); - vec4 c = colormap_f(v); - c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); - - return apply_tonal(c); -} - -vec4 val2c_f32(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); - return apply_tonal(new_color); -} - -vec4 val2c(float x) { - float alpha = x * scale + offset; - alpha = transfer_func(H, alpha, min_value, max_value); - - alpha = mix(alpha, 1.0 - alpha, reversed); - - vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); - return apply_tonal(new_color); -} - -vec4 uvw2c_f32(vec3 uv) { - float val = decode_f32(texture(tex, uv).rgba*255.0); - return val2c_f32(val); -} - -vec4 uvw2c_i32(vec3 uv) { - float val = float(decode_i32(texture(tex, uv).rgba)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return mix(val2c(val), vec4(0.0), float(val == -1.0)); -} - -vec4 uvw2c_u8(vec3 uv) { - float val = float(decode_u8(texture(tex, uv).r)); - return val2c(val); -} - -void main() { - vec3 uv = vec3(frag_uv.xyz); - vec4 color = uvw2c_ra(uv); - - out_frag_color = color; - out_frag_color.a = opacity * out_frag_color.a; }"#, ); out.insert( @@ -7505,7 +6971,543 @@ void main() { }"#, ); out.insert( - r"hips_raytracer_i16.frag", + r"catalogs_ortho.vert", + r#"#version 300 es +precision lowp float; +layout (location = 0) in vec2 offset; +layout (location = 1) in vec2 uv; +layout (location = 2) in vec3 center; + +uniform float current_time; +uniform mat3 inv_model; + +uniform vec2 ndc_to_clip; +uniform float czf; +uniform vec2 kernel_size; + +out vec2 out_uv; +out vec3 out_p; + +const float PI = 3.141592653589793; +const float SQRT_2 = 1.41421356237309504880168872420969808; + +vec2 w2c_sin(vec3 p) { + vec2 q = vec2(-p.x, p.y); + return p.z >= 0.f ? q : normalize(q); +} + +vec2 w2c_sin_no_backface(vec3 p) { + return vec2(-p.x, p.y); +} + +vec2 w2c_ait(vec3 p) { + float r = length(p.zx); + float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) + w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma + float y2d = p.y / w; + + float x2d = 0.0; + if (abs(p.x) < 5e-3) { + float x_over_r = p.x/r; + x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; + } else { + w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) + x2d = sign(-p.x) * w; + } + + return vec2(x2d * 0.5, y2d) / SQRT_2; +} +const float eps = 1.25e-8; +const int n_iter = 100; + +float newton_solve(float z) { + float cte = PI * z; + float x = 2.0 * asin(z); + float f = x + sin(x) - cte; + int i = 0; + while (abs(f) > eps && i < n_iter) { + x -= f / (1.0 + cos(x)); + f = x + sin(x) - cte; + i += 1; + } + + return 0.5 * x; +} + +vec2 w2c_mol(vec3 p) { + float g = newton_solve(p.y); + + float sg = sin(g); + float cg = cos(g); + return vec2((atan(-p.x, p.z) * cg) / PI, sg); +} +vec2 w2c_tan(vec3 p) { + p.z = max(p.z, 1e-2); + return vec2(-p.x, p.y) / (p.z*PI); +} +vec2 w2c_stg(vec3 p) { + float w = (1.0 + p.z) * 0.5; + return vec2(-p.x, p.y) / (PI * w); +} +vec2 w2c_zea(vec3 p) { + float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] + return vec2(-p.x, p.y) * 0.5 / w; +} +vec2 w2c_mer(vec3 p) { + return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; +} + +vec3 lonlat2xyz(vec2 lonlat) { + float t = lonlat.x; + float tc = cos(t); + float ts = sin(t); + + float d = lonlat.y; + float dc = cos(d); + float ds = sin(d); + + return vec3(dc * ts, ds, dc * tc); +} + +uniform int u_proj; + +vec2 proj(vec3 p) { + if (u_proj == 0) { + return w2c_tan(p); + } else if (u_proj == 1) { + return w2c_stg(p); + } else if (u_proj == 2) { + return w2c_sin(p); + } else if (u_proj == 3) { + return w2c_zea(p); + } else if (u_proj == 4) { + return w2c_ait(p); + } else if (u_proj == 5) { + return w2c_mol(p); + } else { + return w2c_mer(p); + } +} + + +void main() { + vec3 p = inv_model * center; + + vec2 center_pos_clip_space = world2clip_orthographic(p); + + vec2 pos_clip_space = center_pos_clip_space; + gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); + + out_uv = uv; + out_p = p; +}"#, + ); + out.insert( + r"hips_rasterizer_u8.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler2DArray; + +uniform sampler2DArray tex; + +in vec3 frag_uv_start; +in vec3 frag_uv_end; +in float frag_blending_factor; + +out vec4 out_frag_color; + +uniform float scale; +uniform float offset; +uniform float blank; +uniform float min_value; +uniform float max_value; +uniform int H; +uniform float reversed; + +uniform sampler2D colormaps; +uniform float num_colormaps; +uniform float colormap_id; + +vec4 colormap_f(float x) { + float id = (colormap_id + 0.5) / num_colormaps; + return texture(colormaps, vec2(x, id)); +} +float linear_f(float x, float min_value, float max_value) { + return clamp((x - min_value)/(max_value - min_value), 0.0, 1.0); +} + +float sqrt_f(float x, float min_value, float max_value) { + float a = linear_f(x, min_value, max_value); + return sqrt(a); +} + +float log_f(float x, float min_value, float max_value) { + float y = linear_f(x, min_value, max_value); + float a = 1000.0; + return log(a*y + 1.0)/log(a); +} + +float asinh_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return asinh(10.0*d)/3.0; +} + +float pow2_f(float x, float min_value, float max_value) { + float d = linear_f(x, min_value, max_value); + return d*d; +} + +float transfer_func(int H, float x, float min_value, float max_value) { + if (H == 0) { + return linear_f(x, min_value, max_value); + } else if (H == 1) { + return sqrt_f(x, min_value, max_value); + } else if (H == 2) { + return log_f(x, min_value, max_value); + } else if (H == 3) { + return asinh_f(x, min_value, max_value); + } else { + return pow2_f(x, min_value, max_value); + } +} + +uniform float k_gamma; +uniform float k_saturation; +uniform float k_contrast; +uniform float k_brightness; +uniform float k_exposure; + +vec4 apply_gamma(vec4 ic, float g) { + float new_r = pow(ic.r, g); + float new_g = pow(ic.g, g); + float new_b = pow(ic.b, g); + + return vec4(new_r, new_g, new_b, ic.a); +} + +vec4 apply_saturation(vec4 color, float value) { + const vec3 luminosity_factor = vec3(0.2126, 0.7152, 0.0722); + vec3 grayscale = vec3(dot(color.rgb, luminosity_factor)); + + return vec4(mix(grayscale, color.rgb, 1.0 + value), color.a); +} + +vec4 apply_contrast(vec4 color, float value) { + return vec4(0.5 + (1.0 + value) * (color.rgb - 0.5), color.a); +} + +vec4 apply_brightness(vec4 color, float value) { + return vec4(color.rgb + value, color.a); +} + +vec4 apply_exposure(vec4 color, float value) { + return vec4((1.0 + value) * color.rgb, color.a); +} + +vec4 apply_tonal(vec4 color) { + return apply_gamma( + apply_saturation( + apply_contrast( + apply_brightness(color, k_brightness), + k_contrast + ), + k_saturation + ), + k_gamma + ); +} +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} +highp float decode_f32(highp vec4 rgba) { + highp float Sign = 1.0 - step(128.0,rgba[0])*2.0; + highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + if (abs(Exponent + 127.0) < 1e-3) { + return 0.0; + } + highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + +int decode_i32(vec4 rgba) { + int r = int(rgba.r * 255.0 + 0.5); + int g = int(rgba.g * 255.0 + 0.5); + int b = int(rgba.b * 255.0 + 0.5); + int a = int(rgba.a * 255.0 + 0.5); + + int value = (r << 24) | (g << 16) | (b << 8) | a; // Combine into a 16-bit integer + + return value; +} + +int decode_i16(vec2 rg) { + int r = int(rg.r * 255.0 + 0.5); + int g = int(rg.g * 255.0 + 0.5); + + int value = (r << 8) | g; // Combine into a 16-bit integer + + if (value >= 32768) { + value -= 65536; + } + + return value; +} + +uint decode_u8(float r) { + uint value = uint(r * 255.0 + 0.5); + return value; +} + + + + +vec4 uvw2c_r(vec3 uv) { + vec2 va = texture(tex, uv).ra; + + va.x = transfer_func(H, va.x, min_value, max_value); + + va.x = mix(va.x, 1.0 - va.x, reversed); + + vec4 c = colormap_f(va.x); + return apply_tonal(c); +} + +vec4 uvw2c_rgba(vec3 uv) { + vec4 c = texture(tex, uv).rgba; + + c.r = transfer_func(H, c.r, min_value, max_value); + c.g = transfer_func(H, c.g, min_value, max_value); + c.b = transfer_func(H, c.b, min_value, max_value); + + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 uvw2c_ra(vec3 uv) { + vec2 c = texture(tex, uv).rg; + + c.r = transfer_func(H, c.r, min_value, max_value); + + c.r = mix(c.r, 1.0 - c.r, reversed); + + vec3 color = colormap_f(c.r).rgb; + + return apply_tonal(vec4(color, c.g)); +} + +vec4 uvw2cmap_rgba(vec3 uv) { + float v = texture(tex, uv).r; + v = transfer_func(H, v, min_value, max_value); + vec4 c = colormap_f(v); + c.rgb = mix(c.rgb, 1.0 - c.rgb, reversed); + + return apply_tonal(c); +} + +vec4 val2c_f32(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x))); + return apply_tonal(new_color); +} + +vec4 val2c(float x) { + float alpha = x * scale + offset; + alpha = transfer_func(H, alpha, min_value, max_value); + + alpha = mix(alpha, 1.0 - alpha, reversed); + + vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank)); + return apply_tonal(new_color); +} + +vec4 uvw2c_f32(vec3 uv) { + float val = decode_f32(texture(tex, uv).rgba*255.0); + return val2c_f32(val); +} + +vec4 uvw2c_i32(vec3 uv) { + float val = float(decode_i32(texture(tex, uv).rgba)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return mix(val2c(val), vec4(0.0), float(val == -1.0)); +} + +vec4 uvw2c_u8(vec3 uv) { + float val = float(decode_u8(texture(tex, uv).r)); + return val2c(val); +} + +uniform float opacity; + +void main() { + vec3 uv0 = frag_uv_start; + vec3 uv1 = frag_uv_end; + uv0.y = 1.0 - uv0.y; + uv1.y = 1.0 - uv1.y; + + vec4 color_start = uvw2c_u8(uv0); + vec4 color_end = uvw2c_u8(uv1); + + out_frag_color = mix(color_start, color_end, frag_blending_factor); + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"catalogs_tan.vert", + r#"#version 300 es +precision lowp float; + +layout (location = 0) in vec2 offset; +layout (location = 1) in vec2 uv; +layout (location = 2) in vec3 center; + +uniform float current_time; +uniform mat3 inv_model; + +uniform vec2 ndc_to_clip; +uniform float czf; +uniform vec2 kernel_size; + +out vec2 out_uv; +out vec3 out_p; + +const float PI = 3.141592653589793; +const float SQRT_2 = 1.41421356237309504880168872420969808; + +vec2 w2c_sin(vec3 p) { + vec2 q = vec2(-p.x, p.y); + return p.z >= 0.f ? q : normalize(q); +} + +vec2 w2c_sin_no_backface(vec3 p) { + return vec2(-p.x, p.y); +} + +vec2 w2c_ait(vec3 p) { + float r = length(p.zx); + float w = sqrt((r * (r + p.z)) * 0.5f); // = cos(b) cos(l/2) + w = sqrt((1.0 + w) * 0.5f); // = 1 / gamma + float y2d = p.y / w; + + float x2d = 0.0; + if (abs(p.x) < 5e-3) { + float x_over_r = p.x/r; + x2d = -p.x * (1.0 - x_over_r*x_over_r/21.0) / w; + } else { + w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) + x2d = sign(-p.x) * w; + } + + return vec2(x2d * 0.5, y2d) / SQRT_2; +} +const float eps = 1.25e-8; +const int n_iter = 100; + +float newton_solve(float z) { + float cte = PI * z; + float x = 2.0 * asin(z); + float f = x + sin(x) - cte; + int i = 0; + while (abs(f) > eps && i < n_iter) { + x -= f / (1.0 + cos(x)); + f = x + sin(x) - cte; + i += 1; + } + + return 0.5 * x; +} + +vec2 w2c_mol(vec3 p) { + float g = newton_solve(p.y); + + float sg = sin(g); + float cg = cos(g); + return vec2((atan(-p.x, p.z) * cg) / PI, sg); +} +vec2 w2c_tan(vec3 p) { + p.z = max(p.z, 1e-2); + return vec2(-p.x, p.y) / (p.z*PI); +} +vec2 w2c_stg(vec3 p) { + float w = (1.0 + p.z) * 0.5; + return vec2(-p.x, p.y) / (PI * w); +} +vec2 w2c_zea(vec3 p) { + float w = sqrt(0.5 + 0.5 * p.z); // <=> sqrt[(1 + x) / 2] + return vec2(-p.x, p.y) * 0.5 / w; +} +vec2 w2c_mer(vec3 p) { + return vec2(atan(-p.x, p.z), atanh(p.y)) / PI; +} + +vec3 lonlat2xyz(vec2 lonlat) { + float t = lonlat.x; + float tc = cos(t); + float ts = sin(t); + + float d = lonlat.y; + float dc = cos(d); + float ds = sin(d); + + return vec3(dc * ts, ds, dc * tc); +} + +uniform int u_proj; + +vec2 proj(vec3 p) { + if (u_proj == 0) { + return w2c_tan(p); + } else if (u_proj == 1) { + return w2c_stg(p); + } else if (u_proj == 2) { + return w2c_sin(p); + } else if (u_proj == 3) { + return w2c_zea(p); + } else if (u_proj == 4) { + return w2c_ait(p); + } else if (u_proj == 5) { + return w2c_mol(p); + } else { + return w2c_mer(p); + } +} + + +void main() { + vec3 p = inv_model * center; + + vec2 center_pos_clip_space = world2clip_gnomonic(p); + + vec2 pos_clip_space = center_pos_clip_space; + gl_Position = vec4((pos_clip_space / (ndc_to_clip * czf)) + offset * kernel_size , 0.f, 1.f); + + out_uv = uv; + out_p = p; +}"#, + ); + out.insert( + r"hips_raytracer_rgba.frag", r#"#version 300 es precision lowp float; precision lowp sampler2DArray; @@ -7513,8 +7515,8 @@ precision mediump int; uniform sampler2DArray tex; -in vec3 frag_pos; in vec2 out_clip_pos; +in vec3 frag_pos; out vec4 out_frag_color; struct Tile { @@ -7526,8 +7528,6 @@ struct Tile { uniform Tile textures_tiles[12]; -uniform float opacity; - uniform float scale; uniform float offset; uniform float blank; @@ -7888,15 +7888,15 @@ vec3 xyz2uv(vec3 xyz) { return vec3(offset, float(tile.texture_idx)); } +uniform float opacity; +uniform vec4 no_tile_color; + void main() { vec3 uv = xyz2uv(normalize(frag_pos)); - - uv.y = 1.0 - uv.y; - vec4 c = uvw2c_i16(uv); - + vec4 c = uvw2c_rgba(uv); out_frag_color = c; - out_frag_color.a = out_frag_color.a * opacity; + out_frag_color = vec4(c.rgb, opacity * c.a); }"#, ); out diff --git a/src/css/aladin.css b/src/css/aladin.css index cbde36fa..d07c1a6d 100644 --- a/src/css/aladin.css +++ b/src/css/aladin.css @@ -3,7 +3,7 @@ border: 0px solid #ddd; /* SVG inside divs add a 4px height: https://stackoverflow.com/questions/75751593/why-there-is-additional-4px-height-for-div-when-there-is-svg-inside-it */ /* disable x swipe on chrome, firefox */ - /* see. https://stackoverflow.com/questions/30636930/disable-web-page-navigation-on-swipeback-and-forward */ + /* Prevent page navigation swipe on windows phone: https://stackoverflow.com/questions/30636930/disable-web-page-navigation-on-swipeback-and-forward */ overscroll-behavior-x: none; overscroll-behavior-y: none; /* Prevents vertical pull-to-refresh */ /* Hide the draggable boxes that goes out of the view */ @@ -15,7 +15,7 @@ font-size: 0.9rem; /* Aladin lite default color */ - --aladin-color: #b232b2; + --aladin-color: #ff54ff; } [data-theme="dark"] { @@ -114,27 +114,30 @@ background: black; } -.aladin-lite-spectra-displayer .aladin-spectra-unit-selector { +.aladin-spectra-displayer { position: absolute; - bottom: 3rem; - right: 0; + left: 50%; + transform: translateX(-50%); + bottom: 0; + overflow: hidden; + width: 100%; } -.aladin-lite-spectra-displayer .aladin-spectra-home { + +.aladin-spectra-displayer .aladin-spectra-unit { position: absolute; - bottom: 5rem; - right: 0; + bottom: 0; + right: 8rem; } -.aladin-lite-spectra-displayer .aladin-spectra-extraction { +.aladin-spectra-displayer .aladin-spectra-home { + position: absolute; + bottom: 0; + right: 6rem; +} +.aladin-spectra-displayer .aladin-spectra-extraction { position: absolute; bottom: 7rem; right: 0; } -.aladin-lite-spectra-displayer .aladin-spectra-hips-selector { - position: absolute; - top: 0; - left: 0; - max-width: 10rem; -} .aladin-imageCanvas { position: absolute; @@ -1302,14 +1305,21 @@ otherwise it fits its content options. If those are too big the select can go ou line-height: 1.7rem; } -.aladin-fov .aladin-zoom-out { - margin-right: 0; - border-right: none; - border-radius: 0.4rem 0px 0px 0.4rem; +.aladin-zoom { + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); } -.aladin-fov .aladin-zoom-in { - border-radius: 0px 0.4rem 0.4rem 0px; +.aladin-zoom-out { + margin-right: 0; + border-top: none; + border-radius: 0 0 0.4rem 0.4rem; +} + +.aladin-zoom-in { + border-radius: 0.4rem 0.4rem 0 0; } .aladin-status-bar { @@ -1348,6 +1358,7 @@ otherwise it fits its content options. If those are too big the select can go ou position: absolute; margin-top: 3rem; margin-bottom: 5rem; + left: 0.2rem; } .aladin-widgets-toolbar > * { diff --git a/src/js/Aladin.js b/src/js/Aladin.js index ffda5223..853f9335 100644 --- a/src/js/Aladin.js +++ b/src/js/Aladin.js @@ -407,7 +407,6 @@ export let Aladin = (function () { // Aladin logo new AladinLogo(this.aladinDiv); - this.reticle = new Reticle(this.options, this); this.popup = new Popup(this.aladinDiv, this.view); this.tooltip = document.createElement('div') @@ -453,7 +452,7 @@ export let Aladin = (function () { } // Format the hipslist given by the user before storing it in the aladin instance - this.hipsFavorites = []; + this.layerFavorites = []; let hipsList = [].concat(options.hipsList); for (var hips of hipsList) { @@ -498,9 +497,11 @@ export let Aladin = (function () { hipsObj["name"] = name; } + hipsObj["type"] = "hips"; + // Merge what is already in the cache for that HiPS with new properties // coming from the MOCServer - this.hipsFavorites.push(hipsObj); + this.layerFavorites.push(hipsObj); // Favorites are also directly pushed to the cache this.hipsCache.append(hipsObj.id, hipsObj) } @@ -511,7 +512,7 @@ export let Aladin = (function () { this._setupUI(options); - ALEvent.FAVORITE_HIPS_LIST_UPDATED.dispatchedTo(document, this.hipsFavorites); + ALEvent.FAVORITE_LAYERS_LIST_UPDATED.dispatchedTo(document, this.layerFavorites); if (options.survey) { if (Array.isArray(options.survey)) { @@ -759,9 +760,6 @@ export let Aladin = (function () { /**** CONSTANTS ****/ Aladin.VERSION = version; - Aladin.JSONP_PROXY = "https://alaskybis.cds.unistra.fr/cgi/JSONProxy"; - //Aladin.JSONP_PROXY = "https://alaskybis.unistra.fr/cgi/JSONProxy"; - Aladin.URL_PREVIEWER = "https://aladin.cds.unistra.fr/AladinLite/"; // access to WASM libraries @@ -804,10 +802,10 @@ export let Aladin = (function () { showCatalog: true, // TODO: still used ?? fullScreen: false, - reticleColor: "rgb(178, 50, 178)", + reticleColor: "#ff54ff", reticleSize: 22, - gridColor: "rgb(178, 50, 178)", - gridOpacity: 0.8, + gridColor: "#ff54ff", + gridOpacity: 1.0, gridOptions: { enabled: false, showLabels: true, @@ -1751,7 +1749,7 @@ export let Aladin = (function () { maxOrder, options ) { - let hipsOptions = { id, name, maxOrder, url, cooFrame, ...options }; + let hipsOptions = { id, name, maxOrder, url, cooFrame, type: "hips", ...options }; let hips = new HiPS(id, url || id, hipsOptions) // A HiPS can be refered by its unique ID thus we add it to the cache (cf excample/al-cfht.html that refers to HiPS object just by their unique ID) @@ -1787,7 +1785,7 @@ export let Aladin = (function () { * Remove a HiPS from the list of favorites. * * This send a event of type - * FAVORITE_HIPS_LIST_UPDATED which can be listened to + * FAVORITE_LAYERS_LIST_UPDATED which can be listened to * * @throws A warning when the asset is currently present in the view * @@ -1807,7 +1805,7 @@ export let Aladin = (function () { } // find the index of the hips to remove - const idx = this.hipsFavorites.findIndex((hipsObj) => { + const idx = this.layerFavorites.findIndex((hipsObj) => { if (typeof hips !== "string") { return hipsObj.name == hips.name || hipsObj.id == hips.id || hipsObj.url == hips.url; } else { @@ -1817,9 +1815,9 @@ export let Aladin = (function () { // a hips matches if (idx >= 0) { - this.hipsFavorites.splice(idx, 1); + this.layerFavorites.splice(idx, 1); // Send a change of favorites for the UI selector to adapt their optional list - ALEvent.FAVORITE_HIPS_LIST_UPDATED.dispatchedTo(document, this.hipsFavorites); + ALEvent.FAVORITE_LAYERS_LIST_UPDATED.dispatchedTo(document, this.layerFavorites); } } @@ -1827,19 +1825,19 @@ export let Aladin = (function () { * Add a HiPS to the list of favorites. * * If already present it will not add it again. This send a event of type - * FAVORITE_HIPS_LIST_UPDATED which can be listened to. Once added, the favorite list + * FAVORITE_LAYERS_LIST_UPDATED which can be listened to. Once added, the favorite list * will be sorted by the name of the hips. * * @memberof Aladin - * @param {HiPS} hips - The HiPS to add to the favorites + * @param {HiPS|Image} layer - The HiPS to add to the favorites */ - Aladin.prototype.addHiPSToFavorites = function(hips) { + Aladin.prototype.addHiPSToFavorites = function(layer) { // find the index of the hips to remove - const idx = this.hipsFavorites.findIndex((hipsObj) => { - if (typeof hips !== "string") { - return hipsObj.name == hips.name || hipsObj.id == hips.id || hipsObj.url == hips.url; + const idx = this.layerFavorites.findIndex((obj) => { + if (typeof layer !== "string") { + return obj.id == layer.id; } else { - return hipsObj.name == hips || hipsObj.id == hips || hipsObj.url == hips; + return obj.id == layer; } }) @@ -1849,14 +1847,14 @@ export let Aladin = (function () { } // add the new favorite HiPS - this.hipsFavorites.push({ - url: hips.url, - id: hips.id, - name: hips.name, + this.layerFavorites.push({ + url: layer.url, + id: layer.id, + name: layer.name, }) // send the final event - ALEvent.FAVORITE_HIPS_LIST_UPDATED.dispatchedTo(document, this.hipsFavorites); + ALEvent.FAVORITE_LAYERS_LIST_UPDATED.dispatchedTo(document, this.layerFavorites); } /** @@ -1982,6 +1980,11 @@ export let Aladin = (function () { Aladin.prototype.addNewImageLayer = function (survey = "P/DSS2/color") { let layerName = Utils.uuidv4(); return this.setOverlayImageLayer(survey, layerName); + //let newHiPS = A.HiPS(survey); + //newHiPS.id = this.hipsCache.makeUniqLayerName(newHiPS.id); + //newHiPS.name = newHiPS.id; + + //return this.setOverlayImageLayer(newHiPS, layerName); }; /** @@ -2080,7 +2083,14 @@ export let Aladin = (function () { * */ Aladin.prototype.setBaseImageLayer = function (urlOrHiPSOrFITS) { - return this.setOverlayImageLayer(urlOrHiPSOrFITS, (this.view.overlayLayers && this.view.overlayLayers[0]) || Utils.uuidv4()); + /*if (this.view._waitsForLayer()) { + // delay this call + this.view.delayedBaseLayerCalledParams = urlOrHiPSOrFITS; + return; + }*/ + let firstLayer = this.view.getFirstLayer(); + + return this.setOverlayImageLayer(urlOrHiPSOrFITS, firstLayer || Utils.uuidv4()); }; /** @@ -2090,7 +2100,9 @@ export let Aladin = (function () { * @returns {HiPS|Image} - Returns the image layer corresponding to the base layer */ Aladin.prototype.getBaseImageLayer = function () { - return this.view.getImageLayer(this.view.overlayLayers && this.view.overlayLayers[0]); + let firstLayer = this.view.getFirstLayer(); + + return this.view.getImageLayer(firstLayer); }; /** @@ -2108,7 +2120,7 @@ export let Aladin = (function () { * on top the 'base' layer. If the layer is already present in the view, it will be replaced by the new HiPS/FITS image given here. */ Aladin.prototype.setOverlayImageLayer = function ( - urlOrHiPSOrFITS, + urlOrHiPSOrImage, layer = "overlay" ) { let imageLayer; @@ -2116,14 +2128,17 @@ export let Aladin = (function () { let hipsCache = this.hipsCache; // 1. User gives an ID - if (typeof urlOrHiPSOrFITS === "string") { - const idOrUrl = urlOrHiPSOrFITS; + if (typeof urlOrHiPSOrImage === "string") { + const idOrUrl = urlOrHiPSOrImage; // many cases here // 1/ It has been already added to the cache let cachedOptions = hipsCache.get(idOrUrl) - if (cachedOptions) { - imageLayer = A.HiPS(idOrUrl, cachedOptions); + if (cachedOptions.type === "hips") { + imageLayer = A.HiPS(idOrUrl, cachedOptions) + } else if (cachedOptions.type === "image") { + imageLayer = A.image(idOrUrl, cachedOptions); + } } else { // 2/ Not in the cache, then we create the hips from this url/id and // go to the case 3 @@ -2133,17 +2148,20 @@ export let Aladin = (function () { } } else { // 3/ It is an image survey. - imageLayer = urlOrHiPSOrFITS; + imageLayer = urlOrHiPSOrImage; - if (imageLayer instanceof HiPS) { - let cachedLayerOptions = hipsCache.get(imageLayer.id) + let cachedLayerOptions = hipsCache.get(imageLayer.id) - if (!cachedLayerOptions) { - hipsCache.append(imageLayer.id, imageLayer.options) - } else { - // Set the image layer object with the options from the cache. - imageLayer.setOptions(cachedLayerOptions) + if (!cachedLayerOptions) { + let type = "hips"; + if (imageLayer instanceof Image) { + type = "image"; } + + hipsCache.append(imageLayer.id, {...imageLayer.options, type}) + } else { + // Set the image layer object with the options from the cache. + imageLayer.setOptions(cachedLayerOptions) } } @@ -2777,18 +2795,18 @@ export let Aladin = (function () { */ Aladin.prototype.pix2world = function (x, y, frame) { if (frame) { - frame = CooFrameEnum.fromString(frame, CooFrameEnum.ICRS); + if (typeof frame === "string") { + frame = CooFrameEnum.fromString(frame, CooFrameEnum.ICRS); + } + + // Map to the numeric wasm-bindgen CooSystem discriminant + frame = (frame.system === CooFrameEnum.SYSTEMS.GAL) + ? Aladin.wasmLibs.core.CooSystem.GAL + : Aladin.wasmLibs.core.CooSystem.ICRS; } + let [lon, lat] = this.view.wasm.pix2world(x, y, frame); - let lonlat = this.view.wasm.pix2world(x, y, frame && frame.system); - - let [lon, lat] = lonlat; - - if (lon < 0) { - return [lon + 360.0, lat]; - } - - return [lon, lat]; + return [lon < 0 ? lon + 360.0 : lon, lat]; }; /** @@ -2805,17 +2823,17 @@ export let Aladin = (function () { */ Aladin.prototype.world2pix = function (lon, lat, frame) { if (frame) { - if (frame instanceof string) { + if (typeof frame === "string") { frame = CooFrameEnum.fromString(frame, CooFrameEnum.ICRS); } - if (frame.label == CooFrameEnum.SYSTEMS.GAL) { - frame = Aladin.wasmLibs.core.CooSystem.GAL; - } - else { - frame = Aladin.wasmLibs.core.CooSystem.ICRS; - } + // Map to the numeric wasm-bindgen CooSystem discriminant + frame = (frame.system === CooFrameEnum.SYSTEMS.GAL) + ? Aladin.wasmLibs.core.CooSystem.GAL + : Aladin.wasmLibs.core.CooSystem.ICRS; } + // frame === undefined/null → passed as-is, WASM treats as None (default system) + return this.view.wasm.world2pix(lon, lat, frame); }; @@ -2891,10 +2909,7 @@ export let Aladin = (function () { */ Aladin.prototype.getFov = function () { var fovX = this.view.fov; - var s = this.getSize(); - - var fovY = (s[1] / s[0]) * fovX; - fovY = Math.min(fovY, 180); + var fovY = this.view.fovY; return [fovX, fovY]; }; diff --git a/src/js/Catalog.js b/src/js/Catalog.js index d0731f18..68f0a1ec 100644 --- a/src/js/Catalog.js +++ b/src/js/Catalog.js @@ -517,7 +517,8 @@ export let Catalog = (function () { var cacheMarkerCtx = this.cacheMarkerCanvas.getContext("2d"); cacheMarkerCtx.fillStyle = this.color; cacheMarkerCtx.beginPath(); - var half = this.markerSize / 2; + var half = Math.max(this.markerSize / 2, 2.0); + cacheMarkerCtx.arc(half, half, half - 2, 0, 2 * Math.PI, false); cacheMarkerCtx.fill(); cacheMarkerCtx.lineWidth = 2; diff --git a/src/js/ColorCfg.js b/src/js/ColorCfg.js deleted file mode 100644 index f8833510..00000000 --- a/src/js/ColorCfg.js +++ /dev/null @@ -1,291 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright 2013 - UDS/CNRS -// The Aladin Lite program is distributed under the terms -// of the GNU Lesser General Public License version 3 -// or (at your option) any later version. -// -// This file is part of Aladin Lite. -// -// Aladin Lite is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Aladin Lite is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Aladin Lite. If not, see . -// - -/****************************************************************************** - * Aladin Lite project - * - * File ColorCfg - * - * Authors: Thomas Boch & Matthieu Baumann [CDS] - * - *****************************************************************************/ - export let ColorCfg = (function() { - /** Constructor - * cooFrame and maxOrder can be set to null - * They will be determined by reading the properties file - */ - function ColorCfg(options) { - // Opacity of the survey/image - this.opacity = (options && options.opacity) || 1.0; - - // Colormap config options - this.colormap = (options && options.colormap) || "native"; - this.colormap = this.colormap.toLowerCase(); - - this.stretch = (options && options.stretch) || "linear"; - this.stretch = this.stretch.toLowerCase(); - this.reversed = false; - // Keep the image tile format because we want the cuts - this.imgFormat = options.imgFormat || 'png'; - - if (options && options.reversed === true) { - this.reversed = true; - } - - this.minCut = { - webp: 0.0, - jpeg: 0.0, - png: 0.0, - fits: undefined // wait the default value coming from the properties - }; - if (options && Number.isFinite(options.minCut)) { - this.minCut[this.imgFormat] = options.minCut; - } - - this.maxCut = { - webp: 255.0, - jpeg: 255.0, - png: 255.0, - fits: undefined // wait the default value coming from the properties - }; - if (options && Number.isFinite(options.maxCut)) { - this.maxCut[this.imgFormat] = options.maxCut; - } - - this.additiveBlending = options && options.additive; - if (this.additiveBlending === undefined) { - this.additiveBlending = false; - } - - // A default value for gamma correction - this.kGamma = (options && options.gamma) || 1.0; - this.kSaturation = (options && options.saturation) || 0.0; - this.kBrightness = (options && options.brightness) || 0.0; - this.kContrast = (options && options.contrast) || 0.0; - }; - - ColorCfg.prototype.get = function() { - let blend = { - srcColorFactor: 'SrcAlpha', - dstColorFactor: 'OneMinusSrcAlpha', - func: 'FuncAdd' - }; - - if (this.additiveBlending) { - blend = { - srcColorFactor: 'SrcAlpha', - dstColorFactor: 'One', - func: 'FuncAdd' - } - } - - let minCut = this.minCut[this.imgFormat] - if (this.imgFormat !== "fits") { - minCut /= 255.0 - } - - let maxCut = this.maxCut[this.imgFormat] - if (this.imgFormat !== "fits") { - maxCut /= 255.0 - } - - // Reset the whole meta object - return { - blendCfg: blend, - opacity: this.opacity, - color: { - // Tonal corrections constants - kGamma: this.kGamma, - kSaturation: this.kSaturation, - kBrightness: this.kBrightness, - kContrast: this.kContrast, - - stretch: this.stretch, - minCut, - maxCut, - reversed: this.reversed, - cmapName: this.colormap, - } - }; - } - - ColorCfg.prototype.setOptions = function(options) { - // Update the imgFormat - this.imgFormat = options.imgFormat || this.imgFormat; - - this.setColormap(options.colormap, options) - - this.setCuts(options.minCut, options.maxCut, options.cutFormat) - - this.setBrightness(options.brightness) - this.setSaturation(options.saturation) - this.setContrast(options.contrast) - - this.setGamma(options.gamma) - - this.setOpacity(options.opacity) - - this.setBlendingConfig(options.additive) - } - - // @api - ColorCfg.prototype.setBrightness = function(kBrightness) { - if (kBrightness == null || kBrightness == undefined) - return; - - kBrightness = +kBrightness || 0.0; // coerce to number - this.kBrightness = Math.max(-1, Math.min(kBrightness, 1)); - }; - - // @api - ColorCfg.prototype.getBrightness = function() { - return this.kBrightness; - }; - - // @api - ColorCfg.prototype.setContrast = function(kContrast) { - if (kContrast == null || kContrast == undefined) - return; - - kContrast = +kContrast || 0.0; // coerce to number - this.kContrast = Math.max(-1, Math.min(kContrast, 1)); - }; - - // @api - ColorCfg.prototype.getContrast = function() { - return this.kContrast; - }; - - // @api - ColorCfg.prototype.setSaturation = function(kSaturation) { - if (kSaturation == null || kSaturation == undefined) - return; - - kSaturation = +kSaturation || 0.0; // coerce to number - - this.kSaturation = Math.max(-1, Math.min(kSaturation, 1)); - }; - - // @api - ColorCfg.prototype.getSaturation = function() { - return this.kSaturation; - }; - - // @api - ColorCfg.prototype.setGamma = function(gamma) { - if (gamma == null || gamma == undefined) - return; - - gamma = +gamma; // coerce to number - this.kGamma = Math.max(0.1, Math.min(gamma, 10)); - }; - - // @api - ColorCfg.prototype.getGamma = function() { - return this.kGamma; - }; - - // @api - ColorCfg.prototype.setOpacity = function(opacity) { - if (opacity == null || opacity == undefined) - return; - - opacity = +opacity; // coerce to number - this.opacity = Math.max(0, Math.min(opacity, 1)); - }; - - // @oldapi - ColorCfg.prototype.setAlpha = ColorCfg.prototype.setOpacity; - - // @api - ColorCfg.prototype.getOpacity = function() { - return this.opacity; - }; - - // @api - ColorCfg.prototype.getAlpha = ColorCfg.prototype.getOpacity; - - // @api - ColorCfg.prototype.setBlendingConfig = function(additive) { - if (additive === null || additive === undefined) - return; - - this.additiveBlending = additive; - }; - - ColorCfg.prototype.getBlendingConfig = function() { - return this.additiveBlending; - }; - - // @api - // Optional arguments, - ColorCfg.prototype.setColormap = function(colormap, options) { - /// colormap - this.colormap = (colormap && colormap.toLowerCase()) || this.colormap; - - /// stretch - let stretch = (options && options.stretch) || this.stretch || "linear"; - this.stretch = stretch.toLowerCase() - - /// reversed - if (options && options.reversed !== undefined) { - this.reversed = options.reversed; - } - } - - // @api - ColorCfg.prototype.getColormap = function() { - return this.colormap; - }; - - ColorCfg.prototype.getReversed = function() { - return this.reversed; - }; - - // Sets the cuts for the current image format - ColorCfg.prototype.setCuts = function(minCut, maxCut, imgFormat) { - imgFormat = imgFormat || this.imgFormat; - - if (minCut instanceof Object) { - // Mincut is given in the form of an javascript object with all the formats - this.minCut = {...this.minCut, ...minCut}; - } else if (minCut !== null && minCut !== undefined) { - this.minCut[imgFormat] = minCut; - } - - if (maxCut instanceof Object) { - this.maxCut = {...this.maxCut, ...maxCut}; - } else if (maxCut !== null && maxCut !== undefined) { - this.maxCut[imgFormat] = maxCut; - } - }; - - // Returns the cuts for the current image format - ColorCfg.prototype.getCuts = function() { - return [ - this.minCut[this.imgFormat], - this.maxCut[this.imgFormat] - ]; - }; - - return ColorCfg; - })(); diff --git a/src/js/DefaultHiPSList.js b/src/js/DefaultHiPSList.js index 0458e2e4..d6cb1bb1 100644 --- a/src/js/DefaultHiPSList.js +++ b/src/js/DefaultHiPSList.js @@ -20,6 +20,8 @@ // along with Aladin Lite. If not, see . // +import { DataproductType } from "../core/pkg/core"; + export let HiPSList = (function () { function HiPSList() {} @@ -33,6 +35,7 @@ export let HiPSList = (function () { imgFormat: "jpeg", cooFrame: "equatorial", startUrl: "https://alasky.cds.unistra.fr/DSS/DSSColor", + dataproductType: "image", }, { creatorDid: "ivo://erosita/dr1/rate/rgb", @@ -42,7 +45,8 @@ export let HiPSList = (function () { tileSize: 512, imgFormat: "png", cooFrame: "equatorial", - startUrl: "https://erosita.mpe.mpg.de/dr1/erodat/static/hips/eRASS1_RGB_Rate_c010/" + startUrl: "https://erosita.mpe.mpg.de/dr1/erodat/static/hips/eRASS1_RGB_Rate_c010/", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/2MASS/color", @@ -53,6 +57,7 @@ export let HiPSList = (function () { imgFormat: "jpeg", cooFrame: "equatorial", startUrl: "https://alaskybis.cds.unistra.fr/2MASS/Color", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/DSS2/red", @@ -64,12 +69,13 @@ export let HiPSList = (function () { cooFrame: "equatorial", numBitsPerPixel: 16, // options - minCut: 1000.0, - maxCut: 10000.0, + minCut: {fits: 1000.0, jpeg: 0.0, png: 0.0}, + maxCut: {fits: 10000.0, jpeg: 255.0, png: 255.0}, colormap: "magma", stretch: "Linear", imgFormat: "fits", startUrl: "https://alaskybis.cds.unistra.fr/DSS/DSS2Merged", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/DM/I/350/gaiaedr3", @@ -79,12 +85,13 @@ export let HiPSList = (function () { tileSize: 512, numBitsPerPixel: -32, cooFrame: "equatorial", - minCut: 0, - maxCut: 12000, + minCut: {fits: 0.0, jpeg: 0.0, png: 0.0}, + maxCut: {fits: 12000.0, jpeg: 255.0, png: 255.0}, stretch: "asinh", colormap: "rdylbu", imgFormat: "fits", startUrl: "https://alaskybis.cds.unistra.fr/ancillary/GaiaEDR3/density-map", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/PanSTARRS/DR1/g", @@ -96,11 +103,12 @@ export let HiPSList = (function () { cooFrame: "equatorial", numBitsPerPixel: -32, // options - minCut: -34, - maxCut: 7000, + minCut: {fits: -34.0, jpeg: 0.0, png: 0.0}, + maxCut: {fits: 7000.0, jpeg: 255.0, png: 255.0}, stretch: "asinh", colormap: "redtemperature", - startUrl: "https://alasky.cds.unistra.fr/Pan-STARRS/DR1/g" + startUrl: "https://alasky.cds.unistra.fr/Pan-STARRS/DR1/g", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/PanSTARRS/DR1/color-z-zg-g", @@ -111,6 +119,7 @@ export let HiPSList = (function () { imgFormat: "jpeg", cooFrame: "equatorial", startUrl: "https://alasky.cds.unistra.fr/Pan-STARRS/DR1/color-z-zg-g", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/DECaPS/DR2/color", @@ -120,7 +129,8 @@ export let HiPSList = (function () { cooFrame: "equatorial", tileSize: 512, imgFormat: "png", - startUrl: "https://alasky.cds.unistra.fr/DECaPS/DR2/CDS_P_DECaPS_DR2_color" + startUrl: "https://alasky.cds.unistra.fr/DECaPS/DR2/CDS_P_DECaPS_DR2_color", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/Fermi/color", @@ -131,6 +141,7 @@ export let HiPSList = (function () { tileSize: 512, cooFrame: "equatorial", startUrl: "https://alasky.cds.unistra.fr/Fermi/Color", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/GALEXGR6_7/NUV", @@ -140,7 +151,8 @@ export let HiPSList = (function () { imgFormat: "png", tileSize: 512, cooFrame: "equatorial", - startUrl: "https://alasky.cds.unistra.fr/GALEX/GALEXGR6_7_NUV" + startUrl: "https://alasky.cds.unistra.fr/GALEX/GALEXGR6_7_NUV", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/IRIS/color", @@ -151,6 +163,7 @@ export let HiPSList = (function () { imgFormat: "jpeg", cooFrame: "galactic", startUrl: "https://alasky.cds.unistra.fr/IRISColor", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/Mellinger/color", @@ -160,7 +173,8 @@ export let HiPSList = (function () { tileSize: 512, imgFormat: "jpeg", cooFrame: "galactic", - startUrl: "https://alasky.cds.unistra.fr/MellingerRGB" + startUrl: "https://alasky.cds.unistra.fr/MellingerRGB", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/SDSS9/color", @@ -170,7 +184,8 @@ export let HiPSList = (function () { tileSize: 512, imgFormat: "jpeg", cooFrame: "equatorial", - startUrl: "https://alasky.cds.unistra.fr/SDSS/DR9/color" + startUrl: "https://alasky.cds.unistra.fr/SDSS/DR9/color", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/SPITZER/color", @@ -180,7 +195,8 @@ export let HiPSList = (function () { tileSize: 512, imgFormat: "jpeg", cooFrame: "galactic", - startUrl: "https://alasky.cds.unistra.fr/Spitzer/SpitzerI1I2I4color" + startUrl: "https://alasky.cds.unistra.fr/Spitzer/SpitzerI1I2I4color", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/allWISE/color", @@ -190,7 +206,8 @@ export let HiPSList = (function () { tileSize: 512, imgFormat: "jpeg", cooFrame: "equatorial", - startUrl: "https://alaskybis.cds.unistra.fr/AllWISE/RGB-W4-W2-W1" + startUrl: "https://alaskybis.cds.unistra.fr/AllWISE/RGB-W4-W2-W1", + dataproductType: "image", }, { creatorDid: "ivo://CDS/P/SDSS9/g", @@ -201,31 +218,34 @@ export let HiPSList = (function () { numBitsPerPixel: 16, imgFormat: "fits", cooFrame: "equatorial", - minCut: 0, - maxCut: 1.8, + minCut: {fits: 0.0, jpeg: 0.0, png: 0.0}, + maxCut: {fits: 1.8, jpeg: 255.0, png: 255.0}, stretch: "linear", colormap: "redtemperature", - startUrl: "https://alasky.cds.unistra.fr/SDSS/DR9/band-g" + startUrl: "https://alasky.cds.unistra.fr/SDSS/DR9/band-g", + dataproductType: "image", }, { id: "P/Finkbeiner", name: "Halpha", maxOrder: 3, - minCut: -10, - maxCut: 800, + minCut: {fits: -10.0, jpeg: 0.0, png: 0.0}, + maxCut: {fits: 800.0, jpeg: 255.0, png: 255.0}, colormap: "rdbu", imgFormat: "fits", - startUrl: "https://alasky.cds.unistra.fr/FinkbeinerHalpha" + startUrl: "https://alasky.cds.unistra.fr/FinkbeinerHalpha", + dataproductType: "image", }, { id: "P/VTSS/Ha", name: "VTSS-Ha", maxOrder: 3, - minCut: -10.0, - maxCut: 100.0, + minCut: {fits: -10.0, jpeg: 0.0, png: 0.0}, + maxCut: {fits: 100.0, jpeg: 255.0, png: 255.0}, colormap: "grayscale", imgFormat: "fits", - startUrl: "https://alasky.cds.unistra.fr/VTSS/Ha" + startUrl: "https://alasky.cds.unistra.fr/VTSS/Ha", + dataproductType: "image", }, { id: "P/GLIMPSE360", diff --git a/src/js/HiPS.js b/src/js/HiPS.js index 3d80f91f..2a237727 100644 --- a/src/js/HiPS.js +++ b/src/js/HiPS.js @@ -30,7 +30,6 @@ * *****************************************************************************/ import { ALEvent } from "./events/ALEvent.js"; -import { ColorCfg } from "./ColorCfg.js"; import { HiPSProperties } from "./HiPSProperties.js"; import { Aladin } from "./Aladin.js"; import { CooFrameEnum } from "./CooFrameEnum.js"; @@ -59,7 +58,7 @@ PropertyParser.cooFrame = function (properties) { let cooFrame = (properties && properties.hips_body && "ICRSd") || (properties && properties.hips_frame) || - "icrs"; + "ICRS"; return cooFrame; }; @@ -80,12 +79,12 @@ PropertyParser.minOrder = function (properties) { return minOrder; }; -PropertyParser.acceptedFormats = function (properties) { - let acceptedFormats = (properties && properties.hips_tile_format) || "jpeg"; +PropertyParser.formats = function (properties) { + let formats = properties?.hips_tile_format; - acceptedFormats = acceptedFormats.split(" ").map((fmt) => fmt.toLowerCase()); + formats = formats?.split(" ").map((fmt) => fmt.toLowerCase()); - return acceptedFormats; + return formats; }; PropertyParser.initialFov = function (properties) { @@ -177,7 +176,7 @@ PropertyParser.isPlanetaryBody = function (properties) { * @property {boolean} [reversed=false] - If true, the colormap is reversed; otherwise, it is not reversed. * @property {number} [minCut] - The minimum cut value for the color configuration. If not given, 0.0 for JPEG/PNG surveys, the value of the property file for FITS surveys * @property {number} [maxCut] - The maximum cut value for the color configuration. If not given, 1.0 for JPEG/PNG surveys, the value of the property file for FITS surveys - * @property {boolean} [additive=false] - If true, additive blending is applied; otherwise, it is not applied. + * @property {boolean} [blending=false] - If true, additive blending is applied; otherwise, it is not applied. * @property {number} [gamma=1.0] - The gamma correction value for the color configuration. * @property {number} [saturation=0.0] - The saturation value for the color configuration. * @property {number} [brightness=0.0] - The brightness value for the color configuration. @@ -256,7 +255,7 @@ export let HiPS = (function () { * @constructs HiPS * * @param {string} id - Mandatory unique identifier for the layer. Can be an arbitrary name - * @param {string|FileList|HiPSLocalFiles} url - Can be: + * @param {string|FileList|HiPSLocalFiles} location - Can be: *
    *
  • An http url towards a HiPS.
  • *
  • A relative path to your HiPS
  • @@ -273,11 +272,11 @@ export let HiPS = (function () { // Unique identifier for a survey this.id = id; - this.options = options; this.name = (options && options.name) || id; - this.startUrl = options.startUrl; + this.startUrl = options && options.startUrl; this.requestMode = options && options.requestMode || 'cors'; this.requestCredentials = options && options.requestCredentials || 'same-origin'; + this.type = "hips" this.slice = 0; @@ -318,21 +317,68 @@ export let HiPS = (function () { this.url = location; - this.maxOrder = options.maxOrder; - this.minOrder = options.minOrder || 0; - this.cooFrame = CooFrameEnum.fromString(options.cooFrame, null); - this.tileSize = options.tileSize; - this.skyFraction = options.skyFraction; - this.imgFormat = options.imgFormat; - this.acceptedFormats = options.formats; - this.defaultFitsMinCut = options.defaultFitsMinCut; - this.defaultFitsMaxCut = options.defaultFitsMaxCut; - this.numBitsPerPixel = options.numBitsPerPixel; - this.creatorDid = options.creatorDid; - this.errorCallback = options.errorCallback; - this.successCallback = options.successCallback; + this.maxOrder = options && options.maxOrder; + this.minOrder = (options && options.minOrder) || 0; + this.cooFrame = options && CooFrameEnum.fromString(options.cooFrame, null)?.system; + this.tileSize = options && options.tileSize; + this.skyFraction = options && options.skyFraction; + this.imgFormat = options && options.imgFormat; + this.formats = options && options.formats; + this.defaultFitsMinCut = options && options.defaultFitsMinCut; + this.defaultFitsMaxCut = options && options.defaultFitsMaxCut; + this.numBitsPerPixel = options && options.numBitsPerPixel; + this.creatorDid = options && options.creatorDid; + this.errorCallback = options && options.errorCallback; + this.successCallback = options && options.successCallback; + this.dataproductType = options && options.dataproductType; + this.tileDepth = options && options.tileDepth; + this.orderFreq = options && options.orderFreq; + this.emMin = options && options.emMin; + this.emMax = options && options.emMax; - this.colorCfg = new ColorCfg(options); + // Opacity of the survey/image + this.opacity = (options && options.opacity) || 1.0; + + // Colormap config options + this.colormap = (options && options.colormap) || "native"; + this.colormap = this.colormap.toLowerCase(); + + this.stretch = (options && options.stretch) || "linear"; + this.stretch = this.stretch.toLowerCase(); + this.reversed = false; + // Keep the image tile format because we want the cuts + this.imgFormat = (options && options.imgFormat) || 'png'; + + if (options && options.reversed === true) { + this.reversed = true; + } + + this.minCut = { + webp: 0.0, + jpeg: 0.0, + png: 0.0, + fits: undefined // wait the default value coming from the properties + }; + + this.maxCut = { + webp: 255.0, + jpeg: 255.0, + png: 255.0, + fits: undefined // wait the default value coming from the properties + }; + + this.setCuts(options.minCut, options.maxCut); + + this.blending = options && options.blending; + if (this.blending === undefined) { + this.blending = false; + } + + // A default value for gamma correction + this.gamma = (options && options.gamma) || 1.0; + this.saturation = (options && options.saturation) || 0.0; + this.brightness = (options && options.brightness) || 0.0; + this.contrast = (options && options.contrast) || 0.0; let self = this; @@ -441,13 +487,12 @@ export let HiPS = (function () { hips_order: self.maxOrder, hips_service_url: self.url, hips_tile_width: self.tileSize, - hips_frame: self.cooFrame.label + hips_frame: self.cooFrame }) } if (self.updateHiPSCache) { - self._saveInCache(); - self.updateHiPSCache = false; + self._updateMetadata() } resolve(self); @@ -491,8 +536,8 @@ export let HiPS = (function () { // HiPS Cube special keywords self.cubeDepth = properties && properties.hips_cube_depth && +properties.hips_cube_depth; self.cubeFirstFrame = properties && properties.hips_cube_firstframe && +properties.hips_cube_firstframe; - self.emMin = properties && properties.em_min && +properties.em_min; - self.emMax = properties && properties.em_max && +properties.em_max; + self.emMin = (properties && properties.em_min && +properties.em_min) || self.emMin; + self.emMax = (properties && properties.em_max && +properties.em_max) || self.emMax; if (self.emMax < self.emMin) { let tmp = self.emMin; @@ -500,11 +545,11 @@ export let HiPS = (function () { self.emMax = tmp; } - self.hipsDataMinMax = PropertyParser.hipsDataMinmax(properties); + self.dataMinMax = PropertyParser.hipsDataMinmax(properties); // HiPS3D special keywords - self.hipsOrderFreq = properties && properties.hips_order_freq && +properties.hips_order_freq; - self.hipsTileDepth = properties && properties.hips_tile_depth && +properties.hips_tile_depth; + self.orderFreq = (properties && properties.hips_order_freq && +properties.hips_order_freq) || self.orderFreq; + self.tileDepth = (properties && properties.hips_tile_depth && +properties.hips_tile_depth) || self.tileDepth; self.obsRestFreq = properties && properties.obs_restfreq && +properties.obs_restfreq; // Max order @@ -514,15 +559,15 @@ export let HiPS = (function () { } // dataproduct type - self.dataproductType = properties && properties.dataproduct_type; + self.dataproductType = properties && properties.dataproduct_type || self.dataproductType; // Tile size self.tileSize = PropertyParser.tileSize(properties) || self.tileSize; // Tile formats - self.acceptedFormats = - PropertyParser.acceptedFormats(properties) || self.acceptedFormats; + self.formats = + PropertyParser.formats(properties) || self.formats || ["jpeg"]; // Min order const minOrder = PropertyParser.minOrder(properties) @@ -535,7 +580,7 @@ export let HiPS = (function () { PropertyParser.cooFrame(properties); // Parse the cooframe from the properties but if it fails, take the one given by the user // If the user gave nothing, then take ICRS as the default one - self.cooFrame = CooFrameEnum.fromString(cooFrame, self.cooFrame || CooFrameEnum.ICRS); + self.cooFrame = CooFrameEnum.fromString(cooFrame, self.cooFrame || CooFrameEnum.ICRS).system; // sky fraction self.skyFraction = PropertyParser.skyFraction(properties); @@ -576,21 +621,21 @@ export let HiPS = (function () { self.creatorDid = self.creatorDid || self.id || self.url; // check the imgFormat with respect to the formats accepted image format - const chooseTileFormat = (acceptedFormats) => { - if (acceptedFormats.indexOf("webp") >= 0) { + const chooseTileFormat = (formats) => { + if (formats.indexOf("webp") >= 0) { return "webp"; - } else if (acceptedFormats.indexOf("png") >= 0) { + } else if (formats.indexOf("png") >= 0) { return "png"; - } else if (acceptedFormats.indexOf("jpeg") >= 0) { + } else if (formats.indexOf("jpeg") >= 0) { return "jpeg"; - } else if (acceptedFormats.indexOf("fits") >= 0) { + } else if (formats.indexOf("fits") >= 0) { return "fits"; - } else if (acceptedFormats.indexOf("fits.fz") >= 0) { + } else if (formats.indexOf("fits.fz") >= 0) { return "fits"; } else { throw ( "Unsupported format(s) found in the properties: " + - acceptedFormats + formats ); } }; @@ -598,9 +643,9 @@ export let HiPS = (function () { // Set an image format with respect to the ones available for that HiPS if: // * the format is unknown // * the format is known but is not available for that HiPS - if (!self.imgFormat || !self.acceptedFormats.includes(self.imgFormat)) { + if (!self.imgFormat || !self.formats.includes(self.imgFormat)) { // Switch automatically to a available format - let imgFormat = chooseTileFormat(self.acceptedFormats); + let imgFormat = chooseTileFormat(self.formats); self.setImageFormat(imgFormat) console.info(self.id + " tile format chosen: " + self.imgFormat) @@ -649,18 +694,7 @@ export let HiPS = (function () { * @returns {string[]} Returns the formats accepted for the survey, i.e. the formats of tiles that are availables. Could be PNG, WEBP, JPG and FITS. */ HiPS.prototype.getAvailableFormats = function () { - return this.acceptedFormats; - }; - - /** - * Sets the opacity factor when rendering the HiPS - * - * @memberof HiPS - * - * @param {number} opacity - Opacity of the survey to set. Between 0 and 1 - */ - HiPS.prototype.setOpacity = function (opacity) { - this.setOptions({opacity}) + return this.formats; }; /** @@ -668,7 +702,7 @@ export let HiPS = (function () { * * @memberof HiPS * - * @param {boolean} [additive=false] - When rendering this survey on top of the already rendered ones, the final color of the screen is computed like: + * @param {boolean} [blending=false] - When rendering this survey on top of the already rendered ones, the final color of the screen is computed like: *
    *
    opacity * this_survey_color + (1 - opacity) * already_rendered_color for the default mode *
    opacity * this_survey_color + already_rendered_color for the additive mode @@ -677,12 +711,12 @@ export let HiPS = (function () { * Additive mode allows you to do linear survey color combination i.e. let's define 3 surveys named s1, s2, s3. Each could be associated to one color channel, i.e. s1 with red, s2 with green and s3 with the blue color channel. * If the additive blending mode is enabled, then the final pixel color of your screen will be: rgb = [s1_opacity * s1_color; s2_opacity * s2_color; s3_opacity * s3_color] */ - HiPS.prototype.setBlendingConfig = function (additive = false) { - this.setOptions({additive}); + HiPS.prototype.setBlendingConfig = function (blending = false) { + this.setOptions({blending}); }; HiPS.prototype.isSpectralCube = function() { - return this.hipsTileDepth !== undefined && this.hipsTileDepth !== null; + return this.tileDepth !== undefined && this.tileDepth !== null; } /** @@ -721,9 +755,7 @@ export let HiPS = (function () { * @param {boolean} [options.reversed=false] - Reverse the colormap axis. */ HiPS.prototype.setColormap = function (colormap, options) { - colormap = colormap || this.options.colormap; - - this.setOptions({colormap, ...options}) + this.setOptions({colormap, ...options}); }; /** @@ -737,54 +769,11 @@ export let HiPS = (function () { * @param {number} maxCut - The high cut value to set for the HiPS. * @param {string} [imgFormat] - The image format for which one wants to set the cuts. By default, the format used is the current imageFormat */ - HiPS.prototype.setCuts = function (minCut, maxCut, imgFormat) { - imgFormat = imgFormat?.toLowerCase(); - - if (imgFormat === "jpg") { - imgFormat = "jpeg"; - } - - this.setOptions({minCut, maxCut, cutFormat: imgFormat}) + HiPS.prototype.setCuts = function (minCut, maxCut, cutFormat) { + this.setOptions({minCut, maxCut, cutFormat}) }; - /** - * Returns the low and high cuts under the form of a 2 element array - * - * @memberof HiPS - * - * @returns {number[]} The low and high cut values for the HiPS. - */ - HiPS.prototype.getCuts = function () { - return this.colorCfg.getCuts(); - }; - - /** - * Sets the gamma correction factor for the HiPS. - * - * This method updates the gamma of the HiPS. - * - * @memberof HiPS - * - * @param {number} gamma - The saturation value to set for the HiPS. Between 0.1 and 10 - */ - HiPS.prototype.setGamma = function (gamma) { - this.setOptions({gamma}) - }; - - /** - * Sets the saturation for the HiPS. - * - * This method updates the saturation of the HiPS. - * - * @memberof HiPS - * - * @param {number} saturation - The saturation value to set for the HiPS. Between 0 and 1 - */ - HiPS.prototype.setSaturation = function (saturation) { - this.setOptions({saturation}) - }; - - /** + /** * Sets the brightness for the HiPS. * * This method updates the brightness of the HiPS. @@ -793,10 +782,15 @@ export let HiPS = (function () { * * @param {number} brightness - The brightness value to set for the HiPS. Between 0 and 1 */ - HiPS.prototype.setBrightness = function (brightness) { + HiPS.prototype.setBrightness = function(brightness) { this.setOptions({brightness}) }; + // @api + HiPS.prototype.getBrightness = function() { + return this.brightness; + }; + /** * Sets the contrast for the HiPS. * @@ -806,10 +800,103 @@ export let HiPS = (function () { * * @param {number} contrast - The contrast value to set for the HiPS. Between 0 and 1 */ - HiPS.prototype.setContrast = function (contrast) { + HiPS.prototype.setContrast = function(contrast) { this.setOptions({contrast}) }; + // @api + HiPS.prototype.getContrast = function() { + return this.kContrast; + }; + + /** + * Sets the saturation for the HiPS. + * + * This method updates the saturation of the HiPS. + * + * @memberof HiPS + * + * @param {number} saturation - The saturation value to set for the HiPS. Between 0 and 1 + */ + HiPS.prototype.setSaturation = function(saturation) { + this.setOptions({saturation}) + }; + + // @api + HiPS.prototype.getSaturation = function() { + return this.kSaturation; + }; + + /** + * Sets the gamma correction factor for the HiPS. + * + * This method updates the gamma of the HiPS. + * + * @memberof HiPS + * + * @param {number} gamma - The saturation value to set for the HiPS. Between 0.1 and 10 + */ + HiPS.prototype.setGamma = function(gamma) { + this.setOptions({gamma}) + }; + + // @api + HiPS.prototype.getGamma = function() { + return this.kGamma; + }; + + /** + * Sets the opacity factor when rendering the HiPS + * + * @memberof HiPS + * + * @param {number} opacity - Opacity of the survey to set. Between 0 and 1 + */ + HiPS.prototype.setOpacity = function(opacity) { + this.setOptions({opacity}) + }; + + /** + * Get the opacity of the HiPS layer + * + * @memberof HiPS + * + * @returns {number} The opacity of the layer + */ + HiPS.prototype.getOpacity = function() { + return this.opacity; + }; + + // @api + HiPS.prototype.getAlpha = HiPS.prototype.getOpacity; + + HiPS.prototype.getBlendingConfig = function() { + return this.blending; + }; + + // @api + HiPS.prototype.getColormap = function() { + return this.colormap; + }; + + HiPS.prototype.getReversed = function() { + return this.reversed; + }; + + /** + * Returns the low and high cuts under the form of a 2 element array + * + * @memberof HiPS + * + * @returns {number[]} The low and high cut values for the HiPS. + */ + HiPS.prototype.getCuts = function() { + return [ + this.minCut[this.imgFormat], + this.maxCut[this.imgFormat] + ]; + }; + HiPS.prototype.setSliceNumber = function(slice) { this.slice = slice; @@ -830,6 +917,9 @@ export let HiPS = (function () { * @param {number} [options.value] = The frequency value expressed in `options.unit` * @param {"Hz"|"m"|"m/s"} [options.unit="Hz"] - The unit of the frequency passed * @param {number} [options.restFreq] - "The rest frequency (in Hz) to use for computing the velocity in m.s-1" + * + * @example + * hips3d.setFrequency({ value: 1420302592, unit: 'Hz' }) */ HiPS.prototype.setFrequency = function(options) { if (this.added) { @@ -858,12 +948,22 @@ export let HiPS = (function () { } } + /** + * Get the frequency in Hz + * + * @memberof HiPS + */ HiPS.prototype.getFrequency = function() { if (this.added) { return this.view.wasm.getFreq(this.layer); } } + /** + * Get the frequency window around the current observed frequency in Hz + * + * @memberof HiPS + */ HiPS.prototype.getFrequencyWindow = function() { if (this.added) { return this.view.wasm.getFreqWindow(this.layer); @@ -874,10 +974,7 @@ export let HiPS = (function () { HiPS.prototype._updateMetadata = function () { try { if (this.added) { - this.view.wasm.setImageMetadata(this.layer, { - ...this.colorCfg.get(), - imgFormat: this.imgFormat, - }); + this.view.wasm.setImageMetadata(this.layer, this._prepareMetadataForWASM()); // once the meta have been well parsed, we can set the meta ALEvent.LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, { layer: this, @@ -906,14 +1003,93 @@ export let HiPS = (function () { * @param {boolean} [options.reversed=false] - If true, the colormap is reversed; otherwise, it is not reversed. * @param {number} [options.minCut] - The minimum cut value for the color configuration. If not given, 0.0 for JPEG/PNG surveys, the value of the property file for FITS surveys * @param {number} [options.maxCut] - The maximum cut value for the color configuration. If not given, 1.0 for JPEG/PNG surveys, the value of the property file for FITS surveys - * @param {boolean} [options.additive=false] - If true, additive blending is applied; otherwise, it is not applied. + * @param {boolean} [options.blending=false] - If true, additive blending is applied; otherwise, it is not applied. * @param {number} [options.gamma=1.0] - The gamma correction value for the color configuration. * @param {number} [options.saturation=0.0] - The saturation value for the color configuration. * @param {number} [options.brightness=0.0] - The brightness value for the color configuration. * @param {number} [options.contrast=0.0] - The contrast value for the color configuration. */ HiPS.prototype.setOptions = function(options) { - this.colorCfg.setOptions(options); + /// imgFormat + if (options && options.imgFormat) { + this.imgFormat = options.imgFormat + } + + /// colormap + if (options && options.colormap) { + this.colormap = options.colormap.toLowerCase() + } + + /// stretch + if (options && options.stretch) { + let stretch = options.stretch; + this.stretch = stretch.toLowerCase() + } + + /// reversed + if (options && options.reversed !== undefined) { + this.reversed = options.reversed; + } + + /// cuts + let cutFormat = options.cutFormat?.toLowerCase() || this.imgFormat; + + if (cutFormat === "jpg") { + cutFormat = "jpeg"; + } + + + let minCut = options && options.minCut; + if (minCut instanceof Object) { + // Mincut is given in the form of an javascript object with all the formats + this.minCut = {...this.minCut, ...minCut}; + } else if (minCut !== null && minCut !== undefined) { + this.minCut[cutFormat] = minCut; + } + + let maxCut = options && options.maxCut; + if (maxCut instanceof Object) { + this.maxCut = {...this.maxCut, ...maxCut}; + } else if (maxCut !== null && maxCut !== undefined) { + this.maxCut[cutFormat] = maxCut; + } + + if (options && Utils.isNumber(options.brightness)) { + let brightness = options.brightness; + + brightness = +brightness || 0.0; // coerce to number + this.brightness = Math.max(-1, Math.min(brightness, 1)); + } + + if (options && Utils.isNumber(options.saturation)) { + let saturation = options.saturation; + saturation = +saturation || 0.0; // coerce to number + + this.saturation = Math.max(-1, Math.min(saturation, 1)); + } + + if (options && Utils.isNumber(options.contrast)) { + let contrast = options.contrast; + + contrast = +contrast || 0.0; // coerce to number + this.contrast = Math.max(-1, Math.min(contrast, 1)); + } + + if (options && Utils.isNumber(options.gamma)) { + let gamma = options.gamma; + gamma = +gamma; // coerce to number + this.gamma = Math.max(0.1, Math.min(gamma, 10)); + } + + if (options && Utils.isNumber(options.opacity)) { + let opacity = options.opacity; + opacity = +opacity; // coerce to number + this.opacity = Math.max(0, Math.min(opacity, 1)); + } + + if (options && options.blending) { + this.blending = options.blending; + } /// Set image format if (options.imgFormat) { @@ -932,10 +1108,10 @@ export let HiPS = (function () { } else { // Passed the check, we erase the image format with the new one // We do nothing if the imgFormat is the same - + // Check the properties to see if the given format is available among the list // If the properties have not been retrieved yet, it will be tested afterwards - const availableFormats = this.acceptedFormats; + const availableFormats = this.formats; // user wants a fits but the metadata tells this format is not available if (!availableFormats || (availableFormats && availableFormats.indexOf(imgFormat) >= 0)) { this.imgFormat = imgFormat; @@ -951,13 +1127,6 @@ export let HiPS = (function () { } } - this.options = { - ...this.options, - ...options, - minCut: this.colorCfg.minCut, - maxCut: this.colorCfg.maxCut - }; - this._updateMetadata(); }; @@ -984,24 +1153,6 @@ export let HiPS = (function () { */ HiPS.prototype.setAlpha = HiPS.prototype.setOpacity; - // @api - HiPS.prototype.getColorCfg = function () { - return this.colorCfg; - }; - - /** - * Get the opacity of the HiPS layer - * - * @memberof HiPS - * - * @returns {number} The opacity of the layer - */ - HiPS.prototype.getOpacity = function () { - return this.colorCfg.getOpacity(); - }; - - HiPS.prototype.getAlpha = HiPS.prototype.getOpacity; - /** * Probe the HiPS at a screen pixel location. * @@ -1076,11 +1227,43 @@ export let HiPS = (function () { return; } + this.updateHiPSCache = false; + let self = this; let hipsCache = this.view.aladin.hipsCache; if (hipsCache.contains(self.id)) { - hipsCache.append(self.id, this.options) + hipsCache.update(self.id, { + creatorDid: self.creatorDid, + url: self.url, + maxOrder: self.maxOrder, + cooFrame: self.cooFrame, + tileSize: self.tileSize, + formats: self.formats, + numBitsPerPixel: self.numBitsPerPixel, + skyFraction: self.skyFraction, + minOrder: self.minOrder, + initialFov: self.initialFov, + initialRa: self.initialRa, + initialDec: self.initialDec, + emMin: self.emMin, + emMax: self.emMax, + // HiPS Cube + cubeDepth: self.cubeDepth, + // HiPS3D + tileDepth: self.tileDepth, + orderFreq: self.orderFreq, + // Dataproduct type + dataproductType: self.dataproductType, + isPlanetaryBody: self.isPlanetaryBody(), + hipsBody: self.hipsBody, + requestCredentials: self.requestCredentials, + requestMode: self.requestMode, + name: this.name, + id: this.id, + type: this.type, + ...this._getMetadata(), + }) } }; @@ -1093,6 +1276,25 @@ export let HiPS = (function () { } }; + HiPS.prototype._getMetadata = function() { + return { + imgFormat: this.imgFormat, + blending: this.blending, + opacity: this.opacity, + // Tonal corrections constants + gamma: this.gamma, + saturation: this.saturation, + brightness: this.brightness, + contrast: this.contrast, + + stretch: this.stretch, + minCut: this.minCut, + maxCut: this.maxCut, + reversed: this.reversed, + colormap: this.colormap, + }; + }; + HiPS.prototype._addToView = function (layer) { if (!this.view) return this; @@ -1106,22 +1308,22 @@ export let HiPS = (function () { creatorDid: self.creatorDid, url: self.url, maxOrder: self.maxOrder, - cooFrame: self.cooFrame.system, + cooFrame: self.cooFrame, tileSize: self.tileSize, - formats: self.acceptedFormats, + formats: self.formats, bitpix: self.numBitsPerPixel, skyFraction: self.skyFraction, minOrder: self.minOrder, - hipsInitialFov: self.initialFov, - hipsInitialRa: self.initialRa, - hipsInitialDec: self.initialDec, + initialFov: self.initialFov, + initialRa: self.initialRa, + initialDec: self.initialDec, emMin: self.emMin, emMax: self.emMax, // HiPS Cube - hipsCubeDepth: self.cubeDepth, + cubeDepth: self.cubeDepth, // HiPS3D - hipsTileDepth: self.hipsTileDepth, - hipsOrderFreq: self.hipsOrderFreq, + tileDepth: self.tileDepth, + orderFreq: self.orderFreq, // Dataproduct type dataproductType: self.dataproductType, isPlanetaryBody: self.isPlanetaryBody(), @@ -1129,10 +1331,7 @@ export let HiPS = (function () { requestCredentials: self.requestCredentials, requestMode: self.requestMode, }, - meta: { - ...this.colorCfg.get(), - imgFormat: this.imgFormat, - } + meta: this._prepareMetadataForWASM() }; let localFiles; @@ -1175,7 +1374,41 @@ export let HiPS = (function () { return this }; + HiPS.prototype._prepareMetadataForWASM = function() { + let metadata = this._getMetadata(); + let blending = { + srcColorFactor: 'SrcAlpha', + dstColorFactor: 'OneMinusSrcAlpha', + func: 'FuncAdd' + }; + + if (this.blending) { + blending = { + srcColorFactor: 'SrcAlpha', + dstColorFactor: 'One', + func: 'FuncAdd' + } + } + + let minCut = this.minCut[this.imgFormat] + if (this.imgFormat !== "fits") { + minCut /= 255.0 + } + + let maxCut = this.maxCut[this.imgFormat] + if (this.imgFormat !== "fits") { + maxCut /= 255.0 + } + + metadata["minCut"] = minCut; + metadata["maxCut"] = maxCut; + metadata["blending"] = blending; + + return metadata; + }; + HiPS.DEFAULT_SURVEY_ID = "P/DSS2/color"; return HiPS; })(); + diff --git a/src/js/HiPSCache.js b/src/js/HiPSCache.js index 90e4fea7..cedd5900 100644 --- a/src/js/HiPSCache.js +++ b/src/js/HiPSCache.js @@ -37,11 +37,17 @@ export let HiPSCache = (function () { this.cache = {} }; + HiPSCache.prototype.update = function (key, obj) { + this.cache[key] = obj; + + ALEvent.HIPS_CACHE_UPDATED.dispatchedTo(document.body); + }; + /* * key can be a CDS ID or an url. TODO could be an options.name too. */ - HiPSCache.prototype.append = function (key, options) { - this.cache[key] = options; + HiPSCache.prototype.append = function (key, obj) { + this.cache[key] = obj; ALEvent.HIPS_CACHE_UPDATED.dispatchedTo(document.body); }; @@ -59,7 +65,9 @@ export let HiPSCache = (function () { * key can be a CDS ID or an url. TODO could be an options.name too. */ HiPSCache.prototype.get = function (key) { - return this.cache[key]; + let obj = this.cache[key]; + + return obj; }; /* diff --git a/src/js/Image.js b/src/js/Image.js index 93055fd0..a57c7a38 100644 --- a/src/js/Image.js +++ b/src/js/Image.js @@ -29,11 +29,10 @@ * Authors: Matthieu Baumann [CDS] * *****************************************************************************/ -import { ColorCfg } from "./ColorCfg.js"; -import { Aladin } from "./Aladin.js"; import { Utils } from "./Utils"; import { AVM } from "./libs/avm.js"; import { HiPS } from "./HiPS.js"; +import { JSONP_PROXY } from "./Constants"; /** * @typedef {Object} WCS @@ -140,13 +139,55 @@ export let Image = (function () { this.id = url; this.name = (options && options.name) || this.url; this.imgFormat = options && options.imgFormat; - this.acceptedFormats = [this.imgFormat]; + this.formats = [this.imgFormat]; + this.type = "image" + + // Opacity of the survey/image + this.opacity = (options && options.opacity) || 1.0; + + // Colormap config options + this.colormap = (options && options.colormap) || "native"; + this.colormap = this.colormap.toLowerCase(); + + this.stretch = (options && options.stretch) || "linear"; + this.stretch = this.stretch.toLowerCase(); + this.reversed = false; + + if (options && options.reversed === true) { + this.reversed = true; + } + + this.minCut = { + webp: 0.0, + jpeg: 0.0, + png: 0.0, + fits: undefined // wait the default value coming from the properties + }; + + this.maxCut = { + webp: 255.0, + jpeg: 255.0, + png: 255.0, + fits: undefined // wait the default value coming from the properties + }; + + this.setCuts(options.minCut, options.maxCut); + + this.blending = options && options.blending; + if (this.blending === undefined) { + this.blending = false; + } + + // A default value for gamma correction + this.gamma = (options && options.gamma) || 1.0; + this.saturation = (options && options.saturation) || 0.0; + this.brightness = (options && options.brightness) || 0.0; + this.contrast = (options && options.contrast) || 0.0; // callbacks this.successCallback = options && options.successCallback; this.errorCallback = options && options.errorCallback; - this.colorCfg = new ColorCfg(options); this.options = options || {}; let self = this; @@ -301,8 +342,6 @@ export let Image = (function () { */ Image.prototype.setAlpha = HiPS.prototype.setOpacity; - Image.prototype.getColorCfg = HiPS.prototype.getColorCfg; - /** * Get the opacity of the image layer * @@ -341,7 +380,13 @@ export let Image = (function () { */ Image.prototype.getAvailableFormats = HiPS.prototype.getAvailableFormats; - + Image.prototype.getColormap = HiPS.prototype.getColormap; + Image.prototype.getReversed = HiPS.prototype.getReversed; + Image.prototype.getBrightness = HiPS.prototype.getBrightness; + Image.prototype.getSaturation = HiPS.prototype.getSaturation; + Image.prototype.getContrast = HiPS.prototype.getContrast; + Image.prototype.getGamma = HiPS.prototype.getGamma; + Image.prototype._setView = function (view) { this.view = view; }; @@ -361,7 +406,29 @@ export let Image = (function () { }; /* Private method view is already attached */ - Image.prototype._saveInCache = HiPS.prototype._saveInCache; + /* Precondition: view is attached */ + Image.prototype._saveInCache = function () { + if (!this.view) { + this.updateHiPSCache = true; + return; + } + + this.updateHiPSCache = false; + + let self = this; + let hipsCache = this.view.aladin.hipsCache; + + if (hipsCache.contains(self.id)) { + hipsCache.update(self.id, { + url: self.url, + formats: self.formats, + name: this.name, + id: this.id, + type: this.type, + ...this._getMetadata(), + }) + } + }; // Private method for updating the view with the new meta Image.prototype._updateMetadata = HiPS.prototype._updateMetadata; @@ -401,7 +468,7 @@ export let Image = (function () { } promise = promise.then((imageParams) => { - self.acceptedFormats = [self.imgFormat]; + self.formats = [self.imgFormat]; // There is at least one entry in imageParams self.added = true; @@ -451,31 +518,27 @@ export let Image = (function () { url: this.url, dataType: 'arrayBuffer', success: (buf) => { + self.imgFormat = 'fits'; return self.view.wasm.addFITSImage( new Uint8Array(buf), - { - ...self.colorCfg.get(), - imgFormat: 'fits', - }, + self._prepareMetadataForWASM(), layer ) }, error: (e) => { console.error(e) - console.info("Trying querying the FITS through proxy:" + Aladin.JSONP_PROXY) + console.info("Trying querying the FITS through proxy:" + JSONP_PROXY) // try as cors - const url = Aladin.JSONP_PROXY + '?url=' + self.url; + const url = JSONP_PROXY + '?url=' + self.url; return Utils.fetch({ - url: url, + url, dataType: 'arrayBuffer', success: (buf) => { + self.imgFormat = 'fits'; return self.view.wasm.addFITSImage( new Uint8Array(buf), - { - ...self.colorCfg.get(), - imgFormat: 'fits', - }, + self._prepareMetadataForWASM(), layer ) }, @@ -484,17 +547,33 @@ export let Image = (function () { }) .then((imageParams) => { self.imgFormat = 'fits' - self.colorCfg.setOptions({imgFormat: 'fits'}); - return Promise.resolve(imageParams); }) }; + Image.prototype._getMetadata = HiPS.prototype._getMetadata; + + Image.prototype._prepareMetadataForWASM = HiPS.prototype._prepareMetadataForWASM; + Image.prototype._addJPGOrPNG = function(layer) { let self = this; let img = document.createElement('img'); - return new Promise((resolve, reject) => { + console.log(this.url) + let checkImgFormat = Utils.fetchWithProxy({url: this.url, dataType: 'arrayBuffer'}) + .then(arrayBuffer => { + const view = new DataView(arrayBuffer); + // See the magic bytes for JPEG or PNG files + if (view.getUint8(0) === 0xFF && view.getUint8(1) === 0xD8) { + return "jpeg"; + } else if (view.getUint8(0) === 0x89 && view.getUint8(1) === 0x50) { + return "png"; + } else { + throw new Error("Unknown image format"); + } + }) + + let loadImg = new Promise((resolve, reject) => { img.src = this.url; img.crossOrigin = "Anonymous"; img.onload = () => { @@ -504,6 +583,8 @@ export let Image = (function () { canvas.width = img.width; canvas.height = img.height; + img.arrat + // Copy the image contents to the canvas var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, img.width, img.height); @@ -525,12 +606,20 @@ export let Image = (function () { // obj.tags (object) = An array containing all the loaded tags e.g. obj.tags['Headline'] // obj.wcs (object) = The wcs parsed from the image if (obj.wcsdata) { + if (obj.wcs.NAXIS1 === undefined) { + obj.wcs.NAXIS1 = img.width; + } + if (img.width !== obj.wcs.NAXIS1) { obj.wcs.CDELT1 = obj.wcs.CDELT1 * (obj.wcs.NAXIS1 / img.width); obj.wcs.CRPIX1 *= img.width / obj.wcs.NAXIS1; obj.wcs.NAXIS1 = img.width; } + if (obj.wcs.NAXIS2 === undefined) { + obj.wcs.NAXIS2 = img.height; + } + if (img.height !== obj.wcs.NAXIS2) { obj.wcs.CDELT2 = obj.wcs.CDELT2 * (obj.wcs.NAXIS2 / img.height); obj.wcs.CRPIX2 *= img.height / obj.wcs.NAXIS2; @@ -561,38 +650,32 @@ export let Image = (function () { return; } - console.error(e); - console.info("Using proxy", Aladin.JSONP_PROXY) + console.info("Using proxy", JSONP_PROXY) proxyUsed = true; - img.src = Aladin.JSONP_PROXY + '?url=' + self.url; + img.src = JSONP_PROXY + '?url=' + self.url; } - }) - .then((bytes) => { - let wcs = self.options && self.options.wcs; - wcs.NAXIS1 = wcs.NAXIS1 || img.width; - wcs.NAXIS2 = wcs.NAXIS2 || img.height; - return self.view.wasm - .addRGBAImage( - bytes, - wcs, - { - ...self.colorCfg.get(), - imgFormat: 'jpeg', - }, - layer - ) - }) - .then((imageParams) => { - self.imgFormat = 'jpeg' - self.colorCfg.setOptions({imgFormat: 'jpeg'}); - return Promise.resolve(imageParams); - }) - /*.catch((e) => { - console.error(e) - })*/ - .finally(() => { - img.remove(); }); + + return Promise.all([loadImg, checkImgFormat]) + .then(([imgBytes, imgFormat]) => { + let wcs = self.options && self.options.wcs; + wcs.NAXIS1 = wcs.NAXIS1 || img.width; + wcs.NAXIS2 = wcs.NAXIS2 || img.height; + wcs.NAXIS = wcs.NAXIS || 2; + + self.imgFormat = imgFormat; + + return self.view.wasm + .addRGBAImage( + imgBytes, + wcs, + self._prepareMetadataForWASM(), + layer + ) + }) + .finally(() => { + img.remove(); + }); }; return Image; diff --git a/src/js/MocServer.js b/src/js/MocServer.js index 227e67d6..0e7b32c1 100644 --- a/src/js/MocServer.js +++ b/src/js/MocServer.js @@ -50,7 +50,7 @@ export class MocServer { static getAllHiPSes() { if (!this._allHiPSes) { const params = { - expr: "dataproduct_type=image||dataproduct_type=cube", + expr: "dataproduct_type=image||dataproduct_type=spectral-cube", //expr: "dataproduct_type=image", get: "record", fmt: "json", @@ -69,8 +69,8 @@ export class MocServer { static getAllHiPSesInsideView(aladin) { let params = { - //expr: "dataproduct_type=image||dataproduct_type=cube", - expr: "dataproduct_type=image", + expr: "dataproduct_type=image||dataproduct_type=spectral-cube", + //expr: "dataproduct_type=image", get: "record", fmt: "json", fields: "ID", @@ -118,6 +118,7 @@ export class MocServer { }; this._allCatalogHiPSes = Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {data: params, dataType: 'json'}) + this._allCatalogHiPSes.then((aa) => console.log(aa)) } return this._allCatalogHiPSes; diff --git a/src/js/ProgressiveCat.js b/src/js/ProgressiveCat.js index 42c6cfe1..d301c7bf 100644 --- a/src/js/ProgressiveCat.js +++ b/src/js/ProgressiveCat.js @@ -248,18 +248,28 @@ export let ProgressiveCat = (function() { _loadMetadata: function() { var self = this; - let request = new Request(self.rootUrl + '/' + 'Metadata.xml', { + let request = new Request(self.rootUrl + '/' + 'metadata.xml', { method: 'GET' }) fetch(request) - .then((resp) => resp.text()) + .then((resp) => { + if (!resp.ok) { + let request = new Request(self.rootUrl + '/' + 'Metadata.xml', { + method: 'GET' + }) + return fetch(request) + .then((resp) => resp.text()) + } else { + return resp.text() + } + }) .then((text) => { let xml = ProgressiveCat.parser.parseFromString(text, "text/xml") self.fields = getFields(self, xml); self._loadAllskyNewMethod(); }) - .catch(err => self._loadAllskyOldMethod()); + .catch(err => self._loadAllskyOldMethod()) }, _loadAllskyNewMethod: function() { @@ -598,10 +608,6 @@ export let ProgressiveCat = (function() { (function(self, norder, ipix) { // wrapping function is needed to be able to retrieve norder and ipix in ajax success function var key = norder + '-' + ipix; Utils.fetch({ - /* - url: Aladin.JSONP_PROXY, - data: {"url": self.getTileURL(norder, ipix)}, - */ // ATTENTION : je passe en JSON direct, car je n'arrive pas a choper les 404 en JSONP url: self.getTileURL(norder, ipix), desc: "Get tile .tsv " + norder + ' ' + ipix + ' of ' + self.name, diff --git a/src/js/Reticle.js b/src/js/Reticle.js index 431a9657..cbf08d62 100644 --- a/src/js/Reticle.js +++ b/src/js/Reticle.js @@ -54,7 +54,7 @@ export let Reticle = (function() { let color = options && options.reticleColor || Aladin.DEFAULT_OPTIONS.reticleColor; let size = options && options.reticleSize || Aladin.DEFAULT_OPTIONS.reticleSize; - + let show; if (options.showReticle === undefined) { show = Aladin.DEFAULT_OPTIONS.showReticle; diff --git a/src/js/SpectraDisplayer.js b/src/js/SpectraDisplayer.js index 24e638ce..d2c783db 100644 --- a/src/js/SpectraDisplayer.js +++ b/src/js/SpectraDisplayer.js @@ -120,6 +120,12 @@ export class SpectraDisplayer extends DOMElement { } }; + updateCanvas() { + this.divNode.querySelectorAll("canvas").forEach(canvas => { + canvas.width = this.view.aladin.aladinDiv.getBoundingClientRect().width + }); + } + constructor(view, options) { super() @@ -142,7 +148,7 @@ export class SpectraDisplayer extends DOMElement { this.scaleX = undefined; this.scaleY = undefined; this.height = options && options.height || 300; - this.width = options && options.width || 600; + this.width = options && options.width || view.aladin.aladinDiv.getBoundingClientRect().width; this.minY = undefined; this.maxY = undefined; this.mouseFreq = undefined; @@ -167,19 +173,22 @@ export class SpectraDisplayer extends DOMElement { name: "unit selector", value: "f", type: 'select', - classList: ['aladin-spectra-unit-selector'], + classList: ['aladin-spectra-unit'], options: [ SpectraDisplayer.UNIT.FREQUENCY.label, SpectraDisplayer.UNIT.WAVELENGTH.label, SpectraDisplayer.UNIT.VELOCITY.label, ], tooltip: { - content: `Unit: ${SpectraDisplayer.UNIT.FREQUENCY.label}, ${SpectraDisplayer.UNIT.WAVELENGTH.label} and ${SpectraDisplayer.UNIT.VELOCITY.label}`, - position: {direction: "right"} + aladin: view.aladin, + global: true, + content: `Unit: Hz, m or km/s`, }, change: (e) => { let label = e.target.value; + let prevUnit = self.unit; + if (label === SpectraDisplayer.UNIT.FREQUENCY.label) { self.unit = SpectraDisplayer.UNIT.FREQUENCY } else if (label === SpectraDisplayer.UNIT.WAVELENGTH.label) { @@ -188,7 +197,15 @@ export class SpectraDisplayer extends DOMElement { self.unit = SpectraDisplayer.UNIT.VELOCITY } - self._redrawLabels(); + if (prevUnit === SpectraDisplayer.UNIT.FREQUENCY) { + self.data.values.reverse(); + } else if (prevUnit === SpectraDisplayer.UNIT.WAVELENGTH && self.unit === SpectraDisplayer.UNIT.FREQUENCY) { + self.data.values.reverse(); + } else if (prevUnit === SpectraDisplayer.UNIT.VELOCITY && self.unit === SpectraDisplayer.UNIT.FREQUENCY) { + self.data.values.reverse(); + } + + self._redraw(self.ctx); }, }) @@ -199,8 +216,9 @@ export class SpectraDisplayer extends DOMElement { url: HomeIconUrl }, tooltip: { - content: "Scale for data", - position: {direction: "right"} + aladin: view.aladin, + global: true, + content: `Auto scale to fit window height`, }, classList: ['aladin-spectra-home'], action(e) { @@ -227,24 +245,23 @@ export class SpectraDisplayer extends DOMElement { this.unit = SpectraDisplayer.UNIT.FREQUENCY; let divNode = document.createElement("div"); - divNode.style.position = "absolute"; - divNode.style.left = "50%"; - divNode.style.transform = "translateX(-50%)"; - divNode.style.bottom = "2px"; - divNode.style.width = this.width + "px"; + //divNode.style.width = this.width + "px"; divNode.style.height = this.height + "px"; - divNode.classList.add("aladin-lite-spectra-displayer") + divNode.classList.add("aladin-spectra-displayer") divNode.appendChild(this.canvas) divNode.appendChild(canvasCursor) divNode.appendChild(canvasLabels) divNode.appendChild(unitSelector.element()) divNode.appendChild(autoCenterBtn.element()) - //divNode.appendChild(extractionBtn.element()) this.divNode = divNode; - this.view.aladin.aladinDiv.appendChild(divNode); + let statusBar = this.view.aladin.statusBar; + this.view.aladin.aladinDiv.insertBefore( + divNode, + statusBar && statusBar.element() + ) this.defineEventListeners() this.hips3DList = new Map(); @@ -381,7 +398,8 @@ export class SpectraDisplayer extends DOMElement { canvas.style.cursor = 'default'; - this.ctxCursor.clearRect(0, 0, this.width, this.height); + let w = this.view.aladin.aladinDiv.getBoundingClientRect().width; + this.ctxCursor.clearRect(0, 0, w, this.height); this.mouseFreq = null; if (my >= v) { @@ -398,12 +416,18 @@ export class SpectraDisplayer extends DOMElement { let curFreq = self.hips.getFrequency(); let curHash = Number(self.view.wasm.freq2hash(self.hips.layer, curFreq)); - let mouseHash = curHash + Math.round((mx - (this.width / 2)) / this.scaleX) + let mouseHash; + if (self.unit === SpectraDisplayer.UNIT.FREQUENCY) { + mouseHash = curHash + Math.round((mx - (w / 2)) / this.scaleX); + } else { + mouseHash = curHash - Math.round((mx - (w / 2)) / this.scaleX); + } this.mouseFreq = self.view.wasm.hash2freq(self.hips.layer, BigInt(mouseHash)); } this._redrawLabels() + if (!this.isDragging) { // Draw the vertical line that can be grabed to move the slice this.ctx.beginPath(); @@ -416,6 +440,12 @@ export class SpectraDisplayer extends DOMElement { this.canvas.style.cursor = 'grab'; } + if (my >= v) { + this.lastMouse = { x: mx, y: my }; + } else { + this.lastMouse = undefined; + } + return; } @@ -429,28 +459,35 @@ export class SpectraDisplayer extends DOMElement { // look where we are in the freq range let j = Utils.binarySearch(self.data.freqs, self.data.freq); - let df, f; + let df, f, f0; if (j > 0 && j < self.data.freqs.length - 1) { df = (self.data.freqs[j + 1] - self.data.freqs[j - 1]) * 0.5; - f = self.data.freq - dx * df; + f0 = self.data.freq; } else if (j == 0) { df = self.data.freqs[1] - self.data.freqs[0] - f = self.data.freqs[0] - dx * df; + f0 = self.data.freq[0]; } else { df = self.data.freqs[self.data.freqs.length - 1] - self.data.freqs[self.data.freqs.length - 2]; - f = self.data.freqs[self.data.freqs.length - 1] - dx * df; + f0 = self.data.freqs[self.data.freqs.length - 1]; + } + + if (this.unit === SpectraDisplayer.UNIT.FREQUENCY) { + f = f0 - dx * df; + } else { + f = f0 + dx * df; } self.hips.setFrequency({ value: f, unit: 'Hz' }) - this.lastMouse = { x: mx, y: my }; } }); Utils.on(canvas, 'mouseup touchend', (e) => { + let w = this.view.aladin.aladinDiv.getBoundingClientRect().width; + if (!this.enabled) { let paramsEvent = { bubbles: e.bubbles, @@ -496,20 +533,27 @@ export class SpectraDisplayer extends DOMElement { if (my >= v) { let dx = (mx - rect.width * 0.5) / this.scaleX; if (dx != 0) { + // Set the frequency // look where we are in the freq range let j = Utils.binarySearch(self.data.freqs, self.data.freq); - let df, f; + let df, f, f0; if (j > 0 && j < self.data.freqs.length - 1) { df = (self.data.freqs[j + 1] - self.data.freqs[j - 1]) * 0.5; - f = self.data.freq + dx * df; + f0 = self.data.freq; } else if (j == 0) { df = self.data.freqs[1] - self.data.freqs[0] - f = self.data.freqs[0] + dx * df; + f0 = self.data.freq[0]; } else { df = self.data.freqs[self.data.freqs.length - 1] - self.data.freqs[self.data.freqs.length - 2]; - f = self.data.freqs[self.data.freqs.length - 1] + dx * df; + f0 = self.data.freqs[self.data.freqs.length - 1]; + } + + if (this.unit === SpectraDisplayer.UNIT.FREQUENCY) { + f = f0 + dx * df; + } else { + f = f0 - dx * df; } self.hips.setFrequency({ @@ -517,11 +561,11 @@ export class SpectraDisplayer extends DOMElement { unit: 'Hz' }) } - this.lastMouse = { x: mx, y: my }; + } - //this.ctxCursor.clearRect(0, 0, this.width, this.height); + //this.ctxCursor.clearRect(0, 0, w, this.height); this.mouseFreq = null; } @@ -542,7 +586,13 @@ export class SpectraDisplayer extends DOMElement { }); Utils.on(canvas, 'wheel', (e) => { - this.ctxCursor.clearRect(0, 0, this.width, this.height); + // stop the propagation to prevent scrolling on the page + e.preventDefault(); + e.stopPropagation(); + + let w = this.view.aladin.aladinDiv.getBoundingClientRect().width; + + this.ctxCursor.clearRect(0, 0, w, this.height); const wheelEvent = new WheelEvent('wheel', { bubbles: true, @@ -594,7 +644,10 @@ export class SpectraDisplayer extends DOMElement { let data = event.detail; if (data.layer === this.hips.layer) { this.data = data; - console.log(data) + if (this.unit !== SpectraDisplayer.UNIT.FREQUENCY) { + this.data.values.reverse(); + } + this._redraw(this.ctx); } }; @@ -623,11 +676,14 @@ export class SpectraDisplayer extends DOMElement { } _redraw() { + const values = this.data.values; let len = values.length; // Clear previous drawing - this.ctx.clearRect(0, 0, this.width, this.height); + let w = this.view.aladin.aladinDiv.getBoundingClientRect().width; + + this.ctx.clearRect(0, 0, w, this.height); // Find min and max for scaling let valuesWithNoNans = values.filter(v=>Number.isFinite(v)); @@ -643,7 +699,7 @@ export class SpectraDisplayer extends DOMElement { this.maxY = Math.max(...valuesWithNoNans) } - this.scaleX = this.width / (len - 1); + this.scaleX = w / (len - 1); this.scaleY = (this.maxY - this.minY === 0) ? 1 : this.height / (this.maxY - this.minY); this._redrawSpectra(values) @@ -656,15 +712,18 @@ export class SpectraDisplayer extends DOMElement { this.ctx.lineWidth = 2; this.ctx.stroke(); - this.ctxCursor.clearRect(0, 0, this.width, this.height); - this.ctxCursor.beginPath(); - this.ctxCursor.moveTo(this.lastMouse.x, this.height); - let v = this.data.values[Math.round(this.lastMouse.x / this.scaleX)] - v = this.height - (v - this.minY) * this.scaleY - this.ctxCursor.lineTo(this.lastMouse.x, v); - this.ctxCursor.strokeStyle = "yellow"; - this.ctxCursor.lineWidth = 2; - this.ctxCursor.stroke() + this.ctxCursor.clearRect(0, 0, w, this.height); + + if (this.lastMouse) { + this.ctxCursor.beginPath(); + this.ctxCursor.moveTo(this.lastMouse.x, this.height); + let v = this.data.values[Math.round(this.lastMouse.x / this.scaleX)] + v = this.height - (v - this.minY) * this.scaleY; + this.ctxCursor.lineTo(this.lastMouse.x, v); + this.ctxCursor.strokeStyle = "yellow"; + this.ctxCursor.lineWidth = 2; + this.ctxCursor.stroke() + } this._redrawLabels() } @@ -697,17 +756,20 @@ export class SpectraDisplayer extends DOMElement { if (Math.abs(value) >= 1 || unit === units[units.length - 1].unit) { // Calculate number of decimal places needed to show the given precision - const decimals = Math.max(0, Math.ceil(-Math.log10(precisionInUnit))); + const decimals = Math.min(8, Math.max(0, Math.ceil(-Math.log10(precisionInUnit)))); return value.toFixed(decimals) + " " + unit; } } } + let w = this.view.aladin.aladinDiv.getBoundingClientRect().width; + // Clear previous drawing - this.ctxLabels.clearRect(0, 0, this.width, this.height); + this.ctxLabels.clearRect(0, 0, w, this.height); let drawLabel = (ctx, str, x, y, strokeStyle, font, fillStyle) => { ctx.strokeStyle = strokeStyle; // contour color + ctx.lineWidth = 3; ctx.strokeText(str, x, y); ctx.fillStyle = fillStyle; @@ -722,11 +784,14 @@ export class SpectraDisplayer extends DOMElement { // min window freq this.ctxLabels.textAlign = "left"; // Horizontally centered + let fmin = spectraValue2String(this.data.freqMin, this.data.freqs[1] - this.data.freqs[0]); + let fmax = spectraValue2String(this.data.freqMax, this.data.freqs[this.data.freqs.length - 1] - this.data.freqs[this.data.freqs.length - 2]); + drawLabel( this.ctxLabels, - spectraValue2String(this.data.freqMin, this.data.freqs[1] - this.data.freqs[0]), + this.unit === SpectraDisplayer.UNIT.FREQUENCY ? fmin : fmax, 0, - this.height - 20, + this.height - 60, 'black', '20px monospace', 'lightgreen' @@ -736,9 +801,9 @@ export class SpectraDisplayer extends DOMElement { this.ctxLabels.textAlign = "right"; // Horizontally centered drawLabel( this.ctxLabels, - spectraValue2String(this.data.freqMax, this.data.freqs[this.data.freqs.length - 1] - this.data.freqs[this.data.freqs.length - 2]), - this.width, - this.height - 20, + this.unit === SpectraDisplayer.UNIT.FREQUENCY ? fmax : fmin, + w, + this.height - 60, 'black', '20px monospace', 'lightgreen' @@ -757,8 +822,8 @@ export class SpectraDisplayer extends DOMElement { drawLabel( this.ctxLabels, str, - this.width / 2, - this.height - 20, + w / 2, + this.height - 10, 'black', '20px monospace', fillStyle diff --git a/src/js/Utils.ts b/src/js/Utils.ts index c524c82a..71669471 100644 --- a/src/js/Utils.ts +++ b/src/js/Utils.ts @@ -431,6 +431,23 @@ Utils.fetch = function(params) { }) } +Utils.fetchWithProxy = function(params) { + return Utils.fetch({ + ...params, + error: (e) => { + console.error(e) + console.info("Trying querying the proxy:" + JSONP_PROXY) + // Try as CORS + const url = JSONP_PROXY + '?url=' + params.url; + console.log(url) + return Utils.fetch({ + ...params, + url, + }); + } + }) +} + Utils.on = function(element, events, callback) { events.split(' ') .forEach(e => { diff --git a/src/js/View.js b/src/js/View.js index 5f9ea7b5..1b404872 100644 --- a/src/js/View.js +++ b/src/js/View.js @@ -105,6 +105,11 @@ export let View = (function () { this.debounceResize = Utils.debounce(() => { self.wasm.resize(self.width, self.height); self.updateZoomState() + + if (self.spectraDisplayer) { + self.spectraDisplayer.updateCanvas() + self.spectraDisplayer._redraw() + } }, 2); // Attach the drag and drop events to the view @@ -318,7 +323,7 @@ export let View = (function () { Object.defineProperties(this, { fov: { get() { - return this.wasm.getFieldOfView(); + return this.wasm.getFieldOfView()[0]; }, set(newFov) { this.setFoV(newFov); @@ -601,7 +606,7 @@ export let View = (function () { if (imageLayer.dataproductType === "spectral-cube") { if (!this.spectraDisplayer) { - this.spectraDisplayer = new SpectraDisplayer(this, {width: 800, height: 300}); + this.spectraDisplayer = new SpectraDisplayer(this, {height: 250}); } this.spectraDisplayer.attachHiPS3D(imageLayer) @@ -641,16 +646,19 @@ export let View = (function () { // prevent default context menu from appearing (potential clash with right-click cuts control) Utils.on(view.catalogCanvas, "contextmenu", function (e) { e.preventDefault(); - let ctxMenu = view.aladin.contextMenu; - if(ctxMenu) { - e.stopPropagation(); - if (!view.rightClick && showContextMenu) { - ctxMenu.attach( - DefaultActionsForContextMenu.getDefaultActions(view.aladin), - null - ); - ctxMenu._show({e}); - } + + if (view.aladin.options.showContextMenu) { + let ctxMenu = view.aladin.contextMenu; + if(ctxMenu) { + e.stopPropagation(); + if (!view.rightClick && showContextMenu) { + ctxMenu.attach( + DefaultActionsForContextMenu.getDefaultActions(view.aladin), + null + ); + ctxMenu._show({e}); + } + } } }); @@ -941,7 +949,7 @@ export let View = (function () { if (view.rightClick) { let ctxMenu = view.aladin.contextMenu; - if (showContextMenu && ctxMenu) { + if (showContextMenu && ctxMenu && view.aladin.options.showContextMenu) { ctxMenu.attach( DefaultActionsForContextMenu.getDefaultActions(view.aladin), null @@ -1760,10 +1768,12 @@ export let View = (function () { this.computeNorder(); let fovX = this.fov; - let fovY = this.height / this.width * fovX; + let fovY = this.wasm.getFieldOfView()[1]; fovX = Math.min(fovX, 360); fovY = Math.min(fovY, 180); + this.fovY = fovY; + this.debounceProgCatOnZoom(); ALEvent.ZOOM_CHANGED.dispatchedTo(this.aladinDiv, { fovX, fovY }); @@ -1801,27 +1811,6 @@ export let View = (function () { // register its promise this.imageLayersBeingQueried.set(layer, imageLayer); - // Check whether this layer already exist - const idxOverlayLayer = this.overlayLayers.findIndex(overlayLayer => overlayLayer == layer); - let alreadyPresentImageLayer; - if (idxOverlayLayer == -1) { - // it does not exist so we add it to the stack - this.overlayLayers.push(layer); - } else { - // it exists - alreadyPresentImageLayer = this.imageLayers.get(layer); - - if (alreadyPresentImageLayer) { - if (alreadyPresentImageLayer.added === true) { - ALEvent.LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: alreadyPresentImageLayer }); - } - - alreadyPresentImageLayer.added = false; - } - // Notify that this image layer has been replaced by the wasm part - this.imageLayers.delete(layer); - } - this.addImageLayer(imageLayer, layer); return imageLayer; @@ -1833,6 +1822,25 @@ export let View = (function () { const layer = imageLayer.layer; imageLayer.added = true; + const idxOverlayLayer = this.overlayLayers.findIndex(overlayLayer => overlayLayer == layer); + if (idxOverlayLayer === -1) { + this.overlayLayers.push(layer); + } else { + // layer already present + // it exists + var alreadyPresentImageLayer = this.imageLayers.get(layer); + + if (alreadyPresentImageLayer) { + //let idx = this.removeImageLayer(layer) + //if (idx >= 0) { + // this.overlayLayers.splice(idx, 0, layer); + //} + } + + // Notify that this image layer has been replaced by the wasm part + //this.imageLayers.delete(layer); + } + this.imageLayers.set(layer, imageLayer); // select the layer if he is on top @@ -1841,6 +1849,21 @@ export let View = (function () { ALEvent.LAYER_ADDED.dispatchedTo(this.aladinDiv, { layer: imageLayer }); } + View.prototype._waitsForLayer = function() { + return this.promises.length !== 0; + } + + View.prototype.getFirstLayer = function() { + let layer = this.overlayLayers && this.overlayLayers[0]; + + if (!layer) { + // the overlay has not yet been added and is currently queried. + layer = this.imageLayersBeingQueried.keys().next().value; + } + + return layer; + } + View.prototype.addImageLayer = function (imageLayer, layer) { let self = this; // start the query @@ -1883,6 +1906,8 @@ export let View = (function () { imageLayer.errorCallback(e); } + this.removeImageLayer(imageLayer); + throw e; }) .finally(() => { @@ -1894,8 +1919,15 @@ export let View = (function () { // Remove the settled promise this.promises.splice(idx, 1); - const noMoreLayersToWaitFor = this.promises.length === 0; - if (noMoreLayersToWaitFor && self.empty) { + const waitsForLayer = this._waitsForLayer(); + if (!waitsForLayer && this.delayedBaseLayerCalledParams && this.delayedBaseLayerCalledParams !== layer) { + this.aladin.setBaseImageLayer(this.delayedBaseLayerCalledParams) + this.delayedBaseLayerCalledParams = null; + + return; + } + + if (!waitsForLayer && self.empty) { // no promises to launch and the view has no HiPS. // This situation can occurs if the MOCServer is out // If so we can directly put the url of the DSS hosted in alasky, @@ -1933,7 +1965,7 @@ export let View = (function () { if (imageLayer === undefined) { // there is nothing to remove - return; + return -1; } // Update the backend @@ -1941,26 +1973,22 @@ export let View = (function () { // Get the survey to remove to dissociate it from the view imageLayer.added = false; - // Delete it - this.imageLayers.delete(layer); const idxOverlaidLayer = this.overlayLayers.findIndex(overlaidLayer => overlaidLayer == layer); - if (idxOverlaidLayer == -1) { - // layer not found - return; - } + // Delete it + this.imageLayers.delete(layer); // Remove it from the layer stack this.overlayLayers.splice(idxOverlaidLayer, 1); - if (this.overlayLayers.length === 0) { - //this.empty = true; - } else if (this.selectedLayer === layer) { + if (this.overlayLayers.length > 0 && this.selectedLayer === layer) { // If the layer removed was selected then we select the last layer this.selectLayer(this.overlayLayers[this.overlayLayers.length - 1]); } ALEvent.LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: imageLayer }); + + return idxOverlaidLayer; }; View.prototype.contains = function(survey) { @@ -2014,8 +2042,7 @@ export let View = (function () { let obj = imageLayer || imageLayerQueried; - if (obj && obj.added) - return obj; + return obj; }; View.prototype.requestRedraw = function () { diff --git a/src/js/events/ALEvent.js b/src/js/events/ALEvent.js index 8241542a..fe02bcec 100644 --- a/src/js/events/ALEvent.js +++ b/src/js/events/ALEvent.js @@ -58,7 +58,7 @@ export class ALEvent { static LAYER_CHANGED = new ALEvent("AL:Layer.changed"); static HIPS_CACHE_UPDATED = new ALEvent("AL:HiPSCache.updated"); - static FAVORITE_HIPS_LIST_UPDATED = new ALEvent("AL:HiPSFavorites.updated"); + static FAVORITE_LAYERS_LIST_UPDATED = new ALEvent("AL:HiPSFavorites.updated"); static GRAPHIC_OVERLAY_LAYER_ADDED = new ALEvent("AL:GraphicOverlayLayer.added"); static GRAPHIC_OVERLAY_LAYER_REMOVED = new ALEvent("AL:GraphicOverlayLayer.removed"); diff --git a/src/js/gui/Box/CatalogQueryBox.js b/src/js/gui/Box/CatalogQueryBox.js index 27cda75c..afa6e0c8 100644 --- a/src/js/gui/Box/CatalogQueryBox.js +++ b/src/js/gui/Box/CatalogQueryBox.js @@ -43,15 +43,19 @@ import { ActionButton } from "../Widgets/ActionButton.js"; export class CatalogQueryBox extends Box { static catalogs = {}; + static catTitle2Id = {}; constructor(aladin, options) { // Query the mocserver MocServer.getAllCatalogHiPSes() .then((catalogs) => { catalogs.forEach((cat) => { - CatalogQueryBox.catalogs[cat.obs_title] = cat; + //console.log(cat) + CatalogQueryBox.catalogs[cat.ID] = cat; + CatalogQueryBox.catTitle2Id[cat.obs_title] = cat.ID; }); - searchDropdown.update({options: Object.keys(CatalogQueryBox.catalogs)}) + //console.log(CatalogQueryBox.catalogs) + searchDropdown.update({options: Object.keys(CatalogQueryBox.catTitle2Id)}) }) const fnIdSelected = function(type, params) { @@ -130,7 +134,8 @@ import { ActionButton } from "../Widgets/ActionButton.js"; }) } catch (e) { // Or he can select a HiPS from the list given - const catalog = CatalogQueryBox.catalogs[value]; + const catID = CatalogQueryBox.catTitle2Id[value]; + const catalog = CatalogQueryBox.catalogs[catID]; if (catalog) { self._selectItem(catalog, aladin); diff --git a/src/js/gui/Box/HiPSCompositeBox.js b/src/js/gui/Box/HiPSCompositeBox.js index 6ca40f12..39ddd51f 100644 --- a/src/js/gui/Box/HiPSCompositeBox.js +++ b/src/js/gui/Box/HiPSCompositeBox.js @@ -22,7 +22,7 @@ import hipsIconUrl from "../../../../assets/icons/hips.svg"; import addIconUrl from "../../../../assets/icons/plus.svg"; import settingsIconUrl from "../../../../assets/icons/settings.svg"; import { Icon } from "../Widgets/Icon.js"; -import { HiPSSelector } from "../Input/HiPSSelector.js"; +import { LayerSelector } from "../Input/LayerSelector.js"; import { HiPSBrowserBox } from "./HiPSBrowserBox.js"; import { Input } from "../Widgets/Input.js"; import { ActionButton } from "../Widgets/ActionButton.js"; @@ -131,7 +131,7 @@ export class HiPSCompositeBox extends Box { this.hipsOptions.push({}); let self = this; let newLayerLayout = [ - new HiPSSelector({ + new LayerSelector({ change(e) { let name = e.target.value; let idLayer = getIdHiPS(e.target); @@ -149,7 +149,7 @@ export class HiPSCompositeBox extends Box { }); } else { // it is an hips - let HiPSOptions = HiPSSelector.cachedHiPS[name]; + let HiPSOptions = LayerSelector.cachedLayers[name]; self.hipsOptions[idLayer].id = HiPSOptions.id || HiPSOptions.url } diff --git a/src/js/gui/Box/HiPSSettingsBox.js b/src/js/gui/Box/HiPSSettingsBox.js index 5f6152a6..31d0c045 100644 --- a/src/js/gui/Box/HiPSSettingsBox.js +++ b/src/js/gui/Box/HiPSSettingsBox.js @@ -40,6 +40,7 @@ import { Form } from "../Widgets/Form.js"; import waveOnIconUrl from '../../../../assets/icons/wave-on.svg'; import { WidgetTogglerButton } from "../Button/Toggler.js"; import { Layout } from "../Layout.js"; + import { Utils } from "../../Utils"; export class HiPSSettingsBox extends Box { // Constructor @@ -204,7 +205,7 @@ import { WidgetTogglerButton } from "../Button/Toggler.js"; value: 'linear', options: ['sqrt', 'linear', 'asinh', 'pow2', 'log'], change(e) { - self.options.layer.setColormap(self.options.layer.getColorCfg().getColormap(), {stretch: e.target.value}); + self.options.layer.setColormap(self.options.layer.getColormap(), {stretch: e.target.value}); }, tooltip: {content: 'stretch function', position: {direction: 'bottom'}} }, @@ -216,7 +217,7 @@ import { WidgetTogglerButton } from "../Button/Toggler.js"; value: 0.0, change: (e) => { let minCut = +e.target.value - self.options.layer.setCuts(minCut, self.options.layer.getColorCfg().getCuts()[1]) + self.options.layer.setCuts(minCut, self.options.layer.getCuts()[1]) }, cssStyle: { width: '7rem' } }, @@ -228,7 +229,7 @@ import { WidgetTogglerButton } from "../Button/Toggler.js"; value: 1.0, change: (e) => { let maxCut = +e.target.value - self.options.layer.setCuts(self.options.layer.getColorCfg().getCuts()[0], maxCut) + self.options.layer.setCuts(self.options.layer.getCuts()[0], maxCut) }, cssStyle: { width: '7rem' } }] @@ -281,17 +282,19 @@ import { WidgetTogglerButton } from "../Button/Toggler.js"; } _update(layer) { - let colorCfg = layer.getColorCfg(); - let stretch = colorCfg.stretch; - let colormap = colorCfg.getColormap(); - let reversed = colorCfg.getReversed(); + let stretch = layer.stretch; + let colormap = layer.getColormap(); + let reversed = layer.getReversed(); - let [minCut, maxCut] = colorCfg.getCuts(); - if (minCut) + let [minCut, maxCut] = layer.getCuts(); + if (Utils.isNumber(minCut)) { this.pixelSettingsContent.set('mincut', +minCut.toFixed(4)) - if (maxCut) + } + + if (Utils.isNumber(maxCut)) { this.pixelSettingsContent.set('maxcut', +maxCut.toFixed(4)) - + } + this.pixelSettingsContent.set('stretch', stretch) let fmtInput = this.pixelSettingsContent.getInput('fmt') fmtInput.innerHTML = ''; @@ -309,10 +312,10 @@ import { WidgetTogglerButton } from "../Button/Toggler.js"; this.colorSettingsContent.set('reverse', reversed); this.opacitySettingsContent.set('opacity', layer.getOpacity()); - this.luminositySettingsContent.set('brightness', colorCfg.getBrightness()); - this.luminositySettingsContent.set('contrast', colorCfg.getContrast()); - this.luminositySettingsContent.set('gamma', colorCfg.getGamma()); - this.luminositySettingsContent.set('saturation', colorCfg.getSaturation()); + this.luminositySettingsContent.set('brightness', layer.getBrightness()); + this.luminositySettingsContent.set('contrast', layer.getContrast()); + this.luminositySettingsContent.set('gamma', layer.getGamma()); + this.luminositySettingsContent.set('saturation', layer.getSaturation()); } update(options) { diff --git a/src/js/gui/Box/StackBox.js b/src/js/gui/Box/StackBox.js index f6ced4cf..5eab54a8 100644 --- a/src/js/gui/Box/StackBox.js +++ b/src/js/gui/Box/StackBox.js @@ -58,7 +58,7 @@ import { HiPSCompositeBox } from "./HiPSCompositeBox.js" import { Catalog } from "../../Catalog.js"; import { ProgressiveCat } from "../../ProgressiveCat.js"; import { Form } from "../Widgets/Form.js"; -import { HiPSSelector } from "./../Input/HiPSSelector.js"; +import { LayerSelector } from "../Input/LayerSelector.js"; import { HiPS } from "../../HiPS.js"; export class OverlayStackBox extends Box { @@ -541,11 +541,6 @@ export class OverlayStackBox extends Box { aladin.hipsBrowser._show({ selected: (hips) => { - let oldHiPS = aladin.getOverlayImageLayer(newLayer); - if (oldHiPS && hips.id === oldHiPS.id) { - return; - } - aladin.setOverlayImageLayer(hips, newLayer); }, position: { @@ -912,9 +907,10 @@ export class OverlayStackBox extends Box { } } - let spectraDisplayer = aladin.view.spectraDisplayer; - if (spectraDisplayer) + /*let spectraDisplayer = aladin.view.spectraDisplayer; + if (spectraDisplayer) { spectraDisplayer.attachHiPS3D(options.layer) + } */ }, widget: catSettingsBox, openDirection: "right" @@ -967,7 +963,7 @@ export class OverlayStackBox extends Box { continue; } - let HiPSselect = new HiPSSelector({ + let layerSelect = new LayerSelector({ layer: hips, change(e) { let name = e.target.value; @@ -976,10 +972,12 @@ export class OverlayStackBox extends Box { if (!aladin.hipsBrowser) { aladin.hipsBrowser = new HiPSBrowserBox(aladin); } - + + let newLayer = Utils.uuidv4(); + aladin.hipsBrowser._show({ selected: (hips) => { - self.aladin.setOverlayImageLayer(hips, hips.layer); + self.aladin.setOverlayImageLayer(hips, newLayer); }, position: { anchor: "center center" } }); @@ -987,10 +985,15 @@ export class OverlayStackBox extends Box { } let overlayLayer; - if (name in HiPSSelector.cachedHiPS) { + if (name in LayerSelector.cachedLayers) { // it is an hips - let HiPSOptions = HiPSSelector.cachedHiPS[name]; - overlayLayer = A.HiPS(HiPSOptions.id || HiPSOptions.url, HiPSOptions); + let layerOptions = LayerSelector.cachedLayers[name]; + //if (layerOptions.type === "hips") { + // overlayLayer = A.HiPS(layerOptions.id || layerOptions.url, layerOptions); + //} else if (layerOptions.type === "image") { + // overlayLayer = A.image(layerOptions.url, layerOptions); + //} + overlayLayer = layerOptions.id; } else { overlayLayer = hips } @@ -1121,12 +1124,12 @@ export class OverlayStackBox extends Box { } btns = btns.concat([swapBtn, deleteBtn]); - let item = Layout.horizontal([HiPSselect, Layout.horizontal(btns)]); + let item = Layout.horizontal([layerSelect, Layout.horizontal(btns)]); layout.push(item); if (!(hips.layer in self.ui)) { self.ui[hips.layer] = { - HiPSSelector: HiPSselect, + //layerSelector: layerSelect, settingsBox, settingsBtn, showBtn, diff --git a/src/js/gui/Button/CtxMenuOpener.js b/src/js/gui/Button/CtxMenuOpener.js index 7e479086..87a6179b 100644 --- a/src/js/gui/Button/CtxMenuOpener.js +++ b/src/js/gui/Button/CtxMenuOpener.js @@ -51,7 +51,7 @@ export class CtxMenuActionButtonOpener extends WidgetTogglerButton { widget: aladin.contextMenu, enable(e) { // If it was hidden then reopen it - if (self.layout) { + if (self.layout && self.ctxMenu) { self.ctxMenu.attach(self.layout, self) } }, diff --git a/src/js/gui/Button/Toggler.js b/src/js/gui/Button/Toggler.js index c322560f..b23f659f 100644 --- a/src/js/gui/Button/Toggler.js +++ b/src/js/gui/Button/Toggler.js @@ -113,9 +113,11 @@ export class TogglerActionButton extends ActionButton { if (enable) enable(o) - widget._show({ - position: self.position - }) + if (widget) { + widget._show({ + position: self.position + }) + } }, off: (_) => { self.close(); @@ -126,12 +128,15 @@ export class TogglerActionButton extends ActionButton { this.update(options) - widget.setToggler(this); + if (widget) + widget.setToggler(this); + this.widget = widget; } close() { - this.widget._hide(); + if (this.widget) + this.widget._hide(); super.close() } diff --git a/src/js/gui/FoV.js b/src/js/gui/FoV.js index b3216378..778275dd 100644 --- a/src/js/gui/FoV.js +++ b/src/js/gui/FoV.js @@ -23,9 +23,9 @@ /****************************************************************************** * Aladin Lite project * - * File Location.js + * File FoV.js * - * Author: Thomas Boch[CDS] + * Author: Matthieu Baumann[CDS] * *****************************************************************************/ import { Numbers } from "../libs/astro/coo.js"; @@ -48,7 +48,7 @@ export class FoV extends DOMElement { let zoomIn = new ActionButton({ classList: 'aladin-zoom-in', size: 'small', - tooltip: {content: 'zoom in', position: {direction: 'top'}}, + tooltip: {content: 'zoom in', position: {direction: 'left'}}, icon: { monochrome: true, size: 'small', @@ -61,7 +61,7 @@ export class FoV extends DOMElement { let zoomOut = new ActionButton({ size: 'small', classList: 'aladin-zoom-out', - tooltip: {content: 'zoom out', position: {direction: 'top'}}, + tooltip: {content: 'zoom out', position: {direction: 'left'}}, icon: { monochrome: true, size: 'small', @@ -74,14 +74,23 @@ export class FoV extends DOMElement { zoomIn.el.classList.add('aladin-zoom-in'); zoomOut.el.classList.add('aladin-zoom-out'); - layout.push(zoomOut) - layout.push(zoomIn) + let aladinZoomDiv = document.createElement("div") + aladinZoomDiv.classList.add('aladin-zoom') + aladinZoomDiv.appendChild(zoomIn.element()); + aladinZoomDiv.appendChild(zoomOut.element()); + + aladin.aladinDiv.appendChild(aladinZoomDiv); + + //layout.push(zoomOut) + //layout.push(zoomIn) } if (options.showFov) { - layout.push(...['
    ', - '
    ×
    ', - '
    ']) + layout.push( + '
    ' + + '
    ×
    ' + + '
    ' + ) } let el = Layout.horizontal(layout); diff --git a/src/js/gui/Input/HiPSSelector.js b/src/js/gui/Input/LayerSelector.js similarity index 70% rename from src/js/gui/Input/HiPSSelector.js rename to src/js/gui/Input/LayerSelector.js index e53367b0..d983feac 100644 --- a/src/js/gui/Input/HiPSSelector.js +++ b/src/js/gui/Input/LayerSelector.js @@ -20,7 +20,7 @@ /****************************************************************************** * Aladin Lite project * - * File gui/Input/HiPSSelector.js + * File gui/Input/LayerSelector.js * * * Author: Matthieu Baumann[CDS] @@ -30,14 +30,14 @@ import { ALEvent } from "../../events/ALEvent.js"; import { Input } from "../Widgets/Input.js"; -export class HiPSSelector extends Input { - static cachedHiPS = {} +export class LayerSelector extends Input { + static cachedLayers = {} static objects = []; // constructor constructor(options) { let surveys = []; - for (var survey of Object.values(HiPSSelector.cachedHiPS)) { + for (var survey of Object.values(LayerSelector.cachedLayers)) { surveys.push({value: survey.id, label: survey.name}); } surveys.sort((s1, s2) => { @@ -82,31 +82,31 @@ export class HiPSSelector extends Input { self = this; - HiPSSelector.objects.push(self); + LayerSelector.objects.push(self); } }; (function () { - ALEvent.FAVORITE_HIPS_LIST_UPDATED.listenedBy(document, (event) => { - let favoritesHips = event.detail; + ALEvent.FAVORITE_LAYERS_LIST_UPDATED.listenedBy(document, (event) => { + let favoritesLayer = event.detail; - HiPSSelector.cachedHiPS = {}; + LayerSelector.cachedLayers = {}; - for (var hips of favoritesHips) { - let key = hips.id || hips.url || hips.name; - HiPSSelector.cachedHiPS[key] = hips; + for (var layer of favoritesLayer) { + let key = layer.id || layer.url || layer.name; + LayerSelector.cachedLayers[key] = layer; } // Update the options of the selector - let favoritesHiPS = [] - for(var hips of Object.values(HiPSSelector.cachedHiPS)) { - favoritesHiPS.push({ - value: hips.id, - label: hips.name + let favorites = [] + for(var layer of Object.values(LayerSelector.cachedLayers)) { + favorites.push({ + value: layer.id, + label: layer.name }) } - favoritesHiPS.sort((s1, s2) => { + favorites.sort((s1, s2) => { const s1l = s1.label.toLowerCase() const s2l = s2.label.toLowerCase() @@ -120,28 +120,28 @@ export class HiPSSelector extends Input { return 0; }); - for (var selector of HiPSSelector.objects) { + for (var selector of LayerSelector.objects) { // refers to an HiPS image survey - let currentFavoriteHiPS = { + let currentFavorite = { value: selector.options.value, label: selector.options.label, }; - let favoritesHiPSCopy = [...favoritesHiPS]; + let favoritesCopy = [...favorites]; // Add the current hips to the selector as well, even if it has been manually // removed from the HiPSList - if (!favoritesHiPSCopy.some(item => item.value === currentFavoriteHiPS.value)) { - favoritesHiPSCopy.push(currentFavoriteHiPS) + if (!favoritesCopy.some(item => item.value === currentFavorite.value)) { + favoritesCopy.push(currentFavorite) } - favoritesHiPSCopy.push("More...") + favoritesCopy.push("More...") - currentFavoriteHiPS["title"] = currentFavoriteHiPS["label"]; + currentFavorite["title"] = currentFavorite["label"]; selector.update({ - ...currentFavoriteHiPS, - options: favoritesHiPSCopy + ...currentFavorite, + options: favoritesCopy }); } }); diff --git a/src/js/libs/avm.js b/src/js/libs/avm.js index 8ef5695d..a669a47c 100644 --- a/src/js/libs/avm.js +++ b/src/js/libs/avm.js @@ -6,7 +6,9 @@ * * Licensed under the MPL http://www.mozilla.org/MPL/MPL-1.1.txt * - * S + * This code has been adapted to the use of Aladin Lite. It features: + * - fixes that has been discovered with the testing of multiple images found. + * - The support of PNGs */ export let AVM = (function() { @@ -117,7 +119,13 @@ export let AVM = (function() { const findAVM = (arrayBuffer) => { const view = new DataView(arrayBuffer); - var oAVM = _obj.findAVMinJPEG(view); + // See the magic bytes for JPEG or PNG files + var oAVM; + if (view.getUint8(0) === 0xFF && view.getUint8(1) === 0xD8) + oAVM = _obj.findAVMinJPEG(view); + if (view.getUint8(0) === 0x89 && view.getUint8(1) === 0x50) + oAVM = _obj.findAVMinPNG(view); + _obj.wcs = oAVM || {}; _obj.wcsdata = _obj.wcs !== undefined && Object.keys(_obj.wcs).length > 0; @@ -136,7 +144,6 @@ export let AVM = (function() { findAVM(arrayBuffer) }) } - } function addEvent(oElement, strEvent, fncHandler){ @@ -172,6 +179,77 @@ export let AVM = (function() { } } + AVM.prototype.findAVMinPNG = function(oFile) { + // PNG signature: 8 bytes - 0x89 PNG \r \n 0x1a \n + if ( + oFile.getUint8(0) !== 0x89 || + oFile.getUint8(1) !== 0x50 || // 'P' + oFile.getUint8(2) !== 0x4E || // 'N' + oFile.getUint8(3) !== 0x47 || // 'G' + oFile.getUint8(4) !== 0x0D || + oFile.getUint8(5) !== 0x0A || + oFile.getUint8(6) !== 0x1A || + oFile.getUint8(7) !== 0x0A + ) return false; // not a valid PNG + + var iOffset = 8; // skip the 8-byte PNG signature + var iLength = oFile.byteLength; + + while (iOffset < iLength) { + // Each PNG chunk: [4 bytes length][4 bytes type][data][4 bytes CRC] + var iChunkLength = oFile.getUint32(iOffset, false); // big-endian + + // Read the 4-byte chunk type as a string + var sChunkType = + String.fromCharCode(oFile.getUint8(iOffset + 4)) + + String.fromCharCode(oFile.getUint8(iOffset + 5)) + + String.fromCharCode(oFile.getUint8(iOffset + 6)) + + String.fromCharCode(oFile.getUint8(iOffset + 7)); + + if (sChunkType === 'iTXt' || sChunkType === 'tEXt' || sChunkType === 'zTXt') { + // AVM data is typically stored in an iTXt chunk with keyword "AVM" + // iTXt layout: [keyword]\0[compression flag][compression method][language]\0[translated keyword]\0[text] + // tEXt layout: [keyword]\0[text] + var iDataOffset = iOffset + 8; // skip length (4) + type (4) + var iDataEnd = iDataOffset + iChunkLength; + + // Read the keyword (null-terminated) + var sKeyword = ''; + var i = iDataOffset; + while (i < iDataEnd && oFile.getUint8(i) !== 0x00) { + sKeyword += String.fromCharCode(oFile.getUint8(i)); + i++; + } + + //if (sKeyword === 'AVM') { + // Skip past the null terminator + i++; // now at compression flag (iTXt) or text start (tEXt) + + if (sChunkType === 'iTXt') { + // Skip: compression flag (1) + compression method (1) + i += 2; + // Skip language tag (null-terminated) + while (i < iDataEnd && oFile.getUint8(i) !== 0x00) i++; + i++; // skip null + // Skip translated keyword (null-terminated) + while (i < iDataEnd && oFile.getUint8(i) !== 0x00) i++; + i++; // skip null + } + + // i now points to the start of the actual AVM text data + return this.readAVMDataAsWCS(oFile, i, iDataEnd - i); + //} + } + + if (sChunkType === 'IEND') break; // end of PNG, stop searching + + // Move to the next chunk: length (4) + type (4) + data + CRC (4) + iOffset += 4 + 4 + iChunkLength + 4; + } + + return false; + } + AVM.prototype.readAVMDataAsWCS = function(oFile) { var tags = undefined; @@ -200,7 +278,7 @@ export let AVM = (function() { if (unwindTag(tags['Spatial.Equinox'])) wcs.EQUINOX = +unwindTag(tags['Spatial.Equinox']); - wcs.NAXIS = tags['Spatial.ReferenceDimension'] && +tags['Spatial.ReferenceDimension'].length; + wcs.NAXIS = 2; wcs.NAXIS1 = tags['Spatial.ReferenceDimension'] && +tags['Spatial.ReferenceDimension'][0]; wcs.NAXIS2 = tags['Spatial.ReferenceDimension'] && +tags['Spatial.ReferenceDimension'][1]; @@ -219,9 +297,9 @@ export let AVM = (function() { wcs.CROTA2 = +unwindTag(tags['Spatial.Rotation']); } } - - wcs.CRPIX1 = tags['Spatial.ReferencePixel'] && +tags['Spatial.ReferencePixel'][0]; - wcs.CRPIX2 = tags['Spatial.ReferencePixel'] && +tags['Spatial.ReferencePixel'][1]; + const spatialRef = tags['Spatial.ReferencePixel'] + wcs.CRPIX1 = spatialRef && +spatialRef[0] + wcs.CRPIX2 = spatialRef && +spatialRef[1] wcs.CRVAL1 = tags['Spatial.ReferenceValue'] && +tags['Spatial.ReferenceValue'][0]; wcs.CRVAL2 = tags['Spatial.ReferenceValue'] && +tags['Spatial.ReferenceValue'][1]; diff --git a/src/js/vo/Datalink.js b/src/js/vo/Datalink.js index 160f1ec7..f222b046 100644 --- a/src/js/vo/Datalink.js +++ b/src/js/vo/Datalink.js @@ -163,15 +163,15 @@ export let Datalink = (function() { let idxSlice = +properties.hips_cube_firstframe; let updateSlice = () => { - let colorCfg = aladinInstance.getOverlayImageLayer(layer).getColorCfg(); + let pHiPS = aladinInstance.getOverlayImageLayer(layer); let hips = aladinInstance.setOverlayImageLayer(cubeOnTheFlyUrl + idxSlice, layer) hips.setOptions({ - opacity: colorCfg.opacity, - minCut: colorCfg.minCut, - maxCut: colorCfg.maxCut, - colormap: colorCfg.colormap, - stretch: colorCfg.stretch, - reversed: colorCfg.reversed + opacity: pHiPS.opacity, + minCut: pHiPS.minCut, + maxCut: pHiPS.maxCut, + colormap: pHiPS.colormap, + stretch: pHiPS.stretch, + reversed: pHiPS.reversed }) slicer.update({