Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/firefly/js/drawingLayers/hpx/HpxDraw.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import chroma from 'chroma-js';
import {getHistgramForNorder, getProGradeTilePixels, getTile} from '../../tables/HpxIndexCntlr';
import {getHistgramForNorder, getTile} from '../../tables/HpxIndexCntlr';
import {getBWBackground} from '../../util/Color';
import CoordSys from '../../visualize/CoordSys';
import {TextLocation} from '../../visualize/draw/DrawingDef';
import {DrawSymbol} from '../../visualize/draw/DrawSymbol';
import FootprintObj from '../../visualize/draw/FootprintObj';
import PointDataObj from '../../visualize/draw/PointDataObj';
import ShapeDataObj, {UnitType} from '../../visualize/draw/ShapeDataObj';
import {getCornersForCell} from '../../visualize/HiPSUtil';
import {getCornersForCell, getProGradeTilePixels} from '../../visualize/HiPSUtil';
import {makeDevicePt} from '../../visualize/Point';
import {computeSimpleDistance, computeSimpleSlope, lineIntersect2, RtoD} from '../../visualize/VisUtil';
import {
Expand Down
8 changes: 2 additions & 6 deletions src/firefly/js/tables/HpxIndexCntlr.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import {ang2pixNest, radecToPolar} from '../externalSource/aladinProj/HealpixInd
import {logger} from '../util/Logger';
import {blockWhileAsyncIdWaiting, synchronizeAsyncFunctionById} from '../util/SynchronizeAsync';
import {createBackgroundRunner, isDefined} from '../util/WebUtil';
import {DEFAULT_COVERAGE_PLOT_ID} from '../visualize/VisConst';
import {getProGradeTilePixels} from '../visualize/HiPSUtil';
import {makeWorldPt} from '../visualize/Point';
import {DEFAULT_COVERAGE_PLOT_ID} from '../visualize/VisConst';
import {convertCelestial} from '../visualize/VisUtil';
import {findTableCenterColumns, makeWorldPtUsingCenterColumns} from '../voAnalyzer/TableAnalysis';
import {getTableModel} from '../voAnalyzer/VoCoreUtils';
Expand Down Expand Up @@ -513,11 +514,6 @@ export function onOrderDataReady(tbl_id) {



export function getProGradeTilePixels(pixel) {
const base= pixel*4;
return [base,base+1,base+2,base+3];
}

export const getRetroGradeIpix= (ipix, retroCnt=1) => retroCnt ? Math.trunc(ipix/(4**retroCnt)) : ipix;

const tableRunIds= {};
Expand Down
10 changes: 10 additions & 0 deletions src/firefly/js/visualize/HiPSUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -786,3 +786,13 @@ function getHipsPixelCutRangeValues(plot) {
}
return defaultRv;
}

/**
* get the 4 child pixels at the prograded norder
* @param {number} pixel
* @return {Array.<number>} array of 4 pixels
*/
export function getProGradeTilePixels(pixel) {
const base = pixel * 4;
return [base, base + 1, base + 2, base + 3];
}
3 changes: 1 addition & 2 deletions src/firefly/js/visualize/WebPlot.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ function getAllHeaderAndWlInfo(cubeCtx, wpInit, plotState) {
allWlMap[bStr]=processAllWavelengthAltWcs(headerAry[b.value], bandWlTable, idx>0);
});

