Compare commits

...

94 Commits

Author SHA1 Message Date
Matthieu Baumann
28869645cd fix opacity restore in UI, and screen size shrinking 2025-12-12 11:27:03 +01:00
Matthieu Baumann
da8eb6f76e feat: swap layer orders 2025-12-08 17:46:34 +01:00
Matthieu Baumann
0e3a359108 wip base layer 2025-12-08 14:33:31 +01:00
Matthieu Baumann
af89535a91 first commit tree 2025-12-08 14:33:29 +01:00
Matthieu Baumann
8d244596ba 3.7.3-beta 2025-11-05 15:16:36 +01:00
Matthieu Baumann
2a23e83c13 update fitsrs version 2025-10-20 14:41:30 +02:00
Matthieu Baumann
7b8272795d cargo clippy 2025-10-20 10:12:58 +02:00
Matthieu Baumann
4d8b4bfb21 Many fixes
* fix: HiPS3D probing spectra in galactic frame
* ui: new more option in the HiPS selector allowing to change the base layer with an arbitrary HiPS from the UI
* fits: support tile compressed image in bintable extensions (SRCNet feature: https://jira.skatelescope.org/browse/MAN-559)
2025-10-20 09:59:49 +02:00
Matthieu Baumann
ebf8845e83 fix opacity param for PolyLines #316 2025-09-29 15:35:02 +02:00
Matthieu Baumann
f863ac902c fix 0.5 pixel offset when plotting FITS image files 2025-09-29 14:29:38 +02:00
Matthieu Baumann
75123e6bc8 first commit 2025-09-25 10:37:40 +02:00
Matthieu Baumann
d5d7d2a650 3.7.2 2025-09-25 10:36:14 +02:00
Matthieu Baumann
d22c25ea8a add npm deploy routine in package json 2025-09-24 17:13:24 +02:00
Matthieu Baumann
acef664b45 cargo clippy 2025-09-24 17:08:32 +02:00
Matthieu Baumann
9e8db0379b cargo clippy & fmt 2025-09-24 16:59:31 +02:00
Matthieu Baumann
032bb57517 rotation improve perf and numerical instabilities 2025-09-23 17:20:16 +02:00
Matthieu Baumann
f0fc39d2c8 drag outside the projection domain 2025-09-22 15:38:58 +02:00
Matthieu Baumann
2df32cb643 change u-strasbg to cds.unistra urls + enhance/simplify inertia effect 2025-09-22 11:11:12 +02:00
Matthieu Baumann
904d449006 fix: setCuts with imageFormat, Add more HiPS search 2025-09-19 13:59:25 +02:00
Matthieu Baumann
2594aff1b6 fix: crossOrigin set for HtmlImageElement from requestCredentials 2025-09-17 17:02:40 +02:00
Matthieu Baumann
547c5422d4 add minified shaders 2025-09-17 14:26:45 +02:00
Matthieu Baumann
9bcc93877b change changelog 2025-09-15 14:11:52 +02:00
Matthieu Baumann
3f6f247735 add changelog 3.7.0 2025-09-15 11:58:51 +02:00
Matthieu Baumann
c6c7ad44c9 add a prod routine only for internal production deployment on our server. npm run serve should behave like before 2025-09-12 18:04:08 +02:00
Matthieu Baumann
cdc1733c4f Add label to checkbox UI filter enabler 2025-09-12 11:05:32 +02:00
Matthieu Baumann
6e40dbbfc1 update testing snapshots 2025-09-11 17:52:27 +02:00
Matthieu Baumann
e03b16119b several fixes: panic when delaying resources treatments + ICRS sexa 2025-09-11 17:49:05 +02:00
Matthieu Baumann
e3162426be fix: restore polyselect 2025-09-11 15:35:39 +02:00
Matthieu Baumann
5a285dabed cargo clippy 2025-09-11 15:00:53 +02:00
Matthieu Baumann
2d04730623 fix: read pixel for jpg (works like rgba) and fits (1px offset fix + switch bytes order) 2025-09-11 14:31:45 +02:00
bmatthieu3
390c9096d7 NED tap access 2025-09-10 11:52:15 +02:00
Thomas Boch
0e9998a7fc Merge pull request #327 from cds-astro/customize-share-url-function
Customize share URL function
2025-09-10 11:22:42 +02:00
Thomas Boch
22cfc972e2 Add customizeShareURLFunction method and associated example, solves issue #273 2025-09-10 11:19:06 +02:00
Matthieu Baumann
a1386c2a13 fmt 2025-09-09 17:09:55 +02:00
Matthieu Baumann
7bdf0cc912 run clippy + fmt 2025-09-09 17:09:55 +02:00
Matthieu Baumann
634d652c54 final commit 2025-09-09 17:09:55 +02:00
Matthieu Baumann
a10699c271 fix spectral display bug, add a new display/hide icon for the spectra displayer 2025-09-09 17:09:55 +02:00
Matthieu Baumann
52b0b6fc4f wip 2025-09-09 17:09:55 +02:00
Matthieu Baumann
9739b87439 wip commit: not finished 2025-09-09 17:09:55 +02:00
Matthieu Baumann
9db098ce6a add some button to the spectral tool 2025-09-09 17:09:55 +02:00
Matthieu Baumann
b35f79ad0a add button to change unit 2025-09-09 17:09:55 +02:00
Matthieu Baumann
6cab5f9efd compute max/min values per order 2025-09-09 17:09:55 +02:00
Matthieu Baumann
556388e260 refac SpectraDisplayer 2025-09-09 17:09:55 +02:00
Matthieu Baumann
e186713aa1 fix fits f64 tiles hips 2025-09-09 17:09:55 +02:00
Matthieu Baumann
97995834bf widget: add spectral line on mouse hovering on the spectra 2025-09-09 17:09:55 +02:00
Matthieu Baumann
11a87901e1 spectra displayer tool: event handling 2025-09-09 17:09:55 +02:00
Matthieu Baumann
5948e6063b wip spectral tool 2025-09-09 17:09:55 +02:00
bmatthieu3
a91b2154a1 wip: optimisation retrieving tiles from blob + imagebitmap when supported by the browser 2025-09-09 17:09:55 +02:00
bmatthieu3
c6c17f0ba2 cargo clippy 2025-09-09 17:09:55 +02:00
bmatthieu3
6879c22b02 fix warnings 2025-09-09 17:09:55 +02:00
bmatthieu3
48802d4494 fix bug to retrieve the correct spectra under the mouse 2025-09-09 17:09:55 +02:00
Matthieu Baumann
5c6405bf8b WIP: spectra plot 2025-09-09 17:09:55 +02:00
Matthieu Baumann
e4689cf674 get spectra under the cursor 2025-09-09 17:09:55 +02:00
Matthieu Baumann
a4fad91abf increase cubic tile buffer size 2025-09-09 17:09:55 +02:00
Matthieu Baumann
0462a451b3 keep the true data of the cubic tiles. This will be needed to get the spectra at a specific sky location 2025-09-09 17:09:55 +02:00
Matthieu Baumann
1dca5b1845 fix fits parsing and jpg/png cubic tiles 2025-09-09 17:09:55 +02:00
Matthieu Baumann
d73cc1c66d code is compiling, HiPSCube is soon to work again. HiPS3D not yet tested/debugged 2025-09-09 17:09:55 +02:00
Matthieu Baumann
560696a6e1 DO NOT COMPILE. first very basic impl. Need testing. 2025-09-09 17:09:55 +02:00
Matthieu Baumann
258bc47d47 refac tile binary heap 2025-09-09 17:09:55 +02:00
Matthieu Baumann
729f86066e refac querying of HiPS3D tiles and HiPSCube tiles + fix rendering fits 2025-09-09 17:09:55 +02:00
Matthieu Baumann
86002f3e89 fix the HiPSCube work again 2025-09-09 17:09:55 +02:00
Matthieu Baumann
9ea92b3bff refact querying ressources (moc, tiles, allsky map) 2025-09-09 17:09:55 +02:00
Matthieu Baumann
f9722a69ff first HiPS3D commit 2025-09-09 17:09:55 +02:00
Matthieu Baumann
c1dcb4b7e6 fix footprints 2025-09-05 14:53:35 +02:00
Matthieu Baumann
a1a8142b32 API doc: share url + send only requestedOptions 2025-09-05 14:49:48 +02:00
Matthieu Baumann
4294976105 final commit 2025-09-04 14:12:51 +02:00
Matthieu Baumann
12947ba9c9 show sources of obscore table 2025-09-04 14:12:51 +02:00
Matthieu Baumann
dd7ae10e0d new onlyFootprint catalog option. If set to false, pointed sources are also shown with the footprint 2025-09-04 14:12:51 +02:00
Matthieu Baumann
610d22fd23 ellipse intersectBBox very coarse approx 2025-09-04 14:12:51 +02:00
Matthieu Baumann
9558bae25c refac: compose Footprint inside Source and not the opposite 2025-09-04 14:12:51 +02:00
Matthieu Baumann
dd00d0db27 wip commit 2025-09-04 14:12:51 +02:00
Matthieu Baumann
1b10c59dca Allow customizing size and color source from its data content
sourceSize and color properties when creating a new Catalog now accepts a function
allowing to set different sizes/colors in function of the source catalog row
2025-09-02 15:06:08 +02:00
Matthieu Baumann
f9b23d286c fine tune the default linewidth values 2025-08-29 16:07:11 +02:00
Matthieu Baumann
809a53e694 run clippy and fmt 2025-08-29 16:07:11 +02:00
Matthieu Baumann
d6583e47ef update default thickness to match with previous lineWidth user were interested of 2025-08-29 16:07:11 +02:00
bmatthieu3
0c9c315f69 anti aliasing on lines (grid + mocs) drawn by the gpu. Change moc linewidth from 3 -> 2 and twick the coogrid ang step/opacity/linewidth 2025-08-29 16:07:11 +02:00
Matthieu Baumann
3454083449 fix Circle::intersectBbox 2025-08-29 11:57:15 +02:00
Erik Mellegard
311fa84919 Fix Circle intersectBBox 2025-08-29 11:57:15 +02:00
Matthieu Baumann
3d445e4f6f fix 26 channel color offset 2025-08-28 17:52:45 +02:00
Matthieu Baumann
950d0c693e Fix wrong coordinates frame 2025-08-28 16:17:30 +02:00
Matthieu Baumann
895aa169b4 fix clipping polyline containing vertices that fails to be projected (i.e. for SIN, TAN proj) 2025-07-04 14:00:55 +02:00
Matthieu Baumann
9201aff1ce Update README.md
Add mention of used rust core libraries
2025-07-01 10:07:19 +02:00
Matthieu Baumann
9f70766c75 use fitsrs 0.3.4 fixed version 2025-06-27 17:26:21 +02:00
Matthieu Baumann
3726ca028c clippy + fmt 2025-06-27 17:26:21 +02:00
Matthieu Baumann
347e09ff70 fix big FITS orientation. new feat: subdivide big PNG/JPG image as well 2025-06-27 17:26:21 +02:00
Matthieu Baumann
e1f85bab97 Update to the new version of fitsrs
* Async code is replaced by sync one. It does not take that much of time to convert the js blob to wasm memory for 1 or 2 GB fits files and the code is much more simplier to write
* GLSL/rust refac has been done to only call fitsrs from only one place in the code
* Raw fits data unit is directly given to the GPU. Then the GPU perform the big to little endian conversion on the fly
* impl GZIP fits support #241
* fix #293
2025-06-27 17:26:21 +02:00
Matthieu Baumann
5d6e113c19 fix filterFn on footprints in catalogs: #301 2025-05-27 14:53:14 +02:00
Matthieu Baumann
2f9a1f297d impl #286: add eRosita to default HiPSList 2025-05-25 00:35:45 +02:00
Matthieu Baumann
0d2c0889a1 update playwright snapshots 2025-05-23 18:48:11 +02:00
Matthieu Baumann
0cbd0c9f23 fix test ci fmt 2025-05-23 18:48:11 +02:00
Matthieu Baumann
16a1e808a2 cargo fmt 2025-05-23 18:48:11 +02:00
Matthieu Baumann
7b718eae96 cargo clippy 2025-05-23 18:48:11 +02:00
Matthieu Baumann
221132ee1a remove warnings 2025-05-23 18:48:11 +02:00
Matthieu Baumann
f796a4c036 fix allsky retrieval and simplify hips rendering 2025-05-23 18:48:11 +02:00
252 changed files with 17880 additions and 7428 deletions

View File

@@ -32,7 +32,7 @@ jobs:
npm install
- name: "Build Aladin Lite"
run: |
npm run build
npm run build:npm
- name: "Publish Aladin Lite to npm"
run: |
npm publish

View File

@@ -25,6 +25,10 @@ jobs:
- name: "Install wasm-pack"
run: |
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -y
- name: "Run cargo clippy"
run: cd src/core && cargo clippy --all-targets --features=webgl2 -- -D warnings
- name: Format check
run: cd src/core && cargo fmt --all -- --check
- name: "Install dependencies"
run: |
npm install

8
.gitignore vendored
View File

@@ -15,9 +15,11 @@ package-lock.json
src/core/Cargo.lock
src/core/target/
# this rust file is generated when compiling the code, so it is not
# useful to put it on git
src/core/src/shaders.rs
# the tmp glsl files used when minifying the shaders into the wasm (build.rs)
src/glsl/webgl2/**/*.min
src/glsl/webgl2/**/*.tmp
package/
## python related
# python environment

View File

@@ -4,11 +4,33 @@
### What's Changed
* [perf] perform CPU computations with Vec3 and Matrix3 and not 4 dimensions matrices/vectors
* [feat] lockNorthUp Aladin object new option locking the north pole up to the view center
## Released
### 3.7.0-beta
#### What's Changed
* [feat] flip longitude axis global method on Aladin by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/245>
* [feat] add rotation event by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/283>
* [docs] just fixing typo in image's doc by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/284>
* [docs] change to an image with correct astrometry in example by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/290>
* [docs] clarify use of precision in Coo by [@alexgoff][alexgoff] in <https://github.com/cds-astro/aladin-lite/pull/294>
* [feat] allow setting HiPS CORS and credential options by [@pmatsson][pmatsson] in <https://github.com/cds-astro/aladin-lite/pull/281>
* [feat] color picker and read pixel(s) API methods by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/280>
* [fix] chandra hips display and akari by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/306>
* [enhancement] update to the new version of fitsrs by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/310>
* [fix] 26 channel color offset by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/320>
* [fix] Circle intersectsBBox by [@emellega][emellega] in <https://github.com/cds-astro/aladin-lite/pull/309>
* [feat] anti aliasing on lines plotted by the GPU by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/239>
* [feat] source custom color and size from its data content by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/321>
* [feat] catalog new select method by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/322>
* [feat] HiPS 3D impl by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/314>
* [feat] customize share URL function by [@tboch][tboch] in <https://github.com/cds-astro/aladin-lite/pull/327>
* enhancement: use TAP entry point to query NED by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/257>
* [perf] refac geometrical computations using Vec3/Mat3 instead of Vec4/Mat4 [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/276>
* [fix] distortion at poles by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/247>
* [feat] new aladin option `lockNorthUp` to keep north pole up [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/272>
### 3.6.3
#### What's Changed

View File

@@ -1,4 +1,4 @@
# [Aladin Lite](https://aladin.u-strasbg.fr/AladinLite)
# [Aladin Lite](https://aladin.cds.unistra.fr/AladinLite)
**An astronomical HiPS visualizer in the browser** <img src="aladin-logo.png" alt="Aladin Lite logo" width="220">
@@ -8,14 +8,20 @@ See [A&A 578, A114 (2015)](https://arxiv.org/abs/1505.02291) and [IVOA HiPS Reco
Aladin Lite is built to be easily embeddable in any web page. It powers astronomical portals like [ESASky](https://sky.esa.int/), [ESO Science Archive portal](http://archive.eso.org/scienceportal/) and [ALMA Portal](https://almascience.eso.org/asax/).
More details on [Aladin Lite documentation page](http://aladin.u-strasbg.fr/AladinLite/doc/).
More details on [Aladin Lite documentation page](http://aladin.cds.unistra.fr/AladinLite/doc/).
A new [API technical documentation](https://cds-astro.github.io/aladin-lite/) is now available.
[![Run tests](https://github.com/cds-astro/aladin-lite/actions/workflows/test.yml/badge.svg)](https://github.com/cds-astro/aladin-lite/actions/workflows/test.yml)
[![API Documentation](https://img.shields.io/badge/API-documentation-blue.svg)](https://cds-astro.github.io/aladin-lite)
[![Release page](https://img.shields.io/badge/Release-download-yellow.svg)](https://aladin.cds.unistra.fr/AladinLite/doc/release/)
Aladin Lite is available [at this link](https://aladin.u-strasbg.fr/AladinLite).
Try Aladin Lite [here](https://aladin.cds.unistra.fr/AladinLite).
Aladin Lite is made possible thanks to pure Rust core libraries:
* [cdshealpix](https://github.com/cds-astro/cds-healpix-rust) - for HEALPix projection and unprojection to/from sky coordinates
* [mapproj](https://github.com/cds-astro/cds-mapproj-rust) - for computing (un)projections described by a WCS
* [fitsrs](https://github.com/cds-astro/fitsrs) - for reading and parsing FITS images
* [moc](https://github.com/cds-astro/cds-moc-rust) - for parsing, manipulating, and serializing multi-order HEALPix coverage maps
## Running & editable JS examples
@@ -102,14 +108,15 @@ Aladin Lite can be imported with:
* [X] FITS images support
* [X] WCS parsing, displaying an (JPEG/PNG) image in aladin lite view
* [X] Display customized shapes (e.g. proper motions) from astronomical catalog data
* [X] AVM tags parsing support
* [X] AVM tags parsing support inside JPEG
* [X] Easy sharing of current « view »
* [ ] All VOTable serializations
* [ ] FITS tables
* [X] Creating HiPS instance from an URL
* [X] Local HiPS loading
* [X] Multiple mirrors handling for HiPS tile retrival
* [ ] HiPS cube
* [X] HiPS cube
* [ ] HiPS3D
## Licence

4
assets/icons/folder.svg Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 1H5L8 3H13V5H3.7457L2.03141 11H4.11144L5.2543 7H16L14 14H0V1Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 322 B

15
assets/icons/swap.svg Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<title>swap-vertical-circle</title>
<g id="Layer_2" data-name="Layer 2">
<g id="invisible_box" data-name="invisible box">
<rect width="48" height="48" fill="none"/>
</g>
<g id="icons_Q2" data-name="icons Q2">
<path d="M19.4,36.4l5-4.9a2.1,2.1,0,0,0,.2-2.7,1.9,1.9,0,0,0-3-.2L20,30.2V15a2,2,0,0,0-4,0V30.2l-1.6-1.6a1.9,1.9,0,0,0-3,.2,2.1,2.1,0,0,0,.2,2.7l5,4.9A1.9,1.9,0,0,0,19.4,36.4Z"/>
<path d="M32,33V17.8l1.6,1.6a1.9,1.9,0,0,0,3-.2,2.1,2.1,0,0,0-.2-2.7l-5-4.9a1.9,1.9,0,0,0-2.8,0l-5,4.9a2.1,2.1,0,0,0-.2,2.7,1.9,1.9,0,0,0,3,.2L28,17.8V33a2,2,0,0,0,4,0Z"/>
<path d="M24,42A18,18,0,1,1,42,24,18.1,18.1,0,0,1,24,42m0,4A22,22,0,1,0,2,24,21.9,21.9,0,0,0,24,46Z"/>
</g>
</g>

After

Width:  |  Height:  |  Size: 948 B

View File

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

View File

@@ -14,7 +14,7 @@
aladin = A.aladin('#aladin-lite-div', {showSettingsControl: true, survey: "P/PanSTARRS/DR1/color-z-zg-g", showReticle: false, projection: "AIT", cooFrame: 'icrs', target: "stephan's quintet", fov: startFov, showGotoControl: false, showFrame: false, fullScreen: true, showLayersControl: 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"});
const nircamJWST = aladin.createImageSurvey('Nircam', "Stephans Quintet NIRCam+MIRI", "https://alasky.cds.unistra.fr/JWST/CDS_P_JWST_Stephans-Quintet_NIRCam-MIRI/", null, null, {imgFormat: 'png', colormap: "viridis"});
aladin.setOverlayImageLayer("CFHT", "CFHT");
aladin.setOverlayImageLayer("Nircam", "Nircam");
@@ -30,7 +30,7 @@
fov *= 0.997;
rotation += 0.07;
aladin.setViewCenter2NorthPoleAngle(rotation)
aladin.setRotation(rotation)
aladin.setFoV(fov);
if (fov < 3 && fov > 0.5) {

View File

@@ -7,7 +7,7 @@
<script type="module">
import A from '../src/js/A.js';
A.init.then(() => {
let aladin = A.aladin('#aladin-lite-div', {fov: 70,projection: "AIT"});
let aladin = A.aladin('#aladin-lite-div', {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);

View File

@@ -26,8 +26,17 @@
limit: 1000,
//orderBy: 'nb_ref',
onClick: 'showTable',
color: 'yellow',
hoverColor: 'blue',
onlyFootprints: false,
color: (s) => {
let coo = A.coo();
coo.parse(s.data['RAJ2000'] + ' ' + s.data['DEJ2000'])
let a = (0.1 * Math.pow(10, +s.data.logD25)) / 60;
let b = (1.0 / Math.pow(10, +s.data.logR25)) * a
return `rgb(${s.data["logR25"]*255.0}, ${s.data["logR25"]*255.0}, 255)`
},
hoverColor: 'red',
shape: (s) => {
let coo = A.coo();
coo.parse(s.data['RAJ2000'] + ' ' + s.data['DEJ2000'])

View File

@@ -31,15 +31,21 @@
hoverColor: 'yellow',
selectionColor: 'white',
// Footprint associated to sources
shape: (s) => {
color: (s) => {
// discard drawing a vector for big pm
let totalPmSquared = s.data.pmra*s.data.pmra + s.data.pmdec*s.data.pmdec;
if (totalPmSquared > 6) {
return;
}
let color = rainbowColorMap((totalPmSquared - 2.5) / 2)
return rainbowColorMap((totalPmSquared - 2.5) / 2)
},
shape: (s) => {
// discard drawing a vector for big pm
let totalPmSquared = s.data.pmra*s.data.pmra + s.data.pmdec*s.data.pmdec;
if (totalPmSquared > 6) {
return;
}
// Compute the mean of pm over the catalog sources
if (!pmraMean || !pmdecMean) {
pmraMean = 0, pmdecMean = 0;
@@ -62,13 +68,24 @@
s.dec,
s.ra + dra,
s.dec + ddec,
{color}
)
}
},
() => {
aladin.addCatalog(pmCat);
pmCat.select((s) => {
let totalPmSquared = s.data.pmra*s.data.pmra + s.data.pmdec*s.data.pmdec;
if (totalPmSquared > 6) {
return false;
}
return totalPmSquared < 3.0;
});
});
aladin.addCatalog(pmCat);
});
function rainbowColorMap(value) {
// Ensure value is within range [0, 1]
value = Math.max(0, Math.min(1, value));

View File

@@ -37,7 +37,10 @@
colorPicker.value = cat.color;
colorPicker.addEventListener('input', function (e) {
// Change the color of the catalog
cat.updateShape({color: this.value});
console.log(this.value)
cat.updateShape({color: () => {
return '#00ff00'
}});
})
// Define the box

View File

@@ -0,0 +1,22 @@
<!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">
</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, survey: 'https://alasky.cds.unistra.fr/DSS/DSSColor/', fov: 1.0, target: 'M 20', showContextMenu: true});
// customize share URL function
aladin.customizeShareURLFunction(() => {return 'https://sky.esa.int/esasky/?target=' + aladin.getRaDec()[0] + '%20' + aladin.getRaDec()[1] + '&fov=' + aladin.getFoV()[0]})
});
</script>
</body>
</html>

View File

@@ -15,7 +15,7 @@
aladin.setBaseImageLayer(dss);
dss.setCuts(2, 10000);
dss.setCuts(2, 100, 'jpeg');
});

View File

@@ -11,11 +11,12 @@
import A from '../src/js/A.js';
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {cooFrame: "icrs", log: false, backgroundColor: 'red'});
aladin = A.aladin('#aladin-lite-div', {cooFrame: "icrs", log: false, backgroundColor: 'rgba(0, 0, 0, 255)'});
aladin.displayFITS(
//'https://fits.gsfc.nasa.gov/samples/FOCx38i0101t_c0f.fits', // url of the fits file
'data/fits/panstarrs-g-m61.fits',
//'https://almascience.eso.org/dataPortal/member.uid___A001_X88f_X297.calibrated_final_cont_Sgr_B1off.pbcor.fits',
{
name: 'm61',
colormap: 'viridis'

View File

@@ -39,7 +39,7 @@ Image Opacity: <br/> <input id="slider" type="range" value=1 min=0 max=1 step=0.
//let fits = aladin.displayFITS('http://goldmine.mib.infn.it/data//B/fits/A04_VC1316_ooooog.fits', 'overlay');
let jpg = aladin.displayJPG(
// the JPG to transform to HiPS
'https://noirlab.edu/public/media/archives/images/large/noirlab1912a.jpg',
'https://owncloud.tuebingen.mpg.de/index.php/s/sdxfNgcEaaXoBp7/download/nightskycam3_2025_08_07_05_17_30_healpix1024_red.fits',
// no options
{
transparency: 1.0,

View File

@@ -12,7 +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: '#ee2345', lineWidth: 3, lineDash: [2, 2]});
var overlay = A.graphicOverlay({color: 'purple', lineWidth: 3, lineDash: [2, 2]});
aladin.addOverlay(overlay);
overlay.addFootprints([
A.polygon([[83.64287, 22.01713], [83.59872, 22.01692], [83.59852, 21.97629], [83.64295, 21.97629]], {hoverColor: 'green'}),

62
examples/al-hips-3D.html Normal file
View File

@@ -0,0 +1,62 @@
<!doctype html>
<html>
<head>
</head>
<body>
<div id="aladin-lite-div" style="width: 768px; height: 512px"></div>
<script>let aladin; let hips;</script>
<script type="module">
import A from '../src/js/A.js';
A.init.then(() => {
aladin = A.aladin(
'#aladin-lite-div',
{
showSimbadPointerControl: true,
projection: 'AIT', // set a projection
fov: 8.0, // initial field of view in degrees
target: '10.6875598 +41.1402170', // initial target
cooFrame: 'icrs', // set galactic frame
reticleColor: '#ff89ff', // change reticle color
showContextMenu: true,
showFrame: true,
showZoomControl:true,
showSettingsControl:true,
fullScreen: true,
samp: true,
}
);
hips = aladin.newImageSurvey("https://alasky.cds.unistra.fr/HIPS3D/LGLBSHI-test-compression/", {
successCallback: (hips) => {
//hips.setFrequency({value: 6.374279333565797E-7, unit: "m"}) // GALFA
}
});
// compressed https://alasky.cds.unistra.fr/test-compression-cubes/DHIGLS/
//hips = aladin.newImageSurvey("http://alasky.cds.unistra.fr/DHIGLS");
//hips = aladin.newImageSurvey("https://alasky.cds.unistra.fr/MUSE3D");
// http://alasky.cds.unistra.fr/LGLBSHI
aladin.setImageLayer(hips)
//hips.setFrequency({value: emMin + delta * i, unit: "m"})
//hips.setFrequency({value: 6.374279333565797E-7, unit: "m"}) // MUSE
//hips.setFrequency({value: 0.21101690259115785, unit: "m"}) // DGHILG
/*let id;
aladin.on("zoomChanged", () => {
if (id)
clearTimeout(id);
id = setTimeout(() => {
console.log("wheel stopped, new cone search here")
}, 500);
})*/
});
</script>
<style>
.aladin-cat-browser-box {
width: 600px;
}
</style>
</body>
</html>

View File

@@ -30,9 +30,14 @@
}
);
hips = aladin.newImageSurvey("https://alasky.cds.unistra.fr/GALFAHI/GALFAHI-Narrow-DR2/");
hips = aladin.newImageSurvey("https://alasky.cds.unistra.fr/GALFAHI/GALFAHI-Narrow-DR2");
aladin.setImageLayer(hips)
setTimeout(() => {
hips.setSliceNumber(100)
}, 1000)
/*let id;
aladin.on("zoomChanged", () => {
if (id)

View File

@@ -11,7 +11,7 @@
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {survey: "data/hips/CDS_P_DSS2_color", target: "05 40 59.12 -02 27 04.1", fov: 2, log: false});
aladin = A.aladin('#aladin-lite-div', {survey: "./data/hips/CDS_P_DSS2_color", target: "05 40 59.12 -02 27 04.1", fov: 2, log: false});
});
</script>

View File

@@ -8,7 +8,7 @@
import A from '../src/js/A.js';
A.init.then(() => {
let aladin = A.aladin('#aladin-lite-div', {fov: 30, target: "280 +0", projection: "AIT", showShareControl:true, showSettingsControl: true, showContextMenu:true});
aladin.setOverlayImageLayer(A.image(
"https://www.virtualastronomy.org/files/avm_examples/spitzer/ssc2005-24a1.jpg",
{

View File

@@ -7,31 +7,55 @@
<script type="module">
import A from '../src/js/A.js';
A.init.then(() => {
let aladin = A.aladin('#aladin-lite-div', {fov: 30, survey: "CDS/P/GALEXGR6/AIS/FUV", target: "280 +0", projection: "AIT", showShareControl:true, showSettingsControl: true, showContextMenu:true});
let aladin = A.aladin('#aladin-lite-div', {fov: 30, target: "286.411023328 -37.3460065319", projection: "AIT", showShareControl:true, showSettingsControl: true, showContextMenu:true});
aladin.setOverlayImageLayer(A.image(
"https://nova.astrometry.net/image/25038473?filename=M61.jpg",
"data/img/m82.png",
{
name: "M61",
name: "M82",
wcs: {
NAXIS: 0, // Minimal header
CTYPE1: 'RA---TAN', // TAN (gnomic) projection
CTYPE2: 'DEC--TAN', // TAN (gnomic) projection
NAXIS: 2, // number of axes
NAXIS1: 3000, // image width
NAXIS2: 1918, // image height
CTYPE3: "RGB", // Tell Aladin this is RGB
WCSAXES: 2, // no comment
CTYPE1: "RA---TAN", // TAN (gnomic) projection + SIP distortions
CTYPE2: "DEC--TAN", // TAN (gnomic) projection + SIP distortions
EQUINOX: 2000.0, // Equatorial coordinates definition (yr)
LONPOLE: 180.0, // no comment
LATPOLE: 0.0, // no comment
CRVAL1: 185.445488837, // RA of reference point
CRVAL2: 4.47896032431, // DEC of reference point
CRPIX1: 588.995094299, // X reference pixel
CRPIX2: 308.307905197, // Y reference pixel
CUNIT1: 'deg', // X pixel scale units
CUNIT2: 'deg', // Y pixel scale units
CD1_1: -0.000223666022989, // Transformation matrix
CD1_2: -0.000296578064584, // no comment
CD2_1: -0.000296427555509, // no comment
CD2_2: 0.000223774308964, // no comment
NAXIS1: 1080, // Image width, in pixels.
NAXIS2: 705 // Image height, in pixels.
CRVAL1: 286.411023328, // RA of reference point
CRVAL2: -37.3460065319, // DEC of reference point
CRPIX1: 2264.1858724, // X reference pixel
CRPIX2: 583.14634196, // Y reference pixel
CUNIT1: "deg", // X pixel scale units
CUNIT2: "deg", // Y pixel scale units
CD1_1: -0.00284225200648, // Transformation matrix
CD1_2: 0.00145908284254, // no comment
CD2_1: -0.00145832184852, // no comment
CD2_2: -0.0028440175499, // no comment
A_ORDER: 2, // Polynomial order, axis 1
A_0_0: 0, A_0_1: 0, A_0_2: 1.97760279295e-7,
A_1_0: 0, A_1_1: 5.32298396638e-7,
A_2_0: -2.16045473726e-6,
B_ORDER: 2, // Polynomial order, axis 2
B_0_0: 0, B_0_1: 0, B_0_2: 3.97377848239e-7,
B_1_0: 0, B_1_1: -2.25823401545e-6,
B_2_0: -1.47800507759e-7,
AP_ORDER: 2, // Inv polynomial order, axis 1
AP_0_0: 0.00617616810622,
AP_0_1: -1.68315582233e-6,
AP_0_2: -1.96504899588e-7,
AP_1_0: -5.8320637913e-6,
AP_1_1: -5.26081207663e-7,
AP_2_0: 2.13760782681e-6,
BP_ORDER: 2, // Inv polynomial order, axis 2
BP_0_0: -0.000681183014773,
BP_0_1: -2.15389849968e-7,
BP_0_2: -3.94508397022e-7,
BP_1_0: 4.51837961352e-6,
BP_1_1: 2.24050293101e-6,
BP_2_0: 1.49195269783e-7
},
successCallback: (ra, dec, fov, image) => {
aladin.gotoRaDec(ra, dec);

View File

@@ -14,7 +14,7 @@
'#aladin-lite-div',
{
showSimbadPointerControl: true,
survey: 'P/allWISE/color', // set initial image survey
survey: 'https://skies.esac.esa.int/AKARI/color/', // set initial image survey
projection: 'AIT', // set a projection
fov: 360, // initial field of view in degrees
target: 'orion', // initial target

View File

@@ -12,7 +12,7 @@
import A from '../src/js/A.js';
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {inertia: false, target: '00 00 00 +07 00 00', fov: 130, survey: 'P/Mellinger/color', showContextMenu: true, fullScreen: true});
aladin = A.aladin('#aladin-lite-div', {inertia: true, target: '00 00 00 +07 00 00', fov: 130, survey: 'P/Mellinger/color', showContextMenu: true, fullScreen: true});
var moc11 = A.MOCFromURL('http://skies.esac.esa.int/HST/NICMOS/Moc.fits', {color: '#84f', lineWidth: 3}, (moc) => {
// moc is ready
console.log(moc.contains(205.9019247, +2.4492764));

View File

@@ -8,7 +8,7 @@
<script type="module">
import A from '../src/js/A.js';
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {projection: 'MOL', fullScreen: true, fov: 360, survey: ['P/DM/vizMine', 'P/HST/GOODS/color', 'P/MATLAS/g'], target: '0 0', showProjectionControl: false, showSettingsControl: false, showLayersControl: true, showCooGrid: false, showFrame: false, showCooLocation: false});
aladin = A.aladin('#aladin-lite-div', {projection: 'MOL', lockNorthUp: true, fullScreen: true, fov: 360, survey: ['P/DM/vizMine', 'P/HST/GOODS/color', 'P/MATLAS/g'], target: '0 0', showProjectionControl: false, showSettingsControl: false, showLayersControl: true, showCooGrid: true, showFrame: false, showCooLocation: false});
});
</script>

View File

@@ -17,6 +17,8 @@
});
aladin.addCatalog(A.catalogFromVizieR("B/assocdata/obscore", "0 +0", 20, {onClick: 'showTable', hoverColor: 'yellow', limit: 1000}))
aladin.addCatalog(A.catalogFromSKAORucio("0 +0", 70, {onClick: 'showTable', hoverColor: 'yellow', limit: 1000}))
});
</script>
</body>

View File

@@ -13,9 +13,15 @@
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {target: 'Gamma Cas', fov: 10, cooFrame: 'icrs'});
var overlay = A.graphicOverlay({lineWidth: 2});
var overlay = A.graphicOverlay({color: 'green', lineWidth: 2});
aladin.addOverlay(overlay);
overlay.add(A.polyline([ [2.29452158, 59.14978110], [10.12683778, 56.53733116], [14.1772154, 60.7167403], [21.45396446, 60.23528403], [28.59885697, 63.67010079] ], {color: 'green'}));
overlay.add(A.polyline([ [2.29452158, 59.14978110], [10.12683778, 56.53733116], [14.1772154, 60.7167403], [21.45396446, 60.23528403], [28.59885697, 63.67010079] ], {
opacity: 0.7
}));
aladin.on('rotationChanged', (rot) => {
aladin.setRotation(rot)
})
});
</script>
</body>

View File

@@ -77,7 +77,7 @@
let bValues = [];
let i = 0;
for(var [r, g, b] of base.probe({type: 'line', x1: p.a.x, y1: p.a.y, x2: p.b.x, y2: p.b.y})) {
for(var [r, g, b] of base.probePixels({type: 'line', x1: p.a.x, y1: p.a.y, x2: p.b.x, y2: p.b.y})) {
xValues.push(i)
rValues.push(r)
gValues.push(g)

View File

@@ -33,10 +33,16 @@ var myFilterFunction = function(source) {
return color>colorThreshold;
}
aladin = A.aladin('#aladin-lite-div', {target: 'M 81', fov: 0.5, survey: 'CDS/P/SDSS9/color'});
var cat = A.catalogFromSimbad('M 81', 0.25, {onClick: 'showTable', verbosity: 3, filter: myFilterFunction});
aladin.addCatalog(cat);
aladin = A.aladin('#aladin-lite-div', {target: 'M 81', fov: 0.5, survey: 'CDS/P/SDSS9/color'});
var cat = A.catalogFromSimbad('M 81', 0.25, {
shape: (s) => {
return A.circle(s.ra, s.dec, 0.003, {lineWidth: 3});
},
onClick: 'showTable', verbosity: 3, filter: myFilterFunction
});
aladin.addCatalog(cat);
});
</script>
</body>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -10,7 +10,7 @@
<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, survey: 'https://alasky.cds.unistra.fr/DSS/DSSColor/', fov: 180, showContextMenu: true});
aladin = A.aladin('#aladin-lite-div', {fullScreen: 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

@@ -1,8 +1,8 @@
{
"homepage": "https://aladin.u-strasbg.fr/",
"homepage": "https://aladin.cds.unistra.fr/",
"name": "aladin-lite",
"type": "module",
"version": "3.7.0-beta",
"version": "3.7.3-beta",
"description": "An astronomical HiPS visualizer in the browser",
"author": "Thomas Boch and Matthieu Baumann",
"license": "GPL-3",
@@ -30,13 +30,17 @@
"HiPS"
],
"scripts": {
"wasm": "wasm-pack build ./src/core --target web --release --out-name core -- --features webgl2",
"wasm:npm": "wasm-pack build ./src/core --target web --release --out-name core -- --features webgl2",
"wasm:prod": "wasm-pack build ./src/core --target web --release --out-name core -- --features \"webgl2 minify_shaders\" && wasm-opt -Oz --strip-debug --strip-producers --dce -o src/core/pkg/core_bg.wasm src/core/pkg/core_bg.wasm",
"wasm:dev": "wasm-pack build ./src/core --target web --release --out-name core -- --features webgl2",
"wasm:dbg": "wasm-pack build --dev ./src/core --target web --out-name core -- --features=webgl2,dbg",
"predeploy": "npm run build && rm -rf aladin-lite*.tgz && npm pack",
"predeploy": "npm run build:prod && rm -rf aladin-lite*.tgz && npm pack",
"deploy": "python3 deploy/deploy.py",
"build": "npm run wasm && vite build",
"build:npm": "npm run wasm:npm && vite build",
"build:prod": "npm run wasm:prod && vite build",
"build:dev": "npm run wasm:dev && vite build",
"build:dbg": "npm run wasm:dbg && vite build",
"dev": "npm run build && vite",
"dev": "npm run build:dev && vite",
"dev:dbg": "npm run build:dbg && vite",
"serve": "npm run dev",
"serve:dbg": "npm run dev:dbg",
@@ -45,14 +49,15 @@
"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:dev": "npm run doc && open docs/index.html"
"doc:dev": "npm run doc && open docs/index.html",
"analyze": "vite build --mode analyze"
},
"devDependencies": {
"@playwright/test": "^1.47.0",
"docdash": "^2.0.2",
"jsdoc": "^4.0.2",
"rollup-plugin-visualizer": "^6.0.3",
"vite": "^4.3.8",
"vite-plugin-glsl": "^1.1.2",
"vite-plugin-top-level-await": "^1.4.1",
"vite-plugin-wasm": "^3.2.2",
"vite-plugin-wasm-pack": "^0.1.12"

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.7.0"
version = "3.7.3-beta"
authors = [ "baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr",]
edition = "2018"
@@ -18,22 +18,20 @@ futures = "0.3.12"
js-sys = "0.3.47"
wasm-bindgen-futures = "0.4.20"
cgmath = "*"
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"
async-channel = "1.8.0"
mapproj = "0.3.0"
fitsrs = "0.2.11"
wcs = "0.3.1"
colorgrad = "0.6.2"
fitsrs = "0.4.1"
[features]
webgl1 = [ "al-core/webgl1", "al-api/webgl1", "web-sys/WebGlRenderingContext", "web-sys/AngleInstancedArrays", "web-sys/ExtSRgb", "web-sys/OesTextureFloat",]
webgl2 = [ "al-core/webgl2", "al-api/webgl2", "web-sys/WebGl2RenderingContext", "web-sys/WebGlVertexArrayObject", "web-sys/ExtColorBufferFloat",]
dbg = [ "dep:console_error_panic_hook",]
minify_shaders = []
[dev-dependencies]
rand = "0.8"
@@ -51,7 +49,8 @@ version = "0.7.3"
[dependencies.moclib]
package = "moc"
version = "0.17.0"
git = "https://github.com/cds-astro/cds-moc-rust"
branch = "main"
[dependencies.serde]
version = "^1.0.183"
@@ -65,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"
@@ -84,7 +83,7 @@ overflow-checks = false
lto = true
panic = "abort"
incremental = false
codegen-units = 16
codegen-units = 1
rpath = false
[package.metadata.wasm-pack.profile.release]

View File

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

View File

@@ -32,5 +32,5 @@ use std::cmp::Eq;
#[wasm_bindgen]
pub enum Formatter {
Sexagesimal,
Decimal
}
Decimal,
}

View File

@@ -4,8 +4,6 @@ use wasm_bindgen::prelude::wasm_bindgen;
#[cfg(feature = "webgl2")]
pub type WebGlRenderingCtx = web_sys::WebGl2RenderingContext;
#[cfg(feature = "webgl1")]
pub type WebGlRenderingCtx = web_sys::WebGlRenderingContext;
#[derive(Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "camelCase")]
@@ -94,7 +92,7 @@ impl fmt::Display for BlendFactor {
BlendFactor::OneMinusSrcAlpha => "OneMinusSrcAlpha",
BlendFactor::OneMinusConstantColor => "OneMinusConstantColor",
};
write!(f, "{}", str)
write!(f, "{str}")
}
}
impl fmt::Display for BlendFunc {
@@ -113,6 +111,6 @@ impl fmt::Display for BlendFunc {
#[cfg(feature = "webgl2")]
BlendFunc::Max => "Max",*/
};
write!(f, "{}", str)
write!(f, "{str}")
}
}

View File

@@ -1,9 +1,8 @@
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug)]
#[derive(Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct HEALPixCellProjeted {
pub ipix: u64,
pub vx: [f64; 4],
pub vy: [f64; 4],
}
}

View File

@@ -34,7 +34,7 @@ pub struct ColorRGBA {
}
use std::ops::Mul;
impl<'a> Mul<f32> for &'a ColorRGB {
impl Mul<f32> for &ColorRGB {
// The multiplication of rational numbers is a closed operation.
type Output = ColorRGB;

View File

@@ -1,39 +1,30 @@
use cgmath::Matrix3;
const GAL2ICRS: &'static Matrix3<f64> = &Matrix3::new(
-0.44482972122205372312012370920248,
0.74698218398450941835110635824212,
0.49410943719710765017955928850141,
-0.19807633727507056817237662907031,
0.45598381369115237931077906137440,
-0.86766613755716255824577781583414,
-0.87343705195577915249273984034980,
-0.48383507361641838378786914298189,
-0.05487565771261968232908806948676,
const GAL2ICRS: &Matrix3<f64> = &Matrix3::new(
-0.444_829_721_222_053_7,
0.746_982_183_984_509_4,
0.494_109_437_197_107_65,
-0.198_076_337_275_070_57,
0.455_983_813_691_152_4,
-0.867_666_137_557_162_6,
-0.873_437_051_955_779_1,
-0.483_835_073_616_418_37,
-0.054_875_657_712_619_68,
);
const ICRS2GAL: &'static Matrix3<f64> = &Matrix3::new(
-0.44482972122205372312012370920248,
-0.19807633727507056817237662907031,
-0.87343705195577915249273984034980,
0.74698218398450941835110635824212,
0.45598381369115237931077906137440,
-0.48383507361641838378786914298189,
0.49410943719710765017955928850141,
-0.86766613755716255824577781583414,
-0.05487565771261968232908806948676,
);
const ID: &'static Matrix3<f64> = &Matrix3::new(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
const ICRS2GAL: &Matrix3<f64> = &Matrix3::new(
-0.444_829_721_222_053_7,
-0.198_076_337_275_070_57,
-0.873_437_051_955_779_1,
0.746_982_183_984_509_4,
0.455_983_813_691_152_4,
-0.483_835_073_616_418_37,
0.494_109_437_197_107_65,
-0.867_666_137_557_162_6,
-0.054_875_657_712_619_68,
);
const ID: &Matrix3<f64> = &Matrix3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
use serde::Deserialize;
use wasm_bindgen::prelude::*;

View File

@@ -48,14 +48,26 @@ pub struct HiPSProperties {
hips_initial_fov: Option<f64>,
hips_initial_ra: Option<f64>,
hips_initial_dec: Option<f64>,
// HiPS cube
hips_cube_depth: Option<u32>,
// HiPS 3D keywords
hips_order_freq: Option<u8>,
hips_tile_depth: Option<u8>,
/// Start of spectral coordinates (in meters)
em_min: Option<f32>,
/// End of spectral coordinates (in meters)
em_max: Option<f32>,
// Parametrable by the user
#[allow(unused)]
min_cutout: Option<f32>,
#[allow(unused)]
max_cutout: Option<f32>,
dataproduct_type: Option<DataproductType>,
creator_did: String,
request_credentials: String,
@@ -63,6 +75,20 @@ pub struct HiPSProperties {
}
impl HiPSProperties {
#[inline(always)]
pub fn get_hips_order_freq(&self) -> Option<u8> {
self.hips_order_freq
}
#[inline(always)]
pub fn get_hips_tile_depth(&self) -> Option<u8> {
self.hips_tile_depth
}
#[inline(always)]
pub fn get_dataproduct_type(&self) -> Option<DataproductType> {
self.dataproduct_type
}
#[inline(always)]
pub fn get_url(&self) -> &str {
&self.url
@@ -137,6 +163,16 @@ impl HiPSProperties {
pub fn get_request_mode(&self) -> &str {
&self.request_mode
}
#[inline(always)]
pub fn get_em_min(&self) -> Option<f32> {
self.em_min
}
#[inline(always)]
pub fn get_em_max(&self) -> Option<f32> {
self.em_max
}
}
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -144,14 +180,27 @@ impl HiPSProperties {
#[serde(rename_all = "camelCase")]
pub enum ImageExt {
Fits,
#[serde(alias = "fits.fz")]
FitsFz,
Jpeg,
Png,
Webp,
}
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[wasm_bindgen]
#[serde(rename_all = "camelCase")]
pub enum DataproductType {
#[serde(rename = "spectral-cube")]
SpectralCube,
Image,
Cube,
}
impl std::fmt::Display for ImageExt {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ImageExt::FitsFz => write!(f, "fits.fz"),
ImageExt::Fits => write!(f, "fits"),
ImageExt::Png => write!(f, "png"),
ImageExt::Jpeg => write!(f, "jpg"),
@@ -163,9 +212,10 @@ impl std::fmt::Display for ImageExt {
use serde::Serialize;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[derive(Clone, Copy, PartialEq, Debug, Deserialize, Serialize)]
#[derive(Clone, Copy, PartialEq, Debug, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub enum TransferFunction {
#[default]
Linear,
Sqrt,
Log,
@@ -189,12 +239,6 @@ impl TransferFunction {
}
}
impl Default for TransferFunction {
fn default() -> Self {
TransferFunction::Linear
}
}
impl From<String> for TransferFunction {
fn from(id: String) -> Self {
TransferFunction::new(&id)

View File

@@ -2,27 +2,29 @@
It is used by al-ui and any javascript application calling
the WASM core of aladin lite v3
*/
pub mod angle;
pub mod blend;
pub mod cell;
pub mod color;
pub mod colormap;
pub mod coo_system;
pub mod fov;
pub mod grid;
pub mod hips;
pub mod image;
pub mod moc;
pub mod resources;
pub mod cell;
pub mod fov;
pub mod image;
pub mod angle;
pub trait Abort {
type Item;
fn unwrap_abort(self) -> Self::Item where Self: Sized;
fn unwrap_abort(self) -> Self::Item
where
Self: Sized;
}
impl<T> Abort for Option<T> {
type Item = T;
#[inline]
fn unwrap_abort(self) -> Self::Item {
use std::process;

View File

@@ -1,7 +1,6 @@
use wasm_bindgen::prelude::wasm_bindgen;
use super::color::{Color, ColorRGBA};
#[derive(Clone, Debug)]
#[wasm_bindgen]
pub struct MOCOptions {
@@ -19,6 +18,7 @@ use crate::{color::ColorRGB, Abort};
use std::convert::TryInto;
#[wasm_bindgen]
impl MOCOptions {
#[allow(clippy::too_many_arguments)]
#[wasm_bindgen(constructor)]
pub fn new(
uuid: String,

View File

@@ -1,20 +1,18 @@
[package]
name = "al-core"
version = "3.6.5"
version = "3.7.0"
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
edition = "2018"
[dependencies]
js-sys = "0.3.47"
cgmath = "*"
jpeg-decoder = "0.3.0"
png = "0.17.6"
fitsrs = "0.2.10"
fitsrs = "0.4.1"
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"

View File

@@ -2,13 +2,13 @@ use std::collections::HashMap;
use colorgrad::Color;
use crate::shader::SendUniformsWithParams;
use crate::Texture2D;
use crate::WebGlContext;
use crate::image::format;
use crate::shader::SendUniformsWithParams;
use wasm_bindgen::JsValue;
use crate::texture::format::RGBA8U;
use crate::webgl_ctx::WebGlRenderingCtx;
use wasm_bindgen::JsValue;
const WIDTH_CMAP_TEX: usize = 256;
@@ -20,7 +20,10 @@ pub struct Colormap {
}
impl Colormap {
pub fn new(label: &str, grad: colorgrad::Gradient) -> Self {
Self { label: label.to_string(), grad }
Self {
label: label.to_string(),
grad,
}
}
pub fn label(&self) -> &Label {
@@ -29,18 +32,20 @@ impl Colormap {
}
fn build_cmaps_texture(gl: &WebGlContext, cmaps: &[Colormap]) -> Result<Texture2D, JsValue> {
let tex_bytes: Vec<u8> = cmaps.iter()
.map(|cmap| {
let tex_bytes: Vec<u8> = cmaps
.iter()
.flat_map(|cmap| {
let mut values = [0_u8; 1024];
for ix in 0..WIDTH_CMAP_TEX {
let rgba = cmap.grad.at(ix as f64 / WIDTH_CMAP_TEX as f64).to_rgba8();
let ptr = values[4*ix..].as_mut_ptr() as *mut [u8; 4];
unsafe { *ptr = rgba; }
let ptr = values[4 * ix..].as_mut_ptr() as *mut [u8; 4];
unsafe {
*ptr = rgba;
}
}
values
})
.flatten()
.collect();
let tex_params = &[
(
@@ -63,12 +68,12 @@ fn build_cmaps_texture(gl: &WebGlContext, cmaps: &[Colormap]) -> Result<Texture2
),
];
Texture2D::create_from_raw_pixels::<format::RGBA8U>(
Texture2D::create_from_raw_pixels::<RGBA8U>(
gl,
WIDTH_CMAP_TEX as i32,
cmaps.len() as i32,
tex_params,
Some(&tex_bytes[..])
Some(&tex_bytes[..]),
)
}
@@ -87,20 +92,38 @@ use crate::Abort;
impl Colormaps {
pub fn new(gl: &WebGlContext) -> Result<Self, JsValue> {
let labels: Vec<_> = [
"blues", "cividis", "cubehelix", "eosb",
"grayscale", "inferno", "magma", "native",
"parula", "plasma", "rainbow", "rdbu",
"rdylbu", "redtemperature", "sinebow", "spectral", "summer",
"viridis", "ylgnbu", "ylorbr", "red", "green", "blue"
"blues",
"cividis",
"cubehelix",
"eosb",
"grayscale",
"inferno",
"magma",
"native",
"parula",
"plasma",
"rainbow",
"rdbu",
"rdylbu",
"redtemperature",
"sinebow",
"spectral",
"summer",
"viridis",
"ylgnbu",
"ylorbr",
"red",
"green",
"blue",
]
.iter()
.map(|cmap_name| cmap_name.to_string())
.collect();
let indices = labels.iter().enumerate()
.map(|(id, label)| {
(label.clone(), id as i32)
})
let indices = labels
.iter()
.enumerate()
.map(|(id, label)| (label.clone(), id as i32))
.collect();
let cmaps = vec![
@@ -111,14 +134,14 @@ impl Colormaps {
Colormap::new("grayscale", {
colorgrad::CustomGradient::new()
.build()
.map_err(|err| JsValue::from_str(&format!("{:?}", err)))?
.map_err(|err| JsValue::from_str(&format!("{err:?}")))?
}),
Colormap::new("inferno", colorgrad::inferno()),
Colormap::new("magma", colorgrad::magma()),
Colormap::new("native", {
colorgrad::CustomGradient::new()
.build()
.map_err(|err| JsValue::from_str(&format!("{:?}", err)))?
.map_err(|err| JsValue::from_str(&format!("{err:?}")))?
}),
Colormap::new("parula", {
colorgrad::CustomGradient::new()
@@ -132,7 +155,7 @@ impl Colormaps {
Color::from_rgba8(249, 250, 20, 255),
])
.build()
.map_err(|err| JsValue::from_str(&format!("{:?}", err)))?
.map_err(|err| JsValue::from_str(&format!("{err:?}")))?
}),
Colormap::new("plasma", colorgrad::plasma()),
Colormap::new("rainbow", {
@@ -150,7 +173,7 @@ impl Colormaps {
Color::from_rgba8(255, 0, 0, 255),
])
.build()
.map_err(|err| JsValue::from_str(&format!("{:?}", err)))?
.map_err(|err| JsValue::from_str(&format!("{err:?}")))?
}),
Colormap::new("rdbu", colorgrad::rd_bu()),
Colormap::new("rdylbu", colorgrad::rd_yl_bu()),
@@ -163,7 +186,7 @@ impl Colormaps {
Color::new(1.0, 1.0, 1.0, 1.0),
])
.build()
.map_err(|err| JsValue::from_str(&format!("{:?}", err)))?
.map_err(|err| JsValue::from_str(&format!("{err:?}")))?
}),
Colormap::new("sinebow", colorgrad::sinebow()),
Colormap::new("spectral", colorgrad::spectral()),
@@ -178,7 +201,7 @@ impl Colormaps {
Color::new(1.0, 0.0, 0.0, 1.0),
])
.build()
.map_err(|err| JsValue::from_str(&format!("{:?}", err)))?
.map_err(|err| JsValue::from_str(&format!("{err:?}")))?
}),
Colormap::new("green", {
colorgrad::CustomGradient::new()
@@ -187,7 +210,7 @@ impl Colormaps {
Color::new(0.0, 1.0, 0.0, 1.0),
])
.build()
.map_err(|err| JsValue::from_str(&format!("{:?}", err)))?
.map_err(|err| JsValue::from_str(&format!("{err:?}")))?
}),
Colormap::new("blue", {
colorgrad::CustomGradient::new()
@@ -196,14 +219,20 @@ impl Colormaps {
Color::new(0.0, 0.0, 1.0, 1.0),
])
.build()
.map_err(|err| JsValue::from_str(&format!("{:?}", err)))?
.map_err(|err| JsValue::from_str(&format!("{err:?}")))?
}),
];
let cmaps_tex = build_cmaps_texture(gl, &cmaps[..])?;
let gl = gl.clone();
Ok(Self { cmaps, cmaps_tex, labels, indices, gl })
Ok(Self {
cmaps,
cmaps_tex,
labels,
indices,
gl,
})
}
#[inline]
@@ -213,12 +242,14 @@ impl Colormaps {
#[inline]
pub fn get(&self, label: &str) -> &Colormap {
if let Some(id) = self.get_id(label).map(|id| *id) {
if let Some(&id) = self.get_id(label) {
&self.cmaps[id as usize]
} else {
crate::log::console_warn(&format!("{:?} is not a valid colormap, replaced with 'grayscale'.", label));
let id_greys = self.get_id("grayscale").map(|id| *id).unwrap_abort();
&self.cmaps[id_greys as usize]
crate::log::console_warn(format!(
"{label:?} is not a valid colormap, replaced with 'grayscale'.",
));
let id_greys = self.get_id("grayscale").unwrap_abort();
&self.cmaps[*id_greys as usize]
}
}
@@ -228,13 +259,13 @@ impl Colormaps {
}
pub fn add_cmap(&mut self, label: Label, cmap: Colormap) -> Result<(), JsValue> {
if let Some(id) = self.get_id(&label).map(|id| *id) {
if let Some(&id) = self.get_id(&label) {
let colormap = &mut self.cmaps[id as usize];
*colormap = cmap;
} else {
let num_cmaps = self.labels.len();
self.labels.push(label.clone());
self.indices.insert(label, num_cmaps as i32);
self.cmaps.push(cmap);
}
@@ -246,7 +277,7 @@ impl Colormaps {
}
}
use crate::shader::{ShaderBound, SendUniforms};
use crate::shader::{SendUniforms, ShaderBound};
impl SendUniforms for Colormaps {
fn attach_uniforms<'a>(&self, shader: &'a ShaderBound<'a>) -> &'a ShaderBound<'a> {
shader
@@ -258,7 +289,11 @@ impl SendUniforms for Colormaps {
}
impl SendUniformsWithParams<Colormaps> for Colormap {
fn attach_uniforms_with_params<'a>(&self, shader: &'a ShaderBound<'a>, cmaps: &Colormaps) -> &'a ShaderBound<'a> {
fn attach_uniforms_with_params<'a>(
&self,
shader: &'a ShaderBound<'a>,
cmaps: &Colormaps,
) -> &'a ShaderBound<'a> {
let cmap_id = cmaps.get_id(&self.label).unwrap_abort();
shader.attach_uniform("colormap_id", &(*cmap_id as f32));
shader

View File

@@ -6,11 +6,11 @@ pub struct Bitmap<F> {
format: std::marker::PhantomData<F>,
}
use crate::image::format::ImageFormat;
use crate::image::Image;
use crate::texture::format::TextureFormat;
impl<F> Bitmap<F>
where
F: ImageFormat + Clone,
F: TextureFormat + Clone,
{
pub fn new(image: web_sys::ImageBitmap) -> Self {
Self {
@@ -23,7 +23,7 @@ use crate::texture::Tex3D;
use wasm_bindgen::JsValue;
impl<F> Image for Bitmap<F>
where
F: ImageFormat + Clone,
F: TextureFormat + Clone,
{
fn insert_into_3d_texture<T: Tex3D>(
&self,
@@ -34,4 +34,8 @@ where
Ok(())
}
fn get_size(&self) -> (u32, u32, u32) {
(self.image.width(), self.image.height(), 1)
}
}

View File

@@ -7,7 +7,7 @@ pub struct Canvas<F> {
impl<F> Canvas<F>
where
F: ImageFormat + Clone,
F: TextureFormat + Clone,
{
pub fn new(canvas: web_sys::HtmlCanvasElement) -> Self {
Self {
@@ -17,14 +17,14 @@ where
}
}
use crate::image::format::ImageFormat;
use crate::image::Image;
use crate::texture::format::TextureFormat;
use crate::texture::Tex3D;
use cgmath::Vector3;
use wasm_bindgen::JsValue;
impl<F> Image for Canvas<F>
where
F: ImageFormat,
F: TextureFormat,
{
fn insert_into_3d_texture<T: Tex3D>(
&self,
@@ -42,4 +42,8 @@ where
Ok(())
}
fn get_size(&self) -> (u32, u32, u32) {
(self.canvas.width(), self.canvas.height(), 1)
}
}

View File

@@ -1,123 +1,188 @@
use cgmath::{Vector2, Vector3};
#[derive(Debug)]
pub struct Fits<'a> {
// Tile size
size: Vector2<i32>,
pub data: Data<'a>,
}
use crate::texture::format::TextureFormat;
use crate::texture::format::R8U;
use cgmath::Vector3;
use fitsrs::hdu::data::bintable::data::BinaryTableData;
use fitsrs::hdu::data::bintable::tile_compressed::pixels::Pixels;
use fitsrs::hdu::header::extension::bintable::TileCompressedImage;
use fitsrs::hdu::header::Bitpix;
use fitsrs::WCS;
use fitsrs::{Fits, HDU};
use std::borrow::Cow;
use std::fmt::Debug;
#[derive(Debug)]
pub enum Data<'a> {
U8(Cow<'a, [u8]>),
I16(Cow<'a, [i16]>),
I32(Cow<'a, [i32]>),
F32(Cow<'a, [f32]>),
}
use fitsrs::{fits::Fits as FitsData, hdu::data::InMemData};
use std::io::Cursor;
use std::ops::Range;
use wasm_bindgen::JsValue;
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"))?;
let header = hdu.get_header();
let xtension = header.get_xtension();
let width = xtension
.get_naxisn(1)
.ok_or_else(|| JsValue::from_str("NAXIS1 not found in the fits"))?;
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::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::F64(slice) => {
let data = slice.iter().map(|v| *v as f32).collect();
Data::F32(Cow::Owned(data))
}
};
Ok(Self {
// Tile size
size: Vector2::new(*width as i32, *height as i32),
// Allocation info of the layout
data,
})
}
pub fn get_size(&self) -> &Vector2<i32> {
&self.size
}
#[derive(Debug)]
pub struct FitsImage<'a> {
// Margin values for HiPS3D cubic tiles
pub trim1: u32,
pub trim2: u32,
pub trim3: u32,
// Image/cube size
pub width: u32,
pub height: u32,
pub depth: u32,
// Bitpix
pub bitpix: Bitpix,
// 1.0 by default
pub bscale: f32,
// 0.0 by default
pub bzero: f32,
// blank
pub blank: Option<f32>,
// optional wcs
pub wcs: Option<WCS>,
// bytes offset where the data bytes are located inside the fits
pub data_byte_offset: Range<usize>,
// raw bytes of the data image (in Big-Endian)
pub raw_bytes: Cow<'a, [u8]>,
}
/*impl Fits<'static> {
pub async fn from_async_reader(reader: IntoAsyncRead<'static>) -> Result<Self, JsValue> {
let fitsrs::fits::AsyncFits { hdu: AsyncHDU { data, header } } = fitsrs::fits::AsyncFits::from_reader(futures::io::BufReader::new(reader))
.await
.map_err(|err| {
JsValue::from_str(&format!("Parsing fits error: {}", err))
})?;
impl<'a> FitsImage<'a> {
/// Get all the hdu images from a fits file
pub fn from_raw_bytes(bytes: &'a [u8]) -> Result<Vec<Self>, JsValue> {
let mut fits = Fits::from_reader(Cursor::new(bytes));
let mut images = vec![];
let width = header.get_axis_size(1)
.ok_or_else(|| JsValue::from_str("NAXIS1 not found in the fits"))?;
while let Some(Ok(hdu)) = fits.next() {
match hdu {
HDU::XImage(hdu) | HDU::Primary(hdu) => {
// Prefer getting the dimension directly from NAXIS1/NAXIS2 instead of from the WCS
// because it may not exist in all HDU images
let naxis = hdu.get_header().get_xtension().get_naxis();
if naxis.len() >= 2 {
let width = naxis[0];
let height = naxis[1];
let depth = if naxis.len() >= 3 { naxis[2] } else { 1 };
let height = header.get_axis_size(2)
.ok_or_else(|| JsValue::from_str("NAXIS2 not found in the fits"))?;
let header = hdu.get_header();
let data = match data {
fitsrs::hdu::data_async::DataOwned::U8(stream) => {
let data = stream.collect().await;
Data::U8(Cow::Owned(data))
},
fitsrs::hdu::data_async::DataOwned::I16(stream) => {
let data = stream.collect().await;
Data::I16(Cow::Owned(data))
},
fitsrs::hdu::data_async::DataOwned::I32(stream) => {
let data = stream.collect().await;
Data::I32(Cow::Owned(data))
},
fitsrs::hdu::data_async::DataOwned::I64(stream) => {
let data = stream.map(|v| v as i32).collect().await;
Data::I32(Cow::Owned(data))
},
fitsrs::hdu::data_async::DataOwned::F32(stream) => {
let data = stream.collect().await;
Data::F32(Cow::Owned(data))
},
fitsrs::hdu::data_async::DataOwned::F64(stream) => {
let data = stream.map(|v| v as f32).collect().await;
Data::F32(Cow::Owned(data))
let bscale = header.get_parsed::<f32>("BSCALE").unwrap_or(1.0);
let bzero = header.get_parsed::<f32>("BZERO").unwrap_or(0.0);
let blank = header.get_parsed::<f32>("BLANK").ok();
let trim1 = header.get_parsed::<u32>("TRIM1").unwrap_or(0);
let trim2 = header.get_parsed::<u32>("TRIM2").unwrap_or(0);
let trim3 = header.get_parsed::<u32>("TRIM3").unwrap_or(0);
let bitpix = hdu.get_header().get_xtension().get_bitpix();
let off = hdu.get_data_unit_byte_offset() as usize;
let len = hdu.get_data_unit_byte_size() as usize;
let data_byte_offset = off..(off + len);
let raw_bytes = Cow::Borrowed(&bytes[data_byte_offset.clone()]);
let wcs = hdu.wcs().ok();
images.push(Self {
trim1,
trim2,
trim3,
width: width as u32,
height: height as u32,
depth: depth as u32,
bitpix,
bscale,
wcs,
bzero,
blank,
data_byte_offset,
raw_bytes,
});
}
}
HDU::XBinaryTable(hdu) => {
let header = hdu.get_header();
let bin_table = header.get_xtension();
if let Some(TileCompressedImage {
z_bitpix: bitpix,
z_naxisn: naxis,
..
}) = &bin_table.get_z_image()
{
if naxis.len() >= 2 {
let width = naxis[0] as u32;
let height = naxis[1] as u32;
let depth = if naxis.len() >= 3 { naxis[2] as u32 } else { 1 };
let bscale = header.get_parsed::<f32>("BSCALE").unwrap_or(1.0);
let bzero = header.get_parsed::<f32>("BZERO").unwrap_or(0.0);
let blank = header.get_parsed::<f32>("BLANK").ok();
let trim1 = header.get_parsed::<u32>("TRIM1").unwrap_or(0);
let trim2 = header.get_parsed::<u32>("TRIM2").unwrap_or(0);
let trim3 = header.get_parsed::<u32>("TRIM3").unwrap_or(0);
let wcs = hdu.wcs().ok();
let off = hdu.get_data_unit_byte_offset() as usize;
let len = hdu.get_data_unit_byte_size() as usize;
let data_byte_offset = off..(off + len);
let mut bitpix = *bitpix;
let raw_bytes = match fits.get_data(&hdu) {
BinaryTableData::TileCompressed(Pixels::U8(pixels)) => {
Some(pixels.collect::<Vec<_>>())
}
BinaryTableData::TileCompressed(Pixels::I16(pixels)) => {
Some(pixels.flat_map(|p| p.to_be_bytes()).collect::<Vec<_>>())
}
BinaryTableData::TileCompressed(Pixels::I32(pixels)) => {
Some(pixels.flat_map(|p| p.to_be_bytes()).collect::<Vec<_>>())
}
BinaryTableData::TileCompressed(Pixels::F32(pixels)) => {
Some(pixels.flat_map(|p| p.to_be_bytes()).collect::<Vec<_>>())
}
BinaryTableData::TileCompressed(Pixels::F64(pixels)) => {
bitpix = Bitpix::F32;
let raw_bytes =
pixels.flat_map(|p| p.to_be_bytes()).collect::<Vec<_>>();
Some(raw_bytes)
}
_ => None,
};
if let Some(raw_bytes) = raw_bytes {
images.push(Self {
trim1,
trim2,
trim3,
width,
height,
depth,
bitpix,
bscale,
wcs,
bzero,
blank,
data_byte_offset,
raw_bytes: Cow::Owned(raw_bytes),
});
}
}
}
}
_ => (),
}
};
}
Ok(Self {
// Tile size
size: Vector2::new(*width as i32, *height as i32),
// Allocation info of the layout
data
})
if !images.is_empty() {
Ok(images)
} else {
Err(JsValue::from_str("Image HDU not found in the FITS"))
}
}
}*/
}
use crate::{image::Image, texture::Tex3D};
impl Image for Fits<'_> {
use std::convert::TryInto;
impl Image for FitsImage<'_> {
fn insert_into_3d_texture<T: Tex3D>(
&self,
// The texture array
@@ -125,89 +190,57 @@ impl Image for Fits<'_> {
// An offset to write the image in the texture array
offset: &Vector3<i32>,
) -> Result<(), JsValue> {
match &self.data {
Data::U8(data) => {
let view = unsafe { R8UI::view(&data) };
textures.tex_sub_image_3d_with_opt_array_buffer_view(
offset.x,
offset.y,
offset.z,
self.size.x,
self.size.y,
1,
Some(view.as_ref()),
);
let view = unsafe {
match self.bitpix {
Bitpix::I64 => {
// convert to i64 first
let new_bytes: Vec<_> = self
.raw_bytes
.chunks_exact(8)
.flat_map(|chunk| {
let bytes: [u8; 8] = chunk.try_into().unwrap();
let value = i64::from_be_bytes(bytes);
(value as i32).to_be_bytes()
})
.collect();
R8U::view(&new_bytes)
}
Bitpix::F64 => {
// convert to i64 first
let new_bytes: Vec<_> = self
.raw_bytes
.chunks_exact(8)
.flat_map(|chunk| {
let bytes: [u8; 8] = chunk.try_into().unwrap();
let value = f64::from_be_bytes(bytes);
(value as f32).to_be_bytes()
})
.collect();
R8U::view(&new_bytes)
}
_ => R8U::view(&self.raw_bytes),
}
Data::I16(data) => {
let view = unsafe { R16I::view(&data) };
textures.tex_sub_image_3d_with_opt_array_buffer_view(
offset.x,
offset.y,
offset.z,
self.size.x,
self.size.y,
1,
Some(view.as_ref()),
);
}
Data::I32(data) => {
let view = unsafe { R32I::view(&data) };
textures.tex_sub_image_3d_with_opt_array_buffer_view(
offset.x,
offset.y,
offset.z,
self.size.x,
self.size.y,
1,
Some(view.as_ref()),
);
}
Data::F32(data) => {
let view = unsafe { R8UI::view(&std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4)) };
textures.tex_sub_image_3d_with_opt_array_buffer_view(
offset.x,
offset.y,
offset.z,
self.size.x,
self.size.y,
1,
Some(view.as_ref()),
);
}
}
};
textures.tex_sub_image_3d_with_opt_array_buffer_view(
offset.x + self.trim1 as i32,
offset.y + self.trim2 as i32,
offset.z + self.trim3 as i32,
self.width as i32,
self.height as i32,
self.depth as i32,
Some(view.as_ref()),
);
Ok(())
}
}
use crate::image::format::ImageFormat;
use wasm_bindgen::JsValue;
pub trait FitsImageFormat: ImageFormat {
const BITPIX: i8;
}
use crate::image::R32F;
impl FitsImageFormat for R32F {
const BITPIX: i8 = -32;
}
#[cfg(feature = "webgl2")]
use crate::image::{R16I, R32I, R64F, R8UI};
#[cfg(feature = "webgl2")]
impl FitsImageFormat for R64F {
const BITPIX: i8 = -64;
}
#[cfg(feature = "webgl2")]
impl FitsImageFormat for R32I {
const BITPIX: i8 = 32;
}
#[cfg(feature = "webgl2")]
impl FitsImageFormat for R16I {
const BITPIX: i8 = 16;
}
#[cfg(feature = "webgl2")]
impl FitsImageFormat for R8UI {
const BITPIX: i8 = 8;
fn get_size(&self) -> (u32, u32, u32) {
// The true image size is given by ONAXISi keywords
(self.width, self.height, self.depth)
}
}

View File

@@ -1,314 +1,9 @@
use crate::texture::pixel::Pixel;
use crate::texture::format::PixelType;
use al_api::hips::ImageExt;
pub enum Bytes<'a> {
Borrowed(&'a [u8]),
Owned(Vec<u8>),
}
pub trait ImageFormat {
type P: Pixel;
type ArrayBufferView: AsRef<js_sys::Object>;
const NUM_CHANNELS: usize;
const FORMAT: u32;
const INTERNAL_FORMAT: i32;
const TYPE: u32;
const CHANNEL_TYPE: ChannelType;
/// Creates a JS typed array which is a view into wasm's linear memory at the slice specified.
/// This function returns a new typed array which is a view into wasm's memory. This view does not copy the underlying data.
///
/// # Safety
///
/// Views into WebAssembly memory are only valid so long as the backing buffer isn't resized in JS. Once this function is called any future calls to Box::new (or malloc of any form) may cause the returned value here to be invalidated. Use with caution!
///
/// Additionally the returned object can be safely mutated but the input slice isn't guaranteed to be mutable.
///
/// Finally, the returned object is disconnected from the input slice's lifetime, so there's no guarantee that the data is read at the right time.
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str>;
}
use crate::webgl_ctx::WebGlRenderingCtx;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct RGB8U;
impl ImageFormat for RGB8U {
type P = [u8; 3];
const NUM_CHANNELS: usize = 3;
const FORMAT: u32 = WebGlRenderingCtx::RGB as u32;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGB8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
const CHANNEL_TYPE: ChannelType = ChannelType::RGB8U;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
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))
}
type ArrayBufferView = js_sys::Uint8Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct RGBA8U;
#[cfg(feature = "webgl2")]
impl ImageFormat for RGBA8U {
type P = [u8; 4];
const NUM_CHANNELS: usize = 4;
const FORMAT: u32 = WebGlRenderingCtx::RGBA as u32;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
const CHANNEL_TYPE: ChannelType = ChannelType::RGBA8U;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
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))
}
type ArrayBufferView = js_sys::Uint8Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct RGBA32F;
impl ImageFormat for RGBA32F {
type P = [f32; 4];
const NUM_CHANNELS: usize = 4;
const FORMAT: u32 = WebGlRenderingCtx::RGBA as u32;
#[cfg(feature = "webgl2")]
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA32F as i32;
#[cfg(feature = "webgl1")]
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA as i32;
const CHANNEL_TYPE: ChannelType = ChannelType::RGBA32F;
const TYPE: u32 = WebGlRenderingCtx::FLOAT;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
Ok(Bytes::Borrowed(raw_bytes))
}
type ArrayBufferView = js_sys::Float32Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct RGB32F;
impl ImageFormat for RGB32F {
type P = [f32; 3];
const NUM_CHANNELS: usize = 3;
const FORMAT: u32 = WebGlRenderingCtx::RGB as u32;
#[cfg(feature = "webgl2")]
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGB32F as i32;
#[cfg(feature = "webgl1")]
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGB as i32;
const CHANNEL_TYPE: ChannelType = ChannelType::RGB32F;
const TYPE: u32 = WebGlRenderingCtx::FLOAT;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
Ok(Bytes::Borrowed(raw_bytes))
}
type ArrayBufferView = js_sys::Float32Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct R32F;
impl ImageFormat for R32F {
type P = [u8; 4];
const NUM_CHANNELS: usize = 4;
const FORMAT: u32 = WebGlRenderingCtx::RGBA as u32;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
const CHANNEL_TYPE: ChannelType = ChannelType::R32F;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
Ok(Bytes::Borrowed(raw_bytes))
}
type ArrayBufferView = js_sys::Uint8Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct R64F;
impl ImageFormat for R64F {
type P = [u8; 4];
const NUM_CHANNELS: usize = 4;
const FORMAT: u32 = WebGlRenderingCtx::RGBA as u32;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
const CHANNEL_TYPE: ChannelType = ChannelType::R32F;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
Ok(Bytes::Borrowed(raw_bytes))
}
type ArrayBufferView = js_sys::Uint8Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[cfg(feature = "webgl2")]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct R8UI;
#[cfg(feature = "webgl2")]
impl ImageFormat for R8UI {
type P = [u8; 1];
const NUM_CHANNELS: usize = 1;
const FORMAT: u32 = WebGlRenderingCtx::RED_INTEGER as u32;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::R8UI as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
const CHANNEL_TYPE: ChannelType = ChannelType::R8UI;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
Ok(Bytes::Borrowed(raw_bytes))
}
type ArrayBufferView = js_sys::Uint8Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[cfg(feature = "webgl2")]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct R16I;
#[cfg(feature = "webgl2")]
impl ImageFormat for R16I {
type P = [i16; 1];
const NUM_CHANNELS: usize = 1;
const FORMAT: u32 = WebGlRenderingCtx::RED_INTEGER as u32;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::R16I as i32;
const TYPE: u32 = WebGlRenderingCtx::SHORT;
const CHANNEL_TYPE: ChannelType = ChannelType::R16I;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
Ok(Bytes::Borrowed(raw_bytes))
}
type ArrayBufferView = js_sys::Int16Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[cfg(feature = "webgl2")]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct R32I;
#[cfg(feature = "webgl2")]
impl ImageFormat for R32I {
type P = [i32; 1];
const NUM_CHANNELS: usize = 1;
const FORMAT: u32 = WebGlRenderingCtx::RED_INTEGER as u32;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::R32I as i32;
const TYPE: u32 = WebGlRenderingCtx::INT;
const CHANNEL_TYPE: ChannelType = ChannelType::R32I;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
Ok(Bytes::Borrowed(raw_bytes))
}
type ArrayBufferView = js_sys::Int32Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum ChannelType {
RGBA32F,
RGB32F,
RGBA8U,
RGB8U,
R32F,
#[cfg(feature = "webgl2")]
R64F,
#[cfg(feature = "webgl2")]
R8UI,
#[cfg(feature = "webgl2")]
R16I,
#[cfg(feature = "webgl2")]
R32I,
}
impl ChannelType {
pub fn is_colored(&self) -> bool {
match self {
ChannelType::RGBA32F
| ChannelType::RGB32F
| ChannelType::RGBA8U
| ChannelType::RGB8U => true,
_ => false,
}
}
}
pub const NUM_CHANNELS: usize = 9;
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub struct ImageFormatType {
pub ext: ImageExt,
pub channel: ChannelType,
pub fmt: PixelType,
}
impl ImageFormatType {
@@ -316,11 +11,11 @@ impl ImageFormatType {
&self.ext
}
pub fn get_channel(&self) -> ChannelType {
self.channel
pub fn get_pixel_format(&self) -> PixelType {
self.fmt
}
pub fn is_colored(&self) -> bool {
self.channel.is_colored()
!matches!(self.ext, ImageExt::Fits)
}
}

View File

@@ -1,13 +1,13 @@
/* ------------------------------------------------------ */
#[derive(Debug)]
pub struct HTMLImage<F> {
image: web_sys::HtmlImageElement,
pub image: web_sys::HtmlImageElement,
format: std::marker::PhantomData<F>,
}
impl<F> HTMLImage<F>
where
F: ImageFormat + Clone,
F: TextureFormat + Clone,
{
pub fn new(image: web_sys::HtmlImageElement) -> Self {
Self {
@@ -15,16 +15,20 @@ where
format: std::marker::PhantomData,
}
}
pub fn element(&self) -> &web_sys::HtmlImageElement {
&self.image
}
}
use crate::image::format::ImageFormat;
use crate::image::Image;
use crate::texture::format::TextureFormat;
use crate::texture::Tex3D;
use cgmath::Vector3;
use wasm_bindgen::JsValue;
impl<F> Image for HTMLImage<F>
where
F: ImageFormat,
F: TextureFormat,
{
fn insert_into_3d_texture<T: Tex3D>(
&self,
@@ -42,4 +46,8 @@ where
Ok(())
}
fn get_size(&self) -> (u32, u32, u32) {
(self.image.width(), self.image.height(), 1)
}
}

View File

@@ -5,6 +5,10 @@ pub mod format;
pub mod html;
pub mod raw;
use crate::image::bitmap::Bitmap;
use crate::image::raw::ImageBuffer;
use crate::texture::format::RGB8U;
use crate::texture::format::RGBA8U;
pub trait ArrayBuffer: AsRef<js_sys::Object> + std::fmt::Debug {
type Item: std::cmp::PartialOrd + Clone + Copy + std::fmt::Debug + cgmath::Zero;
@@ -33,8 +37,7 @@ impl ArrayBuffer for ArrayU8 {
fn empty(size: u32, blank_value: Self::Item) -> Self {
let uint8_arr = js_sys::Uint8Array::new_with_length(size).fill(blank_value, 0, size);
let array = ArrayU8(uint8_arr);
array
ArrayU8(uint8_arr)
}
fn to_vec(&self) -> Vec<Self::Item> {
@@ -65,8 +68,7 @@ impl ArrayBuffer for ArrayI16 {
fn empty(size: u32, blank_value: Self::Item) -> Self {
let int16_arr = js_sys::Int16Array::new_with_length(size).fill(blank_value, 0, size);
let array = ArrayI16(int16_arr);
array
ArrayI16(int16_arr)
}
fn to_vec(&self) -> Vec<Self::Item> {
@@ -97,8 +99,7 @@ impl ArrayBuffer for ArrayI32 {
fn empty(size: u32, blank_value: Self::Item) -> Self {
let int32_arr = js_sys::Int32Array::new_with_length(size).fill(blank_value, 0, size);
let array = ArrayI32(int32_arr);
array
ArrayI32(int32_arr)
}
fn to_vec(&self) -> Vec<Self::Item> {
@@ -129,8 +130,7 @@ impl ArrayBuffer for ArrayF32 {
}
fn empty(size: u32, blank_value: Self::Item) -> Self {
let f32_arr = js_sys::Float32Array::new_with_length(size).fill(blank_value, 0, size);
let array = ArrayF32(f32_arr);
array
ArrayF32(f32_arr)
}
fn to_vec(&self) -> Vec<Self::Item> {
@@ -162,8 +162,7 @@ impl ArrayBuffer for ArrayF64 {
}
fn empty(size: u32, blank_value: Self::Item) -> Self {
let f64_arr = js_sys::Float64Array::new_with_length(size).fill(blank_value, 0, size);
let array = ArrayF64(f64_arr);
array
ArrayF64(f64_arr)
}
fn to_vec(&self) -> Vec<Self::Item> {
@@ -180,6 +179,7 @@ impl ArrayBuffer for ArrayF64 {
}
use self::canvas::Canvas;
use self::fits::FitsImage;
use self::html::HTMLImage;
use wasm_bindgen::JsValue;
pub trait Image {
@@ -190,9 +190,11 @@ pub trait Image {
// An offset to write the image in the texture array
offset: &Vector3<i32>,
) -> Result<(), JsValue>;
fn get_size(&self) -> (u32, u32, u32);
}
impl<'a, I> Image for &'a I
impl<I> Image for &I
where
I: Image,
{
@@ -208,9 +210,15 @@ where
Ok(())
}
#[inline]
fn get_size(&self) -> (u32, u32, u32) {
let image = &**self;
image.get_size()
}
}
use std::{io::Cursor, rc::Rc};
use std::rc::Rc;
impl<I> Image for Rc<I>
where
I: Image,
@@ -227,65 +235,56 @@ where
Ok(())
}
}
/*impl<I> Image for Arc<Mutex<Option<I>>>
where
I: Image,
{
fn tex_sub_image_3d(
&self,
// The texture array
textures: &Texture2DArray,
// An offset to write the image in the texture array
offset: &Vector3<i32>,
) -> Result<(), JsValue> {
if let Some(image) = &*self.lock().unwrap_abort() {
image.tex_sub_image_3d(textures, offset)?;
}
Ok(())
#[inline]
fn get_size(&self) -> (u32, u32, u32) {
let image = &**self;
image.get_size()
}
}*/
#[cfg(feature = "webgl2")]
use crate::image::format::{R16I, R32I, R64F, R8UI};
use crate::{
image::format::{R32F, RGB8U, RGBA8U},
texture::Tex3D,
};
use bitmap::Bitmap;
use fits::Fits;
use raw::ImageBuffer;
#[derive(Debug)]
#[cfg(feature = "webgl2")]
pub enum ImageType {
FitsImage { raw_bytes: js_sys::Uint8Array },
Canvas { canvas: Canvas<RGBA8U> },
ImageRgba8u { image: Bitmap<RGBA8U> },
ImageRgb8u { image: Bitmap<RGB8U> },
HTMLImageRgba8u { image: HTMLImage<RGBA8U> },
HTMLImageRgb8u { image: HTMLImage<RGB8U> },
RawRgb8u { image: ImageBuffer<RGB8U> },
RawRgba8u { image: ImageBuffer<RGBA8U> },
RawR32f { image: ImageBuffer<R32F> },
RawR32i { image: ImageBuffer<R32I> },
RawR16i { image: ImageBuffer<R16I> },
RawR8ui { image: ImageBuffer<R8UI> },
}
#[cfg(feature = "webgl1")]
use crate::texture::format::{R16I, R32F, R32I, R8U};
use crate::texture::Tex3D;
#[derive(Debug)]
pub enum ImageType {
FitsImage { raw_bytes: js_sys::Uint8Array },
Canvas { canvas: Canvas<RGBA8U> },
PngHTMLImageRgba8u { image: HTMLImage<RGBA8U> },
JpgHTMLImageRgb8u { image: HTMLImage<RGB8U> },
PngImageRgba8u { image: Bitmap<RGBA8U> },
JpgImageRgb8u { image: Bitmap<RGB8U> },
RawRgb8u { image: ImageBuffer<RGB8U> },
RawRgba8u { image: ImageBuffer<RGBA8U> },
RawR32f { image: ImageBuffer<R32F> },
FitsRawBytes {
raw_bytes: js_sys::Uint8Array,
size: (u32, u32, u32),
},
Canvas {
canvas: Canvas<RGBA8U>,
},
ImageRgba8u {
image: Bitmap<RGBA8U>,
},
ImageRgb8u {
image: Bitmap<RGB8U>,
},
HTMLImageRgba8u {
image: HTMLImage<RGBA8U>,
},
HTMLImageRgb8u {
image: HTMLImage<RGB8U>,
},
RawRgb8u {
image: ImageBuffer<RGB8U>,
},
RawRgba8u {
image: ImageBuffer<RGBA8U>,
},
RawR32f {
image: ImageBuffer<R32F>,
},
RawR32i {
image: ImageBuffer<R32I>,
},
RawR16i {
image: ImageBuffer<R16I>,
},
RawR8ui {
image: ImageBuffer<R8U>,
},
}
use cgmath::Vector3;
@@ -298,24 +297,24 @@ impl Image for ImageType {
offset: &Vector3<i32>,
) -> Result<(), JsValue> {
match self {
ImageType::FitsImage {
ImageType::FitsRawBytes {
raw_bytes: raw_bytes_buf,
..
} => {
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 raw_bytes = raw_bytes_buf.to_vec();
let mut bytes_reader = Cursor::new(raw_bytes.as_slice());
let fits_img = Fits::from_byte_slice(&mut bytes_reader)?;
fits_img.insert_into_3d_texture(textures, offset)?
let images = FitsImage::from_raw_bytes(&raw_bytes)?;
for image in images {
image.insert_into_3d_texture(textures, offset)?
}
}
ImageType::Canvas { canvas } => canvas.insert_into_3d_texture(textures, offset)?,
ImageType::ImageRgba8u { image } => image.insert_into_3d_texture(textures, offset)?,
ImageType::ImageRgb8u { image } => image.insert_into_3d_texture(textures, offset)?,
ImageType::HTMLImageRgba8u { image } => {
ImageType::HTMLImageRgba8u { image, .. } => {
image.insert_into_3d_texture(textures, offset)?
}
ImageType::HTMLImageRgb8u { image } => {
ImageType::HTMLImageRgb8u { image, .. } => {
image.insert_into_3d_texture(textures, offset)?
}
ImageType::RawRgb8u { image } => image.insert_into_3d_texture(textures, offset)?,
@@ -328,4 +327,21 @@ impl Image for ImageType {
Ok(())
}
fn get_size(&self) -> (u32, u32, u32) {
match self {
ImageType::FitsRawBytes { size, .. } => *size,
ImageType::Canvas { canvas } => canvas.get_size(),
ImageType::ImageRgba8u { image } => image.get_size(),
ImageType::ImageRgb8u { image } => image.get_size(),
ImageType::HTMLImageRgba8u { image } => image.get_size(),
ImageType::HTMLImageRgb8u { image } => image.get_size(),
ImageType::RawRgb8u { image } => image.get_size(),
ImageType::RawRgba8u { image } => image.get_size(),
ImageType::RawR32f { image } => image.get_size(),
ImageType::RawR32i { image } => image.get_size(),
ImageType::RawR16i { image } => image.get_size(),
ImageType::RawR8ui { image } => image.get_size(),
}
}
}

View File

@@ -1,17 +1,18 @@
use crate::image::format::ImageFormat;
use crate::texture::format::TextureFormat;
use crate::texture::pixel::Pixel;
use crate::texture::Tex3D;
#[derive(Debug)]
#[allow(dead_code)]
pub struct ImageBuffer<T>
where
T: ImageFormat,
T: TextureFormat,
{
pub data: Vec<<<T as ImageFormat>::P as Pixel>::Item>,
pub size: Vector2<i32>,
pub data: Box<[<<T as TextureFormat>::P as Pixel>::Item]>,
pub size: (u32, u32, u32),
}
use crate::image::format::Bytes;
use crate::texture::format::Bytes;
pub struct ImageBufferView {
pub x: i32,
@@ -22,56 +23,66 @@ pub struct ImageBufferView {
use wasm_bindgen::JsValue;
impl<T> ImageBuffer<T>
where
T: ImageFormat,
T: TextureFormat,
{
pub fn new(data: Vec<<<T as ImageFormat>::P as Pixel>::Item>, width: i32, height: i32) -> Self {
let size_buf = width * height * (T::NUM_CHANNELS as i32);
debug_assert!(size_buf == data.len() as i32);
pub fn new(
data: Box<[<<T as TextureFormat>::P as Pixel>::Item]>,
width: u32,
height: u32,
depth: u32,
) -> Self {
let size_buf = width * height * depth * (T::NUM_CHANNELS as u32);
debug_assert!(size_buf == data.len() as u32);
//let buf = <<T as ImageFormat>::P as Pixel>::Container::new(buf);
let size = Vector2::new(width, height);
let size = (width, height, depth);
Self { data, size }
}
pub fn from_encoded_raw_bytes(
raw_bytes: &[u8],
width: i32,
height: i32,
width: u32,
height: u32,
) -> Result<Self, JsValue> {
let mut decoded_bytes = match T::decode(raw_bytes).map_err(|e| JsValue::from_str(e))? {
let mut decoded_bytes = match T::decode(raw_bytes).map_err(JsValue::from_str)? {
Bytes::Borrowed(bytes) => bytes.to_vec(),
Bytes::Owned(bytes) => bytes,
};
let decoded_pixels = unsafe {
decoded_bytes.set_len(
decoded_bytes.len() / std::mem::size_of::<<<T as ImageFormat>::P as Pixel>::Item>(),
decoded_bytes.len()
/ std::mem::size_of::<<<T as TextureFormat>::P as Pixel>::Item>(),
);
std::mem::transmute(decoded_bytes)
std::mem::transmute::<Vec<u8>, Vec<<<T as TextureFormat>::P as Pixel>::Item>>(
decoded_bytes,
)
.into_boxed_slice()
};
Ok(Self::new(decoded_pixels, width, height))
Ok(Self::new(decoded_pixels, width, height, 1))
}
pub fn from_raw_bytes(mut raw_bytes: Vec<u8>, width: i32, height: i32) -> Self {
let size_buf = width * height * (std::mem::size_of::<T::P>() as i32);
debug_assert!(size_buf == raw_bytes.len() as i32);
pub fn from_raw_bytes(mut raw_bytes: Vec<u8>, width: u32, height: u32) -> Self {
let size_buf = width * height * (std::mem::size_of::<T::P>() as u32);
debug_assert!(size_buf == raw_bytes.len() as u32);
let decoded_pixels = unsafe {
raw_bytes.set_len(
raw_bytes.len() / std::mem::size_of::<<<T as ImageFormat>::P as Pixel>::Item>(),
);
std::mem::transmute(raw_bytes)
raw_bytes.set_len(raw_bytes.len() / std::mem::size_of::<<T::P as Pixel>::Item>());
std::mem::transmute::<Vec<u8>, Vec<<T::P as Pixel>::Item>>(raw_bytes).into_boxed_slice()
};
Self::new(decoded_pixels, width, height)
Self::new(decoded_pixels, width, height, 1)
}
pub fn empty() -> Self {
let size = Vector2::new(0, 0);
Self { data: vec![], size }
let size = (0, 0, 0);
Self {
data: Box::new([]),
size,
}
}
pub fn allocate(pixel_fill: &<T as ImageFormat>::P, width: i32, height: i32) -> ImageBuffer<T> {
pub fn allocate(pixel_fill: &T::P, width: u32, height: u32) -> ImageBuffer<T> {
let size_buf = ((width * height) as usize) * (T::NUM_CHANNELS);
let data = pixel_fill
@@ -80,9 +91,10 @@ where
.cloned()
.cycle()
.take(size_buf)
.collect::<Vec<_>>();
.collect::<Vec<_>>()
.into_boxed_slice();
ImageBuffer::<T>::new(data, width, height)
ImageBuffer::<T>::new(data, width, height, 1)
}
pub fn tex_sub(&mut self, src: &Self, s: &ImageBufferView, d: &ImageBufferView) {
@@ -91,8 +103,8 @@ where
for ix in s.x..(s.x + s.w) {
for iy in s.y..(s.y + s.h) {
let s_idx = (iy * src.width() + ix) as usize;
let d_idx = (di * self.width() + dj) as usize;
let s_idx = ((iy * src.width() as i32) + ix) as usize;
let d_idx = ((di * self.width() as i32) + dj) as usize;
for i in 0..T::NUM_CHANNELS {
let si = s_idx * T::NUM_CHANNELS + i;
@@ -110,38 +122,38 @@ where
}
}
pub fn iter(&self) -> impl Iterator<Item = &<<T as ImageFormat>::P as Pixel>::Item> {
pub fn iter(&self) -> impl Iterator<Item = &<T::P as Pixel>::Item> {
self.data.iter()
}
pub fn get_data(&self) -> &[<<T as ImageFormat>::P as Pixel>::Item] {
pub fn get_data(&self) -> &[<T::P as Pixel>::Item] {
&self.data
}
pub fn width(&self) -> i32 {
self.size.x
pub fn width(&self) -> u32 {
self.size.0
}
pub fn height(&self) -> i32 {
self.size.y
pub fn height(&self) -> u32 {
self.size.1
}
}
use crate::image::format::{R16I, R32F, R32I, R8UI, RGB8U, RGBA8U};
use crate::texture::format::{R16I, R32F, R32I, R8U, RGB8U, RGBA8U};
pub enum ImageBufferType {
JPG(ImageBuffer<RGB8U>),
PNG(ImageBuffer<RGBA8U>),
R32F(ImageBuffer<R32F>),
R8UI(ImageBuffer<R8UI>),
R8UI(ImageBuffer<R8U>),
R16I(ImageBuffer<R16I>),
R32I(ImageBuffer<R32I>),
}
use crate::image::{ArrayBuffer, Image};
use cgmath::{Vector2, Vector3};
use cgmath::Vector3;
impl<I> Image for ImageBuffer<I>
where
I: ImageFormat,
I: TextureFormat,
{
fn insert_into_3d_texture<T: Tex3D>(
&self,
@@ -150,15 +162,14 @@ where
// An offset to write the image in the texture array
offset: &Vector3<i32>,
) -> Result<(), JsValue> {
let js_array =
<<<I as ImageFormat>::P as Pixel>::Container as ArrayBuffer>::new(&self.data);
let js_array = <<I::P as Pixel>::Container as ArrayBuffer>::new(&self.data);
textures.tex_sub_image_3d_with_opt_array_buffer_view(
offset.x,
offset.y,
offset.z,
self.width(),
self.height(),
1,
self.width() as i32,
self.height() as i32,
self.size.2 as i32,
Some(js_array.as_ref()),
);
@@ -166,7 +177,7 @@ where
}
// The size of the image
/*fn get_size(&self) -> &Vector2<i32> {
&self.size
}*/
fn get_size(&self) -> (u32, u32, u32) {
self.size
}
}

View File

@@ -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;

View File

@@ -6,6 +6,11 @@ extern "C" {
pub fn log(s: &str);
}
#[macro_export]
macro_rules! al_print {
($($arg:tt)*) => { al_core::log(&format!("{:?}", $($arg),*)) };
}
// ----------------------------------------------------------------------------
// Helpers to hide some of the verbosity of web_sys

View File

@@ -93,8 +93,6 @@ pub trait VertexAttribPointerType: std::marker::Sized {
}
}
use crate::webgl_ctx::WebGlRenderingCtx;
use js_sys::WebAssembly;
use wasm_bindgen::JsCast;
impl VertexAttribPointerType for u8 {
type ArrayBufferView = js_sys::Uint8Array;
@@ -308,7 +306,7 @@ impl VertexAttribPointerType for f32 {
type ArrayBufferView = Float32Array;
fn array_buffer_view<'a, B: BufferDataStorage<'a, Self>>(data: B) -> Self::ArrayBufferView {
let data = data.get_slice();
/*let data = data.get_slice();
//unsafe { Self::ArrayBufferView::view(&data) }
let memory_buffer = wasm_bindgen::memory()
.unchecked_ref::<WebAssembly::Memory>()
@@ -316,7 +314,9 @@ impl VertexAttribPointerType for f32 {
let len = data.len();
let ptr = data.as_ptr() as u32 / 4;
Float32Array::new(&memory_buffer).subarray(ptr, ptr + len as u32)
Float32Array::new(&memory_buffer).subarray(ptr, ptr + len as u32)*/
let data = data.get_slice();
unsafe { Self::ArrayBufferView::view(data) }
}
fn buffer_sub_data_with_i32_and_array_buffer_view<'a, B: BufferDataStorage<'a, Self>>(
@@ -409,9 +409,9 @@ impl ArrayBuffer {
}
}
pub fn set_vertex_attrib_pointer_by_name<'a, T: VertexAttribPointerType>(
pub fn set_vertex_attrib_pointer_by_name<T: VertexAttribPointerType>(
&self,
shader: &ShaderBound<'a>,
shader: &ShaderBound<'_>,
location: &str,
) {
let loc = shader.get_attrib_location(&self.gl, location);
@@ -434,11 +434,7 @@ impl ArrayBuffer {
.vertex_attrib_divisor_angle(loc as u32, 0);
}
pub fn disable_vertex_attrib_pointer_by_name<'a>(
&self,
shader: &ShaderBound<'a>,
location: &str,
) {
pub fn disable_vertex_attrib_pointer_by_name(&self, shader: &ShaderBound<'_>, location: &str) {
let loc = shader.get_attrib_location(&self.gl, location);
self.gl.disable_vertex_attrib_array(loc as u32);
}
@@ -466,6 +462,30 @@ impl ArrayBuffer {
);
}
}
/*pub fn update_from_js_array<'a, T: VertexAttribPointerType>(
&mut self,
usage: u32,
data: T::ArrayBufferView,
) {
self.bind();
if self.len >= data.len() {
T::buffer_sub_data_with_i32_and_array_buffer_view(
&self.gl,
data,
WebGlRenderingCtx::ARRAY_BUFFER,
);
} else {
self.len = data.len();
T::buffer_data_with_array_buffer_view(
&self.gl,
data,
WebGlRenderingCtx::ARRAY_BUFFER,
usage,
);
}
}*/
}
impl VertexBufferObject for ArrayBuffer {

View File

@@ -49,7 +49,7 @@ impl ArrayBufferInstanced {
// Total length
let num_f32_in_buf = data.len() as i32;
let num_instances = num_f32_in_buf / (num_f32_per_instance as i32);
let num_instances = num_f32_in_buf / num_f32_per_instance;
let len = data.len();
let buffer = gl
@@ -98,9 +98,9 @@ impl ArrayBufferInstanced {
}
}
pub fn set_vertex_attrib_pointer_by_name<'a, T: VertexAttribPointerType>(
pub fn set_vertex_attrib_pointer_by_name<T: VertexAttribPointerType>(
&self,
shader: &ShaderBound<'a>,
shader: &ShaderBound<'_>,
location: &str,
) {
let loc = shader.get_attrib_location(&self.gl, location);
@@ -124,11 +124,7 @@ impl ArrayBufferInstanced {
.vertex_attrib_divisor_angle(loc as u32, 1);
}
pub fn disable_vertex_attrib_pointer_by_name<'a>(
&self,
shader: &ShaderBound<'a>,
location: &str,
) {
pub fn disable_vertex_attrib_pointer_by_name(&self, shader: &ShaderBound<'_>, location: &str) {
let loc = shader.get_attrib_location(&self.gl, location);
self.gl.disable_vertex_attrib_array(loc as u32);

View File

@@ -37,7 +37,10 @@ impl ElementArrayBuffer {
usage: u32,
data: B,
) -> ElementArrayBuffer {
let buffer = gl.create_buffer().ok_or("failed to create buffer").unwrap_abort();
let buffer = gl
.create_buffer()
.ok_or("failed to create buffer")
.unwrap_abort();
// Bind the buffer
gl.bind_buffer(
WebGlRenderingCtx::ELEMENT_ARRAY_BUFFER,

View File

@@ -2,7 +2,7 @@ use {wasm_bindgen::prelude::*, web_sys::WebGlFramebuffer};
use crate::webgl_ctx::WebGlRenderingCtx;
// Internal format used for the framebuffer final texture
use crate::image::format::RGBA8U;
use crate::texture::format::RGBA8U;
pub struct FrameBufferObject {
gl: WebGlContext,

View File

@@ -117,7 +117,7 @@ pub mod vao {
}
use web_sys::WebGl2RenderingContext;
impl<'a, 'b> ShaderVertexArrayObjectBound<'a, 'b> {
impl<'a> ShaderVertexArrayObjectBound<'a, '_> {
pub fn update_array<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
attr: &'static str,
@@ -132,6 +132,20 @@ pub mod vao {
self
}
/*pub fn update_from_js_array<T: VertexAttribPointerType>(
&mut self,
attr: &'static str,
usage: u32,
js_array: T::ArrayBufferView,
) -> &mut Self {
self.vao
.array_buffer
.get_mut(attr)
.unwrap_abort()
.update_from_js_array::<T>(usage, js_array);
self
}*/
pub fn update_element_array<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
usage: u32,
@@ -162,7 +176,7 @@ pub mod vao {
}
}
impl<'a, 'b> Drop for ShaderVertexArrayObjectBound<'a, 'b> {
impl Drop for ShaderVertexArrayObjectBound<'_, '_> {
fn drop(&mut self) {
self.unbind();
}
@@ -173,7 +187,7 @@ pub mod vao {
_shader: &'b ShaderBound<'b>,
}
impl<'a, 'b> ShaderVertexArrayObjectBoundRef<'a, 'b> {
impl ShaderVertexArrayObjectBoundRef<'_, '_> {
pub fn draw_arrays(&self, mode: u32, byte_offset: i32, size: i32) {
self.vao.gl.draw_arrays(mode, byte_offset, size);
}
@@ -211,7 +225,7 @@ pub mod vao {
}
}
impl<'a, 'b> Drop for ShaderVertexArrayObjectBoundRef<'a, 'b> {
impl Drop for ShaderVertexArrayObjectBoundRef<'_, '_> {
fn drop(&mut self) {
self.unbind();
}
@@ -362,373 +376,7 @@ pub mod vao {
}
}
impl<'a> Drop for VertexArrayObjectBound<'a> {
fn drop(&mut self) {
self.unbind();
}
}
}
#[cfg(feature = "webgl1")]
pub mod vao {
use crate::object::array_buffer::ArrayBuffer;
use crate::object::array_buffer_instanced::ArrayBufferInstanced;
use crate::object::buffer_data::BufferDataStorage;
use crate::object::element_array_buffer::ElementArrayBuffer;
use crate::webgl_ctx::WebGlContext;
use crate::Abort;
use std::collections::HashMap;
pub struct VertexArrayObject {
array_buffer: HashMap<&'static str, ArrayBuffer>,
array_buffer_instanced: HashMap<&'static str, ArrayBufferInstanced>,
element_array_buffer: Option<ElementArrayBuffer>,
idx: u32, // Number of vertex attributes
gl: WebGlContext,
}
impl VertexArrayObject {
pub fn new(gl: &WebGlContext) -> VertexArrayObject {
let array_buffer = HashMap::new();
let array_buffer_instanced = HashMap::new();
let element_array_buffer = None;
let idx = 0;
let gl = gl.clone();
VertexArrayObject {
array_buffer,
array_buffer_instanced,
element_array_buffer,
idx,
gl,
}
}
// Shader has to be already bound before calling this
// This returns a ShaderVertexArrayObjectBound for which it is possible
// to add some buffers and or draw the buffers
pub fn bind<'a, 'b>(
&'a mut self,
_shader: &'b ShaderBound<'b>,
) -> ShaderVertexArrayObjectBound<'a, 'b> {
//self.gl.bind_vertex_array(Some(self.vao.as_ref()));
ShaderVertexArrayObjectBound { vao: self, _shader }
}
// Shader has to be already bound before calling this
// This returns a ShaderVertexArrayObjectBound for which it is possible
// to add some buffers and or draw the buffers
pub fn bind_ref<'a, 'b>(
&'a self,
shader: &'b ShaderBound<'b>,
) -> ShaderVertexArrayObjectBoundRef<'a, 'b> {
//self.gl.bind_vertex_array(Some(self.vao.as_ref()));
ShaderVertexArrayObjectBoundRef { vao: self, shader }
}
// No need to bind a shader here
// This returns a VertexArrayObjectBound for which it is only possible to
// update the buffers
pub fn bind_for_update<'a>(&'a mut self) -> VertexArrayObjectBound<'a> {
//self.gl.bind_vertex_array(Some(self.vao.as_ref()));
VertexArrayObjectBound { vao: self }
}
/*pub fn bind_ref(&self) {
self.gl.bind_vertex_array(Some(self.vao.as_ref()));
}*/
pub fn num_elements(&self) -> usize {
self.element_array_buffer
.as_ref()
.unwrap_abort()
.num_elements()
}
pub fn num_instances(&self) -> i32 {
self.array_buffer_instanced
.values()
.next()
.unwrap_abort()
.num_instances()
}
}
impl Drop for VertexArrayObject {
fn drop(&mut self) {
//self.unbind();
//self.gl.delete_vertex_array(Some(self.vao.as_ref()));
}
}
use crate::shader::ShaderBound;
pub struct ShaderVertexArrayObjectBound<'a, 'b> {
vao: &'a mut VertexArrayObject,
_shader: &'b ShaderBound<'b>,
}
use crate::VertexAttribPointerType;
impl<'a, 'b> ShaderVertexArrayObjectBound<'a, 'b> {
pub fn update_array<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
attr: &'static str,
usage: u32,
array_data: B,
) -> &mut Self {
self.vao
.array_buffer
.get_mut(attr)
.unwrap_abort()
.update(usage, array_data);
self
}
pub fn update_element_array<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
usage: u32,
element_data: B,
) -> &mut Self {
if let Some(ref mut element_array_buffer) = self.vao.element_array_buffer {
element_array_buffer.update(usage, element_data);
}
self
}
pub fn update_instanced_array<B: BufferDataStorage<'a, f32>>(
&mut self,
attr: &'static str,
array_data: B,
) -> &mut Self {
self.vao
.array_buffer_instanced
.get_mut(attr)
.unwrap_abort()
.update(array_data);
self
}
pub fn unbind(&self) {
//self.vao.gl.bind_vertex_array(None);
}
}
impl<'a, 'b> Drop for ShaderVertexArrayObjectBound<'a, 'b> {
fn drop(&mut self) {
self.unbind();
}
}
use crate::webgl_ctx::WebGlRenderingCtx;
pub struct ShaderVertexArrayObjectBoundRef<'a, 'b> {
vao: &'a VertexArrayObject,
shader: &'b ShaderBound<'b>,
}
use crate::object::array_buffer::VertexBufferObject;
impl<'a, 'b> ShaderVertexArrayObjectBoundRef<'a, 'b> {
pub fn draw_arrays(&self, mode: u32, byte_offset: i32, size: i32) {
for (attr, buf) in self.vao.array_buffer.iter() {
buf.bind();
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
}
self.vao.gl.draw_arrays(mode, byte_offset, size);
}
pub fn draw_elements_with_i32(
&self,
mode: u32,
num_elements: Option<i32>,
type_: u32,
byte_offset: i32,
) {
for (attr, buf) in self.vao.array_buffer.iter() {
buf.bind();
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
}
let e = self.vao.element_array_buffer.as_ref().unwrap_abort();
e.bind();
let num_elements = num_elements.unwrap_or(self.vao.num_elements() as i32);
self.vao
.gl
.draw_elements_with_i32(mode, num_elements, type_, byte_offset);
}
pub fn draw_elements_instanced_with_i32(
&self,
mode: u32,
offset_element_idx: i32,
num_instances: i32,
) {
for (attr, buf) in self.vao.array_buffer.iter() {
buf.bind();
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
}
for (attr, inst_buf) in self.vao.array_buffer_instanced.iter() {
inst_buf.bind();
inst_buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
}
let e = self.vao.element_array_buffer.as_ref().unwrap_abort();
e.bind();
self.vao
.gl
.ext
.angles
.draw_elements_instanced_angle_with_i32(
mode,
self.vao.num_elements() as i32,
WebGlRenderingCtx::UNSIGNED_SHORT,
offset_element_idx,
num_instances,
);
}
pub fn unbind(&self) {
//self.vao.gl.bind_vertex_array(None);
}
}
impl<'a, 'b> Drop for ShaderVertexArrayObjectBoundRef<'a, 'b> {
fn drop(&mut self) {
self.unbind();
}
}
// Struct defined when only the Vertex Array Object is
// defined
pub struct VertexArrayObjectBound<'a> {
vao: &'a mut VertexArrayObject,
}
impl<'a> VertexArrayObjectBound<'a> {
/// Precondition: self must be bound
pub fn add_array_buffer<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
size: usize,
attr: &'static str,
usage: u32,
data: B,
) -> &mut Self {
let array_buffer =
ArrayBuffer::new(&self.vao.gl, self.vao.idx, 0, &[size], &[0], usage, data);
// Update the number of vertex attrib
self.vao.idx += 1;
self.vao.array_buffer.insert(attr, array_buffer);
self
}
/// Precondition: self must be bound
pub fn add_instanced_array_buffer<B: BufferDataStorage<'a, f32>>(
&mut self,
size: usize,
attr: &'static str,
usage: u32,
data: B,
) -> &mut Self {
let array_buffer = ArrayBufferInstanced::new(
&self.vao.gl,
self.vao.idx,
0,
&[size],
&[0],
usage,
data,
);
// Update the number of vertex attrib
self.vao.idx += 1;
self.vao.array_buffer_instanced.insert(attr, array_buffer);
self
}
/// Precondition: self must be bound
pub fn add_element_buffer<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
usage: u32,
data: B,
) -> &mut Self {
let element_buffer = ElementArrayBuffer::new(&self.vao.gl, usage, data);
self.vao.element_array_buffer = Some(element_buffer);
self
}
pub fn update_array<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
attr: &'static str,
usage: u32,
array_data: B,
) -> &mut Self {
self.vao
.array_buffer
.get_mut(attr)
.expect("cannot get attribute from the array buffer")
.update(usage, array_data);
self
}
pub fn update_element_array<T: VertexAttribPointerType, B: BufferDataStorage<'a, T>>(
&mut self,
usage: u32,
element_data: B,
) -> &mut Self {
if let Some(ref mut element_array_buffer) = self.vao.element_array_buffer {
element_array_buffer.update(usage, element_data);
}
self
}
pub fn update_instanced_array<B: BufferDataStorage<'a, f32>>(
&mut self,
attr: &'static str,
usage: u32,
array_data: B,
) -> &mut Self {
self.vao
.array_buffer_instanced
.get_mut(attr)
.expect("cannot get attribute from the array buffer")
.update(usage, array_data);
self
}
/*pub fn append_to_instanced_array<B: BufferDataStorage<'a, f32>>(
&mut self,
idx: usize,
buffer: B,
) -> &mut Self {
self.vao.array_buffer_instanced[idx].append(buffer);
self
}*/
pub fn unbind(&self) {
//self.vao.gl.bind_vertex_array(None);
}
}
impl<'a> Drop for VertexArrayObjectBound<'a> {
impl Drop for VertexArrayObjectBound<'_> {
fn drop(&mut self) {
self.unbind();
}

View File

@@ -112,7 +112,7 @@ impl Shader {
pub trait UniformType {
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self);
fn attach_uniform<'a>(name: &str, value: &Self, shader: &ShaderBound<'a>) {
fn attach_uniform(name: &str, value: &Self, shader: &ShaderBound<'_>) {
let location = shader.get_uniform_location(name);
Self::uniform(&shader.gl, location, value);
}
@@ -256,7 +256,7 @@ impl UniformType for ColorRGB {
gl.uniform3f(location, value.r, value.g, value.b);
}
}
impl<'a> UniformType for &'a ColorRGB {
impl UniformType for &ColorRGB {
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) {
gl.uniform3f(location, value.r, value.g, value.b);
}
@@ -268,7 +268,7 @@ impl UniformType for ColorRGBA {
gl.uniform4f(location, value.r, value.g, value.b, value.a);
}
}
impl<'a> UniformType for &'a ColorRGBA {
impl UniformType for &ColorRGBA {
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) {
gl.uniform4f(location, value.r, value.g, value.b, value.a);
}
@@ -328,8 +328,9 @@ impl SendUniformsWithParams<Colormaps> for HiPSColor {
) -> &'a ShaderBound<'a> {
let reversed = self.reversed as u8 as f32;
let cmap = cmaps.get(&self.cmap_name.as_ref());
let cmap = cmaps.get(self.cmap_name.as_ref());
shader
.attach_uniforms_from(cmaps)
.attach_uniforms_with_params_from(cmap, cmaps)
.attach_uniform("H", &self.stretch)
.attach_uniform("min_value", &self.min_cut.unwrap_or(0.0))
@@ -407,7 +408,7 @@ impl<'a> ShaderBound<'a> {
}
}
impl<'a> Drop for ShaderBound<'a> {
impl Drop for ShaderBound<'_> {
fn drop(&mut self) {
self.unbind(&self.gl);
}

View File

@@ -1,4 +1,4 @@
use crate::image::format::ImageFormat;
use crate::texture::format::TextureFormat;
use web_sys::HtmlCanvasElement;
use web_sys::WebGlTexture;
@@ -19,11 +19,10 @@ pub struct Texture3D {
texture: Option<WebGlTexture>,
metadata: Option<Rc<RefCell<Texture2DMeta>>>,
_depth: i32,
}
impl Texture3D {
pub fn create_empty<F: ImageFormat>(
pub fn create_empty<F: TextureFormat>(
gl: &WebGlContext,
// The weight of the individual textures
width: i32,
@@ -54,16 +53,14 @@ impl Texture3D {
let metadata = Some(Rc::new(RefCell::new(Texture2DMeta {
width: width as u32,
height: height as u32,
internal_format: F::INTERNAL_FORMAT,
format: F::FORMAT,
ty: F::TYPE,
channel_type: F::CHANNEL_TYPE
pixel_type: F::PIXEL_TYPE,
})));
Ok(Texture3D {
texture,
gl: gl.clone(),
_depth: depth,
metadata,
})
}
@@ -72,7 +69,7 @@ impl Texture3D {
self.gl.generate_mipmap(WebGlRenderingCtx::TEXTURE_3D);
}
pub fn bind(&self) -> Texture3DBound {
pub fn bind(&self) -> Texture3DBound<'_> {
self.gl
.bind_texture(WebGlRenderingCtx::TEXTURE_3D, self.texture.as_ref());
@@ -114,7 +111,7 @@ pub struct Texture3DBound<'a> {
tex: &'a Texture3D,
}
impl<'a> Texture3DBound<'a> {
impl Texture3DBound<'_> {
pub fn tex_sub_image_3d_with_html_image_element(
&self,
dx: i32,
@@ -196,6 +193,7 @@ impl<'a> Texture3DBound<'a> {
.expect("Sub texture 2d");
}
#[allow(clippy::too_many_arguments)]
pub fn tex_sub_image_3d_with_opt_array_buffer_view(
&self,
dx: i32,
@@ -227,6 +225,7 @@ impl<'a> Texture3DBound<'a> {
}
#[allow(dead_code)]
#[allow(clippy::too_many_arguments)]
pub fn tex_sub_image_3d_with_opt_u8_array(
&self,
idx: i32,

View File

@@ -1,4 +1,5 @@
use crate::image::format::ImageFormat;
use crate::texture::format::PixelType;
use crate::texture::format::TextureFormat;
use web_sys::HtmlCanvasElement;
use web_sys::WebGlTexture;
@@ -11,7 +12,6 @@ use std::cell::RefCell;
use std::rc::Rc;
use wasm_bindgen::prelude::*;
use web_sys::HtmlImageElement;
use crate::texture::ChannelType;
pub struct Texture2DArray {
gl: WebGlContext,
@@ -22,7 +22,7 @@ pub struct Texture2DArray {
}
impl Texture2DArray {
pub fn create_empty<F: ImageFormat>(
pub fn create_empty<F: TextureFormat>(
gl: &WebGlContext,
// The weight of the individual textures
width: i32,
@@ -53,10 +53,9 @@ impl Texture2DArray {
let metadata = Some(Rc::new(RefCell::new(Texture2DMeta {
width: width as u32,
height: height as u32,
internal_format: F::INTERNAL_FORMAT,
format: F::FORMAT,
pixel_type: F::PIXEL_TYPE,
ty: F::TYPE,
channel_type: F::CHANNEL_TYPE
format: F::FORMAT,
})));
Ok(Texture2DArray {
@@ -71,7 +70,7 @@ impl Texture2DArray {
self.gl.generate_mipmap(WebGlRenderingCtx::TEXTURE_2D_ARRAY);
}
pub fn bind(&self) -> Texture2DArrayBound {
pub fn bind(&self) -> Texture2DArrayBound<'_> {
self.gl
.bind_texture(WebGlRenderingCtx::TEXTURE_2D_ARRAY, self.texture.as_ref());
@@ -92,7 +91,7 @@ impl Texture2DArray {
// Attach the texture as the first color attachment
self.gl.framebuffer_texture_layer(
WebGlRenderingCtx::READ_FRAMEBUFFER,
WebGlRenderingCtx::FRAMEBUFFER,
WebGlRenderingCtx::COLOR_ATTACHMENT0,
self.texture.as_ref(),
0,
@@ -116,37 +115,31 @@ impl Texture2DArray {
self.gl
.viewport(0, 0, metadata.width as i32, metadata.height as i32);
#[cfg(feature = "webgl2")]
let value = match metadata.channel_type {
ChannelType::R8UI => {
let value = match metadata.pixel_type {
PixelType::R8U => {
let p = <[u8; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
ChannelType::R16I => {
PixelType::R16I => {
let p = <[i16; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
ChannelType::R32I => {
PixelType::R32I => {
let p = <[i32; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
ChannelType::R32F => {
PixelType::R32F => {
let p = <[f32; 1]>::read_pixel(&self.gl, x, y)?;
crate::log(&format!("{:?}", p));
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
ChannelType::RGB8U => {
PixelType::RGB8U => {
let p = <[u8; 3]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p)?)
}
ChannelType::RGBA8U => {
PixelType::RGBA8U => {
let p = <[u8; 4]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p)?)
}
_ => Err(JsValue::from_str(
"Pixel retrieval not implemented for that texture format.",
)),
};
// Unbind the framebuffer
@@ -198,7 +191,7 @@ pub struct Texture2DArrayBound<'a> {
tex: &'a Texture2DArray,
}
impl<'a> Texture2DArrayBound<'a> {
impl Texture2DArrayBound<'_> {
pub fn tex_sub_image_3d_with_html_image_element(
&self,
dx: i32,

View File

@@ -0,0 +1,205 @@
use crate::texture::pixel::Pixel;
pub type Bytes<'a> = std::borrow::Cow<'a, [u8]>;
pub trait TextureFormat {
type P: Pixel;
type ArrayBufferView: AsRef<js_sys::Object>;
const NUM_CHANNELS: usize;
const FORMAT: u32;
const INTERNAL_FORMAT: i32;
const TYPE: u32;
const PIXEL_TYPE: PixelType;
/// Creates a JS typed array which is a view into wasm's linear memory at the slice specified.
/// This function returns a new typed array which is a view into wasm's memory. This view does not copy the underlying data.
///
/// # Safety
///
/// Views into WebAssembly memory are only valid so long as the backing buffer isn't resized in JS. Once this function is called any future calls to Box::new (or malloc of any form) may cause the returned value here to be invalidated. Use with caution!
///
/// Additionally the returned object can be safely mutated but the input slice isn't guaranteed to be mutable.
///
/// Finally, the returned object is disconnected from the input slice's lifetime, so there's no guarantee that the data is read at the right time.
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str>;
}
use crate::webgl_ctx::WebGlRenderingCtx;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct RGB8U;
impl TextureFormat for RGB8U {
type P = [u8; 3];
const NUM_CHANNELS: usize = 3;
const FORMAT: u32 = WebGlRenderingCtx::RGB;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGB8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
const PIXEL_TYPE: PixelType = PixelType::RGB8U;
fn decode(_raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
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))*/
}
type ArrayBufferView = js_sys::Uint8Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct RGBA8U;
impl TextureFormat for RGBA8U {
type P = [u8; 4];
const NUM_CHANNELS: usize = 4;
const FORMAT: u32 = WebGlRenderingCtx::RGBA;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
const PIXEL_TYPE: PixelType = PixelType::RGBA8U;
fn decode(_raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
/*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;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct R32F;
impl TextureFormat for R32F {
type P = [u8; 4];
const NUM_CHANNELS: usize = 4;
const FORMAT: u32 = WebGlRenderingCtx::RGBA;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
const PIXEL_TYPE: PixelType = PixelType::R32F;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
Ok(Bytes::Borrowed(raw_bytes))
}
type ArrayBufferView = js_sys::Uint8Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct R8U;
impl TextureFormat for R8U {
type P = [u8; 1];
const FORMAT: u32 = WebGlRenderingCtx::RED;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::R8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
const NUM_CHANNELS: usize = 1;
const PIXEL_TYPE: PixelType = PixelType::R8U;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
Ok(Bytes::Borrowed(raw_bytes))
}
type ArrayBufferView = js_sys::Uint8Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct R16I;
impl TextureFormat for R16I {
type P = [u8; 2];
const NUM_CHANNELS: usize = 2;
const FORMAT: u32 = WebGlRenderingCtx::RG;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RG8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
const PIXEL_TYPE: PixelType = PixelType::R16I;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
Ok(Bytes::Borrowed(raw_bytes))
}
type ArrayBufferView = js_sys::Uint8Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct R32I;
impl TextureFormat for R32I {
type P = [u8; 4];
const FORMAT: u32 = WebGlRenderingCtx::RGBA;
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA8 as i32;
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
const NUM_CHANNELS: usize = 4;
const PIXEL_TYPE: PixelType = PixelType::R32I;
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
Ok(Bytes::Borrowed(raw_bytes))
}
type ArrayBufferView = js_sys::Uint8Array;
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
Self::ArrayBufferView::view(s)
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum PixelType {
R8U,
R16I,
R32I,
R32F,
RGB8U,
RGBA8U,
}
impl PixelType {
pub const fn num_channels(&self) -> usize {
match self {
Self::RGB8U => 3,
Self::RGBA8U => 4,
_ => 1,
}
}
}

View File

@@ -1,6 +1,7 @@
pub mod array;
pub use array::Texture2DArray;
pub mod format;
pub mod pixel;
pub use pixel::*;
@@ -11,7 +12,7 @@ pub use mod_3d::Texture3D;
use web_sys::HtmlCanvasElement;
use web_sys::WebGlTexture;
use crate::image::format::ChannelType;
use crate::texture::format::PixelType;
use crate::webgl_ctx::WebGlContext;
use crate::webgl_ctx::WebGlRenderingCtx;
use wasm_bindgen::prelude::*;
@@ -24,9 +25,8 @@ pub static mut CUR_IDX_TEX_UNIT: u8 = 0;
#[allow(dead_code)]
pub struct Texture2DMeta {
pub format: u32,
pub internal_format: i32,
pub ty: u32,
pub channel_type: ChannelType,
pub pixel_type: PixelType,
pub width: u32,
pub height: u32,
@@ -47,13 +47,13 @@ pub enum SamplerType {
Unsigned,
}
use crate::image::format::ImageFormat;
//use super::pixel::PixelType;
use crate::texture::format::TextureFormat;
use std::cell::RefCell;
use std::path::Path;
use std::rc::Rc;
impl Texture2D {
pub fn create_from_path<P: AsRef<Path>, F: ImageFormat>(
pub fn create_from_path<P: AsRef<Path>, F: TextureFormat>(
gl: &WebGlContext,
name: &'static str,
src: &P,
@@ -61,12 +61,11 @@ impl Texture2D {
) -> Result<Texture2D, JsValue> {
let image = HtmlImageElement::new().unwrap_abort();
#[cfg(feature = "webgl2")]
let texture = gl.create_texture();
let onerror = {
Closure::wrap(Box::new(move || {
println!("Cannot load texture located at: {:?}", name);
println!("Cannot load texture located at: {name:?}");
}) as Box<dyn Fn()>)
};
@@ -74,15 +73,13 @@ impl Texture2D {
let height = image.height();
let metadata = Rc::new(RefCell::new(Texture2DMeta {
width: width,
height: height,
internal_format: F::INTERNAL_FORMAT,
width,
height,
format: F::FORMAT,
ty: F::TYPE,
channel_type: F::CHANNEL_TYPE
pixel_type: F::PIXEL_TYPE,
}));
#[cfg(feature = "webgl2")]
let onload = {
let image = image.clone();
let gl = gl.clone();
@@ -132,7 +129,6 @@ impl Texture2D {
let gl = gl.clone();
Ok(Texture2D {
#[cfg(feature = "webgl2")]
texture,
gl,
@@ -141,7 +137,7 @@ impl Texture2D {
})
}
pub fn create_from_raw_pixels<F: ImageFormat>(
pub fn create_from_raw_pixels<F: TextureFormat>(
gl: &WebGlContext,
width: i32,
height: i32,
@@ -166,12 +162,12 @@ impl Texture2D {
Ok(texture)
}
pub fn create_from_raw_bytes<F: ImageFormat>(
pub fn create_from_raw_bytes<F: TextureFormat>(
gl: &WebGlContext,
width: i32,
height: i32,
tex_params: &'static [(u32, u32)],
bytes: Option<&[u8]>,
bytes: &[u8],
) -> Result<Texture2D, JsValue> {
let texture = gl.create_texture();
@@ -188,7 +184,14 @@ impl Texture2D {
width,
height,
);
gl.tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_u8_array(
let view = unsafe {
let len = bytes.len() / (std::mem::size_of::<<F::P as Pixel>::Item>());
let pixels =
std::slice::from_raw_parts(bytes.as_ptr() as *const <F::P as Pixel>::Item, len);
F::view(pixels)
};
gl.tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_array_buffer_view(
WebGlRenderingCtx::TEXTURE_2D,
0,
0,
@@ -197,7 +200,7 @@ impl Texture2D {
height,
F::FORMAT,
F::TYPE,
bytes,
Some(view.as_ref()),
)
.expect("Texture 2D");
@@ -205,10 +208,9 @@ impl Texture2D {
let metadata = Some(Rc::new(RefCell::new(Texture2DMeta {
width: width as u32,
height: height as u32,
internal_format: F::INTERNAL_FORMAT,
format: F::FORMAT,
ty: F::TYPE,
channel_type: F::CHANNEL_TYPE
pixel_type: F::PIXEL_TYPE,
})));
Ok(Texture2D {
@@ -220,7 +222,7 @@ impl Texture2D {
})
}
pub fn create_empty_with_format<F: ImageFormat>(
pub fn create_empty_with_format<F: TextureFormat>(
gl: &WebGlContext,
width: i32,
height: i32,
@@ -246,16 +248,14 @@ impl Texture2D {
let metadata = Some(Rc::new(RefCell::new(Texture2DMeta {
width: width as u32,
height: height as u32,
internal_format: F::INTERNAL_FORMAT,
format: F::FORMAT,
ty: F::TYPE,
channel_type: F::CHANNEL_TYPE
pixel_type: F::PIXEL_TYPE,
})));
Ok(Texture2D {
texture,
gl,
metadata,
})
}
@@ -295,7 +295,7 @@ impl Texture2D {
self
}
pub fn bind(&self) -> Texture2DBound {
pub fn bind(&self) -> Texture2DBound<'_> {
self.gl
.bind_texture(WebGlRenderingCtx::TEXTURE_2D, self.texture.as_ref());
@@ -311,7 +311,7 @@ impl Texture2D {
// Attach the texture as the first color attachment
//self.attach_to_framebuffer();
self.gl.framebuffer_texture_2d(
WebGlRenderingCtx::READ_FRAMEBUFFER,
WebGlRenderingCtx::FRAMEBUFFER,
WebGlRenderingCtx::COLOR_ATTACHMENT0,
WebGlRenderingCtx::TEXTURE_2D,
self.texture.as_ref(),
@@ -335,37 +335,31 @@ impl Texture2D {
self.gl
.viewport(0, 0, metadata.width as i32, metadata.height as i32);
#[cfg(feature = "webgl2")]
let value = match metadata.channel_type {
ChannelType::R8UI => {
let value = match metadata.pixel_type {
PixelType::R8U => {
let p = <[u8; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
ChannelType::R16I => {
PixelType::R16I => {
let p = <[i16; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
ChannelType::R32I => {
PixelType::R32I => {
let p = <[i32; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
ChannelType::R32F => {
PixelType::R32F => {
let p = <[f32; 1]>::read_pixel(&self.gl, x, y)?;
crate::log(&format!("{:?}", p));
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
ChannelType::RGB8U => {
PixelType::RGB8U => {
let p = <[u8; 3]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p)?)
}
ChannelType::RGBA8U => {
PixelType::RGBA8U => {
let p = <[u8; 4]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p)?)
}
_ => Err(JsValue::from_str(
"Pixel retrieval not implemented for that texture format.",
)),
};
// Unbind the framebuffer
@@ -406,7 +400,7 @@ pub struct Texture2DBound<'a> {
texture_2d: &'a Texture2D,
}
impl<'a> Texture2DBound<'a> {
impl Texture2DBound<'_> {
pub fn tex_sub_image_2d_with_u32_and_u32_and_html_image_element(
&self,
dx: i32,
@@ -428,20 +422,6 @@ impl<'a> Texture2DBound<'a> {
image,
)
.expect("Sub texture 2d");
#[cfg(feature = "webgl1")]
self.texture_2d
.gl
.tex_sub_image_2d_with_u32_and_u32_and_image(
WebGlRenderingCtx::TEXTURE_2D,
0,
dx,
dy,
metadata.format,
metadata.ty,
image,
)
.expect("Sub texture 2d");
//self.texture_2d.gl.flush();
}
pub fn tex_sub_image_2d_with_u32_and_u32_and_html_canvas_element(
@@ -465,20 +445,6 @@ impl<'a> Texture2DBound<'a> {
canvas,
)
.expect("Sub texture 2d");
#[cfg(feature = "webgl1")]
self.texture_2d
.gl
.tex_sub_image_2d_with_u32_and_u32_and_canvas(
WebGlRenderingCtx::TEXTURE_2D,
0,
dx,
dy,
metadata.format,
metadata.ty,
canvas,
)
.expect("Sub texture 2d");
//self.texture_2d.gl.flush();
}
pub fn tex_sub_image_2d_with_u32_and_u32_and_image_bitmap(
@@ -595,6 +561,7 @@ pub trait Tex3D {
image: &web_sys::ImageBitmap,
);
#[allow(clippy::too_many_arguments)]
fn tex_sub_image_3d_with_opt_array_buffer_view(
&self,
dx: i32,
@@ -606,6 +573,7 @@ pub trait Tex3D {
view: Option<&js_sys::Object>,
);
#[allow(clippy::too_many_arguments)]
fn tex_sub_image_3d_with_opt_u8_array(
&self,
dx: i32,

View File

@@ -21,127 +21,6 @@ pub trait Pixel:
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue>;
}
impl Pixel for [f32; 4] {
type Item = f32;
type Container = ArrayF32;
const BLACK: Self = [std::f32::NAN; 4];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let pixels = js_sys::Float32Array::new_with_length(4);
#[cfg(feature = "webgl2")]
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RGBA32F,
WebGlRenderingCtx::FLOAT,
Some(&pixels),
)?;
#[cfg(feature = "webgl1")]
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RGBA,
WebGlRenderingCtx::FLOAT,
Some(&pixels),
)?;
let pixels = pixels.to_vec();
Ok([pixels[0], pixels[1], pixels[2], pixels[3]])
}
}
impl Pixel for [f32; 3] {
type Item = f32;
type Container = ArrayF32;
const BLACK: Self = [std::f32::NAN; 3];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let pixels = js_sys::Float32Array::new_with_length(3);
#[cfg(feature = "webgl2")]
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RGB32F,
WebGlRenderingCtx::FLOAT,
Some(&pixels),
)?;
#[cfg(feature = "webgl1")]
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RGB,
WebGlRenderingCtx::FLOAT,
Some(&pixels),
)?;
let pixels = pixels.to_vec();
Ok([pixels[0], pixels[1], pixels[2]])
}
}
impl Pixel for [f32; 1] {
type Item = f32;
type Container = ArrayF32;
const BLACK: Self = [std::f32::NAN];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let p = js_sys::Uint8Array::new_with_length(4);
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RGBA,
WebGlRenderingCtx::UNSIGNED_BYTE,
Some(&p),
)?;
Ok([f32::from_le_bytes([
p.at(0).unwrap(),
p.at(1).unwrap(),
p.at(2).unwrap(),
p.at(3).unwrap(),
])])
}
}
/*use crate::image::ArrayF64;
impl Pixel for [f64; 1] {
type Item = f64;
type Container = ArrayF64;
const BLACK: Self = [std::f64::NAN];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let pixels = js_sys::Float32Array::new_with_length(1);
#[cfg(feature = "webgl2")]
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RED,
WebGlRenderingCtx::FLOAT,
Some(&pixels),
)?;
#[cfg(feature = "webgl1")]
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::LUMINANCE_ALPHA,
WebGlRenderingCtx::FLOAT,
Some(&pixels),
)?;
Ok([pixels.to_vec()[0] as f64])
}
}*/
impl Pixel for [u8; 4] {
type Item = u8;
type Container = ArrayU8;
@@ -169,13 +48,13 @@ impl Pixel for [u8; 3] {
const BLACK: Self = [0, 0, 0];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let pixels = js_sys::Uint8Array::new_with_length(3);
let pixels = js_sys::Uint8Array::new_with_length(4);
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RGB,
WebGlRenderingCtx::RGBA,
WebGlRenderingCtx::UNSIGNED_BYTE,
Some(&pixels),
)?;
@@ -183,7 +62,27 @@ impl Pixel for [u8; 3] {
Ok([pixels[0], pixels[1], pixels[2]])
}
}
#[cfg(feature = "webgl2")]
impl Pixel for [u8; 2] {
type Item = u8;
type Container = ArrayU8;
const BLACK: Self = [0, 0];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let pixels = js_sys::Uint8Array::new_with_length(2);
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RG,
WebGlRenderingCtx::UNSIGNED_BYTE,
Some(&pixels),
)?;
let pixels = pixels.to_vec();
Ok([pixels[0], pixels[1]])
}
}
impl Pixel for [u8; 1] {
type Item = u8;
type Container = ArrayU8;
@@ -204,45 +103,76 @@ impl Pixel for [u8; 1] {
Ok([pixels.to_vec()[0]])
}
}
#[cfg(feature = "webgl2")]
impl Pixel for [i16; 1] {
type Item = i16;
type Container = ArrayI16;
const BLACK: Self = [std::i16::MIN];
const BLACK: Self = [i16::MIN];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let pixels = js_sys::Int16Array::new_with_length(1);
let p = js_sys::Uint8Array::new_with_length(2);
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RED_INTEGER,
WebGlRenderingCtx::SHORT,
Some(&pixels),
WebGlRenderingCtx::RG,
WebGlRenderingCtx::UNSIGNED_BYTE,
Some(&p),
)?;
Ok([pixels.to_vec()[0]])
Ok([i16::from_le_bytes([p.at(1).unwrap(), p.at(0).unwrap()])])
}
}
#[cfg(feature = "webgl2")]
impl Pixel for [i32; 1] {
type Item = i32;
type Container = ArrayI32;
const BLACK: Self = [std::i32::MIN];
const BLACK: Self = [i32::MIN];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let pixels = js_sys::Int32Array::new_with_length(1);
let p = js_sys::Uint8Array::new_with_length(4);
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RED_INTEGER,
WebGlRenderingCtx::INT,
Some(&pixels),
WebGlRenderingCtx::RGBA,
WebGlRenderingCtx::UNSIGNED_BYTE,
Some(&p),
)?;
Ok([pixels.to_vec()[0]])
Ok([i32::from_le_bytes([
p.at(3).unwrap(),
p.at(2).unwrap(),
p.at(1).unwrap(),
p.at(0).unwrap(),
])])
}
}
impl Pixel for [f32; 1] {
type Item = f32;
type Container = ArrayF32;
const BLACK: Self = [f32::NAN];
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue> {
let p = js_sys::Uint8Array::new_with_length(4);
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RGBA,
WebGlRenderingCtx::UNSIGNED_BYTE,
Some(&p),
)?;
Ok([f32::from_le_bytes([
p.at(3).unwrap(),
p.at(2).unwrap(),
p.at(1).unwrap(),
p.at(0).unwrap(),
])])
}
}

View File

@@ -4,17 +4,11 @@ use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
use web_sys::HtmlElement;
#[cfg(feature = "webgl2")]
pub type WebGlRenderingCtx = web_sys::WebGl2RenderingContext;
#[cfg(feature = "webgl1")]
pub type WebGlRenderingCtx = web_sys::WebGlRenderingContext;
#[derive(Clone)]
pub struct WebGlContext {
inner: Rc<WebGlRenderingCtx>,
#[cfg(feature = "webgl1")]
pub ext: WebGlExt,
}
#[derive(Clone)]
@@ -55,32 +49,19 @@ impl WebGlContext {
#[cfg(feature = "webgl2")]
{
if let Ok(r) =
/*if let Ok(r) =
get_extension::<web_sys::ExtColorBufferFloat>(&gl, "EXT_color_buffer_float")
{
let _ = r;
}
}*/
let ctx = WebGlContext { inner: gl };
Ok(ctx)
}
#[cfg(feature = "webgl1")]
{
let angles_ext =
get_extension::<web_sys::AngleInstancedArrays>(&gl, "ANGLE_instanced_arrays")?;
let _ = get_extension::<web_sys::OesTextureFloat>(&gl, "OES_texture_float")?;
let _ = get_extension::<web_sys::ExtSRgb>(&gl, "EXT_sRGB")?;
Ok(WebGlContext {
inner: gl,
ext: WebGlExt { angles: angles_ext },
})
}
}
}
fn get_extension<T>(context: &WebGlRenderingCtx, name: &str) -> Result<T, JsValue>
fn _get_extension<T>(context: &WebGlRenderingCtx, name: &str) -> Result<T, JsValue>
where
T: wasm_bindgen::JsCast,
{

View File

@@ -3,6 +3,8 @@ use walkdir::WalkDir;
extern crate walkdir;
use std::io::BufRead;
use std::process::Command;
// All my shaders reside in the 'src/shaders' directory
fn generate_shaders() -> std::result::Result<(), Box<dyn Error>> {
println!("generate shaders");
@@ -23,16 +25,54 @@ fn generate_shaders() -> std::result::Result<(), Box<dyn Error>> {
.unwrap()
//.with_extension("")
.to_string_lossy()
.to_owned()
.into_owned()
.replace("/", "_")
.replace("\\", "_");
//let out_name = format!("{}/{}", OUT_PATH, out_file_name);
let src = read_shader(path)?;
let mut src = read_shader(path)?;
if std::env::var("CARGO_FEATURE_MINIFY_SHADERS").is_ok() {
println!("Feature `minify_shaders` enabled: running shader minifier");
// save to a minified path
let mut tmp_path = PathBuf::from(path);
let mut min_path = PathBuf::from(path);
let stem = path.file_stem().unwrap();
// Build new filename: stem + ".min" + extension
let tmp_file_name =
format!("{}.{}.tmp", stem.to_string_lossy(), ext.to_string_lossy());
tmp_path.set_file_name(tmp_file_name);
let min_file_name =
format!("{}.{}.min", stem.to_string_lossy(), ext.to_string_lossy());
min_path.set_file_name(min_file_name);
fs::write(tmp_path.clone(), &src)?;
Command::new("mono")
.args([
"/Users/matthieubaumann/Downloads/shader_minifier.exe",
"--format",
"text",
"--aggressive-inlining",
"--preserve-externals",
"--move-declarations",
"-o",
min_path.as_os_str().to_str().expect("Invalid UTF-8!"),
tmp_path.as_os_str().to_str().expect("Invalid UTF-8!"),
])
.status()
.expect("Failed to run shader_minifier");
src = read_shader(min_path)?;
} else {
println!("Feature `minify_shaders` not enabled: skipping");
}
shaders.insert(out_file_name, src);
//fs::write(&out_name, result)?;
println!("cargo:rerun-if-changed=src/shaders/{}", file_name);
println!("cargo:rerun-if-changed=src/shaders/{file_name}");
}
}
}
@@ -49,16 +89,21 @@ fn read_shader<P: AsRef<std::path::Path>>(path: P) -> std::io::Result<String> {
let shader_src = std::io::BufReader::new(file)
.lines()
.flatten()
.map(|l| {
.map_while(Result::ok)
.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];
let incl_file_name = path.parent().unwrap().join(incl_file_name_rel);
read_shader(incl_file_name.to_str().unwrap()).unwrap()
println!("{}", incl_file_name.to_string_lossy());
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<_>>()
@@ -76,7 +121,6 @@ pub fn write(path: PathBuf, entries: HashMap<String, String>) -> Result<(), Box<
let mut all_the_files = File::create(&path)?;
writeln!(&mut all_the_files, r#"use std::collections::HashMap;"#,)?;
writeln!(&mut all_the_files, r#""#,)?;
writeln!(&mut all_the_files, r#"#[allow(dead_code)]"#,)?;
writeln!(
&mut all_the_files,
@@ -87,7 +131,10 @@ pub fn write(path: PathBuf, entries: HashMap<String, String>) -> Result<(), Box<
for (name, content) in entries {
writeln!(
&mut all_the_files,
r##" out.insert(r"{name}", r#"{content}"#);"##,
r##" out.insert(
r"{name}",
r#"{content}"#,
);"##,
)?;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
use js_sys::Reflect;
use wasm_bindgen::JsValue;
use web_sys::window;
pub struct BrowserFeaturesSupport {
pub create_image_bitmap: bool,
}
impl Default for BrowserFeaturesSupport {
fn default() -> Self {
Self::new()
}
}
impl BrowserFeaturesSupport {
pub fn new() -> Self {
let window = window().expect("no global `window` exists");
let create_image_bitmap =
Reflect::has(&window, &JsValue::from_str("createImageBitmap")).unwrap_or(false);
Self {
create_image_bitmap,
}
}
}

View File

@@ -54,8 +54,8 @@ fn linspace(a: f64, b: f64, num: usize) -> Vec<f64> {
res
}
const NUM_VERTICES_WIDTH: usize = 3;
const NUM_VERTICES_HEIGHT: usize = 3;
const NUM_VERTICES_WIDTH: usize = 4;
const NUM_VERTICES_HEIGHT: usize = 4;
const NUM_VERTICES: usize = 4 + 2 * NUM_VERTICES_WIDTH + 2 * NUM_VERTICES_HEIGHT;
// This struct belongs to the CameraViewPort
pub struct FieldOfView {
@@ -79,16 +79,14 @@ impl FieldOfView {
) -> Self {
let mut x_ndc = linspace(-1., 1., NUM_VERTICES_WIDTH + 2);
x_ndc.extend(iter::repeat(1.0).take(NUM_VERTICES_HEIGHT));
x_ndc.extend(iter::repeat_n(1.0, NUM_VERTICES_HEIGHT));
x_ndc.extend(linspace(1., -1., NUM_VERTICES_WIDTH + 2));
x_ndc.extend(iter::repeat(-1.0).take(NUM_VERTICES_HEIGHT));
x_ndc.extend(iter::repeat_n(-1.0, NUM_VERTICES_HEIGHT));
let mut y_ndc = iter::repeat(-1.0)
.take(NUM_VERTICES_WIDTH + 1)
.collect::<Vec<_>>();
let mut y_ndc = iter::repeat_n(-1.0, NUM_VERTICES_WIDTH + 1).collect::<Vec<_>>();
y_ndc.extend(linspace(-1., 1., NUM_VERTICES_HEIGHT + 2));
y_ndc.extend(iter::repeat(1.0).take(NUM_VERTICES_WIDTH));
y_ndc.extend(iter::repeat_n(1.0, NUM_VERTICES_WIDTH));
y_ndc.extend(linspace(1., -1., NUM_VERTICES_HEIGHT + 2));
y_ndc.pop();

View File

@@ -8,8 +8,8 @@ pub use fov::FieldOfView;
pub mod view_hpx_cells;
use crate::CooSystem;
use crate::HEALPixCoverage;
use crate::ProjectionType;
use crate::SpaceMoc;
pub fn build_fov_coverage(
depth: u8,
@@ -18,7 +18,7 @@ pub fn build_fov_coverage(
camera_frame: CooSystem,
frame: CooSystem,
proj: &ProjectionType,
) -> HEALPixCoverage {
) -> SpaceMoc {
if let Some(vertices) = fov.get_vertices() {
// The vertices coming from the camera are in a specific coo sys
// but cdshealpix accepts them to be given in ICRS coo sys
@@ -44,21 +44,20 @@ pub fn build_fov_coverage(
::healpix::nested::hash(depth, lon.to_radians(), lat.to_radians())
});
HEALPixCoverage::from_fixed_hpx_cells(depth, hpx_idxs_iter, Some(vertices.len()))
SpaceMoc::from_fixed_hpx_cells(depth, hpx_idxs_iter, Some(vertices.len()))
} else {
// The polygon is not too small for the depth asked
let inside_vertex = crate::coosys::apply_coo_system(camera_frame, frame, camera_center);
// Prefer to query from_polygon with depth >= 2
let moc = HEALPixCoverage::from_3d_coos(depth, vertices_iter, &inside_vertex);
moc
SpaceMoc::from_3d_coos(depth, vertices_iter, &inside_vertex)
}
} else {
let center_xyz = crate::coosys::apply_coo_system(camera_frame, frame, camera_center);
let biggest_fov_rad = proj.aperture_start().to_radians();
let lonlat = center_xyz.lonlat();
HEALPixCoverage::from_cone(&lonlat, biggest_fov_rad * 0.5, depth)
SpaceMoc::from_cone(&lonlat, biggest_fov_rad * 0.5, depth)
}
}

View File

@@ -3,7 +3,7 @@ use crate::healpix::cell::HEALPixCell;
use crate::math::projection::*;
use crate::HEALPixCoverage;
use crate::SpaceMoc;
use moclib::moc::{range::op::degrade::degrade, RangeMOCIterator};
@@ -84,7 +84,7 @@ impl ViewHpxCells {
self.hpx_cells[frame as usize].get_cells(depth)
}
pub(super) fn get_cov(&self, frame: CooSystem) -> &HEALPixCoverage {
pub(super) fn get_cov(&self, frame: CooSystem) -> &SpaceMoc {
self.hpx_cells[frame as usize].get_cov()
}
@@ -109,7 +109,7 @@ pub struct HpxCells {
// An index vector referring to the indices of each depth cells
//idx_rng: [Option<Range<usize>>; MAX_HPX_DEPTH as usize + 1],
// Coverage created in the frame
cov: HEALPixCoverage,
cov: SpaceMoc,
// boolean refering to if the cells in the view has changed
//new_cells: bool,
}
@@ -127,7 +127,7 @@ use super::FieldOfView;
impl HpxCells {
pub fn new(frame: CooSystem) -> Self {
//let cells = Vec::new();
let cov = HEALPixCoverage::empty(29);
let cov = SpaceMoc::empty(29);
//let idx_rng = Default::default();
@@ -203,7 +203,7 @@ impl HpxCells {
if depth == cov_depth {
self.cov
.flatten_to_fixed_depth_cells()
.map(move |idx| HEALPixCell(depth, idx))
.map(|idx| HEALPixCell(depth, idx))
.collect()
} else if depth > self.cov.depth_max() {
let cov_d = self.cov.depth_max();
@@ -212,7 +212,7 @@ impl HpxCells {
self.cov
.flatten_to_fixed_depth_cells()
.flat_map(move |idx| {
.flat_map(|idx| {
// idx is at depth_max
HEALPixCell(cov_d, idx).get_children_cells(dd)
})
@@ -221,7 +221,7 @@ impl HpxCells {
// compute the cells from the coverage
degrade((&self.cov.0).into_range_moc_iter(), depth)
.flatten_to_fixed_depth_cells()
.map(move |idx| HEALPixCell(depth, idx))
.map(|idx| HEALPixCell(depth, idx))
.collect()
}
}
@@ -257,7 +257,7 @@ impl HpxCells {
}*/
#[inline(always)]
pub fn get_cov(&self) -> &HEALPixCoverage {
pub fn get_cov(&self) -> &SpaceMoc {
&self.cov
}

View File

@@ -8,18 +8,14 @@ pub enum UserAction {
use web_sys::WebGl2RenderingContext;
// Longitude reversed identity matrix
const ID_R: &Matrix3<f64> = &Matrix3::new(
-1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
);
const ID_R: &Matrix3<f64> = &Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
use cgmath::{Vector3, InnerSpace};
use super::{fov::FieldOfView, view_hpx_cells::ViewHpxCells};
use crate::healpix::cell::HEALPixCell;
use crate::healpix::coverage::HEALPixCoverage;
use crate::healpix::moc::SpaceMoc;
use crate::math::angle::ToAngle;
use crate::math::{projection::coo_space::XYZModel, projection::domain::sdf::ProjDef};
use cgmath::{InnerSpace, Vector3};
use cgmath::{Matrix3, Vector2};
const APERTURE_LOWER_LIMIT_RAD: f64 = (1.0_f64 / 36000.0).to_radians();
@@ -220,7 +216,7 @@ impl CameraViewPort {
self.view_hpx_cells.has_changed()
}*/
pub fn get_cov(&self, frame: CooSystem) -> &HEALPixCoverage {
pub fn get_cov(&self, frame: CooSystem) -> &SpaceMoc {
self.view_hpx_cells.get_cov(frame)
}
@@ -285,8 +281,8 @@ impl CameraViewPort {
}
pub fn set_screen_size(&mut self, width: f32, height: f32, projection: &ProjectionType) {
self.width = (width as f32) * self.dpi;
self.height = (height as f32) * self.dpi;
self.width = width * self.dpi;
self.height = height * self.dpi;
self.aspect = width / height;
// Compute the new clip zoom factor
@@ -319,11 +315,7 @@ impl CameraViewPort {
}
pub fn compute_ndc_to_clip_factor(&mut self, proj: &ProjectionType) {
self.ndc_to_clip = if self.height < self.width {
Vector2::new(1.0, (self.height as f64) / (self.width as f64))
} else {
Vector2::new((self.width as f64) / (self.height as f64), 1.0)
};
self.ndc_to_clip = Vector2::new(1.0, (self.height as f64) / (self.width as f64));
let bounds_size_ratio = proj.bounds_size_ratio();
self.ndc_to_clip.y *= bounds_size_ratio;
@@ -378,10 +370,10 @@ impl CameraViewPort {
}
}
let can_unzoom_more = match proj {
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_) => false,
_ => true,
};
let can_unzoom_more = !matches!(
proj,
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_)
);
if !can_unzoom_more && self.zoom_factor >= 1.0 {
return true;
@@ -414,10 +406,10 @@ impl CameraViewPort {
self.last_user_action
};
let can_unzoom_more = match proj {
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_) => false,
_ => true,
};
let can_unzoom_more = !matches!(
proj,
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_)
);
let aperture_start: f64 = proj.aperture_start().to_radians();
@@ -491,10 +483,10 @@ impl CameraViewPort {
self.last_user_action
};
let can_unzoom_more = match proj {
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_) => false,
_ => true,
};
let can_unzoom_more = !matches!(
proj,
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_)
);
// Set the zoom factor
self.zoom_factor = zoom_factor;
@@ -574,42 +566,48 @@ impl CameraViewPort {
}
fn compute_texture_depth(&mut self) {
/*// Compute a depth from a number of pixels on screen
let width = self.width;
let aperture = self.aperture.0 as f32;
// Compute a depth from a number of pixels on screen
/*let width = self.width;
let aperture = self.aperture as f32;
let angle_per_pixel = aperture / width;
let angle_per_pixel = aperture / width;
let two_power_two_times_depth_pixel =
std::f32::consts::PI / (3.0 * angle_per_pixel * angle_per_pixel);
let depth_pixel = (two_power_two_times_depth_pixel.log2() / 2.0).floor() as u32;
let two_power_two_times_depth_pixel =
std::f32::consts::PI / (3.0 * angle_per_pixel * angle_per_pixel);
let depth_pixel = (two_power_two_times_depth_pixel.log2() / 2.0).ceil() as u32;
//let survey_max_depth = conf.get_max_depth();
// The depth of the texture
// A texture of 512x512 pixels will have a depth of 9
const DEPTH_OFFSET_TEXTURE: u32 = 9;
// The depth of the texture corresponds to the depth of a pixel
// minus the offset depth of the texture
self.texture_depth = if DEPTH_OFFSET_TEXTURE > depth_pixel {
0_u8
} else {
(depth_pixel - DEPTH_OFFSET_TEXTURE) as u8
};*/
//let survey_max_depth = conf.get_max_depth();
// The depth of the texture
// A texture of 512x512 pixels will have a depth of 9
const DEPTH_OFFSET_TEXTURE: u32 = 9;
// The depth of the texture corresponds to the depth of a pixel
// minus the offset depth of the texture
self.texture_depth = if DEPTH_OFFSET_TEXTURE > depth_pixel {
0_u8
} else {
(depth_pixel - DEPTH_OFFSET_TEXTURE) as u8
};
*/
let w_screen_device_px = self.width as f64 / (self.dpi as f64);
//let depth_pixel = 29_usize;
let w_screen_px = self.width as f64;
let smallest_cell_size_px = self.dpi as f64;
let mut depth_pixel = 29 as usize;
let pixel_angle_rad = self.get_aperture() / w_screen_device_px;
let hpx_cell_size_rad = (smallest_cell_size_px / w_screen_px) * self.get_aperture();
while depth_pixel > 0 {
if crate::healpix::utils::MEAN_HPX_CELL_RES[depth_pixel] > hpx_cell_size_rad {
break;
// Find the smallest depth such that MEAN_HPX_CELL_RES[depth] > pixel_angle_rad
let depth_pixel = match crate::healpix::utils::MEAN_HPX_CELL_RES.binary_search_by(|&res| {
if res < pixel_angle_rad {
std::cmp::Ordering::Greater
} else if res > pixel_angle_rad {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Equal
}
}) {
Ok(idx) => idx, // exact match
Err(idx) => idx,
};
depth_pixel = depth_pixel - 1;
}
depth_pixel += 1;
//al_core::log(&format!("{:?}", depth_pixel));
const DEPTH_OFFSET_TEXTURE: usize = 9;
self.texture_depth = if DEPTH_OFFSET_TEXTURE > depth_pixel {
0_u8
@@ -618,7 +616,7 @@ impl CameraViewPort {
};
}
pub fn get_texture_depth(&self) -> u8 {
pub fn get_tile_depth(&self) -> u8 {
self.texture_depth
}
@@ -639,10 +637,13 @@ impl CameraViewPort {
&mut self,
dlon: Angle<f64>,
dlat: Angle<f64>,
proj: &ProjectionType
proj: &ProjectionType,
) {
let center = self.get_center();
let rot = Rotation::from_axis_angle(&Vector3::new(center.z, 0.0, -center.x).normalize(), dlat) * Rotation::from_axis_angle(&Vector3::unit_y(), -dlon) * Rotation::from_sky_position(&center);
let rot =
Rotation::from_axis_angle(&Vector3::new(center.z, 0.0, -center.x).normalize(), dlat)
* Rotation::from_axis_angle(&Vector3::unit_y(), -dlon)
* Rotation::from_sky_position(center);
self.set_rotation(&rot, proj);
}

View File

@@ -1,5 +1,5 @@
use cgmath::Vector3;
use al_api::coo_system::CooSystem;
use cgmath::Vector3;
/// This is conversion method returning a transformation
/// matrix when the system requested by the user is not

View File

@@ -10,13 +10,19 @@ pub struct Downloader {
requests: Vec<RequestType>,
queried_list: HashSet<QueryId>,
cache: Cache<QueryId, Resource>,
cache: Cache<QueryId, RequestType>,
}
use crate::fifo_cache::Cache;
use query::Query;
use request::{RequestType, Resource};
use request::RequestType;
impl Default for Downloader {
fn default() -> Self {
Self::new()
}
}
impl Downloader {
pub fn new() -> Downloader {
@@ -56,26 +62,23 @@ impl Downloader {
}
}
pub fn get_received_resources(&mut self) -> Vec<Resource> {
pub fn get_received_resources(&mut self) -> Vec<RequestType> {
let mut rscs = vec![];
let mut not_finished_requests = vec![];
let mut finished_query_list = vec![];
self.requests = self
.requests
.drain(..)
.filter(|request| {
// If the request resolves into a resource
if let Some(rsc) = request.into() {
rscs.push(rsc);
finished_query_list.push(request.id().clone());
false
// The request is not resolved, we keep it
} else {
true
}
})
.collect();
while let Some(request) = self.requests.pop() {
if request.is_resolved() {
finished_query_list.push(request.id().clone());
rscs.push(request);
// The request is not resolved, we keep it
} else {
not_finished_requests.push(request);
}
}
self.requests = not_finished_requests;
for query_id in finished_query_list.into_iter() {
self.queried_list.remove(&query_id);
@@ -92,18 +95,8 @@ impl Downloader {
self.queried_list.contains(id)
}
pub fn delay(&mut self, r: Resource) {
match r {
Resource::Tile(tile) => {
let k = format!(
"{:?}{:?}/{:?}",
tile.get_hips_cdid(),
tile.cell.depth(),
tile.cell.idx()
);
self.cache.insert(k, Resource::Tile(tile));
}
_ => unimplemented!(),
}
pub fn delay(&mut self, r: RequestType) {
let id = r.id().to_owned();
self.cache.insert(id, r);
}
}

View File

@@ -6,14 +6,65 @@ pub trait Query: Sized {
fn id(&self) -> &QueryId;
}
pub type QueryId = String;
use crate::browser_support::BrowserFeaturesSupport;
use crate::healpix::cell::HEALPixFreqCell;
use al_api::hips::DataproductType;
use al_core::image::format::ImageFormatType;
/// Description of a cell to query
#[derive(Clone, PartialEq, Eq)]
pub enum CellDesc {
HiPS2D {
// A description of the tile in space
cell: HEALPixCell,
// Size of the tile requested
tile_size: u32,
},
HiPS3D {
// A description of the tile in space and frequency
cell: HEALPixFreqCell,
// Size of the tile requested
tile_size: u32,
// Depth of the cubic tile
tile_depth: u32,
},
HiPSCube {
// A description of the tile in space
cell: HEALPixCell,
// size of the tile requested
tile_size: u32,
// The channel number to query
channel: u32,
},
}
impl CellDesc {
/*fn get_size(&self) -> (u32, u32, u32) {
match self {
Self::HiPS2D { tile_size, .. } => (*tile_size, *tile_size, 1),
Self::HiPSCube { tile_size, .. } => (*tile_size, *tile_size, 1),
Self::HiPS3D {
tile_size,
tile_depth,
..
} => (*tile_size, *tile_size, *tile_depth),
}
}*/
pub fn get_hpx(&self) -> &HEALPixCell {
match self {
Self::HiPS2D { cell, .. } => cell,
Self::HiPS3D { cell, .. } => &cell.hpx,
Self::HiPSCube { cell, .. } => cell,
}
}
}
#[derive(Eq, PartialEq, Clone)]
pub struct Tile {
pub cell: HEALPixCell,
pub cell: CellDesc,
pub format: ImageFormatType,
// The root url of the HiPS
pub hips_cdid: CreatorDid,
@@ -22,15 +73,20 @@ pub struct Tile {
pub credentials: RequestCredentials,
pub mode: RequestMode,
pub id: QueryId,
pub channel: Option<u32>,
pub create_bitmap_support: bool,
}
use crate::healpix::cell::HEALPixCell;
use crate::renderable::hips::config::HiPSConfig;
use crate::renderable::CreatorDid;
use crate::tile_fetcher::HiPSLocalFiles;
use web_sys::{RequestCredentials, RequestMode};
impl Tile {
pub fn new(cell: &HEALPixCell, channel: Option<u32>, cfg: &HiPSConfig) -> Self {
pub fn new(
cell: &HEALPixCell,
cfg: &HiPSConfig,
browser_support: &BrowserFeaturesSupport,
) -> Self {
let hips_cdid = cfg.get_creator_did();
let hips_url = cfg.get_root_url();
let format = cfg.get_format();
@@ -43,36 +99,108 @@ impl Tile {
let dir_idx = (idx / 10000) * 10000;
let mut url = format!("{}/Norder{}/Dir{}/Npix{}", hips_url, depth, dir_idx, idx);
let url = format!("{hips_url}/Norder{depth}/Dir{dir_idx}/Npix{idx}.{ext}");
// handle cube case
if let Some(channel) = channel {
if channel > 0 {
url.push_str(&format!("_{:?}", channel));
}
}
// add the tile format
url.push_str(&format!(".{}", ext));
let id = format!(
"{}{}{}{}{}",
hips_cdid,
depth,
idx,
channel.unwrap_or(0),
ext
);
let id = format!("{}_{}_{}_{}", hips_cdid, depth, idx, ext);
let tile_size = cfg.get_tile_size() as u32;
Tile {
hips_cdid: hips_cdid.to_string(),
url,
cell: *cell,
cell: CellDesc::HiPS2D {
cell: *cell,
tile_size,
},
format,
credentials,
mode,
id,
channel,
create_bitmap_support: browser_support.create_image_bitmap,
}
}
pub fn new_with_channel(
cell: &HEALPixCell,
channel: u32,
cfg: &HiPSConfig,
browser_support: &BrowserFeaturesSupport,
) -> Self {
let hips_cdid = cfg.get_creator_did();
let hips_url = cfg.get_root_url();
let format = cfg.get_format();
let credentials = cfg.get_request_credentials();
let mode = cfg.get_request_mode();
let ext = format.get_ext_file();
let HEALPixCell(depth, idx) = *cell;
let dir_idx = (idx / 10000) * 10000;
let url = format!("{hips_url}/Norder{depth}/Dir{dir_idx}/Npix{idx}_{channel:?}.{ext}");
let id = format!("{}_{}_{}_{}_{}", hips_cdid, depth, idx, channel, ext);
let tile_size = cfg.get_tile_size() as u32;
Tile {
hips_cdid: hips_cdid.to_string(),
url,
cell: CellDesc::HiPSCube {
cell: *cell,
tile_size,
channel,
},
format,
credentials,
mode,
id,
create_bitmap_support: browser_support.create_image_bitmap,
}
}
pub fn new_cubic(
hpx_f_cell: &HEALPixFreqCell,
cfg: &HiPSConfig,
browser_support: &BrowserFeaturesSupport,
) -> Self {
let hips_cdid = cfg.get_creator_did();
let hips_url = cfg.get_root_url();
let format = cfg.get_format();
let credentials = cfg.get_request_credentials();
let mode = cfg.get_request_mode();
let ext = format.get_ext_file();
// f hash at order_f
let HEALPixFreqCell {
hpx: HEALPixCell(k, n),
f_hash: m,
f_depth: l,
} = *hpx_f_cell;
let d = (n / 10000) * 10000;
let e = (m / 10) * 10;
let url = format!("{hips_url}/Norder{k}_{l}/Dir{d}_{e}/Npix{n}_{m}.{ext}");
let id = format!("{hips_cdid}_{k}_{l}_{n}_{m}_{ext}");
let tile_size = cfg.get_tile_size() as u32;
let tile_depth = cfg.tile_depth.unwrap_or(1) as u32;
Tile {
hips_cdid: hips_cdid.to_string(),
url,
cell: CellDesc::HiPS3D {
cell: hpx_f_cell.clone(),
tile_size,
tile_depth,
},
format,
credentials,
mode,
id,
create_bitmap_support: browser_support.create_image_bitmap,
}
}
}
@@ -90,7 +218,7 @@ impl Query for Tile {
pub struct Allsky {
pub format: ImageFormatType,
pub tile_size: i32,
pub texture_size: i32,
pub allsky_tile_size: i32,
pub channel: Option<u32>,
// The root url of the HiPS
pub hips_cdid: CreatorDid,
@@ -104,8 +232,10 @@ pub struct Allsky {
impl Allsky {
pub fn new(cfg: &HiPSConfig, channel: Option<u32>) -> Self {
let hips_cdid = cfg.get_creator_did().to_string();
let allsky_tile_size = cfg.allsky_tile_size();
let tile_size = cfg.get_tile_size();
let texture_size = cfg.get_texture_size();
let format = cfg.get_format();
let ext = format.get_ext_file();
let credentials = cfg.get_request_credentials();
@@ -116,12 +246,12 @@ impl Allsky {
// handle cube case
if let Some(channel) = channel {
if channel > 0 {
url.push_str(&format!("_{:?}", channel));
url.push_str(&format!("_{channel:?}"));
}
}
// add the tile format
url.push_str(&format!(".{}", ext));
url.push_str(&format!(".{ext}"));
let id = format!(
"{}Allsky{}{}",
@@ -132,14 +262,14 @@ impl Allsky {
Allsky {
tile_size,
texture_size,
allsky_tile_size,
hips_cdid,
url,
format,
id,
credentials,
mode,
channel
channel,
}
}
}
@@ -154,43 +284,8 @@ impl Query for Allsky {
}
/* ---------------------------------- */
pub struct PixelMetadata {
pub format: ImageFormatType,
// The root url of the HiPS
pub hips_cdid: CreatorDid,
// The total url of the query
pub url: Url,
pub id: QueryId,
}
impl PixelMetadata {
pub fn new(cfg: &HiPSConfig) -> Self {
let hips_cdid = cfg.get_creator_did().to_string();
let format = cfg.get_format();
let ext = format.get_ext_file();
let url = format!("{}/Norder3/Allsky.{}", cfg.get_root_url(), ext);
let id = format!("{}Allsky{}", hips_cdid, ext);
PixelMetadata {
hips_cdid,
url,
format,
id,
}
}
}
use super::request::blank::PixelMetadataRequest;
impl Query for PixelMetadata {
type Request = PixelMetadataRequest;
fn id(&self) -> &QueryId {
&self.id
}
}
use al_api::moc::MOCOptions;
/* ---------------------------------- */
pub struct Moc {
// The total url of the query
pub url: Url,
@@ -198,21 +293,41 @@ pub struct Moc {
pub credentials: RequestCredentials,
pub params: MOCOptions,
pub hips_cdid: CreatorDid,
pub dataproduct_type: DataproductType,
}
use std::collections::HashMap;
impl Moc {
pub fn new(
url: String,
mode: RequestMode,
credentials: RequestCredentials,
hips_cdid: CreatorDid,
cfg: &HiPSConfig,
hips_local_files: &HashMap<String, HiPSLocalFiles>,
params: MOCOptions,
) -> Self {
// Try to fetch the MOC
let hips_cdid = cfg.get_creator_did();
let url = if let Some(local_hips) = hips_local_files.get(hips_cdid) {
if let Ok(url) =
web_sys::Url::create_object_url_with_blob(local_hips.get_moc().as_ref())
{
url
} else {
format!("{}/Moc.fits", cfg.get_root_url())
}
} else {
format!("{}/Moc.fits", cfg.get_root_url())
};
let mode = cfg.get_request_mode();
let credentials = cfg.get_request_credentials();
let hips_cdid = cfg.get_creator_did().to_string();
let dataproduct_type = cfg.dataproduct_type;
Moc {
url,
params,
hips_cdid,
mode,
credentials,
dataproduct_type,
}
}
}

View File

@@ -1,22 +1,26 @@
use std::io::Cursor;
use crate::downloader::query;
use crate::renderable::CreatorDid;
use al_core::image::format::ChannelType;
use al_core::image::fits::FitsImage;
use al_core::image::ImageType;
use fitsrs::{fits::Fits, hdu::data::InMemData};
use al_core::texture::format::PixelType;
use fitsrs::hdu::header::Bitpix;
use super::{Request, RequestType};
use crate::downloader::QueryId;
pub struct AllskyRequest {
pub hips_cdid: CreatorDid,
pub url: Url,
pub depth_tile: u8,
//pub depth_tile: u8,
pub id: QueryId,
pub channel: Option<u32>,
request: Request<Vec<ImageType>>,
pub request: Request<Vec<ImageType>>,
}
impl AllskyRequest {
pub fn missing(&self) -> bool {
self.request.data.borrow().is_none()
}
}
impl From<AllskyRequest> for RequestType {
@@ -28,32 +32,17 @@ impl From<AllskyRequest> for RequestType {
use super::Url;
use wasm_bindgen_futures::JsFuture;
use web_sys::{RequestInit, Response, RequestCredentials};
use web_sys::{RequestCredentials, RequestInit, Response};
use al_core::{image::raw::ImageBuffer, texture::pixel::Pixel};
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
async fn query_image(url: &str, credentials: RequestCredentials) -> Result<ImageBuffer<RGBA8U>, JsValue> {
let image = web_sys::HtmlImageElement::new().unwrap_abort();
let image_cloned = image.clone();
let cors_value = match credentials {
RequestCredentials::Include => Some("use-credentials"),
RequestCredentials::SameOrigin => Some("anonymous"),
_ => Some("")
};
let html_img_elt_promise = js_sys::Promise::new(
&mut (Box::new(move |resolve, reject| {
image_cloned.set_cross_origin(cors_value);
image_cloned.set_onload(Some(&resolve));
image_cloned.set_onerror(Some(&reject));
image_cloned.set_src(&url);
}) as Box<dyn FnMut(js_sys::Function, js_sys::Function)>),
);
let _ = JsFuture::from(html_img_elt_promise).await?;
async fn query_allsky(
url: &str,
credentials: RequestCredentials,
) -> Result<ImageBuffer<RGBA8U>, JsValue> {
let image = super::query_html_image(url, credentials).await?;
// The image has been received here
let document = web_sys::window().unwrap_abort().document().unwrap_abort();
@@ -74,7 +63,7 @@ async fn query_image(url: &str, credentials: RequestCredentials) -> Result<Image
let raw_bytes = image_data.data();
Ok(ImageBuffer::from_raw_bytes(raw_bytes.0, w as i32, h as i32))
Ok(ImageBuffer::from_raw_bytes(raw_bytes.0, w, h))
}
impl From<query::Allsky> for AllskyRequest {
@@ -85,55 +74,52 @@ impl From<query::Allsky> for AllskyRequest {
tile_size,
url,
hips_cdid,
texture_size,
allsky_tile_size,
id,
credentials,
mode,
channel: slice,
} = query;
let depth_tile = crate::math::utils::log_2_unchecked(texture_size / tile_size) as u8;
let channel = format.get_channel();
//let depth_tile = crate::math::utils::log_2_unchecked(texture_size / tile_size) as u8;
let channel = format.get_pixel_format();
let url_clone = url.clone();
let request = Request::new(async move {
match channel {
ChannelType::RGB8U => {
let allsky_tile_size = std::cmp::min(tile_size, 64);
let allsky = query_image(&url_clone, credentials).await?;
PixelType::RGB8U => {
let allsky = query_allsky(&url_clone, credentials).await?;
let allsky_tiles = handle_allsky_file::<RGBA8U>(
allsky,
allsky_tile_size,
texture_size,
tile_size,
)?
.into_iter()
.map(|image| {
let ImageBuffer { data, size } = image;
let data = data
.into_iter()
.enumerate()
.filter(|&(i, _)| i % 4 != 3)
.map(|(_, v)| v)
let allsky_tiles =
handle_allsky_file::<RGBA8U>(allsky, allsky_tile_size, tile_size)?
.map(|image| {
let ImageBuffer { data, size } = image;
let data = data
.iter()
.enumerate()
.filter(|&(i, _)| i % 4 != 3)
.map(|(_, v)| *v)
.collect::<Vec<_>>();
let image = ImageBuffer::new(
data.into_boxed_slice(),
size.0,
size.1,
size.2,
);
ImageType::RawRgb8u { image }
})
.collect();
let image = ImageBuffer::new(data, size.x, size.y);
ImageType::RawRgb8u { image }
})
.collect();
Ok(allsky_tiles)
}
ChannelType::RGBA8U => {
let allsky_tile_size = std::cmp::min(tile_size, 64);
let allsky = query_image(&url_clone, credentials).await?;
PixelType::RGBA8U => {
let allsky = query_allsky(&url_clone, credentials).await?;
let allsky_tiles =
handle_allsky_file(allsky, allsky_tile_size, texture_size, tile_size)?
.into_iter()
.map(|image| ImageType::RawRgba8u { image })
.collect();
let allsky_tiles = handle_allsky_file(allsky, allsky_tile_size, tile_size)?
.map(|image| ImageType::RawRgba8u { image })
.collect();
Ok(allsky_tiles)
}
@@ -156,64 +142,66 @@ impl From<query::Allsky> for AllskyRequest {
// Convert the JS ReadableStream to a Rust stream
let mut reader = body.try_into_async_read().map_err(|_| JsValue::from_str("readable stream locked"))?;*/
let array_buffer = JsFuture::from(resp.array_buffer()?).await?;
let bytes_buffer = js_sys::Uint8Array::new(&array_buffer);
let buf = JsFuture::from(resp.array_buffer()?).await?;
let raw_bytes = js_sys::Uint8Array::new(&buf).to_vec();
let num_bytes = bytes_buffer.length() as usize;
let mut raw_bytes = Vec::with_capacity(num_bytes);
unsafe {
raw_bytes.set_len(num_bytes);
}
bytes_buffer.copy_to(&mut raw_bytes[..]);
let mut reader = Cursor::new(&raw_bytes[..]);
let Fits { hdu } = Fits::from_reader(&mut reader)
.map_err(|_| JsValue::from_str("Parsing fits error of allsky"))?;
let data = hdu.get_data();
match data {
InMemData::U8(data) => {
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
let FitsImage {
raw_bytes, bitpix, ..
} = &FitsImage::from_raw_bytes(raw_bytes.as_slice())?[0];
match bitpix {
Bitpix::U8 => {
Ok(handle_allsky_fits(raw_bytes, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawR8ui { image })
.collect())
}
InMemData::I16(data) => {
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
Bitpix::I16 => {
Ok(handle_allsky_fits(raw_bytes, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawR16i { image })
.collect())
}
InMemData::I32(data) => {
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
Bitpix::I32 => {
Ok(handle_allsky_fits(raw_bytes, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawR32i { image })
.collect())
}
InMemData::I64(data) => {
let data = data.iter().map(|v| *v as i32).collect::<Vec<_>>();
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
.map(|image| ImageType::RawR32i { image })
.collect())
}
InMemData::F32(data) => {
Bitpix::I64 => {
let data = unsafe {
std::slice::from_raw_parts(
raw_bytes.as_ptr() as *const i64,
raw_bytes.len() / 8,
)
};
let data = data.iter().map(|v| *v as i32).collect::<Vec<_>>();
let raw_bytes = unsafe {
std::slice::from_raw_parts(
data.as_ptr() as *const u8,
data.len() * 4,
)
};
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
Ok(handle_allsky_fits(raw_bytes, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawR32i { image })
.collect())
}
Bitpix::F32 => {
Ok(handle_allsky_fits(raw_bytes, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawRgba8u { image })
.collect())
}
InMemData::F64(data) => {
let data = data.iter().map(|v| *v as f32).collect::<Vec<_>>();
Bitpix::F64 => {
let data = unsafe {
std::slice::from_raw_parts(
raw_bytes.as_ptr() as *const f64,
raw_bytes.len() / 8,
)
};
let data = data.iter().map(|v| *v as f32).collect::<Vec<_>>();
let raw_bytes = unsafe {
std::slice::from_raw_parts(
data.as_ptr() as *const u8,
data.len() * 4,
)
};
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
Ok(handle_allsky_fits(raw_bytes, tile_size, allsky_tile_size)?
.map(|image| ImageType::RawRgba8u { image })
.collect())
}
@@ -225,7 +213,7 @@ impl From<query::Allsky> for AllskyRequest {
Self {
id,
hips_cdid,
depth_tile,
//depth_tile,
url,
request,
channel: slice,
@@ -233,44 +221,43 @@ impl From<query::Allsky> for AllskyRequest {
}
}
use al_core::image::format::ImageFormat;
use al_core::image::raw::ImageBufferView;
fn handle_allsky_file<F: ImageFormat>(
allsky: ImageBuffer<F>,
use al_core::texture::format::TextureFormat;
fn handle_allsky_file<F: TextureFormat>(
image: ImageBuffer<F>,
allsky_tile_size: i32,
texture_size: i32,
tile_size: i32,
) -> Result<impl Iterator<Item = ImageBuffer<F>>, JsValue> {
let num_tiles_per_texture = (texture_size / tile_size) * (texture_size / tile_size);
let num_tiles = num_tiles_per_texture * 12;
let num_allsky_tiles_per_tile = (tile_size / allsky_tile_size) * (tile_size / allsky_tile_size);
let d3_tile_allsky_size = std::cmp::min(tile_size, 64);
let mut src_idx = 0;
let tiles = (0..num_tiles).map(move |_| {
let mut base_tile =
ImageBuffer::<F>::allocate(&<F as ImageFormat>::P::BLACK, tile_size, tile_size);
for idx_tile in 0..num_allsky_tiles_per_tile {
let tiles = (0..12).map(move |_| {
let mut base_tile = ImageBuffer::<F>::allocate(
&F::P::BLACK,
allsky_tile_size as u32,
allsky_tile_size as u32,
);
for idx_tile in 0..64 {
let (x, y) = crate::utils::unmortonize(idx_tile as u64);
let dx = x * (allsky_tile_size as u32);
let dy = y * (allsky_tile_size as u32);
let dx = x * (d3_tile_allsky_size as u32);
let dy = y * (d3_tile_allsky_size as u32);
let sx = (src_idx % 27) * allsky_tile_size;
let sy = (src_idx / 27) * allsky_tile_size;
let sx = (src_idx % 27) * d3_tile_allsky_size;
let sy = (src_idx / 27) * d3_tile_allsky_size;
let s = ImageBufferView {
x: sx as i32,
y: sy as i32,
w: allsky_tile_size as i32,
h: allsky_tile_size as i32,
x: sx,
y: sy,
w: d3_tile_allsky_size,
h: d3_tile_allsky_size,
};
let d = ImageBufferView {
x: dx as i32,
y: dy as i32,
w: allsky_tile_size as i32,
h: allsky_tile_size as i32,
w: d3_tile_allsky_size,
h: d3_tile_allsky_size,
};
base_tile.tex_sub(&allsky, &s, &d);
base_tile.tex_sub(&image, &s, &d);
src_idx += 1;
}
@@ -281,103 +268,56 @@ fn handle_allsky_file<F: ImageFormat>(
Ok(tiles)
}
fn handle_allsky_fits<F: ImageFormat>(
allsky_data: &[<<F as ImageFormat>::P as Pixel>::Item],
fn handle_allsky_fits<F: TextureFormat>(
image: &[<F::P as Pixel>::Item],
tile_size: i32,
texture_size: i32,
allsky_tile_size: i32,
) -> Result<impl Iterator<Item = ImageBuffer<F>>, JsValue> {
let allsky_tile_size = std::cmp::min(tile_size, 64);
let width_allsky_px = 27 * allsky_tile_size;
let height_allsky_px = 29 * allsky_tile_size;
let d3_tile_allsky_size = std::cmp::min(tile_size, 64);
let width_allsky_px = 27 * d3_tile_allsky_size;
let height_allsky_px = 29 * d3_tile_allsky_size;
// The fits image layout stores rows in reverse
let reversed_rows_data = allsky_data
let reversed_rows_data = image
.chunks(width_allsky_px as usize * F::NUM_CHANNELS)
.rev()
.flatten()
.copied()
.collect::<Vec<_>>();
let allsky = ImageBuffer::<F>::new(reversed_rows_data, width_allsky_px, height_allsky_px);
.collect::<Vec<_>>()
.into_boxed_slice();
let image = ImageBuffer::<F>::new(
reversed_rows_data,
width_allsky_px as u32,
height_allsky_px as u32,
1,
);
let allsky_tiles_iter =
handle_allsky_file::<F>(allsky, allsky_tile_size, texture_size, tile_size)?.map(
move |image| {
// The GPU does a specific transformation on the UV for FITS tiles
// We must revert this to be compatible with this GPU transformation
let new_image_data = image
.get_data()
.chunks((tile_size * tile_size) as usize * F::NUM_CHANNELS)
.flat_map(|c| {
c.chunks(tile_size as usize * F::NUM_CHANNELS)
.rev()
.flatten()
})
.cloned()
.collect();
handle_allsky_file::<F>(image, allsky_tile_size, tile_size)?.map(move |image| {
// The GPU does a specific transformation on the UV for FITS tiles
// We must revert this to be compatible with this GPU transformation
let new_image_data = image
.get_data()
.chunks((allsky_tile_size * allsky_tile_size) as usize * F::NUM_CHANNELS)
.flat_map(|c| {
c.chunks(allsky_tile_size as usize * F::NUM_CHANNELS)
.rev()
.flatten()
})
.cloned()
.collect();
ImageBuffer::<F>::new(new_image_data, tile_size, tile_size)
},
);
ImageBuffer::<F>::new(
new_image_data,
allsky_tile_size as u32,
allsky_tile_size as u32,
1,
)
});
Ok(allsky_tiles_iter)
}
use al_core::image::format::RGBA8U;
use crate::time::Time;
use std::cell::RefCell;
use std::rc::Rc;
pub struct Allsky {
pub image: Rc<RefCell<Option<Vec<ImageType>>>>,
pub time_req: Time,
pub depth_tile: u8,
pub hips_cdid: CreatorDid,
url: Url,
pub channel: Option<u32>,
}
use al_core::texture::format::RGBA8U;
use crate::Abort;
impl Allsky {
pub fn missing(&self) -> bool {
self.image.borrow().is_none()
}
pub fn get_hips_cdid(&self) -> &CreatorDid {
&self.hips_cdid
}
pub fn get_url(&self) -> &Url {
&self.url
}
}
impl<'a> From<&'a AllskyRequest> for Option<Allsky> {
fn from(request: &'a AllskyRequest) -> Self {
let AllskyRequest {
request,
hips_cdid,
depth_tile,
url,
channel,
..
} = request;
if request.is_resolved() {
let Request::<Vec<ImageType>> {
time_request, data, ..
} = request;
Some(Allsky {
time_req: *time_request,
// This is a clone on a Arc, it is supposed to be fast
image: data.clone(),
hips_cdid: hips_cdid.clone(),
url: url.clone(),
depth_tile: *depth_tile,
channel: *channel,
})
} else {
None
}
}
}

View File

@@ -1,164 +0,0 @@
use al_core::image::format::ChannelType;
use std::io::Cursor;
use crate::downloader::query;
use crate::renderable::CreatorDid;
use fitsrs::fits::Fits;
#[derive(Debug, Clone, Copy)]
pub struct Metadata {
pub blank: f32,
pub scale: f32,
pub offset: f32,
}
impl Default for Metadata {
fn default() -> Self {
Metadata {
blank: -1.0,
scale: 1.0,
offset: 0.0,
}
}
}
use super::{Request, RequestType};
use crate::downloader::QueryId;
pub struct PixelMetadataRequest {
pub id: QueryId,
pub url: Url,
pub hips_cdid: CreatorDid,
request: Request<Metadata>,
}
impl From<PixelMetadataRequest> for RequestType {
fn from(request: PixelMetadataRequest) -> Self {
RequestType::PixelMetadata(request)
}
}
use super::Url;
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::JsFuture;
use web_sys::{RequestInit, RequestMode, Response};
impl From<query::PixelMetadata> for PixelMetadataRequest {
// Create a tile request associated to a HiPS
fn from(query: query::PixelMetadata) -> Self {
let query::PixelMetadata {
format,
url,
hips_cdid,
id,
} = query;
let url_clone = url.clone();
let channel = format.get_channel();
let window = web_sys::window().unwrap_abort();
let request = match channel {
ChannelType::R32F | ChannelType::R32I | ChannelType::R16I | ChannelType::R8UI => {
Request::new(async move {
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let request =
web_sys::Request::new_with_str_and_init(&url_clone, &opts).unwrap_abort();
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
// `resp_value` is a `Response` object.
debug_assert!(resp_value.is_instance_of::<Response>());
let resp: Response = resp_value.dyn_into()?;
// See https://github.com/MattiasBuelens/wasm-streams/blob/f6dacf58a8826dc67923ab4a3bae87635690ca64/examples/fetch_as_stream.rs#L25-L33
/*let raw_body = resp.body().ok_or(JsValue::from_str("Cannot extract readable stream"))?;
let body = ReadableStream::from_raw(raw_body.dyn_into()?);
// Convert the JS ReadableStream to a Rust stream
let mut reader = body.try_into_async_read().map_err(|_| JsValue::from_str("readable stream locked"))?;
let image = Fits::new(reader).await?;*/
let array_buffer = JsFuture::from(resp.array_buffer()?).await?;
let bytes_buffer = js_sys::Uint8Array::new(&array_buffer);
let num_bytes = bytes_buffer.length() as usize;
let mut raw_bytes = Vec::with_capacity(num_bytes);
unsafe {
raw_bytes.set_len(num_bytes);
}
bytes_buffer.copy_to(&mut raw_bytes[..]);
let mut reader = Cursor::new(&raw_bytes[..]);
let Fits { hdu } = Fits::from_reader(&mut reader)
.map_err(|_| JsValue::from_str("Parsing fits error"))?;
let header = hdu.get_header();
let scale =
if let Some(fitsrs::card::Value::Float(bscale)) = header.get(b"BSCALE ") {
*bscale as f32
} else {
1.0
};
let offset =
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
};
Ok(Metadata {
blank,
scale,
offset,
})
})
}
_ => Request::new(async move { Ok(Metadata::default()) }),
};
Self {
id,
url,
hips_cdid,
request,
}
}
}
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
pub struct PixelMetadata {
pub value: Rc<RefCell<Option<Metadata>>>,
pub hips_cdid: CreatorDid,
pub url: String,
}
use crate::Abort;
impl<'a> From<&'a PixelMetadataRequest> for Option<PixelMetadata> {
fn from(request: &'a PixelMetadataRequest) -> Self {
let PixelMetadataRequest {
request,
hips_cdid,
url,
..
} = request;
if request.is_resolved() {
let Request::<Metadata> { data, .. } = request;
// It will always be resolved and found as we will request a well know tile (Norder0/Tile0)
Some(PixelMetadata {
hips_cdid: hips_cdid.clone(),
url: url.to_string(),
value: data.clone(),
})
} else {
None
}
}
}

View File

@@ -3,15 +3,15 @@ use crate::renderable::CreatorDid;
use super::{Request, RequestType};
use crate::healpix::coverage::Smoc;
use moclib::deser::fits::MocType;
use moclib::qty::Hpx;
use crate::healpix::moc::Moc;
use crate::healpix::moc::{FreqSpaceMoc, SpaceMoc};
use al_api::hips::DataproductType;
pub struct MOCRequest {
//pub id: QueryId,
pub hips_cdid: CreatorDid,
pub params: MOCOptions,
request: Request<HEALPixCoverage>,
pub request: Request<Moc>,
}
impl From<MOCRequest> for RequestType {
@@ -19,35 +19,14 @@ impl From<MOCRequest> for RequestType {
RequestType::Moc(request)
}
}
use super::Url;
use moclib::deser::fits;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::{RequestInit, Response};
use moclib::moc::range::op::convert::convert_to_u64;
/// Convenient type for Space-MOCs
pub fn from_fits_hpx<T: Idx>(moc: MocType<T, Hpx<T>, Cursor<&[u8]>>) -> Smoc {
match moc {
MocType::Ranges(moc) => convert_to_u64::<T, Hpx<T>, _, Hpx<u64>>(moc).into_range_moc(),
MocType::Cells(moc) => {
convert_to_u64::<T, Hpx<T>, _, Hpx<u64>>(moc.into_cell_moc_iter().ranges())
.into_range_moc()
}
}
}
use crate::healpix::coverage::HEALPixCoverage;
use crate::Abort;
use al_api::moc::MOCOptions;
use moclib::deser::fits::MocIdxType;
use moclib::deser::fits::MocQtyType;
use moclib::idx::Idx;
use moclib::moc::{CellMOCIntoIterator, CellMOCIterator, RangeMOCIterator};
use std::io::Cursor;
use wasm_bindgen::JsValue;
impl From<query::Moc> for MOCRequest {
// Create a tile request associated to a HiPS
fn from(query: query::Moc) -> Self {
@@ -56,7 +35,8 @@ impl From<query::Moc> for MOCRequest {
params,
hips_cdid,
credentials,
mode
mode,
dataproduct_type,
} = query;
let url_clone = url.clone();
@@ -75,25 +55,20 @@ impl From<query::Moc> for MOCRequest {
let resp: Response = resp_value.dyn_into()?;
let array_buffer = JsFuture::from(resp.array_buffer()?).await?;
let bytes_buf = js_sys::Uint8Array::new(&array_buffer);
let num_bytes = bytes_buf.length() as usize;
let mut bytes = Vec::with_capacity(num_bytes);
unsafe {
bytes.set_len(num_bytes);
}
bytes_buf.copy_to(&mut bytes[..]);
let buf = js_sys::Uint8Array::new(&array_buffer);
let bytes = buf.to_vec();
// Coosys is permissive because we load a moc
let smoc = match fits::from_fits_ivoa_custom(Cursor::new(&bytes[..]), true)
.map_err(|e| JsValue::from_str(&e.to_string()))?
{
MocIdxType::U16(MocQtyType::<u16, _>::Hpx(moc)) => Ok(from_fits_hpx(moc)),
MocIdxType::U32(MocQtyType::<u32, _>::Hpx(moc)) => Ok(from_fits_hpx(moc)),
MocIdxType::U64(MocQtyType::<u64, _>::Hpx(moc)) => Ok(from_fits_hpx(moc)),
_ => Err(JsValue::from_str("MOC not supported. Must be a HPX MOC")),
}?;
Ok(HEALPixCoverage(smoc))
Ok(match dataproduct_type {
DataproductType::SpectralCube => {
Moc::FreqSpace(FreqSpaceMoc::from_fits_raw_bytes(&bytes)?)
}
DataproductType::Cube => {
let moc = SpaceMoc::from_fits_raw_bytes(&bytes)?;
Moc::FreqSpace(FreqSpaceMoc::from_space_moc(moc))
}
_ => Moc::Space(SpaceMoc::from_fits_raw_bytes(&bytes)?),
})
});
Self {
@@ -105,39 +80,3 @@ impl From<query::Moc> for MOCRequest {
}
}
}
use std::cell::RefCell;
use std::rc::Rc;
pub struct Moc {
pub moc: Rc<RefCell<Option<HEALPixCoverage>>>,
pub params: MOCOptions,
pub hips_cdid: Url,
}
impl Moc {
pub fn get_hips_cdid(&self) -> &Url {
&self.hips_cdid
}
}
impl<'a> From<&'a MOCRequest> for Option<Moc> {
fn from(request: &'a MOCRequest) -> Self {
let MOCRequest {
request,
hips_cdid,
params,
..
} = request;
if request.is_resolved() {
let Request::<HEALPixCoverage> { data, .. } = request;
Some(Moc {
// This is a clone on a Arc, it is supposed to be fast
moc: data.clone(),
hips_cdid: hips_cdid.clone(),
params: params.clone(),
})
} else {
None
}
}
}

View File

@@ -1,19 +1,18 @@
// A request image should not be used outside this module
// but contained inside a more specific type of query (e.g. for a tile or allsky)
pub mod allsky;
pub mod blank;
pub mod moc;
pub mod tile;
/* ------------------------------------- */
use wasm_bindgen_futures::JsFuture;
use crate::time::Time;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
pub type Url = String;
pub struct Request<R> {
data: Rc<RefCell<Option<R>>>,
time_request: Time,
pub data: Rc<RefCell<Option<R>>>,
pub time_request: Time,
// Flag telling if the tile has been copied so that
// the HtmlImageElement can be reused to download another tile
//ready: bool,
@@ -76,17 +75,19 @@ where
pub fn resolve_status(&self) -> ResolvedStatus {
self.resolved.get()
}
pub fn get_data(&self) -> Rc<RefCell<Option<R>>> {
self.data.clone()
}
}
use allsky::AllskyRequest;
use blank::PixelMetadataRequest;
use moc::MOCRequest;
use tile::TileRequest;
pub enum RequestType {
Tile(TileRequest),
Allsky(AllskyRequest),
PixelMetadata(PixelMetadataRequest),
Moc(MOCRequest), //..
Moc(MOCRequest),
}
use crate::downloader::QueryId;
@@ -95,44 +96,95 @@ impl RequestType {
match self {
RequestType::Tile(request) => &request.id,
RequestType::Allsky(request) => &request.id,
RequestType::PixelMetadata(request) => &request.id,
RequestType::Moc(request) => &request.hips_cdid,
}
}
}
impl<'a> From<&'a RequestType> for Option<Resource> {
fn from(request: &'a RequestType) -> Self {
match request {
RequestType::Tile(request) => Option::<Tile>::from(request).map(Resource::Tile),
RequestType::Allsky(request) => Option::<Allsky>::from(request).map(Resource::Allsky),
RequestType::PixelMetadata(request) => {
Option::<PixelMetadata>::from(request).map(Resource::PixelMetadata)
}
RequestType::Moc(request) => Option::<Moc>::from(request).map(Resource::Moc),
pub fn is_resolved(&self) -> bool {
match self {
RequestType::Tile(request) => request.request.is_resolved(),
RequestType::Allsky(request) => request.request.is_resolved(),
RequestType::Moc(request) => request.request.is_resolved(),
}
}
}
use allsky::Allsky;
use blank::PixelMetadata;
use moc::Moc;
use tile::Tile;
pub enum Resource {
Tile(Tile),
Allsky(Allsky),
PixelMetadata(PixelMetadata),
Moc(Moc),
}
/*
impl Resource {
pub fn id(&self) -> &String {
match self {
Resource::Tile(tile) => &format!("{:?}:{:?}", tile.cell.depth(), tile.cell.idx()),
Resource::Allsky(allsky) => allsky.get_hips_cdid(),
Resource::PixelMetadata(PixelMetadata { hips_cdid, .. }) => hips_cdid,
Resource::Moc(moc) => moc.get_hips_cdid(),
impl From<RequestType> for Option<Resource> {
fn from(request: RequestType) -> Self {
match request {
RequestType::Tile(request) => Option::<Tile>::from(request).map(Resource::Tile),
RequestType::Allsky(request) => Option::<Allsky>::from(request).map(Resource::Allsky),
RequestType::Moc(request) => Option::<FetchedMoc>::from(request).map(Resource::Moc),
}
}
}*/
use crate::Abort;
use web_sys::RequestCredentials;
async fn query_html_image(
url: &str,
credentials: RequestCredentials,
) -> Result<web_sys::HtmlImageElement, JsValue> {
let image = web_sys::HtmlImageElement::new().unwrap_abort();
let image_cloned = image.clone();
// Set the CORS and credentials options for the image
let cors_value = match credentials {
RequestCredentials::Include => Some("use-credentials"),
RequestCredentials::Omit => Some("anonymous"),
RequestCredentials::SameOrigin => Some(""),
_ => None,
};
let promise = js_sys::Promise::new(
&mut (Box::new(move |resolve, reject| {
// Ask for CORS permissions
image_cloned.set_cross_origin(cors_value);
image_cloned.set_onload(Some(&resolve));
image_cloned.set_onerror(Some(&reject));
image_cloned.set_src(url);
}) as Box<dyn FnMut(js_sys::Function, js_sys::Function)>),
);
let _ = JsFuture::from(promise).await?;
Ok(image)
}
use wasm_bindgen::JsCast;
use web_sys::RequestInit;
use web_sys::RequestMode;
use web_sys::Response;
async fn query_bitmap_from_blob(
url: &str,
mode: RequestMode,
credentials: RequestCredentials,
) -> Result<web_sys::ImageBitmap, JsValue> {
let window = web_sys::window().unwrap_abort();
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(mode);
opts.credentials(credentials);
let request = web_sys::Request::new_with_str_and_init(url, &opts).unwrap_abort();
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
// `resp_value` is a `Response` object.
debug_assert!(resp_value.is_instance_of::<Response>());
let resp: Response = resp_value.dyn_into()?;
if resp.ok() {
let blob = JsFuture::from(resp.blob()?)
.await?
.dyn_into::<web_sys::Blob>()?;
let image_bitmap = JsFuture::from(window.create_image_bitmap_with_blob(&blob)?).await?;
Ok(image_bitmap.into())
} else {
Err(JsValue::from_str(
"Response status code not between 200-299.",
))
}
}

View File

@@ -1,23 +1,24 @@
use crate::healpix::cell::HEALPixCell;
use crate::renderable::CreatorDid;
use al_core::image::format::{ChannelType, ImageFormatType, RGB8U, RGBA8U};
use al_core::image::format::ImageFormatType;
use al_core::texture::format::PixelType;
use crate::downloader::query;
use al_core::image::ImageType;
use super::super::query::CellDesc;
use super::Url;
use super::{Request, RequestType};
use crate::downloader::request::query_html_image;
use crate::downloader::QueryId;
pub struct TileRequest {
request: Request<ImageType>,
pub request: Request<ImageType>,
pub id: QueryId,
cell: HEALPixCell,
hips_cdid: CreatorDid,
url: Url,
format: ImageFormatType,
channel: Option<u32>,
pub cell: CellDesc,
pub hips_cdid: CreatorDid,
pub url: Url,
pub format: ImageFormatType,
}
impl From<TileRequest> for RequestType {
@@ -26,37 +27,14 @@ impl From<TileRequest> for RequestType {
}
}
async fn query_html_image(url: &str, credentials: RequestCredentials) -> Result<web_sys::HtmlImageElement, JsValue> {
let image = web_sys::HtmlImageElement::new().unwrap_abort();
let image_cloned = image.clone();
// Set the CORS and credentials options for the image
let cors_value = match credentials {
RequestCredentials::Include => Some("use-credentials"),
RequestCredentials::SameOrigin => Some("anonymous"),
_ => Some("")
};
let promise = js_sys::Promise::new(
&mut (Box::new(move |resolve, reject| {
// Ask for CORS permissions
image_cloned.set_cross_origin(cors_value);
image_cloned.set_onload(Some(&resolve));
image_cloned.set_onerror(Some(&reject));
image_cloned.set_src(&url);
}) as Box<dyn FnMut(js_sys::Function, js_sys::Function)>),
);
let _ = JsFuture::from(promise).await?;
Ok(image)
}
use crate::downloader::request::query_bitmap_from_blob;
use al_core::image::bitmap::Bitmap;
use al_core::image::html::HTMLImage;
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::JsFuture;
use web_sys::{RequestInit, Response, RequestCredentials};
use web_sys::{RequestInit, Response};
impl From<query::Tile> for TileRequest {
// Create a tile request associated to a HiPS
fn from(query: query::Tile) -> Self {
@@ -68,120 +46,93 @@ impl From<query::Tile> for TileRequest {
credentials,
mode,
id,
channel: slice,
create_bitmap_support,
} = query;
let url_clone = url.clone();
let channel = format.get_channel();
let pixel_format = format.get_pixel_format();
let window = web_sys::window().unwrap_abort();
let request = match channel {
ChannelType::RGB8U => Request::new(async move {
/*let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let size = match cell {
CellDesc::HiPS2D { tile_size, .. } | CellDesc::HiPSCube { tile_size, .. } => {
(tile_size, tile_size, 1)
}
CellDesc::HiPS3D {
tile_size,
tile_depth,
..
} => (tile_size, tile_size, tile_depth),
};
let request = web_sys::Request::new_with_str_and_init(&url_clone, &opts).unwrap_abort();
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
// `resp_value` is a `Response` object.
debug_assert!(resp_value.is_instance_of::<Response>());
let resp: Response = resp_value.dyn_into()?;*/
/*/// Bitmap version
let blob = JsFuture::from(resp.blob()?).await?.into();
let image = JsFuture::from(window.create_image_bitmap_with_blob(&blob)?)
.await?
.into();
let image = Bitmap::new(image);
Ok(ImageType::JpgImageRgb8u { image })*/
/*
/// Raw image decoding
let buf = JsFuture::from(resp.array_buffer()?).await?;
let raw_bytes = js_sys::Uint8Array::new(&buf).to_vec();
let image = ImageBuffer::<RGB8U>::from_raw_bytes(&raw_bytes[..], 512, 512)?;
Ok(ImageType::RawRgb8u { image })
*/
// HTMLImageElement
let image = query_html_image(&url_clone, credentials).await?;
// The image has been resolved
Ok(ImageType::HTMLImageRgb8u {
image: HTMLImage::<RGB8U>::new(image),
})
}),
ChannelType::RGBA8U => Request::new(async move {
/*let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let request = web_sys::Request::new_with_str_and_init(&url_clone, &opts).unwrap_abort();
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
// `resp_value` is a `Response` object.
debug_assert!(resp_value.is_instance_of::<Response>());
let resp: Response = resp_value.dyn_into()?;*/
/*/// Bitmap version
let blob = JsFuture::from(resp.blob()?).await?.into();
let image = JsFuture::from(window.create_image_bitmap_with_blob(&blob)?)
.await?
.into();
let image = Bitmap::new(image);
Ok(ImageType::PngImageRgba8u { image })*/
/*
/// Raw image decoding
let buf = JsFuture::from(resp.array_buffer()?).await?;
let raw_bytes = js_sys::Uint8Array::new(&buf).to_vec();
let image = ImageBuffer::<RGBA8U>::from_raw_bytes(&raw_bytes[..], 512, 512)?;
Ok(ImageType::RawRgba8u { image })
*/
// HTMLImageElement
let image = query_html_image(&url_clone, credentials).await?;
// The image has been resolved
Ok(ImageType::HTMLImageRgba8u {
image: HTMLImage::<RGBA8U>::new(image),
})
}),
ChannelType::R32F
| ChannelType::R64F
| ChannelType::R32I
| ChannelType::R16I
| ChannelType::R8UI => Request::new(async move {
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(mode);
opts.credentials(credentials);
let request =
web_sys::Request::new_with_str_and_init(&url_clone, &opts).unwrap_abort();
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
// `resp_value` is a `Response` object.
debug_assert!(resp_value.is_instance_of::<Response>());
let resp: Response = resp_value.dyn_into()?;
// See https://github.com/MattiasBuelens/wasm-streams/blob/f6dacf58a8826dc67923ab4a3bae87635690ca64/examples/fetch_as_stream.rs#L25-L33
/*let raw_body = resp.body().ok_or(JsValue::from_str("Cannot extract readable stream"))?;
let body = ReadableStream::from_raw(raw_body.dyn_into()?);
// Convert the JS ReadableStream to a Rust stream
let mut reader = body.try_into_async_read().map_err(|_| JsValue::from_str("readable stream locked"))?;
let image = Fits::new(reader).await?;
*/
if resp.ok() {
let array_buffer = JsFuture::from(resp.array_buffer()?).await?;
let raw_bytes = js_sys::Uint8Array::new(&array_buffer);
Ok(ImageType::FitsImage { raw_bytes })
let request = match pixel_format {
PixelType::RGB8U => Request::new(async move {
if create_bitmap_support {
// optimized download of tile for GPU (using Blob + Bitmap) without creating any DOM structure
let image_bitmap =
query_bitmap_from_blob(&url_clone, mode, credentials).await?;
Ok(ImageType::ImageRgb8u {
image: Bitmap::new(image_bitmap),
})
} else {
Err(JsValue::from_str(
"Response status code not between 200-299.",
))
// HTMLImageElement
let image = query_html_image(&url_clone, credentials).await?;
// The image has been resolved
Ok(ImageType::HTMLImageRgb8u {
image: HTMLImage::new(image),
})
}
}),
_ => todo!(),
PixelType::RGBA8U => Request::new(async move {
if create_bitmap_support {
// optimized download of tile for GPU (using Blob + Bitmap) without creating any DOM structure
let image_bitmap =
query_bitmap_from_blob(&url_clone, mode, credentials).await?;
Ok(ImageType::ImageRgba8u {
image: Bitmap::new(image_bitmap),
})
} else {
// HTMLImageElement
let image = query_html_image(&url_clone, credentials).await?;
// The image has been resolved
Ok(ImageType::HTMLImageRgba8u {
image: HTMLImage::new(image),
})
}
}),
PixelType::R32F | PixelType::R32I | PixelType::R16I | PixelType::R8U => {
Request::new(async move {
let window = web_sys::window().unwrap_abort();
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(mode);
opts.credentials(credentials);
let request =
web_sys::Request::new_with_str_and_init(&url_clone, &opts).unwrap_abort();
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
// `resp_value` is a `Response` object.
debug_assert!(resp_value.is_instance_of::<Response>());
let resp: Response = resp_value.dyn_into()?;
// See https://github.com/MattiasBuelens/wasm-streams/blob/f6dacf58a8826dc67923ab4a3bae87635690ca64/examples/fetch_as_stream.rs#L25-L33
/*let raw_body = resp.body().ok_or(JsValue::from_str("Cannot extract readable stream"))?;
let body = ReadableStream::from_raw(raw_body.dyn_into()?);
// Convert the JS ReadableStream to a Rust stream
let mut reader = body.try_into_async_read().map_err(|_| JsValue::from_str("readable stream locked"))?;
let image = Fits::new(reader).await?;
*/
if resp.ok() {
let array_buffer = JsFuture::from(resp.array_buffer()?).await?;
let raw_bytes = js_sys::Uint8Array::new(&array_buffer);
Ok(ImageType::FitsRawBytes { raw_bytes, size })
} else {
Err(JsValue::from_str(
"Response status code not between 200-299.",
))
}
})
}
};
Self {
@@ -191,74 +142,8 @@ impl From<query::Tile> for TileRequest {
hips_cdid,
url,
request,
channel: slice,
}
}
}
use crate::time::Time;
use std::cell::RefCell;
use std::rc::Rc;
pub struct Tile {
pub image: Rc<RefCell<Option<ImageType>>>,
pub time_req: Time,
pub cell: HEALPixCell,
pub format: ImageFormatType,
pub channel: Option<u32>,
hips_cdid: CreatorDid,
url: Url,
}
use crate::Abort;
impl Tile {
#[inline(always)]
pub fn missing(&self) -> bool {
self.image.borrow().is_none()
}
#[inline(always)]
pub fn get_hips_cdid(&self) -> &CreatorDid {
&self.hips_cdid
}
#[inline(always)]
pub fn get_url(&self) -> &Url {
&self.url
}
#[inline(always)]
pub fn cell(&self) -> &HEALPixCell {
&self.cell
}
}
impl<'a> From<&'a TileRequest> for Option<Tile> {
fn from(request: &'a TileRequest) -> Self {
let TileRequest {
cell,
request,
hips_cdid,
url,
format,
channel,
..
} = request;
if request.is_resolved() {
let Request::<ImageType> {
time_request, data, ..
} = request;
Some(Tile {
cell: *cell,
time_req: *time_request,
// This is a clone on a Arc, it is supposed to be fast
image: data.clone(),
hips_cdid: hips_cdid.clone(),
url: url.clone(),
format: *format,
channel: *channel,
})
} else {
None
}
}
}

18
src/core/src/event.rs Normal file
View 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");
}

View File

@@ -15,8 +15,9 @@ use healpix::compass_point::MainWind;
use healpix::compass_point::Ordinal;
use healpix::compass_point::OrdinalMap;
use crate::math::lonlat::LonLatT;
use crate::utils;
use crate::Abort;
impl HEALPixCell {
// Build the parent cell
#[inline(always)]
@@ -95,6 +96,13 @@ impl HEALPixCell {
self.depth() == 0
}
#[inline(always)]
pub fn hash_with_dxdy(depth: u8, lon: f64, lat: f64) -> (Self, f64, f64) {
let (hash, dx, dy) = healpix::nested::hash_with_dxdy(depth, lon, lat);
(HEALPixCell(depth, hash), dx, dy)
}
// Find the smallest HEALPix cell containing self and another cells
// Returns None if the 2 HEALPix cell are not located in the same base HEALPix cell
#[inline]
@@ -141,7 +149,7 @@ impl HEALPixCell {
let mut smallest_ancestor = c1.smallest_common_ancestor(c2);
while let (Some(ancestor), Some(cell)) = (smallest_ancestor, cells.next()) {
smallest_ancestor = ancestor.smallest_common_ancestor(&cell);
smallest_ancestor = ancestor.smallest_common_ancestor(cell);
}
smallest_ancestor
@@ -474,12 +482,89 @@ impl Iterator for HEALPixTilesIter {
// Follow the z-order curve
impl PartialOrd for HEALPixCell {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.z_29().partial_cmp(&other.z_29())
Some(self.cmp(other))
}
}
impl Ord for HEALPixCell {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap_abort()
self.z_29().cmp(&other.z_29())
}
}
/// A simple object describing a cubic tile of a HiPS3D
#[derive(Eq, Hash, PartialEq, Clone, Debug)]
pub struct HEALPixFreqCell {
pub hpx: HEALPixCell,
pub f_hash: u64,
pub f_depth: u8,
}
use crate::math::spectra::Freq;
use crate::math::spectra::SpectralUnit;
impl HEALPixFreqCell {
pub fn from_lonlat(lonlat: LonLatT<f64>, freq: Freq, s_depth: u8, f_depth: u8) -> Self {
let hpx = HEALPixCell::new(
s_depth,
lonlat.lon().to_radians(),
lonlat.lat().to_radians(),
);
let f_hash = freq.hash(f_depth);
Self {
hpx,
f_hash,
f_depth,
}
}
pub fn new(hpx: HEALPixCell, f_hash: u64, f_depth: u8) -> Self {
Self {
hpx,
f_hash,
f_depth,
}
}
pub fn hpx_parent(&self) -> Self {
Self {
hpx: self.hpx.parent(),
f_hash: self.f_hash,
f_depth: self.f_depth,
}
}
pub fn parent(&self) -> Self {
Self {
hpx: self.hpx.parent(),
f_hash: self.f_hash >> 1,
f_depth: self.f_depth - 1,
}
}
pub fn is_hpx_root(&self) -> bool {
self.hpx.is_root()
}
pub fn freq_range(&self) -> Range<Freq> {
let f0 = Freq::from_hash_with_order(self.f_hash, self.f_depth);
let f1 = Freq::from_hash_with_order(
(self.f_hash + 1).min(Freq::num_max_cells(self.f_depth) as u64),
self.f_depth,
);
f0..f1
}
pub fn pixel_frequencies(&self, num_pixels: usize) -> impl Iterator<Item = f32> {
let delta_depth = num_pixels.trailing_zeros();
let pixel_depth = self.f_depth + delta_depth as u8;
let h0 = self.f_hash << delta_depth;
let h1 = (self.f_hash + 1) << delta_depth;
(h0..h1).map(move |hash| Freq::from_hash_with_order(hash, pixel_depth).0 as f32)
}
}

View File

@@ -70,7 +70,7 @@ impl IdxVec {
let bbox1 = a1.get_containing_hpx_cell();
let bbox2 = a2.get_containing_hpx_cell();
bbox1.cmp(&bbox2)
bbox1.cmp(bbox2)
});
// At this point the arcs are sorted by the z-order curve of their

View File

@@ -0,0 +1,131 @@
use crate::healpix::cell::HEALPixFreqCell;
use moclib::hpxranges2d::HpxRanges2D;
use moclib::ranges::ranges2d::Ranges2D;
use moclib::qty::{Frequency, MocQty};
#[derive(Debug)]
pub struct FreqSpaceMoc(pub moclib::hpxranges2d::FreqSpaceMoc<u64, u64>);
impl Clone for FreqSpaceMoc {
fn clone(&self) -> Self {
let HpxRanges2D(Moc2DRanges {
ranges2d: Ranges2D { x, y },
..
}) = &**self;
Self(HpxRanges2D(Moc2DRanges::new(x.clone(), y.clone())))
}
}
use wasm_bindgen::JsValue;
use moclib::deser::fits;
use moclib::deser::fits::MocIdxType;
use moclib::deser::fits::MocQtyType;
use moclib::mocranges2d::Moc2DRanges;
use std::io::Cursor;
impl FreqSpaceMoc {
/// Create a FreqSpaceMoc from a
pub fn from_space_moc(moc: SpaceMoc) -> Self {
let moc_2d = Moc2DRanges::new(vec![0..u64::MAX; 1], vec![moc.0.into_moc_ranges().0]);
FreqSpaceMoc(HpxRanges2D(moc_2d))
}
pub fn from_fits_raw_bytes(bytes: &[u8]) -> Result<Self, JsValue> {
let sfmoc = match fits::from_fits_ivoa_custom(Cursor::new(bytes), true)
.map_err(|e| JsValue::from_str(&e.to_string()))?
{
//MocIdxType::U16(MocQtyType::<u16, _>::FreqHpx(moc)) => Ok(from_fits_hpx(moc)),
//MocIdxType::U32(MocQtyType::<u32, _>::FreqHpx(moc)) => Ok(from_fits_hpx(moc)),
MocIdxType::U64(MocQtyType::<u64, _>::FreqHpx(ranges_iter)) => {
/*al_core::log(&format!(
"ranges moc 2D iter from fits {:?}",
));*/
let moc_2d_ranges = Moc2DRanges::from_ranges_it(ranges_iter);
let inner = moclib::hpxranges2d::HpxRanges2D(moc_2d_ranges);
Ok(inner)
}
_ => Err(JsValue::from_str(
"MOC not supported. Must be a FREQ|HPX 2DMOC coded on U64 only",
)),
}?;
Ok(Self(sfmoc))
}
/*pub fn from_fixed_hpx_cells(
depth: u8,
hpx_idx: impl Iterator<Item = u64>,
cap: Option<usize>,
) -> Self {
let moc = RangeMOC::from_fixed_depth_cells(depth, hpx_idx, cap);
SpaceMoc(moc)
}
pub fn from_hpx_cells<'a>(
depth: u8,
hpx_cell_it: impl Iterator<Item = &'a HEALPixCell>,
cap: Option<usize>,
) -> Self {
let cells_it = hpx_cell_it.map(|HEALPixCell(depth, idx)| (*depth, *idx));
let moc = RangeMOC::from_cells(depth, cells_it, cap);
SpaceMoc(moc)
}*/
pub fn f_max_depth(&self) -> u8 {
self.0.compute_min_depth().0
}
pub fn s_max_depth(&self) -> u8 {
self.0.compute_min_depth().1
}
pub fn sky_fraction(&self) -> f64 {
todo!()
}
pub fn intersects_cell(&self, cell: &HEALPixFreqCell) -> bool {
let HEALPixFreqCell {
hpx,
f_hash,
f_depth,
} = *cell;
let f_hash_0 = f_hash << (Frequency::<u64>::MAX_DEPTH - f_depth);
let f_hash_1 = (f_hash + 1) << (Frequency::<u64>::MAX_DEPTH - f_depth);
//let f0 = Frequency::<u64>::hash2freq(5171582628058365952);
//let f1 = Frequency::<u64>::hash2freq(5171590187200806912);
//al_core::log(&format!("F1: {f0}"));
let hpx_ranges_2d = HpxRanges2D::create_from_freq_ranges_positions(
vec![f_hash_0..f_hash_1; 1],
vec![hpx.idx()],
Frequency::<u64>::MAX_DEPTH,
hpx.depth(),
);
!self.0.intersection(&hpx_ranges_2d).is_empty()
}
/*/// provide the list of (hash hpx, hash freq) of the cells contained in the sfmoc
pub fn cells(&self) -> impl Iterator<Item = (u64, u64)> {
todo!()
}*/
}
use core::ops::Deref;
use super::SpaceMoc;
impl Deref for FreqSpaceMoc {
type Target = moclib::hpxranges2d::FreqSpaceMoc<u64, u64>;
fn deref(&'_ self) -> &'_ Self::Target {
&self.0
}
}

View File

@@ -0,0 +1,10 @@
mod freq_space;
mod space;
pub use freq_space::FreqSpaceMoc;
pub use space::SpaceMoc;
pub enum Moc {
FreqSpace(FreqSpaceMoc),
Space(SpaceMoc),
}

View File

@@ -1,8 +1,8 @@
use crate::math::lonlat::LonLat;
use crate::math::lonlat::LonLatT;
use crate::math::PI;
use crate::math::{self, lonlat::LonLat};
use cgmath::Vector3;
use moclib::moc::RangeMOCIntoIterator;
use moclib::{
moc::range::{CellSelection, RangeMOC},
qty::Hpx,
@@ -12,9 +12,63 @@ pub type Smoc = RangeMOC<u64, Hpx<u64>>;
use crate::healpix::cell::HEALPixCell;
#[derive(Clone, Debug)]
pub struct HEALPixCoverage(pub Smoc);
pub struct SpaceMoc(pub Smoc);
use wasm_bindgen::JsValue;
use moclib::deser::fits;
use moclib::deser::fits::MocIdxType;
use moclib::deser::fits::MocQtyType;
use moclib::idx::Idx;
use moclib::moc::range::op::convert::convert_to_u64;
use moclib::moc::{CellMOCIntoIterator, CellMOCIterator, RangeMOCIterator};
/// Convenient type for Space-MOCs
pub fn from_fits_hpx<T: Idx>(moc: MocType<T, Hpx<T>, Cursor<&[u8]>>) -> Smoc {
match moc {
MocType::Ranges(moc) => convert_to_u64::<T, Hpx<T>, _, Hpx<u64>>(moc).into_range_moc(),
MocType::Cells(moc) => {
convert_to_u64::<T, Hpx<T>, _, Hpx<u64>>(moc.into_cell_moc_iter().ranges())
.into_range_moc()
}
}
}
use moclib::deser::fits::MocType;
use std::io::Cursor;
impl SpaceMoc {
pub fn from_fits_raw_bytes(bytes: &[u8]) -> Result<Self, JsValue> {
let smoc = match fits::from_fits_ivoa_custom(Cursor::new(bytes), true)
.map_err(|e| JsValue::from_str(&e.to_string()))?
{
MocIdxType::U16(MocQtyType::<u16, _>::Hpx(moc)) => Ok(from_fits_hpx(moc)),
MocIdxType::U32(MocQtyType::<u32, _>::Hpx(moc)) => Ok(from_fits_hpx(moc)),
MocIdxType::U64(MocQtyType::<u64, _>::Hpx(moc)) => Ok(from_fits_hpx(moc)),
_ => Err(JsValue::from_str("MOC not supported. Must be a HPX MOC")),
}?;
Ok(Self(smoc))
}
pub fn from_json(s: &str) -> Result<Self, JsValue> {
let moc = moclib::deser::json::from_json_aladin::<u64, Hpx<u64>>(s)
.map_err(|e| JsValue::from(js_sys::Error::new(&e.to_string())))?
.into_cell_moc_iter()
.ranges()
.into_range_moc();
Ok(Self(moc))
}
pub fn serialize_to_json(&self) -> Result<String, JsValue> {
let mut buf: Vec<u8> = Default::default();
(&self.0)
.into_range_moc_iter()
.cells()
.to_json_aladin(None, &mut buf)
.map(|()| unsafe { String::from_utf8_unchecked(buf) })
.map_err(|err| JsValue::from_str(&format!("{err:?}")))
}
impl HEALPixCoverage {
pub fn from_3d_coos<T: LonLat<f64>>(
// The depth of the smallest HEALPix cells contained in it
depth: u8,
@@ -38,7 +92,7 @@ impl HEALPixCoverage {
depth,
CellSelection::All,
);
HEALPixCoverage(moc)
SpaceMoc(moc)
}
pub fn from_fixed_hpx_cells(
@@ -47,7 +101,7 @@ impl HEALPixCoverage {
cap: Option<usize>,
) -> Self {
let moc = RangeMOC::from_fixed_depth_cells(depth, hpx_idx, cap);
HEALPixCoverage(moc)
SpaceMoc(moc)
}
pub fn from_hpx_cells<'a>(
@@ -58,14 +112,14 @@ impl HEALPixCoverage {
let cells_it = hpx_cell_it.map(|HEALPixCell(depth, idx)| (*depth, *idx));
let moc = RangeMOC::from_cells(depth, cells_it, cap);
HEALPixCoverage(moc)
SpaceMoc(moc)
}
pub fn from_cone(lonlat: &LonLatT<f64>, rad: f64, depth: u8) -> Self {
if rad >= PI {
Self::allsky(depth)
} else {
HEALPixCoverage(RangeMOC::from_cone(
SpaceMoc(RangeMOC::from_cone(
lonlat.lon().to_radians(),
lonlat.lat().to_radians(),
rad,
@@ -78,16 +132,12 @@ impl HEALPixCoverage {
pub fn allsky(depth_max: u8) -> Self {
let moc = RangeMOC::new_full_domain(depth_max);
HEALPixCoverage(moc)
}
pub fn contains_coo(&self, coo: &Vector3<f64>) -> bool {
let (lon, lat) = math::lonlat::xyz_to_radec(coo);
self.0.is_in(lon.to_radians(), lat.to_radians())
SpaceMoc(moc)
}
pub fn contains_lonlat(&self, lonlat: &LonLatT<f64>) -> bool {
self.0.is_in(lonlat.lon().to_radians(), lonlat.lat().to_radians())
self.0
.is_in(lonlat.lon().to_radians(), lonlat.lat().to_radians())
}
// O(log2(N))
@@ -97,9 +147,9 @@ impl HEALPixCoverage {
self.0.moc_ranges().intersects_range(&z29_rng)
}
pub fn is_intersecting(&self, other: &Self) -> bool {
/*pub fn is_intersecting(&self, other: &Self) -> bool {
!self.0.intersection(&other.0).is_empty()
}
}*/
pub fn depth(&self) -> u8 {
self.0.depth_max()
@@ -110,16 +160,16 @@ impl HEALPixCoverage {
}
pub fn not(&self) -> Self {
HEALPixCoverage(self.0.not())
SpaceMoc(self.0.not())
}
pub fn empty(depth: u8) -> Self {
HEALPixCoverage(RangeMOC::new_empty(depth))
SpaceMoc(RangeMOC::new_empty(depth))
}
}
use core::ops::Deref;
impl Deref for HEALPixCoverage {
impl Deref for SpaceMoc {
type Target = Smoc;
fn deref(&'_ self) -> &'_ Self::Target {

View File

@@ -1,4 +1,4 @@
pub mod cell;
pub mod coverage;
pub mod index_vector;
pub mod moc;
pub mod utils;
pub mod index_vector;

View File

@@ -1,12 +1,11 @@
use crate::healpix::cell::HEALPixCell;
use crate::math::lonlat::LonLatT;
use crate::math::angle::ToAngle;
/// A simple wrapper around sore core methods
/// of cdshealpix
///
/// cdshealpix is developped by F-X. Pineau.
/// Please check its github repo: https://github.com/cds-astro/cds-healpix-rust
use crate::healpix::cell::HEALPixCell;
use crate::math::angle::ToAngle;
use crate::math::lonlat::LonLatT;
/// Get the vertices of an HEALPix cell
use cgmath::BaseFloat;
#[allow(dead_code)]

View File

@@ -3,7 +3,6 @@ use cgmath::Vector3;
use crate::camera::CameraViewPort;
use crate::math::angle::ToAngle;
use crate::math::projection::ProjectionType;
use crate::time::{DeltaTime, Time};
/// State for inertia
pub struct Inertia {
// Initial angular distance
@@ -12,21 +11,20 @@ pub struct Inertia {
// Vector of rotation
axis: Vector3<f64>,
// The time when the inertia begins
time_start: Time,
north_up: bool,
}
impl Inertia {
pub fn new(ampl: f64, axis: Vector3<f64>, north_up: bool) -> Self {
Inertia {
time_start: Time::now(),
ampl,
speed: ampl,
speed: (ampl * 0.5).min(0.1),
axis,
north_up
north_up,
}
}
/*
pub fn apply(&mut self, camera: &mut CameraViewPort, proj: &ProjectionType, _dt: DeltaTime) {
let t = ((Time::now() - self.time_start).as_millis() / 1000.0) as f64;
// Undamped angular frequency of the oscillator
@@ -46,6 +44,27 @@ impl Inertia {
let fov = start_fov * (1_f32 - alpha) + goal_fov * alpha;*/
camera.apply_axis_rotation(&self.axis, self.speed.to_angle(), proj);
if self.north_up {
camera.set_position_angle(0.0.to_angle(), proj);
}
}*/
pub fn apply(&mut self, camera: &mut CameraViewPort, proj: &ProjectionType, dt: f64) {
// Initial angular velocity
//let v0 = self.ampl * 0.5;
// Friction coefficient (tweak this)
const DAMPING_FACTOR: f64 = 5e-3;
self.speed *= (-DAMPING_FACTOR * dt).exp();
let delta_angle = self.speed * dt;
// Exponential decay of angular velocity
// self.speed = (v0 * (-damping * t).exp()).min(3.0);
//camera.apply_axis_rotation(&self.axis, self.speed.to_angle(), proj);
camera.apply_axis_rotation(&self.axis, delta_angle.to_angle(), proj);
if self.north_up {
camera.set_position_angle(0.0.to_angle(), proj);
}

Some files were not shown because too many files have changed in this diff Show More