关于源文件ALIAS_OBJ的解析

来源:互联网 发布:淘宝皮草款式 编辑:程序博客网 时间:2024/06/08 03:35
;
;; PURPOSE:
;   一个使用ALIAS关键词的例子(to allow),即把一个对象加入到不止一个model中。
;   展示了什么样的空间能够通过referencing保存下来。
;   一个来自于很多视窗的大内容对象。
; $Id: //depot/Release/ENVI53_IDL85/idl/idldir/examples/doc/objects/alias_obj.pro#1 $
;
; Copyright (c) 1997-2015, Exelis Visual Information Solutions, Inc. All
;       rights reserved.
;+
; NAME:
;   ALIAS_OBJ
;
; PURPOSE:
;   This procedure serves as an example of using the ALIAS keyword to allow
;   the programmer to add an object to more than one Model object.
;   This procedure demonstrates how space can be saved by referencing
;   a single large object from several views.
;
;
;              Without ALIAS
;
;         view -> model -> graphics atom
;        /
;   scene
;        \
;         view -> model -> graphics atom
;
;
;              With ALIAS
;
;         view -> model
;        /             \
;   scene               graphics atom
;        \             /
;         view -> model
;
;
; CATEGORY:
;   Object graphics.
;
; CALLING SEQUENCE:
;   ALIAS_OBJ, [zData]
;
; OPTIONAL INPUTS:
;   zData: (表面图形)A two-dimensional floating point array representing
;              the data to be displayed as a surface.  By default,
;              the Maroon Bells example data is displayed.
;
; MODIFICATION HISTORY:
;   Written by:  DD, June 1996
;   ALIAS added by : KWS, Oct 1998
;-
;----------------------------------------------------------------------------
FUNCTION Alias_Obj_Toggle_State, wid     ;就是一个value值的转换(Toggle),表示目前的状态。
  WIDGET_CONTROL, wid, GET_VALUE=name
  s = STRPOS(name, '(off)')
  IF (s NE -1) THEN BEGIN
    STRPUT, name, '(on) ', s
    ret = 1
  ENDIF ELSE BEGIN
    s = STRPOS(name, '(on) ')
    STRPUT, name, '(off)',s
    ret = 0
  ENDELSE
  WIDGET_CONTROL, wid, SET_VALUE=name
  RETURN, ret
