;---------------------------------------------------------------------------
; Document name: itool_limbfitter.pro
; Created by:    Liyun Wang, NASA/GSFC, January 25, 1995
;
; Last Modified: Fri Sep 19 09:33:13 1997 (lwang@achilles.nascom.nasa.gov)
;---------------------------------------------------------------------------
;
;+
; PROJECT:
;       SOHO - CDS/SUMER
;
; NAME:
;       ITOOL_LIMBFITTER
;
; PURPOSE:
;       Widget interface of limb-fitter for Image Tool
;
; CALLING SEQUENCE:
;       child = itool_limbfitter(parent)
;
; INPUTS:
;       PARENT - ID of parent widget upon which the limbfit widget is built
;
; OPTIONAL INPUTS:
;       None.
;
; OUTPUTS:
;       CHILD  - ID of the base widget being built and leter on remapped
;
; OPTIONAL OUTPUTS:
;       None.
;
; KEYWORD PARAMETERS:
;       FONT   - Font name to be used for labelling
;
; CALLS:
;       None.
;
; COMMON BLOCKS:
;       LIMB_FITTING - Internal common block used by this routine and
;                      LIMBFIT_EVENT
;       Others       - Included in image_tool_com.pro
;
; RESTRICTIONS:
;       None.
;
; SIDE EFFECTS:
;       None.
;
; CATEGORY:
;       Image Tool
;
; PREVIOUS HISTORY:
;       Written January 25, 1995, Liyun Wang, NASA/GSFC
;
; MODIFICATION HISTORY:
;       Version 1, created, Liyun Wang, NASA/GSFC, January 25, 1995
;       Version 2, Liyun Wang, NASA/GSFC, February 28, 1995
;          Added elliptical limb fitting option
;       Version 3, Liyun Wang, NASA/GSFC, April 27, 1995
;          Added the FONT keyword
;       Version 4, October 27, 1995, Liyun Wang, NASA/GSFC
;          Modified to cope with the cursor tracking option
;       Version 5, November 2, 1995, Liyun Wang, NASA/GSFC
;          Modified to cope with image icon stacking
;       Version 6, June 11, 1997, Liyun Wang, NASA/GSFC
;          Changed call from CROSS_HAIR to ITOOL_CROSS_HAIR
;
; VERSION:
;       Version 6, June 11, 1997
;-
;
PRO UPDATE_FITLIMB, fit_limb
;----------------------------------------------------------------------
; NAME:
;       UPDATE_FITLIMB
;
; PURPOSE:
;       Updates contents of the limb-fitting widget
;
; EXPLANATION:
;       This routine updates contents of the limb-fitting widget and plot the
;       fitted limb and solar disk center on the displayed image.
;
; CALLING SEQUENCE:
;       update_fitlimb
;
; INPUTS:
;       FIT_LIMB - A structure containing widget IDs and other info set as
;                  UVALUE from MK_LIMBFIT_BS.
; SIDE EFFECTS:
;       A dotted circle and a cross hair is over plotted on displayed image
;----------------------------------------------------------------------
;
@image_tool_com

   COMMON limb_fitting, xpos_fit, ypos_fit, fit_num, f_num, $
      fit_result, fit_xs, fit_ys, fit_xx, fit_yy, ifact

   ON_ERROR, 2
   WIDGET_CONTROL, fit_limb.fitted_x, $
      set_value=num2str(fit_result(0), FORMAT='(f10.2)')
   WIDGET_CONTROL, fit_limb.fitted_y, $
      set_value=num2str(fit_result(1), FORMAT='(f10.2)')
   WIDGET_CONTROL, fit_limb.fitted_ddelt1, $
      set_value=num2str(fit_result(2), FORMAT='(f10.2)')
   WIDGET_CONTROL, fit_limb.fitted_ddelt2, $
      set_value=num2str(fit_result(3), FORMAT='(f10.2)')
   WIDGET_CONTROL, fit_limb.fitted_cdelt1, $
      set_value=num2str(fit_result(4), FORMAT='(f10.2)')
   WIDGET_CONTROL, fit_limb.fitted_cdelt2, $
      set_value=num2str(fit_result(5), FORMAT='(f10.2)')
