For Catalog only: plot the sources when the footprints are too small

This commit is contained in:
Matthieu Baumann
2024-04-12 11:50:25 +02:00
committed by Matthieu Baumann
parent c797aec7f7
commit fee97bed40
36 changed files with 590 additions and 327 deletions

View File

@@ -10,7 +10,7 @@
import A from '../src/js/A.js';
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {survey: ["P/PanSTARRS/DR1/color-i-r-g"], showReticle: false, gridOptions: {opacity: 0.5, color: 'rgba(255, 0, 0)'}, projection: "AIT", cooFrame: 'icrs', target: "stephan's quintet", fov: 1000, showGotoControl: false, showFrame: false, fullScreen: true, showLayersControl: true, showCooGrid: true, showCooGridControl: false});
aladin = A.aladin('#aladin-lite-div', {survey: ["P/PanSTARRS/DR1/color-i-r-g"], showReticle: false, projection: "AIT", cooFrame: 'icrs', target: "stephan's quintet", fov: 1000, showGotoControl: false, showFrame: false, fullScreen: true, showLayersControl: true, showCooGrid: true, showCooGridControl: false});
const chft = aladin.createImageSurvey('CFHT', "CFHT deep view of NGC7331 and Stephan's quintet u+g+r", "https://cds.unistra.fr/~derriere/PR_HiPS/2022_Duc/", null, null, {imgFormat: 'png'});
const nircamJWST = aladin.createImageSurvey('Nircam', "Stephans Quintet NIRCam+MIRI", "http://alasky.cds.unistra.fr/JWST/CDS_P_JWST_Stephans-Quintet_NIRCam+MIRI/", null, null, {imgFormat: 'png', colormap: "viridis"});

View File

@@ -15,10 +15,8 @@
</div>
</div>
</div>
<script type="module">
import A from '../src/js/A.js';
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {projection: 'MOL', cooFrame: 'galactic', fov: 360, fullScreen: true, showCooGrid: false, showReticle: false})
aladin.gotoRaDec(79.9525321, -69.2742586)

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: 1024px; height: 100%;"></div>
<script type="module">
import A from '../src/js/A.js';
@@ -19,7 +19,6 @@
target: '19 24 51.556 +45 16 44.36', // initial target
cooFrame: 'equatorial', // set galactic frame
showCooGrid: true, // set the grid
fullScreen: true,
}
);
});

View File