END
;---------------------------《事件响应函数》------------------------------------------
PRO ALIAS_OBJ_EVENT, sEvent
  WIDGET_CONTROL, sEvent.id, GET_UVALUE=uval
  ; Handle KILL requests.
  IF TAG_NAMES(sEvent, /STRUCTURE_NAME) EQ 'WIDGET_KILL_REQUEST' THEN BEGIN
    WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState
    ; Destroy the objects.
    OBJ_DESTROY, sState.oHolder
    WIDGET_CONTROL, sEvent.top, /DESTROY
    RETURN
  ENDIF
  ; Handle other events.
  ;前面的一大部分事件处理主要是属性设置。首先使用setproperty设置属性,再设置由于该属性修改造成的紧接着的变更;
  ;最终使用draw重绘。
  CASE uval OF
    'STYLE': BEGIN
      ;style: 由drap引起的事件,用于sruface显示的方式()。'Point','Wire','Solid',$
      ;'Ruled XZ','Ruled YZ','Lego Wire', $
      ;  'Lego Solid'
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, STYLE=sEvent.index    ;这句话是关键设置,其他的设置是一些属性标识符
      ;style 的设置将决定一些组件(btn)是否可操作。
      CASE sEvent.index OF
        0: BEGIN
          WIDGET_CONTROL, sState.wHide, SENSITIVE=1
          WIDGET_CONTROL, sState.wShading, SENSITIVE=0
        END
        1: BEGIN
          WIDGET_CONTROL, sState.wHide, SENSITIVE=1
          WIDGET_CONTROL, sState.wShading, SENSITIVE=0
        END
        2: BEGIN
          WIDGET_CONTROL, sState.wHide, SENSITIVE=0
          WIDGET_CONTROL, sState.wShading, SENSITIVE=1
        END
        3: BEGIN
          WIDGET_CONTROL, sState.wHide, SENSITIVE=1
          WIDGET_CONTROL, sState.wShading, SENSITIVE=0
        END
        4: BEGIN
          WIDGET_CONTROL, sState.wHide, SENSITIVE=1
          WIDGET_CONTROL, sState.wShading, SENSITIVE=0
        END
        5: BEGIN
          WIDGET_CONTROL, sState.wHide, SENSITIVE=1
          WIDGET_CONTROL, sState.wShading, SENSITIVE=0
        END
        6: BEGIN
          WIDGET_CONTROL, sState.wHide, SENSITIVE=0
          WIDGET_CONTROL, sState.wShading, SENSITIVE=1
        END
      ENDCASE
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'MM_MIN0': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, MIN_VALUE=sState.zMinVals[0]
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'MM_MIN1': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, MIN_VALUE=sState.zMinVals[1]
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'MM_MIN2': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, MIN_VALUE=sState.zMinVals[2]
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'MM_MAX0': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, MAX_VALUE=sState.zMaxVals[0]
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'MM_MAX1': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, MAX_VALUE=sState.zMaxVals[1]
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'MM_MAX2': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, MAX_VALUE=sState.zMaxVals[2]
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'SHADE_FLAT': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, SHADING=0
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'SHADE_GOURAUD': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, SHADING=1
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'VC_OFF': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      wParent = WIDGET_INFO(sEvent.id, /PARENT)
      j = Alias_Obj_Toggle_State(wParent)
      sState.oSurface->SetProperty, VERT_COLORS=0
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'VC_ON': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      wParent = WIDGET_INFO(sEvent.id, /PARENT)
      j = Alias_Obj_Toggle_State(wParent)
      sState.oSurface->SetProperty, VERT_COLORS=sState.vc
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    ;与前面hide区别,这个是idlgrsurface的属性,前面是标识,来确定btn是否可选。
    'HIDE_OFF': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      wParent = WIDGET_INFO(sEvent.id, /PARENT)
      j = Alias_Obj_Toggle_State(wParent)
      sState.oSurface->SetProperty, HIDDEN_LINES=0
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'HIDE_ON': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      wParent = WIDGET_INFO(sEvent.id, /PARENT)
      j = Alias_Obj_Toggle_State(wParent)
      sState.oSurface->SetProperty, HIDDEN_LINES=1
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    ;图形边缘事件
    'SKIRT0': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, SHOW_SKIRT=0
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'SKIRT1': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, SKIRT=sState.zSkirts[0], $
        /SHOW_SKIRT
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'SKIRT2': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, SKIRT=sState.zSkirts[1], $
        /SHOW_SKIRT
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'SKIRT3': BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.oSurface->SetProperty, SKIRT=sState.zSkirts[2], $
        /SHOW_SKIRT
      FOR i=0, sState.nViews-1 DO $
        sState.oWindows[i]->Draw, sState.oViews[i]
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'DRAGQ0' : BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.dragq = 0       
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'DRAGQ1' : BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.dragq = 1
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'SYNCOFF' : BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.sync = 0
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    'SYNCON' : BEGIN
      WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
      sState.sync = 1
      WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
    END
    ;以下是DRAW事件,所有在draw窗口中的事件都发生在这里!
    ELSE: BEGIN
      IF (uval EQ 'DRAW0' OR uval EQ 'DRAW1' OR $
        uval EQ 'DRAW2' OR uval EQ 'DRAW3') THEN BEGIN
        WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
        ; Figure out which of the draw widgets caused the event
        CASE uval OF
          'DRAW0': index=0
          'DRAW1': index=1
          'DRAW2': index=2
          'DRAW3': index=3
        ENDCASE
        ;获取该draw对应的draw、draw_value、group、view
        wDraw = sState.wDraws[index]
        oWindow = sState.oWindows[index]
        oGroup = sState.oGroups[index]
        oTrack = sState.oTracks[index]
        oView = sState.oViews[index]
        ;owindow : draw的 value(用来设置当前显示在哪个draw上)值。
        ; Expose.
        IF (sEvent.type EQ 4) THEN BEGIN       ;暴露事件 type =4
          oWindow->Draw, oView
          WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState , /NO_COPY    
          ;为什么要重新设置set_uvalue呢?前后应该没有变化吧?暂且不管    
          ;因为修改了draw,当然要修改了,其实也没有哦修改,我猜测是作者习惯性的动作!,但是oWindow确实修改了!
          RETURN           ;这儿有个return,直接返回了。
        ENDIF
        ; Handle trackball updates.
        bHaveTransform = oTrack->Update( sEvent, TRANSFORM=qmat )
        IF (bHaveTransform NE 0) THEN BEGIN
          IF (sState.sync EQ 0) THEN BEGIN
             ;print, qmat   ;测试输出
             ;print, t  
            oGroup->GetProperty, TRANSFORM=t
            oGroup->SetProperty, TRANSFORM=t#qmat
            oWindow->Draw, oView
          
            ;t是社么?上面也没有它的定义!很奇怪啊,只能从TRANSFORM上下手。
            ;   ;TRANSFORM: These vectors are translated, rotated,$
            ;   scaled, and projected onto the two-dimensional   $
            ;   rawing surface by multiplying them by
            ;   transformation matrices.
            ;意思就是通过这个变换矩阵,把三维点投影到二维平面上,详细内容见:《Fundamentals of Interactive Computer Graphics》
            ;《交互式计算机图形学基础》,那么具体什么原理呢?我也不知道,就是个矩阵。
          ENDIF ELSE BEGIN
            FOR i=0, sState.nViews-1 DO BEGIN
              sState.oGroups[i]->GetProperty, TRANSFORM=t