;----------------------------------------------------------------------
;  Refresh the image plot
;----------------------------------------------------------------------
   IF !d.window NE root_win THEN setwindow, root_win
   itool_display, image_arr, MAX=cur_max, MIN=cur_min, $
      relative=exptv_rel, csi=csi
   IF N_ELEMENTS(xxx) NE 0 THEN delvarx, xxx, yyy

;----------------------------------------------------------------------
;  Plot the fitted circle and its center
;----------------------------------------------------------------------
   dgr = 6.0
   angle = [0.0, !dtor*(dgr*FINDGEN(360.0/dgr)+dgr)]
   rmajor = fit_result(2)/csi.ddelt1
   rminor = fit_result(3)/csi.ddelt2
   temp = cnvt_coord(fit_result(0), fit_result(1), csi=csi, from=2, to=1)
   px = temp(0, 0)+rmajor*COS(angle)
   py = temp(0, 1)+rminor*SIN(angle)
   PLOTS, px, py, /DEVICE, color=l_color, lines=1, noclip=0, $
      clip=[csi.drpix1, csi.drpix2, csi.drpix1+csi.daxis1, $
            csi.drpix2+csi.daxis2]
   itool_copy_to_pix

   itool_cross_hair, temp(0, 0), temp(0, 1), color=l_color, /keep, $
      pixmap=pix_win

   WIDGET_CONTROL, fit_limb.fit_accept, sensitive=1
   WIDGET_CONTROL, comment_id, set_value='The dotted ' + $
      'circle on the image is the fitted limb. Press the Accept ' + $
      'button if you are satisfied.'
   RETURN
END

PRO make_auto_fit
;----------------------------------------------------------------------
; NAME:
;       MAKE_AUTO_FIT
;
; PURPOSE:
;       Interface to procedures of auto limb fitting.
;
; CALLING SEQUENCE:
;       make_auto_fit
;----------------------------------------------------------------------
;
@image_tool_com

   COMMON limb_fitting, xpos_fit, ypos_fit, fit_num, f_num, $
      fit_result, fit_xs, fit_ys, fit_xx, fit_yy, ifact
   ON_ERROR, 2
   limbfit_flag_sv = limbfit_flag
   WIDGET_CONTROL, tools(curr_tool).base, get_uvalue=fit_limb
   limbfit_flag = 3

   WIDGET_CONTROL, fit_limb.fitted_x, set_value=''
   WIDGET_CONTROL, fit_limb.fitted_y, set_value=''
   WIDGET_CONTROL, fit_limb.fitted_ddelt1, set_value=''
   WIDGET_CONTROL, fit_limb.fitted_ddelt2, set_value=''
   WIDGET_CONTROL, fit_limb.fitted_cdelt1, set_value=''
   WIDGET_CONTROL, fit_limb.fitted_cdelt2, set_value=''
;----------------------------------------------------------------------
;  Try with LIMB_INFO
;----------------------------------------------------------------------
   flash_msg, comment_id, $
      'Calling LIMB_INFO to make a try....', num=2, /nobeep
   WIDGET_CONTROL, /hourglass
   limb_info, image_arr, csi.date_obs, x_temp, y_temp, s_temp, r_temp
   IF !err EQ -1 THEN BEGIN
      flash_msg, comment_id, $
         'LIMB_INFO failed. You may want to try semi-auto fitting by '+$
         'selecting enough points around the limb.', $
         num=2
   ENDIF ELSE BEGIN
      csi.flag = 1
      fit_result = FLTARR(6)
      fit_result(0) = x_temp
      fit_result(1) = y_temp
      fit_result(2) = r_temp
      fit_result(3) = r_temp
      fit_result(4) = s_temp
      fit_result(5) = s_temp
      update_fitlimb, fit_limb
   ENDELSE
   limbfit_flag = limbfit_flag_sv
END

