Compare commits

..

30 Commits

Author SHA1 Message Date
Matthieu Baumann
3476c535e6 v3.8.2 2026-03-06 16:48:31 +01:00
Matthieu Baumann
af3f844bcc fix incl of aladin lite script in head 2026-03-06 16:41:38 +01:00
Tom Donaldson
6dc1722cd2 Update change log. 2026-03-05 16:14:05 +01:00
Tom Donaldson
9a47a6b11d Allow GraphicOverlay.increaseBrightness() to accept named colors 2026-03-05 16:14:05 +01:00
Matthieu Baumann
543fc7ef8e fix doc 2026-03-05 14:57:24 +01:00
Matthieu Baumann
1780d6c55d v3.8.1 2026-03-05 14:41:30 +01:00
Matthieu Baumann
abb75ddb47 unstage snapshots 2026-03-05 14:31:58 +01:00
Matthieu Baumann
d29d900354 add test on rotation angle before giving it to aladin lite: https://github.com/cds-astro/aladin-lite/issues/330 2026-03-05 14:29:14 +01:00
Matthieu Baumann
3a6455e018 impl https://github.com/cds-astro/aladin-lite/issues/337 2026-03-05 11:43:38 +01:00
Tom Donaldson
fb0a562b09 Update CHANGELOG.md 2026-03-05 09:40:48 +01:00
Tom Donaldson
19f06fe0bf Minor doc text update 2026-03-05 09:40:48 +01:00
Tom Donaldson
2fed96e2a4 Minor doc text update 2026-03-05 09:40:48 +01:00
Tom Donaldson
5f5fd7b555 Support selectionLineWidth option for shapes and catalogs 2026-03-05 09:40:48 +01:00
Matthieu Baumann
bdb4f4aa5b fix https://github.com/cds-astro/aladin-lite/issues/342 change wcs if the real size of the image does not match the NAXIS values 2026-03-04 18:51:10 +01:00
Matthieu Baumann
5567b14b6a add getFrequencyWindow on HiPS3D object 2026-03-04 17:55:39 +01:00
Matthieu Baumann
bf290e13e4 Fix stackChanged 2026-03-04 15:19:29 +01:00
Matthieu Baumann
f41765adcd Fix commit
* HiPS3D in fits black artifact removed
* accepts fits ext files when searching for a FITS image to display
2026-03-04 14:34:17 +01:00
Matthieu Baumann
cf8c793312 impl Worker for handling png cropping of tiles 2026-03-03 22:47:20 +01:00
Matthieu Baumann
abd97ffade fix #352 2026-03-03 15:55:54 +01:00
Matthieu Baumann
55cdf454b7 open the context menu if rightclick is not called. potential fix to #348 2026-03-03 11:56:45 +01:00
Matthieu Baumann
a7c412088c do not clip the windows out of the aladin lite div #345 2026-03-02 17:43:43 +01:00
Matthieu Baumann
887284da23 fix #350: preventDefault prevents click event to be triggered after a starttouch/endtouch 2026-03-02 11:30:30 +01:00
Matthieu Baumann
ff6399471c fix #351 2026-03-02 11:02:22 +01:00
Matthieu Baumann
0861294c66 fix #353 by adding more precision when displaying meridian grid labels 2026-03-02 10:11:06 +01:00
Matthieu Baumann
7aab92cb54 web worker WIP 2026-02-05 14:22:35 +01:00
Matthieu Baumann
c8a75b98db fix cargo clippy 2026-02-03 17:43:50 +01:00
Matthieu Baumann
47cbcee65a fix offsrte color picker 2026-02-03 11:36:13 +01:00
Matthieu Baumann
9d29a4f500 add documentation for Toolbar.add method 2026-02-01 16:55:21 +01:00
Matthieu Baumann
ab5019603b remove comment 2026-01-31 23:06:29 +01:00
Matthieu Baumann
5a9561a13a change license from GPL3 to LGPL3 2026-01-31 23:06:29 +01:00
82 changed files with 7173 additions and 7151 deletions

2
.gitignore vendored
View File

@@ -39,6 +39,8 @@ jsdoc-make-responsivedocs
test-results/
playwright-report/
tests/run-examples.spec.ts-snapshots
## packaged tar obtained from the cmd: npm run predeploy or npm run deploy
aladin-lite*.tgz
# folder where the generated bundle lies

View File

@@ -8,6 +8,10 @@
### 3.8.0
* [fix] Fix shape hover colors when overlay uses named color (increaseBrightness() accepts names now) <https://github.com/cds-astro/aladin-lite/issues/355>
* [feat] Selected and hovered items (shapes, sources, ...) are rendered at last. <https://github.com/cds-astro/aladin-lite/issues/337>
* [feat] Add selectionLineWidth option for shapes and catalogs. <https://github.com/cds-astro/aladin-lite/pull/354>
* [fix] horizontal/vertical overlay lines appearing correctly <https://github.com/cds-astro/aladin-lite/issues/334>
* [fix] layer opacity restored when switching from not visible to visible <https://github.com/cds-astro/aladin-lite/issues/332>
* [feat] dark/light mode for the interface

View File

@@ -8,8 +8,8 @@
"dateModified": "2023-01-31",
"issueTracker": "https://github.com/cds-astro/aladin-lite/issues",
"name": "Aladin Lite",
"version": "3.7.3-beta",
"softwareVersion": "3.7.3-beta",
"version": "3.8.1",
"softwareVersion": "3.8.1",
"description": "An astronomical HiPS visualizer in the browser.",
"identifier": "10.5281/zenodo.7638833",
"applicationCategory": "Astronomy, Visualization",

View File

@@ -7,10 +7,10 @@
<script type="module">
import A from '../src/js/A.js';
A.init.then(() => {
let aladin = A.aladin('#aladin-lite-div', {target: "23 28 32.46 -00 10 38.9", fov: 4, projection: "AIT"});
let aladin = A.aladin('#aladin-lite-div', {survey: ["lksldf", 'ljkjsdf', "https://alasky.cds.unistra.fr/HIPS3D/LGLBSHI16"], target: "23 28 32.46 -00 10 38.9", fov: 4, projection: "AIT"});
let hsc = aladin.newImageSurvey("P/HSC/DR2/deep/g", {colormap:"Purples", imgFormat: "fits"});
aladin.setBaseImageLayer(hsc);
//let hsc = aladin.newImageSurvey("P/HSC/DR2/deep/g", {colormap:"Purples", imgFormat: "fits"});
//aladin.setBaseImageLayer(hsc);
});
</script>

View File

@@ -4,7 +4,7 @@
</head>
<body>
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
<div id="aladin-lite-div" style="width: 200px; height: 768px"></div>
<script>let aladin;</script>
<script type="module">
import A from '../src/js/A.js';
@@ -14,7 +14,6 @@
target: "M31",
fov: 89.78,
showContextMenu: true,
fullScreen: true,
showSimbadPointerControl: true,
showShareControl: true,
showSettingsControl: true,

View File

