; $Id: rmd_detector_view.pro,v 1.3 2005/04/07 20:38:32 rdimeo Exp $
;###############################################################################
;
; NAME:
;  RMD_DETECTOR_VIEW
;
; PURPOSE:
;  Three-dimensional detector view of the data on HFBS.
;
; CATEGORY:
;  DAVE, HFBS, Visualization
;
; AUTHOR:
;   Robert M. Dimeo, Ph.D.
;   NIST Center for Neutron Research
;   100 Bureau Drive
;   Gaithersburg, MD 20899
;   Phone: (301) 975-8135
;   E-mail: robert.dimeo@nist.gov
;   http://www.ncnr.nist.gov/staff/dimeo
;
; LICENSE:
;  The software in this file is written by an employee of
;  National Institute of Standards and Technology
;  as part of the DAVE software project.
;
;  The DAVE software package is not subject to copyright protection
;  and is in the public domain. It should be considered as an
;  experimental neutron scattering data reduction, visualization, and
;  analysis system. As such, the authors assume no responsibility
;  whatsoever for its use, and make no guarantees, expressed or
;  implied, about its quality, reliability, or any other
;  characteristic. The use of certain trade names or commercial
;  products does not imply any endorsement of a particular product,
;  nor does it imply that the named product is necessarily the best
;  product for the stated purpose. We would appreciate acknowledgment
;  if the DAVE software is used of if the code in this file is
;  included in another product.
;
;###############################################################################
; +
; NAME:
;  RMD_DETECTOR_VIEW
;
; PURPOSE:
;  Prototype application that simulates viewing data
;  projected (via texture mapping) onto an image
;  plate type detector.  User can change orientation
;  and see how the data appears as a function of
;  time channel and pixel position.
;
; REQUIRED PROGRAMS:
;  CAMERA__DEFINE and programs required therein
;  CENTERTLB
;  XCOLORS and programs required therein
;  RMD_PICKFILE
;
; -

; *********************************** ;
; The following cylinder object creation routine was
; written by Rick Towler.
function makeTube, v0, v1, tubeRadius, p1,_Extra = extra
    ;  MESH_OBJ params
    p2 = v0
    p3 = v1 - v0
    ;  Calculate the x-product
    ;  You'll have to handle special case where p3==[0,0,1]
    cp = CROSSP(p3,[0.,0.,1.])
    ;  Normalize x-product
    s = SQRT(TOTAL(cp^2))
    cp = cp / s
    ;  Create the line to rotate
    array1 = [[v0],[v1]] - REBIN(tubeRadius * cp, 3, 2)
    ;  Create the mesh
    MESH_OBJ, 6, verts, polys, array1, P1=p1, P2=p2, P3=p3
    ;  Create a polygon and a line representing the original vector
    oTube = OBJ_NEW('IDLgrPolygon', verts, POLYGONS=polys, $
        _Extra = extra,shading = 1)
    oVec = OBJ_NEW('IDLgrPolyline', [[v0],[v1]], COLOR=[50,100,255])
    RETURN, [oTube, oVec]
end
; *********************************** ;
function makeSection,radius,nx,th,texture_map = texture_map

; We'll use extrusion to create the cylindrical section
; First create an arc in the x-z plane
height = 1.0
angle = 5.0

;th_lo = -(90.-angle)
;th_hi = 90-angle

th_lo = -(90.0-min(th))
th_hi = th_lo+(max(th)-min(th))
n = nx;25
dth = (th_hi-th_lo)/(n - 1.0)
theta = (th_lo+dth*findgen(n))
x = radius * sin(!dtor * theta)
z = radius * cos(!dtor * theta)
y = replicate(-0.5*height,n)

type = 5 ; extrusion
; Create the array based on x and y
array = fltarr(3,n)
for i = 0,n-1 do array[0:2,i] = [x[i],y[i],z[i]]
; Use MESH_OBJ to create the extruded section
p2 = [0,1,0] ; extrude along the y-axis
mesh_obj, type, verts, polys, array , p1 = 1, p2 = p2
; Create a model into which we'll stuff the POLYGON object
odetector = obj_new('IDLgrModel')
; Create the POLYGON object
osection = obj_new('IDLgrPolygon',verts,style = 2,polygons = polys, $
   color = [255,255,255],texture_map = texture_map)
; Calculate the transformation to get uniform texture
; mapping
yMax = MAX(verts[1,*], MIN = yMin)
rotAngle = atan(verts[2,*],verts[0,*]) * !RADEG
;rotAngle = 90.0-atan(verts[0,*],verts[2,*])*!radeg
cMax = MAX(rotAngle, MIN = cMin)
texCoords = [(rotAngle - cMin) / (cMax - cMin), $
              (verts[1,*] - yMin) / (yMax - yMin)]
