Compare commits

...

27 Commits

Author SHA1 Message Date
Matthieu Baumann
66d3b8dcce first HiPS3D commit 2025-07-01 20:07:14 +02:00
Matthieu Baumann
82580c8104 use fitsrs 0.3.4 fixed version 2025-06-27 17:20:43 +02:00
Matthieu Baumann
bad2211ff0 clippy + fmt 2025-06-27 11:40:33 +02:00
Matthieu Baumann
1fc515e9e9 fix big FITS orientation. new feat: subdivide big PNG/JPG image as well 2025-06-26 23:18:00 +02:00
Matthieu Baumann
d3c869edb4 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-17 19:54:35 +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
Matthieu Baumann
5d20bf3946 add documentation for the EventListener positionChanged and mouseMove events. Return the frame for the mouseMove event. Fix #299 2025-05-14 14:28:41 +02:00
bmatthieu3
5d449b6185 fix #298 A.coo not imported 2025-05-14 10:04:00 +02:00
bmatthieu3
89e3eb7e0a remove rust warning 2025-05-07 16:37:56 +02:00
bmatthieu3
05f1ae2808 feat: color picker and read pixel
This PR targets: #253, #76 and to some extent, maybe #208

It features:
* api: readPixel on a HiPS object method taking x, y pixel values on the
  screen. When no screen coo are given, the one of the center screen is
used.
* api: HiPS.probe a general method using HiPS.readPixel under the hood.
  It allow to probe the survey pixel values on a pixel, along a screen line and
along a great circle arc on the sky
* fix: readPixel could throw an exception if the tile has not been
  received. If so, now, we return a JS null value
* fix: retrieve pixels on fits HiPS bitpix=-32
* feat: a new aladin mode called TOOL_COLOR_PICKER.
* tool: showColorPickerControl: when clicking on it, enter the
  TOOL_COLOR_PICKER mode. The user can move the mouse on a pixel to know
its value. With a click, the pixel value is copied to its clipboard
* fix: restore samp button to connect to a hub
* fix: call redraw when calling gotoRaDec to update instantly the
  imageCanvas #208
* a new global readCanvas method on the Aladin object that
will simply read the final image Canvas. User can give a pixel coo, a
line (2 pixel coos), a rect (2 pixel coos for the box)
2025-05-07 16:37:56 +02:00
Philip Matsson
261ea2b68c Support credentials on MOC queries 2025-05-07 14:49:34 +02:00
Philip Matsson
deab96c4d5 Support credentials on allsky queries, adding docs and cleaning up 2025-05-07 14:49:34 +02:00
Philip Matsson
48ba43eba7 Allow setting HiPS CORS and credential options 2025-05-07 14:49:34 +02:00
Alexandra Moskowitz
5d67b7f260 docs: clarify use of precision in Coo 2025-05-02 15:51:06 +02:00
Thomas Boch
30112c300d Merge pull request #290 from cds-astro/fix-avm-example
docs: change to an image with correct astrometry in example
2025-04-23 13:39:19 +02:00
MARCHAND MANON
35840e60a0 docs: change to an image with correct astrometry in example
Co-authored-by: tboch <tboch@users.noreply.github.com>
2025-04-23 11:55:53 +02:00
MARCHAND MANON
9a3a8a0599 docs: just fixing typo in image's doc 2025-04-16 17:10:52 +02:00
MARCHAND MANON
b5fcd02b96 feat: add a rotationChanged event 2025-04-16 08:40:32 +02:00
MARCHAND MANON
6c3aa0bf00 group hips layer events together 2025-04-16 08:40:32 +02:00
bmatthieu3
ea38cdce30 Reverse the longitude axis globally
This method does add:
* reverseLongitude method on aladin object
* add a longitudeReversed flag in AladinOptions, when creating the
aladin view to reverse the longitude axis globally at start.
* BREAKS the HiPS options API by removing the longitudeReversed option
at the HiPS level, replacing it by a global flag on the aladin lite view
* fix: when adding two times the same survey, we could not change its
hips options

This commit also impl #191
2025-04-09 16:08:48 +02:00
219 changed files with 5434 additions and 5473 deletions

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

View File

@@ -0,0 +1,8 @@
<?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 20 20" xmlns="http://www.w3.org/2000/svg">
<rect x="0" fill="none" width="20" height="20"/>
<g>

After

Width:  |  Height:  |  Size: 529 B

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");

View File

@@ -201,7 +201,7 @@
let stephansNIRCamMIRI
async function s11() {
stephansNIRCamMIRI = aladin.createImageSurvey('CDS/P/JWST/Stephans-Quintet/NIRCam+MIRI', "stephansMIRI", null, null, null)
stephansNIRCamMIRI = aladin.createImageSurvey('CDS/P/JWST/Stephans-Quintet/NIRCam-MIRI', "stephansNircamMIRI", null, null, null)
stephansNIRCamMIRI.setOpacity(0.0)
aladin.setOverlayImageLayer(stephansNIRCamMIRI, 'stephansNIRCamMIRI')

View File

@@ -23,6 +23,7 @@
<script src="https://code.jquery.com/jquery-1.10.1.min.js"></script>
<script type="text/javascript">
let aladin;
let longitudeReversed = false;
</script>
<div id="aladin-lite-div" style="width:100vw;height:100vh;">
<div id="calibCircle" style="display: none;"></div>
@@ -37,8 +38,8 @@
<!-- fin temporaire gestion cercle -->
<b>Orientation</b><br>
<button id="hips-coronelli" class="pure-button" name="ref-hips" onclick="aladin.setImageSurvey('Coronelli')">Normal</button><br>
<button id="hips-illenoroc" class="pure-button" name="ref-hips" onclick="aladin.setImageSurvey('illenoroC')">Inversé</button>
<button id="hips-coronelli" class="pure-button" name="ref-hips" onclick="longitudeReversed = false; aladin.reverseLongitude(longitudeReversed)">Normal</button><br>
<button id="hips-illenoroc" class="pure-button" name="ref-hips" onclick="longitudeReversed = true; aladin.reverseLongitude(longitudeReversed)">Inversé</button>
<br><br>
<b>Constellations</b>
@@ -232,8 +233,7 @@
A.init.then(() => {
var hipsDir="http://alasky.u-strasbg.fr/CDS_P_Coronelli";
aladin = A.aladin("#aladin-lite-div", {showSimbadPointerControl: true, expandLayersControl: true, realFullscreen: true, fov: 100, allowFullZoomout: true, showReticle: false });
aladin.createImageSurvey('illenoroC', 'illenoroC', hipsDir, 'equatorial', 4, {imgFormat: 'jpg', longitudeReversed: false});
aladin.createImageSurvey('Coronelli', 'Coronelli', hipsDir, 'equatorial', 4, {imgFormat: 'jpg', longitudeReversed: true});
aladin.createImageSurvey('Coronelli', 'Coronelli', hipsDir, 'equatorial', 4, {imgFormat: 'jpg'});
aladin.setImageSurvey('Coronelli');
$('#layersControlLeft').show();

View File

@@ -16,6 +16,7 @@
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

@@ -64,6 +64,10 @@
console.log(proj)
});
aladin.on('positionChanged', function(pos) {
console.log(pos)
});
aladin.on('layerChanged', function(imageLayer, layer, state){
console.log(imageLayer, layer, state)
});

View File

@@ -0,0 +1,23 @@
<!doctype html>
<html>
<head>
</head>
<body>
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
<script type="module">
import A from '../src/js/A.js';
A.init.then(() => {
let aladin = A.aladin('#aladin-lite-div', {longitudeReversed: true, showCooGrid: true, cooFrame: 'galactic', target: '0 0', fov: 100});
//aladin.reverseLongitude(true)
setTimeout(
() => {
aladin.reverseLongitude(false)
},
5000
)
});
</script>
</body>
</html>

View File

@@ -5,16 +5,16 @@
<body>
<div>
<div id="offset" style="display: inline-block; width: 200px; height: 100px"></div>
<div id="aladin-lite-div" style="display: inline-block; width: 50%"></div>
<div id="aladin-lite-div" style="width: 500px; height: 500px"></div>
</div>
<!--<script type="text/javascript" src="https://aladin.cds.unistra.fr/AladinLite/api/v3/latest/aladin.js" charset="utf-8"></script>-->
<script>let aladin, hips</script>
<script type="module">
import A from '../src/js/A.js';
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {projection: 'TAN', cooFrame: 'galactic', showSettingsControl: true, showSimbadPointerControl: true, showContextMenu: true, target: 'galactic center', survey: 'P/Finkbeiner'});
aladin = A.aladin('#aladin-lite-div', {projection: 'TAN', cooFrame: 'galactic', showSettingsControl: true, showSimbadPointerControl: true, showContextMenu: true, target: 'galactic center'});
// possible values are 'blues', 'cividis', 'cubehelix', 'eosb', 'grayscale', 'inferno', 'magma', 'native', 'parula', 'plasma', 'rainbow',
// 'rdbu', 'rdylbu', 'redtemperature', 'sinebow', 'spectral', 'summer', 'viridis', 'ylgnbu' and 'ylorbr'

View File

@@ -10,7 +10,7 @@
import A from '../src/js/A.js';
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {survey: 'http://alasky.cds.unistra.fr/ancillary/GaiaDR2/hips-density-map/', showProjectionControl: true, showContextMenu: true, showStatusBar: true, fullScreen: true, target: 'galactic center'});
aladin = A.aladin('#aladin-lite-div', {survey: 'http://alasky.cds.unistra.fr/ancillary/GaiaDR2/hips-density-map/', showProjectionControl: true, showContextMenu: true, showStatusBar: true, fullScreen: true, target: 'galactic center', expandLayersControl: true});
const fluxMap = aladin.createImageSurvey('gdr3-color-flux-map', 'Gaia DR3 flux map', 'https://alasky.u-strasbg.fr/ancillary/GaiaEDR3/color-Rp-G-Bp-flux-map', 'equatorial', 7);
const densityMap = aladin.createImageSurvey('gdr3-density-map', 'Gaia DR3 density map', 'sdfsg', 'equatorial', 7, {formats: ['fits']});

View File

@@ -8,11 +8,11 @@
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/images/sig05-013.jpg",
"https://www.virtualastronomy.org/files/avm_examples/spitzer/ssc2005-24a1.jpg",
{
name: "sig05-017",
name: "spitzer-ssc2005-24a1",
successCallback: (ra, dec, fov, image) => {
console.log(ra, dec)
aladin.gotoRaDec(ra, dec);

View File

@@ -14,7 +14,7 @@
{
name: "M61",
wcs: {
NAXIS: 0, // Minimal header
NAXIS: 2, // Minimal header
CTYPE1: 'RA---TAN', // TAN (gnomic) projection
CTYPE2: 'DEC--TAN', // TAN (gnomic) projection
EQUINOX: 2000.0, // Equatorial coordinates definition (yr)

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: false, showCooGrid: false, showFrame: false, showCooLocation: false});
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});
});
</script>

View File