;  1.t是社么?上面也没有它的定义!很奇怪啊,只能从TRANSFORM上下手。
;  2.TRANSFORM: These vectors are translated, rotated,$
;   scaled, and projected onto the two-dimensional   $
;   rawing surface by multiplying them by
;   transformation matrices.
              ;意思就是通过这个变换矩阵,把三维点投影到二维平面上,详细内容见:《Fundamentals of Interactive Computer Graphics》
              ;《交互式计算机图形学基础》,那么具体什么原理呢?我也不知道,就是个矩阵。
              ;注!这个关键字是get,刚才没看到,所以作者是为了变换这个矩阵。
              ;根据Trackball的返回矩阵,变换三维到二维的变换Matrax,以达到旋转的目的!
;  3.通过print,t 证实t是一个4*4的transform矩阵。:
;  4.qmat的初始值:(怀疑初始值是单位矩阵,下面的是由于手抖而造成的改变)(三维向二维投影需要四维矩阵)
;0.99992741      0.00000000     0.012058024      0.00000000
;0.00000000       1.0000001      0.00000000      0.00000000
;-0.012058024      0.00000000      0.99992741      0.00000000
;0.00000000      0.00000000      0.00000000       1.0000000
;  5.结果已经很明显了,通过改变transform 改变了三维坐标投射到二维screen上的坐标,进而表现为旋转!
              sState.oGroups[i]->SetProperty, TRANSFORM=t#qmat
              sState.oWindows[i]->Draw, sState.oViews[i]
            ENDFOR
          ENDELSE
        ENDIF
        ; Handle other events: PICKING, quality changes, etc.
        ;  Button press.
        IF (sEvent.type EQ 0) THEN BEGIN  
          IF (sEvent.press EQ 4) THEN BEGIN ; Right mouse.
            pick = oWindow->PickData(oView,$                  ;oWindow :draw_value
              sState.oSurface, [sEvent.x,sEvent.y],dataxyz, $
              PATH=[oGroup])
              ;获取点击点的三维坐标
              ;
            IF (pick EQ 1) THEN BEGIN
              str = STRING(dataxyz[0],dataxyz[1],dataxyz[2], $
                FORMAT='("Data point: X=",F7.3,",Y=",F7.3,",Z=",F7.3)')
              WIDGET_CONTROL, sState.wLabel, SET_VALUE=str
            ENDIF ELSE BEGIN
              WIDGET_CONTROL, sState.wLabel, $
                SET_VALUE="Data point: In background."
            ENDELSE