@@ -23,6 +23,9 @@ Image Opacity: <br/> <input id="slider" type="range" value=1 min=0 max=1 step=0.
aladin = A.aladin('#aladin-lite-div', {target: "0 0", cooFrame: "gal"});
var callback = function(ra, dec, fov) {
aladin.gotoRaDec(ra, dec)
aladin.setFov(fov)
A.catalogFromSimbad( {ra: ra, dec: dec} , 1 , {shape: 'circle', color: '#5d5', onClick: 'showTable'}, (cat) => {
aladin.addCatalog(cat)
});
@@ -37,16 +40,14 @@ Image Opacity: <br/> <input id="slider" type="range" value=1 min=0 max=1 step=0.
aladin.setBaseImageLayer('P/Mellinger/color');
//let fits = aladin.displayFITS('http://goldmine.mib.infn.it/data//B/fits/A04_VC1316_ooooog.fits', 'overlay');
let jpg = aladin.displayJPG(
aladin.setOverlayImageLayer(A.image(
// the JPG to transform to HiPS
'https://owncloud.tuebingen.mpg.de/index.php/s/sdxfNgcEaaXoBp7/download/nightskycam3_2025_08_07_05_17_30_healpix1024_red.fits',
'https://www.astropix.org/archive/esahubble/potm2602b/esahubble_potm2602b_1024.jpg',
// no options
{
transparency: 1.0,
successCallback: callback
},
// A callback fn once the overlay is set
callback
);
));
$('input[name=survey]').change(function() {
let surveyName = $(this).val();

View File

@@ -10,6 +10,10 @@
import A from '../src/js/A.js';
A.init.then(() => {
var aladin = A.aladin('#aladin-lite-div', {showContextMenu: true, target: '05 37 58 +08 17 35', fov: 12, backgroundColor: 'rgb(120, 0, 0)'});
aladin.on('stackChanged', function(state) {
console.log(state)
});
var cat = A.catalog({sourceSize: 20, onClick: (s) => {console.log("kjk", s)}});
aladin.addCatalog(cat);
cat.addSources([A.source(83.784490, 9.934156, {name: 'Meissa'}), A.source(88.792939, 7.407064, {name: 'Betelgeuse'}), A.source(81.282764, 6.349703, {name: 'Bellatrix'})]);
@@ -68,10 +72,6 @@
console.log(pos)
});
aladin.on('stackChanged', function(state) {
console.log(state)
});
cat.sources[0].actionClicked();
});
</script>

View File

@@ -12,6 +12,7 @@
A.init.then(() => {
// Start up Aladin Lite
aladin = A.aladin('#aladin-lite-div', {target: 'M 1', fov: 0.2, showContextMenu: true, fullScreen: true});
var overlay = A.graphicOverlay({color: 'purple', lineWidth: 3, lineDash: [2, 2]});
aladin.addOverlay(overlay);
overlay.addFootprints([

View File

@@ -3,8 +3,6 @@
<head>
</head>
<body>
<div id="aladin-lite-div" style="width: 768px; height: 512px"></div>
<script>let aladin;</script>
<script type="module">

View File

@@ -3,9 +3,9 @@
<head>
</head>
<body>
<div id="al1" style="width: 500px; height: 500px">
<div id="al1" style="width: 512px; height: 512px">
</div>
<div id="al2" style="width: 500px; height: 500px">
<div id="al2" style="width: 512px; height: 512px">
</div>
<script type="module">
@@ -15,7 +15,7 @@
A.init.then(() => {
// Start up Aladin Lite
al1 = A.aladin('#al1', {target: 'M51', fov: 0.3, survey: 'P/DSS2/color', fullScreen: false, showContextMenu: true, showShareControl: true});
al2 = A.aladin('#al2', {target: 'M51', fov: 180, survey: 'P/PanSTARRS/DR1/z', fullScreen: false, showContextMenu: true, showShareControl: true});
al2 = A.aladin('#al2', {target: 'M51', fov: 180, survey: 'P/PanSTARRS/DR1/z', showColorPickerControl: true, fullScreen: false, showContextMenu: true, showShareControl: true});
});
</script>

View File

@@ -9,7 +9,7 @@
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {survey: 'data/hips/CDS_P_DSS2_color', target: '05 34 30.89 +22 00 52.9', fov: 0.3, log: false, showSettingsControl: true});
var cat = A.catalogFromURL('data/votable/labels.vot', {sourceSize:12, color: '#cc99bb', displayLabel: true, labelColumn: 'main_id', labelColor: '#ae4', labelFont: '9px sans-serif'});
var cat = A.catalogFromURL('data/votable/labels.vot', {sourceSize:20, shape: 'cross', color: '#cc99bb', displayLabel: true, labelColumn: 'main_id', labelColor: '#ae4', labelFont: '9px sans-serif'});
aladin.addCatalog(cat);
});
</script>

View File

@@ -21,6 +21,7 @@
expandLayersControl: true,
hipsList: [
// High energy (Gamma and X-rays)
'https://alasky.cds.unistra.fr/HIPS3D/GalfaHI',
'CDS/P/HGPS/Flux',
'CDS/P/Fermi/5',
'CDS/P/Fermi/4',

View File

@@ -1,16 +1,20 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, height=device-height, maximum-scale=1.0, initial-scale=1.0, user-scalable=no">
<script type="text/javascript" src="./../dist/aladin.umd.cjs" charset="utf-8"></script>
</head>
<body>
<div id="aladin-lite-div" style="width: 500px; height: 500px"></div>
<script type="text/javascript" src="./../dist/aladin.umd.cjs" charset="utf-8"></script>
<script type="text/javascript">
var aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {fullScreen: true, cooFrame: "ICRSd", showSimbadPointerControl: true, showShareControl: true, showShareControl: true, fov: 180, showContextMenu: true});
aladin = A.aladin('#aladin-lite-div', {fullScreen: true, showCooLocation: true, showSettingsControl: true, cooFrame: "ICRSd", showSimbadPointerControl: true, showShareControl: true, showShareControl: true, fov: 180, showContextMenu: true});
// manage URL parameters
const searchParams = new URL(document.location).searchParams;
if (searchParams.has('baseImageLayer')) {

View File

@@ -2,7 +2,7 @@
"homepage": "https://aladin.cds.unistra.fr/",
"name": "aladin-lite",
"type": "module",
"version": "3.8.0-beta",
"version": "3.8.2",
"description": "An astronomical HiPS visualizer in the browser",
"author": "Thomas Boch and Matthieu Baumann",
"license": "GPL-3",
@@ -48,7 +48,7 @@
"test:build": "cd src/core && cargo test --release --features webgl2",
"test:playwright": "npx playwright test",
"test:update-snapshots": "npx playwright test --update-snapshots",
"doc": "jsdoc -c jsdoc.json src/js src/js/shapes src/js/libs/astro && cp aladin-logo.png docs/ && cp jsdoc-custom-style.css docs/ && cp jsdoc-make-responsive.js docs/",
"doc": "jsdoc -c jsdoc.json src/js src/js/gui src/js/shapes src/js/libs/astro && cp aladin-logo.png docs/ && cp jsdoc-custom-style.css docs/ && cp jsdoc-make-responsive.js docs/",
"doc:dev": "npm run doc && open docs/index.html",
"analyze": "vite build --mode analyze"
},

View File

@@ -3,7 +3,7 @@ name = "aladin-lite"
description = "Aladin Lite v3 introduces a new graphical engine written in Rust with the use of WebGL"
license = "BSD-3-Clause"
repository = "https://github.com/cds-astro/aladin-lite"
version = "3.8.0-beta"
version = "3.8.2"
authors = [ "baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr",]
edition = "2018"
@@ -49,8 +49,7 @@ version = "0.7.3"
[dependencies.moclib]
package = "moc"
git = "https://github.com/cds-astro/cds-moc-rust"
branch = "main"
version = "0.19.1"
[dependencies.serde]
version = "^1.0.183"
@@ -64,7 +63,7 @@ path = "./al-api"
[dependencies.web-sys]
version = "0.3.56"
features = [ "console", "CssStyleDeclaration", "Document", "Element", "HtmlCollection", "CustomEvent", "CustomEventInit", "HtmlElement", "HtmlImageElement", "HtmlCanvasElement", "Blob", "ImageBitmap", "ImageData", "CanvasRenderingContext2d", "WebGlBuffer", "WebGlContextAttributes", "WebGlFramebuffer", "WebGlProgram", "WebGlShader", "WebGlUniformLocation", "WebGlTexture", "WebGlActiveInfo", "Headers", "Window", "Request", "RequestInit", "RequestMode", "RequestCredentials", "Response", "XmlHttpRequest", "XmlHttpRequestResponseType", "PerformanceTiming", "Performance", "Url", "ReadableStream", "File", "FileList",]
features = [ "console", "CssStyleDeclaration", "Document", "Element", "HtmlCollection", "CustomEvent", "CustomEventInit", "HtmlElement", "HtmlImageElement", "HtmlCanvasElement", "Blob", "Url", "ImageBitmap", "ImageData", "CanvasRenderingContext2d", "WebGlBuffer", "WebGlContextAttributes", "WebGlFramebuffer", "WebGlProgram", "WebGlShader", "WebGlUniformLocation", "WebGlTexture", "WebGlActiveInfo", "Headers", "Window", "Request", "RequestInit", "RequestMode", "RequestCredentials", "Response", "XmlHttpRequest", "XmlHttpRequestResponseType", "PerformanceTiming", "Performance", "Url", "ReadableStream", "File", "FileList", "OffscreenCanvas", "OffscreenCanvasRenderingContext2d", "Worker", "WorkerOptions", "WorkerType",]
[dev-dependencies.image-decoder]
package = "image"

View File

@@ -1,6 +1,6 @@
[package]
name = "al-api"
version = "3.8.0"
version = "3.8.2"
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
edition = "2018"

View File

@@ -1,6 +1,6 @@
[package]
name = "al-core"
version = "3.8.0"
version = "3.8.2"
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
edition = "2018"
@@ -35,43 +35,8 @@ webgl2 = [
[dependencies.web-sys]
version = "0.3.56"
features = [
'console',
'CssStyleDeclaration',
'Document',
'Element',
'HtmlCollection',
'HtmlElement',
'HtmlImageElement',
'HtmlCanvasElement',
'Blob',
'ImageBitmap',
'ImageData',
features = [ "console", "CssStyleDeclaration", "Document", "Element", "HtmlCollection", "CustomEvent", "CustomEventInit", "HtmlElement", "HtmlImageElement", "HtmlCanvasElement", "Blob", "ImageBitmap", "ImageData", "CanvasRenderingContext2d", "WebGlBuffer", "WebGlContextAttributes", "WebGlFramebuffer", "WebGlProgram", "WebGlShader", "WebGlUniformLocation", "WebGlTexture", "WebGlActiveInfo", "Headers", "Window", "Request", "RequestInit", "RequestMode", "RequestCredentials", "Response", "XmlHttpRequest", "XmlHttpRequestResponseType", "PerformanceTiming", "Performance", "Url", "ReadableStream", "File", "FileList", "OffscreenCanvas", "OffscreenCanvasRenderingContext2d", "Worker", "WorkerOptions", "WorkerType"]
'CanvasRenderingContext2d',
'WebGlBuffer',
'WebGlContextAttributes',
'WebGlFramebuffer',
'WebGlProgram',
'WebGlShader',
'WebGlUniformLocation',
'WebGlTexture',
'WebGlActiveInfo',
'Window',
'Request',
'RequestInit',
'RequestMode',
'Response',
'XmlHttpRequest',
'XmlHttpRequestResponseType',
'PerformanceTiming',
'Performance',
'Url',
'File',
'FileList'
]
[profile.dev]
opt-level = 's'

View File

@@ -58,11 +58,23 @@ impl Texture3D {
pixel_type: F::PIXEL_TYPE,
})));
Ok(Texture3D {
let s = Texture3D {
texture,
gl: gl.clone(),
metadata,
})
};
let voxel_count = F::NUM_CHANNELS * (width as usize) * (height as usize) * (depth as usize);
let zeros = vec![0xff; voxel_count];
s.bind().tex_sub_image_3d_with_opt_u8_array(
0,
0,
0,
width,
height,
depth,
Some(&zeros[..]),
);
Ok(s)
}
pub fn generate_mipmap(&self) {

View File

@@ -1,21 +0,0 @@
[package]
name = "al-task-exec"
version = "0.1.0"
authors = ["Matthieu Baumann <matthieu.baumann@astro.unistra.fr>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
futures = "0.3.5"
futures-task = "0.3.5"
wasm-bindgen = "0.2.79"
[dependencies.web-sys]
version = "0.3.40"
features = [
'Document',
'Window',
'PerformanceTiming',
'Performance',
]

View File

@@ -1,338 +0,0 @@
use {
futures::{
future::FutureExt,
task::{waker_ref, ArcWake},
},
std::{
future::Future,
sync::Arc,
task::{Context, Poll},
},
};
struct Thread {
_thread: std::thread::Thread,
}
use std::thread;
thread_local! {
static CURRENT_THREAD_NOTIFY: Arc<Thread> = Arc::new(Thread {
_thread: thread::current(),
});
}
impl ArcWake for Thread {
fn wake_by_ref(_arc_self: &Arc<Self>) {}
}
use std::cell::RefCell;
/// `Spawner` spawns new futures onto the task channel.
use std::collections::VecDeque;
use std::pin::Pin;
use std::rc::{Rc, Weak};
use std::collections::HashMap;
// A queue that contains keyed element
struct KeyedVecDeque<K, V>
where
K: Hash + Eq + Clone,
{
keys: VecDeque<K>,
values: HashMap<K, V>,
}
use std::hash::Hash;
impl<K, V> KeyedVecDeque<K, V>
where
K: Hash + Eq + Clone,
{
fn new() -> Self {
let keys = VecDeque::new();
let values = HashMap::new();
Self { keys, values }
}
fn push_front(&mut self, key: K, value: V) {
self.keys.push_front(key.clone());
self.values.insert(key, value);
}
fn pop_back(&mut self) -> Option<(K, V)> {
if self.keys.is_empty() {
None
} else {
let mut v = None;
while !self.keys.is_empty() && v.is_none() {
let k = self.keys.pop_back().unwrap();
v = self.values.remove(&k).map(|v| (k, v));
}
v
}
}
fn remove(&mut self, k: &K) -> Option<V> {
self.values.remove(k)
}
}
type Incoming<K, T> = RefCell<KeyedVecDeque<K, Pin<Box<dyn Future<Output = T> + 'static>>>>;
#[derive(Clone)]
pub struct Spawner<K, T>
where
K: Hash + Eq + Clone,
{
tasks: Weak<Incoming<K, T>>,
}
impl<K, T> Spawner<K, T>
where
K: Hash + Eq + Clone,
{
pub fn spawn(&mut self, key: K, future: impl Future<Output = T> + 'static) {
let future = future.boxed_local();
self.tasks
.upgrade() // convert to Rc
.unwrap()
.borrow_mut() // Push the new task to the front of the queue
.push_front(key, future);
}
}
/// Task executor that receives tasks off of a channel and runs them.
pub struct Executor<K, T>
where
K: Hash + Eq + Clone,
{
tasks: Rc<Incoming<K, T>>,
spawner: Spawner<K, T>,
}
impl<K, T> Default for Executor<K, T>
where
K: Hash + Eq + Clone,
{
fn default() -> Self {
let tasks = Rc::new(RefCell::new(KeyedVecDeque::new()));
let spawner = Spawner {
tasks: Rc::downgrade(&tasks),
};
Executor { tasks, spawner }
}
}
impl<K, T> Executor<K, T>
where
K: Hash + Eq + Clone + Sized,
{
pub fn new() -> Self {
Self::default()
}
pub fn spawner(&mut self) -> &mut Spawner<K, T> {
&mut self.spawner
}
pub fn run(&mut self, timeout: f32) -> Vec<T> {
let mut results = vec![];
CURRENT_THREAD_NOTIFY.with(|thread| {
// Create a `LocalWaker` from the current thread itself
let waker = waker_ref(thread);
let mut cx = Context::from_waker(&waker);
// Take all the task available from the channel
// Exit the loop when either the channel is disconnected or
// there are no tasks available to process.
let mut tasks = self.tasks.borrow_mut();
let window = web_sys::window().expect("should have a window in this context");
let performance = window
.performance()
.expect("performance should be available");
let start = performance.now() as f32;
while let Some((k, mut task)) = tasks.pop_back() {
// Take the future, and if it has not yet completed (is still Some),
// poll it in an attempt to complete it.
// We store `Pin<Box<dyn Future<Output = T> + 'static>>`.
// We can get a `Pin<&mut dyn Future + 'static>`
// from it by calling the `Pin::as_mut` method.
let r = task.as_mut().poll(&mut cx);
match r {
Poll::Pending => {
// Wake up the task pending immediately
cx.waker().clone().wake();
// Reinsert not finished futures into the tasks queue
tasks.push_front(k, task);
}
Poll::Ready(result) => {
// If the future is completed, get the result
// and return it to the user
results.push(result);
}
}
let now = performance.now() as f32;
// Break the running if we exceed the timeout
if (now - start) >= timeout {
break;
}
}
});
results
}
// Remove a task from the executor so that
// it won't be polled anymore
pub fn remove(&mut self, k: &K) {
self.tasks.borrow_mut().remove(k);
}
}
#[cfg(test)]
mod tests {
use super::Executor;
use futures::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
// Define some futures to run concurrently on a single thread
struct LearnTask(usize);
impl LearnTask {
fn new() -> Self {
LearnTask(0)
}
}
impl Future for LearnTask {
type Output = u8;
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
self.0 += 1;
println!("I'm learning {}", self.0);
if self.0 == 10000 {
Poll::Ready(0)
} else {
//cx.waker().clone().wake();
Poll::Pending
}
}
}
struct DanceTask(usize);
impl DanceTask {
fn new() -> Self {
DanceTask(0)
}
}
impl Future for DanceTask {
type Output = u8;
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
self.0 += 1;
println!("I'm dancing {}", self.0);
if self.0 == 10000 {
Poll::Ready(0)
} else {
//cx.waker().clone().wake();
Poll::Pending
}
}
}
#[test]
fn it_works() {
let mut executor = Executor::new();
let spawner = executor.spawner();
// Spawn a task to print before and after waiting on a timer.
spawner.spawn("learning", async {
println!("LEARN begin!");
// Wait for our timer future to complete after two seconds.
LearnTask::new().await;
println!("LEARN done!");
10
});
spawner.spawn("dancing", async {
println!("DANCE begin!");
// Wait for our timer future to complete after two seconds.
DanceTask::new().await;
println!("DANCE done!");
10
});
// Run the executor for a duration of 5 milliseconds
executor.run(5_f32);
}
use futures::stream::Stream;
pub struct ParseTable {
table: Vec<u32>,
ready: bool,
idx: u32,
}
impl ParseTable {
pub fn new(table: Vec<u32>) -> Self {
let idx = 0;
let ready = false;
Self { table, idx, ready }
}
}
impl Stream for ParseTable {
type Item = u32;
/// Attempt to resolve the next item in the stream.
/// Returns `Poll::Pending` if not ready, `Poll::Ready(Some(x))` if a value
/// is ready, and `Poll::Ready(None)` if the stream has completed.
fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
// Deserialize row by row.
let len = self.table.len();
let idx = self.idx as usize;
while self.idx < len as u32 {
if !self.ready {
self.table[idx] += 10;
self.ready = true;
return Poll::Pending;
} else {
println!("{}", idx);
let row = self.table[idx];
self.idx += 1;
self.ready = false;
return Poll::Ready(Some(row));
}
}
Poll::Ready(None)
}
}
use futures::stream::StreamExt; // for `next`
#[test]
fn it_works2() {
let mut executor = Executor::new();
let spawner = executor.spawner();
// Spawn a task to print before and after waiting on a timer.
spawner.spawn("parsing", async {
println!("BEGIN parsing!");
let mut stream = ParseTable::new((0..100000).collect());
let mut results: Vec<u32> = vec![];
while let Some(item) = stream.next().await {
results.push(item);
}
println!("END parsing!");
10
});
// Run the executor for a duration of 5 milliseconds
executor.run(50_f32);
}
}