PRO make_manual_fit
;----------------------------------------------------------------------
; NAME:
;       MAKE_MANUAL_FIT
;
; PURPOSE:
;       Interface to the manual limb fitting process
;
; CALLING SEQUENCE:
;       make_manual_fit
;
;----------------------------------------------------------------------
;
@image_tool_com

   COMMON limb_fitting, xpos_fit, ypos_fit, fit_num, f_num, $
      fit_result, fit_xs, fit_ys, fit_xx, fit_yy, ifact
   ON_ERROR, 2

   WIDGET_CONTROL, tools(curr_tool).base, get_uvalue=fit_limb
   WIDGET_CONTROL, fit_limb.circle, set_button=1
   limbfit_flag = 1

;   WIDGET_CONTROL, fit_limb.fit_man_bs, map=1
   WIDGET_CONTROL, fit_limb.fit_reset, sensitive=1
   WIDGET_CONTROL, fit_limb.fit_accept, sensitive=0

   xpos_fit = INTARR(50) & ypos_fit=xpos_fit
   fit_num = 10
   f_num = 0
;----------------------------------------------------------------------
;  Get info about the fit_zoom window
;----------------------------------------------------------------------
   old_window = !d.window
   WIDGET_CONTROL, fit_limb.fit_zoom, get_value=z_win
   WSET, z_win
   fit_xs = !d.x_size
   fit_ys = !d.y_size
   fit_xx = INTARR(4)
   fit_yy = fit_xx
   xk = fit_xs/2-1 & yk=fit_ys/2-1
   fit_xx(0) = xk-10
   fit_xx(1) = xk+10
   fit_xx(2) = xk & fit_xx(3)=xk
   fit_yy(0) = yk & fit_yy(1)=yk
   fit_yy(2) = yk-10
   fit_yy(3) = yk+10
   ifact = 4
   WSET, old_window
   WIDGET_CONTROL, fit_limb.fit_sld, sensitive=1
   WIDGET_CONTROL, fit_limb.fit_sld, set_value=fit_num
   reset_limbfit, fit_limb
END

PRO reset_limbfit, fit_limb
;----------------------------------------------------------------------
; NAME:
;       RESET_LIMBFIT
;
; PURPOSE:
;       Reset contents on the limb fit widget base
;
; CALLING SEQUENCE:
;       make_manual_fit
;
; INPUTS:
;       FIT_LIMB - A structure containing widget IDs and other info set as
;                  UVALUE from MK_LIMBFIT_BS.
;----------------------------------------------------------------------
;
@image_tool_com

   COMMON limb_fitting, xpos_fit, ypos_fit, fit_num, f_num, $
      fit_result, fit_xs, fit_ys, fit_xx, fit_yy, ifact
   ON_ERROR, 2
   f_num = 0
   xpos_fit(0:fit_num-1) = 0
   ypos_fit(0:fit_num-1) = 0
   WIDGET_CONTROL, fit_limb.fit_accept, sensitive=0
   WIDGET_CONTROL, fit_limb.fit_n, set_value=''
   WIDGET_CONTROL, fit_limb.fit_xpos, set_value=''
   WIDGET_CONTROL, fit_limb.fit_ypos, set_value=''
   WIDGET_CONTROL, fit_limb.fit_last, set_value=num2str(fit_num-f_num)
   WIDGET_CONTROL, fit_limb.fitted_x, set_value=''
   WIDGET_CONTROL, fit_limb.fitted_y, set_value=''
   WIDGET_CONTROL, fit_limb.fitted_ddelt1, set_value=''
   WIDGET_CONTROL, fit_limb.fitted_ddelt2, set_value=''
   WIDGET_CONTROL, fit_limb.fitted_cdelt1, set_value=''
   WIDGET_CONTROL, fit_limb.fitted_cdelt2, set_value=''
   RETURN
END

PRO limbfit_cleanup
@image_tool_com
   WIDGET_CONTROL, draw_id, draw_motion=track_cursor
   WIDGET_CONTROL, draw_icon, draw_button=1
   limbfit_flag = 0
   itool_overlay_bt
   itool_button_refresh
   itool_refresh
END

