fix some bugs, adapt ui for a new release

This commit is contained in:
Matthieu Baumann
2024-07-17 18:43:29 +02:00
parent 80f48331df
commit 5a298f6ee1
23 changed files with 232 additions and 114 deletions

View File

@@ -1,7 +1,10 @@
# Changelogs
## Unreleased
## 3.4.5-beta
* [feat] add `layerChanged` event when a layer is added or removed
* [deprecate] of `select` event, use `objectsSelected` event instead
* [ui] add the ability to switch the tile format to download
## 3.4.3-beta

View File

@@ -9,6 +9,7 @@ See [A&A 578, A114 (2015)](https://arxiv.org/abs/1505.02291) and [IVOA HiPS Reco
Aladin Lite is built to be easily embeddable in any web page. It powers astronomical portals like [ESASky](https://sky.esa.int/), [ESO Science Archive portal](http://archive.eso.org/scienceportal/) and [ALMA Portal](https://almascience.eso.org/asax/).
More details on [Aladin Lite documentation page](http://aladin.u-strasbg.fr/AladinLite/doc/).
A new [API technical documentation](https://cds-astro.github.io/aladin-lite/) is now available.
[![Run tests](https://github.com/cds-astro/aladin-lite/actions/workflows/test.yml/badge.svg)](https://github.com/cds-astro/aladin-lite/actions/workflows/test.yml)
[![API Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://cds-astro.github.io/aladin-lite)

View File

@@ -34,7 +34,7 @@ A.init.then(() => {
console.log(objs, "are selected");
})
aladin.select();
aladin.select('circle');
});
</script>

View File

@@ -2,7 +2,7 @@
"homepage": "https://aladin.u-strasbg.fr/",
"name": "aladin-lite",
"type": "module",
"version": "3.4.4-beta",
"version": "3.4.5-beta",
"description": "An astronomical HiPS visualizer in the browser",
"author": "Thomas Boch and Matthieu Baumann",
"license": "GPL-3",

View File

@@ -7,6 +7,6 @@ use crate::fov::CenteredFoV;
pub struct ImageParams {
pub centered_fov: CenteredFoV,
pub min_cut: Option<f32>,
pub max_cut: Option<f32>,
pub min_cut: f32,
pub max_cut: f32,
}

View File

@@ -180,6 +180,52 @@ impl Texture2D {
Ok(texture)
}
pub fn create_from_raw_bytes<F: ImageFormat>(
gl: &WebGlContext,
width: i32,
height: i32,
tex_params: &'static [(u32, u32)],
bytes: Option<&[u8]>,
) -> Result<Texture2D, JsValue> {
let texture = gl.create_texture();
gl.bind_texture(WebGlRenderingCtx::TEXTURE_2D, texture.as_ref());
for (pname, param) in tex_params.iter() {
gl.tex_parameteri(WebGlRenderingCtx::TEXTURE_2D, *pname, *param as i32);
}
gl.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
WebGlRenderingCtx::TEXTURE_2D,
0,
F::INTERNAL_FORMAT,
width,
height,
0,
F::FORMAT,
F::TYPE,
bytes,
)
.expect("Texture 2D");
let gl = gl.clone();
let metadata = Some(Rc::new(RefCell::new(Texture2DMeta {
width: width as u32,
height: height as u32,
internal_format: F::INTERNAL_FORMAT,
format: F::FORMAT,
type_: F::TYPE,
})));
Ok(Texture2D {
texture,
gl,
metadata,
})
}
pub fn create_empty_unsized(
gl: &WebGlContext,
tex_params: &'static [(u32, u32)],
@@ -591,10 +637,6 @@ impl<'a> Texture2DBoundMut<'a> {
src_type: u32,
pixels: Option<&[u8]>,
) {
//let Texture2DMeta {format, type_, ..} = self.texture_2d.metadata.unwrap_abort();
/*self.texture_2d
.gl
.pixel_storei(WebGlRenderingCtx::UNPACK_ALIGNMENT, 1);*/
self.texture_2d
.gl
.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(

View File

@@ -40,14 +40,6 @@ impl WebGlContext {
let context_options =
js_sys::JSON::parse("{\"antialias\":false, \"preserveDrawingBuffer\": true}")?;
#[cfg(feature = "webgl1")]
let gl = Rc::new(
canvas
.get_context_with_context_options("webgl", context_options.as_ref())?
.unwrap_abort()
.dyn_into::<WebGlRenderingCtx>()
.unwrap_abort(),
);
#[cfg(feature = "webgl2")]
let gl = Rc::new(
canvas
@@ -56,6 +48,9 @@ impl WebGlContext {
.dyn_into::<WebGlRenderingCtx>()
.unwrap_abort(),
);
// https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html
#[cfg(feature = "webgl2")]
gl.pixel_storei(WebGlRenderingCtx::UNPACK_ALIGNMENT, 1);
#[cfg(feature = "webgl2")]
{

View File

@@ -48,10 +48,10 @@ pub struct Image {
/// Parameters extracted from the fits
wcs: WCS,
blank: Option<f32>,
scale: Option<f32>,
offset: Option<f32>,
cuts: Option<Range<f32>>,
blank: f32,
scale: f32,
offset: f32,
cuts: Range<f32>,
/// The center of the fits
centered_fov: CenteredFoV,
@@ -80,9 +80,9 @@ impl Image {
gl: &WebGlContext,
mut reader: R,
wcs: WCS,
mut scale: Option<f32>,
mut offset: Option<f32>,
mut blank: Option<f32>,
scale: Option<f32>,
offset: Option<f32>,
blank: Option<f32>,
// Coo sys of the view
coo_sys: CooSystem,
) -> Result<Self, JsValue>
@@ -101,11 +101,9 @@ impl Image {
let mut max_tex_size_y = max_tex_size;
// apply bscale to the cuts
if F::NUM_CHANNELS == 1 {
offset = offset.or(Some(0.0));
scale = scale.or(Some(1.0));
blank = blank.or(Some(std::f32::NAN));
}
let offset = offset.unwrap_or(0.0);
let scale = scale.unwrap_or(1.0);
let blank = blank.unwrap_or(std::f32::NAN);
let (textures, mut cuts) = if width <= max_tex_size as u64 && height <= max_tex_size as u64
{
@@ -119,14 +117,15 @@ impl Image {
let mut buf = vec![0; num_bytes_to_read];
let _ = reader
.read_exact(&mut buf[..])
.read_exact(&mut buf[..num_bytes_to_read])
.await
.map_err(|e| JsValue::from_str(&format!("{:?}", e)))?;
// bytes aligned
unsafe {
let slice = std::slice::from_raw_parts(
buf.as_mut_ptr() as *const <F::P as Pixel>::Item,
num_bytes_to_read / std::mem::size_of::<<F::P as Pixel>::Item>(),
buf[..].as_ptr() as *const <F::P as Pixel>::Item,
(num_pixels_to_read as usize) * F::NUM_CHANNELS,
);
let cuts = if F::NUM_CHANNELS == 1 {
@@ -135,7 +134,7 @@ impl Image {
.filter_map(|item| {
let t: f32 =
<<F::P as Pixel>::Item as al_core::convert::Cast<f32>>::cast(*item);
if t.is_nan() || t == blank.unwrap() {
if t.is_nan() || t == blank {
None
} else {
Some(t)
@@ -143,22 +142,20 @@ impl Image {
})
.collect::<Vec<_>>();
let cuts = cuts::first_and_last_percent(&mut samples, 1, 99);
Some(cuts)
cuts::first_and_last_percent(&mut samples, 1, 99)
} else {
None
0.0..1.0
};
(
vec![Texture2D::create_from_raw_pixels::<F>(
gl,
width as i32,
height as i32,
TEX_PARAMS,
Some(slice),
)?],
cuts,
)
let texture = Texture2D::create_from_raw_pixels::<F>(
gl,
width as i32,
height as i32,
TEX_PARAMS,
Some(slice),
)?;
(vec![texture], cuts)
}
} else {
subdivide_texture::crop_image::<F, R>(
@@ -172,12 +169,10 @@ impl Image {
.await?
};
if let Some(cuts) = cuts.as_mut() {
let start = cuts.start * scale.unwrap() + offset.unwrap();
let end = cuts.end * scale.unwrap() + offset.unwrap();
let start = cuts.start * scale + offset;
let end = cuts.end * scale + offset;
*cuts = start..end;
}
cuts = start..end;
let num_indices = vec![];
let indices = vec![];
@@ -293,7 +288,7 @@ impl Image {
})
}
pub fn get_cuts(&self) -> &Option<Range<f32>> {
pub fn get_cuts(&self) -> &Range<f32> {
&self.cuts
}
@@ -712,9 +707,9 @@ impl Image {
.attach_uniforms_with_params_from(color, colormaps)
.attach_uniform("opacity", opacity)
.attach_uniform("tex", texture)
.attach_uniform("scale", &self.scale.unwrap_or(1.0))
.attach_uniform("offset", &self.offset.unwrap_or(0.0))
.attach_uniform("blank", &self.blank.unwrap_or(std::f32::NAN))
.attach_uniform("scale", &self.scale)
.attach_uniform("offset", &self.offset)
.attach_uniform("blank", &self.blank)
.bind_vertex_array_object_ref(&self.vao)
.draw_elements_with_i32(
WebGl2RenderingContext::TRIANGLES,

View File

@@ -16,8 +16,8 @@ pub async fn crop_image<'a, F, R>(
height: u64,
mut reader: R,
max_tex_size: u64,
blank: Option<f32>,
) -> Result<(Vec<Texture2D>, Option<Range<f32>>), JsValue>
blank: f32,
) -> Result<(Vec<Texture2D>, Range<f32>), JsValue>
where
F: ImageFormat,
R: AsyncReadExt + Unpin,
@@ -35,12 +35,11 @@ where
];
for _ in 0..num_textures {
tex_chunks.push(Texture2D::create_from_raw_pixels::<F>(
tex_chunks.push(Texture2D::create_empty_with_format::<F>(
gl,
max_tex_size as i32,
max_tex_size as i32,
TEX_PARAMS,
None,
)?);
}
@@ -101,11 +100,7 @@ where
f32,
>>::cast(slice[j]);
if !sj.is_nan() {
if let Some(b) = blank {
if b != sj {
samples.push(sj);
}
} else {
if blank != sj {
samples.push(sj);
}
}
@@ -136,9 +131,9 @@ where
}
let cuts = if F::NUM_CHANNELS == 1 {
Some(cuts::first_and_last_percent(&mut samples, 1, 99))
cuts::first_and_last_percent(&mut samples, 1, 99)
} else {
None
0.0..1.0
};
Ok((tex_chunks, cuts))

View File

@@ -106,16 +106,13 @@ pub struct ImageLayer {
impl ImageLayer {
pub fn get_params(&self) -> ImageParams {
let (min_cut, max_cut) = self.images[0]
.get_cuts()
.as_ref()
.map_or((None, None), |r| (Some(r.start), Some(r.end)));
let cuts = self.images[0].get_cuts();
let centered_fov = self.images[0].get_centered_fov().clone();
ImageParams {
centered_fov,
min_cut,
max_cut,
min_cut: cuts.start,
max_cut: cuts.end,
}
}
}

View File

@@ -134,7 +134,37 @@ A.imageHiPS = function (id, options) {
* @param {ImageOptions} [options] - Options describing the fits file. An url is mandatory
* @returns {Image} - A HiPS image object
* @example
* const sourceObj = A.source(180.0, 30.0, data, options);
* aladin.setOverlayImageLayer(A.image(
* "https://nova.astrometry.net/image/25038473?filename=M61.jpg",
* {
* name: "M61",
* imgFormat: 'jpeg',
* wcs: {
* NAXIS: 0, // Minimal header
* CTYPE1: 'RA---TAN', // TAN (gnomic) projection
* CTYPE2: 'DEC--TAN', // TAN (gnomic) projection
* EQUINOX: 2000.0, // Equatorial coordinates definition (yr)
* LONPOLE: 180.0, // no comment
* LATPOLE: 0.0, // no comment
* CRVAL1: 185.445488837, // RA of reference point
* CRVAL2: 4.47896032431, // DEC of reference point
* CRPIX1: 588.995094299, // X reference pixel
* CRPIX2: 308.307905197, // Y reference pixel
* CUNIT1: 'deg', // X pixel scale units
* CUNIT2: 'deg', // Y pixel scale units
* CD1_1: -0.000223666022989, // Transformation matrix
* CD1_2: 0.000296578064584, // no comment
* CD2_1: -0.000296427555509, // no comment
* CD2_2: -0.000223774308964, // no comment
* NAXIS1: 1080, // Image width, in pixels.
* NAXIS2: 705 // Image height, in pixels.
* },
* successCallback: (ra, dec, fov, image) => {
* aladin.gotoRaDec(ra, dec);
* aladin.setFoV(fov * 5)
* }
* },
* ));
*/
A.image = function (url, options) {
return Aladin.createImageFITS(url, options, options.successCallback, options.errorCallback);

View File

@@ -232,7 +232,9 @@ import { Polyline } from "./shapes/Polyline";
/**
* @typedef {string} ListenerCallback
* String with possible values: 'select',
* String with possible values:
* 'select' (deprecated, use objectsSelected instead),
* 'objectsSelected',
'objectClicked',
'objectHovered',
'objectHoveredStop',
@@ -1949,7 +1951,8 @@ export let Aladin = (function () {
// Select corresponds to rectangular selection
Aladin.AVAILABLE_CALLBACKS = [
"select",
"select", // deprecated, use objectsSelected instead
"objectsSelected",
"objectClicked",
"objectHovered",
@@ -2019,12 +2022,16 @@ aladin.on('objectClicked', function(object, xyMouseCoords) {
$('#infoDiv').html(msg);
});
aladin.on("objectsSelected", (objs) => {
console.log("objs", objs)
})
aladin.on("positionChanged", ({ra, dec}) => {
console.log("positionChanged", ra, dec)
})
aladin.on("layerChanged", (imageHips, layerName, state) => {
console.log("positionChanged", imageHips, layerName, state)
aladin.on("layerChanged", (layer, layerName, state) => {
console.log("layerChanged", layer, layerName, state)
})
*/
Aladin.prototype.on = function (what, myFunction) {

View File

@@ -46,8 +46,13 @@
this.reversed = true;
}
this.minCut = (options && options.minCut) || undefined;
this.maxCut = (options && options.maxCut) || undefined;
if (options && Number.isFinite(options.minCut)) {
this.minCut = options.minCut;
}
if (options && Number.isFinite(options.maxCut)) {
this.maxCut = options.maxCut;
}
this.additiveBlending = options && options.additive;
if (this.additiveBlending === undefined) {

View File

@@ -102,9 +102,11 @@ export class CircleSelect extends FSM {
// execute general callback
if (view.aladin.callbacksByEventName) {
var callback = view.aladin.callbacksByEventName['select'];
var callback = view.aladin.callbacksByEventName['objectsSelected'] || view.aladin.callbacksByEventName['select'];
if (typeof callback === "function") {
let objList = Selector.getObjects(s, view);
view.selectObjects(objList);
callback(objList);
}
}

View File

@@ -171,7 +171,7 @@ export class PolySelect extends FSM {
// execute general callback
if (view.aladin.callbacksByEventName) {
var callback = view.aladin.callbacksByEventName['select'];
var callback = view.aladin.callbacksByEventName['objectsSelected'] || view.aladin.callbacksByEventName['select'];
if (typeof callback === "function") {
console.warn('polygon selection is not fully implemented, PolySelect.contains is needed for finding sources inside a polygon')
}

View File

@@ -107,9 +107,10 @@ export class RectSelect extends FSM {
// execute general callback
if (view.aladin.callbacksByEventName) {
var callback = view.aladin.callbacksByEventName['select'];
var callback = view.aladin.callbacksByEventName['objectsSelected'] || view.aladin.callbacksByEventName['select'];
if (typeof callback === "function") {
let objList = Selector.getObjects(s, view);
view.selectObjects(objList);
callback(objList);
}
}

View File

@@ -20,7 +20,7 @@
/******************************************************************************
* Aladin Lite project
*
* File ImageFITS
* File Image
*
* Authors: Matthieu Baumann [CDS]
*
@@ -40,8 +40,8 @@ import { Aladin } from "./Aladin.js";
* @property {string} [colormap="native"] - The colormap configuration for the survey or image.
* @property {string} [stretch="linear"] - The stretch configuration for the survey or image.
* @property {boolean} [reversed=false] - If true, the colormap is reversed; otherwise, it is not reversed.
* @property {number} [minCut] - The minimum cut value for the color configuration. If not given, 0.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
* @property {number} [maxCut] - The maximum cut value for the color configuration. If not given, 1.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
* @property {number} [minCut=0.0] - The minimum cut value for the color configuration. If not given, 0.0 is chosen
* @property {number} [maxCut=1.0] - The maximum cut value for the color configuration. If not given, 1.0 is chosen
* @property {boolean} [additive=false] - If true, additive blending is applied; otherwise, it is not applied.
* @property {number} [gamma=1.0] - The gamma correction value for the color configuration.
* @property {number} [saturation=0.0] - The saturation value for the color configuration.
@@ -115,10 +115,12 @@ export let Image = (function () {
/*if (options) {
options.stretch = options.stretch || "asinh";
}*/
this.colorCfg = new ColorCfg(options);
this.options = options;
let self = this;
this.query = Promise.resolve(self);
}
@@ -310,9 +312,12 @@ export let Image = (function () {
self.setView(self.view);
// Set the automatic computed cuts
let [minCut, maxCut] = self.getCuts();
minCut = minCut || imageParams.min_cut;
maxCut = maxCut || imageParams.max_cut;
self.setCuts(
imageParams.min_cut,
imageParams.max_cut
minCut,
maxCut
);
self.ra = imageParams.centered_fov.ra;

View File

@@ -184,6 +184,9 @@ export let ImageHiPS = (function () {
? false
: options.longitudeReversed;
this.imgFormat = options.imgFormat;
this.formats = options.formats;
this.defaultFitsMinCut = options.defaultFitsMinCut;
this.defaultFitsMaxCut = options.defaultFitsMaxCut;
this.numBitsPerPixel = options.numBitsPerPixel;
this.creatorDid = options.creatorDid;
this.errorCallback = options.errorCallback;
@@ -331,8 +334,8 @@ export let ImageHiPS = (function () {
// Cutouts
const cutoutFromProperties = PropertyParser.cutouts(properties);
self.minCut = cutoutFromProperties[0];
self.maxCut = cutoutFromProperties[1];
self.defaultFitsMinCut = cutoutFromProperties[0];
self.defaultFitsMaxCut = cutoutFromProperties[1];
// Bitpix
self.numBitsPerPixel =
@@ -418,8 +421,8 @@ export let ImageHiPS = (function () {
let minCut, maxCut;
if (self.imgFormat === "fits") {
// Take into account the default cuts given by the property file (this is true especially for FITS HiPSes)
minCut = self.colorCfg.minCut || self.minCut || 0.0;
maxCut = self.colorCfg.maxCut || self.maxCut || 1.0;
minCut = self.colorCfg.minCut || self.defaultFitsMinCut || 0.0;
maxCut = self.colorCfg.maxCut || self.defaultFitsMaxCut || 1.0;
} else {
minCut = self.colorCfg.minCut || 0.0;
maxCut = self.colorCfg.maxCut || 1.0;
@@ -456,7 +459,6 @@ export let ImageHiPS = (function () {
ImageHiPS.prototype._saveInCache = function () {
let self = this;
let colorOpt = Object.fromEntries(Object.entries(this.colorCfg));
let surveyOpt = {
id: self.id,
@@ -467,9 +469,12 @@ export let ImageHiPS = (function () {
cooFrame: self.cooFrame,
maxOrder: self.maxOrder,
tileSize: self.tileSize,
formats: self.formats,
imgFormat: self.imgFormat,
successCallback: self.successCallback,
errorCallback: self.errorCallback,
defaultFitsMinCut: self.defaultFitsMinCut,
defaultFitsMaxCut: self.defaultFitsMaxCut,
...colorOpt,
};
@@ -577,9 +582,9 @@ export let ImageHiPS = (function () {
self.imgFormat === "jpeg") &&
imgFormat === "fits"
) {
if (self.minCut && self.maxCut) {
if (Number.isFinite(self.defaultFitsMinCut) && Number.isFinite(self.defaultFitsMaxCut)) {
// reset cuts to those given from the properties
self.setCuts(self.minCut, self.maxCut);
self.setCuts(self.defaultFitsMinCut, self.defaultFitsMaxCut);
}
// Switch from fits to png/webp/jpeg
} else if (self.imgFormat === "fits") {

View File

@@ -1668,16 +1668,16 @@ export let View = (function () {
const layerName = imageLayer.layer;
// Check whether this layer already exist
const idxOverlayLayer = this.overlayLayers.findIndex(overlayLayer => overlayLayer == layerName);
let alreadyPresentImageLayer;
if (idxOverlayLayer == -1) {
// it does not exist so we add it to the stack
this.overlayLayers.push(layerName);
} else {
// it exists
let alreadyPresentImageLayer = this.imageLayers.get(layerName);
alreadyPresentImageLayer = this.imageLayers.get(layerName);
alreadyPresentImageLayer.added = false;
// Notify that this image layer has been replaced by the wasm part
ALEvent.HIPS_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: alreadyPresentImageLayer });
this.imageLayers.delete(layerName);
}
imageLayer.added = true;
@@ -1688,6 +1688,11 @@ export let View = (function () {
if (idxOverlayLayer == -1) {
this.selectLayer(layerName);
}
// Notify that this image layer has been replaced by the wasm part
if (alreadyPresentImageLayer) {
ALEvent.HIPS_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: alreadyPresentImageLayer });
}
ALEvent.HIPS_LAYER_ADDED.dispatchedTo(this.aladinDiv, { layer: imageLayer });
}

View File

@@ -99,12 +99,12 @@ import { ColorCfg } from "../../ColorCfg.js";
// Define the contents
let opacitySettingsContent = new Form({
type: 'group',
subInputs: [
{
label: 'opacity:',
name: 'opacity',
tooltip: {content: 1.0, position: {direction: 'bottom'}},
name: 'opacitySlider',
name: 'opacity',
type: 'range',
min: 0.0,
max: 1.0,
@@ -229,7 +229,6 @@ import { ColorCfg } from "../../ColorCfg.js";
options: ColorCfg.COLORMAPS,
change: (e, cmapSelector) => {
self.options.layer.setColormap(e.target.value)
//cmapSelector.update({value: e.target.value})
},
}]
});
@@ -271,14 +270,20 @@ import { ColorCfg } from "../../ColorCfg.js";
}
fmtInput.value = hips.imgFormat;
this.colorSettingsContent.set('cmap', colormap)
this.opacitySettingsContent.set('opacity', hips.getOpacity())
this.colorSettingsContent.set('cmap', colormap);
this.opacitySettingsContent.set('opacity', hips.getOpacity());
this.luminositySettingsContent.set('brightness', colorCfg.getBrightness());
this.luminositySettingsContent.set('contrast', colorCfg.getContrast());
this.luminositySettingsContent.set('saturation', colorCfg.getSaturation());
}
super.update(options)
}
_updateCursors() {
}
_show(options) {
/*if (this.selector) {
this.selector._show();
@@ -300,13 +305,24 @@ import { ColorCfg } from "../../ColorCfg.js";
let colorCfg = hips.getColorCfg();
let stretch = colorCfg.stretch;
let colormap = colorCfg.getColormap();
let [minCut, maxCut] = colorCfg.getCuts();
this.pixelSettingsContent.set('mincut', +minCut.toFixed(4))
this.pixelSettingsContent.set('maxcut', +maxCut.toFixed(4))
this.pixelSettingsContent.set('stretch', stretch)
this.colorSettingsContent.set('cmap', colormap)
this.opacitySettingsContent.set('opacity', hips.getOpacity())
let fmtInput = this.pixelSettingsContent.getInput('fmt')
fmtInput.innerHTML = '';
for (const option of hips.formats) {
fmtInput.innerHTML += "<option>" + option + "</option>";
}
fmtInput.value = hips.imgFormat;
this.colorSettingsContent.set('cmap', colormap);
this.opacitySettingsContent.set('opacity', hips.getOpacity());
this.luminositySettingsContent.set('brightness', colorCfg.getBrightness());
this.luminositySettingsContent.set('contrast', colorCfg.getContrast());
this.luminositySettingsContent.set('saturation', colorCfg.getSaturation());
}
});
}

View File

@@ -646,13 +646,6 @@ export class OverlayStackBox extends Box {
}
);
ALEvent.HIPS_LAYER_RENAMED.listenedBy(
this.aladin.aladinDiv,
function (e) {
updateOverlayList();
}
);
ALEvent.HIPS_LAYER_SWAP.listenedBy(this.aladin.aladinDiv, function (e) {
updateOverlayList();
});
@@ -912,6 +905,7 @@ export class OverlayStackBox extends Box {
hipsOptions.sort()
for (const layer of layers) {
let HiPSSelector = Input.select({
value: layer.name,
options: hipsOptions,
@@ -974,9 +968,11 @@ export class OverlayStackBox extends Box {
});
let settingsBox = new HiPSSettingsBox(self.aladin);
settingsBox.update({ layer });
settingsBox._hide();
let settingsBtn = new TogglerActionButton({
icon: { url: settingsIconUrl, monochrome: true },
size: "small",
@@ -986,6 +982,15 @@ export class OverlayStackBox extends Box {
},
toggled: false,
actionOn: (e) => {
// toggle off the other settings if opened
for (var l in self.HiPSui) {
let ui = self.HiPSui[l]
if (l != layer.layer) {
ui.settingsBtn.close();
}
}
settingsBox._show({
position: {
nextTo: settingsBtn,

View File

@@ -51,6 +51,12 @@ export class TogglerActionButton extends ActionButton {
self = this;
}
close() {
if (this.toggled) {
this.toggle()
}
}
toggle(o) {
this.toggled = !this.toggled;

View File

@@ -60,6 +60,9 @@ export class SettingsCtxMenu extends ContextMenu {
value: reticleColor.toHex(),
name: 'reticleColor',
change(e) {
e.stopPropagation()
e.preventDefault();
let hex = e.target.value;
let reticle = aladin.getReticle();
reticle.update({color: hex})
@@ -170,7 +173,7 @@ export class SettingsCtxMenu extends ContextMenu {
{
label: {
content: [self.reticleColorInput, 'Color']
}
},
},
{
label: Layout.horizontal(['Size', sliderReticleSize]),