View File

@@ -62,6 +62,7 @@ pub struct App {
//ui: GuiRef,
shaders: ShaderManager,
pub camera: CameraViewPort,
worker: Worker,
downloader: Rc<RefCell<Downloader>>,
tile_fetcher: TileFetcherQueue,
@@ -105,6 +106,8 @@ pub struct App {
pub projection: ProjectionType,
cubic_tile_recv: async_channel::Receiver<WorkerResponse>,
// Async data receivers
//img_send: async_channel::Sender<ImageLayer>,
img_recv: async_channel::Receiver<ImageLayer>,
@@ -199,8 +202,7 @@ impl App {
let (_, img_recv) = async_channel::unbounded::<ImageLayer>();
let (ack_img_send, _) = async_channel::unbounded::<ImageParams>();
//let line_renderer = RasterizedLineRenderer::new(&gl)?;
let (cubic_tile_send, cubic_tile_recv) = async_channel::unbounded::<WorkerResponse>();
let dist_dragging = 0.0;
let time_start_dragging = Time::now();
@@ -210,6 +212,48 @@ impl App {
let browser_features_support = BrowserFeaturesSupport::new();
let vel_history = vec![];
let worker = create_worker()?;
// Send the ack to the js promise so that she finished
let onmessage = Closure::<dyn FnMut(web_sys::MessageEvent)>::new(
move |event: web_sys::MessageEvent| {
let data = event.data();
let bytes_js = js_sys::Reflect::get(&data, &"bytes".into())
.unwrap()
.dyn_into::<js_sys::Uint8Array>()
.expect("is not a uint8 buffer");
// Zero-copy clone from JS memory
let mut bytes = vec![0u8; bytes_js.length() as usize];
bytes_js.copy_to(&mut bytes);
let meta: WorkerResponseMeta = serde_wasm_bindgen::from_value(data).unwrap();
let response = WorkerResponse {
bytes,
tile_size: meta.tile_size,
tile_depth: meta.tile_depth,
cell: meta.cell,
hips_cdid: meta.hips_cdid,
};
let c = cubic_tile_send.clone();
wasm_bindgen_futures::spawn_local(async move {
c.send(response).await.unwrap_throw();
})
},
);
worker.set_onmessage(Some(onmessage.as_ref().unchecked_ref()));
// 🚨 VERY IMPORTANT: prevent the closure from being dropped
onmessage.forget();
gl.blend_func(
WebGl2RenderingContext::SRC_ALPHA,
WebGl2RenderingContext::ONE_MINUS_SRC_ALPHA,
);
Ok(App {
gl,
//ui,
@@ -247,6 +291,7 @@ impl App {
time_mouse_high_vel,
dragging,
vel_history,
worker,
prev_cam_position,
out_of_fov,
@@ -262,6 +307,7 @@ impl App {
//img_send,
img_recv,
ack_img_send,
cubic_tile_recv,
browser_features_support, //ack_img_recv,
})
@@ -543,6 +589,27 @@ impl App {
})
}
if let Ok(WorkerResponse {
cell,
hips_cdid,
tile_size,
tile_depth,
bytes,
..
}) = self.cubic_tile_recv.try_recv()
{
if let Some(HiPS::D3(hips)) = self.layers.get_mut_hips_from_cdid(&hips_cdid) {
hips.push_tile_from_png(
&cell,
bytes.into_boxed_slice(),
(tile_size, tile_size, tile_depth),
Time::now(),
)?;
}
self.request_redraw = true;
}
let has_camera_moved = self.camera.has_moved();
{
@@ -692,92 +759,40 @@ impl App {
ImageType::ImageRgba8u {
image: Bitmap { image, .. },
} => {
let document = web_sys::window()
.unwrap_abort()
.document()
.unwrap_abort();
let canvas = document
.create_element("canvas")?
.dyn_into::<web_sys::HtmlCanvasElement>()?;
canvas.set_width(image.width());
canvas.set_height(image.height());
let context = canvas
.get_context("2d")?
.unwrap_abort()
.dyn_into::<web_sys::CanvasRenderingContext2d>()?;
// Get the data once for all for the whole image
// This takes time so better do it once and not repeatly
context.draw_image_with_image_bitmap(
image, 0.0, 0.0,
let msg = js_sys::Object::new();
js_sys::Reflect::set(
&msg,
&"bitmap".into(),
image,
)?;
js_sys::Reflect::set(
&msg,
&"tileSize".into(),
&JsValue::from_f64(*tile_size as f64),
)?;
js_sys::Reflect::set(
&msg,
&"tileDepth".into(),
&JsValue::from_f64(*tile_depth as f64),
)?;
js_sys::Reflect::set(
&msg,
&"cell".into(),
&serde_wasm_bindgen::to_value(&cell)
.expect("Failed to serialize"),
)?;
// Cut the png in several tile images. See page 3 of
// https://aladin.cds.unistra.fr/java/DocTechHiPS3D.pdf
let tile_depth = *tile_depth;
let num_cols =
(tile_depth as f32).sqrt().floor() as u32;
let num_rows = ((tile_depth as f32)
/ (num_cols as f32))
.ceil()
as u32;
js_sys::Reflect::set(
&msg,
&"HiPS".into(),
&JsValue::from_str(&tile.hips_cdid),
)?;
let tile_size = *tile_size;
// Transfer ownership (zero-copy)
let transfer = js_sys::Array::of1(image);
let bytes = context
.get_image_data(
0_f64,
0_f64,
(num_cols * tile_size) as f64,
(num_rows * tile_size) as f64,
)?
.data()
.0;
let mut decoded_bytes = vec![
0_u8;
(tile_size * tile_size * tile_depth * 2)
as usize
];
let mut k = 0;
let mut num_tiles_cropped = 0;
for y in 0..num_rows {
let sy = y * tile_size;
for x in 0..num_cols {
let sx = x * tile_size;
for i in sy..(sy + tile_size) {
for j in sx..(sx + tile_size) {
let id_byte = (j + i
* num_cols
* tile_size)
* 4;
decoded_bytes[k] =
bytes[id_byte as usize];
decoded_bytes[k + 1] =
bytes[id_byte as usize + 3];
k += 2;
}
}
num_tiles_cropped += 1;
if num_tiles_cropped == tile_depth {
break;
}
}
if num_tiles_cropped == tile_depth {
break;
}
}
hips.push_tile_from_png(
cell,
decoded_bytes.into_boxed_slice(),
(tile_size, tile_size, tile_depth),
tile.request.time_request,
self.worker.post_message_with_transfer(
&msg, &transfer,
)?;
}
ImageType::ImageRgb8u {
@@ -1312,6 +1327,18 @@ impl App {
}
}
pub(crate) fn get_hips_frequency_window(&mut self, layer: &str) -> Result<[Freq; 2], JsValue> {
let hips = self
.layers
.get_mut_hips_from_layer(layer)
.ok_or_else(|| JsValue::from_str("Layer not found"))?;
match hips {
HiPS::D2(_) => Err(JsValue::from_str("layer do not refers to a cube")),
HiPS::D3(hips) => Ok(hips.get_freq_window()),
}
}
pub(crate) fn get_freq_from_hash(&mut self, layer: &str, hash: u64) -> Result<f64, JsValue> {
let hips = self
.layers
@@ -1842,3 +1869,132 @@ impl App {
self.rendering
}
}
use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct WorkerResponse {
#[serde(rename = "tileSize")]
pub tile_size: u32,
#[serde(rename = "tileDepth")]
pub tile_depth: u32,
pub bytes: Vec<u8>,
#[serde(rename = "HiPSCDid")]
pub hips_cdid: String,
pub cell: HEALPixFreqCell,
}
#[derive(Deserialize, Debug)]
pub struct WorkerResponseMeta {
#[serde(rename = "tileSize")]
pub tile_size: u32,
#[serde(rename = "tileDepth")]
pub tile_depth: u32,
#[serde(rename = "HiPSCDid")]
pub hips_cdid: String,
pub cell: HEALPixFreqCell,
}
use web_sys::{Worker, WorkerOptions};
pub fn create_worker() -> Result<Worker, JsValue> {
// JS source code of the worker
let worker_source = r#"
self.onmessage = (e) => {
const { bitmap, tileDepth, tileSize, HiPS, cell } = e.data;
// Compute tiling layout
const numCols = Math.floor(bitmap.width / tileSize);
// See HiPS3D doc
const numRows = Math.ceil(tileDepth / numCols);
// Create OffscreenCanvas
const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
const context = canvas.getContext("2d");
// Draw full image once
context.drawImage(bitmap, 0, 0);
// Extract full region needed
const imageData = context.getImageData(
0,
0,
numCols * tileSize,
numRows * tileSize
);
const bytes = imageData.data; // Uint8ClampedArray (RGBA)
// Allocate output buffer (2 bytes per pixel)
const decodedBytes = new Uint8Array(
tileSize * tileSize * tileDepth * 2
);
let k = 0;
let numTilesCropped = 0;
for (let y = 0; y < numRows; y++) {
const sy = y * tileSize;
for (let x = 0; x < numCols; x++) {
const sx = x * tileSize;
for (let i = sy; i < sy + tileSize; i++) {
for (let j = sx; j < sx + tileSize; j++) {
const idByte = (j + i * numCols * tileSize) * 4;
// Copy R channel
decodedBytes[k] = bytes[idByte];
// Copy A channel
decodedBytes[k + 1] = bytes[idByte + 3];
k += 2;
}
}
numTilesCropped++;
if (numTilesCropped === tileDepth) {
break;
}
}
if (numTilesCropped === tileDepth) {
break;
}
}
self.postMessage(
{
tileSize,
tileDepth,
HiPSCDid: HiPS,
cell,
bytes: decodedBytes,
},
[decodedBytes.buffer] // transfer of ownership
);
};
"#;
// Create Blob
let parts = js_sys::Array::of1(&JsValue::from_str(worker_source));
let blob = web_sys::Blob::new_with_str_sequence(&parts)?;
// Create object URL
let url = web_sys::Url::create_object_url_with_blob(&blob)?;
let opts = WorkerOptions::new();
let worker = Worker::new_with_options(&url, &opts)?;
Ok(worker)
}

View File

@@ -1,6 +1,6 @@
use std::cmp::Ordering;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
pub struct HEALPixCell(pub u8, pub u64);
#[derive(Debug)]
@@ -492,7 +492,7 @@ impl Ord for HEALPixCell {
}
/// A simple object describing a cubic tile of a HiPS3D
#[derive(Eq, Hash, PartialEq, Clone, Debug)]
#[derive(Eq, Hash, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct HEALPixFreqCell {
pub hpx: HEALPixCell,
pub f_hash: u64,

View File

@@ -410,6 +410,13 @@ impl WebClient {
self.app.get_hips_frequency(&layer)
}
#[wasm_bindgen(js_name = getFreqWindow)]
pub fn get_hips_frequency_window(&mut self, layer: String) -> Result<Vec<f32>, JsValue> {
let fw = self.app.get_hips_frequency_window(&layer)?;
Ok(vec![fw[0].0 as f32, fw[1].0 as f32])
}
#[wasm_bindgen(js_name = freq2hash)]
pub fn get_freq_hash(&mut self, layer: String, freq: f64) -> Result<u64, JsValue> {
self.app.get_freq_hash(&layer, freq)

View File

@@ -579,8 +579,14 @@ use std::fmt::Display;
impl Display for Angle<f64> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.fmt {
AngleFormatter::Sexagesimal { prec, plus, hours } => {
AngleFormatter::Sexagesimal {
mut prec,
plus,
hours,
} => {
let unit = if hours {
// to preserve the same angular precision the new prec is p+log10(15) ≈ p+1.18 < p+2
prec += 2;
self.to_hours()
} else {
self.to_degrees()

View File

@@ -215,7 +215,7 @@ impl ProjetedGrid {
let start_lon = bbox.lon_min() - (bbox.lon_min() % step_lon);
let mut stop_lon = bbox.lon_max();
if bbox.all_lon() {
stop_lon -= 1e-3;
stop_lon += 1e-3;
}
let mut meridians = vec![];

View File

@@ -309,6 +309,19 @@ impl Cursor {
fn get_freq(&self) -> Freq {
self.freq
}
fn get_freq_window(&self) -> [Freq; 2] {
let FrequencyWindow {
window_pixel_hash: f_hash_val,
pixel_depth,
..
} = self.get_window_frequency_range();
let f0 = Freq::from_hash_with_order(f_hash_val.start, pixel_depth);
let f1 = Freq::from_hash_with_order(f_hash_val.end, pixel_depth);
[f0, f1]
}
}
use super::HpxTileBuffer;
@@ -724,6 +737,10 @@ impl HiPS3D {
self.cursor.get_freq()
}
pub fn get_freq_window(&self) -> [Freq; 2] {
self.cursor.get_freq_window()
}
fn recompute_vertices(&mut self, camera: &CameraViewPort, proj: &ProjectionType) {
self.cells.clear();

View File

@@ -377,7 +377,7 @@ impl HpxFreqTex {
// size of the cube
size: (u32, u32, u32),
) -> Result<(), JsValue> {
let cubic_tile = ImageBuffer::<R16I>::new(decoded_bytes, size.0, size.1, size.2);
let cubic_tile = ImageBuffer::<R8U>::new(decoded_bytes, size.0, size.1, size.2);
cubic_tile.insert_into_3d_texture(&self.texture, &Vector3::<i32>::new(0, 0, 0))?;

View File

@@ -19,17 +19,6 @@ use wasm_bindgen::JsValue;
mod subdivide;
/*
pub(crate) 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(crate) trait HpxTileBuffer {
type T;
type C;

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
overscroll-behavior-x: none;
overscroll-behavior-y: none; /* Prevents vertical pull-to-refresh */
/* Hide the draggable boxes that goes out of the view */
overflow: hidden;
/*overflow: hidden;*/
/* media query on the aladin lite container. not supported everywhere.
There can be a more supported alternative here: https://caniuse.com/?search=grid-template-columns */
/*container-type: inline-size;*/
@@ -343,7 +343,6 @@
color: #222;
/* Allow scrolling but disable scroll bar */
height: min-content;
width: min-content;
border: var(--border-size) solid var(--border-color);

View File

@@ -21,7 +21,7 @@ vec4 val2c_f32(float x) {
// apply reversed
alpha = mix(alpha, 1.0 - alpha, reversed);
vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x)));
vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x)));
return apply_tonal(new_color);
}
@@ -32,7 +32,7 @@ vec4 val2c(float x) {
// apply reversed
alpha = mix(alpha, 1.0 - alpha, reversed);
vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank || isnan(x)));
vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank));
return apply_tonal(new_color);
}

View File

@@ -74,7 +74,7 @@ vec4 val2c_f32(float x) {
// apply reversed
alpha = mix(alpha, 1.0 - alpha, reversed);
vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x)));
vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x)));
return apply_tonal(new_color);
}
@@ -85,7 +85,7 @@ vec4 val2c(float x) {
// apply reversed
alpha = mix(alpha, 1.0 - alpha, reversed);
vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank || isnan(x)));
vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(x == blank));
return apply_tonal(new_color);
}
@@ -96,12 +96,12 @@ vec4 uvw2c_f32(vec3 uv) {
vec4 uvw2c_i32(vec3 uv) {
float val = float(decode_i32(texture(tex, uv).rgba));
return val2c(val);
return mix(val2c(val), vec4(0.0), float(val == -1.0));
}
vec4 uvw2c_i16(vec3 uv) {
float val = float(decode_i16(texture(tex, uv).rg));
return val2c(val);
return mix(val2c(val), vec4(0.0), float(val == -1.0));
}
vec4 uvw2c_u8(vec3 uv) {

View File

@@ -29,7 +29,6 @@
* Author: Thomas Boch[CDS]
*
*****************************************************************************/
import { MOC } from "./MOC.js";
import { GraphicOverlay } from "./Overlay.js";
import { Circle } from "./shapes/Circle.js";
@@ -58,7 +57,6 @@ import aladinCSS from './../css/aladin.css?inline';
///////////////////////////////
/////// Aladin Lite API ///////
///////////////////////////////
/**
* @namespace A
* @description Aladin Lite API namespace for creating celestial objects.
@@ -105,9 +103,9 @@ A.aladin = function (divSelector, options) {
const theme = storedPreference || (systemPrefersDark ? "dark" : "light");
return theme;
}
let theme;
if (options.mode) {
if (options && options.mode) {
let mode = options.mode.toLowerCase();
if (mode === 'dark' || mode === 'light') {
@@ -421,7 +419,7 @@ A.coo = function (longitude, latitude, prec) {
*
* @param {Circle[]|Polyline[]|Ellipse[]|Vector[]} shapes - an array of A.polygon objects
* @param {Source} [source] - a A.source object associated with the footprint
*
*
* @returns {Footprint} Returns a new Footprint object
*/
A.footprint = function(shapes, source) {
@@ -618,6 +616,7 @@ A.catalogFromURL = function (url, options, successCallback, errorCallback, usePr
fp.setColor(c.color);
fp.setHoverColor(c.hoverColor);
fp.setSelectionColor(c.selectionColor);
fp.setSelectionLineWidth(c.selectionLineWidth);
return fp;
})
@@ -1061,6 +1060,7 @@ A.Utils = AladinUtils;
* console.error('Error initializing Aladin Lite:', error);
* });
*/
A.init = (async () => {
const isWebGL2Supported = document
.createElement('canvas')
@@ -1075,6 +1075,6 @@ A.init = (async () => {
// According to caniuse, https://caniuse.com/webgl2, webgl2 is supported by 89% of users
throw "WebGL2 not supported by your browser";
}
})();
})()
export default A;