@@ -10,7 +10,6 @@ A.init.then(() => {
let aladin = A.aladin('#aladin-lite-div', {fov: 70,projection: "AIT"});
aladin.setOverlayImageLayer(A.imageHiPS(
'Fermi',
"https://alasky.cds.unistra.fr/Fermi/Color",
{
name: "Fermi color",

123
examples/al-read-pixel.html Normal file
View File

@@ -0,0 +1,123 @@
<!doctype html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script>
</head>
<body>
<div id="aladin-lite-div" style="width: 768px; height: 512px"></div>
<canvas id="myChart" style="width:100%;max-width:600px"></canvas>
<script>let aladin;</script>
<script type="module">
function getPixelsOnLine(startX, startY, endX, endY){
const pixelCols = [];
var x = Math.floor(startX);
var y = Math.floor(startY);
const xx = Math.floor(endX);
const yy = Math.floor(endY);
const dx = Math.abs(xx - x);
const sx = x < xx ? 1 : -1;
const dy = -Math.abs(yy - y);
const sy = y < yy ? 1 : -1;
var err = dx + dy;
var e2;
var end = false;
while (!end) {
pixelCols.push([x,y]);
if ((x === xx && y === yy)) {
end = true;
} else {
e2 = 2 * err;
if (e2 >= dy) {
err += dy;
x += sx;
}
if (e2 <= dx) {
err += dx;
y += sy;
}
}
}
return pixelCols;
}
import A from '../src/js/A.js';
A.init.then(() => {
aladin = A.aladin(
'#aladin-lite-div',
{
showSimbadPointerControl: true,
survey: 'P/allWISE/color', // set initial image survey
projection: 'AIT', // set a projection
fov: 360, // initial field of view in degrees
target: 'orion', // initial target
cooFrame: 'icrs', // set galactic frame
reticleColor: '#ff89ff', // change reticle color
reticleSize: 64, // change reticle size
showContextMenu: true,
showShareControl: true,
showFrame: true,
showZoomControl:true,
showSettingsControl:true,
showColorPickerControl: true,
showCooGrid: true,
fullScreen: true,
samp: true,
realFullscreen: true,
}
);
let base = aladin.getBaseImageLayer();
aladin.select('line', p => {
let xValues = [];
let rValues = [];
let gValues = [];
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})) {
xValues.push(i)
rValues.push(r)
gValues.push(g)
bValues.push(b)
i++;
}
new Chart("myChart", {
type: "line",
data: {
labels: xValues,
datasets: [{
fill: false,
lineTension: 0,
backgroundColor: "rgba(255,0,0,1.0)",
data: rValues
},
{
fill: false,
lineTension: 0,
backgroundColor: "rgba(0,255,0,1.0)",
data: gValues
},
{
fill: false,
lineTension: 0,
backgroundColor: "rgba(0,0,255,1.0)",
data: bValues
}]
},
options: {
legend: {display: false},
scales: {
yAxes: [{ticks: {min: 0, max:255}}],
}
}
});
})
});
</script>
</body>
</html>

View File

@@ -15,16 +15,17 @@
let survey1 = aladin.getBaseImageLayer();
survey1.setColormap('magma', {stretch: 'linear'});
let survey2 = aladin.newImageSurvey("CSIRO/P/RACS/low/I");
aladin.setImageLayer(survey2)
let survey2 = aladin.newImageSurvey("CSIRO/P/RACS/low/I", {name: 'racs low'});
survey2.setColormap('rdbu', {stretch: 'linear'});
aladin.setImageLayer(survey2)
let survey3 = aladin.newImageSurvey("CSIRO/P/RACS/mid/I");
aladin.setImageLayer(survey3)
survey3.setColormap('cubehelix', {stretch: 'asinh'});
aladin.setImageLayer(survey2);
setTimeout(() => {
aladin.removeHiPSFromFavorites(survey3)
}, 5000);

View File

@@ -5,7 +5,6 @@
<body>
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
<div id='aladin-statsDiv'></div>
<script>let aladin, hips</script>
<script type="module">
import A from '../src/js/A.js';

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>

View File

@@ -17,6 +17,9 @@
aladin.on("zoomChanged", () => {
console.log("zoomChanged")
})
aladin.on("rotationChanged", (rotation) => {
console.log("Rotation just changed to ", rotation);
})
aladin.on("positionChanged", ({ra, dec}) => {
console.log('call to aladin', aladin.pix2world(300, 300))
console.log('positionChanged in icrs', ra, dec)
@@ -25,6 +28,7 @@
aladin.gotoRaDec(0, 20);
aladin.on('rightClickMove', (x, y) => {
aladin.setRotation(aladin.getRotation() + 2)
console.log("right click move", x, y)
})
});

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

@@ -26,8 +26,7 @@ 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"
fitsrs = "0.3.4"
colorgrad = "0.6.2"
[features]
@@ -51,7 +50,7 @@ version = "0.7.3"
[dependencies.moclib]
package = "moc"
version = "0.17.0"
version = "0.18.0"
[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", "Response", "XmlHttpRequest", "XmlHttpRequestResponseType", "PerformanceTiming", "Performance", "Url", "ReadableStream", "File", "FileList",]
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",]
[dev-dependencies.image-decoder]
package = "image"

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,18 +48,42 @@ 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>,
// 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,
request_mode: String,
}
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
@@ -124,6 +148,16 @@ impl HiPSProperties {
pub fn get_initial_dec(&self) -> Option<f64> {
self.hips_initial_dec
}
#[inline(always)]
pub fn get_request_credentials(&self) -> &str {
&self.request_credentials
}
#[inline(always)]
pub fn get_request_mode(&self) -> &str {
&self.request_mode
}
}
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -136,6 +170,15 @@ pub enum ImageExt {
Webp,
}
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[wasm_bindgen]
#[serde(rename_all = "camelCase")]
pub enum DataproductType {
SpectralCube,
Image,
Cube,
}
impl std::fmt::Display for ImageExt {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
@@ -150,9 +193,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,
@@ -176,12 +220,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)
@@ -223,16 +261,10 @@ pub struct ImageMetadata {
pub blend_cfg: BlendCfg,
#[serde(default = "default_opacity")]
pub opacity: f32,
#[serde(default = "default_longitude_reversed")]
pub longitude_reversed: bool,
/// the current format chosen
pub img_format: ImageExt,
}
fn default_longitude_reversed() -> bool {
true
}
fn default_opacity() -> f32 {
1.0
}

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,6 +1,6 @@
[package]
name = "al-core"
version = "3.6.5"
version = "3.7.0"
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
edition = "2018"
@@ -9,7 +9,7 @@ js-sys = "0.3.47"
cgmath = "*"
jpeg-decoder = "0.3.0"
png = "0.17.6"
fitsrs = "0.2.10"
fitsrs = "0.3.4"
al-api = { path = "../al-api" }
serde = { version = "^1.0.59", features = ["derive"] }
serde_json = "1.0"

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) {
(self.image.width(), self.image.height())
}
}

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) {
(self.canvas.width(), self.canvas.height())
}
}

View File

@@ -1,123 +1,113 @@
use cgmath::{Vector2, Vector3};
#[derive(Debug)]
pub struct Fits<'a> {
// Tile size
size: Vector2<i32>,
pub data: Data<'a>,
}
use std::borrow::Cow;
use crate::texture::format::TextureFormat;
use crate::texture::format::R8U;
use cgmath::Vector3;
use fitsrs::card::Value;
use fitsrs::hdu::header::extension::image::Image as XImage;
use fitsrs::hdu::header::Bitpix;
use fitsrs::hdu::header::Header;
use fitsrs::WCS;
use fitsrs::{Fits, HDU};
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 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> {
// get a reference to the header
pub header: Header<XImage>,
// image 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>,
// raw bytes of the data image (in Big-Endian)
pub raw_bytes: &'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 width = hdu.get_header().get_xtension().get_naxisn(1);
let height = hdu.get_header().get_xtension().get_naxisn(2);
let height = header.get_axis_size(2)
.ok_or_else(|| JsValue::from_str("NAXIS2 not found in the fits"))?;
if let (Some(&width), Some(&height)) = (width, height) {
let depth =
*hdu.get_header().get_xtension().get_naxisn(3).unwrap_or(&1) as u32;
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 header = hdu.get_header();
let bscale = match header.get("BSCALE") {
Some(Value::Integer { value, .. }) => *value as f32,
Some(Value::Float { value, .. }) => *value as f32,
_ => 1.0,
};
let bzero = match header.get("BZERO") {
Some(Value::Integer { value, .. }) => *value as f32,
Some(Value::Float { value, .. }) => *value as f32,
_ => 0.0,
};
let blank = match header.get("BLANK") {
Some(Value::Integer { value, .. }) => Some(*value as f32),
Some(Value::Float { value, .. }) => Some(*value as f32),
_ => None,
};
let off = hdu.get_data_unit_byte_offset() as usize;
let len = hdu.get_data_unit_byte_size() as usize;
let raw_bytes = &bytes[off..(off + len)];
let bitpix = hdu.get_header().get_xtension().get_bitpix();
let wcs = hdu.wcs().ok();
images.push(Self {
header: hdu.get_header().clone(),
width: width as u32,
height: height as u32,
depth,
bitpix,
bscale,
wcs,
bzero,
blank,
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<'_> {
impl Image for FitsImage<'_> {
fn insert_into_3d_texture<T: Tex3D>(
&self,
// The texture array
@@ -125,89 +115,21 @@ 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()),
);
}
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()),
);
}
}
let view = unsafe { R8U::view(self.raw_bytes) };
textures.tex_sub_image_3d_with_opt_array_buffer_view(
offset.x,
offset.y,
offset.z,
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) {
(self.width, self.height)
}
}

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

