diff --git a/examples/al-init-custom-options.html b/examples/al-init-custom-options.html index e36368d0..d3aa4b8a 100644 --- a/examples/al-init-custom-options.html +++ b/examples/al-init-custom-options.html @@ -23,6 +23,8 @@ reticleSize: 64, // change reticle size showContextMenu: true, showShareControl: true, + showCooGridControl: true, + showColorPickerControl: true, showFrame: true, showZoomControl:true, showSettingsControl:true, diff --git a/src/core/src/shaders.rs b/src/core/src/shaders.rs index 90efa7c8..d5c4cfa0 100644 --- a/src/core/src/shaders.rs +++ b/src/core/src/shaders.rs @@ -3,18 +3,22 @@ use std::collections::HashMap; pub fn get_all() -> HashMap<&'static str, &'static str> { let mut out = HashMap::new(); out.insert( - r"hips3d_raster.vert", + 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; -layout (location = 0) in vec2 lonlat; -layout (location = 1) in vec3 uv; - -out vec3 frag_uv; - +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; @@ -117,177 +121,1177 @@ vec2 proj(vec3 p) { } } + void main() { - vec3 p_xyz = lonlat2xyz(lonlat); - vec3 p_w = inv_model * p_xyz; - vec2 p_clip = proj(p_w.xyz); + vec3 p = inv_model * center; - vec2 p_ndc = p_clip / (ndc_to_clip * czf); - gl_Position = vec4(p_ndc, 0.0, 1.0); + 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"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"image_base.vert", + r#"#version 300 es +precision highp float; + +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_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_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_backcolor.frag", + 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 vec3 color; +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))); + 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 || isnan(x))); + 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() { - out_frag_color = vec4(color, 1.0); + 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"hips3d_f32.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))); + 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 || isnan(x))); + 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 val2c(val); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return val2c(val); +} + +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_f32(uv); + + out_frag_color = color; + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + 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))); + 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 || isnan(x))); + 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 val2c(val); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return val2c(val); +} + +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"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))); + 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 || isnan(x))); + 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 val2c(val); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return val2c(val); +} + +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( @@ -562,197 +1566,793 @@ void main() { }"#, ); out.insert( - r"catalogs_catalog.frag", + r"hips_raytracer_rgba.frag", r#"#version 300 es precision lowp float; +precision lowp sampler2DArray; +precision mediump int; -in vec2 out_uv; -in vec3 out_p; +uniform sampler2DArray tex; -out vec4 color; +in vec2 out_clip_pos; +in vec3 frag_pos; +out vec4 out_frag_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"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; +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 mat3 u_2world; -uniform vec2 ndc_to_clip; -uniform float czf; -uniform float u_width; -uniform float u_height; -uniform float u_thickness; +uniform Tile textures_tiles[12]; -out vec2 l; +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))); + 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 || isnan(x))); + 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 val2c(val); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return val2c(val); +} + +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 SQRT_2 = 1.41421356237309504880168872420969808; +const float FOUR_OVER_PI = 1.27323954474; +const float TRANSITION_Z = 0.66666666666; +const float TRANSITION_Z_INV = 1.5; -vec2 w2c_sin(vec3 p) { - vec2 q = vec2(-p.x, p.y); - return p.z >= 0.f ? q : normalize(q); +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; } -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; +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 { - w = sqrt((r*r - r*p.z) * 2.0) / w; // = 2 * gamma * cos(b) sin(l/2) - x2d = sign(-p.x) * w; + return x02 - 1.0; } - - 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; +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 0.5 * x; + return 1.0f - p.z; } -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; +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; } -vec3 lonlat2xyz(vec2 lonlat) { - float t = lonlat.x; - float tc = cos(t); - float ts = sin(t); +int ij2z(int i, int j) { + int i4 = i | (j << 2); - float d = lonlat.y; - float dc = cos(d); - float ds = sin(d); + int j4 = (i4 ^ (i4 >> 1)) & 0x22222222; + int i5 = i4 ^ j4 ^ (j4 << 1); - return vec3(dc * ts, ds, dc * tc); + return i5; } -uniform int u_proj; +struct HashDxDy { + int idx; + float dx; + float dy; +}; -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); +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 { - return w2c_mer(p); + 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 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); + vec3 uv = xyz2uv(normalize(frag_pos)); + vec4 c = uvw2c_rgba(uv); - 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_frag_color = c; + out_frag_color = vec4(c.rgb, opacity * c.a); }"#, ); out.insert( - r"passes_post_fragment_100es.frag", + r"hips_raytracer_rgba2cmap.frag", r#"#version 300 es -precision mediump float; +precision lowp float; +precision lowp sampler2DArray; +precision lowp sampler2DArray; +precision lowp isampler2DArray; +precision mediump int; -in vec2 v_tc; -out vec4 color; +in vec3 frag_pos; +in vec2 out_clip_pos; +out vec4 out_frag_color; -uniform sampler2D fbo_tex; +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; +}; -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)); +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); } -vec4 srgba_from_linear(vec4 rgba) { - return vec4(srgb_from_linear(rgba.rgb), 255.0 * rgba.a); +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))); + 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 || isnan(x))); + 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 val2c(val); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return val2c(val); +} + +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() { - color = texture(fbo_tex, v_tc); + 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"catalogs_ortho.vert", + r"catalogs_mercator.vert", r#"#version 300 es precision lowp float; layout (location = 0) in vec2 offset; @@ -874,7 +2474,7 @@ vec2 proj(vec3 p) { void main() { vec3 p = inv_model * center; - vec2 center_pos_clip_space = world2clip_orthographic(p); + 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); @@ -884,9 +2484,10 @@ void main() { }"#, ); out.insert( - r"catalogs_healpix.vert", + 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; @@ -1006,7 +2607,7 @@ vec2 proj(vec3 p) { void main() { vec3 p = inv_model * center; - vec2 center_pos_clip_space = world2clip_healpix(p); + 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); @@ -1016,134 +2617,19 @@ void main() { }"#, ); out.insert( - r"catalogs_arc.vert", + r"colormaps_colormap.vert", r#"#version 300 es precision lowp float; -layout (location = 0) in vec2 offset; +precision lowp sampler2D; + +layout (location = 0) in vec2 position; 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); - + gl_Position = vec4(position, 0.f, 1.f); out_uv = uv; - out_p = p; }"#, ); out.insert( @@ -1542,14 +3028,23 @@ void main() { }"#, ); out.insert( - r"moc_base.vert", + r"hips_rasterizer_raster.vert", r#"#version 300 es precision highp float; -layout (location = 0) in vec2 lonlat; -uniform mat3 u_2world; +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; @@ -1653,61 +3148,23 @@ vec2 proj(vec3 p) { } void main() { - vec3 p_xyz = lonlat2xyz(lonlat); - vec3 p_w = u_2world * p_xyz; + 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.f, 1.f); + 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"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"line_inst_ndc.vert", + r"fits_f32.frag", 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"fits_i32.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler2D; -precision mediump int; +precision highp sampler2D; +precision highp int; out vec4 out_frag_color; in vec2 frag_uv; @@ -1903,7 +3360,7 @@ void main() { vec2 uv = frag_uv; uv.y = 1.0 - uv.y; - out_frag_color = uv2c_i32(frag_uv); + out_frag_color = uv2c_f32(frag_uv); out_frag_color.a = out_frag_color.a * opacity; }"#, ); @@ -1925,913 +3382,12 @@ void main() { }"#, ); out.insert( - r"hips_raytracer_u8.frag", - r#"#version 300 es -precision lowp float; -precision lowp sampler2DArray; -precision lowp usampler2DArray; -precision lowp isampler2DArray; -precision mediump int; - -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 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))); - 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 || isnan(x))); - 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 val2c(val); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return val2c(val); -} - -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_u8(uv); - - - out_frag_color = c; - 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))); - 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 || isnan(x))); - 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 val2c(val); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return val2c(val); -} - -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"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))); - 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 || isnan(x))); - 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"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"hips3d_f32.frag", + r"hips3d_i16.frag", r#"#version 300 es precision lowp float; precision lowp sampler3D; +precision lowp isampler3D; +precision lowp usampler3D; uniform sampler3D tex; @@ -3087,23 +3643,581 @@ void main() { vec3 uv = vec3(frag_uv.xyz); uv.y = 1.0 - uv.y; - vec4 color = uvw2c_f32(uv); + vec4 color = uvw2c_i16(uv); out_frag_color = color; out_frag_color.a = out_frag_color.a * opacity; }"#, ); out.insert( - r"hips_rasterizer_i32.frag", + r"catalogs_aitoff.vert", r#"#version 300 es precision lowp float; -precision lowp sampler2DArray; +layout (location = 0) in vec2 offset; +layout (location = 1) in vec2 uv; +layout (location = 2) in vec3 center; -uniform sampler2DArray tex; +uniform float current_time; +uniform mat3 inv_model; -in vec3 frag_uv_start; -in vec3 frag_uv_end; -in float frag_blending_factor; +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"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"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"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"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"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"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; @@ -3352,15 +4466,217 @@ vec4 uvw2c_u8(vec3 uv) { 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; + vec3 uv = vec3(frag_uv.xyz); + uv.y = 1.0 - uv.y; - vec4 color_start = uvw2c_i32(uv0); - vec4 color_end = uvw2c_i32(uv1); + vec4 color = uvw2c_i32(uv); - out_frag_color = mix(color_start, color_end, frag_blending_factor); + out_frag_color = color; + out_frag_color.a = out_frag_color.a * opacity; +}"#, + ); + out.insert( + r"fits_u8.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))); + 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 || isnan(x))); + 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_u8(frag_uv); out_frag_color.a = out_frag_color.a * opacity; }"#, ); @@ -3765,22 +5081,14 @@ void main() { }"#, ); out.insert( - r"catalogs_mercator.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; @@ -3883,150 +5191,13 @@ 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_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"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; + vec2 p_ndc = p_clip / (ndc_to_clip * czf); + gl_Position = vec4(p_ndc, 0.f, 1.f); }"#, ); out.insert( @@ -4057,18 +5228,16 @@ void main() { }"#, ); out.insert( - r"image_base.vert", + r"passes_post_vertex_100es.vert", r#"#version 300 es -precision highp float; +precision mediump float; -layout (location = 0) in vec2 ndc_pos; -layout (location = 1) in vec2 uv; - -out vec2 frag_uv; +layout (location = 0) in vec2 a_pos; +out vec2 v_tc; void main() { - gl_Position = vec4(ndc_pos, 0.0, 1.0); - frag_uv = uv; + gl_Position = vec4(a_pos * 2. - 1., 0.0, 1.0); + v_tc = a_pos; }"#, ); out.insert( @@ -4337,956 +5506,148 @@ void main() { }"#, ); 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"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))); - 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 || isnan(x))); - 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 val2c(val); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return val2c(val); -} - -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_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))); - 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 || isnan(x))); - 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 val2c(val); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return val2c(val); -} - -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"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))); - 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 || isnan(x))); - 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 val2c(val); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return val2c(val); -} - -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"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( @@ -5552,6 +5913,47 @@ void main() { out_frag_color = mix(color_start, color_end, frag_blending_factor); out_frag_color.a = opacity * out_frag_color.a; +}"#, + ); + 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"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( @@ -5951,27 +6353,26 @@ void main() { }"#, ); out.insert( - r"hips_raytracer_backcolor.vert", + r"fits_base.vert", r#"#version 300 es -precision lowp float; -precision mediump int; +precision highp float; +precision highp int; -layout (location = 0) in vec2 pos_clip_space; +layout (location = 0) in vec2 ndc_pos; +layout (location = 1) in vec2 uv; -uniform vec2 ndc_to_clip; -uniform float czf; +out vec2 frag_uv; void main() { - gl_Position = vec4(pos_clip_space / (ndc_to_clip * czf), 0.0, 1.0); + gl_Position = vec4(ndc_pos, 0.0, 1.0); + frag_uv = uv; }"#, ); out.insert( - r"hips3d_i16.frag", + r"hips3d_u8.frag", r#"#version 300 es precision lowp float; precision lowp sampler3D; -precision lowp isampler3D; -precision lowp usampler3D; uniform sampler3D tex; @@ -6227,14 +6628,27 @@ void main() { vec3 uv = vec3(frag_uv.xyz); uv.y = 1.0 - uv.y; - vec4 color = uvw2c_i16(uv); + vec4 color = uvw2c_u8(uv); out_frag_color = color; out_frag_color.a = out_frag_color.a * opacity; }"#, ); out.insert( - r"catalogs_ortho.frag", + 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; @@ -6248,25 +6662,594 @@ uniform float max_density; // max number of sources in a kernel sized HEALPix ce 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"hips_raytracer_rgba.frag", + 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))); + 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 || isnan(x))); + 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_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"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_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))); + 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 || isnan(x))); + 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 val2c(val); +} + +vec4 uvw2c_i16(vec3 uv) { + float val = float(decode_i16(texture(tex, uv).rg)); + return val2c(val); +} + +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"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"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"hips_raytracer_u8.frag", + r#"#version 300 es +precision lowp float; +precision lowp sampler2DArray; +precision lowp usampler2DArray; +precision lowp isampler2DArray; precision mediump int; uniform sampler2DArray tex; -in vec2 out_clip_pos; in vec3 frag_pos; +in vec2 out_clip_pos; out vec4 out_frag_color; struct Tile { @@ -6278,6 +7261,8 @@ struct Tile { uniform Tile textures_tiles[12]; +uniform float opacity; + uniform float scale; uniform float offset; uniform float blank; @@ -6638,487 +7623,19 @@ 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)); - vec4 c = uvw2c_rgba(uv); + + uv.y = 1.0 - uv.y; + vec4 c = uvw2c_u8(uv); + out_frag_color = c; - out_frag_color = vec4(c.rgb, opacity * c.a); -}"#, - ); - out.insert( - r"fits_u8.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))); - 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 || isnan(x))); - 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_u8(frag_uv); out_frag_color.a = out_frag_color.a * opacity; }"#, ); 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_rasterizer_i16.frag", + r"hips_rasterizer_rgba.frag", r#"#version 300 es precision lowp float; precision lowp sampler2DArray; @@ -7130,6 +7647,7 @@ in vec3 frag_uv_end; in float frag_blending_factor; out vec4 out_frag_color; +uniform float opacity; uniform float scale; uniform float offset; @@ -7373,530 +7891,12 @@ vec4 uvw2c_u8(vec3 uv) { 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); + 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 = out_frag_color.a * opacity; -}"#, - ); - 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"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"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))); - 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 || isnan(x))); - 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 val2c(val); -} - -vec4 uvw2c_i16(vec3 uv) { - float val = float(decode_i16(texture(tex, uv).rg)); - return val2c(val); -} - -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_i32(uv); - - out_frag_color = color; - 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))); - 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 || isnan(x))); - 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_frag_color.a = opacity * out_frag_color.a; }"#, ); out diff --git a/src/css/aladin.css b/src/css/aladin.css index 21091e11..3c608811 100644 --- a/src/css/aladin.css +++ b/src/css/aladin.css @@ -837,7 +837,13 @@ margin-left: 2px; } -.aladin-context-menu-item:hover > .aladin-context-sub-menu { +.aladin-stack-box .aladin-overlay-label { + text-align: center; + width: 100%; +} + +.aladin-context-menu-item:hover > .aladin-context-sub-menu, +.aladin-context-menu-item:focus-within > .aladin-context-sub-menu { display: block; } @@ -1140,18 +1146,6 @@ otherwise it fits its content options. If those are too big the select can go ou * */ -.aladin-stack-control { - position: absolute; - top: 3rem; - left: 0.2rem; -} - -.aladin-settings-control { - position: absolute; - top: 5.4rem; - left: 0.2rem; -} - .aladin-indicator { display: inline-block; width: 10px; @@ -1173,25 +1167,6 @@ otherwise it fits its content options. If those are too big the select can go ou text-decoration: underline; } -.aladin-simbadPointer-control { - position: absolute; - top: 7.8rem; - left: 0.2rem; -} - -.aladin-grid-control { - position: absolute; - top: 10.2rem; - left: 0.2rem; -} - - -.aladin-colorPicker-control { - position: absolute; - top: 15rem; - left: 0.2rem; -} - .aladin-cooFrame { position: absolute; top: 0.2rem; @@ -1208,6 +1183,7 @@ otherwise it fits its content options. If those are too big the select can go ou min-width: 17rem; max-width: 300px; } + .aladin-item-selected { border: var(--border-size) solid orange; } @@ -1323,10 +1299,14 @@ otherwise it fits its content options. If those are too big the select can go ou color: var(--text-color); } -.aladin-share-control { +.aladin-widgets-toolbar { position: absolute; - top: 12.6rem; - left: 0.2rem; + top: 3rem; + left: .2rem; +} + +.aladin-widgets-toolbar > * { + padding-top: 0rem; } .aladin-fullScreen-control { diff --git a/src/js/Aladin.js b/src/js/Aladin.js index fa265da1..8db22edc 100644 --- a/src/js/Aladin.js +++ b/src/js/Aladin.js @@ -42,7 +42,8 @@ import { Coo } from "./libs/astro/coo.js"; import { CooConversion } from "./CooConversion.js"; import { HiPSCache } from "./HiPSCache.js"; import { HiPSList } from "./DefaultHiPSList.js"; - +import stackOverlayIconUrl from './../../assets/icons/stack.svg'; +import { Toolbar } from "./gui/Toolbar.js"; import { ProjectionEnum } from "./ProjectionEnum.js"; import { ALEvent } from "./events/ALEvent.js"; @@ -64,17 +65,18 @@ import A from "./A.js"; import { StatusBarBox } from "./gui/Box/StatusBarBox.js"; import { FullScreenActionButton } from "./gui/Button/FullScreen.js"; import { ProjectionActionButton } from "./gui/Button/Projection.js"; - +import { Stack } from "./gui/Button/Stack.js"; // features import { SettingsButton } from "./gui/Button/Settings"; import { SimbadPointer } from "./gui/Button/SimbadPointer"; import { ColorPicker } from "./gui/Button/ColorPicker"; -import { OverlayStackButton } from "./gui/Button/OverlayStack"; +import { OverlayStackBox } from "./gui/Box/StackBox.js"; import { GridEnabler } from "./gui/Button/GridEnabler"; import { CooFrame } from "./gui/Input/CooFrame"; import { Circle } from "./shapes/Circle"; import { Ellipse } from "./shapes/Ellipse"; import { Polyline } from "./shapes/Polyline"; +import { WidgetTogglerButton } from "./gui/Button/Toggler.js"; /** * @typedef {Object} AladinOptions @@ -586,50 +588,52 @@ export let Aladin = (function () { } //////////////////////////////////////////////////// - let stack = new OverlayStackButton(this); - let simbad = new SimbadPointer(this); - let colorPicker = new ColorPicker(this); - let grid = new GridEnabler(this); - this.addUI(stack); - this.addUI(simbad); - this.addUI(grid); - this.addUI(colorPicker) + let widgets = {}; // Add the layers control - if (!options.showLayersControl) { - stack._hide(); - } - - // Add the simbad pointer control - if (!options.showSimbadPointerControl) { - simbad._hide(); - } - - // Add the projection control - // Add the coo grid control - if (!options.showCooGridControl) { - grid._hide(); - } - - // Add the projection control - // Add the coo grid control - if (!options.showColorPickerControl) { - colorPicker._hide(); + if (options.showLayersControl) { + let stack = new Stack(this); + widgets["stack"] = stack } // Settings control if (options.showSettingsControl) { - let settings = new SettingsButton(this, { - features: { stack, simbad, grid }, - }); - this.addUI(settings); + let settings = new SettingsButton(this); + widgets["settings"] = settings + } + + // Add the simbad pointer control + if (options.showSimbadPointerControl) { + let simbad = new SimbadPointer(this); + widgets["simbad"] = simbad + } + + // Add the projection control + // Add the coo grid control + if (options.showCooGridControl) { + let grid = new GridEnabler(this); + widgets["grid"] = grid; + } + + // Add the projection control + // Add the coo grid control + if (options.showColorPickerControl) { + let picker = new ColorPicker(this); + widgets["picker"] = picker; } // share control panel if (options.showShareControl) { - this.addUI(new ShareActionButton(self)); + let share = new ShareActionButton(this); + widgets["share"] = share; } + let toolbar = new Toolbar(widgets, { + classList: ["aladin-widgets-toolbar"] + }) + this.toolbar = toolbar; + this.addUI(toolbar) + if (options.showProjectionControl) { this.projBtn = new ProjectionActionButton(this); this.addUI(this.projBtn); @@ -768,7 +772,7 @@ export let Aladin = (function () { self.isInFullscreen = !self.isInFullscreen; - ContextMenu.hideAll(); + this.contextMenu && this.contextMenu._hide(); this.ui.forEach(ui => { if (ui.toggle) { diff --git a/src/js/Image.js b/src/js/Image.js index 4c44c855..5de0f494 100644 --- a/src/js/Image.js +++ b/src/js/Image.js @@ -362,7 +362,9 @@ export let Image = (function () { // Private method for updating the view with the new meta Image.prototype._updateMetadata = HiPS.prototype._updateMetadata; - Image.prototype._add2View = function (layer) { + Image.prototype._removeFromView = HiPS.prototype._removeFromView; + + Image.prototype._addToView = function (layer) { this.layer = layer; let self = this; diff --git a/src/js/View.js b/src/js/View.js index 0b8486f3..ba9c3019 100644 --- a/src/js/View.js +++ b/src/js/View.js @@ -944,7 +944,7 @@ export let View = (function () { DefaultActionsForContextMenu.getDefaultActions(view.aladin), null ); - ctxMenu.show({e}); + ctxMenu._show({e}); } view.rightClick = false; diff --git a/src/js/gui/Box/HiPSBrowserBox.js b/src/js/gui/Box/HiPSBrowserBox.js index d1a785ff..5e04f6c3 100644 --- a/src/js/gui/Box/HiPSBrowserBox.js +++ b/src/js/gui/Box/HiPSBrowserBox.js @@ -27,7 +27,7 @@ import filterOffUrl from "../../../../assets/icons/filter-off.svg"; import helpIconUrl from "../../../../assets/icons/help.svg"; import { Input } from "../Widgets/Input.js"; -import { TogglerActionButton } from "../Button/Toggler.js"; +import { WidgetTogglerButton } from "../Button/Toggler.js"; import { Layout } from "../Layout.js"; import { HiPSFilterBox } from "./HiPSFilterBox.js"; import A from "../../A.js"; @@ -262,7 +262,12 @@ export class HiPSBrowserBox extends Box { let infoCurrentHiPSBtn = ActionButton.BUTTONS(aladin) .infoHiPS({disable: true}) - let filterBtn = new TogglerActionButton({ + let filterBox = new HiPSFilterBox(aladin, { + callback: (params) => { + self._filterHiPSList(params); + }, + }) + let filterBtn = new WidgetTogglerButton({ icon: { url: filterOffUrl, monochrome: true, @@ -273,14 +278,12 @@ export class HiPSBrowserBox extends Box { position: { direction: "top" }, }, toggled: false, - actionOn: (e) => { - self.filterBox._show({position: { + widget: { + position: { anchor: 'right center' - }}); - }, - actionOff: (e) => { - self.filterBox._hide(); - }, + }, + obj: filterBox, + } }); let filterNumberElt = document.createElement("div"); @@ -331,11 +334,7 @@ export class HiPSBrowserBox extends Box { self = this; this.searchTree = searchTree; - this.filterBox = new HiPSFilterBox(aladin, { - callback: (params) => { - self._filterHiPSList(params); - }, - }) + this.filterBox = filterBox; this.filterNumberElt = filterNumberElt; this.filterBox._hide(); diff --git a/src/js/gui/Box/HiPSCompositeBox.js b/src/js/gui/Box/HiPSCompositeBox.js index ed1079c6..6ca40f12 100644 --- a/src/js/gui/Box/HiPSCompositeBox.js +++ b/src/js/gui/Box/HiPSCompositeBox.js @@ -224,8 +224,7 @@ export class HiPSCompositeBox extends Box { }, this.aladin.aladinDiv); layerSettingsBox._hide() - // catalog settings - let layerSettingsBtn = new TogglerActionButton({ + /*let layerSettingsBtn = new TogglerActionButton({ icon: { url: settingsIconUrl, monochrome: true }, size: "small", tooltip: { @@ -233,7 +232,7 @@ export class HiPSCompositeBox extends Box { position: { direction: "top" }, }, toggled: false, - actionOn: (_) => { + on: (_) => { layerSettingsBox._show({ position: { nextTo: layerSettingsBtn, @@ -248,13 +247,13 @@ export class HiPSCompositeBox extends Box { self.openSettings = layerSettingsBtn; }, - actionOff: (_) => { + off: (_) => { layerSettingsBox._hide(); if (self.openSettings === layerSettingsBtn) { self.openSettings = null; } }, - }); + });*/ return layerSettingsBtn } diff --git a/src/js/gui/Box/HiPSSettingsBox.js b/src/js/gui/Box/HiPSSettingsBox.js index edd24eb9..31075d07 100644 --- a/src/js/gui/Box/HiPSSettingsBox.js +++ b/src/js/gui/Box/HiPSSettingsBox.js @@ -36,7 +36,7 @@ import { Form } from "../Widgets/Form.js"; import pixelHistIconUrl from '../../../../assets/icons/pixel_histogram.svg'; import { RadioButton } from "../Widgets/Radio.js"; import waveOnIconUrl from '../../../../assets/icons/wave-on.svg'; -import { TogglerActionButton } from "../Button/Toggler.js"; +import { WidgetTogglerButton } from "../Button/Toggler.js"; import { Layout } from "../Layout.js"; export class HiPSSettingsBox extends Box { @@ -317,7 +317,7 @@ import { TogglerActionButton } from "../Button/Toggler.js"; if (options.layer.isSpectralCube && options.layer.isSpectralCube()) { let spectraDisplayer = self.aladin.view.spectraDisplayer; - self.spectraBtn = new TogglerActionButton({ + self.spectraBtn = new WidgetTogglerButton({ content: 'Spectra', icon: { size: 'small', @@ -326,12 +326,11 @@ import { TogglerActionButton } from "../Button/Toggler.js"; }, tooltip: {content: 'Show/hide spectra', position: {direction: 'bottom'}}, toggled: true, - actionOn: () => { + enabled(o) { spectraDisplayer.attachHiPS3D(options.layer) - spectraDisplayer.show() }, - actionOff: () => { - spectraDisplayer.hide() + widget: { + obj: spectraDisplayer } }); diff --git a/src/js/gui/Box/StackBox.js b/src/js/gui/Box/StackBox.js index 75b14249..9d8b00fd 100644 --- a/src/js/gui/Box/StackBox.js +++ b/src/js/gui/Box/StackBox.js @@ -45,7 +45,7 @@ import settingsIconUrl from "../../../../assets/icons/settings.svg"; import searchIconImg from "../../../../assets/icons/search.svg"; import downloadIconUrl from '../../../../assets/icons/download.svg'; import swapIcon from '../../../../assets/icons/swap.svg' -import { TogglerActionButton } from "../Button/Toggler.js"; +import { WidgetTogglerButton } from "../Button/Toggler.js"; import { Icon } from "../Widgets/Icon.js"; import { Box } from "../Widgets/Box.js"; import { CtxMenuActionButtonOpener } from "../Button/CtxMenuOpener.js"; @@ -126,7 +126,7 @@ export class OverlayStackBox extends Box { }, }; // Constructor - constructor(aladin, stackBtn) { + constructor(aladin) { super( { close: true, @@ -138,8 +138,6 @@ export class OverlayStackBox extends Box { }, aladin.aladinDiv ); - this.stackBtn = stackBtn; - this.aladin = aladin; this.mode = "stack"; @@ -730,12 +728,9 @@ export class OverlayStackBox extends Box { } } - if (this.addOverlayBtn) this.addOverlayBtn.hideMenu(); + if (this.addOverlayBtn) this.addOverlayBtn.close(); - if (this.addHiPSBtn) this.addHiPSBtn.hideMenu(); - - // toggle the button because the window is closed - this.stackBtn.update({toggled: false}); + if (this.addHiPSBtn) this.addHiPSBtn.close(); super._hide(); } @@ -767,6 +762,7 @@ export class OverlayStackBox extends Box { _createOverlaysList() { let self = this; + let aladin = self.aladin; let layout = []; const overlays = Array.from(this.aladin.getOverlays()) @@ -848,21 +844,15 @@ export class OverlayStackBox extends Box { label: 'Shape', name: 'shape', type: 'select', - options: (() => { - if (overlay.shapeFn) { - return ['custom'] - } else { - return [ - { value: "plus", label: "+" }, - { value: "rhomb", label: "◇" }, - { value: "triangle", label: "△" }, - { value: "cross", label: "✕" }, - { value: "square", label: "□" }, - { value: "circle", label: "○" }, - ] - } - })(), - value: overlay.shape, + options: [ + { value: "plus", label: "+" }, + { value: "rhomb", label: "◇" }, + { value: "triangle", label: "△" }, + { value: "cross", label: "✕" }, + { value: "square", label: "□" }, + { value: "circle", label: "○" }, + ], + value: (overlay.shapeFn && "square") || overlay.shape, change: (e) => { const shape = e.target.value overlay.setShape(shape) @@ -884,7 +874,7 @@ export class OverlayStackBox extends Box { catSettingsBox._hide() // catalog settings - let catSettingsBtn = new TogglerActionButton({ + let catSettingsBtn = new WidgetTogglerButton({ icon: { url: settingsIconUrl, monochrome: true }, size: "small", tooltip: { @@ -892,7 +882,7 @@ export class OverlayStackBox extends Box { position: { direction: "top" }, }, toggled: false, - actionOn: (e) => { + enable: (_) => { // toggle off the other settings if opened for (var l in self.ui) { let ui = self.ui[l] @@ -903,17 +893,16 @@ export class OverlayStackBox extends Box { } } - catSettingsBox._show({ - position: { - nextTo: catSettingsBtn, - direction: "right", - aladin: self.aladin, - }, - }); - }, - actionOff: (e) => { - catSettingsBox._hide(); + let spectraDisplayer = aladin.view.spectraDisplayer; + if (spectraDisplayer) + spectraDisplayer.attachHiPS3D(options.layer) }, + widget: { + obj: catSettingsBox, + position: { + direction: "right", + } + } }); optBtn.push(catSettingsBtn); @@ -933,12 +922,11 @@ export class OverlayStackBox extends Box { } )); - layout.push( - { - start: [this._addOverlayIcon(overlay), name], - end: [optBtn] - }, - ); + layout.push([ + this._addOverlayIcon(overlay), + '
' + name + "
", + optBtn + ]); } return layout; @@ -999,7 +987,7 @@ export class OverlayStackBox extends Box { let deleteBtn = ActionButton.createSmallSizedIconBtn({ icon: { url: removeIconUrl, monochrome: true }, tooltip: { content: "Remove", position: { direction: "top" } }, - action(e) { + action: (e) => { aladin.removeImageLayer(hips.layer); // remove HiPS cube player if any aladin.removeUIByName("cube_displayer" + hips.layer) @@ -1045,13 +1033,10 @@ export class OverlayStackBox extends Box { }, }); - if (!this.settingsBox) { - this.settingsBox = new HiPSSettingsBox(self.aladin); - } + let settingsBox = new HiPSSettingsBox(self.aladin); + settingsBox._hide(); - this.settingsBox._hide(); - - let settingsBtn = new TogglerActionButton({ + let settingsBtn = new WidgetTogglerButton({ icon: { url: settingsIconUrl, monochrome: true }, size: "small", tooltip: { @@ -1059,7 +1044,7 @@ export class OverlayStackBox extends Box { position: { direction: "top" }, }, toggled: false, - actionOn: (e) => { + enable: (_) => { // toggle off the other settings if opened for (var l in self.ui) { let ui = self.ui[l] @@ -1069,18 +1054,14 @@ export class OverlayStackBox extends Box { } } - this.settingsBox.update({ layer: hips }); - this.settingsBox._show({ - position: { - nextTo: settingsBtn, - direction: "right", - aladin: self.aladin, - }, - }); - }, - actionOff: (e) => { - this.settingsBox._hide(); + settingsBox.update({ layer: hips }); }, + widget: { + obj: settingsBox, + position: { + direction: "right", + } + } }); let loadMOCBtn = ActionButton.BUTTONS(self.aladin) @@ -1135,7 +1116,7 @@ export class OverlayStackBox extends Box { if (!(hips.layer in self.ui)) { self.ui[hips.layer] = { HiPSSelector: HiPSselect, - settingsBox: this.settingsBox, + settingsBox, settingsBtn, showBtn, }; @@ -1200,7 +1181,5 @@ export class OverlayStackBox extends Box { ...options, ...{ position: this.position }, }); - - this.stackBtn.update({toggled: true}); } } diff --git a/src/js/gui/Button/CtxMenuOpener.js b/src/js/gui/Button/CtxMenuOpener.js index ae37bb61..5716b50d 100644 --- a/src/js/gui/Button/CtxMenuOpener.js +++ b/src/js/gui/Button/CtxMenuOpener.js @@ -28,12 +28,10 @@ * *****************************************************************************/ -import { ActionButton } from "../Widgets/ActionButton.js"; - +import { WidgetTogglerButton } from "./Toggler.js"; +/* export class CtxMenuActionButtonOpener extends ActionButton { - static currentlyOpened = null; - // Constructor constructor(options, aladin) { let self; @@ -49,36 +47,19 @@ export class CtxMenuActionButtonOpener extends ActionButton { }) }; - super({ action(e) { enableTooltips() - let isHidden = self.ctxMenu.isHidden; + let wasClosed = self.ctxMenu.isHidden; + self.close() - self.ctxMenu._hide() - - if (self.ctxMenu.attached === self && !isHidden) { + if (self.ctxMenu.toggler === self && !wasClosed) { return; } // If it was hidden then reopen it - if (options.action) { - options.action(e) - } - - if (self.layout) { - self.ctxMenu.attach(self.layout, self) - } - - self.ctxMenu.show({ - position: { - nextTo: self, - direction: options.openDirection, - }, - }); - - CtxMenuActionButtonOpener.currentlyOpened = self; + self.open(e); // the panel is now open and we know the button has a tooltip // => we close it! @@ -98,12 +79,85 @@ export class CtxMenuActionButtonOpener extends ActionButton { this.layout = options.ctxMenu; } - hideMenu() { + close() { + this.closed = true; this.ctxMenu._hide(); } + open(e) { + if (this.layout) { + this.ctxMenu.attach(this.layout, this) + } + + this.ctxMenu.show({ + position: { + nextTo: this, + direction: this.options.openDirection, + }, + }); + + this.closed = false; + } + _hide() { - this.hideMenu(); + this.close(); super._hide(); } +}*/ + +export class CtxMenuActionButtonOpener extends WidgetTogglerButton { + + // Constructor + constructor(options, aladin) { + let self; + + const enableTooltips = () => { + aladin.aladinDiv.removeEventListener('click', enableTooltips); + + aladin.aladinDiv.querySelectorAll('.aladin-tooltip') + // for each tooltips reset its visibility and transition delay + .forEach((t) => { + t.style.visibility = '' + t.style.transitionDelay = '' + }) + }; + super({ + widget: { + obj: aladin.contextMenu, + position: {direction: (options && options.openDirection) || 'right'} + }, + enable(e) { + enableTooltips() + // If it was hidden then reopen it + if (self.layout) { + self.ctxMenu.attach(self.layout, self) + } + + // the panel is now open and we know the button has a tooltip + // => we close it! + if (self.tooltip && !self.ctxMenu.isHidden) { + self.tooltip.element().style.visibility = 'hidden' + self.tooltip.element().style.transitionDelay = '0ms'; + + aladin.aladinDiv.addEventListener("click", enableTooltips) + } + }, + ...options, + }) + + self = this; + + this.ctxMenu = aladin.contextMenu; + this.layout = options.ctxMenu; + } + + update(options) { + if (options && options.ctxMenu) { + console.log(this.ctxMenu, "attach", options.ctxMenu) + this.layout = options.ctxMenu; + this.ctxMenu.attach(this.layout, this) + } + + super.update(options) + } } \ No newline at end of file diff --git a/src/js/gui/Button/Projection.js b/src/js/gui/Button/Projection.js index 8b690d30..79e08516 100644 --- a/src/js/gui/Button/Projection.js +++ b/src/js/gui/Button/Projection.js @@ -44,7 +44,9 @@ import { ALEvent } from "../../events/ALEvent"; constructor(aladin, options) { options = options || {}; options.verbosity = (options && options.verbosity) || 'full'; + let projectionName = aladin.getProjectionName(); + let self; let ctxMenu = _buildLayout(aladin); super({ @@ -57,9 +59,11 @@ import { ALEvent } from "../../events/ALEvent"; content: projectionName, tooltip: {content: 'Change the view projection', position: {direction: 'bottom left'}}, ctxMenu, + openDirection: 'left', ...options }, aladin); + self = this; this.aladin = aladin; this._addEventListeners() @@ -81,13 +85,11 @@ import { ALEvent } from "../../events/ALEvent"; function _buildLayout(aladin) { let layout = []; - let aladinProj = aladin.getProjectionName(); for (const key in ProjectionEnum) { let proj = ProjectionEnum[key]; layout.push({ label: proj.label, - selected: aladinProj === key, action(o) { aladin.setProjection(key) } diff --git a/src/js/gui/Button/Settings.js b/src/js/gui/Button/Settings.js index 29abad5b..8250f365 100644 --- a/src/js/gui/Button/Settings.js +++ b/src/js/gui/Button/Settings.js @@ -27,6 +27,11 @@ import { Utils } from "../../Utils"; import { GridSettingsCtxMenu } from "./../CtxMenu/GridSettings.js"; import { CtxMenuActionButtonOpener } from "./CtxMenuOpener"; import settingsIconUrl from './../../../../assets/icons/settings.svg'; +import { SimbadPointer } from "./SimbadPointer.js"; +import { GridEnabler } from "./GridEnabler.js"; +import { Stack } from "./Stack.js"; +import { ColorPicker } from "./ColorPicker.js"; +import { ShareActionButton } from "./ShareView.js"; /****************************************************************************** * Aladin Lite project @@ -62,248 +67,306 @@ import settingsIconUrl from './../../../../assets/icons/settings.svg'; direction: 'right' } }, - ctxMenu: _buildLayout(aladin, options), + ctxMenu: undefined, ...options }, aladin); - } -} -function _buildLayout(aladin, options) { - let backgroundColorInput = Input.color({ - name: 'color', - value: (() => { - let {r, g, b} = aladin.getBackgroundColor(); - return Color.rgbToHex(r, g, b); - })(), - change(e) { - let hex = e.target.value; - aladin.setBackgroundColor(hex) - } - }); - - let reticleColorInput = Input.color({ - value: new Color(aladin.getReticle().getColor()).toHex(), - name: 'reticleColor', - change(e) { - let hex = e.target.value; - aladin.setDefaultColor(hex) - } - }); - - // Event received from aladin - ALEvent.BACKGROUND_COLOR_CHANGED.listenedBy(aladin.aladinDiv, function (e) { - const {r, g, b} = e.detail.color; - - let hex = Color.rgbToHex(r, g, b); - backgroundColorInput.set(hex) - }); - - ALEvent.RETICLE_CHANGED.listenedBy(aladin.aladinDiv, function (e) { - const color = e.detail.color; - let hex = new Color(color).toHex(); - - reticleColorInput.set(hex) - }); - - const toggleCheckbox = (checkbox) => { - const pastVal = checkbox.get(); - const curVal = !pastVal; - - checkbox.set(curVal) - - return curVal; - }; - - let hpxGridCheckbox = Input.checkbox({ - name: 'hpxgrid', checked: aladin.healpixGrid(), - click(e) { - let newVal = toggleCheckbox(hpxGridCheckbox); - aladin.showHealpixGrid(newVal) - } - }) - let reticleCheckbox = Input.checkbox({ - name: 'reticle', - checked: aladin.isReticleDisplayed(), - click(e) { - let newVal = toggleCheckbox(reticleCheckbox); - aladin.showReticle(newVal) - } - }) - - let features = options && options.features; - const toggleFeature = (name) => { - let feature = features[name]; - if(feature.isHidden) { - feature._show(); - } else { - feature._hide(); - } + this.aladin = aladin; + let ctxMenu = this._buildLayout() + this.update({ctxMenu}) } - let reticle = aladin.getReticle(); - - let sliderReticleSize = Input.slider({ - name: 'reticleSize', - type: 'range', - min: 0.0, - max: 50, - value: reticle.getSize(), - change(e) { - reticle.update({size: e.target.value}) - } - }); - - let sampBtn = new SAMPActionButton({ - size: 'small', - action(conn) { - if (conn.isConnected()) { - conn.unregister(); - } else { - conn.register(); + _buildLayout() { + let self = this; + let aladin = this.aladin; + let backgroundColorInput = Input.color({ + name: 'color', + value: (() => { + let {r, g, b} = aladin.getBackgroundColor(); + return Color.rgbToHex(r, g, b); + })(), + change(e) { + let hex = e.target.value; + aladin.setBackgroundColor(hex) } + }); - //self._hide() - } - }, aladin); - - return [ - GridSettingsCtxMenu.getLayout(aladin), - { - label: { - content: ['Reticle'] - }, - subMenu: [ - { - label: { - content: [reticleCheckbox, 'Show/Hide'] - }, - mustHide: false, - action(o) { - let newVal = toggleCheckbox(reticleCheckbox); - aladin.showReticle(newVal) - } - }, - { - label: { - content: [reticleColorInput, 'Color'] - }, - }, - { - label: Layout.horizontal(['Size', sliderReticleSize]), - } - ] - }, - { - label: { - content: [backgroundColorInput, 'Back color'] - }, - }, - { - label: { - content: 'Light/Dark mode' - }, - action(o) { - const currentTheme = aladin.aladinDiv.getAttribute("data-theme"); - const newTheme = currentTheme === "dark" ? "light" : "dark"; - aladin.aladinDiv.setAttribute("data-theme", newTheme); - localStorage.setItem("theme", newTheme); + let reticleColorInput = Input.color({ + value: new Color(aladin.getReticle().getColor()).toHex(), + name: 'reticleColor', + change(e) { + let hex = e.target.value; + aladin.setDefaultColor(hex) } - }, - { - label: { - content: [hpxGridCheckbox, 'HEALPix grid'] - }, - mustHide: false, - action(o) { + }); + + // Event received from aladin + ALEvent.BACKGROUND_COLOR_CHANGED.listenedBy(aladin.aladinDiv, function (e) { + const {r, g, b} = e.detail.color; + + let hex = Color.rgbToHex(r, g, b); + backgroundColorInput.set(hex) + }); + + ALEvent.RETICLE_CHANGED.listenedBy(aladin.aladinDiv, function (e) { + const color = e.detail.color; + let hex = new Color(color).toHex(); + + reticleColorInput.set(hex) + }); + + const toggleCheckbox = (checkbox) => { + const pastVal = checkbox.get(); + const curVal = !pastVal; + + checkbox.set(curVal) + + return curVal; + }; + + let hpxGridCheckbox = Input.checkbox({ + name: 'hpxgrid', checked: aladin.healpixGrid(), + click(e) { let newVal = toggleCheckbox(hpxGridCheckbox); aladin.showHealpixGrid(newVal) } - }, - { - label: { - content: [sampBtn, 'SAMP'] - }, - }, - { - label: 'Tools', - subMenu: [ - { - label: 'Stack', - selected: !features['stack'].isHidden, - action(o) { - toggleFeature('stack') - } - }, - { - label: 'Simbad', - selected: !features['simbad'].isHidden, - action(o) { - toggleFeature('simbad'); - } - }, - { - label: 'Grid', - selected: !features['grid'].isHidden, - action(o) { - toggleFeature('grid'); - } + }) + let reticleCheckbox = Input.checkbox({ + name: 'reticle', + checked: aladin.isReticleDisplayed(), + click(e) { + let newVal = toggleCheckbox(reticleCheckbox); + aladin.showReticle(newVal) + } + }) + + let reticle = aladin.getReticle(); + + let sliderReticleSize = Input.slider({ + name: 'reticleSize', + type: 'range', + min: 0.0, + max: 50, + value: reticle.getSize(), + change(e) { + reticle.update({size: e.target.value}) + } + }); + + let sampBtn = new SAMPActionButton({ + size: 'small', + action(conn) { + if (conn.isConnected()) { + conn.unregister(); + } else { + conn.register(); } - ] - }, - { - label: { - icon: { - monochrome: true, - tooltip: {content: 'Documentation about Aladin Lite', position: {direction: 'top'}}, - url: helpIconBtn, - size: 'small', - cssStyle: { - cursor: 'help', - } + + //self._hide() + } + }, aladin); + + return [ + GridSettingsCtxMenu.getLayout(aladin), + { + label: { + content: ['Reticle'] }, - content: 'Help' - }, - subMenu: [ - { - label: 'Aladin Lite API', - action(o) { - Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/API/') - } - }, - { - label: { - content: 'Contact us', - tooltip: { content: 'For bug reports, discussions, feature ideas...', position: {direction: 'bottom'} } - }, - subMenu: [ - { - label: 'GitHub', - action(o) { - Utils.openNewTab('https://github.com/cds-astro/aladin-lite/issues') - } + subMenu: [ + { + label: { + content: [reticleCheckbox, 'Show/Hide'] }, - { - label: 'by email', - action(o) { - Utils.openNewTab('mailto:matthieu.baumann@astro.unistra.fr,thomas.boch@astro.unistra.fr?subject=Aladin Lite issue&body=message%20goes%20here') + mustHide: false, + action(o) { + let newVal = toggleCheckbox(reticleCheckbox); + aladin.showReticle(newVal) + } + }, + { + label: { + content: [reticleColorInput, 'Color'] + }, + }, + { + label: Layout.horizontal(['Size', sliderReticleSize]), + } + ] + }, + { + label: { + content: [backgroundColorInput, 'Back color'] + }, + }, + { + label: { + content: 'Light/Dark mode' + }, + mustHide: false, + action(o) { + const currentTheme = aladin.aladinDiv.getAttribute("data-theme"); + const newTheme = currentTheme === "dark" ? "light" : "dark"; + aladin.aladinDiv.setAttribute("data-theme", newTheme); + localStorage.setItem("theme", newTheme); + } + }, + { + label: { + content: [hpxGridCheckbox, 'HEALPix grid'] + }, + mustHide: false, + action(o) { + let newVal = toggleCheckbox(hpxGridCheckbox); + aladin.showHealpixGrid(newVal) + } + }, + { + label: { + content: [sampBtn, 'SAMP'] + }, + }, + { + label: 'Tools', + subMenu: [ + { + label: 'Stack', + mustHide: false, + action: (o) => { + let toolbar = aladin.toolbar; + if (!toolbar.has('stack')) { + toolbar.add('stack', new Stack(aladin)); + } else { + if (toolbar.enabled('stack')) { + toolbar.disable('stack') + } else { + toolbar.enable('stack') + } } } - ], + }, + { + label: 'Simbad', + mustHide: false, + action: (o) => { + let toolbar = aladin.toolbar; + if (!toolbar.has('simbad')) { + toolbar.add('simbad', new SimbadPointer(aladin)); + } else { + if (toolbar.enabled('simbad')) { + toolbar.disable('simbad') + } else { + toolbar.enable('simbad') + } + } + } + }, + { + label: 'Grid', + mustHide: false, + action: (o) => { + let toolbar = aladin.toolbar; + if (!toolbar.has('grid')) { + toolbar.add('grid', new GridEnabler(aladin)); + } else { + if (toolbar.enabled('grid')) { + toolbar.disable('grid') + } else { + toolbar.enable('grid') + } + } + } + }, + { + label: 'Color picker', + mustHide: false, + action: (o) => { + let toolbar = aladin.toolbar; + if (!toolbar.has('picker')) { + toolbar.add('picker', new ColorPicker(aladin)); + } else { + if (toolbar.enabled('picker')) { + toolbar.disable('picker') + } else { + toolbar.enable('picker') + } + } + } + }, + { + label: 'Share view', + mustHide: false, + action: (o) => { + let toolbar = aladin.toolbar; + if (!toolbar.has('share')) { + toolbar.add('share', new ShareActionButton(aladin)); + } else { + if (toolbar.enabled('share')) { + toolbar.disable('share') + } else { + toolbar.enable('share') + } + } + } + }, + ] + }, + { + label: { + icon: { + monochrome: true, + tooltip: {content: 'Documentation about Aladin Lite', position: {direction: 'top'}}, + url: helpIconBtn, + size: 'small', + cssStyle: { + cursor: 'help', + } + }, + content: 'Help' }, - { - label: 'General documentation', - - action(o) { - Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/') + subMenu: [ + { + label: 'Aladin Lite API', + action(o) { + Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/API/') + } + }, + { + label: { + content: 'Contact us', + tooltip: { content: 'For bug reports, discussions, feature ideas...', position: {direction: 'bottom'} } + }, + subMenu: [ + { + label: 'GitHub', + action(o) { + Utils.openNewTab('https://github.com/cds-astro/aladin-lite/issues') + } + }, + { + label: 'by email', + action(o) { + Utils.openNewTab('mailto:matthieu.baumann@astro.unistra.fr,thomas.boch@astro.unistra.fr?subject=Aladin Lite issue&body=message%20goes%20here') + } + } + ], + }, + { + label: 'General documentation', + + action(o) { + Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/') + } + }, + { + label: Layout.horizontal('Examples', { tooltip: { content: 'How to embed Aladin Lite
into your own webpages!', position: {direction: 'bottom'}}}), + action(o) { + Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/') + } } - }, - { - label: Layout.horizontal('Examples', { tooltip: { content: 'How to embed Aladin Lite
into your own webpages!', position: {direction: 'bottom'}}}), - action(o) { - Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/') - } - } - ] - } - ] + ] + } + ] + } + } + diff --git a/src/js/gui/Button/OverlayStack.js b/src/js/gui/Button/Stack.js similarity index 65% rename from src/js/gui/Button/OverlayStack.js rename to src/js/gui/Button/Stack.js index 9f04007c..7b0f1556 100644 --- a/src/js/gui/Button/OverlayStack.js +++ b/src/js/gui/Button/Stack.js @@ -17,11 +17,9 @@ // along with Aladin Lite. // -import { CtxMenuActionButtonOpener } from "./CtxMenuOpener"; -import stackOverlayIconUrl from './../../../../assets/icons/stack.svg'; import { OverlayStackBox } from "../Box/StackBox"; - -import { ActionButton } from "./../Widgets/ActionButton"; +import { WidgetTogglerButton } from "./Toggler"; +import stackOverlayIconUrl from "./../../../../assets/icons/stack.svg"; /****************************************************************************** * Aladin Lite project * @@ -34,18 +32,22 @@ import { ActionButton } from "./../Widgets/ActionButton"; * *****************************************************************************/ /** - * Class representing a Tabs layout - * @extends CtxMenuActionButtonOpener + * Class representing the stack + * @extends WidgetTogglerButton */ - export class OverlayStackButton extends ActionButton { + export class Stack extends WidgetTogglerButton { /** * UI responsible for displaying the viewport infos * @param {Aladin} aladin - The aladin instance. */ - constructor(aladin, options) { - let self; - let stack; + constructor(aladin) { super({ + widget: { + obj: new OverlayStackBox(aladin), + position: { + direction: "right" + } + }, icon: { size: 'medium', monochrome: true, @@ -58,24 +60,6 @@ import { ActionButton } from "./../Widgets/ActionButton"; direction: 'top right' } }, - toggled: false, - action(e) { - if (stack.isHidden) { - aladin.contextMenu && aladin.contextMenu._hide() - - stack._show({ - position: { - nextTo: self, - direction: 'right' - } - }) - } else { - stack._hide() - } - }, - ...options }); - self = this; - stack = new OverlayStackBox(aladin, self); } } diff --git a/src/js/gui/Button/Toggler.js b/src/js/gui/Button/Toggler.js index 4d93a3ce..a733b21d 100644 --- a/src/js/gui/Button/Toggler.js +++ b/src/js/gui/Button/Toggler.js @@ -43,8 +43,7 @@ export class TogglerActionButton extends ActionButton { ...options, toggled, action(o) { - options.action && options.action(o); - self.toggle(o); + self.toggle(o) } }) this.toggled = toggled; @@ -67,15 +66,73 @@ export class TogglerActionButton extends ActionButton { toggle(o) { this.toggled = !this.toggled; - if (this.toggled && this.options.actionOn) { - this.options.actionOn(o) + if (this.toggled && this.options.on) { + this.options.on(o) } - if (!this.toggled && this.options.actionOff) { - this.options.actionOff(o) + if (!this.toggled && this.options.off) { + this.options.off(o) } // once the actions has been executed, modify the styling - this.update({toggled: this.toggled, tooltip: this.toggled ? this.options.tooltipOn : this.options.tooltipOff}) + this.update({toggled: this.toggled}) } -} \ No newline at end of file + + // It may happen that the widget closes and so the toggler + // has to be notified. For example when the user clicks on a Box that + // is attached to a toggler. + notify(state) { + if (this.toggled === state) + return; + + this.toggled = state; + this.update({toggled: this.toggled}) + } +} + +/** + * Class representing a Tabs layout + * @extends TogglerActionButton + */ + export class WidgetTogglerButton extends TogglerActionButton { + /** + * UI responsible for displaying the viewport infos + * @param {Aladin} aladin - The aladin instance. + */ + constructor(options) { + let self; + + let {position, obj} = options && options.widget; + + let widget = obj; + let enable = options && options.enable; + + super({ + toggled: false, + on(o) { + if (enable) + enable(o) + + widget._show({position}) + }, + off(_) { + self.close(); + }, + ...options + }); + self = this; + + widget.setToggler(this); + this.widget = widget; + + if (position && position.direction) { + position['nextTo'] = this; + } + } + + close() { + this.widget._hide(); + + super.close() + } +} diff --git a/src/js/gui/Toolbar.js b/src/js/gui/Toolbar.js new file mode 100644 index 00000000..7c7e0ed7 --- /dev/null +++ b/src/js/gui/Toolbar.js @@ -0,0 +1,144 @@ +// Copyright 2023 - UDS/CNRS +// The Aladin Lite program is distributed under the terms +// of the GNU General Public License version 3. +// +// 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 General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// 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 General Public License for more details. +// +// The GNU General Public License is available in COPYING file +// along with Aladin Lite. +// +import { Layout } from "./Layout"; + +/****************************************************************************** + * Aladin Lite project + * + * File gui/Widgets/layout/Horizontal.js + * + * A layout grouping widgets horizontaly + * + * + * Author: Matthieu Baumann[CDS] + * + *****************************************************************************/ + +export class Toolbar extends Layout { + /** + * Create a layout + * @param {layout: Array.} layout - Represents the structure of the Tabs + * @param {Object} options - Options object + * @param {DOMElement} target - The parent element. + * @param {String} position - The position of the tabs layout relative to the target. + * For the list of possibilities, see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML + */ + constructor(widgets, options, target, position = "beforeend") { + let layout = Object.values(widgets); + super( + layout, + { + vertical: true, + ...options + }, + target, + position + ) + + this.toggled = null; + let self = this; + + for (let widget of this.layout) { + const action = widget.options.action; + widget.update({ + action: (o) => { + // toggle off the current toggled widget + self._toggleOffWidget(widget) + self.toggled = widget; + + action(o) + } + }) + } + + this.widgets = widgets; + } + + // Close the toggled widget if the user clicks on another one + _toggleOffWidget(widget) { + if (this.toggled && this.toggled !== widget) { + let canBeClosed = this.toggled && this.toggled.close; + if (canBeClosed) { + this.toggled.close(); + } + + this.toggled = null; + } + } + + has(name) { + return name in this.widgets; + } + + enabled(name) { + if (!this.has(name)) { + return false; + } + + let widget = this.widgets[name]; + return widget.el.disabled === false; + } + + enable(name) { + if (!this.has(name)) { + return; + } + + let widget = this.widgets[name]; + widget.update({disable: false}) + } + + disable(name) { + if (!this.has(name)) { + return; + } + + let widget = this.widgets[name]; + widget.update({disable: true}) + } + + add(name, widget) { + this.widgets[name] = widget; + + const action = widget.options.action; + widget.update({ + action: (o) => { + // toggle off the current toggled widget + this._toggleOffWidget(widget) + this.toggled = widget; + + action(o) + } + }) + + this.appendLast(widget); + } + + remove(name) { + let widget = this.widgets[name]; + + if (this.toggled === widget) + this.toggled = null; + + this.removeItem(widget); + + delete this.widgets[name]; + widget.remove() + } +} \ No newline at end of file diff --git a/src/js/gui/Widgets/ActionButton.js b/src/js/gui/Widgets/ActionButton.js index 003ce797..5a2c6267 100644 --- a/src/js/gui/Widgets/ActionButton.js +++ b/src/js/gui/Widgets/ActionButton.js @@ -25,6 +25,7 @@ import { Layout } from "../Layout"; import infoIconUrl from "../../../../assets/icons/info.svg" import targetIconUrl from "../../../../assets/icons/target.svg"; import removeIconUrl from "../../../../assets/icons/remove.svg"; + import A from "../../A"; /****************************************************************************** * Aladin Lite project @@ -68,19 +69,20 @@ import A from "../../A"; * * @example * const actionButton = new ActionButton({ - * toggled: false, - * action: (e) => { /* callback function * }, - * title: "Click me", - * iconURL: "path/to/icon.png", - * cssStyle: "color: red;", - * tooltip: { - * position: { - * direction: 'left, - * }, - * content: 'A tooltip' - * }, - * position: { nextTo: someDOMElement, direction: 'right' } - * }, document.getElementById('container')); + size: 'small', + content: '❌', + //tooltip: {content: 'Close the window', position: {direction: 'bottom'}}, + action(e) { + self._hide(); + }, + cssStyle: { + position: 'absolute', + }, + position: { + top: 0, + right: 0, + } +}); */ export class ActionButton extends DOMElement { constructor(options, target, position = "beforeend") { @@ -290,7 +292,21 @@ export class ActionButton extends DOMElement { }, action }) - } + }, + close: (widget) => { + return new ActionButton({ + size: 'small', + content: '❌', + action(_) { + widget.close(); + }, + cssStyle: { + position: 'absolute', + top: 0, + right: 0, + }, + }); + }, } } } diff --git a/src/js/gui/Widgets/Box.js b/src/js/gui/Widgets/Box.js index 724e885e..4372b027 100644 --- a/src/js/gui/Widgets/Box.js +++ b/src/js/gui/Widgets/Box.js @@ -26,7 +26,7 @@ import { Layout } from "../Layout"; /****************************************************************************** * Aladin Lite project * - * File gui/Tab.js + * File gui/Widgets/Box.js * * A context menu that shows when the user right clicks, or long touch on touch device * @@ -62,21 +62,9 @@ export class Box extends DOMElement { let close = this.options.close === false ? false : true; let draggable = false; if (close) { - new ActionButton({ - size: 'small', - content: '❌', - //tooltip: {content: 'Close the window', position: {direction: 'bottom'}}, - action(e) { - self._hide(); - }, - cssStyle: { - position: 'absolute', - }, - position: { - top: 0, - right: 0, - } - }, this.el); + this.el.appendChild( + ActionButton.BUTTONS(null).close(this).element() + ); } if (this.options.onDragged) { diff --git a/src/js/gui/Widgets/ContextMenu.js b/src/js/gui/Widgets/ContextMenu.js index ddbedcbd..671236a9 100644 --- a/src/js/gui/Widgets/ContextMenu.js +++ b/src/js/gui/Widgets/ContextMenu.js @@ -55,7 +55,7 @@ export class ContextMenu extends DOMElement { this.cssStyleDefault = el.style; - if (!options || options.hideOnClick === undefined || options.hideOnClick === true || typeof options.hideOnClick === 'function') { + /*if (!options || options.hideOnClick === undefined || options.hideOnClick === true || typeof options.hideOnClick === 'function') { this.aladin.aladinDiv.addEventListener('click', (e) => { if (!el.contains(e.target)) { if (options && options.hideOnClick && typeof options.hideOnClick === 'function') { @@ -65,7 +65,7 @@ export class ContextMenu extends DOMElement { } } }); - } + }*/ if (!options || options.hideOnResize === undefined || options.hideOnResize === true) { if (Utils.hasTouchScreen()) { @@ -333,7 +333,7 @@ export class ContextMenu extends DOMElement { parent.style.display = ""; } - show(options) { + _show(options) { this.el.innerHTML = ''; this.el.style = this.cssStyleDefault @@ -363,14 +363,11 @@ export class ContextMenu extends DOMElement { super._show() } - attach(options, attached) { - this.attached = attached; - this.menuOptions = options; - } + attach(options, toggler) { + this._hide() + this.setToggler(toggler) - /* Hide all the defined menus */ - static hideAll() { - ContextMenu._menus.forEach((menu) => menu._hide()) + this.menuOptions = options; } /// Context menu predefined items diff --git a/src/js/gui/Widgets/Widget.js b/src/js/gui/Widgets/Widget.js index cc5fd04f..f87e4180 100644 --- a/src/js/gui/Widgets/Widget.js +++ b/src/js/gui/Widgets/Widget.js @@ -105,9 +105,10 @@ export class DOMElement { } else if (elmt instanceof Element) { parent.insertAdjacentElement('beforeend', elmt); } else { - let wrapEl = document.createElement('div'); - wrapEl.innerHTML = elmt; - parent.insertAdjacentElement('beforeend', wrapEl); + const template = document.createElement('template'); + template.innerHTML = elmt; + + parent.append(template.content.cloneNode(true)); } } } @@ -297,12 +298,24 @@ export class DOMElement { } } + setToggler(toggler) { + this.toggler = toggler; + } + _show() { + if (this.toggler) { + this.toggler.notify(true) + } + this.el.style.display = "" this.isHidden = false; } _hide() { + if (this.toggler) { + this.toggler.notify(false) + } + this.isHidden = true; this.el.style.display = 'none'; }