wip: hips3d struct and hips enum

This commit is contained in:
Matthieu Baumann
2024-10-17 17:17:46 +02:00
committed by Matthieu Baumann
parent 8ca394459d
commit 1e51a6e17e
34 changed files with 1994 additions and 853 deletions

View File

@@ -0,0 +1,52 @@
<!doctype html>
<html>
<head>
</head>
<body>
<div id="aladin-lite-div" style="width: 768px; height: 512px"></div>
<script>let aladin; let hips;</script>
<script type="module">
import A from '../src/js/A.js';
A.init.then(() => {
aladin = A.aladin(
'#aladin-lite-div',
{
showSimbadPointerControl: true,
projection: 'AIT', // set a projection
fov: 360, // initial field of view in degrees
target: '169.58868 +45.74914', // initial target
cooFrame: 'icrs', // set galactic frame
reticleColor: '#ff89ff', // change reticle color
reticleSize: 64, // change reticle size
showContextMenu: true,
showFrame: true,
showZoomControl:true,
showSettingsControl:true,
showCooGrid: true,
fullScreen: true,
samp: true,
}
);
hips = aladin.newImageSurvey("https://alasky.cds.unistra.fr/CALIFA/V500DR2/");
aladin.setImageLayer(hips)
/*let id;
aladin.on("zoomChanged", () => {
if (id)
clearTimeout(id);
id = setTimeout(() => {
console.log("wheel stopped, new cone search here")
}, 500);
})*/
});
</script>
<style>
.aladin-cat-browser-box {
width: 600px;
}
</style>
</body>
</html>

View File

@@ -50,13 +50,11 @@ optional = true
[dependencies.healpix]
package = "cdshealpix"
git = "https://github.com/cds-astro/cds-healpix-rust"
branch = "master"
version = "0.7.0"
[dependencies.moclib]
package = "moc"
git = "https://github.com/cds-astro/cds-moc-rust"
branch = "main"
version = "0.17.0"
[dependencies.serde]
version = "^1.0.183"

View File

@@ -19,7 +19,7 @@ where
}
}
}
use crate::texture::{Tex3D, Texture2DArray};
use crate::texture::Tex3D;
use wasm_bindgen::JsValue;
impl<F> Image for Bitmap<F>
where

View File

@@ -19,7 +19,7 @@ where
use crate::image::format::ImageFormat;
use crate::image::Image;
use crate::texture::{Tex3D, Texture2DArray};
use crate::texture::Tex3D;
use cgmath::Vector3;
use wasm_bindgen::JsValue;
impl<F> Image for Canvas<F>

View File