osection->setproperty,texture_coord=texCoords
odetector->add,osection
return,odetector
end
; *********************************** ;
pro rmd_detector_view_cleanup,tlb
compile_opt idl2,hidden
widget_control,tlb,get_uvalue = pstate
wdelete,(*pstate).winpix
heap_free,pstate
help,/heap,/brief
end
; *********************************** ;
function rmd_dv_makepts,xlo,xhi,nx
compile_opt idl2,hidden
return,xlo+((xhi-xlo)/(nx-1.0))*findgen(nx)
end
; *********************************** ;
function rmd_dv_gaussian,x,area,center,fwhm
compile_opt idl2,hidden
sigma = fwhm/2.354
yg = (area/sqrt(2.0*!dpi*sigma^2))*exp(-0.5*((x-center)/sigma)^2)
return,yg
end
; *********************************** ;
function rmd_dv_lorentzian,x,area,center,fwhm
compile_opt idl2,hidden
gamma = 0.5*fwhm
yg = (area*gamma/!dpi)/((x-center)^2+gamma^2)
return,yg
end
; *********************************** ;
function rmd_dv_create_data
compile_opt idl2,hidden
; As the name suggests, this function simply creates some
; random byte data.
nx = 64 & ny = 16 & nframes = 151
elo = -25.0 & ehi = 25.0
im_plate = bytarr(nx,ny,nframes)
bg = 50.0
area = 500.0
x = rmd_dv_makepts(elo,ehi,nframes)
yp = bg+rmd_dv_gaussian(x,area,0.0,2.0) + $
   rmd_dv_gaussian(x,0.5*area,-5.0,2.0)+rmd_dv_gaussian(x,0.5*area,5.0,2.0)+ $
   rmd_dv_lorentzian(x,2.*area,0.0,10.0)

y = fltarr(nx,ny,nframes)
for i = 0,nframes-1 do y[*,*,i] = randomn(s,nx,ny,poisson = yp[i])
ymult = rebin(0.55+0.45*hanning(nx,ny),nx,ny,nframes)

;y = fltarr(nx,ny,nframes)
;for i = 0,nframes-1 do y[*,0:ny-1,i] = $
;   rebin(0.55+0.45*hanning(nx)*randomn(s,nx,poisson = yp[i]),nx,ny,/sample)
;;ymult = rebin(y,nx,ny,nframes)
;
y = y*ymult
detangles = rmd_dv_makepts(14.46,121.5,nx)
;detangles = [14.46,20.98,27.08,32.31,36.0,43.75,51.5,59.25,67.0,$
;             74.75,82.5,90.25,98.0,105.75,113.5,121.5]
im_plate = bytscl(y)
str = {im_plate:im_plate,energy:x,z:y,dz:sqrt(y),  $
         detector_angles_ptr:ptr_new(detangles,/no_copy)}
return,str
end
; *********************************** ;
function rmd_dv_get_raw_hfbs_data,filename
compile_opt idl2,hidden
catch,the_error
if the_error ne 0 then begin
   void = dialog_message(!error_state.msg)
   if n_elements(lun) ne 0 then free_lun,lun,/force
   return,0B
endif
if n_params() eq 0 then filename = sourceroot()+'20050221_14.hfbs'
if ~file_test(filename) then return,0B
nlines = file_lines(filename)
enchilada = strarr(nlines)
openr,lun,filename,/get_lun
readf,lun,enchilada
free_lun,lun,/force

true = 1B & count = 0L
while true do begin
   text = enchilada[count]
   if strpos(text,'# Detector 0') ne -1 then begin
      det_line = count
      true = 0B
   endif
   count++
endwhile
text_data = enchilada[det_line:*]
ndet = 20 & nchan = 4096
datStr = {instring:'',data:fltarr(2,nchan)}
str_arr = replicate(datStr,ndet)
reads,text_data,str_arr
data = transpose(str_arr.data[1,*])
error = sqrt(data)
data = (transpose(data))
error = (transpose(error))
ch_lo = 0 & ch_hi = 4095
;data = (data[1024:3071,*])[*,1:16]
;error = (error[1024:3071,*])[*,1:16]
data = data[*,1:16]
error = error[*,1:16]
data = reverse(data,2) & error = reverse(error,2)
dsize = size(data,/dimensions)
nx = dsize[1] & nchan = dsize[0]
channel = ch_lo+findgen(nchan)
; Reform QTY and DQTY so that they have the dimensions
; (nx,ny,nframes)
nframes = nchan
ny = 4
qty1 = rebin(data,nframes,nx,ny,/sample)
dqty1 = rebin(error,nframes,nx,ny,/sample)
qty2 = fltarr(nx,ny,nframes)
dqty2 = fltarr(nx,ny,nframes)
for i = 0,nframes-1 do begin
   qty2[0:nx-1,0:ny-1,i] = reform(qty1[i,0:nx-1,0:ny-1],nx,ny)
   dqty2[0:nx-1,0:ny-1,i] = reform(dqty1[i,0:nx-1,0:ny-1],nx,ny)
