@cquiroz/aladin-lite
Version:
AladinLite module
757 lines (642 loc) • 22.5 kB
JavaScript
import Constants from './Constants';
import SpatialVector from './SpatialVector';
class LongRangeSetBuilder {
constructor() {
this.items = [];
}
appendRange(first, last) {
for (var i = first; last >= i; i++) {
i in this.items || this.items.push(i);
}
}
}
function bigAnd(v1, v2) {
var hi = 0x80000000;
var low = 0x7fffffff;
var hi1 = ~~(v1 / hi);
var hi2 = ~~(v2 / hi);
var low1 = (v1 & low) >>> 0;
var low2 = (v2 & low) >>> 0;
var h = (hi1 & hi2) >>> 0;
var l = (low1 & low2) >>> 0;
return h * hi + l;
}
function bigOr(v1, v2) {
var hi = 0x80000000;
var low = 0x7fffffff;
var hi1 = ~~(v1 / hi);
var hi2 = ~~(v2 / hi);
var low1 = (v1 & low) >>> 0;
var low2 = (v2 & low) >>> 0;
var h = (hi1 | hi2) >>> 0;
var l = (low1 | low2) >>> 0;
return h * hi + l;
}
var orAll = function orAll() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return args.reduce((prev, curr) => bigOr(prev, curr), 0);
};
var powerOf2 = new Array(53).fill(0).map((val, idx) => 2 ** idx);
var shiftRight = (v, bits) => Math.trunc(v / powerOf2[bits]);
var shiftLeft = (v, bits) => Math.trunc(v * powerOf2[bits]);
export var ORDER_MAX = 26;
var NSIDELIST = new Array(ORDER_MAX).fill(0).map((val, idx) => 2 ** idx);
var JPLL = [1, 3, 5, 7, 0, 2, 4, 6, 1, 3, 5, 7];
var JRLL = [2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4];
var NS_MAX = NSIDELIST[NSIDELIST.length - 1];
var Z0 = Constants.TWOTHIRD;
var TAB_SIZE = 256;
/* eslint-disable no-mixed-operators */
var CTAB = new Array(TAB_SIZE).fill(0).map((v, i) => 1 & i | (2 & i) << 7 | (4 & i) >>> 1 | (8 & i) << 6 | (16 & i) >>> 2 | (32 & i) << 5 | (64 & i) >>> 3 | (128 & i) << 4);
var UTAB = new Array(TAB_SIZE).fill(0).map((v, i) => 1 & i | (2 & i) << 1 | (4 & i) << 2 | (8 & i) << 3 | (16 & i) << 4 | (32 & i) << 5 | (64 & i) << 6 | (128 & i) << 7);
/* eslint-enable no-mixed-operators */
export class HealpixIndex {
constructor(nside) {
this.nside = nside;
this.nl2 = 2 * nside;
this.nl3 = 3 * nside;
this.nl4 = 4 * nside;
this.npface = nside * nside;
this.ncap = 2 * nside * (nside - 1);
this.npix = 12 * this.npface;
this.fact2 = 4 / this.npix;
this.fact1 = (nside << 1) * this.fact2;
this.order = HealpixIndex.nside2order(nside);
}
static calculateNSide(pixsize) {
var i = 0;
var n = pixsize * pixsize;
var a = 180 / Constants.PI;
var e = 3600 * 3600 * 4 * Constants.PI * a * a;
var h = parseInt(e / n);
var r = h / 12;
var o = Math.sqrt(r);
var c = NS_MAX;
var u = 0;
for (var p = 0; NSIDELIST.length > p; p++) {
if (c >= Math.abs(o - NSIDELIST[p])) {
c = Math.abs(o - NSIDELIST[p]);
i = NSIDELIST[p];
u = p;
}
if (o > i && NS_MAX > o) {
i = NSIDELIST[u + 1];
}
if (o > NS_MAX) {
console.log('nside cannot be bigger than ' + NS_MAX);
return NS_MAX;
}
}
return i;
}
static nside2order(nside) {
return (nside & nside - 1) > 0 ? -1 : parseInt(Math.log2(nside));
}
ang2pix_nest(theta, phi) {
var tp, o, c, jp, jm, ntt, face_num, ix, iy;
phi >= Constants.TWOPI && (phi -= Constants.TWOPI);
0 > phi && (phi += Constants.TWOPI);
if (theta > Constants.PI || 0 > theta) {
throw new Error({
name: 'Illegal argument',
message: 'theta must be between 0 and ' + Constants.PI
});
}
if (0 > phi) {
throw new Error({
name: 'Illegal argument',
message: 'phi must be between 0 and ' + Constants.TWOPI
});
}
var z = Math.cos(theta);
var za = Math.abs(z);
var tt = phi / Constants.PIOVER2;
if (Z0 >= za) {
//Equatorial region
var M = this.nside * (.5 + tt);
var y = this.nside * .75 * z;
var u = M - y;
var p = M + y; // o = u >>> this.order;
// c = p >>> this.order;
o = shiftRight(u, this.order);
c = shiftRight(p, this.order);
face_num = o === c ? 4 === o ? 4 : o + 4 : c > o ? o : c + 8;
ix = parseInt(p & this.nside - 1);
iy = parseInt(this.nside - (u & this.nside - 1) - 1);
} else {
// polar region, za > 2/3
ntt = parseInt(tt);
if (ntt >= 4) ntt = 3;
tp = tt - ntt;
var tmp = this.nside * Math.sqrt(3 * (1 - za)); // (the index of edge lines increase when distance from the closest pole goes up)
jp = parseInt(tp * tmp);
jm = parseInt((1 - tp) * tmp);
jp = Math.min(NS_MAX - 1, jp);
jm = Math.min(NS_MAX - 1, jm); // finds the face and pixel's (x,y)
if (z >= 0) {
face_num = ntt; // in {0,3}
ix = parseInt(this.nside - jm - 1);
iy = parseInt(this.nside - jp - 1);
} else {
face_num = ntt + 8; // in {8,11}
ix = jp;
iy = jm;
}
}
return this.xyf2nest(ix, iy, face_num);
}
xyf2nest(ix, iy, face_num) {
// const nest= (face_num << 2 * this.order) +
// (this.utab[255 & ix] |
// this.utab[255 & ix >>> 8] * Math.pow(2,16) |
// this.utab[255 & ix >>> 16] * Math.pow(2,32)|
// this.utab[255 & ix >>> 24] * Math.pow(2,48)|
// this.utab[255 & iy] << 1 |
// this.utab[255 & iy >>> 8] * Math.pow(2,17) |
// this.utab[255 & iy >>> 16] * Math.pow(2,33) |
// this.utab[255 & iy >>> 24] * Math.pow(2,49));
var nest = shiftLeft(face_num, 2 * this.order) + orAll(UTAB[255 & ix], shiftLeft(UTAB[bigAnd(255, shiftRight(ix, 8))], 16), shiftLeft(UTAB[bigAnd(255, shiftRight(ix, 16))], 32), shiftLeft(UTAB[bigAnd(255, shiftRight(ix, 24))], 48), shiftLeft(UTAB[bigAnd(255, iy)], 1), shiftLeft(UTAB[bigAnd(255, shiftRight(iy, 8))], 17), shiftLeft(UTAB[bigAnd(255, shiftRight(iy, 16))], 33), shiftLeft(UTAB[bigAnd(255, shiftRight(iy, 24))], 49)); // if (nest>0x7FFFFFF) {
// console.log('xyf2nest: nest greater');
// }
return nest;
}
nest2xyf(ipix) {
// if (ipix>0x7FFFFFF) {
// console.log('nest2xyf: ipix greater');
// }
var s = {};
s.face_num = shiftRight(ipix, 2 * this.order);
var i = bigAnd(ipix, this.npface - 1); // n = (93823560581120 & i) >>> 16 | (614882086624428e4 & i) >>> 31 | 21845 & i | (1431633920 & i) >>> 15;
var n = orAll(shiftRight(bigAnd(0x555500000000, i), 16), shiftRight(bigAnd(0x5555000000000000, i), 31), bigAnd(0x5555, i), shiftRight(bigAnd(0x55550000, i), 15)); // s.ix = this.ctab[255 & n] | this.ctab[255 & n >>> 8] << 4 | this.ctab[255 & n >>> 16] << 16 | this.ctab[255 & n >>> 24] << 20;
s.ix = orAll(CTAB[bigAnd(255, n)], shiftLeft(CTAB[255 & shiftRight(n, 8)], 4), shiftLeft(CTAB[255 & shiftRight(n, 16)], 16), shiftLeft(CTAB[255 & shiftRight(n, 24)], 20));
i = shiftRight(i, 1); // n = (93823560581120 & i) >>> 16 | (614882086624428e4 & i) >>> 31 | 21845 & i | (1431633920 & i) >>> 15;
n = orAll(shiftRight(bigAnd(0x555500000000, i), 16), shiftRight(bigAnd(0x5555000000000000, i), 31), bigAnd(0x5555, i), shiftRight(bigAnd(0x55550000, i), 15)); // s.iy = this.ctab[255 & n] | this.ctab[255 & n >>> 8] << 4 | this.ctab[255 & n >>> 16] << 16 | this.ctab[255 & n >>> 24] << 20;
s.iy = orAll(CTAB[bigAnd(255, n)], shiftLeft(CTAB[255 & shiftRight(n, 8)], 4), shiftLeft(CTAB[255 & shiftRight(n, 16)], 16), shiftLeft(CTAB[255 & shiftRight(n, 24)], 20));
return s;
}
pix2ang_nest(ipix) {
if (0 > ipix || ipix > this.npix - 1) {
throw new Error({
name: 'Illegal argument',
message: 'ipix out of range'
});
}
var nr, z, kshift;
var e = this.nest2xyf(ipix);
var h = e.ix;
var r = e.iy;
var o = e.face_num;
var jr = (JRLL[o] << this.order) - h - r - 1;
if (this.nside > jr) {
nr = jr;
z = 1 - nr * nr * this.fact2;
kshift = 0;
} else if (jr > this.nl3) {
nr = this.nl4 - jr;
z = nr * nr * this.fact2 - 1;
kshift = 0;
} else {
nr = this.nside;
z = (this.nl2 - jr) * this.fact1;
kshift = 1 & jr - this.nside;
}
var theta = Math.acos(z);
var jp = (JPLL[o] * nr + h - r + 1 + kshift) / 2;
jp > this.nl4 && (jp -= this.nl4);
1 > jp && (jp += this.nl4);
var phi = (jp - .5 * (kshift + 1)) * (Constants.PIOVER2 / nr);
return {
theta,
phi
};
}
static nside2Npix(nside) {
if (0 > nside || (nside & -nside) !== nside || nside > NS_MAX) {
throw new Error({
name: 'Illegal argument',
message: 'nside should be >0, power of 2, <' + NS_MAX
});
}
var i = 12 * nside * nside;
return i;
}
xyf2ring(ix, iy, face_num) {
var nr, kshift, startpix;
var r = JRLL[face_num] * this.nside - ix - iy - 1;
if (this.nside > r) {
nr = r;
startpix = 2 * nr * (nr - 1);
kshift = 0;
} else if (r > 3 * this.nside) {
nr = this.nl4 - r;
startpix = this.npix - 2 * (nr + 1) * nr;
kshift = 0;
} else {
nr = this.nside;
startpix = this.ncap + (r - this.nside) * this.nl4;
kshift = 1 & r - this.nside;
}
var jp = (JPLL[face_num] * nr + ix - iy + 1 + kshift) / 2;
if (jp > this.nl4) {
jp -= this.nl4;
} else if (1 > jp) {
jp += this.nl4;
}
return startpix + jp - 1;
}
nest2ring(ipnest) {
var s = this.nest2xyf(ipnest);
var i = this.xyf2ring(s.ix, s.iy, s.face_num);
return i;
}
corners_nest(ipix, step) {
var i = this.nest2ring(ipix);
return this.corners_ring(i, step);
}
pix2ang_ring(ipix) {
var theta, phi, iring, iphi, ip, fodd, hip, fihip;
if (0 > ipix || ipix > this.npix - 1) {
throw new Error({
name: 'Illegal argument',
message: 'ipix out of range'
});
}
var ipix1 = ipix + 1; // in {1, npix}
if (this.ncap >= ipix1) {
hip = ipix1 / 2;
fihip = parseInt(hip);
iring = parseInt(Math.sqrt(hip - Math.sqrt(fihip))) + 1;
iphi = ipix1 - 2 * iring * (iring - 1);
theta = Math.acos(1 - iring * iring * this.fact2);
phi = (iphi - .5) * Constants.PI / (2 * iring);
} else {
if (this.npix - this.ncap > ipix) {
ip = ipix - this.ncap;
iring = ip / this.nl4 + this.nside;
iphi = ip % this.nl4 + 1;
fodd = bigAnd(1, iring + this.nside) > 0 ? 1 : .5;
theta = Math.acos((this.nl2 - iring) * this.fact1);
phi = (iphi - fodd) * Constants.PI / this.nl2;
} else {
ip = this.npix - ipix;
iring = parseInt(.5 * (1 + Math.sqrt(2 * ip - 1)));
iphi = 4 * iring + 1 - (ip - 2 * iring * (iring - 1));
theta = Math.acos(-1 + Math.pow(iring, 2) * this.fact2);
phi = (iphi - .5) * Constants.PI / (2 * iring);
}
}
return [theta, phi];
}
ring(ipix) {
var s,
i,
n = 0;
var a = ipix + 1;
var e = 0;
if (this.ncap >= a) {
i = a / 2;
e = parseInt(i);
n = parseInt(Math.sqrt(i - Math.sqrt(e))) + 1;
} else if (this.nl2 * (5 * this.nside + 1) >= a) {
s = parseInt(a - this.ncap - 1);
n = parseInt(s / this.nl4 + this.nside);
} else {
s = this.npix - a + 1;
i = s / 2;
e = parseInt(i);
n = parseInt(Math.sqrt(i - Math.sqrt(e))) + 1;
n = this.nl4 - n;
}
return n;
}
integration_limits_in_costh(i_th) {
var s, i, n;
var a = 1 * this.nside;
if (this.nside >= i_th) {
i = 1 - Math.pow(i_th, 2) / 3 / this.npface;
n = 1 - Math.pow(i_th - 1, 2) / 3 / this.npface;
s = i_th === this.nside ? 2 * (this.nside - 1) / 3 / a : 1 - Math.pow(i_th + 1, 2) / 3 / this.npface;
} else if (this.nl3 > i_th) {
i = 2 * (2 * this.nside - i_th) / 3 / a;
n = 2 * (2 * this.nside - i_th + 1) / 3 / a;
s = 2 * (2 * this.nside - i_th - 1) / 3 / a;
} else {
n = i_th === this.nl3 ? 2 * (-this.nside + 1) / 3 / a : -1 + Math.pow(4 * this.nside - i_th + 1, 2) / 3 / this.npface;
s = -1 + Math.pow(this.nl4 - i_th - 1, 2) / 3 / this.npface;
i = -1 + Math.pow(this.nl4 - i_th, 2) / 3 / this.npface;
}
return [n, i, s];
}
pixel_boundaries(i_th, i_phi, i_zone, cos_theta) {
var sq3th, factor, jd, ju, ku, kd, phi_l, phi_r;
var r_n_nside = 1 * this.nside;
if (Math.abs(cos_theta) >= 1 - 1 / 3 / this.npface) {
phi_l = i_zone * Constants.PIOVER2;
phi_r = (i_zone + 1) * Constants.PIOVER2;
return [phi_l, phi_r];
}
if (1.5 * cos_theta >= 1) {
sq3th = Math.sqrt(3 * (1 - cos_theta));
factor = 1 / r_n_nside / sq3th;
jd = i_phi;
ju = jd - 1;
ku = i_th - i_phi;
kd = ku + 1;
phi_l = Constants.PIOVER2 * (Math.max(ju * factor, 1 - kd * factor) + i_zone);
phi_r = Constants.PIOVER2 * (Math.min(1 - ku * factor, jd * factor) + i_zone);
} else if (1.5 * cos_theta > -1) {
var cth34 = .5 * (1 - 1.5 * cos_theta);
var cth34_1 = cth34 + 1;
var modfactor = this.nside + i_th % 2;
jd = i_phi - (modfactor - i_th) / 2;
ju = jd - 1;
ku = (modfactor + i_th) / 2 - i_phi;
kd = ku + 1;
phi_l = Constants.PIOVER2 * (Math.max(cth34_1 - kd / r_n_nside, -cth34 + ju / r_n_nside) + i_zone);
phi_r = Constants.PIOVER2 * (Math.min(cth34_1 - ku / r_n_nside, -cth34 + jd / r_n_nside) + i_zone);
} else {
sq3th = Math.sqrt(3 * (1 + cos_theta));
factor = 1 / r_n_nside / sq3th;
var M = 2 * this.nside;
jd = i_th - M + i_phi;
ju = jd - 1;
ku = M - i_phi;
kd = ku + 1;
phi_l = Constants.PIOVER2 * (Math.max(1 - (M - ju) * factor, (M - kd) * factor) + i_zone);
phi_r = Constants.PIOVER2 * (Math.min(1 - (M - jd) * factor, (M - ku) * factor) + i_zone);
}
return [phi_l, phi_r];
}
static vector(theta, phi) {
var x = Math.sin(theta) * Math.cos(phi);
var y = Math.sin(theta) * Math.sin(phi);
var z = Math.cos(theta);
return new SpatialVector(x, y, z);
}
corners_ring(pix, step) {
var n = 2 * step + 2;
var res = Array(n);
var e = this.pix2ang_ring(pix);
var h = Math.cos(e[0]);
var r = e[0];
var o = e[1];
var c = parseInt(o / Constants.PIOVER2);
var u = this.ring(pix);
var p = Math.min(u, Math.min(this.nside, this.nl4 - u));
var d = Constants.PIOVER2 / p;
var l = u >= this.nside && this.nl3 >= u ? parseInt(o / d + u % 2 / 2) + 1 : parseInt(o / d) + 1;
l -= c * p;
var f = n / 2;
var I = this.integration_limits_in_costh(u);
var M = Math.acos(I[0]);
var y = Math.acos(I[2]);
var g = this.pixel_boundaries(u, l, c, I[0]);
res[0] = l > p / 2 ? HealpixIndex.vector(M, g[1]) : HealpixIndex.vector(M, g[0]);
g = this.pixel_boundaries(u, l, c, I[2]);
res[f] = l > p / 2 ? HealpixIndex.vector(y, g[1]) : HealpixIndex.vector(y, g[0]);
if (1 === step) {
var P = Math.acos(I[1]);
g = this.pixel_boundaries(u, l, c, I[1]);
res[1] = HealpixIndex.vector(P, g[0]);
res[3] = HealpixIndex.vector(P, g[1]);
} else {
var x = I[2] - I[0];
var C = x / (step + 1);
for (var v = 1; step >= v; v++) {
h = I[0] + C * v;
r = Math.acos(h);
g = this.pixel_boundaries(u, l, c, h);
res[v] = HealpixIndex.vector(r, g[0]);
res[n - v] = HealpixIndex.vector(r, g[1]);
}
}
return res;
}
static vec2Ang(spatialVector) {
var s = spatialVector.z / spatialVector.length();
var i = Math.acos(s);
var n = 0;
if (0 !== spatialVector.x || 0 !== spatialVector.y) {
n = Math.atan2(spatialVector.y, spatialVector.x);
}
if (0 > n) {
n += 2 * Math.PI;
}
return [i, n];
}
/**
* Returns a range set of pixels whose centers lie within a given disk. <p>
* This method is more efficient in the RING scheme.
* @param {SpatialVector} spatialVector the angular coordinates of the disk center
* @param {number} radius the radius (in radians) of the disk
* @param {boolean} nest true if nest, false if ring
* @param {boolean} inclusive
* @return {Array.<number> }the requested set of pixel number ranges
*/
queryDisc(spatialVector, radius, nest, inclusive) {
if (0 > radius || radius > Constants.PI) {
throw new Error({
name: 'Illegal argument',
message: 'angular radius is in RADIAN and should be in [0,pi]'
});
}
var d, f, y, v;
var pixset = new LongRangeSetBuilder();
var rsmall = inclusive ? radius + Constants.PI / this.nl4 : radius;
var [theta, phi] = HealpixIndex.vec2Ang(spatialVector);
var z0 = Math.cos(theta);
var xa = 1 / Math.sqrt((1 - z0) * (1 + z0));
var rlat1 = theta - rsmall;
var rlat2 = theta + rsmall;
var cosrsmall = Math.cos(rsmall);
var zmax = Math.cos(rlat1);
var irmin = this.ringAbove(zmax) + 1;
var zmin = Math.cos(rlat2);
var h = this.ringAbove(zmin);
if (irmin > h && 0 === h) h = irmin;
if (0 >= rlat1) {
for (var m = 1; irmin > m; ++m) {
this.inRing(m, 0, Math.PI, pixset);
}
}
for (var iz = irmin; h >= iz; ++iz) {
v = this.nside > iz ? 1 - iz * iz * this.fact2 : this.nl3 >= iz ? (this.nl2 - iz) * this.fact1 : -1 + (this.nl4 - iz) * (this.nl4 - iz) * this.fact2;
d = (cosrsmall - v * z0) * xa;
f = 1 - v * v - d * d;
y = Math.atan2(Math.sqrt(f), d);
if (isNaN(y)) y = rsmall;
this.inRing(iz, phi, y, pixset);
}
if (rlat2 >= Math.PI) {
for (var _m = h + 1; this.nl4 > _m; ++_m) {
this.inRing(_m, 0, Math.PI, pixset, false);
}
}
if (nest) {
var nestPixset = [];
for (var i = 0; pixset.items.length > i; i++) {
var nestPix = this.ring2nest(pixset.items[i]);
nestPixset.indexOf(nestPix) >= 0 || nestPixset.push(nestPix);
} // console.log(retPixAry);
return nestPixset;
} else {
return pixset.items;
}
}
inRing(iz, phi0, dphi, nest, conservative) {
var e,
h,
r,
o,
c = false,
d = 0,
f = 0,
I = 0,
u;
var p = 1e-12;
var M = (phi0 - dphi) % Constants.TWOPI - p;
var y = phi0 + dphi + p;
var g = (phi0 + dphi) % Constants.TWOPI + p;
if (p > Math.abs(dphi - Constants.PI)) c = true;
if (iz >= this.nside && this.nl3 >= iz) {
d = iz - this.nside + 1;
r = this.ncap + this.nl4 * (d - 1);
o = r + this.nl4 - 1;
e = d % 2;
h = this.nl4;
} else {
if (this.nside > iz) {
d = iz;
r = 2 * d * (d - 1);
o = r + 4 * d - 1;
} else {
d = 4 * this.nside - iz;
r = this.npix - 2 * d * (d + 1);
o = r + 4 * d - 1;
}
h = 4 * d;
e = 1;
}
if (c) {
nest.appendRange(r, o);
return;
}
var l = e / 2;
if (conservative) {
f = Math.round(h * M / Constants.TWOPI - l);
I = Math.round(h * y / Constants.TWOPI - l);
f %= h;
I > h && (I %= h);
} else {
f = Math.ceil(h * M / Constants.TWOPI - l);
I = parseInt(h * g / Constants.TWOPI - l);
f > I && 1 === iz && (I = parseInt(h * y / Constants.TWOPI - l));
f === I + 1 && (f = I);
if (1 === f - I && Constants.PI > dphi * h) {
console.log('the interval is too small and away from center');
return undefined;
}
f = Math.min(f, h - 1);
I = Math.max(I, 0);
}
f > I && (u = true);
if (u) {
f += r;
I += r;
nest.appendRange(r, I);
nest.appendRange(f, o);
} else {
if (0 > f) {
f = Math.abs(f);
nest.appendRange(r, r + I);
nest.appendRange(o - f + 1, o);
return;
}
f += r;
I += r;
nest.appendRange(f, I);
}
}
ringAbove(z) {
var az = Math.abs(z);
if (az > Constants.TWOTHIRD) {
var iring = parseInt(this.nside * Math.sqrt(3 * (1 - az)));
return z > 0 ? iring : 4 * this.nside - iring - 1;
}
return parseInt(this.nside * (2 - 1.5 * z));
}
ring2nest(ipRing) {
var xyf = this.ring2xyf(ipRing);
var nest = this.xyf2nest(xyf.ix, xyf.iy, xyf.face_num);
return nest;
}
ring2xyf(pix) {
var iring, iphi, kshift, nr;
var ret = {}; // Xyf
if (this.ncap > pix) {
// North Polar cap
iring = parseInt(.5 * (1 + Math.sqrt(1 + 2 * pix)));
iphi = pix + 1 - 2 * iring * (iring - 1);
kshift = 0;
nr = iring;
ret.face_num = 0;
var r = iphi - 1;
if (r >= 2 * iring) {
ret.face_num = 2;
r -= 2 * iring;
}
r >= iring && ++ret.face_num;
} else if (this.npix - this.ncap > pix) {
// Equatorial region
var ip = pix - this.ncap;
if (this.order >= 0) {
iring = shiftRight(ip, this.order + 2) + this.nside; // iphi = (ip & this.nl4 - 1) + 1;
iphi = bigAnd(ip, this.nl4 - 1) + 1;
} else {
iring = ip / this.nl4 + this.nside;
iphi = ip % this.nl4 + 1;
}
kshift = bigAnd(1, iring + this.nside);
nr = this.nside;
var c, u;
var ire = iring - this.nside + 1;
var irm = this.nl2 + 2 - ire;
if (this.order >= 0) {
c = shiftRight(iphi - parseInt(ire / 2) + this.nside - 1, this.order);
u = shiftRight(iphi - parseInt(irm / 2) + this.nside - 1, this.order);
} else {
c = (iphi - parseInt(ire / 2) + this.nside - 1) / this.nside;
u = (iphi - parseInt(irm / 2) + this.nside - 1) / this.nside;
}
if (u === c) {
ret.face_num = 4 === u ? 4 : parseInt(u) + 4;
} else {
ret.face_num = c > u ? parseInt(u) : parseInt(c) + 8;
}
} else {
// South Polar cap
var _ip = this.npix - pix;
iring = parseInt(.5 * (1 + Math.sqrt(2 * _ip - 1)));
iphi = 4 * iring + 1 - (_ip - 2 * iring * (iring - 1));
kshift = 0;
nr = iring;
iring = 2 * this.nl2 - iring;
ret.face_num = 8;
var _r = iphi - 1;
if (_r >= 2 * nr) {
ret.face_num = 10;
_r -= 2 * nr;
}
_r >= nr && ++ret.face_num;
}
var d = iring - JRLL[ret.face_num] * this.nside + 1;
var f = 2 * iphi - JPLL[ret.face_num] * nr - kshift - 1;
f >= this.nl2 && (f -= 8 * this.nside); // ret.ix = f - d >>> 1;
// ret.iy = -(f + d) >>> 1;
ret.ix = shiftRight(f - d, 1);
ret.iy = shiftRight(-(f + d), 1);
return ret;
}
}