Fix MOC settings after its creation

MOC settings after their creation was not possible. This PR fix it.
It is also possible to directly set the 'color', 'fillColor', 'opacity'
and 'lineWidth' MOC properties without doing any reportChange
afterwards. These settings will automatically notify the wasm part for
change of the MOC options and will update the view.
This commit is contained in:
bmatthieu3
2025-03-24 11:47:25 +01:00
committed by Matthieu Baumann
parent 9b31210066
commit f656176e28
12 changed files with 170 additions and 69 deletions

View File

@@ -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)
});
</script>
</body>

View File

@@ -88,4 +88,4 @@ codegen-units = 16
rpath = false
[package.metadata.wasm-pack.profile.release]
wasm-opt = true
wasm-opt = false

View File

@@ -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"),

View File

@@ -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;

View File

@@ -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,

View File

@@ -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<HEALPixCoverage>,
}
@@ -41,6 +41,7 @@ pub fn from_fits_hpx<T: Idx>(moc: MocType<T, Hpx<T>, 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<RefCell<Option<HEALPixCoverage>>>,
pub params: al_api::moc::MOC,
pub params: MOCOptions,
pub hips_cdid: Url,
}

View File

@@ -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<bool, JsValue> {
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<JsValue, JsValue> {
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<u8> = 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

View File

@@ -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<MOC>,
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());
}
}

View File

@@ -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
}

View File

@@ -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<MOCHierarchy>,
cfgs: Vec<Cfg>,
cfgs: Vec<MOCOptions>,
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<Cfg> {
let name = cfg.get_uuid();
if let Some(idx) = self.cfgs.iter().position(|cfg| cfg.get_uuid() == name) {
) -> Option<MOCOptions> {
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<Cfg> {
let name = cfg.get_uuid();
) -> Option<MOCOptions> {
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(

View File

@@ -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();

View File

@@ -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;