diff --git a/examples/al-hips-3D.html b/examples/al-hips-3D.html
index 3aef41ed..dc7788c3 100644
--- a/examples/al-hips-3D.html
+++ b/examples/al-hips-3D.html
@@ -28,9 +28,9 @@
}
);
- hips = aladin.newImageSurvey("https://alasky.cds.unistra.fr/GALFAHI/GALFAHI-Narrow-DR2-3D", {
+ hips = aladin.newImageSurvey("http://alasky.cds.unistra.fr/HIPS3D/MUSE-test", {
successCallback: (hips) => {
- hips.setFrequency({value: 0.21101690259115785, unit: "m"}) // GALFA
+ //hips.setFrequency({value: 6.374279333565797E-7, unit: "m"}) // GALFA
}
});
// compressed https://alasky.cds.unistra.fr/test-compression-cubes/DHIGLS/
@@ -40,7 +40,7 @@
aladin.setImageLayer(hips)
//hips.setFrequency({value: emMin + delta * i, unit: "m"})
- //hips.setFrequency({value: 0.21059156059066345, unit: "m"}) // MUSE
+ //hips.setFrequency({value: 6.374279333565797E-7, unit: "m"}) // MUSE
//hips.setFrequency({value: 0.21101690259115785, unit: "m"}) // DGHILG
/*let id;
diff --git a/src/core/src/healpix/cell.rs b/src/core/src/healpix/cell.rs
index f218afeb..86dd01b2 100644
--- a/src/core/src/healpix/cell.rs
+++ b/src/core/src/healpix/cell.rs
@@ -556,6 +556,16 @@ impl HEALPixFreqCell {
f0..f1
}
+
+ pub fn pixel_frequencies(&self, num_pixels: usize) -> impl Iterator- {
+ let delta_depth = num_pixels.trailing_zeros();
+ let pixel_depth = self.f_depth + delta_depth as u8;
+
+ let h0 = self.f_hash << delta_depth;
+ let h1 = (self.f_hash + 1) << delta_depth;
+
+ (h0..h1).map(move |hash| Freq::from_hash_with_order(hash, pixel_depth).0 as f32)
+ }
}
// Utils
diff --git a/src/core/src/renderable/hips/d3/mod.rs b/src/core/src/renderable/hips/d3/mod.rs
index 01d6bf9c..19668843 100644
--- a/src/core/src/renderable/hips/d3/mod.rs
+++ b/src/core/src/renderable/hips/d3/mod.rs
@@ -226,16 +226,24 @@ impl Cursor {
let f_hash_val = self.freq.hash(pixel_depth);
//let f_hash_val_0 = (f_hash_val as i64 - NUM_VALUES as i64).max(0) as u64;
- let f_hash_val_1 = (f_hash_val as i64 + dx)
+ let h1 = (f_hash_val as i64 + dx)
.min(
(Frequency::::n_cells_max() >> (Frequency::::MAX_DEPTH - pixel_depth))
as i64,
)
.max(0) as u64;
- let f_dx_th_slice = Freq::from_hash_with_order(f_hash_val_1, pixel_depth);
+ let h0 = (f_hash_val as i64 - dx)
+ .min(
+ (Frequency::::n_cells_max() >> (Frequency::::MAX_DEPTH - pixel_depth))
+ as i64,
+ )
+ .max(0) as u64;
- f_dx_th_slice - self.freq
+ let f1 = Freq::from_hash_with_order(h1, pixel_depth);
+ let f0 = Freq::from_hash_with_order(h0, pixel_depth);
+
+ Freq((f1 - f0).0 * 0.5)
}
fn get_surrounding_cell_hashes_along_spectra_axis(&self) -> Range {
@@ -578,13 +586,6 @@ impl HiPS3D {
)
.unwrap_abort();
- Reflect::set(
- &spectra_js_obj,
- &JsValue::from_str("fOrder"),
- &JsValue::from_f64(pixel_depth as f64),
- )
- .unwrap_abort();
-
let mut start = window_pixel_hash.start.max(domain_pixel_hash.start);
let mut end = window_pixel_hash.end.min(domain_pixel_hash.end);
@@ -614,10 +615,12 @@ impl HiPS3D {
.unwrap_abort();
}
+ let mut freqs = vec![];
let spectra = self
.cursor
.get_surrounding_cells_along_spectra_axis()
.flat_map(|c| {
+ freqs.extend(c.pixel_frequencies(tile_depth as usize));
if let Some(cubic_tex) = self.buffer.get(&c) {
(0..(tile_depth as u32))
.map(|z| cubic_tex.read_pixel(x, y, z).unwrap_or(f32::NAN))
@@ -644,6 +647,15 @@ impl HiPS3D {
)
.unwrap_abort();
+ //al_core::log(&format!("{:?}", freqs));
+
+ Reflect::set(
+ &spectra_js_obj,
+ &JsValue::from_str("freqs"),
+ &js_sys::Float32Array::from(&freqs[..]),
+ )
+ .unwrap_abort();
+
crate::event::send_custom_event("spectra", JsValue::from(spectra_js_obj));
}
diff --git a/src/core/src/renderable/hips/d3/texture.rs b/src/core/src/renderable/hips/d3/texture.rs
index d6f41e25..817d2cb4 100644
--- a/src/core/src/renderable/hips/d3/texture.rs
+++ b/src/core/src/renderable/hips/d3/texture.rs
@@ -1,5 +1,6 @@
use crate::time::Time;
+use crate::renderable::hips::d3::Freq;
use crate::Abort;
use crate::WebGlContext;
use al_core::image::fits::FitsImage;
@@ -304,6 +305,18 @@ impl HpxFreqTex {
}
}
+ pub fn frequencies(&self) -> Vec {
+ let delta_depth = self.num_slices.trailing_zeros();
+ let pixel_depth = self.cell.f_depth + delta_depth as u8;
+
+ let h0 = self.cell.f_hash << delta_depth;
+ let h1 = (self.cell.f_hash + 1) << delta_depth;
+
+ (h0..h1)
+ .map(|hash| Freq::from_hash_with_order(hash, pixel_depth).0 as f32)
+ .collect()
+ }
+
pub fn set_data_from_jpeg(
&mut self,
// the tile image of the whole cubic tile
diff --git a/src/js/HiPS.js b/src/js/HiPS.js
index efb42950..35f0b9bd 100644
--- a/src/js/HiPS.js
+++ b/src/js/HiPS.js
@@ -675,6 +675,10 @@ export let HiPS = (function () {
this.setOptions({additive});
};
+ HiPS.prototype.isSpectralCube = function() {
+ return this.hipsTileDepth !== undefined && this.hipsTileDepth !== null;
+ }
+
/**
* Sets the colormap when rendering the HiPS.
*
diff --git a/src/js/SpectraDisplayer.js b/src/js/SpectraDisplayer.js
index 01f22230..1154fa42 100644
--- a/src/js/SpectraDisplayer.js
+++ b/src/js/SpectraDisplayer.js
@@ -22,6 +22,8 @@ import { Input } from "./gui/Widgets/Input";
import HomeIconUrl from '../../assets/icons/maximize.svg';
import SpectraIconUrl from '../../assets/icons/freq.svg';
import { ALEvent } from "./events/ALEvent";
+import { Utils } from "./Utils";
+import { Aladin } from "./Aladin";
/******************************************************************************
* Aladin Lite project
@@ -185,20 +187,6 @@ export class SpectraDisplayer {
},
})
- this.selector = new Input({
- label: "HiPS3D selector:",
- name: "layer selector",
- type: 'select',
- classList: ['aladin-spectra-hips-selector'],
- options: [],
- change: (e) => {
- let name = e.target.value;
- let hips = self.hips3DList.get(name);
-
- self.attachHiPS3D(hips)
- },
- })
-
let autoCenterBtn = new ActionButton({
size: 'small',
icon: {
@@ -249,11 +237,6 @@ export class SpectraDisplayer {
divNode.appendChild(autoCenterBtn.element())
divNode.appendChild(extractionBtn.element())
- let divHiPSSelector = document.createElement("div")
- divHiPSSelector.innerHTML = 'Survey:';
- divHiPSSelector.appendChild(this.selector.element())
- divNode.appendChild(divHiPSSelector)
-
this.divNode = divNode;
this.view.aladin.aladinDiv.appendChild(divNode);
@@ -264,7 +247,7 @@ export class SpectraDisplayer {
defineEventListeners() {
let lastMouse = { x: 0, y: 0 };
- let isDragging = false;
+ this.isDragging = false;
let canvas = this.canvas;
let ctxCursor = this.ctxCursor;
@@ -285,7 +268,7 @@ export class SpectraDisplayer {
v = this.height - (v - this.minY) * this.scaleY
if (my >= v) {
- isDragging = true;
+ this.isDragging = true;
lastMouse = { x: mx, y: my };
canvas.style.cursor = 'grabbing';
} else {
@@ -294,11 +277,11 @@ export class SpectraDisplayer {
this.ctx.beginPath();
this.ctx.moveTo(this.scaleX * len / 2, this.height);
this.ctx.lineTo(this.scaleX * len / 2, this.height - (this.maxY - this.minY) * this.scaleY);
- this.ctx.strokeStyle = "red";
+ this.ctx.strokeStyle = Aladin.DEFAULT_OPTIONS.reticleColor;
this.ctx.lineWidth = 10;
if (this.ctx.isPointInStroke(mx, my)) {
- isDragging = true;
+ this.isDragging = true;
lastMouse = { x: mx, y: my };
canvas.style.cursor = 'grabbing';
} else {
@@ -374,7 +357,7 @@ export class SpectraDisplayer {
this._redrawLabels()
- if (!isDragging) {
+ if (!this.isDragging) {
// Draw the vertical line that can be grabed to move the slice
this.ctx.beginPath();
this.ctx.moveTo(this.scaleX * len / 2, this.height);
@@ -395,26 +378,32 @@ export class SpectraDisplayer {
// is dragged
let dx = (mx - lastMouse.x) / this.scaleX;
if (dx != 0) {
- // set the frequency
- let curFreq = self.hips.getFrequency();
+ // Set the frequency
- // let curHash = Number(self.view.wasm.freq2hash(self.hips.layer, curFreq));
- //let nextHash = curHash - Math.round(dx)
- //let nextFreq = self.view.wasm.hash2freq(self.hips.layer, BigInt(nextHash));
- let nextFreq = curFreq - Math.ceil(dx) * self.data.freqStep;
+ // look where we are in the freq range
+ let j = Utils.binarySearch(self.data.freqs, self.data.freq);
+ let df, f;
+ if (j > 0 && j < self.data.freqs.length - 1) {
+ df = (self.data.freqs[j + 1] - self.data.freqs[j - 1]) * 0.5;
+ f = self.data.freq - dx * df;
+ } else if (j == 0) {
+ df = self.data.freqs[1] - self.data.freqs[0]
+ f = self.data.freqs[0] - dx * df;
+ } else {
+ df = self.data.freqs[self.data.freqs.length - 1] - self.data.freqs[self.data.freqs.length - 2];
+ f = self.data.freqs[self.data.freqs.length - 1] - dx * df;
+ }
self.hips.setFrequency({
- value: nextFreq,
+ value: f,
unit: 'Hz'
})
- const correctedMx = (Math.ceil(dx) * this.scaleX) + lastMouse.x;
- //const correctedMx = mx;
- lastMouse = { x: correctedMx, y: my };
+ lastMouse = { x: mx, y: my };
}
});
canvas.addEventListener('mouseup', (e) => {
- isDragging = false;
+ this.isDragging = false;
canvas.style.cursor = 'default';
const clickEvent = new MouseEvent('click', {
@@ -427,7 +416,7 @@ export class SpectraDisplayer {
});
canvas.addEventListener('mouseout', (e) => {
- isDragging = false;
+ this.isDragging = false;
});
canvas.addEventListener('wheel', (e) => {
@@ -450,6 +439,7 @@ export class SpectraDisplayer {
this.view.catalogCanvas.dispatchEvent(wheelEvent);
});
+ /*
const updateSelectorList = () => {
let options = [];
for (const hipsName of this.hips3DList.keys()) {
@@ -505,7 +495,7 @@ export class SpectraDisplayer {
updateSelectorList()
}
- );
+ );*/
}
hide() {
@@ -540,7 +530,6 @@ export class SpectraDisplayer {
let data = event.detail;
if (data.layer === this.hips.layer) {
this.data = data;
- console.log(data)
this._redraw(this.ctx);
}
};
@@ -550,7 +539,7 @@ export class SpectraDisplayer {
this.resetScale();
this.show()
- this.selector.update({value: hips.name, title: hips.name})
+ //this.selector.update({value: hips.name, title: hips.name})
}
}
@@ -578,8 +567,6 @@ export class SpectraDisplayer {
// Find min and max for scaling
let valuesWithNoNans = values.filter(v=>Number.isFinite(v));
- const fOrder = this.data.fOrder;
-
if (Number.isFinite(this.minY)) {
this.minY = Math.min(...valuesWithNoNans, this.minY)
} else {
@@ -600,7 +587,7 @@ export class SpectraDisplayer {
this.ctx.beginPath();
this.ctx.moveTo(this.scaleX * len / 2, this.height);
this.ctx.lineTo(this.scaleX * len / 2, this.height - (this.maxY - this.minY) * this.scaleY);
- this.ctx.strokeStyle = "red";
+ this.ctx.strokeStyle = Aladin.DEFAULT_OPTIONS.reticleColor;
this.ctx.lineWidth = 2;
this.ctx.stroke();
@@ -644,6 +631,15 @@ export class SpectraDisplayer {
// Clear previous drawing
this.ctxLabels.clearRect(0, 0, this.width, this.height);
+ let drawLabel = (ctx, str, x, y, strokeStyle, font, fillStyle) => {
+ ctx.strokeStyle = strokeStyle; // contour color
+ ctx.strokeText(str, x, y);
+
+ ctx.fillStyle = fillStyle;
+ ctx.font = font
+ ctx.fillText(str, x, y);
+ };
+
// Draw the min and max frequencies
this.ctxLabels.font = "20px monospace"; // You can also use "Courier New", "Consolas", etc.
this.ctxLabels.fillStyle = "lightgreen";
@@ -651,22 +647,47 @@ export class SpectraDisplayer {
// min window freq
this.ctxLabels.textAlign = "left"; // Horizontally centered
- this.ctxLabels.fillText(spectraValue2String(this.data.freqMin, this.data.freqStep), 0, this.height - 20);
+ drawLabel(
+ this.ctxLabels,
+ spectraValue2String(this.data.freqMin, this.data.freqs[1] - this.data.freqs[0]),
+ 0,
+ this.height - 20,
+ 'black',
+ '20px monospace',
+ 'lightgreen'
+ )
// max window freq
this.ctxLabels.textAlign = "right"; // Horizontally centered
- this.ctxLabels.fillText(spectraValue2String(this.data.freqMax, this.data.freqStep), this.width, this.height - 20);
+ drawLabel(
+ this.ctxLabels,
+ spectraValue2String(this.data.freqMax, this.data.freqs[this.data.freqs.length - 1] - this.data.freqs[this.data.freqs.length - 2]),
+ this.width,
+ this.height - 20,
+ 'black',
+ '20px monospace',
+ 'lightgreen'
+ )
// current window freq
this.ctxLabels.textAlign = "center"; // Horizontally centered
- let fStr;
- if (this.mouseFreq) {
- this.ctxLabels.fillStyle = "yellow";
- fStr = spectraValue2String(this.mouseFreq, this.data.freqStep);
+ let str, fillStyle;
+ if (!this.isDragging && this.mouseFreq) {
+ fillStyle = "yellow";
+ str = spectraValue2String(this.mouseFreq, this.data.freqStep);
} else {
- fStr = spectraValue2String(this.data.freq, this.data.freqStep);
+ fillStyle = Aladin.DEFAULT_OPTIONS.reticleColor;
+ str = spectraValue2String(this.data.freq, this.data.freqStep);
}
- this.ctxLabels.fillText(fStr, this.width / 2, this.height - 20);
+ drawLabel(
+ this.ctxLabels,
+ str,
+ this.width / 2,
+ this.height - 20,
+ 'black',
+ '20px monospace',
+ fillStyle
+ )
}
_redrawSpectra(array) {
@@ -676,16 +697,15 @@ export class SpectraDisplayer {
let strokeStyle = "red";
this.ctx.strokeStyle = strokeStyle
- let fOrder = this.data.fOrder;
-
let prevY;
let i = 0;
let i1 = array.length;
- while (i <= i1) {
+
+ while (i < i1) {
let y;
const x = i * this.scaleX;
- const inValidDomain = this.data.freqIdxStart !== undefined && this.data.freqIdxEnd !== undefined && i > this.data.freqIdxStart && i < this.data.freqIdxEnd;
+ const inValidDomain = this.data.freqIdxStart !== undefined && this.data.freqIdxEnd !== undefined && i >= this.data.freqIdxStart && i <= this.data.freqIdxEnd;
if (inValidDomain) {
const tileNotReceived = !Number.isFinite(array[i]);
@@ -720,6 +740,9 @@ export class SpectraDisplayer {
}
y = this.height - (array[i] - this.minY) * this.scaleY;
+
+
+
if (i === 0) {
this.ctx.moveTo(x, y);
} else {
diff --git a/src/js/Utils.ts b/src/js/Utils.ts
index 98a1bf02..df3d671c 100644
--- a/src/js/Utils.ts
+++ b/src/js/Utils.ts
@@ -149,12 +149,18 @@ Utils.inverseNewtonRaphson = function(y: number, f: Function, fPrime: Function,
Utils.binarySearch = function(array, value) {
var low = 0,
high = array.length;
-
+ var mid;
while (low < high) {
- var mid = (low + high) >>> 1;
- if (array[mid] > value) low = mid + 1;
- else high = mid;
+ mid = Math.floor((low + high) / 2);
+ if (array[mid] === value) {
+ return mid;
+ } else if (array[mid] < value) {
+ low = mid + 1;
+ } else {
+ high = mid;
+ }
}
+
return low;
}
diff --git a/src/js/View.js b/src/js/View.js
index f076ee4b..8055ca14 100644
--- a/src/js/View.js
+++ b/src/js/View.js
@@ -1300,7 +1300,7 @@ export let View = (function () {
if (!view.throttledTouchPadZoom) {
view.throttledTouchPadZoom = () => {
- const factor = Utils.detectTrackPad(e) ? 1.06 : 1.2;
+ const factor = Utils.detectTrackPad(e) ? 1.05 : 1.2;
const currZoomFactor = view.zoom.isZooming ? view.zoom.finalZoom : view.zoomFactor;
let newZoomFactor = view.delta > 0 ? currZoomFactor * factor : currZoomFactor / factor;
diff --git a/src/js/gui/Box/HiPSSettingsBox.js b/src/js/gui/Box/HiPSSettingsBox.js
index 6a701aad..5bb971eb 100644
--- a/src/js/gui/Box/HiPSSettingsBox.js
+++ b/src/js/gui/Box/HiPSSettingsBox.js
@@ -35,6 +35,7 @@ import { Form } from "../Widgets/Form.js";
import colorIconUrl from '../../../../assets/icons/color.svg';
import pixelHistIconUrl from '../../../../assets/icons/pixel_histogram.svg';
import { RadioButton } from "../Widgets/Radio.js";
+ import waveOnIconUrl from '../../../../assets/icons/wave-on.svg';
import { Layout } from "../Layout.js";
@@ -42,58 +43,62 @@ import { Form } from "../Widgets/Form.js";
// Constructor
constructor(aladin, options) {
let self;
- let selector = new RadioButton({
- luminosity: {
- icon: {
- size: 'small',
- monochrome: true,
- url: luminosityIconUrl
+
+ let radioOptions = () => {
+ return {
+ luminosity: {
+ icon: {
+ size: 'small',
+ monochrome: true,
+ url: luminosityIconUrl
+ },
+ tooltip: {content: 'Contrast', position: {direction: 'bottom'}},
+ action: (e) => {
+ const content = Layout.vertical({
+ layout: [self.selector, self.luminositySettingsContent]
+ });
+ self.update({content})
+ }
},
- tooltip: {content: 'Contrast', position: {direction: 'bottom'}},
- action: (e) => {
- const content = Layout.vertical({
- layout: [self.selector, self.luminositySettingsContent]
- });
- self.update({content})
- }
- },
- opacity: {
- icon: {
- size: 'small',
- monochrome: true,
- url: opacityIconUrl
+ opacity: {
+ icon: {
+ size: 'small',
+ monochrome: true,
+ url: opacityIconUrl
+ },
+ tooltip: {content: 'Opacity', position: {direction: 'bottom'}},
+ action: (e) => {
+ const content = Layout.vertical({layout: [self.selector, self.opacitySettingsContent]});
+ self.update({content})
+ }
},
- tooltip: {content: 'Opacity', position: {direction: 'bottom'}},
- action: (e) => {
- const content = Layout.vertical({layout: [self.selector, self.opacitySettingsContent]});
- self.update({content})
- }
- },
- colors: {
- icon: {
- size: 'small',
- url: colorIconUrl
+ colors: {
+ icon: {
+ size: 'small',
+ url: colorIconUrl
+ },
+ tooltip: {content: 'Colormap', position: {direction: 'bottom'}},
+ action: (e) => {
+ const content = Layout.vertical({layout: [self.selector, self.colorSettingsContent]});
+ self.update({content})
+ }
},
- tooltip: {content: 'Colormap', position: {direction: 'bottom'}},
- action: (e) => {
- const content = Layout.vertical({layout: [self.selector, self.colorSettingsContent]});
- self.update({content})
- }
- },
- pixel: {
- icon: {
- size: 'small',
- monochrome: true,
- url: pixelHistIconUrl
+ pixel: {
+ icon: {
+ size: 'small',
+ monochrome: true,
+ url: pixelHistIconUrl
+ },
+ tooltip: {content: 'Cutouts', position: {direction: 'bottom'}},
+ action: (e) => {
+ const content = Layout.vertical({layout: [self.selector, self.pixelSettingsContent]});
+ self.update({content})
+ }
},
- tooltip: {content: 'Cutouts', position: {direction: 'bottom'}},
- action: (e) => {
- const content = Layout.vertical({layout: [self.selector, self.pixelSettingsContent]});
- self.update({content})
- }
- },
- selected: 'opacity'
- }, aladin);
+ selected: 'opacity'
+ }
+ };
+ let selector = new RadioButton(radioOptions(), aladin);
// Define the contents
@@ -283,6 +288,8 @@ import { Form } from "../Widgets/Form.js";
aladin.aladinDiv)
self = this;
+ this.radioOptions = radioOptions;
+
this.aladin = aladin;
this._addListeners()
@@ -328,6 +335,34 @@ import { Form } from "../Widgets/Form.js";
update(options) {
if (options.layer) {
+ let self = this;
+ if (options.layer.isSpectralCube()) {
+ self.selector = new RadioButton({
+ ...this.radioOptions(),
+ spectra: {
+ icon: {
+ size: 'small',
+ monochrome: true,
+ url: waveOnIconUrl
+ },
+ tooltip: {content: 'Spectra', position: {direction: 'bottom'}},
+ action: (e) => {
+ let spectraDisplayer = self.aladin.view.spectraDisplayer;
+ if (spectraDisplayer.isHidden) {
+ spectraDisplayer.attachHiPS3D(options.layer)
+ spectraDisplayer.show()
+ } else {
+ spectraDisplayer.hide()
+ }
+ }
+ }
+ }, self.aladin);
+
+ console.log(self)
+
+ self.update({content: Layout.vertical([self.selector, self.opacitySettingsContent])})
+ }
+
this._update(options.layer)
}
@@ -335,9 +370,12 @@ import { Form } from "../Widgets/Form.js";
}
_addListeners() {
+ let self = this;
+
ALEvent.HIPS_LAYER_CHANGED.listenedBy(this.aladin.aladinDiv, (e) => {
const hips = e.detail.layer;
let selectedLayer = this.options.layer;
+
if (selectedLayer && hips.layer === selectedLayer.layer) {
this._update(hips)
}