Compare commits

...

6 Commits

Author SHA1 Message Date
Matthieu Baumann
fe8cbd97ec update playwright snapshots 2025-05-23 18:44:56 +02:00
Matthieu Baumann
02ff1cdbdd fix test ci fmt 2025-05-23 18:25:15 +02:00
Matthieu Baumann
867f90f433 cargo fmt 2025-05-23 18:21:24 +02:00
Matthieu Baumann
d3e1cccca4 cargo clippy 2025-05-23 17:51:07 +02:00
Matthieu Baumann
0339183fdf remove warnings 2025-05-23 15:04:39 +02:00
Matthieu Baumann
cc35560e5c fix allsky retrieval and simplify hips rendering 2025-05-22 19:07:45 +02:00
131 changed files with 1499 additions and 2214 deletions

View File

@@ -25,6 +25,10 @@ jobs:
- name: "Install wasm-pack"
run: |
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -y
- name: "Run cargo clippy"
run: cd src/core && cargo clippy --all-targets --features=webgl2 -- -D warnings
- name: Format check
run: cd src/core && cargo fmt --all -- --check
- name: "Install dependencies"
run: |
npm install

View File

@@ -14,7 +14,7 @@
aladin = A.aladin('#aladin-lite-div', {showSettingsControl: true, survey: "P/PanSTARRS/DR1/color-z-zg-g", showReticle: false, projection: "AIT", cooFrame: 'icrs', target: "stephan's quintet", fov: startFov, showGotoControl: false, showFrame: false, fullScreen: true, showLayersControl: true, showCooGridControl: false});
const chft = aladin.createImageSurvey('CFHT', "CFHT deep view of NGC7331 and Stephan's quintet u+g+r", "https://cds.unistra.fr/~derriere/PR_HiPS/2022_Duc/", null, null, {imgFormat: 'png'});
const nircamJWST = aladin.createImageSurvey('Nircam', "Stephans Quintet NIRCam+MIRI", "http://alasky.cds.unistra.fr/JWST/CDS_P_JWST_Stephans-Quintet_NIRCam+MIRI/", null, null, {imgFormat: 'png', colormap: "viridis"});
const nircamJWST = aladin.createImageSurvey('Nircam', "Stephans Quintet NIRCam+MIRI", "https://alasky.cds.unistra.fr/JWST/CDS_P_JWST_Stephans-Quintet_NIRCam-MIRI/", null, null, {imgFormat: 'png', colormap: "viridis"});
aladin.setOverlayImageLayer("CFHT", "CFHT");
aladin.setOverlayImageLayer("Nircam", "Nircam");

View File

@@ -14,7 +14,7 @@
'#aladin-lite-div',
{
showSimbadPointerControl: true,
survey: 'P/allWISE/color', // set initial image survey
survey: 'https://skies.esac.esa.int/AKARI/color/', // set initial image survey
projection: 'AIT', // set a projection
fov: 360, // initial field of view in degrees
target: 'orion', // initial target

View File

@@ -12,7 +12,7 @@
import A from '../src/js/A.js';
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {inertia: false, target: '00 00 00 +07 00 00', fov: 130, survey: 'P/Mellinger/color', showContextMenu: true, fullScreen: true});
aladin = A.aladin('#aladin-lite-div', {inertia: true, target: '00 00 00 +07 00 00', fov: 130, survey: 'P/Mellinger/color', showContextMenu: true, fullScreen: true});
var moc11 = A.MOCFromURL('http://skies.esac.esa.int/HST/NICMOS/Moc.fits', {color: '#84f', lineWidth: 3}, (moc) => {
// moc is ready
console.log(moc.contains(205.9019247, +2.4492764));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -1,6 +1,6 @@
[package]
name = "al-api"
version = "3.6.5"
version = "3.7.0"
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
edition = "2018"

View File

@@ -32,5 +32,5 @@ use std::cmp::Eq;
#[wasm_bindgen]
pub enum Formatter {
Sexagesimal,
Decimal
}
Decimal,
}

View File

@@ -4,8 +4,6 @@ use wasm_bindgen::prelude::wasm_bindgen;
#[cfg(feature = "webgl2")]
pub type WebGlRenderingCtx = web_sys::WebGl2RenderingContext;
#[cfg(feature = "webgl1")]
pub type WebGlRenderingCtx = web_sys::WebGlRenderingContext;
#[derive(Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "camelCase")]

View File

@@ -1,9 +1,8 @@
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug)]
#[derive(Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct HEALPixCellProjeted {
pub ipix: u64,
pub vx: [f64; 4],
pub vy: [f64; 4],
}
}

View File