;;sstatebtndown 确定鼠标哪个键被按下
            sState.btndown = 4b
            WIDGET_CONTROL, wDraw, /DRAW_MOTION
          ENDIF ELSE BEGIN ; other mouse button.
            sState.btndown = 1b
;    widget_control,draw1, get_value = owindow
;1.修改当前默认显示draw。
;2. 更容易的修改owindow的值,如下面的quality
            IF (sState.sync EQ 0) THEN BEGIN
              oWindow->SetProperty, QUALITY=sState.dragq
              oWindow->Draw, oView
              WIDGET_CONTROL, wDraw, /DRAW_MOTION
            ENDIF ELSE BEGIN
              FOR i=0, sState.nViews-1 DO BEGIN
                sState.oWindows[i]->SetProperty, $
                  QUALITY=sState.dragq
                sState.oWindows[i]->Draw, sState.oViews[i]
              ENDFOR
              WIDGET_CONTROL, sState.wDraws[0], /DRAW_MOTION
              WIDGET_CONTROL, sState.wDraws[1], /DRAW_MOTION
              WIDGET_CONTROL, sState.wDraws[2], /DRAW_MOTION
              WIDGET_CONTROL, sState.wDraws[3], /DRAW_MOTION
            ENDELSE
          ENDELSE
        ENDIF
        ; Button motion.
        IF (sEvent.type EQ 2) THEN BEGIN         ;鼠标移动
          IF (sState.btndown EQ 4b) THEN BEGIN ; Right mouse button.;;并且打印出三维坐标
            pick = oWindow->PickData(oView, $
              sState.oSurface, [sEvent.x,sEvent.y], dataxyz, $
              PATH=[oGroup])
            ;;PickData( View, Object, Location, XYZLocation [,DIMENSIONS=[width,height]]
            ;;[, PATH=objref(s)][, PICK_STATUS=variable])
            ;有时候无法保存是因为空格在搞鬼
      ;;函数方法将缓冲区的二维设备空间中的一个点映射到对象树的三维数据空间中的一个点。生成的三维数据空间坐标将返回到用户指定的变量中。该函数返回一个pickdata如果在缓冲装置
      ;;的空间”,点击“图形对象的指定位置,或否则为零。
            IF (pick EQ 1) THEN BEGIN          ;return means 二维撞到了一个图像或者zore,otherwise。
              str = STRING(dataxyz[0],dataxyz[1],dataxyz[2], $
                FORMAT='("Data point: X=",F7.3,",Y=",F7.3,",Z=",F7.3)')
              WIDGET_CONTROL, sState.wLabel, SET_VALUE=str
            ENDIF ELSE BEGIN
              WIDGET_CONTROL, sState.wLabel, $
                SET_VALUE="Data point: In background."
            ENDELSE
          ENDIF
        ENDIF
        ; Button release.
        IF (sEvent.type EQ 1) THEN BEGIN         ;鼠标弹起
          IF (sState.btndown EQ 1b) THEN BEGIN
            IF (sState.sync EQ 0) THEN BEGIN
              oWindow->SetProperty, QUALITY=2
              oWindow->Draw, oView
            ENDIF ELSE BEGIN
              FOR i=0, sState.nViews-1 DO BEGIN
                sState.oWindows[i]->SetProperty, QUALITY=2
                sState.oWindows[i]->Draw, sState.oViews[i]
              ENDFOR
            ENDELSE
          ENDIF
          sState.btndown = 0b
          WIDGET_CONTROL, sState.wDraws[0], DRAW_MOTION=0
          WIDGET_CONTROL, sState.wDraws[1], DRAW_MOTION=0
          WIDGET_CONTROL, sState.wDraws[2], DRAW_MOTION=0
          WIDGET_CONTROL, sState.wDraws[3], DRAW_MOTION=0
        ENDIF
        WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
      END
    END
  ENDCASE