@@ -7,7 +7,7 @@ pub struct HTMLImage<F> {
impl<F> HTMLImage<F>
where
F: ImageFormat + Clone,
F: TextureFormat + Clone,
{
pub fn new(image: web_sys::HtmlImageElement) -> 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 HTMLImage<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) {
(self.image.width(), self.image.height())
}
}

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);
}
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) {
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) {
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),
},
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,16 +297,16 @@ 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)?,
@@ -328,4 +327,21 @@ impl Image for ImageType {
Ok(())
}
fn get_size(&self) -> (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 data: Vec<<<T as TextureFormat>::P as Pixel>::Item>,
pub size: Vector2<i32>,
}
use crate::image::format::Bytes;
use crate::texture::format::Bytes;
pub struct ImageBufferView {
pub x: i32,
@@ -22,9 +23,13 @@ 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 {
pub fn new(
data: Vec<<<T as TextureFormat>::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);
//let buf = <<T as ImageFormat>::P as Pixel>::Container::new(buf);
@@ -37,16 +42,19 @@ where
width: i32,
height: i32,
) -> 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,
)
};
Ok(Self::new(decoded_pixels, width, height))
@@ -57,10 +65,8 @@ where
debug_assert!(size_buf == raw_bytes.len() as i32);
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)
};
Self::new(decoded_pixels, width, height)
@@ -71,7 +77,7 @@ where
Self { data: vec![], size }
}
pub fn allocate(pixel_fill: &<T as ImageFormat>::P, width: i32, height: i32) -> ImageBuffer<T> {
pub fn allocate(pixel_fill: &T::P, width: i32, height: i32) -> ImageBuffer<T> {
let size_buf = ((width * height) as usize) * (T::NUM_CHANNELS);
let data = pixel_fill
@@ -110,11 +116,11 @@ 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
}
@@ -127,12 +133,12 @@ where
}
}
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>),
}
@@ -141,7 +147,7 @@ use crate::image::{ArrayBuffer, Image};
use cgmath::{Vector2, Vector3};
impl<I> Image for ImageBuffer<I>
where
I: ImageFormat,
I: TextureFormat,
{
fn insert_into_3d_texture<T: Tex3D>(
&self,
@@ -150,8 +156,7 @@ 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,
@@ -166,7 +171,7 @@ where
}
// The size of the image
/*fn get_size(&self) -> &Vector2<i32> {
&self.size
}*/
fn get_size(&self) -> (u32, u32) {
(self.size.x as u32, self.size.y as u32)
}
}

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

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

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,
@@ -162,7 +162,7 @@ pub mod vao {
}
}
impl<'a, 'b> Drop for ShaderVertexArrayObjectBound<'a, 'b> {
impl Drop for ShaderVertexArrayObjectBound<'_, '_> {
fn drop(&mut self) {
self.unbind();
}
@@ -173,7 +173,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 +211,7 @@ pub mod vao {
}
}
impl<'a, 'b> Drop for ShaderVertexArrayObjectBoundRef<'a, 'b> {
impl Drop for ShaderVertexArrayObjectBoundRef<'_, '_> {
fn drop(&mut self) {
self.unbind();
}
@@ -362,373 +362,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;
@@ -23,7 +23,7 @@ pub struct Texture3D {
}
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,9 +54,9 @@ 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,
type_: F::TYPE,
ty: F::TYPE,
pixel_type: F::PIXEL_TYPE,
})));
Ok(Texture3D {
@@ -113,7 +113,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,
@@ -135,7 +135,7 @@ impl<'a> Texture3DBound<'a> {
image.height() as i32,
1,
metadata.format,
metadata.type_,
metadata.ty,
image,
)
.expect("Sub texture 3d");
@@ -162,7 +162,7 @@ impl<'a> Texture3DBound<'a> {
canvas.height() as i32,
1,
metadata.format,
metadata.type_,
metadata.ty,
canvas,
)
.expect("Sub texture 2d");
@@ -189,12 +189,13 @@ impl<'a> Texture3DBound<'a> {
image.height() as i32,
1,
metadata.format,
metadata.type_,
metadata.ty,
image,
)
.expect("Sub texture 2d");
}
#[allow(clippy::too_many_arguments)]
pub fn tex_sub_image_3d_with_opt_array_buffer_view(
&self,
dx: i32,
@@ -219,13 +220,14 @@ impl<'a> Texture3DBound<'a> {
h,
d,
metadata.format,
metadata.type_,
metadata.ty,
image,
)
.expect("Sub texture 2d");
}
#[allow(dead_code)]
#[allow(clippy::too_many_arguments)]
pub fn tex_sub_image_3d_with_opt_u8_array(
&self,
idx: i32,
@@ -249,7 +251,7 @@ impl<'a> Texture3DBound<'a> {
h,
d,
metadata.format,
metadata.type_,
metadata.ty,
pixels,
)
.expect("Sub texture 2d");

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;
@@ -21,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,
@@ -52,9 +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,
pixel_type: F::PIXEL_TYPE,
ty: F::TYPE,
format: F::FORMAT,
type_: F::TYPE,
})));
Ok(Texture2DArray {
@@ -114,35 +115,31 @@ impl Texture2DArray {
self.gl
.viewport(0, 0, metadata.width as i32, metadata.height as i32);
#[cfg(feature = "webgl2")]
let value = match (metadata.format, metadata.type_) {
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::UNSIGNED_BYTE) => {
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])?)
}
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::SHORT) => {
PixelType::R16I => {
let p = <[i16; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::INT) => {
PixelType::R32I => {
let p = <[i32; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
(WebGlRenderingCtx::RED, WebGlRenderingCtx::FLOAT) => {
PixelType::R32F => {
let p = <[f32; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
(WebGlRenderingCtx::RGB, WebGlRenderingCtx::UNSIGNED_BYTE) => {
PixelType::RGB8U => {
let p = <[u8; 3]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p)?)
}
(WebGlRenderingCtx::RGBA, WebGlRenderingCtx::UNSIGNED_BYTE) => {
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
@@ -194,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,
@@ -216,7 +213,7 @@ impl<'a> Texture2DArrayBound<'a> {
image.height() as i32,
1,
metadata.format,
metadata.type_,
metadata.ty,
image,
)
.expect("Sub texture 3d");
@@ -243,7 +240,7 @@ impl<'a> Texture2DArrayBound<'a> {
canvas.height() as i32,
1,
metadata.format,
metadata.type_,
metadata.ty,
canvas,
)
.expect("Sub texture 2d");
@@ -270,7 +267,7 @@ impl<'a> Texture2DArrayBound<'a> {
image.height() as i32,
1,
metadata.format,
metadata.type_,
metadata.ty,
image,
)
.expect("Sub texture 2d");
@@ -299,7 +296,7 @@ impl<'a> Texture2DArrayBound<'a> {
h,
1,
metadata.format,
metadata.type_,
metadata.ty,
image,
)
.expect("Sub texture 2d");
@@ -328,7 +325,7 @@ impl<'a> Texture2DArrayBound<'a> {
h,
1,
metadata.format,
metadata.type_,
metadata.ty,
pixels,
)
.expect("Sub texture 2d");

View File

@@ -0,0 +1,204 @@
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> {
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))
}
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,
}
}
}
pub const NUM_CHANNELS: usize = 6;

View File

@@ -1,6 +1,7 @@
pub mod array;
pub use array::Texture2DArray;
pub mod format;
pub mod pixel;
pub use pixel::*;
@@ -11,6 +12,7 @@ pub use mod_3d::Texture3D;
use web_sys::HtmlCanvasElement;
use web_sys::WebGlTexture;
use crate::texture::format::PixelType;
use crate::webgl_ctx::WebGlContext;
use crate::webgl_ctx::WebGlRenderingCtx;
use wasm_bindgen::prelude::*;
@@ -23,8 +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 type_: u32,
pub ty: u32,
pub pixel_type: PixelType,
pub width: u32,
pub height: u32,
@@ -45,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,
@@ -59,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()>)
};
@@ -72,14 +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,
type_: F::TYPE,
ty: F::TYPE,
pixel_type: F::PIXEL_TYPE,
}));
#[cfg(feature = "webgl2")]
let onload = {
let image = image.clone();
let gl = gl.clone();
@@ -129,7 +129,6 @@ impl Texture2D {
let gl = gl.clone();
Ok(Texture2D {
#[cfg(feature = "webgl2")]
texture,
gl,
@@ -138,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,
@@ -163,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();
@@ -185,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,
@@ -194,7 +200,7 @@ impl Texture2D {
height,
F::FORMAT,
F::TYPE,
bytes,
Some(view.as_ref()),
)
.expect("Texture 2D");
@@ -202,9 +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,
type_: F::TYPE,
ty: F::TYPE,
pixel_type: F::PIXEL_TYPE,
})));
Ok(Texture2D {
@@ -216,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,
@@ -242,15 +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,
type_: F::TYPE,
ty: F::TYPE,
pixel_type: F::PIXEL_TYPE,
})));
Ok(Texture2D {
texture,
gl,
metadata,
})
}
@@ -330,35 +335,31 @@ impl Texture2D {
self.gl
.viewport(0, 0, metadata.width as i32, metadata.height as i32);
#[cfg(feature = "webgl2")]
let value = match (metadata.format, metadata.type_) {
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::UNSIGNED_BYTE) => {
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])?)
}
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::SHORT) => {
PixelType::R16I => {
let p = <[i16; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::INT) => {
PixelType::R32I => {
let p = <[i32; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
(WebGlRenderingCtx::RED, WebGlRenderingCtx::FLOAT) => {
PixelType::R32F => {
let p = <[f32; 1]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p[0])?)
}
(WebGlRenderingCtx::RGB, WebGlRenderingCtx::UNSIGNED_BYTE) => {
PixelType::RGB8U => {
let p = <[u8; 3]>::read_pixel(&self.gl, x, y)?;
Ok(serde_wasm_bindgen::to_value(&p)?)
}
(WebGlRenderingCtx::RGBA, WebGlRenderingCtx::UNSIGNED_BYTE) => {
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
@@ -399,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,
@@ -417,24 +418,10 @@ impl<'a> Texture2DBound<'a> {
dx,
dy,
metadata.format,
metadata.type_,
metadata.ty,
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.type_,
image,
)
.expect("Sub texture 2d");
//self.texture_2d.gl.flush();
}
pub fn tex_sub_image_2d_with_u32_and_u32_and_html_canvas_element(
@@ -454,24 +441,10 @@ impl<'a> Texture2DBound<'a> {
dx,
dy,
metadata.format,
metadata.type_,
metadata.ty,
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.type_,
canvas,
)
.expect("Sub texture 2d");
//self.texture_2d.gl.flush();
}
pub fn tex_sub_image_2d_with_u32_and_u32_and_image_bitmap(
@@ -491,7 +464,7 @@ impl<'a> Texture2DBound<'a> {
dx,
dy,
metadata.format,
metadata.type_,
metadata.ty,
image,
)
.expect("Sub texture 2d");
@@ -504,7 +477,7 @@ impl<'a> Texture2DBound<'a> {
dx,
dy,
metadata.format,
metadata.type_,
metadata.ty,
image,
)
.expect("Sub texture 2d");
@@ -530,7 +503,7 @@ impl<'a> Texture2DBound<'a> {
width,
height,
metadata.format,
metadata.type_,
metadata.ty,
image,
)
.expect("Sub texture 2d");
@@ -556,7 +529,7 @@ impl<'a> Texture2DBound<'a> {
width,
height,
metadata.format,
metadata.type_,
metadata.ty,
pixels,
)
.expect("Sub texture 2d");
@@ -588,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,
@@ -599,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,133 +21,32 @@ pub trait Pixel:
fn read_pixel(gl: &WebGlContext, x: i32, y: i32) -> Result<Self, JsValue>;
}
impl Pixel for [f32; 4] {
impl Pixel for [f32; 1] {
type Item = f32;
type Container = ArrayF32;
const BLACK: Self = [std::f32::NAN; 4];
const BLACK: Self = [f32::NAN];
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")]
let p = js_sys::Uint8Array::new_with_length(4);
gl.read_pixels_with_opt_array_buffer_view(
x,
y,
1,
1,
WebGlRenderingCtx::RGBA,
WebGlRenderingCtx::FLOAT,
Some(&pixels),
WebGlRenderingCtx::UNSIGNED_BYTE,
Some(&p),
)?;
let pixels = pixels.to_vec();
Ok([pixels[0], pixels[1], pixels[2], pixels[3]])
Ok([f32::from_le_bytes([
p.at(0).unwrap(),
p.at(1).unwrap(),
p.at(2).unwrap(),
p.at(3).unwrap(),
])])
}
}
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 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]])
}
}
/*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;
@@ -189,7 +88,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;
@@ -210,45 +129,50 @@ 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(0).unwrap(), p.at(1).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(0).unwrap(),
p.at(1).unwrap(),
p.at(2).unwrap(),
p.at(3).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,45 +49,12 @@ impl WebGlContext {
#[cfg(feature = "webgl2")]
{
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>
where
T: wasm_bindgen::JsCast,
{
// `unchecked_into` is used here because WebGL extensions aren't actually JS classes
// these objects are duck-type representations of the actual Rust classes
// https://github.com/rustwasm/wasm-bindgen/pull/1449
context
.get_extension(name)
.ok()
.and_then(|maybe_ext| maybe_ext.map(|ext| ext.unchecked_into::<T>()))
.ok_or_else(|| JsValue::from_str("Failed to load ext"))
}
use std::ops::Deref;
impl Deref for WebGlContext {
type Target = WebGlRenderingCtx;

View File

@@ -23,7 +23,7 @@ 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);
@@ -31,8 +31,7 @@ fn generate_shaders() -> std::result::Result<(), Box<dyn Error>> {
let src = read_shader(path)?;
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,13 +48,15 @@ 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_while(Result::ok)
.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);
println!("{}", incl_file_name.to_string_lossy());
read_shader(incl_file_name.to_str().unwrap()).unwrap()
} else {
l
@@ -76,7 +77,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 +87,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}"#,
);"##,
)?;
}

View File

@@ -1,13 +1,13 @@
use crate::math::angle::ToAngle;
use crate::math::spectra::Freq;
use crate::renderable::hips::HiPS;
use crate::renderable::image::Image;
use crate::renderable::ImageLayer;
use crate::tile_fetcher::HiPSLocalFiles;
use crate::math::angle::ToAngle;
use crate::renderable::hips::HiPS;
use crate::{
//async_task::{BuildCatalogIndex, ParseTableTask, TaskExecutor, TaskResult, TaskType},
camera::CameraViewPort,
downloader::Downloader,
healpix::coverage::HEALPixCoverage,
healpix::moc::SpaceMoc,
inertia::Inertia,
math::{
self,
@@ -22,7 +22,10 @@ use crate::{
time::DeltaTime,
};
use al_api::moc::MOCOptions;
use wcs::WCS;
use al_core::image::fits::FitsImage;
use al_core::image::ImageType;
use fitsrs::WCS;
use std::io::Cursor;
use wasm_bindgen::prelude::*;
@@ -35,7 +38,8 @@ use al_api::{
grid::GridCfg,
hips::{HiPSCfg, ImageMetadata},
};
use fitsrs::{fits::AsyncFits, hdu::extension::AsyncXtensionHDU};
use crate::healpix::moc::Moc;
use web_sys::{HtmlElement, WebGl2RenderingContext};
@@ -98,17 +102,15 @@ pub struct App {
pub projection: ProjectionType,
// Async data receivers
img_send: async_channel::Sender<ImageLayer>,
//img_send: async_channel::Sender<ImageLayer>,
img_recv: async_channel::Receiver<ImageLayer>,
ack_img_send: async_channel::Sender<ImageParams>,
ack_img_recv: async_channel::Receiver<ImageParams>,
//ack_img_recv: async_channel::Receiver<ImageParams>,
// callbacks
//callback_position_changed: js_sys::Function,
}
use cgmath::{Vector2, Vector3};
use futures::io::BufReader; // for `next`
use crate::math::projection::*;
pub const BLENDING_ANIM_DURATION: DeltaTime = DeltaTime::from_millis(200.0); // in ms
@@ -190,8 +192,8 @@ impl App {
let moc = MOCRenderer::new(&gl)?;
gl.clear_color(0.1, 0.1, 0.1, 1.0);
let (img_send, img_recv) = async_channel::unbounded::<ImageLayer>();
let (ack_img_send, ack_img_recv) = async_channel::unbounded::<ImageParams>();
let (_, img_recv) = async_channel::unbounded::<ImageLayer>();
let (ack_img_send, _) = async_channel::unbounded::<ImageParams>();
//let line_renderer = RasterizedLineRenderer::new(&gl)?;
@@ -248,10 +250,10 @@ impl App {
colormaps,
projection,
img_send,
//img_send,
img_recv,
ack_img_send,
ack_img_recv,
//ack_img_recv,
})
}
@@ -260,34 +262,37 @@ impl App {
self.tile_fetcher.clear();
// Loop over the hipss
for hips in self.layers.get_mut_hipses() {
if self.camera.get_texture_depth() == 0 {
let allsky_query = match hips {
HiPS::D2(h) => query::Allsky::new(h.get_config(), None),
HiPS::D3(h) => query::Allsky::new(h.get_config(), Some(h.get_slice() as u32)),
};
if self.downloader.borrow().is_queried(&allsky_query.id) {
// do not ask for tiles if we download the allsky
continue;
if self.camera.get_tile_depth() == 0 {
match hips {
HiPS::D2(h) => {
let query = query::Allsky::new(h.get_config(), None);
if self.downloader.borrow().is_queried(&query.id) {
// do not ask for tiles if we download the allsky
continue;
}
}
// no Allsky generated for HiPS3D
HiPS::D3(h) => (),
}
}
let cfg = hips.get_config();
let min_tile_depth = cfg.delta_depth().max(cfg.get_min_depth_tile());
let min_tile_depth = cfg.get_min_depth_tile();
let mut ancestors = HashSet::new();
if let Some(tiles) = hips.look_for_new_tiles(&mut self.camera, &self.projection) {
if let Some(tiles) = hips.look_for_new_tiles(&self.camera) {
for tile_cell in tiles {
self.tile_fetcher.append(hips.get_tile_query(&tile_cell));
// check if we are starting aladin lite or not.
// If so we want to retrieve only the tiles in the view and access them
// directly i.e. without blending them with less precised tiles
if self.tile_fetcher.get_num_tile_fetched() > 0 {
if tile_cell.depth() >= min_tile_depth + 3 {
let ancestor_tile_cell = tile_cell.ancestor(3);
ancestors.insert(ancestor_tile_cell);
}
if self.tile_fetcher.get_num_tile_fetched() > 0
&& tile_cell.depth() >= min_tile_depth + 3
{
let ancestor_tile_cell = tile_cell.ancestor(3);
ancestors.insert(ancestor_tile_cell);
}
}
}
@@ -301,9 +306,9 @@ impl App {
}
}
HiPS::D3(hips) => {
let slice = hips.get_slice();
let freq = hips.get_freq();
for ancestor in ancestors {
if !hips.contains_tile(&ancestor, slice) {
if !hips.contains_tile(&ancestor, freq) {
self.tile_fetcher.append(hips.get_tile_query(&ancestor));
}
}
@@ -431,11 +436,9 @@ impl App {
);
// 3. project on screen
if let Some(p) = self.projection.model_to_clip_space(&xyz, &self.camera) {
Some([p.x, p.y])
} else {
None
}
self.projection
.model_to_clip_space(&xyz, &self.camera)
.map(|p| [p.x, p.y])
};
if let (Some(c1), Some(c2), Some(c3), Some(c4)) = (
@@ -477,15 +480,11 @@ impl App {
self.catalog_loaded
}
pub(crate) fn get_moc(&self, moc_uuid: &str) -> Option<&HEALPixCoverage> {
pub(crate) fn get_moc(&self, moc_uuid: &str) -> Option<&SpaceMoc> {
self.moc.get_hpx_coverage(moc_uuid)
}
pub(crate) fn add_moc(
&mut self,
moc: HEALPixCoverage,
options: MOCOptions,
) -> Result<(), JsValue> {
pub(crate) fn add_moc(&mut self, moc: SpaceMoc, options: MOCOptions) -> Result<(), JsValue> {
self.moc
.push_back(moc, options, &mut self.camera, &self.projection);
self.request_redraw = true;
@@ -517,7 +516,7 @@ impl App {
}*/
pub(crate) fn is_inerting(&self) -> bool {
return self.inertia.is_some();
self.inertia.is_some()
}
pub(crate) fn update(&mut self, dt: DeltaTime) -> Result<bool, JsValue> {
@@ -578,16 +577,14 @@ impl App {
}
// Tiles are fetched if:
let fetch_tiles = self.inertia.is_none() &&
// * the user is not zooming
!self.camera.has_zoomed() &&
// * no inertia action is in progress
(
let fetch_tiles =
// * the user is not panning the view
!self.dragging ||
// * or the user is but did not move for at least 100ms
(self.dragging && Time::now() - self.camera.get_time_of_last_move() >= DeltaTime(100.0))
);
(Time::now() - self.camera.get_time_of_last_move() >= DeltaTime(100.0) || !self.dragging) &&
// * no inertia action is in progress
self.inertia.is_none() &&
// * the user is not zooming
!self.camera.has_zoomed();
if fetch_tiles {
self.tile_fetcher.notify(self.downloader.clone(), None);
@@ -596,126 +593,66 @@ impl App {
let rscs_received = self.downloader.borrow_mut().get_received_resources();
let mut tile_copied = false;
//let mut tile_copied = false;
for rsc in rscs_received {
match rsc {
Resource::Tile(tile) => {
//if !_has_camera_zoomed {
if let Some(hips) = self.layers.get_mut_hips_from_cdid(&tile.get_hips_cdid()) {
if let Some(hips) = self.layers.get_mut_hips_from_cdid(tile.get_hips_cdid()) {
let cfg = hips.get_config_mut();
if cfg.get_format() == tile.format {
let delta_depth = cfg.delta_depth();
let fov_coverage = self.camera.get_cov(cfg.get_frame());
let included_or_near_coverage = tile
.cell()
.get_texture_cell(delta_depth)
.get_tile_cells(delta_depth)
.any(|neighbor_tile_cell| {
fov_coverage.intersects_cell(&neighbor_tile_cell)
});
let included_in_coverage = fov_coverage.intersects_cell(tile.cell());
//let is_tile_root = tile.cell().depth() == delta_depth;
//let _depth = tile.cell().depth();
// do not perform tex_sub costly GPU calls while the camera is zooming
if tile.cell().is_root() || included_or_near_coverage {
//let is_missing = tile.missing();
/*self.tile_fetcher.notify_tile(
&tile,
true,
false,
&mut self.downloader,
);*/
/*let image = if is_missing {
// Otherwise we push nothing, it is probably the case where:
// - an request error occured on a valid tile
// - the tile is not present, e.g. chandra HiPS have not the 0, 1 and 2 order tiles
None
} else {
Some(image)
};*/
use al_core::image::ImageType;
use fitsrs::fits::Fits;
use std::io::Cursor;
if tile.cell().is_root() || included_in_coverage {
//if let Some(image) = image.as_ref() {
match &*tile.image.borrow() {
Some(ImageType::FitsImage {
raw_bytes: raw_bytes_buf,
}) => {
// check if the metadata has not been set
if !cfg.fits_metadata {
let num_bytes = raw_bytes_buf.length() as usize;
let mut raw_bytes = vec![0; num_bytes];
raw_bytes_buf.copy_to(&mut raw_bytes[..]);
if let Some(ImageType::FitsRawBytes {
raw_bytes: raw_bytes_buf,
..
}) = &*tile.image.borrow()
{
// check if the metadata has not been set
if hips.get_fits_params().is_none() {
let raw_bytes = raw_bytes_buf.to_vec();
let mut bytes_reader =
Cursor::new(raw_bytes.as_slice());
let Fits { hdu } = Fits::from_reader(&mut bytes_reader)
.map_err(|_| {
JsValue::from_str("Parsing fits error")
})?;
let header = hdu.get_header();
let bscale =
if let Some(fitsrs::card::Value::Float(bscale)) =
header.get(b"BSCALE ")
{
*bscale as f32
} else {
1.0
};
let bzero =
if let Some(fitsrs::card::Value::Float(bzero)) =
header.get(b"BZERO ")
{
*bzero as f32
} else {
0.0
};
let blank =
if let Some(fitsrs::card::Value::Float(blank)) =
header.get(b"BLANK ")
{
*blank as f32
} else {
std::f32::NAN
};
cfg.set_fits_metadata(bscale, bzero, blank);
}
let FitsImage {
bscale,
bzero,
blank,
..
} = FitsImage::from_raw_bytes(raw_bytes.as_slice())?[0];
hips.set_fits_params(bscale, bzero, blank);
}
_ => (),
};
//}
let image = tile.image.clone();
match &*image.borrow() {
Some(img) => {
if tile_copied {
self.downloader
.borrow_mut()
.delay(Resource::Tile(tile));
continue;
}
if let Some(img) = &*image.borrow() {
/*if tile_copied {
self.downloader
.borrow_mut()
.delay(Resource::Tile(tile));
continue;
}*/
self.request_redraw = true;
tile_copied = true;
match hips {
HiPS::D2(hips) => {
hips.add_tile(&tile.cell, img, tile.time_req)?
}
HiPS::D3(hips) => hips.add_tile(
&tile.cell,
img,
tile.time_req,
tile.channel.unwrap() as u16,
)?,
self.request_redraw = true;
//tile_copied = true;
match hips {
HiPS::D2(hips) => {
hips.add_tile(&tile.cell, img, tile.time_req)?
}
self.time_start_blending = Time::now();
HiPS::D3(hips) => hips.add_tile(
&tile.cell,
img,
tile.time_req,
tile.channel.unwrap() as u16,
)?,
}
_ => (),
self.time_start_blending = Time::now();
};
}
}
@@ -729,12 +666,9 @@ impl App {
if is_missing {
// The allsky image is missing so we donwload all the tiles contained into
// the 0's cell
let cfg = hips.get_config();
for texture_cell in crate::healpix::cell::ALLSKY_HPX_CELLS_D0 {
for cell in texture_cell.get_tile_cells(cfg.delta_depth()) {
let query = hips.get_tile_query(&cell);
self.tile_fetcher.append_base_tile(query);
}
for base_hpx_cell in crate::healpix::cell::ALLSKY_HPX_CELLS_D0 {
let query = hips.get_tile_query(base_hpx_cell);
self.tile_fetcher.append_base_tile(query);
}
} else {
// tell the hips to not download tiles which order is <= 3 because the allsky
@@ -745,25 +679,21 @@ impl App {
}
}
}
Resource::PixelMetadata(metadata) => {
if let Some(hips) = self.layers.get_mut_hips_from_cdid(&metadata.hips_cdid) {
let cfg = hips.get_config_mut();
if let Some(metadata) = &*metadata.value.borrow() {
cfg.blank = metadata.blank;
cfg.offset = metadata.offset;
cfg.scale = metadata.scale;
}
}
}
Resource::Moc(moc) => {
let moc_hips_cdid = moc.get_hips_cdid();
Resource::Moc(fetched_moc) => {
let moc_hips_cdid = fetched_moc.get_hips_cdid();
//let url = &moc_url[..moc_url.find("/Moc.fits").unwrap_abort()];
if let Some(hips) = self.layers.get_mut_hips_from_cdid(moc_hips_cdid) {
let request::moc::Moc { moc, .. } = moc;
let request::moc::FetchedMoc { moc, .. } = fetched_moc;
if let Some(moc) = &*moc.borrow() {
hips.set_moc(moc.clone());
match (hips, moc) {
(HiPS::D2(hips), Moc::Space(moc)) => {
hips.set_moc(moc.clone());
}
(HiPS::D3(hips), Moc::FreqSpace(moc)) => {
hips.set_moc(moc.clone());
}
_ => (),
}
self.request_for_new_tiles = true;
self.request_redraw = true;
@@ -789,20 +719,32 @@ impl App {
Ok(has_camera_moved)
}
pub(crate) fn read_pixel(&self, pos: &Vector2<f64>, layer: &str) -> Result<JsValue, JsValue> {
if let Some(lonlat) = self.screen_to_world(pos) {
if let Some(hips) = self.layers.get_hips_from_layer(layer) {
hips.read_pixel(&lonlat, &self.camera)
} else if let Some(_image) = self.layers.get_image_from_layer(layer) {
Err(JsValue::from_str("TODO: read pixel value"))
} else {
Err(JsValue::from_str("Survey not found"))
}
pub(crate) fn read_pixel(&self, x: f64, y: f64, layer: &str) -> Result<JsValue, JsValue> {
if let Some(hips) = self.layers.get_hips_from_layer(layer) {
hips.read_pixel(x, y, &self.camera, &self.projection)
} else if let Some(_image) = self.layers.get_image_from_layer(layer) {
// FIXME handle the case of an image
Ok(JsValue::null())
} else {
Err(JsValue::from_str(&"position is out of projection"))
Err(JsValue::from_str("Survey not found"))
}
}
pub(crate) fn read_line_of_pixels(
&self,
x1: f64,
y1: f64,
x2: f64,
y2: f64,
layer: &str,
) -> Result<Vec<JsValue>, JsValue> {
let pixels = crate::math::utils::bresenham(x1, y1, x2, y2)
.map(|(x, y)| self.read_pixel(x, y, layer))
.collect::<Result<Vec<_>, _>>()?;
Ok(pixels)
}
pub(crate) fn draw_grid_labels(&mut self) -> Result<(), JsValue> {
self.grid.draw_labels()
}
@@ -917,7 +859,6 @@ impl App {
WebGl2RenderingContext::ONE,
);
grid.draw(camera, projection, shaders)?;
// Ok(())
// },
// None,
@@ -943,7 +884,7 @@ impl App {
}
pub(crate) fn rename_layer(&mut self, layer: &str, new_layer: &str) -> Result<(), JsValue> {
self.layers.rename_layer(&layer, &new_layer)
self.layers.rename_layer(layer, new_layer)
}
pub(crate) fn swap_layers(
@@ -987,263 +928,122 @@ impl App {
Ok(())
}
pub(crate) fn add_image_from_blob_and_wcs(
pub(crate) fn add_rgba_image(
&mut self,
layer: String,
stream: web_sys::ReadableStream,
bytes: &[u8],
wcs: WCS,
cfg: ImageMetadata,
) -> Result<js_sys::Promise, JsValue> {
let gl = self.gl.clone();
let img_sender = self.img_send.clone();
let ack_img_recv = self.ack_img_recv.clone();
// Stop the current inertia
self.inertia = None;
// And disable it while the fits has not been loaded
let disable_inertia = self.disable_inertia.clone();
*(disable_inertia.borrow_mut()) = true;
let camera_coo_sys = self.camera.get_coo_system();
let fut = async move {
use crate::renderable::image::Image;
use futures::future::Either;
use futures::TryStreamExt;
use js_sys::Uint8Array;
use wasm_streams::ReadableStream;
match Image::from_rgba_bytes(&gl, bytes, wcs, camera_coo_sys) {
Ok(image) => {
let layer = ImageLayer {
images: vec![image],
id: layer.clone(),
layer,
meta: cfg,
};
let body = ReadableStream::from_raw(stream.dyn_into()?);
let params = layer.get_params();
// Convert the JS ReadableStream to a Rust stream
let bytes_reader = match body.try_into_async_read() {
Ok(async_read) => Either::Left(async_read),
Err((_err, body)) => Either::Right(
body.into_stream()
.map_ok(|js_value| {
js_value.dyn_into::<Uint8Array>().unwrap_throw().to_vec()
})
.map_err(|_js_error| {
std::io::Error::new(std::io::ErrorKind::Other, "failed to read")
})
.into_async_read(),
),
};
use al_core::image::format::RGBA8U;
match Image::from_reader_and_wcs::<_, RGBA8U>(
&gl,
bytes_reader,
wcs,
None,
None,
None,
camera_coo_sys,
)
.await
{
Ok(image) => {
let img = ImageLayer {
images: vec![image],
id: layer.clone(),
layer,
meta: cfg,
};
self.layers.add_image(
layer,
&mut self.camera,
&self.projection,
&mut self.tile_fetcher,
)?;
img_sender.send(img).await.unwrap();
self.request_redraw = true;
// Wait for the ack here
let image_params = ack_img_recv
.recv()
.await
.map_err(|_| JsValue::from_str("Problem receiving fits"))?;
serde_wasm_bindgen::to_value(&image_params).map_err(|e| e.into())
}
Err(error) => Err(error),
let promise = js_sys::Promise::resolve(&serde_wasm_bindgen::to_value(&params)?);
Ok(promise)
}
};
let reenable_inertia = Closure::new(move || {
// renable inertia again
*(disable_inertia.borrow_mut()) = false;
});
let promise = wasm_bindgen_futures::future_to_promise(fut)
// Reenable inertia independantly from whether the
// fits has been correctly parsed or not
.finally(&reenable_inertia);
// forget the closure, it is not very proper to do this as
// it won't be deallocated
reenable_inertia.forget();
Ok(promise)
Err(error) => Err(error),
}
}
pub(crate) fn add_image_fits(
pub(crate) fn add_fits_image(
&mut self,
stream: web_sys::ReadableStream,
bytes: &[u8],
meta: ImageMetadata,
layer: String,
) -> Result<js_sys::Promise, JsValue> {
let gl = self.gl.clone();
let fits_sender = self.img_send.clone();
let ack_fits_recv = self.ack_img_recv.clone();
// Stop the current inertia
self.inertia = None;
// And disable it while the fits has not been loaded
let disable_inertia = self.disable_inertia.clone();
*(disable_inertia.borrow_mut()) = true;
let camera_coo_sys = self.camera.get_coo_system();
let fut = async move {
use crate::renderable::image::Image;
use futures::future::Either;
use futures::TryStreamExt;
use js_sys::Uint8Array;
use wasm_streams::ReadableStream;
// FIXME: this is done to prevent the view inerting after being unblocked
// Get the response's body as a JS ReadableStream
let body = ReadableStream::from_raw(stream.dyn_into()?);
let gz = fitsrs::gz::GzReader::new(Cursor::new(bytes))
.map_err(|_| JsValue::from_str("Error creating gz wrapper"))?;
// Convert the JS ReadableStream to a Rust stream
let bytes_reader = match body.try_into_async_read() {
Ok(async_read) => Either::Left(async_read),
Err((_err, body)) => Either::Right(
body.into_stream()
.map_ok(|js_value| {
js_value.dyn_into::<Uint8Array>().unwrap_throw().to_vec()
})
.map_err(|_js_error| {
std::io::Error::new(std::io::ErrorKind::Other, "failed to read")
})
.into_async_read(),
),
};
let mut reader = BufReader::new(bytes_reader);
let AsyncFits { mut hdu } = AsyncFits::from_reader(&mut reader)
.await
.map_err(|e| JsValue::from_str(&format!("Fits file parsing: reason: {}", e)))?;
let mut hdu_ext_idx = 0;
let mut images = vec![];
match Image::from_fits_hdu_async(&gl, &mut hdu.0, camera_coo_sys).await {
Ok(image) => {
images.push(image);
let mut hdu_ext = hdu.next().await;
// Continue parsing the file extensions here
while let Ok(Some(mut xhdu)) = hdu_ext {
match &mut xhdu {
AsyncXtensionHDU::Image(xhdu_img) => {
match Image::from_fits_hdu_async(&gl, xhdu_img, camera_coo_sys)
.await
{
Ok(image) => {
images.push(image);
}
Err(error) => {
al_core::log::console_warn(&
format!("The extension {hdu_ext_idx} has not been parsed, reason:")
);
al_core::log::console_warn(error);
}
}
}
_ => {
al_core::log::console_warn(&
format!("The extension {hdu_ext_idx} is a BinTable/AsciiTable and is thus discarded")
);
}
let parse_fits_images_from_bytes = |raw_bytes: &[u8]| -> Result<Vec<Image>, JsValue> {
Ok(FitsImage::from_raw_bytes(raw_bytes)?
.into_iter()
.filter_map(
|FitsImage {
bitpix,
bscale,
bzero,
blank,
wcs,
raw_bytes,
..
}| {
if let Some(wcs) = wcs {
let image = Image::from_fits_hdu(
&gl,
wcs,
bitpix,
raw_bytes,
bscale,
bzero,
blank,
camera_coo_sys,
)
.ok()?;
Some(image)
} else {
None
}
hdu_ext_idx += 1;
hdu_ext = xhdu.next().await;
}
}
Err(error) => {
al_core::log::console_warn(error);
let mut hdu_ext = hdu.next().await;
while let Ok(Some(mut xhdu)) = hdu_ext {
match &mut xhdu {
AsyncXtensionHDU::Image(xhdu_img) => {
match Image::from_fits_hdu_async(&gl, xhdu_img, camera_coo_sys)
.await
{
Ok(image) => {
images.push(image);
}
Err(error) => {
al_core::log::console_warn(&
format!("The extension {hdu_ext_idx} has not been parsed, reason:")
);
al_core::log::console_warn(error);
}
}
}
_ => {
al_core::log::console_warn(&
format!("The extension {hdu_ext_idx} is a BinTable/AsciiTable and is thus discarded")
);
}
}
hdu_ext_idx += 1;
hdu_ext = xhdu.next().await;
}
}
}
if images.is_empty() {
Err(JsValue::from_str("no images have been parsed"))
} else {
let fits = ImageLayer {
images,
id: layer.clone(),
layer,
meta,
};
fits_sender.send(fits).await.unwrap();
// Wait for the ack here
let image_params = ack_fits_recv
.recv()
.await
.map_err(|_| JsValue::from_str("Problem receiving fits"))?;
serde_wasm_bindgen::to_value(&image_params).map_err(|e| e.into())
}
},
)
.collect::<Vec<_>>())
};
let reenable_inertia = Closure::new(move || {
// renable inertia again
*(disable_inertia.borrow_mut()) = false;
});
let images = match gz {
fitsrs::gz::GzReader::GzReader(bytes) => parse_fits_images_from_bytes(bytes.get_ref())?,
fitsrs::gz::GzReader::Reader(bytes) => parse_fits_images_from_bytes(bytes.get_ref())?,
};
let promise = wasm_bindgen_futures::future_to_promise(fut)
// Reenable inertia independantly from whether the
// fits has been correctly parsed or not
.finally(&reenable_inertia);
if images.is_empty() {
Err(JsValue::from_str("no images have been parsed"))
} else {
let layer = ImageLayer {
images,
id: layer.clone(),
// forget the closure, it is not very proper to do this as
// it won't be deallocated
reenable_inertia.forget();
layer,
meta,
};
Ok(promise)
let params = layer.get_params();
self.layers.add_image(
layer,
&mut self.camera,
&self.projection,
&mut self.tile_fetcher,
)?;
self.request_redraw = true;
let promise = js_sys::Promise::resolve(&serde_wasm_bindgen::to_value(&params)?);
Ok(promise)
}
}
pub(crate) fn get_layer_cfg(&self, layer: &str) -> Result<ImageMetadata, JsValue> {
@@ -1253,7 +1053,7 @@ impl App {
pub(crate) fn set_hips_slice_number(&mut self, layer: &str, slice: u32) -> Result<(), JsValue> {
let hips = self
.layers
.get_mut_hips_from_layer(&layer)
.get_mut_hips_from_layer(layer)
.ok_or_else(|| JsValue::from_str("Layer not found"))?;
self.request_for_new_tiles = true;
@@ -1261,7 +1061,7 @@ impl App {
match hips {
HiPS::D2(_) => Err(JsValue::from_str("layer do not refers to a cube")),
HiPS::D3(hips) => {
hips.set_slice(slice as u16);
hips.set_freq(Freq(slice as f64));
Ok(())
}
@@ -1323,6 +1123,11 @@ impl App {
self.camera.get_longitude_reversed()
}
pub(crate) fn set_longitude_reversed(&mut self, longitude_reversed: bool) {
self.camera
.set_longitude_reversed(longitude_reversed, &self.projection);
}
pub(crate) fn add_catalog(&mut self, _name: String, table: JsValue, _colormap: String) {
//let mut exec_ref = self.exec.borrow_mut();
let _table = table;
@@ -1521,7 +1326,6 @@ impl App {
return;
}
let now = Time::now();
let dragging_duration = (now - self.time_start_dragging).as_secs();
let dragging_vel = self.dist_dragging / dragging_duration;
@@ -1542,7 +1346,7 @@ impl App {
let axis = self.prev_cam_position.cross(*center).normalize();
//let delta_time = ((now - time_of_last_move).0 as f64).max(1.0);
let delta_angle = math::vector::angle3(&self.prev_cam_position, &center).to_radians();
let delta_angle = math::vector::angle3(&self.prev_cam_position, center).to_radians();
let ampl = delta_angle * (dragging_vel as f64) * 5e-3;
//let ampl = (dragging_vel * 0.01) as f64;
@@ -1595,7 +1399,7 @@ impl App {
let prev_pos = w1;
let cur_pos = w2;
if prev_pos != cur_pos {
let prev_cam_position = self.camera.get_center().clone();
let prev_cam_position = *self.camera.get_center();
if self.north_up {
let lonlat1 = prev_pos.lonlat();
@@ -1603,15 +1407,24 @@ impl App {
let dlon = lonlat2.lon() - lonlat1.lon();
let dlat = lonlat2.lat() - lonlat1.lat();
self.camera.apply_lonlat_rotation(dlon, dlat, &self.projection);
self.camera
.apply_lonlat_rotation(dlon, dlat, &self.projection);
// Detect if a pole has been crossed
let north_pole = Vector3::new(0.0, 1.0, 0.0);
let south_pole = Vector3::new(0.0, -1.0, 0.0);
let cross_north_pole = crate::math::lonlat::is_in(&prev_cam_position, &self.camera.get_center(), &north_pole);
let cross_south_pole = crate::math::lonlat::is_in(&prev_cam_position, &self.camera.get_center(), &south_pole);
let cross_north_pole = crate::math::lonlat::is_in(
&prev_cam_position,
self.camera.get_center(),
&north_pole,
);
let cross_south_pole = crate::math::lonlat::is_in(
&prev_cam_position,
self.camera.get_center(),
&south_pole,
);
let cross_pole = cross_north_pole | cross_south_pole;
@@ -1623,7 +1436,7 @@ impl App {
};
let fov = self.camera.get_aperture();
let pole = if center.y >= 0.0 {
north_pole
} else {
@@ -1636,10 +1449,12 @@ impl App {
// too near to the pole
let axis = center.cross(pole).normalize();
use crate::math::rotation::Rotation;
let new_center = Rotation::from_axis_angle(&axis, (-5e-3 * fov).to_angle()).rotate(&pole);
let new_center = Rotation::from_axis_angle(&axis, (-5e-3 * fov).to_angle())
.rotate(&pole);
self.camera.set_center_xyz(&new_center, &self.projection);
self.camera.set_position_angle(0.0.to_angle(), &self.projection);
self.camera
.set_position_angle(0.0.to_angle(), &self.projection);
}
} else {
/* 1. Rotate by computing the angle between the last and current position */
@@ -1647,7 +1462,8 @@ impl App {
let d = math::vector::angle3(&prev_pos, &cur_pos);
let axis = prev_pos.cross(cur_pos).normalize();
self.camera.apply_axis_rotation(&(-axis), d, &self.projection);
self.camera
.apply_axis_rotation(&(-axis), d, &self.projection);
}
self.prev_cam_position = prev_cam_position;
@@ -1672,7 +1488,7 @@ impl App {
}
pub(crate) fn get_norder(&self) -> i32 {
self.camera.get_texture_depth() as i32
self.camera.get_tile_depth() as i32
}
pub(crate) fn get_zoom_factor(&self) -> f64 {

View File

@@ -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
@@ -378,10 +374,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 +410,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 +487,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;
@@ -598,7 +594,7 @@ impl CameraViewPort {
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 mut depth_pixel = 29_usize;
let hpx_cell_size_rad = (smallest_cell_size_px / w_screen_px) * self.get_aperture();
@@ -607,7 +603,7 @@ impl CameraViewPort {
break;
}
depth_pixel = depth_pixel - 1;
depth_pixel -= 1;
}
depth_pixel += 1;
const DEPTH_OFFSET_TEXTURE: usize = 9;
@@ -618,7 +614,7 @@ impl CameraViewPort {
};
}
pub fn get_texture_depth(&self) -> u8 {
pub fn get_tile_depth(&self) -> u8 {
self.texture_depth
}
@@ -639,10 +635,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

@@ -18,6 +18,12 @@ use crate::fifo_cache::Cache;
use query::Query;
use request::{RequestType, Resource};
impl Default for Downloader {
fn default() -> Self {
Self::new()
}
}
impl Downloader {
pub fn new() -> Downloader {
let requests = Vec::with_capacity(32);

View File

@@ -9,9 +9,10 @@ pub trait Query: Sized {
pub type QueryId = String;
use al_api::hips::DataproductType;
use al_core::image::format::ImageFormatType;
#[derive(Eq, Hash, PartialEq, Clone)]
#[derive(Eq, PartialEq, Clone)]
pub struct Tile {
pub cell: HEALPixCell,
pub format: ImageFormatType,
@@ -19,18 +20,29 @@ pub struct Tile {
pub hips_cdid: CreatorDid,
// The total url of the query
pub url: Url,
pub size: u32, // size of the tile requested
pub credentials: RequestCredentials,
pub mode: RequestMode,
pub id: QueryId,
pub channel: Option<u32>,
}
/*pub enum {
}*/
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 {
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();
@@ -38,17 +50,17 @@ impl Tile {
let dir_idx = (idx / 10000) * 10000;
let mut url = format!("{}/Norder{}/Dir{}/Npix{}", hips_url, depth, dir_idx, idx);
let mut url = format!("{hips_url}/Norder{depth}/Dir{dir_idx}/Npix{idx}");
// 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!(
"{}{}{}{}{}",
@@ -59,13 +71,17 @@ impl Tile {
ext
);
let size = cfg.get_tile_size() as u32;
Tile {
hips_cdid: hips_cdid.to_string(),
url,
cell: *cell,
format,
credentials,
mode,
id,
channel,
size,
}
}
}
@@ -83,34 +99,40 @@ 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,
// The total url of the query
pub url: Url,
pub credentials: RequestCredentials,
pub mode: RequestMode,
pub id: QueryId,
}
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();
let mode = cfg.get_request_mode();
let mut url = format!("{}/Norder3/Allsky", cfg.get_root_url());
// 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{}{}",
@@ -121,11 +143,13 @@ impl Allsky {
Allsky {
tile_size,
texture_size,
allsky_tile_size,
hips_cdid,
url,
format,
id,
credentials,
mode,
channel,
}
}
@@ -141,55 +165,50 @@ 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,
pub mode: RequestMode,
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, hips_cdid: CreatorDid, params: MOCOptions) -> Self {
pub fn new(
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,18 +1,16 @@
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>,
@@ -28,26 +26,17 @@ impl From<AllskyRequest> for RequestType {
use super::Url;
use wasm_bindgen_futures::JsFuture;
use web_sys::{RequestInit, RequestMode, Response};
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) -> Result<ImageBuffer<RGBA8U>, JsValue> {
let image = web_sys::HtmlImageElement::new().unwrap_abort();
let image_cloned = image.clone();
let html_img_elt_promise = js_sys::Promise::new(
&mut (Box::new(move |resolve, reject| {
image_cloned.set_cross_origin(Some(""));
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();
@@ -79,60 +68,54 @@ 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).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
.into_iter()
.enumerate()
.filter(|&(i, _)| i % 4 != 3)
.map(|(_, v)| v)
.collect();
let image = ImageBuffer::new(data, size.x, size.y);
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).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)
}
_ => {
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
opts.mode(mode);
opts.credentials(credentials);
let window = web_sys::window().unwrap_abort();
let request = web_sys::Request::new_with_str_and_init(&url_clone, &opts)?;
@@ -147,64 +130,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())
}
@@ -216,7 +201,7 @@ impl From<query::Allsky> for AllskyRequest {
Self {
id,
hips_cdid,
depth_tile,
//depth_tile,
url,
request,
channel: slice,
@@ -224,44 +209,40 @@ 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 tiles = (0..12).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 {
ImageBuffer::<F>::allocate(&F::P::BLACK, allsky_tile_size, allsky_tile_size);
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;
}
@@ -272,48 +253,46 @@ 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);
let image = ImageBuffer::<F>::new(reversed_rows_data, width_allsky_px, height_allsky_px);
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, allsky_tile_size)
});
Ok(allsky_tiles_iter)
}
use al_core::image::format::RGBA8U;
use al_core::texture::format::RGBA8U;
use crate::time::Time;
use std::cell::RefCell;
@@ -321,8 +300,7 @@ use std::rc::Rc;
pub struct Allsky {
pub image: Rc<RefCell<Option<Vec<ImageType>>>>,
pub time_req: Time,
pub depth_tile: u8,
//pub depth_tile: u8,
pub hips_cdid: CreatorDid,
url: Url,
pub channel: Option<u32>,
@@ -349,7 +327,7 @@ impl<'a> From<&'a AllskyRequest> for Option<Allsky> {
let AllskyRequest {
request,
hips_cdid,
depth_tile,
//depth_tile,
url,
channel,
..
@@ -364,7 +342,7 @@ impl<'a> From<&'a AllskyRequest> for Option<Allsky> {
image: data.clone(),
hips_cdid: hips_cdid.clone(),
url: url.clone(),
depth_tile: *depth_tile,
//depth_tile: *depth_tile,
channel: *channel,
})
} else {

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,7 +3,9 @@ use crate::renderable::CreatorDid;
use super::{Request, RequestType};
use crate::healpix::coverage::Smoc;
use crate::healpix::moc::Moc;
use crate::healpix::moc::{FreqSpaceMoc, SpaceMoc};
use al_api::hips::DataproductType;
use moclib::deser::fits::MocType;
use moclib::qty::Hpx;
@@ -11,7 +13,7 @@ pub struct MOCRequest {
//pub id: QueryId,
pub hips_cdid: CreatorDid,
pub params: MOCOptions,
request: Request<HEALPixCoverage>,
request: Request<Moc>,
}
impl From<MOCRequest> for RequestType {
@@ -21,31 +23,13 @@ impl From<MOCRequest> for RequestType {
}
use super::Url;
use moclib::deser::fits;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::{RequestInit, RequestMode, Response};
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 {
@@ -55,6 +39,9 @@ impl From<query::Moc> for MOCRequest {
url,
params,
hips_cdid,
credentials,
mode,
dataproduct_type,
} = query;
let url_clone = url.clone();
@@ -63,7 +50,8 @@ impl From<query::Moc> for MOCRequest {
let request = Request::new(async move {
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
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?;
@@ -72,25 +60,16 @@ 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)?)
}
_ => Moc::Space(SpaceMoc::from_fits_raw_bytes(&bytes)?),
})
});
Self {
@@ -105,19 +84,19 @@ impl From<query::Moc> for MOCRequest {
use std::cell::RefCell;
use std::rc::Rc;
pub struct Moc {
pub moc: Rc<RefCell<Option<HEALPixCoverage>>>,
pub struct FetchedMoc {
pub moc: Rc<RefCell<Option<Moc>>>,
pub params: MOCOptions,
pub hips_cdid: Url,
}
impl Moc {
impl FetchedMoc {
pub fn get_hips_cdid(&self) -> &Url {
&self.hips_cdid
}
}
impl<'a> From<&'a MOCRequest> for Option<Moc> {
impl<'a> From<&'a MOCRequest> for Option<FetchedMoc> {
fn from(request: &'a MOCRequest) -> Self {
let MOCRequest {
request,
@@ -126,8 +105,8 @@ impl<'a> From<&'a MOCRequest> for Option<Moc> {
..
} = request;
if request.is_resolved() {
let Request::<HEALPixCoverage> { data, .. } = request;
Some(Moc {
let Request::<Moc> { data, .. } = request;
Some(FetchedMoc {
// This is a clone on a Arc, it is supposed to be fast
moc: data.clone(),
hips_cdid: hips_cdid.clone(),

View File

@@ -1,11 +1,10 @@
// 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};
@@ -79,13 +78,11 @@ where
}
use allsky::AllskyRequest;
use blank::PixelMetadataRequest;
use moc::MOCRequest;
use tile::TileRequest;
pub enum RequestType {
Tile(TileRequest),
Allsky(AllskyRequest),
PixelMetadata(PixelMetadataRequest),
Moc(MOCRequest), //..
}
@@ -95,7 +92,6 @@ 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,
}
}
@@ -106,33 +102,48 @@ impl<'a> From<&'a RequestType> for Option<Resource> {
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),
RequestType::Moc(request) => Option::<FetchedMoc>::from(request).map(Resource::Moc),
}
}
}
use crate::Abort;
use allsky::Allsky;
use blank::PixelMetadata;
use moc::Moc;
use tile::Tile;
pub enum Resource {
Tile(Tile),
Allsky(Allsky),
PixelMetadata(PixelMetadata),
Moc(Moc),
Moc(FetchedMoc),
}
/*
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(),
}
}
}*/
use web_sys::RequestCredentials;
use self::moc::FetchedMoc;
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)
}

View File

@@ -1,14 +1,15 @@
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, RGB8U, RGBA8U};
use crate::downloader::query;
use al_core::image::ImageType;
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 id: QueryId,
@@ -26,30 +27,11 @@ impl From<TileRequest> for RequestType {
}
}
async fn query_html_image(url: &str) -> Result<web_sys::HtmlImageElement, JsValue> {
let image = web_sys::HtmlImageElement::new().unwrap_abort();
let image_cloned = image.clone();
let promise = js_sys::Promise::new(
&mut (Box::new(move |resolve, reject| {
// Ask for CORS permissions
image_cloned.set_cross_origin(Some(""));
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 al_core::image::html::HTMLImage;
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::JsFuture;
use web_sys::{RequestInit, RequestMode, Response};
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 {
@@ -58,120 +40,70 @@ impl From<query::Tile> for TileRequest {
cell,
url,
hips_cdid,
credentials,
mode,
id,
channel: slice,
channel,
size,
} = 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 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 })
*/
let request = match pixel_format {
PixelType::RGB8U => Request::new(async move {
// HTMLImageElement
let image = query_html_image(&url_clone).await?;
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 })
*/
PixelType::RGBA8U => Request::new(async move {
// HTMLImageElement
let image = query_html_image(&url_clone).await?;
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(RequestMode::Cors);
PixelType::R32F | PixelType::R32I | PixelType::R16I | PixelType::R8U => {
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()?);
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);
// 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 })
} else {
Err(JsValue::from_str(
"Response status code not between 200-299.",
))
}
}),
_ => todo!(),
Ok(ImageType::FitsRawBytes {
raw_bytes,
size: (size, size),
})
} else {
Err(JsValue::from_str(
"Response status code not between 200-299.",
))
}
})
}
};
Self {
@@ -181,7 +113,7 @@ impl From<query::Tile> for TileRequest {
hips_cdid,
url,
request,
channel: slice,
channel,
}
}
}

View File

@@ -16,7 +16,7 @@ use healpix::compass_point::Ordinal;
use healpix::compass_point::OrdinalMap;
use crate::utils;
use crate::Abort;
impl HEALPixCell {
// Build the parent cell
#[inline(always)]
@@ -141,7 +141,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
@@ -225,21 +225,6 @@ impl HEALPixCell {
}
}
#[inline(always)]
pub(crate) fn has_7_neigh(&self) -> bool {
let base_cell = self.ancestor(self.depth());
let nside_minus_one = (self.nside() - 1) as u32;
let (x, y) = self.offset_in_parent(&base_cell);
match base_cell.idx() {
0..=3 => (x == 0 && y == nside_minus_one) || (y == 0 && x == nside_minus_one),
4..=7 => (x == 0 && y == 0) || (x == nside_minus_one && y == nside_minus_one),
8..=11 => (x == 0 && y == nside_minus_one) || (y == 0 && x == nside_minus_one),
_ => unreachable!()
}
}
#[inline(always)]
pub(crate) fn is_on_base_cell_edges(&self) -> bool {
let base_cell = self.ancestor(self.depth());
@@ -489,12 +474,12 @@ 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())
}
}

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,161 @@
use crate::math::lonlat::LonLatT;
use crate::math::PI;
use crate::math::{self, lonlat::LonLat};
use cgmath::Vector3;
use moclib::elemset::range::uniq::HpxUniqRanges;
use moclib::hpxranges2d::HpxRanges2D;
use moclib::moc::RangeMOCIntoIterator;
use moclib::moc2d::{HasTwoMaxDepth, RangeMOC2Iterator};
use moclib::ranges::ranges2d::Ranges2D;
use moclib::{
moc::range::{CellSelection, RangeMOC},
moc2d::range::RangeMOC2,
qty::Hpx,
ranges::SNORanges,
};
use moclib::qty::Frequency;
use crate::healpix::cell::HEALPixCell;
#[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::deser::fits::RangeMoc2DIterFromFits;
use moclib::idx::Idx;
use moclib::moc::range::op::convert::convert_to_u64;
use moclib::moc::{CellMOCIntoIterator, CellMOCIterator, RangeMOCIterator};
use moclib::mocranges2d::Moc2DRanges;
use moclib::deser::fits::MocType;
use std::io::Cursor;
use crate::math::spectra::Freq;
use crate::math::spectra::SpectralUnit;
impl FreqSpaceMoc {
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)) => {
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))
}
/// This methods builds a SFMOC made of:
/// * the cells in the spatial viewport at a specific frequency f
/// * the +/- f_window cells containing inside lonlat on the frequency axis
/// This is the method to use when looking for new cube HiPS3D tiles
pub fn from_coos_freq<L: LonLat<f64>, F: SpectralUnit>(
// The depth of the smallest HEALPix cells contained in it
depth: u8,
// The vertices of the polygon delimiting the coverage
vertices_iter: impl Iterator<Item = L>,
// A vertex being inside the coverage,
// typically the center of projection
inside: &L,
// The freq at which we want to compute the sfmoc
f: F,
// Frequency window i.e. the number of cells around f to query
f_window: u8,
) -> Self {
let freq: Freq = f.into();
todo!();
/*let lonlat = vertices_iter
.map(|vertex| {
let LonLatT(lon, lat) = vertex.lonlat();
(lon.to_radians(), lat.to_radians())
})
.collect::<Vec<_>>();
let LonLatT(in_lon, in_lat) = inside.lonlat();
let moc = RangeMOC2::from_freqranges_in_hz_and_coos(
&lonlat[..],
(in_lon.to_radians(), in_lat.to_radians()),
depth,
CellSelection::All,
);
SpaceFreqMoc(moc)*/
}
/*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, hpx_cell: &HEALPixCell, f: Freq) -> bool {
let z29_rng = hpx_cell.z_29_rng();
let f_hash = f.hash();
self.0.contains(f_hash, &z29_rng)
}
/*/// 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;
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

@@ -3,6 +3,7 @@ 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 +13,65 @@ 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();
let json = (&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:?}")));
json
}
impl HEALPixCoverage {
pub fn from_3d_coos<T: LonLat<f64>>(
// The depth of the smallest HEALPix cells contained in it
depth: u8,
@@ -38,7 +95,7 @@ impl HEALPixCoverage {
depth,
CellSelection::All,
);
HEALPixCoverage(moc)
SpaceMoc(moc)
}
pub fn from_fixed_hpx_cells(
@@ -47,7 +104,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 +115,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 +135,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 +150,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 +163,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

@@ -23,7 +23,7 @@ impl Inertia {
ampl,
speed: ampl,
axis,
north_up
north_up,
}
}

View File

@@ -88,9 +88,11 @@ use math::projection::*;
use moclib::moc::RangeMOCIntoIterator;
//use votable::votable::VOTableWrapper;
use crate::tile_fetcher::HiPSLocalFiles;
use al_api::moc::MOCOptions;
use wasm_bindgen::prelude::*;
use web_sys::HtmlElement;
use al_api::moc::MOCOptions;
use fitsrs::{WCSParams, WCS};
use crate::math::angle::ToAngle;
@@ -110,14 +112,10 @@ mod shader;
mod tile_fetcher;
mod time;
use crate::downloader::request::moc::from_fits_hpx;
use crate::{
camera::CameraViewPort, healpix::coverage::HEALPixCoverage, math::lonlat::LonLatT,
shader::ShaderManager, time::DeltaTime,
camera::CameraViewPort, healpix::moc::SpaceMoc, math::lonlat::LonLatT, shader::ShaderManager,
time::DeltaTime,
};
use moclib::deser::fits;
use moclib::deser::fits::MocIdxType;
use moclib::deser::fits::MocQtyType;
use std::io::Cursor;
@@ -134,10 +132,6 @@ use cgmath::{Vector2, Vector3};
use crate::healpix::cell::HEALPixCell;
use math::angle::ArcDeg;
use moclib::{
moc::{CellMOCIntoIterator, CellMOCIterator, RangeMOCIterator},
qty::Hpx,
};
#[wasm_bindgen]
pub struct WebClient {
@@ -187,7 +181,7 @@ impl WebClient {
#[wasm_bindgen(js_name = isInerting)]
pub fn is_inerting(&self) -> bool {
return self.app.is_inerting();
self.app.is_inerting()
}
/// Update the view
@@ -350,33 +344,31 @@ impl WebClient {
Ok(())
}
#[wasm_bindgen(js_name = addImageFITS)]
pub fn add_image_fits(
#[wasm_bindgen(js_name = addFITSImage)]
pub fn add_fits_image(
&mut self,
stream: web_sys::ReadableStream,
bytes: &[u8],
cfg: JsValue,
layer: String,
) -> Result<js_sys::Promise, JsValue> {
let cfg: ImageMetadata = serde_wasm_bindgen::from_value(cfg)?;
self.app.add_image_fits(stream, cfg, layer)
self.app.add_fits_image(bytes, cfg, layer)
}
#[wasm_bindgen(js_name = addImageWithWCS)]
pub fn add_image_with_wcs(
#[wasm_bindgen(js_name = addRGBAImage)]
pub fn add_rgba_image(
&mut self,
stream: web_sys::ReadableStream,
bytes: &[u8],
wcs: JsValue,
cfg: JsValue,
layer: String,
) -> Result<js_sys::Promise, JsValue> {
use wcs::{WCSParams, WCS};
let cfg: ImageMetadata = serde_wasm_bindgen::from_value(cfg)?;
let wcs_params: WCSParams = serde_wasm_bindgen::from_value(wcs)?;
let wcs = WCS::new(&wcs_params).map_err(|e| JsValue::from_str(&format!("{:?}", e)))?;
self.app
.add_image_from_blob_and_wcs(layer, stream, wcs, cfg)
let wcs = WCS::new(&wcs_params).map_err(|e| JsValue::from_str(&format!("{e:?}")))?;
self.app.add_rgba_image(layer, bytes, wcs, cfg)
}
#[wasm_bindgen(js_name = removeLayer)]
@@ -566,6 +558,12 @@ impl WebClient {
self.app.get_longitude_reversed()
}
/// Set the longitude axis reversed globally
#[wasm_bindgen(js_name = setLongitudeReversed)]
pub fn set_longitude_reversed(&mut self, longitude_reversed: bool) {
self.app.set_longitude_reversed(longitude_reversed);
}
/// Get the field of view angle value when the view is zoomed out to its maximum
///
/// This method is dependent of the projection currently set.
@@ -589,7 +587,8 @@ impl WebClient {
/// Set the zoom factor of the view
#[wasm_bindgen(js_name = setZoomFactor)]
pub fn set_zoom_factor(&mut self, zoom_factor: f64) -> Result<(), JsValue> {
Ok(self.app.set_zoom_factor(zoom_factor))
self.app.set_zoom_factor(zoom_factor);
Ok(())
}
/// Set the center of the view in ICRS coosys
@@ -719,16 +718,12 @@ impl WebClient {
let vertices = lon
.iter()
.zip(lat.iter())
.map(|(&lon, &lat)| {
let xy = self
.app
.flat_map(|(&lon, &lat)| {
self.app
.world_to_screen(lon, lat)
.map(|v| [v.x, v.y])
.unwrap_or([0.0, 0.0]);
xy
.unwrap_or([0.0, 0.0])
})
.flatten()
.collect::<Vec<_>>();
vertices.into_boxed_slice()
@@ -894,7 +889,7 @@ impl WebClient {
Ok(())
}
/// Project a line to the screen
/// Project a great circle arc on the screen
///
/// # Returns
///
@@ -909,23 +904,33 @@ impl WebClient {
/// * `lat1` - The latitude in degrees of the starting line point
/// * `lon2` - The longitude in degrees of the ending line point
/// * `lat2` - The latitude in degrees of the ending line point
/*#[wasm_bindgen(js_name = projectLine)]
pub fn project_line(
#[wasm_bindgen(js_name = projectGreatCircleArc)]
pub fn project_great_circle_arc(
&self,
lon1: f64,
lat1: f64,
lon2: f64,
lat2: f64,
) -> Result<Box<[f64]>, JsValue> {
let vertices = self.app.project_line(lon1, lat1, lon2, lat2);
let vertices = crate::renderable::line::great_circle_arc::project(
lon1.to_radians(),
lat1.to_radians(),
lon2.to_radians(),
lat2.to_radians(),
&self.app.camera,
&self.app.projection,
);
let vertices = vertices
.into_iter()
.flat_map(|v| vec![v.x, v.y])
.flat_map(|ndc| {
let sxy = crate::math::projection::ndc_to_screen_space(&ndc, &self.app.camera);
[sxy.x, sxy.y]
})
.collect::<Vec<_>>();
Ok(vertices.into_boxed_slice())
}*/
}
/// Get the list of colormap supported
///
@@ -969,7 +974,7 @@ impl WebClient {
let grad = colorgrad::CustomGradient::new()
.colors(&rgba_colors?)
.build()
.map_err(|err| JsValue::from_str(&format!("{:?}", err)))?;
.map_err(|err| JsValue::from_str(&format!("{err:?}")))?;
let cmap = Colormap::new(&label, grad);
self.app.add_cmap(label, cmap)?;
@@ -999,10 +1004,21 @@ impl WebClient {
/// * `x` - The x screen coordinate in pixels
/// * `y` - The y screen coordinate in pixels
/// * `base_url` - The base url of the hips identifying it
#[wasm_bindgen(js_name = readPixel)]
pub fn read_pixel(&self, x: f64, y: f64, layer: String) -> Result<JsValue, JsValue> {
let pixel = self.app.read_pixel(&Vector2::new(x, y), layer.as_str())?;
Ok(pixel)
#[wasm_bindgen(js_name = probePixel)]
pub fn probe_pixel(&self, x: f64, y: f64, layer: String) -> Result<JsValue, JsValue> {
self.app.read_pixel(x, y, layer.as_str())
}
#[wasm_bindgen(js_name = probeLineOfPixels)]
pub fn probe_line_of_pixels(
&self,
x1: f64,
y1: f64,
x2: f64,
y2: f64,
layer: String,
) -> Result<Vec<JsValue>, JsValue> {
self.app.read_line_of_pixels(x1, y1, x2, y2, layer.as_str())
}
#[wasm_bindgen(js_name = getVisibleCells)]
@@ -1035,20 +1051,11 @@ impl WebClient {
}
#[wasm_bindgen(js_name = addJSONMoc)]
pub fn add_json_moc(
&mut self,
options: MOCOptions,
data: &JsValue,
) -> Result<(), JsValue> {
pub fn add_json_moc(&mut self, options: MOCOptions, data: &JsValue) -> Result<(), JsValue> {
let str: String = js_sys::JSON::stringify(data)?.into();
let moc = moclib::deser::json::from_json_aladin::<u64, Hpx<u64>>(&str)
.map_err(|e| JsValue::from(js_sys::Error::new(&e.to_string())))?
.into_cell_moc_iter()
.ranges()
.into_range_moc();
self.app.add_moc(HEALPixCoverage(moc), options)?;
let smoc = SpaceMoc::from_json(&str)?;
self.app.add_moc(smoc, options)?;
Ok(())
}
@@ -1056,18 +1063,8 @@ impl WebClient {
#[wasm_bindgen(js_name = addFITSMOC)]
pub fn add_fits_moc(&mut self, options: MOCOptions, data: &[u8]) -> Result<(), JsValue> {
//let bytes = js_sys::Uint8Array::new(array_buffer).to_vec();
let moc = match fits::from_fits_ivoa_custom(Cursor::new(&data[..]), false)
.map_err(|e| JsValue::from_str(&e.to_string()))?
{
MocIdxType::U16(MocQtyType::<u16, _>::Hpx(moc)) => {
Ok(crate::downloader::request::moc::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")),
}?;
self.app.add_moc(HEALPixCoverage(moc), options)?;
let smoc = SpaceMoc::from_fits_raw_bytes(data)?;
self.app.add_moc(smoc, options)?;
Ok(())
}
@@ -1082,7 +1079,7 @@ impl WebClient {
) -> Result<(), JsValue> {
let tile_d = self.app.get_norder();
let pixel_d = tile_d + 9;
let moc = HEALPixCoverage::from_cone(
let moc = SpaceMoc::from_cone(
&LonLatT::new(
ra_deg.to_radians().to_angle(),
dec_deg.to_radians().to_angle(),
@@ -1116,7 +1113,7 @@ impl WebClient {
let v_in = &Vector3::new(1.0, 0.0, 0.0);
let mut moc = HEALPixCoverage::from_3d_coos(pixel_d as u8 - 1, vertex_it, &v_in);
let mut moc = SpaceMoc::from_3d_coos(pixel_d as u8 - 1, vertex_it, v_in);
if moc.sky_fraction() > 0.5 {
moc = moc.not();
}
@@ -1141,12 +1138,7 @@ impl WebClient {
}
#[wasm_bindgen(js_name = mocContains)]
pub fn moc_contains(
&mut self,
moc_uuid: String,
lon: f64,
lat: f64,
) -> Result<bool, JsValue> {
pub fn moc_contains(&mut self, moc_uuid: String, lon: f64, lat: f64) -> Result<bool, JsValue> {
let moc = self
.app
.get_moc(&moc_uuid)
@@ -1167,15 +1159,9 @@ impl WebClient {
.get_moc(&moc_uuid)
.ok_or_else(|| JsValue::from(js_sys::Error::new("MOC not found")))?;
let mut buf: Vec<u8> = Default::default();
let json = (&moc.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)))?;
let json = moc.serialize_to_json()?;
serde_wasm_bindgen::to_value(&json).map_err(|err| JsValue::from_str(&format!("{:?}", err)))
serde_wasm_bindgen::to_value(&json).map_err(|err| JsValue::from_str(&format!("{err:?}")))
}
#[wasm_bindgen(js_name = getMOCSkyFraction)]

View File

@@ -1,5 +1,5 @@
use cgmath::BaseFloat;
use crate::Abort;
use cgmath::BaseFloat;
// ArcDeg wrapper structure
#[derive(Clone, Copy)]
pub struct ArcDeg<T: BaseFloat>(pub T);
@@ -42,12 +42,12 @@ where
}
}
impl<T> ToString for ArcDeg<T>
impl<T> Display for ArcDeg<T>
where
T: BaseFloat + ToString,
T: BaseFloat + Display,
{
fn to_string(&self) -> String {
self.0.to_string()
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
@@ -55,7 +55,6 @@ where
#[derive(Clone, Copy)]
pub struct ArcHour<T: BaseFloat>(pub T);
impl<T> From<Rad<T>> for ArcHour<T>
where
T: BaseFloat,
@@ -81,12 +80,12 @@ where
}
}
impl<T> ToString for ArcHour<T>
impl<T> Display for ArcHour<T>
where
T: BaseFloat + ToString,
T: BaseFloat + Display,
{
fn to_string(&self) -> String {
self.0.to_string()
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
@@ -132,12 +131,12 @@ where
}
}
impl<T> ToString for ArcMin<T>
impl<T> Display for ArcMin<T>
where
T: BaseFloat + ToString,
T: BaseFloat + Display,
{
fn to_string(&self) -> String {
self.0.to_string()
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
@@ -171,12 +170,12 @@ where
}
}
impl<T> ToString for ArcSec<T>
impl<T> Display for ArcSec<T>
where
T: BaseFloat + ToString,
T: BaseFloat + Display,
{
fn to_string(&self) -> String {
self.0.to_string()
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
@@ -192,130 +191,6 @@ where
}
use al_api::angle::Format;
/*
pub enum SerializeFmt {
DMS,
HMS,
DMM,
DD,
}
use al_api::angle_fmt::AngleSerializeFmt;
impl From<AngleSerializeFmt> for SerializeFmt {
fn from(value: AngleSerializeFmt) -> Self {
match value {
AngleSerializeFmt::DMS => SerializeFmt::DMS,
AngleSerializeFmt::HMS => SerializeFmt::HMS,
AngleSerializeFmt::DMM => SerializeFmt::DMM,
AngleSerializeFmt::DD => SerializeFmt::DD,
}
}
}
impl SerializeFmt {
pub fn to_string<S: BaseFloat + ToString>(&self, angle: Angle<S>) -> String {
match &self {
Self::DMS => DMS::to_string(angle),
Self::HMS => HMS::to_string(angle),
Self::DMM => DMM::to_string(angle),
Self::DD => DD::to_string(angle),
}
}
}*/
/*pub trait SerializeToString {
fn to_string(&self) -> String;
}
impl<S> SerializeToString for Angle<S>
where
S: BaseFloat + ToString,
{
fn to_string<F: FormatType>(&self) -> String {
F::to_string(*self)
}
}*/
/*
pub struct DMS;
pub struct HMS;
pub struct DMM;
pub struct DD;
pub trait FormatType {
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String;
}
impl FormatType for DD {
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String {
let angle = Rad(angle.0);
let degrees: ArcDeg<S> = angle.into();
degrees.to_string()
}
}
impl FormatType for DMM {
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String {
let angle = Rad(angle.0);
let mut degrees: ArcDeg<S> = angle.into();
let minutes = degrees.get_frac_minutes();
degrees.truncate();
let mut result = degrees.to_string() + " ";
result += &minutes.to_string();
result
}
}
impl FormatType for DMS {
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String {
let angle = Rad(angle.0);
let degrees: ArcDeg<S> = angle.into();
let minutes = degrees.get_frac_minutes();
let seconds = minutes.get_frac_seconds();
let num_sec_per_minutes = S::from(60).unwrap_abort();
let degrees = degrees.trunc();
let minutes = minutes.trunc() % num_sec_per_minutes;
let seconds = seconds.trunc() % num_sec_per_minutes;
let mut result = degrees.to_string() + "°";
result += &minutes.to_string();
result += "\'";
result += &seconds.to_string();
result += "\'\'";
result
}
}
impl FormatType for HMS {
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String {
let angle = Rad(angle.0);
let hours: ArcHour<S> = angle.into();
let minutes = hours.get_frac_minutes();
let seconds = minutes.get_frac_seconds();
let num_sec_per_minutes = S::from(60).unwrap_abort();
let hours = hours.trunc();
let minutes = minutes.trunc() % num_sec_per_minutes;
let seconds = seconds.trunc() % num_sec_per_minutes;
let mut result = hours.to_string() + "h";
result += &minutes.to_string();
result += "\'";
result += &seconds.to_string();
result += "\'\'";
result
}
}*/
#[derive(Clone, Copy, Debug, Eq, Hash, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -330,7 +205,10 @@ where
{
pub fn new<T: Into<Rad<S>>>(angle: T) -> Angle<S> {
let radians: Rad<S> = angle.into();
Angle { rad: radians.0, fmt: AngleFormatter::default() }
Angle {
rad: radians.0,
fmt: AngleFormatter::default(),
}
}
pub fn cos(&self) -> S {
@@ -429,7 +307,10 @@ where
S: BaseFloat,
{
fn to_angle(self) -> Angle<S> {
Angle { rad: self, fmt: Default::default() }
Angle {
rad: self,
fmt: Default::default(),
}
}
}
@@ -685,7 +566,7 @@ pub enum AngleFormatter {
Decimal {
/// Number of digit of precision
prec: u8,
}
},
}
impl Default for AngleFormatter {
@@ -711,7 +592,7 @@ impl Display for Angle<f64> {
// Format the unit value to sexagesimal.
// The precision 8 corresponds to the formatting: deg/hour min sec.ddd
write!(f, "{}", Format::toSexagesimal(unit, 8, plus))
},
}
AngleFormatter::Decimal { prec } => {
write!(f, "{:.1$}°", self.to_degrees(), prec as usize)
}

View File

@@ -27,7 +27,7 @@ where
/// * ``lat`` - Latitude
pub fn new(mut lon: Angle<S>, lat: Angle<S>) -> LonLatT<S> {
if lon.to_radians() < S::zero() {
lon = lon + S::from(TWICE_PI).unwrap_abort();
lon += S::from(TWICE_PI).unwrap_abort();
}
LonLatT(lon, lat)
@@ -49,8 +49,8 @@ where
}
use crate::math::angle::ToAngle;
impl From<wcs::LonLat> for LonLatT<f64> {
fn from(lonlat: wcs::LonLat) -> Self {
impl From<fitsrs::wcs::LonLat> for LonLatT<f64> {
fn from(lonlat: fitsrs::wcs::LonLat) -> Self {
Self(lonlat.lon().to_angle(), lonlat.lat().to_angle())
}
}
@@ -121,7 +121,8 @@ pub fn ang_between_lonlat<S: BaseFloat>(lonlat1: LonLatT<S>, lonlat2: LonLatT<S>
let abs_diff_lon = (lonlat1.lon() - lonlat2.lon()).abs();
(lonlat1.lat().sin() * lonlat2.lat().sin()
+ lonlat1.lat().cos() * lonlat2.lat().cos() * abs_diff_lon.cos())
.acos().to_angle()
.acos()
.to_angle()
}
#[inline]
@@ -180,7 +181,7 @@ pub fn unproj(
camera: &CameraViewPort,
) -> Option<LonLatT<f64>> {
projection
.normalized_device_to_model_space(&ndc_xy, camera)
.normalized_device_to_model_space(ndc_xy, camera)
.map(|model_pos| model_pos.lonlat())
}
@@ -201,13 +202,14 @@ pub fn unproj_from_screen(
camera: &CameraViewPort,
) -> Option<LonLatT<f64>> {
projection
.screen_to_model_space(&xy, camera)
.screen_to_model_space(xy, camera)
.map(|model_pos| model_pos.lonlat())
}
#[inline]
pub fn is_in(v1: &Vector3<f64>, v2: &Vector3<f64>, v: &Vector3<f64>) -> bool {
let theta = crate::math::vector::angle3(&v1, &v2).abs();
let theta = crate::math::vector::angle3(v1, v2).abs();
crate::math::vector::angle3(&v1, &v).abs() < theta && crate::math::vector::angle3(&v, &v2).abs() < theta
crate::math::vector::angle3(v1, v).abs() < theta
&& crate::math::vector::angle3(v, v2).abs() < theta
}

View File

@@ -5,10 +5,12 @@ pub const HALF_PI: f64 = std::f64::consts::PI * 0.5;
pub const MINUS_HALF_PI: f64 = -std::f64::consts::PI * 0.5;
pub const TWO_SQRT_TWO: f64 = 2.82842712475;
pub const SQRT_TWO: f64 = 1.41421356237;
pub const SQRT_TWO: f64 = std::f64::consts::SQRT_2;
pub const ZERO: f64 = 0.0;
pub mod spectra;
pub mod angle;
pub mod lonlat;
pub mod projection;

View File

@@ -16,7 +16,7 @@ impl ProjDef for Ellipse {
let mut p = Vector2::new(xy.x.abs(), xy.y.abs());
let mut ab = Vector2::new(self.a, self.b);
let sdf = if p.x == 0.0 {
if p.x == 0.0 {
-(self.b - p.y)
} else if p.y == 0.0 {
-(self.a - p.x)
@@ -61,8 +61,6 @@ impl ProjDef for Ellipse {
let q = Vector2::new(ab.x * co, ab.y * si);
(q - p).magnitude() * (p.y - q.y).signum()
};
sdf
}
}
}

View File

@@ -2,4 +2,4 @@ pub mod disk;
pub mod ellipse;
pub mod parabola;
pub mod rect;
pub mod triangle;
pub mod triangle;

View File

@@ -22,6 +22,12 @@ pub struct Cod {
pub x_max: f64,
}
impl Default for Cod {
fn default() -> Self {
Self::new()
}
}
impl Cod {
pub const fn new() -> Self {
Self {
@@ -57,7 +63,7 @@ impl ProjDef for Cod {
let e = b / a;
let ext_ellipse = Translate {
off: center_ellipse,
def: Ellipse { a: a, b: b },
def: Ellipse { a, b },
};
// Small ellipse where projection is not defined

View File

@@ -5,5 +5,5 @@ pub mod par;
pub mod basic;
pub mod op;
pub mod sdf;
pub mod op;

View File

@@ -36,7 +36,7 @@ where
let d = valid_reg.sdf(&v);
// Perform the ray marching advancement
v = v + dir * d;
v += dir * d;
is_in = valid_reg.is_in(&v);
i += 1;

View File

@@ -163,7 +163,7 @@ impl ProjectionType {
}
/// Screen to model space deprojection
///
/// Perform a screen to the world space deprojection
///
/// # Arguments
@@ -184,8 +184,6 @@ impl ProjectionType {
self.clip_to_world_space(&pos_clip_space)
}
/// Screen to model space deprojection
/// Perform a screen to the world space deprojection
///
/// # Arguments
@@ -262,8 +260,6 @@ impl ProjectionType {
self.world_to_clip_space(&pos_world_space)
}
/// World to screen space projection
/// World to screen space transformation
///
/// # Arguments
@@ -682,7 +678,7 @@ use self::coo_space::XYNDC;
use super::lonlat::LonLatT;
impl<'a, P> Projection for &'a P
impl<P> Projection for &P
where
P: CanonicalProjection,
{
@@ -775,8 +771,8 @@ mod tests {
for y in 0..(h as u32) {
let xy = Vector2::new(x, y);
let clip_xy = Vector2::new(
2.0 * ((xy.x as f64) / (w as f64)) - 1.0,
2.0 * ((xy.y as f64) / (h as f64)) - 1.0,
2.0 * ((xy.x as f64) / w) - 1.0,
2.0 * ((xy.y as f64) / h) - 1.0,
);
let rgb = if let Some(pos) = projection.clip_to_world_space(&clip_xy) {
let pos = pos.normalize();
@@ -789,7 +785,7 @@ mod tests {
Rgb([255, 255, 255])
};
img.put_pixel(x as u32, y as u32, rgb);
img.put_pixel(x, y, rgb);
}
}
img.save(filename).unwrap_abort();

View File

@@ -1,15 +1,15 @@
use crate::math;
use crate::math::angle::ToAngle;
use cgmath::Vector3;
use cgmath::{BaseFloat, InnerSpace};
use cgmath::{Euler, Quaternion};
use cgmath::Vector3;
#[derive(Clone, Copy, Debug)]
// Internal structure of a rotation, a quaternion
// All operations are done on it
pub struct Rotation<S: BaseFloat>(pub Quaternion<S>);
use cgmath::{Matrix3};
use cgmath::Matrix3;
impl<S> From<&Matrix3<S>> for Rotation<S>
where
S: BaseFloat,
@@ -117,7 +117,7 @@ where
// Define a rotation from a normalized vector
pub fn from_sky_position(pos: &Vector3<S>) -> Rotation<S> {
let (lon, lat) = math::lonlat::xyz_to_radec(&pos);
let (lon, lat) = math::lonlat::xyz_to_radec(pos);
let rot_y = Matrix3::from_angle_y(lon);
let rot_x = Matrix3::from_angle_x(-lat);

View File

@@ -0,0 +1,62 @@
pub trait SpectralUnit: Into<Freq> + Clone + Copy {
fn hash(&self) -> u64 {
let f: Freq = (*self).into();
Frequency::freq2hash(f.0)
}
}
use moclib::qty::Frequency;
/// Frequency in Hz unit
#[derive(Clone, Copy)]
pub struct Freq(pub f64);
impl Freq {
fn from_hash(hash: u64) -> Self {
let f = Frequency::hash2freq(hash);
Freq(f)
}
}
/// Wavelength in meter unit
#[derive(Clone, Copy)]
pub struct Wavelength(f64);
/// Velocity in meter/sec unit
#[derive(Clone, Copy)]
pub struct Velocity {
/// A rest frequency to compute the velocity from
/// given by the obs_restfreq HiPS property
rest_freq: Freq,
/// The velocity in m/s
velocity: f64,
}
const SPEED_OF_LIGHT: f64 = 299792458.0;
impl From<Velocity> for Freq {
fn from(v: Velocity) -> Self {
let Velocity {
rest_freq,
velocity,
} = v;
// v = c * (of - f) / of
// v * of = c * (of - f)
// c * f = c * of - v * of = of * (c - v)
// f = of * (c - v) / c = of * (1 - v / c)
Freq(rest_freq.0 * (1.0 - velocity / SPEED_OF_LIGHT))
}
}
impl From<Wavelength> for Freq {
fn from(lambda: Wavelength) -> Self {
Freq(SPEED_OF_LIGHT / lambda.0)
}
}
impl SpectralUnit for Freq {}
impl SpectralUnit for Wavelength {}
impl SpectralUnit for Velocity {}

View File

@@ -38,22 +38,18 @@ impl BoundingBox {
PoleContained::None => {
// The polygon does not contain any pole
// Meridian 0deg is not crossing the polygon
let (min_lat, max_lat) = lat
.iter()
.fold((std::f64::MAX, std::f64::MIN), |(min, max), &b| {
(min.min(b), max.max(b))
});
let (min_lat, max_lat) = lat.iter().fold((f64::MAX, f64::MIN), |(min, max), &b| {
(min.min(b), max.max(b))
});
let (min_lon, max_lon) = lon
.iter()
.fold((std::f64::MAX, std::f64::MIN), |(min, max), &b| {
(min.min(b), max.max(b))
});
let (min_lon, max_lon) = lon.iter().fold((f64::MAX, f64::MIN), |(min, max), &b| {
(min.min(b), max.max(b))
});
(min_lon..max_lon, min_lat..max_lat)
}
PoleContained::South => {
let max_lat = lat.iter().fold(std::f64::MIN, |a, b| a.max(*b));
let max_lat = lat.iter().fold(f64::MIN, |a, b| a.max(*b));
(
if intersect_zero_meridian {
-PI..PI
@@ -64,7 +60,7 @@ impl BoundingBox {
)
}
PoleContained::North => {
let min_lat = lat.iter().fold(std::f64::MAX, |a, b| a.min(*b));
let min_lat = lat.iter().fold(f64::MAX, |a, b| a.min(*b));
(
if intersect_zero_meridian {
-PI..PI

View File

@@ -13,18 +13,18 @@ pub enum HEALPixBBox {
impl PartialOrd for HEALPixBBox {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(HEALPixBBox::AllSky, HEALPixBBox::AllSky) => Some(Ordering::Equal),
(HEALPixBBox::AllSky, HEALPixBBox::Cell(_)) => Some(Ordering::Greater),
(HEALPixBBox::Cell(_), HEALPixBBox::AllSky) => Some(Ordering::Less),
(HEALPixBBox::Cell(c1), HEALPixBBox::Cell(c2)) => c1.partial_cmp(c2),
}
Some(self.cmp(other))
}
}
impl Ord for HEALPixBBox {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap_abort()
match (self, other) {
(HEALPixBBox::AllSky, HEALPixBBox::AllSky) => Ordering::Equal,
(HEALPixBBox::AllSky, HEALPixBBox::Cell(_)) => Ordering::Greater,
(HEALPixBBox::Cell(_), HEALPixBBox::AllSky) => Ordering::Less,
(HEALPixBBox::Cell(c1), HEALPixBBox::Cell(c2)) => c1.cmp(c2),
}
}
}

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