@@ -34,7 +34,7 @@ pub struct ColorRGBA {
}
use std::ops::Mul;
impl<'a> Mul<f32> for &'a ColorRGB {
impl Mul<f32> for &ColorRGB {
// The multiplication of rational numbers is a closed operation.
type Output = ColorRGB;

View File

@@ -1,39 +1,30 @@
use cgmath::Matrix3;
const GAL2ICRS: &'static Matrix3<f64> = &Matrix3::new(
-0.44482972122205372312012370920248,
0.74698218398450941835110635824212,
0.49410943719710765017955928850141,
-0.19807633727507056817237662907031,
0.45598381369115237931077906137440,
-0.86766613755716255824577781583414,
-0.87343705195577915249273984034980,
-0.48383507361641838378786914298189,
-0.05487565771261968232908806948676,
const GAL2ICRS: &Matrix3<f64> = &Matrix3::new(
-0.444_829_721_222_053_7,
0.746_982_183_984_509_4,
0.494_109_437_197_107_65,
-0.198_076_337_275_070_57,
0.455_983_813_691_152_4,
-0.867_666_137_557_162_6,
-0.873_437_051_955_779_1,
-0.483_835_073_616_418_37,
-0.054_875_657_712_619_68,
);
const ICRS2GAL: &'static Matrix3<f64> = &Matrix3::new(
-0.44482972122205372312012370920248,
-0.19807633727507056817237662907031,
-0.87343705195577915249273984034980,
0.74698218398450941835110635824212,
0.45598381369115237931077906137440,
-0.48383507361641838378786914298189,
0.49410943719710765017955928850141,
-0.86766613755716255824577781583414,
-0.05487565771261968232908806948676,
);
const ID: &'static Matrix3<f64> = &Matrix3::new(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
const ICRS2GAL: &Matrix3<f64> = &Matrix3::new(
-0.444_829_721_222_053_7,
-0.198_076_337_275_070_57,
-0.873_437_051_955_779_1,
0.746_982_183_984_509_4,
0.455_983_813_691_152_4,
-0.483_835_073_616_418_37,
0.494_109_437_197_107_65,
-0.867_666_137_557_162_6,
-0.054_875_657_712_619_68,
);
const ID: &Matrix3<f64> = &Matrix3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
use serde::Deserialize;
use wasm_bindgen::prelude::*;

View File

@@ -163,9 +163,10 @@ impl std::fmt::Display for ImageExt {
use serde::Serialize;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[derive(Clone, Copy, PartialEq, Debug, Deserialize, Serialize)]
#[derive(Clone, Copy, PartialEq, Debug, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub enum TransferFunction {
#[default]
Linear,
Sqrt,
Log,
@@ -189,12 +190,6 @@ impl TransferFunction {
}
}
impl Default for TransferFunction {
fn default() -> Self {
TransferFunction::Linear
}
}
impl From<String> for TransferFunction {
fn from(id: String) -> Self {
TransferFunction::new(&id)

View File

@@ -2,27 +2,29 @@
It is used by al-ui and any javascript application calling
the WASM core of aladin lite v3
*/
pub mod angle;
pub mod blend;
pub mod cell;
pub mod color;
pub mod colormap;
pub mod coo_system;
pub mod fov;
pub mod grid;
pub mod hips;
pub mod image;
pub mod moc;
pub mod resources;
pub mod cell;
pub mod fov;
pub mod image;
pub mod angle;
pub trait Abort {
type Item;
fn unwrap_abort(self) -> Self::Item where Self: Sized;
fn unwrap_abort(self) -> Self::Item
where
Self: Sized;
}
impl<T> Abort for Option<T> {
type Item = T;
#[inline]
fn unwrap_abort(self) -> Self::Item {
use std::process;

View File

@@ -1,7 +1,6 @@
use wasm_bindgen::prelude::wasm_bindgen;
use super::color::{Color, ColorRGBA};
#[derive(Clone, Debug)]
#[wasm_bindgen]
pub struct MOCOptions {
@@ -19,6 +18,7 @@ use crate::{color::ColorRGB, Abort};
use std::convert::TryInto;
#[wasm_bindgen]
impl MOCOptions {
#[allow(clippy::too_many_arguments)]
#[wasm_bindgen(constructor)]
pub fn new(
uuid: String,

View File

@@ -1,6 +1,6 @@
[package]
name = "al-core"
version = "3.6.5"
version = "3.7.0"
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
edition = "2018"

View File

@@ -2,13 +2,13 @@ use std::collections::HashMap;
use colorgrad::Color;
use crate::Texture2D;
use crate::WebGlContext;
use crate::image::format;
use crate::shader::SendUniformsWithParams;
use crate::Texture2D;
use crate::WebGlContext;
use wasm_bindgen::JsValue;
use crate::webgl_ctx::WebGlRenderingCtx;
use wasm_bindgen::JsValue;
const WIDTH_CMAP_TEX: usize = 256;
@@ -20,7 +20,10 @@ pub struct Colormap {
}
impl Colormap {
pub fn new(label: &str, grad: colorgrad::Gradient) -> Self {
Self { label: label.to_string(), grad }
Self {
label: label.to_string(),
grad,
}
}
pub fn label(&self) -> &Label {
@@ -29,18 +32,20 @@ impl Colormap {
}
fn build_cmaps_texture(gl: &WebGlContext, cmaps: &[Colormap]) -> Result<Texture2D, JsValue> {
let tex_bytes: Vec<u8> = cmaps.iter()
.map(|cmap| {
let tex_bytes: Vec<u8> = cmaps
.iter()
.flat_map(|cmap| {
let mut values = [0_u8; 1024];
for ix in 0..WIDTH_CMAP_TEX {
let rgba = cmap.grad.at(ix as f64 / WIDTH_CMAP_TEX as f64).to_rgba8();
let ptr = values[4*ix..].as_mut_ptr() as *mut [u8; 4];
unsafe { *ptr = rgba; }
let ptr = values[4 * ix..].as_mut_ptr() as *mut [u8; 4];
unsafe {
*ptr = rgba;
}
}
values
})
.flatten()
.collect();
let tex_params = &[
(
@@ -68,7 +73,7 @@ fn build_cmaps_texture(gl: &WebGlContext, cmaps: &[Colormap]) -> Result<Texture2
WIDTH_CMAP_TEX as i32,
cmaps.len() as i32,
tex_params,
Some(&tex_bytes[..])
Some(&tex_bytes[..]),
)
}
@@ -87,20 +92,38 @@ use crate::Abort;
impl Colormaps {
pub fn new(gl: &WebGlContext) -> Result<Self, JsValue> {
let labels: Vec<_> = [
"blues", "cividis", "cubehelix", "eosb",
"grayscale", "inferno", "magma", "native",
"parula", "plasma", "rainbow", "rdbu",
"rdylbu", "redtemperature", "sinebow", "spectral", "summer",
"viridis", "ylgnbu", "ylorbr", "red", "green", "blue"
"blues",
"cividis",
"cubehelix",
"eosb",
"grayscale",
"inferno",
"magma",
"native",
"parula",
"plasma",
"rainbow",
"rdbu",
"rdylbu",
"redtemperature",
"sinebow",
"spectral",
"summer",
"viridis",
"ylgnbu",
"ylorbr",
"red",
"green",
"blue",
]
.iter()
.map(|cmap_name| cmap_name.to_string())
.collect();
let indices = labels.iter().enumerate()
.map(|(id, label)| {
(label.clone(), id as i32)
})
let indices = labels
.iter()
.enumerate()
.map(|(id, label)| (label.clone(), id as i32))
.collect();
let cmaps = vec![
@@ -203,7 +226,13 @@ impl Colormaps {
let cmaps_tex = build_cmaps_texture(gl, &cmaps[..])?;
let gl = gl.clone();
Ok(Self { cmaps, cmaps_tex, labels, indices, gl })
Ok(Self {
cmaps,
cmaps_tex,
labels,
indices,
gl,
})
}
#[inline]
@@ -213,12 +242,15 @@ impl Colormaps {
#[inline]
pub fn get(&self, label: &str) -> &Colormap {
if let Some(id) = self.get_id(label).map(|id| *id) {
if let Some(&id) = self.get_id(label) {
&self.cmaps[id as usize]
} else {
crate::log::console_warn(&format!("{:?} is not a valid colormap, replaced with 'grayscale'.", label));
let id_greys = self.get_id("grayscale").map(|id| *id).unwrap_abort();
&self.cmaps[id_greys as usize]
crate::log::console_warn(format!(
"{:?} is not a valid colormap, replaced with 'grayscale'.",
label
));
let id_greys = self.get_id("grayscale").unwrap_abort();
&self.cmaps[*id_greys as usize]
}
}
@@ -228,13 +260,13 @@ impl Colormaps {
}
pub fn add_cmap(&mut self, label: Label, cmap: Colormap) -> Result<(), JsValue> {
if let Some(id) = self.get_id(&label).map(|id| *id) {
if let Some(&id) = self.get_id(&label) {
let colormap = &mut self.cmaps[id as usize];
*colormap = cmap;
} else {
let num_cmaps = self.labels.len();
self.labels.push(label.clone());
self.indices.insert(label, num_cmaps as i32);
self.cmaps.push(cmap);
}
@@ -246,7 +278,7 @@ impl Colormaps {
}
}
use crate::shader::{ShaderBound, SendUniforms};
use crate::shader::{SendUniforms, ShaderBound};
impl SendUniforms for Colormaps {
fn attach_uniforms<'a>(&self, shader: &'a ShaderBound<'a>) -> &'a ShaderBound<'a> {
shader
@@ -258,7 +290,11 @@ impl SendUniforms for Colormaps {
}
impl SendUniformsWithParams<Colormaps> for Colormap {
fn attach_uniforms_with_params<'a>(&self, shader: &'a ShaderBound<'a>, cmaps: &Colormaps) -> &'a ShaderBound<'a> {
fn attach_uniforms_with_params<'a>(
&self,
shader: &'a ShaderBound<'a>,
cmaps: &Colormaps,
) -> &'a ShaderBound<'a> {
let cmap_id = cmaps.get_id(&self.label).unwrap_abort();
shader.attach_uniform("colormap_id", &(*cmap_id as f32));
shader

View File

@@ -34,4 +34,8 @@ where
Ok(())
}
fn get_size(&self) -> (u32, u32) {
(self.image.width(), self.image.height())
}
}

View File

@@ -42,4 +42,8 @@ where
Ok(())
}
fn get_size(&self) -> (u32, u32) {
(self.canvas.width(), self.canvas.height())
}
}

View File

@@ -23,7 +23,7 @@ use std::io::Cursor;
impl<'a> Fits<'a> {
pub fn from_byte_slice(bytes_reader: &'a mut Cursor<&[u8]>) -> Result<Self, JsValue> {
let FitsData { hdu } = FitsData::from_reader(bytes_reader)
.map_err(|_| JsValue::from_str(&"Parsing fits error"))?;
.map_err(|_| JsValue::from_str("Parsing fits error"))?;
let header = hdu.get_header();
let xtension = header.get_xtension();
@@ -59,63 +59,8 @@ impl<'a> Fits<'a> {
data,
})
}
pub fn get_size(&self) -> &Vector2<i32> {
&self.size
}
}
/*impl Fits<'static> {
pub async fn from_async_reader(reader: IntoAsyncRead<'static>) -> Result<Self, JsValue> {
let fitsrs::fits::AsyncFits { hdu: AsyncHDU { data, header } } = fitsrs::fits::AsyncFits::from_reader(futures::io::BufReader::new(reader))
.await
.map_err(|err| {
JsValue::from_str(&format!("Parsing fits error: {}", err))
})?;
let width = header.get_axis_size(1)
.ok_or_else(|| JsValue::from_str("NAXIS1 not found in the fits"))?;
let height = header.get_axis_size(2)
.ok_or_else(|| JsValue::from_str("NAXIS2 not found in the fits"))?;
let data = match data {
fitsrs::hdu::data_async::DataOwned::U8(stream) => {
let data = stream.collect().await;
Data::U8(Cow::Owned(data))
},
fitsrs::hdu::data_async::DataOwned::I16(stream) => {
let data = stream.collect().await;
Data::I16(Cow::Owned(data))
},
fitsrs::hdu::data_async::DataOwned::I32(stream) => {
let data = stream.collect().await;
Data::I32(Cow::Owned(data))
},
fitsrs::hdu::data_async::DataOwned::I64(stream) => {
let data = stream.map(|v| v as i32).collect().await;
Data::I32(Cow::Owned(data))
},
fitsrs::hdu::data_async::DataOwned::F32(stream) => {
let data = stream.collect().await;
Data::F32(Cow::Owned(data))
},
fitsrs::hdu::data_async::DataOwned::F64(stream) => {
let data = stream.map(|v| v as f32).collect().await;
Data::F32(Cow::Owned(data))
}
};
Ok(Self {
// Tile size
size: Vector2::new(*width as i32, *height as i32),
// Allocation info of the layout
data
})
}
}*/
use crate::{image::Image, texture::Tex3D};
impl Image for Fits<'_> {
fn insert_into_3d_texture<T: Tex3D>(
@@ -127,7 +72,7 @@ impl Image for Fits<'_> {
) -> Result<(), JsValue> {
match &self.data {
Data::U8(data) => {
let view = unsafe { R8UI::view(&data) };
let view = unsafe { R8UI::view(data) };
textures.tex_sub_image_3d_with_opt_array_buffer_view(
offset.x,
offset.y,
@@ -139,7 +84,7 @@ impl Image for Fits<'_> {
);
}
Data::I16(data) => {
let view = unsafe { R16I::view(&data) };
let view = unsafe { R16I::view(data) };
textures.tex_sub_image_3d_with_opt_array_buffer_view(
offset.x,
offset.y,
@@ -151,7 +96,7 @@ impl Image for Fits<'_> {
);
}
Data::I32(data) => {
let view = unsafe { R32I::view(&data) };
let view = unsafe { R32I::view(data) };
textures.tex_sub_image_3d_with_opt_array_buffer_view(
offset.x,
offset.y,
@@ -163,7 +108,12 @@ impl Image for Fits<'_> {
);
}
Data::F32(data) => {
let view = unsafe { R8UI::view(&std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4)) };
let view = unsafe {
R8UI::view(std::slice::from_raw_parts(
data.as_ptr() as *const u8,
data.len() * 4,
))
};
textures.tex_sub_image_3d_with_opt_array_buffer_view(
offset.x,
offset.y,
@@ -178,6 +128,10 @@ impl Image for Fits<'_> {
Ok(())
}
fn get_size(&self) -> (u32, u32) {
(self.size.x as u32, self.size.y as u32)
}
}
use crate::image::format::ImageFormat;

View File

@@ -40,7 +40,7 @@ impl ImageFormat for RGB8U {
const NUM_CHANNELS: usize = 3;
const FORMAT: u32 = WebGlRenderingCtx::RGB as u32;
const FORMAT: u32 = WebGlRenderingCtx::RGB;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGB8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
@@ -70,7 +70,7 @@ impl ImageFormat for RGBA8U {
const NUM_CHANNELS: usize = 4;
const FORMAT: u32 = WebGlRenderingCtx::RGBA as u32;
const FORMAT: u32 = WebGlRenderingCtx::RGBA;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
@@ -99,7 +99,7 @@ impl ImageFormat for RGBA32F {
const NUM_CHANNELS: usize = 4;
const FORMAT: u32 = WebGlRenderingCtx::RGBA as u32;
const FORMAT: u32 = WebGlRenderingCtx::RGBA;
#[cfg(feature = "webgl2")]
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA32F as i32;
@@ -128,7 +128,7 @@ impl ImageFormat for RGB32F {
const NUM_CHANNELS: usize = 3;
const FORMAT: u32 = WebGlRenderingCtx::RGB as u32;
const FORMAT: u32 = WebGlRenderingCtx::RGB;
#[cfg(feature = "webgl2")]
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGB32F as i32;
#[cfg(feature = "webgl1")]
@@ -156,7 +156,7 @@ impl ImageFormat for R32F {
const NUM_CHANNELS: usize = 4;
const FORMAT: u32 = WebGlRenderingCtx::RGBA as u32;
const FORMAT: u32 = WebGlRenderingCtx::RGBA;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
@@ -180,7 +180,7 @@ impl ImageFormat for R64F {
const NUM_CHANNELS: usize = 4;
const FORMAT: u32 = WebGlRenderingCtx::RGBA as u32;
const FORMAT: u32 = WebGlRenderingCtx::RGBA;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
@@ -206,7 +206,7 @@ impl ImageFormat for R8UI {
const NUM_CHANNELS: usize = 1;
const FORMAT: u32 = WebGlRenderingCtx::RED_INTEGER as u32;
const FORMAT: u32 = WebGlRenderingCtx::RED_INTEGER;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::R8UI as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
@@ -232,7 +232,7 @@ impl ImageFormat for R16I {
const NUM_CHANNELS: usize = 1;
const FORMAT: u32 = WebGlRenderingCtx::RED_INTEGER as u32;
const FORMAT: u32 = WebGlRenderingCtx::RED_INTEGER;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::R16I as i32;
const TYPE: u32 = WebGlRenderingCtx::SHORT;
const CHANNEL_TYPE: ChannelType = ChannelType::R16I;
@@ -257,7 +257,7 @@ impl ImageFormat for R32I {
const NUM_CHANNELS: usize = 1;
const FORMAT: u32 = WebGlRenderingCtx::RED_INTEGER as u32;
const FORMAT: u32 = WebGlRenderingCtx::RED_INTEGER;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::R32I as i32;
const TYPE: u32 = WebGlRenderingCtx::INT;
@@ -293,13 +293,10 @@ pub enum ChannelType {
impl ChannelType {
pub fn is_colored(&self) -> bool {
match self {
ChannelType::RGBA32F
| ChannelType::RGB32F
| ChannelType::RGBA8U
| ChannelType::RGB8U => true,
_ => false,
}
matches!(
self,
ChannelType::RGBA32F | ChannelType::RGB32F | ChannelType::RGBA8U | ChannelType::RGB8U
)
}
}

View File

@@ -42,4 +42,8 @@ where
Ok(())
}
fn get_size(&self) -> (u32, u32) {
(self.image.width(), self.image.height())
}
}

View File

@@ -5,6 +5,10 @@ pub mod format;
pub mod html;
pub mod raw;
use crate::image::bitmap::Bitmap;
use crate::image::format::RGB8U;
use crate::image::format::RGBA8U;
use crate::image::raw::ImageBuffer;
pub trait ArrayBuffer: AsRef<js_sys::Object> + std::fmt::Debug {
type Item: std::cmp::PartialOrd + Clone + Copy + std::fmt::Debug + cgmath::Zero;
@@ -33,8 +37,7 @@ impl ArrayBuffer for ArrayU8 {
fn empty(size: u32, blank_value: Self::Item) -> Self {
let uint8_arr = js_sys::Uint8Array::new_with_length(size).fill(blank_value, 0, size);
let array = ArrayU8(uint8_arr);
array
ArrayU8(uint8_arr)
}
fn to_vec(&self) -> Vec<Self::Item> {
@@ -65,8 +68,7 @@ impl ArrayBuffer for ArrayI16 {
fn empty(size: u32, blank_value: Self::Item) -> Self {
let int16_arr = js_sys::Int16Array::new_with_length(size).fill(blank_value, 0, size);
let array = ArrayI16(int16_arr);
array
ArrayI16(int16_arr)
}
fn to_vec(&self) -> Vec<Self::Item> {
@@ -97,8 +99,7 @@ impl ArrayBuffer for ArrayI32 {
fn empty(size: u32, blank_value: Self::Item) -> Self {
let int32_arr = js_sys::Int32Array::new_with_length(size).fill(blank_value, 0, size);
let array = ArrayI32(int32_arr);
array
ArrayI32(int32_arr)
}
fn to_vec(&self) -> Vec<Self::Item> {
@@ -129,8 +130,7 @@ impl ArrayBuffer for ArrayF32 {
}
fn empty(size: u32, blank_value: Self::Item) -> Self {
let f32_arr = js_sys::Float32Array::new_with_length(size).fill(blank_value, 0, size);
let array = ArrayF32(f32_arr);
array
ArrayF32(f32_arr)
}
fn to_vec(&self) -> Vec<Self::Item> {
@@ -162,8 +162,7 @@ impl ArrayBuffer for ArrayF64 {
}
fn empty(size: u32, blank_value: Self::Item) -> Self {
let f64_arr = js_sys::Float64Array::new_with_length(size).fill(blank_value, 0, size);
let array = ArrayF64(f64_arr);
array
ArrayF64(f64_arr)
}
fn to_vec(&self) -> Vec<Self::Item> {
@@ -190,9 +189,11 @@ pub trait Image {
// An offset to write the image in the texture array
offset: &Vector3<i32>,
) -> Result<(), JsValue>;
fn get_size(&self) -> (u32, u32);
}
impl<'a, I> Image for &'a I
impl<I> Image for &I
where
I: Image,
{
@@ -208,6 +209,11 @@ where
Ok(())
}
fn get_size(&self) -> (u32, u32) {
let image = &**self;
image.get_size()
}
}
use std::{io::Cursor, rc::Rc};
@@ -227,65 +233,58 @@ where
Ok(())
}
}
/*impl<I> Image for Arc<Mutex<Option<I>>>
where
I: Image,
{
fn tex_sub_image_3d(
&self,
// The texture array
textures: &Texture2DArray,
// An offset to write the image in the texture array
offset: &Vector3<i32>,
) -> Result<(), JsValue> {
if let Some(image) = &*self.lock().unwrap_abort() {
image.tex_sub_image_3d(textures, offset)?;
}
Ok(())
fn get_size(&self) -> (u32, u32) {
let image = &**self;
image.get_size()
}
}*/
}
#[cfg(feature = "webgl2")]
use crate::image::format::{R16I, R32I, R64F, R8UI};
use crate::{
image::format::{R32F, RGB8U, RGBA8U},
texture::Tex3D,
};
use crate::{image::format::R32F, texture::Tex3D};
use bitmap::Bitmap;
use fits::Fits;
use raw::ImageBuffer;
#[derive(Debug)]
#[cfg(feature = "webgl2")]
pub enum ImageType {
FitsImage { raw_bytes: js_sys::Uint8Array },
Canvas { canvas: Canvas<RGBA8U> },
ImageRgba8u { image: Bitmap<RGBA8U> },
ImageRgb8u { image: Bitmap<RGB8U> },
HTMLImageRgba8u { image: HTMLImage<RGBA8U> },
HTMLImageRgb8u { image: HTMLImage<RGB8U> },
RawRgb8u { image: ImageBuffer<RGB8U> },
RawRgba8u { image: ImageBuffer<RGBA8U> },
RawR32f { image: ImageBuffer<R32F> },
RawR32i { image: ImageBuffer<R32I> },
RawR16i { image: ImageBuffer<R16I> },
RawR8ui { image: ImageBuffer<R8UI> },
}
#[cfg(feature = "webgl1")]
pub enum ImageType {
FitsImage { raw_bytes: js_sys::Uint8Array },
Canvas { canvas: Canvas<RGBA8U> },
PngHTMLImageRgba8u { image: HTMLImage<RGBA8U> },
JpgHTMLImageRgb8u { image: HTMLImage<RGB8U> },
PngImageRgba8u { image: Bitmap<RGBA8U> },
JpgImageRgb8u { image: Bitmap<RGB8U> },
RawRgb8u { image: ImageBuffer<RGB8U> },
RawRgba8u { image: ImageBuffer<RGBA8U> },
RawR32f { image: ImageBuffer<R32F> },
FitsImage {
raw_bytes: js_sys::Uint8Array,
size: (u32, u32),
},
Canvas {
canvas: Canvas<RGBA8U>,
},
ImageRgba8u {
image: Bitmap<RGBA8U>,
},
ImageRgb8u {
image: Bitmap<RGB8U>,
},
HTMLImageRgba8u {
image: HTMLImage<RGBA8U>,
},
HTMLImageRgb8u {
image: HTMLImage<RGB8U>,
},
RawRgb8u {
image: ImageBuffer<RGB8U>,
},
RawRgba8u {
image: ImageBuffer<RGBA8U>,
},
RawR32f {
image: ImageBuffer<R32F>,
},
RawR32i {
image: ImageBuffer<R32I>,
},
RawR16i {
image: ImageBuffer<R16I>,
},
RawR8ui {
image: ImageBuffer<R8UI>,
},
}
use cgmath::Vector3;
@@ -300,6 +299,7 @@ impl Image for ImageType {
match self {
ImageType::FitsImage {
raw_bytes: raw_bytes_buf,
..
} => {
let num_bytes = raw_bytes_buf.length() as usize;
let mut raw_bytes = vec![0; num_bytes];
@@ -328,4 +328,21 @@ impl Image for ImageType {
Ok(())
}
fn get_size(&self) -> (u32, u32) {
match self {
ImageType::FitsImage { size, .. } => *size,
ImageType::Canvas { canvas } => canvas.get_size(),
ImageType::ImageRgba8u { image } => image.get_size(),
ImageType::ImageRgb8u { image } => image.get_size(),
ImageType::HTMLImageRgba8u { image } => image.get_size(),
ImageType::HTMLImageRgb8u { image } => image.get_size(),
ImageType::RawRgb8u { image } => image.get_size(),
ImageType::RawRgba8u { image } => image.get_size(),
ImageType::RawR32f { image } => image.get_size(),
ImageType::RawR32i { image } => image.get_size(),
ImageType::RawR16i { image } => image.get_size(),
ImageType::RawR8ui { image } => image.get_size(),
}
}
}

View File

@@ -37,7 +37,7 @@ where
width: i32,
height: i32,
) -> Result<Self, JsValue> {
let mut decoded_bytes = match T::decode(raw_bytes).map_err(|e| JsValue::from_str(e))? {
let mut decoded_bytes = match T::decode(raw_bytes).map_err(JsValue::from_str)? {
Bytes::Borrowed(bytes) => bytes.to_vec(),
Bytes::Owned(bytes) => bytes,
};
@@ -46,7 +46,9 @@ where
decoded_bytes.set_len(
decoded_bytes.len() / std::mem::size_of::<<<T as ImageFormat>::P as Pixel>::Item>(),
);
std::mem::transmute(decoded_bytes)
std::mem::transmute::<Vec<u8>, Vec<<<T as ImageFormat>::P as Pixel>::Item>>(
decoded_bytes,
)
};
Ok(Self::new(decoded_pixels, width, height))
@@ -60,7 +62,7 @@ where
raw_bytes.set_len(
raw_bytes.len() / std::mem::size_of::<<<T as ImageFormat>::P as Pixel>::Item>(),
);
std::mem::transmute(raw_bytes)
std::mem::transmute::<Vec<u8>, Vec<<<T as ImageFormat>::P as Pixel>::Item>>(raw_bytes)
};
Self::new(decoded_pixels, width, height)
@@ -166,7 +168,7 @@ where
}
// The size of the image
/*fn get_size(&self) -> &Vector2<i32> {
&self.size
}*/
fn get_size(&self) -> (u32, u32) {
(self.size.x as u32, self.size.y as u32)
}
}

View File

@@ -409,9 +409,9 @@ impl ArrayBuffer {
}
}
pub fn set_vertex_attrib_pointer_by_name<'a, T: VertexAttribPointerType>(
pub fn set_vertex_attrib_pointer_by_name<T: VertexAttribPointerType>(
&self,
shader: &ShaderBound<'a>,
shader: &ShaderBound<'_>,
location: &str,
) {
let loc = shader.get_attrib_location(&self.gl, location);
@@ -434,11 +434,7 @@ impl ArrayBuffer {
.vertex_attrib_divisor_angle(loc as u32, 0);
}
pub fn disable_vertex_attrib_pointer_by_name<'a>(
&self,
shader: &ShaderBound<'a>,
location: &str,
) {
pub fn disable_vertex_attrib_pointer_by_name(&self, shader: &ShaderBound<'_>, location: &str) {
let loc = shader.get_attrib_location(&self.gl, location);
self.gl.disable_vertex_attrib_array(loc as u32);
}

View File

@@ -49,7 +49,7 @@ impl ArrayBufferInstanced {
// Total length
let num_f32_in_buf = data.len() as i32;
let num_instances = num_f32_in_buf / (num_f32_per_instance as i32);
let num_instances = num_f32_in_buf / num_f32_per_instance;
let len = data.len();
let buffer = gl
@@ -98,9 +98,9 @@ impl ArrayBufferInstanced {
}
}
pub fn set_vertex_attrib_pointer_by_name<'a, T: VertexAttribPointerType>(
pub fn set_vertex_attrib_pointer_by_name<T: VertexAttribPointerType>(
&self,
shader: &ShaderBound<'a>,
shader: &ShaderBound<'_>,
location: &str,
) {
let loc = shader.get_attrib_location(&self.gl, location);
@@ -124,11 +124,7 @@ impl ArrayBufferInstanced {
.vertex_attrib_divisor_angle(loc as u32, 1);
}
pub fn disable_vertex_attrib_pointer_by_name<'a>(
&self,
shader: &ShaderBound<'a>,
location: &str,
) {
pub fn disable_vertex_attrib_pointer_by_name(&self, shader: &ShaderBound<'_>, location: &str) {
let loc = shader.get_attrib_location(&self.gl, location);
self.gl.disable_vertex_attrib_array(loc as u32);

View File

@@ -37,7 +37,10 @@ impl ElementArrayBuffer {
usage: u32,
data: B,
) -> ElementArrayBuffer {
let buffer = gl.create_buffer().ok_or("failed to create buffer").unwrap_abort();
let buffer = gl
.create_buffer()
.ok_or("failed to create buffer")
.unwrap_abort();
// Bind the buffer
gl.bind_buffer(
WebGlRenderingCtx::ELEMENT_ARRAY_BUFFER,

View File

@@ -117,7 +117,7 @@ pub mod vao {
}
use web_sys::WebGl2RenderingContext;
impl<'a, 'b> ShaderVertexArrayObjectBound<'a, 'b> {
impl<'a> ShaderVertexArrayObjectBound<'a, '_> {
pub fn update_array<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
attr: &'static str,
@@ -162,7 +162,7 @@ pub mod vao {
}
}
impl<'a, 'b> Drop for ShaderVertexArrayObjectBound<'a, 'b> {
impl Drop for ShaderVertexArrayObjectBound<'_, '_> {
fn drop(&mut self) {
self.unbind();
}
@@ -173,7 +173,7 @@ pub mod vao {
_shader: &'b ShaderBound<'b>,
}
impl<'a, 'b> ShaderVertexArrayObjectBoundRef<'a, 'b> {
impl ShaderVertexArrayObjectBoundRef<'_, '_> {
pub fn draw_arrays(&self, mode: u32, byte_offset: i32, size: i32) {
self.vao.gl.draw_arrays(mode, byte_offset, size);
}
@@ -211,7 +211,7 @@ pub mod vao {
}
}
impl<'a, 'b> Drop for ShaderVertexArrayObjectBoundRef<'a, 'b> {
impl Drop for ShaderVertexArrayObjectBoundRef<'_, '_> {
fn drop(&mut self) {
self.unbind();
}
@@ -362,373 +362,7 @@ pub mod vao {
}
}
impl<'a> Drop for VertexArrayObjectBound<'a> {
fn drop(&mut self) {
self.unbind();
}
}
}
#[cfg(feature = "webgl1")]
pub mod vao {
use crate::object::array_buffer::ArrayBuffer;
use crate::object::array_buffer_instanced::ArrayBufferInstanced;
use crate::object::buffer_data::BufferDataStorage;
use crate::object::element_array_buffer::ElementArrayBuffer;
use crate::webgl_ctx::WebGlContext;
use crate::Abort;
use std::collections::HashMap;
pub struct VertexArrayObject {
array_buffer: HashMap<&'static str, ArrayBuffer>,
array_buffer_instanced: HashMap<&'static str, ArrayBufferInstanced>,
element_array_buffer: Option<ElementArrayBuffer>,
idx: u32, // Number of vertex attributes
gl: WebGlContext,
}
impl VertexArrayObject {
pub fn new(gl: &WebGlContext) -> VertexArrayObject {
let array_buffer = HashMap::new();
let array_buffer_instanced = HashMap::new();
let element_array_buffer = None;
let idx = 0;
let gl = gl.clone();
VertexArrayObject {
array_buffer,
array_buffer_instanced,
element_array_buffer,
idx,
gl,
}
}
// Shader has to be already bound before calling this
// This returns a ShaderVertexArrayObjectBound for which it is possible
// to add some buffers and or draw the buffers
pub fn bind<'a, 'b>(
&'a mut self,
_shader: &'b ShaderBound<'b>,
) -> ShaderVertexArrayObjectBound<'a, 'b> {
//self.gl.bind_vertex_array(Some(self.vao.as_ref()));
ShaderVertexArrayObjectBound { vao: self, _shader }
}
// Shader has to be already bound before calling this
// This returns a ShaderVertexArrayObjectBound for which it is possible
// to add some buffers and or draw the buffers
pub fn bind_ref<'a, 'b>(
&'a self,
shader: &'b ShaderBound<'b>,
) -> ShaderVertexArrayObjectBoundRef<'a, 'b> {
//self.gl.bind_vertex_array(Some(self.vao.as_ref()));
ShaderVertexArrayObjectBoundRef { vao: self, shader }
}
// No need to bind a shader here
// This returns a VertexArrayObjectBound for which it is only possible to
// update the buffers
pub fn bind_for_update<'a>(&'a mut self) -> VertexArrayObjectBound<'a> {
//self.gl.bind_vertex_array(Some(self.vao.as_ref()));
VertexArrayObjectBound { vao: self }
}
/*pub fn bind_ref(&self) {
self.gl.bind_vertex_array(Some(self.vao.as_ref()));
}*/
pub fn num_elements(&self) -> usize {
self.element_array_buffer
.as_ref()
.unwrap_abort()
.num_elements()
}
pub fn num_instances(&self) -> i32 {
self.array_buffer_instanced
.values()
.next()
.unwrap_abort()
.num_instances()
}
}
impl Drop for VertexArrayObject {
fn drop(&mut self) {
//self.unbind();
//self.gl.delete_vertex_array(Some(self.vao.as_ref()));
}
}
use crate::shader::ShaderBound;
pub struct ShaderVertexArrayObjectBound<'a, 'b> {
vao: &'a mut VertexArrayObject,
_shader: &'b ShaderBound<'b>,
}
use crate::VertexAttribPointerType;
impl<'a, 'b> ShaderVertexArrayObjectBound<'a, 'b> {
pub fn update_array<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
attr: &'static str,
usage: u32,
array_data: B,
) -> &mut Self {
self.vao
.array_buffer
.get_mut(attr)
.unwrap_abort()
.update(usage, array_data);
self
}
pub fn update_element_array<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
usage: u32,
element_data: B,
) -> &mut Self {
if let Some(ref mut element_array_buffer) = self.vao.element_array_buffer {
element_array_buffer.update(usage, element_data);
}
self
}
pub fn update_instanced_array<B: BufferDataStorage<'a, f32>>(
&mut self,
attr: &'static str,
array_data: B,
) -> &mut Self {
self.vao
.array_buffer_instanced
.get_mut(attr)
.unwrap_abort()
.update(array_data);
self
}
pub fn unbind(&self) {
//self.vao.gl.bind_vertex_array(None);
}
}
impl<'a, 'b> Drop for ShaderVertexArrayObjectBound<'a, 'b> {
fn drop(&mut self) {
self.unbind();
}
}
use crate::webgl_ctx::WebGlRenderingCtx;
pub struct ShaderVertexArrayObjectBoundRef<'a, 'b> {
vao: &'a VertexArrayObject,
shader: &'b ShaderBound<'b>,
}
use crate::object::array_buffer::VertexBufferObject;
impl<'a, 'b> ShaderVertexArrayObjectBoundRef<'a, 'b> {
pub fn draw_arrays(&self, mode: u32, byte_offset: i32, size: i32) {
for (attr, buf) in self.vao.array_buffer.iter() {
buf.bind();
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
}
self.vao.gl.draw_arrays(mode, byte_offset, size);
}
pub fn draw_elements_with_i32(
&self,
mode: u32,
num_elements: Option<i32>,
type_: u32,
byte_offset: i32,
) {
for (attr, buf) in self.vao.array_buffer.iter() {
buf.bind();
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
}
let e = self.vao.element_array_buffer.as_ref().unwrap_abort();
e.bind();
let num_elements = num_elements.unwrap_or(self.vao.num_elements() as i32);
self.vao
.gl
.draw_elements_with_i32(mode, num_elements, type_, byte_offset);
}
pub fn draw_elements_instanced_with_i32(
&self,
mode: u32,
offset_element_idx: i32,
num_instances: i32,
) {
for (attr, buf) in self.vao.array_buffer.iter() {
buf.bind();
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
}
for (attr, inst_buf) in self.vao.array_buffer_instanced.iter() {
inst_buf.bind();
inst_buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
}
let e = self.vao.element_array_buffer.as_ref().unwrap_abort();
e.bind();
self.vao
.gl
.ext
.angles
.draw_elements_instanced_angle_with_i32(
mode,
self.vao.num_elements() as i32,
WebGlRenderingCtx::UNSIGNED_SHORT,
offset_element_idx,
num_instances,
);
}
pub fn unbind(&self) {
//self.vao.gl.bind_vertex_array(None);
}
}
impl<'a, 'b> Drop for ShaderVertexArrayObjectBoundRef<'a, 'b> {
fn drop(&mut self) {
self.unbind();
}
}
// Struct defined when only the Vertex Array Object is
// defined
pub struct VertexArrayObjectBound<'a> {
vao: &'a mut VertexArrayObject,
}
impl<'a> VertexArrayObjectBound<'a> {
/// Precondition: self must be bound
pub fn add_array_buffer<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
size: usize,
attr: &'static str,
usage: u32,
data: B,
) -> &mut Self {
let array_buffer =
ArrayBuffer::new(&self.vao.gl, self.vao.idx, 0, &[size], &[0], usage, data);
// Update the number of vertex attrib
self.vao.idx += 1;
self.vao.array_buffer.insert(attr, array_buffer);
self
}
/// Precondition: self must be bound
pub fn add_instanced_array_buffer<B: BufferDataStorage<'a, f32>>(
&mut self,
size: usize,
attr: &'static str,
usage: u32,
data: B,
) -> &mut Self {
let array_buffer = ArrayBufferInstanced::new(
&self.vao.gl,
self.vao.idx,
0,
&[size],
&[0],
usage,
data,
);
// Update the number of vertex attrib
self.vao.idx += 1;
self.vao.array_buffer_instanced.insert(attr, array_buffer);
self
}
/// Precondition: self must be bound
pub fn add_element_buffer<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
usage: u32,
data: B,
) -> &mut Self {
let element_buffer = ElementArrayBuffer::new(&self.vao.gl, usage, data);
self.vao.element_array_buffer = Some(element_buffer);
self
}
pub fn update_array<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
attr: &'static str,
usage: u32,
array_data: B,
) -> &mut Self {
self.vao
.array_buffer
.get_mut(attr)
.expect("cannot get attribute from the array buffer")
.update(usage, array_data);
self
}
pub fn update_element_array<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
usage: u32,
element_data: B,
) -> &mut Self {
if let Some(ref mut element_array_buffer) = self.vao.element_array_buffer {
element_array_buffer.update(usage, element_data);
}
self
}
pub fn update_instanced_array<B: BufferDataStorage<'a, f32>>(
&mut self,
attr: &'static str,
usage: u32,
array_data: B,
) -> &mut Self {
self.vao
.array_buffer_instanced
.get_mut(attr)
.expect("cannot get attribute from the array buffer")
.update(usage, array_data);
self
}
/*pub fn append_to_instanced_array<B: BufferDataStorage<'a, f32>>(
&mut self,
idx: usize,
buffer: B,
) -> &mut Self {
self.vao.array_buffer_instanced[idx].append(buffer);
self
}*/
pub fn unbind(&self) {
//self.vao.gl.bind_vertex_array(None);
}
}
impl<'a> Drop for VertexArrayObjectBound<'a> {
impl Drop for VertexArrayObjectBound<'_> {
fn drop(&mut self) {
self.unbind();
}

View File

@@ -112,7 +112,7 @@ impl Shader {
pub trait UniformType {
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self);
fn attach_uniform<'a>(name: &str, value: &Self, shader: &ShaderBound<'a>) {
fn attach_uniform(name: &str, value: &Self, shader: &ShaderBound<'_>) {
let location = shader.get_uniform_location(name);
Self::uniform(&shader.gl, location, value);
}
@@ -256,7 +256,7 @@ impl UniformType for ColorRGB {
gl.uniform3f(location, value.r, value.g, value.b);
}
}
impl<'a> UniformType for &'a ColorRGB {
impl UniformType for &ColorRGB {
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) {
gl.uniform3f(location, value.r, value.g, value.b);
}
@@ -268,7 +268,7 @@ impl UniformType for ColorRGBA {
gl.uniform4f(location, value.r, value.g, value.b, value.a);
}
}
impl<'a> UniformType for &'a ColorRGBA {
impl UniformType for &ColorRGBA {
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) {
gl.uniform4f(location, value.r, value.g, value.b, value.a);
}
@@ -328,7 +328,7 @@ impl SendUniformsWithParams<Colormaps> for HiPSColor {
) -> &'a ShaderBound<'a> {
let reversed = self.reversed as u8 as f32;
let cmap = cmaps.get(&self.cmap_name.as_ref());
let cmap = cmaps.get(self.cmap_name.as_ref());
shader
.attach_uniforms_with_params_from(cmap, cmaps)
.attach_uniform("H", &self.stretch)
@@ -407,7 +407,7 @@ impl<'a> ShaderBound<'a> {
}
}
impl<'a> Drop for ShaderBound<'a> {
impl Drop for ShaderBound<'_> {
fn drop(&mut self) {
self.unbind(&self.gl);
}

View File

@@ -57,7 +57,7 @@ impl Texture3D {
internal_format: F::INTERNAL_FORMAT,
format: F::FORMAT,
ty: F::TYPE,
channel_type: F::CHANNEL_TYPE
channel_type: F::CHANNEL_TYPE,
})));
Ok(Texture3D {
@@ -114,7 +114,7 @@ pub struct Texture3DBound<'a> {
tex: &'a Texture3D,
}
impl<'a> Texture3DBound<'a> {
impl Texture3DBound<'_> {
pub fn tex_sub_image_3d_with_html_image_element(
&self,
dx: i32,
@@ -196,6 +196,7 @@ impl<'a> Texture3DBound<'a> {
.expect("Sub texture 2d");
}
#[allow(clippy::too_many_arguments)]
pub fn tex_sub_image_3d_with_opt_array_buffer_view(
&self,
dx: i32,
@@ -227,6 +228,7 @@ impl<'a> Texture3DBound<'a> {
}
#[allow(dead_code)]
#[allow(clippy::too_many_arguments)]
pub fn tex_sub_image_3d_with_opt_u8_array(
&self,
idx: i32,

View File

@@ -3,6 +3,7 @@ use web_sys::HtmlCanvasElement;
use web_sys::WebGlTexture;
use crate::texture::pixel::Pixel;
use crate::texture::ChannelType;
use crate::texture::Texture2DMeta;
use crate::webgl_ctx::WebGlContext;
use crate::webgl_ctx::WebGlRenderingCtx;
@@ -11,7 +12,6 @@ use std::cell::RefCell;
use std::rc::Rc;
use wasm_bindgen::prelude::*;
use web_sys::HtmlImageElement;
use crate::texture::ChannelType;
pub struct Texture2DArray {
gl: WebGlContext,
@@ -56,7 +56,7 @@ impl Texture2DArray {
internal_format: F::INTERNAL_FORMAT,
format: F::FORMAT,
ty: F::TYPE,
channel_type: F::CHANNEL_TYPE
channel_type: F::CHANNEL_TYPE,
})));
Ok(Texture2DArray {
@@ -198,7 +198,7 @@ pub struct Texture2DArrayBound<'a> {
tex: &'a Texture2DArray,
}
impl<'a> Texture2DArrayBound<'a> {
impl Texture2DArrayBound<'_> {
pub fn tex_sub_image_3d_with_html_image_element(
&self,
dx: i32,

View File

@@ -74,12 +74,12 @@ impl Texture2D {
let height = image.height();
let metadata = Rc::new(RefCell::new(Texture2DMeta {
width: width,
height: height,
width,
height,
internal_format: F::INTERNAL_FORMAT,
format: F::FORMAT,
ty: F::TYPE,
channel_type: F::CHANNEL_TYPE
channel_type: F::CHANNEL_TYPE,
}));
#[cfg(feature = "webgl2")]
@@ -208,7 +208,7 @@ impl Texture2D {
internal_format: F::INTERNAL_FORMAT,
format: F::FORMAT,
ty: F::TYPE,
channel_type: F::CHANNEL_TYPE
channel_type: F::CHANNEL_TYPE,
})));
Ok(Texture2D {
@@ -249,7 +249,7 @@ impl Texture2D {
internal_format: F::INTERNAL_FORMAT,
format: F::FORMAT,
ty: F::TYPE,
channel_type: F::CHANNEL_TYPE
channel_type: F::CHANNEL_TYPE,
})));
Ok(Texture2D {
texture,
@@ -406,7 +406,7 @@ pub struct Texture2DBound<'a> {
texture_2d: &'a Texture2D,
}
impl<'a> Texture2DBound<'a> {
impl Texture2DBound<'_> {
pub fn tex_sub_image_2d_with_u32_and_u32_and_html_image_element(
&self,
dx: i32,
@@ -428,20 +428,6 @@ impl<'a> Texture2DBound<'a> {
image,
)
.expect("Sub texture 2d");
#[cfg(feature = "webgl1")]
self.texture_2d
.gl
.tex_sub_image_2d_with_u32_and_u32_and_image(
WebGlRenderingCtx::TEXTURE_2D,
0,
dx,
dy,
metadata.format,
metadata.ty,
image,
)
.expect("Sub texture 2d");
//self.texture_2d.gl.flush();
}
pub fn tex_sub_image_2d_with_u32_and_u32_and_html_canvas_element(
@@ -465,20 +451,6 @@ impl<'a> Texture2DBound<'a> {
canvas,
)
.expect("Sub texture 2d");
#[cfg(feature = "webgl1")]
self.texture_2d
.gl
.tex_sub_image_2d_with_u32_and_u32_and_canvas(
WebGlRenderingCtx::TEXTURE_2D,
0,
dx,
dy,
metadata.format,
metadata.ty,
canvas,
)
.expect("Sub texture 2d");
//self.texture_2d.gl.flush();
}
pub fn tex_sub_image_2d_with_u32_and_u32_and_image_bitmap(
@@ -595,6 +567,7 @@ pub trait Tex3D {
image: &web_sys::ImageBitmap,
);
#[allow(clippy::too_many_arguments)]
fn tex_sub_image_3d_with_opt_array_buffer_view(
&self,
dx: i32,
@@ -606,6 +579,7 @@ pub trait Tex3D {
view: Option<&js_sys::Object>,
);
#[allow(clippy::too_many_arguments)]
fn tex_sub_image_3d_with_opt_u8_array(
&self,
dx: i32,

View File

@@ -24,7 +24,7 @@ pub trait Pixel:
impl Pixel for [f32; 4] {
type Item = f32;
type Container = ArrayF32;
const BLACK: Self = [std::f32::NAN; 4];
const BLACK: Self = [f32::NAN; 4];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let pixels = js_sys::Float32Array::new_with_length(4);
@@ -56,7 +56,7 @@ impl Pixel for [f32; 4] {
impl Pixel for [f32; 3] {
type Item = f32;
type Container = ArrayF32;
const BLACK: Self = [std::f32::NAN; 3];
const BLACK: Self = [f32::NAN; 3];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let pixels = js_sys::Float32Array::new_with_length(3);
@@ -88,7 +88,7 @@ impl Pixel for [f32; 3] {
impl Pixel for [f32; 1] {
type Item = f32;
type Container = ArrayF32;
const BLACK: Self = [std::f32::NAN];
const BLACK: Self = [f32::NAN];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let p = js_sys::Uint8Array::new_with_length(4);
@@ -208,7 +208,7 @@ impl Pixel for [u8; 1] {
impl Pixel for [i16; 1] {
type Item = i16;
type Container = ArrayI16;
const BLACK: Self = [std::i16::MIN];
const BLACK: Self = [i16::MIN];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let pixels = js_sys::Int16Array::new_with_length(1);
@@ -229,7 +229,7 @@ impl Pixel for [i16; 1] {
impl Pixel for [i32; 1] {
type Item = i32;
type Container = ArrayI32;
const BLACK: Self = [std::i32::MIN];
const BLACK: Self = [i32::MIN];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let pixels = js_sys::Int32Array::new_with_length(1);

View File

@@ -12,9 +12,6 @@ pub type WebGlRenderingCtx = web_sys::WebGlRenderingContext;
#[derive(Clone)]
pub struct WebGlContext {
inner: Rc<WebGlRenderingCtx>,
#[cfg(feature = "webgl1")]
pub ext: WebGlExt,
}
#[derive(Clone)]
@@ -55,45 +52,12 @@ impl WebGlContext {
#[cfg(feature = "webgl2")]
{
if let Ok(r) =
get_extension::<web_sys::ExtColorBufferFloat>(&gl, "EXT_color_buffer_float")
{
let _ = r;
}
let ctx = WebGlContext { inner: gl };
Ok(ctx)
}
#[cfg(feature = "webgl1")]
{
let angles_ext =
get_extension::<web_sys::AngleInstancedArrays>(&gl, "ANGLE_instanced_arrays")?;
let _ = get_extension::<web_sys::OesTextureFloat>(&gl, "OES_texture_float")?;
let _ = get_extension::<web_sys::ExtSRgb>(&gl, "EXT_sRGB")?;
Ok(WebGlContext {
inner: gl,
ext: WebGlExt { angles: angles_ext },
})
}
}
}
fn get_extension<T>(context: &WebGlRenderingCtx, name: &str) -> Result<T, JsValue>
where
T: wasm_bindgen::JsCast,
{
// `unchecked_into` is used here because WebGL extensions aren't actually JS classes
// these objects are duck-type representations of the actual Rust classes
// https://github.com/rustwasm/wasm-bindgen/pull/1449
context
.get_extension(name)
.ok()
.and_then(|maybe_ext| maybe_ext.map(|ext| ext.unchecked_into::<T>()))
.ok_or_else(|| JsValue::from_str("Failed to load ext"))
}
use std::ops::Deref;
impl Deref for WebGlContext {
type Target = WebGlRenderingCtx;

View File

@@ -23,7 +23,7 @@ fn generate_shaders() -> std::result::Result<(), Box<dyn Error>> {
.unwrap()
//.with_extension("")
.to_string_lossy()
.to_owned()
.into_owned()
.replace("/", "_")
.replace("\\", "_");
//let out_name = format!("{}/{}", OUT_PATH, out_file_name);
@@ -49,7 +49,7 @@ fn read_shader<P: AsRef<std::path::Path>>(path: P) -> std::io::Result<String> {
let shader_src = std::io::BufReader::new(file)
.lines()
.flatten()
.map_while(Result::ok)
.map(|l| {
if l.starts_with("#include") {
let incl_file_names: Vec<_> = l.split_terminator(&[';', ' '][..]).collect();
@@ -76,7 +76,6 @@ pub fn write(path: PathBuf, entries: HashMap<String, String>) -> Result<(), Box<
let mut all_the_files = File::create(&path)?;
writeln!(&mut all_the_files, r#"use std::collections::HashMap;"#,)?;
writeln!(&mut all_the_files, r#""#,)?;
writeln!(&mut all_the_files, r#"#[allow(dead_code)]"#,)?;
writeln!(
&mut all_the_files,
@@ -87,7 +86,10 @@ pub fn write(path: PathBuf, entries: HashMap<String, String>) -> Result<(), Box<
for (name, content) in entries {
writeln!(
&mut all_the_files,
r##" out.insert(r"{name}", r#"{content}"#);"##,
r##" out.insert(
r"{name}",
r#"{content}"#,
);"##,
)?;
}

View File

@@ -4,7 +4,6 @@ use crate::tile_fetcher::HiPSLocalFiles;
use crate::math::angle::ToAngle;
use crate::renderable::hips::HiPS;
use crate::{
//async_task::{BuildCatalogIndex, ParseTableTask, TaskExecutor, TaskResult, TaskType},
camera::CameraViewPort,
downloader::Downloader,
healpix::coverage::HEALPixCoverage,
@@ -260,7 +259,7 @@ impl App {
self.tile_fetcher.clear();
// Loop over the hipss
for hips in self.layers.get_mut_hipses() {
if self.camera.get_texture_depth() == 0 {
if self.camera.get_tile_depth() == 0 {
let allsky_query = match hips {
HiPS::D2(h) => query::Allsky::new(h.get_config(), None),
HiPS::D3(h) => query::Allsky::new(h.get_config(), Some(h.get_slice() as u32)),
@@ -273,21 +272,21 @@ impl App {
let cfg = hips.get_config();
let min_tile_depth = cfg.delta_depth().max(cfg.get_min_depth_tile());
let min_tile_depth = cfg.get_min_depth_tile();
let mut ancestors = HashSet::new();
if let Some(tiles) = hips.look_for_new_tiles(&mut self.camera, &self.projection) {
if let Some(tiles) = hips.look_for_new_tiles(&self.camera) {
for tile_cell in tiles {
self.tile_fetcher.append(hips.get_tile_query(&tile_cell));
// check if we are starting aladin lite or not.
// If so we want to retrieve only the tiles in the view and access them
// directly i.e. without blending them with less precised tiles
if self.tile_fetcher.get_num_tile_fetched() > 0 {
if tile_cell.depth() >= min_tile_depth + 3 {
let ancestor_tile_cell = tile_cell.ancestor(3);
ancestors.insert(ancestor_tile_cell);
}
if self.tile_fetcher.get_num_tile_fetched() > 0
&& tile_cell.depth() >= min_tile_depth + 3
{
let ancestor_tile_cell = tile_cell.ancestor(3);
ancestors.insert(ancestor_tile_cell);
}
}
}
@@ -431,11 +430,9 @@ impl App {
);
// 3. project on screen
if let Some(p) = self.projection.model_to_clip_space(&xyz, &self.camera) {
Some([p.x, p.y])
} else {
None
}
self.projection
.model_to_clip_space(&xyz, &self.camera)
.map(|p| [p.x, p.y])
};
if let (Some(c1), Some(c2), Some(c3), Some(c4)) = (
@@ -517,7 +514,7 @@ impl App {
}*/
pub(crate) fn is_inerting(&self) -> bool {
return self.inertia.is_some();
self.inertia.is_some()
}
pub(crate) fn update(&mut self, dt: DeltaTime) -> Result<bool, JsValue> {
@@ -578,16 +575,14 @@ impl App {
}
// Tiles are fetched if:
let fetch_tiles = self.inertia.is_none() &&
// * the user is not zooming
!self.camera.has_zoomed() &&
// * no inertia action is in progress
(
let fetch_tiles =
// * the user is not panning the view
!self.dragging ||
// * or the user is but did not move for at least 100ms
(self.dragging && Time::now() - self.camera.get_time_of_last_move() >= DeltaTime(100.0))
);
(Time::now() - self.camera.get_time_of_last_move() >= DeltaTime(100.0) || !self.dragging) &&
// * no inertia action is in progress
self.inertia.is_none() &&
// * the user is not zooming
!self.camera.has_zoomed();
if fetch_tiles {
self.tile_fetcher.notify(self.downloader.clone(), None);
@@ -596,30 +591,23 @@ impl App {
let rscs_received = self.downloader.borrow_mut().get_received_resources();
let mut tile_copied = false;
//let mut tile_copied = false;
for rsc in rscs_received {
match rsc {
Resource::Tile(tile) => {
//if !_has_camera_zoomed {
if let Some(hips) = self.layers.get_mut_hips_from_cdid(&tile.get_hips_cdid()) {
if let Some(hips) = self.layers.get_mut_hips_from_cdid(tile.get_hips_cdid()) {
let cfg = hips.get_config_mut();
if cfg.get_format() == tile.format {
let delta_depth = cfg.delta_depth();
let fov_coverage = self.camera.get_cov(cfg.get_frame());
let included_or_near_coverage = tile
.cell()
.get_texture_cell(delta_depth)
.get_tile_cells(delta_depth)
.any(|neighbor_tile_cell| {
fov_coverage.intersects_cell(&neighbor_tile_cell)
});
let included_in_coverage = fov_coverage.intersects_cell(tile.cell());
//let is_tile_root = tile.cell().depth() == delta_depth;
//let _depth = tile.cell().depth();
// do not perform tex_sub costly GPU calls while the camera is zooming
if tile.cell().is_root() || included_or_near_coverage {
if tile.cell().is_root() || included_in_coverage {
//let is_missing = tile.missing();
/*self.tile_fetcher.notify_tile(
&tile,
@@ -640,82 +628,73 @@ impl App {
use fitsrs::fits::Fits;
use std::io::Cursor;
//if let Some(image) = image.as_ref() {
match &*tile.image.borrow() {
Some(ImageType::FitsImage {
raw_bytes: raw_bytes_buf,
}) => {
// check if the metadata has not been set
if !cfg.fits_metadata {
let num_bytes = raw_bytes_buf.length() as usize;
let mut raw_bytes = vec![0; num_bytes];
raw_bytes_buf.copy_to(&mut raw_bytes[..]);
if let Some(ImageType::FitsImage {
raw_bytes: raw_bytes_buf,
..
}) = &*tile.image.borrow()
{
// check if the metadata has not been set
if !cfg.fits_metadata {
let num_bytes = raw_bytes_buf.length() as usize;
let mut raw_bytes = vec![0; num_bytes];
raw_bytes_buf.copy_to(&mut raw_bytes[..]);
let mut bytes_reader =
Cursor::new(raw_bytes.as_slice());
let Fits { hdu } = Fits::from_reader(&mut bytes_reader)
.map_err(|_| {
JsValue::from_str("Parsing fits error")
})?;
let mut bytes_reader = Cursor::new(raw_bytes.as_slice());
let Fits { hdu } = Fits::from_reader(&mut bytes_reader)
.map_err(|_| JsValue::from_str("Parsing fits error"))?;
let header = hdu.get_header();
let bscale =
if let Some(fitsrs::card::Value::Float(bscale)) =
header.get(b"BSCALE ")
{
*bscale as f32
} else {
1.0
};
let bzero =
if let Some(fitsrs::card::Value::Float(bzero)) =
header.get(b"BZERO ")
{
*bzero as f32
} else {
0.0
};
let blank =
if let Some(fitsrs::card::Value::Float(blank)) =
header.get(b"BLANK ")
{
*blank as f32
} else {
std::f32::NAN
};
let header = hdu.get_header();
let bscale =
if let Some(fitsrs::card::Value::Float(bscale)) =
header.get(b"BSCALE ")
{
*bscale as f32
} else {
1.0
};
let bzero = if let Some(fitsrs::card::Value::Float(bzero)) =
header.get(b"BZERO ")
{
*bzero as f32
} else {
0.0
};
let blank = if let Some(fitsrs::card::Value::Float(blank)) =
header.get(b"BLANK ")
{
*blank as f32
} else {
f32::NAN
};
cfg.set_fits_metadata(bscale, bzero, blank);
}
cfg.set_fits_metadata(bscale, bzero, blank);
}
_ => (),
};
//}
let image = tile.image.clone();
match &*image.borrow() {
Some(img) => {
if tile_copied {
self.downloader
.borrow_mut()
.delay(Resource::Tile(tile));
continue;
}
if let Some(img) = &*image.borrow() {
/*if tile_copied {
self.downloader
.borrow_mut()
.delay(Resource::Tile(tile));
continue;
}*/
self.request_redraw = true;
tile_copied = true;
match hips {
HiPS::D2(hips) => {
hips.add_tile(&tile.cell, img, tile.time_req)?
}
HiPS::D3(hips) => hips.add_tile(
&tile.cell,
img,
tile.time_req,
tile.channel.unwrap() as u16,
)?,
self.request_redraw = true;
//tile_copied = true;
match hips {
HiPS::D2(hips) => {
hips.add_tile(&tile.cell, img, tile.time_req)?
}
self.time_start_blending = Time::now();
HiPS::D3(hips) => hips.add_tile(
&tile.cell,
img,
tile.time_req,
tile.channel.unwrap() as u16,
)?,
}
_ => (),
self.time_start_blending = Time::now();
};
}
}
@@ -729,12 +708,9 @@ impl App {
if is_missing {
// The allsky image is missing so we donwload all the tiles contained into
// the 0's cell
let cfg = hips.get_config();
for texture_cell in crate::healpix::cell::ALLSKY_HPX_CELLS_D0 {
for cell in texture_cell.get_tile_cells(cfg.delta_depth()) {
let query = hips.get_tile_query(&cell);
self.tile_fetcher.append_base_tile(query);
}
for base_hpx_cell in crate::healpix::cell::ALLSKY_HPX_CELLS_D0 {
let query = hips.get_tile_query(base_hpx_cell);
self.tile_fetcher.append_base_tile(query);
}
} else {
// tell the hips to not download tiles which order is <= 3 because the allsky
@@ -800,7 +776,14 @@ impl App {
}
}
pub(crate) fn read_line_of_pixels(&self, x1: f64, y1: f64, x2: f64, y2: f64, layer: &str) -> Result<Vec<JsValue>, JsValue> {
pub(crate) fn read_line_of_pixels(
&self,
x1: f64,
y1: f64,
x2: f64,
y2: f64,
layer: &str,
) -> Result<Vec<JsValue>, JsValue> {
let pixels = crate::math::utils::bresenham(x1, y1, x2, y2)
.map(|(x, y)| self.read_pixel(x, y, layer))
.collect::<Result<Vec<_>, _>>()?;
@@ -922,7 +905,6 @@ impl App {
WebGl2RenderingContext::ONE,
);
grid.draw(camera, projection, shaders)?;
// Ok(())
// },
// None,
@@ -948,7 +930,7 @@ impl App {
}
pub(crate) fn rename_layer(&mut self, layer: &str, new_layer: &str) -> Result<(), JsValue> {
self.layers.rename_layer(&layer, &new_layer)
self.layers.rename_layer(layer, new_layer)
}
pub(crate) fn swap_layers(
@@ -1028,9 +1010,7 @@ impl App {
.map_ok(|js_value| {
js_value.dyn_into::<Uint8Array>().unwrap_throw().to_vec()
})
.map_err(|_js_error| {
std::io::Error::new(std::io::ErrorKind::Other, "failed to read")
})
.map_err(|_js_error| std::io::Error::other("failed to read"))
.into_async_read(),
),
};
@@ -1121,9 +1101,7 @@ impl App {
.map_ok(|js_value| {
js_value.dyn_into::<Uint8Array>().unwrap_throw().to_vec()
})
.map_err(|_js_error| {
std::io::Error::new(std::io::ErrorKind::Other, "failed to read")
})
.map_err(|_js_error| std::io::Error::other("failed to read"))
.into_async_read(),
),
};
@@ -1154,8 +1132,7 @@ impl App {
images.push(image);
}
Err(error) => {
al_core::log::console_warn(&
format!("The extension {hdu_ext_idx} has not been parsed, reason:")
al_core::log::console_warn(format!("The extension {hdu_ext_idx} has not been parsed, reason:")
);
al_core::log::console_warn(error);
@@ -1163,8 +1140,7 @@ impl App {
}
}
_ => {
al_core::log::console_warn(&
format!("The extension {hdu_ext_idx} is a BinTable/AsciiTable and is thus discarded")
al_core::log::console_warn(format!("The extension {hdu_ext_idx} is a BinTable/AsciiTable and is thus discarded")
);
}
}
@@ -1189,8 +1165,7 @@ impl App {
images.push(image);
}
Err(error) => {
al_core::log::console_warn(&
format!("The extension {hdu_ext_idx} has not been parsed, reason:")
al_core::log::console_warn(format!("The extension {hdu_ext_idx} has not been parsed, reason:")
);
al_core::log::console_warn(error);
@@ -1198,8 +1173,7 @@ impl App {
}
}
_ => {
al_core::log::console_warn(&
format!("The extension {hdu_ext_idx} is a BinTable/AsciiTable and is thus discarded")
al_core::log::console_warn(format!("The extension {hdu_ext_idx} is a BinTable/AsciiTable and is thus discarded")
);
}
}
@@ -1258,7 +1232,7 @@ impl App {
pub(crate) fn set_hips_slice_number(&mut self, layer: &str, slice: u32) -> Result<(), JsValue> {
let hips = self
.layers
.get_mut_hips_from_layer(&layer)
.get_mut_hips_from_layer(layer)
.ok_or_else(|| JsValue::from_str("Layer not found"))?;
self.request_for_new_tiles = true;
@@ -1329,7 +1303,8 @@ impl App {
}
pub(crate) fn set_longitude_reversed(&mut self, longitude_reversed: bool) {
self.camera.set_longitude_reversed(longitude_reversed, &self.projection);
self.camera
.set_longitude_reversed(longitude_reversed, &self.projection);
}
pub(crate) fn add_catalog(&mut self, _name: String, table: JsValue, _colormap: String) {
@@ -1530,7 +1505,6 @@ impl App {
return;
}
let now = Time::now();
let dragging_duration = (now - self.time_start_dragging).as_secs();
let dragging_vel = self.dist_dragging / dragging_duration;
@@ -1551,7 +1525,7 @@ impl App {
let axis = self.prev_cam_position.cross(*center).normalize();
//let delta_time = ((now - time_of_last_move).0 as f64).max(1.0);
let delta_angle = math::vector::angle3(&self.prev_cam_position, &center).to_radians();
let delta_angle = math::vector::angle3(&self.prev_cam_position, center).to_radians();
let ampl = delta_angle * (dragging_vel as f64) * 5e-3;
//let ampl = (dragging_vel * 0.01) as f64;
@@ -1604,7 +1578,7 @@ impl App {
let prev_pos = w1;
let cur_pos = w2;
if prev_pos != cur_pos {
let prev_cam_position = self.camera.get_center().clone();
let prev_cam_position = *self.camera.get_center();
if self.north_up {
let lonlat1 = prev_pos.lonlat();
@@ -1612,15 +1586,24 @@ impl App {
let dlon = lonlat2.lon() - lonlat1.lon();
let dlat = lonlat2.lat() - lonlat1.lat();
self.camera.apply_lonlat_rotation(dlon, dlat, &self.projection);
self.camera
.apply_lonlat_rotation(dlon, dlat, &self.projection);
// Detect if a pole has been crossed
let north_pole = Vector3::new(0.0, 1.0, 0.0);
let south_pole = Vector3::new(0.0, -1.0, 0.0);
let cross_north_pole = crate::math::lonlat::is_in(&prev_cam_position, &self.camera.get_center(), &north_pole);
let cross_south_pole = crate::math::lonlat::is_in(&prev_cam_position, &self.camera.get_center(), &south_pole);
let cross_north_pole = crate::math::lonlat::is_in(
&prev_cam_position,
self.camera.get_center(),
&north_pole,
);
let cross_south_pole = crate::math::lonlat::is_in(
&prev_cam_position,
self.camera.get_center(),
&south_pole,
);
let cross_pole = cross_north_pole | cross_south_pole;
@@ -1632,7 +1615,7 @@ impl App {
};
let fov = self.camera.get_aperture();
let pole = if center.y >= 0.0 {
north_pole
} else {
@@ -1645,10 +1628,12 @@ impl App {
// too near to the pole
let axis = center.cross(pole).normalize();
use crate::math::rotation::Rotation;
let new_center = Rotation::from_axis_angle(&axis, (-5e-3 * fov).to_angle()).rotate(&pole);
let new_center = Rotation::from_axis_angle(&axis, (-5e-3 * fov).to_angle())
.rotate(&pole);
self.camera.set_center_xyz(&new_center, &self.projection);
self.camera.set_position_angle(0.0.to_angle(), &self.projection);
self.camera
.set_position_angle(0.0.to_angle(), &self.projection);
}
} else {
/* 1. Rotate by computing the angle between the last and current position */
@@ -1656,7 +1641,8 @@ impl App {
let d = math::vector::angle3(&prev_pos, &cur_pos);
let axis = prev_pos.cross(cur_pos).normalize();
self.camera.apply_axis_rotation(&(-axis), d, &self.projection);
self.camera
.apply_axis_rotation(&(-axis), d, &self.projection);
}
self.prev_cam_position = prev_cam_position;
@@ -1681,7 +1667,7 @@ impl App {
}
pub(crate) fn get_norder(&self) -> i32 {
self.camera.get_texture_depth() as i32
self.camera.get_tile_depth() as i32
}
pub(crate) fn get_zoom_factor(&self) -> f64 {

View File

@@ -79,16 +79,14 @@ impl FieldOfView {
) -> Self {
let mut x_ndc = linspace(-1., 1., NUM_VERTICES_WIDTH + 2);
x_ndc.extend(iter::repeat(1.0).take(NUM_VERTICES_HEIGHT));
x_ndc.extend(iter::repeat_n(1.0, NUM_VERTICES_HEIGHT));
x_ndc.extend(linspace(1., -1., NUM_VERTICES_WIDTH + 2));
x_ndc.extend(iter::repeat(-1.0).take(NUM_VERTICES_HEIGHT));
x_ndc.extend(iter::repeat_n(-1.0, NUM_VERTICES_HEIGHT));
let mut y_ndc = iter::repeat(-1.0)
.take(NUM_VERTICES_WIDTH + 1)
.collect::<Vec<_>>();
let mut y_ndc = iter::repeat_n(-1.0, NUM_VERTICES_WIDTH + 1).collect::<Vec<_>>();
y_ndc.extend(linspace(-1., 1., NUM_VERTICES_HEIGHT + 2));
y_ndc.extend(iter::repeat(1.0).take(NUM_VERTICES_WIDTH));
y_ndc.extend(iter::repeat_n(1.0, NUM_VERTICES_WIDTH));
y_ndc.extend(linspace(1., -1., NUM_VERTICES_HEIGHT + 2));
y_ndc.pop();

View File

@@ -50,9 +50,8 @@ pub fn build_fov_coverage(
let inside_vertex = crate::coosys::apply_coo_system(camera_frame, frame, camera_center);
// Prefer to query from_polygon with depth >= 2
let moc = HEALPixCoverage::from_3d_coos(depth, vertices_iter, &inside_vertex);
moc
HEALPixCoverage::from_3d_coos(depth, vertices_iter, &inside_vertex)
}
} else {
let center_xyz = crate::coosys::apply_coo_system(camera_frame, frame, camera_center);

View File

@@ -8,18 +8,14 @@ pub enum UserAction {
use web_sys::WebGl2RenderingContext;
// Longitude reversed identity matrix
const ID_R: &Matrix3<f64> = &Matrix3::new(
-1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
);
const ID_R: &Matrix3<f64> = &Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
use cgmath::{Vector3, InnerSpace};
use super::{fov::FieldOfView, view_hpx_cells::ViewHpxCells};
use crate::healpix::cell::HEALPixCell;
use crate::healpix::coverage::HEALPixCoverage;
use crate::math::angle::ToAngle;
use crate::math::{projection::coo_space::XYZModel, projection::domain::sdf::ProjDef};
use cgmath::{InnerSpace, Vector3};
use cgmath::{Matrix3, Vector2};
const APERTURE_LOWER_LIMIT_RAD: f64 = (1.0_f64 / 36000.0).to_radians();
@@ -285,8 +281,8 @@ impl CameraViewPort {
}
pub fn set_screen_size(&mut self, width: f32, height: f32, projection: &ProjectionType) {
self.width = (width as f32) * self.dpi;
self.height = (height as f32) * self.dpi;
self.width = width * self.dpi;
self.height = height * self.dpi;
self.aspect = width / height;
// Compute the new clip zoom factor
@@ -378,10 +374,10 @@ impl CameraViewPort {
}
}
let can_unzoom_more = match proj {
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_) => false,
_ => true,
};
let can_unzoom_more = !matches!(
proj,
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_)
);
if !can_unzoom_more && self.zoom_factor >= 1.0 {
return true;
@@ -414,10 +410,10 @@ impl CameraViewPort {
self.last_user_action
};
let can_unzoom_more = match proj {
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_) => false,
_ => true,
};
let can_unzoom_more = !matches!(
proj,
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_)
);
let aperture_start: f64 = proj.aperture_start().to_radians();
@@ -491,10 +487,10 @@ impl CameraViewPort {
self.last_user_action
};
let can_unzoom_more = match proj {
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_) => false,
_ => true,
};
let can_unzoom_more = !matches!(
proj,
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_)
);
// Set the zoom factor
self.zoom_factor = zoom_factor;
@@ -598,7 +594,7 @@ impl CameraViewPort {
let w_screen_px = self.width as f64;
let smallest_cell_size_px = self.dpi as f64;
let mut depth_pixel = 29 as usize;
let mut depth_pixel = 29_usize;
let hpx_cell_size_rad = (smallest_cell_size_px / w_screen_px) * self.get_aperture();
@@ -607,7 +603,7 @@ impl CameraViewPort {
break;
}
depth_pixel = depth_pixel - 1;
depth_pixel -= 1;
}
depth_pixel += 1;
const DEPTH_OFFSET_TEXTURE: usize = 9;
@@ -618,7 +614,7 @@ impl CameraViewPort {
};
}
pub fn get_texture_depth(&self) -> u8 {
pub fn get_tile_depth(&self) -> u8 {
self.texture_depth
}
@@ -639,10 +635,13 @@ impl CameraViewPort {
&mut self,
dlon: Angle<f64>,
dlat: Angle<f64>,
proj: &ProjectionType
proj: &ProjectionType,
) {
let center = self.get_center();
let rot = Rotation::from_axis_angle(&Vector3::new(center.z, 0.0, -center.x).normalize(), dlat) * Rotation::from_axis_angle(&Vector3::unit_y(), -dlon) * Rotation::from_sky_position(&center);
let rot =
Rotation::from_axis_angle(&Vector3::new(center.z, 0.0, -center.x).normalize(), dlat)
* Rotation::from_axis_angle(&Vector3::unit_y(), -dlon)
* Rotation::from_sky_position(center);
self.set_rotation(&rot, proj);
}

View File

@@ -1,5 +1,5 @@
use cgmath::Vector3;
use al_api::coo_system::CooSystem;
use cgmath::Vector3;
/// This is conversion method returning a transformation
/// matrix when the system requested by the user is not

View File

@@ -18,6 +18,12 @@ use crate::fifo_cache::Cache;
use query::Query;
use request::{RequestType, Resource};
impl Default for Downloader {
fn default() -> Self {
Self::new()
}
}
impl Downloader {
pub fn new() -> Downloader {
let requests = Vec::with_capacity(32);

View File

@@ -19,6 +19,7 @@ pub struct Tile {
pub hips_cdid: CreatorDid,
// The total url of the query
pub url: Url,
pub size: u32, // size of the tile requested
pub credentials: RequestCredentials,
pub mode: RequestMode,
pub id: QueryId,
@@ -64,6 +65,7 @@ impl Tile {
ext
);
let size = cfg.get_tile_size();
Tile {
hips_cdid: hips_cdid.to_string(),
url,
@@ -73,6 +75,7 @@ impl Tile {
mode,
id,
channel,
size: size as u32,
}
}
}
@@ -90,7 +93,7 @@ impl Query for Tile {
pub struct Allsky {
pub format: ImageFormatType,
pub tile_size: i32,
pub texture_size: i32,
pub allsky_tile_size: i32,
pub channel: Option<u32>,
// The root url of the HiPS
pub hips_cdid: CreatorDid,
@@ -104,8 +107,10 @@ pub struct Allsky {
impl Allsky {
pub fn new(cfg: &HiPSConfig, channel: Option<u32>) -> Self {
let hips_cdid = cfg.get_creator_did().to_string();
let allsky_tile_size = cfg.allsky_tile_size();
let tile_size = cfg.get_tile_size();
let texture_size = cfg.get_texture_size();
let format = cfg.get_format();
let ext = format.get_ext_file();
let credentials = cfg.get_request_credentials();
@@ -132,14 +137,14 @@ impl Allsky {
Allsky {
tile_size,
texture_size,
allsky_tile_size,
hips_cdid,
url,
format,
id,
credentials,
mode,
channel
channel,
}
}
}

View File

@@ -12,7 +12,7 @@ use crate::downloader::QueryId;
pub struct AllskyRequest {
pub hips_cdid: CreatorDid,
pub url: Url,
pub depth_tile: u8,
//pub depth_tile: u8,
pub id: QueryId,
pub channel: Option<u32>,
@@ -28,32 +28,17 @@ impl From<AllskyRequest> for RequestType {
use super::Url;
use wasm_bindgen_futures::JsFuture;
use web_sys::{RequestInit, Response, RequestCredentials};
use web_sys::{RequestCredentials, RequestInit, Response};
use al_core::{image::raw::ImageBuffer, texture::pixel::Pixel};
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
async fn query_image(url: &str, credentials: RequestCredentials) -> Result<ImageBuffer<RGBA8U>, JsValue> {
let image = web_sys::HtmlImageElement::new().unwrap_abort();
let image_cloned = image.clone();
let cors_value = match credentials {
RequestCredentials::Include => Some("use-credentials"),
RequestCredentials::SameOrigin => Some("anonymous"),
_ => Some("")
};
let html_img_elt_promise = js_sys::Promise::new(
&mut (Box::new(move |resolve, reject| {
image_cloned.set_cross_origin(cors_value);
image_cloned.set_onload(Some(&resolve));
image_cloned.set_onerror(Some(&reject));
image_cloned.set_src(&url);
}) as Box<dyn FnMut(js_sys::Function, js_sys::Function)>),
);
let _ = JsFuture::from(html_img_elt_promise).await?;
async fn query_allsky(
url: &str,
credentials: RequestCredentials,
) -> Result<ImageBuffer<RGBA8U>, JsValue> {
let image = super::query_html_image(url, credentials).await?;
// The image has been received here
let document = web_sys::window().unwrap_abort().document().unwrap_abort();
@@ -85,55 +70,46 @@ impl From<query::Allsky> for AllskyRequest {
tile_size,
url,
hips_cdid,
texture_size,
allsky_tile_size,
id,
credentials,
mode,
channel: slice,
} = query;
let depth_tile = crate::math::utils::log_2_unchecked(texture_size / tile_size) as u8;
//let depth_tile = crate::math::utils::log_2_unchecked(texture_size / tile_size) as u8;
let channel = format.get_channel();
let url_clone = url.clone();
let request = Request::new(async move {
match channel {
ChannelType::RGB8U => {
let allsky_tile_size = std::cmp::min(tile_size, 64);
let allsky = query_image(&url_clone, credentials).await?;
let allsky = query_allsky(&url_clone, credentials).await?;
let allsky_tiles = handle_allsky_file::<RGBA8U>(
allsky,
allsky_tile_size,
texture_size,
tile_size,
)?
.into_iter()
.map(|image| {
let ImageBuffer { data, size } = image;
let data = data
.into_iter()
.enumerate()
.filter(|&(i, _)| i % 4 != 3)
.map(|(_, v)| v)
let allsky_tiles =
handle_allsky_file::<RGBA8U>(allsky, allsky_tile_size, tile_size)?
.map(|image| {
let ImageBuffer { data, size } = image;
let data = data
.into_iter()
.enumerate()
.filter(|&(i, _)| i % 4 != 3)
.map(|(_, v)| v)
.collect();
let image = ImageBuffer::new(data, size.x, size.y);
ImageType::RawRgb8u { image }
})
.collect();
let image = ImageBuffer::new(data, size.x, size.y);
ImageType::RawRgb8u { image }
})
.collect();
Ok(allsky_tiles)
}
ChannelType::RGBA8U => {
let allsky_tile_size = std::cmp::min(tile_size, 64);
let allsky = query_image(&url_clone, credentials).await?;
let allsky = query_allsky(&url_clone, credentials).await?;
let allsky_tiles =
handle_allsky_file(allsky, allsky_tile_size, texture_size, tile_size)?
.into_iter()
.map(|image| ImageType::RawRgba8u { image })
.collect();
let allsky_tiles = handle_allsky_file(allsky, allsky_tile_size, tile_size)?
.map(|image| ImageType::RawRgba8u { image })
.collect();
Ok(allsky_tiles)
}
@@ -160,10 +136,7 @@ impl From<query::Allsky> for AllskyRequest {
let bytes_buffer = js_sys::Uint8Array::new(&array_buffer);
let num_bytes = bytes_buffer.length() as usize;
let mut raw_bytes = Vec::with_capacity(num_bytes);
unsafe {
raw_bytes.set_len(num_bytes);
}
let mut raw_bytes = vec![0; num_bytes];
bytes_buffer.copy_to(&mut raw_bytes[..]);
let mut reader = Cursor::new(&raw_bytes[..]);
let Fits { hdu } = Fits::from_reader(&mut reader)
@@ -173,23 +146,23 @@ impl From<query::Allsky> for AllskyRequest {
match data {
InMemData::U8(data) => {
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
Ok(handle_allsky_fits(data, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawR8ui { image })
.collect())
}
InMemData::I16(data) => {
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
Ok(handle_allsky_fits(data, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawR16i { image })
.collect())
}
InMemData::I32(data) => {
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
Ok(handle_allsky_fits(data, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawR32i { image })
.collect())
}
InMemData::I64(data) => {
let data = data.iter().map(|v| *v as i32).collect::<Vec<_>>();
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
Ok(handle_allsky_fits(&data, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawR32i { image })
.collect())
}
@@ -200,7 +173,7 @@ impl From<query::Allsky> for AllskyRequest {
data.len() * 4,
)
};
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
Ok(handle_allsky_fits(data, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawRgba8u { image })
.collect())
}
@@ -213,7 +186,7 @@ impl From<query::Allsky> for AllskyRequest {
)
};
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
Ok(handle_allsky_fits(data, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawRgba8u { image })
.collect())
}
@@ -225,7 +198,7 @@ impl From<query::Allsky> for AllskyRequest {
Self {
id,
hips_cdid,
depth_tile,
//depth_tile,
url,
request,
channel: slice,
@@ -236,41 +209,40 @@ impl From<query::Allsky> for AllskyRequest {
use al_core::image::format::ImageFormat;
use al_core::image::raw::ImageBufferView;
fn handle_allsky_file<F: ImageFormat>(
allsky: ImageBuffer<F>,
image: ImageBuffer<F>,
allsky_tile_size: i32,
texture_size: i32,
tile_size: i32,
) -> Result<impl Iterator<Item = ImageBuffer<F>>, JsValue> {
let num_tiles_per_texture = (texture_size / tile_size) * (texture_size / tile_size);
let num_tiles = num_tiles_per_texture * 12;
let num_allsky_tiles_per_tile = (tile_size / allsky_tile_size) * (tile_size / allsky_tile_size);
let d3_tile_allsky_size = std::cmp::min(tile_size, 64);
let mut src_idx = 0;
let tiles = (0..num_tiles).map(move |_| {
let mut base_tile =
ImageBuffer::<F>::allocate(&<F as ImageFormat>::P::BLACK, tile_size, tile_size);
for idx_tile in 0..num_allsky_tiles_per_tile {
let tiles = (0..12).map(move |_| {
let mut base_tile = ImageBuffer::<F>::allocate(
&<F as ImageFormat>::P::BLACK,
allsky_tile_size,
allsky_tile_size,
);
for idx_tile in 0..64 {
let (x, y) = crate::utils::unmortonize(idx_tile as u64);
let dx = x * (allsky_tile_size as u32);
let dy = y * (allsky_tile_size as u32);
let dx = x * (d3_tile_allsky_size as u32);
let dy = y * (d3_tile_allsky_size as u32);
let sx = (src_idx % 27) * allsky_tile_size;
let sy = (src_idx / 27) * allsky_tile_size;
let sx = (src_idx % 27) * d3_tile_allsky_size;
let sy = (src_idx / 27) * d3_tile_allsky_size;
let s = ImageBufferView {
x: sx as i32,
y: sy as i32,
w: allsky_tile_size as i32,
h: allsky_tile_size as i32,
x: sx,
y: sy,
w: d3_tile_allsky_size,
h: d3_tile_allsky_size,
};
let d = ImageBufferView {
x: dx as i32,
y: dy as i32,
w: allsky_tile_size as i32,
h: allsky_tile_size as i32,
w: d3_tile_allsky_size,
h: d3_tile_allsky_size,
};
base_tile.tex_sub(&allsky, &s, &d);
base_tile.tex_sub(&image, &s, &d);
src_idx += 1;
}
@@ -282,42 +254,40 @@ fn handle_allsky_file<F: ImageFormat>(
}
fn handle_allsky_fits<F: ImageFormat>(
allsky_data: &[<<F as ImageFormat>::P as Pixel>::Item],
image: &[<<F as ImageFormat>::P as Pixel>::Item],
tile_size: i32,
texture_size: i32,
allsky_tile_size: i32,
) -> Result<impl Iterator<Item = ImageBuffer<F>>, JsValue> {
let allsky_tile_size = std::cmp::min(tile_size, 64);
let width_allsky_px = 27 * allsky_tile_size;
let height_allsky_px = 29 * allsky_tile_size;
let d3_tile_allsky_size = std::cmp::min(tile_size, 64);
let width_allsky_px = 27 * d3_tile_allsky_size;
let height_allsky_px = 29 * d3_tile_allsky_size;
// The fits image layout stores rows in reverse
let reversed_rows_data = allsky_data
let reversed_rows_data = image
.chunks(width_allsky_px as usize * F::NUM_CHANNELS)
.rev()
.flatten()
.copied()
.collect::<Vec<_>>();
let allsky = ImageBuffer::<F>::new(reversed_rows_data, width_allsky_px, height_allsky_px);
let image = ImageBuffer::<F>::new(reversed_rows_data, width_allsky_px, height_allsky_px);
let allsky_tiles_iter =
handle_allsky_file::<F>(allsky, allsky_tile_size, texture_size, tile_size)?.map(
move |image| {
// The GPU does a specific transformation on the UV for FITS tiles
// We must revert this to be compatible with this GPU transformation
let new_image_data = image
.get_data()
.chunks((tile_size * tile_size) as usize * F::NUM_CHANNELS)
.flat_map(|c| {
c.chunks(tile_size as usize * F::NUM_CHANNELS)
.rev()
.flatten()
})
.cloned()
.collect();
handle_allsky_file::<F>(image, allsky_tile_size, tile_size)?.map(move |image| {
// The GPU does a specific transformation on the UV for FITS tiles
// We must revert this to be compatible with this GPU transformation
let new_image_data = image
.get_data()
.chunks((allsky_tile_size * allsky_tile_size) as usize * F::NUM_CHANNELS)
.flat_map(|c| {
c.chunks(allsky_tile_size as usize * F::NUM_CHANNELS)
.rev()
.flatten()
})
.cloned()
.collect();
ImageBuffer::<F>::new(new_image_data, tile_size, tile_size)
},
);
ImageBuffer::<F>::new(new_image_data, allsky_tile_size, allsky_tile_size)
});
Ok(allsky_tiles_iter)
}
@@ -330,8 +300,7 @@ use std::rc::Rc;
pub struct Allsky {
pub image: Rc<RefCell<Option<Vec<ImageType>>>>,
pub time_req: Time,
pub depth_tile: u8,
//pub depth_tile: u8,
pub hips_cdid: CreatorDid,
url: Url,
pub channel: Option<u32>,
@@ -358,7 +327,7 @@ impl<'a> From<&'a AllskyRequest> for Option<Allsky> {
let AllskyRequest {
request,
hips_cdid,
depth_tile,
//depth_tile,
url,
channel,
..
@@ -373,7 +342,7 @@ impl<'a> From<&'a AllskyRequest> for Option<Allsky> {
image: data.clone(),
hips_cdid: hips_cdid.clone(),
url: url.clone(),
depth_tile: *depth_tile,
//depth_tile: *depth_tile,
channel: *channel,
})
} else {

View File

@@ -83,10 +83,7 @@ impl From<query::PixelMetadata> for PixelMetadataRequest {
let bytes_buffer = js_sys::Uint8Array::new(&array_buffer);
let num_bytes = bytes_buffer.length() as usize;
let mut raw_bytes = Vec::with_capacity(num_bytes);
unsafe {
raw_bytes.set_len(num_bytes);
}
let mut raw_bytes = vec![0; num_bytes];
bytes_buffer.copy_to(&mut raw_bytes[..]);
let mut reader = Cursor::new(&raw_bytes[..]);
@@ -110,7 +107,7 @@ impl From<query::PixelMetadata> for PixelMetadataRequest {
if let Some(fitsrs::card::Value::Float(blank)) = header.get(b"BLANK ") {
*blank as f32
} else {
std::f32::NAN
f32::NAN
};
Ok(Metadata {

View File

@@ -56,7 +56,7 @@ impl From<query::Moc> for MOCRequest {
params,
hips_cdid,
credentials,
mode
mode,
} = query;
let url_clone = url.clone();
@@ -77,10 +77,7 @@ impl From<query::Moc> for MOCRequest {
let bytes_buf = js_sys::Uint8Array::new(&array_buffer);
let num_bytes = bytes_buf.length() as usize;
let mut bytes = Vec::with_capacity(num_bytes);
unsafe {
bytes.set_len(num_bytes);
}
let mut bytes = vec![0; num_bytes];
bytes_buf.copy_to(&mut bytes[..]);
// Coosys is permissive because we load a moc

View File

@@ -5,7 +5,7 @@ pub mod blank;
pub mod moc;
pub mod tile;
/* ------------------------------------- */
use wasm_bindgen_futures::JsFuture;
use crate::time::Time;
use std::cell::{Cell, RefCell};
@@ -114,6 +114,7 @@ impl<'a> From<&'a RequestType> for Option<Resource> {
}
}
use crate::Abort;
use allsky::Allsky;
use blank::PixelMetadata;
use moc::Moc;
@@ -125,14 +126,32 @@ pub enum Resource {
Moc(Moc),
}
/*
impl Resource {
pub fn id(&self) -> &String {
match self {
Resource::Tile(tile) => &format!("{:?}:{:?}", tile.cell.depth(), tile.cell.idx()),
Resource::Allsky(allsky) => allsky.get_hips_cdid(),
Resource::PixelMetadata(PixelMetadata { hips_cdid, .. }) => hips_cdid,
Resource::Moc(moc) => moc.get_hips_cdid(),
}
}
}*/
use web_sys::RequestCredentials;
async fn query_html_image(
url: &str,
credentials: RequestCredentials,
) -> Result<web_sys::HtmlImageElement, JsValue> {
let image = web_sys::HtmlImageElement::new().unwrap_abort();
let image_cloned = image.clone();
// Set the CORS and credentials options for the image
let cors_value = match credentials {
RequestCredentials::Include => Some("use-credentials"),
RequestCredentials::SameOrigin => Some("anonymous"),
_ => Some(""),
};
let promise = js_sys::Promise::new(
&mut (Box::new(move |resolve, reject| {
// Ask for CORS permissions
image_cloned.set_cross_origin(cors_value);
image_cloned.set_onload(Some(&resolve));
image_cloned.set_onerror(Some(&reject));
image_cloned.set_src(url);
}) as Box<dyn FnMut(js_sys::Function, js_sys::Function)>),
);
let _ = JsFuture::from(promise).await?;
Ok(image)
}

View File

@@ -7,8 +7,8 @@ use al_core::image::ImageType;
use super::Url;
use super::{Request, RequestType};
use crate::downloader::request::query_html_image;
use crate::downloader::QueryId;
pub struct TileRequest {
request: Request<ImageType>,
pub id: QueryId,
@@ -26,37 +26,11 @@ impl From<TileRequest> for RequestType {
}
}
async fn query_html_image(url: &str, credentials: RequestCredentials) -> Result<web_sys::HtmlImageElement, JsValue> {
let image = web_sys::HtmlImageElement::new().unwrap_abort();
let image_cloned = image.clone();
// Set the CORS and credentials options for the image
let cors_value = match credentials {
RequestCredentials::Include => Some("use-credentials"),
RequestCredentials::SameOrigin => Some("anonymous"),
_ => Some("")
};
let promise = js_sys::Promise::new(
&mut (Box::new(move |resolve, reject| {
// Ask for CORS permissions
image_cloned.set_cross_origin(cors_value);
image_cloned.set_onload(Some(&resolve));
image_cloned.set_onerror(Some(&reject));
image_cloned.set_src(&url);
}) as Box<dyn FnMut(js_sys::Function, js_sys::Function)>),
);
let _ = JsFuture::from(promise).await?;
Ok(image)
}
use al_core::image::html::HTMLImage;
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::JsFuture;
use web_sys::{RequestInit, Response, RequestCredentials};
use web_sys::{RequestInit, Response};
impl From<query::Tile> for TileRequest {
// Create a tile request associated to a HiPS
fn from(query: query::Tile) -> Self {
@@ -69,6 +43,7 @@ impl From<query::Tile> for TileRequest {
mode,
id,
channel: slice,
size,
} = query;
let url_clone = url.clone();
@@ -174,7 +149,10 @@ impl From<query::Tile> for TileRequest {
let array_buffer = JsFuture::from(resp.array_buffer()?).await?;
let raw_bytes = js_sys::Uint8Array::new(&array_buffer);
Ok(ImageType::FitsImage { raw_bytes })
Ok(ImageType::FitsImage {
raw_bytes,
size: (size, size),
})
} else {
Err(JsValue::from_str(
"Response status code not between 200-299.",

View File

@@ -16,7 +16,7 @@ use healpix::compass_point::Ordinal;
use healpix::compass_point::OrdinalMap;
use crate::utils;
use crate::Abort;
impl HEALPixCell {
// Build the parent cell
#[inline(always)]
@@ -141,7 +141,7 @@ impl HEALPixCell {
let mut smallest_ancestor = c1.smallest_common_ancestor(c2);
while let (Some(ancestor), Some(cell)) = (smallest_ancestor, cells.next()) {
smallest_ancestor = ancestor.smallest_common_ancestor(&cell);
smallest_ancestor = ancestor.smallest_common_ancestor(cell);
}
smallest_ancestor
@@ -474,12 +474,12 @@ impl Iterator for HEALPixTilesIter {
// Follow the z-order curve
impl PartialOrd for HEALPixCell {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.z_29().partial_cmp(&other.z_29())
Some(self.cmp(other))
}
}
impl Ord for HEALPixCell {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap_abort()
self.z_29().cmp(&other.z_29())
}
}

View File

@@ -87,7 +87,8 @@ impl HEALPixCoverage {
}
pub fn contains_lonlat(&self, lonlat: &LonLatT<f64>) -> bool {
self.0.is_in(lonlat.lon().to_radians(), lonlat.lat().to_radians())
self.0
.is_in(lonlat.lon().to_radians(), lonlat.lat().to_radians())
}
// O(log2(N))

View File

@@ -70,7 +70,7 @@ impl IdxVec {
let bbox1 = a1.get_containing_hpx_cell();
let bbox2 = a2.get_containing_hpx_cell();
bbox1.cmp(&bbox2)
bbox1.cmp(bbox2)
});
// At this point the arcs are sorted by the z-order curve of their

View File

@@ -1,4 +1,4 @@
pub mod cell;
pub mod coverage;
pub mod index_vector;
pub mod utils;
pub mod index_vector;

View File

@@ -1,12 +1,11 @@
use crate::healpix::cell::HEALPixCell;
use crate::math::lonlat::LonLatT;
use crate::math::angle::ToAngle;
/// A simple wrapper around sore core methods
/// of cdshealpix
///
/// cdshealpix is developped by F-X. Pineau.
/// Please check its github repo: https://github.com/cds-astro/cds-healpix-rust
use crate::healpix::cell::HEALPixCell;
use crate::math::angle::ToAngle;
use crate::math::lonlat::LonLatT;
/// Get the vertices of an HEALPix cell
use cgmath::BaseFloat;
#[allow(dead_code)]

View File

@@ -23,7 +23,7 @@ impl Inertia {
ampl,
speed: ampl,
axis,
north_up
north_up,
}
}

View File

@@ -88,9 +88,9 @@ use math::projection::*;
use moclib::moc::RangeMOCIntoIterator;
//use votable::votable::VOTableWrapper;
use crate::tile_fetcher::HiPSLocalFiles;
use al_api::moc::MOCOptions;
use wasm_bindgen::prelude::*;
use web_sys::HtmlElement;
use al_api::moc::MOCOptions;
use crate::math::angle::ToAngle;
@@ -187,7 +187,7 @@ impl WebClient {
#[wasm_bindgen(js_name = isInerting)]
pub fn is_inerting(&self) -> bool {
return self.app.is_inerting();
self.app.is_inerting()
}
/// Update the view
@@ -595,7 +595,8 @@ impl WebClient {
/// Set the zoom factor of the view
#[wasm_bindgen(js_name = setZoomFactor)]
pub fn set_zoom_factor(&mut self, zoom_factor: f64) -> Result<(), JsValue> {
Ok(self.app.set_zoom_factor(zoom_factor))
self.app.set_zoom_factor(zoom_factor);
Ok(())
}
/// Set the center of the view in ICRS coosys
@@ -725,16 +726,12 @@ impl WebClient {
let vertices = lon
.iter()
.zip(lat.iter())
.map(|(&lon, &lat)| {
let xy = self
.app
.flat_map(|(&lon, &lat)| {
self.app
.world_to_screen(lon, lat)
.map(|v| [v.x, v.y])
.unwrap_or([0.0, 0.0]);
xy
.unwrap_or([0.0, 0.0])
})
.flatten()
.collect::<Vec<_>>();
vertices.into_boxed_slice()
@@ -924,10 +921,12 @@ impl WebClient {
lat2: f64,
) -> Result<Box<[f64]>, JsValue> {
let vertices = crate::renderable::line::great_circle_arc::project(
lon1.to_radians(), lat1.to_radians(),
lon2.to_radians(), lat2.to_radians(),
lon1.to_radians(),
lat1.to_radians(),
lon2.to_radians(),
lat2.to_radians(),
&self.app.camera,
&self.app.projection
&self.app.projection,
);
let vertices = vertices
@@ -1019,7 +1018,14 @@ impl WebClient {
}
#[wasm_bindgen(js_name = probeLineOfPixels)]
pub fn probe_line_of_pixels(&self, x1: f64, y1: f64, x2: f64, y2: f64, layer: String) -> Result<Vec<JsValue>, JsValue> {
pub fn probe_line_of_pixels(
&self,
x1: f64,
y1: f64,
x2: f64,
y2: f64,
layer: String,
) -> Result<Vec<JsValue>, JsValue> {
self.app.read_line_of_pixels(x1, y1, x2, y2, layer.as_str())
}
@@ -1053,11 +1059,7 @@ impl WebClient {
}
#[wasm_bindgen(js_name = addJSONMoc)]
pub fn add_json_moc(
&mut self,
options: MOCOptions,
data: &JsValue,
) -> Result<(), JsValue> {
pub fn add_json_moc(&mut self, options: MOCOptions, data: &JsValue) -> Result<(), JsValue> {
let str: String = js_sys::JSON::stringify(data)?.into();
let moc = moclib::deser::json::from_json_aladin::<u64, Hpx<u64>>(&str)
@@ -1074,7 +1076,7 @@ impl WebClient {
#[wasm_bindgen(js_name = addFITSMOC)]
pub fn add_fits_moc(&mut self, options: MOCOptions, data: &[u8]) -> Result<(), JsValue> {
//let bytes = js_sys::Uint8Array::new(array_buffer).to_vec();
let moc = match fits::from_fits_ivoa_custom(Cursor::new(&data[..]), false)
let moc = match fits::from_fits_ivoa_custom(Cursor::new(data), false)
.map_err(|e| JsValue::from_str(&e.to_string()))?
{
MocIdxType::U16(MocQtyType::<u16, _>::Hpx(moc)) => {
@@ -1134,7 +1136,7 @@ impl WebClient {
let v_in = &Vector3::new(1.0, 0.0, 0.0);
let mut moc = HEALPixCoverage::from_3d_coos(pixel_d as u8 - 1, vertex_it, &v_in);
let mut moc = HEALPixCoverage::from_3d_coos(pixel_d as u8 - 1, vertex_it, v_in);
if moc.sky_fraction() > 0.5 {
moc = moc.not();
}
@@ -1159,12 +1161,7 @@ impl WebClient {
}
#[wasm_bindgen(js_name = mocContains)]
pub fn moc_contains(
&mut self,
moc_uuid: String,
lon: f64,
lat: f64,
) -> Result<bool, JsValue> {
pub fn moc_contains(&mut self, moc_uuid: String, lon: f64, lat: f64) -> Result<bool, JsValue> {
let moc = self
.app
.get_moc(&moc_uuid)

View File

@@ -1,5 +1,5 @@
use cgmath::BaseFloat;
use crate::Abort;
use cgmath::BaseFloat;
// ArcDeg wrapper structure
#[derive(Clone, Copy)]
pub struct ArcDeg<T: BaseFloat>(pub T);
@@ -42,12 +42,12 @@ where
}
}
impl<T> ToString for ArcDeg<T>
impl<T> Display for ArcDeg<T>
where
T: BaseFloat + ToString,
T: BaseFloat + Display,
{
fn to_string(&self) -> String {
self.0.to_string()
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
@@ -55,7 +55,6 @@ where
#[derive(Clone, Copy)]
pub struct ArcHour<T: BaseFloat>(pub T);
impl<T> From<Rad<T>> for ArcHour<T>
where
T: BaseFloat,
@@ -81,12 +80,12 @@ where
}
}
impl<T> ToString for ArcHour<T>
impl<T> Display for ArcHour<T>
where
T: BaseFloat + ToString,
T: BaseFloat + Display,
{
fn to_string(&self) -> String {
self.0.to_string()
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
@@ -132,12 +131,12 @@ where
}
}
impl<T> ToString for ArcMin<T>
impl<T> Display for ArcMin<T>
where
T: BaseFloat + ToString,
T: BaseFloat + Display,
{
fn to_string(&self) -> String {
self.0.to_string()
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
@@ -171,12 +170,12 @@ where
}
}
impl<T> ToString for ArcSec<T>
impl<T> Display for ArcSec<T>
where
T: BaseFloat + ToString,
T: BaseFloat + Display,
{
fn to_string(&self) -> String {
self.0.to_string()
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
@@ -192,130 +191,6 @@ where
}
use al_api::angle::Format;
/*
pub enum SerializeFmt {
DMS,
HMS,
DMM,
DD,
}
use al_api::angle_fmt::AngleSerializeFmt;
impl From<AngleSerializeFmt> for SerializeFmt {
fn from(value: AngleSerializeFmt) -> Self {
match value {
AngleSerializeFmt::DMS => SerializeFmt::DMS,
AngleSerializeFmt::HMS => SerializeFmt::HMS,
AngleSerializeFmt::DMM => SerializeFmt::DMM,
AngleSerializeFmt::DD => SerializeFmt::DD,
}
}
}
impl SerializeFmt {
pub fn to_string<S: BaseFloat + ToString>(&self, angle: Angle<S>) -> String {
match &self {
Self::DMS => DMS::to_string(angle),
Self::HMS => HMS::to_string(angle),
Self::DMM => DMM::to_string(angle),
Self::DD => DD::to_string(angle),
}
}
}*/
/*pub trait SerializeToString {
fn to_string(&self) -> String;
}
impl<S> SerializeToString for Angle<S>
where
S: BaseFloat + ToString,
{
fn to_string<F: FormatType>(&self) -> String {
F::to_string(*self)
}
}*/
/*
pub struct DMS;
pub struct HMS;
pub struct DMM;
pub struct DD;
pub trait FormatType {
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String;
}
impl FormatType for DD {
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String {
let angle = Rad(angle.0);
let degrees: ArcDeg<S> = angle.into();
degrees.to_string()
}
}
impl FormatType for DMM {
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String {
let angle = Rad(angle.0);
let mut degrees: ArcDeg<S> = angle.into();
let minutes = degrees.get_frac_minutes();
degrees.truncate();
let mut result = degrees.to_string() + " ";
result += &minutes.to_string();
result
}
}
impl FormatType for DMS {
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String {
let angle = Rad(angle.0);
let degrees: ArcDeg<S> = angle.into();
let minutes = degrees.get_frac_minutes();
let seconds = minutes.get_frac_seconds();
let num_sec_per_minutes = S::from(60).unwrap_abort();
let degrees = degrees.trunc();
let minutes = minutes.trunc() % num_sec_per_minutes;
let seconds = seconds.trunc() % num_sec_per_minutes;
let mut result = degrees.to_string() + "°";
result += &minutes.to_string();
result += "\'";
result += &seconds.to_string();
result += "\'\'";
result
}
}
impl FormatType for HMS {
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String {
let angle = Rad(angle.0);
let hours: ArcHour<S> = angle.into();
let minutes = hours.get_frac_minutes();
let seconds = minutes.get_frac_seconds();
let num_sec_per_minutes = S::from(60).unwrap_abort();
let hours = hours.trunc();
let minutes = minutes.trunc() % num_sec_per_minutes;
let seconds = seconds.trunc() % num_sec_per_minutes;
let mut result = hours.to_string() + "h";
result += &minutes.to_string();
result += "\'";
result += &seconds.to_string();
result += "\'\'";
result
}
}*/
#[derive(Clone, Copy, Debug, Eq, Hash, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -330,7 +205,10 @@ where
{
pub fn new<T: Into<Rad<S>>>(angle: T) -> Angle<S> {
let radians: Rad<S> = angle.into();
Angle { rad: radians.0, fmt: AngleFormatter::default() }
Angle {
rad: radians.0,
fmt: AngleFormatter::default(),
}
}
pub fn cos(&self) -> S {
@@ -429,7 +307,10 @@ where
S: BaseFloat,
{
fn to_angle(self) -> Angle<S> {
Angle { rad: self, fmt: Default::default() }
Angle {
rad: self,
fmt: Default::default(),
}
}
}
@@ -685,7 +566,7 @@ pub enum AngleFormatter {
Decimal {
/// Number of digit of precision
prec: u8,
}
},
}
impl Default for AngleFormatter {
@@ -711,7 +592,7 @@ impl Display for Angle<f64> {
// Format the unit value to sexagesimal.
// The precision 8 corresponds to the formatting: deg/hour min sec.ddd
write!(f, "{}", Format::toSexagesimal(unit, 8, plus))
},
}
AngleFormatter::Decimal { prec } => {
write!(f, "{:.1$}°", self.to_degrees(), prec as usize)
}

View File

@@ -27,7 +27,7 @@ where
/// * ``lat`` - Latitude
pub fn new(mut lon: Angle<S>, lat: Angle<S>) -> LonLatT<S> {
if lon.to_radians() < S::zero() {
lon = lon + S::from(TWICE_PI).unwrap_abort();
lon += S::from(TWICE_PI).unwrap_abort();
}
LonLatT(lon, lat)
@@ -121,7 +121,8 @@ pub fn ang_between_lonlat<S: BaseFloat>(lonlat1: LonLatT<S>, lonlat2: LonLatT<S>
let abs_diff_lon = (lonlat1.lon() - lonlat2.lon()).abs();
(lonlat1.lat().sin() * lonlat2.lat().sin()
+ lonlat1.lat().cos() * lonlat2.lat().cos() * abs_diff_lon.cos())
.acos().to_angle()
.acos()
.to_angle()
}
#[inline]
@@ -180,7 +181,7 @@ pub fn unproj(
camera: &CameraViewPort,
) -> Option<LonLatT<f64>> {
projection
.normalized_device_to_model_space(&ndc_xy, camera)
.normalized_device_to_model_space(ndc_xy, camera)
.map(|model_pos| model_pos.lonlat())
}
@@ -201,13 +202,14 @@ pub fn unproj_from_screen(
camera: &CameraViewPort,
) -> Option<LonLatT<f64>> {
projection
.screen_to_model_space(&xy, camera)
.screen_to_model_space(xy, camera)
.map(|model_pos| model_pos.lonlat())
}
#[inline]
pub fn is_in(v1: &Vector3<f64>, v2: &Vector3<f64>, v: &Vector3<f64>) -> bool {
let theta = crate::math::vector::angle3(&v1, &v2).abs();
let theta = crate::math::vector::angle3(v1, v2).abs();
crate::math::vector::angle3(&v1, &v).abs() < theta && crate::math::vector::angle3(&v, &v2).abs() < theta
crate::math::vector::angle3(v1, v).abs() < theta
&& crate::math::vector::angle3(v, v2).abs() < theta
}

View File

@@ -5,7 +5,7 @@ pub const HALF_PI: f64 = std::f64::consts::PI * 0.5;
pub const MINUS_HALF_PI: f64 = -std::f64::consts::PI * 0.5;
pub const TWO_SQRT_TWO: f64 = 2.82842712475;
pub const SQRT_TWO: f64 = 1.41421356237;
pub const SQRT_TWO: f64 = std::f64::consts::SQRT_2;
pub const ZERO: f64 = 0.0;

View File

@@ -16,7 +16,7 @@ impl ProjDef for Ellipse {
let mut p = Vector2::new(xy.x.abs(), xy.y.abs());
let mut ab = Vector2::new(self.a, self.b);
let sdf = if p.x == 0.0 {
if p.x == 0.0 {
-(self.b - p.y)
} else if p.y == 0.0 {
-(self.a - p.x)
@@ -61,8 +61,6 @@ impl ProjDef for Ellipse {
let q = Vector2::new(ab.x * co, ab.y * si);
(q - p).magnitude() * (p.y - q.y).signum()
};
sdf
}
}
}

View File

@@ -2,4 +2,4 @@ pub mod disk;
pub mod ellipse;
pub mod parabola;
pub mod rect;
pub mod triangle;
pub mod triangle;

View File

@@ -22,6 +22,12 @@ pub struct Cod {
pub x_max: f64,
}
impl Default for Cod {
fn default() -> Self {
Self::new()
}
}
impl Cod {
pub const fn new() -> Self {
Self {
@@ -57,7 +63,7 @@ impl ProjDef for Cod {
let e = b / a;
let ext_ellipse = Translate {
off: center_ellipse,
def: Ellipse { a: a, b: b },
def: Ellipse { a, b },
};
// Small ellipse where projection is not defined

View File

@@ -5,5 +5,5 @@ pub mod par;
pub mod basic;
pub mod op;
pub mod sdf;
pub mod op;

View File

@@ -36,7 +36,7 @@ where
let d = valid_reg.sdf(&v);
// Perform the ray marching advancement
v = v + dir * d;
v += dir * d;
is_in = valid_reg.is_in(&v);
i += 1;

View File

@@ -163,7 +163,7 @@ impl ProjectionType {
}
/// Screen to model space deprojection
///
/// Perform a screen to the world space deprojection
///
/// # Arguments
@@ -184,8 +184,6 @@ impl ProjectionType {
self.clip_to_world_space(&pos_clip_space)
}
/// Screen to model space deprojection
/// Perform a screen to the world space deprojection
///
/// # Arguments
@@ -262,8 +260,6 @@ impl ProjectionType {
self.world_to_clip_space(&pos_world_space)
}
/// World to screen space projection
/// World to screen space transformation
///
/// # Arguments
@@ -682,7 +678,7 @@ use self::coo_space::XYNDC;
use super::lonlat::LonLatT;
impl<'a, P> Projection for &'a P
impl<P> Projection for &P
where
P: CanonicalProjection,
{
@@ -775,8 +771,8 @@ mod tests {
for y in 0..(h as u32) {
let xy = Vector2::new(x, y);
let clip_xy = Vector2::new(
2.0 * ((xy.x as f64) / (w as f64)) - 1.0,
2.0 * ((xy.y as f64) / (h as f64)) - 1.0,
2.0 * ((xy.x as f64) / w) - 1.0,
2.0 * ((xy.y as f64) / h) - 1.0,
);
let rgb = if let Some(pos) = projection.clip_to_world_space(&clip_xy) {
let pos = pos.normalize();
@@ -789,7 +785,7 @@ mod tests {
Rgb([255, 255, 255])
};
img.put_pixel(x as u32, y as u32, rgb);
img.put_pixel(x, y, rgb);
}
}
img.save(filename).unwrap_abort();

View File

@@ -1,15 +1,15 @@
use crate::math;
use crate::math::angle::ToAngle;
use cgmath::Vector3;
use cgmath::{BaseFloat, InnerSpace};
use cgmath::{Euler, Quaternion};
use cgmath::Vector3;
#[derive(Clone, Copy, Debug)]
// Internal structure of a rotation, a quaternion
// All operations are done on it
pub struct Rotation<S: BaseFloat>(pub Quaternion<S>);
use cgmath::{Matrix3};
use cgmath::Matrix3;
impl<S> From<&Matrix3<S>> for Rotation<S>
where
S: BaseFloat,
@@ -117,7 +117,7 @@ where
// Define a rotation from a normalized vector
pub fn from_sky_position(pos: &Vector3<S>) -> Rotation<S> {
let (lon, lat) = math::lonlat::xyz_to_radec(&pos);
let (lon, lat) = math::lonlat::xyz_to_radec(pos);
let rot_y = Matrix3::from_angle_y(lon);
let rot_x = Matrix3::from_angle_x(-lat);

View File

@@ -38,22 +38,18 @@ impl BoundingBox {
PoleContained::None => {
// The polygon does not contain any pole
// Meridian 0deg is not crossing the polygon
let (min_lat, max_lat) = lat
.iter()
.fold((std::f64::MAX, std::f64::MIN), |(min, max), &b| {
(min.min(b), max.max(b))
});
let (min_lat, max_lat) = lat.iter().fold((f64::MAX, f64::MIN), |(min, max), &b| {
(min.min(b), max.max(b))
});
let (min_lon, max_lon) = lon
.iter()
.fold((std::f64::MAX, std::f64::MIN), |(min, max), &b| {
(min.min(b), max.max(b))
});
let (min_lon, max_lon) = lon.iter().fold((f64::MAX, f64::MIN), |(min, max), &b| {
(min.min(b), max.max(b))
});
(min_lon..max_lon, min_lat..max_lat)
}
PoleContained::South => {
let max_lat = lat.iter().fold(std::f64::MIN, |a, b| a.max(*b));
let max_lat = lat.iter().fold(f64::MIN, |a, b| a.max(*b));
(
if intersect_zero_meridian {
-PI..PI
@@ -64,7 +60,7 @@ impl BoundingBox {
)
}
PoleContained::North => {
let min_lat = lat.iter().fold(std::f64::MAX, |a, b| a.min(*b));
let min_lat = lat.iter().fold(f64::MAX, |a, b| a.min(*b));
(
if intersect_zero_meridian {
-PI..PI

View File

@@ -13,18 +13,18 @@ pub enum HEALPixBBox {
impl PartialOrd for HEALPixBBox {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(HEALPixBBox::AllSky, HEALPixBBox::AllSky) => Some(Ordering::Equal),
(HEALPixBBox::AllSky, HEALPixBBox::Cell(_)) => Some(Ordering::Greater),
(HEALPixBBox::Cell(_), HEALPixBBox::AllSky) => Some(Ordering::Less),
(HEALPixBBox::Cell(c1), HEALPixBBox::Cell(c2)) => c1.partial_cmp(c2),
}
Some(self.cmp(other))
}
}
impl Ord for HEALPixBBox {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap_abort()
match (self, other) {
(HEALPixBBox::AllSky, HEALPixBBox::AllSky) => Ordering::Equal,
(HEALPixBBox::AllSky, HEALPixBBox::Cell(_)) => Ordering::Greater,
(HEALPixBBox::Cell(_), HEALPixBBox::AllSky) => Ordering::Less,
(HEALPixBBox::Cell(c1), HEALPixBBox::Cell(c2)) => c1.cmp(c2),
}
}
}

View File

@@ -1,26 +1,26 @@
pub mod bbox;
pub mod region;
pub mod great_circle_arc;
pub mod region;
use super::{PI, TWICE_PI};
#[inline]
pub fn is_in_lon_range(lon0: f64, lon1: f64, lon2: f64) -> bool {
// First version of the code:
// First version of the code:
// ((v2.lon() - v1.lon()).abs() > PI) != ((v2.lon() > coo.lon()) != (v1.lon() > coo.lon()))
//
// Lets note
//
// Lets note
// - lonA = v1.lon()
// - lonB = v2.lon()
// - lon0 = coo.lon()
// When (lonB - lonA).abs() <= PI
// When (lonB - lonA).abs() <= PI
// => lonB > lon0 != lonA > lon0 like in PNPOLY
// A B lonA <= lon0 && lon0 < lonB
// --[++++[--
// B A lonB <= lon0 && lon0 < lonA
//
// But when (lonB - lonA).abs() > PI, then the test should be
// => lonA >= lon0 == lonB >= lon0
// But when (lonB - lonA).abs() > PI, then the test should be
// => lonA >= lon0 == lonB >= lon0
// <=> !(lonA >= lon0 != lonB >= lon0)
// A | B (lon0 < lonB) || (lonA <= lon0)
// --[++|++[--
@@ -32,17 +32,17 @@ pub fn is_in_lon_range(lon0: f64, lon1: f64, lon2: f64) -> bool {
// --]++|++]--
// B | A (lon0 <= lonA) || (lonB < lon0)
//
// So the previous code was bugged in this very specific case:
// So the previous code was bugged in this very specific case:
// - `lon0` has the same value as a vertex being part of:
// - one segment that do not cross RA=0
// - plus one segment crossing RA=0.
// - the point have an odd number of intersections with the polygon
// - the point have an odd number of intersections with the polygon
// (since it will be counted 0 or 2 times instead of 1).
let dlon = lon2 - lon1;
if dlon < 0.0 {
(dlon >= -PI) == (lon2 <= lon0 && lon0 < lon1)
} else {
(dlon <= PI) == (lon1 <= lon0 && lon0 < lon2)
(dlon <= PI) == (lon1 <= lon0 && lon0 < lon2)
}
}
@@ -58,4 +58,3 @@ pub fn distance_from_two_lon(lon1: f64, lon2: f64) -> f64 {
lon2 - lon1
}
}

View File

@@ -248,7 +248,7 @@ impl Region {
Region::AllSky => true,
Region::Polygon { polygon, bbox, .. } => {
// Fast checking with the bbox
if !bbox.contains_lonlat(&lonlat) {
if !bbox.contains_lonlat(lonlat) {
return false;
}

View File

@@ -115,7 +115,6 @@ pub fn ccw_tri<S: BaseFloat>(a: &[S; 2], b: &[S; 2], c: &[S; 2]) -> bool {
a[0] * b[1] + a[1] * c[0] + b[0] * c[1] - c[0] * b[1] - c[1] * a[0] - b[0] * a[1] >= S::zero()
}
struct PixelBresenhamIter {
x: i32,
y: i32,
@@ -133,17 +132,28 @@ impl PixelBresenhamIter {
fn new(sx: f64, sy: f64, ex: f64, ey: f64) -> Self {
let x = sx.floor() as i32;
let y = sy.floor() as i32;
let xx = ex.floor() as i32;
let yy = ey.floor() as i32;
let dx = (xx - x).abs();
let dx = (xx - x).abs();
let sx = if x < xx { 1 } else { -1 };
let dy = -(yy - y).abs();
let sy = if y < yy { 1 } else { -1 };
let err = dx + dy;
let end = false;
Self { x, y, xx, yy, dx, sx, dy, sy, err, end }
Self {
x,
y,
xx,
yy,
dx,
sx,
dy,
sy,
err,
end,
}
}
}
@@ -177,4 +187,4 @@ impl Iterator for PixelBresenhamIter {
pub fn bresenham(sx: f64, sy: f64, ex: f64, ey: f64) -> impl Iterator<Item = (f64, f64)> {
PixelBresenhamIter::new(sx, sy, ex, ey)
}
}

View File

@@ -1,6 +1,6 @@
use crate::math::angle::Angle;
use cgmath::{BaseFloat, InnerSpace, Vector2, Vector3};
use crate::math::angle::ToAngle;
use cgmath::{BaseFloat, InnerSpace, Vector2, Vector3};
#[inline]
pub fn angle2<S: BaseFloat>(ab: &Vector2<S>, bc: &Vector2<S>) -> Angle<S> {
@@ -60,13 +60,12 @@ impl NormedVector2 {
Self(normed_v)
}
pub const unsafe fn new_unsafe(x: f64, y: f64) -> Self {
pub const fn new_unsafe(x: f64, y: f64) -> Self {
let v = Vector2::new(x, y);
Self(v)
}
}
use std::ops::Deref;
impl Deref for NormedVector2 {
type Target = Vector2<f64>;
@@ -77,7 +76,7 @@ impl Deref for NormedVector2 {
}
use std::ops::Mul;
impl<'a> Mul<f64> for &'a NormedVector2 {
impl Mul<f64> for &NormedVector2 {
// The multiplication of rational numbers is a closed operation.
type Output = Vector2<f64>;

View File

@@ -189,7 +189,7 @@ impl Manager {
) {
// Create the HashMap storing the source indices with respect to the
// HEALPix cell at depth 7 in which they are contained
let catalog = Catalog::new::<P>(&self.gl, colormap, sources);
let catalog = Catalog::new(&self.gl, colormap, sources);
// Update the number of sources loaded
//self.num_sources += num_instances_in_catalog as usize;
@@ -239,7 +239,7 @@ impl Manager {
catalog.update(cells);
}
} else {
let depth = camera.get_texture_depth().min(7);
let depth = camera.get_tile_depth().min(7);
let cells = camera.get_hpx_cells(depth, CooSystem::ICRS);
for catalog in self.catalogs.values_mut() {
@@ -286,11 +286,7 @@ const MAX_SOURCES_PER_CATALOG: f32 = 50000.0;
use crate::Abort;
impl Catalog {
fn new<P: Projection>(
gl: &WebGlContext,
colormap: Colormap,
mut lonlat: Box<[LonLatT<f32>]>,
) -> Catalog {
fn new(gl: &WebGlContext, colormap: Colormap, mut lonlat: Box<[LonLatT<f32>]>) -> Catalog {
let alpha = 1_f32;
let strength = 1_f32;
let index_vec = IdxVec::from_coo(&mut lonlat);
@@ -406,7 +402,7 @@ impl Catalog {
for cell in cells {
let sources_idx = self.index_vec.get_item_indices_inside_hpx_cell(cell);
total_sources += (sources_idx.end - sources_idx.start) as usize;
total_sources += sources_idx.end - sources_idx.start;
}
total_sources
@@ -424,8 +420,7 @@ impl Catalog {
for c in cell.get_children_cells(delta_depth as u8) {
// Define the total number of sources being in this kernel depth tile
let sources_in_cell = self.index_vec.get_item_indices_inside_hpx_cell(&c);
let num_sources_in_kernel_cell =
(sources_in_cell.end - sources_in_cell.start) as usize;
let num_sources_in_kernel_cell = sources_in_cell.end - sources_in_cell.start;
if num_sources_in_kernel_cell > 0 {
let num_sources = (((num_sources_in_kernel_cell as f32) / num_sources_in_fov)
* MAX_SOURCES_PER_CATALOG) as usize;
@@ -459,6 +454,7 @@ impl Catalog {
);
}
#[allow(clippy::too_many_arguments)]
fn draw(
&self,
gl: &WebGlContext,
@@ -515,7 +511,7 @@ impl Catalog {
.draw_elements_instanced_with_i32(
WebGl2RenderingContext::TRIANGLES,
0,
self.num_instances as i32,
self.num_instances,
);
Ok(())
},

View File

@@ -10,8 +10,8 @@ use crate::math::lonlat::LonLat;
use crate::math::projection::coo_space::XYScreen;
use crate::math::TWICE_PI;
use crate::math::angle::ToAngle;
use crate::math::angle::AngleFormatter;
use crate::math::angle::ToAngle;
use al_api::angle::Formatter;
use cgmath::Vector2;
use core::ops::Range;
@@ -40,7 +40,7 @@ impl Label {
camera: &CameraViewPort,
projection: &ProjectionType,
fmt: Formatter,
grid_decimal_prec: u8
grid_decimal_prec: u8,
) -> Option<Self> {
let fov = camera.get_field_of_view();
let d = if fov.contains_north_pole() {
@@ -79,12 +79,16 @@ impl Label {
let mut angle = lon.to_angle();
let fmt = match fmt {
Formatter::Decimal => {
AngleFormatter::Decimal { prec: grid_decimal_prec }
Formatter::Decimal => AngleFormatter::Decimal {
prec: grid_decimal_prec,
},
Formatter::Sexagesimal => {
// Sexagesimal formatting for longitudes is HMS
AngleFormatter::Sexagesimal { prec: grid_decimal_prec, plus: false, hours: true }
AngleFormatter::Sexagesimal {
prec: grid_decimal_prec,
plus: false,
hours: true,
}
}
};
angle.set_format(fmt);
@@ -113,7 +117,7 @@ impl Label {
camera: &CameraViewPort,
projection: &ProjectionType,
fmt: Formatter,
grid_decimal_prec: u8
grid_decimal_prec: u8,
) -> Option<Self> {
let lonlat = match options {
LabelOptions::Centered => {
@@ -145,12 +149,16 @@ impl Label {
let mut angle = lat.to_angle();
let fmt = match fmt {
Formatter::Decimal => {
AngleFormatter::Decimal { prec: grid_decimal_prec }
Formatter::Decimal => AngleFormatter::Decimal {
prec: grid_decimal_prec,
},
Formatter::Sexagesimal => {
// Sexagesimal formatting for latitudes is DMS with an optional '+' character
AngleFormatter::Sexagesimal { prec: grid_decimal_prec, plus: true, hours: false }
AngleFormatter::Sexagesimal {
prec: grid_decimal_prec,
plus: true,
hours: false,
}
}
};
angle.set_format(fmt);

View File

@@ -16,7 +16,7 @@ pub fn get_intersecting_meridian(
camera: &CameraViewPort,
projection: &ProjectionType,
fmt: Formatter,
grid_decimal_prec: u8
grid_decimal_prec: u8,
) -> Option<Meridian> {
let fov = camera.get_field_of_view();
if fov.contains_both_poles() {
@@ -27,7 +27,7 @@ pub fn get_intersecting_meridian(
camera,
projection,
fmt,
grid_decimal_prec
grid_decimal_prec,
);
Some(meridian)
} else {
@@ -42,7 +42,7 @@ pub fn get_intersecting_meridian(
camera,
projection,
fmt,
grid_decimal_prec
grid_decimal_prec,
);
Some(meridian)
}
@@ -60,7 +60,15 @@ pub fn get_intersecting_meridian(
lat1..MINUS_HALF_PI
};
Meridian::new(lon, &lat, LabelOptions::OnSide, camera, projection, fmt, grid_decimal_prec)
Meridian::new(
lon,
&lat,
LabelOptions::OnSide,
camera,
projection,
fmt,
grid_decimal_prec,
)
}
2 => {
// full intersection
@@ -77,7 +85,7 @@ pub fn get_intersecting_meridian(
camera,
projection,
fmt,
grid_decimal_prec
grid_decimal_prec,
)
}
_ => Meridian::new(
@@ -87,8 +95,8 @@ pub fn get_intersecting_meridian(
camera,
projection,
fmt,
grid_decimal_prec
)
grid_decimal_prec,
),
};
Some(meridian)
@@ -113,9 +121,17 @@ impl Meridian {
camera: &CameraViewPort,
projection: &ProjectionType,
fmt: Formatter,
grid_decimal_prec: u8
grid_decimal_prec: u8,
) -> Self {
let label = Label::from_meridian(lon, lat, label_options, camera, projection, fmt, grid_decimal_prec);
let label = Label::from_meridian(
lon,
lat,
label_options,
camera,
projection,
fmt,
grid_decimal_prec,
);
// Draw the full parallel
let vertices = crate::renderable::line::great_circle_arc::project(

View File

@@ -36,12 +36,12 @@ pub struct ProjetedGrid {
vao: VertexArrayObject,
gl: WebGlContext,
}
use self::meridian::Meridian;
use crate::renderable::text::TextRenderManager;
use crate::renderable::Renderer;
use al_api::angle::Formatter;
use wasm_bindgen::JsValue;
use web_sys::HtmlElement;
use al_api::angle::Formatter;
use self::meridian::Meridian;
impl ProjetedGrid {
pub fn new(gl: WebGlContext, aladin_div: &HtmlElement) -> Result<ProjetedGrid, JsValue> {
let text_renderer = TextRenderManager::new(aladin_div)?;
@@ -181,7 +181,7 @@ impl ProjetedGrid {
{
let position = position.cast::<f32>().unwrap_abort();
self.text_renderer
.add_label(&content, &position, cgmath::Rad(*rot as f32))?;
.add_label(content, &position, cgmath::Rad(*rot as f32))?;
}
self.text_renderer.end();
}
@@ -205,7 +205,7 @@ impl ProjetedGrid {
self.meridians = {
// Select the good step with a binary search
let step_lon_precised =
(bbox.get_lon_size() as f64) * step_line_px / (camera.get_width() as f64);
bbox.get_lon_size() * step_line_px / (camera.get_width() as f64);
let step_lon = select_fixed_step(step_lon_precised);
let decimal_lon_prec = step_lon.to_degrees().log10().abs().ceil() as u8;
@@ -220,9 +220,13 @@ impl ProjetedGrid {
let mut meridians = vec![];
let mut lon = start_lon;
while lon < stop_lon {
if let Some(p) =
meridian::get_intersecting_meridian(lon, camera, projection, self.fmt, decimal_lon_prec)
{
if let Some(p) = meridian::get_intersecting_meridian(
lon,
camera,
projection,
self.fmt,
decimal_lon_prec,
) {
meridians.push(p);
}
lon += step_lon;
@@ -232,7 +236,7 @@ impl ProjetedGrid {
self.parallels = {
let step_lat_precised =
(bbox.get_lat_size() as f64) * step_line_px / (camera.get_height() as f64);
bbox.get_lat_size() * step_line_px / (camera.get_height() as f64);
let step_lat = select_fixed_step(step_lat_precised);
let decimal_lat_prec = step_lat.to_degrees().log10().abs().ceil() as u8;
@@ -246,7 +250,13 @@ impl ProjetedGrid {
let mut parallels = vec![];
while lat < stop_lat {
if let Some(p) = parallel::get_intersecting_parallel(lat, camera, projection, self.fmt, decimal_lat_prec) {
if let Some(p) = parallel::get_intersecting_parallel(
lat,
camera,
projection,
self.fmt,
decimal_lat_prec,
) {
parallels.push(p);
}
lat += step_lat;
@@ -269,12 +279,10 @@ impl ProjetedGrid {
let mut buf: Vec<f32> = vec![];
for vertices in paths {
let vertices = vertices.as_ref();
let path_vertices_buf_iter = vertices
.iter()
.zip(vertices.iter().skip(1))
.map(|(a, b)| [a[0], a[1], b[0], b[1]])
.flatten();
.flat_map(|(a, b)| [a[0], a[1], b[0], b[1]]);
buf.extend(path_vertices_buf_iter);
}

View File

@@ -12,13 +12,12 @@ use crate::renderable::line;
use core::ops::Range;
pub fn get_intersecting_parallel(
lat: f64,
camera: &CameraViewPort,
projection: &ProjectionType,
fmt: Formatter,
grid_decimal_prec: u8
grid_decimal_prec: u8,
) -> Option<Parallel> {
let fov = camera.get_field_of_view();
if fov.get_bounding_box().get_lon_size() > PI {
@@ -33,7 +32,7 @@ pub fn get_intersecting_parallel(
LabelOptions::Centered,
projection,
fmt,
grid_decimal_prec
grid_decimal_prec,
))
} else {
// Longitude fov < PI
@@ -50,7 +49,7 @@ pub fn get_intersecting_parallel(
LabelOptions::Centered,
projection,
fmt,
grid_decimal_prec
grid_decimal_prec,
))
}
Intersection::Intersect { vertices } => {
@@ -74,7 +73,7 @@ pub fn get_intersecting_parallel(
LabelOptions::OnSide,
projection,
fmt,
grid_decimal_prec
grid_decimal_prec,
))
}
Intersection::Empty => None,
@@ -100,9 +99,17 @@ impl Parallel {
label_options: LabelOptions,
projection: &ProjectionType,
fmt: Formatter,
grid_decimal_prec: u8
grid_decimal_prec: u8,
) -> Self {
let label = Label::from_parallel(lat, lon, label_options, camera, projection, fmt, grid_decimal_prec);
let label = Label::from_parallel(
lat,
lon,
label_options,
camera,
projection,
fmt,
grid_decimal_prec,
);
// Draw the full parallel
let vertices = if lon.end - lon.start > PI {

View File

@@ -10,20 +10,13 @@ pub struct HiPSConfig {
// TODO: Make that independant of the HiPS but of the ImageFormat
// The size of the texture images
pub texture_size: i32,
tile_size: i32,
// Delta depth i.e. log2(texture_size / tile_size)
delta_depth: u8,
min_depth_tile: u8,
min_depth_texture: u8,
// the number of slices for cubes
cube_depth: Option<u32>,
// Num tiles per texture
num_tiles_per_texture: usize,
// Max depth of the current HiPS tiles
max_depth_texture: u8,
max_depth_tile: u8,
pub is_allsky: bool,
@@ -40,7 +33,6 @@ pub struct HiPSConfig {
pub tex_storing_fits: bool,
pub tex_storing_unsigned_int: bool,
pub size_tile_uv: f32,
pub frame: CooSystem,
pub bitpix: Option<i32>,
format: ImageFormatType,
@@ -52,7 +44,6 @@ pub struct HiPSConfig {
pub request_mode: RequestMode,
}
use crate::math;
use crate::HiPSProperties;
use al_api::coo_system::CooSystem;
use wasm_bindgen::JsValue;
@@ -147,38 +138,12 @@ impl HiPSConfig {
}),
}?;
/*let dataproduct_subtype = properties.get_dataproduct_subtype().clone();
let colored = if tex_storing_fits {
false
} else {
if let Some(subtypes) = &dataproduct_subtype {
subtypes.iter().any(|subtype| subtype == "color")
} else {
false
}
};*/
let texture_size = std::cmp::min(512, tile_size << max_depth_tile);
//let texture_size = tile_size;
let num_tile_per_side_texture = (texture_size / tile_size) as usize;
let delta_depth = math::utils::log_2_unchecked(num_tile_per_side_texture) as u8;
let num_tiles_per_texture = num_tile_per_side_texture * num_tile_per_side_texture;
let max_depth_texture = max_depth_tile - delta_depth;
let size_tile_uv = 1_f32 / ((1 << delta_depth) as f32);
let frame = properties.get_frame();
let sky_fraction = properties.get_sky_fraction().unwrap_or(1.0);
let is_allsky = sky_fraction >= 1.0;
let min_depth_tile = properties.get_min_order().unwrap_or(0);
let min_depth_texture = if min_depth_tile >= delta_depth {
min_depth_tile - delta_depth
} else {
0
};
let request_credentials = match properties.get_request_credentials() {
"include" => RequestCredentials::Include,
@@ -198,17 +163,7 @@ impl HiPSConfig {
creator_did,
// HiPS name
root_url: root_url.to_string(),
// Texture config
// The size of the texture images
texture_size,
// Delta depth i.e. log2(texture_size / tile_size)
delta_depth,
// Num tiles per texture
num_tiles_per_texture,
// Max depth of the current HiPS tiles
max_depth_texture,
max_depth_tile,
min_depth_texture,
min_depth_tile,
is_allsky,
@@ -225,13 +180,12 @@ impl HiPSConfig {
// the number of slices in a cube
cube_depth,
size_tile_uv,
frame,
bitpix,
format,
tile_size,
request_credentials,
request_mode
request_mode,
};
Ok(hips_config)
@@ -332,18 +286,8 @@ impl HiPSConfig {
}
#[inline(always)]
pub fn delta_depth(&self) -> u8 {
self.delta_depth
}
#[inline(always)]
pub fn num_tiles_per_texture(&self) -> usize {
self.num_tiles_per_texture
}
#[inline(always)]
pub fn get_texture_size(&self) -> i32 {
self.texture_size
pub fn allsky_tile_size(&self) -> i32 {
(self.get_tile_size() << 3).min(512)
}
#[inline(always)]
@@ -351,11 +295,6 @@ impl HiPSConfig {
self.min_depth_tile
}
#[inline(always)]
pub fn get_min_depth_texture(&self) -> u8 {
self.min_depth_texture
}
#[inline(always)]
pub fn get_creator_did(&self) -> &str {
&self.creator_did
@@ -366,11 +305,6 @@ impl HiPSConfig {
self.tile_size
}
#[inline(always)]
pub fn get_max_depth_texture(&self) -> u8 {
self.max_depth_texture
}
#[inline(always)]
pub fn get_max_depth_tile(&self) -> u8 {
self.max_depth_tile
@@ -391,7 +325,6 @@ impl HiPSConfig {
self.format.is_colored()
}
#[inline(always)]
pub fn get_request_credentials(&self) -> RequestCredentials {
self.request_credentials
@@ -409,8 +342,7 @@ impl SendUniforms for HiPSConfig {
fn attach_uniforms<'a>(&self, shader: &'a ShaderBound<'a>) -> &'a ShaderBound<'a> {
// Send max depth
shader
.attach_uniform("max_depth", &(self.max_depth_texture as i32))
.attach_uniform("size_tile_uv", &self.size_tile_uv)
.attach_uniform("max_depth", &(self.max_depth_tile as i32))
.attach_uniform("tex_storing_fits", &self.tex_storing_fits)
.attach_uniform("scale", &self.scale)
.attach_uniform("offset", &self.offset)

View File

@@ -10,7 +10,6 @@ use cgmath::Vector3;
use al_api::hips::ImageExt;
use al_core::webgl_ctx::WebGlRenderingCtx;
use al_core::image::format::ImageFormat;
use al_core::image::format::{R16I, R32F, R32I, R64F, R8UI, RGB8U, RGBA8U};
use al_core::image::Image;
use al_core::shader::{SendUniforms, ShaderBound};
@@ -49,12 +48,16 @@ impl Eq for TextureCellItem {}
// Ordering based on the time the tile has been requested
impl PartialOrd for TextureCellItem {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
other.time_request.partial_cmp(&self.time_request)
Some(self.cmp(other))
}
}
impl Ord for TextureCellItem {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap_abort()
other
.time_request
.partial_cmp(&self.time_request)
.unwrap_abort()
}
}
@@ -135,65 +138,104 @@ pub struct HiPS2DBuffer {
base_textures: [HpxTexture2D; NUM_HPX_TILES_DEPTH_ZERO],
// Array of 2D textures
texture_2d_array: Texture2DArray,
tile_pixels: Texture2DArray,
allsky_pixels: Texture2DArray,
available_tiles_during_frame: bool,
// allsky rendering mode <=> raytracing on the 12 base tiles
allsky_rendering: bool,
}
// Define a set of textures compatible with the HEALPix tile format and size
fn create_texture_array<F: ImageFormat>(
fn create_hpx_texture_storage(
gl: &WebGlContext,
config: &HiPSConfig,
// The texture image channel definition
channel: ChannelType,
// 256 is a consensus for targetting the maximum GPU architectures. We create a 128 slices to optimize performance
num_tiles: i32,
// The size of the tile
tile_size: i32,
) -> Result<Texture2DArray, JsValue> {
let texture_size = config.get_texture_size();
Texture2DArray::create_empty::<F>(
gl,
texture_size,
texture_size,
// 256 is a consensus for targetting the maximum GPU architectures. We create a 128 slices to optimize performance
128,
&[
(
WebGlRenderingCtx::TEXTURE_MIN_FILTER,
// apply mipmapping
WebGlRenderingCtx::NEAREST_MIPMAP_NEAREST,
),
(
WebGlRenderingCtx::TEXTURE_MAG_FILTER,
WebGlRenderingCtx::NEAREST,
),
// Prevents s-coordinate wrapping (repeating)
(
WebGlRenderingCtx::TEXTURE_WRAP_S,
WebGlRenderingCtx::CLAMP_TO_EDGE,
),
// Prevents t-coordinate wrapping (repeating)
(
WebGlRenderingCtx::TEXTURE_WRAP_T,
WebGlRenderingCtx::CLAMP_TO_EDGE,
),
(
WebGlRenderingCtx::TEXTURE_WRAP_R,
WebGlRenderingCtx::CLAMP_TO_EDGE,
),
],
)
let tex_params = &[
(
WebGlRenderingCtx::TEXTURE_MIN_FILTER,
// apply mipmapping
WebGlRenderingCtx::NEAREST_MIPMAP_NEAREST,
),
(
WebGlRenderingCtx::TEXTURE_MAG_FILTER,
WebGlRenderingCtx::NEAREST,
),
// Prevents s-coordinate wrapping (repeating)
(
WebGlRenderingCtx::TEXTURE_WRAP_S,
WebGlRenderingCtx::CLAMP_TO_EDGE,
),
// Prevents t-coordinate wrapping (repeating)
(
WebGlRenderingCtx::TEXTURE_WRAP_T,
WebGlRenderingCtx::CLAMP_TO_EDGE,
),
(
WebGlRenderingCtx::TEXTURE_WRAP_R,
WebGlRenderingCtx::CLAMP_TO_EDGE,
),
];
match channel {
ChannelType::RGBA8U => Texture2DArray::create_empty::<RGBA8U>(
gl, tile_size, tile_size,
// 256 is a consensus for targetting the maximum GPU architectures. We create a 128 slices to optimize performance
num_tiles, tex_params,
),
ChannelType::RGB8U => Texture2DArray::create_empty::<RGB8U>(
gl, tile_size, tile_size,
// 256 is a consensus for targetting the maximum GPU architectures. We create a 128 slices to optimize performance
num_tiles, tex_params,
),
ChannelType::R32F => Texture2DArray::create_empty::<R32F>(
gl, tile_size, tile_size,
// 256 is a consensus for targetting the maximum GPU architectures. We create a 128 slices to optimize performance
num_tiles, tex_params,
),
#[cfg(feature = "webgl2")]
ChannelType::R8UI => Texture2DArray::create_empty::<R8UI>(
gl, tile_size, tile_size,
// 256 is a consensus for targetting the maximum GPU architectures. We create a 128 slices to optimize performance
num_tiles, tex_params,
),
#[cfg(feature = "webgl2")]
ChannelType::R16I => Texture2DArray::create_empty::<R16I>(
gl, tile_size, tile_size,
// 256 is a consensus for targetting the maximum GPU architectures. We create a 128 slices to optimize performance
num_tiles, tex_params,
),
#[cfg(feature = "webgl2")]
ChannelType::R32I => Texture2DArray::create_empty::<R32I>(
gl, tile_size, tile_size,
// 256 is a consensus for targetting the maximum GPU architectures. We create a 128 slices to optimize performance
num_tiles, tex_params,
),
#[cfg(feature = "webgl2")]
ChannelType::R64F => Texture2DArray::create_empty::<R64F>(
gl, tile_size, tile_size,
// 256 is a consensus for targetting the maximum GPU architectures. We create a 128 slices to optimize performance
num_tiles, tex_params,
),
_ => unimplemented!(),
}
}
impl HiPS2DBuffer {
pub fn push_allsky(&mut self, allsky: Allsky) -> Result<(), JsValue> {
let Allsky {
image,
time_req,
depth_tile,
..
image, time_req, ..
} = allsky;
{
let mutex_locked = image.borrow();
let images = mutex_locked.as_ref().unwrap_abort();
for (idx, image) in images.iter().enumerate() {
self.push(&HEALPixCell(depth_tile, idx as u64), image, time_req)?;
self.push(&HEALPixCell(0, idx as u64), image, time_req)?;
}
}
@@ -204,23 +246,15 @@ impl HiPS2DBuffer {
// For that purpose, we first need to verify that its
// texture ancestor exists and then, it it contains the tile
pub fn contains_tile(&self, cell: &HEALPixCell) -> bool {
let dd = self.config.delta_depth();
let texture_cell = cell.get_texture_cell(dd);
let tex_cell_is_root = texture_cell.is_root();
if tex_cell_is_root {
let HEALPixCell(_, idx) = texture_cell;
self.base_textures[idx as usize].contains_tile(cell)
let cell_is_root = cell.is_root();
if cell_is_root {
self.base_textures[cell.idx() as usize].is_on_gpu()
} else if let Some(texture) = self.get(cell) {
// The texture is present in the buffer
// We must check whether it contains the tile
texture.is_on_gpu()
} else {
if let Some(texture) = self.get(&texture_cell) {
// The texture is present in the buffer
// We must check whether it contains the tile
texture.contains_tile(cell)
} else {
// The texture in which cell should be is not present
false
}
false
}
}
@@ -237,17 +271,14 @@ impl HiPS2DBuffer {
pub fn update_priority(&mut self, cell: &HEALPixCell /*, new_fov_cell: bool*/) {
debug_assert!(self.contains_tile(cell));
let dd = self.config.delta_depth();
// Get the texture cell in which the tile has to be
let texture_cell = cell.get_texture_cell(dd);
if texture_cell.is_root() {
if cell.is_root() {
return;
}
let texture = self
.textures
.get_mut(&texture_cell)
.get_mut(cell)
.expect("Texture cell has not been found while the buffer contains one of its tile!");
// Reset the time the tile has been received if it is a new cell present in the fov
//if new_fov_cell {
@@ -272,16 +303,43 @@ impl HiPS2DBuffer {
image: I,
time_request: Time,
) -> Result<(), JsValue> {
if !self.contains_tile(cell) {
let dd = self.config.delta_depth();
// Get the texture cell in which the tile has to be
let tex_cell = cell.get_texture_cell(dd);
let cell_is_root = cell.is_root();
let tex_cell_is_root = tex_cell.is_root();
if !tex_cell_is_root && !self.textures.contains_key(&tex_cell) {
if cell_is_root {
let tile_size = image.get_size().0;
let is_tile = tile_size == self.config.get_tile_size() as u32;
let texture = &mut self.base_textures[cell.idx() as usize];
if is_tile {
texture.copy_to_gpu(
cell, // The tile cell
&image,
&self.tile_pixels,
)?;
}
let is_allsky_tile = tile_size == self.config.allsky_tile_size() as u32;
if is_allsky_tile {
texture.copy_to_gpu(
cell, // The tile cell
&image,
&self.allsky_pixels,
)?;
self.num_root_textures_available += 1;
if self.num_root_textures_available == 12 {
self.allsky_pixels.generate_mipmap()
}
}
self.available_tiles_during_frame = true;
} else {
// Not root cells
if !self.contains_tile(cell) {
// The texture is not among the essential ones
// (i.e. is not a root texture)
let texture = if self.is_heap_full() {
let mut texture = if self.is_heap_full() {
// Pop the oldest requested texture
let oldest_texture = self.heap.pop().unwrap_abort();
// Ensure this is not a base texture
@@ -291,64 +349,84 @@ impl HiPS2DBuffer {
let mut texture = self.textures.remove(&oldest_texture.cell).expect(
"Texture (oldest one) has not been found in the buffer of textures",
);
texture.replace(&tex_cell, time_request);
texture.replace(cell, time_request);
texture
} else {
let idx = NUM_HPX_TILES_DEPTH_ZERO + self.heap.len();
HpxTexture2D::new(&tex_cell, idx as i32, time_request)
HpxTexture2D::new(cell, idx as i32, time_request)
};
texture.copy_to_gpu(
cell, // The tile cell
&image,
&self.tile_pixels,
)?;
// Push it to the buffer
self.heap.push(&texture);
self.textures.insert(*cell, texture);
self.textures.insert(tex_cell, texture);
self.available_tiles_during_frame = true;
}
if tex_cell_is_root {
self.num_root_textures_available += 1;
if self.num_root_textures_available == 12 {
self.texture_2d_array.generate_mipmap()
}
}
// At this point, the texture that should contain the tile
// is in the buffer
// and the tile is not already in any textures of the buffer
// We can safely push it
// First get the texture
let texture = if !tex_cell_is_root {
self.textures
.get_mut(&tex_cell)
.expect("the cell has to be in the tile buffer")
} else {
let HEALPixCell(_, idx) = tex_cell;
&mut self.base_textures[idx as usize]
};
send_to_gpu(
cell,
texture,
image,
&self.texture_2d_array,
&mut self.config,
)?;
texture.append(
cell, // The tile cell
&self.config,
);
self.available_tiles_during_frame = true;
}
Ok(())
}
pub fn get_texture(&self) -> &Texture2DArray {
&self.texture_2d_array
pub(crate) fn read_pixel(
&self,
cell: &HEALPixCell,
dx: f64,
dy: f64,
) -> Result<JsValue, JsValue> {
let value = if let Some(tile) = self.get(cell) {
// Index of the texture in the total set of textures
let tile_idx = tile.idx();
// The size of the global texture containing the tiles
let tile_size = self.config.get_tile_size() as f32;
// Offset in the slice in pixels
let mut pos_tex = Vector3::new(
(dy * (tile_size as f64)) as i32,
(dx * (tile_size as f64)) as i32,
tile_idx,
);
// Offset in the slice in pixels
if self.config.tex_storing_fits {
let uvy = 1.0 - (pos_tex.y as f32 / tile_size);
pos_tex.y = (uvy * tile_size) as i32;
}
let value = self
.tile_pixels
.read_pixel(pos_tex.x, pos_tex.y, pos_tex.z)?;
if self.config.tex_storing_fits {
// scale the value
let f64_v = value
.as_f64()
.ok_or("Error unwraping the pixel read value.")?;
let scale = self.config.scale as f64;
let offset = self.config.offset as f64;
JsValue::from_f64(f64_v * scale + offset)
} else {
value
}
} else {
JsValue::null()
};
Ok(value)
}
pub fn render_allsky(&mut self, flag: bool) {
self.allsky_rendering = flag;
}
}
@@ -357,6 +435,7 @@ impl HpxTileBuffer for HiPS2DBuffer {
fn new(gl: &WebGlContext, config: HiPSConfig) -> Result<Self, JsValue> {
let size = 128 - NUM_HPX_TILES_DEPTH_ZERO;
//let size = 128;
// Ensures there is at least space for the 12
// root textures
//debug_assert!(size >= NUM_HPX_TILES_DEPTH_ZERO);
@@ -379,27 +458,18 @@ impl HpxTileBuffer for HiPS2DBuffer {
HpxTexture2D::new(&HEALPixCell(0, 11), 11, now),
];
let channel = config.get_format().get_channel();
let tile_size = config.get_tile_size();
let tile_pixels = create_hpx_texture_storage(gl, channel, 128, tile_size)?;
let texture_2d_array = match channel {
ChannelType::RGBA32F => unimplemented!(),
ChannelType::RGB32F => unimplemented!(),
ChannelType::RGBA8U => create_texture_array::<RGBA8U>(gl, &config)?,
ChannelType::RGB8U => create_texture_array::<RGB8U>(gl, &config)?,
ChannelType::R32F => create_texture_array::<R32F>(gl, &config)?,
#[cfg(feature = "webgl2")]
ChannelType::R8UI => create_texture_array::<R8UI>(gl, &config)?,
#[cfg(feature = "webgl2")]
ChannelType::R16I => create_texture_array::<R16I>(gl, &config)?,
#[cfg(feature = "webgl2")]
ChannelType::R32I => create_texture_array::<R32I>(gl, &config)?,
#[cfg(feature = "webgl2")]
ChannelType::R64F => create_texture_array::<R64F>(gl, &config)?,
};
let allsky_tile_size = config.allsky_tile_size();
let allsky_pixels = create_hpx_texture_storage(gl, channel, 12, allsky_tile_size)?;
// The root textures have not been loaded
let num_root_textures_available = 0;
let available_tiles_during_frame = false;
let allsky_rendering = false;
Ok(HiPS2DBuffer {
config,
heap,
@@ -408,8 +478,12 @@ impl HpxTileBuffer for HiPS2DBuffer {
num_root_textures_available,
textures,
base_textures,
texture_2d_array,
tile_pixels,
allsky_pixels,
available_tiles_during_frame,
allsky_rendering,
})
}
@@ -417,22 +491,11 @@ impl HpxTileBuffer for HiPS2DBuffer {
self.config.set_image_ext(ext)?;
let channel = self.config.get_format().get_channel();
let tile_size = self.config.get_tile_size();
self.tile_pixels = create_hpx_texture_storage(gl, channel, 128, tile_size)?;
self.texture_2d_array = match channel {
ChannelType::RGBA32F => unimplemented!(),
ChannelType::RGB32F => unimplemented!(),
ChannelType::RGBA8U => create_texture_array::<RGBA8U>(gl, &self.config)?,
ChannelType::RGB8U => create_texture_array::<RGB8U>(gl, &self.config)?,
ChannelType::R32F => create_texture_array::<R32F>(gl, &self.config)?,
#[cfg(feature = "webgl2")]
ChannelType::R8UI => create_texture_array::<R8UI>(gl, &self.config)?,
#[cfg(feature = "webgl2")]
ChannelType::R16I => create_texture_array::<R16I>(gl, &self.config)?,
#[cfg(feature = "webgl2")]
ChannelType::R32I => create_texture_array::<R32I>(gl, &self.config)?,
#[cfg(feature = "webgl2")]
ChannelType::R64F => create_texture_array::<R64F>(gl, &self.config)?,
};
let allsky_tile_size = self.config.allsky_tile_size();
self.allsky_pixels = create_hpx_texture_storage(gl, channel, 12, allsky_tile_size)?;
let now = Time::now();
self.base_textures = [
@@ -473,7 +536,7 @@ impl HpxTileBuffer for HiPS2DBuffer {
// must have been written for the GPU
fn contains(&self, cell: &HEALPixCell) -> bool {
if let Some(t) = self.get(cell) {
t.is_full()
t.is_on_gpu()
} else {
false
}
@@ -498,36 +561,6 @@ impl HpxTileBuffer for HiPS2DBuffer {
}
}
fn send_to_gpu<I: Image>(
cell: &HEALPixCell,
texture: &HpxTexture2D,
image: I,
texture_array: &Texture2DArray,
cfg: &mut HiPSConfig,
) -> Result<(), JsValue> {
// Index of the texture in the total set of textures
let texture_idx = texture.idx();
// Index of the slice of textures
let idx_slice = texture_idx;
// Row and column indexes of the tile in its texture
let delta_depth = cfg.delta_depth();
let (idx_col_in_tex, idx_row_in_tex) = cell.get_offset_in_texture_cell(delta_depth);
// The size of a tile in its texture
let tile_size = cfg.get_tile_size();
// Offset in the slice in pixels
let offset = Vector3::new(
(idx_row_in_tex as i32) * tile_size,
(idx_col_in_tex as i32) * tile_size,
idx_slice,
);
image.insert_into_3d_texture(texture_array, &offset)?;
Ok(())
}
impl SendUniforms for HiPS2DBuffer {
// Send only the allsky textures
fn attach_uniforms<'a>(&self, shader: &'a ShaderBound<'a>) -> &'a ShaderBound<'a> {
@@ -537,21 +570,25 @@ impl SendUniforms for HiPS2DBuffer {
let texture_uniforms = TextureUniforms::new(texture, idx as i32);
shader.attach_uniforms_from(&texture_uniforms);
}*/
let shader = shader.attach_uniforms_from(&self.config);
//if self.raytracing {
for idx in 0..NUM_HPX_TILES_DEPTH_ZERO {
let cell = HEALPixCell(0, idx as u64);
if self.allsky_rendering {
for idx in 0..NUM_HPX_TILES_DEPTH_ZERO {
let cell = HEALPixCell(0, idx as u64);
let texture = self.get(&cell).unwrap();
let texture_uniforms = HpxTexture2DUniforms::new(texture, idx as i32);
shader.attach_uniforms_from(&texture_uniforms);
let texture = self.get(&cell).unwrap();
let texture_uniforms = HpxTexture2DUniforms::new(texture, idx as i32);
shader.attach_uniforms_from(&texture_uniforms);
}
shader
.attach_uniform("tex", &self.allsky_pixels)
.attach_uniform("num_slices", &12);
} else {
shader
.attach_uniform("tex", &self.tile_pixels)
.attach_uniform("num_slices", &self.tile_pixels.num_slices);
}
//}
let shader = shader
.attach_uniforms_from(&self.config)
.attach_uniform("tex", &self.texture_2d_array)
.attach_uniform("num_slices", &(self.texture_2d_array.num_slices as i32));
shader
}

View File

@@ -2,16 +2,16 @@ pub mod buffer;
pub mod texture;
use crate::app::BLENDING_ANIM_DURATION;
use crate::downloader::query;
use crate::math::angle::ToAngle;
use crate::renderable::hips::HpxTile;
use al_api::hips::ImageExt;
use al_api::hips::ImageMetadata;
use al_core::colormap::Colormap;
use al_core::colormap::Colormaps;
use al_core::image::format::ChannelType;
use cgmath::Vector3;
use cgmath::Vector2;
use crate::math::angle::ToAngle;
use crate::downloader::query;
use cgmath::Vector3;
use al_core::image::Image;
@@ -35,8 +35,8 @@ use crate::healpix::{cell::HEALPixCell, coverage::HEALPixCoverage};
use crate::time::Time;
use super::config::HiPSConfig;
use std::collections::HashSet;
use crate::math::lonlat::LonLat;
use std::collections::HashSet;
// Recursively compute the number of subdivision needed for a cell
// to not be too much skewed
@@ -83,7 +83,10 @@ impl<'a> HpxDrawData<'a> {
let start_time = BLENDING_ANIM_DURATION.as_millis();
Self {
cell, uv_0, uv_1, start_time
cell,
uv_0,
uv_1,
start_time,
}
}
}
@@ -110,29 +113,27 @@ pub fn get_raster_shader<'a>(
"hips_rasterizer_color_to_colormap.frag",
)
}
} else if config.tex_storing_unsigned_int {
crate::shader::get_shader(
gl,
shaders,
"hips_rasterizer_raster.vert",
"hips_rasterizer_grayscale_to_colormap_u.frag",
)
} else if config.tex_storing_integers {
crate::shader::get_shader(
gl,
shaders,
"hips_rasterizer_raster.vert",
"hips_rasterizer_grayscale_to_colormap_i.frag",
)
} else {
if config.tex_storing_unsigned_int {
crate::shader::get_shader(
gl,
shaders,
"hips_rasterizer_raster.vert",
"hips_rasterizer_grayscale_to_colormap_u.frag",
)
} else if config.tex_storing_integers {
crate::shader::get_shader(
gl,
shaders,
"hips_rasterizer_raster.vert",
"hips_rasterizer_grayscale_to_colormap_i.frag",
)
} else {
crate::shader::get_shader(
gl,
shaders,
"hips_rasterizer_raster.vert",
"hips_rasterizer_grayscale_to_colormap.frag",
)
}
crate::shader::get_shader(
gl,
shaders,
"hips_rasterizer_raster.vert",
"hips_rasterizer_grayscale_to_colormap.frag",
)
}
}
@@ -159,29 +160,27 @@ pub fn get_raytracer_shader<'a>(
"hips_raytracer_color_to_colormap.frag",
)
}
} else if config.tex_storing_unsigned_int {
crate::shader::get_shader(
gl,
shaders,
"hips_raytracer_raytracer.vert",
"hips_raytracer_grayscale_to_colormap_u.frag",
)
} else if config.tex_storing_integers {
crate::shader::get_shader(
gl,
shaders,
"hips_raytracer_raytracer.vert",
"hips_raytracer_grayscale_to_colormap_i.frag",
)
} else {
if config.tex_storing_unsigned_int {
crate::shader::get_shader(
gl,
shaders,
"hips_raytracer_raytracer.vert",
"hips_raytracer_grayscale_to_colormap_u.frag",
)
} else if config.tex_storing_integers {
crate::shader::get_shader(
gl,
shaders,
"hips_raytracer_raytracer.vert",
"hips_raytracer_grayscale_to_colormap_i.frag",
)
} else {
crate::shader::get_shader(
gl,
shaders,
"hips_raytracer_raytracer.vert",
"hips_raytracer_grayscale_to_colormap.frag",
)
}
crate::shader::get_shader(
gl,
shaders,
"hips_raytracer_raytracer.vert",
"hips_raytracer_grayscale_to_colormap.frag",
)
}
}
@@ -305,46 +304,20 @@ impl HiPS2D {
pub fn look_for_new_tiles<'a>(
&'a mut self,
camera: &'a CameraViewPort,
proj: &ProjectionType,
) -> Option<impl Iterator<Item = HEALPixCell> + 'a> {
// do not add tiles if the view is already at depth 0
let cfg = self.get_config();
let mut depth_tile = (camera.get_texture_depth() + cfg.delta_depth())
let depth_tile = camera
.get_tile_depth()
.min(cfg.get_max_depth_tile())
.max(cfg.get_min_depth_tile());
let dd = cfg.delta_depth();
//let min_depth_tile = self.get_min_depth_tile();
//let delta_depth = self.get_config().delta_depth();
//let min_bound_depth = min_depth_tile.max(delta_depth);
// do not ask to query tiles that:
// * either do not exist because < to min_depth_tile
// * either are part of a base tile already handled i.e. tiles < delta_depth
//console_log(depth_tile);
//console_log(min_bound_depth);
//if depth_tile >= min_bound_depth {
//let depth_tile = depth_tile.max(min_bound_depth);
let survey_frame = cfg.get_frame();
let mut already_considered_tiles = HashSet::new();
// raytracer is rendering and the shader only renders HPX texture cells of depth 0
if camera.is_raytracing(proj) {
depth_tile = 0;
}
let tile_cells_iter = camera
.get_hpx_cells(depth_tile, survey_frame)
//.flat_map(move |cell| {
// let texture_cell = cell.get_texture_cell(delta_depth);
// texture_cell.get_tile_cells(delta_depth)
//})
.into_iter()
.flat_map(move |tile_cell| {
let tex_cell = tile_cell.get_texture_cell(dd);
tex_cell.get_tile_cells(dd)
})
.filter(move |tile_cell| {
if already_considered_tiles.contains(tile_cell) {
return false;
@@ -392,7 +365,7 @@ impl HiPS2D {
let cfg = self.get_config();
// Get the coo system transformation matrix
let hips_frame = cfg.get_frame();
let depth = camera.get_texture_depth().min(cfg.get_max_depth_texture());
let depth = camera.get_tile_depth().min(cfg.get_max_depth_tile());
let hpx_cells_in_view = camera.get_hpx_cells(depth, hips_frame);
let new_cells = if hpx_cells_in_view.len() != self.hpx_cells_in_view.len() {
@@ -433,7 +406,7 @@ impl HiPS2D {
x: f64,
y: f64,
camera: &CameraViewPort,
proj: &ProjectionType
proj: &ProjectionType,
) -> Result<JsValue, JsValue> {
if let Some(xyz) = proj.screen_to_model_space(&Vector2::new(x, y), camera) {
// 1. Convert it to the hips frame system
@@ -441,62 +414,16 @@ impl HiPS2D {
let camera_frame = camera.get_coo_system();
let hips_frame = cfg.get_frame();
let lonlat =
crate::coosys::apply_coo_system(camera_frame, hips_frame, &xyz).lonlat();
let lonlat = crate::coosys::apply_coo_system(camera_frame, hips_frame, &xyz).lonlat();
// Get the array of textures from that survey
let depth = camera.get_texture_depth().min(cfg.get_max_depth_texture());
let depth = camera.get_tile_depth().min(cfg.get_max_depth_tile());
// compute the tex
let (pix, dx, dy) = crate::healpix::utils::hash_with_dxdy(depth, &lonlat);
let texture_cell = HEALPixCell(depth, pix);
let tile_cell = HEALPixCell(depth, pix);
let value = if let Some(texture) = self.buffer.get(&texture_cell) {
// Index of the texture in the total set of textures
let texture_idx = texture.idx();
// The size of the global texture containing the tiles
let texture_size = cfg.get_texture_size();
// Offset in the slice in pixels
let mut pos_tex = Vector3::new(
(dy * (texture_size as f64)) as i32,
(dx * (texture_size as f64)) as i32,
texture_idx,
);
// Offset in the slice in pixels
if cfg.tex_storing_fits {
let texture_size = cfg.get_texture_size() as f32;
let mut uvy = pos_tex.y as f32 / texture_size;
uvy = cfg.size_tile_uv + 2.0 * cfg.size_tile_uv * (uvy / cfg.size_tile_uv).floor()
- uvy;
pos_tex.y = (uvy * texture_size) as i32;
}
let mut value = self
.buffer
.get_texture()
.read_pixel(pos_tex.x, pos_tex.y, pos_tex.z)?;
if cfg.tex_storing_fits {
// scale the value
let f64_v = value
.as_f64()
.ok_or_else(|| "Error unwraping the pixel read value.")?;
let scale = cfg.scale as f64;
let offset = cfg.offset as f64;
value = JsValue::from_f64(f64_v * scale + offset);
}
value
} else {
JsValue::null()
};
Ok(value)
self.buffer.read_pixel(&tile_cell, dx, dy)
} else {
Err(JsValue::from_str("Out of projection"))
}
@@ -518,16 +445,19 @@ impl HiPS2D {
// Define a global level of subdivisions for all the healpix tile cells in the view
// This should prevent seeing many holes
// We compute it from the first cell in the view but it might be an under/over estimate for the other cells in the view
let num_sub = self.hpx_cells_in_view.iter()
let num_sub = self
.hpx_cells_in_view
.iter()
.map(|cell| super::subdivide::num_hpx_subdivision(cell, camera, projection))
.max().unwrap();
.max()
.unwrap();
//let num_sub =
// super::subdivide::num_hpx_subdivision(&self.hpx_cells_in_view[0], camera, projection);
for cell in &self.hpx_cells_in_view {
// filter textures that are not in the moc
let cell_in_cov = if let Some(moc) = self.footprint_moc.as_ref() {
if moc.intersects_cell(&cell) {
if moc.intersects_cell(cell) {
// Rasterizer does not render tiles that are not in the MOC
// This is not a problem for transparency rendered HiPses (FITS or PNG)
// but JPEG tiles do have black when no pixels data is found
@@ -568,29 +498,20 @@ impl HiPS2D {
} else {
unreachable!()
}
} else {
if let Some(parent_cell) = self.buffer.get_nearest_parent(cell) {
if let Some(ending_cell_in_tex) = self.buffer.get(&parent_cell) {
if let Some(grand_parent_cell) =
self.buffer.get_nearest_parent(&parent_cell)
} else if let Some(parent_cell) = self.buffer.get_nearest_parent(cell) {
if let Some(ending_cell_in_tex) = self.buffer.get(&parent_cell) {
if let Some(grand_parent_cell) =
self.buffer.get_nearest_parent(&parent_cell)
{
if let Some(starting_cell_in_tex) = self.buffer.get(&grand_parent_cell)
{
if let Some(starting_cell_in_tex) =
self.buffer.get(&grand_parent_cell)
{
Some(HpxDrawData::from_texture(
starting_cell_in_tex,
ending_cell_in_tex,
cell,
))
} else {
// no blending
Some(HpxDrawData::from_texture(
ending_cell_in_tex,
ending_cell_in_tex,
cell,
))
}
Some(HpxDrawData::from_texture(
starting_cell_in_tex,
ending_cell_in_tex,
cell,
))
} else {
// no blending
Some(HpxDrawData::from_texture(
ending_cell_in_tex,
ending_cell_in_tex,
@@ -598,16 +519,22 @@ impl HiPS2D {
))
}
} else {
unreachable!()
Some(HpxDrawData::from_texture(
ending_cell_in_tex,
ending_cell_in_tex,
cell,
))
}
} else {
// No ancestor has been found in the buffer to draw.
// We might want to check if the HiPS channel is JPEG to mock a cell that will be drawn in black
if channel == ChannelType::RGB8U {
Some(HpxDrawData::new(cell))
} else {
None
}
unreachable!()
}
} else {
// No ancestor has been found in the buffer to draw.
// We might want to check if the HiPS channel is JPEG to mock a cell that will be drawn in black
if channel == ChannelType::RGB8U {
Some(HpxDrawData::new(cell))
} else {
None
}
}
} else {
@@ -624,15 +551,15 @@ impl HiPS2D {
cell,
uv_0,
uv_1,
start_time
}) = hpx_cell {
start_time,
}) = hpx_cell
{
let d01s = uv_0[TileCorner::BottomRight].x - uv_0[TileCorner::BottomLeft].x;
let d02s = uv_0[TileCorner::TopLeft].y - uv_0[TileCorner::BottomLeft].y;
let d01e = uv_1[TileCorner::BottomRight].x - uv_1[TileCorner::BottomLeft].x;
let d02e = uv_1[TileCorner::TopLeft].y - uv_1[TileCorner::BottomLeft].y;
let sub_cells =
super::subdivide::subdivide_hpx_cell(cell, num_sub, camera);
let sub_cells = super::subdivide::subdivide_hpx_cell(cell, num_sub, camera);
let mut pos = Vec::with_capacity(sub_cells.len() * 4);
@@ -752,7 +679,7 @@ impl HiPS2D {
image: I,
time_request: Time,
) -> Result<(), JsValue> {
self.buffer.push(&cell, image, time_request)
self.buffer.push(cell, image, time_request)
}
pub fn add_allsky(&mut self, allsky: Allsky) -> Result<(), JsValue> {
@@ -771,7 +698,7 @@ impl HiPS2D {
}
pub fn draw(
&self,
&mut self,
shaders: &mut ShaderManager,
colormaps: &Colormaps,
camera: &CameraViewPort,
@@ -785,10 +712,20 @@ impl HiPS2D {
let hips_frame = hips_cfg.get_frame();
let c = selected_frame.to(hips_frame);
let raytracing = camera.is_raytracing(proj);
let config = self.get_config();
let mut draw_allsky = camera.is_raytracing(proj);
if !draw_allsky {
let tile_size = self.get_config().get_tile_size();
let pixel_p1 =
camera.get_tile_depth() as u32 + crate::math::utils::log_2_unchecked(tile_size);
//self.gl.enable(WebGl2RenderingContext::BLEND);
let tile_size_order3_in_allsky = tile_size.min(64);
let pixel_p2 = 3 + crate::math::utils::log_2_unchecked(tile_size_order3_in_allsky);
draw_allsky = pixel_p1 <= pixel_p2;
}
self.buffer.render_allsky(draw_allsky);
let config = self.get_config();
let ImageMetadata {
color,
@@ -801,10 +738,10 @@ impl HiPS2D {
let cmap = colormaps.get(color.cmap_name.as_ref());
blend_cfg.enable(&self.gl, || {
if raytracing {
if draw_allsky {
let w2v = c * (*camera.get_w2m());
let shader = get_raytracer_shader(cmap, &self.gl, shaders, &config)?;
let shader = get_raytracer_shader(cmap, &self.gl, shaders, config)?;
let shader = shader.bind(&self.gl);
shader
@@ -815,7 +752,14 @@ impl HiPS2D {
.attach_uniforms_from(color)
.attach_uniform("model", &w2v)
.attach_uniform("current_time", &utils::get_current_time())
.attach_uniform("no_tile_color", &(if config.get_format().get_channel() == ChannelType::RGB8U { Vector4::new(0.0, 0.0, 0.0, 1.0) } else { Vector4::new(0.0, 0.0, 0.0, 0.0) }))
.attach_uniform(
"no_tile_color",
&(if config.get_format().get_channel() == ChannelType::RGB8U {
Vector4::new(0.0, 0.0, 0.0, 1.0)
} else {
Vector4::new(0.0, 0.0, 0.0, 0.0)
}),
)
.attach_uniform("opacity", opacity)
.attach_uniforms_from(colormaps);
@@ -835,7 +779,7 @@ impl HiPS2D {
// - The UVs are changed if:
// * new cells are added/removed (because new cells are added)
// * there are new available tiles for the GPU
let shader = get_raster_shader(cmap, &self.gl, shaders, &config)?.bind(&self.gl);
let shader = get_raster_shader(cmap, &self.gl, shaders, config)?.bind(&self.gl);
shader
.attach_uniforms_from(&self.buffer)

View File

@@ -1,13 +1,13 @@
use crate::Vector3;
use crate::{healpix::cell::HEALPixCell, time::Time};
use std::collections::HashSet;
use al_core::image::Image;
use al_core::Texture2DArray;
use wasm_bindgen::JsValue;
pub struct HpxTexture2D {
texture_cell: HEALPixCell,
tile_cell: HEALPixCell,
// Precomputed uniq number
uniq: i32,
// The cells located in the Texture
tiles: HashSet<HEALPixCell>,
// Position of the texture in the buffer
idx: i32,
// The time the texture has been received
@@ -25,44 +25,30 @@ pub struct HpxTexture2D {
time_request: Time,
// Full flag telling the texture has been filled
full: bool,
// Num tiles written for the gpu
num_tiles_written: usize,
// Flag telling whether the texture is available
// for drawing
//missing: bool,
copied_to_gpu: bool,
}
use crate::renderable::hips::config::HiPSConfig;
use crate::renderable::hips::HpxTile;
impl HpxTexture2D {
pub fn new(cell: &HEALPixCell, idx: i32, time_request: Time) -> Self {
let tiles = HashSet::new();
let start_time = None;
let full = false;
let texture_cell = *cell;
let uniq = texture_cell.uniq();
//let missing = true;
let num_tiles_written = 0;
let copied_to_gpu = false;
let tile_cell = *cell;
let uniq = cell.uniq();
Self {
texture_cell,
tile_cell,
uniq,
time_request,
tiles,
idx,
start_time,
full,
num_tiles_written,
copied_to_gpu,
}
}
pub fn is_full(&self) -> bool {
self.full
pub fn is_on_gpu(&self) -> bool {
self.copied_to_gpu
}
pub fn idx(&self) -> i32 {
@@ -70,61 +56,34 @@ impl HpxTexture2D {
}
// Setter
pub fn replace(&mut self, texture_cell: &HEALPixCell, time_request: Time) {
pub fn replace(&mut self, tile_cell: &HEALPixCell, time_request: Time) {
// Cancel the tasks copying the tiles contained in the texture
// which have not yet been completed.
//self.clear_tasks_in_progress(config, exec);
self.texture_cell = *texture_cell;
self.uniq = texture_cell.uniq();
self.full = false;
self.tile_cell = *tile_cell;
self.uniq = tile_cell.uniq();
self.copied_to_gpu = false;
self.start_time = None;
self.time_request = time_request;
self.tiles.clear();
//self.missing = true;
self.num_tiles_written = 0;
}
// Cell must be contained in the texture
pub fn contains_tile(&self, tile_cell: &HEALPixCell) -> bool {
self.is_full() || self.tiles.contains(tile_cell)
}
// Panic if cell is not contained in the texture
// Do nothing if the texture is full
// Return true if the tile is newly added
pub fn append(&mut self, cell: &HEALPixCell, cfg: &HiPSConfig) {
let texture_cell = cell.get_texture_cell(cfg.delta_depth());
debug_assert!(texture_cell == self.texture_cell);
debug_assert!(!self.full);
pub fn copy_to_gpu<I: Image>(
&mut self,
cell: &HEALPixCell,
image: &I,
gpu_texture: &Texture2DArray,
) -> Result<(), JsValue> {
debug_assert!(*cell == self.tile_cell);
debug_assert!(!self.copied_to_gpu);
//self.missing &= missing;
//self.start_time = Some(Time::now());
//self.full = true;
let num_tiles_per_texture = cfg.num_tiles_per_texture();
let c = *cell;
self.copied_to_gpu = true;
self.start_time = Some(Time::now());
if c == texture_cell {
self.num_tiles_written = num_tiles_per_texture;
self.full = true;
self.start_time = Some(Time::now());
} else {
// Sub-tile appending. This code is called for tile size is < 512
// Cell has the good ancestor for this texture
let new_tile = self.tiles.insert(c);
// Ensures the tile was not already present in the buffer
// This is the case because already contained cells do not
// lead to new requests
debug_assert!(new_tile);
self.num_tiles_written += 1;
if self.num_tiles_written == num_tiles_per_texture {
// The texture is full and available
self.full = true;
self.start_time = Some(Time::now());
}
}
image.insert_into_3d_texture(gpu_texture, &Vector3::new(0, 0, self.idx()))
}
}
@@ -132,7 +91,7 @@ impl HpxTile for HpxTexture2D {
// Getter
// Returns the current time if the texture is not full
fn start_time(&self) -> Time {
if self.is_full() {
if self.is_on_gpu() {
self.start_time.unwrap_abort()
} else {
Time::now()
@@ -144,20 +103,20 @@ impl HpxTile for HpxTexture2D {
}
fn cell(&self) -> &HEALPixCell {
&self.texture_cell
&self.tile_cell
}
}
use std::cmp::Ordering;
impl PartialOrd for HpxTexture2D {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.uniq.partial_cmp(&other.uniq)
Some(self.cmp(other))
}
}
use crate::Abort;
impl Ord for HpxTexture2D {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap_abort()
self.uniq.cmp(&other.uniq)
}
}
@@ -181,7 +140,7 @@ impl<'a> HpxTexture2DUniforms<'a> {
}
use al_core::shader::{SendUniforms, ShaderBound};
impl<'a> SendUniforms for HpxTexture2DUniforms<'a> {
impl SendUniforms for HpxTexture2DUniforms<'_> {
// Info: These uniforms are used for raytracing drawing mode only
fn attach_uniforms<'b>(&self, shader: &'b ShaderBound<'b>) -> &'b ShaderBound<'b> {
shader
@@ -195,9 +154,9 @@ impl<'a> SendUniforms for HpxTexture2DUniforms<'a> {
// This is useful for FITS tiles only because:
// - for JPEG, missing tiles are inserted in the buffer and black is drawn
// - for PNG, tiles are not inserted but default color chosen is fully transparent (might be vec4(0.0, 0.0, 0.0, 0.0))
//
//
// Therefore for FITS files we must indicate GPU which base tiles are missing so that we draw fully transparent pixels
&((!self.texture.full as u8) as f32),
&((!self.texture.copied_to_gpu as u8) as f32),
)
.attach_uniform(
&format!("{}{}", self.name, "start_time"),

View File

@@ -44,7 +44,7 @@ impl HiPS3DBuffer {
let Allsky {
image,
time_req,
depth_tile,
//depth_tile,
channel,
..
} = allsky;
@@ -54,7 +54,7 @@ impl HiPS3DBuffer {
let images = mutex_locked.as_ref().unwrap_abort();
for (idx, image) in images.iter().enumerate() {
self.push(
&HEALPixCell(depth_tile, idx as u64),
&HEALPixCell(0, idx as u64),
image,
time_req,
channel.map(|c| c as u16).unwrap_or(0),
@@ -107,7 +107,7 @@ impl HiPS3DBuffer {
// must have been written for the GPU
pub fn contains_tile(&self, texture_cell: &HEALPixCell, slice: u16) -> bool {
self.get(texture_cell)
.map_or(false, |t| t.contains_slice(slice))
.is_some_and(|t| t.contains_slice(slice))
}
/// Accessors

View File

@@ -66,29 +66,27 @@ pub fn get_raster_shader<'a>(
"hips3d_rasterizer_color_to_colormap.frag",
)
}
} else if config.tex_storing_unsigned_int {
crate::shader::get_shader(
gl,
shaders,
"hips3d_rasterizer_raster.vert",
"hips3d_rasterizer_grayscale_to_colormap_u.frag",
)
} else if config.tex_storing_integers {
crate::shader::get_shader(
gl,
shaders,
"hips3d_rasterizer_raster.vert",
"hips3d_rasterizer_grayscale_to_colormap_i.frag",
)
} else {
if config.tex_storing_unsigned_int {
crate::shader::get_shader(
gl,
shaders,
"hips3d_rasterizer_raster.vert",
"hips3d_rasterizer_grayscale_to_colormap_u.frag",
)
} else if config.tex_storing_integers {
crate::shader::get_shader(
gl,
shaders,
"hips3d_rasterizer_raster.vert",
"hips3d_rasterizer_grayscale_to_colormap_i.frag",
)
} else {
crate::shader::get_shader(
gl,
shaders,
"hips3d_rasterizer_raster.vert",
"hips3d_rasterizer_grayscale_to_colormap.frag",
)
}
crate::shader::get_shader(
gl,
shaders,
"hips3d_rasterizer_raster.vert",
"hips3d_rasterizer_grayscale_to_colormap.frag",
)
}
}
@@ -202,46 +200,26 @@ impl HiPS3D {
pub fn look_for_new_tiles<'a>(
&'a mut self,
camera: &'a CameraViewPort,
proj: &ProjectionType,
//proj: &ProjectionType,
) -> Option<impl Iterator<Item = HEALPixCell> + 'a> {
// do not add tiles if the view is already at depth 0
let cfg = self.get_config();
let mut depth_tile = (camera.get_texture_depth() + cfg.delta_depth())
let depth_tile = camera
.get_tile_depth()
.min(cfg.get_max_depth_tile())
.max(cfg.get_min_depth_tile());
let dd = cfg.delta_depth();
//let min_depth_tile = self.get_min_depth_tile();
//let delta_depth = self.get_config().delta_depth();
//let min_bound_depth = min_depth_tile.max(delta_depth);
// do not ask to query tiles that:
// * either do not exist because < to min_depth_tile
// * either are part of a base tile already handled i.e. tiles < delta_depth
//console_log(depth_tile);
//console_log(min_bound_depth);
//if depth_tile >= min_bound_depth {
//let depth_tile = depth_tile.max(min_bound_depth);
let survey_frame = cfg.get_frame();
let mut already_considered_tiles = HashSet::new();
// raytracer is rendering and the shader only renders HPX texture cells of depth 0
if camera.is_raytracing(proj) {
/*if camera.is_raytracing(proj) {
depth_tile = 0;
}
}*/
let tile_cells_iter = camera
.get_hpx_cells(depth_tile, survey_frame)
//.flat_map(move |cell| {
// let texture_cell = cell.get_texture_cell(delta_depth);
// texture_cell.get_tile_cells(delta_depth)
//})
.into_iter()
.flat_map(move |tile_cell| {
let tex_cell = tile_cell.get_texture_cell(dd);
tex_cell.get_tile_cells(dd)
})
.filter(move |tile_cell| {
if already_considered_tiles.contains(tile_cell) {
return false;
@@ -317,25 +295,26 @@ impl HiPS3D {
// We compute it from the first cell in the view but it might be an under/over estimate for the other cells in the view
//let num_sub = super::subdivide::num_hpx_subdivision(&self.hpx_cells_in_view[0], camera, proj);
let num_sub = self.hpx_cells_in_view.iter()
let num_sub = self
.hpx_cells_in_view
.iter()
.map(|cell| super::subdivide::num_hpx_subdivision(cell, camera, proj))
.max().unwrap();
.max()
.unwrap();
for cell in &self.hpx_cells_in_view {
// filter textures that are not in the moc
let cell = if let Some(moc) = self.footprint_moc.as_ref() {
if moc.intersects_cell(&cell) {
if moc.intersects_cell(cell) {
Some(&cell)
} else if channel == ChannelType::RGB8U {
// Rasterizer does not render tiles that are not in the MOC
// This is not a problem for transparency rendered HiPses (FITS or PNG)
// but JPEG tiles do have black when no pixels data is found
// We therefore must draw in black for the tiles outside the HiPS MOC
Some(&cell)
} else {
if channel == ChannelType::RGB8U {
// Rasterizer does not render tiles that are not in the MOC
// This is not a problem for transparency rendered HiPses (FITS or PNG)
// but JPEG tiles do have black when no pixels data is found
// We therefore must draw in black for the tiles outside the HiPS MOC
Some(&cell)
} else {
None
}
None
}
} else {
Some(&cell)
@@ -347,7 +326,7 @@ impl HiPS3D {
let hpx_cell_texture = if self.buffer.contains_tile(cell, self.slice) {
slice_contained = self.slice;
self.buffer.get(cell)
} else if let Some(next_slice) = self.buffer.find_nearest_slice(&cell, self.slice) {
} else if let Some(next_slice) = self.buffer.find_nearest_slice(cell, self.slice) {
slice_contained = next_slice;
self.buffer.get(cell)
} else if let Some(parent_cell) = self.buffer.get_nearest_parent(cell) {
@@ -363,7 +342,7 @@ impl HiPS3D {
if let Some(texture) = hpx_cell_texture {
self.slice_indices.push(slice_contained as usize);
self.cells.push(texture.cell().clone());
self.cells.push(*texture.cell());
// The slice is sure to be contained so we can unwrap
let hpx_slice_tex = texture.extract_2d_slice_texture(slice_contained).unwrap();
@@ -383,7 +362,12 @@ impl HiPS3D {
let (i, j) = sub_cell.offset_in_parent(cell);
let nside = (1 << (sub_cell.depth() - cell.depth())) as f32;
for ((lon, lat), (di, dj)) in sub_cell.vertices().iter().zip([(0, 0), (1, 0), (1, 1), (0, 1)]) {
for ((lon, lat), (di, dj)) in
sub_cell
.vertices()
.iter()
.zip([(0, 0), (1, 0), (1, 1), (0, 1)])
{
let hj0 = ((j + dj) as f32) / nside;
let hi0 = ((i + di) as f32) / nside;
@@ -403,7 +387,6 @@ impl HiPS3D {
idx + off_indices,
idx + 2 + off_indices,
idx + 1 + off_indices,
idx + off_indices,
idx + 3 + off_indices,
idx + 2 + off_indices,
@@ -431,9 +414,7 @@ impl HiPS3D {
self.num_indices.push(self.idx_vertices.len() - tmp);
// Replace options with an arbitrary vertex
let position_iter = pos
.into_iter()
.flatten();
let position_iter = pos.into_iter().flatten();
self.position.extend(position_iter);
}
}
@@ -467,7 +448,7 @@ impl HiPS3D {
let cfg = self.get_config();
// Get the coo system transformation matrix
let hips_frame = cfg.get_frame();
let depth = camera.get_texture_depth().min(cfg.get_max_depth_texture());
let depth = camera.get_tile_depth().min(cfg.get_max_depth_tile());
let hpx_cells_in_view = camera.get_hpx_cells(depth, hips_frame);
let new_cells = if hpx_cells_in_view.len() != self.hpx_cells_in_view.len() {
@@ -556,7 +537,7 @@ impl HiPS3D {
// * there are new available tiles for the GPU
let mut off_idx = 0;
let shader = get_raster_shader(cmap, &self.gl, shaders, &hips_cfg)?;
let shader = get_raster_shader(cmap, &self.gl, shaders, hips_cfg)?;
for (slice_idx, (cell, num_indices)) in self
.slice_indices
@@ -593,7 +574,7 @@ impl HiPS3D {
(off_idx * std::mem::size_of::<u16>()) as i32,
);
off_idx += (*num_indices) as usize;
off_idx += *num_indices;
Ok(())
})?;
@@ -613,7 +594,7 @@ impl HiPS3D {
time_request: Time,
slice_idx: u16,
) -> Result<(), JsValue> {
self.buffer.push(&cell, image, time_request, slice_idx)
self.buffer.push(cell, image, time_request, slice_idx)
}
pub fn add_allsky(&mut self, allsky: Allsky) -> Result<(), JsValue> {

View File

@@ -46,7 +46,7 @@ impl HpxTexture3D {
pub fn new(tile_cell: HEALPixCell, time_request: Time) -> Self {
let start_time = None;
let uniq = tile_cell.uniq();
let textures = std::iter::repeat(None).take(512).collect();
let textures = std::iter::repeat_n(None, 512).collect();
let blocks = [0; 512];
let block_indices = Vec::new();
Self {
@@ -80,8 +80,8 @@ impl HpxTexture3D {
};
let m1 = (!m2) & !(1 << (31 - slice_idx));
let lb = ((block & m1) >> (32 - slice_idx)) as u32;
let rb = (block & m2) as u32;
let lb = (block & m1) >> (32 - slice_idx);
let rb = block & m2;
let lb_trailing_zeros = (lb.trailing_zeros() as u16).min(slice_idx as u16);
let rb_leading_zeros = (rb.leading_zeros() - slice_idx - 1) as u16;
@@ -166,10 +166,10 @@ impl HpxTexture3D {
let slice_b2 = ((*b_idx_2 << 5) + b2_lz) as u16;
if slice - slice_b1 <= slice_b2 - slice {
// the nearest slice is in b1
Some(slice_b1 as u16)
Some(slice_b1)
} else {
// the nearest slice is in b2
Some(slice_b2 as u16)
Some(slice_b2)
}
}
(None, Some(b_idx_2)) => {
@@ -225,7 +225,7 @@ impl HpxTexture3D {
) -> Result<(), JsValue> {
let block_idx = (slice >> 5) as usize;
let texture = if let Some(texture) = self.textures[block_idx as usize].as_ref() {
let texture = if let Some(texture) = self.textures[block_idx].as_ref() {
texture
} else {
let tile_size = cfg.get_tile_size();
@@ -340,7 +340,7 @@ impl HpxTile for HpxTexture3D {
use std::cmp::Ordering;
impl PartialOrd for HpxTexture3D {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.uniq.partial_cmp(&other.uniq)
Some(self.cmp(other))
}
}
use crate::Abort;
@@ -356,40 +356,3 @@ impl PartialEq for HpxTexture3D {
}
}
impl Eq for HpxTexture3D {}
/*
pub struct TextureUniforms<'a> {
texture: &'a HpxTexture3D,
name: String,
}
impl<'a> TextureUniforms<'a> {
pub fn new(texture: &Texture, idx_texture: i32) -> TextureUniforms {
let name = format!("textures_tiles[{}].", idx_texture);
TextureUniforms { texture, name }
}
}
use al_core::shader::{SendUniforms, ShaderBound};
impl<'a> SendUniforms for TextureUniforms<'a> {
fn attach_uniforms<'b>(&self, shader: &'b ShaderBound<'b>) -> &'b ShaderBound<'b> {
shader
.attach_uniform(&format!("{}{}", self.name, "uniq"), &self.texture.uniq)
.attach_uniform(
&format!("{}{}", self.name, "texture_idx"),
&self.texture.idx,
)
.attach_uniform(
&format!("{}{}", self.name, "empty"),
//&((self.texture.full as u8) as f32),
&0.0,
)
.attach_uniform(
&format!("{}{}", self.name, "start_time"),
&self.texture.start_time(),
);
shader
}
}
*/

View File

@@ -75,20 +75,18 @@ use crate::downloader::query;
use crate::renderable::hips::HiPS::{D2, D3};
use crate::renderable::HiPS3D;
use crate::ProjectionType;
#[allow(clippy::large_enum_variant)]
pub enum HiPS {
D2(HiPS2D),
D3(HiPS3D),
}
impl HiPS {
pub fn look_for_new_tiles(
&mut self,
camera: &CameraViewPort,
proj: &ProjectionType,
) -> Option<Vec<HEALPixCell>> {
pub fn look_for_new_tiles(&mut self, camera: &CameraViewPort) -> Option<Vec<HEALPixCell>> {
match self {
D2(hips) => hips.look_for_new_tiles(camera, proj).map(|it| it.collect()),
D3(hips) => hips.look_for_new_tiles(camera, proj).map(|it| it.collect()),
D2(hips) => hips.look_for_new_tiles(camera).map(|it| it.collect()),
D3(hips) => hips.look_for_new_tiles(camera).map(|it| it.collect()),
}
}
@@ -98,7 +96,7 @@ impl HiPS {
x: f64,
y: f64,
camera: &CameraViewPort,
proj: &ProjectionType
proj: &ProjectionType,
) -> Result<JsValue, JsValue> {
match self {
D2(hips) => hips.read_pixel(x, y, camera, proj),

View File

@@ -162,7 +162,7 @@ impl RayTracer {
&self.vao
}
pub fn draw<'a>(&self, shader: &ShaderBound<'a>) {
pub fn draw(&self, shader: &ShaderBound<'_>) {
#[cfg(feature = "webgl2")]
shader
.bind_vertex_array_object_ref(&self.vao)

View File

@@ -1,8 +1,8 @@
use crate::camera::CameraViewPort;
use crate::math::angle::ToAngle;
use crate::math::projection::ProjectionType;
use crate::math::vector::dist2;
use crate::HEALPixCell;
use crate::math::angle::ToAngle;
const M: f64 = 220.0 * 220.0;
@@ -19,8 +19,8 @@ fn is_too_large(cell: &HEALPixCell, camera: &CameraViewPort, projection: &Projec
if vertices.len() < 4 {
false
} else {
let d1 = dist2(vertices[0].as_ref(), &vertices[2].as_ref());
let d2 = dist2(vertices[1].as_ref(), &vertices[3].as_ref());
let d1 = dist2(vertices[0].as_ref(), vertices[2].as_ref());
let d2 = dist2(vertices[1].as_ref(), vertices[3].as_ref());
d1 > M || d2 > M
}
@@ -80,7 +80,7 @@ pub(crate) fn subdivide_hpx_cell(
camera: &CameraViewPort,
) -> Box<[HEALPixCell]> {
cell.get_children_cells(num_subdivisions)
.map(|child_cell| {
.flat_map(|child_cell| {
/*let cell_depth = child_cell.depth();
// Largest deformation cell among the cells of a specific depth
@@ -104,7 +104,6 @@ pub(crate) fn subdivide_hpx_cell(
let lat = child_cell.center().1;
let c0 = child_cell.ancestor(child_cell.depth()).idx();
let c3 = child_cell.ancestor(child_cell.depth() - 3);
@@ -115,7 +114,8 @@ pub(crate) fn subdivide_hpx_cell(
let (x, y) = child_cell.get_offset_in_texture_cell(child_cell.depth());
let nside = child_cell.nside() as u32;
let collignon_equatorial_frontier = ((0..=3).contains(&c0) || (8..=11).contains(&c0)) && ((x + y) as i32 - (nside as i32) + 1).abs() <= 1;
let collignon_equatorial_frontier = ((0..=3).contains(&c0) || (8..=11).contains(&c0))
&& ((x + y) as i32 - (nside as i32) + 1).abs() <= 1;
// A HEALPix cell would be distorted if
let hpx_num_sub = if camera.get_aperture() >= 15.0_f64.to_radians() &&
@@ -123,26 +123,27 @@ pub(crate) fn subdivide_hpx_cell(
((0..=3).contains(&c0) || (8..=11).contains(&c0)) &&
// neighbors of cells having only 7 neighbors (the most distorted ones)
c3_s
{
// more specific cells needing a subdivision
2
} else if collignon_equatorial_frontier && c3_s {
// on the collignon/equatorial fence and part of a 3 order cell having only 7 neighbors
1
} else if child_cell.is_on_pole() {
// it lies on a pole
1
} else if child_cell.is_on_base_cell_edges() && lat.abs() >= healpix::TRANSITION_LATITUDE {
// it lies on a frontier between base cells and at a high absolute latitude
1
} else {
0
};
{
// more specific cells needing a subdivision
2
} else if collignon_equatorial_frontier && c3_s {
// on the collignon/equatorial fence and part of a 3 order cell having only 7 neighbors
1
} else if child_cell.is_on_pole() {
// it lies on a pole
1
} else if child_cell.is_on_base_cell_edges()
&& lat.abs() >= healpix::TRANSITION_LATITUDE
{
// it lies on a frontier between base cells and at a high absolute latitude
1
} else {
0
};
// Subdivide one more time if the HEALPix cell is distorted
child_cell.get_children_cells(hpx_num_sub)
})
.flatten()
.collect::<Vec<_>>()
.into_boxed_slice()
}

View File

@@ -247,10 +247,10 @@ fn recursive_triangulation(
return;
}
const D_TO_EAST: NormedVector2 = unsafe { NormedVector2::new_unsafe(1.0, 0.0) };
const D_TO_WEST: NormedVector2 = unsafe { NormedVector2::new_unsafe(-1.0, 0.0) };
const D_TO_NORTH: NormedVector2 = unsafe { NormedVector2::new_unsafe(0.0, 1.0) };
const D_TO_SOUTH: NormedVector2 = unsafe { NormedVector2::new_unsafe(0.0, -1.0) };
const D_TO_EAST: NormedVector2 = NormedVector2::new_unsafe(1.0, 0.0);
const D_TO_WEST: NormedVector2 = NormedVector2::new_unsafe(-1.0, 0.0);
const D_TO_NORTH: NormedVector2 = NormedVector2::new_unsafe(0.0, 1.0);
const D_TO_SOUTH: NormedVector2 = NormedVector2::new_unsafe(0.0, -1.0);
match (bl_in, br_in, tr_in, tl_in) {
// 0 VERTEX case

View File

@@ -1,6 +1,10 @@
use std::cmp::Ordering;
use std::ops::Range;
pub fn first_and_last_percent<T>(slice: &mut [T], mut first_percent: i32, mut last_percent: i32) -> Range<T>
pub fn first_and_last_percent<T>(
slice: &mut [T],
mut first_percent: i32,
mut last_percent: i32,
) -> Range<T>
where
T: PartialOrd + Copy,
{
@@ -13,11 +17,13 @@ where
let i2 = ((last_percent as f32) * 0.01 * (n as f32)) as usize;
let min_val = {
let (_, min_val, _) = slice.select_nth_unstable_by(i1, |a, b| a.partial_cmp(b).unwrap_or(Ordering::Greater));
let (_, min_val, _) =
slice.select_nth_unstable_by(i1, |a, b| a.partial_cmp(b).unwrap_or(Ordering::Greater));
*min_val
};
let max_val = {
let (_, max_val, _) = slice.select_nth_unstable_by(i2, |a, b| a.partial_cmp(b).unwrap_or(Ordering::Greater));
let (_, max_val, _) =
slice.select_nth_unstable_by(i2, |a, b| a.partial_cmp(b).unwrap_or(Ordering::Greater));
*max_val
};

View File

@@ -96,8 +96,7 @@ fn get_coord_uv_it(
tex_patch_x
.clone()
.skip(1)
.map(|x1| vec![(x1, 1.0), (x1, 0.0)])
.flatten(),
.flat_map(|x1| vec![(x1, 1.0), (x1, 0.0)]),
)
.chain(std::iter::once((
xmax,
@@ -108,12 +107,12 @@ fn get_coord_uv_it(
},
)));
let mut step_x = (xmin..xmax).step_by(step as usize);
let mut step_x = (xmin..xmax).step_by(step);
let mut cur_step = step_x.next().unwrap();
x_it.clone()
.zip(x_it.clone().skip(1))
.map(move |(x1, x2)| {
.flat_map(move |(x1, x2)| {
let mut xk = vec![x1];
while cur_step < x2.0 {
@@ -130,7 +129,6 @@ fn get_coord_uv_it(
xk
})
.flatten()
.chain(std::iter::once((
xmax,
if xmax % max_tex_size == 0 {
@@ -166,7 +164,7 @@ fn build_range_indices(it: impl Iterator<Item = (u64, f32)> + Clone) -> Vec<Rang
idx_ranges
}
#[allow(dead_code)]
#[allow(clippy::too_many_arguments)]
pub fn vertices(
xy_min: &(f64, f64),
xy_max: &(f64, f64),
@@ -192,7 +190,7 @@ pub fn vertices(
let mut uv = vec![];
let pos = y_it
.map(|(y, uvy)| {
.flat_map(|(y, uvy)| {
x_it.clone().map(move |(x, uvx)| {
let ndc = if let Some(xyz) = wcs.unproj_xyz(&ImgXY::new(x as f64, y as f64)) {
let xyz = crate::coosys::apply_coo_system(
@@ -211,7 +209,6 @@ pub fn vertices(
(ndc, [uvx, uvy])
})
})
.flatten()
.map(|(p, uu)| {
uv.extend_from_slice(&uu);
p
@@ -227,8 +224,7 @@ pub fn vertices(
let patch_indices = build_indices_iter
.flatten()
.map(|indices| [indices.0, indices.1, indices.2])
.flatten()
.flat_map(|indices| [indices.0, indices.1, indices.2])
.collect::<Vec<_>>();
num_indices.push(patch_indices.len() as u32);

View File

@@ -2,8 +2,8 @@ pub mod cuts;
pub mod grid;
pub mod subdivide_texture;
use al_core::webgl_ctx::WebGlRenderingCtx;
use al_core::convert::Cast;
use al_core::webgl_ctx::WebGlRenderingCtx;
use std::fmt::Debug;
use std::marker::Unpin;
use std::vec;
@@ -107,18 +107,16 @@ impl Image {
let offset = offset.unwrap_or(0.0);
let scale = scale.unwrap_or(1.0);
let (textures, cuts) = if width <= max_tex_size as u64 && height <= max_tex_size as u64
{
let (textures, cuts) = if width <= max_tex_size as u64 && height <= max_tex_size as u64 {
max_tex_size_x = width as usize;
max_tex_size_y = height as usize;
// can fit in one texture
let num_pixels_to_read = (width as usize) * (height as usize);
let num_bytes_to_read =
num_pixels_to_read * std::mem::size_of::<F::P>();
let num_bytes_to_read = num_pixels_to_read * std::mem::size_of::<F::P>();
let mut buf = vec![0; num_bytes_to_read];
let _ = reader
reader
.read_exact(&mut buf[..num_bytes_to_read])
.await
.map_err(|e| JsValue::from_str(&format!("{:?}", e)))?;
@@ -127,7 +125,7 @@ impl Image {
unsafe {
let data = std::slice::from_raw_parts_mut(
buf[..].as_mut_ptr() as *mut PixelItem<F>,
(num_pixels_to_read as usize) * F::NUM_CHANNELS,
num_pixels_to_read * F::NUM_CHANNELS,
);
let texture = Texture2D::create_from_raw_pixels::<F>(
@@ -159,9 +157,11 @@ impl Image {
let cuts = match F::CHANNEL_TYPE {
ChannelType::R32F | ChannelType::R64F => {
let pixels = std::slice::from_raw_parts(data.as_ptr() as *const f32, data.len() / 4);
let pixels =
std::slice::from_raw_parts(data.as_ptr() as *const f32, data.len() / 4);
let mut sub_pixels = pixels.iter()
let mut sub_pixels = pixels
.iter()
.step_by(100)
.filter(|pixel| (*pixel).is_finite())
.cloned()
@@ -172,10 +172,11 @@ impl Image {
ChannelType::R8UI | ChannelType::R16I | ChannelType::R32I => {
// BLANK is only valid for those channels/BITPIX (> 0)
if let Some(blank) = blank {
let mut sub_pixels = data.iter()
let mut sub_pixels = data
.iter()
.step_by(100)
.filter_map(|pixel| {
let pixel = <PixelItem::<F> as Cast<f32>>::cast(*pixel);
let pixel = <PixelItem<F> as Cast<f32>>::cast(*pixel);
if pixel != blank {
Some(pixel)
@@ -188,16 +189,17 @@ impl Image {
cuts::first_and_last_percent(&mut sub_pixels, 1, 99)
} else {
// No blank value => we consider all the values
let mut sub_pixels = data.iter()
let mut sub_pixels = data
.iter()
.step_by(100)
.map(|pixel| <PixelItem::<F> as Cast<f32>>::cast(*pixel))
.map(|pixel| <PixelItem<F> as Cast<f32>>::cast(*pixel))
.collect::<Vec<_>>();
cuts::first_and_last_percent(&mut sub_pixels, 1, 99)
}
}
// RGB(A) images
_ => 0.0..1.0
_ => 0.0..1.0,
};
(vec![texture], cuts)
@@ -352,15 +354,9 @@ impl Image {
// Load the FITS file
let header = hdu.get_header();
let scale = header
.get_parsed::<f64>(b"BSCALE ")
.map(|v| v.unwrap());
let offset = header
.get_parsed::<f64>(b"BZERO ")
.map(|v| v.unwrap());
let blank = header
.get_parsed::<f64>(b"BLANK ")
.map(|v| v.unwrap());
let scale = header.get_parsed::<f64>(b"BSCALE ").map(|v| v.unwrap());
let offset = header.get_parsed::<f64>(b"BZERO ").map(|v| v.unwrap());
let blank = header.get_parsed::<f64>(b"BLANK ").map(|v| v.unwrap());
// Create a WCS from a specific header unit
let wcs = WCS::from_fits_header(header)
@@ -647,8 +643,7 @@ impl Image {
let texture = &self.textures[idx_tex];
let num_indices = self.num_indices[idx] as i32;
let shader_bound = shader
.bind(&self.gl);
let shader_bound = shader.bind(&self.gl);
shader_bound
.attach_uniforms_from(colormaps)

View File

@@ -14,7 +14,7 @@ use std::ops::Range;
use al_core::convert::Cast;
type PixelItem<F> = <<F as ImageFormat>::P as Pixel>::Item;
pub async fn crop_image<'a, F, R>(
pub async fn crop_image<F, R>(
gl: &WebGlContext,
width: u64,
height: u64,
@@ -119,7 +119,10 @@ where
match F::CHANNEL_TYPE {
ChannelType::R32F | ChannelType::R64F => {
let pixels = std::slice::from_raw_parts(data.as_ptr() as *const f32, data.len() / 4);
let pixels = std::slice::from_raw_parts(
data.as_ptr() as *const f32,
data.len() / 4,
);
for i in (0..width).step_by(step_cut) {
if (xmin..(xmin + num_pixels_to_read)).contains(&i) {
@@ -130,14 +133,14 @@ where
}
}
}
},
}
ChannelType::R8UI | ChannelType::R16I | ChannelType::R32I => {
if let Some(blank) = blank {
for i in (0..width).step_by(step_cut) {
if (xmin..(xmin + num_pixels_to_read)).contains(&i) {
let j = (i - xmin) as usize;
let pixel = <PixelItem::<F> as Cast<f32>>::cast(data[j]);
let pixel = <PixelItem<F> as Cast<f32>>::cast(data[j]);
if pixel != blank {
sub_pixels.push(pixel);
@@ -148,14 +151,14 @@ where
for i in (0..width).step_by(step_cut) {
if (xmin..(xmin + num_pixels_to_read)).contains(&i) {
let j = (i - xmin) as usize;
let pixel = <PixelItem::<F> as Cast<f32>>::cast(data[j]);
sub_pixels.push(pixel);
let pixel = <PixelItem<F> as Cast<f32>>::cast(data[j]);
sub_pixels.push(pixel);
}
}
}
},
// colored pixels
}
// colored pixels
_ => (),
}
}
@@ -163,7 +166,7 @@ where
F::view(data)
};
(&mut tex_chunks[id_t as usize])
tex_chunks[id_t as usize]
.bind()
.tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_array_buffer_view(
0,

View File

@@ -70,7 +70,10 @@ fn sub_valid_domain(
while crate::math::vector::angle3(&vv, &vi).to_radians() > d_alpha {
let vm = (vv + vi).normalize();
// check whether is it defined or not
if let Some(_) = projection.model_to_normalized_device_space(&vm, camera) {
if projection
.model_to_normalized_device_space(&vm, camera)
.is_some()
{
vv = vm;
} else {
vi = vm;
@@ -89,8 +92,8 @@ fn project_line(
projection: &ProjectionType,
iter: usize,
) -> bool {
let p1 = projection.model_to_normalized_device_space(&v1, camera);
let p2 = projection.model_to_normalized_device_space(&v2, camera);
let p1 = projection.model_to_normalized_device_space(v1, camera);
let p2 = projection.model_to_normalized_device_space(v2, camera);
if iter < MAX_ITERATION {
// Project them. We are always facing the camera
@@ -159,6 +162,7 @@ fn project_line(
}
}
#[allow(clippy::too_many_arguments)]
fn subdivide(
vertices: &mut Vec<XYNDC<f64>>,
v1: &XYZModel<f64>,

Some files were not shown because too many files have changed in this diff Show More