diff --git a/examples/al-moc-json.html b/examples/al-moc-json.html index dece85da..bc995fa4 100644 --- a/examples/al-moc-json.html +++ b/examples/al-moc-json.html @@ -24,8 +24,14 @@ //var json = {"3":[517], //"4":[2065, 2067]}; - var moc = A.MOCFromJSON(json, {opacity: 0.5, color: 'magenta', lineWidth: 1, adaptativeDisplay: false}); + var moc = A.MOCFromJSON(json, {opacity: 0.5, color: 'magenta', lineWidth: 1, fill: true}); aladin.addMOC(moc); + + // Change the moc options after its creation + setTimeout(() => { + moc.opacity = 0.2 + moc.fillColor = "orange" + }, 3000) }); diff --git a/src/core/Cargo.toml b/src/core/Cargo.toml index 826c7ce9..e8d31750 100644 --- a/src/core/Cargo.toml +++ b/src/core/Cargo.toml @@ -88,4 +88,4 @@ codegen-units = 16 rpath = false [package.metadata.wasm-pack.profile.release] -wasm-opt = true +wasm-opt = false diff --git a/src/core/al-api/src/moc.rs b/src/core/al-api/src/moc.rs index e5673696..68083800 100644 --- a/src/core/al-api/src/moc.rs +++ b/src/core/al-api/src/moc.rs @@ -4,7 +4,7 @@ use super::color::{Color, ColorRGBA}; #[derive(Clone, Debug)] #[wasm_bindgen] -pub struct MOC { +pub struct MOCOptions { uuid: String, pub line_width: f32, pub perimeter: bool, @@ -18,7 +18,7 @@ pub struct MOC { use crate::{color::ColorRGB, Abort}; use std::convert::TryInto; #[wasm_bindgen] -impl MOC { +impl MOCOptions { #[wasm_bindgen(constructor)] pub fn new( uuid: String, @@ -58,13 +58,13 @@ impl MOC { } } -impl MOC { +impl MOCOptions { pub fn get_uuid(&self) -> &String { &self.uuid } } -impl Default for MOC { +impl Default for MOCOptions { fn default() -> Self { Self { uuid: String::from("moc"), diff --git a/src/core/src/app.rs b/src/core/src/app.rs index 8ed59439..4927d7bd 100644 --- a/src/core/src/app.rs +++ b/src/core/src/app.rs @@ -20,6 +20,7 @@ use crate::{ tile_fetcher::TileFetcherQueue, time::DeltaTime, }; +use al_api::moc::MOCOptions; use crate::math::angle::ToAngle; use al_core::image::format::ChannelType; use wcs::WCS; @@ -477,25 +478,25 @@ impl App { self.catalog_loaded } - pub(crate) fn get_moc(&self, cfg: &al_api::moc::MOC) -> Option<&HEALPixCoverage> { - self.moc.get_hpx_coverage(cfg) + pub(crate) fn get_moc(&self, moc_uuid: &str) -> Option<&HEALPixCoverage> { + self.moc.get_hpx_coverage(moc_uuid) } pub(crate) fn add_moc( &mut self, - cfg: al_api::moc::MOC, moc: HEALPixCoverage, + options: MOCOptions, ) -> Result<(), JsValue> { self.moc - .push_back(moc, cfg, &mut self.camera, &self.projection); + .push_back(moc, options, &mut self.camera, &self.projection); self.request_redraw = true; Ok(()) } - pub(crate) fn remove_moc(&mut self, cfg: &al_api::moc::MOC) -> Result<(), JsValue> { + pub(crate) fn remove_moc(&mut self, moc_uuid: &str) -> Result<(), JsValue> { self.moc - .remove(cfg, &mut self.camera, &self.projection) + .remove(moc_uuid, &mut self.camera, &self.projection) .ok_or_else(|| JsValue::from_str("MOC not found"))?; self.request_redraw = true; @@ -503,9 +504,9 @@ impl App { Ok(()) } - pub(crate) fn set_moc_cfg(&mut self, cfg: al_api::moc::MOC) -> Result<(), JsValue> { + pub(crate) fn set_moc_options(&mut self, options: MOCOptions) -> Result<(), JsValue> { self.moc - .set_cfg(cfg, &mut self.camera, &self.projection, &mut self.shaders) + .set_options(options, &mut self.camera, &self.projection, &mut self.shaders) .ok_or_else(|| JsValue::from_str("MOC not found"))?; self.request_redraw = true; diff --git a/src/core/src/downloader/query.rs b/src/core/src/downloader/query.rs index 314ce036..19b38c20 100644 --- a/src/core/src/downloader/query.rs +++ b/src/core/src/downloader/query.rs @@ -176,16 +176,16 @@ impl Query for PixelMetadata { &self.id } } - +use al_api::moc::MOCOptions; /* ---------------------------------- */ pub struct Moc { // The total url of the query pub url: Url, - pub params: al_api::moc::MOC, + pub params: MOCOptions, pub hips_cdid: CreatorDid, } impl Moc { - pub fn new(url: String, hips_cdid: CreatorDid, params: al_api::moc::MOC) -> Self { + pub fn new(url: String, hips_cdid: CreatorDid, params: MOCOptions) -> Self { Moc { url, params, diff --git a/src/core/src/downloader/request/moc.rs b/src/core/src/downloader/request/moc.rs index 459b148e..c5e81fce 100644 --- a/src/core/src/downloader/request/moc.rs +++ b/src/core/src/downloader/request/moc.rs @@ -10,7 +10,7 @@ use moclib::qty::Hpx; pub struct MOCRequest { //pub id: QueryId, pub hips_cdid: CreatorDid, - pub params: al_api::moc::MOC, + pub params: MOCOptions, request: Request, } @@ -41,6 +41,7 @@ pub fn from_fits_hpx(moc: MocType, Cursor<&[u8]>>) -> Smoc { use crate::healpix::coverage::HEALPixCoverage; use crate::Abort; +use al_api::moc::MOCOptions; use moclib::deser::fits::MocIdxType; use moclib::deser::fits::MocQtyType; use moclib::idx::Idx; @@ -106,7 +107,7 @@ use std::cell::RefCell; use std::rc::Rc; pub struct Moc { pub moc: Rc>>, - pub params: al_api::moc::MOC, + pub params: MOCOptions, pub hips_cdid: Url, } diff --git a/src/core/src/lib.rs b/src/core/src/lib.rs index 38309b62..e42ad536 100644 --- a/src/core/src/lib.rs +++ b/src/core/src/lib.rs @@ -90,6 +90,7 @@ use moclib::moc::RangeMOCIntoIterator; use crate::tile_fetcher::HiPSLocalFiles; use wasm_bindgen::prelude::*; use web_sys::HtmlElement; +use al_api::moc::MOCOptions; use crate::math::angle::ToAngle; @@ -1014,7 +1015,7 @@ impl WebClient { #[wasm_bindgen(js_name = addJSONMoc)] pub fn add_json_moc( &mut self, - params: &al_api::moc::MOC, + options: MOCOptions, data: &JsValue, ) -> Result<(), JsValue> { let str: String = js_sys::JSON::stringify(data)?.into(); @@ -1025,13 +1026,13 @@ impl WebClient { .ranges() .into_range_moc(); - self.app.add_moc(params.clone(), HEALPixCoverage(moc))?; + self.app.add_moc(HEALPixCoverage(moc), options)?; Ok(()) } #[wasm_bindgen(js_name = addFITSMOC)] - pub fn add_fits_moc(&mut self, params: &al_api::moc::MOC, data: &[u8]) -> Result<(), JsValue> { + pub fn add_fits_moc(&mut self, options: MOCOptions, data: &[u8]) -> Result<(), JsValue> { //let bytes = js_sys::Uint8Array::new(array_buffer).to_vec(); let moc = match fits::from_fits_ivoa_custom(Cursor::new(&data[..]), false) .map_err(|e| JsValue::from_str(&e.to_string()))? @@ -1044,7 +1045,7 @@ impl WebClient { _ => Err(JsValue::from_str("MOC not supported. Must be a HPX MOC")), }?; - self.app.add_moc(params.clone(), HEALPixCoverage(moc))?; + self.app.add_moc(HEALPixCoverage(moc), options)?; Ok(()) } @@ -1052,7 +1053,7 @@ impl WebClient { #[wasm_bindgen(js_name = addConeMOC)] pub fn add_cone_moc( &mut self, - params: &al_api::moc::MOC, + options: MOCOptions, ra_deg: f64, dec_deg: f64, rad_deg: f64, @@ -1068,7 +1069,7 @@ impl WebClient { pixel_d as u8 - 1, ); - self.app.add_moc(params.clone(), moc)?; + self.app.add_moc(moc, options)?; Ok(()) } @@ -1076,7 +1077,7 @@ impl WebClient { #[wasm_bindgen(js_name = addPolyMOC)] pub fn add_poly_moc( &mut self, - params: &al_api::moc::MOC, + params: MOCOptions, ra_deg: &[f64], dec_deg: &[f64], ) -> Result<(), JsValue> { @@ -1098,21 +1099,21 @@ impl WebClient { moc = moc.not(); } - self.app.add_moc(params.clone(), moc)?; + self.app.add_moc(moc, params)?; Ok(()) } #[wasm_bindgen(js_name = removeMoc)] - pub fn remove_moc(&mut self, params: &al_api::moc::MOC) -> Result<(), JsValue> { - self.app.remove_moc(params)?; + pub fn remove_moc(&mut self, moc_uuid: String) -> Result<(), JsValue> { + self.app.remove_moc(&moc_uuid)?; Ok(()) } #[wasm_bindgen(js_name = setMocParams)] - pub fn set_moc_cfg(&mut self, cfg: &al_api::moc::MOC) -> Result<(), JsValue> { - self.app.set_moc_cfg(cfg.clone())?; + pub fn set_moc_options(&mut self, options: MOCOptions) -> Result<(), JsValue> { + self.app.set_moc_options(options)?; Ok(()) } @@ -1120,13 +1121,13 @@ impl WebClient { #[wasm_bindgen(js_name = mocContains)] pub fn moc_contains( &mut self, - params: &al_api::moc::MOC, + moc_uuid: String, lon: f64, lat: f64, ) -> Result { let moc = self .app - .get_moc(params) + .get_moc(&moc_uuid) .ok_or_else(|| JsValue::from(js_sys::Error::new("MOC not found")))?; let location = LonLatT::new(ArcDeg(lon).into(), ArcDeg(lat).into()); @@ -1136,12 +1137,12 @@ impl WebClient { #[wasm_bindgen(js_name = mocSerialize)] pub fn moc_serialize( &mut self, - params: &al_api::moc::MOC, + moc_uuid: String, _format: String, // todo support the fits/ascii serialization ) -> Result { let moc = self .app - .get_moc(params) + .get_moc(&moc_uuid) .ok_or_else(|| JsValue::from(js_sys::Error::new("MOC not found")))?; let mut buf: Vec = Default::default(); @@ -1156,8 +1157,8 @@ impl WebClient { } #[wasm_bindgen(js_name = getMOCSkyFraction)] - pub fn get_moc_sky_fraction(&mut self, params: &al_api::moc::MOC) -> f32 { - if let Some(moc) = self.app.get_moc(params) { + pub fn get_moc_sky_fraction(&mut self, moc_uuid: String) -> f32 { + if let Some(moc) = self.app.get_moc(&moc_uuid) { moc.sky_fraction() as f32 } else { 0.0 diff --git a/src/core/src/renderable/moc/hierarchy.rs b/src/core/src/renderable/moc/hierarchy.rs index e3698cf6..b5e90d74 100644 --- a/src/core/src/renderable/moc/hierarchy.rs +++ b/src/core/src/renderable/moc/hierarchy.rs @@ -1,26 +1,34 @@ use super::MOC; use crate::{camera::CameraViewPort, HEALPixCoverage}; -use al_api::moc::MOC as Cfg; +use al_api::moc::MOCOptions; pub struct MOCHierarchy { full_res_depth: u8, // MOC at different resolution mocs: Vec, + gl: WebGlContext, } use al_core::WebGlContext; impl MOCHierarchy { - pub fn from_full_res_moc(gl: WebGlContext, full_res_moc: HEALPixCoverage, cfg: &Cfg) -> Self { + pub fn from_full_res_moc(gl: WebGlContext, full_res_moc: HEALPixCoverage, options: &MOCOptions) -> Self { let full_res_depth = full_res_moc.depth(); let mut mocs: Vec<_> = (0..full_res_depth) - .map(|d| MOC::new(gl.clone(), HEALPixCoverage(full_res_moc.degraded(d)), cfg)) + .map(|d| MOC::new(gl.clone(), HEALPixCoverage(full_res_moc.degraded(d)), options)) .collect(); - mocs.push(MOC::new(gl, full_res_moc, cfg)); + mocs.push(MOC::new(gl.clone(), full_res_moc, options)); Self { mocs, full_res_depth, + gl, + } + } + + pub fn set_options(&mut self, options: &MOCOptions) { + for moc in &mut self.mocs { + moc.set_options(&options, self.gl.clone()); } } diff --git a/src/core/src/renderable/moc/mod.rs b/src/core/src/renderable/moc/mod.rs index 3da8a4ca..f25e2791 100644 --- a/src/core/src/renderable/moc/mod.rs +++ b/src/core/src/renderable/moc/mod.rs @@ -7,7 +7,7 @@ use crate::healpix::coverage::HEALPixCoverage; use crate::math::projection::ProjectionType; use crate::renderable::WebGl2RenderingContext; use crate::shader::ShaderManager; -use al_api::moc::MOC as Cfg; +use al_api::moc::MOCOptions; use wasm_bindgen::JsValue; @@ -36,7 +36,7 @@ pub struct MOC { } impl MOC { - pub(super) fn new(gl: WebGlContext, moc: HEALPixCoverage, cfg: &Cfg) -> Self { + pub(super) fn new(gl: WebGlContext, moc: HEALPixCoverage, cfg: &MOCOptions) -> Self { let sky_fraction = moc.sky_fraction() as f32; let max_order = moc.depth_max(); @@ -112,6 +112,47 @@ impl MOC { num_vertices }*/ + pub fn set_options(&mut self, cfg: &MOCOptions, gl: WebGlContext) { + let inner = [ + if cfg.perimeter { + // draw only perimeter + Some(MOCIntern::new( + gl.clone(), + RenderModeType::Perimeter { + thickness: cfg.line_width, + color: cfg.color, + }, + )) + } else { + None + }, + if cfg.filled { + // change color + let fill_color = cfg.fill_color; + // draw the edges + Some(MOCIntern::new( + gl.clone(), + RenderModeType::Filled { color: fill_color }, + )) + } else { + None + }, + if cfg.edges { + Some(MOCIntern::new( + gl, + RenderModeType::Edge { + thickness: cfg.line_width, + color: cfg.color, + }, + )) + } else { + None + }, + ]; + + self.inner = inner; + } + pub fn sky_fraction(&self) -> f32 { self.sky_fraction } diff --git a/src/core/src/renderable/moc/renderer.rs b/src/core/src/renderable/moc/renderer.rs index 8f70863f..1bc34576 100644 --- a/src/core/src/renderable/moc/renderer.rs +++ b/src/core/src/renderable/moc/renderer.rs @@ -6,11 +6,11 @@ use super::hierarchy::MOCHierarchy; use al_api::coo_system::CooSystem; -use al_api::moc::MOC as Cfg; +use al_api::moc::MOCOptions; pub struct MOCRenderer { mocs: Vec, - cfgs: Vec, + cfgs: Vec, gl: WebGlContext, } @@ -68,7 +68,7 @@ impl MOCRenderer { pub fn push_back( &mut self, moc: HEALPixCoverage, - cfg: Cfg, + cfg: MOCOptions, camera: &mut CameraViewPort, proj: &ProjectionType, ) { @@ -80,10 +80,8 @@ impl MOCRenderer { //self.layers.push(key); } - pub fn get_hpx_coverage(&self, cfg: &Cfg) -> Option<&HEALPixCoverage> { - let name = cfg.get_uuid(); - - if let Some(idx) = self.cfgs.iter().position(|cfg| cfg.get_uuid() == name) { + pub fn get_hpx_coverage(&self, moc_uuid: &str) -> Option<&HEALPixCoverage> { + if let Some(idx) = self.cfgs.iter().position(|cfg| cfg.get_uuid() == moc_uuid) { Some(&self.mocs[idx].get_full_moc()) } else { None @@ -92,13 +90,11 @@ impl MOCRenderer { pub fn remove( &mut self, - cfg: &Cfg, + moc_uuid: &str, camera: &mut CameraViewPort, proj: &ProjectionType, - ) -> Option { - let name = cfg.get_uuid(); - - if let Some(idx) = self.cfgs.iter().position(|cfg| cfg.get_uuid() == name) { + ) -> Option { + if let Some(idx) = self.cfgs.iter().position(|cfg| cfg.get_uuid() == moc_uuid) { self.mocs.remove(idx); camera.unregister_view_frame(CooSystem::ICRS, proj); @@ -108,20 +104,21 @@ impl MOCRenderer { } } - pub fn set_cfg( + pub fn set_options( &mut self, - cfg: Cfg, + options: MOCOptions, camera: &mut CameraViewPort, projection: &ProjectionType, shaders: &mut ShaderManager, - ) -> Option { - let name = cfg.get_uuid(); + ) -> Option { + let name = options.get_uuid(); if let Some(idx) = self.cfgs.iter().position(|cfg| cfg.get_uuid() == name) { let old_cfg = self.cfgs[idx].clone(); - self.cfgs[idx] = cfg; + self.mocs[idx].set_options(&options); + self.cfgs[idx] = options; - let _ = self.draw(camera, projection, shaders); + //let _ = self.draw(camera, projection, shaders); Some(old_cfg) } else { @@ -131,7 +128,7 @@ impl MOCRenderer { } pub fn is_empty(&self) -> bool { - self.cfgs.is_empty() + self.mocs.is_empty() } pub fn draw( diff --git a/src/core/src/tile_fetcher.rs b/src/core/src/tile_fetcher.rs index a2d935b3..8ae7ed02 100644 --- a/src/core/src/tile_fetcher.rs +++ b/src/core/src/tile_fetcher.rs @@ -5,6 +5,7 @@ use crate::Abort; use std::cell::RefCell; use std::collections::{HashMap, VecDeque}; use std::rc::Rc; +use al_api::moc::MOCOptions; const MAX_NUM_TILE_FETCHING: usize = 8; const MAX_QUERY_QUEUE_LENGTH: usize = 100; @@ -208,7 +209,7 @@ impl TileFetcherQueue { downloader.borrow_mut().fetch(query::Moc::new( moc_url, cfg.get_creator_did().to_string(), - al_api::moc::MOC::default(), + MOCOptions::default(), )); let tile_size = cfg.get_tile_size(); diff --git a/src/js/MOC.js b/src/js/MOC.js index c4fc55e1..7e2140bd 100644 --- a/src/js/MOC.js +++ b/src/js/MOC.js @@ -48,6 +48,9 @@ export let MOC = (function() { options = options || {}; this.name = options.name || "MOC"; + // User can change the opacity, color, lineWidth and fillColor properties + this._defineProperties() + this.color = options.color || Color.getNextColor(); this.color = Color.standardizeColor(this.color); @@ -131,7 +134,7 @@ export let MOC = (function() { let self = this; this.view = view; - this.mocParams = new Aladin.wasmLibs.core.MOC(this.uuid, this.opacity, this.lineWidth, this.perimeter, this.fill, this.edge, this.isShowing, this.color, this.fillColor); + this.mocParams = new Aladin.wasmLibs.core.MOCOptions(this.uuid, this.opacity, this.lineWidth, this.perimeter, this.fill, this.edge, this.isShowing, this.color, this.fillColor); this.promiseFetchData .then((data) => { @@ -160,7 +163,7 @@ export let MOC = (function() { } // Cache the sky fraction - self.skyFrac = self.view.wasm.getMOCSkyFraction(this.mocParams); + self.skyFrac = self.view.wasm.getMOCSkyFraction(this.uuid); // Add it to the view self.view.mocs.push(self); @@ -178,11 +181,53 @@ export let MOC = (function() { }) }; + MOC.prototype._defineProperties = function() { + Object.defineProperties(this, { + opacity: { + get() { + return this._opacity; + }, + set(opacity) { + this._opacity = opacity; + this.reportChange(); + } + }, + color: { + get() { + return this._color; + }, + set(color) { + this._color = Color.standardizeColor(color); + this.reportChange(); + } + }, + fillColor: { + get() { + return this._fillColor; + }, + set(fillColor) { + this._fillColor = Color.standardizeColor(fillColor); + this.reportChange(); + } + }, + lineWidth: { + get() { + return this._lineWidth; + }, + set(lineWidth) { + this._lineWidth = lineWidth; + this.reportChange(); + } + }, + }); + }; + MOC.prototype.reportChange = function() { if (this.view) { // update the new moc params to the backend - this.mocParams = new Aladin.wasmLibs.core.MOC(this.uuid, this.opacity, this.lineWidth, this.perimeter, this.fill, this.edge, this.isShowing, this.color, this.fillColor); + this.mocParams = new Aladin.wasmLibs.core.MOCOptions(this.uuid, this.opacity, this.lineWidth, this.perimeter, this.fill, this.edge, this.isShowing, this.color, this.fillColor); this.view.wasm.setMocParams(this.mocParams); + this.view.requestRedraw(); } }; @@ -190,7 +235,7 @@ export let MOC = (function() { MOC.prototype.delete = function() { if (this.view) { // update the new moc params to the backend - this.view.wasm.removeMoc(this.mocParams); + this.view.wasm.removeMoc(this.uuid); this.view.requestRedraw(); } }; @@ -236,7 +281,7 @@ export let MOC = (function() { } // update the new moc params to the backend - return this.view.wasm.mocContains(this.mocParams, ra, dec); + return this.view.wasm.mocContains(this.uuid, ra, dec); }; /** @@ -250,7 +295,7 @@ export let MOC = (function() { throw this.name + " is not yet ready, either because it has not been downloaded yet or because it has not been added to the aladin instance." } - return this.view.wasm.mocSerialize(this.mocParams, format); + return this.view.wasm.mocSerialize(this.uuid, format); } return MOC;