endfor
z = qty2 & dz = dqty2
dets = [14.46,20.98,27.08,32.31,36.0,43.75,51.5,59.25,67.0,$
        74.75,82.5,90.25,98.0,105.75,113.5,121.5]
im_plate = bytscl(alog10(1.+z))
str = {  im_plate:im_plate,               $
         energy:channel,                  $
         z:z,                             $
         dz:dz,                           $
         detector_angles_ptr:ptr_new(dets)    }

catch,/cancel
return,str
end
; *********************************** ;
function rmd_dv_get_dave_data,filename
compile_opt idl2,hidden
if n_params() eq 0 then filename = sourceroot()+'20010618_01dyn.dave'
if ~file_test(filename) then return,0B
restore,filename
ret_val = get_dave_ptr_contents(daveptr,  $
            qty = qty,                    $
            err = dqty,                   $
            xvals = x,                    $
            specificstr = sp              )
qty = reverse(qty,2) & dqty = reverse(dqty,2)
dets = *sp.phiptr
nx = n_elements(dets)
ny = 4
nframes = n_elements(x)
heap_free,daveptr
; Reform QTY and DQTY so that they have the dimensions
; (nx,ny,nframes)
qty1 = rebin(qty,nframes,nx,ny,/sample)
dqty1 = rebin(dqty,nframes,nx,ny,/sample)
qty2 = fltarr(nx,ny,nframes)
dqty2 = fltarr(nx,ny,nframes)
for i = 0,nframes-1 do begin
   qty2[0:nx-1,0:ny-1,i] = reform(qty1[i,0:nx-1,0:ny-1],nx,ny)
   dqty2[0:nx-1,0:ny-1,i] = reform(dqty1[i,0:nx-1,0:ny-1],nx,ny)
endfor
z = qty2 & dz = dqty2
im_plate = bytscl(alog10(1.+z))
str = {  im_plate:im_plate,               $
         energy:x,                        $
         z:z,                             $
         dz:dz,                           $
         detector_angles_ptr:ptr_new(dets)    }
return,str
end
; *********************************** ;
pro rmd_dv_plot,event
widget_control,event.top,get_uvalue = pstate
device,get_decomposed = dc
device,decomposed = 0
loadct,0,/silent
x = *(*pstate).penergy
xhi = max(x,min = xlo)
zhi = max(*(*pstate).pz)
z = (*(*pstate).pz)[(*pstate).xindex,(*pstate).yindex,*]
dz = (*(*pstate).pdz)[(*pstate).xindex,(*pstate).yindex,*]
;format = '(i3)'
;title = 'Pixel = ('+strtrim(string((*pstate).xindex+1,format = format),2)+  $
;   ','+strtrim(string((*pstate).yindex+1,format = format),2)+')'
dets = *(*pstate).detangles
ndets = n_elements(dets)
det = dets[ndets-(*pstate).xindex-1]
format = '(f10.2)'
title = '!42h!3= '+strtrim(string(det,format = format),2)
if (*pstate).autoscale then begin
   xlo = min(x,max = xhi)
   ylo = min(z,max = yhi)
   delx = 0.1*(xhi-xlo)
   dely = 0.1*yhi
   (*pstate).xrange = [xlo-delx,xhi+delx]
   (*pstate).yrange = [-dely,yhi+dely]
endif
plot,x,z,psym = 4,title = title, $
   yrange = (*pstate).yrange,/ysty, $
   xrange = (*pstate).xrange,/xsty,xtitle = 'Channel'
errplot,x,z-dz,z+dz,width = 0.0
plots,[x[(*pstate).zindex],x[(*pstate).zindex]],!y.crange,/data
device,decomposed = dc
*(*pstate).xptr = !x & *(*pstate).yptr = !y
end
; *********************************** ;
pro rmd_dv_refresh_plot,event
widget_control,event.top,get_uvalue = pstate
wset,(*pstate).winpix
rmd_dv_plot,event
wset,(*pstate).winvis
device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*pstate).winpix]
end
; *********************************** ;
pro rmd_dv_win_events,event
compile_opt idl2,hidden
widget_control,event.top,get_uvalue = pstate
if ~obj_valid((*pstate).ocamera) then return
possibleEventTypes = [ 'DOWN', 'UP', 'MOTION', 'SCROLL', 'EXPOSE' ]
thisEvent = possibleEventTypes[event.type]
possibleButtons = [  'NONE', 'LEFT', 'MIDDLE', 'NONE', 'RIGHT',   $
                     'RIGHTLEFT','MIDDLERIGHT']
thisButton = possibleButtons[event.press]
case thisevent of
'DOWN':  $
   begin
      (*pstate).mouse = thisbutton
      update = (*pstate).otrack->update(event,transform = new)
   end