END
;---------------------《开始》(程序由此开始运行)--------------------------
PRO ALIAS_OBJ_my, zData
  xdim = 640
  ydim = 480
  nViews = 4
  ; Default surface data is the Maroon Bells sample data
  ;这个是内部带的文件,是一个二进制文件,里面存储的一系列三维坐标
  IF (N_ELEMENTS(zData) EQ 0) THEN BEGIN
    RESTORE, filepath('marbells.dat', $
      subdir=['examples','data'])
    image = bytscl(elev, min=2658, max=4241)
    image = image[10:*,*] ; remove bad data
    zData = CONGRID(image, 80, 80, /INTERP) ; cut it down to size
  ENDIF
    ;以上是数据跨度什么的
    ;以下是创建界面
    ;一些设置空间选用的简单的快速图形法;显示操作使用的对象图形法。
  ; Compute potential skirt values.
  zMax = MAX(zData, MIN=zMin)
  zQuart = (zMax - zMin) * 0.25
  zSkirts = [zMin-zQuart, zMin, zMin+zQuart]
  ; Create the widgets.
  wBase = WIDGET_BASE(/COLUMN, XPAD=0, YPAD=0, $
    TITLE="Aliased Objects Example", $
    /TLB_KILL_REQUEST_EVENTS)
  wDrawBase = WIDGET_BASE(wBase, ROW=2)
  wDraws = LONARR(4)
  wDraws[0] = WIDGET_DRAW(wDrawBase, XSIZE=xdim/2, YSIZE=ydim/2, $
    UVALUE='DRAW0', $
    RETAIN=0, /EXPOSE_EVENTS, /BUTTON_EVENTS, $
    GRAPHICS_LEVEL=2)
  wDraws[1] = WIDGET_DRAW(wDrawBase, XSIZE=xdim/2, YSIZE=ydim/2, $
    UVALUE='DRAW1', $
    RETAIN=0, /EXPOSE_EVENTS, /BUTTON_EVENTS, $
    GRAPHICS_LEVEL=2)
  wDraws[2] = WIDGET_DRAW(wDrawBase, XSIZE=xdim/2, YSIZE=ydim/2, $
    UVALUE='DRAW2', $
    RETAIN=0, /EXPOSE_EVENTS, /BUTTON_EVENTS, $
    GRAPHICS_LEVEL=2)
  wDraws[3] = WIDGET_DRAW(wDrawBase, XSIZE=xdim/2, YSIZE=ydim/2, $
    UVALUE='DRAW3', $
    RETAIN=0, /EXPOSE_EVENTS, /BUTTON_EVENTS, $
    GRAPHICS_LEVEL=2)
  wGuiBase = WIDGET_BASE(wBase, /ROW, /ALIGN_CENTER)
  wStyleDrop = WIDGET_DROPLIST(wGuiBase, VALUE=['Point','Wire','Solid',$
    'Ruled XZ','Ruled YZ','Lego Wire', $
    'Lego Solid'], /FRAME, $
    TITLE='Style', UVALUE='STYLE')
  wOptions = WIDGET_BUTTON(wGuiBase, MENU=2, VALUE="Additional Options...")
  wDrag = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Drag Quality")
  wButton = WIDGET_BUTTON(wDrag, VALUE='Low', UVALUE='DRAGQ0')
  wButton = WIDGET_BUTTON(wDrag, VALUE='Medium', UVALUE='DRAGQ1')
  wHide = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Hidden Lines (off)")
  wButton = WIDGET_BUTTON(wHide, VALUE='Off', UVALUE='HIDE_OFF')
  wButton = WIDGET_BUTTON(wHide, VALUE='On', UVALUE='HIDE_ON')
  wMinMax = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Minimum")
  zMinVals = [zMin, zMin+zQuart, zMin+2*zQuart]
  zLabels = ['Reset', STRCOMPRESS(STRING(zMinVals[1:2]), /REMOVE_ALL)]
  wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[0], UVALUE='MM_MIN0')
  wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[1], UVALUE='MM_MIN1')
  wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[2], UVALUE='MM_MIN2')
  wMinMax = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Maximum")
  zMaxVals = [zMax, zMax-zQuart, zMax-2*zQuart]
  zLabels = ['Reset', STRCOMPRESS(STRING(zMaxVals[1:2]), /REMOVE_ALL)]
  wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[0], UVALUE='MM_MAX0')
  wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[1], UVALUE='MM_MAX1')
  wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[2], UVALUE='MM_MAX2')
  wShading = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Shading")
  wButton = WIDGET_BUTTON(wShading, VALUE='Flat', UVALUE='SHADE_FLAT')
  wButton = WIDGET_BUTTON(wShading, VALUE='Gouraud', UVALUE='SHADE_GOURAUD')
  wVC = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Vertex Colors (off)")
  wButton = WIDGET_BUTTON(wVC, VALUE='Off', UVALUE='VC_OFF')
  wButton = WIDGET_BUTTON(wVC, VALUE='On', UVALUE='VC_ON')
  zLabels = ['None', STRCOMPRESS(STRING(zSkirts[*]), /REMOVE_ALL)]
  wSkirt = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Skirt")
  wButton = WIDGET_BUTTON(wSkirt, VALUE=zLabels[0], UVALUE='SKIRT0')
  wButton = WIDGET_BUTTON(wSkirt, VALUE=zLabels[1], UVALUE='SKIRT1')
  wButton = WIDGET_BUTTON(wSkirt, VALUE=zLabels[2], UVALUE='SKIRT2')
  wButton = WIDGET_BUTTON(wSkirt, VALUE=zLabels[3], UVALUE='SKIRT3')
  wSync = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Synchronize Views")
  wButton = WIDGET_BUTTON(wSync, VALUE='Off', UVALUE='SYNCOFF')
  wButton = WIDGET_BUTTON(wSync, VALUE='On', UVALUE='SYNCON')
  ; Status line.
  wGuiBase2 = WIDGET_BASE(wBase, /COLUMN, /ALIGN_CENTER)
  wLabel = WIDGET_LABEL(wGuiBase2, /FRAME, $
    VALUE="Left Mouse: Trackball    Right Mouse: Data Picking" )
  wLabel = WIDGET_LABEL(wGuiBase2, VALUE=" ", /DYNAMIC_RESIZE)
  WIDGET_CONTROL, wBase, /REALIZE
  ; Get the window ids of the drawables.
  ; These window objects are freed when the widgets die.
  oWindows = OBJARR(nViews)
  WIDGET_CONTROL, wDraws[0], GET_VALUE=oTmp
  oWindows[0] = oTmp
  WIDGET_CONTROL, wDraws[1], GET_VALUE=oTmp
  oWindows[1] = oTmp
  WIDGET_CONTROL, wDraws[2], GET_VALUE=oTmp
  oWindows[2] = oTmp
  WIDGET_CONTROL, wDraws[3], GET_VALUE=oTmp
  oWindows[3] = oTmp
  ; Set default droplist items.
  WIDGET_CONTROL, wStyleDrop, SET_DROPLIST_SELECT=2
  WIDGET_CONTROL, wHide, SENSITIVE=0
  ; Compute viewplane rect based on aspect ratio.
     ;这个地方需要注意,以上的空间不明白是么意思,可以根据value(一般为控件运行时的显示名),是什么对象的及属性来确定干什么的。
     ;前面需要快速图形法的一些基础
  aspect = FLOAT(xdim) / FLOAT(ydim)
  sqrt2 = SQRT(2.0)
  myview = [ -sqrt2*0.5, -sqrt2*0.5, sqrt2, sqrt2 ]
  IF (aspect GT 1) THEN BEGIN
    myview[0] = myview[0] - ((aspect-1.0)*myview[2])/2.0
    myview[2] = myview[2] * aspect
  ENDIF ELSE BEGIN
    myview[1] = myview[1] - (((1.0/aspect)-1.0)*myview[3])/2.0
    myview[3] = myview[3] / aspect
  ENDELSE
  ;下面是一些对象图形法的东西,不是最基本的对象(idlgrsurface/light等),一般都是容器(view/model等),
  ;一个包一个
  ; Create views.
  oViews = OBJARR(nViews);
  oViews[0] = OBJ_NEW('IDLgrView', PROJECTION=1, $
    VIEWPLANE_RECT=myview, COLOR=[40,40,40])
  oViews[1] = OBJ_NEW('IDLgrView', PROJECTION=1, $
    VIEWPLANE_RECT=myview, COLOR=[40,40,40])
  oViews[2] = OBJ_NEW('IDLgrView', PROJECTION=1, $
    VIEWPLANE_RECT=myview, COLOR=[40,40,40])
  oViews[3] = OBJ_NEW('IDLgrView', PROJECTION=2, $
    EYE=1.8, ZCLIP=[1.4,-1.4],$
    VIEWPLANE_RECT=myview, COLOR=[40,40,40])
  ; Create models.
  ; View -> Top -> Group -> Surface
  ; Lights are applied to Top.
  ; Rotations are applied to Group.
  oTops = OBJARR(nViews)
  oGroups = OBJARR(nViews)
  FOR i=0, nViews-1 DO BEGIN
    oTops[i] = OBJ_NEW('IDLgrModel')
    oGroups[i] = OBJ_NEW('IDLgrModel')
    oTops[i]->Add, oGroups[i]
  ENDFOR
  ; Compute data bounds.
  sz = SIZE(zData)
  xMax = sz[1] - 1
  yMax = sz[2] - 1
  zMin2 = zMin - 1
  zMax2 = zMax + 1
  ; Compute coordinate conversion to normalize.
  xs = [-0.5,1.0/xMax]
  ys = [-0.5,1.0/yMax]
  zs = [(-zMin2/(zMax2-zMin2))-0.5, 1.0/(zMax2-zMin2)]
  ; Generate vertex colors to emulate height fields.
  vc = BYTARR(3,sz[1]*sz[2], /NOZERO)
  cbins=[[255,0,0],$
    [255,85,0],$
    [255,170,0],$
    [255,255,0],$
    [170,255,0],$
    [85,255,0],$
    [0,255,0]]
  zi = ROUND((zData - zMin)/(zMax-zMin) * 6.0)
  ;这个语句有什么特殊的作用?
  ;(zData - zMin)/(zMax-zMin)这个语句将图像数据投射到[0,1],
  ;乘以6后,则规整到[0,6],与颜色数组cbins对应。
  ;总之,就是根据数值进行分段吧
  vc[*,*] = cbins[*,zi]
  ; Create the surface.  Note: Only one!
  ;实例化IDLgrSurface对象,只有一个,对应多个视窗显示。与使用ALIAS关键字相对应,一对多显示。
  oSurface = OBJ_NEW('IDLgrSurface', zData, STYLE=2, SHADING=0, $
    COLOR=[60,60,255], BOTTOM=[64,192,128], $
    XCOORD_CONV=xs, YCOORD_CONV=ys, ZCOORD_CONV=zs)
  ; Use ↑ALIAS↑ to place the surface in 4 models.
  FOR i=0, nViews-1 DO $
    oGroups[i]->Add, oSurface, /ALIAS
  ;表面加入到了groups中,并没有在otops中!
  ;灯光加入到了oTops中,并没有在ogroup中!oTops和groups都是Idlgrmodel实例。
  ;级别表如下:
  ;View -> Top -> Group -> Surface
  ; Create some lights.
  ; Use ALIAS again to share the light objects.
  oLight = OBJ_NEW('IDLgrLight', LOCATION=[2,2,2], TYPE=1)
  FOR i=0, nViews-1 DO $
    oTops[i]->Add, oLight, /ALIAS
  oLight = OBJ_NEW('IDLgrLight', TYPE=0, INTENSITY=0.5)
  FOR i=0, nViews-1 DO $
    oTops[i]->Add, oLight, /ALIAS
  ; Place the models in the views.
  FOR i=0, nViews-1 DO $
    oViews[i]->Add, oTops[i]
  ; Label each view.
  oText = OBJ_NEW('IDLgrText', 'Rotate about Y', /ONGLASS, $
    COLOR=[255,255,255], LOCATIONS=[[-0.9, -0.6, -0.0]])
  oTops[0]->Add, oText
  oText = OBJ_NEW('IDLgrText', 'Rotate about Z', /ONGLASS, $
    COLOR=[255,255,255], LOCATIONS=[[-0.9, -0.6, -0.0]])
  oTops[1]->Add, oText
  oText = OBJ_NEW('IDLgrText', 'Rotate about X', /ONGLASS, $
    COLOR=[255,255,255], LOCATIONS=[[-0.9, -0.6, -0.0]])
  oTops[2]->Add, oText
  oText = OBJ_NEW('IDLgrText', 'Perspective', /ONGLASS, $
    COLOR=[255,255,255], LOCATIONS=[[-0.9, -0.6, -0.0]])
  oTops[3]->Add, oText
  ; Rotate to standard view for initial display.
  FOR i=0, nViews-1 DO BEGIN
    oGroups[i]->Rotate, [1,0,0], -90
    oGroups[i]->Rotate, [0,1,0], 30
    oGroups[i]->Rotate, [1,0,0], 30
  ENDFOR
 
  ;这是什么?trackball?跟踪球
  ;新建四Trackball对象。
  ;好像可以根据鼠标移动生成四维映射矩阵。
  ; Create 4 trackballs with different constraints(限制).
  oTracks = OBJARR(4)
  oTracks[0] = OBJ_NEW('Trackball', [xdim/2, ydim/2.], xdim/2., $
    /CONSTRAIN, AXIS=1)
  oTracks[1] = OBJ_NEW('Trackball', [xdim/2, ydim/2.], xdim/2., $
    /CONSTRAIN, AXIS=2)
  oTracks[2] = OBJ_NEW('Trackball', [xdim/2, ydim/2.], xdim/2., $
    /CONSTRAIN, AXIS=0)
    ;constrain 使用了此关键字则变换是被axis约束的,默认是不约束的。
  oTracks[3] = OBJ_NEW('Trackball', [xdim/2, ydim/2.], xdim/2.)
  ;关于对象销毁机制的介绍:
  ; Create a holder object for easy destruction.
  ; Destroying a View also destroys everything inside of it.
  ; But objects created with ALIAS are not destroyed this way.
  oHolder = OBJ_NEW('IDL_Container')
  oHolder->Add, oViews
  oHolder->Add, oTracks
  oHolder->Add, oSurface