PRO LIMBFIT_EVENT, event, uvalue
;----------------------------------------------------------------------
; NAME:
;       LIMBFIT_EVENT
;
; PURPOSE:
;       Event handler for events generated by the limb-fitting base widget
;
; CALLING SEQUENCE:
;       limbfit_event, event, uvalue
;
; INPUTS:
;       EVENT  - The event structure handled by XMANAGER
;       UVALUE - User value from a widget
;----------------------------------------------------------------------
;
@image_tool_com

   COMMON limb_fitting, xpos_fit, ypos_fit, fit_num, f_num, $
      fit_result, fit_xs, fit_ys, fit_xx, fit_yy, ifact

   ON_ERROR, 2

   IF limbfit_flag EQ 0 THEN RETURN

;---------------------------------------------------------------------------
;  Use the rather crude help system for now
;---------------------------------------------------------------------------
   IF help_mode THEN BEGIN
      xshow_help, help_stc, 'LIMB_FITTING', tbase=help_wbase
      RETURN
   ENDIF

   WIDGET_CONTROL, tools(curr_tool).base, get_uvalue=fit_limb
   CASE (uvalue) OF
      'DRAW': BEGIN             ; Event from the draw widget
         IF limbfit_flag EQ 3 THEN RETURN
         cx = event.x & cy=event.y
         cursor_info, cx, cy, txt_id, csi=csi, d_mode=d_mode
;---------------------------------------------------------------------------
;        Plot an enlarged image around the cursor center in a zoom-in window
;---------------------------------------------------------------------------
         xx0 = 0 > (cx-fit_xs/(ifact*2)) ;left edge from center
         yy0 = 0 > (cy-fit_ys/(ifact*2)) ;bottom
         nnx = fit_xs/ifact     ;Size of new image
         nny = fit_ys/ifact
         nnx = nnx < (!d.x_vsize-xx0)
         nny = nny < (!d.y_vsize-yy0)
         xx0 = xx0 < (!d.x_vsize - nnx)
         yy0 = yy0 < (!d.y_vsize - nny)
         a = TVRD(xx0, yy0, nnx, nny) ;Read image
         old_window = !d.window
         WIDGET_CONTROL, fit_limb.fit_zoom, get_value=z_win
         WSET, z_win
         xss = nnx * ifact	;Make integer rebin factors
         yss = nny * ifact
         TV, REBIN(a, xss, yss, sample=1), /dev
         PLOTS, fit_xx(0:1), fit_yy(0:1), /dev
         PLOTS, fit_xx(2:3), fit_yy(2:3), /dev
         WSET, old_window
         IF event.type EQ 0 THEN BEGIN
;---------------------------------------------------------------------------
;           Select points around the limb
;---------------------------------------------------------------------------
            IF f_num LT fit_num THEN BEGIN
               itool_cross_hair, cx, cy, color=l_color, /keep, pixmap=pix_win
;----------------------------------------------------------------------
;              xpos_fit and ypos_fit should be converted into data
;              coordinate system (i.e., in data pixels)
;----------------------------------------------------------------------
               temp = cnvt_coord(cx, cy, csi=csi, from=1, to=2)
               xpos_fit(f_num) = temp(0, 0)
               ypos_fit(f_num) = temp(0, 1)
               WIDGET_CONTROL, fit_limb.fit_sld, sensitive=0
               WIDGET_CONTROL, fit_limb.fit_n, $
                  set_value=num2str(f_num+1)
               WIDGET_CONTROL, fit_limb.fit_xpos, $
                  set_value=num2str(xpos_fit(f_num))
               WIDGET_CONTROL, fit_limb.fit_ypos, $
                  set_value=num2str(ypos_fit(f_num))
               WIDGET_CONTROL, fit_limb.fit_last, $
                  set_value=num2str(fit_num-f_num-1)
               f_num = f_num+1
               IF f_num EQ fit_num THEN BEGIN
                  WIDGET_CONTROL, comment_id, /hour, set_value=$
                     'Calling non-lenear least squre fitting routine...'
                  xxx_tmp = FLOAT(xpos_fit(0:fit_num-1))
                  yyy_tmp = FLOAT(ypos_fit(0:fit_num-1))
                  acc = 1.e-6
                  dtx = TRANSPOSE([[xxx_tmp], [yyy_tmp]])
                  sig = FLTARR(fit_num) & dty=sig
                  sig(*) = 1.0
                  dty(*) = 0.0
                  IF limbfit_flag EQ 1 THEN BEGIN
                     b = [271.0, 264.0, 200.0]
                     nl_lsqfit, dtx, dty, sig, b, chisq, acc, funcs=$
                        'funcir'
                     a = [b, b(2)]
                  ENDIF ELSE BEGIN
                     a = [271.0, 264.0, 200.0, 200.0]
                     nl_lsqfit, dtx, dty, sig, a, chisq, acc, funcs=$
                        'ellipse'
                  ENDELSE