'UP':  $
   begin
      if (*pstate).mouse eq 'RIGHT' then begin
         ; Set the default orientation
         new = intarr(4,4)
         for i = 0,3 do new[i,i] = 1
         (*pstate).polymodel->setproperty,transform = new
         (*pstate).owin->draw,(*pstate).viewgroup
      endif
      (*pstate).mouse = ''
   end
'MOTION':   $
   begin
      update = (*pstate).otrack->update(event,transform = new)
      if (*pstate).mouse eq 'LEFT' then begin
;         update = (*pstate).otrack->update(event,transform = new)
         if (update) then begin
            (*pstate).polymodel->getproperty,transform = old
            (*pstate).polymodel->setproperty,transform = old # new
            (*pstate).owin->draw,(*pstate).viewgroup
         endif
      endif else begin

         osel = (*pstate).owin->select((*pstate).ocamera,[event.x,event.y])
         if ~obj_valid(osel[0]) then return
         if obj_class(osel[0]) eq 'IDLGRPOLYGON' then begin
            osel[0]->getproperty,data = this_data

;            if total(size(this_data,/dimensions)) ne 7 then return
            if total(size(this_data,/dimensions)) ne (*pstate).this_size then return
            slider = widget_info(event.top,find_by_uname = 'UPDATE_ENERGY_CHANNEL')
            widget_control,slider,get_value = index & (*pstate).zindex = index - 1
            im_plate = (*(*pstate).pdata)[*,*,(*pstate).zindex]
            (*pstate).o_image->setproperty,data = im_plate
            pick = (*pstate).owin->pickdata((*pstate).ocamera,osel[0],  $
               [event.x,event.y],dataxyz)
            verts = this_data
            (*pstate).oscat->getproperty,data = scat_line
            zdist = sqrt(1.-dataxyz[0]^2-dataxyz[1]^2)
            new_scat_line = [[scat_line[*,0]],[dataxyz[[0,1]],dataxyz[2]]]
            (*pstate).oscat->setproperty,data = new_scat_line
            ; Convert the DATAXYZ coordinates to normal coordinates
            osel[0]->getproperty,xrange = xr,yrange = yr,zrange = zr
            xrange = xr[1]-xr[0] & zrange = zr[1]-zr[0] & yrange = yr[1]-yr[0]
            (*pstate).o_image->getproperty,data = data
            dsize = size(data,/dimensions)

            ; Get the mapping out
            rotAngle = atan(verts[2,*],verts[0,*]); * !RADEG
            ;rotAngle = !pi/2.-atan(verts[0,*],verts[2,*])
            cMax = MAX(rotAngle, MIN = cMin)
            x = -(*pstate).distance*cos(rmd_dv_makepts(cMin,cMax,dsize[0]))
            if (dataxyz[0] ge max(x)) or (dataxyz[0] le min(x)) then return
            if (abs(dataxyz[1]) ge 0.5*yrange) then return
            x = -(*pstate).distance*cos(rmd_dv_makepts(cMin,cMax,dsize[0]))
            delx = abs(x-dataxyz[0])
            delz = abs(rmd_dv_makepts(yr[0],yr[1],dsize[1])-dataxyz[1])
            (*pstate).xindex = where(delx eq min(delx))
            (*pstate).yindex = where(delz eq min(delz))
;            ; Highlight the selected pixel
            im_plate[(*pstate).xindex,(*pstate).yindex] = 255B
            (*pstate).o_image->setproperty,data = im_plate
            (*pstate).owin->draw,(*pstate).viewgroup
            rmd_dv_refresh_plot,event
         endif


      endelse
   end
else:
endcase

end
; *********************************** ;
pro rmd_dv_change_colors,event
widget_control,event.top,get_uvalue = pstate
this_event = tag_names(event,/structure_name)
case this_event of
"WIDGET_BUTTON":  $
   xcolors,group_leader = event.top,notifyid = [event.id,event.top]
"XCOLORS_LOAD":   $
   begin
      for i = 0,255 do (*pstate).opalette->setrgb,i,event.r[i],event.g[i],event.b[i]
      (*pstate).owin->draw,(*pstate).viewgroup
   end
else:
endcase
end
; *********************************** ;
pro rmd_dv_resize,event
widget_control,event.top,get_uvalue = pstate
device,get_screen_size = screen_size
col_size = widget_info((*pstate).col_base,/geometry)
winxsize = fix(0.5*(event.x - col_size.xsize)) > 0.4*(screen_size[0] < screen_size[1])
winysize = event.y > 0.4*(screen_size[0] < screen_size[1])
wdelete,(*pstate).winpix
window,/free,/pixmap,xsize = winxsize,ysize = winysize
(*pstate).winpix = !d.window
widget_control,(*pstate).win2,draw_xsize = winxsize,draw_ysize = winysize
widget_control,(*pstate).win1,draw_xsize = winxsize,draw_ysize = winysize
(*pstate).owin->draw,(*pstate).viewgroup
rmd_dv_refresh_plot,event
centertlb,event.top
end
; *********************************** ;
pro rmd_dv_load_test_data,event
widget_control,event.top,get_uvalue = pstate
if ~obj_valid((*pstate).ocamera) then begin
   rmd_dv_scene_setup,event,source = 'TEST'
   return
