1; -----------------------------------------------------------------------------
2;
3;  Copyright (C) 1997-2013  Krzysztof M. Gorski, Eric Hivon, Anthony J. Banday
4;
5;
6;
7;
8;
9;  This file is part of HEALPix.
10;
11;  HEALPix is free software; you can redistribute it and/or modify
12;  it under the terms of the GNU General Public License as published by
13;  the Free Software Foundation; either version 2 of the License, or
14;  (at your option) any later version.
15;
16;  HEALPix is distributed in the hope that it will be useful,
17;  but WITHOUT ANY WARRANTY; without even the implied warranty of
18;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19;  GNU General Public License for more details.
20;
21;  You should have received a copy of the GNU General Public License
22;  along with HEALPix; if not, write to the Free Software
23;  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24;
25;  For more information about HEALPix see http://healpix.sourceforge.net
26;
27; -----------------------------------------------------------------------------
28pro intrs_intrv, d1, d2, di, ni
29;=======================================================================
30; computes the intersection di
31; of 2 intervals d1 (= [a1,b1]) and d2 (= [a2,b2])
32; on the periodic domain ( = [A,B], where A and B are arbitrary)
33; ni is the resulting number of intervals (0,1, or 2)
34;
35; if a1<b1 then d1 = {x | a1 <= x <= b1}
36; if a1>b1 then d1 = {x | a1 <= x <= B  U  A <= x <= b1}
37;=======================================================================
38
39tr12 = (d1[0] lt d1[1])
40tr21 = ~tr12
41tr34 = (d2[0] lt d2[1])
42tr43 = ~tr34
43tr13 = (d1[0] lt d2[0])
44tr31 = ~tr13
45tr24 = (d1[1] lt d2[1])
46tr42 = ~tr24
47tr14 = (d1[0] lt d2[1])
48tr32 = (d2[0] lt d1[1])
49
50ik = -1
51dk = replicate(-1.0d9,4)
52
53
54if ((tr31 && tr14)  ||  (tr43 && (tr31 || tr14))) then begin
55   ik ++
56   dk[ik] = d1[0]  ; a1
57endif
58if ((tr13 && tr32)  ||  (tr21 && (tr13 || tr32))) then begin
59   ik ++
60   dk[ik] = d2[0]  ; a2
61endif
62if ((tr32 && tr24)  ||  (tr43 && (tr32 || tr24))) then begin
63   ik ++
64   dk[ik] = d1[1]  ; b1
65endif
66if ((tr14 && tr42)  ||  (tr21 && (tr14 || tr42))) then begin
67   ik ++
68   dk[ik] =  d2[1]  ; b2
69endif
70
71
72di = replicate(0.0d0,4)
73nk = ik+1
74case nk of
75    0 : begin
76        ni = 0
77    end
78    2 : begin
79        ni = 1
80        di[0:1] = [ dk[0], dk[1] ] ; [a1,b1] or [a1,b2] or [a2,b1] or [a2,b2]
81    end
82    4 : begin
83        ni = 2
84        di[0:3] = [ dk[0], dk[3], dk[1], dk[2] ] ; [a1,b2] U [a2,b1]
85    end
86    else : begin
87        print, ik
88        print,dk
89        print,d1,d2
90        message,"error in intrs_intrv"
91    end
92endcase
93
94return
95end
96
97
98;===========================================================================
99pro process_intervals, interval1, interval_list, interval_out, n_out
100
101interval_out = 0.0d0
102n_out = 0
103
104n_in = n_elements(interval_list)/2
105if (n_in gt 0) then begin
106    interval_out = dblarr(2*n_in+2)
107    for i_in =0, n_in-1 do begin
108        intrs_intrv, interval1, interval_list[2*i_in:2*i_in+1], tmp_list_out, n_tmp_out ; 0, 1 or 2 intervals
109        if (n_tmp_out gt 0) then begin
110            interval_out[2*n_out] = tmp_list_out[0:2*n_tmp_out-1]
111            n_out += n_tmp_out
112        endif
113    endfor
114    interval_out = (n_out gt 0) ? interval_out[0:2*n_out-1] : 0.0d0
115endif
116
117return
118end
119
120;===========================================================================
121
122pro query_triangle, nside, v1, v2, v3, listpix, nlist, $
123             help=help, $
124             nested=nested, $
125             inclusive=inclusive, $
126             walltime=walltime  ;, circle=circle
127;+
128; NAME:
129;    QUERY_TRIANGLE
130;
131; PURPOSE:
132;     Returns the list of Healpix pixels enclose in a spherical triangles
133;     defined by its 3 vertices
134;
135; CATEGORY:
136;
137;
138;
139; CALLING SEQUENCE:
140;    query_triangle, Nside, V1, V2, V3, Listpix, Nlist [, /HELP, /NESTED, /INCLUSIVE, WALLTIME=]
141;
142;
143; INPUTS:
144;
145;     Nside       = Healpix resolution parameter (a power of 2)
146;
147;     V1, V2, V3  = 3D vector location of the 3 triangle vertices
148;        They do not need to be normalised to unity
149;
150; KEYWORD PARAMETERS:
151;
152;     /HELP:   prints this documentation header and exits
153;
154;     /INCLUSIVE :0 by default, only the pixels whose center
155;                       lie in the triangle are listed on output
156;                  if set to 1, all pixels overlapping the triangle are output
157;
158;     /NESTED  :0 by default, the output list is in RING scheme
159;                  if set to 1, the output list is in NESTED scheme
160;
161; OUTPUTS:
162;
163;     List_pix    = list of pixel lying in the triangle
164;
165;     Nlist       = number of pixels in the list
166;
167;
168; OPTIONAL OUTPUTS:
169;     WALLTIME=  contains on output the wall clock time used by the routine [s]
170;
171; COMMON BLOCKS:
172; SIDE EFFECTS:
173; RESTRICTIONS:
174; PROCEDURE:
175;    calls angulardistance, ring_num, ring2z, discphirange_at_z, in_ring, ring2nest
176;       and process_intervals, intrs_intrv (included in this file).
177;
178; EXAMPLE:
179;   query_triangle, 128, [1,0,0], [0,1,0], [0,0,1], listpix, nlist
180;    will return the pixels contained in the octant (x,y,z)>0
181;
182;
183; MODIFICATION HISTORY:
184;
185;
186; v1.0, EH, Caltech, Aug-Sep-2002 : adapted from F90 code
187; v1.1, EH, IAP, Mar-2011: added walltime
188; v1.2, EH, IAP, Apr-2011: fewer spurious false positive pixels
189;  (for squeezed triangles in inclusive mode) by requiring
190;   pixels to be within a disc enclosing the triangle
191; 2012-01-14: systematically returns Listpix=[-1], Nlist=0 in case of problem
192;=======================================================================
193;-
194
195tstart = systime(1)
196routine = 'query_triangle'
197syntax = routine+', Nside, V1, V2, V3, Listpix [, Nlist, HELP=, NESTED=, INCLUSIVE=, WALLTIME=]'
198nlist = 0 & listpix = [-1]
199
200if keyword_set(help) then begin
201    doc_library,routine
202    return
203endif
204
205if (n_params() lt 5 or n_params() gt 6) then begin
206    print,syntax
207    return
208endif
209
210
211npix = nside2npix(nside)
212if (npix lt 0) then begin
213    message,"Invalid Nside = "+string(nside)
214endif
215lnside = long(nside)
216
217do_inclusive = keyword_set(inclusive)
218
219; normalize vectors
220vv = dblarr(3,3)
221vv[*,0] = v1 / sqrt(total(v1*v1, /double))
222vv[*,1] = v2 / sqrt(total(v2*v2, /double))
223vv[*,2] = v3 / sqrt(total(v3*v3, /double))
224
225
226; determ = (vect1 X vect2) . vect3
227; determines the left(<0)/right(>0) handedness of the triangle
228determ = vv[0,0]*vv[1,1]*vv[2,2] + vv[0,1]*vv[1,2]*vv[2,0] + vv[0,2]*vv[1,0]*vv[2,1] $
229       - vv[2,0]*vv[1,1]*vv[0,2] - vv[2,1]*vv[1,2]*vv[0,0] - vv[2,2]*vv[1,0]*vv[0,1]
230
231; scalar product of vertices vectors
232sprod = dblarr(3)
233sprod[0] = total(vv[*,1]*vv[*,2])
234sprod[1] = total(vv[*,2]*vv[*,0])
235sprod[2] = total(vv[*,0]*vv[*,1])
236
237; vector orthogonal to the great circle containing the vertex doublet
238vo = dblarr(3,3)
239vo[0,0] = vect_prod( vv[*,1], vv[*,2])
240vo[0,1] = vect_prod( vv[*,2], vv[*,0])
241vo[0,2] = vect_prod( vv[*,0], vv[*,1])
242vonorm = sqrt(total(vo^2, 1))
243
244if (abs(determ) lt 1.d-20 || min(vonorm) lt 1.d-20) then begin
245    message,/info,' ************************************************************'
246    message,/info,' The triangle is degenerate (2 of the vertices are antipodal or degenerate)'
247    message,/info,' The query can not be performed '
248    message,/info,' ************************************************************'
249    return
250endif
251sdet = (determ lt 0.d0) ? -1.d0 : 1.d0 ; = +1 or -1, the sign of determ
252; normalize the orthogonal vector
253for i=0,2 do vo[0,i] = vo[*,i] /  vonorm[i]  ;;;;SQRT(total(vo[*,i]^2))
254
255; test presence of poles in the triangle
256zmax = -1.0d0
257zmin =  1.0d0
258testp = (vo[2,*] * sdet ge 0.0d0) ; north pole in each hemisphere
259if (total(testp) eq 3) then begin
260   zmax = 1.0d0 ; north pole in the triangle
261endif
262if (total(testp) eq 0) then begin
263   zmin = -1.0d0 ; south pole in the triangle
264endif
265
266; look for northernest and southernest points in the triangle
267;   node(1,2) = vector of norm=1, in the plane defined by (1,2) and with z=0
268test1a = ((vv[2,2] - sprod[0] * vv[2,1]) ge 0.0d0) ; segment 2-3 : -vector(3) . node(2,3)
269test1b = ((vv[2,1] - sprod[0] * vv[2,2]) ge 0.0d0) ;                vector(2) . node(2,3)
270test2a = ((vv[2,2] - sprod[1] * vv[2,0]) ge 0.0d0) ; segment 1-3 : -vector(3) . node(1,3)
271test2b = ((vv[2,0] - sprod[1] * vv[2,2]) ge 0.0d0) ;                vector(1) . node(1,3)
272test3a = ((vv[2,1] - sprod[2] * vv[2,0]) ge 0.0d0) ; segment 1-2 : -vector(2) . node(1,2)
273test3b = ((vv[2,0] - sprod[2] * vv[2,1]) ge 0.0d0) ;                vector(1) . node(1,2)
274
275; sin of theta for orthogonal vector
276sto = SQRT( (1.0d0-vo[2,*])*(1.0d0+vo[2,*]) )
277
278; for each segment (=side of the triangle) the extrema are either
279; - the 2 vertices
280; - one of the vertices and a point within the segment
281
282; segment 2-3
283z1max = vv[2,1]
284z1min = vv[2,2]
285if ( test1a EQ test1b ) then begin
286   zz = sto[0]
287   if ((z1min+z1max) ge 0.0d0) then z1max =  zz else z1min = -zz
288endif
289
290; segment 1-3
291z2max = vv[2,2]
292z2min = vv[2,0]
293if ( test2a EQ test2b ) then begin
294   zz = sto[1]
295   if ((z2min+z2max) ge 0.0d0) then z2max =  zz else z2min = -zz
296endif
297
298; segment 1-2
299z3max = vv[2,0]
300z3min = vv[2,1]
301if ( test3a EQ test3b ) then begin
302   zz = sto[2]
303   if ((z3min+z3max) ge 0.0d0) then z3max =  zz else z3min = -zz
304endif
305
306zmax = MAX([z1max, z2max, z3max, zmax])
307zmin = MIN([z1min, z2min, z3min, zmin])
308
309; if we are inclusive, move the upper point up, and the lower point down, by a half pixel size
310offset = 0.0d0
311sin_off = 0.0d0
312if (do_inclusive) then begin
313; offset = 1.36d0 * !DPI / (4.0d0*nside)
314    offset = fudge_query_radius(nside, 0.d0)
315    sin_off = sin(offset)
316    cos_off = cos(offset)
317;    zmax = cos( acos(zmax) - offset) < 1.d0
318;    zmin = cos( acos(zmin) + offset) > (-1.d0)
319    zmax = (cos_off * zmax + sin_off * sqrt(1.d0 - zmax^2)) < 1.d0 ;cos(theta_zmax-offset)
320    zmin = (cos_off * zmin - sin_off * sqrt(1.d0 - zmin^2)) > (-1.d0) ;cos(theta_zmin+offset)
321endif
322
323; northernmost and sourthernmost ring number
324irmin = ring_num(lnside, zmax)
325irmax = ring_num(lnside, zmin)
326
327; build list of phi range for disc containing triangle
328nz = irmax - irmin + 1
329help,nz,irmin,irmax,zmin,zmax
330izlist = irmin + lindgen(nz)    ; list of rings
331zlist  = ring2z(lnside, izlist) ; list of z
332if (do_inclusive) then begin
333; find one small circle containing all points,
334; increased by fudge offset in inclusive case
335    junk = min(sprod, longside)
336    lsp1 = (longside+1) mod 3
337    lsp2 = (longside+2) mod 3
338    vcenter = 0.5d0*(vv[*,lsp1] + vv[*,lsp2]) ; mid point of longest side
339    vcenter /= sqrt(total(vcenter^2))
340    dd = angulardistance(vcenter, transpose(vv))
341    radius_eff = max(dd) + offset
342    dphilist = discphirange_at_z (vcenter, radius_eff, zlist, phi0=phi0disc) ; phi range in each ring
343;     print,'radius',radius_eff,offset
344;     print,'vcenter',vcenter
345endif
346; -------- loop on the rings -------------------------
347
348tgthi = -1.0d30 * vo[2,*]
349phi0i =  replicate(0.0d0, 3)
350kk = where(sto gt 1.0d-10, nkk)
351if (nkk gt 0) then begin
352    tgthi[kk] = -vo[2,kk] / sto[kk] ; -cotan(theta_orth)
353    phi0i[kk] = ATAN(vo[1,kk],vo[0,kk])
354endif
355
356; the triangle boundaries are geodesics : intersection of the sphere with plans going thru (0,0,0)
357; if we are inclusive, the boundaries are the intersecion of the sphere with plans pushed outward
358; by sin(offset)
359twopi = 2.0d0 * !DPI
360
361dom    = dblarr(2,4)
362
363; create WORK array to collect list of pixels
364l64 = (nside gt 8192)
365nw = ceil(surface_triangle(vv[*,0], vv[*,1], vv[*,2]) / (4*!DPI) * npix * 1.4 + 12*nside, l64=l64)
366nw <= npix
367work = (l64) ? lon64arr(nw,/nozero) : lonarr(nw,/nozero)
368;
369nlist = 0
370for iz = irmin, irmax do begin
371    iz0 = iz - irmin
372    z = zlist[iz0]
373
374    ; computes the 3 intervals described by the 3 great circles
375    st = SQRT(1.0d0 - z*z)
376    tgth = z / st               ; cotan(theta_ring)
377    dc  = tgthi * tgth - sdet * sin_off / ((sto+1.d-30) * st) ; !!! sto is slightly offset to avoid division by 0
378    for j=0,2 do begin
379        if (dc[j]*sdet le -1.0d0) then begin ; the whole iso-latitude ring is on the right side of the great circle
380            dom[0:1, j] = [ 0.0d0, twopi ]
381        endif else if (dc[j]*sdet ge 1.0d0) then begin ; all on the wrong side
382            dom[0:1, j] = [ -1.000001d0, -1.0d0 ] * (j+1) *0.5d0
383        endif else begin        ; some is good, some is bad
384            dom[0:1, j] = (phi0i[j] + ACOS(dc[j]) * sdet * [-1.0d0, 1.0d0] + twopi) MOD twopi
385        endelse
386    endfor
387    dom[0:1,3] = [ -1.000001d0, -1.0d0 ] * 2.d0
388    if (do_inclusive && dphilist[iz0] ge 0.d0) then dom[0:1,3] = (phi0disc + dphilist[iz0]*[-1.d0,1.d0] + twopi) mod twopi
389
390
391    ; identify the intersections (0,1,2 or 3) of the 3 intervals
392    ; -------------------------------------------------------
393    ; start with the first 2 intervals
394    process_intervals, dom[0:1,1], dom[0:1,0], alldom, ndom ; ndom = 0, 1 or 2
395    if (ndom eq 0) then goto, empty
396    ; add the third interval
397    process_intervals, dom[0:1,2], alldom[0:2*ndom-1], alldom, ndom ; ndom = 0, 1, 2 or 3
398    if (ndom eq 0) then goto, empty
399    ; add the fourth interval in inclusive mode
400    if (do_inclusive) then begin
401        process_intervals, dom[0:1,3], alldom[0:2*ndom-1], alldom, ndom ; ndom = 0, 1, 2, 3 or 4
402        if (ndom eq 0) then goto, empty
403    endif
404    ;print,'alldom',alldom[0:2*ndom-1]
405
406    for idom=0,ndom-1 do begin
407        a_i = alldom[2*idom]
408        b_i = alldom[2*idom+1]
409        phi0     = (a_i + b_i) * 0.5d0
410        dphiring = (b_i - a_i) * 0.5d0
411        if (dphiring lt 0.0d0) then begin
412            phi0 = phi0 + !dpi
413            dphiring = dphiring + !dpi
414        endif
415
416        ;        ------- finds pixels in the triangle on that ring ---------
417        listir=in_ring( lnside, iz, phi0, dphiring, nir)
418        ;print,'iz, phi0, dphiring', iz, phi0, dphiring, nir
419
420        ;        ----------- merge pixel lists -----------
421        if (nir gt 0) then begin
422            work[nlist] = listir
423            nlist = nlist + nir
424        endif
425    endfor
426empty:
427endfor ;-----------------------------------------
428
429if (nlist eq 0) then begin
430    listpix = [-1L]
431endif else begin
432    listpix = work[0:nlist-1]
433    if keyword_set(nested) then begin
434        ring2nest, nside, listpix, listpix
435    endif
436endelse
437
438walltime = systime(1) - tstart
439return
440end
441
442