;----------------------------------------------------------------------
;                 Get the apparent radius of the solar disc
;----------------------------------------------------------------------
                  angles = pb0r(csi.date_obs)
                  sradius = 60.*angles(2) ; in arcseconds
                  cdelt1 = sradius/a(2)
                  cdelt2 = sradius/a(3)
                  fit_result = [a, cdelt1, cdelt2]
                  update_fitlimb, fit_limb
               ENDIF
            ENDIF
         ENDIF
      END
      'FIT_RESET': BEGIN
         IF !d.window NE root_win THEN setwindow, root_win
         itool_display, image_arr, MAX=cur_max, MIN=cur_min, $
            relative=exptv_rel, csi=csi
         IF N_ELEMENTS(xxx) NE 0 THEN delvarx, xxx, yyy
         fit_num = 10
         WIDGET_CONTROL, fit_limb.fit_sld, sensitive=1
         WIDGET_CONTROL, fit_limb.fit_sld, set_value=fit_num
         reset_limbfit, fit_limb
      END
      'FIT_AUTO': BEGIN
         make_auto_fit
      END

      'fit_circle': limbfit_flag = 1
      'fit_ellipse': limbfit_flag = 2

      'FIT_SLD': BEGIN
         WIDGET_CONTROL, fit_limb.fit_sld, get_value=fit_num
         reset_limbfit, fit_limb
      END
      'FIT_ACCEPT': BEGIN
         WIDGET_CONTROL, comment_id, set_value=''
         csi.crpix1 = fit_result(0)
         csi.crpix2 = fit_result(1)
         csi.crval1 = 0.0
         csi.crval2 = 0.0
         csi.cdelt1 = fit_result(4)
         csi.cdelt2 = fit_result(5)
         csi.flag = 1
         d_mode = 3
         WIDGET_CONTROL, txt_lb, set_value=$
            '(in solar disc coordinate system)'
         itool_switcher, 0
         itool_overlay_bt
         itool_button_refresh
         itool_refresh
      END
      'FIT_CANCEL': BEGIN
         WIDGET_CONTROL, comment_id, set_value=''
         itool_switcher, 0
         itool_button_refresh
         itool_refresh
      END
      ELSE: RETURN
   ENDCASE
END