endif
str = rmd_dv_create_data()
*(*pstate).detangles = *str.detector_angles_ptr
ptr_free,str.detector_angles_ptr

*(*pstate).pz = str.z
*(*pstate).pdz = str.dz
*(*pstate).pdata = str.im_plate
*(*pstate).penergy = str.energy
d = size(*(*pstate).pdata,/dimensions)
nx = d[0] & ny = d[1] & nframes = d[2]
(*pstate).xindex = fix(0.5*nx)
(*pstate).yindex = fix(0.5*ny)
(*pstate).zindex = fix(0.5*nframes)
; Change the slider
widget_control,(*pstate).slider,set_slider_max = nframes,   $
   set_value = (*pstate).zindex
; Change the texture map image
(*pstate).o_image->setproperty,data = (*(*pstate).pdata)[*,*,(*pstate).zindex]
(*pstate).owin->draw,(*pstate).viewgroup
end
; *********************************** ;
pro rmd_dv_load_hfbs_data,event
widget_control,event.top,get_uvalue = pstate
str = rmd_dv_get_dave_data()
if size(str,/tname) eq 'BYTE' then return
*(*pstate).detangles = *str.detector_angles_ptr
ptr_free,str_detector_angles_ptr
*(*pstate).pz = str.z
*(*pstate).pdz = str.dz
*(*pstate).pdata = str.im_plate
*(*pstate).penergy = str.energy
d = size(*(*pstate).pdata,/dimensions)
nx = d[0] & ny = d[1] & nframes = d[2]
(*pstate).xindex = fix(0.5*nx)
(*pstate).yindex = fix(0.5*ny)
(*pstate).zindex = fix(0.5*nframes)
; Change the slider
widget_control,(*pstate).slider,set_slider_max = nframes,   $
   set_value = (*pstate).zindex
; Change the texture map image
(*pstate).o_image->setproperty,data = (*(*pstate).pdata)[*,*,(*pstate).zindex]
(*pstate).owin->draw,(*pstate).viewgroup
end
; *********************************** ;
pro rmd_dv_load_raw_hfbs,event
widget_control,event.top,get_uvalue = pstate
if ~obj_valid((*pstate).ocamera) then begin
   rmd_dv_scene_setup,event,source = 'HFBS'
   return
endif
defsysv, '!dave_defaults', exists = exist
if ~exist then $
   path = sourceroot() else $
   path = (*!dave_defaults).datdir
files = rmd_pickfile(   group_leader = event.top,           $
                              filter_in = '*.hfbs',         $
                              filter_out = filter_out,      $
                              path = path,                  $
                              cancelled = cancelled,        $
                              title = 'Select an HFBS file' )
if cancelled then return
if ~file_test(files) then return
str = rmd_dv_get_raw_hfbs_data(files)
if size(str,/tname) eq 'BYTE' then return
*(*pstate).detangles = *str.detector_angles_ptr
ptr_free,str.detector_angles_ptr
*(*pstate).pz = str.z
*(*pstate).pdz = str.dz
*(*pstate).pdata = str.im_plate
*(*pstate).penergy = str.energy
d = size(*(*pstate).pdata,/dimensions)
nx = d[0] & ny = d[1] & nframes = d[2]
(*pstate).xindex = fix(0.5*nx)
(*pstate).yindex = fix(0.5*ny)
(*pstate).zindex = fix(0.5*nframes)
; Change the slider
widget_control,(*pstate).slider,set_slider_max = nframes,   $
   set_value = (*pstate).zindex
; Change the texture map image
(*pstate).o_image->setproperty,data = (*(*pstate).pdata)[*,*,(*pstate).zindex]
(*pstate).owin->draw,(*pstate).viewgroup
end
; *********************************** ;
pro rmd_dv_plot_events,event
compile_opt idl2,hidden
widget_control,event.top,get_uvalue = pstate
if n_elements(*(*pstate).pz) eq 0 then return
case event.type of
0: begin    ; button press
      (*pstate).mouse2 = event.press
      if (*pstate).mouse2 eq 4 then begin
         (*pstate).autoscale = 1
         rmd_dv_refresh_plot,event
      endif
      if (*pstate).mouse2 eq 1 then begin
         (*pstate).xbox[0] = event.x
         (*pstate).ybox[0] = event.y
         !x = *(*pstate).xptr
         !y = *(*pstate).yptr
         rmd_dv_refresh_plot,event
         empty
         (*pstate).autoscale = 0
      endif
   end