;没有把oTop加进去!
  ; Save state.  这个是事件传递的参数数组,主要通过它来把主函数的信息,传输到事件响应函数中
  sState = {btndown:  0b,$     ;
    dragq:    0,         $     ;Quality
    nViews:   nViews,    $
    oGroups:  oGroups,   $
    oHolder:  oHolder,   $
    oSurface: oSurface,  $     ;IDLgrSurface
    oTracks:  oTracks,   $     ;4*Trackball(obj)
    oViews:   oViews,    $
    oWindows: oWindow】ps,  $     ;draw_value 用于设置在哪一个draw控件上显示。
    sync:     0,         $
    vc:       vc,        $     ;vertax color
    wDraws:   wDraws,    $     ;widget_draw
    wHide:    wHide,     $     ;WIDGET_BUTTON   wire下 line是透视,还是隐藏
    wLabel:   wLabel,    $     ;叙述一些状态事件/提示(静态文本控件)
    wShading: wShading,  $     ;button-shading  
    zMaxVals: zMaxVals,  $
    zMinVals: zMinVals,  $
    zSkirts:  zSkirts    $     ;边沿,挡板
  }
  WIDGET_CONTROL, wBase, SET_UVALUE=sState, /NO_COPY
  XMANAGER, 'ALIAS_OBJ', wBase, /NO_BLOCK
END