@@ -116,7 +116,6 @@ impl<'a> Fits<'a> {
}
}*/
use crate::Texture2DArray;
use crate::{image::Image, texture::Tex3D};
impl Image for Fits<'_> {
fn insert_into_3d_texture<T: Tex3D>(

View File

@@ -2,6 +2,7 @@ use crate::image::format::ImageFormat;
use web_sys::HtmlCanvasElement;
use web_sys::WebGlTexture;
use crate::texture::pixel::Pixel;
use crate::texture::Texture2DMeta;
use crate::webgl_ctx::WebGlContext;
use crate::webgl_ctx::WebGlRenderingCtx;
@@ -80,6 +81,89 @@ impl Texture2DArray {
.active_texture(WebGlRenderingCtx::TEXTURE0 + idx_tex_unit as u32);
self
}
pub fn read_pixel(&self, x: i32, y: i32, slice_idx: i32) -> Result<JsValue, JsValue> {
// Create and bind the framebuffer
let reader = self.gl.create_framebuffer();
self.gl
.bind_framebuffer(WebGlRenderingCtx::FRAMEBUFFER, reader.as_ref());
// Attach the texture as the first color attachment
self.gl.framebuffer_texture_layer(
WebGlRenderingCtx::READ_FRAMEBUFFER,
WebGlRenderingCtx::COLOR_ATTACHMENT0,
self.texture.as_ref(),
0,
slice_idx,
);
let status = self
.gl
.check_framebuffer_status(WebGlRenderingCtx::FRAMEBUFFER);
if status != WebGlRenderingCtx::FRAMEBUFFER_COMPLETE {
// Unbind the framebuffer
self.gl
.bind_framebuffer(WebGlRenderingCtx::FRAMEBUFFER, None);
// Delete the framebuffer
self.gl.delete_framebuffer(reader.as_ref());
Err(JsValue::from_str("incomplete framebuffer"))
} else {
// set the viewport as the FBO won't be the same dimension as the screen
let metadata = self.metadata.as_ref().unwrap_abort().borrow();
self.gl
.viewport(0, 0, metadata.width as i32, metadata.height as i32);
#[cfg(feature = "webgl2")]
let value = match (metadata.format, metadata.type_) {
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::UNSIGNED_BYTE) => {
let p = <[u8; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::SHORT) => {
let p = <[i16; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::INT) => {
let p = <[i32; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
(WebGlRenderingCtx::RED, WebGlRenderingCtx::FLOAT) => {
let p = <[f32; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
(WebGlRenderingCtx::RGB, WebGlRenderingCtx::UNSIGNED_BYTE) => {
let p = <[u8; 3]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p)?)
}
(WebGlRenderingCtx::RGBA, WebGlRenderingCtx::UNSIGNED_BYTE) => {
let p = <[u8; 4]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p)?)
}
_ => Err(JsValue::from_str(
"Pixel retrieval not implemented for that texture format.",
)),
};
// Unbind the framebuffer
self.gl
.bind_framebuffer(WebGlRenderingCtx::FRAMEBUFFER, None);
// Delete the framebuffer
self.gl.delete_framebuffer(reader.as_ref());
// set the viewport as the FBO won't be the same dimension as the screen
let canvas = self
.gl
.canvas()
.unwrap_abort()
.dyn_into::<web_sys::HtmlCanvasElement>()
.unwrap_abort();
self.gl
.viewport(0, 0, canvas.width() as i32, canvas.height() as i32);
value
}
}
}
impl Drop for Texture2DArray {

View File

@@ -328,7 +328,7 @@ impl Texture2D {
// set the viewport as the FBO won't be the same dimension as the screen
let metadata = self.metadata.as_ref().unwrap_abort().borrow();
self.gl
.viewport(x, y, metadata.width as i32, metadata.height as i32);
.viewport(0, 0, metadata.width as i32, metadata.height as i32);
#[cfg(feature = "webgl2")]
let value = match (metadata.format, metadata.type_) {

View File

@@ -1,6 +1,7 @@
use crate::renderable::ImageLayer;
use crate::tile_fetcher::HiPSLocalFiles;
use crate::renderable::hips::HiPS;
use crate::{
//async_task::{BuildCatalogIndex, ParseTableTask, TaskExecutor, TaskResult, TaskType},
camera::CameraViewPort,
@@ -19,7 +20,6 @@ use crate::{
tile_fetcher::TileFetcherQueue,
time::DeltaTime,
};
use al_core::log::console_log;
use wcs::WCS;
use wasm_bindgen::prelude::*;
@@ -28,7 +28,6 @@ use al_core::colormap::{Colormap, Colormaps};
use al_core::WebGlContext;
use super::coosys;
use crate::Abort;
use al_api::{
coo_system::CooSystem,
grid::GridCfg,
@@ -140,7 +139,7 @@ impl App {
//gl.enable(WebGl2RenderingContext::SCISSOR_TEST);
//gl.enable(WebGl2RenderingContext::CULL_FACE);
//gl.cull_face(WebGl2RenderingContext::BACK);
gl.cull_face(WebGl2RenderingContext::BACK);
//gl.enable(WebGl2RenderingContext::CULL_FACE);
// The tile buffer responsible for the tile requests
@@ -153,7 +152,7 @@ impl App {
FrameBufferObject::new(&gl, screen_size.x as usize, screen_size.y as usize)?;
let _fbo_ui = FrameBufferObject::new(&gl, screen_size.x as usize, screen_size.y as usize)?;
// The surveys storing the textures of the resolved tiles
// The hipss storing the textures of the resolved tiles
let layers = Layers::new(&gl, &projection)?;
let time_start_blending = Time::now();
@@ -255,21 +254,22 @@ impl App {
}
fn look_for_new_tiles(&mut self) -> Result<(), JsValue> {
// Move the views of the different active surveys
// Move the views of the different active hipss
self.tile_fetcher.clear();
// Loop over the surveys
for survey in self.layers.values_mut_hips() {
let cfg = survey.get_config();
if self.camera.get_texture_depth() == 0
&& self
.downloader
.borrow()
.is_queried(&query::Allsky::new(cfg).id)
{
// Loop over the hipss
for hips in self.layers.get_mut_hipses() {
if self.camera.get_texture_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)),
};
if self.downloader.borrow().is_queried(&allsky_query.id) {
// do not ask for tiles if we download the allsky
continue;
}
}
let cfg = hips.get_config();
let min_tile_depth = cfg.delta_depth().max(cfg.get_min_depth_tile());
let mut ancestors = HashSet::new();
@@ -278,16 +278,9 @@ impl App {
let root_url = cfg.get_root_url().to_string();
let format = cfg.get_format();
if let Some(tiles_iter) = survey.look_for_new_tiles(&mut self.camera, &self.projection)
{
for tile_cell in tiles_iter.into_iter() {
self.tile_fetcher.append(query::Tile::new(
&tile_cell,
creator_did.clone(),
root_url.clone(),
format,
None,
));
if let Some(tiles) = hips.look_for_new_tiles(&mut self.camera, &self.projection) {
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
@@ -301,15 +294,21 @@ impl App {
}
}
// Request for ancestor
match hips {
HiPS::D2(hips) => {
for ancestor in ancestors {
if !survey.update_priority_tile(&ancestor) {
self.tile_fetcher.append(query::Tile::new(
&ancestor,
creator_did.clone(),
root_url.clone(),
format,
None,
));
if !hips.update_priority_tile(&ancestor) {
self.tile_fetcher.append(hips.get_tile_query(&ancestor));
}
}
}
HiPS::D3(hips) => {
let slice = hips.get_slice();
for ancestor in ancestors {
if !hips.contains_tile(&ancestor, slice) {
self.tile_fetcher.append(hips.get_tile_query(&ancestor));
}
}
}
}
}
@@ -345,7 +344,7 @@ impl App {
colormap,
&mut self.shaders,
&self.camera,
self.surveys.get_view().unwrap_abort(),
self.hipss.get_view().unwrap_abort(),
);
self.catalog_loaded = true;
self.request_redraw = true;
@@ -380,7 +379,7 @@ impl App {
colormap,
&mut self.shaders,
&self.camera,
self.surveys.get_view().unwrap_abort(),
self.hipss.get_view().unwrap_abort(),
);
self.catalog_loaded = true;
self.request_redraw = true;
@@ -395,7 +394,6 @@ impl App {
use crate::downloader::request::Resource;
use al_api::cell::HEALPixCellProjeted;
use crate::downloader::request::tile::Tile;
use crate::healpix::cell::HEALPixCell;
use al_api::color::ColorRGB;
@@ -566,11 +564,11 @@ impl App {
{
// Newly available tiles must lead to
// 1. Surveys must be aware of the new available tiles
//self.surveys.set_available_tiles(&available_tiles);
// 2. Get the resolved tiles and push them to the image surveys
//self.hipss.set_available_tiles(&available_tiles);
// 2. Get the resolved tiles and push them to the image hipss
/*let is_there_new_available_tiles = self
.downloader
.get_resolved_tiles(/*&available_tiles, */&mut self.surveys);*/
.get_resolved_tiles(/*&available_tiles, */&mut self.hipss);*/
if self.request_for_new_tiles
//&& Time::now() - self.last_time_request_for_new_tiles > DeltaTime::from(200.0)
@@ -606,9 +604,8 @@ impl App {
match rsc {
Resource::Tile(tile) => {
//if !_has_camera_zoomed {
if let Some(survey) = self.layers.get_mut_hips_from_cdid(&tile.get_hips_cdid())
{
let cfg = survey.get_config_mut();
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();
@@ -707,7 +704,17 @@ impl App {
self.request_redraw = true;
tile_copied = true;
survey.add_tile(&tile.cell, img, tile.time_req)?;
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.time_start_blending = Time::now();
}
@@ -720,28 +727,22 @@ impl App {
Resource::Allsky(allsky) => {
let hips_cdid = allsky.get_hips_cdid();
if let Some(survey) = self.layers.get_mut_hips_from_cdid(hips_cdid) {
if let Some(hips) = self.layers.get_mut_hips_from_cdid(hips_cdid) {
let is_missing = allsky.missing();
if is_missing {
// The allsky image is missing so we donwload all the tiles contained into
// the 0's cell
let cfg = survey.get_config();
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 = query::Tile::new(
&cell,
cfg.get_creator_did().to_string(),
cfg.get_root_url().to_string(),
cfg.get_format(),
None,
);
let query = hips.get_tile_query(&cell);
self.tile_fetcher.append_base_tile(query);
}
}
} else {
// tell the survey to not download tiles which order is <= 3 because the allsky
// tell the hips to not download tiles which order is <= 3 because the allsky
// give them already
survey.add_allsky(allsky)?;
hips.add_allsky(allsky)?;
// Once received ask for redraw
self.request_redraw = true;
}
@@ -793,8 +794,8 @@ impl App {
pub(crate) fn read_pixel(&self, pos: &Vector2<f64>, layer: &str) -> Result<JsValue, JsValue> {
if let Some(lonlat) = self.screen_to_world(pos) {
if let Some(survey) = self.layers.get_hips_from_layer(layer) {
survey.read_pixel(&lonlat, &self.camera)
if let Some(hips) = self.layers.get_hips_from_layer(layer) {
hips.read_pixel(&lonlat, &self.camera)
} else if let Some(_image) = self.layers.get_image_from_layer(layer) {
Err(JsValue::from_str("TODO: read pixel value"))
} else {
@@ -960,14 +961,14 @@ impl App {
Ok(())
}
pub(crate) fn add_image_hips(
pub(crate) fn add_hips(
&mut self,
hips_cfg: HiPSCfg,
local_files: Option<HiPSLocalFiles>,
) -> Result<(), JsValue> {
let cdid = hips_cfg.properties.get_creator_did().to_string();
let hips = self.layers.add_image_hips(
let hips = self.layers.add_hips(
&self.gl,
hips_cfg,
&mut self.camera,
@@ -1252,18 +1253,25 @@ impl App {
self.layers.get_layer_cfg(layer)
}
pub(crate) fn set_hips_url(&mut self, cdid: &String, new_url: String) -> Result<(), JsValue> {
self.layers.set_survey_url(cdid, new_url.clone())?;
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)
.ok_or_else(|| JsValue::from_str("Layer not found"))?;
//let hips = self.layers.get_hips_from_url(&new_url).unwrap_abort();
// Relaunch the base tiles for the survey to be ready with the new url
//self.tile_fetcher
// .launch_starting_hips_requests(hips, &mut self.downloader);
self.request_for_new_tiles = true;
match hips {
HiPS::D2(_) => Err(JsValue::from_str("layer do not refers to a cube")),
HiPS::D3(hips) => {
hips.set_slice(slice as u16);
Ok(())
}
}
}
pub(crate) fn set_image_survey_color_cfg(
pub(crate) fn set_image_hips_color_cfg(
&mut self,
layer: String,
meta: ImageMetadata,
@@ -1271,19 +1279,19 @@ impl App {
let old_meta = self.layers.get_layer_cfg(&layer)?;
// Set the new meta
// keep the old meta data
let new_img_fmt = meta.img_format;
let new_img_ext = meta.img_format;
self.layers
.set_layer_cfg(layer.clone(), meta, &mut self.camera, &self.projection)?;
if old_meta.img_format != new_img_fmt {
if old_meta.img_format != new_img_ext {
// The image format has been changed
let hips = self
.layers
.get_mut_hips_from_layer(&layer)
.ok_or_else(|| JsValue::from_str("Layer not found"))?;
hips.set_img_format(new_img_fmt)?;
hips.set_image_ext(new_img_ext)?;
// Relaunch the base tiles for the survey to be ready with the new url
// Relaunch the base tiles for the hips to be ready with the new url
self.tile_fetcher
.launch_starting_hips_requests(hips, self.downloader.clone());
@@ -1364,8 +1372,8 @@ impl App {
self.request_redraw = true;
}
pub(crate) fn set_survey_url(&mut self, cdid: &String, new_url: String) -> Result<(), JsValue> {
self.layers.set_survey_url(cdid, new_url)
pub(crate) fn set_hips_url(&mut self, cdid: &String, new_url: String) -> Result<(), JsValue> {
self.layers.set_hips_url(cdid, new_url)
}
pub(crate) fn set_catalog_opacity(

View File

@@ -20,19 +20,18 @@ pub struct Tile {
// The total url of the query
pub url: Url,
pub id: QueryId,
pub channel: Option<u32>,
}
use crate::healpix::cell::HEALPixCell;
use crate::renderable::hips::config::HiPSConfig;
use crate::renderable::CreatorDid;
impl Tile {
pub fn new(
cell: &HEALPixCell,
hips_cdid: String,
hips_url: String,
format: ImageFormatType,
channel: Option<u32>,
) -> Self {
pub fn new(cell: &HEALPixCell, channel: Option<u32>, cfg: &HiPSConfig) -> Self {
let hips_cdid = cfg.get_creator_did();
let hips_url = cfg.get_root_url();
let format = cfg.get_format();
let ext = format.get_ext_file();
let HEALPixCell(depth, idx) = *cell;
@@ -43,22 +42,30 @@ impl Tile {
// handle cube case
if let Some(channel) = channel {
if channel > 0 {
url.push_str(&format!("_{:?}", channel));
}
}
// add the tile format
url.push_str(&format!(".{}", ext));
let channel = channel.unwrap_or(0);
let id = format!("{}{}{}{}{}", hips_cdid, depth, idx, channel, ext);
let id = format!(
"{}{}{}{}{}",
hips_cdid,
depth,
idx,
channel.unwrap_or(0),
ext
);
Tile {
hips_cdid,
hips_cdid: hips_cdid.to_string(),
url,
cell: *cell,
format,
id,
channel,
}
}
}
@@ -77,6 +84,7 @@ pub struct Allsky {
pub format: ImageFormatType,
pub tile_size: i32,
pub texture_size: i32,
pub channel: Option<u32>,
// The root url of the HiPS
pub hips_cdid: CreatorDid,
// The total url of the query
@@ -85,16 +93,31 @@ pub struct Allsky {
}
impl Allsky {
pub fn new(cfg: &HiPSConfig) -> Self {
pub fn new(cfg: &HiPSConfig, channel: Option<u32>) -> Self {
let hips_cdid = cfg.get_creator_did().to_string();
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 url = format!("{}/Norder3/Allsky.{}", cfg.get_root_url(), ext);
let mut url = format!("{}/Norder3/Allsky", cfg.get_root_url());
let id = format!("{}Allsky{}", cfg.get_creator_did(), ext);
// handle cube case
if let Some(channel) = channel {
if channel > 0 {
url.push_str(&format!("_{:?}", channel));
}
}
// add the tile format
url.push_str(&format!(".{}", ext));
let id = format!(
"{}Allsky{}{}",
cfg.get_creator_did(),
ext,
channel.unwrap_or(0)
);
Allsky {
tile_size,
@@ -103,6 +126,7 @@ impl Allsky {
url,
format,
id,
channel,
}
}
}

View File

@@ -14,6 +14,7 @@ pub struct AllskyRequest {
pub url: Url,
pub depth_tile: u8,
pub id: QueryId,
pub channel: Option<u32>,
request: Request<Vec<ImageType>>,
}
@@ -80,6 +81,7 @@ impl From<query::Allsky> for AllskyRequest {
hips_cdid,
texture_size,
id,
channel: slice,
} = query;
let depth_tile = crate::math::utils::log_2_unchecked(texture_size / tile_size) as u8;
@@ -212,6 +214,7 @@ impl From<query::Allsky> for AllskyRequest {
depth_tile,
url,
request,
channel: slice,
}
}
}
@@ -306,7 +309,6 @@ use al_core::image::format::RGBA8U;
use crate::time::Time;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
pub struct Allsky {
pub image: Rc<RefCell<Option<Vec<ImageType>>>>,
pub time_req: Time,
@@ -314,6 +316,7 @@ pub struct Allsky {
pub hips_cdid: CreatorDid,
url: Url,
pub channel: Option<u32>,
}
use crate::Abort;
@@ -339,6 +342,7 @@ impl<'a> From<&'a AllskyRequest> for Option<Allsky> {
hips_cdid,
depth_tile,
url,
channel,
..
} = request;
if request.is_resolved() {
@@ -352,6 +356,7 @@ impl<'a> From<&'a AllskyRequest> for Option<Allsky> {
hips_cdid: hips_cdid.clone(),
url: url.clone(),
depth_tile: *depth_tile,
channel: *channel,
})
} else {
None

View File

@@ -134,7 +134,6 @@ impl From<query::PixelMetadata> for PixelMetadataRequest {
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
#[derive(Debug)]
pub struct PixelMetadata {
pub value: Rc<RefCell<Option<Metadata>>>,

View File

@@ -104,7 +104,6 @@ impl From<query::Moc> for MOCRequest {
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
pub struct Moc {
pub moc: Rc<RefCell<Option<HEALPixCoverage>>>,
pub params: al_api::moc::MOC,

View File

@@ -10,7 +10,6 @@ pub mod tile;
use crate::time::Time;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
pub type Url = String;
pub struct Request<R> {
data: Rc<RefCell<Option<R>>>,
@@ -27,7 +26,6 @@ pub enum ResolvedStatus {
Failed,
Found,
}
use crate::Abort;
use std::future::Future;
use wasm_bindgen::JsValue;
impl<R> Request<R>

View File

@@ -10,14 +10,14 @@ use super::{Request, RequestType};
use crate::downloader::QueryId;
pub struct TileRequest {
request: Request<ImageType>,
pub id: QueryId,
cell: HEALPixCell,
hips_cdid: CreatorDid,
url: Url,
format: ImageFormatType,
request: Request<ImageType>,
channel: Option<u32>,
}
impl From<TileRequest> for RequestType {
@@ -59,6 +59,7 @@ impl From<query::Tile> for TileRequest {
url,
hips_cdid,
id,
channel: slice,
} = query;
let url_clone = url.clone();
@@ -180,6 +181,7 @@ impl From<query::Tile> for TileRequest {
hips_cdid,
url,
request,
channel: slice,
}
}
}
@@ -187,12 +189,12 @@ impl From<query::Tile> for TileRequest {
use crate::time::Time;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
pub struct Tile {
pub image: Rc<RefCell<Option<ImageType>>>,
pub time_req: Time,
pub cell: HEALPixCell,
pub format: ImageFormatType,
pub channel: Option<u32>,
hips_cdid: CreatorDid,
url: Url,
}
@@ -228,6 +230,7 @@ impl<'a> From<&'a TileRequest> for Option<Tile> {
hips_cdid,
url,
format,
channel,
..
} = request;
if request.is_resolved() {
@@ -242,6 +245,7 @@ impl<'a> From<&'a TileRequest> for Option<Tile> {
hips_cdid: hips_cdid.clone(),
url: url.clone(),
format: *format,
channel: *channel,
})
} else {
None

View File

@@ -320,13 +320,13 @@ impl WebClient {
Ok(self.app.get_norder())
}
/// Set new image surveys
/// Set new image hips
///
/// Send the image surveys to render inside the Aladin Lite view
/// Send the image hips to render inside the Aladin Lite view
///
/// # Arguments
///
/// * `surveys` - A list/array of survey. A survey is a javascript object
/// * `hips` - A list/array of hips. A hips is a javascript object
/// having the specific form. Please check the file in core/src/hips.rs to see
/// the different semantics accepted.
///
@@ -362,19 +362,19 @@ impl WebClient {
///
/// # Panics
///
/// * If the surveys do not match SimpleHiPS type
/// * If the number of surveys is greater than 4. For the moment, due to the limitations
/// of WebGL2 texture units on some architectures, the total number of surveys rendered is
/// * If the hips do not match SimpleHiPS type
/// * If the number of hips is greater than 4. For the moment, due to the limitations
/// of WebGL2 texture units on some architectures, the total number of hips rendered is
/// limited to 4.
#[wasm_bindgen(js_name = addHiPS)]
pub fn add_image_hips(
pub fn add_hips(
&mut self,
hips: JsValue,
files: Option<HiPSLocalFiles>,
) -> Result<(), JsValue> {
// Deserialize the survey objects that compose the survey
// Deserialize the hips objects that compose the hips
let hips = serde_wasm_bindgen::from_value(hips)?;
self.app.add_image_hips(hips, files)?;
self.app.add_hips(hips, files)?;
Ok(())
}
@@ -410,7 +410,7 @@ impl WebClient {
#[wasm_bindgen(js_name = removeLayer)]
pub fn remove_layer(&mut self, layer: String) -> Result<(), JsValue> {
// Deserialize the survey objects that compose the survey
// Deserialize the hips objects that compose the hips
self.app.remove_layer(&layer)?;
Ok(())
@@ -418,7 +418,7 @@ impl WebClient {
#[wasm_bindgen(js_name = renameLayer)]
pub fn rename_layer(&mut self, layer: String, new_layer: String) -> Result<(), JsValue> {
// Deserialize the survey objects that compose the survey
// Deserialize the hips objects that compose the hips
self.app.rename_layer(&layer, &new_layer)
}
@@ -428,7 +428,7 @@ impl WebClient {
first_layer: String,
second_layer: String,
) -> Result<(), JsValue> {
// Deserialize the survey objects that compose the survey
// Deserialize the hips objects that compose the hips
self.app.swap_layers(&first_layer, &second_layer)
}
@@ -444,15 +444,15 @@ impl WebClient {
// Set a new color associated with a layer
#[wasm_bindgen(js_name = setImageMetadata)]
pub fn set_survey_color_cfg(&mut self, layer: String, meta: JsValue) -> Result<(), JsValue> {
pub fn set_hips_color_cfg(&mut self, layer: String, meta: JsValue) -> Result<(), JsValue> {
let meta = serde_wasm_bindgen::from_value(meta)?;
self.app.set_image_survey_color_cfg(layer, meta)
self.app.set_image_hips_color_cfg(layer, meta)
}
#[wasm_bindgen(js_name = setImageSurveyUrl)]
pub fn set_survey_url(&mut self, cdid: String, new_url: String) -> Result<(), JsValue> {
self.app.set_survey_url(&cdid, new_url)
#[wasm_bindgen(js_name = setSliceNumber)]
pub fn set_hips_slice_number(&mut self, layer: String, slice: u32) -> Result<(), JsValue> {
self.app.set_hips_slice_number(&layer, slice)
}
#[wasm_bindgen(js_name = setBackgroundColor)]
@@ -963,7 +963,7 @@ impl WebClient {
/// Read the pixel value
///
/// The current implementation only returns the pixel value
/// of the first survey of the `layer` specified.
/// of the first hips of the `layer` specified.
///
/// # Returns
///
@@ -975,7 +975,7 @@ impl WebClient {
///
/// * `x` - The x screen coordinate in pixels
/// * `y` - The y screen coordinate in pixels
/// * `base_url` - The base url of the survey identifying it
/// * `base_url` - The base url of the hips identifying it
#[wasm_bindgen(js_name = readPixel)]
pub fn read_pixel(&self, x: f64, y: f64, layer: String) -> Result<JsValue, JsValue> {
let pixel = self.app.read_pixel(&Vector2::new(x, y), layer.as_str())?;

View File

@@ -73,7 +73,7 @@ impl PrimInt for i32 {
pub fn log_2_unchecked<T>(x: T) -> u32
where
T: Zero + PrimInt + std::cmp::PartialOrd
T: Zero + PrimInt + std::cmp::PartialOrd,
{
debug_assert!(x > T::zero());
num_bits::<T>() as u32 - x.leading_zeros() - 1
@@ -105,7 +105,6 @@ pub fn lambert_wm1(x: f32) -> f32 {
* (1.0 - 1.0 / (1.0 + ((m1 * s_div_2_root) / (1.0 + m2 * s * (m3 * s_root).exp()))))
}
#[inline]
pub fn ccw_tri<S: BaseFloat>(a: &[S; 2], b: &[S; 2], c: &[S; 2]) -> bool {
// From: https://math.stackexchange.com/questions/1324179/how-to-tell-if-3-connected-points-are-connected-clockwise-or-counter-clockwise
@@ -113,5 +112,5 @@ pub fn ccw_tri<S: BaseFloat>(a: &[S; 2], b: &[S; 2], c: &[S; 2]) -> bool {
// | x2, y2, 1 | > 0 => the triangle is given in anticlockwise order
// | x3, y3, 1 |
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()
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()
}

View File

@@ -1,19 +1,6 @@
use al_api::hips::ImageExt;
use al_core::{image::format::ImageFormat, image::raw::ImageBuffer};
use al_core::{image::ImageType, pixel::Pixel};
use al_core::{
image::{
format::{R16I, R32F, R32I, R8UI},
Image,
},
Texture2DArray,
};
use cgmath::Vector3;
use al_core::image::format::{ChannelType, ImageFormatType, RGB8U, RGBA8U};
use al_core::image::format::{ChannelType, ImageFormatType};
#[derive(Debug)]
pub struct HiPSConfig {
pub root_url: String,
@@ -228,7 +215,7 @@ impl HiPSConfig {
Ok(hips_config)
}
pub fn set_image_fmt(&mut self, ext: ImageExt) -> Result<(), JsValue> {
pub fn set_image_ext(&mut self, ext: ImageExt) -> Result<(), JsValue> {
let format = match ext {
ImageExt::Fits => {
// Check the bitpix to determine the internal format of the tiles
@@ -297,17 +284,6 @@ impl HiPSConfig {
self.format = format;
// Recompute if the survey will be colored or not
/*self.colored = if self.tex_storing_fits {
false
} else {
if let Some(subtypes) = &self.dataproduct_subtype {
subtypes.iter().any(|subtype| subtype == "color")
} else {
false
}
};*/
Ok(())
}
@@ -321,6 +297,10 @@ impl HiPSConfig {
self.root_url = root_url;
}
pub fn get_cube_depth(&self) -> Option<u32> {
self.cube_depth
}
#[inline(always)]
pub fn set_fits_metadata(&mut self, bscale: f32, bzero: f32, blank: f32) {
self.scale = bscale;

View File

@@ -4,11 +4,15 @@ use std::collections::HashMap;
use al_core::image::format::ChannelType;
use crate::renderable::hips::HpxTile;
use cgmath::Vector3;
use al_api::hips::ImageExt;
use al_core::webgl_ctx::WebGlRenderingCtx;
use crate::math::lonlat::LonLat;
use crate::CameraViewPort;
use crate::LonLatT;
use al_core::image::format::ImageFormat;
use al_core::image::format::{R16I, R32F, R32I, R64F, R8UI, RGB8U, RGBA8U};
use al_core::image::Image;
@@ -16,12 +20,11 @@ use al_core::shader::{SendUniforms, ShaderBound};
use al_core::Texture2DArray;
use al_core::WebGlContext;
use super::texture::{Texture, TextureUniforms};
use super::texture::{HpxTexture2D, HpxTexture2DUniforms};
use crate::downloader::request::allsky::Allsky;
use crate::healpix::cell::HEALPixCell;
use crate::healpix::cell::NUM_HPX_TILES_DEPTH_ZERO;
use crate::math::lonlat::LonLatT;
use crate::renderable::hips::config::HiPSConfig;
use crate::time::Time;
use crate::Abort;
@@ -58,24 +61,24 @@ impl Ord for TextureCellItem {
}
}
impl From<Texture> for TextureCellItem {
fn from(texture: Texture) -> Self {
impl From<HpxTexture2D> for TextureCellItem {
fn from(texture: HpxTexture2D) -> Self {
let time_request = texture.time_request();
let cell = *texture.cell();
Self { cell, time_request }
}
}
impl From<&Texture> for TextureCellItem {
fn from(texture: &Texture) -> Self {
impl From<&HpxTexture2D> for TextureCellItem {
fn from(texture: &HpxTexture2D) -> Self {
let time_request = texture.time_request();
let cell = *texture.cell();
Self { cell, time_request }
}
}
impl From<&mut Texture> for TextureCellItem {
fn from(texture: &mut Texture) -> Self {
impl From<&mut HpxTexture2D> for TextureCellItem {
fn from(texture: &mut HpxTexture2D) -> Self {
let time_request = texture.time_request();
let cell = *texture.cell();
@@ -121,6 +124,7 @@ impl HEALPixCellHeap {
}
}
use crate::renderable::hips::HpxTileBuffer;
// Fixed sized binary heap
pub struct HiPS2DBuffer {
// Some information about the HiPS
@@ -130,8 +134,8 @@ pub struct HiPS2DBuffer {
num_root_textures_available: u8,
size: usize,
textures: HashMap<HEALPixCell, Texture>,
base_textures: [Texture; NUM_HPX_TILES_DEPTH_ZERO],
textures: HashMap<HEALPixCell, HpxTexture2D>,
base_textures: [HpxTexture2D; NUM_HPX_TILES_DEPTH_ZERO],
// Array of 2D textures
texture_2d_array: Texture2DArray,
@@ -180,110 +184,6 @@ fn create_texture_array<F: ImageFormat>(
}
impl HiPS2DBuffer {
pub fn new(gl: &WebGlContext, config: HiPSConfig) -> Result<HiPS2DBuffer, JsValue> {
let size = 128 - NUM_HPX_TILES_DEPTH_ZERO;
// Ensures there is at least space for the 12
// root textures
//debug_assert!(size >= NUM_HPX_TILES_DEPTH_ZERO);
let heap = HEALPixCellHeap::with_capacity(size);
let textures = HashMap::with_capacity(size);
let now = Time::now();
let base_textures = [
Texture::new(&HEALPixCell(0, 0), 0, now),
Texture::new(&HEALPixCell(0, 1), 1, now),
Texture::new(&HEALPixCell(0, 2), 2, now),
Texture::new(&HEALPixCell(0, 3), 3, now),
Texture::new(&HEALPixCell(0, 4), 4, now),
Texture::new(&HEALPixCell(0, 5), 5, now),
Texture::new(&HEALPixCell(0, 6), 6, now),
Texture::new(&HEALPixCell(0, 7), 7, now),
Texture::new(&HEALPixCell(0, 8), 8, now),
Texture::new(&HEALPixCell(0, 9), 9, now),
Texture::new(&HEALPixCell(0, 10), 10, now),
Texture::new(&HEALPixCell(0, 11), 11, now),
];
let channel = config.get_format().get_channel();
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)?,
};
// The root textures have not been loaded
let num_root_textures_available = 0;
let available_tiles_during_frame = false;
Ok(HiPS2DBuffer {
config,
heap,
size,
num_root_textures_available,
textures,
base_textures,
texture_2d_array,
available_tiles_during_frame,
})
}
pub fn set_format(&mut self, gl: &WebGlContext, ext: ImageExt) -> Result<(), JsValue> {
self.config.set_image_fmt(ext)?;
let channel = self.config.get_format().get_channel();
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 now = Time::now();
self.base_textures = [
Texture::new(&HEALPixCell(0, 0), 0, now),
Texture::new(&HEALPixCell(0, 1), 1, now),
Texture::new(&HEALPixCell(0, 2), 2, now),
Texture::new(&HEALPixCell(0, 3), 3, now),
Texture::new(&HEALPixCell(0, 4), 4, now),
Texture::new(&HEALPixCell(0, 5), 5, now),
Texture::new(&HEALPixCell(0, 6), 6, now),
Texture::new(&HEALPixCell(0, 7), 7, now),
Texture::new(&HEALPixCell(0, 8), 8, now),
Texture::new(&HEALPixCell(0, 9), 9, now),
Texture::new(&HEALPixCell(0, 10), 10, now),
Texture::new(&HEALPixCell(0, 11), 11, now),
];
self.heap.clear();
self.textures.clear();
//self.ready = false;
self.num_root_textures_available = 0;
self.available_tiles_during_frame = false;
Ok(())
}
pub fn push_allsky(&mut self, allsky: Allsky) -> Result<(), JsValue> {
let Allsky {
image,
@@ -303,8 +203,72 @@ impl HiPS2DBuffer {
Ok(())
}
// This method pushes a new downloaded tile into the buffer
// It must be ensured that the tile is not already contained into the buffer
// Check whether the buffer has a tile
// 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)
} 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
}
}
}
fn is_heap_full(&self) -> bool {
// Check that there are no more than num_textures
// textures in the buffer
let num_textures_heap = self.heap.len();
num_textures_heap == self.size
}
// Update the priority of the texture containing the tile
// It must be ensured that the tile is already contained in the buffer
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() {
return;
}
let texture = self
.textures
.get_mut(&texture_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 {
// texture.update_start_time(Time::now());
//}
// MAYBE WE DO NOT NEED TO UPDATE THE TIME REQUEST IN THE BHEAP
// BECAUSE IT INTRODUCES UNECESSARY CODE COMPLEXITY
// Root textures are always in the buffer
// But other textures can be removed thanks to the heap
// data-structure. We have to update the time_request of the texture
// and push it again in the heap to update its position.
let mut tex_cell_item: TextureCellItem = texture.into();
tex_cell_item.time_request = Time::now();
self.heap.update_entry(tex_cell_item);
}
pub fn push<I: Image>(
&mut self,
cell: &HEALPixCell,
@@ -336,7 +300,7 @@ impl HiPS2DBuffer {
} else {
let idx = NUM_HPX_TILES_DEPTH_ZERO + self.heap.len();
Texture::new(&tex_cell, idx as i32, time_request)
HpxTexture2D::new(&tex_cell, idx as i32, time_request)
};
// Push it to the buffer
@@ -385,102 +349,171 @@ impl HiPS2DBuffer {
Ok(())
}
}
impl HpxTileBuffer for HiPS2DBuffer {
type T = HpxTexture2D;
fn new(gl: &WebGlContext, config: HiPSConfig) -> Result<Self, JsValue> {
let size = 128 - NUM_HPX_TILES_DEPTH_ZERO;
// Ensures there is at least space for the 12
// root textures
//debug_assert!(size >= NUM_HPX_TILES_DEPTH_ZERO);
let heap = HEALPixCellHeap::with_capacity(size);
let textures = HashMap::with_capacity(size);
let now = Time::now();
let base_textures = [
HpxTexture2D::new(&HEALPixCell(0, 0), 0, now),
HpxTexture2D::new(&HEALPixCell(0, 1), 1, now),
HpxTexture2D::new(&HEALPixCell(0, 2), 2, now),
HpxTexture2D::new(&HEALPixCell(0, 3), 3, now),
HpxTexture2D::new(&HEALPixCell(0, 4), 4, now),
HpxTexture2D::new(&HEALPixCell(0, 5), 5, now),
HpxTexture2D::new(&HEALPixCell(0, 6), 6, now),
HpxTexture2D::new(&HEALPixCell(0, 7), 7, now),
HpxTexture2D::new(&HEALPixCell(0, 8), 8, now),
HpxTexture2D::new(&HEALPixCell(0, 9), 9, now),
HpxTexture2D::new(&HEALPixCell(0, 10), 10, now),
HpxTexture2D::new(&HEALPixCell(0, 11), 11, now),
];
let channel = config.get_format().get_channel();
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)?,
};
// The root textures have not been loaded
let num_root_textures_available = 0;
let available_tiles_during_frame = false;
Ok(HiPS2DBuffer {
config,
heap,
size,
num_root_textures_available,
textures,
base_textures,
texture_2d_array,
available_tiles_during_frame,
})
}
fn set_image_ext(&mut self, gl: &WebGlContext, ext: ImageExt) -> Result<(), JsValue> {
self.config.set_image_ext(ext)?;
let channel = self.config.get_format().get_channel();
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 now = Time::now();
self.base_textures = [
HpxTexture2D::new(&HEALPixCell(0, 0), 0, now),
HpxTexture2D::new(&HEALPixCell(0, 1), 1, now),
HpxTexture2D::new(&HEALPixCell(0, 2), 2, now),
HpxTexture2D::new(&HEALPixCell(0, 3), 3, now),
HpxTexture2D::new(&HEALPixCell(0, 4), 4, now),
HpxTexture2D::new(&HEALPixCell(0, 5), 5, now),
HpxTexture2D::new(&HEALPixCell(0, 6), 6, now),
HpxTexture2D::new(&HEALPixCell(0, 7), 7, now),
HpxTexture2D::new(&HEALPixCell(0, 8), 8, now),
HpxTexture2D::new(&HEALPixCell(0, 9), 9, now),
HpxTexture2D::new(&HEALPixCell(0, 10), 10, now),
HpxTexture2D::new(&HEALPixCell(0, 11), 11, now),
];
self.heap.clear();
self.textures.clear();
//self.ready = false;
self.num_root_textures_available = 0;
self.available_tiles_during_frame = false;
Ok(())
}
// This method pushes a new downloaded tile into the buffer
// It must be ensured that the tile is not already contained into the buffer
// Return if tiles did become available
pub fn reset_available_tiles(&mut self) -> bool {
fn reset_available_tiles(&mut self) -> bool {
let available_tiles_during_frame = self.available_tiles_during_frame;
self.available_tiles_during_frame = false;
available_tiles_during_frame
}
fn is_heap_full(&self) -> bool {
// Check that there are no more than num_textures
// textures in the buffer
let num_textures_heap = self.heap.len();
num_textures_heap == self.size
}
// Tell if a texture is available meaning all its sub tiles
// must have been written for the GPU
pub fn contains(&self, texture_cell: &HEALPixCell) -> bool {
if let Some(t) = self.get(texture_cell) {
fn contains(&self, cell: &HEALPixCell) -> bool {
if let Some(t) = self.get(cell) {
t.is_full()
} else {
false
}
}
// Check whether the buffer has a tile
// 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(cell)
/// Accessors
fn get(&self, cell: &HEALPixCell) -> Option<&Self::T> {
if cell.is_root() {
let HEALPixCell(_, idx) = cell;
Some(&self.base_textures[*idx as usize])
} 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(cell)
} else {
// The texture in which cell should be is not present
false
self.textures.get(cell)
}
}
fn config(&self) -> &HiPSConfig {
&self.config
}
// Update the priority of the texture containing the tile
// It must be ensured that the tile is already contained in the buffer
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() {
return;
fn config_mut(&mut self) -> &mut HiPSConfig {
&mut self.config
}
let texture = self
.textures
.get_mut(&texture_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 {
// texture.update_start_time(Time::now());
//}
fn read_pixel(&self, pos: &LonLatT<f64>, camera: &CameraViewPort) -> Result<JsValue, JsValue> {
// 1. Convert it to the hips frame system
let cfg = self.config();
let camera_frame = camera.get_coo_system();
let hips_frame = cfg.get_frame();
// MAYBE WE DO NOT NEED TO UPDATE THE TIME REQUEST IN THE BHEAP
// BECAUSE IT INTRODUCES UNECESSARY CODE COMPLEXITY
// Root textures are always in the buffer
// But other textures can be removed thanks to the heap
// data-structure. We have to update the time_request of the texture
// and push it again in the heap to update its position.
let mut tex_cell_item: TextureCellItem = texture.into();
tex_cell_item.time_request = Time::now();
let pos: LonLatT<f64> =
crate::coosys::apply_coo_system(camera_frame, hips_frame, &pos.vector()).lonlat();
self.heap.update_entry(tex_cell_item);
}
// Get the array of textures from that survey
let depth = camera.get_texture_depth().min(cfg.get_max_depth_texture());
// lonlat is given in the
pub fn get_pixel_position_in_texture(
&self,
lonlat: &LonLatT<f64>,
depth: u8,
) -> Result<Vector3<i32>, JsValue> {
let (pix, dx, dy) = crate::healpix::utils::hash_with_dxdy(depth, lonlat);
// compute the tex
let (pix, dx, dy) = crate::healpix::utils::hash_with_dxdy(depth, &pos);
let texture_cell = HEALPixCell(depth, pix);
if let Some(texture) = self.get(&texture_cell) {
let cfg = &self.config;
let cfg = self.config();
// Index of the texture in the total set of textures
let texture_idx = texture.idx();
@@ -489,24 +522,38 @@ impl HiPS2DBuffer {
let texture_size = cfg.get_texture_size();
// Offset in the slice in pixels
let mut offset = Vector3::new(
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 self.config.tex_storing_fits {
let texture_size = self.config.get_texture_size() as f32;
let mut uvy = offset.y as f32 / texture_size;
uvy = self.config.size_tile_uv
+ 2.0 * self.config.size_tile_uv * (uvy / self.config.size_tile_uv).floor()
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;
offset.y = (uvy * texture_size) as i32;
pos_tex.y = (uvy * texture_size) as i32;
}
Ok(offset)
let mut value = self
.texture_2d_array
.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(|| JsValue::from_str("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);
}
Ok(value)
} else {
Err(JsValue::from_str(&format!(
"{:?} not loaded in the GPU, please wait before trying again.",
@@ -514,53 +561,11 @@ impl HiPS2DBuffer {
)))
}
}
/// Accessors
pub fn get(&self, texture_cell: &HEALPixCell) -> Option<&Texture> {
if texture_cell.is_root() {
let HEALPixCell(_, idx) = texture_cell;
Some(&self.base_textures[*idx as usize])
} else {
self.textures.get(texture_cell)
}
}
// Get the nearest parent tile found in the CPU buffer
pub fn get_nearest_parent(&self, cell: &HEALPixCell) -> Option<HEALPixCell> {
if cell.is_root() {
// Root cells are in the buffer by definition
Some(*cell)
} else {
let mut parent_cell = cell.parent();
while !self.contains(&parent_cell) && !parent_cell.is_root() {
parent_cell = parent_cell.parent();
}
if self.contains(&parent_cell) {
Some(parent_cell)
} else {
None
}
}
}
pub fn config(&self) -> &HiPSConfig {
&self.config
}
pub fn config_mut(&mut self) -> &mut HiPSConfig {
&mut self.config
}
pub fn get_texture_array(&self) -> &Texture2DArray {
&self.texture_2d_array
}
}
fn send_to_gpu<I: Image>(
cell: &HEALPixCell,
texture: &Texture,
texture: &HpxTexture2D,
image: I,
texture_array: &Texture2DArray,
cfg: &mut HiPSConfig,
@@ -605,7 +610,7 @@ impl SendUniforms for HiPS2DBuffer {
let cell = HEALPixCell(0, idx as u64);
let texture = self.get(&cell).unwrap();
let texture_uniforms = TextureUniforms::new(texture, idx as i32);
let texture_uniforms = HpxTexture2DUniforms::new(texture, idx as i32);
shader.attach_uniforms_from(&texture_uniforms);
}
//}

View File

@@ -1,12 +1,15 @@
pub mod buffer;
pub mod texture;
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 crate::downloader::query;
use al_core::image::Image;
use al_core::shader::Shader;
@@ -26,7 +29,6 @@ use crate::{math::lonlat::LonLatT, utils};
use crate::downloader::request::allsky::Allsky;
use crate::healpix::{cell::HEALPixCell, coverage::HEALPixCoverage};
use crate::math::lonlat::LonLat;
use crate::renderable::utils::index_patch::DefaultPatchIndexIter;
use crate::time::Time;
@@ -37,7 +39,7 @@ use std::collections::HashSet;
// to not be too much skewed
use buffer::HiPS2DBuffer;
use texture::Texture;
use texture::HpxTexture2D;
use super::raytracing::RayTracer;
use super::uv::{TileCorner, TileUVW};
@@ -47,76 +49,16 @@ use cgmath::Matrix;
use wasm_bindgen::JsValue;
use web_sys::WebGl2RenderingContext;
const M: f64 = 280.0 * 280.0;
const N: f64 = 150.0 * 150.0;
const RAP: f64 = 0.7;
fn is_too_large(cell: &HEALPixCell, camera: &CameraViewPort, projection: &ProjectionType) -> bool {
let vertices = cell
.vertices()
.iter()
.filter_map(|(lon, lat)| {
let vertex = crate::math::lonlat::radec_to_xyzw(Angle(*lon), Angle(*lat));
projection.icrs_celestial_to_screen_space(&vertex, camera)
})
.collect::<Vec<_>>();
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());
if d1 > M || d2 > M {
true
} else if d1 < N && d2 < N {
false
} else {
let rap = if d2 > d1 { d1 / d2 } else { d2 / d1 };
rap < RAP
}
}
}
fn num_subdivision(cell: &HEALPixCell, camera: &CameraViewPort, projection: &ProjectionType) -> u8 {
let d = cell.depth();
// Subdivide all cells at least one time.
// TODO: use a single subdivision number computed from the current cells inside the view
// i.e. subdivide all cells in the view with the cell that has to be the most subdivided
let mut num_sub = 1;
if d < 2 {
num_sub = 2 - d;
}
// Largest deformation cell among the cells of a specific depth
let largest_center_to_vertex_dist =
healpix::largest_center_to_vertex_distance(d, 0.0, healpix::TRANSITION_LATITUDE);
let smallest_center_to_vertex_dist =
healpix::largest_center_to_vertex_distance(d, 0.0, healpix::LAT_OF_SQUARE_CELL);
let (lon, lat) = cell.center();
let center_to_vertex_dist = healpix::largest_center_to_vertex_distance(d, lon, lat);
let skewed_factor = (center_to_vertex_dist - smallest_center_to_vertex_dist)
/ (largest_center_to_vertex_dist - smallest_center_to_vertex_dist);
if skewed_factor > 0.25 || is_too_large(cell, camera, projection) || cell.is_on_pole() {
num_sub += 1;
}
num_sub
}
pub struct TextureToDraw<'a, 'b> {
pub starting_texture: &'a Texture,
pub ending_texture: &'a Texture,
pub starting_texture: &'a HpxTexture2D,
pub ending_texture: &'a HpxTexture2D,
pub cell: &'b HEALPixCell,
}
impl<'a, 'b> TextureToDraw<'a, 'b> {
fn new(
starting_texture: &'a Texture,
ending_texture: &'a Texture,
starting_texture: &'a HpxTexture2D,
ending_texture: &'a HpxTexture2D,
cell: &'b HEALPixCell,
) -> TextureToDraw<'a, 'b> {
TextureToDraw {
@@ -209,7 +151,7 @@ pub fn get_raytracer_shader<'a>(
pub struct HiPS2D {
//color: Color,
// The image survey texture buffer
textures: HiPS2DBuffer,
buffer: HiPS2DBuffer,
// The projected vertices data
// For WebGL2 wasm, the data are interleaved
@@ -241,6 +183,8 @@ pub struct HiPS2D {
hpx_cells_in_view: Vec<HEALPixCell>,
}
use super::HpxTileBuffer;
impl HiPS2D {
pub fn new(config: HiPSConfig, gl: &WebGlContext) -> Result<Self, JsValue> {
let mut vao = VertexArrayObject::new(gl);
@@ -294,7 +238,7 @@ impl HiPS2D {
.unbind();
let num_idx = 0;
let textures = HiPS2DBuffer::new(gl, config)?;
let buffer = HiPS2DBuffer::new(gl, config)?;
let gl = gl.clone();
let footprint_moc = None;
@@ -302,7 +246,7 @@ impl HiPS2D {
// request the allsky texture
Ok(Self {
// The image survey texture buffer
textures,
buffer,
num_idx,
vao,
@@ -382,7 +326,12 @@ impl HiPS2D {
}
pub fn contains_tile(&self, cell: &HEALPixCell) -> bool {
self.textures.contains_tile(cell)
self.buffer.contains_tile(cell)
}
pub fn get_tile_query(&self, cell: &HEALPixCell) -> query::Tile {
let cfg = self.get_config();
query::Tile::new(cell, None, cfg)
}
pub fn update(&mut self, camera: &mut CameraViewPort, projection: &ProjectionType) {
@@ -393,7 +342,7 @@ impl HiPS2D {
}
// rasterizer mode
let available_tiles = self.textures.reset_available_tiles();
let available_tiles = self.buffer.reset_available_tiles();
let new_cells_in_view = self.retrieve_cells_in_camera(camera);
if new_cells_in_view || available_tiles {
@@ -434,51 +383,21 @@ impl HiPS2D {
self.footprint_moc.as_ref()
}
pub fn set_img_format(&mut self, ext: ImageExt) -> Result<(), JsValue> {
self.textures.set_format(&self.gl, ext)
pub fn set_image_ext(&mut self, ext: ImageExt) -> Result<(), JsValue> {
self.buffer.set_image_ext(&self.gl, ext)
}
pub fn is_allsky(&self) -> bool {
self.textures.config().is_allsky
self.buffer.config().is_allsky
}
// Position given is in the camera space
pub fn read_pixel(
&self,
pos: &LonLatT<f64>,
p: &LonLatT<f64>,
camera: &CameraViewPort,
) -> Result<JsValue, JsValue> {
// 1. Convert it to the hips frame system
let cfg = self.textures.config();
let camera_frame = camera.get_coo_system();
let hips_frame = cfg.get_frame();
let pos = crate::coosys::apply_coo_system(camera_frame, hips_frame, &pos.vector());
// Get the array of textures from that survey
let tile_depth = camera.get_texture_depth().min(cfg.get_max_depth_texture());
let pos_tex = self
.textures
.get_pixel_position_in_texture(&pos.lonlat(), tile_depth)?;
let slice_idx = pos_tex.z as usize;
let texture_array = self.textures.get_texture_array();
unimplemented!();
/*let value = texture_array[slice_idx].read_pixel(pos_tex.x, pos_tex.y)?;
if cfg.tex_storing_fits {
let value = value
.as_f64()
.ok_or_else(|| JsValue::from_str("Error unwraping the pixel read value."))?;
let scale = cfg.scale as f64;
let offset = cfg.offset as f64;
Ok(JsValue::from_f64(value * scale + offset))
} else {
Ok(value)
}*/
self.buffer.read_pixel(p, camera)
}
fn recompute_vertices(&mut self, camera: &mut CameraViewPort, projection: &ProjectionType) {
@@ -488,7 +407,7 @@ impl HiPS2D {
self.time_tile_received.clear();
self.idx_vertices.clear();
let cfg = self.textures.config();
let cfg = self.buffer.config();
// Get the coo system transformation matrix
let channel = cfg.get_format().get_channel();
@@ -516,10 +435,10 @@ impl HiPS2D {
};
if let Some(cell) = cell {
let texture_to_draw = if self.textures.contains(cell) {
if let Some(ending_cell_in_tex) = self.textures.get(cell) {
if let Some(parent_cell) = self.textures.get_nearest_parent(cell) {
if let Some(starting_cell_in_tex) = self.textures.get(&parent_cell) {
let texture_to_draw = if self.buffer.contains(cell) {
if let Some(ending_cell_in_tex) = self.buffer.get(cell) {
if let Some(parent_cell) = self.buffer.get_nearest_parent(cell) {
if let Some(starting_cell_in_tex) = self.buffer.get(&parent_cell) {
Some(TextureToDraw::new(
starting_cell_in_tex,
ending_cell_in_tex,
@@ -544,13 +463,13 @@ impl HiPS2D {
None
}
} else {
if let Some(parent_cell) = self.textures.get_nearest_parent(cell) {
if let Some(ending_cell_in_tex) = self.textures.get(&parent_cell) {
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.textures.get_nearest_parent(&parent_cell)
self.buffer.get_nearest_parent(&parent_cell)
{
if let Some(starting_cell_in_tex) =
self.textures.get(&grand_parent_cell)
self.buffer.get(&grand_parent_cell)
{
Some(TextureToDraw::new(
starting_cell_in_tex,
@@ -595,7 +514,8 @@ impl HiPS2D {
let start_time = ending_texture.start_time().as_millis();
let num_subdivision = num_subdivision(cell, camera, projection);
let num_subdivision =
super::subdivide::num_hpxcell_subdivision(cell, camera, projection);
let n_segments_by_side: usize = 1 << (num_subdivision as usize);
let n_segments_by_side_f32 = n_segments_by_side as f32;
@@ -693,9 +613,9 @@ impl HiPS2D {
// Return a boolean to signal if the tile is present or not in the survey
pub fn update_priority_tile(&mut self, cell: &HEALPixCell) -> bool {
if self.textures.contains_tile(cell) {
if self.buffer.contains_tile(cell) {
// The cell is present in the survey, we update its priority
self.textures.update_priority(cell);
self.buffer.update_priority(cell);
true
} else {
false
@@ -708,22 +628,22 @@ impl HiPS2D {
image: I,
time_request: Time,
) -> Result<(), JsValue> {
self.textures.push(&cell, image, time_request)
self.buffer.push(&cell, image, time_request)
}
pub fn add_allsky(&mut self, allsky: Allsky) -> Result<(), JsValue> {
self.textures.push_allsky(allsky)
self.buffer.push_allsky(allsky)
}
/* Accessors */
#[inline]
pub fn get_config(&self) -> &HiPSConfig {
self.textures.config()
self.buffer.config()
}
#[inline]
pub fn get_config_mut(&mut self) -> &mut HiPSConfig {
self.textures.config_mut()
self.buffer.config_mut()
}
pub fn draw(
@@ -737,7 +657,7 @@ impl HiPS2D {
) -> Result<(), JsValue> {
// Get the coo system transformation matrix
let selected_frame = camera.get_coo_system();
let hips_cfg = self.textures.config();
let hips_cfg = self.buffer.config();
let hips_frame = hips_cfg.get_frame();
let c = selected_frame.to(hips_frame);
@@ -768,7 +688,7 @@ impl HiPS2D {
let shader = shader.bind(&self.gl);
shader
.attach_uniforms_from(camera)
.attach_uniforms_from(&self.textures)
.attach_uniforms_from(&self.buffer)
// send the cmap appart from the color config
.attach_uniforms_with_params_from(cmap, colormaps)
.attach_uniforms_from(color)
@@ -796,7 +716,7 @@ impl HiPS2D {
let shader = get_raster_shader(cmap, &self.gl, shaders, &config)?.bind(&self.gl);
shader
.attach_uniforms_from(&self.textures)
.attach_uniforms_from(&self.buffer)
// send the cmap appart from the color config
.attach_uniforms_with_params_from(cmap, colormaps)
.attach_uniforms_from(color)

View File

@@ -2,7 +2,7 @@ use crate::{healpix::cell::HEALPixCell, time::Time};
use std::collections::HashSet;
pub struct Texture {
pub struct HpxTexture2D {
texture_cell: HEALPixCell,
// Precomputed uniq number
uniq: i32,
@@ -36,17 +36,20 @@ pub struct Texture {
use crate::renderable::hips::config::HiPSConfig;
impl Texture {
pub fn new(texture_cell: &HEALPixCell, idx: i32, time_request: Time) -> Texture {
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 = *texture_cell;
let texture_cell = *cell;
let uniq = texture_cell.uniq();
//let missing = true;
let num_tiles_written = 0;
Texture {
Self {
texture_cell,
uniq,
time_request,
@@ -55,14 +58,42 @@ impl Texture {
start_time,
full,
num_tiles_written,
//missing,
}
}
pub fn is_full(&self) -> bool {
self.full
}
pub fn idx(&self) -> i32 {
self.idx
}
// Setter
pub fn replace(&mut self, texture_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.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 /*, missing: bool */) {
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);
@@ -95,19 +126,12 @@ impl Texture {
}
}
}
}
// Cell must be contained in the texture
pub fn contains(&self, cell: &HEALPixCell) -> bool {
self.is_full() || self.tiles.contains(cell)
}
pub fn is_full(&self) -> bool {
self.full
}
impl HpxTile for HpxTexture2D {
// Getter
// Returns the current time if the texture is not full
pub fn start_time(&self) -> Time {
fn start_time(&self) -> Time {
if self.is_full() {
self.start_time.unwrap_abort()
} else {
@@ -115,80 +139,49 @@ impl Texture {
}
}
pub fn time_request(&self) -> Time {
fn time_request(&self) -> Time {
self.time_request
}
pub fn cell(&self) -> &HEALPixCell {
fn cell(&self) -> &HEALPixCell {
&self.texture_cell
}
pub fn idx(&self) -> i32 {
self.idx
}
/*pub fn is_missing(&self) -> bool {
self.missing
}*/
// Setter
pub fn replace(&mut self, texture_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.start_time = None;
self.time_request = time_request;
self.tiles.clear();
//self.missing = true;
self.num_tiles_written = 0;
}
/*pub fn clear_tasks_in_progress(&self, config: &HiPSConfig, exec: &mut TaskExecutor) {
for tile_cell in self.texture_cell.get_tile_cells(config) {
let tile = Tile::new(&tile_cell, config);
exec.remove(&TaskType::ImageTile2GpuTask(tile));
}
}*/
}
use std::cmp::Ordering;
impl PartialOrd for Texture {
impl PartialOrd for HpxTexture2D {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.uniq.partial_cmp(&other.uniq)
}
}
use crate::Abort;
impl Ord for Texture {
impl Ord for HpxTexture2D {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap_abort()
}
}
impl PartialEq for Texture {
impl PartialEq for HpxTexture2D {
fn eq(&self, other: &Self) -> bool {
self.uniq == other.uniq
}
}
impl Eq for Texture {}
impl Eq for HpxTexture2D {}
pub struct TextureUniforms<'a> {
texture: &'a Texture,
pub struct HpxTexture2DUniforms<'a> {
texture: &'a HpxTexture2D,
name: String,
}
impl<'a> TextureUniforms<'a> {
pub fn new(texture: &Texture, idx_texture: i32) -> TextureUniforms {
impl<'a> HpxTexture2DUniforms<'a> {
pub fn new(texture: &'a HpxTexture2D, idx_texture: i32) -> Self {
let name = format!("textures_tiles[{}].", idx_texture);
TextureUniforms { texture, name }
HpxTexture2DUniforms { texture, name }
}
}
use al_core::shader::{SendUniforms, ShaderBound};
impl<'a> SendUniforms for TextureUniforms<'a> {
impl<'a> SendUniforms for HpxTexture2DUniforms<'a> {
fn attach_uniforms<'b>(&self, shader: &'b ShaderBound<'b>) -> &'b ShaderBound<'b> {
shader
.attach_uniform(&format!("{}{}", self.name, "uniq"), &self.texture.uniq)

View File

@@ -1,34 +1,23 @@
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::collections::HashMap;
use al_core::image::format::ChannelType;
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 crate::CameraViewPort;
use crate::LonLatT;
use al_core::image::Image;
use al_core::shader::{SendUniforms, ShaderBound};
use al_core::Texture2DArray;
use al_core::WebGlContext;
use super::texture::HEALPixTexturedCube;
use super::texture::HpxTexture3D;
use crate::downloader::request::allsky::Allsky;
use crate::healpix::cell::HEALPixCell;
use crate::healpix::cell::NUM_HPX_TILES_DEPTH_ZERO;
use crate::math::lonlat::LonLatT;
use crate::renderable::hips::config::HiPSConfig;
use crate::renderable::hips::HpxTileBuffer;
use crate::time::Time;
use crate::Abort;
use crate::JsValue;
use al_api::hips::ImageExt;
// Fixed sized binary heap
pub struct HiPS3DBuffer {
// Some information about the HiPS
textures: HashMap<HEALPixCell, HEALPixTexturedCube>,
textures: HashMap<HEALPixCell, HpxTexture3D>,
config: HiPSConfig,
num_root_textures_available: u8,
@@ -56,58 +45,12 @@ impl HiPS3DBuffer {
})
}
/*
pub fn set_format(&mut self, gl: &WebGlContext, ext: ImageExt) -> Result<(), JsValue> {
self.config.set_image_fmt(ext)?;
let channel = self.config.get_format().get_channel();
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 now = Time::now();
self.base_textures = [
Texture::new(&HEALPixCell(0, 0), 0, now),
Texture::new(&HEALPixCell(0, 1), 1, now),
Texture::new(&HEALPixCell(0, 2), 2, now),
Texture::new(&HEALPixCell(0, 3), 3, now),
Texture::new(&HEALPixCell(0, 4), 4, now),
Texture::new(&HEALPixCell(0, 5), 5, now),
Texture::new(&HEALPixCell(0, 6), 6, now),
Texture::new(&HEALPixCell(0, 7), 7, now),
Texture::new(&HEALPixCell(0, 8), 8, now),
Texture::new(&HEALPixCell(0, 9), 9, now),
Texture::new(&HEALPixCell(0, 10), 10, now),
Texture::new(&HEALPixCell(0, 11), 11, now),
];
self.heap.clear();
self.textures.clear();
//self.ready = false;
self.num_root_textures_available = 0;
self.available_tiles_during_frame = false;
Ok(())
}*/
pub fn push_allsky(&mut self, allsky: Allsky, slice_idx: u16) -> Result<(), JsValue> {
pub fn push_allsky(&mut self, allsky: Allsky) -> Result<(), JsValue> {
let Allsky {
image,
time_req,
depth_tile,
channel,
..
} = allsky;
@@ -119,7 +62,7 @@ impl HiPS3DBuffer {
&HEALPixCell(depth_tile, idx as u64),
image,
time_req,
slice_idx,
channel.map(|c| c as u16).unwrap_or(0),
)?;
}
}
@@ -127,6 +70,10 @@ impl HiPS3DBuffer {
Ok(())
}
pub fn find_nearest_slice(&self, cell: &HEALPixCell, slice: u16) -> Option<u16> {
self.get(cell).and_then(|t| t.find_nearest_slice(slice))
}
// This method pushes a new downloaded tile into the buffer
// It must be ensured that the tile is not already contained into the buffer
pub fn push<I: Image>(
@@ -140,13 +87,13 @@ impl HiPS3DBuffer {
tex
} else {
self.textures
.insert(*cell, HEALPixTexturedCube::new(*cell, time_request));
.insert(*cell, HpxTexture3D::new(*cell, time_request));
self.textures.get_mut(cell).unwrap()
};
// copy to the 3D textured block
tex.append_slice(image, slice_idx, &self.config, &self.gl)?;
tex.append(image, slice_idx, &self.config, &self.gl)?;
self.available_tiles_during_frame = true;
@@ -163,80 +110,16 @@ impl HiPS3DBuffer {
// Tell if a texture is available meaning all its sub tiles
// must have been written for the GPU
pub fn contains(&self, texture_cell: &HEALPixCell) -> bool {
self.get(texture_cell).is_some()
pub fn contains_tile(&self, texture_cell: &HEALPixCell, slice: u16) -> bool {
self.get(texture_cell)
.map_or(false, |t| t.contains_slice(slice))
}
// lonlat is given in the
/*pub fn get_pixel_position_in_texture(
&self,
lonlat: &LonLatT<f64>,
depth: u8,
) -> Result<Vector3<i32>, JsValue> {
let (pix, dx, dy) = crate::healpix::utils::hash_with_dxdy(depth, lonlat);
let texture_cell = HEALPixCell(depth, pix);
if let Some(texture) = self.get(&texture_cell) {
let cfg = &self.config;
// 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 offset = 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 self.config.tex_storing_fits {
let texture_size = self.config.get_texture_size() as f32;
let mut uvy = offset.y as f32 / texture_size;
uvy = self.config.size_tile_uv
+ 2.0 * self.config.size_tile_uv * (uvy / self.config.size_tile_uv).floor()
- uvy;
offset.y = (uvy * texture_size) as i32;
}
Ok(offset)
} else {
Err(JsValue::from_str(&format!(
"{:?} not loaded in the GPU, please wait before trying again.",
texture_cell
)))
}
}*/
/// Accessors
pub fn get(&self, cell: &HEALPixCell) -> Option<&HEALPixTexturedCube> {
pub fn get(&self, cell: &HEALPixCell) -> Option<&HpxTexture3D> {
self.textures.get(cell)
}
// Get the nearest parent tile found in the CPU buffer
pub fn get_nearest_parent(&self, cell: &HEALPixCell) -> Option<HEALPixCell> {
if cell.is_root() {
// Root cells are in the buffer by definition
Some(*cell)
} else {
let mut parent_cell = cell.parent();
while !self.contains(&parent_cell) && !parent_cell.is_root() {
parent_cell = parent_cell.parent();
}
if self.contains(&parent_cell) {
Some(parent_cell)
} else {
None
}
}
}
pub fn config(&self) -> &HiPSConfig {
&self.config
}
@@ -244,10 +127,62 @@ impl HiPS3DBuffer {
pub fn config_mut(&mut self) -> &mut HiPSConfig {
&mut self.config
}
}
/*pub fn get_texture_array(&self) -> &Texture2DArray {
&self.texture_2d_array
}*/
impl HpxTileBuffer for HiPS3DBuffer {
type T = HpxTexture3D;
fn new(gl: &WebGlContext, config: HiPSConfig) -> Result<Self, JsValue> {
let textures = HashMap::new();
let num_root_textures_available = 0;
let available_tiles_during_frame = false;
let gl = gl.clone();
Ok(Self {
config,
num_root_textures_available,
textures,
available_tiles_during_frame,
gl,
})
}
// Return if tiles did become available
fn reset_available_tiles(&mut self) -> bool {
let available_tiles_during_frame = self.available_tiles_during_frame;
self.available_tiles_during_frame = false;
available_tiles_during_frame
}
fn set_image_ext(&mut self, gl: &WebGlContext, ext: ImageExt) -> Result<(), JsValue> {
todo!();
}
fn read_pixel(&self, pos: &LonLatT<f64>, camera: &CameraViewPort) -> Result<JsValue, JsValue> {
todo!();
}
// Tell if a texture is available meaning all its sub tiles
// must have been written for the GPU
fn contains(&self, cell: &HEALPixCell) -> bool {
self.get(cell).is_some()
}
/// Accessors
fn get(&self, cell: &HEALPixCell) -> Option<&HpxTexture3D> {
self.textures.get(cell)
}
fn config(&self) -> &HiPSConfig {
&self.config
}
fn config_mut(&mut self) -> &mut HiPSConfig {
&mut self.config
}
}
/*

View File

@@ -1,2 +1,657 @@
pub mod buffer;
pub mod texture;
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 al_core::image::Image;
use al_core::shader::Shader;
use al_core::webgl_ctx::GlWrapper;
use al_core::VecData;
use al_core::VertexArrayObject;
use al_core::WebGlContext;
use crate::math::{angle::Angle, vector::dist2};
use crate::ProjectionType;
use crate::camera::CameraViewPort;
use crate::downloader::query;
use crate::shader::ShaderManager;
use crate::{math::lonlat::LonLatT, utils};
use crate::downloader::request::allsky::Allsky;
use crate::healpix::{cell::HEALPixCell, coverage::HEALPixCoverage};
use crate::renderable::utils::index_patch::DefaultPatchIndexIter;
use crate::time::Time;
use super::config::HiPSConfig;
use std::collections::HashSet;
// Recursively compute the number of subdivision needed for a cell
// to not be too much skewed
use super::d2::texture::HpxTexture2D;
use buffer::HiPS3DBuffer;
use super::raytracing::RayTracer;
use super::uv::{TileCorner, TileUVW};
use cgmath::Matrix;
use wasm_bindgen::JsValue;
use web_sys::WebGl2RenderingContext;
pub fn get_raster_shader<'a>(
cmap: &Colormap,
gl: &WebGlContext,
shaders: &'a mut ShaderManager,
config: &HiPSConfig,
) -> Result<&'a Shader, JsValue> {
if config.get_format().is_colored() && cmap.label() == "native" {
crate::shader::get_shader(
gl,
shaders,
"hips3d_rasterizer_raster.vert",
"hips3d_rasterizer_color.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",
)
}
}
}
/*
pub fn get_raytracer_shader<'a>(
cmap: &Colormap,
gl: &WebGlContext,
shaders: &'a mut ShaderManager,
config: &HiPSConfig,
) -> Result<&'a Shader, JsValue> {
//let colored_hips = config.is_colored();
if config.get_format().is_colored() && cmap.label() == "native" {
crate::shader::get_shader(
gl,
shaders,
"hips_raytracer_raytracer.vert",
"hips_raytracer_color.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",
)
}
}
}*/
pub struct HiPS3D {
//color: Color,
// The image survey texture buffer
buffer: HiPS3DBuffer,
// The projected vertices data
// For WebGL2 wasm, the data are interleaved
//#[cfg(feature = "webgl2")]
//vertices: Vec<f32>,
//#[cfg(feature = "webgl1")]
// layout (location = 0) in vec3 position;
position: Vec<f32>,
//#[cfg(feature = "webgl1")]
// layout (location = 1) in vec3 uv_start;
uv: Vec<f32>,
idx_vertices: Vec<u16>,
vao: VertexArrayObject,
gl: WebGlContext,
footprint_moc: Option<HEALPixCoverage>,
// A buffer storing the cells in the view
hpx_cells_in_view: Vec<HEALPixCell>,
// The current slice index
slice: u16,
num_indices: Vec<usize>,
slice_indices: Vec<usize>,
cells: Vec<HEALPixCell>,
}
use super::HpxTileBuffer;
impl HiPS3D {
pub fn new(config: HiPSConfig, gl: &WebGlContext) -> Result<Self, JsValue> {
let mut vao = VertexArrayObject::new(gl);
let slice = 0;
let num_indices = vec![];
let slice_indices = vec![];
// layout (location = 0) in vec2 lonlat;
// layout (location = 1) in vec3 position;
// layout (location = 2) in vec3 uv_start;
// layout (location = 3) in vec3 uv_end;
// layout (location = 4) in float time_tile_received;
//let vertices = vec![0.0; MAX_NUM_FLOATS_TO_DRAW];
//let indices = vec![0_u16; MAX_NUM_INDICES_TO_DRAW];
//let vertices = vec![];
let position = vec![];
let uv = vec![];
let idx_vertices = vec![];
#[cfg(feature = "webgl2")]
vao.bind_for_update()
.add_array_buffer_single(
2,
"position",
WebGl2RenderingContext::DYNAMIC_DRAW,
VecData::<f32>(&position),
)
.add_array_buffer_single(
3,
"uv",
WebGl2RenderingContext::DYNAMIC_DRAW,
VecData::<f32>(&uv),
)
// Set the element buffer
.add_element_buffer(
WebGl2RenderingContext::DYNAMIC_DRAW,
VecData::<u16>(&idx_vertices),
)
.unbind();
let buffer = HiPS3DBuffer::new(gl, config)?;
let cells = vec![];
let gl = gl.clone();
let footprint_moc = None;
let hpx_cells_in_view = vec![];
// request the allsky texture
Ok(Self {
// The image survey texture buffer
buffer,
vao,
gl,
position,
uv,
idx_vertices,
footprint_moc,
hpx_cells_in_view,
slice,
cells,
num_indices,
slice_indices,
})
}
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())
.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;
}
already_considered_tiles.insert(*tile_cell);
if let Some(moc) = self.footprint_moc.as_ref() {
moc.intersects_cell(tile_cell)
} else {
true
}
});
Some(tile_cells_iter)
}
pub fn set_slice(&mut self, slice: u16) {
self.slice = slice;
}
pub fn get_tile_query(&self, cell: &HEALPixCell) -> query::Tile {
let cfg = self.get_config();
query::Tile::new(cell, Some(self.get_slice() as u32), cfg)
}
pub fn contains_tile(&self, cell: &HEALPixCell, slice: u16) -> bool {
self.buffer.contains_tile(cell, slice)
}
pub fn draw(
&mut self,
shaders: &mut ShaderManager,
colormaps: &Colormaps,
camera: &mut CameraViewPort,
raytracer: &RayTracer,
cfg: &ImageMetadata,
proj: &ProjectionType,
) -> Result<(), JsValue> {
//let raytracing = camera.is_raytracing(proj);
//if raytracing {
// self.draw_internal(shaders, colormaps, camera, raytracer, cfg, proj)
//} else {
// rasterizer mode
let available_tiles = self.reset_available_tiles();
let new_cells_in_view = self.retrieve_cells_in_camera(camera);
if new_cells_in_view || available_tiles {
// TODO: append the vertices independently to the draw method
self.recompute_vertices(camera, proj);
}
self.draw_internal(shaders, colormaps, camera, raytracer, cfg, proj)
//}
}
fn recompute_vertices(&mut self, camera: &CameraViewPort, proj: &ProjectionType) {
self.cells.clear();
self.slice_indices.clear();
self.position.clear();
self.uv.clear();
self.idx_vertices.clear();
self.num_indices.clear();
let mut off_indices = 0;
let channel = self.get_config().get_format().get_channel();
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) {
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
}
}
} else {
Some(&cell)
};
let mut slice_contained = 0;
if let Some(cell) = cell {
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) {
slice_contained = next_slice;
self.buffer.get(cell)
} else if let Some(parent_cell) = self.buffer.get_nearest_parent(cell) {
// find the slice of the parent available, if possible near slice
slice_contained = self
.buffer
.find_nearest_slice(&parent_cell, self.slice)
.unwrap();
self.buffer.get(&parent_cell)
} else {
None
};
if let Some(texture) = hpx_cell_texture {
self.slice_indices.push(slice_contained as usize);
self.cells.push(texture.cell().clone());
// The slice is sure to be contained so we can unwrap
let hpx_slice_tex = texture.extract_2d_slice_texture(slice_contained).unwrap();
let uv_1 = TileUVW::new(cell, &hpx_slice_tex, self.get_config());
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 num_subdivision =
super::subdivide::num_hpxcell_subdivision(cell, camera, proj);
let n_segments_by_side: usize = 1 << (num_subdivision as usize);
let n_segments_by_side_f32 = n_segments_by_side as f32;
let n_vertices_per_segment = n_segments_by_side + 1;
let mut pos = Vec::with_capacity((n_segments_by_side + 1) * 4);
let grid_lonlat =
healpix::nested::grid(cell.depth(), cell.idx(), n_segments_by_side as u16);
let grid_lonlat_iter = grid_lonlat.iter();
for (idx, &(lon, lat)) in grid_lonlat_iter.enumerate() {
let i: usize = idx / n_vertices_per_segment;
let j: usize = idx % n_vertices_per_segment;
let hj0 = (j as f32) / n_segments_by_side_f32;
let hi0 = (i as f32) / n_segments_by_side_f32;
let uv_end = [
uv_1[TileCorner::BottomLeft].x + hj0 * d01e,
uv_1[TileCorner::BottomLeft].y + hi0 * d02e,
uv_1[TileCorner::BottomLeft].z,
];
self.uv.extend(uv_end);
pos.push([lon as f32, lat as f32]);
}
let patch_indices_iter = DefaultPatchIndexIter::new(
&(0..=n_segments_by_side),
&(0..=n_segments_by_side),
n_vertices_per_segment,
)
.flatten()
.map(|indices| {
[
indices.0 + off_indices,
indices.1 + off_indices,
indices.2 + off_indices,
]
})
.flatten();
let tmp = self.idx_vertices.len();
self.idx_vertices.extend(patch_indices_iter);
self.num_indices.push(self.idx_vertices.len() - tmp);
off_indices += pos.len() as u16;
// Replace options with an arbitrary vertex
let position_iter = pos
.into_iter()
//.map(|ndc| ndc.unwrap_or([0.0, 0.0]))
.flatten();
self.position.extend(position_iter);
}
}
}
{
let mut vao = self.vao.bind_for_update();
vao.update_array(
"position",
WebGl2RenderingContext::DYNAMIC_DRAW,
VecData(&self.position),
)
.update_array(
"uv",
WebGl2RenderingContext::DYNAMIC_DRAW,
VecData(&self.uv),
)
.update_element_array(
WebGl2RenderingContext::DYNAMIC_DRAW,
VecData(&self.idx_vertices),
);
}
}
fn reset_available_tiles(&mut self) -> bool {
self.buffer.reset_available_tiles()
}
// returns a boolean if the view cells has changed with respect to the last frame
fn retrieve_cells_in_camera(&mut self, camera: &CameraViewPort) -> bool {
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 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() {
true
} else {
!self
.hpx_cells_in_view
.iter()
.zip(hpx_cells_in_view.iter())
.all(|(&a, &b)| a == b)
};
self.hpx_cells_in_view = hpx_cells_in_view;
new_cells
}
#[inline]
pub fn set_moc(&mut self, moc: HEALPixCoverage) {
self.footprint_moc = Some(moc);
}
#[inline]
pub fn get_moc(&self) -> Option<&HEALPixCoverage> {
self.footprint_moc.as_ref()
}
pub fn set_image_ext(&mut self, ext: ImageExt) -> Result<(), JsValue> {
self.buffer.set_image_ext(&self.gl, ext)
}
pub fn is_allsky(&self) -> bool {
self.buffer.config().is_allsky
}
// Position given is in the camera space
pub fn read_pixel(
&self,
p: &LonLatT<f64>,
camera: &CameraViewPort,
) -> Result<JsValue, JsValue> {
self.buffer.read_pixel(p, camera)
}
fn draw_internal(
&self,
shaders: &mut ShaderManager,
colormaps: &Colormaps,
camera: &mut CameraViewPort,
raytracer: &RayTracer,
cfg: &ImageMetadata,
proj: &ProjectionType,
) -> Result<(), JsValue> {
let hips_cfg = self.buffer.config();
// Get the coo system transformation matrix
let selected_frame = camera.get_coo_system();
let hips_frame = hips_cfg.get_frame();
let c = selected_frame.to(hips_frame);
let big_fov = camera.is_raytracing(proj);
if big_fov {
self.gl.enable(WebGl2RenderingContext::CULL_FACE);
}
let ImageMetadata {
color,
opacity,
blend_cfg,
..
} = cfg;
let cmap = colormaps.get(color.cmap_name.as_ref());
let v2w = (*camera.get_m2w()) * c.transpose();
// The rasterizer has a buffer containing:
// - The vertices of the HEALPix cells for the most refined survey
// - The starting and ending uv for the blending animation
// - The time for each HEALPix cell at which the animation begins
//
// Each of these data can be changed at different circumstances:
// - The vertices are changed if:
// * new cells are added/removed (because new cells are added)
// to the previous frame.
// - The UVs are changed if:
// * new cells are added/removed (because new cells are added)
// * there are new available tiles for the GPU
let mut off_idx = 0;
for (slice_idx, (cell, num_indices)) in self
.slice_indices
.iter()
.zip(self.cells.iter().zip(self.num_indices.iter()))
{
blend_cfg.enable(&self.gl, || {
let shader = get_raster_shader(cmap, &self.gl, shaders, &hips_cfg)?.bind(&self.gl);
shader
.attach_uniform(
"tex",
self.buffer
.get(cell)
.unwrap()
.get_3d_block_from_slice(*slice_idx as u16)
.unwrap(),
)
.attach_uniforms_with_params_from(cmap, colormaps)
.attach_uniforms_from(color)
.attach_uniforms_from(camera)
.attach_uniform("inv_model", &v2w)
.attach_uniform("opacity", opacity)
.attach_uniform("u_proj", proj)
.attach_uniforms_from(colormaps)
.bind_vertex_array_object_ref(&self.vao)
.draw_elements_with_i32(
WebGl2RenderingContext::TRIANGLES,
Some(*num_indices as i32),
WebGl2RenderingContext::UNSIGNED_SHORT,
(off_idx * std::mem::size_of::<u16>()) as i32,
);
off_idx += (*num_indices) as usize;
Ok(())
})?;
}
if big_fov {
self.gl.disable(WebGl2RenderingContext::CULL_FACE);
}
Ok(())
}
pub fn add_tile<I: Image>(
&mut self,
cell: &HEALPixCell,
image: I,
time_request: Time,
slice_idx: u16,
) -> Result<(), JsValue> {
self.buffer.push(&cell, image, time_request, slice_idx)
}
pub fn add_allsky(&mut self, allsky: Allsky) -> Result<(), JsValue> {
self.buffer.push_allsky(allsky)
}
#[inline]
pub fn get_slice(&self) -> u16 {
self.slice
}
/* Accessors */
#[inline]
pub fn get_config(&self) -> &HiPSConfig {
self.buffer.config()
}
#[inline]
pub fn get_config_mut(&mut self) -> &mut HiPSConfig {
self.buffer.config_mut()
}
}

View File

@@ -1,16 +1,16 @@
use crate::renderable::hips::d2::texture::HpxTexture2D;
use crate::{healpix::cell::HEALPixCell, time::Time};
use al_core::image::format::{
ChannelType, ImageFormatType, R16I, R32F, R32I, R64F, R8UI, RGB32F, RGB8U, RGBA32F, RGBA8U,
ChannelType, R16I, R32F, R32I, R64F, R8UI, RGB32F, RGB8U, RGBA32F, RGBA8U,
};
use al_core::image::Image;
use al_core::texture::Texture3D;
use al_core::webgl_ctx::WebGlRenderingCtx;
use cgmath::Vector3;
use std::collections::HashSet;
use wasm_bindgen::JsValue;
pub struct HEALPixTexturedCube {
pub struct HpxTexture3D {
tile_cell: HEALPixCell,
// Precomputed uniq number
uniq: i32,
@@ -31,50 +31,205 @@ pub struct HEALPixTexturedCube {
// We autorize 512 cubic tiles of size 32 each which allows to store max 16384 slices
textures: Vec<Option<Texture3D>>,
// A set of already inserted slices. Each cubic tiles can have 32 slices. The occupancy of the
// slices inside a cubic tile is done with a u32 mask
slices: [u32; 512],
// slices inside a cubic tile is done with a u32 mask. Limited to 16384 slices
blocks: [u32; 512],
// sorted index list of 32-length blocks that are not empty
block_indices: Vec<usize>,
}
use crate::renderable::hips::config::HiPSConfig;
use crate::WebGlContext;
impl HEALPixTexturedCube {
use crate::renderable::hips::HpxTile;
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 slices = [0; 512];
let blocks = [0; 512];
let block_indices = Vec::new();
Self {
tile_cell,
uniq,
time_request,
start_time,
textures,
slices,
blocks,
block_indices,
}
}
// Get the good cubic texture and the slice idx inside it
pub fn get_cubic_texture_from_slice(&self, slice: u16) -> (Option<&Texture3D>, u8) {
let cube_idx = slice >> 5;
pub fn find_nearest_slice(&self, slice: u16) -> Option<u16> {
let block_idx = (slice >> 5) as usize;
match self.block_indices.binary_search(&block_idx) {
Ok(_) => {
if self.contains_slice(slice) {
Some(slice)
} else {
// the slice is not present but we know there is one in the block
let block = self.blocks[block_idx];
let slice_idx = (slice & 0x1f) as u32;
let m2 = if slice_idx == 31 {
0
} else {
0xffffffff >> (slice_idx + 1)
};
let m1 = (!m2) & !(1 << (31 - slice_idx));
al_core::log(&format!("m1 {:#x} m2 {:#x} {:?}", m1, m2, slice_idx));
let lb = ((block & m1) >> (32 - slice_idx)) as u32;
let rb = (block & m2) as u32;
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;
let no_more_left_bits = slice_idx - (lb_trailing_zeros as u32) == 0;
let no_more_right_bits = slice_idx + (rb_leading_zeros as u32) == 31;
al_core::log(&format!(
"{:?} {:?} slice idx {:?}, {:x?} rb {:?}",
no_more_left_bits,
no_more_right_bits,
slice_idx,
lb,
lb_trailing_zeros as u32
));
match (no_more_left_bits, no_more_right_bits) {
(false, false) => {
if lb_trailing_zeros <= rb_leading_zeros {
Some(slice - lb_trailing_zeros - 1)
} else {
Some(slice + rb_leading_zeros + 1)
}
}
(false, true) => {
if lb_trailing_zeros <= rb_leading_zeros {
Some(slice - lb_trailing_zeros - 1)
} else {
// explore next block
if block_idx == self.blocks.len() - 1 {
// no after block
Some(slice - lb_trailing_zeros - 1)
} else {
// get the next block
let next_block = self.blocks[block_idx + 1];
let num_bits_to_next_block =
next_block.leading_zeros() as u16 + rb_leading_zeros;
if num_bits_to_next_block < lb_trailing_zeros {
Some(slice + num_bits_to_next_block + 1)
} else {
Some(slice - lb_trailing_zeros - 1)
}
}
}
}
(true, false) => {
if rb_leading_zeros <= lb_trailing_zeros {
Some(slice + rb_leading_zeros + 1)
} else {
// explore previous block
if block_idx == 0 {
// no after block
Some(slice + rb_leading_zeros + 1)
} else {
// get the next block
let prev_block = self.blocks[block_idx - 1];
let num_bits_from_prev_block =
prev_block.trailing_zeros() as u16 + lb_trailing_zeros;
if num_bits_from_prev_block < rb_leading_zeros {
Some(slice - num_bits_from_prev_block - 1)
} else {
Some(slice + rb_leading_zeros + 1)
}
}
}
}
(true, true) => unreachable!(),
}
}
}
Err(i) => {
match (self.block_indices.get(i - 1), self.block_indices.get(i)) {
(Some(b_idx_1), Some(b_idx_2)) => {
let b1 = self.blocks[*b_idx_1];
let b2 = self.blocks[*b_idx_2];
let b1_tz = b1.trailing_zeros() as usize;
let b2_lz = b2.leading_zeros() as usize;
let slice_b1 = ((*b_idx_1 << 5) + 32 - b1_tz - 1) as u16;
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)
} else {
// the nearest slice is in b2
Some(slice_b2 as u16)
}
}
(None, Some(b_idx_2)) => {
let b2 = self.blocks[*b_idx_2];
let b2_lz = b2.leading_zeros() as usize;
Some(((*b_idx_2 << 5) + b2_lz) as u16)
}
(Some(b_idx_1), None) => {
let b1 = self.blocks[*b_idx_1];
let b1_tz = b1.trailing_zeros() as usize;
Some(((*b_idx_1 << 5) + 32 - b1_tz - 1) as u16)
}
(None, None) => None,
}
}
}
}
pub fn get_3d_block_from_slice(&self, slice: u16) -> Option<&Texture3D> {
let block_idx = slice >> 5;
self.textures[block_idx as usize].as_ref()
}
pub fn extract_2d_slice_texture(&self, slice: u16) -> Option<HpxTexture2D> {
// Find the good sub cube containing the slice
let block_idx = (slice >> 5) as usize;
let slice_idx = (slice & 0x1f) as u8;
(self.textures[cube_idx as usize].as_ref(), slice_idx)
// check the texture is there
if self.blocks[block_idx] & (1 << (31 - slice_idx)) != 0 {
Some(HpxTexture2D::new(
&self.tile_cell,
slice_idx as i32,
self.time_request,
))
} else {
None
}
}
// 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_slice<I: Image>(
pub fn append<I: Image>(
&mut self,
image: I,
slice: u16,
cfg: &HiPSConfig,
gl: &WebGlContext,
) -> Result<(), JsValue> {
let cube_idx = (slice >> 5) as usize;
let block_idx = (slice >> 5) as usize;
let texture = if let Some(texture) = self.textures[cube_idx as usize].as_ref() {
let texture = if let Some(texture) = self.textures[block_idx as usize].as_ref() {
texture
} else {
let tile_size = cfg.get_tile_size();
@@ -133,16 +288,23 @@ impl HEALPixTexturedCube {
Texture3D::create_empty::<R32I>(gl, tile_size, tile_size, 32, params)
}
};
self.textures[cube_idx] = Some(texture?);
self.textures[block_idx] = Some(texture?);
self.textures[cube_idx].as_ref().unwrap()
self.textures[block_idx].as_ref().unwrap()
};
let slice_idx = slice & 0x1f;
// if there is already something, do not tex sub
if self.slices[cube_idx] & (1 << slice_idx) == 0 {
image.insert_into_3d_texture(texture, &Vector3::<i32>::new(0, 0, slice_idx as i32))?
if self.blocks[block_idx] & (1 << (31 - slice_idx)) == 0 {
image.insert_into_3d_texture(texture, &Vector3::<i32>::new(0, 0, slice_idx as i32))?;
match self.block_indices.binary_search(&block_idx) {
Ok(i) => {} // element already in vector @ `pos`
Err(i) => self.block_indices.insert(i, block_idx),
}
self.blocks[block_idx] |= 1 << (31 - slice_idx);
}
self.start_time = Some(Time::now());
@@ -152,15 +314,17 @@ impl HEALPixTexturedCube {
// Cell must be contained in the texture
pub fn contains_slice(&self, slice: u16) -> bool {
let cube_idx = (slice >> 5) as usize;
let slice_idx = slice & 0x1f;
let block_idx = (slice >> 5) as usize;
let idx_in_block = slice & 0x1f;
self.slices[cube_idx] & (1 << slice_idx) == 1
(self.blocks[block_idx] >> (31 - idx_in_block)) & 0x1 == 1
}
}
impl HpxTile for HpxTexture3D {
// Getter
// Returns the current time if the texture is not full
pub fn start_time(&self) -> Time {
fn start_time(&self) -> Time {
if let Some(t) = self.start_time {
t
} else {
@@ -168,54 +332,38 @@ impl HEALPixTexturedCube {
}
}
pub fn time_request(&self) -> Time {
fn time_request(&self) -> Time {
self.time_request
}
pub fn cell(&self) -> &HEALPixCell {
fn cell(&self) -> &HEALPixCell {
&self.tile_cell
}
// Setter
/*pub fn replace(&mut self, texture_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.start_time = None;
self.time_request = time_request;
self.tiles.clear();
//self.missing = true;
self.num_tiles_written = 0;
}*/
}
use std::cmp::Ordering;
impl PartialOrd for HEALPixTexturedCube {
impl PartialOrd for HpxTexture3D {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.uniq.partial_cmp(&other.uniq)
}
}
use crate::Abort;
impl Ord for HEALPixTexturedCube {
impl Ord for HpxTexture3D {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap_abort()
}
}
impl PartialEq for HEALPixTexturedCube {
impl PartialEq for HpxTexture3D {
fn eq(&self, other: &Self) -> bool {
self.uniq == other.uniq
}
}
impl Eq for HEALPixTexturedCube {}
impl Eq for HpxTexture3D {}
/*
pub struct TextureUniforms<'a> {
texture: &'a HEALPixTexturedCube,
texture: &'a HpxTexture3D,
name: String,
}

View File

@@ -7,3 +7,155 @@ mod triangulation;
pub mod uv;
pub use d2::HiPS2D;
use crate::downloader::request::allsky::Allsky;
use crate::renderable::HiPSConfig;
use crate::time::Time;
use crate::CameraViewPort;
use crate::HEALPixCell;
use crate::HEALPixCoverage;
use crate::LonLatT;
use crate::WebGlContext;
use al_api::hips::ImageExt;
use al_core::image::Image;
use wasm_bindgen::JsValue;
mod subdivide;
trait HpxTile {
// Getter
// Returns the current time if the texture is not full
fn start_time(&self) -> Time;
fn time_request(&self) -> Time;
fn cell(&self) -> &HEALPixCell;
}
pub trait HpxTileBuffer {
type T: HpxTile;
fn new(gl: &WebGlContext, config: HiPSConfig) -> Result<Self, JsValue>
where
Self: Sized;
fn set_image_ext(&mut self, gl: &WebGlContext, ext: ImageExt) -> Result<(), JsValue>;
// Return if tiles did become available
fn reset_available_tiles(&mut self) -> bool;
/// Accessors
fn get(&self, cell: &HEALPixCell) -> Option<&Self::T>;
fn contains(&self, cell: &HEALPixCell) -> bool;
// Get the nearest parent tile found in the CPU buffer
fn get_nearest_parent(&self, cell: &HEALPixCell) -> Option<HEALPixCell> {
/*if cell.is_root() {
// Root cells are in the buffer by definition
Some(*cell)
} else {*/
let mut parent_cell = cell.parent();
while !self.contains(&parent_cell) && !parent_cell.is_root() {
parent_cell = parent_cell.parent();
}
if self.contains(&parent_cell) {
Some(parent_cell)
} else {
None
}
//}
}
fn config_mut(&mut self) -> &mut HiPSConfig;
fn config(&self) -> &HiPSConfig;
fn read_pixel(&self, pos: &LonLatT<f64>, camera: &CameraViewPort) -> Result<JsValue, JsValue>;
}
use crate::downloader::query;
use crate::renderable::hips::HiPS::{D2, D3};
use crate::renderable::HiPS3D;
use crate::ProjectionType;
pub enum HiPS {
D2(HiPS2D),
D3(HiPS3D),
}
impl HiPS {
pub fn look_for_new_tiles(
&mut self,
camera: &CameraViewPort,
proj: &ProjectionType,
) -> 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()),
}
}
// Position given is in the camera space
pub fn read_pixel(
&self,
p: &LonLatT<f64>,
camera: &CameraViewPort,
) -> Result<JsValue, JsValue> {
match self {
D2(hips) => hips.read_pixel(p, camera),
D3(hips) => hips.read_pixel(p, camera),
}
}
#[inline]
pub fn get_config(&self) -> &HiPSConfig {
match self {
D2(hips) => hips.get_config(),
D3(hips) => hips.get_config(),
}
}
#[inline]
pub fn get_config_mut(&mut self) -> &mut HiPSConfig {
match self {
D2(hips) => hips.get_config_mut(),
D3(hips) => hips.get_config_mut(),
}
}
pub fn set_image_ext(&mut self, ext: ImageExt) -> Result<(), JsValue> {
match self {
D2(hips) => hips.set_image_ext(ext),
D3(hips) => hips.set_image_ext(ext),
}
}
#[inline]
pub fn set_moc(&mut self, moc: HEALPixCoverage) {
match self {
D2(hips) => hips.set_moc(moc),
D3(hips) => hips.set_moc(moc),
}
}
#[inline]
pub fn get_tile_query(&self, cell: &HEALPixCell) -> query::Tile {
match self {
HiPS::D2(hips) => hips.get_tile_query(cell),
HiPS::D3(hips) => hips.get_tile_query(cell),
}
}
#[inline]
pub fn add_allsky(&mut self, allsky: Allsky) -> Result<(), JsValue> {
match self {
HiPS::D2(hips) => hips.add_allsky(allsky),
HiPS::D3(hips) => hips.add_allsky(allsky),
}
}
pub fn is_allsky(&self) -> bool {
self.get_config().is_allsky
}
}

View File

@@ -0,0 +1,69 @@
use crate::camera::CameraViewPort;
use crate::math::angle::Angle;
use crate::math::projection::ProjectionType;
use crate::math::vector::dist2;
use crate::HEALPixCell;
const M: f64 = 280.0 * 280.0;
const N: f64 = 150.0 * 150.0;
const RAP: f64 = 0.7;
fn is_too_large(cell: &HEALPixCell, camera: &CameraViewPort, projection: &ProjectionType) -> bool {
let vertices = cell
.vertices()
.iter()
.filter_map(|(lon, lat)| {
let vertex = crate::math::lonlat::radec_to_xyzw(Angle(*lon), Angle(*lat));
projection.icrs_celestial_to_screen_space(&vertex, camera)
})
.collect::<Vec<_>>();
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());
if d1 > M || d2 > M {
true
} else if d1 < N && d2 < N {
false
} else {
let rap = if d2 > d1 { d1 / d2 } else { d2 / d1 };
rap < RAP
}
}
}
pub fn num_hpxcell_subdivision(
cell: &HEALPixCell,
camera: &CameraViewPort,
projection: &ProjectionType,
) -> u8 {
let d = cell.depth();
// Subdivide all cells at least one time.
// TODO: use a single subdivision number computed from the current cells inside the view
// i.e. subdivide all cells in the view with the cell that has to be the most subdivided
let mut num_sub = 1;
if d < 2 {
num_sub = 2 - d;
}
// Largest deformation cell among the cells of a specific depth
let largest_center_to_vertex_dist =
healpix::largest_center_to_vertex_distance(d, 0.0, healpix::TRANSITION_LATITUDE);
let smallest_center_to_vertex_dist =
healpix::largest_center_to_vertex_distance(d, 0.0, healpix::LAT_OF_SQUARE_CELL);
let (lon, lat) = cell.center();
let center_to_vertex_dist = healpix::largest_center_to_vertex_distance(d, lon, lat);
let skewed_factor = (center_to_vertex_dist - smallest_center_to_vertex_dist)
/ (largest_center_to_vertex_dist - smallest_center_to_vertex_dist);
if skewed_factor > 0.25 || is_too_large(cell, camera, projection) || cell.is_on_pole() {
num_sub += 1;
}
num_sub
}

View File

@@ -13,12 +13,13 @@ impl<T> Deref for UV<T> {
}
use super::config::HiPSConfig;
use super::d2::texture::Texture;
use super::d2::texture::HpxTexture2D;
use crate::healpix::cell::HEALPixCell;
use crate::renderable::hips::HpxTile;
pub struct TileUVW([Vector3<f32>; 4]);
impl TileUVW {
// The texture cell passed must be a child of texture
pub fn new(cell: &HEALPixCell, texture: &Texture, cfg: &HiPSConfig) -> TileUVW {
pub fn new(cell: &HEALPixCell, texture: &HpxTexture2D, cfg: &HiPSConfig) -> TileUVW {
// Index of the texture in the total set of textures
let texture_idx = texture.idx();

View File

@@ -14,8 +14,6 @@ use crate::tile_fetcher::TileFetcherQueue;
use al_core::image::format::ChannelType;
pub use hips::HiPS2D;
pub use catalog::Manager;
use al_api::color::ColorRGB;
@@ -43,6 +41,9 @@ use hips::raytracing::RayTracer;
use std::collections::HashMap;
use hips::d2::HiPS2D;
use hips::d3::HiPS3D;
use wasm_bindgen::JsValue;
use web_sys::WebGl2RenderingContext;
@@ -54,10 +55,12 @@ pub trait Renderer {
pub(crate) type Id = String; // ID of an image, can be an url or a uuidv4
pub(crate) type CreatorDid = String;
use hips::HiPS;
type LayerId = String;
pub struct Layers {
// Surveys to query
surveys: HashMap<CreatorDid, HiPS2D>,
hipses: HashMap<CreatorDid, HiPS>,
images: HashMap<Id, Vec<Image>>, // an url can contain multiple images i.e. a fits file can contain
// multiple image extensions
// The meta data associated with a layer
@@ -121,7 +124,8 @@ impl ImageLayer {
impl Layers {
pub fn new(gl: &WebGlContext, projection: &ProjectionType) -> Result<Self, JsValue> {
let surveys = HashMap::new();
let hipses = HashMap::new();
let images = HashMap::new();
let meta = HashMap::new();
let ids = HashMap::new();
@@ -155,7 +159,7 @@ impl Layers {
let background_color = DEFAULT_BACKGROUND_COLOR;
Ok(Layers {
surveys,
hipses,
images,
meta,
@@ -171,19 +175,10 @@ impl Layers {
})
}
pub fn set_survey_url(&mut self, cdid: &CreatorDid, new_url: String) -> Result<(), JsValue> {
if let Some(survey) = self.surveys.get_mut(cdid) {
pub fn set_hips_url(&mut self, cdid: &CreatorDid, new_url: String) -> Result<(), JsValue> {
if let Some(hips) = self.hipses.get_mut(cdid) {
// update the root_url
survey.get_config_mut().set_root_url(new_url.clone());
//self.surveys.insert(new_url.clone(), survey);
// update all the layer urls
/*for id in self.ids.values_mut() {
if *id == past_url {
*id = new_url.clone();
}
}*/
hips.get_config_mut().set_root_url(new_url.clone());
Ok(())
} else {
@@ -192,8 +187,8 @@ impl Layers {
}
/*pub fn reset_frame(&mut self) {
for survey in self.surveys.values_mut() {
survey.reset_frame();
for hips in self.hips.values_mut() {
hips.reset_frame();
}
}*/
@@ -221,15 +216,15 @@ impl Layers {
let raytracer = &self.raytracer;
let raytracing = camera.is_raytracing(projection);
// Check whether a survey to plot is allsky
// Check whether a hips to plot is allsky
// if neither are, we draw a font
// if there are, we do not draw nothing
let render_background_color = !self.layers.iter().any(|layer| {
let meta = self.meta.get(layer).unwrap_abort();
let cdid = self.ids.get(layer).unwrap_abort();
if let Some(survey) = self.surveys.get(cdid) {
let hips_cfg = survey.get_config();
(survey.is_allsky() || hips_cfg.get_format().get_channel() == ChannelType::RGB8U)
if let Some(hips) = self.hipses.get(cdid) {
let hips_cfg = hips.get_config();
(hips.is_allsky() || hips_cfg.get_format().get_channel() == ChannelType::RGB8U)
&& meta.opacity == 1.0
} else {
// image fits case
@@ -268,13 +263,13 @@ impl Layers {
let meta = self.meta.get(layer).expect("Meta should be found");
let id = self.ids.get(layer).expect("Url should be found");
if let Some(survey) = self.surveys.get_mut(id) {
let hips_cfg = survey.get_config();
if let Some(hips) = self.hipses.get_mut(id) {
let hips_cfg = hips.get_config();
let fully_covering_survey = (survey.is_allsky()
let fully_covering_hips = (hips.is_allsky()
|| hips_cfg.get_format().get_channel() == ChannelType::RGB8U)
&& meta.opacity == 1.0;
if fully_covering_survey {
if fully_covering_hips {
idx_start_layer = idx_layer;
}
}
@@ -284,13 +279,19 @@ impl Layers {
for layer in rendered_layers {
let draw_opt = self.meta.get(layer).expect("Meta should be found");
if draw_opt.visible() {
// 1. Update the survey if necessary
// 1. Update the hips if necessary
let id = self.ids.get(layer).expect("Url should be found");
if let Some(survey) = self.surveys.get_mut(id) {
survey.update(camera, projection);
if let Some(hips) = self.hipses.get_mut(id) {
match hips {
HiPS::D2(hips) => {
hips.update(camera, projection);
// 2. Draw it if its opacity is not null
survey.draw(shaders, colormaps, camera, raytracer, draw_opt, projection)?;
hips.draw(shaders, colormaps, camera, raytracer, draw_opt, projection)?;
}
HiPS::D3(hips) => {
hips.draw(shaders, colormaps, camera, raytracer, draw_opt, projection)?;
}
}
} else if let Some(images) = self.images.get_mut(id) {
// 2. Draw it if its opacity is not null
for image in images {
@@ -338,14 +339,14 @@ impl Layers {
Ok(id_layer)
} else {
// Resource not needed anymore
if let Some(s) = self.surveys.remove(&id) {
if let Some(hips) = self.hipses.remove(&id) {
// A HiPS has been found and removed
let hips_frame = s.get_config().get_frame();
let hips_frame = hips.get_config().get_frame();
// remove the frame
camera.unregister_view_frame(hips_frame, proj);
// remove the local files access from the tile fetcher
tile_fetcher.delete_hips_local_files(s.get_config().get_creator_did());
tile_fetcher.delete_hips_local_files(hips.get_config().get_creator_did());
Ok(id_layer)
} else if let Some(_) = self.images.remove(&id) {
@@ -353,7 +354,7 @@ impl Layers {
Ok(id_layer)
} else {
Err(JsValue::from_str(&format!(
"Url found {:?} is associated to no surveys.",
"Url found {:?} is associated to no 2D HiPSes.",
id
)))
}
@@ -408,14 +409,14 @@ impl Layers {
Ok(())
}
pub fn add_image_hips(
pub fn add_hips(
&mut self,
gl: &WebGlContext,
hips: HiPSCfg,
camera: &mut CameraViewPort,
proj: &ProjectionType,
tile_fetcher: &mut TileFetcherQueue,
) -> Result<&HiPS2D, JsValue> {
) -> Result<&HiPS, JsValue> {
let HiPSCfg {
layer,
properties,
@@ -444,13 +445,13 @@ impl Layers {
camera.set_longitude_reversed(longitude_reversed, proj);
// 3. Add the image survey
// 3. Add the image hips
let creator_did = String::from(properties.get_creator_did());
// The layer does not already exist
// Let's check if no other hipses points to the
// same url than `hips`
let cdid_already_found = self
.surveys
.hipses
.keys()
.any(|hips_cdid| hips_cdid == &creator_did);
@@ -469,16 +470,21 @@ impl Layers {
}*/
camera.register_view_frame(cfg.get_frame(), proj);
let hips = HiPS2D::new(cfg, gl)?;
// add the frame to the camera
let hips = if cfg.get_cube_depth().is_some() {
// HiPS cube
HiPS::D3(HiPS3D::new(cfg, gl)?)
} else {
HiPS::D2(HiPS2D::new(cfg, gl)?)
};
self.surveys.insert(creator_did.clone(), hips);
// add the frame to the camera
self.hipses.insert(creator_did.clone(), hips);
}
self.ids.insert(layer.clone(), creator_did.clone());
let hips = self
.surveys
.hipses
.get(&creator_did)
.ok_or(JsValue::from_str("HiPS not found"))?;
Ok(hips)
@@ -559,15 +565,15 @@ impl Layers {
&mut self,
layer: String,
meta: ImageMetadata,
camera: &mut CameraViewPort,
camera: &CameraViewPort,
projection: &ProjectionType,
) -> Result<(), JsValue> {
let layer_ref = layer.as_str();
/*if let Some(meta_old) = self.meta.get(layer_ref) {
if !meta_old.visible() && meta.visible() {
if let Some(survey) = self.get_mut_hips_from_layer(layer_ref) {
survey.recompute_vertices(camera, projection);
if let Some(hips) = self.get_mut_hips_from_layer(layer_ref) {
hips.recompute_vertices(camera, projection);
}
if let Some(images) = self.get_mut_image_from_layer(layer_ref) {
@@ -587,8 +593,8 @@ impl Layers {
for idx in 0..layer_idx {
let cur_layer = self.layers[idx].clone();
if let Some(survey) = self.get_mut_hips_from_layer(&cur_layer) {
survey.recompute_vertices(camera, projection);
if let Some(hips) = self.get_mut_hips_from_layer(&cur_layer) {
hips.recompute_vertices(camera, projection);
} else if let Some(images) = self.get_mut_image_from_layer(&cur_layer) {
for image in images {
image.recompute_vertices(camera, projection)?;
@@ -598,7 +604,7 @@ impl Layers {
}
}*/
// Expect the image survey to be found in the hash map
// Expect the image hips to be found in the hash map
self.meta.insert(layer.clone(), meta).ok_or_else(|| {
JsValue::from(js_sys::Error::new(&format!("{:?} layer not found", layer)))
})?;
@@ -608,35 +614,27 @@ impl Layers {
// Accessors
// HiPSes getters
pub fn get_hips_from_layer(&self, layer: &str) -> Option<&HiPS2D> {
pub fn get_hips_from_layer(&self, layer: &str) -> Option<&HiPS> {
self.ids
.get(layer)
.map(|cdid| self.surveys.get(cdid))
.map(|cdid| self.hipses.get(cdid))
.flatten()
}
pub fn get_mut_hips_from_layer(&mut self, layer: &str) -> Option<&mut HiPS2D> {
pub fn get_mut_hips_from_layer(&mut self, layer: &str) -> Option<&mut HiPS> {
if let Some(cdid) = self.ids.get_mut(layer) {
self.surveys.get_mut(cdid)
self.hipses.get_mut(cdid)
} else {
None
}
}
pub fn get_mut_hips_from_cdid(&mut self, cdid: &str) -> Option<&mut HiPS2D> {
self.surveys.get_mut(cdid)
pub fn get_mut_hips_from_cdid(&mut self, cdid: &str) -> Option<&mut HiPS> {
self.hipses.get_mut(cdid)
}
pub fn get_hips_from_cdid(&mut self, cdid: &str) -> Option<&HiPS2D> {
self.surveys.get(cdid)
}
pub fn values_hips(&self) -> impl Iterator<Item = &HiPS2D> {
self.surveys.values()
}
pub fn values_mut_hips(&mut self) -> impl Iterator<Item = &mut HiPS2D> {
self.surveys.values_mut()
pub fn get_mut_hipses(&mut self) -> impl Iterator<Item = &mut HiPS> {
self.hipses.values_mut()
}
// Fits images getters

View File

@@ -1,5 +1,4 @@
use crate::downloader::{query, Downloader};
use crate::renderable::HiPS2D;
use crate::time::{DeltaTime, Time};
use crate::Abort;
@@ -10,6 +9,8 @@ use std::rc::Rc;
const MAX_NUM_TILE_FETCHING: usize = 8;
const MAX_QUERY_QUEUE_LENGTH: usize = 100;
use crate::renderable::hips::HiPS;
pub struct TileFetcherQueue {
// A stack of queries to fetch
queries: VecDeque<query::Tile>,
@@ -30,7 +31,6 @@ pub struct HiPSLocalFiles {
use crate::tile_fetcher::query::Tile;
use crate::HEALPixCell;
use al_api::hips::ImageExt;
use al_core::image::format::ImageFormatType;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;
@@ -52,13 +52,7 @@ impl HiPSLocalFiles {
}
pub fn insert(&mut self, depth: u8, ipix: u64, ext: ImageExt, file: web_sys::File) {
let mut tiles_per_fmt = match ext {
ImageExt::Fits => &mut self.tiles[0],
ImageExt::Jpeg => &mut self.tiles[1],
ImageExt::Png => &mut self.tiles[2],
ImageExt::Webp => &mut self.tiles[3],
};
let tiles_per_fmt = &mut self.tiles[ext as usize];
tiles_per_fmt[depth as usize].insert(ipix, file);
}
@@ -190,7 +184,7 @@ impl TileFetcherQueue {
pub fn launch_starting_hips_requests(
&mut self,
hips: &HiPS2D,
hips: &HiPS,
downloader: Rc<RefCell<Downloader>>,
) {
let cfg = hips.get_config();
@@ -221,7 +215,13 @@ impl TileFetcherQueue {
//Request the allsky for the small tile size or if base tiles are not available
if tile_size <= 128 || cfg.get_min_depth_tile() > 0 {
// Request the allsky
downloader.borrow_mut().fetch(query::Allsky::new(cfg));
downloader.borrow_mut().fetch(query::Allsky::new(
cfg,
match hips {
HiPS::D2(_) => None,
HiPS::D3(h) => Some(h.get_slice() as u32),
},
));
} else if cfg.get_min_depth_tile() == 0 {
#[cfg(target_arch = "wasm32")]
{
@@ -231,13 +231,7 @@ impl TileFetcherQueue {
let min_order = cfg.get_min_depth_texture();
for tile_cell in crate::healpix::cell::ALLSKY_HPX_CELLS_D0 {
if let Ok(query) = self.check_in_file_list(query::Tile::new(
tile_cell,
hips_cdid.clone(),
hips_url.clone(),
hips_fmt,
None,
)) {
if let Ok(query) = self.check_in_file_list(hips.get_tile_query(tile_cell)) {
let dl = downloader.clone();
crate::utils::set_timeout(

View File

@@ -0,0 +1,21 @@
#version 300 es
precision lowp float;
precision lowp sampler3D;
precision lowp isampler3D;
precision lowp usampler3D;
uniform sampler3D tex;
in vec3 frag_uv;
out vec4 out_frag_color;
uniform float opacity;
#include ../../hips/color.glsl;
void main() {
vec4 color = get_color_from_texture(vec3(frag_uv.xy, mod(frag_uv.z, 32.0) / 32.0));
out_frag_color = color;
out_frag_color.a = opacity * out_frag_color.a;
}

View File

@@ -0,0 +1,22 @@
#version 300 es
precision lowp float;
precision lowp sampler3D;
precision lowp isampler3D;
precision lowp usampler3D;
uniform sampler3D tex;
in vec3 frag_uv;
out vec4 out_frag_color;
#include ../../hips/color.glsl;
uniform float opacity;
void main() {
vec4 color = get_colormap_from_grayscale_texture(frag_uv);
out_frag_color = color;
out_frag_color.a = out_frag_color.a * opacity;
}

View File

@@ -0,0 +1,22 @@
#version 300 es
precision lowp float;
precision lowp sampler3D;
precision lowp isampler3D;
precision lowp usampler3D;
uniform isampler3D tex;
in vec3 frag_uv;
out vec4 out_frag_color;
#include ../../hips/color_i.glsl;
uniform float opacity;
void main() {
vec4 color = get_colormap_from_grayscale_texture(frag_uv);
out_frag_color = color;
out_frag_color.a = out_frag_color.a * opacity;
}

View File

@@ -0,0 +1,22 @@
#version 300 es
precision lowp float;
precision lowp sampler3D;
precision lowp isampler3D;
precision lowp usampler3D;
uniform usampler3D tex;
in vec3 frag_uv;
out vec4 out_frag_color;
#include ../../hips/color_u.glsl;
uniform float opacity;
void main() {
vec4 color = get_colormap_from_grayscale_texture(frag_uv);
out_frag_color = color;
out_frag_color.a = out_frag_color.a * opacity;
}

View File

@@ -0,0 +1,26 @@
#version 300 es
precision lowp float;
layout (location = 0) in vec2 lonlat;
layout (location = 1) in vec3 uv;
out vec3 frag_uv;
// current time in ms
uniform mat4 inv_model;
uniform vec2 ndc_to_clip;
uniform float czf;
#include ../../projection/projection.glsl;
void main() {
vec3 p_xyz = lonlat2xyz(lonlat);
vec4 p_w = inv_model * vec4(p_xyz, 1.0);
// 3. Process the projection
vec2 p_clip = proj(p_w.xyz);
vec2 p_ndc = p_clip / (ndc_to_clip * czf);
gl_Position = vec4(p_ndc, 0.0, 1.0);
frag_uv = uv;
}

View File

@@ -287,7 +287,9 @@ export let HiPS = (function () {
HiPS.prototype._parseProperties = function(properties) {
let self = this;
self.creatorDid = properties.creator_did || self.creatorDid;
// url
// Cube depth
self.cubeDepth = properties && properties.hips_cube_depth && +properties.hips_cube_depth;
// Max order
self.maxOrder =
@@ -704,6 +706,12 @@ export let HiPS = (function () {
this.setOptions({contrast})
};
HiPS.prototype.setSliceNumber = function(slice) {
if (this.added) {
this.view.wasm.setSliceNumber(this.layer, slice);
}
}
// Private method for updating the backend with the new meta
HiPS.prototype._updateMetadata = function () {
try {
@@ -960,6 +968,7 @@ export let HiPS = (function () {
hipsInitialFov: self.initialFov,
hipsInitialRa: self.initialRa,
hipsInitialDec: self.initialDec,
hipsCubeDepth: self.cubeDepth,
isPlanetaryBody: self.isPlanetaryBody(),
hipsBody: self.hipsBody,
},