1: begin ; button release
    if (*pstate).mouse2 eq 1 then begin
      xll = (*pstate).xbox[0] < (*pstate).xbox[1]
      yll = (*pstate).ybox[0] < (*pstate).ybox[1]
      w = abs((*pstate).xbox[1] - (*pstate).xbox[0])
      h = abs((*pstate).ybox[1] - (*pstate).ybox[0])
      xur = xll + w
      yur = yll + h
      !x = *(*pstate).xptr & !y = *(*pstate).yptr
      ll = convert_coord(xll,yll,/device,/to_data)
      ur = convert_coord(xur,yur,/device,/to_data)
      (*pstate).xrange = [ll[0],ur[0]]
      (*pstate).yrange = [ll[1],ur[1]]
      rmd_dv_refresh_plot,event
      (*pstate).mouse2 = 0B
    endif
    if (*pstate).mouse2 eq 4 then begin
      rmd_dv_refresh_plot,event
      (*pstate).mouse2 = 0B
    endif
   end
2: begin ; mouse motion
      if (*pstate).mouse2 eq 1 then begin

         (*pstate).xbox[1] = event.x
         (*pstate).ybox[1] = event.y
         xc = [(*pstate).xbox[0],event.x,event.x,$
              (*pstate).xbox[0],$
              (*pstate).xbox[0]]
         yc = [(*pstate).ybox[0],(*pstate).ybox[0],$
              event.y,event.y,$
              (*pstate).ybox[0]]
         !x = *(*pstate).xptr & !y = *(*pstate).yptr
         wset,(*pstate).winvis
         device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*pstate).winpix]
         plots,xc,yc,/device,thick = 2.0
         empty
      endif
   end
else:
endcase
end
; *********************************** ;
pro rmd_detector_view_event,event
compile_opt idl2,hidden
if tag_names(event,/structure_name) eq "WIDGET_BASE" then rmd_dv_resize,event
uname = widget_info(event.id,/uname)
case uname of
'ABOUT': $
   begin
      strout = ['Written by R.M.Dimeo']
      void = dialog_message(dialog_parent = event.top,strout,/info)
   end
'WIN2':  rmd_dv_plot_events,event
'LOAD_TEST':   rmd_dv_load_test_data,event
'LOAD_RAW_HFBS':   rmd_dv_load_raw_hfbs,event
'LOAD_HFBS':   rmd_dv_load_hfbs_data,event
'QUIT':  widget_control,event.top,/destroy
'CHANGE_COLORS':  rmd_dv_change_colors,event
'RESET_ORIENTATION': $
   begin
      widget_control,event.top,get_uvalue = pstate
         new = intarr(4,4)
         for i = 0,3 do new[i,i] = 1
         (*pstate).polymodel->setproperty,transform = new
         (*pstate).owin->draw,(*pstate).viewgroup
   end
'UPDATE_ENERGY_CHANNEL':  $
   begin
      widget_control,event.top,get_uvalue = pstate
      widget_control,event.id,get_value = index
      (*pstate).zindex = index - 1
      im_plate = (*(*pstate).pdata)[*,*,(*pstate).zindex]
      im_plate[(*pstate).xindex,(*pstate).yindex] = 255B
      (*pstate).o_image->setproperty,data = im_plate
      energy = (*(*pstate).penergy)[(*pstate).zindex]
      format = '(i5)'
      (*pstate).etext->setproperty,strings = 'Channel: '+ $
         strtrim(string(energy,format = format),2)
      (*pstate).owin->draw,(*pstate).viewgroup
      rmd_dv_refresh_plot,event
   end
'WIN':   rmd_dv_win_events,event
else:
endcase
end
; *********************************** ;
pro rmd_dv_scene_setup,event,source = source
if n_elements(source) eq 0 then source = 'TEST'
widget_control,event.top,get_uvalue = pstate
if source ne 'TEST' then begin
   defsysv, '!dave_defaults', exists = exist
   if ~exist then $
      path = sourceroot() else $
      path = (*!dave_defaults).datdir
      files = rmd_pickfile(         group_leader = event.top,     $
                                    filter_in = '*.hfbs',         $
                                    filter_out = filter_out,      $
                                    path = path,                  $
                                    cancelled = cancelled,        $
                                    title = 'Select an HFBS file' )
   if cancelled then return
   if ~file_test(files) then return
   str = rmd_dv_get_raw_hfbs_data(files)
   if size(str,/tname) eq 'BYTE' then return
endif else begin
   str =  rmd_dv_create_data()
endelse