plotState.getBands().forEach( (b,idx) => {
plotState.getBands().forEach( (b) => {
const bStr= b.toString();
if (wlDataAry[b.value]) {
allWlMap[bStr]['']= wlDataAry[b.value];
Expand Down Expand Up @@ -592,7 +592,6 @@ export const WebPlot= {

const hipsPlot= {
//HiPS specific
nside: 3,
colorTableId: NO_COLOR_TABLE,
hipsUrlRoot,
dataCoordSys : hipsCoordSys,
Expand Down
5 changes: 3 additions & 2 deletions src/firefly/js/visualize/draw/MocObj.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {getAppOptions} from '../../api/ApiUtil.js';
import {getProGradeTilePixels} from '../../tables/HpxIndexCntlr';
import {logger} from '../../util/Logger';
import DrawObj from './DrawObj';
import {makeWorldPt} from '../Point.js';
Expand All @@ -10,7 +9,9 @@ import {
getMocOrderIndex, getMocSidePointsNuniq, getCornerForPix, getMocNuniq,
isTileVisibleByPosition, initSidePoints, NSIDE4, getSidePointsNorder, NSIDE2
} from '../HiPSMocUtil.js';
import {getHealpixCornerTool, getAllVisibleHiPSCells, getPointMaxSide, getHiPSNorderlevel} from '../HiPSUtil.js';
import {
getHealpixCornerTool, getAllVisibleHiPSCells, getPointMaxSide, getHiPSNorderlevel, getProGradeTilePixels
} from '../HiPSUtil.js';
import DrawOp from './DrawOp.js';
import CsysConverter, {CysConverter} from '../CsysConverter.js';
import {Style, TextLocation,DEFAULT_FONT_SIZE} from './DrawingDef.js';
Expand Down
161 changes: 126 additions & 35 deletions src/firefly/js/visualize/iv/HiPSCellFinder.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
/*
* License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
*/
import {getCornersForCell, getVisibleHiPSCells, tileCoordsWrap} from 'firefly/visualize/HiPSUtil.js';
import {isHiPSAitoff} from 'firefly/visualize/WebPlot.js';
import {isQuadTileOnScreen} from 'firefly/visualize/iv/TileDrawHelper.jsx';
import {makeDevicePt} from 'firefly/visualize/Point.js';
import {CysConverter} from 'firefly/api/ApiUtilImage.jsx';
import { getHealpixCornerTool, getProGradeTilePixels, getVisibleHiPSCells, tileCoordsWrap} from '../HiPSUtil.js';
import {isHiPSAitoff} from '../WebPlot.js';
import {isQuadTileOnScreen} from './TileDrawHelper.jsx';
import {makeDevicePt} from '../Point';
import {CysConverter} from '../CsysConverter';

const imageOffsets= [[0,0],[0,1],[1,0],[1,1]]; // which half of the parent tile image, in units of the child tile size

/**
* @global
* @public
* @typedef {Object} HiPSDeviceTileData
*
* @prop {number} tileNumber - HiPS pixel number
* @prop {number} nside - healpix level
* @prop {number} norder - healpix order
* @prop {Array.<DevicePt>} devPtCorners - the target corners of the tile in device coordinates
* @prop {number} dx - x offset into image
* @prop {number} dy - y offset into image
* @prop {boolean} [coordsWrap]
* @prop {number} dx - x offset into image, for a subCell of a normal tile: 0 or 1, the half of the parent tile
* @prop {number} dy - y offset into image, for a subCell of a normal tile: 0 or 1, the half of the parent tile
* @prop {boolean} [incompleteCell] - some corners cannot be projected or the cell wraps, only the subCells are drawn
* @prop {Array.<HiPSDeviceTileData>} [subCells]
*/

Expand All @@ -40,22 +42,30 @@ export function findCellOnScreen(plot, viewDim, norder, fov,centerWp, desiredNor
let badCnt;
let cell;
let devPtCorners;
const dataCoordSys= plot.dataCoordSys;
const centerDevPt= cc.getDeviceCoords(centerWp);
// this function is performance sensitive, use for loops instead of map and filter
if (isHiPSAitoff(plot)){
const aitoffAll= fov>200;
for(let i= 0; (i<cells.length); i++) {
cell= tileCoordsWrap(cc, cells[i].wpCorners) ? makeWrappingCell(cells[i],norder,plot.dataCoordSys, cc) : cells[i];
cell= cells[i];
const coordsWrap = tileCoordsWrap(cc, cell.wpCorners);
devPtCorners= [];
if (!cell.coordsWrap || !aitoffAll ) {
if (coordsWrap) {
const subCells= computeIncompleteTiles(cc, dataCoordSys, norder, cell.ipix, viewDim, {checkWrap:true, allOnScreen:aitoffAll});
if (subCells) {
retCells.push({devPtCorners, tileNumber:cell.ipix, dx:0, dy:0, norder, incompleteCell:true, subCells});
}
}
else {
for(let j=0; (j<cell.wpCorners.length); j++) {
devPtCorners[j]= cc.getDeviceCoords(cell.wpCorners[j]);
if (!devPtCorners[j]) break;
}
}
if (aitoffAll || isQuadTileOnScreen(devPtCorners, viewDim)) {
retCells.push({devPtCorners, tileNumber:cell.ipix, dx:0, dy:0, nside: norder,
coordsWrap:Boolean(cell.coordsWrap), subCells: cell.subCells});
if (aitoffAll || isQuadTileOnScreen(devPtCorners, viewDim)) {
const subCells= computeDeeperTiles(cc,dataCoordSys,norder,desiredNorder,cell.ipix);
retCells.push({devPtCorners, tileNumber:cell.ipix, dx:0, dy:0, norder, incompleteCell:false, subCells});
}
}
}
}
Expand All @@ -72,39 +82,120 @@ export function findCellOnScreen(plot, viewDim, norder, fov,centerWp, desiredNor
}
if (badCnt===1) devPtCorners= shim1DevPtCorner(devPtCorners,centerDevPt, viewDim);
else if (badCnt===2) devPtCorners= shim2DevPtCorner(devPtCorners,centerDevPt, viewDim);
if (isQuadTileOnScreen(devPtCorners, viewDim)) {
retCells.push({devPtCorners, tileNumber:cells[i].ipix, dx:0, dy:0, nside: norder});

if (badCnt>2) {
const subCells= computeIncompleteTiles(cc,dataCoordSys,norder,cells[i].ipix,viewDim, {centerDevPt});
if (subCells) retCells.push({devPtCorners, tileNumber:cells[i].ipix, dx:0, dy:0, norder,subCells, incompleteCell:true});
}
else if (isQuadTileOnScreen(devPtCorners, viewDim)) {
const subCells= computeDeeperTiles(cc,dataCoordSys,norder,desiredNorder,cells[i].ipix);
retCells.push({devPtCorners, tileNumber:cells[i].ipix, dx:0, dy:0, norder,subCells});
}
}
}
return retCells;
}

/**
*
* @param wc
* @param {number} norder
* Compute the drawable parts of a cell that has some corners that cannot be projected (e.g. at the edge of the sky)
* or, with checkWrap, that wraps around the edge of an aitoff projection.
* Children that fully project are kept, incomplete children are replaced by their fully projected grandchildren.
* Only parts that are on the screen are kept, unless allOnScreen is true.
* @param {CysConverter} cc
* @param {CoordinateSys} dataCoordSys
* @param {number} norder - norder of the parent tile
* @param {number} npix - healpix pixel number of the parent tile
* @param {{width:number,height:number}} viewDim
* @param {Object} [options]
* @param {boolean} [options.checkWrap] - treat a child that wraps as incomplete
* @param {boolean} [options.allOnScreen] - skip the on screen check, everything is visible (aitoff full sky)
* @param {DevicePt} [options.centerDevPt] - center of the view, if defined grandchild cells are shimmed
* @return {Array.<HiPSDeviceTileData>|undefined} the drawable child cells, or undefined if none
*/
function computeIncompleteTiles(cc, dataCoordSys, norder, npix, viewDim, {checkWrap=false, allOnScreen=false, centerDevPt}={}) {
const onScreen= (t) => !t.incompleteCell && (allOnScreen || isQuadTileOnScreen(t.devPtCorners, viewDim));
const subTiles= getProgradeTiles(cc,dataCoordSys,norder,npix, {allowPartial:true, checkWrap});
if (!subTiles) return;
subTiles.forEach( (t) => {
if (t.incompleteCell) {
const grandChildCells= (getProgradeTiles(cc,dataCoordSys,norder+1,t.tileNumber,
{allowPartial:true, checkWrap, shim:Boolean(centerDevPt), centerDevPt, viewDim}) ?? [])
.filter(onScreen);
t.subCells= grandChildCells.length>0 ? grandChildCells : undefined;
}
});
const retSubTiles= subTiles.filter( (t) => t.incompleteCell ? t.subCells : onScreen(t));
return retSubTiles.length>0 ? retSubTiles : undefined;
}

/**
* Compute the child cells of a tile so it can be drawn as smaller quads when zoomed in beyond the tile norder.
* Goes at most two levels deep: one level when desiredNorder is norder+1, otherwise two (the second in each child's subCells).
* The dx/dy of each child is 0 or 1, the half of the parent tile image it comes from.
* @param {CysConverter} cc
* @prop {HiPSDeviceTileData}
* @param {CoordinateSys} dataCoordSys
* @param {number} norder - norder of the parent tile
* @param {number} desiredNorder
* @param {number} npix - healpix pixel number of the parent tile
* @return {Array.<HiPSDeviceTileData>|undefined} the 4 child cells, or undefined if not needed or any child cannot be projected
*/
function makeWrappingCell(wc, norder, dataCoordSys, cc) {
if (norder>2) return {...wc, coordsWrap:true};
const nside= norder+1;
const subCells= Array(4).fill(nside,0,4).map( (sc,idx) => {
const tileNumber = wc.ipix * 4 + idx;
const wpCorners= getCornersForCell(nside, tileNumber, dataCoordSys);
return {
nside, tileNumber, wpCorners, dx: 0, dy: 0,
devPtCorners: wpCorners.map( (wp) => cc.getDeviceCoords(wp)),
};
}
)
.filter( (sc) => !tileCoordsWrap(cc, sc.wpCorners, 10));
return { ...wc, coordsWrap: true, subCells };
function computeDeeperTiles(cc, dataCoordSys, norder, desiredNorder, npix) {
if (!desiredNorder) return;
if (desiredNorder<=norder) return;
const subTiles= getProgradeTiles(cc,dataCoordSys,norder,npix);
if (!subTiles) return;
if (desiredNorder===norder+1) return subTiles;
subTiles.forEach( (t) => {
t.subCells= getProgradeTiles(cc,dataCoordSys,norder+1,t.tileNumber);
});
return subTiles;
}

/**
* Make the 4 child cells (at norder+1) of a healpix pixel, with device corners and image offsets.
* @param {CysConverter} cc
* @param {CoordinateSys} dataCoordSys
* @param {number} norder - norder of the parent tile
* @param {number} npix - healpix pixel number of the parent tile
* @param {Object} [options]
* @param {boolean} [options.allowPartial] - allow for an incomplete tile set
* @param {boolean} [options.checkWrap] - a child that wraps (aitoff) gets no corners and is marked incomplete
* @param {boolean} [options.shim] - if true, fill in 1 or 2 unprojectable corners of a child using the shim functions
* @param {DevicePt} [options.centerDevPt] - center of the view, required when shim is true
* @param {{width:number,height:number}} [options.viewDim] - required when shim is true
* @return {Array.<HiPSDeviceTileData>|undefined} the 4 child cells, or undefined if any child cannot be projected
* and allowPartial is false. With allowPartial, a child with unprojectable corners has incompleteCell set.
* The devPtCorners of an incomplete child only has the projectable corners, so the positions do not match the healpix corners.
*/
function getProgradeTiles(cc, dataCoordSys, norder, npix,
{allowPartial=false, checkWrap=false, shim=false, centerDevPt, viewDim}={}) {
const proNorder= norder+1;
const proNside= 2**proNorder;
const healpixCache=getHealpixCornerTool();
const pgTiles= getProGradeTilePixels(npix)
.map( (tileNumber,idx) => {
const {wpCorners}= healpixCache.makeCornersForPix(tileNumber, proNside, dataCoordSys);
const wraps= checkWrap && tileCoordsWrap(cc, wpCorners, 10);
const devCorners= wraps ? [] : wpCorners.map( (wp) => cc.getDeviceCoords(wp));
let devPtCorners= devCorners.filter(Boolean);
if (shim && devPtCorners.length<4 && centerDevPt && viewDim) {
if (devPtCorners.length===3) devPtCorners= shim1DevPtCorner(devCorners,centerDevPt, viewDim);
else if (devPtCorners.length===2) devPtCorners= shim2DevPtCorner(devCorners,centerDevPt, viewDim);
}

if (!allowPartial && devPtCorners.length!==4) return;

return {
tileNumber,
norder: proNorder,
devPtCorners,
incompleteCell: devPtCorners.length!==4,
dx: imageOffsets[idx][0],
dy: imageOffsets[idx][1],
};
});
return pgTiles.every(Boolean) ? pgTiles : undefined; // a child is only undefined when allowPartial is false
}

function shim1DevPtCorner(devPtCorners,centerDevPt, viewDim) {
const {width,height}= viewDim;
Expand Down
43 changes: 19 additions & 24 deletions src/firefly/js/visualize/iv/HiPSRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*/

import {isNil} from 'lodash';
import BrowserInfo from '../../util/BrowserInfo';
import {callWhileAwaiting} from '../../util/WebUtil';
import {NO_COLOR_TABLE} from '../rawData/ColorTable';
import {retrieveAndProcessImage} from './ImageProcessor.js';
Expand Down Expand Up @@ -47,6 +46,20 @@ export function makeHipsRenderer(screenRenderParams, totalCnt, isBaseImage, scre
// ------------------------- private functions
// ------------------------------------------------------------

const normalCaseDrawOneCell= (tile, image, tileSize= tile.tileSize || image.width, x= tile.dx, y= tile.dy) => {
if (tile.subCells && (tile.incompleteCell || desiredNorder>norder)) {
const half= tileSize/2;
tile.subCells.forEach( (subTile) => {
normalCaseDrawOneCell(subTile,image, half, x+subTile.dx*half, y+subTile.dy*half);
});
}
else {
if (!tile.incompleteCell) {
drawOneHiPSTile(offscreenCtx, image, tile.devPtCorners, tileSize, {x,y}, isMaxOrder, norder, desiredNorder);
}
}
};

/**
* draw a single tile async (retrieve image and draw tile)
* @param src
Expand Down Expand Up @@ -117,21 +130,15 @@ export function makeHipsRenderer(screenRenderParams, totalCnt, isBaseImage, scre
}
if (abortRender) return;

const tileSize= tile.tileSize || image.width;
drawOneHiPSTile(offscreenCtx, image, tile.devPtCorners, tileSize, {x:tile.dx,y:tile.dy}, isMaxOrder, norder, desiredNorder);
normalCaseDrawOneCell(tile,image);


if (doRenderNow()) renderToScreen();
renderComplete= (renderedCnt === totalCnt);
}).catch(() => {
renderedCnt++;
if (abortRender) return;
if (tile.devPtCorners.filter( (t) => t).length ===4) {
drawOneHiPSTile(offscreenCtx, emptyTileCanvas, tile.devPtCorners, 512, {x:tile.dx,y:tile.dy}, isMaxOrder, norder, desiredNorder);
}
else {
console.log('********************* found one');
}
normalCaseDrawOneCell(tile, emptyTileCanvas);
addFailedImage(src);
if (doRenderNow()) {
renderComplete= true;
Expand All @@ -145,21 +152,9 @@ export function makeHipsRenderer(screenRenderParams, totalCnt, isBaseImage, scre
* draw a tile when all
* @param image
* @param {HiPSDeviceTileData} tile
* @param {HiPSAllSkyCacheInfo} [cachedAllSkyData]
*/
const drawTileImmediate= (image, tile, cachedAllSkyData) => {
if (image) {
if (tile.coordsWrap && cachedAllSkyData) {
tile.subCells?.forEach( (cell) => {
const subImage= cachedAllSkyData.order3Array[cell.tileNumber];
drawOneHiPSTile(offscreenCtx, subImage, cell.devPtCorners, subImage.width, {x:0,y:0}, isMaxOrder, norder, desiredNorder);
});
}
else {
const tileSize= tile.tileSize || image.width;
drawOneHiPSTile(offscreenCtx, image, tile.devPtCorners, tileSize, {x:tile.dx,y:tile.dy}, isMaxOrder, norder, desiredNorder);
}
}
const drawTileImmediate= (image, tile) => {
if (image) normalCaseDrawOneCell(tile,image);
renderedCnt++;
if (renderedCnt === totalCnt) {
renderComplete= true;
Expand Down Expand Up @@ -229,7 +224,7 @@ export function makeHipsRenderer(screenRenderParams, totalCnt, isBaseImage, scre
if (abortRender) return;
const allSkyAry= norder===3 ? cachedAllSky.order3Array : cachedAllSky.order2Array;
for(let i=0; i<tilesToLoad.length; i++) { // do a classic for loop to increase the fps by 3 or 4
drawTileImmediate(allSkyAry[tilesToLoad[i].tileNumber], tilesToLoad[i], cachedAllSky);
drawTileImmediate(allSkyAry[tilesToLoad[i].tileNumber], tilesToLoad[i]);
}
},

Expand Down
Loading
Loading