@@ -12,7 +12,7 @@
// Start up Aladin Lite
aladin = A.aladin('#aladin-lite-div', {
target: "M31",
fov: 2,
fov: 89.78,
showContextMenu: true,
fullScreen: true,
showSimbadPointerControl: true,

View File

@@ -33,6 +33,7 @@
selectionColor: 'white',
// Footprint associated to sources
shape: (s) => {
// Compute the mean of pm over the catalog sources
if (!pmraMean || !pmdecMean) {
pmraMean = 0, pmdecMean = 0;
@@ -47,16 +48,20 @@
pmdecMean /= numSources
}
console.log("mean", pmraMean, pmdecMean)
let dra = +s.data.pmra - pmraMean;
let ddec = +s.data.pmdec - pmdecMean;
let mag = Math.sqrt(dra * dra + ddec * ddec);
// discard drawing a vector for big pm
if (mag > 1) {
let totalPmSquared = s.data.pmra*s.data.pmra + s.data.pmdec*s.data.pmdec;
if (totalPmSquared > 6) {
return;
}
let color = rainbowColorMap(mag * 2)
let color = rainbowColorMap((totalPmSquared - 2.5) / 2)
return A.vector(
s.ra,

View File

@@ -35,7 +35,8 @@
let a = +s.data.size_maj;
let b = +s.data.size_min;
if (a < 0.1 || b < 0.1)
let galaxy = ['Seyfert', 'Gin', 'StarburstG', 'LINER', 'AGN', 'Galaxy'].some((n) => s.data.main_type.indexOf(n) >= 0)
if (!galaxy)
return;
let angle = +s.data.size_angle || 0.0;

View File

@@ -29,7 +29,6 @@ A.init.then(() => {
aladin.addCatalog(A.catalogFromURL(vmc_cepheids, {onClick: 'showTable', sourceSize:14, color: '#fff080'}));
aladin.addCatalog(A.catalogFromURL(pessto, {onClick: 'showPopup', sourceSize:14, color: '#00f080'}));
aladin.on('select', (objs) => {

View File

@@ -5,11 +5,10 @@
<body>
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
<script> let aladin;
</script>
<script type="module">
import A from '../src/js/A.js';
var aladin;
A.init.then(() => {
aladin = A.aladin(
'#aladin-lite-div',
@@ -22,8 +21,9 @@
reticleColor: '#00ff00', // change reticle color
reticleSize: 40, // change reticle size
gridOptions: {color: 'pink'},
showCooGrid: true, // set the grid
showCooGrid: false, // set the grid
fullScreen: true,
inertia: false,
showStatusBar: false,
showShareControl: true,
showSettingsControl: true,

View File

@@ -17,45 +17,34 @@ pub enum Data<'a> {
I32(Cow<'a, [i32]>),
F32(Cow<'a, [f32]>),
}
use fitsrs::{fits::Fits as FitsData, hdu::data::InMemData};
use std::io::Cursor;
use fitsrs::{
hdu::data::InMemData,
fits::Fits as FitsData,
};
impl<'a> Fits<'a> {
pub fn from_byte_slice(bytes_reader: &'a mut Cursor<&[u8]>) -> Result<Self, JsValue> {
let FitsData { hdu } = FitsData::from_reader(bytes_reader)
.map_err(|_| {
JsValue::from_str(&"Parsing fits error")
})?;
.map_err(|_| JsValue::from_str(&"Parsing fits error"))?;
let header = hdu.get_header();
let xtension = header.get_xtension();
let width = xtension.get_naxisn(1)
let width = xtension
.get_naxisn(1)
.ok_or_else(|| JsValue::from_str("NAXIS1 not found in the fits"))?;
let height = xtension.get_naxisn(2)
let height = xtension
.get_naxisn(2)
.ok_or_else(|| JsValue::from_str("NAXIS2 not found in the fits"))?;
let data = hdu.get_data();
let data = match *data {
InMemData::U8(slice) => {
Data::U8(Cow::Borrowed(slice))
},
InMemData::I16(slice) => {
Data::I16(Cow::Borrowed(slice))
},
InMemData::I32(slice) => {
Data::I32(Cow::Borrowed(slice))
},
InMemData::U8(slice) => Data::U8(Cow::Borrowed(slice)),
InMemData::I16(slice) => Data::I16(Cow::Borrowed(slice)),
InMemData::I32(slice) => Data::I32(Cow::Borrowed(slice)),
InMemData::I64(slice) => {
let data = slice.iter().map(|v| *v as i32).collect();
Data::I32(Cow::Owned(data))
},
InMemData::F32(slice) => {
Data::F32(Cow::Borrowed(slice))
},
}
InMemData::F32(slice) => Data::F32(Cow::Borrowed(slice)),
InMemData::F64(slice) => {
let data = slice.iter().map(|v| *v as f32).collect();
Data::F32(Cow::Owned(data))
@@ -66,8 +55,8 @@ impl<'a> Fits<'a> {
// Tile size
size: Vector2::new(*width as i32, *height as i32),
// Allocation info of the layout
data
// Allocation info of the layout
data,
})
}
@@ -121,14 +110,14 @@ impl<'a> Fits<'a> {
// Tile size
size: Vector2::new(*width as i32, *height as i32),
// Allocation info of the layout
// Allocation info of the layout
data
})
}
}*/
use crate::Texture2DArray;
use crate::image::Image;
use crate::Texture2DArray;
impl Image for Fits<'_> {
fn tex_sub_image_3d(
&self,
@@ -138,7 +127,7 @@ impl Image for Fits<'_> {
offset: &Vector3<i32>,
) -> Result<(), JsValue> {
match &self.data {
Data::U8(data) => {
Data::U8(data) => {
let view = unsafe { R8UI::view(&data) };
textures[offset.z as usize]
.bind()
@@ -150,7 +139,7 @@ impl Image for Fits<'_> {
Some(view.as_ref()),
);
}
Data::I16(data) => {
Data::I16(data) => {
let view = unsafe { R16I::view(&data) };
textures[offset.z as usize]
.bind()
@@ -162,7 +151,7 @@ impl Image for Fits<'_> {
Some(view.as_ref()),
);
}
Data::I32(data) => {
Data::I32(data) => {
let view = unsafe { R32I::view(&data) };
textures[offset.z as usize]
.bind()
@@ -174,7 +163,7 @@ impl Image for Fits<'_> {
Some(view.as_ref()),
);
}
Data::F32(data) => {
Data::F32(data) => {
let view = unsafe { R32F::view(&data) };
textures[offset.z as usize]
.bind()
@@ -192,8 +181,8 @@ impl Image for Fits<'_> {
}
}
use wasm_bindgen::JsValue;
use crate::image::format::ImageFormat;
use wasm_bindgen::JsValue;
pub trait FitsImageFormat: ImageFormat {
const BITPIX: i8;
@@ -205,7 +194,7 @@ impl FitsImageFormat for R32F {
}
#[cfg(feature = "webgl2")]
use crate::image::{R16I, R32I, R8UI, R64F};
use crate::image::{R16I, R32I, R64F, R8UI};
#[cfg(feature = "webgl2")]
impl FitsImageFormat for R64F {
const BITPIX: i8 = -64;

View File

@@ -606,11 +606,11 @@ impl App {
for rsc in rscs_received {
match rsc {
Resource::Tile(tile) => {
if !has_camera_moved {
if !_has_camera_zoomed {
if let Some(survey) =
self.layers.get_mut_hips_from_cdid(&tile.get_hips_cdid())
{
let cfg = survey.get_config();
let cfg = survey.get_config_mut();
if cfg.get_format() == tile.format {
let delta_depth = cfg.delta_depth();
@@ -649,6 +649,63 @@ impl App {
} else {
Some(image)
};
use al_core::image::ImageType;
use fitsrs::fits::Fits;
use std::{io::Cursor, rc::Rc};
if let Some(image) = image.as_ref() {
match &*image.lock().unwrap_abort() {
Some(ImageType::FitsImage {
raw_bytes: raw_bytes_buf,
}) => {
// check if the metadata has not been set
if !cfg.fits_metadata {
let num_bytes =
raw_bytes_buf.length() as usize;
let mut raw_bytes = vec![0; num_bytes];
raw_bytes_buf.copy_to(&mut raw_bytes[..]);
let mut bytes_reader =
Cursor::new(raw_bytes.as_slice());
let Fits { hdu } =
Fits::from_reader(&mut bytes_reader)
.map_err(|_| {
JsValue::from_str(
"Parsing fits error",
)
})?;
let header = hdu.get_header();
let bscale = if let Some(
fitsrs::card::Value::Float(bscale),
) = header.get(b"BSCALE ")
{
*bscale as f32
} else {
1.0
};
let bzero = if let Some(
fitsrs::card::Value::Float(bzero),
) = header.get(b"BZERO ")
{
*bzero as f32
} else {
0.0
};
let blank = if let Some(
fitsrs::card::Value::Float(blank),
) = header.get(b"BLANK ")
{
*blank as f32
} else {
std::f32::NAN
};
cfg.set_fits_metadata(bscale, bzero, blank);
}
}
_ => (),
}
}
survey.add_tile(&cell, image, time_req)?;
self.request_redraw = true;
@@ -1501,6 +1558,10 @@ impl App {
self.request_redraw = true;
}
pub(crate) fn set_inertia(&mut self, inertia: bool) {
*self.disable_inertia.borrow_mut() = !inertia;
}
/*pub(crate) fn project_line(&self, lon1: f64, lat1: f64, lon2: f64, lat2: f64) -> Vec<Vector2<f64>> {
let v1: Vector3<f64> = LonLatT::new(ArcDeg(lon1).into(), ArcDeg(lat1).into()).vector();
let v2: Vector3<f64> = LonLatT::new(ArcDeg(lon2).into(), ArcDeg(lat2).into()).vector();

View File

@@ -11,6 +11,7 @@ use crate::healpix::cell::HEALPixCell;
use crate::healpix::coverage::HEALPixCoverage;
use crate::math::angle::ToAngle;
use crate::math::{projection::coo_space::XYZWModel, projection::domain::sdf::ProjDef};
use al_core::log::console_log;
use al_core::{info, inforec, log};
use cgmath::{Matrix4, Vector2};
@@ -380,6 +381,8 @@ impl CameraViewPort {
}
};
//console_log(&format!("clip factor {:?}", self.aperture));
// Project this vertex into the screen
self.moved = true;
self.zoomed = true;

View File

@@ -73,6 +73,7 @@ extern "C" {
#[macro_use]
mod utils;
use al_core::log::console_log;
use math::projection::*;
use renderable::coverage::moc::MOC;
//use votable::votable::VOTableWrapper;
@@ -484,6 +485,13 @@ impl WebClient {
Ok(())
}
#[wasm_bindgen(js_name = setInertia)]
pub fn set_inertia(&mut self, inertia: bool) -> Result<(), JsValue> {
self.app.set_inertia(inertia);
Ok(())
}
/// Set the absolute orientation of the view
///
/// # Arguments

View File

@@ -384,29 +384,20 @@ impl ImageSurveyTextures {
&mut self.base_textures[idx as usize]
};*/
if let Some(image) = image {
send_to_gpu(cell, texture, image, &self.texture_2d_array, &self.config)?;
// Once the texture has been received in the GPU
texture.append(
cell, // The tile cell
&self.config,
false,
);
} else {
send_to_gpu(
cell,
texture,
self.config.get_default_image(),
&self.texture_2d_array,
&self.config,
)?;
// Once the texture has been received in the GPU
texture.append(
cell, // The tile cell
&self.config,
true,
);
};
let missing = image.is_none();
send_to_gpu(
cell,
texture,
image,
&self.texture_2d_array,
&mut self.config,
)?;
texture.append(
cell, // The tile cell
&self.config,
missing,
);
self.available_tiles_during_frame = true;
//self.ready = true;
@@ -638,9 +629,9 @@ impl ImageSurveyTextures {
fn send_to_gpu<I: Image>(
cell: &HEALPixCell,
texture: &Texture,
image: I,
image: Option<I>,
texture_array: &Texture2DArray,
cfg: &HiPSConfig,
cfg: &mut HiPSConfig,
) -> Result<(), JsValue> {
// Index of the texture in the total set of textures
let texture_idx = texture.idx();
@@ -672,7 +663,12 @@ fn send_to_gpu<I: Image>(
idx_slice,
);
image.tex_sub_image_3d(&texture_array, &offset)
if let Some(image) = image {
image.tex_sub_image_3d(&texture_array, &offset)
} else {
cfg.get_default_image()
.tex_sub_image_3d(&texture_array, &offset)
}
}
impl SendUniforms for ImageSurveyTextures {

View File

@@ -156,6 +156,7 @@ pub struct HiPSConfig {
// TODO: store this values in the ImageSurvey
// These are proper to the survey (FITS one) and not
// to a specific survey color
pub fits_metadata: bool,
pub scale: f32,
pub offset: f32,
pub blank: f32,
@@ -180,7 +181,7 @@ use wasm_bindgen::JsValue;
const NUM_TEXTURES_BY_SIDE_SLICE: i32 = 8;
const NUM_TEXTURES_BY_SLICE: i32 = NUM_TEXTURES_BY_SIDE_SLICE * NUM_TEXTURES_BY_SIDE_SLICE;
const NUM_SLICES: i32 = 2;
const NUM_SLICES: i32 = 1;
impl HiPSConfig {
/// Define a HiPS configuration
@@ -328,6 +329,7 @@ impl HiPSConfig {
is_allsky,
fits_metadata: false,
scale: 1.0,
offset: 0.0,
blank: -1.0, // by default, set it to -1
@@ -449,6 +451,7 @@ impl HiPSConfig {
self.scale = bscale;
self.offset = bzero;
self.blank = blank;
self.fits_metadata = true;
}
#[inline(always)]

View File

@@ -2,7 +2,6 @@
position: relative;
border: 0px solid #ddd;
/* SVG inside divs add a 4px height: https://stackoverflow.com/questions/75751593/why-there-is-additional-4px-height-for-div-when-there-is-svg-inside-it */
/* disable x swipe on chrome, firefox */
/* see. https://stackoverflow.com/questions/30636930/disable-web-page-navigation-on-swipeback-and-forward */
overscroll-behavior-x: none;
@@ -17,12 +16,6 @@
top: 0;
}
/*.aladin-gridCanvas {
position: absolute;
left: 0;
top: 0;
}*/
.aladin-catalogCanvas {
position: absolute;
left: 0;
@@ -451,6 +444,7 @@ canvas {
background-image:none;
text-indent: 0rem;
font-size: 1rem;
line-height: 1.2rem;
}
.aladin-input-text.search.aladin-unknownObject {

View File

@@ -49,6 +49,7 @@ import { ImageFITS } from "./ImageFITS.js";
import { DefaultActionsForContextMenu } from "./DefaultActionsForContextMenu.js";
import { SAMPConnector } from "./vo/samp.js";
import { Reticle } from "./Reticle.js";
import { requestAnimFrame } from "./libs/RequestAnimationFrame.js";
// GUI
import { AladinLogo } from "./gui/AladinLogo.js";
@@ -271,7 +272,7 @@ export let Aladin = (function () {
// Grid
let gridOptions = options.gridOptions;
console.log(options.gridOptions)
// color and opacity can be defined by two variables. The item in gridOptions
// should take precedence.
gridOptions["color"] = options.gridOptions.color || options.gridColor;
@@ -279,7 +280,7 @@ export let Aladin = (function () {
if (options && options.showCooGrid) {
gridOptions.enabled = true;
}
console.log(gridOptions)
this.setCooGrid(gridOptions);
this.gotoObject(options.target, undefined);
@@ -368,6 +369,10 @@ export let Aladin = (function () {
this.samp = new SAMPConnector(this);
}
if (options.inertia !== undefined) {
this.wasm.setInertia(options.inertia)
}
this._setupUI(options);
};
@@ -493,6 +498,7 @@ export let Aladin = (function () {
target: "0 +0",
cooFrame: "J2000",
fov: 60,
inertia: true,
backgroundColor: "rgb(60, 60, 60)",
// Zoom toolbar
showZoomControl: false,
@@ -935,9 +941,9 @@ export let Aladin = (function () {
var idTimeoutAnim;
var doAnimation = function (aladin) {
if (idTimeoutAnim) {
/*if (idTimeoutAnim) {
clearTimeout(idTimeoutAnim)
}
}*/
var params = aladin.animationParams;
if (params == null || !params['running']) {
@@ -964,8 +970,11 @@ export let Aladin = (function () {
//var curDec = params['decStart'] + (params['decEnd'] - params['decStart']) * (now-params['start']) / (params['end'] - params['start']);
aladin.gotoRaDec(curRa, curDec);
idTimeoutAnim = setTimeout(function () { doAnimation(aladin); }, 10);
//idTimeoutAnim = setTimeout(function () { doAnimation(aladin); }, 10);
requestAnimFrame(() => {
doAnimation(aladin)
})
};
/*

View File

@@ -42,7 +42,6 @@ import { Ellipse } from "./Ellipse.js";
import { Circle } from "./Circle.js";
import { Footprint } from "./Footprint.js";
/**
* Represents a catalog with configurable options for display and interaction.
*
@@ -497,10 +496,6 @@ export let Catalog = (function() {
this.setFields(fields);
}
//let footprints = this.parseFootprintsFromSources(sources);
//sources = sources.filter((s) => s.hasFootprint !== true);
//this.addFootprints(footprints);
this.sources = this.sources.concat(sources);
for (var k=0, len=sources.length; k<len; k++) {
sources[k].setCatalog(this);
@@ -713,19 +708,20 @@ export let Catalog = (function() {
};
Catalog.prototype.draw = function(ctx, width, height) {
if (! this.isShowing) {
if (!this.isShowing) {
return;
}
// tracé simple
ctx.strokeStyle = this.color;
// Draw the footprints
// Draw the footprints first
this.drawFootprints(ctx);
if (this._shapeIsFunction) {
ctx.save();
}
const sourcesInView = this.drawSources(ctx, width, height);
const drawnSources = this.drawSources(ctx, width, height);
if (this._shapeIsFunction) {
ctx.restore();
@@ -735,18 +731,19 @@ export let Catalog = (function() {
if (this.displayLabel) {
ctx.fillStyle = this.labelColor;
ctx.font = this.labelFont;
sourcesInView.forEach((s) => {
drawnSources.forEach((s) => {
this.drawSourceLabel(s, ctx);
})
}
};
Catalog.prototype.drawSources = function(ctx, width, height) {
let inside = [];
if (!this.sources) {
return;
}
let sourcesInsideView = [];
let xy = this.view.wasm.worldToScreenVec(this.ra, this.dec);
let self = this;
@@ -757,12 +754,12 @@ export let Catalog = (function() {
s.y = xy[2*idx + 1];
self.drawSource(s, ctx, width, height)
sourcesInsideView.push(s);
inside.push(s);
}
}
});
return sourcesInsideView;
return inside;
};
Catalog.prototype.drawSource = function(s, ctx, width, height) {
@@ -770,7 +767,7 @@ export let Catalog = (function() {
return false;
}
if (s.hasFootprint) {
if (s.hasFootprint && !s.tooSmallFootprint) {
return false;
}
@@ -821,8 +818,14 @@ export let Catalog = (function() {
this.recomputeFootprints = false;
}
var f;
for(let k = 0; k < this.footprints.length; k++) {
this.footprints[k].draw(ctx, this.view)
f = this.footprints[k];
f.draw(ctx, this.view)
f.source.tooSmallFootprint = f.isTooSmall()
// propagate the info that the footprint is too small
//f.source.tooSmallFootprint = f.isTooSmall
};
};

View File

@@ -199,7 +199,7 @@ export let Circle = (function() {
// TODO
Circle.prototype.draw = function(ctx, view, noStroke) {
if (! this.isShowing) {
return;
return false;
}
noStroke = noStroke===true || false;
@@ -207,7 +207,7 @@ export let Circle = (function() {
if (!centerXyview) {
// the center goes out of the projection
// we do not draw it
return;
return false;
}
this.center = {
x: centerXyview[0],
@@ -237,7 +237,7 @@ export let Circle = (function() {
});
if (hidden) {
return;
return false;
}
// Then we can draw
@@ -271,6 +271,8 @@ export let Circle = (function() {
}
ctx.stroke();
}
return true;
};
Circle.prototype.isInStroke = function(ctx, view, x, y) {

View File

@@ -30,6 +30,7 @@
import { Utils } from "./Utils";
import { Overlay } from "./Overlay.js";
import { requestAnimFrame } from "./libs/RequestAnimationFrame";
/**
* @typedef {Object} ShapeOptions
@@ -231,22 +232,25 @@ export let Ellipse = (function() {
}
// TODO
Ellipse.prototype.draw = function(ctx, view, noStroke) {
Ellipse.prototype.draw = function(ctx, view, noStroke, noSmallCheck) {
if (! this.isShowing) {
return;
return false;
}
let px_per_deg = view.width / view.fov;
/*if (this.a * 2 * px_per_deg < this.lineWidth || this.b * 2 * px_per_deg < this.lineWidth) {
return;
}*/
const px_per_deg = view.width / view.fov;
noSmallCheck = noSmallCheck===true || false;
if (!noSmallCheck) {
this.isTooSmall = this.b * 2 * px_per_deg < this.lineWidth;
if (this.isTooSmall) {
return false;
}
}
var originScreen = view.aladin.world2pix(this.centerRaDec[0], this.centerRaDec[1]);
if (!originScreen) {
// the center goes out of the projection
// we do not draw it
return;
return false;
}
// 1. Find the spherical tangent vector going to the north
@@ -256,7 +260,7 @@ export let Ellipse = (function() {
let toNorthScreen = view.aladin.world2pix(toNorth[0], toNorth[1]);
if(!toNorthScreen) {
return;
return false;
}
// 3. normalize this vector
@@ -338,10 +342,15 @@ export let Ellipse = (function() {
}
ctx.stroke();
}
return true;
};
Ellipse.prototype.isInStroke = function(ctx, view, x, y) {
this.draw(ctx, view, true);
if (!this.draw(ctx, view, true, true)) {
return false;
}
return ctx.isPointInStroke(x, y);
};

View File

@@ -56,11 +56,6 @@ export class CircleSelect extends FSM {
let draw = () => {
let ctx = view.catalogCtx;
/*if (!view.catalogCanvasCleared) {
ctx.clearRect(0, 0, view.width, view.height);
view.catalogCanvasCleared = true;
}*/
// draw the selection
ctx.fillStyle = options.color + '7f';
ctx.strokeStyle = options.color;

View File

@@ -119,11 +119,6 @@ export class PolySelect extends FSM {
let draw = () => {
let ctx = view.catalogCtx;
/*if (!view.catalogCanvasCleared) {
ctx.clearRect(0, 0, view.width, view.height);
view.catalogCanvasCleared = true;
}*/
// draw the selection
ctx.save();
ctx.fillStyle = options.color + '7f';

View File

@@ -57,11 +57,6 @@ export class RectSelect extends FSM {
let draw = () => {
let ctx = view.catalogCtx;
/*if (!view.catalogCanvasCleared) {
ctx.clearRect(0, 0, view.width, view.height);
view.catalogCanvasCleared = true;
}*/
// draw the selection
ctx.fillStyle = options.color + '7f';
ctx.strokeStyle = options.color;

View File

@@ -170,7 +170,7 @@ export let Footprint= (function() {
}
Footprint.prototype.draw = function(ctx, view, noStroke) {
this.shapes.forEach((shape) => shape.draw(ctx, view, noStroke))
return this.shapes.some((shape) => {return shape.draw(ctx, view, noStroke)})
};
Footprint.prototype.actionClicked = function() {
@@ -192,6 +192,10 @@ export let Footprint= (function() {
return this.shapes.some((shape) => shape.isInStroke(ctx, view, x, y));
};
Footprint.prototype.isTooSmall = function(view) {
return this.shapes.every((shape) => shape.isTooSmall);
};
Footprint.prototype.getCatalog = function() {
return this.source && this.source.catalog;
};

View File

@@ -355,6 +355,7 @@ export let ImageSurvey = (function () {
minCut = self.colorCfg.minCut || 0.0;
maxCut = self.colorCfg.maxCut || 1.0;
}
self.colorCfg.setCuts(minCut, maxCut);
// Coo frame
@@ -469,6 +470,7 @@ export let ImageSurvey = (function () {
// Switch from png/webp/jpeg to fits
if ((self.imgFormat === 'png' || self.imgFormat === "webp" || self.imgFormat === "jpeg") && imgFormat === 'fits') {
if (self.minCut && self.maxCut) {
// reset cuts to those given from the properties
self.setCuts(self.minCut, self.maxCut)
}
// Switch from fits to png/webp/jpeg
@@ -891,7 +893,7 @@ export let ImageSurvey = (function () {
imgFormat: 'jpeg',
cooFrame: 'equatorial'
},
/*SDSS9_g: {
SDSS9_g: {
creatorDid: "ivo://CDS/P/SDSS9/g",
id: "P/SDSS9/g",
name: "SDSS9 band-g",
@@ -905,7 +907,7 @@ export let ImageSurvey = (function () {
maxCut: 1.8,
stretch: 'linear',
colormap: "redtemperature",
}*/
}
/*
{
id: "P/Finkbeiner",

View File

@@ -32,6 +32,7 @@
import { Polyline } from "./Polyline.js";
import { Utils } from './Utils';
import { Overlay } from "./Overlay.js";
import { Ellipse } from "./Ellipse.js";
/**
* Represents an line shape
@@ -99,33 +100,36 @@ export let Line = (function() {
setSelectionColor: Polyline.prototype.setSelectionColor,
setHoverColor: Polyline.prototype.setHoverColor,
draw: function(ctx, view, noStroke) {
draw: function(ctx, view, noStroke, noSmallCheck) {
noStroke = noStroke===true || false;
noSmallCheck = noSmallCheck===true || false;
// project
const v1 = view.aladin.world2pix(this.ra1, this.dec1, this.frame);
if (!v1)
return;
return false;
const v2 = view.aladin.world2pix(this.ra2, this.dec2, this.frame);
if (!v2)
return;
return false;
const xmin = Math.min(v1.x, v2.x);
const xmax = Math.max(v1.x, v2.x);
const ymin = Math.min(v1.y, v2.y);
const ymax = Math.max(v1.y, v2.y);
const xmin = Math.min(v1[0], v2[0]);
const xmax = Math.max(v1[0], v2[0]);
const ymin = Math.min(v1[1], v2[1]);
const ymax = Math.max(v1[1], v2[1]);
// out of bbox
if (xmax < 0 || xmin > view.width || ymax < 0 || ymin > view.height) {
return;
return false;
}
let baseColor = this.color || (this.overlay && this.overlay.color) || '#ff0000';
let lineWidth = this.lineWidth || this.overlay.lineWidth || 3;
// too small
if ((xmax - xmin) < lineWidth || (ymax - ymin) < lineWidth) {
return;
if(!noSmallCheck) {
this.isTooSmall = (xmax - xmin) < 1 && (ymax - ymin) < 1;
if (this.isTooSmall) {
return false;
}
}
if (this.isSelected) {
@@ -171,12 +175,12 @@ export let Line = (function() {
if (!noStroke) {
ctx.stroke();
}
return true;
},
isInStroke: function(ctx, view, x, y) {
this.draw(ctx, view, true);
return ctx.isPointInStroke(x, y);
},
isInStroke: Ellipse.prototype.isInStroke,
/*Line.prototype.intersectsBBox = function(x, y, w, h) {
// todo
};*/

View File

@@ -243,11 +243,11 @@ export let Polyline = (function() {
Polyline.prototype.draw = function(ctx, view, noStroke) {
if (! this.isShowing) {
return;
return false;
}
if (! this.radecArray || this.radecArray.length<2) {
return;
return false;
}
noStroke = noStroke===true || false;
@@ -289,7 +289,7 @@ export let Polyline = (function() {
for (var k=0; k<len; k++) {
var xyview = view.aladin.world2pix(this.radecArray[k][0], this.radecArray[k][1]);
if (!xyview) {
return;
return false;
}
xyView.push({x: xyview[0], y: xyview[1]});
@@ -302,11 +302,11 @@ export let Polyline = (function() {
// 2. do not draw the polygon if it lies in less than linewidth pixels
if (xmax < 0 || xmin > view.width || ymax < 0 || ymin > view.height) {
return;
return false;
}
if ((xmax - xmin) < this.lineWidth || (ymax - ymin) < this.lineWidth) {
return;
return false;
}
let drawLine;
@@ -451,6 +451,8 @@ export let Polyline = (function() {
ctx.fill();
ctx.restore();
}
return true;
};
Polyline.prototype.isInStroke = function(ctx, view, x, y) {

View File

@@ -90,7 +90,6 @@ export let Popup = (function() {
}
source.popup = this;
this.source = source;
this.setPosition(source.x, source.y);
};

View File

@@ -99,13 +99,14 @@ export class Selector {
if (view.catalogs) {
for (var k = 0; k < view.catalogs.length; k++) {
cat = view.catalogs[k];
if (!cat.isShowing) {
continue;
}
sources = cat.getSources();
for (var l = 0; l < sources.length; l++) {
s = sources[l];
if (!s.isShowing || !s.x || !s.y) {
if (!s.isShowing || !s.x || !s.y || s.tooSmallFootprint === false) {
continue;
}
if (selection.contains(s)) {

View File

@@ -62,7 +62,7 @@ export let SimbadPointer = (function() {
}
content += '<br><a target="_blank" href="http://cdsportal.u-strasbg.fr/?target=' + encodeURIComponent(objName) + '">Query in CDS portal</a>';
content += '</div>';
aladinInstance.showPopup(objCoo.lon, objCoo.lat, title, content);
}
else {

View File

@@ -125,6 +125,39 @@ Utils.isInt = function (n: string | number) {
return Utils.isNumber(n) && Math.floor(n as number) === n
}
// Newton-Raphson method to find the approximate inverse of f(x)
Utils.inverseNewtonRaphson = function(y: number, f: Function, fPrime: Function, tolerance=1e-6, maxIterations=100) {
let x_guess = 0.5; // Initial guess
let iteration = 0;
while (iteration < maxIterations) {
let f_x = f(x_guess);
let error = Math.abs(f_x - y);
if (error < tolerance) {
return x_guess; // Found approximate inverse
}
let derivative = fPrime(x_guess);
x_guess = x_guess - (f_x - y) / derivative; // Newton-Raphson update
iteration++;
}
return null; // No convergence within maxIterations
}
Utils.binarySearch = function(array, value) {
var low = 0,
high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
if (array[mid] > value) low = mid + 1;
else high = mid;
}
return low;
}
/* a debounce function, used to prevent multiple calls to the same function if less than delay milliseconds have passed */
Utils.debounce = function (fn, delay) {
var timer = null

View File

@@ -43,7 +43,7 @@ import { CooFrameEnum } from "./CooFrameEnum.js";
import { requestAnimFrame } from "./libs/RequestAnimationFrame.js";
import { WebGLCtx } from "./WebGL.js";
import { ALEvent } from "./events/ALEvent.js";
import { ColorCfg } from "./ColorCfg.js";
import { Zoom } from './Zoom.js'
import { Footprint } from "./Footprint.js";
import { Selector } from "./Selector.js";
import { ObsCore } from "./vo/ObsCore.js";
@@ -133,14 +133,11 @@ export let View = (function () {
lon = lat = 0;
// FoV init settings
const si = 500000.0;
const alpha = 40.0;
let initialFov = this.options.fov || 180.0;
this.pinchZoomParameters = {
isPinching: false, // true if a pinch zoom is ongoing
initialFov: undefined,
initialDistance: undefined,
initialAccDelta: Math.pow(si / initialFov, 1.0 / alpha)
};
// Projection definition
@@ -148,7 +145,8 @@ export let View = (function () {
this.setProjection(projName)
// Then set the zoom properly once the projection is defined
this.setZoom(initialFov);
this.wasm.setFieldOfView(initialFov);
this.updateZoomState();
// Target position settings
this.viewCenter = { lon, lat }; // position of center of view
@@ -216,6 +214,7 @@ export let View = (function () {
initialFingerAngle: undefined,
rotationInitiated: false
}
this.zoom = new Zoom(this);
this.fadingLatestUpdate = null;
this.dateRequestRedraw = null;
@@ -665,8 +664,6 @@ export let View = (function () {
view.dragging = false;
view.pinchZoomParameters.isPinching = true;
//var fov = view.aladin.getFov();
//view.pinchZoomParameters.initialFov = Math.max(fov[0], fov[1]);
var fov = view.wasm.getFieldOfView();
view.pinchZoomParameters.initialFov = fov;
view.pinchZoomParameters.initialDistance = Math.sqrt(Math.pow(e.targetTouches[0].clientX - e.targetTouches[1].clientX, 2) + Math.pow(e.targetTouches[0].clientY - e.targetTouches[1].clientY, 2));
@@ -1062,13 +1059,21 @@ export let View = (function () {
var eventCount = 0;
var eventCountStart;
var isTouchPad;
var scale = 0.0;
let id;
Utils.on(view.catalogCanvas, 'wheel', function (e) {
e.preventDefault();
e.stopPropagation();
const xymouse = Utils.relMouseCoords(e);
view.wheelTriggered = true;
clearTimeout(id);
id = setTimeout(() => {
view.wheelTriggered = false;
}, 100);
const xymouse = Utils.relMouseCoords(e);
view.xy = xymouse
ALEvent.CANVAS_EVENT.dispatchedTo(view.aladinDiv, {
state: {
mode: view.mode,
@@ -1083,21 +1088,25 @@ export let View = (function () {
return;
}
var delta = e.deltaY || e.detail || (-e.wheelDelta);
if (!view.debounceProgCatOnZoom) {
var self = view;
view.debounceProgCatOnZoom = Utils.debounce(function () {
self.refreshProgressiveCats();
self.drawAllOverlays();
}, 300);
}
// Limit the minimum and maximum zoom levels
//var delta = e.deltaY;
// this seems to happen in context of Jupyter notebook --> we have to invert the direction of scroll
// hope this won't trigger some side effects ...
/*if (e.hasOwnProperty('originalEvent')) {
delta = -e.deltaY;
}*/
view.debounceProgCatOnZoom();
view.throttledZoomChanged();
// Zoom heuristic
// First detect the device
// See https://stackoverflow.com/questions/10744645/detect-touchpad-vs-mouse-in-javascript
// for detecting the use of a touchpad
var isTouchPadDefined = isTouchPad || typeof isTouchPad !== "undefined";
if (!isTouchPadDefined) {
view.isTouchPadDefined = isTouchPad || typeof isTouchPad !== "undefined";
if (!view.isTouchPadDefined) {
if (eventCount === 0) {
view.delta = 0;
eventCountStart = new Date().getTime();
}
@@ -1109,50 +1118,75 @@ export let View = (function () {
} else {
isTouchPad = false;
}
isTouchPadDefined = true;
view.isTouchPadDefined = true;
}
}
// The value of the field of view is determined
// inside the backend
const triggerZoom = (amount) => {
if (delta < 0.0) {
view.increaseZoom(amount);
} else {
view.decreaseZoom(amount);
}
};
if (isTouchPadDefined) {
let dt = performance.now() - view.then
let a0, a1;
// touchpad
if (isTouchPad) {
a1 = 0.002;
a0 = 0.0002;
} else {
a1 = 0.01;
a0 = 0.0004;
}
const alpha = Math.pow(view.fov / view.projection.fov, 0.5);
const lerp = a0 * alpha + a1 * (1.0 - alpha);
triggerZoom(lerp);
// only ensure the touch pad test has been done before zooming
if (!view.isTouchPadDefined) {
return false;
}
if (!view.debounceProgCatOnZoom) {
var self = view;
view.debounceProgCatOnZoom = Utils.debounce(function () {
self.refreshProgressiveCats();
self.drawAllOverlays();
}, 300);
}
// touch pad defined
view.debounceProgCatOnZoom();
view.throttledZoomChanged();
if (isTouchPad) {
view.delta = e.deltaY || e.detail || (-e.wheelDelta);
if (!view.throttledTouchPadZoom) {
let radec;
view.throttledTouchPadZoom = Utils.throttle(() => {
if (!view.zoom.isZooming && !view.wheelTriggered) {
// start zooming detected
radec = view.aladin.pix2world(view.xy.x, view.xy.y);
}
let amount = view.delta > 0 ? -Zoom.MAX_IDX_DELTA_PER_TROTTLE : Zoom.MAX_IDX_DELTA_PER_TROTTLE;
if (amount === 0)
return;
// change the zoom level
let newFov = Zoom.determineNextFov(view, amount);
view.zoom.apply({
stop: newFov,
duration: 300
});
//view.setZoom(newFov)
/*if (amount > 0 && radec) {
let sRaDec = view.aladin.getRaDec();
let moveTo = function() {
const t = 1 - (view.x - view.x1) / (view.x2 - view.x1);
let ra = (0.5 + t*0.5) * sRaDec[0] + (0.5 - t*0.5) * radec[0]
let dec = (0.5 + t*0.5) * sRaDec[1] + (0.5 - t*0.5) * radec[1]
view.aladin.gotoRaDec(ra, dec)
if (t >= 1e-2)
requestAnimFrame(moveTo)
}
//requestAnimFrame(moveTo)
}*/
}, 30);
}
view.throttledTouchPadZoom();
} else {
if (!view.throttledMouseScrollZoom) {
view.throttledMouseScrollZoom = Utils.throttle(() => {
let newFov = view.delta > 0 ? view.fov * 1.15 : view.fov / 1.15;
// standard mouse wheel zooming
view.zoom.apply({
stop: newFov,
duration: 300
});
}, 30);
}
view.throttledMouseScrollZoom()
}
return false;
});
@@ -1257,11 +1291,11 @@ export let View = (function () {
this.catalogCanvasCleared = true;
}
this.catalogForPopup.draw(ctx, this.cooFrame, this.width, this.height, this.largestDim);
this.catalogForPopup.draw(ctx, this.width, this.height);
// draw popup overlay layer
if (this.overlayForPopup.isShowing) {
this.overlayForPopup.draw(ctx, this.cooFrame, this.width, this.height, this.largestDim);
this.overlayForPopup.draw(ctx);
}
}
@@ -1313,6 +1347,11 @@ export let View = (function () {
}
if (this.mode === View.SELECT) {
if (!this.catalogCanvasCleared) {
ctx.clearRect(0, 0, this.width, this.height);
this.catalogCanvasCleared = true;
}
this.selector.dispatch('draw')
}
};
@@ -1425,7 +1464,6 @@ export let View = (function () {
};
// Called for touchmove events
// initialAccDelta must be consistent with fovDegrees here
View.prototype.setZoom = function (fov) {
// limit the fov in function of the projection
fov = Math.min(fov, this.projection.fov);
@@ -1443,43 +1481,21 @@ export let View = (function () {
}
this.wasm.setFieldOfView(fov);
this.updateZoomState();
};
View.prototype.increaseZoom = function (amount) {
const si = 500000.0;
const alpha = 40.0;
let initialAccDelta = this.pinchZoomParameters.initialAccDelta + amount;
let new_fov = si / Math.pow(initialAccDelta, alpha);
if (new_fov < 0.00002777777) {
new_fov = 0.00002777777;
}
this.pinchZoomParameters.initialAccDelta = initialAccDelta;
this.setZoom(new_fov);
View.prototype.increaseZoom = function () {
this.zoom.apply({
stop: Zoom.determineNextFov(this, 6),
duration: 300
});
}
View.prototype.decreaseZoom = function (amount) {
const si = 500000.0;
const alpha = 40.0;
let initialAccDelta = this.pinchZoomParameters.initialAccDelta - amount;
if (initialAccDelta <= 0.0) {
initialAccDelta = 1e-3;
}
let new_fov = si / Math.pow(initialAccDelta, alpha);
if (new_fov >= this.projection.fov) {
new_fov = this.projection.fov;
}
this.pinchZoomParameters.initialAccDelta = initialAccDelta;
this.setZoom(new_fov);
View.prototype.decreaseZoom = function () {
this.zoom.apply({
stop: Zoom.determineNextFov(this, -6),
duration: 300
});
}
View.prototype.setRotation = function(rotation) {
@@ -1507,11 +1523,6 @@ export let View = (function () {
// Get the new zoom values from the backend
let fov = this.wasm.getFieldOfView();
// Update the pinch zoom parameters consequently
const si = 500000.0;
const alpha = 40.0;
this.pinchZoomParameters.initialAccDelta = Math.pow(si / fov, 1.0 / alpha);
// Save it
this.fov = fov;
this.computeNorder();
@@ -1987,17 +1998,19 @@ export let View = (function () {
let closest = null;
footprints.forEach((footprint) => {
// Hidden footprints are not considered
let lineWidth = footprint.getLineWidth();
if (!footprint.source.tooSmallFootprint) {
// Hidden footprints are not considered
let lineWidth = footprint.getLineWidth();
footprint.setLineWidth(10.0);
if (footprint.isShowing && footprint.isInStroke(ctx, this, x * window.devicePixelRatio, y * window.devicePixelRatio)) {
closest = footprint;
}
footprint.setLineWidth(lineWidth);
footprint.setLineWidth(10.0);
if (footprint.isShowing && footprint.isInStroke(ctx, this, x * window.devicePixelRatio, y * window.devicePixelRatio)) {
closest = footprint;
}
footprint.setLineWidth(lineWidth);
if (closest) {
return closest;
if (closest) {
return closest;
}
}
})

168
src/js/Zoom.js Normal file
View File

@@ -0,0 +1,168 @@
// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// 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.
//
// 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.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
/******************************************************************************
* Aladin Lite project
*
* File Tile
*
* Author: Matthieu Baumann[CDS]
*
*****************************************************************************/
import { Utils } from "./Utils";
import { requestAnimFrame } from "./libs/RequestAnimationFrame.js";
export let Zoom = (function() {
// constructor
function Zoom(view) {
this.view = view;
};
Zoom.LEVELS = [
360, 330, 300, 275, 250, 225, 200, 190,
180, 170, 160, 150, 140, 130, 120, 110, 100,
95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35, 30, 25, 20, 18, 16, 14, 12, 10,
9, 8, 7, 6, 5, 4, 3, 2, 1.75, 1.5, 1.25, 1,
55/60, 50/60, 45/60, 40/60, 35/60, 30/60, 25/60, 20/60, 15/60, 10/60,
9/60, 8/60, 7/60, 6/60, 5/60, 4/60, 3/60, 2/60, 1/60,
50/3600, 40/3600, 30/3600, 20/3600, 10/3600,
9/3600, 8/3600, 7/3600, 6/3600, 5/3600, 4/3600, 3/3600, 2/3600, 1/3600,
9/36000, 8/36000, 7/36000, 6/36000, 5/36000, 4/36000, 3/36000, 2/36000, 1/36000
];
Zoom.MAX_IDX_DELTA_PER_TROTTLE = 2;
Zoom.determineNextFov = function(view, amount) {
if (!view.idx)
view.idx = Utils.binarySearch(Zoom.LEVELS, view.fov);
let deltaIdx = amount;
view.idx += deltaIdx;
// clamp to the array indices
if (view.idx < 0) {
view.idx = 0
}
if (view.idx >= Zoom.LEVELS.length) {
view.idx = Zoom.LEVELS.length - 1
}
return Zoom.LEVELS[view.idx];
}
Zoom.prototype.apply = function(options) {
let startZoom = options['start'] || this.view.fov;
let finalZoom = options['stop'] || undefined;
let interpolationDuration = options['duration'] || 1000; // default to 1seconds
if (!finalZoom)
return;
this.finalZoom = finalZoom;
if (!this.isZooming) {
this.isZooming = true;
this.startTime = performance.now();
this.x1 = 0
this.x2 = 1;
this.y1 = startZoom;
this.y2 = finalZoom;
this.m1 = finalZoom - startZoom;
this.m2 = 0;
this.x = this.x1;
} else {
// find the startTime
this.x = (performance.now() - this.startTime) / interpolationDuration;
let m1 = Zoom.hermiteCubic.fPrime(this.x, this.x1, this.x2, this.y1, this.y2, this.m1, this.m2)
let y1 = Zoom.hermiteCubic.f(this.x, this.x1, this.x2, this.y1, this.y2, this.m1, this.m2);
this.y1 = y1;
this.x1 = this.x;
this.x2 = this.x1 + 1;
this.y2 = finalZoom;
this.m1 = m1;
this.m2 = 0;
}
// Initialize current zoom to the current zoom level
let interpolatedZoom;
let self = this;
// Recursive function to perform interpolation for each frame
function interpolateFrame() {
//console.log('zooming')
//fps = 1000 / self.dt;
//totalFrames = interpolationDuration * fps; // Total number of frames
self.x = ( performance.now() - self.startTime ) / interpolationDuration;
// Calculate step size for each frame
//stepSize = (desiredZoom - currentZoom) / totalFrames;
interpolatedZoom = Zoom.hermiteCubic.f(self.x, self.x1, self.x2, self.y1, self.y2, self.m1, self.m2);
// Clamp the interpolation in case it is < 0 for a time
if (interpolatedZoom < Zoom.min()) {
interpolatedZoom = Zoom.min();
}
// Apply zoom level to map or perform any necessary rendering
self.view.setZoom(interpolatedZoom);
self.fov = interpolatedZoom;
// Check if interpolation is complete
if (self.x >= self.x2 || Math.abs(interpolatedZoom - self.finalZoom) < 1e-4) {
self.view.setZoom(self.finalZoom);
self.isZooming = false;
} else {
// Request the next frame
requestAnimFrame(interpolateFrame);
}
}
// Start interpolation by requesting the first frame
requestAnimFrame(interpolateFrame);
}
Zoom.max = function() {
return Zoom.LEVELS[0];
}
Zoom.min = function() {
return Zoom.LEVELS[Zoom.LEVELS.length - 1];
}
Zoom.hermiteCubic = {
f: function(x, x1, x2, y1, y2, m1, m2) {
let t = (x - x1) / (x2 - x1)
let t2 = t*t;
let t3 = t2*t;
return (1 - 3*t2 + 2*t3) * y1 + (t - 2*t2 + t3) * m1 + (3*t2 - 2*t3) * y2 + (-t2 + t3) * m2
},
fPrime: function(x, x1, x2, y1, y2, m1, m2) {
let t = (x - x1) / (x2 - x1)
let t2 = t*t;
return (1 / (x2 - x1))*((-6*t+6*t2)*y1 + (1 - 4*t + 3*t2)*m1 + (6*t - 6*t2)*y2 + m2*(3*t2 - 2*t))
}
}
return Zoom;
})();

View File

@@ -699,7 +699,40 @@ export class OverlayStack extends ContextMenu {
};
l.subMenu = [];
l.subMenu.push({
label: {
icon: {
url: searchIconImg,
monochrome: true,
tooltip: {content: 'Find a specific survey <br /> in our database...', position: { direction: 'bottom' }},
cssStyle: {
cursor: 'help',
},
},
content: 'More...'
},
action(o) {
o.stopPropagation();
o.preventDefault();
self._hide();
self.hipsBox = new HiPSSelectorBox(self.aladin)
self.hipsBox.attach((HiPSId) => {
self.aladin.setOverlayImageLayer(HiPSId, layer.layer);
self.show();
});
self.hipsBox._show({
position: self.position,
})
self.mode = 'hips';
}
})
for(const [id, ll] of defaultLayers) {
backgroundUrl = OverlayStack.previewImagesUrl[ll.name];
if (!backgroundUrl) {
@@ -732,41 +765,6 @@ export class OverlayStack extends ContextMenu {
})
}
l.subMenu.push({
label: {
icon: {
url: searchIconImg,
monochrome: true,
tooltip: {content: 'Find a specific survey <br /> in our database...', position: { direction: 'top' }},
cssStyle: {
cursor: 'help',
},
},
content: 'More...'
},
action(o) {
o.stopPropagation();
o.preventDefault();
self._hide();
self.hipsBox = new HiPSSelectorBox(self.aladin)
self.hipsBox.attach(
(HiPSId) => {
self.aladin.setOverlayImageLayer(HiPSId, layer.layer);
self.show();
}
);
self.hipsBox._show({
position: self.position,
})
self.mode = 'hips';
}
})
l.action = (o) => {
let oldLayerClassName = 'a' + self.aladin.getSelectedLayer().replace(/[.\/ ]/g, '')
self.el.querySelector('.' + oldLayerClassName).style.removeProperty('border')

View File

@@ -15,28 +15,4 @@ Element.prototype.swap = function (node) {
// Move `node` to before the sibling of `this`
parent.insertBefore(node, sibling);
};
export let Utils = {}
/**
* Append el to target
*
* target must be an DOM Element/Node
*
* @API
*
* @param el: el can be a Widget or Element object. Otherwise it is considered as text
* @param target: target must be an DOM Element/Node
*
*/
Utils.binarySearch = function(array, value) {
var low = 0,
high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
if (array[mid] < value) low = mid + 1;
else high = mid;
}
return low;
}
};

View File

@@ -30,7 +30,6 @@
*
*****************************************************************************/
import { Utils } from "../Utils.js";
import { DOMElement } from "./Widget.js";
export class Table extends DOMElement {

View File

@@ -304,7 +304,7 @@ export class DOMElement {
attachTo(target, position = 'beforeend') {
if(target) {
if (typeof position === 'number') {
target.insertChildAtIndex(this.element(), position)
target.insertBefore(this.element(), target.childNodes[position]);
} else {
target.insertAdjacentElement(position, this.element());
}