FUNCTION itool_limbfitter, parent, font=font
;---------------------------------------------------------------------------
;  Main routine
;---------------------------------------------------------------------------
@image_tool_com
   ON_ERROR, 2
   child = WIDGET_BASE(parent, map=0, /column, xpad=10)
   mk_dfont, bfont=bfont, lfont=lfont
   fit_title = WIDGET_LABEL(child, value='Image Tool Limb Fitter')

   temp = WIDGET_BASE(child, /row, /frame, xpad=30)
   fit_reset = WIDGET_BUTTON(temp, value='Reset', uvalue='FIT_RESET')
   fit_cancel = WIDGET_BUTTON(temp, value='Cancel', uvalue='FIT_CANCEL')
   fit_accept = WIDGET_BUTTON(temp, value='Accept', uvalue='FIT_ACCEPT')
   junk = WIDGET_LABEL(temp, value=' ', font=lfont)
   auto_fit = WIDGET_BUTTON(temp, value='Auto Fit', uvalue='FIT_AUTO')

   fit_rslt = WIDGET_BASE(child, /column, /frame, space=-10)
   tmp = WIDGET_LABEL(fit_rslt, value='Results')
   temp = WIDGET_BASE(fit_rslt, /column, xpad=10)
   tmp = WIDGET_BASE(temp, /row)
   xsize = 8
   fitted_x = cw_field(tmp, title=' CRPIX1', xsize=xsize, value=' ', $
                       font=font, fieldfont=font)
   fitted_y = cw_field(tmp, title=' CRPIX2', xsize=xsize, value=' ', $
                       font=font, fieldfont=font)
   tmp = WIDGET_BASE(temp, /row)
   fitted_rx = cw_field(tmp, title='RADIUS1', xsize=xsize, value=' ', $
                        font=font, fieldfont=font)
   fitted_ry = cw_field(tmp, title='RADIUS2', xsize=xsize, value=' ', $
                        font=font, fieldfont=font)

   tmp = WIDGET_BASE(temp, /row)
   fitted_cdelt1 = cw_field(tmp, title=' CDELT1', xsize=xsize, value=' ', $
                         font=font, fieldfont=font)
   fitted_cdelt2 = cw_field(tmp, title=' CDELT2', xsize=xsize, value=' ', $
                         font=font, fieldfont=font)

   fit_man_bs = WIDGET_BASE(child, /column, /frame)

   row = WIDGET_BASE(fit_man_bs, /row)

   temp = WIDGET_BASE(row, /column)
   fit_sld = WIDGET_SLIDER(temp, /drag, maximum=50, minimum=4, $
                           value=10, uvalue='FIT_SLD', font=font)
   fit_tmp = WIDGET_LABEL(temp, value='NUMBER OF POINTS TO USE', $
                          font=font)

   junk = WIDGET_BASE(row, /column, /exclusive, /frame)
   circle = WIDGET_BUTTON(junk, value='as a circle', font=lfont, $
                          uvalue='fit_circle')
   junk1 = WIDGET_BUTTON(junk, value='as an ellipse', font=lfont, $
                         uvalue='fit_ellipse')

   fit_row3 = WIDGET_BASE(fit_man_bs, /row, space=10)

   temp = WIDGET_BASE(fit_row3, /column, /frame)

   tmp = WIDGET_BASE(temp, /row)
   junk = WIDGET_LABEL(tmp, value='    POINT #', font=font)
   fit_n = WIDGET_TEXT(tmp, value='', xsize=2, font=font)

   tmp = WIDGET_BASE(temp, /row)
   fit_xpos = cw_field(tmp, title='X', /row, xsize=4, value=' ', $
                       font=font, fieldfont=font)
   fit_ypos = cw_field(tmp, title='Y', /row, xsize=4, value=' ', $
                       font=font, fieldfont=font)

   tmp = WIDGET_BASE(temp, /row)
   junk = WIDGET_LABEL(tmp, value='', font=font)
   fit_last = WIDGET_TEXT(tmp, value='', font=font, xsize=2)
   junk = WIDGET_LABEL(tmp, value='POINTS TO GO', font=font)

   fit_zoom = WIDGET_DRAW(fit_row3, xsize=130, ysize=130)
   fit_limb = {fit_limb, fit_xpos:fit_xpos, fit_ypos:fit_ypos, fit_n:fit_n, $
               fit_last:fit_last, fit_sld:fit_sld, fit_zoom:fit_zoom, $
               fitted_x:fitted_x, fitted_y:fitted_y, fitted_ddelt1:fitted_rx, $
               fitted_ddelt2:fitted_ry, fitted_cdelt1:fitted_cdelt1, $
               fitted_cdelt2:fitted_cdelt2, fit_accept:fit_accept, $
               fit_title:fit_title, fit_man_bs:fit_man_bs, $
               fit_reset:fit_reset, circle:circle}

;---------------------------------------------------------------------------
;  Save widget IDs and other info into the UVALUE of base CHILD as a structure
;---------------------------------------------------------------------------
   WIDGET_CONTROL, child, set_uvalue=fit_limb
   RETURN, child
END

;---------------------------------------------------------------------------
; End of 'itool_limbfitter.pro'.
;---------------------------------------------------------------------------