PEARL Procedures  rev-distro-2.0.3-0-g0fb0fd9
Igor procedures for the analysis of PEARL data
pearl-anglescan-process.ipf
Go to the documentation of this file.
1 #pragma rtGlobals=3 // Use modern global access method and strict wave access.
2 #pragma version = 1.8
3 #pragma IgorVersion = 6.2
4 #pragma ModuleName = PearlAnglescanProcess
5 #include "pearl-vector-operations"
6 #include "pearl-polar-coordinates"
7 #include <New Polar Graphs>
8 
9 // copyright (c) 2013-17 Paul Scherrer Institut
10 //
11 // Licensed under the Apache License, Version 2.0 (the "License");
12 // you may not use this file except in compliance with the License.
13 // You may obtain a copy of the License at
14 // http:///www.apache.org/licenses/LICENSE-2.0
15 //
16 // Please acknowledge the use of this code.
17 
76 
81 
82 
105 function strip_delete_frames(strip, qlo, qhi, theta, tilt, phi)
106  wave strip // 2D data, X-axis = analyser angle, Y-axis = arbitrary manipulator scan
107  variable qlo
108  variable qhi
109  wave theta
110  wave tilt
111  wave phi
112 
113  if (qlo > qhi)
114  return -1
115  endif
116 
117  // source indices
118  variable snx = dimsize(strip, 0)
119  variable sny = dimsize(strip, 1)
120  variable sq1lo = 0
121  variable sq1hi = max(qlo-1, 0)
122  variable sq2lo = min(qhi+1, sny - 1)
123  variable sq2hi = dimsize(strip, 1) - 1
124 
125  // dest indices
126  variable dnx = snx
127  variable dny = sny - (sq2lo - sq1hi + 1)
128  variable dq1lo = 0
129  variable dq1hi = sq1hi
130  variable dq2lo = dq1hi + 1
131  variable dq2hi = dny - 1
132  variable q1ofs = sq1lo - dq1lo
133  variable q2ofs = sq2lo - dq2lo
134 
135  duplicate /free strip, strip_copy
136  redimension /n=(dnx,dny) strip
137  strip[][dq1lo,dq1hi] = strip_copy[p][q + q1ofs]
138  strip[][dq2lo,dq2hi] = strip_copy[p][q + q2ofs]
139 
140  duplicate /free theta, theta_copy
141  redimension /n=(dny) theta
142  theta[dq1lo,dq1hi] = theta_copy[p + q1ofs]
143  theta[dq2lo,dq2hi] = theta_copy[p + q2ofs]
144 
145  duplicate /free tilt, tilt_copy
146  redimension /n=(dny) tilt
147  tilt[dq1lo,dq1hi] = tilt_copy[p + q1ofs]
148  tilt[dq2lo,dq2hi] = tilt_copy[p + q2ofs]
149 
150  duplicate /free phi, phi_copy
151  redimension /n=(dny) phi
152  phi[dq1lo,dq1hi] = phi_copy[p + q1ofs]
153  phi[dq2lo,dq2hi] = phi_copy[p + q2ofs]
154 
155  return 0
156 end
157 
187 function normalize_strip_x(strip, [smooth_method, smooth_factor, check])
188  wave strip
189  variable smooth_method
190  variable smooth_factor
191  variable check
192 
193  if (ParamIsDefault(smooth_method))
194  smooth_method = 4
195  endif
196  if (ParamIsDefault(smooth_factor))
197  switch(smooth_method)
198  case 4:
199  smooth_factor = 0.5
200  break
201  default:
202  smooth_factor = 2
203  endswitch
204  endif
205  if (ParamIsDefault(check))
206  check = 0
207  endif
208 
209  // average over all scan positions
210  wave raw_dist = ad_profile_x(strip, -inf, inf, "")
211 
212  // remove nans
213  extract /free /indx raw_dist, clean_index, numtype(raw_dist) == 0
214  duplicate /free raw_dist, dist, dist_x
215  redimension /n=(numpnts(clean_index)) dist, dist_x
216  dist = raw_dist[clean_index[p]]
217  dist_x = pnt2x(raw_dist, clean_index[p])
218  variable div = mean(dist)
219  dist /= div
220 
221  if (check)
222  duplicate /o raw_dist, check_dist
223  check_dist = numtype(raw_dist) == 0 ? interp(x, dist_x, dist) : nan
224  endif
225 
226  // smooth distribution function
227  switch(smooth_method)
228  case 1:
229  Smooth /B /E=3 smooth_factor, dist
230  break
231  case 2:
232  Smooth /E=3 smooth_factor, dist
233  break
234  case 3:
235  make /n=1 /d /free fit_params
236  fit_scienta_ang_transm(raw_dist, fit_params)
237  duplicate /free raw_dist, dist, dist_x
238  dist_x = x
239  dist = scienta_ang_transm(fit_params, x)
240  break
241  case 4:
242  loess /smth=(smooth_factor) srcWave=dist, factors={dist_x}
243  break
244  endswitch
245 
246  if (check)
247  duplicate /o raw_dist, check_smoo
248  check_smoo = interp(x, dist_x, dist)
249  endif
250 
251  // divide
252  if (check != 2)
253  strip /= interp(x, dist_x, dist)
254  endif
255 end
256 
283 function normalize_strip_phi(strip, theta, phi, [theta_offset, theta_range, check])
284  wave strip
285  wave theta
286  wave phi
287  variable theta_offset
288  variable theta_range
289  variable check
290 
291  if (ParamIsDefault(check))
292  check = 0
293  endif
294  if (ParamIsDefault(theta_offset))
295  theta_offset = 0
296  endif
297  if (ParamIsDefault(theta_range))
298  theta_offset = 10
299  endif
300 
301  // average over analyser angles
302  wave dist = ad_profile_y(strip, -inf, inf, "")
303 
304  // smooth distribution function
305  duplicate /free dist, dist_smoo
306  duplicate /free theta, theta_int
307  theta_int = theta - theta_offset
308  duplicate /free phi, phi_int
309  setscale /p x phi_int[0], phi_int[1] - phi_int[0], waveunits(phi, -1), dist, dist_smoo
310 
311  extract /free /indx dist, red_idx, theta_int < theta_range
312  duplicate /free red_idx, red_dist, red_phi
313  red_dist = dist[red_idx]
314  red_phi = phi_int[red_idx]
315 
316  variable wavg = mean(red_dist)
317  make /n=4 /d /free coef
318  coef[0] = {wavg, wavg/100, pi/180, 0}
319  CurveFit /q /h="0010" /g /w=2 sin, kwcWave=coef, red_dist /x=red_phi
320  dist_smoo = coef[0] + coef[1] * sin(coef[2] * phi_int[p] + coef[3])
321 
322  // divide
323  if (check != 2)
324  strip = strip / dist_smoo[q] * coef[0]
325  endif
326 
327  // check
328  if (check)
329  duplicate /o dist, check_dist
330  duplicate /o dist_smoo, check_smoo
331  setscale /p x dimoffset(dist,0), dimdelta(dist,0), waveunits(dist,0), check_dist, check_smoo
332  endif
333 end
334 
366 function normalize_strip_theta(strip, theta, [theta_offset, smooth_method, smooth_factor, check])
367  wave strip
368  wave theta
369  variable theta_offset
370  variable smooth_method
371  variable smooth_factor
372  variable check
373 
374  if (ParamIsDefault(check))
375  check = 0
376  endif
377  if (ParamIsDefault(theta_offset))
378  theta_offset = 0
379  endif
380  if (ParamIsDefault(smooth_method))
381  smooth_method = 4
382  endif
383  if (ParamIsDefault(smooth_factor))
384  smooth_factor = 0.5
385  endif
386 
387  // average over analyser angles
388  wave dist = ad_profile_y(strip, -inf, inf, "")
389 
390  // smooth distribution function
391  duplicate /free dist, dist_smoo
392  duplicate /free theta, theta_int
393  theta_int = theta - theta_offset
394  setscale /p x theta_int[0], theta_int[1] - theta_int[0], waveunits(theta,-1), dist, dist_smoo
395  variable nx = dimsize(strip, 0)
396  variable ix
397 
398  switch(smooth_method)
399  case 1:
400  Smooth /B /E=3 smooth_factor, dist_smoo
401  break
402  case 2:
403  Smooth /E=3 smooth_factor, dist_smoo
404  break
405  case 4:
406  loess /dest=dist_smoo /smth=(smooth_factor) srcWave=dist, factors={theta_int}
407  break
408  case 3:
409  for (ix = 0; ix < nx; ix += 1)
410  dist = strip[ix][p]
411  if (smooth_factor > 1)
412  CurveFit /nthr=0 /q /w=2 poly smooth_factor+1, dist /x=theta_int /d=dist_smoo
413  else
414  CurveFit /nthr=0 /q /w=2 line, dist /x=theta_int /d=dist_smoo
415  endif
416  strip[ix,ix][] /= dist_smoo[q]
417  endfor
418  dist_smoo = 1
419  break
420  endswitch
421 
422  // divide
423  if (check != 2)
424  strip /= dist_smoo[q]
425  endif
426 
427  // check
428  if (check)
429  duplicate /o dist, check_dist
430  duplicate /o dist_smoo, check_smoo
431  setscale /p x dimoffset(dist,0), dimdelta(dist,0), waveunits(dist,0), check_dist, check_smoo
432  endif
433 end
434 
464 function normalize_strip_thetaphi(strip, theta, phi, [theta_offset, smooth_method, smooth_factor, check])
465  wave strip
466  wave theta
467  wave phi
468  variable theta_offset
469  variable smooth_method
470  variable smooth_factor
471  variable check
472 
473  if (ParamIsDefault(check))
474  check = 0
475  endif
476  if (ParamIsDefault(theta_offset))
477  theta_offset = 0
478  endif
479  if (ParamIsDefault(smooth_method))
480  smooth_method = 4
481  endif
482  if (ParamIsDefault(smooth_factor))
483  smooth_factor = 0.5
484  endif
485 
486  // average over analyser angles
487  wave dist = ad_profile_y(strip, -inf, inf, "")
488 
489  // smooth distribution function
490  duplicate /free dist, dist_smoo
491  duplicate /free theta, theta_int
492  theta_int = theta - theta_offset
493  setscale /p x theta_int[0], theta_int[1] - theta_int[0], waveunits(theta,-1), dist, dist_smoo
494  variable nx = dimsize(strip, 0)
495  variable ix
496 
497  switch(smooth_method)
498  case 4:
499  loess /dest=dist_smoo /smth=(smooth_factor) srcWave=dist, factors={theta_int, phi}
500  break
501  default:
502  abort "smooth method not supported"
503  endswitch
504 
505  // divide
506  if (check != 2)
507  strip /= dist_smoo[q]
508  endif
509 
510  // check
511  if (check)
512  duplicate /o dist, check_dist
513  duplicate /o dist_smoo, check_smoo
514  setscale /p x dimoffset(dist,0), dimdelta(dist,0), waveunits(dist,0), check_dist, check_smoo
515  endif
516 end
517 
531 function normalize_strip_2d(strip, theta, [theta_offset, smooth_method, smooth_factor, check])
532  wave strip
533  wave theta
534  variable theta_offset
535  variable smooth_method
536  variable smooth_factor
537  variable check
538 
539  if (ParamIsDefault(check))
540  check = 0
541  endif
542  if (ParamIsDefault(theta_offset))
543  theta_offset = 0
544  endif
545  if (ParamIsDefault(smooth_method))
546  smooth_method = 4
547  endif
548  if (ParamIsDefault(smooth_factor))
549  smooth_factor = 0.5
550  endif
551 
552  variable nx = dimsize(strip, 0)
553  variable ny = dimsize(strip, 1)
554 
555  duplicate /free strip, dist, alpha_int, theta_int
556  theta_int = theta[q] - theta_offset
557  alpha_int = dimoffset(strip, 0) + p * dimdelta(strip, 0)
558  redimension /n=(nx * ny) dist, alpha_int, theta_int
559 
560  switch(smooth_method)
561  case 4:
562  loess /dest=dist_smoo /smth=(smooth_factor) srcWave=dist, factors={alpha_int, theta_int}
563  redimension /n=(nx, ny) dist_smoo
564  break
565  default:
566  Abort "undefined smooth method"
567  break
568  endswitch
569 
570  // divide
571  if (check != 2)
572  strip /= dist_smoo
573  endif
574 
575  // check
576  if (check)
577  //duplicate /o dist, check_dist
578  duplicate /o dist_smoo, check_smoo
579  endif
580 end
581 
591 function crop_strip(strip, xlo, xhi)
592  wave strip
593  variable xlo
594  variable xhi
595 
596  variable plo = round((xlo - dimoffset(strip, 0)) / dimdelta(strip, 0))
597  variable phi = round((xhi - dimoffset(strip, 0)) / dimdelta(strip, 0))
598  xlo = plo * dimdelta(strip, 0) + dimoffset(strip, 0)
599  xhi = phi * dimdelta(strip, 0) + dimoffset(strip, 0)
600  variable nx = phi - plo + 1
601  variable ny = dimsize(strip, 1)
602 
603  duplicate /free strip, strip_copy
604  redimension /n=(nx,ny) strip
605  strip = strip_copy[p + plo][q]
606  setscale /i x xlo, xhi, waveunits(strip, 0), strip
607 end
608 
651 function pizza_service(data, nickname, theta_offset, tilt_offset, phi_offset, [npolar, nograph, folding, xpdplot])
652  wave data
653  string nickname
654  variable theta_offset
655  variable tilt_offset
656  variable phi_offset
657  variable npolar
658  variable nograph
659  variable folding
660  variable xpdplot
661 
662  if (ParamIsDefault(npolar))
663  npolar = 91
664  endif
665  if (ParamIsDefault(nograph))
666  nograph = 0
667  endif
668  if (ParamIsDefault(folding))
669  folding = 1
670  endif
671  if (ParamIsDefault(xpdplot))
672  xpdplot = 0
673  endif
674 
675  // sort out data folder structure
676  dfref saveDF = GetDataFolderDFR()
677  dfref dataDF = GetWavesDataFolderDFR(data)
678  setdatafolder dataDF
679  if (DataFolderExists(":attr"))
680  setdatafolder :attr
681  endif
682  dfref attrDF = GetDataFolderDFR()
683 
684  wave /sdfr=attrDF ManipulatorTheta
685  wave /sdfr=attrDF ManipulatorTilt
686  wave /sdfr=attrDF ManipulatorPhi
687 
688  if ((dimsize(ManipulatorTheta, 0) != dimsize(data, 1)) || (dimsize(ManipulatorTilt, 0) != dimsize(data, 1)) || (dimsize(ManipulatorPhi, 0) != dimsize(data, 1)))
689  Abort "Warning: The dimension size of the manipulator waves does not match the Y dimension of the data wave!\rIf you restructured the data wave, please use pizza_service_2 with properly scaled manipulator waves."
690  endif
691 
692  duplicate /free ManipulatorTheta, m_theta
693  duplicate /free ManipulatorTilt, m_tilt
694  duplicate /free ManipulatorPhi, m_phi
695 
696  m_theta -= theta_offset
697  m_tilt -= tilt_offset
698  m_phi -= phi_offset
699 
700  pizza_service_2(data, nickname, m_theta, m_tilt, m_phi, npolar=npolar, nograph=nograph, folding=folding, xpdplot=xpdplot)
701 
702  setdatafolder saveDF
703 end
704 
744 function pizza_service_2(data, nickname, m_theta, m_tilt, m_phi, [npolar, nograph, folding, xpdplot])
745  wave data
746  string nickname
747  wave m_theta
748  wave m_tilt
749  wave m_phi
750  variable npolar
751  variable nograph
752  variable folding
753  variable xpdplot
754 
755  if (ParamIsDefault(npolar))
756  npolar = 91
757  endif
758  if (ParamIsDefault(nograph))
759  nograph = 0
760  endif
761  if (ParamIsDefault(folding))
762  folding = 1
763  endif
764  if (ParamIsDefault(xpdplot))
765  xpdplot = 0
766  endif
767 
768  if ((dimsize(m_theta, 0) != dimsize(data, 1)) || (dimsize(m_tilt, 0) != dimsize(data, 1)) || (dimsize(m_phi, 0) != dimsize(data, 1)))
769  Abort "Warning: The dimension size of the manipulator waves does not match the Y dimension of the data wave!"
770  endif
771 
772  string graphname = "graph_" + nickname
773  string outprefix = nickname
774 
775  // sort out data folder structure
776  dfref saveDF = GetDataFolderDFR()
777  dfref dataDF = GetWavesDataFolderDFR(data)
778  setdatafolder dataDF
779 
780  if (xpdplot)
781  setdatafolder root:
782  outprefix = nickname
783  else
784  setdatafolder dataDF
785  newdatafolder /s/o $nickname
786  outprefix = ""
787  endif
788  dfref destDF = GetDataFolderDFR()
789 
790  // performance monitoring
791  variable timerRefNum
792  variable /g pol_perf_secs
793  timerRefNum = startMSTimer
794 
795  duplicate /free m_tilt, corr_tilt
796  duplicate /free m_phi, corr_phi
797  corr_tilt = -m_tilt // checked 140702
798  corr_phi = m_phi // checked 140702
799 
800  make /n=1/d/free d_polar, d_azi
801 
802  convert_angles_ttpd2polar(m_theta, corr_tilt, corr_phi, data, d_polar, d_azi)
803  d_azi += 180 // changed 151030 (v1.6)
804  make_hemi_grid(npolar, outprefix, xpdplot=xpdplot)
805  variable ifold
806  for (ifold = 0; ifold < folding; ifold += 1)
807  d_azi = d_azi >= 360 ? d_azi - 360 : d_azi
808  hemi_add_anglescan(outprefix, data, d_polar, d_azi)
809  d_azi += 360 / folding
810  endfor
811 
812  // normalize folding
813  if (strlen(outprefix))
814  string s_prefix = outprefix + "_"
815  string s_int = s_prefix + "i"
816  else
817  s_prefix = ""
818  s_int = "values"
819  endif
820  if (folding > 1)
821  wave values = $s_int
822  values /= folding
823  endif
824 
825  if (!nograph)
826  display_hemi_scan(outprefix, graphname = graphname)
827  endif
828 
829  if (timerRefNum >= 0)
830  pol_perf_secs = stopMSTimer(timerRefNum) / 1e6
831  endif
832 
833  setdatafolder saveDF
834 end
835 
861 function show_analyser_line(theta, tilt, phi, theta_offset, tilt_offset, phi_offset, [npolar, nograph, xpdplot])
862  variable theta
863  variable tilt
864  variable phi
865  variable theta_offset
866  variable tilt_offset
867  variable phi_offset
868  variable npolar
869  variable nograph
870  variable xpdplot
871 
872  string nickname = "analyser"
873 
874  if (ParamIsDefault(npolar))
875  npolar = 91
876  endif
877  if (ParamIsDefault(nograph))
878  nograph = 0
879  endif
880  if (ParamIsDefault(xpdplot))
881  xpdplot = 0
882  endif
883  string graphname = "graph_" + nickname
884  string outprefix = nickname
885 
886  // sort out data folder structure
887  dfref saveDF = GetDataFolderDFR()
888  dfref dataDF = saveDF
889  if (xpdplot)
890  setdatafolder root:
891  outprefix = nickname
892  else
893  setdatafolder dataDF
894  newdatafolder /s/o $nickname
895  outprefix = ""
896  endif
897  dfref destDF = GetDataFolderDFR()
898 
899  make /n=1 /free m_theta
900  make /n=1 /free m_tilt
901  make /n=1 /free m_phi
902  m_theta = theta - theta_offset
903  m_tilt = tilt - tilt_offset
904  m_tilt *= -1 // checked 140702
905  m_phi = phi - phi_offset
906  //m_phi *= -1 // checked 140702
907 
908  make /n=60 /free data
909  setscale /i x -30, 30, data
910  data = x
911  make /n=1/d/free d_polar, d_azi
912 
913  convert_angles_ttpa2polar(m_theta, m_tilt, m_phi, data, d_polar, d_azi)
914  d_azi += 180 // changed 151030 (v1.6)
915  d_azi = d_azi >= 360 ? d_azi - 360 : d_azi
916  make_hemi_grid(npolar, outprefix, xpdplot=xpdplot)
917  hemi_add_anglescan(outprefix, data, d_polar, d_azi)
918 
919  if (!nograph)
920  display_hemi_scan(outprefix, graphname = graphname)
921  endif
922 
923  setdatafolder saveDF
924 end
925 
930 
931 function convert_angles_ttpd2polar(theta, tilt, phi, data, polar, azi)
932  wave theta, tilt, phi // see convert_angles_ttpa2polar
933  wave data // in, 1D or 2D
934  // X-scale must be set to analyser angle scale
935  wave polar, azi // see convert_angles_ttpa2polar
936 
937  make /n=(dimsize(data, 0)) /d /free ana
938  setscale /p x dimoffset(data, 0), dimdelta(data, 0), waveunits(data, 0), ana
939  ana = x
940  convert_angles_ttpa2polar(theta, tilt, phi, ana, polar, azi)
941 end
942 
970 function convert_angles_ttpa2polar(theta, tilt, phi, analyser, polar, azi)
971  wave theta
972  wave tilt
973  wave phi
974  wave analyser
975  wave polar, azi
976 
977  variable nn = numpnts(theta)
978  variable na = numpnts(analyser)
979  redimension /n=(na, nn) polar, azi
980 
981  variable radius = 1 // don't need to specify - everything is scalable
982 
983  // step 1: calculate cartesian detection vectors at normal emission
984  // this is simply a polar-cartesian mapping, independent of the manipulator
985  // phi=0 is in the polar rotation plane
986  make /n=(3,na) /d /free w_orig_polar, w_orig_cart, w_rot_cart, w_rot_polar
987  w_orig_polar[0][] = radius
988  w_orig_polar[1][] = analyser[q]
989  w_orig_polar[2][] = 0
990  polar2cart_wave(w_orig_polar, w_orig_cart)
991  // if the angle-dispersive axis was horizontal, we'd need to rotate the detector
992  //rotate_z_wave(w_orig_cart, 90)
993 
994  variable ii
995  for (ii = 0; ii < nn; ii += 1)
996  // step 2: rotate the detection vectors according to the manipulator angles
997  // the order of rotations is important because we rotate about fixed axes
998  // y-axis = tilt rotation axis
999  // x-axis = polar rotation axis
1000  // z-axis = normal emission = azimuthal rotation axis
1001  w_rot_cart = w_orig_cart
1002  rotate_y_wave(w_rot_cart, -tilt[ii])
1003  rotate_x_wave(w_rot_cart, -theta[ii])
1004  rotate_z_wave(w_rot_cart, -phi[ii] - 90)
1005  // map the vectors back to the sample coordinate system
1006  cart2polar_wave(w_rot_cart, w_rot_polar)
1007  // copy to output
1008  polar[][ii] = w_rot_polar[1][p]
1009  azi[][ii] = w_rot_polar[2][p]
1010  endfor
1011 end
1012 
1013 static function line_average(source, dest)
1014  // is this function used?
1015  wave source
1016  wave dest
1017 
1018  variable ii
1019  variable nn = dimsize(source, 1)
1020  make /n=(dimsize(source, 0))/d/free line
1021  for (ii = 0; ii < nn; ii += 1)
1022  line = source[p][ii]
1023  wavestats /q line
1024  dest[][ii] = line[p] / v_max
1025  endfor
1026 end
1027 
1031 static function calc_nth(Theta_st, Theta_in, th, Phi_ran, Phi_ref, Holomode)
1032  Variable Theta_st, Theta_in, th, Phi_ran, Phi_ref
1033  String Holomode
1034  Variable The_step
1035  Variable deg2rad=0.01745329
1036 
1037  if ( cmpstr(Holomode, "Stereographic") == 0)
1038  The_step =trunc( Phi_ran*sin(th*deg2rad)*Phi_ref/Theta_st )
1039  if(th==90)
1040  The_step =trunc( Phi_ran*sin(th*pi/180)*Phi_ref/Theta_st )
1041  endif
1042  else
1043  if (cmpstr(Holomode, "Parallel") == 0)
1044  The_step=trunc( Phi_ran*tan(th*deg2rad)*Phi_ref/Theta_st )
1045  else
1046  if ( cmpstr(Holomode, "h") == 0)
1047  The_step=trunc( th/Theta_in*Phi_ran/Theta_st )
1048  else
1049  //altro
1050  endif
1051  endif
1052  endif
1053 
1054  return(The_step)
1055 end
1056 
1060 static function calc_phi_step(Theta_in, th, Theta_st, Phi_ran, Phi_ref, Holomode)
1061  Variable Theta_in, th, Theta_st, Phi_ran, Phi_ref
1062  String Holomode
1063 
1064  Variable Phi_st
1065  Variable deg2rad=0.01745329
1066 
1067  if ( cmpstr(Holomode, "Stereographic") == 0 )
1068  if ((th < 0.5) || (trunc(Phi_ran*sin(th*deg2rad)*Phi_ref/Theta_st) == 0))
1069  Phi_st=0.0
1070  else
1071  Phi_st=Phi_ran/(trunc(Phi_ran*sin(th*deg2rad)*Phi_ref/Theta_st))
1072  endif
1073  if(th==90)
1074  Phi_st=2.0
1075  endif
1076  endif
1077 
1078  if ( cmpstr(Holomode, "Parallel") == 0 )
1079  if((th < 0.5) || (trunc(Phi_ran*tan(th*deg2rad)*Phi_ref/Theta_st) == 0))
1080  Phi_st=0.0
1081  else
1082  Phi_st=Phi_ran/(trunc(Phi_ran*tan(th*deg2rad)*Phi_ref/Theta_st))
1083  endif
1084  endif
1085 
1086  if ( cmpstr(Holomode, "h") == 0 )
1087  if((th < 0.5) || (trunc(Phi_ran*sin(th*deg2rad)*Phi_ref/Theta_st) == 0))
1088  Phi_st=0.0
1089  else
1090  Phi_st=Phi_ran/trunc(th/Theta_in*Phi_ran/Theta_st)
1091  endif
1092  endif
1093 
1094  if (Phi_st==0)
1095  Phi_st=360
1096  endif
1097 
1098  return(Phi_st)
1099 end
1100 
1104 static function Calc_The_step(th, Theta_st, Holomode)
1105  String Holomode
1106  Variable th, Theta_st
1107 
1108  Variable deg2rad=0.01745329, dt_loc,The_step
1109 
1110  if ( (cmpstr(Holomode, "Stereographic")) ==0 )
1111  The_step=Theta_st
1112  endif
1113 
1114  if ( (cmpstr(Holomode, "h")) ==0 )
1115  The_step=Theta_st
1116  endif
1117 
1118  if ( cmpstr(Holomode, "Parallel") == 0 )
1119  if(th < 89.5)
1120  dt_loc = Theta_st/cos(th*deg2rad)
1121  if(dt_loc > 10)
1122  dt_loc=10
1123  endif
1124  The_step=dt_loc
1125  else
1126  The_step=10
1127  endif
1128  endif
1129  return(The_step)
1130 end
1131 
1135 static function CalcN_Theta(HoloMode,Theta_in,Theta_ran,Theta_st)
1136  String HoloMode
1137  Variable Theta_in,Theta_ran,Theta_st
1138  Variable n_theta, aux, aux1,ii
1139 
1140  aux = Theta_in
1141  aux1= Theta_in - Theta_ran
1142  ii = 0
1143  do
1144  aux = aux - Calc_The_step(aux, Theta_st, HoloMode)
1145  if(aux<=Theta_in-Theta_ran)
1146  aux=Theta_in-Theta_ran
1147  endif
1148  ii = ii+1
1149  while((aux>aux1)%&(Theta_in-aux<=Theta_ran)) //
1150  n_theta=ii+1
1151  Return(n_theta)
1152 end
1153 
1173 function make_hemi_grid(npol, nickname, [xpdplot])
1174  variable npol
1175  string nickname
1176  variable xpdplot
1177 
1178  if (ParamIsDefault(xpdplot))
1179  xpdplot = 0
1180  endif
1181 
1182  string HoloMode = "h"
1183  variable Theta_in = 90
1184  variable Theta_ran = 90
1185  variable Theta_st = 90 / (npol - 1)
1186  variable Phi_ran = 360
1187  variable Phi_ref = 1
1188  variable Phi_in = 0
1189 
1190  variable n_theta = CalcN_Theta(HoloMode, Theta_in, Theta_ran, Theta_st)
1191 
1192  // wave names
1193  if (strlen(nickname))
1194  string s_prefix = nickname + "_"
1195  string s_int = s_prefix + "i" // Intensity wave (counts/sec)
1196  else
1197  s_prefix = ""
1198  s_int = "values" // "i" is not a valid wave name
1199  endif
1200  string s_polar = s_prefix + "pol" // thetas for each int-point of the holo
1201  string s_azim = s_prefix + "az" // phis for each int-point of the holo
1202 
1203  string s_index = s_prefix + "index" // starting index for each theta
1204  string s_theta = s_prefix + "th" // theta values
1205  string s_dphi = s_prefix + "dphi" // delta phis at each theta
1206  string s_nphis = s_prefix + "nphis" // number of phis at each theta
1207 
1208  string s_HoloData = s_prefix + "data" // All holo exp.- parameter information
1209  string s_HoloInfo = s_prefix + "info"
1210 
1211  // the following two waves are used by the pearl-anglescan procedures but not by XPDplot
1212  string s_tot = s_prefix + "tot" // accumulated counts at each point
1213  string s_weight = s_prefix + "wt" // total accumulation time at each point (arb. units)
1214 
1215  make /O/D/n=(n_theta) $s_index /wave=index
1216  make /O/D/n=(n_theta) $s_theta /wave=theta
1217  make /O/D/n=(n_theta) $s_dphi /wave=dphi
1218  make /O/D/n=(n_theta) $s_nphis /wave=nphis
1219 
1220  //---------- calculate phi-step-size for this theta:
1221  variable aux = Calc_The_step(Theta_in, Theta_st, HoloMode)
1222  dphi[0] = calc_phi_step(Theta_in, Theta_in, aux, Phi_ran, Phi_ref, HoloMode)
1223  Theta[0] = Theta_in
1224  nphis[0] = calc_nth(aux, Theta_in, Theta_in, Phi_ran, Phi_ref, HoloMode)
1225  Index[0] = nphis[0]
1226 
1227  //---------- calculate number of phis, phi-step, and starting-index for each theta:
1228  variable ii = 1
1229  do
1230  Theta[ii] = Theta[ii-1] - aux
1231  if(Theta[ii] <= Theta_in-Theta_ran)
1232  Theta[ii] = Theta_in-Theta_ran
1233  endif
1234  aux = Calc_The_step(Theta[ii], Theta_st, HoloMode)
1235  dphi[ii] = calc_phi_step(Theta_in, Theta[ii], aux, Phi_ran, Phi_ref, HoloMode)
1236  nphis[ii] = calc_nth(aux, Theta_in, Theta[ii], Phi_ran, Phi_ref, HoloMode)
1237  Index[ii] = Index[ii-1] + nphis[ii]
1238  ii=ii+1
1239  while(ii < n_theta)
1240 
1241  if (Index[n_theta-1]==Index[n_theta-2])
1242  Index[n_theta-1]=Index[n_theta-2]+1
1243  nphis[n_theta-1]=1
1244  endif
1245 
1246  variable NumPoints = sum(nphis, 0, numpnts(nphis))
1247 
1248  //---------- calculate theta and phi for each data point:
1249  make /O/D/N=(NumPoints) $s_polar /wave=polar, $s_azim /wave=azim
1250  note azim, "version=1.6"
1251 
1252  ii = 0
1253  variable StartIndex = 0
1254  variable EndIndex
1255  do
1256  EndIndex=Index[ii]
1257  Polar[StartIndex, EndIndex-1]=Theta[ii]
1258  Azim[StartIndex, EndIndex-1]= mod(Phi_ran+(x-StartIndex)*dphi[ii]+Phi_in,Phi_ran)
1259  ii = ii + 1
1260  StartIndex = EndIndex
1261  while(ii < n_theta)
1262 
1263  duplicate /o azim, $s_int /wave=values
1264  duplicate /o azim, $s_tot /wave=totals
1265  duplicate /o azim, $s_weight /wave=weights
1266  values = nan
1267  totals = 0
1268  weights = 0
1269 
1270  // XPDplot metadata
1271  if (xpdplot)
1272  string s_FileName = ""
1273  string s_Comment = "created by pearl-anglescan-process.ipf"
1274  string s_HoloMode = "Stereographic"
1275  variable /g gb_SpectraFile = 0
1276 
1277  Make/O/D/n=22 $s_HoloData /wave=HoloData
1278  HoloData[0] = NaN // v_StartKE
1279  HoloData[1] = NaN // v_StoppKE
1280  HoloData[6] = NumPoints
1281  HoloData[7] = Theta_in
1282  HoloData[8] = Theta_ran
1283  HoloData[9] = Theta_st
1284  HoloData[11] = Phi_in
1285  HoloData[12] = Phi_ran
1286  HoloData[13] = Theta_st
1287  HoloData[15] = Phi_ref
1288  HoloData[16] = Phi_ran
1289  HoloData[17] = 0 // v_HoloBit (stereographic)
1290 
1291  Make/O/T/n=22 $s_HoloInfo /wave=HoloInfo
1292  HoloInfo[0] = s_FileName
1293  HoloInfo[1] = s_Comment
1294  HoloInfo[10] = s_HoloMode
1295  HoloInfo[11] = "" // s_MeasuringMode
1296 
1297  // notebook for XPDplot
1298  if (WinType(NickName) == 5)
1299  Notebook $NickName selection={startOfFile, endOfFile}
1300  Notebook $NickName text=""
1301  else
1302  NewNotebook /F=0 /K=1 /N=$NickName /W=(5,40,341,260)
1303  Notebook $NickName defaultTab=140
1304  Notebook $NickName statusWidth=300
1305  Notebook $NickName backRGB=(56797,56797,56797)
1306  Notebook $NickName pageMargins={80,80,80,80}
1307  Notebook $NickName fSize=10
1308  Notebook $NickName fStyle=0,textRGB=(65535,0,26214)
1309  Notebook $NickName textRGB=(65535,0,26214)
1310  endif
1311  Notebook $NickName text = "File:\t" + s_FileName + "\r"
1312  Notebook $NickName text = "*** " + s_Comment + " ***\r\r"
1313  Notebook $NickName text = "Angle-Mode:\t" + s_HoloMode + "\r"
1314  Notebook $NickName text = "XPDplot Nickname:\t" + NickName + "\r"
1315  endif
1316 end
1317 
1324 function /s get_hemi_nickname(w)
1325  wave w
1326 
1327  string prefix = get_hemi_prefix(w)
1328  string wname = nameofwave(w)
1329  string nickname
1330 
1331  if (strlen(prefix))
1332  nickname = prefix
1333  else
1334  string s_wave_df = GetWavesDataFolder(w, 1)
1335  dfref parent_df = $(s_wave_df + "::")
1336  nickname = GetDataFolder(0, parent_df)
1337  endif
1338 
1339  return nickname
1340 end
1341 
1349 function /s get_hemi_prefix(w)
1350  wave w
1351 
1352  string wname = nameofwave(w)
1353  string prefix
1354  if (ItemsInList(wname, "_") >= 2)
1355  prefix = StringFromList(0, wname, "_")
1356  else
1357  prefix = ""
1358  endif
1359 
1360  return prefix
1361 end
1362 
1380 function /df find_hemi_data(nickname, prefix, intwave)
1381  string nickname
1382  string &prefix
1383  string &intwave
1384 
1385  dfref datadf
1386  prefix = ""
1387  intwave = "values"
1388  if (strlen(nickname))
1389  if (DataFolderExists(nickname))
1390  datadf = $nickname
1391  else
1392  datadf = getdatafolderdfr()
1393  prefix = nickname + "_"
1394  intwave = prefix + "i"
1395  if (exists(intwave) != 1)
1396  datadf = root:
1397  endif
1398  endif
1399  else
1400  datadf = getdatafolderdfr()
1401  prefix = ""
1402  intwave = "values"
1403  endif
1404  return datadf
1405 end
1406 
1414 function clear_hemi_grid(nickname)
1415  string nickname
1416 
1417  dfref datadf
1418  string s_prefix
1419  string s_int
1420  datadf = find_hemi_data(nickname, s_prefix, s_int)
1421 
1422  string s_totals = s_prefix + "tot"
1423  string s_weights = s_prefix + "wt"
1424 
1425  wave /sdfr=datadf /z w_values = $s_int
1426  wave /sdfr=datadf /z w_totals = $s_totals
1427  wave /sdfr=datadf /z w_weights = $s_weights
1428 
1429  if (waveexists(w_totals))
1430  w_totals = 0
1431  endif
1432  if (waveexists(w_weights))
1433  w_weights = 0
1434  endif
1435  if (waveexists(w_values))
1436  w_values = nan
1437  endif
1438 end
1439 
1461 function duplicate_hemi_scan(source_nickname, dest_folder, dest_nickname, [xpdplot])
1462  string source_nickname
1463  dfref dest_folder
1464  string dest_nickname
1465  variable xpdplot
1466 
1467  if (ParamIsDefault(xpdplot))
1468  xpdplot = 0
1469  endif
1470 
1471  dfref savedf = getdatafolderdfr()
1472 
1473  // source data
1474  string s_prefix = ""
1475  string s_int = "values"
1476  dfref source_df = find_hemi_data(source_nickname, s_prefix, s_int)
1477  string s_polar = s_prefix + "pol"
1478  string s_azim = s_prefix + "az"
1479  string s_theta = s_prefix + "th"
1480  string s_tot = s_prefix + "tot"
1481  string s_weight = s_prefix + "wt"
1482  string s_matrix = s_prefix + "matrix"
1483 
1484  wave /sdfr=source_df theta1 = $s_theta
1485  wave /sdfr=source_df polar1 = $s_polar
1486  wave /sdfr=source_df azim1 = $s_azim
1487  wave /sdfr=source_df tot1 = $s_tot
1488  wave /sdfr=source_df weight1 = $s_weight
1489  wave /sdfr=source_df values1 = $s_int
1490  wave /sdfr=source_df /z matrix1 = $s_matrix
1491 
1492  variable npol = numpnts(theta1)
1493 
1494  setdatafolder dest_folder
1495  make_hemi_grid(npol, dest_nickname, xpdplot=xpdplot)
1496 
1497  // dest data
1498  dfref dest_df = find_hemi_data(dest_nickname, s_prefix, s_int)
1499  s_polar = s_prefix + "pol"
1500  s_azim = s_prefix + "az"
1501  s_theta = s_prefix + "th"
1502  s_tot = s_prefix + "tot"
1503  s_weight = s_prefix + "wt"
1504  s_matrix = s_prefix + "matrix"
1505 
1506  wave /sdfr=dest_df theta2 = $s_theta
1507  wave /sdfr=dest_df polar2 = $s_polar
1508  wave /sdfr=dest_df azim2 = $s_azim
1509  wave /sdfr=dest_df tot2 = $s_tot
1510  wave /sdfr=dest_df weight2 = $s_weight
1511  wave /sdfr=dest_df values2 = $s_int
1512 
1513  tot2 = tot1
1514  weight2 = weight1
1515  values2 = values1
1516  if (waveexists(matrix1))
1517  setdatafolder dest_df
1518  duplicate /o matrix1, $s_matrix
1519  endif
1520 
1521  if (!(NumberByKey("version", note(azim1), "=", "\r") >= 1.6))
1522  azim2 += 180 // changed 151030 (v1.6)
1523  azim2 = azim2 >= 360 ? azim2 - 360 : azim2
1524  endif
1525 
1526  setdatafolder saveDF
1527 end
1528 
1536 function rotate_hemi_scan(nickname, angle)
1537  string nickname
1538  variable angle
1539 
1540  dfref savedf = getdatafolderdfr()
1541 
1542  string s_prefix = ""
1543  string s_int = "values"
1544  dfref df = find_hemi_data(nickname, s_prefix, s_int)
1545 
1546  string s_polar = s_prefix + "pol"
1547  string s_azim = s_prefix + "az"
1548  string s_tot = s_prefix + "tot"
1549  string s_weight = s_prefix + "wt"
1550 
1551  wave /sdfr=df polar = $s_polar
1552  wave /sdfr=df azim = $s_azim
1553  wave /sdfr=df tot = $s_tot
1554  wave /sdfr=df weight = $s_weight
1555  wave /sdfr=df values = $s_int
1556 
1557  azim += angle
1558  azim = azim < 0 ? azim + 360 : azim
1559  azim = azim >= 360 ? azim - 360 : azim
1560 
1561  duplicate /free polar, neg_polar
1562  neg_polar = -polar
1563  sort {neg_polar, azim}, polar, azim, tot, weight, values
1564 
1565  setdatafolder saveDF
1566 end
1567 
1584 function /s prepare_hemi_scan_display(nickname, [projection])
1585  string nickname
1586  variable projection
1587 
1588  dfref savedf = getdatafolderdfr()
1589 
1590  if (ParamIsDefault(projection))
1591  projection = 1
1592  endif
1593 
1594  // hemi grid waves
1595  string s_prefix = ""
1596  string s_int = "values"
1597  dfref df = find_hemi_data(nickname, s_prefix, s_int)
1598 
1599  string s_polar = s_prefix + "pol"
1600  string s_azim = s_prefix + "az"
1601 
1602  wave /sdfr=df /z values = $s_int
1603  wave /sdfr=df /z azim = $s_azim
1604  wave /sdfr=df /z polar = $s_polar
1605 
1606  setdatafolder df
1607  string s_ster_rad = s_prefix + "ster_rad"
1608  duplicate /o polar, $s_ster_rad /wave=ster_rad
1609  ster_rad = calc_graph_radius(polar, projection=projection)
1610 
1611  string s_ster_x = s_prefix + "ster_x"
1612  string s_ster_y = s_prefix + "ster_y"
1613  duplicate /o azim, $s_ster_x /wave=ster_x, $s_ster_y /wave=ster_y
1614  ster_x = ster_rad * cos(azim * pi / 180)
1615  ster_y = ster_rad * sin(azim * pi / 180)
1616 
1617  setdatafolder savedf
1618 end
1619 
1666 function /s display_hemi_scan(nickname, [projection, graphtype, do_ticks, do_grids, graphname])
1667  string nickname
1668  variable projection
1669  variable graphtype
1670  variable do_ticks
1671  variable do_grids
1672  string graphname
1673 
1674  dfref savedf = getdatafolderdfr()
1675 
1676  if (ParamIsDefault(projection))
1677  projection = 1
1678  endif
1679  if (ParamIsDefault(graphtype))
1680  graphtype = 1
1681  endif
1682  if (ParamIsDefault(do_ticks))
1683  do_ticks = 3
1684  endif
1685  if (ParamIsDefault(do_grids))
1686  do_grids = 3
1687  endif
1688  if (ParamIsDefault(graphname))
1689  if (strlen(nickname) > 0)
1690  graphname = nickname
1691  else
1692  graphname = GetDataFolder(0)
1693  endif
1694  endif
1695 
1696  prepare_hemi_scan_display(nickname, projection=projection)
1697 
1698  // hemi grid waves
1699  string s_prefix = ""
1700  string s_int = "values"
1701  dfref df = find_hemi_data(nickname, s_prefix, s_int)
1702 
1703  string s_polar = s_prefix + "pol"
1704  string s_azim = s_prefix + "az"
1705  string s_matrix = s_prefix + "matrix"
1706  string s_ster_rad = s_prefix + "ster_rad"
1707 
1708  wave /sdfr=df /z values = $s_int
1709  wave /sdfr=df /z azim = $s_azim
1710  wave /sdfr=df /z polar = $s_polar
1711  wave /sdfr=df /z ster_rad = $s_ster_rad
1712  wave /sdfr=df /z matrix = $s_matrix
1713 
1714  setdatafolder df
1715  variable azim_offset = 0
1716  if (!(NumberByKey("version", note(azim), "=", "\r") >= 1.6))
1717  DoAlert /T="display hemi scan" 0, "your dataset doesn't include the version 1.6 flag. if it was created with an earlier version that might be okay. please check that the orientation is correct!"
1718  azim_offset = 180 // changed 151030 (v1.6)
1719  endif
1720 
1721  string s_trace
1722  DoWindow $graphname
1723  if (v_flag)
1724  if (str2num(GetUserData(graphname, "", "graphtype")) == graphtype)
1725  // graph exists and will update automatically - do not recreate
1726  graphtype = 0
1727  else
1728  // graph exists - but needs recreating
1729  killwindow $graphname
1730  endif
1731  endif
1732 
1733  switch(graphtype)
1734  case 1:
1735  graphname = display_polar_graph(graphname, angle_offset=azim_offset, do_ticks=do_ticks)
1736 
1737  s_trace = WMPolarAppendTrace(graphname, ster_rad, azim, 360)
1738  ModifyGraph /W=$graphname mode($s_trace)=2, lsize($s_trace)=2
1739  ModifyGraph /W=$graphname zColor($s_trace)={values,*,*,BlueGreenOrange,0}
1740 
1741  ColorScale /W=$graphname /C /N=text0 /E=2 /F=0 /B=1 /A=RB /X=0.00 /Y=0.00 trace=polarY0
1742  ColorScale /W=$graphname /C /N=text0 side=2, width=5, heightPct=40, frame=0.50, lblMargin=0
1743  ColorScale /W=$graphname /C /N=text0 nticks=2, minor=1, tickLen=4.00, tickThick=0.50
1744 
1745  SetWindow $graphname, userdata(projection)=num2str(projection)
1746  SetWindow $graphname, userdata(graphtype)=num2str(graphtype)
1747  draw_hemi_axes(graphname, do_grids=do_grids)
1748  break
1749  case 3:
1750  graphname = display_polar_graph(graphname, angle_offset=azim_offset, do_ticks=do_ticks)
1751 
1752  s_trace = WMPolarAppendTrace(graphname, ster_rad, azim, 360)
1753  ModifyGraph /W=$graphname mode($s_trace)=0, lsize($s_trace)=0
1754  AppendImage /L=VertCrossing /B=HorizCrossing matrix
1755 
1756  ColorScale /W=$graphname /C /N=text0 /E=2 /F=0 /B=1 /A=RB /X=0.00 /Y=0.00 image=$s_matrix
1757  ColorScale /W=$graphname /C /N=text0 side=2, width=5, heightPct=40, frame=0.50, lblMargin=0
1758  ColorScale /W=$graphname /C /N=text0 nticks=2, minor=1, tickLen=4.00, tickThick=0.50
1759 
1760  SetWindow $graphname, userdata(projection)=num2str(projection)
1761  SetWindow $graphname, userdata(graphtype)=num2str(graphtype)
1762  draw_hemi_axes(graphname, do_grids=do_grids)
1763  break
1764  endswitch
1765 
1766  setdatafolder savedf
1767  return graphname
1768 end
1769 
1811 static function /s display_polar_graph(graphname, [angle_offset, do_ticks])
1812 
1813  string graphname
1814  variable angle_offset
1815  variable do_ticks
1816 
1817  dfref savedf = GetDataFolderDFR()
1818 
1819  if (ParamIsDefault(angle_offset))
1820  angle_offset = 0
1821  endif
1822  if (ParamIsDefault(do_ticks))
1823  do_ticks = 3
1824  endif
1825 
1826  if ((strlen(graphname) == 0) || (wintype(graphname) == 0))
1827  Display /k=1 /W=(10,45,360,345)
1828  DoWindow /C $graphname
1829  graphname = WMNewPolarGraph("", graphname)
1830  WMPolarGraphSetVar(graphname, "zeroAngleWhere", angle_offset)
1831 
1832  WMPolarGraphSetVar(graphname, "angleAxisThick", 0.5)
1833  WMPolarGraphSetStr(graphname, "doMajorAngleTicks", "manual")
1834  WMPolarGraphSetVar(graphname, "majorAngleInc", 30) // major ticks in 30 deg steps
1835  WMPolarGraphSetVar(graphname, "minorAngleTicks", 2) // minor ticks in 10 deg steps
1836  WMPolarGraphSetStr(graphname, "angleTicksLocation", "Outside")
1837  WMPolarGraphSetVar(graphname, "doAngleTickLabelSubRange", 1)
1838  WMPolarGraphSetVar(graphname, "angleTickLabelRangeStart", 0)
1839  WMPolarGraphSetVar(graphname, "angleTickLabelRangeExtent", 90)
1840  WMPolarGraphSetStr(graphname, "angleTickLabelNotation", "%g°")
1841 
1842  WMPolarGraphSetVar(graphname, "doPolarGrids", 0)
1843  WMPolarGraphSetVar(graphname, "doRadiusTickLabels", 0)
1844  WMPolarGraphSetStr(graphname, "radiusAxesWhere", " Off") // note the leading spaces, cf. WMPolarAnglesForRadiusAxes
1845  WMPolarGraphSetStr(graphname, "radiusTicksLocation", "Off")
1846 
1847  WMPolarGraphSetVar(graphname, "majorTickLength", 2)
1848  WMPolarGraphSetVar(graphname, "majorTickThick", 0.5)
1849  WMPolarGraphSetVar(graphname, "minorTickLength", 1)
1850  WMPolarGraphSetVar(graphname, "minorTickThick", 0.5)
1851  WMPolarGraphSetVar(graphname, "tickLabelOpaque", 0)
1852  WMPolarGraphSetVar(graphname, "tickLabelFontSize", 7)
1853 
1854  // changes
1855  if (do_ticks & 1)
1856  WMPolarGraphSetStr(graphname, "angleTicksLocation", "Outside")
1857  else
1858  WMPolarGraphSetStr(graphname, "angleTicksLocation", "Off")
1859  endif
1860  if (do_ticks & 2)
1861  WMPolarGraphSetVar(graphname, "doMinorAngleTicks", 1)
1862  else
1863  WMPolarGraphSetVar(graphname, "doMinorAngleTicks", 0)
1864  endif
1865 
1866  DoWindow /T $graphname, graphname
1867 
1868  // cursor info in angles
1869  string graphdf = "root:packages:WMPolarGraphs:" + graphname
1870  setdatafolder graphdf
1871  // current theta, phi coordinates are stored in global variables in the package folder of the graph
1872  variable /g csrA_theta
1873  variable /g csrA_phi
1874  variable /g csrB_theta
1875  variable /g csrB_phi
1876  // the text box is hidden initially. it shows up and hides with the cursor info box.
1877  string tb
1878  tb = "\\{"
1879  tb = tb + "\"A = (%.1f, %.1f)\","
1880  tb = tb + graphdf + ":csrA_theta,"
1881  tb = tb + graphdf + ":csrA_phi"
1882  tb = tb + "}"
1883  TextBox /W=$graphname /A=LT /B=1 /E=2 /F=0 /N=tb_angles /X=1 /Y=1 /V=0 tb
1884  tb = "\\{"
1885  tb = tb + "\"B = (%.1f, %.1f)\","
1886  tb = tb + graphdf + ":csrB_theta,"
1887  tb = tb + graphdf + ":csrB_phi"
1888  tb = tb + "}"
1889  AppendText /W=$graphname /N=tb_angles tb
1890  // updates are triggered by a window hook
1891  SetWindow $graphname, hook(polar_graph_hook)=PearlAnglescanProcess#polar_graph_hook
1892  else
1893  // graph window exists
1894  DoWindow /F $graphname
1895  endif
1896 
1897  setdatafolder savedf
1898  return graphname
1899 end
1900 
1926 static function /s draw_hemi_axes(graphname, [do_grids])
1927  string graphname
1928  variable do_grids
1929 
1930  if (ParamIsDefault(do_grids))
1931  do_grids = 3
1932  endif
1933 
1934  dfref savedf = GetDataFolderDFR()
1935 
1936  string sproj = GetUserData(graphname, "", "projection")
1937  variable projection = str2num("0" + sproj)
1938 
1939  SetDrawLayer /W=$graphname ProgFront
1940 
1941  // polar axis
1942  SetDrawEnv /W=$graphname xcoord=HorizCrossing, ycoord=VertCrossing
1943  SetDrawEnv /W=$graphname linethick= 0.5
1944  SetDrawEnv /W=$graphname dash=2
1945  SetDrawEnv /W=$graphname fillpat=0
1946  SetDrawEnv /W=$graphname fname="default", fsize=7
1947  SetDrawEnv /W=$graphname textxjust=1, textyjust=1
1948  //SetDrawEnv /W=$graphname linefgc=(65535,65535,65535)
1949  SetDrawEnv /W=$graphname save
1950 
1951  if (do_grids & 1)
1952  DrawLine /W=$graphname 0, -2, 0, 2
1953  DrawLine /W=$graphname -2, 0, 2, 0
1954  endif
1955 
1956  variable radi
1957  if (do_grids & 2)
1958  radi = calc_graph_radius(0.5, projection=projection)
1959  DrawOval /W=$graphname -radi, radi, radi, -radi
1960  radi = calc_graph_radius(30, projection=projection)
1961  DrawOval /W=$graphname -radi, radi, radi, -radi
1962  radi = calc_graph_radius(60, projection=projection)
1963  DrawOval /W=$graphname -radi, radi, radi, -radi
1964 
1965  SetDrawEnv /W=$graphname textxjust= 1,textyjust= 2
1966  SetDrawEnv /W=$graphname save
1967  radi = calc_graph_radius(30, projection=projection)
1968  DrawText /W=$graphname radi, -0.1, "30°"
1969  radi = calc_graph_radius(60, projection=projection)
1970  DrawText /W=$graphname radi, -0.1, "60°"
1971  endif
1972 
1973  setdatafolder savedf
1974 end
1975 
1998 function draw_diffraction_cone(graphname, groupname, theta_axis, theta_inner, phi)
1999  string graphname
2000  string groupname
2001 
2002  variable theta_axis
2003  variable theta_inner
2004  variable phi
2005 
2006  variable r_axis = calc_graph_radius(theta_axis)
2007  variable r_inner = calc_graph_radius(theta_inner)
2008  variable r_outer = calc_graph_radius(2 * theta_axis - theta_inner)
2009 
2010  SetDrawEnv push
2011  SetDrawLayer UserFront
2012  DrawAction getgroup=$groupname, delete
2013  SetDrawEnv gstart, gname=$groupname
2014  variable xc, yc, xr, yr
2015 
2016  // cone periphery
2017  variable r_center = (r_outer + r_inner) / 2
2018  variable r_radius = (r_outer - r_inner) / 2
2019  xc = r_center * cos(phi * pi / 180)
2020  yc = r_center * sin(phi * pi / 180)
2021  xr = r_radius
2022  yr = r_radius
2023  SetDrawEnv xcoord=HorizCrossing, ycoord=VertCrossing
2024  SetDrawEnv dash=11, fillpat=0
2025  DrawOval xc - xr, yc - yr, xc + xr, yc + yr
2026 
2027  // cone axis
2028  xc = r_axis * cos(phi * pi / 180)
2029  yc = r_axis * sin(phi * pi / 180)
2030  r_radius = calc_graph_radius(2)
2031  xr = r_radius
2032  yr = r_radius
2033  SetDrawEnv xcoord=HorizCrossing, ycoord=VertCrossing
2034  SetDrawEnv fillfgc=(0,0,0)
2035  DrawOval xc - xr, yc - yr, xc + xr, yc + yr
2036 
2037  SetDrawEnv gstop
2038  SetDrawEnv pop
2039 end
2040 
2062 function /s display_scanlines(nickname, alpha_lo, alpha_hi, m_theta, m_tilt, m_phi, [folding, projection])
2063  string nickname
2064  variable alpha_lo
2065  variable alpha_hi
2066  wave m_theta
2067  wave m_tilt
2068  wave m_phi
2069  variable folding
2070  variable projection
2071 
2072  if (ParamIsDefault(folding))
2073  folding = 1
2074  endif
2075  if (ParamIsDefault(projection))
2076  projection = 1
2077  endif
2078 
2079  // sort out data folder structure
2080  dfref saveDF = GetDataFolderDFR()
2081  newdatafolder /s/o $nickname
2082  string graphname = "graph_" + nickname
2083 
2084  duplicate /free m_tilt, loc_m_tilt
2085  loc_m_tilt = -m_tilt
2086 
2087  make /n=1 /d /free d_polar, d_azi
2088  variable n_alpha = round(alpha_hi - alpha_lo) + 1
2089  make /n=(n_alpha) /d /free analyser
2090  setscale /i x alpha_lo, alpha_hi, "°", analyser
2091  analyser = x
2092 
2093  convert_angles_ttpa2polar(m_theta, loc_m_tilt, m_phi, analyser, d_polar, d_azi)
2094  duplicate /free d_polar, d_radius
2095  d_radius = calc_graph_radius(d_polar, projection=projection)
2096  d_azi += 180 // changed 151030 (v1.6)
2097 
2098  graphname = display_polar_graph(graphname)
2099  SetWindow $graphname, userdata(projection)=num2str(projection)
2100 
2101  variable ifold
2102  variable iang
2103  variable nang = numpnts(m_theta)
2104  string s_rad
2105  string s_azi
2106  string s_trace
2107  for (ifold = 0; ifold < folding; ifold += 1)
2108  d_azi = d_azi >= 360 ? d_azi - 360 : d_azi
2109  for (iang = 0; iang < nang; iang += 1)
2110  sprintf s_rad, "rad_%d_%d", ifold, iang
2111  duplicate /o analyser, $s_rad
2112  wave w_rad = $s_rad
2113  w_rad = d_radius[p][iang]
2114 
2115  sprintf s_azi, "azi_%d_%d", ifold, iang
2116  duplicate /o analyser, $s_azi
2117  wave w_azi = $s_azi
2118  w_azi = d_azi[p][iang]
2119 
2120  if (numtype(sum(w_rad)) == 0)
2121  s_trace = WMPolarAppendTrace(graphname, w_rad, w_azi, 360)
2122  ModifyGraph /w=$graphname mode($s_trace)=0, lsize($s_trace)=0.5
2123  endif
2124  endfor
2125  d_azi += 360 / folding
2126  endfor
2127 
2128  draw_hemi_axes(graphname)
2129 
2130  setdatafolder saveDF
2131  return graphname
2132 end
2133 
2150 constant kProjDist = 0
2151 constant kProjStereo = 1
2152 constant kProjArea = 2
2153 constant kProjGnom = 3
2154 constant kProjOrtho = 4
2155 
2156 static constant kProjScaleDist = 2
2157 static constant kProjScaleStereo = 2
2158 static constant kProjScaleArea = 2
2159 // scaled so that radius(gnom) = radius(stereo) for polar = 88
2160 static constant kProjScaleGnom = 0.06744519021
2161 static constant kProjScaleOrtho = 2
2162 
2177 threadsafe function calc_graph_radius(polar, [projection])
2178  variable polar
2179  variable projection
2180 
2181  if (ParamIsDefault(projection))
2182  projection = 1
2183  endif
2184 
2185  variable radius
2186  switch(projection)
2187  case kProjStereo: // stereographic
2188  radius = kProjScaleStereo * tan(polar / 2 * pi / 180)
2189  break
2190  case kProjArea: // equal area
2191  radius = kProjScaleArea * sin(polar / 2 * pi / 180)
2192  break
2193  case kProjGnom: // gnomonic
2194  radius = polar < 90 ? kProjScaleGnom * tan(polar * pi / 180) : inf
2195  break
2196  case kProjOrtho: // orthographic
2197  radius = kProjScaleOrtho * sin(polar * pi / 180)
2198  break
2199  default: // equidistant
2200  radius = kProjScaleDist * polar / 90
2201  endswitch
2202 
2203  return radius
2204 end
2205 
2222 threadsafe function calc_graph_polar(x, y, [projection])
2223  variable x
2224  variable y
2225  variable projection
2226 
2227  if (ParamIsDefault(projection))
2228  projection = 1
2229  endif
2230 
2231  variable radius
2232  variable polar
2233 
2234  radius = sqrt(x^2 + y^2)
2235  switch(projection)
2236  case kProjStereo: // stereographic
2237  polar = 2 * atan(radius / kProjScaleStereo) * 180 / pi
2238  break
2239  case kProjArea: // equal area
2240  polar = 2 * asin(radius / kProjScaleArea) * 180 / pi
2241  break
2242  case kProjGnom: // gnomonic
2243  polar = atan(radius / kProjScaleGnom) * 180 / pi
2244  break
2245  case kProjOrtho: // orthographic
2246  polar = asin(radius / kProjScaleOrtho) * 180 / pi
2247  break
2248  default: // equidistant
2249  polar = 90 * radius / kProjScaleDist
2250  endswitch
2251 
2252  return polar
2253 end
2254 
2275 threadsafe function calc_graph_azi(x, y, [projection,zeroAngle])
2276  variable x
2277  variable y
2278  variable projection
2279  variable zeroAngle
2280 
2281  if (ParamIsDefault(projection))
2282  projection = 1
2283  endif
2284  if (ParamIsDefault(zeroAngle))
2285  zeroAngle = 0
2286  endif
2287 
2288  variable azi
2289  if (x > 0)
2290  azi = atan(y / x) * 180 / pi
2291  else
2292  azi = atan(y / x) * 180 / pi + 180
2293  endif
2294 
2295  azi += zeroAngle
2296  if (azi < 0)
2297  azi += 360
2298  endif
2299  if (azi >= 360)
2300  azi -= 360
2301  endif
2302  if (numtype(azi) != 0)
2303  azi = 0
2304  endif
2305 
2306  return azi
2307 end
2308 
2320 static function update_polar_info(graphname)
2321  string graphname
2322 
2323  dfref savedf = GetDataFolderDFR()
2324 
2325  string graphdf = "root:packages:WMPolarGraphs:" + graphname
2326  setdatafolder graphdf
2327 
2328  nvar csrA_theta
2329  nvar csrA_phi
2330  nvar csrB_theta
2331  nvar csrB_phi
2332 
2333  string sproj = GetUserData(graphname, "", "projection")
2334  variable projection = str2num("0" + sproj)
2335  nvar zeroAngleWhere
2336 
2337  variable x = hcsr(A, graphname)
2338  variable y = vcsr(A, graphname)
2339  csrA_theta = calc_graph_polar(x, y, projection=projection)
2340  csrA_phi = calc_graph_azi(x, y, projection=projection, zeroAngle=zeroAngleWhere)
2341 
2342  x = hcsr(B, graphname)
2343  y = vcsr(B, graphname)
2344  csrB_theta = calc_graph_polar(x, y, projection=projection)
2345  csrB_phi = calc_graph_azi(x, y, projection=projection, zeroAngle=zeroAngleWhere)
2346 
2347  setdatafolder savedf
2348 end
2349 
2355 static function polar_graph_hook(s)
2356  STRUCT WMWinHookStruct &s
2357 
2358  Variable hookResult = 0
2359 
2360  switch(s.eventCode)
2361  case 7: // cursor moved
2362  update_polar_info(s.winname)
2363  break
2364  case 20: // show info
2365  TextBox /W=$s.winname /N=tb_angles /C /V=1
2366  break
2367  case 21: // hide info
2368  TextBox /W=$s.winname /N=tb_angles /C /V=0
2369  break
2370  endswitch
2371 
2372  return hookResult // 0 if nothing done, else 1
2373 end
2374 
2375 function set_polar_graph_cursor(nickname, cursorname, polar_angle, azim_angle, [graphname])
2376  string nickname
2377  string cursorname
2378  variable polar_angle
2379  variable azim_angle
2380  string graphname
2381 
2382  if (ParamIsDefault(graphname))
2383  if (strlen(nickname) > 0)
2384  graphname = nickname
2385  else
2386  graphname = GetDataFolder(0)
2387  endif
2388  endif
2389 
2390  string s_prefix = ""
2391  string s_int = "values"
2392  dfref df = find_hemi_data(nickname, s_prefix, s_int)
2393 
2394  string s_polar = s_prefix + "pol"
2395  string s_azim = s_prefix + "az"
2396  wave /sdfr=df /z azim = $s_azim
2397  wave /sdfr=df /z polar = $s_polar
2398 
2399  FindLevel /P /Q polar, polar_angle
2400  if (v_flag == 0)
2401  variable polar_level = floor(v_levelx)
2402  FindLevel /P /Q /R=[polar_level] azim, azim_angle
2403  if (v_flag == 0)
2404  variable azim_level = round(v_levelx)
2405  string tracename = "polarY0"
2406  Cursor /W=$graphname /P $cursorname $traceName azim_level
2407  endif
2408  endif
2409 end
2410 
2420 function hemi_add_anglescan(nickname, values, polar, azi, [weights])
2421  string nickname // name prefix of holo waves.
2422  // may be empty.
2423  wave values // intensity values
2424  // the wave can be one- or two-dimensional.
2425  // no specific order required, the function sorts the arrays internally
2426  wave polar // polar coordinates. allowed range 0 <= theta <= 90
2427  // dimensions corresponding to value.
2428  wave azi // azimuthal coordinates. allowed range -360 <= phi < +360
2429  // dimensions corresponding to value.
2430  wave weights // total accumulation time of each point of values. default = 1
2431 
2432  if (ParamIsDefault(weights))
2433  duplicate /free values, weights
2434  weights = 1
2435  endif
2436 
2437  // quick check whether hemi grid is existing
2438  string s_prefix = ""
2439  string s_int = "values"
2440  dfref df = find_hemi_data(nickname, s_prefix, s_int)
2441 
2442  string s_polar = s_prefix + "pol"
2443  string s_azim = s_prefix + "az"
2444  string s_theta = s_prefix + "th"
2445 
2446  wave /sdfr=df /z w_values = $s_int
2447  wave /sdfr=df /z w_azim = $s_azim
2448  wave /sdfr=df /z w_polar = $s_polar
2449  wave /sdfr=df /z w_theta = $s_theta
2450  if (!waveexists(w_values) || !waveexists(w_azim) || !waveexists(w_polar))
2451  abort "Missing hemispherical scan grid. Please call make_hemi_grid() first."
2452  endif
2453 
2454  // make internal copies, one-dimensional, ordered in theta
2455  duplicate /free values, values_copy
2456  duplicate /free polar, polar_copy
2457  duplicate /free azi, azi_copy
2458  duplicate /free weights, weights_copy
2459  variable nn = dimsize(values, 0) * max(dimsize(values, 1), 1)
2460  redimension /n=(nn) values_copy, polar_copy, azi_copy, weights_copy
2461  sort /r polar_copy, polar_copy, azi_copy, values_copy, weights_copy
2462 
2463  variable pol
2464  variable pol_st = abs(w_theta[1] - w_theta[0])
2465  variable pol1, pol2
2466 
2467  duplicate /free azi_copy, azi_slice
2468  duplicate /free values_copy, values_slice
2469  duplicate /free weights_copy, weights_slice
2470  for (pol = 90; pol >= 0; pol -= pol_st)
2471  pol1 = pol - pol_st / 2
2472  pol2 = pol + pol_st / 2
2473  extract /free /indx polar_copy, sel, (pol1 < polar_copy) && (polar_copy <= pol2)
2474  if (numpnts(sel) > 0)
2475  redimension /n=(numpnts(sel)) azi_slice, values_slice, weights_slice
2476  azi_slice = azi_copy[sel]
2477  values_slice = values_copy[sel]
2478  weights_slice = weights_copy[sel]
2479  hemi_add_aziscan(nickname, values_slice, pol, azi_slice, weights=weights_slice)
2480  endif
2481  endfor
2482 end
2483 
2490 function hemi_add_aziscan(nickname, values, polar, azi, [weights])
2491  string nickname // name prefix of holo waves.
2492  // may be empty.
2493  wave values // intensity values of the azimuthal scan at the positions given in the azi parameter
2494  variable polar // polar angle where to add the azi scan
2495  wave azi // angle positions of the azimuthal scan
2496  // acceptable range: >= -360 and < +360
2497  // no specific order required, the function sorts the array internally
2498  wave weights // total accumulation time of each point of values. default = 1
2499 
2500  if (ParamIsDefault(weights))
2501  duplicate /free values, weights
2502  weights = 1
2503  endif
2504 
2505  // hemi grid waves
2506  string s_prefix = ""
2507  string s_int = "values"
2508  dfref df = find_hemi_data(nickname, s_prefix, s_int)
2509 
2510  string s_totals = s_prefix + "tot"
2511  string s_weights = s_prefix + "wt"
2512  string s_polar = s_prefix + "pol"
2513  string s_azim = s_prefix + "az"
2514  string s_index = s_prefix + "index"
2515  string s_theta = s_prefix + "th"
2516  string s_dphi = s_prefix + "dphi"
2517  string s_nphis = s_prefix + "nphis"
2518 
2519  wave /sdfr=df w_polar = $s_polar
2520  wave /sdfr=df w_azim = $s_azim
2521  wave /sdfr=df w_values = $s_int
2522  wave /sdfr=df w_totals = $s_totals
2523  wave /sdfr=df w_weights = $s_weights
2524  wave /sdfr=df w_index = $s_index
2525  wave /sdfr=df w_theta = $s_theta
2526  wave /sdfr=df w_dphi = $s_dphi
2527  wave /sdfr=df w_nphis = $s_nphis
2528 
2529  // destination slice coordinates
2530  //polar = round(polar)
2531  //variable ipol = 90 - polar
2532  variable ipol = BinarySearch(w_theta, polar)
2533  if (ipol < 0)
2534  abort "assertion failed in hemi_add_aziscan(): polar angle not found in grid."
2535  endif
2536 
2537  variable d1, d2
2538  if (ipol >= 1)
2539  d1 = w_index[ipol - 1]
2540  else
2541  d1 = 0
2542  endif
2543  d2 = w_index[ipol] - 1
2544  variable nd = d2 - d1 + 1
2545  variable dphi = w_dphi[ipol]
2546  variable az1, az2
2547 
2548  // source slice coordinates
2549  // order the slice from -dphi/2 to 360-dphi/2
2550  azi = azi < 0 ? azi + 360 : azi
2551  azi = azi >= 360 - dphi/2 ? azi - 360 : azi
2552  duplicate /free values, sel_values
2553  duplicate /free weights, sel_weights
2554 
2555  // loop over destination
2556  variable id
2557  variable v1, v2, w1, w2
2558  for (id = 0; id < nd; id += 1)
2559  az1 = (id - 0.5) * dphi
2560  az2 = (id + 0.5) * dphi
2561  extract /free /indx azi, sel, (az1 <= azi) && (azi < az2)
2562  if (numpnts(sel) > 0)
2563  redimension /n=(numpnts(sel)) sel_values, sel_weights
2564  sel_values = values[sel]
2565  sel_weights = weights[sel]
2566  v1 = w_totals[d1 + id]
2567  w1 = w_weights[d1 + id]
2568  if ((numtype(v1) == 2) || (w1 <= 0))
2569  v1 = 0
2570  w1 = 0
2571  endif
2572  v2 = sum(sel_values)
2573  w2 = sum(sel_weights)
2574  w_totals[d1 + id] = v1 + v2
2575  w_weights[d1 + id] = w1 + w2
2576  endif
2577  endfor
2578  w_values[d1, d1 + nd - 1] = w_totals[p] / w_weights[p]
2579 end
2580 
2608 function interpolate_hemi_scan(nickname, [projection])
2609  string nickname
2610  variable projection
2611 
2612  dfref savedf = GetDataFolderDFR()
2613 
2614  if (ParamIsDefault(projection))
2615  projection = 1
2616  endif
2617 
2618  string s_prefix = ""
2619  string s_int = "values"
2620  dfref df = find_hemi_data(nickname, s_prefix, s_int)
2621  prepare_hemi_scan_display(nickname, projection=projection)
2622 
2623  string s_polar = s_prefix + "pol"
2624  string s_azim = s_prefix + "az"
2625  string s_matrix = s_prefix + "matrix"
2626  string s_ster_rad = s_prefix + "ster_rad"
2627  string s_ster_x = s_prefix + "ster_x"
2628  string s_ster_y = s_prefix + "ster_y"
2629 
2630  wave /sdfr=df /z values = $s_int
2631  wave /sdfr=df /z azim = $s_azim
2632  wave /sdfr=df /z polar = $s_polar
2633  wave /sdfr=df /z ster_rad = $s_ster_rad
2634  wave /sdfr=df /z ster_x = $s_ster_x
2635  wave /sdfr=df /z ster_y = $s_ster_y
2636 
2637  variable min_ster_x = wavemin(ster_x)
2638  variable max_ster_x = wavemax(ster_x)
2639  variable x0 = min_ster_x
2640  variable xn = 181
2641  variable dx = (max_ster_x - min_ster_x) / (xn - 1)
2642  make /n=(numpnts(ster_x), 3) /free triplet
2643  triplet[][0] = ster_x[p]
2644  triplet[][1] = ster_y[p]
2645  triplet[][2] = values[p]
2646 
2647  variable size = 181
2648  setdatafolder df
2649  make /n=(size, size) /d /o $(s_prefix + "matrix") /wave=matrix
2650  make /n=(size, size) /free mnorm
2651  ImageFromXYZ /as {ster_x, ster_y, values}, matrix, mnorm
2652  matrix /= mnorm
2653  matrixfilter NanZapMedian, matrix
2654  matrixfilter gauss, matrix
2655 
2656  duplicate /free values, ster_finite
2657  ster_finite = (numtype(values) == 0) * (ster_x^2 + ster_y^2)
2658  variable ster_max = wavemax(ster_finite)
2659  matrix = (x^2 + y^2) <= ster_max ? matrix : nan
2660 
2661  setdatafolder savedf
2662 end
2663 
2674 function quick_pizza_image(data, nickname, theta_offset, tilt_offset, phi_offset, [npolar, nograph, folding])
2675  wave data // 2D intensity wave, see requirements above
2676  string nickname // nick name for output data
2677  // in default mode, this will be the name of a child folder containing the output
2678  // in XPDplot mode, this will be a prefix of the generated data in the root folder
2679  variable theta_offset // manipulator theta angle corresponding to normal emission
2680  variable tilt_offset // manipulator tilt angle corresponding to normal emission
2681  variable phi_offset // manipulator phi angle corresponding to phi_result = 0
2682  variable npolar // number of polar angles, determines polar and azimuthal step size
2683  // default = 91 (1 degree steps)
2684  variable nograph // 0 (default) = display a new polar graph
2685  // 1 = don't display a new graph (if a graph is existing from a previous call, it will update)
2686  variable folding // rotational averaging, default = 1
2687 
2688  if (ParamIsDefault(npolar))
2689  npolar = 91
2690  endif
2691  if (ParamIsDefault(nograph))
2692  nograph = 0
2693  endif
2694  if (ParamIsDefault(folding))
2695  folding = 1
2696  endif
2697  string graphname = "graph_" + nickname
2698  string s_prefix = ""
2699 
2700  // sort out data folder structure
2701  dfref saveDF = GetDataFolderDFR()
2702  dfref dataDF = GetWavesDataFolderDFR(data)
2703  setdatafolder dataDF
2704  if (DataFolderExists(":attr"))
2705  setdatafolder :attr
2706  endif
2707  dfref attrDF = GetDataFolderDFR()
2708  setdatafolder dataDF
2709  newdatafolder /s/o $nickname
2710  dfref destDF = GetDataFolderDFR()
2711 
2712  // performance monitoring
2713  variable timerRefNum
2714  variable /g xyz_perf_secs
2715  timerRefNum = startMSTimer
2716 
2717  wave /sdfr=attrDF ManipulatorTheta
2718  wave /sdfr=attrDF ManipulatorTilt
2719  wave /sdfr=attrDF ManipulatorPhi
2720  duplicate /free ManipulatorTheta, m_theta
2721  duplicate /free ManipulatorTilt, m_tilt
2722  duplicate /free ManipulatorPhi, m_phi
2723  m_theta -= theta_offset
2724  m_tilt -= tilt_offset
2725  m_tilt *= -1 // checked 140702
2726  m_phi -= phi_offset
2727  //m_phi *= -1 // checked 140702
2728 
2729  make /n=1/d/free d_polar, d_azi
2730  convert_angles_ttpd2polar(m_theta, m_tilt, m_phi, data, d_polar, d_azi)
2731  d_azi += 180 // changed 151030 (v1.6)
2732  d_azi = d_azi >= 360 ? d_azi - 360 : d_azi
2733 
2734  duplicate /free data, values
2735  variable nn = dimsize(values, 0) * max(dimsize(values, 1), 1)
2736  redimension /n=(nn) values, d_polar, d_azi
2737  duplicate /o d_polar, ster_rad, ster_x, ster_y
2738 
2739  variable projection = 1
2740  switch(projection)
2741  case 1: // stereographic
2742  ster_rad = 2 * tan(d_polar / 2 * pi / 180)
2743  break
2744  case 2: // azimuthal
2745  ster_rad = 2 * cos((180 - d_polar) / 2 * pi / 180)
2746  break
2747  endswitch
2748  string s_ster_x = s_prefix + "ster_x"
2749  string s_ster_y = s_prefix + "ster_y"
2750 
2751  nn = 401
2752  make /n=(nn, nn) /d /o matrix
2753  make /n=(nn, nn) /free mnorm
2754  setscale /i x -2, +2, matrix, mnorm
2755  setscale /i y -2, +2, matrix, mnorm
2756  matrix = 0
2757  mnorm = 0
2758 
2759  variable ifold
2760  for (ifold = 0; ifold < folding; ifold += 1)
2761  ster_x = ster_rad * cos(d_azi * pi / 180)
2762  ster_y = ster_rad * sin(d_azi * pi / 180)
2763  ImageFromXYZ {ster_x, ster_y, values}, matrix, mnorm
2764  d_azi = d_azi >= 180 ? d_azi + 360 / folding - 180 : d_azi + 360 / folding
2765  endfor
2766 
2767  matrix /= mnorm
2768  matrixfilter /n=5 NanZapMedian matrix
2769  matrixfilter /n=3 gauss matrix
2770 
2771  if (!nograph)
2772  display /k=1
2773  appendimage matrix
2774  modifygraph width={Plan,1,bottom,left}
2775  endif
2776 
2777  if (timerRefNum >= 0)
2778  xyz_perf_secs = stopMSTimer(timerRefNum) / 1e6
2779  endif
2780 
2781  setdatafolder saveDF
2782 end
2783 
2785 function save_hemi_scan(nickname, pathname, filename)
2786  string nickname
2787  string pathname
2788  string filename
2789 
2790  dfref savedf = getdatafolderdfr()
2791 
2792  // source data
2793  string s_prefix = ""
2794  string s_int = "values"
2795  dfref df = find_hemi_data(nickname, s_prefix, s_int)
2796 
2797  string s_polar = s_prefix + "pol"
2798  string s_azim = s_prefix + "az"
2799  string s_theta = s_prefix + "th"
2800  string s_tot = s_prefix + "tot"
2801  string s_weight = s_prefix + "wt"
2802 
2803  wave /sdfr=df theta1 = $s_theta
2804  wave /sdfr=df polar1 = $s_polar
2805  wave /sdfr=df azim1 = $s_azim
2806  wave /sdfr=df tot1 = $s_tot
2807  wave /sdfr=df weight1 = $s_weight
2808  wave /sdfr=df values1 = $s_int
2809 
2810  save /m="\r\n" /o /p=$pathname /t theta1, polar1, azim1, tot1, weight1, values1 as filename
2811 
2812  setdatafolder saveDF
2813 end
2814 
2818 function load_hemi_scan(nickname, pathname, filename)
2819  string nickname
2820  string pathname
2821  string filename
2822 
2823  dfref savedf = getdatafolderdfr()
2824 
2825  //loadwave /p=$pathname /t theta1, polar1, azim1, tot1, weight1, values1 as filename
2826  //LoadWave /t/p=pearl_explorer_filepath/q filename
2827  //svar waves = s_wavenames
2828  //if (v_flag > 0)
2829  // string /g pearl_explorer_import = "load_itx_file"
2830  //endif
2831 
2832  setdatafolder saveDF
2833 end
2834 
2867 function import_tpi_scan(nickname, theta, phi, intensity, [folding, npolar, nograph, xpdplot])
2868  string nickname
2869  wave theta
2870  wave phi
2871  wave intensity
2872 
2873  variable folding
2874  variable npolar
2875  variable nograph
2876  variable xpdplot
2877 
2878  if (ParamIsDefault(npolar))
2879  npolar = 91
2880  endif
2881  if (ParamIsDefault(nograph))
2882  nograph = 0
2883  endif
2884  if (ParamIsDefault(folding))
2885  folding = 1
2886  endif
2887  if (ParamIsDefault(xpdplot))
2888  xpdplot = 0
2889  endif
2890 
2891  make_hemi_grid(npolar, nickname, xpdplot=xpdplot)
2892 
2893  variable ifold
2894  duplicate /free phi, fold_phi
2895  for (ifold = 0; ifold < folding; ifold += 1)
2896  hemi_add_anglescan(nickname, intensity, theta, fold_phi)
2897  fold_phi = fold_phi >= 180 ? fold_phi + 360 / folding - fold_phi : fold_phi + 360 / folding
2898  endfor
2899 
2900  if (nograph==0)
2901  display_hemi_scan(nickname)
2902  endif
2903 end
2904 
2916 function trim_hemi_scan(nickname, theta_max)
2917  string nickname
2918  variable theta_max
2919 
2920  string s_prefix = ""
2921  string s_int = "values"
2922  dfref df = find_hemi_data(nickname, s_prefix, s_int)
2923 
2924  string s_totals = s_prefix + "tot"
2925  string s_weights = s_prefix + "wt"
2926  string s_polar = s_prefix + "pol"
2927 
2928  wave /sdfr=df w_polar = $s_polar
2929  wave /sdfr=df w_values = $s_int
2930  wave /sdfr=df w_totals = $s_totals
2931  wave /sdfr=df w_weights = $s_weights
2932 
2933  w_values = w_polar <= theta_max ? w_totals / w_weights : nan
2934 end
2935 
2955 function /wave hemi_polar_cut(nickname, azim)
2956  string nickname
2957  variable azim
2958 
2959  dfref savedf = getdatafolderdfr()
2960  string s_prefix = ""
2961  string s_int = "values"
2962  dfref df = find_hemi_data(nickname, s_prefix, s_int)
2963 
2964  string s_totals = s_prefix + "tot"
2965  string s_weights = s_prefix + "wt"
2966  string s_polar = s_prefix + "pol"
2967  string s_azim = s_prefix + "az"
2968  string s_index = s_prefix + "index"
2969  string s_theta = s_prefix + "th"
2970  string s_dphi = s_prefix + "dphi"
2971  string s_nphis = s_prefix + "nphis"
2972  string s_cut
2973  sprintf s_cut, "%s_azi%03u", s_int, round(azim)
2974 
2975  wave /sdfr=df w_polar = $s_polar
2976  wave /sdfr=df w_azim = $s_azim
2977  wave /sdfr=df w_values = $s_int
2978  wave /sdfr=df w_totals = $s_totals
2979  wave /sdfr=df w_weights = $s_weights
2980  wave /sdfr=df w_index = $s_index
2981  wave /sdfr=df w_theta = $s_theta
2982  wave /sdfr=df w_dphi = $s_dphi
2983  wave /sdfr=df w_nphis = $s_nphis
2984 
2985  variable npol = numpnts(w_theta)
2986  variable ipol
2987  variable pol_st = abs(w_theta[1] - w_theta[0])
2988  variable pol
2989  variable pol1, pol2
2990  variable nsel
2991 
2992  setdatafolder df
2993  make /n=(npol) /o $s_cut
2994  wave w_cut = $s_cut
2995  setscale /i x w_theta[0], w_theta[numpnts(w_theta)-1], "deg", w_cut
2996  make /n=1 /free azi_slice
2997  make /n=1 /free values_slice
2998 
2999  for (ipol = 0; ipol < npol; ipol += 1)
3000  pol = w_theta[ipol]
3001  pol1 = pol - pol_st / 2
3002  pol2 = pol + pol_st / 2
3003  extract /free /indx w_polar, sel, (pol1 < w_polar) && (w_polar <= pol2)
3004  nsel = numpnts(sel)
3005  if (nsel > 0)
3006  redimension /n=(nsel+2) azi_slice, values_slice
3007  azi_slice[1, nsel] = w_azim[sel[p-1]]
3008  azi_slice[0] = azi_slice[nsel] - 360
3009  azi_slice[nsel+1] = azi_slice[1] + 360
3010  values_slice[1, nsel] = w_values[sel[p-1]]
3011  values_slice[0] = values_slice[nsel]
3012  values_slice[nsel+1] = values_slice[1]
3013  w_cut[ipol] = interp(azim, azi_slice, values_slice)
3014  else
3015  w_cut[ipol] = nan
3016  endif
3017  endfor
3018 
3019  setdatafolder savedf
3020  return w_cut
3021 end
3022 
3041 function /wave hemi_azi_cut(nickname, pol)
3042  string nickname
3043  variable pol
3044 
3045  dfref savedf = getdatafolderdfr()
3046  string s_prefix = ""
3047  string s_int = "values"
3048  dfref df = find_hemi_data(nickname, s_prefix, s_int)
3049 
3050  string s_totals = s_prefix + "tot"
3051  string s_weights = s_prefix + "wt"
3052  string s_polar = s_prefix + "pol"
3053  string s_azim = s_prefix + "az"
3054  string s_index = s_prefix + "index"
3055  string s_theta = s_prefix + "th"
3056  string s_dphi = s_prefix + "dphi"
3057  string s_nphis = s_prefix + "nphis"
3058  string s_cut
3059  sprintf s_cut, "%s_pol%03u", s_int, round(pol)
3060 
3061  wave /sdfr=df w_polar = $s_polar
3062  wave /sdfr=df w_azim = $s_azim
3063  wave /sdfr=df w_values = $s_int
3064  wave /sdfr=df w_totals = $s_totals
3065  wave /sdfr=df w_weights = $s_weights
3066  wave /sdfr=df w_index = $s_index
3067  wave /sdfr=df w_theta = $s_theta
3068  wave /sdfr=df w_dphi = $s_dphi
3069  wave /sdfr=df w_nphis = $s_nphis
3070 
3071  variable pol_st = abs(w_theta[1] - w_theta[0])
3072  variable pol1, pol2
3073  variable nsel
3074 
3075  pol1 = pol - pol_st / 2
3076  pol2 = pol + pol_st / 2
3077  extract /free /indx w_polar, sel, (pol1 < w_polar) && (w_polar <= pol2)
3078  nsel = numpnts(sel)
3079  if (nsel > 0)
3080  setdatafolder df
3081  make /n=(nsel) /o $s_cut
3082  wave w_cut = $s_cut
3083  w_cut = w_values[sel]
3084  setscale /i x w_azim[sel[0]], w_azim[sel[nsel-1]], "°", w_cut
3085  setdatafolder savedf
3086  return w_cut
3087  else
3088  setdatafolder savedf
3089  return $""
3090  endif
3091  setdatafolder savedf
3092 end
3093 
3094 static function check_contrast(values, pcmin, pcmax, vmin, vmax)
3095  wave values
3096  variable pcmin
3097  variable pcmax
3098  variable &vmin
3099  variable &vmax
3100 
3101  dfref save_df = GetDataFolderDFR()
3102  dfref dfr = NewFreeDataFolder()
3103  setdatafolder dfr
3104  StatsQuantiles /inan /iw /q /z values
3105  wave index = w_quantilesindex
3106  variable imin = round(numpnts(index) * pcmin / 100)
3107  variable imax = round(numpnts(index) * (100 - pcmax) / 100)
3108  vmin = values[index[imin]]
3109  vmax = values[index[imax]]
3110  setdatafolder save_df
3111 end
3112 
3129 function set_contrast(pcmin, pcmax, [graphname, colortable])
3130  variable pcmin
3131  variable pcmax
3132  string graphname
3133  string colortable
3134 
3135  if (ParamIsDefault(graphname))
3136  graphname = ""
3137  endif
3138  if (ParamIsDefault(colortable))
3139  colortable = ""
3140  endif
3141 
3142  dfref save_df = GetDataFolderDFR()
3143 
3144  string objname
3145  string info
3146  string wname
3147  string ctab
3148  variable rev
3149  variable n
3150  variable i
3151  variable vmin
3152  variable vmax
3153 
3154  string traces = TraceNameList(graphname, ";", 1+4)
3155  n = ItemsInList(traces, ";")
3156  for (i = 0; i < n; i += 1)
3157  objname = StringFromList(i, traces, ";")
3158  info = TraceInfo(graphname, objname, 0)
3159  if (strlen(info) > 0)
3160  info = StringByKey("RECREATION", info, ":", ";")
3161  info = StringByKey("zColor(x)", info, "=", ";")
3162  if (strlen(info) > 2)
3163  info = info[1,strlen(info)-2]
3164  wname = StringFromList(0, info, ",")
3165  wave w = $wname
3166  ctab = StringFromList(3, info, ",")
3167  rev = str2num("0" + StringFromList(4, info, ","))
3168  if (strlen(colortable) > 0)
3169  ctab = colortable
3170  endif
3171  check_contrast(w, pcmin, pcmax, vmin, vmax)
3172  ModifyGraph /w=$graphname zColor($objname)={w, vmin, vmax, $ctab, rev}
3173  endif
3174  endif
3175  endfor
3176 
3177  string images = ImageNameList(graphname, ";")
3178  n = ItemsInList(images, ";")
3179  for (i = 0; i < n; i += 1)
3180  objname = StringFromList(i, images, ";")
3181  wave w = ImageNameToWaveRef(graphname, objname)
3182  info = ImageInfo(graphname, objname, 0)
3183  if (strlen(info) > 0)
3184  info = StringByKey("RECREATION", info, ":", ";")
3185  info = StringByKey("ctab", info, "=", ";")
3186  if (strlen(info) > 2)
3187  info = info[1,strlen(info)-2]
3188  ctab = StringFromList(2, info, ",")
3189  rev = str2num("0" + StringFromList(3, info, ","))
3190  if (strlen(colortable) > 0)
3191  ctab = colortable
3192  endif
3193  check_contrast(w, pcmin, pcmax, vmin, vmax)
3194  ModifyImage /w=$graphname $objname ctab={vmin, vmax, $ctab, rev}
3195  endif
3196  endif
3197  endfor
3198 
3199  setdatafolder save_df
3200 end
wave hemi_polar_cut(string nickname, variable azim)
extract a polar cut from a hemispherical scan.
variable trim_hemi_scan(string nickname, variable theta_max)
trim a hemispherical scan at grazing angle
string prepare_hemi_scan_display(string nickname, variable projection=defaultValue)
create waves for plotting a hemispherical angle scan.
variable pizza_service_2(wave data, string nickname, wave m_theta, wave m_tilt, wave m_phi, variable npolar=defaultValue, variable nograph=defaultValue, variable folding=defaultValue, variable xpdplot=defaultValue)
create a pizza plot from a measured (energy-integrated) data strip
static const variable kProjScaleOrtho
variable polar2cart_wave(wave in, wave out)
variable normalize_strip_theta(wave strip, wave theta, variable theta_offset=defaultValue, variable smooth_method=defaultValue, variable smooth_factor=defaultValue, variable check=defaultValue)
divide the strip by the average polar distribution.
variable normalize_strip_2d(wave strip, wave theta, variable theta_offset=defaultValue, variable smooth_method=defaultValue, variable smooth_factor=defaultValue, variable check=defaultValue)
divide the strip by a two-dimensional normalization function.
variable crop_strip(wave strip, variable xlo, variable xhi)
crop a strip at the sides.
const variable kProjGnom
threadsafe wave ad_profile_y(wave dataset, variable p1, variable p2, string destname, variable noavg=defaultValue)
1D cut through 2D dataset along Y dimension, new destination wave.
variable make_hemi_grid(variable npol, string nickname, variable xpdplot=defaultValue)
create a hemispherical, constant solid angle grid
variable hemi_add_anglescan(string nickname, wave values, wave polar, wave azi, wave weights=defaultValue)
add an arbitrary angle scan to a hemispherical scan grid.
string get_hemi_nickname(wave w)
finds the nick name given any hemi wave
string get_hemi_prefix(wave w)
finds the prefix given any hemi wave
string display_hemi_scan(string nickname, variable projection=defaultValue, variable graphtype=defaultValue, variable do_ticks=defaultValue, variable do_grids=defaultValue, string graphname=defaultValue)
display a plot of a hemispherical angle scan.
static variable CalcN_Theta(string HoloMode, variable Theta_in, variable Theta_ran, variable Theta_st)
calculate the number of thetas for a pattern
dfr find_hemi_data(string nickname, string *prefix, string *intwave)
finds the folder, prefix and name of holo waves given their nick name
variable convert_angles_ttpd2polar(wave theta, wave tilt, wave phi, wave data, wave polar, wave azi)
convert angles from TTPA (theta-tilt-phi-analyser) scheme to polar coordinates.
variable clear_hemi_grid(string nickname)
clear a hemispherical scan grid
static const variable kProjScaleDist
threadsafe variable calc_graph_polar(variable x, variable y, variable projection=defaultValue)
calculate polar angle from Cartesian coordinate
static variable check_contrast(wave values, variable pcmin, variable pcmax, variable *vmin, variable *vmax)
static variable Calc_The_step(variable th, variable Theta_st, string Holomode)
calculate delta-theta for a given theta
const variable kProjArea
static variable line_average(wave source, wave dest)
threadsafe variable calc_graph_radius(variable polar, variable projection=defaultValue)
calculate the projected polar angle
variable pizza_service(wave data, string nickname, variable theta_offset, variable tilt_offset, variable phi_offset, variable npolar=defaultValue, variable nograph=defaultValue, variable folding=defaultValue, variable xpdplot=defaultValue)
create a pizza plot from a measured (energy-integrated) data strip
variable rotate_x_wave(wave inout, variable angle)
variable cart2polar_wave(wave in, wave out)
string display_scanlines(string nickname, variable alpha_lo, variable alpha_hi, wave m_theta, wave m_tilt, wave m_phi, variable folding=defaultValue, variable projection=defaultValue)
display a polar graph with lines indicating the angles covered by an angle scan.
wave hemi_azi_cut(string nickname, variable pol)
extract an azimuthal cut from a hemispherical scan
variable draw_diffraction_cone(string graphname, string groupname, variable theta_axis, variable theta_inner, variable phi)
draw the circle of a diffraction cone in a stereographic polar graph.
variable interpolate_hemi_scan(string nickname, variable projection=defaultValue)
interpolate a hemispherical scan onto a rectangular grid
variable hemi_add_aziscan(string nickname, wave values, variable polar, wave azi, wave weights=defaultValue)
add an azimuthal scan to a hemispherical scan grid.
static const variable kProjScaleGnom
variable rotate_y_wave(wave inout, variable angle)
variable rotate_hemi_scan(string nickname, variable angle)
azimuthally rotate a hemispherical scan dataset.
static const variable kProjScaleStereo
processing and holographic mapping of angle scanned XPD data.
static string display_polar_graph(string graphname, variable angle_offset=defaultValue, variable do_ticks=defaultValue)
displays an empty polar graph
const variable kProjOrtho
variable save_hemi_scan(string nickname, string pathname, string filename)
save a hemispherical scan to an Igor text file
variable normalize_strip_phi(wave strip, wave theta, wave phi, variable theta_offset=defaultValue, variable theta_range=defaultValue, variable check=defaultValue)
divide the strip by a sine function in phi (wobble correction).
variable normalize_strip_thetaphi(wave strip, wave theta, wave phi, variable theta_offset=defaultValue, variable smooth_method=defaultValue, variable smooth_factor=defaultValue, variable check=defaultValue)
divide the strip by a smooth polar-azimuthal distribution.
const variable kProjStereo
variable quick_pizza_image(wave data, string nickname, variable theta_offset, variable tilt_offset, variable phi_offset, variable npolar=defaultValue, variable nograph=defaultValue, variable folding=defaultValue)
map angle scan data onto a rectangular grid in stereographic projection
static variable polar_graph_hook(WMWinHookStruct *s)
polar graph window hook
const variable kProjDist
threadsafe variable calc_graph_azi(variable x, variable y, variable projection=defaultValue, variable zeroAngle=defaultValue)
calculate azimuthal angle from Cartesian coordinate
variable set_contrast(variable pcmin, variable pcmax, string graphname=defaultValue, string colortable=defaultValue)
set the pseudocolor contrast by percentile.
variable strip_delete_frames(wave strip, variable qlo, variable qhi, wave theta, wave tilt, wave phi)
delete a contiguous range of frames from a strip.
variable rotate_z_wave(wave inout, variable angle)
static variable calc_nth(variable Theta_st, variable Theta_in, variable th, variable Phi_ran, variable Phi_ref, string Holomode)
calculate the number of phis for a given theta
static const variable kProjScaleArea
variable show_analyser_line(variable theta, variable tilt, variable phi, variable theta_offset, variable tilt_offset, variable phi_offset, variable npolar=defaultValue, variable nograph=defaultValue, variable xpdplot=defaultValue)
calculate and display the line seen by the analyser for a specific emission angle ...
variable convert_angles_ttpa2polar(wave theta, wave tilt, wave phi, wave analyser, wave polar, wave azi)
convert angles from TTPA (theta-tilt-phi-analyser) scheme to polar coordinates.
static string draw_hemi_axes(string graphname, variable do_grids=defaultValue)
draw polar and azimuthal grids in an existing polar graph.
variable duplicate_hemi_scan(string source_nickname, dfref dest_folder, string dest_nickname, variable xpdplot=defaultValue)
duplicate a hemispherical scan dataset.
variable set_polar_graph_cursor(string nickname, string cursorname, variable polar_angle, variable azim_angle, string graphname=defaultValue)
static variable update_polar_info(string graphname)
update the angles info based on cursors A and B of a given polar graph window
variable import_tpi_scan(string nickname, wave theta, wave phi, wave intensity, variable folding=defaultValue, variable npolar=defaultValue, variable nograph=defaultValue, variable xpdplot=defaultValue)
import a hemispherical scan from theta-phi-intensity waves and display it
variable load_hemi_scan(string nickname, string pathname, string filename)
load a hemispherical scan from an Igor text file
threadsafe wave ad_profile_x(wave dataset, variable q1, variable q2, string destname, variable noavg=defaultValue)
1D cut through 2D dataset along X dimension, new destination wave.
static variable calc_phi_step(variable Theta_in, variable th, variable Theta_st, variable Phi_ran, variable Phi_ref, string Holomode)
calculate delta-phi for a given theta
variable normalize_strip_x(wave strip, variable smooth_method=defaultValue, variable smooth_factor=defaultValue, variable check=defaultValue)
divide the strip by the average X distribution.