View File

@@ -308,6 +308,29 @@ export let Aladin = (function () {
const self = this;
ALEvent.GRAPHIC_OVERLAY_LAYER_ADDED.listenedBy(
aladinDiv,
(e) => {
const {overlay} = e.detail;
let callback = this.callbacksByEventName["stackChanged"];
callback && callback({
change: 'added',
overlay,
});
}
);
ALEvent.GRAPHIC_OVERLAY_LAYER_REMOVED.listenedBy(
aladinDiv, (e) => {
const {overlay} = e.detail;
let callback = this.callbacksByEventName["stackChanged"];
callback && callback({
change: 'removed',
overlay,
});
}
);
ALEvent.LAYER_ADDED.listenedBy(aladinDiv, (e) => {
const {layer} = e.detail;
let callback = this.callbacksByEventName["stackChanged"];
@@ -440,6 +463,7 @@ export let Aladin = (function () {
if (typeof hips === "string") {
try {
url = new URL(hips).href;
id = url;
} catch (e) {
id = hips;
}
@@ -487,7 +511,7 @@ export let Aladin = (function () {
this._setupUI(options);
ALEvent.FAVORITE_HIPS_LIST_UPDATED.dispatchedTo(document.body, this.hipsFavorites);
ALEvent.FAVORITE_HIPS_LIST_UPDATED.dispatchedTo(document, this.hipsFavorites);
if (options.survey) {
if (Array.isArray(options.survey)) {
@@ -630,10 +654,10 @@ export let Aladin = (function () {
////////////////////////////////////////////////////
let widgets = {};
let stack;
// Add the layers control
if (options.showLayersControl) {
let stack = new Stack(this);
stack = new Stack(this);
widgets["stack"] = stack
}
@@ -683,7 +707,7 @@ export let Aladin = (function () {
}
if (options.expandLayersControl) {
stack.click();
stack && stack.click();
}
this._applyMediaQueriesUI();
@@ -1600,7 +1624,7 @@ export let Aladin = (function () {
this.view.addCatalog(catalog);
ALEvent.GRAPHIC_OVERLAY_LAYER_ADDED.dispatchedTo(this.aladinDiv, {
layer: catalog,
overlay: catalog,
});
};
@@ -1613,9 +1637,7 @@ export let Aladin = (function () {
Aladin.prototype.addOverlay = function (overlay) {
this.view.addOverlay(overlay);
ALEvent.GRAPHIC_OVERLAY_LAYER_ADDED.dispatchedTo(this.aladinDiv, {
layer: overlay,
});
ALEvent.GRAPHIC_OVERLAY_LAYER_ADDED.dispatchedTo(this.aladinDiv, { overlay });
};
@@ -1797,7 +1819,7 @@ export let Aladin = (function () {
if (idx >= 0) {
this.hipsFavorites.splice(idx, 1);
// Send a change of favorites for the UI selector to adapt their optional list
ALEvent.FAVORITE_HIPS_LIST_UPDATED.dispatchedTo(document.body, this.hipsFavorites);
ALEvent.FAVORITE_HIPS_LIST_UPDATED.dispatchedTo(document, this.hipsFavorites);
}
}
@@ -1834,7 +1856,7 @@ export let Aladin = (function () {
})
// send the final event
ALEvent.FAVORITE_HIPS_LIST_UPDATED.dispatchedTo(document.body, this.hipsFavorites);
ALEvent.FAVORITE_HIPS_LIST_UPDATED.dispatchedTo(document, this.hipsFavorites);
}
/**
@@ -2161,6 +2183,10 @@ export let Aladin = (function () {
* view in the counter clockwise order (or towards the east)
*/
Aladin.prototype.setRotation = function (rotation) {
if (!rotation) {
console.warn("Rotation angle is not valid:", rotation)
return;
}
this.view.setRotation(rotation);
};

View File

@@ -55,6 +55,7 @@ If a function is given, user can return Image, HTMLImageCanvas, HTMLImageElement
* @property {string} [decField] - The ID or name of the field holding Declination (dec).
* @property {function} [filter] - The filtering function for sources.
* @property {string} [selectionColor="#00ff00"] - The color to apply to selected sources in the catalog.
* @property {string} [selectionLineWidth] - The line width to apply to selected source footprints in the catalog (i.e. the shapes returned by a custom shape function).
* @property {string} [hoverColor=color] - The color to apply to sources in the catalog when they are hovered.
* @property {boolean} [displayLabel=false] - Whether to display labels for sources.
* @property {string} [labelColumn] - The name of the column to be used for the label.
@@ -113,6 +114,7 @@ export let Catalog = (function () {
// allows for filtering of sources
this.filterFn = options.filter || undefined; // TODO: do the same for catalog
this.selectionColor = options.selectionColor || "#00ff00";
this.selectionLineWidth = options.selectionLineWidth || undefined;
this.hoverColor = options.hoverColor || undefined;
// when footprints are associated to source, do we need to draw the point source as well ?
@@ -143,19 +145,6 @@ export let Catalog = (function () {
// cacheCanvas permet de ne créer le path de la source qu'une fois, et de le réutiliser (cf. http://simonsarris.com/blog/427-increasing-performance-by-caching-paths-on-canvas)
this.updateShape(options);
this.cacheMarkerCanvas = document.createElement("canvas");
this.cacheMarkerCanvas.width = this.markerSize;
this.cacheMarkerCanvas.height = this.markerSize;
var cacheMarkerCtx = this.cacheMarkerCanvas.getContext("2d");
cacheMarkerCtx.fillStyle = this.color;
cacheMarkerCtx.beginPath();
var half = this.markerSize / 2;
cacheMarkerCtx.arc(half, half, half - 2, 0, 2 * Math.PI, false);
cacheMarkerCtx.fill();
cacheMarkerCtx.lineWidth = 2;
cacheMarkerCtx.strokeStyle = "#ccc";
cacheMarkerCtx.stroke();
this.isShowing = true;
}
@@ -505,6 +494,7 @@ export let Catalog = (function () {
* @param {Object} [options] - shape options
* @param {string} [options.color] - the color of the shape
* @param {string} [options.selectionColor] - the color of the shape when selected
* @param {string} [selectionLineWidth] - The line width to apply to selected source footprints in the catalog.
* @param {number} [options.sourceSize] - size of the shape
* @param {string} [options.hoverColor=options.color] - the color to apply to sources in the catalog when they are hovered.
* @param {string|Function|HTMLImageCanvas|HTMLImageElement} [options.shape="square"] - the type of the shape. Can be square, rhomb, plus, cross, triangle, circle.
@@ -515,9 +505,25 @@ export let Catalog = (function () {
options = options || {};
this.color = options.color || this.color || Color.getNextColor();
this.selectionColor = options.selectionColor || this.selectionColor || Color.getNextColor();
this.selectionLineWidth = options.selectionLineWidth || this.selectionLineWidth;
this.hoverColor = options.hoverColor || this.hoverColor || undefined;
this.sourceSize = options.sourceSize || this.sourceSize || 6;
this.shape = options.shape || this.shape || "square";
this.markerSize = this.sourceSize;
this.cacheMarkerCanvas = document.createElement("canvas");
this.cacheMarkerCanvas.width = this.markerSize;
this.cacheMarkerCanvas.height = this.markerSize;
var cacheMarkerCtx = this.cacheMarkerCanvas.getContext("2d");
cacheMarkerCtx.fillStyle = this.color;
cacheMarkerCtx.beginPath();
var half = this.markerSize / 2;
cacheMarkerCtx.arc(half, half, half - 2, 0, 2 * Math.PI, false);
cacheMarkerCtx.fill();
cacheMarkerCtx.lineWidth = 2;
cacheMarkerCtx.strokeStyle = '#ccc';
cacheMarkerCtx.stroke();
if (typeof this.shape === "function") {
this.shapeFn = this.shape;
this.shape = "custom"
@@ -560,7 +566,7 @@ export let Catalog = (function () {
this.reportChange();
};
/**
* Add sources to the catalog
@@ -629,11 +635,12 @@ export let Catalog = (function () {
let hoverColor = this.hoverColor || color;
for (var shape of shapes) {
// Set the same color of the shape than the catalog.
// Set the same color of the shape than the catalog.
// FIXME: the color/shape could be a parameter at the source level, allowing the user single catalogs handling different shapes
shape.setColor(color)
shape.setSelectionColor(this.selectionColor);
shape.setHoverColor(hoverColor);
shape.setSelectionLineWidth(this.selectionLineWidth);
}
let footprint;
@@ -785,9 +792,9 @@ export let Catalog = (function () {
* Get one source by its index in the catalog
*
* @memberof Catalog
*
*
* @param {number} idx - the index of the source in the catalog sources
*
*
* @returns {Source} - the source at the index
*/
Catalog.prototype.getSource = function (idx) {
@@ -811,7 +818,7 @@ export let Catalog = (function () {
* Set the color of the catalog
*
* @memberof Catalog
*
*
* @param {String} - the new color
*/
Catalog.prototype.setColor = function (color) {
@@ -823,7 +830,7 @@ export let Catalog = (function () {
* Set the color of selected sources
*
* @memberof Catalog
*
*
* @param {String} - the new color
*/
Catalog.prototype.setSelectionColor = function (color) {
@@ -831,11 +838,23 @@ export let Catalog = (function () {
this.updateShape();
};
/**
* Set the selectionLineWidth which can be used by the shape draw function for selected catalog elements.
*
* @memberof Catalog
*
* @param {String} - the new selection line width
*/
Catalog.prototype.setSelectionLineWidth = function (selectionLineWidth) {
this.selectionLineWidth = selectionLineWidth;
this.updateShape();
};
/**
* Select sources of the catalog matching a given callback
*
* @memberof Catalog
*
*
* @param {Function} filter - A filter callback to select sources of a catalog.
*/
Catalog.prototype.select = function(filter) {
@@ -861,7 +880,7 @@ export let Catalog = (function () {
* Set the color of hovered sources
*
* @memberof Catalog
*
*
* @param {String} - the new color
*/
Catalog.prototype.setHoverColor = function (color) {
@@ -873,7 +892,7 @@ export let Catalog = (function () {
* Set the size of the catalog sources
*
* @memberof Catalog
*
*
* @param {number} - the new size
*/
Catalog.prototype.setSourceSize = function (sourceSize) {
@@ -886,7 +905,7 @@ export let Catalog = (function () {
* Set the shape of the catalog sources
*
* @memberof Catalog
*
*
* @param {string|Function|HTMLImageCanvas|HTMLImageElement} [shape="square"] - the type of the shape. Can be square, rhomb, plus, cross, triangle, circle.
* A callback function can also be called that return an HTMLImageElement in function of the source object. A canvas or an image can also be given.
*/
@@ -899,7 +918,7 @@ export let Catalog = (function () {
* Get the size of the catalog sources
*
* @memberof Catalog
*
*
* @returns {number} - the size of the sources
*/
Catalog.prototype.getSourceSize = function () {
@@ -910,7 +929,7 @@ export let Catalog = (function () {
* Remove a specific source from the catalog
*
* @memberof Catalog
*
*
* @param {Source} - the source to remove
*/
Catalog.prototype.remove = function (source) {
@@ -986,12 +1005,18 @@ export let Catalog = (function () {
s.x = xy[2 * idx];
s.y = xy[2 * idx + 1];
if (s.isHovered || s.isSelected) {
// These sources will be drawn on the top of the others so we mark it as drawn
// for the moment but will really draw them after all catalogs have been drawn
return true;
}
return self.drawSource(s, ctx, width, height)
};
this.sources.forEach((s, idx) => {
let drawn = false;
if (xy[2 * idx] && xy[2 * idx + 1]) {
if (self.filterFn) {
if(!self.filterFn(s)) {
@@ -1095,7 +1120,7 @@ export let Catalog = (function () {
let color = s.color || this.color;
let cacheCanvas = this.getCacheCanvas(shape, color, size)
ctx.drawImage(
cacheCanvas,
s.x - cacheCanvas.width / 2,
@@ -1119,7 +1144,7 @@ export let Catalog = (function () {
return;
}
ctx.fillText(label, s.x, s.y);
ctx.fillText(label, s.x + this.sourceSize / 2, s.y);
};
// callback function to be called when the status of one of the sources has changed

View File

@@ -126,9 +126,10 @@ export let DefaultActionsForContextMenu = (function () {
},
subMenu: [
{
label: 'FITS image', action(o) {
label: 'Load image', action(o) {
let input = document.createElement('input');
input.type = 'file';
input.accept = ['.fits', '.png', '.jpg'];
input.onchange = _ => {
let files = Array.from(input.files);
@@ -137,14 +138,16 @@ export let DefaultActionsForContextMenu = (function () {
const name = file.name;
// Consider other cases
const image = a.createImageFITS(
const image = A.image(
url,
{name},
(ra, dec, fov, _) => {
// Center the view around the new fits object
a.gotoRaDec(ra, dec);
a.setFoV(fov * 1.1);
}
{
name,
successCallback: (ra, dec, fov, _) => {
// Center the view around the new fits object
a.gotoRaDec(ra, dec);
a.setFoV(fov * 1.1);
}
},
);
a.setOverlayImageLayer(image, name)

View File

@@ -51,6 +51,7 @@ export let Footprint= (function() {
this.shapes = [].concat(shapes);
this.isShowing = true;
this.isSelected = false;
this.isHovered = false;
this.overlay = null;
@@ -71,7 +72,7 @@ export let Footprint= (function() {
/*Footprint.prototype.setCatalog = function(catalog) {
if (this.source) {
this.source.setCatalog(catalog);
}
};*/
@@ -94,10 +95,12 @@ export let Footprint= (function() {
};
Footprint.prototype.select = function() {
this.isSelected = true;
this.shapes.forEach((shape) => shape.select())
};
Footprint.prototype.deselect = function() {
this.isSelected = false;
this.shapes.forEach((shape) => shape.deselect())
};
@@ -137,6 +140,14 @@ export let Footprint= (function() {
this.shapes.forEach((shape) => shape.setLineWidth(lineWidth))
};
Footprint.prototype.getSelectionLineWidth = function() {
return this.shapes && this.shapes[0].getSelectionLineWidth();
};
Footprint.prototype.setSelectionLineWidth = function(selectionLineWidth) {
this.shapes.forEach((shape) => shape.setSelectionLineWidth(selectionLineWidth))
};
Footprint.prototype.setColor = function(color) {
if(!color) {
return;

View File

@@ -864,6 +864,12 @@ export let HiPS = (function () {
}
}
HiPS.prototype.getFrequencyWindow = function() {
if (this.added) {
return this.view.wasm.getFreqWindow(this.layer);
}
}
// Private method for updating the backend with the new meta
HiPS.prototype._updateMetadata = function () {
try {

View File

@@ -526,10 +526,14 @@ export let Image = (function () {
// obj.wcs (object) = The wcs parsed from the image
if (obj.wcsdata) {
if (img.width !== obj.wcs.NAXIS1) {
obj.wcs.CDELT1 = obj.wcs.CDELT1 * (obj.wcs.NAXIS1 / img.width);
obj.wcs.CRPIX1 *= img.width / obj.wcs.NAXIS1;
obj.wcs.NAXIS1 = img.width;
}
if (img.height !== obj.wcs.NAXIS2) {
obj.wcs.CDELT2 = obj.wcs.CDELT2 * (obj.wcs.NAXIS2 / img.height);
obj.wcs.CRPIX2 *= img.height / obj.wcs.NAXIS2;
obj.wcs.NAXIS2 = img.height;
}
@@ -567,7 +571,6 @@ export let Image = (function () {
let wcs = self.options && self.options.wcs;
wcs.NAXIS1 = wcs.NAXIS1 || img.width;
wcs.NAXIS2 = wcs.NAXIS2 || img.height;
return self.view.wasm
.addRGBAImage(
bytes,

View File

@@ -199,7 +199,7 @@ export let MOC = (function() {
self.view.insertOverlay(self, idx);
// Tell the MOC has been fully loaded and can be sent as an event
ALEvent.GRAPHIC_OVERLAY_LAYER_ADDED.dispatchedTo(self.view.aladinDiv, {layer: self});
ALEvent.GRAPHIC_OVERLAY_LAYER_ADDED.dispatchedTo(self.view.aladinDiv, {overlay: self});
self.view.requestRedraw();
})

View File

@@ -48,7 +48,7 @@ import { Color } from './Color';
export let GraphicOverlay = (function() {
/**
* Represents an overlay containing Footprints, whether it is
* Represents an overlay containing Footprints, whether it is
*
* @class
* @constructs GraphicOverlay
@@ -124,10 +124,10 @@ export let GraphicOverlay = (function() {
/**
* Parse a STCS string and returns a list of footprints (only circles, polygons and ellipses given in ICRS or FK5J2000 frame are handled).
* For visualization purposes, the difference between FK5J2000 and ICRS system is not noticeable. Therefore one can be interpreted as the other.
* For visualization purposes, the difference between FK5J2000 and ICRS system is not noticeable. Therefore one can be interpreted as the other.
*
* @memberof GraphicOverlay
*
*
* @returns {Circle[]|Polyline[]|Ellipse[]} The list of mixed circles, polygons and ellipses
*/
GraphicOverlay.parseSTCS = function(stcs, options) {
@@ -216,8 +216,8 @@ export let GraphicOverlay = (function() {
* Add an array (or single) shapes (i.e. Footprint, Circle, Polyline, Ellipse, Vector, ...)
*
* @memberof GraphicOverlay
*
* @param {Footprint[]|Circle[]|Polyline[]|Ellipse[]|Vector[]} overlaysToAdd - a list (or single) shapes to add to the overlay
*
* @param {Footprint[]|Circle[]|Polyline[]|Ellipse[]|Vector[]} overlaysToAdd - a list (or single) shapes to add to the overlay
*/
GraphicOverlay.prototype.addFootprints = function(overlaysToAdd) {
overlaysToAdd = [].concat(overlaysToAdd)
@@ -250,9 +250,9 @@ export let GraphicOverlay = (function() {
* Returns a shape by an index
*
* @memberof GraphicOverlay
*
*
* @param {number} idx - The index of the shape to retrieve
*
*
* @returns {Footprint|Circle|Polyline|Ellipse|Vector} The shape
*/
GraphicOverlay.prototype.getFootprint = function(idx) {
@@ -293,31 +293,12 @@ export let GraphicOverlay = (function() {
ctx.lineWidth = this.lineWidth;
ctx.setLineDash(this.lineDash);
// 1. Drawing polygons
// TODO: les overlay polygons devrait se tracer lui meme (methode draw)
//ctx.lineWidth = this.lineWidth;
//ctx.beginPath();
/*var xyviews = [];
for (var k=0, len = this.overlays.length; k<len; k++) {
xyviews.push(this.drawFootprint(this.overlays[k], ctx, width, height));
}*/
//ctx.stroke();
// selection drawing
/*ctx.strokeStyle= Overlay.increaseBrightness(this.color, 50);
ctx.beginPath();
for (var k=0, len = this.overlays.length; k<len; k++) {
if (this.overlays[k].isSelected) {
this.drawFootprintSelected(ctx, xyviews[k]);
}
}
ctx.stroke();*/
// 2. Circle and polylines drawing
for (var k=0; k<this.overlayItems.length; k++) {
this.overlayItems[k].draw(ctx, this.view);
let item = this.overlayItems[k];
if (!item.isHovered && !item.isSelected) {
item.draw(ctx, this.view);
}
}
ctx.restore();
@@ -327,13 +308,16 @@ export let GraphicOverlay = (function() {
* Increase the brightness of a color by a percentage
*
* @memberof GraphicOverlay
*
* @param {string} hex - The color given in hexadecimal e.g. '#ffa0bb'
*
* @param {string} color - Any CSS color string (hex e.g. '#ffa0bb', rgb(), named color)
* @param {number} percent - The percentage to increase the brightness of
*
*
* @returns {string} The new color given as an hexadecimal string
*/
GraphicOverlay.increaseBrightness = function(hex, percent){
GraphicOverlay.increaseBrightness = function(color, percent){
// Normalize the color to hex.
var hex = Color.standardizeColor(color);
// strip the leading # if it's there
hex = hex.replace(/^\s*#|\s*$/g, '');
@@ -356,7 +340,7 @@ export let GraphicOverlay = (function() {
* Set the color of the shapes inside the overlay
*
* @memberof GraphicOverlay
*
*
* @param {string} color - the new color in hexadecimal e.g. '#ff00ff'
*/
GraphicOverlay.prototype.setColor = function(color) {
@@ -368,7 +352,7 @@ export let GraphicOverlay = (function() {
* Set the line width of the shapes inside the overlay
*
* @memberof GraphicOverlay
*
*
* @param {number} lineWidth - the new line width in pixels
*/
GraphicOverlay.prototype.setLineWidth = function(lineWidth) {
@@ -380,7 +364,7 @@ export let GraphicOverlay = (function() {
* Set the dash line property
*
* @memberof GraphicOverlay
*
*
* @param {Array.<number>} [lineDash=[]] - See the segments property {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash#segments| here}
*/
GraphicOverlay.prototype.setLineDash = function(lineDash) {

View File

@@ -72,7 +72,7 @@ export let ProgressiveCat = (function() {
// allows for filtering of sources
this.filterFn = options.filter || undefined; // TODO: do the same for catalog
this.filterFn = options.filter || undefined; // TODO: do the same for catalog
this.onClick = options.onClick || undefined; // TODO: inherit from catalog

View File

@@ -29,6 +29,25 @@
*
*****************************************************************************/
/**
* @typedef {Object} SourceOptions
* @description Options for describing a source
*
* @property {boolean} [marker=false] - If the source is a marker. A source marker is associated to a popup that is shown when clicking on it.
* @property {string} [popupTitle=''] - Only for marker. The title of the popup.
* @property {string} [popupDesc=''] - Only for marker. The content of the popup.
* @property {number} [useMarkerDefaultIcon=true] - Only for marker. Set to false to use the regular shape option from the catalog and not the specific marker shape.
*/
/**
* @typedef {Object} MarkerOptions
* @description Options for describing a source marker
*
* @property {string} [popupTitle=''] - Only for marker. The title of the popup.
* @property {string} [popupDesc=''] - Only for marker. The content of the popup.
* @property {number} [useMarkerDefaultIcon=true] - Only for marker. Set to false to use the regular shape option from the catalog and not the specific marker shape.
*/
export let Source = (function() {
// constructor
let Source = function(ra, dec, data, options) {
@@ -179,7 +198,9 @@ export let Source = (function() {
view.aladin.popup.setTitle(this.popupTitle);
view.aladin.popup.setText(this.popupDesc);
view.aladin.popup.setSource(this);
view.aladin.popup.show();
if (this.popupDesc || this.popupTitle)
view.aladin.popup.show();
return;
}
@@ -229,5 +250,11 @@ export let Source = (function() {
}
};
Source.prototype.draw = function(ctx, width, height) {
if (this.catalog) {
this.catalog.drawSource(this, ctx, width, height);
}
}
return Source;
})();

View File

@@ -594,6 +594,7 @@ export class SpectraDisplayer extends DOMElement {
let data = event.detail;
if (data.layer === this.hips.layer) {
this.data = data;
console.log(data)
this._redraw(this.ctx);
}
};

View File

@@ -49,7 +49,7 @@ import { Image } from "./Image.js";
import { Color } from "./Color.js";
import { SpectraDisplayer } from "./SpectraDisplayer.js";
import { DefaultActionsForContextMenu } from "./DefaultActionsForContextMenu.js";
import { Source } from "./Source.js";
export let View = (function () {
/** Constructor */
@@ -397,7 +397,7 @@ export let View = (function () {
var computedWidth = Math.floor(parseFloat(this.aladinDiv.getBoundingClientRect().width)) || 1.0;
var computedHeight = Math.floor(parseFloat(this.aladinDiv.getBoundingClientRect().height)) || 1.0;
this.width = Math.max(computedWidth, 1);
this.height = Math.max(computedHeight, 1); // this prevents many problems when div size is equal to 0
@@ -640,10 +640,17 @@ export let View = (function () {
// prevent default context menu from appearing (potential clash with right-click cuts control)
Utils.on(view.catalogCanvas, "contextmenu", function (e) {
// do something here...
e.preventDefault();
if(view.aladin.contextMenu) {
let ctxMenu = view.aladin.contextMenu;
if(ctxMenu) {
e.stopPropagation();
if (!view.rightClick && showContextMenu) {
ctxMenu.attach(
DefaultActionsForContextMenu.getDefaultActions(view.aladin),
null
);
ctxMenu._show({e});
}
}
});
@@ -656,7 +663,7 @@ export let View = (function () {
view.aladin.statusBar.removeMessage('opening-ctxmenu')
}
view.aladin.contextMenu && view.aladin.contextMenu.show({e});
view.aladin.contextMenu && view.aladin.contextMenu._show({e});
};
var longTouchTimer;
var longTouchDuration = 800;
@@ -666,7 +673,7 @@ export let View = (function () {
var handleSelect = function(xy, tolerance) {
tolerance = tolerance || 5;
var objs = view.closestObjects(xy.x, xy.y, tolerance);
view.unselectObjects();
if (objs) {
@@ -710,7 +717,7 @@ export let View = (function () {
view.selectObjects(objs);
view.lastClickedObject = objs;
} else {
// If there is a past clicked object
if (view.lastClickedObject) {
@@ -724,7 +731,6 @@ export let View = (function () {
}
var touchStartTime;
Utils.on(view.catalogCanvas, "mousedown touchstart", function (e) {
e.preventDefault();
e.stopPropagation();
const xymouse = Utils.relMouseCoords(e);
@@ -737,7 +743,6 @@ export let View = (function () {
state: {
mode: view.mode,
dragging: view.dragging,
rightClickPressed: view.rightClick
},
type: e.type,
xy: xymouse,
@@ -774,7 +779,7 @@ export let View = (function () {
})
}
longTouchTimer = setTimeout(() => {onlongtouch(e); view.dragging = false;}, longTouchDuration);
longTouchTimer = setTimeout(() => {onlongtouch(e); view.dragging = false;}, longTouchDuration);
touchStartTime = Date.now();
}
@@ -823,7 +828,6 @@ export let View = (function () {
Utils.on(view.catalogCanvas, "click", function (e) {
// call listener of 'click' event
if (view.mode == View.TOOL_SIMBAD_POINTER) {
// call Simbad pointer or Planetary features
GenericPointer(view, e);
@@ -844,11 +848,10 @@ export let View = (function () {
})
return; // listeners are not called
}
});
Utils.on(document, "mouseup touchend", function(e) {
var wasDragging = view.realDragging === true;
var wasDragging = view.realDragging === true;
if (view.dragging) { // if we were dragging, reset to default cursor
if(view.mode === View.PAN) {
@@ -877,7 +880,6 @@ export let View = (function () {
state: {
mode: view.mode,
dragging: view.dragging,
rightClickPressed: view.rightClick
},
type: e.type,
ev: e,
@@ -931,10 +933,10 @@ export let View = (function () {
if (e.type === "touchend") {
if (view.mode === View.SELECT) {
view.selector.dispatch('mouseup', {coo: xymouse})
return;
}
}
}
}
if (view.rightClick) {
@@ -948,7 +950,7 @@ export let View = (function () {
}
view.rightClick = false;
showContextMenu = true;
return;
}
@@ -989,7 +991,7 @@ export let View = (function () {
}
});
var lastHoveredObject; // save last object hovered by mouse
view.lastHoveredObject = null;
var lastMouseMovePos = null;
const pickColor = (xymouse) => {
const layers = view.aladin.getStackLayers()
@@ -1026,8 +1028,8 @@ export let View = (function () {
view.colorPickerTool.domElement.style.display = "none"
}
view.colorPickerTool.domElement.style.left = `${xymouse.x}px`;
view.colorPickerTool.domElement.style.top = `${xymouse.y}px`;
view.colorPickerTool.domElement.style.left = `${xymouse.x + view.aladin.aladinDiv.getBoundingClientRect().x}px`;
view.colorPickerTool.domElement.style.top = `${xymouse.y + view.aladin.aladinDiv.getBoundingClientRect().y}px`;
}
Utils.on(view.catalogCanvas, "mousemove touchmove", function (e) {
@@ -1039,7 +1041,6 @@ export let View = (function () {
state: {
mode: view.mode,
dragging: view.dragging,
rightClickPressed: view.rightClick
},
type: e.type,
xy: xymouse,
@@ -1161,26 +1162,26 @@ export let View = (function () {
for (let o of closests) {
if (typeof objHoveredFunction === 'function' && (!lastHoveredObject || !lastHoveredObject.includes(o))) {
if (typeof objHoveredFunction === 'function' && (!view.lastHoveredObject || !view.lastHoveredObject.includes(o))) {
var ret = objHoveredFunction(o, xymouse);
}
if (o.isFootprint()) {
if (typeof footprintHoveredFunction === 'function' && (!lastHoveredObject || !lastHoveredObject.includes(o))) {
if (typeof footprintHoveredFunction === 'function' && (!view.lastHoveredObject || !view.lastHoveredObject.includes(o))) {
var ret = footprintHoveredFunction(o, xymouse);
}
}
if (!lastHoveredObject || !lastHoveredObject.includes(o)) {
if (!view.lastHoveredObject || !view.lastHoveredObject.includes(o)) {
o.hover();
}
}
// unhover the objects in lastHoveredObjects that are not in closest anymore
if (lastHoveredObject) {
if (view.lastHoveredObject) {
var objHoveredStopFunction = view.aladin.callbacksByEventName['objectHoveredStop'];
for (let lho of lastHoveredObject) {
for (let lho of view.lastHoveredObject) {
if (!closests.includes(lho)) {
lho.unhover();
@@ -1190,19 +1191,19 @@ export let View = (function () {
}
}
}
lastHoveredObject = closests;
view.lastHoveredObject = closests;
} else {
view.setCursor('default');
if (lastHoveredObject) {
if (view.lastHoveredObject) {
var objHoveredStopFunction = view.aladin.callbacksByEventName['objectHoveredStop'];
/*if (typeof objHoveredStopFunction === 'function') {
// call callback function to notify we left the hovered object
var ret = objHoveredStopFunction(lastHoveredObject, xymouse);
var ret = objHoveredStopFunction(view.lastHoveredObject, xymouse);
}
lastHoveredObject.unhover();*/
for (let lho of lastHoveredObject) {
view.lastHoveredObject.unhover();*/
for (let lho of view.lastHoveredObject) {
lho.unhover();
if (typeof objHoveredStopFunction === 'function') {
@@ -1210,8 +1211,8 @@ export let View = (function () {
}
}
}
lastHoveredObject = null;
view.lastHoveredObject = null;
}
if (e.type === "mousemove") {
@@ -1276,7 +1277,6 @@ export let View = (function () {
state: {
mode: view.mode,
dragging: view.dragging,
rightClickPressed: view.rightClick
},
type: e.type,
xy: xymouse,
@@ -1286,7 +1286,7 @@ export let View = (function () {
if (typeof onWheelTriggeredFunction === 'function') {
onWheelTriggeredFunction(e)
} else {
// Default Aladin Lite zooming
// Default Aladin Lite zooming
const normalizedDelta = e.deltaY && normalizeWheel(e) || e.detail || (-e.wheelDelta);
// Accumulate the normalized delta
// We do not zoom because we cannot rely on "wheel" event
@@ -1326,7 +1326,7 @@ export let View = (function () {
break;
default:
break;
}
});
};
@@ -1444,6 +1444,7 @@ export let View = (function () {
cat.draw(ctx, this.width, this.height);
}
}
// draw popup catalog
if (this.catalogForPopup.isShowing && this.catalogForPopup.sources.length > 0) {
if (!this.catalogCanvasCleared) {
@@ -1471,6 +1472,32 @@ export let View = (function () {
}
}
// Draw selected items (catalog sources, overlay, footprints, ...) afterwards
if (this.selection) {
this.selection.forEach((objList) => {
objList.forEach((o) => {
if (o instanceof Source) {
o.draw(ctx, this.width, this.height)
} else {
// Circle, Ellipse, Footprints, ...
o.draw(ctx, this)
}
})
});
}
// Draw hovered items afterwards
if (this.lastHoveredObject) {
this.lastHoveredObject.forEach((o) => {
if (o instanceof Source) {
o.draw(ctx, this.width, this.height)
} else {
// Circle, Ellipse, Footprints, ...
o.draw(ctx, this)
}
})
}
// Redraw HEALPix grid
if (this.displayHpxGrid) {
if (!this.catalogCanvasCleared) {
@@ -1588,14 +1615,14 @@ export let View = (function () {
if (this.manualSelection) {
return;
}
// unselect the previous selection
this.unselectObjects();
if (Array.isArray(selection)) {
this.selection = selection;
} else {
// select the new
// select the new
this.selection = Selector.getObjects(selection, this);
}
@@ -1612,7 +1639,7 @@ export let View = (function () {
let cat = obj.getCatalog();
// trigger the non action clicked if it does not show the table
// table show is handled below
// table show is handled below
if (obj.actionClicked) {
if (!cat || !cat.onClick || cat.onClick !== "showTable") {
obj.actionClicked()
@@ -1642,13 +1669,13 @@ export let View = (function () {
} else {
source = o;
}
return source;
});
let tableColor = catalog.color;
if (catalog.colorFn) {
tableColor = "white"
tableColor = "white"
}
let table = {
@@ -1658,7 +1685,7 @@ export let View = (function () {
'fields': catalog.fields,
'showCallback': ObsCore.SHOW_CALLBACKS(this.aladin)
};
return table;
})
@@ -1866,6 +1893,15 @@ export let View = (function () {
// Remove the settled promise
this.promises.splice(idx, 1);
const noMoreLayersToWaitFor = this.promises.length === 0;
if (noMoreLayersToWaitFor && self.empty) {
// no promises to launch and the view has no HiPS.
// This situation can occurs if the MOCServer is out
// If so we can directly put the url of the DSS hosted in alasky,
// it the best I can do if the MOCServer is out
self.aladin.setBaseImageLayer(Aladin.DEFAULT_OPTIONS.survey);
}
})
}
@@ -1976,7 +2012,10 @@ export let View = (function () {
let imageLayerQueried = this.imageLayersBeingQueried.get(layer);
let imageLayer = this.imageLayers.get(layer);
return imageLayer || imageLayerQueried;
let obj = imageLayer || imageLayerQueried;
if (obj && obj.added)
return obj;
};
View.prototype.requestRedraw = function () {
@@ -1994,7 +2033,7 @@ export let View = (function () {
}
this.projection = ProjectionEnum[projName];
// Change the projection here
this.wasm.setProjection(projName);
this.updateZoomState()
@@ -2123,7 +2162,7 @@ export let View = (function () {
this.mocs = [];
this.allOverlayLayers.forEach((overlay) => {
ALEvent.GRAPHIC_OVERLAY_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: overlay });
ALEvent.GRAPHIC_OVERLAY_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { overlay });
})
this.allOverlayLayers = [];
@@ -2159,7 +2198,7 @@ export let View = (function () {
this.overlays.splice(indexToDelete, 1);
}
ALEvent.GRAPHIC_OVERLAY_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: overlay });
ALEvent.GRAPHIC_OVERLAY_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { overlay });
this.mustClearCatalog = true;
this.requestRedraw();
@@ -2235,8 +2274,12 @@ export let View = (function () {
footprints.forEach((footprint) => {
const originLineWidth = footprint.getLineWidth();
let spreadedLineWidth = (originLineWidth || 1) + 3;
let drawingLineWidth = originLineWidth;
if (footprint.isSelected && footprint.getSelectionLineWidth()) {
drawingLineWidth = footprint.getSelectionLineWidth();
}
let spreadedLineWidth = (drawingLineWidth || 1) + 3;
footprint.setLineWidth(spreadedLineWidth);
if (footprint.isShowing && footprint.isInStroke(ctx, this, x * window.devicePixelRatio, y * window.devicePixelRatio)) {
closests.push(footprint);
@@ -2272,8 +2315,12 @@ export let View = (function () {
if (s.isFootprint() && !s.tooSmallFootprint) {
let footprint = s.footprint;
const originLineWidth = footprint.getLineWidth();
let spreadedLineWidth = (originLineWidth || 1) + 3;
let drawingLineWidth = originLineWidth;
if (footprint.isSelected && footprint.getSelectionLineWidth()) {
drawingLineWidth = footprint.getSelectionLineWidth();
}
let spreadedLineWidth = (drawingLineWidth || 1) + 3;
footprint.setLineWidth(spreadedLineWidth);
if (footprint.isShowing && footprint.isInStroke(ctx, this, x * window.devicePixelRatio, y * window.devicePixelRatio)) {
closests.push(s);

View File

@@ -28,9 +28,7 @@ export let WebGLCtx = (function() {
function WebGLCtx(ctx, div) {
this.webclient = new ctx.WebClient(
div,
{
'kernel': kernel,
}
{ 'kernel': kernel }
);
};

View File

@@ -1090,7 +1090,7 @@ export class OverlayStackBox extends Box {
monochrome: true,
},
tooltip: {
content: "Swap 2 layers",
content: "Click on this button for both layers you want to swap",
position: { direction: "top" },
},
toggled: false,

View File

@@ -46,7 +46,7 @@ export class ColorPicker extends ActionButton {
classList: ['aladin-colorPicker-control'],
size: 'medium',
tooltip: {
content: 'A color picker tool',
content: 'Pixel value extractor',
position: { direction: 'top right' },
},
action(o) {
@@ -70,7 +70,7 @@ export class ColorPicker extends ActionButton {
if (this.aladin.statusBar) {
this.aladin.statusBar.appendMessage({
id: 'colorpicker',
message: 'Color picker mode, click on a pixel to copy it',
message: 'Pixel value extractor, click on a pixel to copy it',
type: 'info'
})
}

View File

@@ -56,7 +56,7 @@ import stackOverlayIconUrl from "./../../../../assets/icons/stack.svg";
tooltip: {
content: 'Open the overlays menu',
position: {
direction: (options && options.openDirection) || 'left'
direction: (options && options.openDirection) || 'top'
}
},
});

View File

@@ -87,7 +87,7 @@ export class HiPSSelector extends Input {
};
(function () {
ALEvent.FAVORITE_HIPS_LIST_UPDATED.listenedBy(document.body, (event) => {
ALEvent.FAVORITE_HIPS_LIST_UPDATED.listenedBy(document, (event) => {
let favoritesHips = event.detail;
HiPSSelector.cachedHiPS = {};

View File

@@ -27,7 +27,7 @@ import { isJSObject } from "./Utils";
/******************************************************************************
* Aladin Lite project
*
* File gui/Widgets/layout/Horizontal.js
* File gui/Layout.js
*
* A layout grouping widgets horizontaly
*
@@ -37,14 +37,6 @@ import { isJSObject } from "./Utils";
*****************************************************************************/
export class Layout extends DOMElement {
/**
* Create a layout
* @param {layout: Array.<DOMElement | String>} layout - Represents the structure of the Tabs
* @param {Object} options - Options object
* @param {DOMElement} target - The parent element.
* @param {String} position - The position of the tabs layout relative to the target.
* For the list of possibilities, see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
*/
constructor(layout, options, target, position = "beforeend") {
let el = document.createElement('div');
@@ -211,10 +203,6 @@ export class Layout extends DOMElement {
this._show();
}
/**
* Empty the layout
* @param {content: String|DOMElement, swappable: Boolean, disabled: Boolean, selected: Boolean} item - Represents the structure of the Tabs
*/
empty() {
// remove all the sub elements
this.layout = [];

View File

@@ -1,20 +1,23 @@
// Copyright 2023 - UDS/CNRS
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
// of the GNU Lesser General Public License version 3
// or (at your option) any later version.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
// You should have received a copy of the GNU Lesser General Public License
// along with Aladin Lite. If not, see <https://www.gnu.org/licenses/>.
//
import { Layout } from "./Layout";
import { ActionButton } from "./Widgets/ActionButton";
@@ -22,7 +25,7 @@ import { DOMElement } from "./Widgets/Widget";
/******************************************************************************
* Aladin Lite project
*
* File gui/Widgets/layout/Horizontal.js
* File gui/Toolbar.js
*
* A layout grouping widgets horizontaly
*
@@ -33,12 +36,12 @@ import { DOMElement } from "./Widgets/Widget";
export class Toolbar extends Layout {
/**
* Create a layout
* @param {Object[]} widgets - A list of predefined widgets
* Toolbar instance constructor
*
* @class
* @constructs Toolbar
* @param {Object} options - Options object
* @param {DOMElement} target - The parent element.
* @param {String} position - The position of the tabs layout relative to the target.
* For the list of possibilities, see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
*/
constructor(options, target) {
let position = (options && options.position) || 'topleft';
@@ -50,8 +53,6 @@ export class Toolbar extends Layout {
target,
)
console.log("options toolbar", options)
this.position = position;
this.vertical = options && options.vertical === true;
@@ -102,6 +103,33 @@ export class Toolbar extends Layout {
widget.update({disabled: true})
}
/**
* Add custom clickable actions to the toolbar
*
* @memberof Toolbar
* @param {string} name - A name identifier for your new action
* @param {Object} widget - Your clickable action, same as for defining an action button
* @param {boolean} [widget.toggled=false] - Whether the button is initially toggled.
* @param {function} [widget.action] - The callback function to execute when the button is clicked.
* @param {string} [widget.title] - The title attribute for the button.
* @param {Object} [widget.icon] - An icon object for the button.
* @param {boolean} [widget.disabled=false] - Whether the button is initially disabled.
* @param {CSSStyleSheet} [widget.cssStyle] - The CSS styles to apply to the button.
* @param {Object} [widget.tooltip] - A tooltip appearing when the user hovers the button.
* @param {string} [widget.size] - The size of the button. Can be 'medium' or 'small'
*
* @example
* aladin.getToolbar()
* .add('action_custom', {
* icon: {
* url: yourIconUrl,
* size: "medium"
* },
* action(_) {
* console.log("do a thing")
* }
* });
*/
add(name, widget) {
if (!(widget instanceof DOMElement)) {
widget = new ActionButton(widget)

View File

@@ -203,7 +203,7 @@ function dragElement(triggerElt, elmnt, onDragged) {
document.onmouseup = null;
document.onmousemove = null;
var r = elmnt.getBoundingClientRect();
/*var r = elmnt.getBoundingClientRect();
if (t < r.height / 2) {
elmnt.style.top = r.height / 2 + "px";
@@ -221,7 +221,7 @@ function dragElement(triggerElt, elmnt, onDragged) {
if (t + r.height / 2 > aladinDiv.offsetHeight) {
elmnt.style.top = (aladinDiv.offsetHeight - r.height / 2) + "px";
}
}*/
}
}

View File

@@ -181,7 +181,6 @@ export let AVM = (function() {
if (this.xmp) {
tags = this.readAVM(this.xmp);
if (tags) {
this.tags = tags;

View File

@@ -52,6 +52,7 @@ export let Circle = (function() {
this.fillColor = options['fillColor'] || undefined;
this.lineWidth = options["lineWidth"] || undefined;
this.selectionColor = options["selectionColor"] || '#00ff00';
this.selectionLineWidth = options["selectionLineWidth"] || undefined;
this.hoverColor = options["hoverColor"] || undefined;
this.opacity = options['opacity'] || 1;
@@ -112,6 +113,20 @@ export let Circle = (function() {
return this.lineWidth;
};
Circle.prototype.setSelectionLineWidth = function(selectionLineWidth) {
if (this.selectionLineWidth == selectionLineWidth) {
return;
}
this.selectionLineWidth = selectionLineWidth;
if (this.overlay) {
this.overlay.reportChange();
}
};
Circle.prototype.getSelectionLineWidth = function() {
return this.selectionLineWidth;
};
Circle.prototype.setOverlay = function(overlay) {
this.overlay = overlay;
};
@@ -162,6 +177,7 @@ export let Circle = (function() {
}
this.isHovered = true;
this.setLineWidth(this.getLineWidth() + 2)
this.setSelectionLineWidth(this.getSelectionLineWidth() + 2)
if (this.overlay) {
this.overlay.reportChange();
}
@@ -173,6 +189,7 @@ export let Circle = (function() {
}
this.isHovered = false;
this.setLineWidth(this.getLineWidth() - 2)
this.setSelectionLineWidth(this.getSelectionLineWidth() - 2)
if (this.overlay) {
this.overlay.reportChange();
@@ -203,10 +220,19 @@ export let Circle = (function() {
return false;
}
// Decide which line width to use.
if (!this.lineWidth) {
this.lineWidth = (this.overlay && this.overlay.lineWidth) || 2;
}
let drawingLineWidth = this.lineWidth;
if (this.isSelected && this.selectionLineWidth) {
drawingLineWidth = this.selectionLineWidth;
}
noSmallCheck = noSmallCheck===true || false;
if (!noSmallCheck) {
const px_per_deg = view.width / view.fov;
this.isTooSmall = this.radiusDegrees * 2 * px_per_deg < this.lineWidth;
this.isTooSmall = this.radiusDegrees * 2 * px_per_deg < drawingLineWidth;
if (this.isTooSmall) {
return false;
}
@@ -302,11 +328,7 @@ export let Circle = (function() {
ctx.strokeStyle = baseColor;
}
if (!this.lineWidth) {
this.lineWidth = (this.overlay && this.overlay.lineWidth) || 2;
}
ctx.lineWidth = this.lineWidth;
ctx.lineWidth = drawingLineWidth;
ctx.globalAlpha = this.opacity;
ctx.beginPath();
ctx.arc(this.center.x, this.center.y, this.radius, 0, 2*Math.PI, false);
@@ -336,7 +358,7 @@ export let Circle = (function() {
}
// compute the absolute distance between the middle of the bbox
// and the center of the circle
// and the center of the circle
const circleDistance = {
x: Math.abs(centerXyview[0] - (x + w/2)),
y: Math.abs(centerXyview[1] - (y + h/2))

View File

@@ -23,11 +23,11 @@
/******************************************************************************
* Aladin Lite project
*
*
* File Ellipse
*
*
* Author: Matthieu Baumann[CDS]
*
*
*****************************************************************************/
import { Utils } from "./../Utils";
@@ -54,6 +54,7 @@ export let Ellipse = (function() {
this.color = options['color'] || undefined;
this.fillColor = options['fillColor'] || undefined;
this.lineWidth = options["lineWidth"] || undefined;
this.selectionLineWidth = options["selectionLineWidth"] || undefined;
this.selectionColor = options["selectionColor"] || '#00ff00';
this.hoverColor = options["hoverColor"] || undefined;
this.opacity = options['opacity'] || 1;
@@ -66,7 +67,7 @@ export let Ellipse = (function() {
this.setAxisLength(a, b);
this.setRotation(theta);
this.overlay = null;
this.isShowing = true;
this.isSelected = false;
this.isHovered = false;
@@ -120,6 +121,20 @@ export let Ellipse = (function() {
this.overlay = overlay;
};
Ellipse.prototype.setSelectionLineWidth = function(selectionLineWidth) {
if (this.selectionLineWidth == selectionLineWidth) {
return;
}
this.selectionLineWidth = selectionLineWidth;
if (this.overlay) {
this.overlay.reportChange();
}
};
Ellipse.prototype.getSelectionLineWidth = function() {
return this.selectionLineWidth;
};
Ellipse.prototype.show = function() {
if (this.isShowing) {
return;
@@ -129,7 +144,7 @@ export let Ellipse = (function() {
this.overlay.reportChange();
}
};
Ellipse.prototype.hide = function() {
if (! this.isShowing) {
return;
@@ -139,7 +154,7 @@ export let Ellipse = (function() {
this.overlay.reportChange();
}
};
Ellipse.prototype.select = function() {
if (this.isSelected) {
return;
@@ -167,6 +182,7 @@ export let Ellipse = (function() {
}
this.isHovered = true;
this.setLineWidth(this.getLineWidth() + 2)
this.setSelectionLineWidth(this.getSelectionLineWidth() + 2)
if (this.overlay) {
this.overlay.reportChange();
}
@@ -178,6 +194,7 @@ export let Ellipse = (function() {
}
this.isHovered = false;
this.setLineWidth(this.getLineWidth() - 2)
this.setSelectionLineWidth(this.getSelectionLineWidth() - 2)
if (this.overlay) {
this.overlay.reportChange();
}
@@ -221,10 +238,19 @@ export let Ellipse = (function() {
return false;
}
// Decide which line width to use.
if (!this.lineWidth) {
this.lineWidth = (this.overlay && this.overlay.lineWidth) || 2;
}
let drawingLineWidth = this.lineWidth;
if (this.isSelected && this.selectionLineWidth) {
drawingLineWidth = this.selectionLineWidth;
}
const px_per_deg = view.width / view.fov;
noSmallCheck = noSmallCheck===true || false;
if (!noSmallCheck) {
this.isTooSmall = this.b * 2 * px_per_deg < this.lineWidth;
this.isTooSmall = this.b * 2 * px_per_deg < drawingLineWidth;
if (this.isTooSmall) {
return false;
}
@@ -250,7 +276,7 @@ export let Ellipse = (function() {
// 3. normalize this vector
let toNorthVec = [toNorthScreen[0] - originScreen[0], toNorthScreen[1] - originScreen[1]];
let norm = Math.sqrt(toNorthVec[0]*toNorthVec[0] + toNorthVec[1]*toNorthVec[1]);
toNorthVec = [toNorthVec[0] / norm, toNorthVec[1] / norm];
let toWestVec = [1.0, 0.0];
@@ -287,11 +313,7 @@ export let Ellipse = (function() {
ctx.strokeStyle = baseColor;
}
if (!this.lineWidth) {
this.lineWidth = (this.overlay && this.overlay.lineWidth) || 2;
}
ctx.lineWidth = this.lineWidth;
ctx.lineWidth = drawingLineWidth;
ctx.globalAlpha = this.opacity;
ctx.beginPath();
@@ -353,7 +375,7 @@ export let Ellipse = (function() {
}
// compute the absolute distance between the middle of the bbox
// and the center of the circle
// and the center of the circle
const circleDistance = {
x: Math.abs(centerXyview[0] - (x + w/2)),
y: Math.abs(centerXyview[1] - (y + h/2))
@@ -371,6 +393,6 @@ export let Ellipse = (function() {
const cornerDistanceSquared = dx*dx + dy*dy;
return (cornerDistanceSquared <= (this.aPixels*this.aPixels));
};
return Ellipse;
})();

View File

@@ -45,7 +45,8 @@ import { ProjectionEnum } from "../ProjectionEnum.js";
* @property {string} [color] - The color of the shape
* @property {string} [fill=false] - Fill the shape with fillColor
* @property {string} [fillColor] - A filling color for the shape
* @property {number} [lineWidth=3] - The line width in pixels
* @property {number} [lineWidth=2] - The line width in pixels (inherited from overlay, if any, where it defaults to 3)
* @property {number} [selectionLineWidth=lineWidth] - The line width in pixels when the shape is selected
* @property {number} [opacity=1] - The opacity, between 0 (totally transparent) and 1 (totally opaque)
* @property {string} [selectionColor='#00ff00'] - A selection color
* @property {string} [hoverColor] - A hovered color
@@ -75,7 +76,7 @@ export let Polyline = (function() {
* @param {Array.<number[]>} raDecArray - right-ascension/declination 2-tuple array describing the polyline's vertices in degrees
* @param {ShapeOptions} options - Configuration options for the polyline. Additional properties:
* @param {boolean} [options.closed=false] - Close the polyline, default to false.
*
*
* @returns {Polyline} - The polyline shape object
*/
let Polyline = function(raDecArray, options) {
@@ -85,6 +86,7 @@ export let Polyline = (function() {
this.fillColor = options['fillColor'] || undefined;
this.opacity = options['opacity'] || undefined;
this.lineWidth = options["lineWidth"] || undefined;
this.selectionLineWidth = options["selectionLineWidth"] || undefined;
this.selectionColor = options["selectionColor"] || '#00ff00';
this.hoverColor = options["hoverColor"] || undefined;
@@ -151,6 +153,7 @@ export let Polyline = (function() {
}
this.isHovered = true;
this.setLineWidth(this.getLineWidth() + 2)
this.setSelectionLineWidth(this.getSelectionLineWidth() + 2)
if (this.overlay) {
this.overlay.reportChange();
}
@@ -162,6 +165,7 @@ export let Polyline = (function() {
}
this.isHovered = false;
this.setLineWidth(this.getLineWidth() - 2)
this.setSelectionLineWidth(this.getSelectionLineWidth() - 2)
if (this.overlay) {
this.overlay.reportChange();
}
@@ -182,6 +186,21 @@ export let Polyline = (function() {
}
};
Polyline.prototype.getSelectionLineWidth = function() {
return this.selectionLineWidth;
};
Polyline.prototype.setSelectionLineWidth = function(selectionLineWidth) {
if (this.selectionLineWidth == selectionLineWidth) {
return;
}
this.selectionLineWidth = selectionLineWidth;
if (this.overlay) {
this.overlay.reportChange();
}
};
Polyline.prototype.setColor = function(color) {
if (!color || this.color == color) {
return;
@@ -239,9 +258,14 @@ export let Polyline = (function() {
baseColor = '#ff0000';
}
// Decide which line width to use.
if (!this.lineWidth) {
this.lineWidth = (this.overlay && this.overlay.lineWidth) || 2;
}
var drawingLineWidth = this.lineWidth;
if (this.isSelected && this.selectionLineWidth) {
drawingLineWidth = this.selectionLineWidth;
}
if (this.isSelected) {
if(this.selectionColor) {
@@ -293,7 +317,7 @@ export let Polyline = (function() {
// do not draw neither if the polygone does not lie inside lineWidth
if (!noSmallCheck) {
this.isTooSmall = (xmax - xmin) < this.lineWidth && (ymax - ymin) < this.lineWidth;
this.isTooSmall = (xmax - xmin) < drawingLineWidth && (ymax - ymin) < drawingLineWidth;
if (this.isTooSmall) {
return false;
@@ -374,7 +398,7 @@ export let Polyline = (function() {
let v1 = this.closed ? 0 : 1;
ctx.globalAlpha = this.opacity;
ctx.lineWidth = this.lineWidth;
ctx.lineWidth = drawingLineWidth;
ctx.beginPath();
for (var k = 0; k < nSegment; k++) {
@@ -449,7 +473,7 @@ export let Polyline = (function() {
if (v1 && v2) {
const line = {x1: v1.x, y1: v1.y, x2: v2.x, y2: v2.y}; // new segment
_drawLine(line, ctx);
if (ctx.isPointInStroke(x, y)) { // x,y is on line?
return true;
}
@@ -494,7 +518,6 @@ export let Polyline = (function() {
}
if (this.closed && poly.length === this.raDecArray.length) {
console.log("closed poly")
const corners = [
{ x, y },
{ x: x + w, y },
@@ -583,7 +606,7 @@ export let Polyline = (function() {
let right = Polyline.segmentsIntersect({x: x1, y: y1}, {x: x2, y: y2}, {x: rw, y: 0}, {x: rw, y: rh});
let top = Polyline.segmentsIntersect({x: x1, y: y1}, {x: x2, y: y2}, {x: 0, y: 0}, {x: rw, y: 0});
let bottom = Polyline.segmentsIntersect({x: x1, y: y1}, {x: x2, y: y2}, {x: 0, y: rh}, {x: rw, y: rh});
// if ANY of the above are true, the line
// has hit the rectangle
if (left || right || top || bottom) {

View File

@@ -22,13 +22,13 @@
/******************************************************************************
* Aladin Lite project
*
*
* Class Vector
*
*
* A vector is a graphical overlay connecting 2 points with end or begin arrows on it
*
*
* Author: Matthieu Baumann[CDS]
*
*
*****************************************************************************/
import { Polyline } from "./Polyline.js";
import { Utils } from '../Utils';
@@ -38,7 +38,7 @@ import { Ellipse } from "./Ellipse.js";
export let Vector = (function() {
/**
* Represents an vector.
*
*
* A vector is a graphical overlay connecting 2 sky positions with end or begin arrows on it
*
* @class
@@ -49,7 +49,7 @@ export let Vector = (function() {
* @param {number} dec2 - Declination (Dec) coordinate of the center in degrees.
* @param {ShapeOptions} options - Options for configuring the vector. Additional properties:
* @param {boolean} [options.arrow=false] - Add an arrow pointing from (ra1, dec1) to (ra2, dec2)
*
*
* @returns {Vector} - The vector shape object
*/
let Vector = function(ra1, dec1, ra2, dec2, options) {
@@ -57,6 +57,7 @@ export let Vector = (function() {
this.color = options['color'] || undefined;
this.opacity = options['opacity'] || undefined;
this.lineWidth = options['lineWidth'] || undefined;
this.selectionLineWidth = options["selectionLineWidth"] || undefined;
this.selectionColor = options["selectionColor"] || '#00ff00';
this.hoverColor = options["hoverColor"] || undefined;
this.arrow = options["arrow"] === undefined ? false : options["arrow"];
@@ -81,16 +82,19 @@ export let Vector = (function() {
isFootprint: Polyline.prototype.isFootprint,
show: Polyline.prototype.show,
hide: Polyline.prototype.hide,
select: Polyline.prototype.select,
deselect: Polyline.prototype.deselect,
hover: Polyline.prototype.hover,
unhover: Polyline.prototype.unhover,
getLineWidth: Polyline.prototype.getLineWidth,
setLineWidth: Polyline.prototype.setLineWidth,
getSelectionLineWidth: Polyline.prototype.getSelectionLineWidth,
setSelectionLineWidth: Polyline.prototype.setSelectionLineWidth,
setColor: Polyline.prototype.setColor,
setSelectionColor: Polyline.prototype.setSelectionColor,
setHoverColor: Polyline.prototype.setHoverColor,
@@ -105,7 +109,7 @@ export let Vector = (function() {
const v2 = view.aladin.world2pix(this.ra2, this.dec2);
if (!v2)
return false;
const xmin = Math.min(v1[0], v2[0]);
const xmax = Math.max(v1[0], v2[0]);
const ymin = Math.min(v1[1], v2[1]);
@@ -120,6 +124,10 @@ export let Vector = (function() {
if (!this.lineWidth) {
this.lineWidth = (this.overlay && this.overlay.lineWidth) || 2;
}
let drawingLineWidth = this.lineWidth;
if (this.isSelected && this.selectionLineWidth) {
drawingLineWidth = this.selectionLineWidth;
}
// too small
if(!noSmallCheck) {
@@ -137,7 +145,7 @@ export let Vector = (function() {
ctx.strokeStyle = baseColor;
}
ctx.lineWidth = this.lineWidth;
ctx.lineWidth = drawingLineWidth;
ctx.globalAlpha = this.opacity;
ctx.beginPath();
@@ -147,7 +155,7 @@ export let Vector = (function() {
if (this.arrow) {
// draw the arrow
var angle, x, y, xh, yh;
var arrowRad = this.lineWidth * 3;
var arrowRad = drawingLineWidth * 3;
angle = Math.atan2(v2[1] - v1[1], v2[0] - v1[0])
xh = v2[0];
@@ -192,7 +200,7 @@ export let Vector = (function() {
xy1 = {x: xy1[0], y: xy1[1]};
xy2 = {x: xy2[0], y: xy2[1]};
// Check if line segment intersects with the bounding box
if (Polyline.segmentIntersectsBox(xy1, xy2, x, y, w, h)) {
return true;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 KiB