*(*pstate).detangles = *str.detector_angles_ptr
ptr_free,str.detector_angles_ptr
*(*pstate).pz = str.z
*(*pstate).pdz = str.dz
*(*pstate).pdata = str.im_plate
*(*pstate).penergy = str.energy
d = size(*(*pstate).pdata,/dimensions)
nx = d[0] & ny = d[1] & nframes = d[2]
(*pstate).nx = nx & (*pstate).ny = ny & (*pstate).nframes = nframes
; Create the scene using a Camera object to facilitate viewing
; Start by initializing colors for the image detector
device,get_decomposed = dc
device,decomposed = 0
loadct,3,/silent  ; start with the red-white color table
tvlct,r,g,b,/get
device,decomposed = dc
; Create a palette object for the pixel data
(*pstate).opalette = obj_new('IDLgrPalette', r,g,b)
(*pstate).ogc->add,(*pstate).opalette
im_plate = *(*pstate).pdata
(*pstate).o_image = obj_new('IDLgrImage',im_plate[*,*,fix(0.5*nframes)], $
   palette = (*pstate).opalette)
(*pstate).ogc->add,(*pstate).o_image
(*pstate).distance = -1.0

(*pstate).o_detector = makeSection((*pstate).distance,(nx>100),   $
   *(*pstate).detangles,texture_map = (*pstate).o_image)
(*pstate).ogc->add,(*pstate).o_detector
poly = ((*pstate).o_detector->get(/all))[0]
poly->getproperty,data = verts
(*pstate).this_size = total(size(verts,/dimensions))

; Put a tube at the origin to represent the sample.
tubeRadius = 0.05
p1 = 20
sample = obj_new('IDLgrModel')
v0 = [0,0.125,0] & v1 = [0,-0.125,0]
tube_objects = makeTube(v0, v1, tubeRadius, p1,color = [255,255,0],style = 2)
(*pstate).ogc->add,tube_objects
sample -> Add, tube_objects[0]

; Add a tube to indicate the "straight-through beam"

get_lost_objects = makeTube([-(*pstate).distance,0.,0.],[0.0,0.,0.], 0.25*tubeRadius, $
   p1,color = [0,0,255],style = 2)
sample ->Add,get_lost_objects[0]
(*pstate).ogc->add,get_lost_objects
(*pstate).ogc->add,sample
scat_line = [[0.,0.,0.],[0.,0.,(*pstate).distance]]
(*pstate).oscat = obj_new('IDLgrPolyLine',scat_line,color = [0,255,0],thick = 2.0,shading = 1)
sample->add,(*pstate).oscat
(*pstate).ogc->add,(*pstate).oscat

vprect = 1.*[-2.,-2.,4.,4.]
cloc = [0.0,0.0,4.0]
lookat = [0.0,0.0,0.0]

(*pstate).polyModel = obj_new('IDLgrModel')
(*pstate).polyModel->add,[(*pstate).o_detector,sample]
zclip = 2.*[4.,-4.]

olight_model = obj_new('IDLgrModel')
olight1 = obj_new('IDLgrLight',type = 1,location = [1,1,5],color = replicate(192B,3))
olight2 = obj_new('IDLgrLight',type = 1,location = [1,-1,5],color = replicate(192B,3))
olight_model->add,[olight1,olight2]

topModel = obj_new('IDLgrModel')
topModel->add,(*pstate).polyModel
(*pstate).oGC->add,topModel
(*pstate).oCamera = obj_new('Camera', color=[0,0,0], viewplane_rect=vpRect, $
         projection=2, camera_location=cloc, zclip=zclip, lookat=lookat, $
         track = 0, eye=1.1*max(zclip),/double)
(*pstate).oCamera->add,topModel

topModel->add,olight_model
(*pstate).oGC->add, (*pstate).oCamera

; Create a HUD view for text
this_vp = [0,0,1,1]
hudview = obj_new('IDLgrView',viewplane_rect = this_vp, $
   zclip=[1,-1],color = [0,0,0],eye = 2,projection = 1,/transparent)
(*pstate).ogc->add,hudview
titlefont = obj_new('idlgrfont','Helvetica',size = 12)
this_text = ['Use mouse to rotate scene','Use slider to change channel']
textcolor = [255,255,0]
title1 = obj_new('idlgrtext',this_text[0], $
        color=textcolor, /onglass, locations=[.5,.95], $
        font=titleFont, alignment=0.5)
title2 = obj_new('idlgrtext',this_text[1], $
        color=textcolor, /onglass, locations=[.5,.9], $
        font=titleFont, alignment=0.5)
(*pstate).etext = obj_new('idlgrtext','Channel: ', $
        color=textcolor, /onglass, locations=[.5,.1], $
        font=titleFont, alignment=0.5)
(*pstate).ogc -> add, [titleFont,title1,title2,(*pstate).etext]
hudmodel = obj_new('idlgrmodel')
(*pstate).ogc -> add, hudmodel
hudmodel -> add, [title1,title2,(*pstate).etext]
hudview->add,hudmodel
(*pstate).viewgroup = obj_new('IDLgrViewgroup')
(*pstate).ogc -> add, (*pstate).viewgroup
(*pstate).viewgroup -> add, [(*pstate).ocamera, hudview]
format = '(i5)'
(*pstate).etext->setproperty,strings = 'Channel: '+ $
   strtrim(string((*(*pstate).penergy)[fix(0.5*(*pstate).nframes)],format = format),2)
