mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2025-12-25 04:16:55 -08:00
Compare commits
6 Commits
feat-keep-
...
3.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bde5a37b51 | ||
|
|
fbdc7e2e76 | ||
|
|
312b9844d1 | ||
|
|
0e740454bd | ||
|
|
18e98e9f5f | ||
|
|
c938a58cbc |
191
CHANGELOG.md
191
CHANGELOG.md
@@ -1,113 +1,38 @@
|
||||
# Changelogs
|
||||
|
||||
## Unreleased
|
||||
## unreleased
|
||||
|
||||
### What's Changed
|
||||
* [fix] request a redraw after adding an array of footprints [PR #218]
|
||||
|
||||
* [perf] perform CPU computations with Vec3 and Matrix3 and not 4 dimensions matrices/vectors
|
||||
* [feat] lockNorthUp Aladin object new option locking the north pole up to the view center
|
||||
|
||||
## Released
|
||||
|
||||
### 3.6.3
|
||||
|
||||
#### What's Changed
|
||||
|
||||
* [maint] remove Shift shorcut for triggering rectangular source selection. Shortcuts will be implemented later. by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/269>
|
||||
* [fix] decide to show the contextual menu only if user has not right click while moving more than 10px. by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/269>
|
||||
* [fix] CSS selector took the whole div in smartphone usage. by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/269>
|
||||
* [fix] smartphone 2 fingers zoom pinching around the 180deg meridian. by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/269>
|
||||
* [fix] use copy2Clipboard utils functions from contextual menu. by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/269>
|
||||
* [fix] improved ID/HiPS url detection (retry if a relative path to a local HiPS is given as it is considered as an ID). by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/269>
|
||||
|
||||
### 3.6.1
|
||||
|
||||
#### What's Changed
|
||||
* [feat] AVM tags support found in jpeg headers by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/178>
|
||||
* [feat] Merge remove overlay by name to Develop by [@Xen0Xys][Xen0Xys] in <https://github.com/cds-astro/aladin-lite/pull/182>
|
||||
* [fix] Restore A.footprint function by [@szpetny][szpetny] in <https://github.com/cds-astro/aladin-lite/pull/183>
|
||||
* [testing] Merge playwright tests to Develop by [@Xen0Xys][Xen0Xys] in <https://github.com/cds-astro/aladin-lite/pull/176>
|
||||
* [fix] Version 3.5.1 by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/180>
|
||||
* [fix] Also add style sheet in shadow dom context by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/194>
|
||||
* [feat] Add selectionColor, onClick to catalog parameters handled by updateShape by [@simontorres][simontorres] in <https://github.com/cds-astro/aladin-lite/pull/198>
|
||||
* [feat] HiPS cube support by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/204>
|
||||
* [feat] PolySelect implementation and miscellaneous selection improvements by [@pmatsson][pmatsson] in <https://github.com/cds-astro/aladin-lite/pull/195>
|
||||
* [fix] Request a redraw after adding an array of footprints by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/218>
|
||||
* [fix] Circle opacity not working by [@tboch][tboch] in <https://github.com/cds-astro/aladin-lite/pull/222>
|
||||
* [fix] Bug introduced in #218 by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/223>
|
||||
* [fix] CSS style for canvas element is more selective by [@tboch][tboch] in <https://github.com/cds-astro/aladin-lite/pull/224>
|
||||
* [maint] Add zenodo.json file by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/226>
|
||||
* [maint] Remove old files using jquery by [@cquiroz][cquiroz] in <https://github.com/cds-astro/aladin-lite/pull/231>
|
||||
* [fix] Display of HiPS with maxOrder = 0 by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/236>
|
||||
* [fix] Combine requestedOptions and Aladin.DEFAULT_OPTIONS with spread operator by [@pcuste1][pcuste1] in <https://github.com/cds-astro/aladin-lite/pull/237>
|
||||
* [fix] Add back cmap reverse checkbox by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/238>
|
||||
* [fix] JPEG draw black screen when tiles are missing by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/246>
|
||||
* [fix] Filled moc drawing reverse longitude by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/250>
|
||||
* [doc] Deprecate longitudeReversed HiPS options property by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/251>
|
||||
* [fix] Stop contextmenu event propagation only if context menu enabled by [@alexgoff][alexgoff] in <https://github.com/cds-astro/aladin-lite/pull/259>
|
||||
* [ui] Minor fixes and enhancements by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/256>
|
||||
* [fix] Handle NaNs by the GPU (fix for windows) by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/254>
|
||||
* [fix] Grid labels formatting by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/261>
|
||||
* [feat] Shape function given to a catalog by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/262>
|
||||
* [fix] Tooltip pos out of fov by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/263>
|
||||
* [fix] MOC settings after its creation by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/265>
|
||||
* [fix] ICRS to FK5J2000 cooframe renaming by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/264>
|
||||
* [fix] Revert #264 by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/268>
|
||||
|
||||
#### New Contributors
|
||||
* [@cquiroz][cquiroz] made their first contribution in <https://github.com/cds-astro/aladin-lite/pull/231>
|
||||
* [@pcuste1][pcuste1] made their first contribution in <https://github.com/cds-astro/aladin-lite/pull/237>
|
||||
* [@alexgoff][alexgoff] made their first contribution in <https://github.com/cds-astro/aladin-lite/pull/259>
|
||||
|
||||
**Full Changelog**: <https://github.com/cds-astro/aladin-lite/compare/3.4.5...3.6.1>
|
||||
|
||||
### 3.6.1-beta
|
||||
|
||||
#### What's Changed
|
||||
* [feat] Add selectionColor, onClick to catalog parameters handled by updateShape by [@simontorres][simontorres] in <https://github.com/cds-astro/aladin-lite/pull/198>
|
||||
* [feat] HiPS cube support by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/204>
|
||||
* [feat] PolySelect implementation and miscellaneous selection improvements by [@pmatsson][pmatsson] in <https://github.com/cds-astro/aladin-lite/pull/195>
|
||||
* [fix] Request a redraw after adding an array of footprints by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/218>
|
||||
* [fix] Circle opacity not working by [@tboch][tboch] in <https://github.com/cds-astro/aladin-lite/pull/222>
|
||||
* [fix] Bug introduced in #218 by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/223>
|
||||
* [fix] CSS style for canvas element is more selective by [@tboch][tboch] in <https://github.com/cds-astro/aladin-lite/pull/224>
|
||||
* [maint] add zenodo.json file by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/226>
|
||||
* [maint] Remove old files using jquery by [@cquiroz][cquiroz] in <https://github.com/cds-astro/aladin-lite/pull/231>
|
||||
|
||||
#### New Contributors
|
||||
* [@cquiroz][cquiroz] made their first contribution in <https://github.com/cds-astro/aladin-lite/pull/231>
|
||||
|
||||
**Full Changelog**: <https://github.com/cds-astro/aladin-lite/compare/3.5.1-beta...3.6.1-beta>
|
||||
|
||||
### 3.5.1-beta
|
||||
## 3.5.1-beta
|
||||
|
||||
* [feat] Add support for name removing in `removeOverlay` method
|
||||
* [test] Add support of playwright. Instructions in the readme for running the test matching snapshots [PR #176]
|
||||
* [fix] Order of overlays in the stack now matches the addMOC/addCatalog/addOverlay calls ordering
|
||||
* [doc] Expose the API of Coo class
|
||||
* [fix] Insert aladin css inside the aladin lite so that it should be compliant with the use of shadow DOMs [cds-astro/ipyaladin#113], [marimo-team/marimo#2106]
|
||||
* [feat] Add possibility of giving a local JS FileList to load a locally-stored HiPS without starting an HTTP server <https://github.com/cds-astro/aladin-lite/pull/103>
|
||||
* [feat] Add possibility of giving a local JS FileList to load a locally-stored HiPS without starting an HTTP server [cds-astro/aladin-lite#103]
|
||||
* [fix] removeOverlayByName
|
||||
|
||||
### 3.5.0-beta
|
||||
## 3.5.0-beta
|
||||
|
||||
* [enhancement] add `options.colnames` to A.catalogFromVizieR to tell VizieR we want absolutely want to retrieve specific columns
|
||||
* [feat] provide a new drawAxes option to A.ellipse. This is useful for plotting error ellipsis.
|
||||
|
||||
### 3.4.5-beta
|
||||
## 3.4.5-beta
|
||||
|
||||
* [feat] add `layerChanged` event when a layer is added or removed
|
||||
* [deprecate] of `select` event, use `objectsSelected` event instead
|
||||
* [ui] add the ability to switch the tile format to download
|
||||
|
||||
### 3.4.3-beta
|
||||
## 3.4.3-beta
|
||||
|
||||
* [bugfix] zoom control buttons
|
||||
* [feat] save a MOC as a JSON file from the UI <https://github.com/cds-astro/aladin-lite/issues/154>
|
||||
* [feat] save a source selection from the measurement table as a CSV file
|
||||
* [feat] allow to display jpeg/jpg images with a wcs passed as a JS dictionary: <https://github.com/cds-astro/aladin-lite/issues/173>
|
||||
|
||||
### 3.4.2-beta
|
||||
## 3.4.2-beta
|
||||
|
||||
* [impr] Improve smartphone support by setting media queries + a better logic for deploying the contextual menu sub options.
|
||||
* [impr] Improve `WCS` view export with 3rd euler rotation encoding: <https://github.com/cds-astro/aladin-lite/issues/170>. Still some cases are to be handled like: crval on the equator or cylindrical with a galactic frame rotation.
|
||||
@@ -115,21 +40,21 @@
|
||||
* [feat] Add new method `Aladin#getViewImageBuffer` to get the current view as a PNG buffer
|
||||
* [feat] New line rasterizer using GL instancing. This enhances the rendering speed of MOCs.
|
||||
|
||||
### 3.3.3
|
||||
## 3.3.3
|
||||
|
||||
* [feat] UI: add HiPS basic filter that filters the `hipsList` given
|
||||
* [feat] New `hipsList` option parameter when instancing a new Aladin object.
|
||||
* [feat] Zoom smoothing using hermite cubic interpolation functions
|
||||
* [feat] shape option of Catalog and ProgressiveCat accepts a function returning a Footprint. This allow user to
|
||||
associate a footprint to a specific source
|
||||
* [feat] Hover color support by [@pmatsson][pmatsson] and [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/145>
|
||||
* [feat] Hover color support by @pmatsson and @bmatthieu3 in <https://github.com/cds-astro/aladin-lite/pull/145>
|
||||
|
||||
### 3.3.2
|
||||
## 3.3.2
|
||||
|
||||
* [fixed] do not allow to query the properties several times for an imageHiPS
|
||||
* [fixed] Detecting raytracing rendering mode. Adapt the rendering mode in function of the fov value and the projection used. Some projections do have more distortions with wide FoVs so it is better to use the raytracing rendering mode when fov >= smaller FoV threshold.
|
||||
|
||||
### 3.3.0
|
||||
## 3.3.0
|
||||
|
||||
* [fixed] multiple calls to setImageSurvey with the same survey object led to strange behaviour.
|
||||
* [perf] Display the first tile received instantly with no blending. Should enhance the slow reported in issue #88.
|
||||
@@ -150,61 +75,61 @@
|
||||
* [fixed] The parameters `gridColor` and `gridOpacity`, `gridOptions.showLabels` now work as expected
|
||||
* New documentation API (W.I.P) here: <https://cds-astro.github.io/aladin-lite/>
|
||||
* New release page here: <https://aladin.cds.unistra.fr/AladinLite/doc/release/>
|
||||
* A major UI update by [@bmatthieu3][bmatthieu3]
|
||||
* A major UI update by @bmatthieu3
|
||||
1. Some API new classes A.box, A.button
|
||||
2. A status bar where the user can enque messages for a specific amount of time (Aladin.addStatusBarMessage)
|
||||
* Remove of JQuery and autocompletejs dependencies by [@bmatthieu3][bmatthieu3]
|
||||
* Fix some performances issues, i.e. a bug when resizing the aladin lite view and which launched several parallel requestAnimationFrame by [@bmatthieu3][bmatthieu3]
|
||||
* Remove of JQuery and autocompletejs dependencies by @bmatthieu3
|
||||
* Fix some performances issues, i.e. a bug when resizing the aladin lite view and which launched several parallel requestAnimationFrame by @bmatthieu3
|
||||
* Polygon and circular selection (see Aladin class API documentation for how to use it)
|
||||
* ObsCore and Datalink votable parsing and interpretation. This work is still in progress and made in the frame of the SKA radio mission by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/116>
|
||||
* SODA service query window formular by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/116>
|
||||
* read only catalog option by [@szpetny][szpetny] in <https://github.com/cds-astro/aladin-lite/pull/117>
|
||||
* Small changed regarding drawing a footprint by [@szpetny][szpetny] in <https://github.com/cds-astro/aladin-lite/pull/118>
|
||||
* Object and footprint click/hover events expose mouse coordinates by [@szpetny][szpetny] in <https://github.com/cds-astro/aladin-lite/pull/121>
|
||||
* A proposal of a new feature - fill the polygon with a color by [@szpetny][szpetny] in <https://github.com/cds-astro/aladin-lite/pull/122>
|
||||
* update getViewWCS to adapt to projection by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/119>
|
||||
* New SAMP support by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/128>
|
||||
* A possibility to create Coo and Footprint objects by [@szpetny][szpetny] in <https://github.com/cds-astro/aladin-lite/pull/130>
|
||||
* ObsCore and Datalink votable parsing and interpretation. This work is still in progress and made in the frame of the SKA radio mission by @bmatthieu3 in <https://github.com/cds-astro/aladin-lite/pull/116>
|
||||
* SODA service query window formular by @bmatthieu3 in <https://github.com/cds-astro/aladin-lite/pull/116>
|
||||
* read only catalog option by @szpetny in <https://github.com/cds-astro/aladin-lite/pull/117>
|
||||
* Small changed regarding drawing a footprint by @szpetny in <https://github.com/cds-astro/aladin-lite/pull/118>
|
||||
* Object and footprint click/hover events expose mouse coordinates by @szpetny in <https://github.com/cds-astro/aladin-lite/pull/121>
|
||||
* A proposal of a new feature - fill the polygon with a color by @szpetny in <https://github.com/cds-astro/aladin-lite/pull/122>
|
||||
* update getViewWCS to adapt to projection by @ManonMarchand in <https://github.com/cds-astro/aladin-lite/pull/119>
|
||||
* New SAMP support by @bmatthieu3 in <https://github.com/cds-astro/aladin-lite/pull/128>
|
||||
* A possibility to create Coo and Footprint objects by @szpetny in <https://github.com/cds-astro/aladin-lite/pull/130>
|
||||
* new method aladin.getFrame() that returns the name of the current coordinate system
|
||||
* `getViewWCS` now adapts to the `cooFrame` and the `projection`
|
||||
* `getFov` is no longer capped at 180°
|
||||
* bugfix `setProjection` now also updates for 'PAR' and 'SFL' projections
|
||||
|
||||
### 3.2.0
|
||||
## 3.2.0
|
||||
|
||||
* MOC rendering perf enhanced. Possibility to draw only the perimeter of a MOC object (perimeter set to True)
|
||||
* Many fixes e.g. footprint rendering for all sky projections
|
||||
* A line/shape webgl rasterizer thanks to the use of the `lyon`crate. MOCs and grid lines are rendered that way. Therefore, it is possible to change the grid lines thickness
|
||||
* Use of vite for the project management and deployment
|
||||
|
||||
### 3.1.0
|
||||
## 3.1.0
|
||||
|
||||
* Add message for safari users to enable WebGL2 feature and reload the page by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/54>
|
||||
* Starting fits support by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/70>
|
||||
* display fits images with the drag and drop by [@bmatthieu3][bmatthieu3]
|
||||
* Add message for safari users to enable WebGL2 feature and reload the page by @bmatthieu3 in <https://github.com/cds-astro/aladin-lite/pull/54>
|
||||
* Starting fits support by @bmatthieu3 in <https://github.com/cds-astro/aladin-lite/pull/70>
|
||||
* display fits images with the drag and drop by @bmatthieu3
|
||||

|
||||
* support `webp` tile format by [@bmatthieu3][bmatthieu3] and [@tboch][tboch]
|
||||
* planetary name resolver by [@tboch][tboch]
|
||||
* small ui changes and bug fixes by [@bmatthieu3][bmatthieu3]
|
||||
* add codemeta and its validatior action by [@ManonMarchand][ManonMarchand] in <https://github.com/cds-astro/aladin-lite/pull/66>
|
||||
* support `webp` tile format by @bmatthieu3 and @tboch
|
||||
* planetary name resolver by @tboch
|
||||
* small ui changes and bug fixes by @bmatthieu3
|
||||
* add codemeta and its validatior action by @ManonMarchand in <https://github.com/cds-astro/aladin-lite/pull/66>
|
||||
|
||||
### 3.0.0
|
||||
## 3.0.0
|
||||
|
||||
Official release of Aladin Lite v3, [as announced in CDS news](https://cds.unistra.fr/news.php?fn_mode=fullnews&fn_incl=0&fn_id=958).
|
||||
|
||||
* Fix missing tiles issue by [@tboch][tboch] in <https://github.com/cds-astro/aladin-lite/pull/18>
|
||||
* Hips catalogue filtering by [@tboch][tboch] in <https://github.com/cds-astro/aladin-lite/pull/28>
|
||||
* Make footprint selection easier by [@tboch][tboch] in <https://github.com/cds-astro/aladin-lite/pull/19>
|
||||
* Bug fix: enable different colors for multiple polylines in same layer by [@tboch][tboch] in <https://github.com/cds-astro/aladin-lite/pull/30>
|
||||
* Method remove to delete individual source from a catalogue layer by [@tboch][tboch] in <https://github.com/cds-astro/aladin-lite/pull/37>
|
||||
* Stop animation by [@tboch][tboch] in <https://github.com/cds-astro/aladin-lite/pull/40>
|
||||
* Add message for safari users to enable WebGL2 feature and reload the page by [@bmatthieu3][bmatthieu3] in <https://github.com/cds-astro/aladin-lite/pull/54>
|
||||
* Fix missing tiles issue by @tboch in <https://github.com/cds-astro/aladin-lite/pull/18>
|
||||
* Hips catalogue filtering by @tboch in <https://github.com/cds-astro/aladin-lite/pull/28>
|
||||
* Make footprint selection easier by @tboch in <https://github.com/cds-astro/aladin-lite/pull/19>
|
||||
* Bug fix: enable different colors for multiple polylines in same layer by @tboch in <https://github.com/cds-astro/aladin-lite/pull/30>
|
||||
* Method remove to delete individual source from a catalogue layer by @tboch in <https://github.com/cds-astro/aladin-lite/pull/37>
|
||||
* Stop animation by @tboch in <https://github.com/cds-astro/aladin-lite/pull/40>
|
||||
* Add message for safari users to enable WebGL2 feature and reload the page by @bmatthieu3 in <https://github.com/cds-astro/aladin-lite/pull/54>
|
||||
|
||||
## Version: 2.x.x
|
||||
## 2.x.x
|
||||
|
||||
### 2020-08
|
||||
|
||||
* polyline improvements (by [@imbasimba][imbasimba])
|
||||
* polyline improvements (by @imbasimba)
|
||||
|
||||
### 2020-07
|
||||
|
||||
@@ -469,33 +394,3 @@ New in the API:
|
||||
|
||||
* ajout catalogue progressif
|
||||
* ajout on select, objectClicked, objectHovered
|
||||
|
||||
|
||||
## Contributors
|
||||
|
||||
Aladin Lite is made possible thanks to those people:
|
||||
|
||||
* [@alexgoff][alexgoff]
|
||||
* [@bmatthieu3][bmatthieu3]
|
||||
* [@cquiroz][cquiroz]
|
||||
* [@imbasimba][imbasimba]
|
||||
* [@ManonMarchand][ManonMarchand]
|
||||
* [@pcuste1][pcuste1]
|
||||
* [@pmatsson][pmatsson]
|
||||
* [@simontorres][simontorres]
|
||||
* [@szpetny][szpetny]
|
||||
* [@tboch][tboch]
|
||||
* [@Xen0Xys][Xen0Xys]
|
||||
|
||||
[alexgoff]: https://github.com/alexgoff
|
||||
[bmatthieu3]: https://github.com/bmatthieu3
|
||||
[cquiroz]: https://github.com/cquiroz
|
||||
[imbasimba]: https://github.com/imbasimba
|
||||
[ManonMarchand]: https://github.com/ManonMarchand
|
||||
[pcuste1]: https://github.com/pcuste1
|
||||
[pmatsson]: https://github.com/pmatsson
|
||||
[simontorres]: https://github.com/simontorres
|
||||
[szpetny]: https://github.com/szpetny
|
||||
[tboch]: https://github.com/tboch
|
||||
[Xen0Xys]: https://github.com/Xen0Xys
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ A new [API technical documentation](https://cds-astro.github.io/aladin-lite/) is
|
||||
|
||||
[](https://github.com/cds-astro/aladin-lite/actions/workflows/test.yml)
|
||||
[](https://cds-astro.github.io/aladin-lite)
|
||||
[](https://aladin.cds.unistra.fr/AladinLite/doc/release/)
|
||||
[](https://aladin.cds.unistra.fr/AladinLite/doc/release/)
|
||||
|
||||
Aladin Lite is available [at this link](https://aladin.u-strasbg.fr/AladinLite).
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
"dateModified": "2023-01-31",
|
||||
"issueTracker": "https://github.com/cds-astro/aladin-lite/issues",
|
||||
"name": "Aladin Lite",
|
||||
"version": "3.6.4",
|
||||
"softwareVersion": "3.6.4",
|
||||
"version": "3.6.5",
|
||||
"softwareVersion": "3.6.5",
|
||||
"description": "An astronomical HiPS visualizer in the browser.",
|
||||
"identifier": "10.5281/zenodo.7638833",
|
||||
"applicationCategory": "Astronomy, Visualization",
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
// Start up Aladin Lite
|
||||
aladin = A.aladin('#aladin-lite-div', {survey: "CDS/P/DSS2/color", target: 'M 1', fov: 0.2, showContextMenu: true, fullScreen: true});
|
||||
aladin = A.aladin('#aladin-lite-div', {target: 'M 1', fov: 0.2, showContextMenu: true, fullScreen: true});
|
||||
var overlay = A.graphicOverlay({color: '#ee2345', lineWidth: 3, lineDash: [2, 2]});
|
||||
/*aladin.addOverlay(overlay);
|
||||
aladin.addOverlay(overlay);
|
||||
overlay.addFootprints([
|
||||
A.polygon([[83.64287, 22.01713], [83.59872, 22.01692], [83.59852, 21.97629], [83.64295, 21.97629]], {hoverColor: 'green'}),
|
||||
A.polygon([[83.62807, 22.06330], [83.58397, 22.02280], [83.62792, 22.02258]]),
|
||||
@@ -21,7 +21,7 @@
|
||||
]);
|
||||
overlay.add(A.circle(83.66067, 22.03081, 0.04, {color: 'cyan'})); // radius in degrees
|
||||
overlay.add(A.vector(83.66067, 22.03081, 0.04, {color: 'cyan'})); // radius in degrees
|
||||
*/
|
||||
|
||||
aladin.on("footprintClicked", (footprint, xyMouseCoords) => {
|
||||
console.log("footprint clicked catched: ", footprint, "mouse coords xy: ", xyMouseCoords.x, xyMouseCoords.y);
|
||||
})
|
||||
@@ -35,8 +35,8 @@
|
||||
console.log("Object hovered stopped: ", object, "mouse coords xy: ", xyMouseCoords.x, xyMouseCoords.y);
|
||||
})
|
||||
|
||||
const cat = A.catalogFromVizieR('B/assocdata/obscore', 'M 1', 10, {onClick: 'showTable', selectionColor: "orange", hoverColor: 'red', limit: 10000});
|
||||
aladin.addCatalog(cat);
|
||||
//const cat = A.catalogFromVizieR('B/assocdata/obscore', 'M 1', 10, {onClick: 'showTable', selectionColor: "orange", hoverColor: 'red', limit: 10000});
|
||||
//aladin.addCatalog(cat);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
A.init.then(() => {
|
||||
let aladin = A.aladin('#aladin-lite-div', {fov: 30, survey: "CDS/P/DSS2/color", target: "280 +0", projection: "AIT", showShareControl:true, showSettingsControl: true, showContextMenu:true});
|
||||
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",
|
||||
|
||||
@@ -29,9 +29,18 @@
|
||||
showCooGrid: true,
|
||||
fullScreen: true,
|
||||
samp: true,
|
||||
lockNorthUp: true,
|
||||
realFullscreen: true,
|
||||
}
|
||||
);
|
||||
|
||||
/*let id;
|
||||
aladin.on("zoomChanged", () => {
|
||||
if (id)
|
||||
clearTimeout(id);
|
||||
id = setTimeout(() => {
|
||||
console.log("wheel stopped, new cone search here")
|
||||
}, 500);
|
||||
})*/
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"homepage": "https://aladin.u-strasbg.fr/",
|
||||
"name": "aladin-lite",
|
||||
"type": "module",
|
||||
"version": "3.7.0-beta",
|
||||
"version": "3.6.5",
|
||||
"description": "An astronomical HiPS visualizer in the browser",
|
||||
"author": "Thomas Boch and Matthieu Baumann",
|
||||
"license": "GPL-3",
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "aladin-lite"
|
||||
description = "Aladin Lite v3 introduces a new graphical engine written in Rust with the use of WebGL"
|
||||
license = "BSD-3-Clause"
|
||||
repository = "https://github.com/cds-astro/aladin-lite"
|
||||
version = "3.7.0"
|
||||
version = "3.6.5"
|
||||
authors = [ "baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr",]
|
||||
edition = "2018"
|
||||
|
||||
@@ -88,4 +88,4 @@ codegen-units = 16
|
||||
rpath = false
|
||||
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = false
|
||||
wasm-opt = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "al-api"
|
||||
version = "3.6.4"
|
||||
version = "3.6.5"
|
||||
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
@@ -1,37 +1,45 @@
|
||||
use cgmath::Matrix3;
|
||||
use cgmath::Matrix4;
|
||||
|
||||
const GAL2ICRS: &'static Matrix3<f64> = &Matrix3::new(
|
||||
const GAL2ICRS: &'static Matrix4<f64> = &Matrix4::new(
|
||||
-0.44482972122205372312012370920248,
|
||||
0.74698218398450941835110635824212,
|
||||
0.49410943719710765017955928850141,
|
||||
|
||||
0.0,
|
||||
-0.19807633727507056817237662907031,
|
||||
0.45598381369115237931077906137440,
|
||||
-0.86766613755716255824577781583414,
|
||||
|
||||
0.0,
|
||||
-0.87343705195577915249273984034980,
|
||||
-0.48383507361641838378786914298189,
|
||||
-0.05487565771261968232908806948676,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
);
|
||||
|
||||
const ICRS2GAL: &'static Matrix3<f64> = &Matrix3::new(
|
||||
const ICRS2GAL: &'static Matrix4<f64> = &Matrix4::new(
|
||||
-0.44482972122205372312012370920248,
|
||||
-0.19807633727507056817237662907031,
|
||||
-0.87343705195577915249273984034980,
|
||||
|
||||
0.0,
|
||||
0.74698218398450941835110635824212,
|
||||
0.45598381369115237931077906137440,
|
||||
-0.48383507361641838378786914298189,
|
||||
|
||||
0.0,
|
||||
0.49410943719710765017955928850141,
|
||||
-0.86766613755716255824577781583414,
|
||||
-0.05487565771261968232908806948676,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
);
|
||||
|
||||
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 ID: &'static Matrix4<f64> = &Matrix4::new(
|
||||
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
|
||||
);
|
||||
|
||||
|
||||
@@ -48,7 +56,7 @@ pub const NUM_COOSYSTEM: usize = 2;
|
||||
|
||||
impl CooSystem {
|
||||
#[inline]
|
||||
pub fn to(&self, coo_system: Self) -> &Matrix3<f64> {
|
||||
pub fn to(&self, coo_system: Self) -> &Matrix4<f64> {
|
||||
match (self, coo_system) {
|
||||
(CooSystem::GAL, CooSystem::ICRS) => GAL2ICRS,
|
||||
(CooSystem::ICRS, CooSystem::GAL) => ICRS2GAL,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "al-core"
|
||||
version = "3.6.4"
|
||||
version = "3.6.5"
|
||||
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
@@ -218,19 +218,19 @@ impl UniformType for Matrix2<f32> {
|
||||
}
|
||||
}
|
||||
|
||||
use cgmath::Matrix3;
|
||||
impl UniformType for Matrix3<f32> {
|
||||
use cgmath::Matrix4;
|
||||
impl UniformType for Matrix4<f32> {
|
||||
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) {
|
||||
gl.uniform_matrix3fv_with_f32_array(location, false, value.as_ref() as &[f32; 9]);
|
||||
gl.uniform_matrix4fv_with_f32_array(location, false, value.as_ref() as &[f32; 16]);
|
||||
}
|
||||
}
|
||||
use crate::Abort;
|
||||
|
||||
impl UniformType for Matrix3<f64> {
|
||||
impl UniformType for Matrix4<f64> {
|
||||
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) {
|
||||
// Cast the matrix
|
||||
let mat_f32 = value.cast::<f32>().unwrap_abort();
|
||||
gl.uniform_matrix3fv_with_f32_array(location, false, mat_f32.as_ref() as &[f32; 9]);
|
||||
gl.uniform_matrix4fv_with_f32_array(location, false, mat_f32.as_ref() as &[f32; 16]);
|
||||
}
|
||||
}
|
||||
use super::texture::Texture2D;
|
||||
|
||||
@@ -35,6 +35,7 @@ use al_api::{
|
||||
grid::GridCfg,
|
||||
hips::{HiPSCfg, ImageMetadata},
|
||||
};
|
||||
use cgmath::Vector4;
|
||||
use fitsrs::{fits::AsyncFits, hdu::extension::AsyncXtensionHDU};
|
||||
|
||||
use web_sys::{HtmlElement, WebGl2RenderingContext};
|
||||
@@ -74,7 +75,6 @@ pub struct App {
|
||||
// Task executor
|
||||
//exec: Rc<RefCell<TaskExecutor>>,
|
||||
inertia: Option<Inertia>,
|
||||
north_up: bool,
|
||||
disable_inertia: Rc<RefCell<bool>>,
|
||||
dist_dragging: f32,
|
||||
time_start_dragging: Time,
|
||||
@@ -172,7 +172,7 @@ impl App {
|
||||
//let tasks_finished = false;
|
||||
let request_redraw = false;
|
||||
let rendering = true;
|
||||
let prev_cam_position = *camera.get_center();
|
||||
let prev_cam_position = camera.get_center().truncate();
|
||||
//let prev_center = Vector3::new(0.0, 1.0, 0.0);
|
||||
let out_of_fov = false;
|
||||
let catalog_loaded = false;
|
||||
@@ -244,7 +244,7 @@ impl App {
|
||||
catalog_loaded,
|
||||
|
||||
tile_fetcher,
|
||||
north_up: false,
|
||||
|
||||
colormaps,
|
||||
projection,
|
||||
|
||||
@@ -421,17 +421,17 @@ impl App {
|
||||
|
||||
let v = cell.vertices();
|
||||
let proj2screen = |(lon, lat): &(f64, f64)| -> Option<[f64; 2]> {
|
||||
// 1. convert to xyz
|
||||
let xyz = crate::math::lonlat::radec_to_xyz(lon.to_angle(), lat.to_angle());
|
||||
// 1. convert to xyzw
|
||||
let xyzw = crate::math::lonlat::radec_to_xyzw(lon.to_angle(), lat.to_angle());
|
||||
// 2. get it back to the camera frame system
|
||||
let xyz = crate::coosys::apply_coo_system(
|
||||
let xyzw = crate::coosys::apply_coo_system(
|
||||
CooSystem::ICRS,
|
||||
self.camera.get_coo_system(),
|
||||
&xyz,
|
||||
&xyzw,
|
||||
);
|
||||
|
||||
// 3. project on screen
|
||||
if let Some(p) = self.projection.model_to_clip_space(&xyz, &self.camera) {
|
||||
if let Some(p) = self.projection.model_to_clip_space(&xyzw, &self.camera) {
|
||||
Some([p.x, p.y])
|
||||
} else {
|
||||
None
|
||||
@@ -1353,12 +1353,11 @@ impl App {
|
||||
pub(crate) fn resize(&mut self, width: f32, height: f32) {
|
||||
self.camera.set_screen_size(width, height, &self.projection);
|
||||
self.camera
|
||||
.set_zoom_factor(self.camera.get_zoom_factor(), &self.projection);
|
||||
|
||||
.set_aperture(self.camera.get_aperture(), &self.projection);
|
||||
// resize the view fbo
|
||||
//let screen_size = self.camera.get_screen_size();
|
||||
//self._fbo_view
|
||||
// .resize(screen_size.x as usize, screen_size.y as usize);
|
||||
let screen_size = self.camera.get_screen_size();
|
||||
self._fbo_view
|
||||
.resize(screen_size.x as usize, screen_size.y as usize);
|
||||
// resize the ui fbo
|
||||
//self.fbo_ui.resize(w as usize, h as usize);
|
||||
|
||||
@@ -1444,9 +1443,9 @@ impl App {
|
||||
}
|
||||
|
||||
pub(crate) fn view_to_icrs_coosys(&self, lonlat: &LonLatT<f64>) -> LonLatT<f64> {
|
||||
let celestial_pos = lonlat.vector();
|
||||
let celestial_pos: Vector4<_> = lonlat.vector();
|
||||
let view_system = self.camera.get_coo_system();
|
||||
let (ra, dec) = math::lonlat::xyz_to_radec(&coosys::apply_coo_system(
|
||||
let (ra, dec) = math::lonlat::xyzw_to_radec(&coosys::apply_coo_system(
|
||||
view_system,
|
||||
CooSystem::ICRS,
|
||||
&celestial_pos,
|
||||
@@ -1457,7 +1456,7 @@ impl App {
|
||||
|
||||
/// lonlat must be given in icrs frame
|
||||
pub(crate) fn set_center(&mut self, lonlat: &LonLatT<f64>) {
|
||||
self.prev_cam_position = *self.camera.get_center();
|
||||
self.prev_cam_position = self.camera.get_center().truncate();
|
||||
|
||||
self.camera.set_center(lonlat, &self.projection);
|
||||
self.request_for_new_tiles = true;
|
||||
@@ -1521,11 +1520,11 @@ 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;
|
||||
|
||||
let _dist_dragging = self.dist_dragging;
|
||||
// Detect if there has been a recent acceleration
|
||||
// It is also possible that the dragging time is too short and if it is the case, trigger the inertia
|
||||
let recent_acceleration = (Time::now() - self.time_mouse_high_vel).as_secs() < 0.1
|
||||
@@ -1538,31 +1537,31 @@ impl App {
|
||||
// Start inertia here
|
||||
// Angular distance between the previous and current
|
||||
// center position
|
||||
let center = self.camera.get_center();
|
||||
let axis = self.prev_cam_position.cross(*center).normalize();
|
||||
let center = self.camera.get_center().truncate();
|
||||
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, ¢er).to_radians();
|
||||
let ampl = delta_angle * (dragging_vel as f64) * 5e-3;
|
||||
//let ampl = (dragging_vel * 0.01) as f64;
|
||||
|
||||
self.inertia = Some(Inertia::new(ampl.to_radians(), axis, self.north_up))
|
||||
self.inertia = Some(Inertia::new(ampl.to_radians(), axis))
|
||||
}
|
||||
|
||||
pub(crate) fn set_position_angle(&mut self, theta: ArcDeg<f64>) {
|
||||
pub(crate) fn set_view_center_pos_angle(&mut self, theta: ArcDeg<f64>) {
|
||||
self.camera
|
||||
.set_position_angle(theta.into(), &self.projection);
|
||||
.set_center_pos_angle(theta.into(), &self.projection);
|
||||
// New tiles can be needed and some tiles can be removed
|
||||
self.request_for_new_tiles = true;
|
||||
|
||||
self.request_redraw = true;
|
||||
}
|
||||
|
||||
pub(crate) fn get_position_angle(&self) -> Angle<f64> {
|
||||
self.camera.get_position_angle()
|
||||
pub(crate) fn get_north_shift_angle(&self) -> Angle<f64> {
|
||||
self.camera.get_center_pos_angle()
|
||||
}
|
||||
|
||||
pub(crate) fn set_fov(&mut self, fov: f64) {
|
||||
pub(crate) fn set_fov(&mut self, fov: Angle<f64>) {
|
||||
// For the moment, no animation is triggered.
|
||||
// The fov is directly set
|
||||
self.camera.set_aperture(fov, &self.projection);
|
||||
@@ -1570,20 +1569,17 @@ impl App {
|
||||
self.request_redraw = true;
|
||||
}
|
||||
|
||||
pub(crate) fn set_fov_range(&mut self, min_fov: Option<f64>, max_fov: Option<f64>) {
|
||||
self.camera.set_fov_range(
|
||||
min_fov.map(|v| v.to_radians()),
|
||||
max_fov.map(|v| v.to_radians()),
|
||||
&self.projection,
|
||||
);
|
||||
self.request_for_new_tiles = true;
|
||||
self.request_redraw = true;
|
||||
}
|
||||
|
||||
pub(crate) fn set_inertia(&mut self, inertia: bool) {
|
||||
*self.disable_inertia.borrow_mut() = !inertia;
|
||||
}
|
||||
|
||||
/*pub(crate) fn project_line(&self, lon1: f64, lat1: f64, lon2: f64, lat2: f64) -> Vec<Vector2<f64>> {
|
||||
let v1: Vector3<f64> = LonLatT::new(ArcDeg(lon1).into(), ArcDeg(lat1).into()).vector();
|
||||
let v2: Vector3<f64> = LonLatT::new(ArcDeg(lon2).into(), ArcDeg(lat2).into()).vector();
|
||||
|
||||
line::project_along_great_circles(&v1, &v2, &self.camera, self.projection)
|
||||
}*/
|
||||
|
||||
pub(crate) fn go_from_to(&mut self, s1x: f64, s1y: f64, s2x: f64, s2y: f64) {
|
||||
// Select the HiPS layer rendered lastly
|
||||
if let (Some(w1), Some(w2)) = (
|
||||
@@ -1592,65 +1588,22 @@ impl App {
|
||||
self.projection
|
||||
.screen_to_model_space(&Vector2::new(s2x, s2y), &self.camera),
|
||||
) {
|
||||
let prev_pos = w1;
|
||||
let cur_pos = w2;
|
||||
let prev_pos = w1.truncate();
|
||||
//let cur_pos = w1.truncate();
|
||||
let cur_pos = w2.truncate();
|
||||
//let next_pos = w2.truncate();
|
||||
if prev_pos != cur_pos {
|
||||
let prev_cam_position = self.camera.get_center().clone();
|
||||
/* 1. Rotate by computing the angle between the last and current position */
|
||||
|
||||
if self.north_up {
|
||||
let lonlat1 = prev_pos.lonlat();
|
||||
let lonlat2 = cur_pos.lonlat();
|
||||
// Apply the rotation to the camera to
|
||||
// go from the current pos to the next position
|
||||
let axis = prev_pos.cross(cur_pos).normalize();
|
||||
|
||||
let dlon = lonlat2.lon() - lonlat1.lon();
|
||||
let dlat = lonlat2.lat() - lonlat1.lat();
|
||||
|
||||
self.camera.apply_lonlat_rotation(dlon, dlat, &self.projection);
|
||||
let d = math::vector::angle3(&prev_pos, &cur_pos);
|
||||
|
||||
// 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);
|
||||
self.prev_cam_position = self.camera.get_center().truncate();
|
||||
self.camera.apply_rotation(&(-axis), d, &self.projection);
|
||||
|
||||
let cross_pole = cross_north_pole | cross_south_pole;
|
||||
|
||||
// Detect if a pole has been crossed
|
||||
let center = if cross_pole {
|
||||
&prev_cam_position
|
||||
} else {
|
||||
self.camera.get_center()
|
||||
};
|
||||
|
||||
let fov = self.camera.get_aperture();
|
||||
|
||||
let pole = if center.y >= 0.0 {
|
||||
north_pole
|
||||
} else {
|
||||
south_pole
|
||||
};
|
||||
|
||||
let c2p = crate::math::vector::angle3(center, &pole).to_radians();
|
||||
let near_pole = c2p.abs() < 5e-3 * fov;
|
||||
if near_pole || cross_pole {
|
||||
// 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);
|
||||
|
||||
self.camera.set_center_xyz(&new_center, &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 */
|
||||
|
||||
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.prev_cam_position = prev_cam_position;
|
||||
self.request_for_new_tiles = true;
|
||||
}
|
||||
} else {
|
||||
@@ -1658,10 +1611,6 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lock_north_up(&mut self) {
|
||||
self.north_up = true;
|
||||
}
|
||||
|
||||
pub(crate) fn add_cmap(&mut self, label: String, cmap: Colormap) -> Result<(), JsValue> {
|
||||
self.colormaps.add_cmap(label, cmap)
|
||||
}
|
||||
@@ -1675,19 +1624,13 @@ impl App {
|
||||
self.camera.get_texture_depth() as i32
|
||||
}
|
||||
|
||||
pub(crate) fn get_zoom_factor(&self) -> f64 {
|
||||
self.camera.get_zoom_factor()
|
||||
}
|
||||
|
||||
pub(crate) fn set_zoom_factor(&mut self, zoom_factor: f64) {
|
||||
self.camera.set_zoom_factor(zoom_factor, &self.projection);
|
||||
|
||||
self.request_for_new_tiles = true;
|
||||
self.request_redraw = true;
|
||||
pub(crate) fn get_clip_zoom_factor(&self) -> f64 {
|
||||
self.camera.get_clip_zoom_factor()
|
||||
}
|
||||
|
||||
pub(crate) fn get_fov(&self) -> f64 {
|
||||
self.camera.get_aperture().to_degrees()
|
||||
let deg: ArcDeg<f64> = self.camera.get_aperture().into();
|
||||
deg.0
|
||||
}
|
||||
|
||||
pub(crate) fn get_colormaps(&self) -> &Colormaps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use cgmath::{Matrix3, Vector2};
|
||||
use cgmath::{Matrix4, Vector2};
|
||||
|
||||
use crate::math::projection::coo_space::{XYZModel, XYZWorld, XYNDC};
|
||||
use crate::math::projection::coo_space::{XYZWModel, XYZWWorld, XYNDC};
|
||||
|
||||
use crate::math::sph_geom::region::{Intersection, PoleContained, Region};
|
||||
use crate::math::{projection::Projection, sph_geom::bbox::BoundingBox};
|
||||
@@ -14,7 +14,7 @@ fn ndc_to_world(
|
||||
ndc_to_clip: &Vector2<f64>,
|
||||
clip_zoom_factor: f64,
|
||||
projection: &ProjectionType,
|
||||
) -> Option<Vec<XYZWorld<f64>>> {
|
||||
) -> Option<Vec<XYZWWorld<f64>>> {
|
||||
// Deproject the FOV from ndc to the world space
|
||||
let mut world_coo = Vec::with_capacity(ndc_coo.len());
|
||||
|
||||
@@ -34,7 +34,7 @@ fn ndc_to_world(
|
||||
|
||||
Some(world_coo)
|
||||
}
|
||||
fn world_to_model(world_coo: &[XYZWorld<f64>], w2m: &Matrix3<f64>) -> Vec<XYZModel<f64>> {
|
||||
fn world_to_model(world_coo: &[XYZWWorld<f64>], w2m: &Matrix4<f64>) -> Vec<XYZWModel<f64>> {
|
||||
let mut model_coo = Vec::with_capacity(world_coo.len());
|
||||
|
||||
for w in world_coo.iter() {
|
||||
@@ -61,8 +61,8 @@ const NUM_VERTICES: usize = 4 + 2 * NUM_VERTICES_WIDTH + 2 * NUM_VERTICES_HEIGHT
|
||||
pub struct FieldOfView {
|
||||
// Vertices
|
||||
ndc_vertices: Vec<XYNDC<f64>>,
|
||||
world_vertices: Option<Vec<XYZWorld<f64>>>,
|
||||
model_vertices: Option<Vec<XYZModel<f64>>>,
|
||||
world_vertices: Option<Vec<XYZWWorld<f64>>>,
|
||||
model_vertices: Option<Vec<XYZWModel<f64>>>,
|
||||
|
||||
reg: Region,
|
||||
}
|
||||
@@ -73,7 +73,7 @@ impl FieldOfView {
|
||||
ndc_to_clip: &Vector2<f64>,
|
||||
clip_zoom_factor: f64,
|
||||
// rotation
|
||||
rotation_mat: &Matrix3<f64>,
|
||||
rotation_mat: &Matrix4<f64>,
|
||||
// projection
|
||||
projection: &ProjectionType,
|
||||
) -> Self {
|
||||
@@ -123,7 +123,7 @@ impl FieldOfView {
|
||||
&mut self,
|
||||
ndc_to_clip: &Vector2<f64>,
|
||||
clip_zoom_factor: f64,
|
||||
rotate_mat: &Matrix3<f64>,
|
||||
rotate_mat: &Matrix4<f64>,
|
||||
projection: &ProjectionType,
|
||||
) {
|
||||
self.world_vertices = ndc_to_world(
|
||||
@@ -135,7 +135,7 @@ impl FieldOfView {
|
||||
self.set_rotation(rotate_mat);
|
||||
}
|
||||
|
||||
pub fn set_rotation(&mut self, rotate_mat: &Matrix3<f64>) {
|
||||
pub fn set_rotation(&mut self, rotate_mat: &Matrix4<f64>) {
|
||||
if let Some(world_vertices) = &self.world_vertices {
|
||||
self.model_vertices = Some(world_to_model(world_vertices, rotate_mat));
|
||||
} else {
|
||||
@@ -186,7 +186,7 @@ impl FieldOfView {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_vertices(&self) -> Option<&Vec<XYZModel<f64>>> {
|
||||
pub fn get_vertices(&self) -> Option<&Vec<XYZWModel<f64>>> {
|
||||
self.model_vertices.as_ref()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pub mod viewport;
|
||||
use crate::math::lonlat::LonLat;
|
||||
use crate::math::projection::coo_space::XYZModel;
|
||||
use crate::math::projection::coo_space::XYZWModel;
|
||||
pub use viewport::CameraViewPort;
|
||||
|
||||
pub mod fov;
|
||||
@@ -14,7 +14,7 @@ use crate::ProjectionType;
|
||||
pub fn build_fov_coverage(
|
||||
depth: u8,
|
||||
fov: &FieldOfView,
|
||||
camera_center: &XYZModel<f64>,
|
||||
camera_center: &XYZWModel<f64>,
|
||||
camera_frame: CooSystem,
|
||||
frame: CooSystem,
|
||||
proj: &ProjectionType,
|
||||
@@ -40,7 +40,7 @@ pub fn build_fov_coverage(
|
||||
// See https://github.com/cds-astro/cds-moc-rust/issues/3
|
||||
|
||||
let hpx_idxs_iter = vertices_iter.map(|v| {
|
||||
let (lon, lat) = crate::math::lonlat::xyz_to_radec(&v);
|
||||
let (lon, lat) = crate::math::lonlat::xyzw_to_radec(&v);
|
||||
::healpix::nested::hash(depth, lon.to_radians(), lat.to_radians())
|
||||
});
|
||||
|
||||
@@ -55,10 +55,10 @@ pub fn build_fov_coverage(
|
||||
moc
|
||||
}
|
||||
} else {
|
||||
let center_xyz = crate::coosys::apply_coo_system(camera_frame, frame, camera_center);
|
||||
let center_xyzw = crate::coosys::apply_coo_system(camera_frame, frame, camera_center);
|
||||
|
||||
let biggest_fov_rad = proj.aperture_start().to_radians();
|
||||
let lonlat = center_xyz.lonlat();
|
||||
let lonlat = center_xyzw.lonlat();
|
||||
HEALPixCoverage::from_cone(&lonlat, biggest_fov_rad * 0.5, depth)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::camera::XYZModel;
|
||||
use crate::camera::XYZWModel;
|
||||
use crate::healpix::cell::HEALPixCell;
|
||||
|
||||
use crate::math::projection::*;
|
||||
@@ -30,7 +30,7 @@ impl ViewHpxCells {
|
||||
&mut self,
|
||||
camera_depth: u8,
|
||||
fov: &FieldOfView,
|
||||
center: &XYZModel<f64>,
|
||||
center: &XYZWModel<f64>,
|
||||
camera_frame: CooSystem,
|
||||
proj: &ProjectionType,
|
||||
// survey frame
|
||||
@@ -48,7 +48,7 @@ impl ViewHpxCells {
|
||||
&mut self,
|
||||
camera_depth: u8,
|
||||
fov: &FieldOfView,
|
||||
center: &XYZModel<f64>,
|
||||
center: &XYZWModel<f64>,
|
||||
camera_frame: CooSystem,
|
||||
proj: &ProjectionType,
|
||||
// survey frame
|
||||
@@ -68,7 +68,7 @@ impl ViewHpxCells {
|
||||
&mut self,
|
||||
camera_depth: u8,
|
||||
fov: &FieldOfView,
|
||||
center: &XYZModel<f64>,
|
||||
center: &XYZWModel<f64>,
|
||||
camera_frame: CooSystem,
|
||||
proj: &ProjectionType,
|
||||
) {
|
||||
@@ -149,7 +149,7 @@ impl HpxCells {
|
||||
&mut self,
|
||||
camera_depth: u8,
|
||||
fov: &FieldOfView,
|
||||
center: &XYZModel<f64>,
|
||||
center: &XYZWModel<f64>,
|
||||
camera_frame: CooSystem,
|
||||
proj: &ProjectionType,
|
||||
) {
|
||||
|
||||
@@ -8,32 +8,26 @@ 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: &Matrix4<f64> = &Matrix4::new(
|
||||
-1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.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::math::angle::ToAngle;
|
||||
use crate::math::{projection::coo_space::XYZModel, projection::domain::sdf::ProjDef};
|
||||
|
||||
use cgmath::{Matrix3, Vector2};
|
||||
const APERTURE_LOWER_LIMIT_RAD: f64 = (1.0_f64 / 36000.0).to_radians();
|
||||
const ZOOM_FACTOR_UPPER_LIMIT: f64 = 2.0;
|
||||
use crate::math::{projection::coo_space::XYZWModel, projection::domain::sdf::ProjDef};
|
||||
|
||||
use cgmath::{Matrix4, Vector2};
|
||||
pub struct CameraViewPort {
|
||||
// The field of view angle
|
||||
aperture: f64,
|
||||
aperture: Angle<f64>,
|
||||
// The rotation of the camera
|
||||
center: Vector3<f64>,
|
||||
center: Vector4<f64>,
|
||||
w2m_rot: Rotation<f64>,
|
||||
|
||||
w2m: Matrix3<f64>,
|
||||
m2w: Matrix3<f64>,
|
||||
w2m: Matrix4<f64>,
|
||||
m2w: Matrix4<f64>,
|
||||
// The width over height ratio
|
||||
aspect: f32,
|
||||
// The width of the screen in pixels
|
||||
@@ -50,7 +44,7 @@ pub struct CameraViewPort {
|
||||
|
||||
// Internal variable used for projection purposes
|
||||
ndc_to_clip: Vector2<f64>,
|
||||
zoom_factor: f64,
|
||||
clip_zoom_factor: f64,
|
||||
// The vertices in model space of the camera
|
||||
// This is useful for computing views according
|
||||
// to different image surveys
|
||||
@@ -77,11 +71,6 @@ pub struct CameraViewPort {
|
||||
gl: WebGlContext,
|
||||
coo_sys: CooSystem,
|
||||
reversed_longitude: bool,
|
||||
|
||||
// min field of view, by default 0.1 arcsec
|
||||
pub(crate) min_fov: Option<f64>,
|
||||
// an optional max field of view
|
||||
pub(crate) max_fov: Option<f64>,
|
||||
}
|
||||
use al_api::coo_system::CooSystem;
|
||||
use al_core::WebGlContext;
|
||||
@@ -92,7 +81,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use crate::LonLatT;
|
||||
use cgmath::SquareMatrix;
|
||||
use cgmath::{SquareMatrix, Vector4};
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
const MAX_DPI_LIMIT: f32 = 2.0;
|
||||
@@ -107,11 +96,11 @@ impl CameraViewPort {
|
||||
) -> CameraViewPort {
|
||||
let last_user_action = UserAction::Starting;
|
||||
|
||||
let aperture = projection.aperture_start().to_radians();
|
||||
let aperture = projection.aperture_start();
|
||||
|
||||
let w2m = Matrix3::identity();
|
||||
let w2m = Matrix4::identity();
|
||||
let m2w = w2m;
|
||||
let center = Vector3::new(0.0, 0.0, 0.0);
|
||||
let center = Vector4::new(0.0, 0.0, 0.0, 1.0);
|
||||
let moved = false;
|
||||
let zoomed = false;
|
||||
|
||||
@@ -133,9 +122,9 @@ impl CameraViewPort {
|
||||
|
||||
let aspect = height / width;
|
||||
let ndc_to_clip = Vector2::new(1.0, (height as f64) / (width as f64));
|
||||
let zoom_factor = 1.0;
|
||||
let clip_zoom_factor = 1.0;
|
||||
|
||||
let fov = FieldOfView::new(&ndc_to_clip, zoom_factor, &w2m, projection);
|
||||
let fov = FieldOfView::new(&ndc_to_clip, clip_zoom_factor, &w2m, projection);
|
||||
let gl = gl.clone();
|
||||
|
||||
let is_allsky = true;
|
||||
@@ -165,7 +154,7 @@ impl CameraViewPort {
|
||||
|
||||
// Internal variable used for projection purposes
|
||||
ndc_to_clip,
|
||||
zoom_factor,
|
||||
clip_zoom_factor,
|
||||
// The field of view
|
||||
fov,
|
||||
view_hpx_cells,
|
||||
@@ -188,9 +177,6 @@ impl CameraViewPort {
|
||||
coo_sys,
|
||||
// a flag telling if the viewport has a reversed longitude axis
|
||||
reversed_longitude,
|
||||
|
||||
min_fov: None,
|
||||
max_fov: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,13 +223,13 @@ impl CameraViewPort {
|
||||
|
||||
// check the projection
|
||||
match proj {
|
||||
ProjectionType::Tan(_) => self.aperture >= 100.0_f64.to_radians(),
|
||||
ProjectionType::Mer(_) => self.aperture >= 120.0_f64.to_radians(),
|
||||
ProjectionType::Stg(_) => self.aperture >= 200.0_f64.to_radians(),
|
||||
ProjectionType::Tan(_) => self.aperture >= 100.0_f64.to_radians().to_angle(),
|
||||
ProjectionType::Mer(_) => self.aperture >= 120.0_f64.to_radians().to_angle(),
|
||||
ProjectionType::Stg(_) => self.aperture >= 200.0_f64.to_radians().to_angle(),
|
||||
ProjectionType::Sin(_) => false,
|
||||
ProjectionType::Ait(_) => self.aperture >= 100.0_f64.to_radians(),
|
||||
ProjectionType::Mol(_) => self.aperture >= 100.0_f64.to_radians(),
|
||||
ProjectionType::Zea(_) => self.aperture >= 140.0_f64.to_radians(),
|
||||
ProjectionType::Ait(_) => self.aperture >= 100.0_f64.to_radians().to_angle(),
|
||||
ProjectionType::Mol(_) => self.aperture >= 100.0_f64.to_radians().to_angle(),
|
||||
ProjectionType::Zea(_) => self.aperture >= 140.0_f64.to_radians().to_angle(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,8 +278,12 @@ impl CameraViewPort {
|
||||
// Compute the new clip zoom factor
|
||||
self.compute_ndc_to_clip_factor(projection);
|
||||
|
||||
self.fov
|
||||
.set_aperture(&self.ndc_to_clip, self.zoom_factor, &self.w2m, projection);
|
||||
self.fov.set_aperture(
|
||||
&self.ndc_to_clip,
|
||||
self.clip_zoom_factor,
|
||||
&self.w2m,
|
||||
projection,
|
||||
);
|
||||
|
||||
let proj_area = projection.get_area();
|
||||
self.is_allsky = !proj_area.is_in(&math::projection::ndc_to_clip_space(
|
||||
@@ -335,74 +325,7 @@ impl CameraViewPort {
|
||||
self.set_aperture(self.aperture, proj);
|
||||
}
|
||||
|
||||
/// Give a FoV range in radians
|
||||
pub(crate) fn set_fov_range(
|
||||
&mut self,
|
||||
mut min_fov: Option<f64>,
|
||||
mut max_fov: Option<f64>,
|
||||
proj: &ProjectionType,
|
||||
) {
|
||||
// Invert the min and max bounds if min > max
|
||||
if let (Some(min_fov), Some(max_fov)) = (min_fov.as_mut(), max_fov.as_mut()) {
|
||||
if *max_fov < *min_fov {
|
||||
std::mem::swap(max_fov, min_fov);
|
||||
}
|
||||
}
|
||||
|
||||
self.min_fov = min_fov;
|
||||
self.max_fov = max_fov;
|
||||
|
||||
self.set_aperture(self.aperture, proj);
|
||||
}
|
||||
|
||||
pub(crate) fn at_zoom_boundaries(&self, proj: &ProjectionType) -> bool {
|
||||
// The zoom factor cannot exceed an upper limit
|
||||
if self.zoom_factor >= ZOOM_FACTOR_UPPER_LIMIT {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The field of view cannot go deeper a lower limit
|
||||
if self.aperture <= APERTURE_LOWER_LIMIT_RAD {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The field of view might be forced in a user defined range
|
||||
if let Some(min_fov) = self.min_fov {
|
||||
if self.aperture <= min_fov {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(max_fov) = self.max_fov {
|
||||
if self.aperture >= max_fov {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
let can_unzoom_more = match proj {
|
||||
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_) => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if !can_unzoom_more && self.zoom_factor >= 1.0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn set_aperture(&mut self, mut aperture: f64, proj: &ProjectionType) {
|
||||
// Force the given aperture by a range given by the user
|
||||
if let Some(min_fov) = self.min_fov {
|
||||
aperture = aperture.max(min_fov);
|
||||
}
|
||||
|
||||
if let Some(max_fov) = self.max_fov {
|
||||
aperture = aperture.min(max_fov);
|
||||
}
|
||||
|
||||
// Limit internally the aperture to 0.1 arcsec
|
||||
aperture = aperture.max(APERTURE_LOWER_LIMIT_RAD);
|
||||
|
||||
pub fn set_aperture(&mut self, aperture: Angle<f64>, proj: &ProjectionType) {
|
||||
// Checking if we are zooming or unzooming
|
||||
// This is used internaly for the raytracer to compute
|
||||
// blending between tiles and their parents (or children)
|
||||
@@ -415,15 +338,23 @@ impl CameraViewPort {
|
||||
};
|
||||
|
||||
let can_unzoom_more = match proj {
|
||||
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_) => false,
|
||||
ProjectionType::Tan(_)
|
||||
| ProjectionType::Mer(_)
|
||||
//| ProjectionType::Air(_)
|
||||
| ProjectionType::Stg(_) => false,
|
||||
//| ProjectionType::Car(_)
|
||||
//| ProjectionType::Cea(_)
|
||||
//| ProjectionType::Cyp(_)
|
||||
//| ProjectionType::Hpx(_) => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
let aperture_start: f64 = proj.aperture_start().to_radians();
|
||||
let aperture_start = proj.aperture_start();
|
||||
|
||||
self.zoom_factor = if aperture > aperture_start {
|
||||
self.clip_zoom_factor = if aperture > aperture_start {
|
||||
//al_core::log(&format!("a: {:?}, as: {:?}", aperture, aperture_start));
|
||||
if can_unzoom_more {
|
||||
aperture / aperture_start
|
||||
aperture.to_radians() / aperture_start.to_radians()
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
@@ -431,24 +362,35 @@ impl CameraViewPort {
|
||||
// Compute the new clip zoom factor
|
||||
let a = aperture.abs();
|
||||
|
||||
let v0 = math::lonlat::radec_to_xyz(-a.to_angle() / 2.0, 0.0.to_angle());
|
||||
let v1 = math::lonlat::radec_to_xyz(a.to_angle() / 2.0, 0.0.to_angle());
|
||||
let v0 = math::lonlat::radec_to_xyzw(-a / 2.0, 0.0.to_angle());
|
||||
let v1 = math::lonlat::radec_to_xyzw(a / 2.0, 0.0.to_angle());
|
||||
|
||||
// Vertex in the WCS of the FOV
|
||||
if let (Some(p0), Some(p1)) =
|
||||
(proj.world_to_clip_space(&v0), proj.world_to_clip_space(&v1))
|
||||
{
|
||||
(0.5 * (p1.x - p0.x).abs()).min(1.0)
|
||||
if self.width < self.height {
|
||||
if let (Some(p0), Some(p1)) =
|
||||
(proj.world_to_clip_space(&v0), proj.world_to_clip_space(&v1))
|
||||
{
|
||||
(0.5 * (p1.x - p0.x).abs()).min(1.0)
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
} else {
|
||||
1.0
|
||||
if let (Some(p0), Some(p1)) =
|
||||
(proj.world_to_clip_space(&v0), proj.world_to_clip_space(&v1))
|
||||
{
|
||||
(0.5 * (p1.x - p0.x).abs()).min(1.0)
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Limit the zoom factor to not unzoom too much
|
||||
self.zoom_factor = self.zoom_factor.min(ZOOM_FACTOR_UPPER_LIMIT);
|
||||
|
||||
// Limit later the aperture to aperture_start
|
||||
self.aperture = aperture.min(aperture_start);
|
||||
//self.aperture = aperture;
|
||||
|
||||
//al_core::log(&format!("zoom factor {:?}", self.clip_zoom_factor));
|
||||
|
||||
//console_log(&format!("clip factor {:?}", self.aperture));
|
||||
|
||||
// Project this vertex into the screen
|
||||
self.moved = true;
|
||||
@@ -456,101 +398,7 @@ impl CameraViewPort {
|
||||
self.time_last_move = Time::now();
|
||||
|
||||
self.fov
|
||||
.set_aperture(&self.ndc_to_clip, self.zoom_factor, &self.w2m, proj);
|
||||
|
||||
let proj_area = proj.get_area();
|
||||
self.is_allsky = !proj_area.is_in(&math::projection::ndc_to_clip_space(
|
||||
&Vector2::new(-1.0, -1.0),
|
||||
self,
|
||||
));
|
||||
|
||||
self.compute_texture_depth();
|
||||
|
||||
// Recompute the scissor with the new aperture
|
||||
self.recompute_scissor();
|
||||
|
||||
// Compute the hpx cells
|
||||
self.view_hpx_cells.update(
|
||||
self.texture_depth,
|
||||
&self.fov,
|
||||
&self.center,
|
||||
self.get_coo_system(),
|
||||
proj,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn set_zoom_factor(&mut self, zoom_factor: f64, proj: &ProjectionType) {
|
||||
// Checking if we are zooming or unzooming
|
||||
// This is used internaly for the raytracer to compute
|
||||
// blending between tiles and their parents (or children)
|
||||
self.last_user_action = if self.zoom_factor > zoom_factor {
|
||||
UserAction::Zooming
|
||||
} else if self.zoom_factor < zoom_factor {
|
||||
UserAction::Unzooming
|
||||
} else {
|
||||
self.last_user_action
|
||||
};
|
||||
|
||||
let can_unzoom_more = match proj {
|
||||
ProjectionType::Tan(_) | ProjectionType::Mer(_) | ProjectionType::Stg(_) => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
// Set the zoom factor
|
||||
self.zoom_factor = zoom_factor;
|
||||
// Limit it to prevent unzooming infinitely
|
||||
self.zoom_factor = self.zoom_factor.min(ZOOM_FACTOR_UPPER_LIMIT);
|
||||
|
||||
let aperture_start = proj.aperture_start().to_radians();
|
||||
|
||||
// clamp it to one if we cannot unzoom more (because of the projection)
|
||||
let aperture = if !can_unzoom_more && zoom_factor >= 1.0 {
|
||||
self.zoom_factor = 1.0;
|
||||
|
||||
aperture_start
|
||||
} else if can_unzoom_more && zoom_factor >= 1.0 {
|
||||
aperture_start
|
||||
} else {
|
||||
// zoom_factor < 1.0
|
||||
if let Some((lon, _)) = proj
|
||||
.clip_to_world_space(&Vector2::new(self.zoom_factor, 0.0))
|
||||
.map(|xyz| math::lonlat::xyz_to_radec(&xyz))
|
||||
{
|
||||
lon.to_radians().abs() * 2.0
|
||||
} else {
|
||||
aperture_start
|
||||
}
|
||||
};
|
||||
|
||||
// Force the given aperture to be in an optional range given by the user
|
||||
let mut clamped_aperture = aperture;
|
||||
if let Some(min_fov) = self.min_fov {
|
||||
clamped_aperture = clamped_aperture.max(min_fov);
|
||||
}
|
||||
|
||||
if let Some(max_fov) = self.max_fov {
|
||||
clamped_aperture = clamped_aperture.min(max_fov);
|
||||
}
|
||||
|
||||
// The aperture must also be > to a lower limit
|
||||
clamped_aperture = clamped_aperture.max(APERTURE_LOWER_LIMIT_RAD);
|
||||
|
||||
if clamped_aperture != aperture {
|
||||
// there has been a clamping of the aperture, then we recompute the zoom factor
|
||||
// with the new clamped aperture
|
||||
self.set_aperture(clamped_aperture, proj);
|
||||
return;
|
||||
}
|
||||
|
||||
self.aperture = aperture;
|
||||
|
||||
// Project this vertex into the screen
|
||||
self.moved = true;
|
||||
self.zoomed = true;
|
||||
self.time_last_move = Time::now();
|
||||
|
||||
self.fov
|
||||
.set_aperture(&self.ndc_to_clip, self.zoom_factor, &self.w2m, proj);
|
||||
.set_aperture(&self.ndc_to_clip, self.clip_zoom_factor, &self.w2m, proj);
|
||||
|
||||
let proj_area = proj.get_area();
|
||||
self.is_allsky = !proj_area.is_in(&math::projection::ndc_to_clip_space(
|
||||
@@ -600,7 +448,8 @@ impl CameraViewPort {
|
||||
let smallest_cell_size_px = self.dpi as f64;
|
||||
let mut depth_pixel = 29 as usize;
|
||||
|
||||
let hpx_cell_size_rad = (smallest_cell_size_px / w_screen_px) * self.get_aperture();
|
||||
let hpx_cell_size_rad =
|
||||
(smallest_cell_size_px / w_screen_px) * self.get_aperture().to_radians();
|
||||
|
||||
while depth_pixel > 0 {
|
||||
if crate::healpix::utils::MEAN_HPX_CELL_RES[depth_pixel] > hpx_cell_size_rad {
|
||||
@@ -622,7 +471,7 @@ impl CameraViewPort {
|
||||
self.texture_depth
|
||||
}
|
||||
|
||||
pub fn apply_axis_rotation(
|
||||
pub fn apply_rotation(
|
||||
&mut self,
|
||||
axis: &cgmath::Vector3<f64>,
|
||||
angle: Angle<f64>,
|
||||
@@ -635,29 +484,14 @@ impl CameraViewPort {
|
||||
self.update_rot_matrices(proj);
|
||||
}
|
||||
|
||||
pub fn apply_lonlat_rotation(
|
||||
&mut self,
|
||||
dlon: Angle<f64>,
|
||||
dlat: Angle<f64>,
|
||||
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(¢er);
|
||||
|
||||
self.set_rotation(&rot, proj);
|
||||
}
|
||||
|
||||
/// center lonlat must be given in icrs frame
|
||||
pub fn set_center(&mut self, lonlat: &LonLatT<f64>, proj: &ProjectionType) {
|
||||
let icrs_pos = lonlat.vector();
|
||||
self.set_center_xyz(&icrs_pos, proj);
|
||||
}
|
||||
let icrs_pos: Vector4<_> = lonlat.vector();
|
||||
|
||||
pub fn set_center_xyz(&mut self, xyz: &Vector3<f64>, proj: &ProjectionType) {
|
||||
let center = CooSystem::ICRS.to(self.get_coo_system()) * xyz;
|
||||
let center = (CooSystem::ICRS.to(self.get_coo_system()) * icrs_pos).truncate();
|
||||
let rot_to_center = Rotation::from_sky_position(¢er);
|
||||
|
||||
let phi = self.get_position_angle();
|
||||
let phi = self.get_center_pos_angle();
|
||||
let third_euler_rot = Rotation::from_axis_angle(¢er, phi);
|
||||
|
||||
let rot = third_euler_rot * rot_to_center;
|
||||
@@ -667,8 +501,8 @@ impl CameraViewPort {
|
||||
self.set_rotation(&rot, proj);
|
||||
}
|
||||
|
||||
pub fn set_position_angle(&mut self, phi: Angle<f64>, proj: &ProjectionType) {
|
||||
let c = self.center;
|
||||
pub fn set_center_pos_angle(&mut self, phi: Angle<f64>, proj: &ProjectionType) {
|
||||
let c = self.center.truncate();
|
||||
let rot_to_center = Rotation::from_sky_position(&c);
|
||||
let third_euler_rot = Rotation::from_axis_angle(&c, phi);
|
||||
|
||||
@@ -676,7 +510,7 @@ impl CameraViewPort {
|
||||
self.set_rotation(&total_rot, proj);
|
||||
}
|
||||
|
||||
pub fn set_rotation(&mut self, rot: &Rotation<f64>, proj: &ProjectionType) {
|
||||
fn set_rotation(&mut self, rot: &Rotation<f64>, proj: &ProjectionType) {
|
||||
self.w2m_rot = *rot;
|
||||
|
||||
self.update_rot_matrices(proj);
|
||||
@@ -690,7 +524,7 @@ impl CameraViewPort {
|
||||
// Compute the center position according to the new coordinate frame system
|
||||
let new_center = coosys::apply_coo_system(self.coo_sys, new_coo_sys, &self.center);
|
||||
// Create a rotation object from that position
|
||||
let new_rotation = Rotation::from_sky_position(&new_center);
|
||||
let new_rotation = Rotation::from_sky_position(&new_center.truncate());
|
||||
// Apply it to the center of the view
|
||||
self.set_rotation(&new_rotation, proj);
|
||||
|
||||
@@ -731,11 +565,11 @@ impl CameraViewPort {
|
||||
}
|
||||
|
||||
// Accessors
|
||||
pub fn get_w2m(&self) -> &cgmath::Matrix3<f64> {
|
||||
pub fn get_w2m(&self) -> &cgmath::Matrix4<f64> {
|
||||
&self.w2m
|
||||
}
|
||||
|
||||
pub fn get_m2w(&self) -> &cgmath::Matrix3<f64> {
|
||||
pub fn get_m2w(&self) -> &cgmath::Matrix4<f64> {
|
||||
&self.m2w
|
||||
}
|
||||
|
||||
@@ -747,11 +581,11 @@ impl CameraViewPort {
|
||||
&self.ndc_to_clip
|
||||
}
|
||||
|
||||
pub fn get_zoom_factor(&self) -> f64 {
|
||||
self.zoom_factor
|
||||
pub fn get_clip_zoom_factor(&self) -> f64 {
|
||||
self.clip_zoom_factor
|
||||
}
|
||||
|
||||
pub fn get_vertices(&self) -> Option<&Vec<XYZModel<f64>>> {
|
||||
pub fn get_vertices(&self) -> Option<&Vec<XYZWModel<f64>>> {
|
||||
self.fov.get_vertices()
|
||||
}
|
||||
|
||||
@@ -789,14 +623,13 @@ impl CameraViewPort {
|
||||
self.zoomed = false;
|
||||
}
|
||||
|
||||
/// Aperture is given in radians
|
||||
#[inline]
|
||||
pub fn get_aperture(&self) -> f64 {
|
||||
pub fn get_aperture(&self) -> Angle<f64> {
|
||||
self.aperture
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_center(&self) -> &Vector3<f64> {
|
||||
pub fn get_center(&self) -> &Vector4<f64> {
|
||||
&self.center
|
||||
}
|
||||
|
||||
@@ -813,7 +646,7 @@ impl CameraViewPort {
|
||||
self.coo_sys
|
||||
}
|
||||
|
||||
pub fn get_position_angle(&self) -> Angle<f64> {
|
||||
pub fn get_center_pos_angle(&self) -> Angle<f64> {
|
||||
(self.w2m.x.y).atan2(self.w2m.y.y).to_angle()
|
||||
}
|
||||
}
|
||||
@@ -855,7 +688,7 @@ impl SendUniforms for CameraViewPort {
|
||||
fn attach_uniforms<'a>(&self, shader: &'a ShaderBound<'a>) -> &'a ShaderBound<'a> {
|
||||
shader
|
||||
.attach_uniform("ndc_to_clip", &self.ndc_to_clip) // Send ndc to clip
|
||||
.attach_uniform("czf", &self.zoom_factor); // Send clip zoom factor
|
||||
.attach_uniform("czf", &self.clip_zoom_factor); // Send clip zoom factor
|
||||
|
||||
shader
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use cgmath::Vector3;
|
||||
use cgmath::Vector4;
|
||||
use al_api::coo_system::CooSystem;
|
||||
|
||||
/// This is conversion method returning a transformation
|
||||
@@ -6,7 +6,7 @@ use al_api::coo_system::CooSystem;
|
||||
/// ICRS.
|
||||
/// The core projections are always performed in ICRS
|
||||
#[inline]
|
||||
pub fn apply_coo_system(c1: CooSystem, c2: CooSystem, v: &Vector3<f64>) -> Vector3<f64> {
|
||||
pub fn apply_coo_system(c1: CooSystem, c2: CooSystem, v: &Vector4<f64>) -> Vector4<f64> {
|
||||
let c1_2_c2_mat = c1.to(c2);
|
||||
c1_2_c2_mat * (*v)
|
||||
}
|
||||
|
||||
@@ -185,14 +185,24 @@ impl From<query::Allsky> for AllskyRequest {
|
||||
.collect())
|
||||
}
|
||||
InMemData::F32(data) => {
|
||||
let data = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4) };
|
||||
let data = unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
data.as_ptr() as *const u8,
|
||||
data.len() * 4,
|
||||
)
|
||||
};
|
||||
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
|
||||
.map(|image| ImageType::RawRgba8u { image })
|
||||
.collect())
|
||||
}
|
||||
InMemData::F64(data) => {
|
||||
let data = data.iter().map(|v| *v as f32).collect::<Vec<_>>();
|
||||
let data = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4) };
|
||||
let data = unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
data.as_ptr() as *const u8,
|
||||
data.len() * 4,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
|
||||
.map(|image| ImageType::RawRgba8u { image })
|
||||
@@ -228,71 +238,77 @@ fn handle_allsky_file<F: ImageFormat>(
|
||||
let num_allsky_tiles_per_tile = (tile_size / allsky_tile_size) * (tile_size / allsky_tile_size);
|
||||
|
||||
let mut src_idx = 0;
|
||||
let tiles = (0..num_tiles)
|
||||
.map(move |_| {
|
||||
let mut base_tile =
|
||||
let tiles = (0..num_tiles).map(move |_| {
|
||||
let mut base_tile =
|
||||
ImageBuffer::<F>::allocate(&<F as ImageFormat>::P::BLACK, tile_size, tile_size);
|
||||
for idx_tile in 0..num_allsky_tiles_per_tile {
|
||||
let (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);
|
||||
for idx_tile in 0..num_allsky_tiles_per_tile {
|
||||
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 sx = (src_idx % 27) * allsky_tile_size;
|
||||
let sy = (src_idx / 27) * allsky_tile_size;
|
||||
let s = ImageBufferView {
|
||||
x: sx as i32,
|
||||
y: sy as i32,
|
||||
w: allsky_tile_size as i32,
|
||||
h: allsky_tile_size as i32,
|
||||
};
|
||||
let d = ImageBufferView {
|
||||
x: dx as i32,
|
||||
y: dy as i32,
|
||||
w: allsky_tile_size as i32,
|
||||
h: allsky_tile_size as i32,
|
||||
};
|
||||
let sx = (src_idx % 27) * allsky_tile_size;
|
||||
let sy = (src_idx / 27) * allsky_tile_size;
|
||||
let s = ImageBufferView {
|
||||
x: sx as i32,
|
||||
y: sy as i32,
|
||||
w: allsky_tile_size as i32,
|
||||
h: allsky_tile_size as i32,
|
||||
};
|
||||
let d = ImageBufferView {
|
||||
x: dx as i32,
|
||||
y: dy as i32,
|
||||
w: allsky_tile_size as i32,
|
||||
h: allsky_tile_size as i32,
|
||||
};
|
||||
|
||||
base_tile.tex_sub(&allsky, &s, &d);
|
||||
base_tile.tex_sub(&allsky, &s, &d);
|
||||
|
||||
src_idx += 1;
|
||||
}
|
||||
src_idx += 1;
|
||||
}
|
||||
|
||||
base_tile
|
||||
});
|
||||
base_tile
|
||||
});
|
||||
|
||||
Ok(tiles)
|
||||
}
|
||||
|
||||
fn handle_allsky_fits<F: ImageFormat>(
|
||||
allsky_data: &[<<F as ImageFormat>::P as Pixel>::Item],
|
||||
|
||||
tile_size: i32,
|
||||
texture_size: i32,
|
||||
) -> Result<impl Iterator<Item=ImageBuffer<F>>, JsValue> {
|
||||
) -> 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;
|
||||
// The fits image layout stores rows in reverse
|
||||
let reversed_rows_data = allsky_data
|
||||
.chunks(width_allsky_px as usize)
|
||||
.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 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 mut new_image_data = Vec::with_capacity(tile_size as usize);
|
||||
for c in image.get_data().chunks((tile_size * tile_size) as usize) {
|
||||
new_image_data.extend(c.chunks(tile_size as usize).rev().flatten());
|
||||
}
|
||||
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();
|
||||
|
||||
ImageBuffer::<F>::new(new_image_data, tile_size, tile_size)
|
||||
});
|
||||
ImageBuffer::<F>::new(new_image_data, tile_size, tile_size)
|
||||
},
|
||||
);
|
||||
|
||||
Ok(allsky_tiles_iter)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::math::lonlat::LonLatT;
|
||||
use crate::math::PI;
|
||||
use crate::math::{self, lonlat::LonLat};
|
||||
|
||||
use cgmath::Vector3;
|
||||
use cgmath::{Vector4};
|
||||
use moclib::{
|
||||
moc::range::{CellSelection, RangeMOC},
|
||||
qty::Hpx,
|
||||
@@ -81,8 +81,8 @@ impl HEALPixCoverage {
|
||||
HEALPixCoverage(moc)
|
||||
}
|
||||
|
||||
pub fn contains_coo(&self, coo: &Vector3<f64>) -> bool {
|
||||
let (lon, lat) = math::lonlat::xyz_to_radec(coo);
|
||||
pub fn contains_coo(&self, coo: &Vector4<f64>) -> bool {
|
||||
let (lon, lat) = math::lonlat::xyzw_to_radec(coo);
|
||||
self.0.is_in(lon.to_radians(), lat.to_radians())
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::camera::CameraViewPort;
|
||||
use crate::math::angle::ToAngle;
|
||||
use crate::math::projection::ProjectionType;
|
||||
use crate::time::{DeltaTime, Time};
|
||||
|
||||
/// State for inertia
|
||||
pub struct Inertia {
|
||||
// Initial angular distance
|
||||
@@ -13,17 +14,15 @@ pub struct Inertia {
|
||||
axis: Vector3<f64>,
|
||||
// The time when the inertia begins
|
||||
time_start: Time,
|
||||
north_up: bool,
|
||||
}
|
||||
|
||||
impl Inertia {
|
||||
pub fn new(ampl: f64, axis: Vector3<f64>, north_up: bool) -> Self {
|
||||
pub fn new(ampl: f64, axis: Vector3<f64>) -> Self {
|
||||
Inertia {
|
||||
time_start: Time::now(),
|
||||
ampl,
|
||||
ampl: ampl,
|
||||
speed: ampl,
|
||||
axis,
|
||||
north_up
|
||||
axis: axis,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +35,7 @@ impl Inertia {
|
||||
// where:
|
||||
// * k is the stiffness of the ressort
|
||||
// * m is its mass
|
||||
let w0 = 7.0;
|
||||
let w0 = 10.0;
|
||||
// The angular distance goes from d0 to 0.0
|
||||
//self.speed = self.ampl * ((-w0 * t).exp());
|
||||
// The angular distance goes from d0 to 0.0
|
||||
@@ -44,11 +43,7 @@ impl Inertia {
|
||||
/*let alpha = 1_f32 + (0_f32 - 1_f32) * (10_f32 * t + 1_f32) * (-10_f32 * t).exp();
|
||||
let alpha = alpha * alpha;
|
||||
let fov = start_fov * (1_f32 - alpha) + goal_fov * alpha;*/
|
||||
camera.apply_axis_rotation(&self.axis, self.speed.to_angle(), proj);
|
||||
|
||||
if self.north_up {
|
||||
camera.set_position_angle(0.0.to_angle(), proj);
|
||||
}
|
||||
camera.apply_rotation(&self.axis, self.speed.to_angle(), proj)
|
||||
}
|
||||
|
||||
pub fn get_start_ampl(&self) -> f64 {
|
||||
|
||||
@@ -130,7 +130,7 @@ use al_core::Colormap;
|
||||
use al_core::WebGlContext;
|
||||
|
||||
use app::App;
|
||||
use cgmath::{Vector2, Vector3};
|
||||
use cgmath::{Vector2, Vector4};
|
||||
|
||||
use crate::healpix::cell::HEALPixCell;
|
||||
use math::angle::ArcDeg;
|
||||
@@ -469,12 +469,6 @@ impl WebClient {
|
||||
Ok(fov)
|
||||
}
|
||||
|
||||
/// Get the max aperture of a projection (in degrees)
|
||||
#[wasm_bindgen(js_name = atZoomBoundaries)]
|
||||
pub fn get_max_aperture(&self) -> bool {
|
||||
self.app.camera.at_zoom_boundaries(&self.app.projection)
|
||||
}
|
||||
|
||||
/// Set the field of view
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -482,12 +476,13 @@ impl WebClient {
|
||||
/// * `fov` - The field of view in degrees
|
||||
#[wasm_bindgen(js_name = setFieldOfView)]
|
||||
pub fn set_fov(&mut self, fov: f64) -> Result<(), JsValue> {
|
||||
self.app.set_fov(fov.to_radians());
|
||||
let fov = ArcDeg(fov).into();
|
||||
|
||||
self.app.set_fov(fov);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enable/Disable inertia effect after panning and releasing the mouse
|
||||
#[wasm_bindgen(js_name = setInertia)]
|
||||
pub fn set_inertia(&mut self, inertia: bool) -> Result<(), JsValue> {
|
||||
self.app.set_inertia(inertia);
|
||||
@@ -495,57 +490,23 @@ impl WebClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set a range of FoVs that contrains the zooming in that range
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `min_fov` - The minimum field of view value in degrees
|
||||
/// * `max_fov` - The maximum field of view value in degrees
|
||||
#[wasm_bindgen(js_name = setFoVRange)]
|
||||
pub fn set_fov_range(
|
||||
&mut self,
|
||||
min_fov: Option<f64>,
|
||||
max_fov: Option<f64>,
|
||||
) -> Result<(), JsValue> {
|
||||
self.app.set_fov_range(min_fov, max_fov);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the FoV range in degrees
|
||||
#[wasm_bindgen(js_name = getFoVRange)]
|
||||
pub fn get_fov_range(&self) -> Box<[f64]> {
|
||||
Box::new([
|
||||
self.app
|
||||
.camera
|
||||
.min_fov
|
||||
.map(|v| v.to_degrees())
|
||||
.unwrap_or(-1.0),
|
||||
self.app
|
||||
.camera
|
||||
.max_fov
|
||||
.map(|v| v.to_degrees())
|
||||
.unwrap_or(-1.0),
|
||||
])
|
||||
}
|
||||
|
||||
/// Set the absolute orientation of the view
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `theta` - The rotation angle in degrees
|
||||
#[wasm_bindgen(js_name = setRotation)]
|
||||
pub fn set_rotation(&mut self, theta: f64) -> Result<(), JsValue> {
|
||||
#[wasm_bindgen(js_name = setViewCenter2NorthPoleAngle)]
|
||||
pub fn set_view_center_pos_angle(&mut self, theta: f64) -> Result<(), JsValue> {
|
||||
let theta = ArcDeg(theta);
|
||||
self.app.set_position_angle(theta);
|
||||
self.app.set_view_center_pos_angle(theta);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the absolute orientation angle of the view
|
||||
#[wasm_bindgen(js_name = getRotation)]
|
||||
pub fn get_rotation(&mut self) -> Result<f64, JsValue> {
|
||||
let phi = self.app.get_position_angle();
|
||||
#[wasm_bindgen(js_name = getViewCenter2NorthPoleAngle)]
|
||||
pub fn get_north_shift_angle(&mut self) -> Result<f64, JsValue> {
|
||||
let phi = self.app.get_north_shift_angle();
|
||||
Ok(phi.to_degrees())
|
||||
}
|
||||
|
||||
@@ -576,20 +537,14 @@ impl WebClient {
|
||||
self.app.get_max_fov().to_degrees()
|
||||
}
|
||||
|
||||
/// Get the zoom factor of the view
|
||||
/// Get the clip zoom factor of the view
|
||||
///
|
||||
/// This factor is deduced from the field of view angle.
|
||||
/// It is a constant which when multiplied to the screen coordinates
|
||||
/// gives the coordinates in clipping space.
|
||||
#[wasm_bindgen(js_name = getZoomFactor)]
|
||||
pub fn get_zoom_factor(&self) -> Result<f64, JsValue> {
|
||||
Ok(self.app.get_zoom_factor())
|
||||
}
|
||||
|
||||
/// 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))
|
||||
#[wasm_bindgen(js_name = getClipZoomFactor)]
|
||||
pub fn get_clip_zoom_factor(&self) -> Result<f64, JsValue> {
|
||||
Ok(self.app.get_clip_zoom_factor())
|
||||
}
|
||||
|
||||
/// Set the center of the view in ICRS coosys
|
||||
@@ -610,11 +565,6 @@ impl WebClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = lockNorthUp)]
|
||||
pub fn lock_north_up(&mut self) {
|
||||
self.app.lock_north_up();
|
||||
}
|
||||
|
||||
/// Get the center of the view
|
||||
///
|
||||
/// This returns a javascript array of size 2.
|
||||
@@ -1109,12 +1059,12 @@ impl WebClient {
|
||||
let vertex_it = ra_deg
|
||||
.iter()
|
||||
.zip(dec_deg.iter())
|
||||
.map(|(ra, dec)| -> Vector3<f64> {
|
||||
.map(|(ra, dec)| -> Vector4<f64> {
|
||||
let lonlat = LonLatT(ra.to_radians().to_angle(), dec.to_radians().to_angle());
|
||||
lonlat.vector()
|
||||
});
|
||||
|
||||
let v_in = &Vector3::new(1.0, 0.0, 0.0);
|
||||
let v_in = &Vector4::new(1.0, 0.0, 0.0, 1.0);
|
||||
|
||||
let mut moc = HEALPixCoverage::from_3d_coos(pixel_d as u8 - 1, vertex_it, &v_in);
|
||||
if moc.sky_fraction() > 0.5 {
|
||||
|
||||
@@ -145,6 +145,15 @@ where
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ArcSec<T: BaseFloat>(pub T);
|
||||
|
||||
impl<T> ArcSec<T>
|
||||
where
|
||||
T: BaseFloat,
|
||||
{
|
||||
fn _truncate(&mut self) {
|
||||
*self = Self((*self).trunc());
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Rad<T>> for ArcSec<T>
|
||||
where
|
||||
T: BaseFloat,
|
||||
@@ -400,7 +409,7 @@ where
|
||||
S::max_value().to_angle()
|
||||
}
|
||||
|
||||
pub const fn to_radians(&self) -> S {
|
||||
pub fn to_radians(&self) -> S {
|
||||
self.rad
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::math::TWICE_PI;
|
||||
use crate::Abort;
|
||||
use cgmath::{BaseFloat, Matrix3, Rad, Vector3};
|
||||
use cgmath::{BaseFloat, Matrix3, Rad, Vector3, Vector4};
|
||||
|
||||
pub trait LonLat<S: BaseFloat> {
|
||||
fn lon(&self) -> Angle<S>;
|
||||
@@ -116,6 +116,43 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> LonLat<S> for Vector4<S>
|
||||
where
|
||||
S: BaseFloat,
|
||||
{
|
||||
#[inline]
|
||||
fn lon(&self) -> Angle<S> {
|
||||
let rad = Rad(self.x.atan2(self.z));
|
||||
Angle::new(rad)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lat(&self) -> Angle<S> {
|
||||
let rad = Rad(self.y.atan2((self.x * self.x + self.z * self.z).sqrt()));
|
||||
Angle::new(rad)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lonlat(&self) -> LonLatT<S> {
|
||||
let lon = self.x.atan2(self.z);
|
||||
let lat = self.y.atan2((self.x * self.x + self.z * self.z).sqrt());
|
||||
|
||||
LonLatT::new(lon.to_angle(), lat.to_angle())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_lonlat(lonlat: &LonLatT<S>) -> Self {
|
||||
let theta = lonlat.lon();
|
||||
let delta = lonlat.lat();
|
||||
Vector4::<S>::new(
|
||||
delta.cos() * theta.sin(),
|
||||
delta.sin(),
|
||||
delta.cos() * theta.cos(),
|
||||
S::one(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ang_between_lonlat<S: BaseFloat>(lonlat1: LonLatT<S>, lonlat2: LonLatT<S>) -> Angle<S> {
|
||||
let abs_diff_lon = (lonlat1.lon() - lonlat2.lon()).abs();
|
||||
@@ -132,6 +169,14 @@ pub fn xyz_to_radec<S: BaseFloat>(v: &Vector3<S>) -> (Angle<S>, Angle<S>) {
|
||||
(lon, lat)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn xyzw_to_radec<S: BaseFloat>(v: &Vector4<S>) -> (Angle<S>, Angle<S>) {
|
||||
let lon = (v.x.atan2(v.z)).to_angle();
|
||||
let lat = (v.y.atan2((v.x * v.x + v.z * v.z).sqrt())).to_angle();
|
||||
|
||||
(lon, lat)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn radec_to_xyz<S: BaseFloat>(theta: Angle<S>, delta: Angle<S>) -> Vector3<S> {
|
||||
let (ds, dc) = delta.to_radians().sin_cos();
|
||||
@@ -140,6 +185,13 @@ pub fn radec_to_xyz<S: BaseFloat>(theta: Angle<S>, delta: Angle<S>) -> Vector3<S
|
||||
Vector3::<S>::new(dc * ts, ds, dc * tc)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn radec_to_xyzw<S: BaseFloat>(theta: Angle<S>, delta: Angle<S>) -> Vector4<S> {
|
||||
let xyz = radec_to_xyz(theta, delta);
|
||||
|
||||
Vector4::<S>::new(xyz.x, xyz.y, xyz.z, S::one())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn radec_to_basis<S: BaseFloat>(theta: Angle<S>, delta: Angle<S>) -> Matrix3<S> {
|
||||
Matrix3::<S>::new(
|
||||
@@ -169,8 +221,8 @@ pub fn proj(
|
||||
projection: &ProjectionType,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYNDC<f64>> {
|
||||
let xyz = lonlat.vector();
|
||||
projection.model_to_normalized_device_space(&xyz, camera)
|
||||
let xyzw = lonlat.vector();
|
||||
projection.model_to_normalized_device_space(&xyzw, camera)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -190,8 +242,8 @@ pub fn proj_to_screen(
|
||||
projection: &ProjectionType,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYScreen<f64>> {
|
||||
let xyz = lonlat.vector();
|
||||
projection.model_to_screen_space(&xyz, camera)
|
||||
let xyzw = lonlat.vector();
|
||||
projection.model_to_screen_space(&xyzw, camera)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -204,10 +256,3 @@ pub fn unproj_from_screen(
|
||||
.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();
|
||||
|
||||
crate::math::vector::angle3(&v1, &v).abs() < theta && crate::math::vector::angle3(&v, &v2).abs() < theta
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use cgmath::{Vector2, Vector3};
|
||||
use cgmath::{Vector2, Vector3, Vector4};
|
||||
|
||||
pub type XYScreen<S> = Vector2<S>;
|
||||
pub type XYNDC<S> = Vector2<S>;
|
||||
pub type XYClip<S> = Vector2<S>;
|
||||
pub type XYZWorld<S> = Vector3<S>;
|
||||
pub type XYZModel<S> = Vector3<S>;
|
||||
pub type XYZWWorld<S> = Vector4<S>;
|
||||
pub type XYZWModel<S> = Vector4<S>;
|
||||
|
||||
pub enum CooSpace {
|
||||
Screen,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
use crate::camera::CameraViewPort;
|
||||
use crate::domain::sdf::ProjDefType;
|
||||
|
||||
use coo_space::XYZModel;
|
||||
use coo_space::XYZWModel;
|
||||
//use crate::num_traits::FloatConst;
|
||||
use crate::math::PI;
|
||||
|
||||
@@ -21,8 +21,8 @@ pub mod domain;
|
||||
|
||||
use crate::math::angle::ToAngle;
|
||||
|
||||
use crate::math::angle::Angle;
|
||||
use domain::{basic, full::FullScreen};
|
||||
use crate::math::angle::Angle;
|
||||
/* S <-> NDC space conversion methods */
|
||||
pub fn screen_to_ndc_space(
|
||||
pos_screen_space: &XYScreen<f64>,
|
||||
@@ -61,7 +61,7 @@ pub fn ndc_to_screen_space(
|
||||
/* NDC <-> CLIP space conversion methods */
|
||||
pub fn clip_to_ndc_space(pos_clip_space: &XYClip<f64>, camera: &CameraViewPort) -> XYNDC<f64> {
|
||||
let ndc_to_clip = camera.get_ndc_to_clip();
|
||||
let clip_zoom_factor = camera.get_zoom_factor();
|
||||
let clip_zoom_factor = camera.get_clip_zoom_factor();
|
||||
|
||||
Vector2::new(
|
||||
pos_clip_space.x / (ndc_to_clip.x * clip_zoom_factor),
|
||||
@@ -74,7 +74,7 @@ pub fn ndc_to_clip_space(
|
||||
camera: &CameraViewPort,
|
||||
) -> XYClip<f64> {
|
||||
let ndc_to_clip = camera.get_ndc_to_clip();
|
||||
let clip_zoom_factor = camera.get_zoom_factor();
|
||||
let clip_zoom_factor = camera.get_clip_zoom_factor();
|
||||
|
||||
Vector2::new(
|
||||
pos_normalized_device.x * ndc_to_clip.x * clip_zoom_factor,
|
||||
@@ -101,7 +101,7 @@ pub fn screen_to_clip_space(
|
||||
|
||||
use al_api::coo_system::CooSystem;
|
||||
|
||||
use crate::coo_space::{XYClip, XYZWorld};
|
||||
use crate::coo_space::{XYClip, XYZWWorld};
|
||||
|
||||
pub enum ProjectionType {
|
||||
// Zenithal projections
|
||||
@@ -174,7 +174,7 @@ impl ProjectionType {
|
||||
&self,
|
||||
pos_screen_space: &XYScreen<f64>,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYZWorld<f64>> {
|
||||
) -> Option<XYZWWorld<f64>> {
|
||||
// Change the screen position according to the dpi
|
||||
//let dpi = camera.get_dpi();
|
||||
let pos_screen_space = *pos_screen_space;
|
||||
@@ -196,7 +196,7 @@ impl ProjectionType {
|
||||
&self,
|
||||
pos_screen_space: &XYScreen<f64>,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYZModel<f64>> {
|
||||
) -> Option<XYZWModel<f64>> {
|
||||
self.screen_to_world_space(pos_screen_space, camera)
|
||||
.map(|world_pos| camera.get_w2m() * world_pos)
|
||||
}
|
||||
@@ -205,14 +205,14 @@ impl ProjectionType {
|
||||
&self,
|
||||
ndc_pos: &XYNDC<f64>,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYZModel<f64>> {
|
||||
) -> Option<XYZWModel<f64>> {
|
||||
self.normalized_device_to_world_space(ndc_pos, camera)
|
||||
.map(|world_pos| camera.get_w2m() * world_pos)
|
||||
}
|
||||
|
||||
pub fn model_to_screen_space(
|
||||
&self,
|
||||
pos_model_space: &XYZModel<f64>,
|
||||
pos_model_space: &XYZWModel<f64>,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYScreen<f64>> {
|
||||
let m2w = camera.get_m2w();
|
||||
@@ -222,7 +222,7 @@ impl ProjectionType {
|
||||
|
||||
pub fn icrs_celestial_to_screen_space(
|
||||
&self,
|
||||
celestial_pos: &XYZModel<f64>,
|
||||
celestial_pos: &XYZWModel<f64>,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYScreen<f64>> {
|
||||
self.icrs_celestial_to_normalized_device_space(celestial_pos, camera)
|
||||
@@ -231,7 +231,7 @@ impl ProjectionType {
|
||||
|
||||
pub fn icrs_celestial_to_normalized_device_space(
|
||||
&self,
|
||||
celestial_pos: &XYZModel<f64>,
|
||||
celestial_pos: &XYZWModel<f64>,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYNDC<f64>> {
|
||||
let view_coosys = camera.get_coo_system();
|
||||
@@ -244,7 +244,7 @@ impl ProjectionType {
|
||||
|
||||
pub fn model_to_normalized_device_space(
|
||||
&self,
|
||||
pos_model_space: &XYZModel<f64>,
|
||||
pos_model_space: &XYZWModel<f64>,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYNDC<f64>> {
|
||||
let m2w = camera.get_m2w();
|
||||
@@ -254,7 +254,7 @@ impl ProjectionType {
|
||||
|
||||
pub fn model_to_clip_space(
|
||||
&self,
|
||||
pos_model_space: &XYZModel<f64>,
|
||||
pos_model_space: &XYZWModel<f64>,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYClip<f64>> {
|
||||
let m2w = camera.get_m2w();
|
||||
@@ -272,7 +272,7 @@ impl ProjectionType {
|
||||
/// * `y` - Y mouse position in homogenous screen space (between [-1, 1])
|
||||
pub fn world_to_normalized_device_space(
|
||||
&self,
|
||||
pos_world_space: &XYZWorld<f64>,
|
||||
pos_world_space: &XYZWWorld<f64>,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYNDC<f64>> {
|
||||
self.world_to_clip_space(pos_world_space)
|
||||
@@ -283,30 +283,39 @@ impl ProjectionType {
|
||||
&self,
|
||||
ndc_pos: &XYNDC<f64>,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYZWorld<f64>> {
|
||||
) -> Option<XYZWWorld<f64>> {
|
||||
let clip_pos = ndc_to_clip_space(ndc_pos, camera);
|
||||
self.clip_to_world_space(&clip_pos)
|
||||
}
|
||||
|
||||
pub fn world_to_screen_space(
|
||||
&self,
|
||||
pos_world_space: &XYZWorld<f64>,
|
||||
pos_world_space: &XYZWWorld<f64>,
|
||||
camera: &CameraViewPort,
|
||||
) -> Option<XYScreen<f64>> {
|
||||
self.world_to_normalized_device_space(pos_world_space, camera)
|
||||
.map(|pos_normalized_device| ndc_to_screen_space(&pos_normalized_device, camera))
|
||||
}
|
||||
|
||||
/*pub(crate) fn is_allsky(&self) -> bool {
|
||||
match self {
|
||||
ProjectionType::Sin(_) | ProjectionType::Tan(_) => false,
|
||||
//| ProjectionType::Feye(_)
|
||||
//| ProjectionType::Ncp(_) => false,
|
||||
_ => true,
|
||||
}
|
||||
}*/
|
||||
|
||||
pub const fn bounds_size_ratio(&self) -> f64 {
|
||||
match self {
|
||||
// Zenithal projections
|
||||
/* TAN, Gnomonic projection */
|
||||
ProjectionType::Tan(_) => 1.0,
|
||||
/* STG, Stereographic projection */
|
||||
/* STG, Stereographic projection */
|
||||
ProjectionType::Stg(_) => 1.0,
|
||||
/* SIN, Orthographic */
|
||||
/* SIN, Orthographic */
|
||||
ProjectionType::Sin(_) => 1.0,
|
||||
/* ZEA, Equal-area */
|
||||
/* ZEA, Equal-area */
|
||||
ProjectionType::Zea(_) => 1.0,
|
||||
/* FEYE, Fish-eyes */
|
||||
//ProjectionType::Feye(_) => 1.0,
|
||||
@@ -318,6 +327,7 @@ impl ProjectionType {
|
||||
//ProjectionType::Arc(_) => 1.0,
|
||||
/* NCP, */
|
||||
//ProjectionType::Ncp(_) => 1.0,
|
||||
|
||||
// Pseudo-cylindrical projections
|
||||
/* AIT, Aitoff */
|
||||
ProjectionType::Ait(_) => 2.0,
|
||||
@@ -327,6 +337,7 @@ impl ProjectionType {
|
||||
//ProjectionType::Par(_) => 2.0,
|
||||
// SFL, */
|
||||
//ProjectionType::Sfl(_) => 2.0,
|
||||
|
||||
// Cylindrical projections
|
||||
// MER, Mercator */
|
||||
ProjectionType::Mer(_) => 1.0,
|
||||
@@ -336,9 +347,11 @@ impl ProjectionType {
|
||||
//ProjectionType::Cea(_) => 1.0,
|
||||
// CYP, */
|
||||
//ProjectionType::Cyp(_) => 1.0,
|
||||
|
||||
// Conic projections
|
||||
// COD, */
|
||||
//ProjectionType::Cod(_) => 1.0,
|
||||
|
||||
// HEALPix hybrid projection
|
||||
//ProjectionType::Hpx(_) => 2.0,
|
||||
}
|
||||
@@ -354,7 +367,7 @@ impl ProjectionType {
|
||||
/* SIN, Orthographic */
|
||||
ProjectionType::Sin(_) => 180.0_f64.to_radians().to_angle(),
|
||||
/* ZEA, Equal-area */
|
||||
ProjectionType::Zea(_) => 359.9_f64.to_radians().to_angle(),
|
||||
ProjectionType::Zea(_) => 360.0_f64.to_radians().to_angle(),
|
||||
/* FEYE, Fish-eyes */
|
||||
//ProjectionType::Feye(_) => 190.0,
|
||||
/* AIR, */
|
||||
@@ -395,7 +408,7 @@ impl ProjectionType {
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn get_area(&self) -> &ProjDefType {
|
||||
pub fn get_area(&self) -> &ProjDefType {
|
||||
match self {
|
||||
// Zenithal projections
|
||||
/* TAN, Gnomonic projection */
|
||||
@@ -501,7 +514,7 @@ impl ProjectionType {
|
||||
|
||||
impl Projection for ProjectionType {
|
||||
/// Deprojection
|
||||
fn clip_to_world_space(&self, xy: &XYClip<f64>) -> Option<XYZWorld<f64>> {
|
||||
fn clip_to_world_space(&self, xy: &XYClip<f64>) -> Option<XYZWWorld<f64>> {
|
||||
match self {
|
||||
// Zenithal projections
|
||||
/* TAN, Gnomonic projection */
|
||||
@@ -545,11 +558,11 @@ impl Projection for ProjectionType {
|
||||
|
||||
// Conic projections
|
||||
// COD, */
|
||||
/*ProjectionType::Cod(cod) => cod.clip_to_world_space(xy).map(|xyz| {
|
||||
/*ProjectionType::Cod(cod) => cod.clip_to_world_space(xy).map(|xyzw| {
|
||||
let rot = Rotation::from_sky_position(
|
||||
&LonLatT::new(0.0_f64.to_angle(), (HALF_PI * 0.5).to_angle()).vector(),
|
||||
);
|
||||
rot.inv_rotate(&xyz)
|
||||
rot.inv_rotate(&xyzw)
|
||||
}),*/
|
||||
// HEALPix hybrid projection
|
||||
//ProjectionType::Hpx(hpx) => hpx.clip_to_world_space(xy),
|
||||
@@ -557,47 +570,47 @@ impl Projection for ProjectionType {
|
||||
}
|
||||
|
||||
// Projection
|
||||
fn world_to_clip_space(&self, xyz: &XYZWorld<f64>) -> Option<XYClip<f64>> {
|
||||
fn world_to_clip_space(&self, xyzw: &XYZWWorld<f64>) -> Option<XYClip<f64>> {
|
||||
match self {
|
||||
// Zenithal projections
|
||||
/* TAN, Gnomonic projection */
|
||||
ProjectionType::Tan(tan) => tan.world_to_clip_space(xyz),
|
||||
ProjectionType::Tan(tan) => tan.world_to_clip_space(xyzw),
|
||||
/* STG, Stereographic projection */
|
||||
ProjectionType::Stg(stg) => stg.world_to_clip_space(xyz),
|
||||
ProjectionType::Stg(stg) => stg.world_to_clip_space(xyzw),
|
||||
/* SIN, Orthographic */
|
||||
ProjectionType::Sin(sin) => sin.world_to_clip_space(xyz),
|
||||
ProjectionType::Sin(sin) => sin.world_to_clip_space(xyzw),
|
||||
/* ZEA, Equal-area */
|
||||
ProjectionType::Zea(zea) => zea.world_to_clip_space(xyz),
|
||||
ProjectionType::Zea(zea) => zea.world_to_clip_space(xyzw),
|
||||
/* FEYE, Fish-eyes */
|
||||
//ProjectionType::Feye(feye) => feye.world_to_clip_space(xyz),
|
||||
//ProjectionType::Feye(feye) => feye.world_to_clip_space(xyzw),
|
||||
/* AIR, */
|
||||
//ProjectionType::Air(air) => air.world_to_clip_space(xyz),
|
||||
//ProjectionType::Air(air) => air.world_to_clip_space(xyzw),
|
||||
//AZP: {fov: 180},
|
||||
//Azp(mapproj::zenithal::azp::Azp),
|
||||
/* ARC, */
|
||||
//ProjectionType::Arc(arc) => arc.world_to_clip_space(xyz),
|
||||
//ProjectionType::Arc(arc) => arc.world_to_clip_space(xyzw),
|
||||
/* NCP, */
|
||||
//ProjectionType::Ncp(ncp) => ncp.world_to_clip_space(xyz),
|
||||
//ProjectionType::Ncp(ncp) => ncp.world_to_clip_space(xyzw),
|
||||
|
||||
// Pseudo-cylindrical projections
|
||||
/* AIT, Aitoff */
|
||||
ProjectionType::Ait(ait) => ait.world_to_clip_space(xyz),
|
||||
ProjectionType::Ait(ait) => ait.world_to_clip_space(xyzw),
|
||||
// MOL, Mollweide */
|
||||
ProjectionType::Mol(mol) => mol.world_to_clip_space(xyz),
|
||||
ProjectionType::Mol(mol) => mol.world_to_clip_space(xyzw),
|
||||
// PAR, */
|
||||
//ProjectionType::Par(par) => par.world_to_clip_space(xyz),
|
||||
//ProjectionType::Par(par) => par.world_to_clip_space(xyzw),
|
||||
// SFL, */
|
||||
//ProjectionType::Sfl(sfl) => sfl.world_to_clip_space(xyz),
|
||||
//ProjectionType::Sfl(sfl) => sfl.world_to_clip_space(xyzw),
|
||||
|
||||
// Cylindrical projections
|
||||
// MER, Mercator */
|
||||
ProjectionType::Mer(mer) => mer.world_to_clip_space(xyz),
|
||||
ProjectionType::Mer(mer) => mer.world_to_clip_space(xyzw),
|
||||
// CAR, */
|
||||
//ProjectionType::Car(car) => car.world_to_clip_space(xyz),
|
||||
//ProjectionType::Car(car) => car.world_to_clip_space(xyzw),
|
||||
// CEA, */
|
||||
//ProjectionType::Cea(cea) => cea.world_to_clip_space(xyz),
|
||||
//ProjectionType::Cea(cea) => cea.world_to_clip_space(xyzw),
|
||||
// CYP, */
|
||||
//ProjectionType::Cyp(cyp) => cyp.world_to_clip_space(xyz),
|
||||
//ProjectionType::Cyp(cyp) => cyp.world_to_clip_space(xyzw),
|
||||
// Conic projections
|
||||
// COD, */
|
||||
/*ProjectionType::Cod(cod) => {
|
||||
@@ -605,10 +618,10 @@ impl Projection for ProjectionType {
|
||||
let rot = Rotation::from_sky_position(
|
||||
&LonLatT::new(0.0_f64.to_angle(), (HALF_PI * 0.5).to_angle()).vector(),
|
||||
);
|
||||
cod.world_to_clip_space(&rot.rotate(&xyz))
|
||||
cod.world_to_clip_space(&rot.rotate(&xyzw))
|
||||
}*/
|
||||
// HEALPix hybrid projection
|
||||
//ProjectionType::Hpx(hpx) => hpx.world_to_clip_space(xyz),
|
||||
//ProjectionType::Hpx(hpx) => hpx.world_to_clip_space(xyzw),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -642,7 +655,7 @@ impl UniformType for ProjectionType {
|
||||
}
|
||||
}
|
||||
|
||||
use cgmath::Vector3;
|
||||
use cgmath::Vector4;
|
||||
|
||||
use mapproj::CanonicalProjection;
|
||||
pub trait Projection {
|
||||
@@ -651,24 +664,24 @@ pub trait Projection {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * ``pos_clip_space`` - The position in the clipping space (orthonorlized space)
|
||||
fn clip_to_world_space(&self, xy_clip: &XYClip<f64>) -> Option<XYZWorld<f64>>;
|
||||
fn clip_to_world_space(&self, xy_clip: &XYClip<f64>) -> Option<XYZWWorld<f64>>;
|
||||
/// World to the clipping space deprojection
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * ``pos_world_space`` - The position in the world space
|
||||
fn world_to_clip_space(&self, pos_world_space: &XYZWorld<f64>) -> Option<XYClip<f64>>;
|
||||
fn world_to_clip_space(&self, pos_world_space: &XYZWWorld<f64>) -> Option<XYClip<f64>>;
|
||||
|
||||
/// (`alpha_p`, `delta_p`) in the WCS II paper from Mark Calabretta.
|
||||
#[inline]
|
||||
fn north_pole_world_space(&self) -> XYZWorld<f64> {
|
||||
fn north_pole_world_space(&self) -> XYZWWorld<f64> {
|
||||
// This is always defined
|
||||
self.clip_to_world_space(&XYClip::new(0.0, 1.0 - 1e-5))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn south_pole_world_space(&self) -> XYZWorld<f64> {
|
||||
fn south_pole_world_space(&self) -> XYZWWorld<f64> {
|
||||
// This is always defined
|
||||
self.clip_to_world_space(&XYClip::new(0.0, -1.0 + 1e-5))
|
||||
.unwrap()
|
||||
@@ -691,7 +704,7 @@ where
|
||||
/// # Arguments
|
||||
///
|
||||
/// * ``pos_clip_space`` - The position in the clipping space (orthonorlized space)
|
||||
fn clip_to_world_space(&self, xy_clip: &XYClip<f64>) -> Option<XYZWorld<f64>> {
|
||||
fn clip_to_world_space(&self, xy_clip: &XYClip<f64>) -> Option<XYZWWorld<f64>> {
|
||||
let proj_bounds = self.bounds();
|
||||
// Scale the xy_clip space so that it maps the proj definition domain of mapproj
|
||||
let xy_mapproj = {
|
||||
@@ -716,7 +729,7 @@ where
|
||||
// Xmpp <-> Zal
|
||||
// -Ympp <-> Xal
|
||||
// Zmpp <-> Yal
|
||||
Vector3::new(-xyz_mapproj.y(), xyz_mapproj.z(), xyz_mapproj.x())
|
||||
Vector4::new(-xyz_mapproj.y(), xyz_mapproj.z(), xyz_mapproj.x(), 1.0)
|
||||
})
|
||||
}
|
||||
/// World to the clipping space deprojection
|
||||
@@ -724,7 +737,7 @@ where
|
||||
/// # Arguments
|
||||
///
|
||||
/// * ``pos_world_space`` - The position in the world space
|
||||
fn world_to_clip_space(&self, pos_world_space: &XYZWorld<f64>) -> Option<XYClip<f64>> {
|
||||
fn world_to_clip_space(&self, pos_world_space: &XYZWWorld<f64>) -> Option<XYClip<f64>> {
|
||||
// Xmpp <-> Zal
|
||||
// -Ympp <-> Xal
|
||||
// Zmpp <-> Yal
|
||||
@@ -779,7 +792,7 @@ mod tests {
|
||||
2.0 * ((xy.y as f64) / (h as f64)) - 1.0,
|
||||
);
|
||||
let rgb = if let Some(pos) = projection.clip_to_world_space(&clip_xy) {
|
||||
let pos = pos.normalize();
|
||||
let pos = pos.truncate().normalize();
|
||||
Rgb([
|
||||
((pos.x * 0.5 + 0.5) * 256.0) as u8,
|
||||
((pos.y * 0.5 + 0.5) * 256.0) as u8,
|
||||
|
||||
@@ -2,31 +2,29 @@ use crate::math;
|
||||
use crate::math::angle::ToAngle;
|
||||
use cgmath::{BaseFloat, InnerSpace};
|
||||
use cgmath::{Euler, Quaternion};
|
||||
use cgmath::Vector3;
|
||||
use cgmath::{Vector3, Vector4};
|
||||
|
||||
#[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};
|
||||
impl<S> From<&Matrix3<S>> for Rotation<S>
|
||||
use cgmath::{Matrix3, Matrix4};
|
||||
impl<S> From<&Matrix4<S>> for Rotation<S>
|
||||
where
|
||||
S: BaseFloat,
|
||||
{
|
||||
fn from(m: &Matrix3<S>) -> Self {
|
||||
/*let m: [[S; 3]; 3] = (*m).into();
|
||||
fn from(m: &Matrix4<S>) -> Self {
|
||||
let m: [[S; 4]; 4] = (*m).into();
|
||||
|
||||
let t = Matrix3::new(
|
||||
m[0][0], m[0][1], m[0][2],
|
||||
m[1][0], m[1][1], m[1][2],
|
||||
m[2][0], m[2][1], m[2][2],
|
||||
);*/
|
||||
m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2],
|
||||
);
|
||||
|
||||
Rotation((*m).into())
|
||||
Rotation(t.into())
|
||||
}
|
||||
}
|
||||
impl<S> From<&Rotation<S>> for Matrix3<S>
|
||||
impl<S> From<&Rotation<S>> for Matrix4<S>
|
||||
where
|
||||
S: BaseFloat,
|
||||
{
|
||||
@@ -55,7 +53,7 @@ where
|
||||
Rotation(q)
|
||||
}
|
||||
|
||||
pub fn get_rot_x(&self) -> Matrix3<S> {
|
||||
pub fn get_rot_x(&self) -> Matrix4<S> {
|
||||
let mut q = self.0;
|
||||
|
||||
q.v.z = S::zero();
|
||||
@@ -68,7 +66,7 @@ where
|
||||
q.into()
|
||||
}
|
||||
|
||||
pub fn get_rot_y(&self) -> Matrix3<S> {
|
||||
pub fn get_rot_y(&self) -> Matrix4<S> {
|
||||
let mut q = self.0;
|
||||
|
||||
q.v.x = S::zero();
|
||||
@@ -81,7 +79,7 @@ where
|
||||
q.into()
|
||||
}
|
||||
|
||||
pub fn get_rot_z(&self) -> Matrix3<S> {
|
||||
pub fn get_rot_z(&self) -> Matrix4<S> {
|
||||
let mut q = self.0;
|
||||
|
||||
q.v.x = S::zero();
|
||||
@@ -104,36 +102,36 @@ where
|
||||
Rotation(*q)
|
||||
}
|
||||
|
||||
pub fn from_matrix3(mat: &Matrix3<S>) -> Rotation<S> {
|
||||
pub fn from_matrix4(mat: &Matrix4<S>) -> Rotation<S> {
|
||||
mat.into()
|
||||
}
|
||||
|
||||
// Define a rotation from an axis and a angle
|
||||
pub fn from_axis_angle(axis: &Vector3<S>, angle: Angle<S>) -> Rotation<S> {
|
||||
let angle: Rad<S> = angle.into();
|
||||
let mat = Matrix3::from_axis_angle(axis.normalize(), angle);
|
||||
(&mat).into()
|
||||
let mat4 = Matrix4::from_axis_angle(axis.normalize(), angle);
|
||||
(&mat4).into()
|
||||
}
|
||||
|
||||
// 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 rot_y = Matrix3::from_angle_y(lon);
|
||||
let rot_x = Matrix3::from_angle_x(-lat);
|
||||
let rot_y = Matrix4::from_angle_y(lon);
|
||||
let rot_x = Matrix4::from_angle_x(-lat);
|
||||
|
||||
let mat = rot_y * rot_x;
|
||||
(&(mat)).into()
|
||||
let mat4 = rot_y * rot_x;
|
||||
(&(mat4)).into()
|
||||
}
|
||||
|
||||
// Apply a rotation to a position
|
||||
pub fn rotate(&self, pos_world_space: &Vector3<S>) -> Vector3<S> {
|
||||
let w2m: &Matrix3<S> = &self.into();
|
||||
pub fn rotate(&self, pos_world_space: &Vector4<S>) -> Vector4<S> {
|
||||
let w2m: &Matrix4<S> = &self.into();
|
||||
|
||||
w2m * pos_world_space
|
||||
}
|
||||
pub fn inv_rotate(&self, pos_model_space: &Vector3<S>) -> Vector3<S> {
|
||||
let w2m: &Matrix3<S> = &self.into();
|
||||
pub fn inv_rotate(&self, pos_model_space: &Vector4<S>) -> Vector4<S> {
|
||||
let w2m: &Matrix4<S> = &self.into();
|
||||
let m2w = w2m.transpose();
|
||||
|
||||
m2w * pos_model_space
|
||||
@@ -156,7 +154,7 @@ where
|
||||
/// Equations come from this paper (Appendix 6):
|
||||
/// https://ntrs.nasa.gov/api/citations/19770024290/downloads/19770024290.pdf
|
||||
pub fn euler_yxz(&self) -> (Angle<S>, Angle<S>, Angle<S>) {
|
||||
let m: Matrix3<S> = self.0.into();
|
||||
let m: Matrix4<S> = self.0.into();
|
||||
|
||||
let a = m.x.z.atan2(m.z.z);
|
||||
let b = (-m.z.y).atan2((S::one() - m.z.y * m.z.y).sqrt());
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::bbox::BoundingBox;
|
||||
use crate::math::angle::ToAngle;
|
||||
|
||||
use crate::math::{lonlat::LonLatT, projection::coo_space::XYZModel, MINUS_HALF_PI};
|
||||
use crate::math::{lonlat::LonLatT, projection::coo_space::XYZWModel, MINUS_HALF_PI};
|
||||
use cgmath::Vector3;
|
||||
use healpix::sph_geom::coo3d::Vec3;
|
||||
use healpix::sph_geom::coo3d::{Coo3D, UnitVect3};
|
||||
@@ -38,11 +38,11 @@ pub enum Intersection {
|
||||
// The segment does not intersect the region
|
||||
Empty,
|
||||
// The segment does intersect the region
|
||||
Intersect { vertices: Box<[XYZModel<f64>]> },
|
||||
Intersect { vertices: Box<[XYZWModel<f64>]> },
|
||||
}
|
||||
|
||||
impl Region {
|
||||
pub fn from_vertices(vertices: &[XYZModel<f64>], control_point: &XYZModel<f64>) -> Self {
|
||||
pub fn from_vertices(vertices: &[XYZWModel<f64>], control_point: &XYZWModel<f64>) -> Self {
|
||||
let (vertices, (lon, lat)): (Vec<_>, (Vec<_>, Vec<_>)) = vertices
|
||||
.iter()
|
||||
.map(|v| {
|
||||
@@ -101,7 +101,7 @@ impl Region {
|
||||
let vertices = polygon
|
||||
.intersect_parallel_all(lat)
|
||||
.iter()
|
||||
.map(|v| XYZModel::new(v.y(), v.z(), v.x()))
|
||||
.map(|v| XYZWModel::new(v.y(), v.z(), v.x(), 1.0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !vertices.is_empty() {
|
||||
@@ -193,10 +193,10 @@ impl Region {
|
||||
let coo2 =
|
||||
Coo3D::from_sph_coo(lonlat2.lon().to_radians(), lonlat2.lat().to_radians());
|
||||
|
||||
let vertices: Vec<cgmath::Vector3<f64>> = polygon
|
||||
let vertices: Vec<cgmath::Vector4<f64>> = polygon
|
||||
.intersect_great_circle_arc_all(&coo1, &coo2)
|
||||
.iter()
|
||||
.map(|v| XYZModel::new(v.y(), v.z(), v.x()))
|
||||
.map(|v| XYZWModel::new(v.y(), v.z(), v.x(), 1.0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !vertices.is_empty() {
|
||||
@@ -225,10 +225,10 @@ impl Region {
|
||||
// The polygon is included inside the region
|
||||
Region::AllSky => Intersection::Included,
|
||||
Region::Polygon { polygon, .. } => {
|
||||
let vertices: Vec<cgmath::Vector3<f64>> = polygon
|
||||
let vertices: Vec<cgmath::Vector4<f64>> = polygon
|
||||
.intersect_great_circle_all(&UnitVect3::new_unsafe(n.z, n.x, n.y))
|
||||
.iter()
|
||||
.map(|v| XYZModel::new(v.y(), v.z(), v.x()))
|
||||
.map(|v| XYZWModel::new(v.y(), v.z(), v.x(), 1.0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Test whether a point on the meridian is included
|
||||
|
||||
@@ -232,6 +232,7 @@ impl Manager {
|
||||
// Cells that are of depth > 7 are not handled by the hashmap (limited to depth 7)
|
||||
// For these cells, we draw all the sources lying in the ancestor cell of depth 7 containing
|
||||
// this cell
|
||||
//if camera.get_aperture() > P::RASTER_THRESHOLD_ANGLE {
|
||||
if camera.get_field_of_view().is_allsky() {
|
||||
let cells = crate::healpix::cell::ALLSKY_HPX_CELLS_D0;
|
||||
|
||||
@@ -413,7 +414,7 @@ impl Catalog {
|
||||
}
|
||||
|
||||
// Cells are of depth <= 7
|
||||
pub fn update(&mut self, cells: &[HEALPixCell]) {
|
||||
fn update(&mut self, cells: &[HEALPixCell]) {
|
||||
let num_sources_in_fov = self.get_total_num_sources_in_fov(cells) as f32;
|
||||
// reset the sources in the frame
|
||||
let mut sources: Vec<_> = vec![];
|
||||
|
||||
@@ -66,8 +66,8 @@ impl Label {
|
||||
let m1: Vector3<_> = lonlat.vector();
|
||||
let m2 = (m1 + d * 1e-3).normalize();
|
||||
|
||||
let d1 = projection.model_to_screen_space(&m1, camera)?;
|
||||
let d2 = projection.model_to_screen_space(&m2, camera)?;
|
||||
let d1 = projection.model_to_screen_space(&m1.extend(1.0), camera)?;
|
||||
let d2 = projection.model_to_screen_space(&m2.extend(1.0), camera)?;
|
||||
|
||||
let dt = (d2 - d1).normalize();
|
||||
let db = Vector2::new(dt.y.abs(), dt.x.abs());
|
||||
@@ -126,7 +126,7 @@ impl Label {
|
||||
let m1: Vector3<_> = lonlat.vector();
|
||||
|
||||
let mut t = Vector3::new(-m1.z, 0.0, m1.x).normalize();
|
||||
let center = camera.get_center();
|
||||
let center = camera.get_center().truncate();
|
||||
|
||||
let dot_t_center = center.dot(t);
|
||||
if dot_t_center.abs() < 1e-4 {
|
||||
@@ -137,8 +137,8 @@ impl Label {
|
||||
|
||||
let m2 = (m1 + t * 1e-3).normalize();
|
||||
|
||||
let d1 = projection.model_to_screen_space(&m1, camera)?;
|
||||
let d2 = projection.model_to_screen_space(&m2, camera)?;
|
||||
let d1 = projection.model_to_screen_space(&m1.extend(1.0), camera)?;
|
||||
let d2 = projection.model_to_screen_space(&m2.extend(1.0), camera)?;
|
||||
|
||||
let dt = (d2 - d1).normalize();
|
||||
let db = Vector2::new(dt.y.abs(), dt.x.abs());
|
||||
|
||||
@@ -59,7 +59,7 @@ fn generate_xyz_position(projection: &ProjectionType) -> Vec<f32> {
|
||||
2.0 * ((xy.y as f64) / (h as f64)) - 1.0,
|
||||
);
|
||||
if let Some(pos) = projection.clip_to_world_space(&clip_xy) {
|
||||
let pos = pos.normalize();
|
||||
let pos = pos.truncate().normalize();
|
||||
/*let mut d: u32 = 0;
|
||||
d |= 3 << 30;
|
||||
d |= (((pos.z * 0.5 + 0.5) * (1024.0 as f64)) as u32) << 20;
|
||||
|
||||
@@ -11,7 +11,7 @@ fn is_too_large(cell: &HEALPixCell, camera: &CameraViewPort, projection: &Projec
|
||||
.vertices()
|
||||
.iter()
|
||||
.filter_map(|(lon, lat)| {
|
||||
let vertex = crate::math::lonlat::radec_to_xyz(lon.to_angle(), lat.to_angle());
|
||||
let vertex = crate::math::lonlat::radec_to_xyzw(lon.to_angle(), lat.to_angle());
|
||||
projection.icrs_celestial_to_screen_space(&vertex, camera)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use cgmath::Vector3;
|
||||
use cgmath::Vector4;
|
||||
use std::ops::RangeInclusive;
|
||||
use wcs::ImgXY;
|
||||
|
||||
@@ -195,14 +195,14 @@ pub fn vertices(
|
||||
.map(|(y, uvy)| {
|
||||
x_it.clone().map(move |(x, uvx)| {
|
||||
let ndc = if let Some(xyz) = wcs.unproj_xyz(&ImgXY::new(x as f64, y as f64)) {
|
||||
let xyz = crate::coosys::apply_coo_system(
|
||||
let xyzw = crate::coosys::apply_coo_system(
|
||||
CooSystem::ICRS,
|
||||
camera.get_coo_system(),
|
||||
&Vector3::new(xyz.y(), xyz.z(), xyz.x()),
|
||||
&Vector4::new(xyz.y(), xyz.z(), xyz.x(), 1.0),
|
||||
);
|
||||
|
||||
projection
|
||||
.model_to_normalized_device_space(&xyz, camera)
|
||||
.model_to_normalized_device_space(&xyzw, camera)
|
||||
.map(|v| [v.x as f32, v.y as f32])
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::marker::Unpin;
|
||||
use std::vec;
|
||||
|
||||
use al_api::coo_system::CooSystem;
|
||||
use cgmath::Vector3;
|
||||
use cgmath::Vector4;
|
||||
use futures::stream::TryStreamExt;
|
||||
use futures::AsyncRead;
|
||||
|
||||
@@ -265,7 +265,7 @@ impl Image {
|
||||
let inside = crate::coosys::apply_coo_system(
|
||||
CooSystem::ICRS,
|
||||
coo_sys,
|
||||
&Vector3::new(center_xyz.y(), center_xyz.z(), center_xyz.x()),
|
||||
&Vector4::new(center_xyz.y(), center_xyz.z(), center_xyz.x(), 1.0),
|
||||
);
|
||||
|
||||
let vertices = [
|
||||
@@ -285,7 +285,7 @@ impl Image {
|
||||
crate::coosys::apply_coo_system(
|
||||
CooSystem::ICRS,
|
||||
coo_sys,
|
||||
&Vector3::new(xyz.y(), xyz.z(), xyz.x()),
|
||||
&Vector4::new(xyz.y(), xyz.z(), xyz.x(), 1.0),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@@ -556,7 +556,7 @@ impl Image {
|
||||
let inside = crate::coosys::apply_coo_system(
|
||||
CooSystem::ICRS,
|
||||
self.coo_sys,
|
||||
&Vector3::new(center_xyz.y(), center_xyz.z(), center_xyz.x()),
|
||||
&Vector4::new(center_xyz.y(), center_xyz.z(), center_xyz.x(), 1.0),
|
||||
);
|
||||
|
||||
let vertices = [
|
||||
@@ -580,7 +580,7 @@ impl Image {
|
||||
crate::coosys::apply_coo_system(
|
||||
CooSystem::ICRS,
|
||||
self.coo_sys,
|
||||
&Vector3::new(xyz.y(), xyz.z(), xyz.x()),
|
||||
&Vector4::new(xyz.y(), xyz.z(), xyz.x(), 1.0),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -32,8 +32,8 @@ pub fn project(
|
||||
let v1: Vector3<_> = lonlat1.vector();
|
||||
let v2: Vector3<_> = lonlat2.vector();
|
||||
|
||||
let p1 = projection.model_to_normalized_device_space(&v1, camera);
|
||||
let p2 = projection.model_to_normalized_device_space(&v2, camera);
|
||||
let p1 = projection.model_to_normalized_device_space(&v1.extend(1.0), camera);
|
||||
let p2 = projection.model_to_normalized_device_space(&v2.extend(1.0), camera);
|
||||
|
||||
match (p1, p2) {
|
||||
(Some(_), Some(_)) => {
|
||||
@@ -63,14 +63,14 @@ fn sub_valid_domain(
|
||||
projection: &ProjectionType,
|
||||
camera: &CameraViewPort,
|
||||
) -> (XYZModel<f64>, XYZModel<f64>) {
|
||||
let d_alpha = camera.get_aperture() * 0.02;
|
||||
let d_alpha = camera.get_aperture().to_radians() * 0.02;
|
||||
|
||||
let mut vv = valid_v;
|
||||
let mut vi = invalid_v;
|
||||
while crate::math::vector::angle3(&vv, &vi).to_radians() > d_alpha {
|
||||
let vm = (vv + vi).normalize();
|
||||
// check whether is it defined or not
|
||||
if let Some(_) = projection.model_to_normalized_device_space(&vm, camera) {
|
||||
if let Some(_) = projection.model_to_normalized_device_space(&vm.extend(1.0), camera) {
|
||||
vv = vm;
|
||||
} else {
|
||||
vi = vm;
|
||||
@@ -89,13 +89,13 @@ fn project_line(
|
||||
projection: &ProjectionType,
|
||||
iter: usize,
|
||||
) -> bool {
|
||||
let p1 = projection.model_to_normalized_device_space(&v1, camera);
|
||||
let p2 = projection.model_to_normalized_device_space(&v2, camera);
|
||||
let p1 = projection.model_to_normalized_device_space(&v1.extend(1.0), camera);
|
||||
let p2 = projection.model_to_normalized_device_space(&v2.extend(1.0), camera);
|
||||
|
||||
if iter < MAX_ITERATION {
|
||||
// Project them. We are always facing the camera
|
||||
let vm = (v1 + v2).normalize();
|
||||
let pm = projection.model_to_normalized_device_space(&vm, camera);
|
||||
let pm = projection.model_to_normalized_device_space(&vm.extend(1.0), camera);
|
||||
|
||||
match (p1, pm, p2) {
|
||||
(Some(p1), Some(pm), Some(p2)) => {
|
||||
|
||||
@@ -60,7 +60,7 @@ pub fn project(lat: f64, mut lon1: f64, lon2: f64, camera: &CameraViewPort, proj
|
||||
// * valid_lon and invalid_lon are well defined, i.e. they can be between [-PI; PI] or [0, 2PI] depending
|
||||
// whether they cross or not the zero meridian
|
||||
fn sub_valid_domain(lat: f64, valid_lon: f64, invalid_lon: f64, projection: &ProjectionType, camera: &CameraViewPort) -> (f64, f64) {
|
||||
let d_alpha = camera.get_aperture() * 0.02;
|
||||
let d_alpha = camera.get_aperture().to_radians() * 0.02;
|
||||
|
||||
let mut l_valid = valid_lon;
|
||||
let mut l_invalid = invalid_lon;
|
||||
|
||||
@@ -56,7 +56,7 @@ impl MOCHierarchy {
|
||||
let mut d = self.full_res_depth as usize;
|
||||
|
||||
let hpx_cell_size_rad =
|
||||
(smallest_cell_size_px / w_screen_px) * camera.get_aperture();
|
||||
(smallest_cell_size_px / w_screen_px) * camera.get_aperture().to_radians();
|
||||
|
||||
while d > 0 {
|
||||
//self.mocs[d].cell_indices_in_view(camera);
|
||||
|
||||
@@ -14,6 +14,8 @@ use crate::tile_fetcher::TileFetcherQueue;
|
||||
|
||||
use al_core::image::format::ChannelType;
|
||||
|
||||
pub use catalog::Manager;
|
||||
|
||||
use al_api::color::ColorRGB;
|
||||
use al_api::hips::HiPSCfg;
|
||||
use al_api::hips::ImageMetadata;
|
||||
|
||||
@@ -2,10 +2,10 @@ use crate::downloader::{query, Downloader};
|
||||
use crate::time::{DeltaTime, Time};
|
||||
use crate::Abort;
|
||||
|
||||
use al_api::moc::MOCOptions;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::rc::Rc;
|
||||
use al_api::moc::MOCOptions;
|
||||
|
||||
const MAX_NUM_TILE_FETCHING: usize = 8;
|
||||
const MAX_QUERY_QUEUE_LENGTH: usize = 100;
|
||||
@@ -214,7 +214,7 @@ impl TileFetcherQueue {
|
||||
|
||||
let tile_size = cfg.get_tile_size();
|
||||
//Request the allsky for the small tile size or if base tiles are not available
|
||||
if tile_size <= 128 || cfg.get_min_depth_tile() > 0 {
|
||||
if tile_size <= 256 || cfg.get_min_depth_tile() > 0 {
|
||||
// Request the allsky
|
||||
downloader.borrow_mut().fetch(query::Allsky::new(
|
||||
cfg,
|
||||
|
||||
@@ -56,7 +56,6 @@ pub unsafe fn transmute_vec_to_u8<I>(mut s: Vec<I>) -> Vec<u8> {
|
||||
std::mem::transmute(s)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn transmute_vec<I, O>(mut s: Vec<I>) -> Result<Vec<O>, &'static str> {
|
||||
if std::mem::size_of::<I>() % std::mem::size_of::<O>() > 0 {
|
||||
Err("The input type is not a multiple of the output type")
|
||||
@@ -101,13 +100,15 @@ pub(super) fn merge_overlapping_intervals(mut intervals: Vec<Range<usize>>) -> V
|
||||
Execute a closure after some delay. This mimics the javascript built-in setTimeout procedure.
|
||||
*/
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use {std::cell::Cell, std::rc::Rc, wasm_bindgen::closure::Closure, wasm_bindgen::JsCast};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(crate) fn set_timeout<F>(f: F, delay: i32) -> Rc<Cell<i32>>
|
||||
pub(crate) fn set_timeout<F>(f: F, delay: i32)
|
||||
where
|
||||
F: 'static + FnOnce() -> (),
|
||||
{
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::closure::Closure;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
let timeout_id = Rc::new(Cell::new(0));
|
||||
let t_id = timeout_id.clone();
|
||||
let cb = Closure::once_into_js(move || {
|
||||
@@ -130,24 +131,4 @@ where
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
timeout_id
|
||||
}
|
||||
/*
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(crate) fn debounce<F1, F2>(f: F1, delay: i32) -> Closure<F2>
|
||||
where
|
||||
F1: 'static + FnOnce() -> (),
|
||||
F2: 'static + FnOnce() -> (),
|
||||
{
|
||||
let timeout_id = Rc::new(Cell::new(0));
|
||||
|
||||
Closure::once(move || {
|
||||
web_sys::window()
|
||||
.unwrap()
|
||||
.clear_timeout_with_handle(timeout_id.get());
|
||||
|
||||
timeout_id.set(set_timeout(f, delay));
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,8 @@ layout (location = 1) in vec2 uv;
|
||||
layout (location = 2) in vec3 center;
|
||||
|
||||
uniform float current_time;
|
||||
uniform mat3 inv_model;
|
||||
uniform mat4 model;
|
||||
uniform mat4 inv_model;
|
||||
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
@@ -17,7 +18,7 @@ out vec3 out_p;
|
||||
#include ../projection/projection.glsl;
|
||||
|
||||
void main() {
|
||||
vec3 p = inv_model * center;
|
||||
vec3 p = vec3(inv_model * vec4(center, 1.0f));
|
||||
//p = check_inversed_longitude(p);
|
||||
|
||||
vec2 center_pos_clip_space = world2clip_aitoff(p);
|
||||
|
||||
@@ -5,7 +5,7 @@ layout (location = 1) in vec2 uv;
|
||||
layout (location = 2) in vec3 center;
|
||||
|
||||
uniform float current_time;
|
||||
uniform mat3 inv_model;
|
||||
uniform mat4 inv_model;
|
||||
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
@@ -17,7 +17,7 @@ out vec3 out_p;
|
||||
#include ../projection/projection.glsl;
|
||||
|
||||
void main() {
|
||||
vec3 p = inv_model * center;
|
||||
vec3 p = vec3(inv_model * vec4(center, 1.0f));
|
||||
//p = check_inversed_longitude(p);
|
||||
|
||||
vec2 center_pos_clip_space = world2clip_arc(p);
|
||||
|
||||
@@ -5,7 +5,8 @@ layout (location = 1) in vec2 uv;
|
||||
layout (location = 2) in vec3 center;
|
||||
|
||||
uniform float current_time;
|
||||
uniform mat3 inv_model;
|
||||
uniform mat4 model;
|
||||
uniform mat4 inv_model;
|
||||
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
@@ -18,7 +19,7 @@ out vec3 out_p;
|
||||
|
||||
|
||||
void main() {
|
||||
vec3 p = inv_model * center;
|
||||
vec3 p = vec3(inv_model * vec4(center, 1.0f));
|
||||
//p = check_inversed_longitude(p);
|
||||
|
||||
vec2 center_pos_clip_space = world2clip_healpix(p);
|
||||
|
||||
@@ -5,7 +5,7 @@ layout (location = 1) in vec2 uv;
|
||||
layout (location = 2) in vec3 center;
|
||||
|
||||
uniform float current_time;
|
||||
uniform mat3 inv_model;
|
||||
uniform mat4 inv_model;
|
||||
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
@@ -18,7 +18,7 @@ out vec3 out_p;
|
||||
|
||||
|
||||
void main() {
|
||||
vec3 p = inv_model * center;
|
||||
vec3 p = vec3(inv_model * vec4(center, 1.0f));
|
||||
//p = check_inversed_longitude(p);
|
||||
|
||||
vec2 center_pos_clip_space = world2clip_mercator(p);
|
||||
|
||||
@@ -5,7 +5,7 @@ layout (location = 1) in vec2 uv;
|
||||
layout (location = 2) in vec3 center;
|
||||
|
||||
uniform float current_time;
|
||||
uniform mat3 inv_model;
|
||||
uniform mat4 inv_model;
|
||||
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
@@ -18,7 +18,7 @@ out vec3 out_p;
|
||||
|
||||
|
||||
void main() {
|
||||
vec3 p = inv_model * center;
|
||||
vec3 p = vec3(inv_model * vec4(center, 1.0f));
|
||||
//p = check_inversed_longitude(p);
|
||||
|
||||
vec2 center_pos_clip_space = world2clip_mollweide(p);
|
||||
|
||||
@@ -5,7 +5,7 @@ layout (location = 1) in vec2 uv;
|
||||
layout (location = 2) in vec3 center;
|
||||
|
||||
uniform float current_time;
|
||||
uniform mat3 inv_model;
|
||||
uniform mat4 inv_model;
|
||||
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
@@ -18,7 +18,7 @@ out vec3 out_p;
|
||||
|
||||
|
||||
void main() {
|
||||
vec3 p = inv_model * center;
|
||||
vec3 p = vec3(inv_model * vec4(center, 1.0f));
|
||||
//p = check_inversed_longitude(p);
|
||||
|
||||
vec2 center_pos_clip_space = world2clip_orthographic(p);
|
||||
|
||||
@@ -6,7 +6,7 @@ layout (location = 1) in vec2 uv;
|
||||
layout (location = 2) in vec3 center;
|
||||
|
||||
uniform float current_time;
|
||||
uniform mat3 inv_model;
|
||||
uniform mat4 inv_model;
|
||||
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
@@ -19,7 +19,7 @@ out vec3 out_p;
|
||||
|
||||
|
||||
void main() {
|
||||
vec3 p = inv_model * center;
|
||||
vec3 p = vec3(inv_model * vec4(center, 1.0f));
|
||||
//p = check_inversed_longitude(p);
|
||||
|
||||
vec2 center_pos_clip_space = world2clip_gnomonic(p);
|
||||
|
||||
@@ -12,7 +12,7 @@ out vec3 frag_uv_end;
|
||||
out float frag_blending_factor;
|
||||
|
||||
// current time in ms
|
||||
uniform mat3 inv_model;
|
||||
uniform mat4 inv_model;
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
uniform float current_time;
|
||||
@@ -21,9 +21,9 @@ uniform float current_time;
|
||||
|
||||
void main() {
|
||||
//vec3 xyz = lonlat2xyz(lonlat);
|
||||
vec3 p_w = inv_model * xyz;
|
||||
vec4 p_w = inv_model * vec4(xyz, 1.0);
|
||||
// 3. Process the projection
|
||||
vec2 p_clip = proj(p_w);
|
||||
vec2 p_clip = proj(p_w.xyz);
|
||||
|
||||
vec2 p_ndc = p_clip / (ndc_to_clip * czf);
|
||||
gl_Position = vec4(p_ndc, 0.0, 1.0);
|
||||
|
||||
@@ -10,12 +10,12 @@ out vec3 frag_pos;
|
||||
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
uniform mat3 model;
|
||||
uniform mat4 model;
|
||||
|
||||
void main() {
|
||||
vec2 uv = pos_clip_space * 0.5 + 0.5;
|
||||
|
||||
frag_pos = model * pos_world_space;
|
||||
frag_pos = vec3(model * vec4(pos_world_space, 1.0));
|
||||
|
||||
gl_Position = vec4(pos_clip_space / (ndc_to_clip * czf), 0.0, 1.0);
|
||||
out_clip_pos = pos_clip_space;
|
||||
|
||||
@@ -7,7 +7,7 @@ layout (location = 1) in vec3 uv;
|
||||
out vec3 frag_uv;
|
||||
|
||||
// current time in ms
|
||||
uniform mat3 inv_model;
|
||||
uniform mat4 inv_model;
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
|
||||
@@ -15,7 +15,7 @@ uniform float czf;
|
||||
|
||||
void main() {
|
||||
vec3 p_xyz = lonlat2xyz(lonlat);
|
||||
vec3 p_w = inv_model * p_xyz;
|
||||
vec4 p_w = inv_model * vec4(p_xyz, 1.0);
|
||||
// 3. Process the projection
|
||||
vec2 p_clip = proj(p_w.xyz);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ layout (location = 0) in vec2 p_a_lonlat;
|
||||
layout (location = 1) in vec2 p_b_lonlat;
|
||||
layout (location = 2) in vec2 vertex;
|
||||
|
||||
uniform mat3 u_2world;
|
||||
uniform mat4 u_2world;
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
uniform float u_width;
|
||||
@@ -20,11 +20,11 @@ void main() {
|
||||
vec3 p_a_xyz = lonlat2xyz(p_a_lonlat);
|
||||
vec3 p_b_xyz = lonlat2xyz(p_b_lonlat);
|
||||
// 2. Convert to the world coo system
|
||||
vec3 p_a_w = u_2world * p_a_xyz;
|
||||
vec3 p_b_w = u_2world * p_b_xyz;
|
||||
vec4 p_a_w = u_2world * vec4(p_a_xyz, 1.0);
|
||||
vec4 p_b_w = u_2world * vec4(p_b_xyz, 1.0);
|
||||
// 3. Process the projection
|
||||
vec2 p_a_clip = proj(p_a_w);
|
||||
vec2 p_b_clip = proj(p_b_w);
|
||||
vec2 p_a_clip = proj(p_a_w.xyz);
|
||||
vec2 p_b_clip = proj(p_b_w.xyz);
|
||||
|
||||
vec2 da = p_a_clip - p_b_clip;
|
||||
l = dot(da, da);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
precision highp float;
|
||||
layout (location = 0) in vec2 lonlat;
|
||||
|
||||
uniform mat3 u_2world;
|
||||
uniform mat4 u_2world;
|
||||
uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
|
||||
@@ -12,9 +12,9 @@ void main() {
|
||||
// 1. Convert (lon, lat) into (x, y, z) space coo.
|
||||
vec3 p_xyz = lonlat2xyz(lonlat);
|
||||
// 2. Convert to the world coo system
|
||||
vec3 p_w = u_2world * p_xyz;
|
||||
vec4 p_w = u_2world * vec4(p_xyz, 1.0);
|
||||
// 3. Process the projection
|
||||
vec2 p_clip = proj(p_w);
|
||||
vec2 p_clip = proj(p_w.xyz);
|
||||
|
||||
vec2 p_ndc = p_clip / (ndc_to_clip * czf);
|
||||
gl_Position = vec4(p_ndc, 0.f, 1.f);
|
||||
|
||||
159
src/js/Aladin.js
159
src/js/Aladin.js
@@ -123,8 +123,7 @@ import { Polyline } from "./shapes/Polyline";
|
||||
* @property {boolean} [showReticle=true] - Whether to show the reticle.
|
||||
* @property {boolean} [showCatalog=true] - Whether to show the catalog.
|
||||
* @property {boolean} [showCooGrid=true] - Whether the coordinates grid should be shown at startup.
|
||||
* @property {boolean} [inertia=true] - Whether mouse release triggers an inertia effect.
|
||||
* @property {boolean} [lockNorthUp=false] - If true, the north pole will always be up.
|
||||
*
|
||||
* @property {boolean} [fullScreen=false] - Whether to start in full-screen mode.
|
||||
* @property {string} [reticleColor="rgb(178, 50, 178)"] - Color of the reticle in RGB format.
|
||||
* @property {number} [reticleSize=22] - Size of the reticle.
|
||||
@@ -224,15 +223,29 @@ import { Polyline } from "./shapes/Polyline";
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('select'|'objectsSelected'|'objectClicked'|'objectHovered'|'objectHoveredStop'|'footprintClicked'|'footprintHovered'|'positionChanged'|'zoomChanged'|'click'|'rightClickMove'|'mouseMove'|'wheelTriggered'|'fullScreenToggled'|'cooFrameChanged'|'resizeChanged'|'projectionChanged'|'layerChanged')} EventListener
|
||||
*
|
||||
* Some remarks:
|
||||
* <ul>
|
||||
* <li>'select' is <b>deprecated</b>, please use objectsSelected instead.</li>
|
||||
* <li>'mouseMove', 'click', 'wheelTriggered' are low level event listeners allowing the user to redefine basic functions. For example listening for 'wheelTriggered' will disable the default zooming heuristic then letting you to redefine it.</li>
|
||||
* <li>'objectsSelected', 'objectClicked', 'objectHovered', 'objectHoveredStop', 'footprintClicked', 'footprintHovered' are triggered when a catalog source/footprint has been clicked, hovered, ...
|
||||
* <li>Whenever the position (resp the fov) of the view has been changed 'positionChanged' (resp 'zoomChanged') is called</li>
|
||||
* </ul>
|
||||
* @typedef {string} ListenerCallback
|
||||
* String with possible values:
|
||||
* 'select' (deprecated, use objectsSelected instead),
|
||||
* 'objectsSelected',
|
||||
'objectClicked',
|
||||
'objectHovered',
|
||||
'objectHoveredStop',
|
||||
|
||||
'footprintClicked',
|
||||
'footprintHovered',
|
||||
|
||||
'positionChanged',
|
||||
'zoomChanged',
|
||||
|
||||
'click',
|
||||
'rightClickMove',
|
||||
'mouseMove',
|
||||
|
||||
'fullScreenToggled',
|
||||
'cooFrameChanged',
|
||||
'resizeChanged',
|
||||
'projectionChanged',
|
||||
'layerChanged'
|
||||
*/
|
||||
|
||||
export let Aladin = (function () {
|
||||
@@ -413,6 +426,8 @@ export let Aladin = (function () {
|
||||
// Merge what is already in the cache for that HiPS with new properties
|
||||
// coming from the MOCServer
|
||||
this.hipsFavorites.push(hipsObj);
|
||||
// Favorites are also directly pushed to the cache
|
||||
this.hipsCache.append(hipsObj.id, hipsObj)
|
||||
}
|
||||
|
||||
this._setupUI(options);
|
||||
@@ -432,11 +447,7 @@ export let Aladin = (function () {
|
||||
});
|
||||
} else if (options.survey === HiPS.DEFAULT_SURVEY_ID) {
|
||||
// DSS is cached inside HiPS class, no need to provide any further information
|
||||
const survey = this.createImageSurvey(
|
||||
HiPS.DEFAULT_SURVEY_ID
|
||||
);
|
||||
|
||||
this.setBaseImageLayer(survey);
|
||||
this.setBaseImageLayer(HiPS.DEFAULT_SURVEY_ID);
|
||||
} else {
|
||||
this.setBaseImageLayer(options.survey);
|
||||
}
|
||||
@@ -472,22 +483,24 @@ export let Aladin = (function () {
|
||||
// maximize control
|
||||
if (options.showFullscreenControl) {
|
||||
// react to fullscreenchange event to restore initial width/height (if user pressed ESC to go back from full screen)
|
||||
// This event is only triggered with realFullscreen on
|
||||
Utils.on(
|
||||
document,
|
||||
"fullscreenchange webkitfullscreenchange mozfullscreenchange MSFullscreenChange",
|
||||
function (e) {
|
||||
() => {
|
||||
var fullscreenElt =
|
||||
document.fullscreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.msFullscreenElement;
|
||||
if (fullscreenElt === null || fullscreenElt === undefined) {
|
||||
self.toggleFullscreen(options.realFullscreen);
|
||||
|
||||
var fullScreenToggledFn =
|
||||
self.callbacksByEventName["fullScreenToggled"];
|
||||
typeof fullScreenToggledFn === "function" &&
|
||||
fullScreenToggledFn(self.isInFullscreen);
|
||||
// fix: Only toggle off the screen once because in case of closing the real fullscreen from the ui button, this could be called 2 times
|
||||
// * one toggleFullscreen from the button itself
|
||||
// * one toggleFullscreen from the fullscreenchange event
|
||||
// => resulting in closing and opening the fullscreen again.
|
||||
if (self.isInFullscreen) {
|
||||
self.toggleFullscreen(options.realFullscreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -505,18 +518,12 @@ export let Aladin = (function () {
|
||||
this.samp = new SAMPConnector(this);
|
||||
}
|
||||
|
||||
// lockNorthUp option
|
||||
this.lockNorthUp = options.lockNorthUp || false;
|
||||
if (this.lockNorthUp) {
|
||||
this.wasm.lockNorthUp();
|
||||
}
|
||||
|
||||
if (options.inertia !== undefined) {
|
||||
this.wasm.setInertia(options.inertia);
|
||||
}
|
||||
|
||||
if (options.northPoleOrientation) {
|
||||
this.setRotation(options.northPoleOrientation);
|
||||
this.setViewCenter2NorthPoleAngle(options.northPoleOrientation);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -709,7 +716,6 @@ export let Aladin = (function () {
|
||||
Aladin.prototype.toggleFullscreen = function (realFullscreen) {
|
||||
let self = this;
|
||||
|
||||
realFullscreen = Boolean(realFullscreen);
|
||||
self.isInFullscreen = !self.isInFullscreen;
|
||||
|
||||
ContextMenu.hideAll();
|
||||
@@ -720,13 +726,7 @@ export let Aladin = (function () {
|
||||
ui.toggle();
|
||||
}
|
||||
})
|
||||
|
||||
if (this.aladinDiv.classList.contains("aladin-fullscreen")) {
|
||||
this.aladinDiv.classList.remove("aladin-fullscreen");
|
||||
} else {
|
||||
this.aladinDiv.classList.add("aladin-fullscreen");
|
||||
}
|
||||
|
||||
|
||||
if (realFullscreen) {
|
||||
// go to "real" full screen mode
|
||||
if (self.isInFullscreen) {
|
||||
@@ -757,6 +757,8 @@ export let Aladin = (function () {
|
||||
}
|
||||
}
|
||||
|
||||
this.aladinDiv.classList.toggle("aladin-fullscreen");
|
||||
|
||||
// Delay the fixLayoutDimensions layout for firefox
|
||||
/*setTimeout(function () {
|
||||
self.view.fixLayoutDimensions();
|
||||
@@ -815,7 +817,7 @@ export let Aladin = (function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the field of view (FoV) of the view in degrees.
|
||||
* Sets the field of view (FoV) of the Aladin instance to the specified angle in degrees.
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {number} FoV - The angle of the field of view in degrees.
|
||||
@@ -825,30 +827,11 @@ export let Aladin = (function () {
|
||||
* aladin.setFoV(60);
|
||||
*/
|
||||
Aladin.prototype.setFoV = function (FoV) {
|
||||
this.view.setFoV(FoV);
|
||||
this.view.setZoom(FoV);
|
||||
};
|
||||
|
||||
Aladin.prototype.setFov = Aladin.prototype.setFoV;
|
||||
|
||||
/**
|
||||
* Set the screen scaling zoom factor of the view.
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {number} zoomFactor - Scaling screen factor
|
||||
*/
|
||||
Aladin.prototype.setZoomFactor = function (zoomFactor) {
|
||||
this.view.setZoomFactor(zoomFactor);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the screen scaling zoom factor of the view.
|
||||
*
|
||||
* @memberof Aladin
|
||||
*/
|
||||
Aladin.prototype.getZoomFactor = function () {
|
||||
return this.view.zoomFactor;
|
||||
};
|
||||
|
||||
// @API
|
||||
// (experimental) try to adjust the FoV to the given object name. Does nothing if object is not known from Simbad
|
||||
Aladin.prototype.adjustFovForObject = function (objectName) {
|
||||
@@ -1309,16 +1292,18 @@ export let Aladin = (function () {
|
||||
Aladin.prototype.zoomToFoV = function (fov, duration, complete) {
|
||||
duration = duration || 5;
|
||||
|
||||
var fovArray = this.getFov();
|
||||
this.zoomAnimationParams = null;
|
||||
|
||||
this.zoomAnimationParams = {
|
||||
start: new Date().getTime(),
|
||||
end: new Date().getTime() + 1000 * duration,
|
||||
fovStart: Math.max(fovArray[0], fovArray[1]),
|
||||
fovEnd: fov,
|
||||
complete: complete,
|
||||
running: true,
|
||||
};
|
||||
var zoomAnimationParams = {};
|
||||
zoomAnimationParams["start"] = new Date().getTime();
|
||||
zoomAnimationParams["end"] = new Date().getTime() + 1000 * duration;
|
||||
var fovArray = this.getFov();
|
||||
zoomAnimationParams["fovStart"] = Math.max(fovArray[0], fovArray[1]);
|
||||
zoomAnimationParams["fovEnd"] = fov;
|
||||
zoomAnimationParams["complete"] = complete;
|
||||
zoomAnimationParams["running"] = true;
|
||||
|
||||
this.zoomAnimationParams = zoomAnimationParams;
|
||||
doZoomAnimation(this);
|
||||
};
|
||||
|
||||
@@ -1579,10 +1564,8 @@ export let Aladin = (function () {
|
||||
let hipsOptions = { id, name, maxOrder, url, cooFrame, ...options };
|
||||
let hips = new HiPS(id, url || id, hipsOptions)
|
||||
|
||||
// This allows to retrieve the survey's options when it will be
|
||||
// added later to the view.
|
||||
if (this instanceof Aladin && !this.hipsCache.contains(hips.id)) {
|
||||
// Add it to the cache as soon as possible if we have a reference to the aladin object
|
||||
// A HiPS can be refered by its unique ID thus we add it to the cache (cf excample/al-cfht.html that refers to HiPS object just by their unique ID)
|
||||
if (this instanceof Aladin) {
|
||||
this.hipsCache.append(hips.id, hipsOptions)
|
||||
}
|
||||
|
||||
@@ -1939,12 +1922,14 @@ export let Aladin = (function () {
|
||||
let imageLayer;
|
||||
|
||||
let hipsCache = this.hipsCache;
|
||||
|
||||
// 1. User gives an ID
|
||||
if (typeof urlOrHiPSOrFITS === "string") {
|
||||
const idOrUrl = urlOrHiPSOrFITS;
|
||||
// many cases here
|
||||
// 1/ It has been already added to the cache
|
||||
let cachedOptions = hipsCache.get(idOrUrl)
|
||||
|
||||
if (cachedOptions) {
|
||||
imageLayer = A.HiPS(idOrUrl, cachedOptions);
|
||||
} else {
|
||||
@@ -1964,7 +1949,7 @@ export let Aladin = (function () {
|
||||
if (!cachedLayerOptions) {
|
||||
hipsCache.append(imageLayer.id, imageLayer.options)
|
||||
} else {
|
||||
// set the options from what is in the cache
|
||||
// Set the options from what is in the cache
|
||||
imageLayer.setOptions(cachedLayerOptions);
|
||||
}
|
||||
}
|
||||
@@ -2008,7 +1993,7 @@ export let Aladin = (function () {
|
||||
* view in the counter clockwise order (or towards the east)
|
||||
*/
|
||||
Aladin.prototype.setRotation = function (rotation) {
|
||||
this.view.setRotation(rotation);
|
||||
this.view.setViewCenter2NorthPoleAngle(rotation);
|
||||
};
|
||||
|
||||
|
||||
@@ -2021,7 +2006,7 @@ export let Aladin = (function () {
|
||||
* @returns {number} - Angle between the position center and the north pole
|
||||
*/
|
||||
Aladin.prototype.getRotation = function () {
|
||||
return this.view.wasm.getRotation();
|
||||
return this.view.wasm.getViewCenter2NorthPoleAngle();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2124,7 +2109,6 @@ export let Aladin = (function () {
|
||||
"click",
|
||||
"rightClickMove",
|
||||
"mouseMove",
|
||||
"wheelTriggered",
|
||||
|
||||
"fullScreenToggled",
|
||||
"cooFrameChanged",
|
||||
@@ -2137,14 +2121,14 @@ export let Aladin = (function () {
|
||||
* Listen aladin for specific events
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {EventListener} what - e.g. objectHovered, select, zoomChanged, positionChanged, wheelTriggered
|
||||
* @param {ListenerCallback} what - e.g. objectHovered, select, zoomChanged, positionChanged
|
||||
* @param {function} myFunction - a callback function.
|
||||
* Note: <ul>
|
||||
* <li>positionChanged and zoomChanged are throttled every 100ms.</li>
|
||||
* <li>positionChanged's callback gives an object having ra and dec keywords of the current position in ICRS frame. See the below example.</li>
|
||||
* </ul>
|
||||
* @example
|
||||
// Define a function triggered when a source is hovered
|
||||
// define function triggered when a source is hovered
|
||||
aladin.on('objectHovered', function(object, xyMouseCoords) {
|
||||
if (object) {
|
||||
msg = 'You hovered object ' + object.data.name + ' located at ' + object.ra + ', ' + object.dec + '; mouse coords - x: '
|
||||
@@ -2320,6 +2304,15 @@ export let Aladin = (function () {
|
||||
* aladin.setCooGrid({ enabled: true });
|
||||
*/
|
||||
Aladin.prototype.setCooGrid = function (options) {
|
||||
if (options.color) {
|
||||
// 1. the user has maybe given some
|
||||
options.color = new Color(options.color);
|
||||
// 3. convert from 0-255 to 0-1
|
||||
options.color.r /= 255;
|
||||
options.color.g /= 255;
|
||||
options.color.b /= 255;
|
||||
}
|
||||
|
||||
this.view.setGridOptions(options);
|
||||
};
|
||||
|
||||
@@ -2564,15 +2557,14 @@ export let Aladin = (function () {
|
||||
* Restrict the FoV range between a min and a max value
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {number} [minFoV=1.0 / 36000.0] - in degrees. By default, the zoom is limited to 0.1 arcsec
|
||||
* @param {number} maxFoV - in degrees. If undefined, zooming out is not limited
|
||||
* @param {number} minFoV - in degrees when zoom in at max. If undefined, the zooming in is not limited
|
||||
* @param {number} maxFoV - in degrees when zoom out at max. If undefined, the zooming out is not limited
|
||||
*
|
||||
* @example
|
||||
* let aladin = A.aladin('#aladin-lite-div');
|
||||
* aladin.setFoVRange(30, 60);
|
||||
*/
|
||||
Aladin.prototype.setFoVRange = function (minFoV, maxFoV) {
|
||||
minFoV = minFoV || (1.0 / 36000.0);
|
||||
this.view.setFoVRange(minFoV, maxFoV);
|
||||
};
|
||||
|
||||
@@ -2709,11 +2701,16 @@ export let Aladin = (function () {
|
||||
* and the second element is the FoV height.
|
||||
*/
|
||||
Aladin.prototype.getFov = function () {
|
||||
// can go up to 1000 deg
|
||||
var fovX = this.view.fov;
|
||||
var s = this.getSize();
|
||||
|
||||
// constrain to the projection definition domain
|
||||
fovX = Math.min(fovX, this.view.projection.fov);
|
||||
var fovY = (s[1] / s[0]) * fovX;
|
||||
|
||||
fovY = Math.min(fovY, 180);
|
||||
// TODO : take into account AITOFF projection where fov can be larger than 180
|
||||
|
||||
return [fovX, fovY];
|
||||
};
|
||||
|
||||
@@ -613,7 +613,6 @@ export let Catalog = (function () {
|
||||
// Set the same color of the shape than the catalog.
|
||||
// FIXME: the color/shape could be a parameter at the source level, allowing the user single catalogs handling different shapes
|
||||
shape.setColor(this.color)
|
||||
|
||||
shape.setSelectionColor(this.selectionColor);
|
||||
shape.setHoverColor(this.hoverColor);
|
||||
}
|
||||
|
||||
@@ -37,13 +37,6 @@ export let MOC = (function() {
|
||||
* @class
|
||||
* @constructs MOC
|
||||
* @param {MOCOptions} options - Configuration options for the MOC
|
||||
* @property {boolean} perimeter - Show perimeter
|
||||
* @property {boolean} edge - Show the edges of HEALPix cells composing the MOC
|
||||
* @property {boolean} fill - Fill the MOC
|
||||
* @property {number} lineWidth - The width of lines used to draw the MOC
|
||||
* @property {number} opacity - Opacity of the edges, cells, etc... of a MOC
|
||||
* @property {string} color - Color of the edges/perimeter
|
||||
* @property {string} fillColor - Color to fill the MOC with
|
||||
*/
|
||||
let MOC = function(options) {
|
||||
//this.order = undefined;
|
||||
@@ -226,33 +219,6 @@ export let MOC = (function() {
|
||||
this.reportChange();
|
||||
}
|
||||
},
|
||||
perimeter: {
|
||||
get() {
|
||||
return this._perimeter;
|
||||
},
|
||||
set(perimeter) {
|
||||
this._perimeter = perimeter;
|
||||
this.reportChange();
|
||||
}
|
||||
},
|
||||
fill: {
|
||||
get() {
|
||||
return this._fill;
|
||||
},
|
||||
set(fill) {
|
||||
this._fill = fill;
|
||||
this.reportChange();
|
||||
}
|
||||
},
|
||||
edge: {
|
||||
get() {
|
||||
return this._edge;
|
||||
},
|
||||
set(edge) {
|
||||
this._edge = edge;
|
||||
this.reportChange();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -29,26 +29,26 @@
|
||||
*****************************************************************************/
|
||||
export let ProjectionEnum = {
|
||||
// Zenithal
|
||||
TAN: {id: 1, label: "Tangential"}, /* Gnomonic projection */
|
||||
STG: {id: 2, label: "Stereographic"}, /* Stereographic projection */
|
||||
SIN: {id: 3, label: "Spheric"}, /* Orthographic */
|
||||
TAN: {id: 1, fov: 150, label: "Tangential"}, /* Gnomonic projection */
|
||||
STG: {id: 2, fov: 240, label: "Stereographic"}, /* Stereographic projection */
|
||||
SIN: {id: 3, fov: 1000, label: "Spheric"}, /* Orthographic */
|
||||
// TODO: fix why the projection disappears at fov = 360.0
|
||||
ZEA: {id: 4, label: "Zenital equal-area"}, /* Equal-area */
|
||||
ZEA: {id: 4, fov: 1000, label: "Zenital equal-area"}, /* Equal-area */
|
||||
//FEYE: {id: 5, fov: 190, label: "fish eye"},
|
||||
//AIR: {id: 6, fov: 360, label: "airy"},
|
||||
//AZP: {fov: 180},
|
||||
//ARC: {id: 7, fov: 360, label: "zenital equidistant"},
|
||||
//NCP: {id: 8, fov: 180, label: "north celestial pole"},
|
||||
// Cylindrical
|
||||
MER: {id: 9, label: "Mercator"},
|
||||
MER: {id: 9, fov: 360, label: "Mercator"},
|
||||
//CAR: {id: 10, fov: 360, label: "plate carrée"},
|
||||
//CEA: {id: 11, fov: 360, label: "cylindrical equal area"},
|
||||
//CYP: {id: 12, fov: 360, label: "cylindrical perspective"},
|
||||
// Pseudo-cylindrical
|
||||
AIT: {id: 13, label: "Hammer-Aïtoff"},
|
||||
AIT: {id: 13, fov: 1000, label: "Hammer-Aïtoff"},
|
||||
//PAR: {id: 14, fov: 360, label: "parabolic"},
|
||||
//SFL: {id: 15, fov: 360, label: "sanson-flamsteed"},
|
||||
MOL: {id: 16, label: "Mollweide"},
|
||||
MOL: {id: 16, fov: 1000, label: "Mollweide"},
|
||||
// Conic
|
||||
//COD: {id: 17, fov: 360, label: "conic equidistant"},
|
||||
// Hybrid
|
||||
|
||||
@@ -194,22 +194,6 @@ Utils.throttle = function (fn, threshhold, scope) {
|
||||
}
|
||||
}
|
||||
|
||||
// Way of detecting if the computer has trackpad or a regular mouse wheel thanks to that post:
|
||||
// https://stackoverflow.com/questions/10744645/detect-touchpad-vs-mouse-in-javascript
|
||||
Utils.detectTrackPad = function (e) {
|
||||
var isTrackpad = false;
|
||||
if (e.wheelDeltaY) {
|
||||
if (e.wheelDeltaY === (e.deltaY * -3)) {
|
||||
isTrackpad = true;
|
||||
}
|
||||
}
|
||||
else if (e.deltaMode === 0) {
|
||||
isTrackpad = true;
|
||||
}
|
||||
|
||||
return isTrackpad
|
||||
}
|
||||
|
||||
|
||||
/* A LRU cache, inspired by https://gist.github.com/devinus/409353#file-gistfile1-js */
|
||||
// TODO : utiliser le LRU cache pour les tuiles images
|
||||
|
||||
320
src/js/View.js
320
src/js/View.js
@@ -49,7 +49,6 @@ import { Layout } from "./gui/Layout.js";
|
||||
import { SAMPActionButton } from "./gui/Button/SAMP.js";
|
||||
import { HiPS } from "./HiPS.js";
|
||||
import { Image } from "./Image.js";
|
||||
import { Color } from "./Color.js";
|
||||
|
||||
export let View = (function () {
|
||||
|
||||
@@ -98,16 +97,6 @@ export let View = (function () {
|
||||
console.error("Problem initializing Aladin Lite. Please contact the support by contacting Matthieu Baumann (baumannmatthieu0@gmail.com) or Thomas Boch (thomas.boch@astro.unistra.fr). You can also open an issue on the Aladin Lite github repository here: https://github.com/cds-astro/aladin-lite. Message error:" + e)
|
||||
}
|
||||
|
||||
this._defineProperties();
|
||||
|
||||
// I realized debouncing a little bit the resize process
|
||||
// prevents the div from flickering in white !
|
||||
// Let's keep that like that (with a very small debounced time of 2ms).
|
||||
this.debounceResize = Utils.debounce(() => {
|
||||
self.wasm.resize(self.width, self.height);
|
||||
self.updateZoomState()
|
||||
}, 2);
|
||||
|
||||
// Attach the drag and drop events to the view
|
||||
this.aladinDiv.ondrop = (event) => {
|
||||
const files = Utils.getDroppedFilesHandler(event);
|
||||
@@ -183,14 +172,13 @@ export let View = (function () {
|
||||
View.CALLBACKS_THROTTLE_TIME_MS,
|
||||
);
|
||||
|
||||
this.debounceProgCatOnZoom = Utils.debounce(() => {
|
||||
self.refreshProgressiveCats();
|
||||
self.drawAllOverlays();
|
||||
}, 300);
|
||||
|
||||
this.mustClearCatalog = true;
|
||||
this.mode = View.PAN;
|
||||
|
||||
// 0.1 arcsec
|
||||
this.minFoV = 1 / 36000;
|
||||
this.maxFoV = null;
|
||||
|
||||
this.healpixGrid = new HealpixGrid();
|
||||
this.then = Date.now();
|
||||
|
||||
@@ -198,9 +186,10 @@ export let View = (function () {
|
||||
lon = lat = 0;
|
||||
|
||||
// FoV init settings
|
||||
let initialFov = this.options.fov || 180.0;
|
||||
this.pinchZoomParameters = {
|
||||
isPinching: false, // true if a pinch zoom is ongoing
|
||||
initialZoomFactor: undefined,
|
||||
initialFov: undefined,
|
||||
initialDistance: undefined,
|
||||
};
|
||||
|
||||
@@ -209,7 +198,7 @@ export let View = (function () {
|
||||
this.setProjection(projName)
|
||||
|
||||
// Then set the zoom properly once the projection is defined
|
||||
this.fov = this.options.fov || 180.0
|
||||
this.setZoom(initialFov)
|
||||
|
||||
// Target position settings
|
||||
this.viewCenter = { lon, lat }; // position of center of view
|
||||
@@ -275,6 +264,11 @@ export let View = (function () {
|
||||
init(this);
|
||||
// listen to window resize and reshape canvases
|
||||
this.resizeTimer = null;
|
||||
/*if ('ontouchstart' in window) {
|
||||
Utils.on(document, 'orientationchange', (e) => {
|
||||
self.fixLayoutDimensions();
|
||||
})
|
||||
} else {*/
|
||||
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
self.fixLayoutDimensions();
|
||||
@@ -284,6 +278,24 @@ export let View = (function () {
|
||||
|
||||
self.fixLayoutDimensions();
|
||||
self.redraw()
|
||||
|
||||
// in some contexts (Jupyter notebook for instance), the parent div changes little time after Aladin Lite creation
|
||||
// this results in canvas dimension to be incorrect.
|
||||
// The following line tries to fix this issue
|
||||
/*setTimeout(function () {
|
||||
var computedWidth = $(self.aladinDiv).width();
|
||||
var computedHeight = $(self.aladinDiv).height();
|
||||
|
||||
if (self.width !== computedWidth || self.height === computedHeight) {
|
||||
self.fixLayoutDimensions();
|
||||
// As the WebGL backend has been resized correctly by
|
||||
// the previous call, we can get the zoom factor from it
|
||||
|
||||
self.setZoom(self.fov); // needed to force recomputation of displayed FoV
|
||||
}
|
||||
|
||||
self.requestRedraw();
|
||||
}, 1000);*/
|
||||
};
|
||||
|
||||
// different available modes
|
||||
@@ -299,27 +311,6 @@ export let View = (function () {
|
||||
View.CALLBACKS_THROTTLE_TIME_MS = 100; // minimum time between two consecutive callback calls
|
||||
|
||||
|
||||
View.prototype._defineProperties = function() {
|
||||
Object.defineProperties(this, {
|
||||
fov: {
|
||||
get() {
|
||||
return this.wasm.getFieldOfView();
|
||||
},
|
||||
set(newFov) {
|
||||
this.setFoV(newFov);
|
||||
}
|
||||
},
|
||||
zoomFactor: {
|
||||
get() {
|
||||
return this.wasm.getZoomFactor();
|
||||
},
|
||||
set(newZoomFactor) {
|
||||
this.setZoomFactor(newZoomFactor);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// (re)create needed canvases
|
||||
View.prototype.createCanvases = function () {
|
||||
let imageCanvas = this.aladinDiv.querySelector('.aladin-imageCanvas');
|
||||
@@ -355,21 +346,17 @@ export let View = (function () {
|
||||
};
|
||||
|
||||
View.prototype.setFoVRange = function(minFoV, maxFoV) {
|
||||
this.wasm.setFoVRange(minFoV, maxFoV)
|
||||
this.updateZoomState();
|
||||
}
|
||||
|
||||
View.prototype.getFoVRange = function() {
|
||||
let [minFoV, maxFoV] = this.wasm.getFoVRange();
|
||||
if (minFoV == -1.0) {
|
||||
minFoV = null;
|
||||
if (minFoV && maxFoV && minFoV > maxFoV) {
|
||||
var tmp = minFoV;
|
||||
minFoV = maxFoV;
|
||||
maxFoV = tmp;
|
||||
}
|
||||
|
||||
if (maxFoV == -1.0) {
|
||||
maxFoV = null;
|
||||
}
|
||||
this.minFoV = minFoV || (1.0 / 36000);
|
||||
this.maxFoV = maxFoV;
|
||||
|
||||
return [minFoV, maxFoV]
|
||||
// reset the field of view
|
||||
this.setZoom(this.fov);
|
||||
}
|
||||
|
||||
// called at startup and when window is resized
|
||||
@@ -404,11 +391,15 @@ export let View = (function () {
|
||||
this.catalogCtx.canvas.style.height = this.height + "px";
|
||||
this.catalogCtx.scale(window.devicePixelRatio, window.devicePixelRatio);
|
||||
|
||||
/*this.gridCtx = this.gridCanvas.getContext("2d");
|
||||
this.gridCtx.canvas.width = this.width;
|
||||
this.gridCtx.canvas.height = this.height;
|
||||
*/
|
||||
this.imageCtx = this.imageCanvas.getContext("webgl2");
|
||||
this.imageCtx.canvas.style.width = this.width + "px";
|
||||
this.imageCtx.canvas.style.height = this.height + "px";
|
||||
|
||||
this.debounceResize();
|
||||
this.wasm.resize(this.width, this.height);
|
||||
this.setZoom(this.fov)
|
||||
|
||||
pixelateCanvasContext(this.imageCtx, this.aladin.options.pixelateCanvas);
|
||||
|
||||
@@ -435,7 +426,6 @@ export let View = (function () {
|
||||
this.aladinDiv.style.removeProperty('line-height');
|
||||
|
||||
this.throttledDivResized();
|
||||
|
||||
};
|
||||
|
||||
var pixelateCanvasContext = function (ctx, pixelateFlag) {
|
||||
@@ -592,7 +582,7 @@ export let View = (function () {
|
||||
const [lon, lat] = view.aladin.pix2world(xymouse.x, xymouse.y, 'icrs');
|
||||
view.pointTo(lon, lat);
|
||||
// reset the rotation around center view
|
||||
view.setRotation(0.0);
|
||||
view.setViewCenter2NorthPoleAngle(0.0);
|
||||
}
|
||||
catch (err) {
|
||||
return;
|
||||
@@ -640,6 +630,7 @@ export let View = (function () {
|
||||
var footprintClickedFunction = view.aladin.callbacksByEventName['footprintClicked'];
|
||||
|
||||
let objsByCats = {};
|
||||
let shapes = [];
|
||||
for (let o of objs) {
|
||||
// classify the different objects by catalog
|
||||
let cat = o.getCatalog && o.getCatalog();
|
||||
@@ -658,11 +649,22 @@ export let View = (function () {
|
||||
footprintClickedFunction(o, xy);
|
||||
}
|
||||
}
|
||||
|
||||
// If this shape has a catalog then it will be selected from its source
|
||||
// so we will not add it
|
||||
if (!cat) {
|
||||
shapes.push(o);
|
||||
}
|
||||
}
|
||||
|
||||
// rewrite objs
|
||||
// Rewrite objs
|
||||
objs = Array.from(Object.values(objsByCats));
|
||||
// Add the external shapes (i.e. which are not associated with catalog sources e.g. those from GraphicOverlay)
|
||||
if (shapes.length > 0) {
|
||||
objs.push(shapes)
|
||||
}
|
||||
view.selectObjects(objs);
|
||||
|
||||
view.lastClickedObject = objs;
|
||||
|
||||
} else {
|
||||
@@ -735,13 +737,9 @@ export let View = (function () {
|
||||
if (e.type === 'touchstart' && e.targetTouches && e.targetTouches.length >= 2) {
|
||||
view.dragging = false;
|
||||
|
||||
// Do not start the pinched rotation if the north up is locked
|
||||
if (view.aladin.lockNorthUp === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
view.pinchZoomParameters.isPinching = true;
|
||||
view.pinchZoomParameters.initialZoomFactor = view.zoomFactor;
|
||||
var fov = view.wasm.getFieldOfView();
|
||||
view.pinchZoomParameters.initialFov = fov;
|
||||
view.pinchZoomParameters.initialDistance = Math.sqrt(Math.pow(e.targetTouches[0].clientX - e.targetTouches[1].clientX, 2) + Math.pow(e.targetTouches[0].clientY - e.targetTouches[1].clientY, 2));
|
||||
|
||||
view.fingersRotationParameters.initialViewAngleFromCenter = view.wasm.getViewCenter2NorthPoleAngle();
|
||||
@@ -828,7 +826,7 @@ export let View = (function () {
|
||||
|
||||
if ((e.type === 'touchend' || e.type === 'touchcancel') && view.pinchZoomParameters.isPinching) {
|
||||
view.pinchZoomParameters.isPinching = false;
|
||||
view.pinchZoomParameters.initialZoomFactor = view.pinchZoomParameters.initialDistance = undefined;
|
||||
view.pinchZoomParameters.initialFov = view.pinchZoomParameters.initialDistance = undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1014,13 +1012,13 @@ export let View = (function () {
|
||||
// planetary survey case
|
||||
rotation -= fingerAngleDiff;
|
||||
}
|
||||
view.setRotation(rotation);
|
||||
view.setViewCenter2NorthPoleAngle(rotation);
|
||||
}
|
||||
|
||||
// zoom
|
||||
const dist = Math.sqrt(Math.pow(e.touches[0].clientX - e.touches[1].clientX, 2) + Math.pow(e.touches[0].clientY - e.touches[1].clientY, 2));
|
||||
const zoomFactor = view.pinchZoomParameters.initialZoomFactor * view.pinchZoomParameters.initialDistance / dist;
|
||||
view.zoomFactor = zoomFactor;
|
||||
const fov = Math.min(Math.max(view.pinchZoomParameters.initialFov * view.pinchZoomParameters.initialDistance / dist, 0.00002777777), view.projection.fov);
|
||||
view.setZoom(fov);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1148,6 +1146,10 @@ export let View = (function () {
|
||||
|
||||
// disable text selection on IE
|
||||
//Utils.on(view.aladinDiv, "selectstart", function () { return false; })
|
||||
/*var eventCount = 0;
|
||||
var eventCountStart;
|
||||
var isTouchPad;
|
||||
let id;*/
|
||||
|
||||
Utils.on(view.catalogCanvas, 'wheel', function (e) {
|
||||
e.preventDefault();
|
||||
@@ -1165,30 +1167,82 @@ export let View = (function () {
|
||||
xy: xymouse,
|
||||
});
|
||||
|
||||
var onWheelTriggeredFunction = view.aladin.callbacksByEventName['wheelTriggered'];
|
||||
if (typeof onWheelTriggeredFunction === 'function') {
|
||||
onWheelTriggeredFunction(e)
|
||||
} else {
|
||||
// Default Aladin Lite zooming
|
||||
view.delta = e.deltaY || e.detail || (-e.wheelDelta);
|
||||
if (view.rightClick) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!view.debounceProgCatOnZoom) {
|
||||
var self = view;
|
||||
view.debounceProgCatOnZoom = Utils.debounce(function () {
|
||||
self.refreshProgressiveCats();
|
||||
self.drawAllOverlays();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
view.debounceProgCatOnZoom();
|
||||
//view.throttledZoomChanged();
|
||||
|
||||
// Zoom heuristic
|
||||
// First detect the device
|
||||
// See https://stackoverflow.com/questions/10744645/detect-touchpad-vs-mouse-in-javascript
|
||||
// for detecting the use of a touchpad
|
||||
/*view.isTouchPadDefined = isTouchPad || typeof isTouchPad !== "undefined";
|
||||
if (!view.isTouchPadDefined) {
|
||||
if (eventCount === 0) {
|
||||
view.delta = 0;
|
||||
eventCountStart = new Date().getTime();
|
||||
}
|
||||
|
||||
eventCount++;
|
||||
|
||||
if (new Date().getTime() - eventCountStart > 100) {
|
||||
if (eventCount > 10) {
|
||||
isTouchPad = true;
|
||||
} else {
|
||||
isTouchPad = false;
|
||||
}
|
||||
view.isTouchPadDefined = true;
|
||||
}
|
||||
}*/
|
||||
|
||||
// only ensure the touch pad test has been done before zooming
|
||||
/*if (!view.isTouchPadDefined) {
|
||||
return false;
|
||||
}*/
|
||||
|
||||
// touch pad defined
|
||||
view.delta = e.deltaY || e.detail || (-e.wheelDelta);
|
||||
|
||||
//if (isTouchPad) {
|
||||
if (!view.throttledTouchPadZoom) {
|
||||
view.throttledTouchPadZoom = () => {
|
||||
const factor = Utils.detectTrackPad(e) ? 1.04 : 1.2;
|
||||
const currZoomFactor = view.zoom.isZooming ? view.zoom.finalZoom : view.zoomFactor;
|
||||
//const currZoomFactor = view.wasm.getZoomFactor();
|
||||
let newZoomFactor = view.delta > 0 ? currZoomFactor * factor : currZoomFactor / factor;
|
||||
const factor = 2.0;
|
||||
let newFov = view.delta > 0 ? view.fov * factor : view.fov / factor;
|
||||
|
||||
// inside case
|
||||
view.zoom.apply({
|
||||
stop: newZoomFactor,
|
||||
duration: 100,
|
||||
stop: newFov,
|
||||
duration: 100
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
view.throttledTouchPadZoom();
|
||||
}
|
||||
/*} else {
|
||||
if (!view.throttledMouseScrollZoom) {
|
||||
view.throttledMouseScrollZoom = () => {
|
||||
const factor = 2
|
||||
let newFov = view.delta > 0 ? view.fov * factor : view.fov / factor;
|
||||
// standard mouse wheel zooming
|
||||
view.zoom.apply({
|
||||
stop: newFov,
|
||||
duration: 100
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
view.throttledMouseScrollZoom()
|
||||
}*/
|
||||
|
||||
return false;
|
||||
});
|
||||
@@ -1209,7 +1263,7 @@ export let View = (function () {
|
||||
switch (e.keyCode) {
|
||||
// escape
|
||||
case 27:
|
||||
// if there is a selection occuring
|
||||
// Called when realfullscreen is false. Escaping from real fullscreen does not seem to trigger the keydown event
|
||||
if (view.aladin.isInFullscreen) {
|
||||
view.aladin.toggleFullscreen(view.aladin.options.realFullscreen);
|
||||
}
|
||||
@@ -1504,44 +1558,45 @@ export let View = (function () {
|
||||
};
|
||||
|
||||
// Called for touchmove events
|
||||
View.prototype.setZoomFactor = function(zoomFactor) {
|
||||
this.wasm.setZoomFactor(zoomFactor);
|
||||
this.updateZoomState();
|
||||
}
|
||||
View.prototype.setZoom = function (fov) {
|
||||
// limit the fov in function of the projection
|
||||
fov = Math.min(fov, this.projection.fov);
|
||||
|
||||
View.prototype.setFoV = function(fov) {
|
||||
this.wasm.setFieldOfView(fov)
|
||||
this.updateZoomState();
|
||||
}
|
||||
// then clamp the fov between minFov and maxFov
|
||||
const minFoV = this.minFoV;
|
||||
const maxFoV = this.maxFoV;
|
||||
|
||||
if (minFoV) {
|
||||
fov = Math.max(fov, minFoV);
|
||||
}
|
||||
|
||||
if (maxFoV) {
|
||||
fov = Math.min(fov, maxFoV);
|
||||
}
|
||||
|
||||
this.wasm.setFieldOfView(fov);
|
||||
this.updateZoomState(fov);
|
||||
};
|
||||
|
||||
View.prototype.increaseZoom = function () {
|
||||
this.zoom.apply({
|
||||
stop: this.zoomFactor / 1.4,
|
||||
duration: 200,
|
||||
stop: this.fov / 1.8,
|
||||
duration: 100
|
||||
});
|
||||
}
|
||||
|
||||
View.prototype.decreaseZoom = function () {
|
||||
this.zoom.apply({
|
||||
stop: this.zoomFactor * 1.4,
|
||||
duration: 200,
|
||||
stop: this.fov * 1.8,
|
||||
duration: 100
|
||||
});
|
||||
}
|
||||
|
||||
View.prototype.setRotation = function(rotation) {
|
||||
this.wasm.setRotation(rotation);
|
||||
View.prototype.setViewCenter2NorthPoleAngle = function(rotation) {
|
||||
this.wasm.setViewCenter2NorthPoleAngle(rotation);
|
||||
}
|
||||
|
||||
View.prototype.setGridOptions = function (options) {
|
||||
if (options.color) {
|
||||
// 1. the user has maybe given some
|
||||
options.color = new Color(options.color);
|
||||
// 3. convert from 0-255 to 0-1
|
||||
options.color.r /= 255;
|
||||
options.color.g /= 255;
|
||||
options.color.b /= 255;
|
||||
}
|
||||
|
||||
this.gridCfg = {...this.gridCfg, ...options};
|
||||
this.wasm.setGridOptions(this.gridCfg);
|
||||
|
||||
@@ -1554,9 +1609,26 @@ export let View = (function () {
|
||||
|
||||
View.prototype.getGridOptions = function() {
|
||||
return this.gridCfg;
|
||||
};
|
||||
}
|
||||
|
||||
View.prototype.updateZoomState = function () {
|
||||
View.prototype.updateZoomState = function (fov) {
|
||||
// Get the new zoom values from the backend
|
||||
const newFov = fov || this.wasm.getFieldOfView()
|
||||
|
||||
// Disable the coo grid labels if we are too unzoomed
|
||||
const maxFovGridLabels = 360;
|
||||
if (this.fov <= maxFovGridLabels && newFov > maxFovGridLabels) {
|
||||
let gridOptions = this.getGridOptions()
|
||||
if (gridOptions) {
|
||||
this.originalShowLabels = gridOptions.showLabels;
|
||||
this.aladin.setCooGrid({showLabels: false});
|
||||
}
|
||||
|
||||
} else if (this.fov > maxFovGridLabels && newFov <= maxFovGridLabels) {
|
||||
this.aladin.setCooGrid({showLabels:this.originalShowLabels});
|
||||
}
|
||||
|
||||
this.fov = newFov;
|
||||
this.computeNorder();
|
||||
|
||||
let fovX = this.fov;
|
||||
@@ -1564,8 +1636,6 @@ export let View = (function () {
|
||||
fovX = Math.min(fovX, 360);
|
||||
fovY = Math.min(fovY, 180);
|
||||
|
||||
this.debounceProgCatOnZoom();
|
||||
|
||||
ALEvent.ZOOM_CHANGED.dispatchedTo(this.aladinDiv, { fovX, fovY });
|
||||
|
||||
this.throttledZoomChanged();
|
||||
@@ -1871,7 +1941,8 @@ export let View = (function () {
|
||||
|
||||
// Change the projection here
|
||||
this.wasm.setProjection(projName);
|
||||
this.updateZoomState()
|
||||
let newProjFov = Math.min(this.fov, this.projection.fov);
|
||||
this.setZoom(newProjFov)
|
||||
|
||||
const projFn = this.aladin.callbacksByEventName['projectionChanged'];
|
||||
(typeof projFn === 'function') && projFn(projName);
|
||||
@@ -2101,26 +2172,21 @@ export let View = (function () {
|
||||
}
|
||||
|
||||
let closests = [];
|
||||
const fLineWidth = (footprints && footprints[0] && footprints[0].getLineWidth()) || 1;
|
||||
let lw = fLineWidth + 3;
|
||||
//for (var lw = startLw + 1; lw <= startLw + 3; lw++) {
|
||||
footprints.forEach((footprint) => {
|
||||
if (!footprint.source || !footprint.source.tooSmallFootprint) {
|
||||
// Hidden footprints are not considered
|
||||
//let originLineWidth = footprint.getLineWidth();
|
||||
|
||||
footprint.setLineWidth(lw);
|
||||
if (footprint.isShowing && footprint.isInStroke(ctx, this, x * window.devicePixelRatio, y * window.devicePixelRatio)) {
|
||||
closests.push(footprint);
|
||||
}
|
||||
footprint.setLineWidth(fLineWidth);
|
||||
}
|
||||
})
|
||||
|
||||
/* if (closests.length > 0) {
|
||||
break;
|
||||
footprints.forEach((footprint) => {
|
||||
if (!footprint.source || !footprint.source.tooSmallFootprint) {
|
||||
const originLineWidth = footprint.getLineWidth();
|
||||
let spreadedLineWidth = (originLineWidth || 1) + 3;
|
||||
|
||||
footprint.setLineWidth(spreadedLineWidth);
|
||||
if (footprint.isShowing && footprint.isInStroke(ctx, this, x * window.devicePixelRatio, y * window.devicePixelRatio)) {
|
||||
closests.push(footprint);
|
||||
}
|
||||
|
||||
footprint.setLineWidth(originLineWidth);
|
||||
}
|
||||
}*/
|
||||
})
|
||||
|
||||
|
||||
return closests;
|
||||
};
|
||||
@@ -2132,13 +2198,11 @@ export let View = (function () {
|
||||
var canvas = this.catalogCanvas;
|
||||
var ctx = canvas.getContext("2d");
|
||||
// this makes footprint selection easier as the catch-zone is larger
|
||||
//let pastLineWidth = ctx.lineWidth;
|
||||
|
||||
let closests = [];
|
||||
if (this.overlays) {
|
||||
for (var k = 0; k < this.overlays.length; k++) {
|
||||
overlay = this.overlays[k];
|
||||
|
||||
closests = closests.concat(this.closestFootprints(overlay.overlayItems, ctx, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,16 +36,24 @@ import { requestAnimFrame } from "./libs/RequestAnimationFrame.js";
|
||||
};
|
||||
|
||||
Zoom.prototype.apply = function(options) {
|
||||
let startZoom = options['start'] || this.view.zoomFactor;
|
||||
let startZoom = options['start'] || this.view.fov;
|
||||
let finalZoom = options['stop'] || undefined;
|
||||
let interpolationDuration = options['duration'] || 1000; // default to 1seconds
|
||||
if (!finalZoom)
|
||||
return;
|
||||
|
||||
// Get a relative error for stopping the zooming
|
||||
const relativeErr = Math.abs(finalZoom - startZoom) * 0.01;
|
||||
const zoomFn = (zoom) => {
|
||||
this.view.setZoomFactor(zoom)
|
||||
// clamp the zoom to the view params minFov and maxFov and the projection bounds
|
||||
//finalZoom = Math.min(finalZoom, this.view.projection.fov);
|
||||
// then clamp the fov between minFov and maxFov
|
||||
const minFoV = this.view.minFoV;
|
||||
const maxFoV = this.view.maxFoV;
|
||||
|
||||
if (minFoV) {
|
||||
finalZoom = Math.max(finalZoom, minFoV);
|
||||
}
|
||||
|
||||
if (maxFoV) {
|
||||
finalZoom = Math.min(finalZoom, maxFoV);
|
||||
}
|
||||
|
||||
this.finalZoom = finalZoom;
|
||||
@@ -89,7 +97,6 @@ import { requestAnimFrame } from "./libs/RequestAnimationFrame.js";
|
||||
if (self.stop) {
|
||||
self.isZooming = false;
|
||||
self.stop = false;
|
||||
self.finalZoom = undefined;
|
||||
} else {
|
||||
self.x = ( performance.now() - self.startTime ) / interpolationDuration;
|
||||
interpolatedZoom = Zoom.hermiteCubic.f(self.x, self.x1, self.x2, self.y1, self.y2, self.m1, self.m2);
|
||||
@@ -97,13 +104,13 @@ import { requestAnimFrame } from "./libs/RequestAnimationFrame.js";
|
||||
interpolatedZoom = Math.max(0, interpolatedZoom);
|
||||
|
||||
// Apply zoom level to map or perform any necessary rendering
|
||||
zoomFn(interpolatedZoom);
|
||||
self.view.setZoom(interpolatedZoom);
|
||||
|
||||
self.fov = interpolatedZoom;
|
||||
|
||||
if (self.x >= self.x2 || Math.abs(interpolatedZoom - self.finalZoom) <= relativeErr) {
|
||||
zoomFn(self.finalZoom);
|
||||
if (self.x >= self.x2 || Math.abs(interpolatedZoom - self.finalZoom) < 1e-4) {
|
||||
self.view.setZoom(self.finalZoom);
|
||||
|
||||
self.isZooming = false;
|
||||
} else if (self.view.wasm.atZoomBoundaries()) {
|
||||
self.isZooming = false;
|
||||
} else {
|
||||
// Request the next frame
|
||||
|
||||
@@ -528,15 +528,7 @@ export class OverlayStackBox extends Box {
|
||||
position: self.position,
|
||||
});*/
|
||||
self.aladin.addNewImageLayer(
|
||||
A.imageHiPS('P/DSS2/color', {
|
||||
errorCallback: (e) => {
|
||||
aladin.addStatusBarMessage({
|
||||
duration: 2000,
|
||||
type: 'info',
|
||||
message: 'DSS2 colored HiPS could not plot',
|
||||
})
|
||||
}
|
||||
})
|
||||
'P/DSS2/color'
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -940,6 +932,7 @@ export class OverlayStackBox extends Box {
|
||||
options.push(value)
|
||||
}
|
||||
|
||||
|
||||
let HiPSSelector = Input.select({
|
||||
value,
|
||||
options,
|
||||
|
||||
@@ -57,8 +57,8 @@ export class FullScreenActionButton extends ActionButton {
|
||||
if (aladin.statusBar) {
|
||||
aladin.statusBar.removeMessage('tooltip')
|
||||
}
|
||||
|
||||
aladin.toggleFullscreen(aladin.options.realFullscreen);
|
||||
|
||||
aladin.toggleFullscreen(aladin.options.realFullscreen);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ export let Circle = (function() {
|
||||
|
||||
this.color = options['color'] || undefined;
|
||||
this.fillColor = options['fillColor'] || undefined;
|
||||
this.lineWidth = options["lineWidth"] || 2;
|
||||
this.lineWidth = options["lineWidth"] || undefined;
|
||||
this.selectionColor = options["selectionColor"] || '#00ff00';
|
||||
this.hoverColor = options["hoverColor"] || undefined;
|
||||
this.opacity = options['opacity'] || 1;
|
||||
@@ -297,6 +297,10 @@ export let Circle = (function() {
|
||||
ctx.strokeStyle = baseColor;
|
||||
}
|
||||
|
||||
if (!this.lineWidth) {
|
||||
this.lineWidth = (this.overlay && this.overlay.lineWidth) || 2;
|
||||
}
|
||||
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
ctx.globalAlpha = this.opacity;
|
||||
ctx.beginPath();
|
||||
|
||||
@@ -51,7 +51,7 @@ export let Ellipse = (function() {
|
||||
|
||||
this.color = options['color'] || undefined;
|
||||
this.fillColor = options['fillColor'] || undefined;
|
||||
this.lineWidth = options["lineWidth"] || 2;
|
||||
this.lineWidth = options["lineWidth"] || undefined;
|
||||
this.selectionColor = options["selectionColor"] || '#00ff00';
|
||||
this.hoverColor = options["hoverColor"] || undefined;
|
||||
this.opacity = options['opacity'] || 1;
|
||||
@@ -283,6 +283,10 @@ export let Ellipse = (function() {
|
||||
ctx.strokeStyle = baseColor;
|
||||
}
|
||||
|
||||
if (!this.lineWidth) {
|
||||
this.lineWidth = (this.overlay && this.overlay.lineWidth) || 2;
|
||||
}
|
||||
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
ctx.globalAlpha = this.opacity;
|
||||
ctx.beginPath();
|
||||
|
||||
Reference in New Issue
Block a user