mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2026-04-28 11:53:18 -07:00
WIP: spectra plot
This commit is contained in:
committed by
Matthieu Baumann
parent
e4689cf674
commit
5c6405bf8b
@@ -24,7 +24,6 @@
|
||||
showFrame: true,
|
||||
showZoomControl:true,
|
||||
showSettingsControl:true,
|
||||
showCooGrid: true,
|
||||
fullScreen: true,
|
||||
samp: true,
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ futures = "0.3.12"
|
||||
js-sys = "0.3.47"
|
||||
wasm-bindgen-futures = "0.4.20"
|
||||
cgmath = "*"
|
||||
url-lite = "0.1.0"
|
||||
# url-lite = "0.1.0"
|
||||
serde_json = "1.0.104"
|
||||
serde-wasm-bindgen = "0.5"
|
||||
enum_dispatch = "0.3.8"
|
||||
wasm-bindgen = "=0.2.92"
|
||||
wasm-streams = "0.3.0"
|
||||
#wasm-streams = "0.3.0"
|
||||
async-channel = "1.8.0"
|
||||
mapproj = "0.3.0"
|
||||
fitsrs = "0.3.4"
|
||||
@@ -64,7 +64,7 @@ path = "./al-api"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.56"
|
||||
features = [ "console", "CssStyleDeclaration", "Document", "Element", "HtmlCollection", "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", "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",]
|
||||
|
||||
[dev-dependencies.image-decoder]
|
||||
package = "image"
|
||||
|
||||
@@ -7,14 +7,14 @@ edition = "2018"
|
||||
[dependencies]
|
||||
js-sys = "0.3.47"
|
||||
cgmath = "*"
|
||||
jpeg-decoder = "0.3.0"
|
||||
png = "0.17.6"
|
||||
#jpeg-decoder = "0.3.0"
|
||||
#png = "0.17.6"
|
||||
fitsrs = "0.3.4"
|
||||
al-api = { path = "../al-api" }
|
||||
serde = { version = "^1.0.59", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde-wasm-bindgen = "0.4"
|
||||
wasm-streams = "0.3.0"
|
||||
# wasm-streams = "0.3.0"
|
||||
futures = "0.3.25"
|
||||
colorgrad = "0.6.2"
|
||||
wasm-bindgen = "0.2.92"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
extern crate futures;
|
||||
extern crate jpeg_decoder as jpeg;
|
||||
extern crate png;
|
||||
//extern crate jpeg_decoder as jpeg;
|
||||
//extern crate png;
|
||||
extern crate serde_json;
|
||||
extern crate wasm_streams;
|
||||
//extern crate wasm_streams;
|
||||
|
||||
pub mod convert;
|
||||
pub mod image;
|
||||
|
||||
@@ -44,12 +44,13 @@ impl TextureFormat for RGB8U {
|
||||
const PIXEL_TYPE: PixelType = PixelType::RGB8U;
|
||||
|
||||
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
|
||||
let mut decoder = jpeg::Decoder::new(raw_bytes);
|
||||
todo!()
|
||||
/*let mut decoder = jpeg::Decoder::new(raw_bytes);
|
||||
let bytes = decoder
|
||||
.decode()
|
||||
.map_err(|_| "Cannot decoder jpeg. This image may not be compressed.")?;
|
||||
|
||||
Ok(Bytes::Owned(bytes))
|
||||
Ok(Bytes::Owned(bytes))*/
|
||||
}
|
||||
|
||||
type ArrayBufferView = js_sys::Uint8Array;
|
||||
@@ -73,12 +74,14 @@ impl TextureFormat for RGBA8U {
|
||||
const PIXEL_TYPE: PixelType = PixelType::RGBA8U;
|
||||
|
||||
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
|
||||
let mut decoder = jpeg::Decoder::new(raw_bytes);
|
||||
/*let mut decoder = jpeg::Decoder::new(raw_bytes);
|
||||
let bytes = decoder
|
||||
.decode()
|
||||
.map_err(|_| "Cannot decoder png. This image may not be compressed.")?;
|
||||
|
||||
Ok(Bytes::Owned(bytes))
|
||||
*/
|
||||
todo!()
|
||||
}
|
||||
|
||||
type ArrayBufferView = js_sys::Uint8Array;
|
||||
|
||||
@@ -49,7 +49,7 @@ fn read_shader<P: AsRef<std::path::Path>>(path: P) -> std::io::Result<String> {
|
||||
let shader_src = std::io::BufReader::new(file)
|
||||
.lines()
|
||||
.map_while(Result::ok)
|
||||
.map(|l| {
|
||||
.filter_map(|l| {
|
||||
if l.starts_with("#include") {
|
||||
let incl_file_names: Vec<_> = l.split_terminator(&[';', ' '][..]).collect();
|
||||
let incl_file_name_rel = incl_file_names[1];
|
||||
@@ -57,9 +57,12 @@ fn read_shader<P: AsRef<std::path::Path>>(path: P) -> std::io::Result<String> {
|
||||
|
||||
println!("{}", incl_file_name.to_string_lossy());
|
||||
|
||||
read_shader(incl_file_name.to_str().unwrap()).unwrap()
|
||||
Some(read_shader(incl_file_name.to_str().unwrap()).unwrap())
|
||||
} else if l.trim_start().starts_with("//") {
|
||||
// comment
|
||||
None
|
||||
} else {
|
||||
l
|
||||
Some(l)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
|
||||
@@ -35,6 +35,7 @@ use fitsrs::WCS;
|
||||
use moclib::qty::{Frequency, MocQty};
|
||||
use std::hint::unreachable_unchecked;
|
||||
use std::io::Cursor;
|
||||
use std::time::Duration;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@@ -490,6 +491,10 @@ impl App {
|
||||
}
|
||||
|
||||
pub(crate) fn update(&mut self, dt: DeltaTime) -> Result<bool, JsValue> {
|
||||
// a timer stopping the frame if it takes too long
|
||||
// useful for garanting a framerate
|
||||
let rendering_timer = Time::now();
|
||||
|
||||
if let Some(inertia) = self.inertia.as_mut() {
|
||||
inertia.apply(&mut self.camera, &self.projection, dt);
|
||||
// Always request for new tiles while moving
|
||||
@@ -565,10 +570,25 @@ impl App {
|
||||
|
||||
let mut tile_copied = false;
|
||||
|
||||
const MAX_FRAME_TIME: DeltaTime = DeltaTime::from_millis(1000.0/25.0);
|
||||
|
||||
for rsc in rscs_received {
|
||||
if Time::now() - rendering_timer >= MAX_FRAME_TIME {
|
||||
self.downloader
|
||||
.borrow_mut()
|
||||
.delay(rsc);
|
||||
continue;
|
||||
}
|
||||
|
||||
match rsc {
|
||||
RequestType::Tile(tile) => {
|
||||
//if !_has_camera_zoomed {
|
||||
if self.camera.has_moved() {
|
||||
self.downloader
|
||||
.borrow_mut()
|
||||
.delay(RequestType::Tile(tile));
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(hips) = self.layers.get_mut_hips_from_cdid(&tile.hips_cdid) {
|
||||
let cfg = hips.get_config();
|
||||
|
||||
|
||||
18
src/core/src/event.rs
Normal file
18
src/core/src/event.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::{window, CustomEvent, CustomEventInit};
|
||||
|
||||
pub(crate) fn send_custom_event(name: &str, value: JsValue) {
|
||||
// Create event details (optional)
|
||||
let mut event_init = CustomEventInit::new();
|
||||
event_init.detail(&value);
|
||||
|
||||
// Create the event
|
||||
let event = CustomEvent::new_with_event_init_dict(name, &event_init)
|
||||
.expect("Failed to create custom event");
|
||||
|
||||
// Dispatch the event on the window or any target element
|
||||
window()
|
||||
.expect("no global `window` exists")
|
||||
.dispatch_event(&event)
|
||||
.expect("failed to dispatch event");
|
||||
}
|
||||
@@ -103,6 +103,7 @@ mod shaders;
|
||||
|
||||
mod coosys;
|
||||
mod downloader;
|
||||
mod event;
|
||||
mod fifo_cache;
|
||||
mod healpix;
|
||||
mod inertia;
|
||||
|
||||
@@ -130,7 +130,7 @@ impl Cursor {
|
||||
let freq = cfg.em_min.unwrap_abort();
|
||||
let location = LonLatT::new(0.0.to_angle(), 0.0.to_angle());
|
||||
|
||||
let f_max_order = cfg.max_depth_freq.unwrap_abort();
|
||||
let f_max_order = cfg.max_depth_freq.unwrap_or(Frequency::<u64>::MAX_DEPTH);
|
||||
let s_max_order = cfg.max_depth_tile;
|
||||
|
||||
let cell = HEALPixFreqCell::from_lonlat(location, freq, 0, 0);
|
||||
@@ -485,7 +485,10 @@ impl HiPS3D {
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
|
||||
al_core::log(&format!("{:?}", spectra));
|
||||
//al_core::log(&format!("{:?}", spectra));
|
||||
let array = js_sys::Float32Array::from(&spectra[..]);
|
||||
|
||||
crate::event::send_custom_event("spectra", JsValue::from(array))
|
||||
}
|
||||
|
||||
pub fn set_cursor_location(&mut self, lonlat: LonLatT<f64>, camera: &CameraViewPort) {
|
||||
@@ -494,18 +497,23 @@ impl HiPS3D {
|
||||
.get_tile_depth()
|
||||
.min(cfg.get_max_depth_tile())
|
||||
.max(cfg.get_min_depth_tile());
|
||||
let dataproduct_type = cfg.dataproduct_type;
|
||||
|
||||
self.cursor.set_location(lonlat, s_order);
|
||||
|
||||
// update the spectra
|
||||
self.compute_spectra_on_cursor();
|
||||
if dataproduct_type == DataproductType::SpectralCube {
|
||||
self.compute_spectra_on_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_freq(&mut self, f: Freq) {
|
||||
self.cursor.set_freq(f);
|
||||
|
||||
// update the spectra
|
||||
self.compute_spectra_on_cursor();
|
||||
if self.get_config().dataproduct_type == DataproductType::SpectralCube {
|
||||
self.compute_spectra_on_cursor();
|
||||
}
|
||||
|
||||
// Flag telling to recompute the mesh afterwards
|
||||
self.move_freq = true;
|
||||
@@ -565,14 +573,24 @@ impl HiPS3D {
|
||||
.max()
|
||||
.unwrap();
|
||||
|
||||
let cfg = self.get_config();
|
||||
let dataproduct_type = cfg.dataproduct_type;
|
||||
let max_depth_tile = cfg.max_depth_tile;
|
||||
let min_depth_tile = cfg.get_min_depth_tile();
|
||||
|
||||
let em_min = cfg.em_min;
|
||||
let em_max = cfg.em_max;
|
||||
let cube_depth = cfg.get_cube_depth();
|
||||
let max_depth_freq = cfg.max_depth_freq;
|
||||
|
||||
for cell in &self.hpx_cells_in_view {
|
||||
// filter textures that are not in the moc
|
||||
let cell = match self.get_config().dataproduct_type {
|
||||
let cell = match dataproduct_type {
|
||||
DataproductType::SpectralCube => {
|
||||
// Determination of the f_order from the s_order
|
||||
// From https://aladin.cds.unistra.fr/java/DocTechHiPS3D.pdf page 3
|
||||
let f_max_order = self.get_config().max_depth_freq.unwrap_abort();
|
||||
let s_max_order = self.get_config().max_depth_tile;
|
||||
let f_max_order = max_depth_freq.unwrap_abort();
|
||||
let s_max_order = max_depth_tile;
|
||||
let s_order = cell.depth();
|
||||
|
||||
let f_order = f_max_order - (s_max_order - s_order);
|
||||
@@ -599,19 +617,11 @@ impl HiPS3D {
|
||||
}
|
||||
}
|
||||
DataproductType::Cube => {
|
||||
/*al_core::log(&format!(
|
||||
"{:?}, {:?} {:?} {:?}",
|
||||
self.freq.0,
|
||||
self.get_config().em_min,
|
||||
self.get_config().em_max,
|
||||
self.get_config().get_cube_depth(),
|
||||
));*/
|
||||
|
||||
let channel_idx = (((self.get_freq().0
|
||||
- self.get_config().em_min.unwrap_abort().0)
|
||||
/ (self.get_config().em_max.unwrap_abort().0
|
||||
- self.get_config().em_min.unwrap_abort().0))
|
||||
* (self.get_config().get_cube_depth().unwrap_abort() as f64))
|
||||
- em_min.unwrap_abort().0)
|
||||
/ (em_max.unwrap_abort().0
|
||||
- em_min.unwrap_abort().0))
|
||||
* (cube_depth.unwrap_abort() as f64))
|
||||
as u64;
|
||||
|
||||
let tile_depth = 32;
|
||||
@@ -666,25 +676,29 @@ impl HiPS3D {
|
||||
};
|
||||
|
||||
if let Some(texture) = hpx_cell_texture {
|
||||
self.cells.push(texture.cell.clone());
|
||||
let texture_cell = texture.cell.clone();
|
||||
// The slice is sure to be contained so we can unwrap
|
||||
let slice_position = match self.get_config().dataproduct_type {
|
||||
let slice_position = match dataproduct_type {
|
||||
DataproductType::SpectralCube => {
|
||||
let f_hash_0 = texture.cell.f_hash
|
||||
<< (Frequency::<u64>::MAX_DEPTH - texture.cell.f_depth);
|
||||
let f_hash_1 = (texture.cell.f_hash + 1)
|
||||
<< (Frequency::<u64>::MAX_DEPTH - texture.cell.f_depth);
|
||||
|
||||
// 1. hash of the frequency at max order
|
||||
let f_hash = Frequency::<u64>::freq2hash(self.get_freq().0);
|
||||
// b. compute the hash range
|
||||
let delta_f_order = Frequency::<u64>::MAX_DEPTH - texture_cell.f_depth;
|
||||
let f_order_hash_0 = texture_cell.f_hash;
|
||||
let f_order_hash_1 = f_order_hash_0 + 1;
|
||||
|
||||
// 3. hash range at max order
|
||||
let f_hash_0 = f_order_hash_0 << delta_f_order;
|
||||
let f_hash_1 = f_order_hash_1 << delta_f_order;
|
||||
|
||||
(f_hash - f_hash_0) as f32 / (f_hash_1 - f_hash_0) as f32
|
||||
}
|
||||
DataproductType::Cube => {
|
||||
let channel_idx = (((self.get_freq().0
|
||||
- self.get_config().em_min.unwrap_abort().0)
|
||||
/ (self.get_config().em_max.unwrap_abort().0
|
||||
- self.get_config().em_min.unwrap_abort().0))
|
||||
* (self.get_config().get_cube_depth().unwrap_abort() as f64))
|
||||
- em_min.unwrap_abort().0)
|
||||
/ (em_max.unwrap_abort().0
|
||||
- em_min.unwrap_abort().0))
|
||||
* (cube_depth.unwrap_abort() as f64))
|
||||
as u64;
|
||||
let tile_depth = 32;
|
||||
|
||||
@@ -693,7 +707,7 @@ impl HiPS3D {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let uv_1 = TileUVW::new(&cell.hpx, &Some(texture.cell.hpx), slice_position);
|
||||
let uv_1 = TileUVW::new(&cell.hpx, &Some(texture_cell.hpx), slice_position);
|
||||
let d01e = uv_1[TileCorner::BottomRight].x - uv_1[TileCorner::BottomLeft].x;
|
||||
let d02e = uv_1[TileCorner::TopLeft].y - uv_1[TileCorner::BottomLeft].y;
|
||||
|
||||
@@ -732,12 +746,12 @@ impl HiPS3D {
|
||||
|
||||
// GL TRIANGLES
|
||||
self.idx_vertices.extend([
|
||||
idx + off_indices,
|
||||
idx + 1 + off_indices,
|
||||
idx + 3 + off_indices,
|
||||
idx + 2 + off_indices,
|
||||
idx + 1 + off_indices,
|
||||
idx + off_indices,
|
||||
idx + 3 + off_indices,
|
||||
idx + 2 + off_indices,
|
||||
]);
|
||||
// GL LINES
|
||||
/*self.idx_vertices.extend([
|
||||
@@ -764,6 +778,9 @@ impl HiPS3D {
|
||||
// Replace options with an arbitrary vertex
|
||||
let position_iter = pos.into_iter().flatten();
|
||||
self.position.extend(position_iter);
|
||||
|
||||
self.cells.push(texture_cell);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,10 +78,11 @@ impl HpxFreqData {
|
||||
let z = z - trim.2;
|
||||
|
||||
let data_raw_bytes = &raw_bytes[data_byte_offset.clone()];
|
||||
|
||||
let pixel_bytes_off = (x + y * naxis.0 + z * (naxis.0 * naxis.1)) as usize;
|
||||
|
||||
let bytes_per_pixel = bitpix.byte_size();
|
||||
|
||||
let pixel_bytes_off =
|
||||
bytes_per_pixel * (x + y * naxis.0 + z * (naxis.0 * naxis.1)) as usize;
|
||||
|
||||
let p = &data_raw_bytes[pixel_bytes_off..(pixel_bytes_off + bytes_per_pixel)];
|
||||
let pixel = match bitpix {
|
||||
Bitpix::U8 => Pixel::U8(p[0]),
|
||||
@@ -92,7 +93,7 @@ impl HpxFreqData {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Some(pixel.to_f32() * *bscale + *bzero)
|
||||
Some(pixel.to_f32() * (*bscale) + (*bzero))
|
||||
}
|
||||
}
|
||||
HpxFreqData::Jpeg { data, size } => {
|
||||
@@ -191,14 +192,7 @@ impl HpxFreqTex {
|
||||
let start_time = None;
|
||||
|
||||
let texture = match pixel_format {
|
||||
PixelType::RGBA8U => Texture3D::create_empty::<R8U>(
|
||||
gl,
|
||||
tile_size as i32,
|
||||
tile_size as i32,
|
||||
num_slices as i32,
|
||||
TEX_PARAMS,
|
||||
),
|
||||
PixelType::RGB8U => Texture3D::create_empty::<R8U>(
|
||||
PixelType::RGBA8U | PixelType::RGB8U | PixelType::R8U => Texture3D::create_empty::<R8U>(
|
||||
gl,
|
||||
tile_size as i32,
|
||||
tile_size as i32,
|
||||
@@ -212,13 +206,6 @@ impl HpxFreqTex {
|
||||
num_slices as i32,
|
||||
TEX_PARAMS,
|
||||
),
|
||||
PixelType::R8U => Texture3D::create_empty::<R8U>(
|
||||
gl,
|
||||
tile_size as i32,
|
||||
tile_size as i32,
|
||||
num_slices as i32,
|
||||
TEX_PARAMS,
|
||||
),
|
||||
PixelType::R16I => Texture3D::create_empty::<R16I>(
|
||||
gl,
|
||||
tile_size as i32,
|
||||
@@ -233,8 +220,6 @@ impl HpxFreqTex {
|
||||
num_slices as i32,
|
||||
TEX_PARAMS,
|
||||
),
|
||||
// No color cubes
|
||||
_ => unreachable!(),
|
||||
}?;
|
||||
|
||||
let data = None;
|
||||
|
||||
@@ -53,15 +53,6 @@ float one_minus_z_neg(vec3 p) {
|
||||
int ij2z(int i, int j) {
|
||||
int i4 = i | (j << 2);
|
||||
|
||||
/*int j1 = (i1 ^ (i1 >> 8)) & 0x0000FF00;
|
||||
int i2 = i1 ^ j1 ^ (j1 << 8);
|
||||
|
||||
int j2 = (i2 ^ (i2 >> 4)) & 0x00F000F0;
|
||||
int i3 = i2 ^ j2 ^ (j2 << 4);
|
||||
|
||||
int j3 = (i3 ^ (i3 >> 2)) & 0x0C0C0C0C;
|
||||
int i4 = i3 ^ j3 ^ (j3 << 2);*/
|
||||
|
||||
int j4 = (i4 ^ (i4 >> 1)) & 0x22222222;
|
||||
int i5 = i4 ^ j4 ^ (j4 << 1);
|
||||
|
||||
|
||||
@@ -26,27 +26,27 @@ uniform int u_proj;
|
||||
|
||||
vec2 proj(vec3 p) {
|
||||
if (u_proj == 0) {
|
||||
/* TAN, Gnomonic projection */
|
||||
// TAN, Gnomonic projection
|
||||
return w2c_tan(p);
|
||||
} else if (u_proj == 1) {
|
||||
/* STG, Stereographic projection */
|
||||
// STG, Stereographic projection
|
||||
return w2c_stg(p);
|
||||
} else if (u_proj == 2) {
|
||||
/* SIN, Orthographic */
|
||||
// SIN, Orthographic
|
||||
return w2c_sin(p);
|
||||
} else if (u_proj == 3) {
|
||||
/* ZEA, Equal-area */
|
||||
// ZEA, Equal-area
|
||||
return w2c_zea(p);
|
||||
} else if (u_proj == 4) {
|
||||
// Pseudo-cylindrical projections
|
||||
/* AIT, Aitoff */
|
||||
// AIT, Aitoff
|
||||
return w2c_ait(p);
|
||||
} else if (u_proj == 5) {
|
||||
// MOL, Mollweide */
|
||||
// MOL, Mollweide
|
||||
return w2c_mol(p);
|
||||
} else {
|
||||
// Cylindrical projections
|
||||
// MER, Mercator */
|
||||
// MER, Mercator
|
||||
return w2c_mer(p);
|
||||
}
|
||||
}
|
||||
@@ -494,6 +494,63 @@ export let HiPS = (function () {
|
||||
|
||||
// dataproduct type
|
||||
self.dataproductType = properties && properties.dataproduct_type;
|
||||
if (self.dataproductType === "spectral-cube") {
|
||||
if (!self.spectraUpdatedCallback) {
|
||||
let createPlotCanvas = (id = "plot", width = 600, height = 300) => {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.id = id;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
canvas.style.pointerEvents = "none";
|
||||
canvas.style.position = "absolute";
|
||||
|
||||
self.view.aladinDiv.appendChild(canvas); // or insert it into a specific container
|
||||
return canvas;
|
||||
};
|
||||
|
||||
const canvas = createPlotCanvas();
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
function drawPlot(ctx, data) {
|
||||
const width = ctx.canvas.width;
|
||||
const height = ctx.canvas.height;
|
||||
const len = data.length;
|
||||
|
||||
// Clear previous drawing
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
// Find min and max for scaling
|
||||
const minY = Math.min(...data);
|
||||
const maxY = Math.max(...data);
|
||||
|
||||
const scaleX = width / (len - 1);
|
||||
const scaleY = (maxY - minY === 0) ? 1 : height / (maxY - minY);
|
||||
|
||||
ctx.beginPath();
|
||||
for (let i = 0; i < len; i++) {
|
||||
const x = i * scaleX;
|
||||
const y = height - (data[i] - minY) * scaleY;
|
||||
if (i === 0) {
|
||||
ctx.moveTo(x, y);
|
||||
} else {
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
ctx.strokeStyle = "blue";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
self.spectraUpdatedCallback = (event) => {
|
||||
const data = event.detail;
|
||||
drawPlot(ctx, data);
|
||||
};
|
||||
} else {
|
||||
window.removeEventListener("spectra", self.spectraUpdatedCallback);
|
||||
}
|
||||
|
||||
window.addEventListener("spectra", self.spectraUpdatedCallback);
|
||||
}
|
||||
|
||||
// Tile size
|
||||
self.tileSize =
|
||||
|
||||
@@ -71,7 +71,7 @@ export class ALEvent {
|
||||
static SAMP_CONNECTED = new ALEvent("AL:samp.connected");
|
||||
static SAMP_DISCONNECTED = new ALEvent("AL:samp.disconnected");
|
||||
|
||||
static CANVAS_EVENT = new ALEvent("AL:Event");
|
||||
static CANVAS_EVENT = new ALEvent("AL:Event");
|
||||
|
||||
static RETICLE_CHANGED = new ALEvent("AL:Reticle.changed")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user