(*pstate).oWin->draw,(*pstate).viewgroup

widget_control,(*pstate).slider,set_slider_max = d[2]
widget_control,(*pstate).slider,set_value = fix(0.5*(*pstate).nframes)+1
xsize = !d.x_size & ysize = !d.y_size
(*pstate).otrack = obj_new('trackball',[xsize/2.,ysize/2.],xsize/2.)

end
; *********************************** ;
pro rmd_detector_view,group_leader = group_leader
compile_opt idl2,hidden
if n_elements(group_leader) eq 0 then group_leader = 0L
device,decomposed = 0,get_screen_size = screen_size
!except = 0
xsize = (ysize = fix(0.4*(screen_size[0] < screen_size[1])))
ogc = obj_new('idl_container')
reg_name = 'RMD_DETECTOR_VIEW'
if xregistered(reg_name) then return
tlb = widget_base(/row,title = 'HFBS Raw Data Detector View',/tlb_size_events, $
   mbar = bar,group_leader = group_leader)
col_base = widget_base(tlb,/col)
filemenu = widget_button(bar,value = 'FILE',/menu)
display_menu = widget_button(bar,value = 'DISPLAY')
void = widget_button(filemenu,value = 'Load raw HFBS data',uname = 'LOAD_RAW_HFBS')
void = widget_button(filemenu,value = 'Load test data',uname = 'LOAD_TEST')
;void = widget_button(filemenu,value = 'Load HFBS data',uname = 'LOAD_HFBS')
void = widget_button(display_menu,value = 'Change colors',uname = 'CHANGE_COLORS')
void = widget_button(filemenu,value = 'About Detector View',uname = 'ABOUT')
void = widget_button(filemenu,value = 'Quit',uname = 'QUIT')
void = widget_button(display_menu,value = 'Reset detector orientation', $
   uname = 'RESET_ORIENTATION')
slider = widget_slider(col_base,/vertical,title = 'Channel',value = 1,   $
   min = 1,max = 2,uname = 'UPDATE_ENERGY_CHANNEL')
win1 = widget_draw(tlb,xsize = xsize,ysize = ysize,graphics_level = 2, $
   uname = 'WIN',/button_events,/motion_events,retain = 2)

win2 = widget_draw(tlb,xsize = xsize,ysize = ysize,   $
   /button_events,/motion_events,uname = 'WIN2')
centertlb,tlb
widget_control,tlb,/realize
widget_control,win1,get_value = owin
widget_control,win2,get_value = winvis
window,/free,/pixmap,xsize = xsize,ysize = ysize
winpix = !d.window
ogc->add,owin



pz = ptr_new(/allocate_heap)
pdz = ptr_new(/allocate_heap)
pdata = ptr_new(/allocate_heap)
penergy = ptr_new(/allocate_heap)
opalette = obj_new()
o_image = obj_new()
detangles = ptr_new(/allocate_heap)
o_detector = obj_new()
viewgroup = obj_new()
ocamera = obj_new()
otrack = obj_new()
oscat = obj_new()
etext = obj_new()
polymodel = obj_new()
xptr = ptr_new(/allocate_heap)
yptr = ptr_new(/allocate_heap)
this_size = 0

state =  {  owin:owin,                       $
            otrack:otrack,                   $
            win1:win1,win2:win2,             $
            detangles:detangles,             $
            col_base:col_base,               $
            nx:0,ny:0,nframes:0,             $
            ogc:ogc,                         $
            o_image:o_image,                 $
            viewgroup:viewgroup,             $
            oscat:oscat,                     $
            opalette:opalette,               $
            polymodel:polymodel,             $
            penergy:penergy,                 $
            ocamera:ocamera,                 $
            distance:0.,                     $
            etext:etext,                     $
            xindex:0L,yindex:0L,zindex:0L,   $
            winpix:winpix,                   $
            this_size:this_size,             $
            winvis:winvis,                   $
            slider:slider,                   $
            o_detector:o_detector,           $
            mouse:'',                        $
            pz:pz,pdz:pdz,                   $
            xptr:xptr,yptr:yptr,             $
            mouse2:0B,                       $
            xrange:fltarr(2),                $
            yrange:fltarr(2),                $
            xbox:intarr(2),                  $
            ybox:intarr(2),                  $
            autoscale:1B,                    $
            pdata:pdata                      }

pstate = ptr_new(state,/no_copy)
widget_control,tlb,set_uvalue = pstate
xmanager,reg_name,tlb,/no_block,cleanup = 'rmd_detector_view_cleanup'

end