www.pudn.com > 3dmesh.rar > decimate.pro


FUNCTION my_trackball_CONSTRAIN, pt, vec 
    ; Project the point. 
    proj = pt - TOTAL(vec * pt) * vec 
 
    ; Normalizing factor. 
    norm = SQRT(TOTAL(proj^2)) 
 
    IF (norm GT 0.0) THEN BEGIN 
        IF (proj[2] LT 0.0) THEN $ 
            cpt = -1.0 / norm * proj $ 
        ELSE $ 
            cpt = 1.0 / norm * proj 
    ENDIF ELSE IF vec[2] EQ 1.0 THEN $ 
        cpt = [1.0, 0.0, 0.0] $ 
    ELSE $ 
        cpt = [-vec[1], vec[0], 0.0] / SQRT(TOTAL(vec[0:1]^2)) 
 
    RETURN, cpt 
END 
 
;---------------------------------------------------------------------------- 
; my_trackball::UPDATE 
; 
; Purpose: 
;  Given a widget event structure, updates the my_trackball state. 
; 
;  The return value is nonzero if a transformation matrix is calculated 
;  as a result of the event, or zero otherwise. 
; 
; Arguments: 
;  sEvent - The widget event structure. 
; 
; Keywords: 
;  TRANSFORM - If a transformation matrix is calculated, upon return 
;              transform will contain a 4x4 matrix. 
; 
FUNCTION my_trackball::UPDATE, sEvent, TRANSFORM=transform, $ 
     MOUSE=mouse, TRANSLATE=translate 
  ; Initialize return value. 
  translate=1 
  bHaveTransform=0 
 
  IF (N_ELEMENTS(mouse) NE 0) THEN BEGIN 
      if (mouse ne 1) and (mouse ne 2) and (mouse ne 4) then begin 
          PRINT, 'my_trackball: invalid value for MOUSE keyword.' 
          RETURN, 0 
      ENDIF ELSE $ 
          self.mouse = mouse 
  ENDIF ; Don't set self.mouse if no argument, keep setting from INIT 
 
  ; Ignore non-Draw-Widget events. 
  IF (TAG_NAMES(sEvent, /STRUCTURE_NAME) NE 'WIDGET_DRAW') THEN $ 
    RETURN, bHaveTransform 
  ; Determine event type. 
 
  CASE sEvent.type OF 
    0: BEGIN    ;Button press. 
         ; Only handle event if appropriate mouse button. 
         ;IF (sEvent.press EQ self.mouse) THEN BEGIN 
           ; Calculate distance of mouse click from center of unit circle. 
           xy = ([sEvent.x,sEvent.y] - self.center) / self.radius 
           r = TOTAL(xy^2) 
           IF (r GT 1.0) THEN $ 
             self.pt1 = [xy/SQRT(r) ,0.0] $ 
           ELSE $ 
             self.pt1 = [xy,SQRT(1.0-r)] 
 
           ; Constrain if necessary. 
           IF (self.constrain NE 0) THEN BEGIN 
               vec = [0.,0.,0.] 
               vec[self.axis] = 1.0 
               self.pt1 = my_trackball_CONSTRAIN( self.pt1, vec) 
           ENDIF 
           self.pt0 = self.pt1 
           self.btndown = 1b 
           self.whichbtn = sEvent.press 
         ;ENDIF 
       END 
 
    2: BEGIN    ;Button motion. 
         IF (self.btndown EQ 1b) THEN BEGIN 
           ; Calculate distance of mouse click from center of unit circle. 
           xy = ([sEvent.x,sEvent.y] - self.center) / $ 
                self.radius 
           r = TOTAL(xy^2) 
           IF (r GT 1.0) THEN $ 
             pt1 = [xy/SQRT(r) ,0.0] $ 
           ELSE $ 
             pt1 = [xy,SQRT(1.0-r)] 
 
           ; Constrain if necessary. 
           IF (self.constrain NE 0) THEN BEGIN 
               vec = [0.,0.,0.] 
               vec[self.axis] = 1.0 
               pt1 = my_trackball_CONSTRAIN( pt1, vec) 
           ENDIF 
 
           ; Update the transform only if the mouse button has actually 
           ; moved from its previous location. 
           pt0 = self.pt0 
           IF ((pt0[0] NE pt1[0]) OR $ 
               (pt0[1] NE pt1[1]) OR $ 
               (pt0[2] NE pt1[2])) THEN BEGIN 
             ; Compute transformation. 
             q = [CROSSP(pt0,pt1), TOTAL(pt0*pt1)] 
 
             x = q[0] 
             y = q[1] 
             z = q[2] 
             w = q[3] 
 
			 CASE self.whichbtn OF 
 
			 1b: BEGIN 
              	; rotation 
               transform = [[ w^2+x^2-y^2-z^2, 2*(x*y-w*z), 2*(x*z+w*y), 0], $ 
                            [ 2*(x*y+w*z), w^2-x^2+y^2-z^2, 2*(y*z-w*x), 0], $ 
                            [ 2*(x*z-w*y), 2*(y*z+w*x), w^2-x^2-y^2+z^2, 0], $ 
                            [ 0          , 0          , 0              , 1]] 
			 END 
			 2b: BEGIN 
  				; translation only 
               transform = [[ 1,0,0, pt1[0]-pt0[0] ], $ 
                            [ 0,1,0, pt1[1]-pt0[1]], $ 
                            [ 0,0,1, 0], $ 
                            [ 0          , 0          , 0              , 1]] 
			 END 
			 4b: BEGIN 
			 	; scale only 
               diff = pt1[0]-pt0[0] 
               if diff eq 0.0 then sc=1 
               if diff LT 0 then sc=0.85 else sc=1.15 
               transform = [[ sc,0,0,0], $ 
                            [ 0,sc,0,0], $ 
                            [ 0,0,sc,0], $ 
                            [ 0          , 0          , 0              , 1]] 
			 END 
			 ELSE: 
 
			 ENDCASE 
 
 
             bHaveTransform = 1b 
             self.pt0 = pt1 
           ENDIF 
 
           self.pt1 = pt1 
         ENDIF 
       END 
 
    1: BEGIN    ;Button Release. 
         IF (self.btndown EQ 1b) THEN $ 
           self.btndown = 0b 
       END 
 
    ELSE: RETURN, bHaveTransform 
 
   ENDCASE 
 
  RETURN, bHaveTransform 
END 
 
;---------------------------------------------------------------------------- 
; my_trackball::INIT 
; 
; Purpose: 
;   Initializes the my_trackball state. 
; 
; Arguments: 
;  center - A two-dimensional vector, [x,y], representing the requested 
;           center (measured in device units) of the my_trackball. 
;  radius - The requested radius (measured in device units) of the my_trackball. 
; 
; Keywords: 
;  AXIS -  Set this keyword to indicate the axis about which rotations 
;          are to be constrained if the CONSTRAIN keyword is set to a 
;          nonzero value).  Valid values include: 
;               0 = X 
;               1 = Y 
;               2 = Z (default) 
;  CONSTRAIN - Set this keyword to a nonzero value to indicate that the 
;          my_trackball transformations are to be constrained about a given 
;          axis (as specified by the AXIS keyword).  The default is zero 
;          (no constraints). 
;  MOUSE - Set this keyword to a bitmask to indicate which mouse button to 
;          honor for my_trackball events.  The least significant bit represents 
;          the leftmost button, the next highest bit represents the middle 
;          button, and the next highest bit represents the right button. 
;          The default is 1b, for the left moust button. 
; 
FUNCTION my_trackball::INIT, center, radius, AXIS=axis, CONSTRAIN=constrain, $ 
                          MOUSE=mouse 
    IF (N_ELEMENTS(center) NE 2) THEN BEGIN 
        PRINT, 'my_trackball: center must be a two-dimensional array.' 
        RETURN, 0 
    ENDIF 
 
    IF (N_ELEMENTS(radius) NE 1) THEN BEGIN 
        PRINT, 'my_trackball: invalid radius.' 
        RETURN, 0 
    ENDIF 
 
    IF (N_ELEMENTS(axis) NE 0) THEN BEGIN 
        IF (axis lt 0) OR (axis gt 2) THEN BEGIN 
            PRINT, 'my_trackball: invalid value for AXIS keyword.' 
            RETURN, 0 
        ENDIF ELSE $ 
            self.axis = axis 
    ENDIF ELSE $ 
        self.axis = 2 
 
    IF (N_ELEMENTS(constrain) NE 0) THEN $ 
        self.constrain = constrain $ 
    ELSE $ 
        self.constrain = 0 
 
    IF (N_ELEMENTS(mouse) NE 0) THEN BEGIN 
        if (mouse ne 1) and (mouse ne 2) and (mouse ne 4) then begin 
            PRINT, 'my_trackball: invalid value for MOUSE keyword.' 
            RETURN, 0 
        ENDIF ELSE $ 
            self.mouse = mouse 
    ENDIF ELSE $ 
        self.mouse = 1 ; Default is left mouse button 
 
    self.center = center 
    self.radius = radius 
    self.btndown = 0b 
    self.whichbtn = 0b 
 
    RETURN, 1 
END 
 
;---------------------------------------------------------------------------- 
; my_trackball::CLEANUP 
; 
; Purpose: 
;   Cleanup the my_trackball when it is destroyed. 
; 
; Arguments: 
;   
; 
; Keywords: 
;   
; 
PRO my_trackball::CLEANUP 
    ; No work needs to be done.  We provide this method to avoid any 
    ; problems resolving the Cleanup method call on Windows 3.11 
    ; which has short filename restrictions. 
END 
 
;---------------------------------------------------------------------------- 
; my_trackball::RESET 
; 
; Purpose: 
;   Resets the my_trackball state. 
; 
; Arguments: 
;  center - A two-dimensional vector, [x,y], representing the requested 
;           center (measured in device units) of the my_trackball. 
;  radius - The requested radius (measured in device units) of the my_trackball. 
; 
; Keywords: 
;  MOUSE - Set this keyword to a bitmask to indicate which mouse button to 
;          honor for my_trackball events.  The least significant bit represents 
;          the leftmost button, the next highest bit represents the middle 
;          button, and the next highest bit represents the right button. 
;          The default is 1b, for the left moust button. 
; 
PRO my_trackball::RESET, center, radius, AXIS=axis, CONSTRAIN=constrain, $ 
                      MOUSE=mouse 
    IF (N_ELEMENTS(center) NE 2) THEN BEGIN 
        PRINT, 'my_trackball: center must be a two-dimensional array.' 
        RETURN 
    ENDIF 
 
    IF (N_ELEMENTS(radius) NE 1) THEN BEGIN 
        PRINT, 'my_trackball: invalid radius.' 
        RETURN 
    ENDIF 
 
    IF (N_ELEMENTS(axis) NE 0) THEN BEGIN 
        IF (axis lt 0) OR (axis gt 2) THEN BEGIN 
            PRINT, 'my_trackball: invalid value for AXIS keyword.' 
            RETURN 
        ENDIF ELSE $ 
            self.axis = axis 
    ENDIF ELSE $ 
        self.axis = 2 
 
    IF (N_ELEMENTS(constrain) NE 0) THEN $ 
        self.constrain = constrain $ 
    ELSE $ 
        self.constrain = 0 
 
    IF (N_ELEMENTS(mouse) NE 0) THEN BEGIN 
        if (mouse ne 1) and (mouse ne 2) and (mouse ne 4) then begin 
            PRINT, 'my_trackball: invalid value for MOUSE keyword.' 
            RETURN 
        ENDIF ELSE $ 
            self.mouse = mouse 
    ENDIF ELSE $ 
        self.mouse = 1 ; Default is left mouse button 
 
    self.center = center 
    self.radius = radius 
 
    self.btndown = 0b 
 
END 
 
;---------------------------------------------------------------------------- 
; my_trackball__DEFINE 
; 
; Purpose: 
;  Defines the object structure for a my_trackball object. 
; 
PRO my_trackball__define 
 
  struct = {my_trackball, $ 
            btndown: 0b,    $ 
            whichbtn: 0b,   $ 
            axis: 0, $ 
            constrain: 0b, $ 
            mouse: 0b, $ 
            center: LONARR(2), $ 
            radius: 0.0, $ 
            pt0: FLTARR(3), $ 
            pt1: FLTARR(3) $ 
           } 
END 
 
 
; 
; $Id: decimate.pro,v 1.3 2000/01/21 00:25:30 scottm Exp $ 
; 
; Copyright (c) 1997-2000, Research Systems, Inc.  All rights reserved. 
;	Unauthorized reproduction prohibited. 
;+ 
; NAME: 
;	DECIMATE 
; 
; PURPOSE:h 
;	This procedure serves as an example of using the MESH_DECIMATE 
;	function to simplify a polygonal mesh object. 
; 
; CATEGORY: 
;	Object graphics. 
; 
; CALLING SEQUENCE: 
;	DECIMATE 
; 
; MODIFICATION HISTORY: 
; 	Written by:	KS, September 1999 
;- 
 
;---------------------------------------------------------------------------- 
 
; remove 2 elements from an array at index i 
FUNCTION remove_pair, pairs, i 
	n = N_ELEMENTS(pairs) 
	IF ((n MOD 2) EQ 1) THEN STOP 
	IF (n EQ 0) THEN RETURN, 0L 
	IF (n EQ 2) THEN RETURN, 0L 
	IF (i EQ 0) THEN RETURN, pairs[2:*] 
	IF (i EQ n-2) THEN RETURN, pairs[0:i-1] 
	RETURN, [pairs[0:i-1],pairs[i+2:*]] 
END 
 
 
; 
; Given the results of MESH_CLIP, with CUT_VERTS, return a 
; connectivity list that describes the caps of the clipped mesh 
; 
; Ambiguity notes 
; This code does not do well when two or more "caps" share a vertex. 
; At such a vertex, there would be more than two line segments 
; passing through.  When approaching this vertex via one line 
; segment, it is difficult to choose which other line segment to 
; move along next, if there is more than one. 
; 
; A different approach must be used.  Something like building 
; a data structure that contains all connectivity info.  Then 
; perhaps we can make better choices, like staying on a particular 
; loop. 
; 
FUNCTION compute_caps, conn, cut 
	IF (N_ELEMENTS(cut) EQ 0) THEN RETURN, [0L] 
 
	; First, construct a list of pairs. 
	; The pairs represent the endpoints of every line segment in the mesh 
	; that is on the clip plane (has endpoints in the cut_vert list). 
	pairs = [0L] ; start with a dummy element 
	FOR i=0L, N_ELEMENTS(conn)-1, 4 DO BEGIN 
		IF (conn[i] NE 3) THEN STOP 
		; Check to see if each triangle vertex is in the cut list 
		; Each vertex should be there only once, if at all. 
		w1 = WHERE(cut EQ conn[i+1], count1) 
		IF (count1 GT 1) THEN STOP 
		w2 = WHERE(cut EQ conn[i+2], count2) 
		IF (count2 GT 1) THEN STOP 
		w3 = WHERE(cut EQ conn[i+3], count3) 
		IF (count3 GT 1) THEN STOP 
		; The interesting cases are when 2 or 3 verts of the triangle are 
		; in the cut_verts list. 
		IF (count1+count2+count3 EQ 2) THEN BEGIN 
			; Find which two and then add to pairs list. 
			IF (count1 eq 0) THEN pair = [conn[i+2],conn[i+3]] $ 
			ELSE IF (count2 eq 0) THEN pair = [conn[i+1], conn[i+3]] $ 
			ELSE IF (count3 eq 0) THEN pair = [conn[i+1], conn[i+2]] 
			pairs = [pairs, pair] 
		END ELSE IF (count1+count2+count3 EQ 3) THEN BEGIN 
			; This case is strange, but does happen. 
			; Add all three pairs to the pair list. 
			; This might be a bad thing to do. 
			pairs = [pairs, conn[i+1], conn[i+2], conn[i+2], conn[i+3], $ 
				conn[i+3], conn[i+1]] 
		ENDIF 
	ENDFOR 
	; Get out if no pairs generated. 
	IF (N_ELEMENTS(pairs) EQ 1) THEN RETURN, [0L] 
	pairs = pairs[1:*] ; remove dummy element 
 
	; Examine all the pairs, looking for indicies that only 
	; appear once.  These indices are in pairs that are not 
	; in a loop.  I call these strands. 
	minpair = MIN(pairs, MAX=maxpair) 
	strands = [0L] 
	FOR i=minpair, maxpair DO BEGIN 
		result = WHERE(pairs EQ i, count) 
		IF (count EQ 1) THEN strands = [strands, i] 
	ENDFOR 
 
	; Init the returned connectivity list 
	out_conn = [0L] 
	start_poly = 0 
 
	; Start processing the strands. 
	; We pick a strand from our list and follow its segments 
	; until we hit a dead end. 
	FOR strand=1, N_ELEMENTS(strands)-1 DO BEGIN 
		; need to search again because pairs are being removed. 
		result = WHERE(pairs EQ strands[strand], count) 
		; loop until the end of the strand is reached. 
		WHILE (count gt 0) DO BEGIN 
			; we cannot pick which one to use!  Just pick any one. 
			IF (count GT 1) THEN PRINT, 'Ambiguity in a strand' 
			i = result[0] 
			; add next index to polygon 
			out_conn = [out_conn, pairs[i]] 
			out_conn[start_poly] = out_conn[start_poly] + 1 
			; The next one to seek is the other index in the pair 
			IF ( (i MOD 2) EQ 0) THEN target = pairs[i+1] $ 
			ELSE target = pairs[i-1] 
			; done with this pair now 
			pairs = remove_pair(pairs, i - (i MOD 2)) 
			; find other segment sharing a vert with last pair. 
			result = WHERE(pairs EQ target, count) 
		ENDWHILE 
		; start a new polygon 
		start_poly = N_ELEMENTS(out_conn) 
		out_conn = [out_conn, 0] 
	ENDFOR 
 
	; This is basically the same loop as above, except 
	; that we are processing the remaining pairs, which 
	; should all be in loops.  Removing pairs as we go 
	; prevents infinite loops. 
	; Keep working on pairs until less than 3 pairs left. 
	WHILE (N_ELEMENTS(pairs) GE 6) DO BEGIN 
		; just start with first pair - it does not matter 
		out_conn = [out_conn, pairs[0]] 
		out_conn[start_poly] = out_conn[start_poly] + 1 
		target = pairs[1] 
		pairs = remove_pair(pairs, 0) 
		result = WHERE(pairs EQ target, count) 
		WHILE (count gt 0) DO BEGIN 
			IF (count GT 1) THEN PRINT, 'Ambiguity in a loop' 
			i = result[0] 
			out_conn = [out_conn, pairs[i]] 
			out_conn[start_poly] = out_conn[start_poly] + 1 
			IF ( (i MOD 2) EQ 0) THEN target = pairs[i+1] $ 
			ELSE target = pairs[i-1] 
			pairs = remove_pair(pairs, i - (i MOD 2)) 
			result = WHERE(pairs EQ target, count) 
		ENDWHILE 
		start_poly = N_ELEMENTS(out_conn) 
		out_conn = [out_conn, 0] 
	ENDWHILE 
 
	out_conn = out_conn[0:N_ELEMENTS(out_conn)-1] 
	RETURN, out_conn 
END 
 
 
FUNCTION Toggle_State, wid 
 
    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 read_smf_file, filename, verts, faces 
 
	OPENR, lun, filename, /get_lun, ERROR=err 
	IF (err NE 0) THEN BEGIN 
		PRINTF, -2, !ERR_STRING 
		RETURN 
	ENDIF 
	; Read header records so we know how much data to read. 
	buff = '' 
	num_vertices = 0L 
	num_faces = 0L 
	READF, lun, buff 
	IF ( buff NE '#$SMF 1.0') THEN RETURN 
	READF, lun, buff 
	IF ( STRMID(buff, 0, 11) NE '#$vertices ') THEN RETURN 
    READS, STRMID(buff, 11),  num_vertices 
	READF, lun, buff 
	IF ( STRMID(buff, 0, 8) NE '#$faces ') THEN RETURN 
    READS, STRMID(buff, 8),  num_faces 
 
	verts=FLTARR(3,num_vertices) 
	faces=LONARR(num_faces * 4) 
 
	v = 0L 
	f = 0L 
	aa = 0L 
	bb = 0L 
	cc = 0L 
	WHILE NOT EOF(lun) DO BEGIN 
	    READF, lun, buff 
	    ch = STRMID(buff, 0, 1) 
	    IF ch EQ 'v' THEN BEGIN 
	        READS, STRMID(buff, 1), a, b, c 
	        verts[0,v] = a 
	        verts[1,v] = b 
	        verts[2,v] = c 
	        v = v + 1 
	    ENDIF ELSE IF ch EQ 'f' OR ch EQ 't' THEN BEGIN 
	        READS, STRMID(buff, 1), aa, bb, cc 
	        faces[f+0] = 3 
	        faces[f+1] = aa-1 
	        faces[f+2] = bb-1 
	        faces[f+3] = cc-1 
	        f = f + 4 
	        ENDIF 
	    ENDWHILE 
 
	CLOSE, lun 
	FREE_LUN, lun 
END 
 
;---------------------------------------------------------------------------- 
PRO write_smf_file, filename, verts, faces 
        OPENW, wlun, filename, /GET_LUN 
        FOR i=0L, N_ELEMENTS(verts[0,*])-1 DO BEGIN 
                PRINTF, wlun, "v ", verts[0,i], " ", verts[1,i]," ", verts[2,i] 
        ENDFOR 
        f = 0L 
        FOR i=0L, N_ELEMENTS(faces)/4-1 DO BEGIN 
                PRINTF, wlun, "t ", faces[f+1]+1, faces[f+2]+1, faces[f+3]+1 
                f = f + 4 
        ENDFOR 
        CLOSE, wlun 
        FREE_LUN, wlun 
END 
 
;---------------------------------------------------------------------------- 
PRO compute_geometry, sState 
    WIDGET_CONTROL,HOURGLASS=1 
	; Clip model to each of the planes. 
 
	CASE sState.capping OF 
	0 : BEGIN ; no capping 
		IF (N_ELEMENTS(*(sState.faces)) GT 1) THEN BEGIN 
			result = MESH_CLIP(sState.xplane0, *(sState.verts), *(sState.faces), $ 
				new_verts, new_faces) 
			print,'xplane0',sState.xplane0 
		ENDIF 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.yplane0, new_verts, new_faces, $ 
				new_verts, new_faces) 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.zplane0, new_verts, new_faces, $ 
				new_verts, new_faces) 
		IF (N_ELEMENTS(new_faces) GT 1) THEN BEGIN 
			result = MESH_CLIP(sState.xplane1, new_verts, new_faces, $ 
				new_verts, new_faces) 
			print,'xplane1',sState.xplane1 
		ENDIF 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.yplane1, new_verts, new_faces, $ 
				new_verts, new_faces) 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.zplane1, new_verts, new_faces, $ 
				new_verts, new_faces) 
		END 
	1 : BEGIN ; naive capping 
		IF (N_ELEMENTS(*(sState.faces)) GT 1) THEN $ 
			result = MESH_CLIP(sState.xplane0, *(sState.verts), *(sState.faces), $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		new_faces = [new_faces, N_ELEMENTS(cut), cut] 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.yplane0, new_verts, new_faces, $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		new_faces = [new_faces, N_ELEMENTS(cut), cut] 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.zplane0, new_verts, new_faces, $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		new_faces = [new_faces, N_ELEMENTS(cut), cut] 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.xplane1, new_verts, new_faces, $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		new_faces = [new_faces, N_ELEMENTS(cut), cut] 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.yplane1, new_verts, new_faces, $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		new_faces = [new_faces, N_ELEMENTS(cut), cut] 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.zplane1, new_verts, new_faces, $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		new_faces = [new_faces, N_ELEMENTS(cut), cut] 
		END 
	2 : BEGIN ; smart capping 
		IF (N_ELEMENTS(*(sState.faces)) GT 1) THEN $ 
			result = MESH_CLIP(sState.xplane0, *(sState.verts), *(sState.faces), $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		cut = compute_caps(new_faces, cut) 
		new_faces = [new_faces, cut] 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.yplane0, new_verts, new_faces, $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		cut = compute_caps(new_faces, cut) 
		new_faces = [new_faces, cut] 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.zplane0, new_verts, new_faces, $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		cut = compute_caps(new_faces, cut) 
		new_faces = [new_faces, cut] 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.xplane1, new_verts, new_faces, $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		cut = compute_caps(new_faces, cut) 
		new_faces = [new_faces, cut] 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.yplane1, new_verts, new_faces, $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		cut = compute_caps(new_faces, cut) 
		new_faces = [new_faces, cut] 
		IF (N_ELEMENTS(new_faces) GT 1) THEN $ 
			result = MESH_CLIP(sState.zplane1, new_verts, new_faces, $ 
				new_verts, new_faces, CUT_VERTS=cut) 
		cut = compute_caps(new_faces, cut) 
		new_faces = [new_faces, cut] 
		END 
	ELSE : BEGIN 
		STOP 
		END 
	ENDCASE 
 
    ; Decimate model, if requested 
 	IF (sState.decValue ne 100 AND N_ELEMENTS(new_faces) GT 1) THEN BEGIN 
 	   IF (sState.movev EQ 0) THEN BEGIN 
	       numTris = MESH_DECIMATE(new_verts, new_faces, new_faces, $ 
              PERCENT_VERTICES=sState.decValue) 
       END ELSE BEGIN 
	       numTris = MESH_DECIMATE(new_verts, new_faces, new_faces, $ 
              VERTICES=new_verts, PERCENT_VERTICES=sState.decValue) 
       ENDELSE 
	END ELSE BEGIN 
	   numTris = MESH_NUMTRIANGLES(new_faces) 
	ENDELSE 
 
	; Update the mesh with the new vertices and polygons for display 
    sState.oMesh->SetProperty, DATA = new_verts, POLYGONS=new_faces 
 
	;write_smf_file, 'new.smf', new_verts, new_faces 
 
	; Update stats widget 
    str = STRING(N_ELEMENTS(new_verts[0,*]), numTris, $ 
	    FORMAT='("Model has ",I0," vertices and ",I0," faces.")') 
    WIDGET_CONTROL, sState.wLabel, SET_VALUE=str 
    WIDGET_CONTROL,HOURGLASS=0 
END 
 
;---------------------------------------------------------------------------- 
PRO DECIMATE_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 
       PTR_FREE, sState.verts 
       PTR_FREE, sState.faces 
       WIDGET_CONTROL, sEvent.top, /DESTROY 
       RETURN 
    ENDIF 
 
    ; Handle other events. 
    CASE uval OF 
    	'FILESEL': BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
   			WIDGET_CONTROL, sState.wLabel, SET_VALUE='Reading file' 
			filename = 'D:\Temp\0711\3dmesh\cow.smf' 
    		;filename = STRUPCASE(DIALOG_PICKFILE(/MUST_EXIST)) 
 
;om=get_dxf_objects(dialog_pickfile(PATH='d:\pro\visualization\object_graphics\my_obj_world')) 
;xobjview, om 
 
    		;filename = 'd:\pro\visualization\object_graphics\decimator_plus_slicing\cow.smf' 
    		CASE 1 OF 
    			STRMID(filename, 3, 3, /REVERSE_OFFSET) NE "SMF": BEGIN 
					read_smf_file, filename, verts, faces 
    			END 
    		ELSE: BEGIN 
		        WIDGET_CONTROL, sState.wLabel, SET_VALUE='Unrecognized file' 
	            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
		        RETURN 
    			END 
    		ENDCASE 
    		IF (N_ELEMENTS(verts) EQ 0) THEN BEGIN 
		        WIDGET_CONTROL, sState.wLabel, SET_VALUE='File read error' 
    		ENDIF ELSE BEGIN 
 
				sState.oTrack->Reset, [sState.xdim/2, sState.ydim/2.], sState.xdim/2. 
				sState.oGroup->Reset 
				str = STRING(N_ELEMENTS(verts[0,*]), MESH_NUMTRIANGLES(faces), $ 
					FORMAT='("Model has ",I0," vertices and ",I0," faces.")') 
    			WIDGET_CONTROL, sState.wLabel, SET_VALUE=str 
 
			    ; Compute data bounds. 
    			xMax = MAX(verts[0,*], MIN=xMin) 
    			yMax = MAX(verts[1,*], MIN=yMin) 
    			zMax = MAX(verts[2,*], MIN=zMin) 
 
				; Center normalized object in the viewport. 
    			xExtent = xMax - xMin 
    			yExtent = yMax - yMin 
    			zExtent = zMax - zMin 
    			Range = MAX([xExtent, yExtent, zExtent]) 
 
				xConv = [-xMin / Range - xExtent / (2. * Range), 1.0 / Range] 
				yConv = [-yMin / Range - yExtent / (2. * Range), 1.0 / Range] 
				zConv = [-zMin / Range - 0.0, 1.0 / Range] 
 
    			; Create the mesh. 
    			sState.oMesh->SetProperty, DATA=verts, POLYGONS=faces, $ 
                    XCOORD_CONV=xConv, YCOORD_CONV=yConv, ZCOORD_CONV=zConv 
                IF (sState.oGroup->Count() NE 0) THEN $ 
	    			sState.oGroup->Remove, /ALL 
    			sState.oGroup->Add, sState.oMesh 
 
			    ; Get ranges and planes to use during clipping 
			    sState.oMesh->GetProperty, XRANGE=xrange, $ 
			    	YRANGE=yrange, ZRANGE=zrange 
    			WIDGET_CONTROL, sState.wClipX0, SET_VALUE=100 
    			WIDGET_CONTROL, sState.wClipY0, SET_VALUE=100 
    			WIDGET_CONTROL, sState.wClipZ0, SET_VALUE=100 
    			WIDGET_CONTROL, sState.wClipX1, SET_VALUE=100 
    			WIDGET_CONTROL, sState.wClipY1, SET_VALUE=100 
    			WIDGET_CONTROL, sState.wClipZ1, SET_VALUE=100 
			    sState.xplane0 = [1.0, 0.0, 0.0, -xrange[1]] 
				sState.yplane0 = [0.0, 1.0, 0.0, -yrange[1]] 
				sState.zplane0 = [0.0, 0.0, 1.0, -zrange[1]] 
			    sState.xplane1 = [-1.0, 0.0, 0.0, xrange[0]] 
				sState.yplane1 = [0.0, -1.0, 0.0, yrange[0]] 
				sState.zplane1 = [0.0, 0.0, -1.0, zrange[0]] 
				PTR_FREE, sState.verts 
				PTR_FREE, sState.faces 
    			sState.verts = PTR_NEW(verts, /NO_COPY) 
    			sState.faces = PTR_NEW(faces, /NO_COPY) 
    			sState.xrange = xrange 
    			sState.yrange = yrange 
    			sState.zrange = zrange 
    			sState.decValue = 100 
    			WIDGET_CONTROL, sState.wDec, SET_VALUE=100 
	            sState.oWindow->Draw, sState.oView 
 
    		ENDELSE 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
		  END 
        'STYLE': BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
            sState.oMesh->SetProperty, STYLE=sEvent.index 
            CASE sEvent.index OF 
                0: BEGIN ; Point 
                       WIDGET_CONTROL, sState.wHide, SENSITIVE=1 
                       WIDGET_CONTROL, sState.wShading, SENSITIVE=0 
                   END 
                1: BEGIN ; Wire 
                       WIDGET_CONTROL, sState.wHide, SENSITIVE=1 
                       WIDGET_CONTROL, sState.wShading, SENSITIVE=0 
                   END 
                2: BEGIN ; Solid 
                       WIDGET_CONTROL, sState.wHide, SENSITIVE=0 
                       WIDGET_CONTROL, sState.wShading, SENSITIVE=1 
                   END 
            ENDCASE 
            sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
          END 
        'SHADE_FLAT': BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
            sState.oMesh->SetProperty, SHADING=0 
            sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
          END 
        'SHADE_GOURAUD': BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
            sState.oMesh->SetProperty, SHADING=1 
            sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
          END 
        'HIDE_OFF': BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
            wParent = WIDGET_INFO(sEvent.id, /PARENT) 
            j = Toggle_State(wParent) 
            sState.oMesh->SetProperty, HIDDEN_LINES=0 
            sState.oWindow->Draw, sState.oView 
            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 = Toggle_State(wParent) 
            sState.oMesh->SetProperty, HIDDEN_LINES=1 
            sState.oWindow->Draw, sState.oView 
            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 
        'CAPPING_OFF' : BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
            sState.capping = 0 
			compute_geometry, sState 
		    sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
          END 
        'CAPPING_NAIVE' : BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
            sState.capping = 1 
			compute_geometry, sState 
		    sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
          END 
        'CAPPING_SMART' : BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
            sState.capping = 2 
			compute_geometry, sState 
		    sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
          END 
 
		'VERTEX_MOVE' : BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
            sState.movev = sEvent.select 
			compute_geometry, sState 
		    sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
		  END 
 
		'XSLICE0'  : BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
			sState.xplane0[3] = -(((sEvent.value / 100.0) * $ 
		    	(sState.xrange[1]-sState.xrange[0])) + (sState.xrange[0])) 
			compute_geometry, sState 
		    sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
		  END 
		'YSLICE0'  : BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
			sState.yplane0[3] = -(((sEvent.value / 100.0) * $ 
		        (sState.yrange[1]-sState.yrange[0])) + (sState.yrange[0])) 
			compute_geometry, sState 
		    sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
		  END 
        'ZSLICE0'  : BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
        	sState.zplane0[3] = -(((sEvent.value / 100.0) * $ 
                (sState.zrange[1]-sState.zrange[0])) + (sState.zrange[0])) 
			compute_geometry, sState 
		    sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
		  END 
		'XSLICE1'  : BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
			sState.xplane1[3] = ((((100-sEvent.value) / 100.0) * $ 
		        (sState.xrange[1]-sState.xrange[0])) + (sState.xrange[0])) 
			compute_geometry, sState 
		    sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
		  END 
		'YSLICE1'  : BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
			sState.yplane1[3] = ((((100-sEvent.value) / 100.0) * $ 
		        (sState.yrange[1]-sState.yrange[0])) + (sState.yrange[0])) 
			compute_geometry, sState 
		    sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
		  END 
        'ZSLICE1'  : BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
            sState.zplane1[3] = ((((100-sEvent.value) / 100.0) * $ 
                (sState.zrange[1]-sState.zrange[0])) + (sState.zrange[0])) 
			compute_geometry, sState 
		    sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
		  END 
        'DEC'     : BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
        	sState.decValue = sEvent.value 
			compute_geometry, sState 
		    sState.oWindow->Draw, sState.oView 
            WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
          END 
        'DRAW': BEGIN 
            WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY 
 
            ; Expose. 
            IF (sEvent.type EQ 4) THEN BEGIN 
                sState.oWindow->Draw, sState.oView 
                WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
                RETURN 
            ENDIF 
 
           ; Handle trackball updates. 
           bHaveTransform = sState.oTrack->Update( sEvent, TRANSFORM=qmat ) 
           IF (bHaveTransform NE 0) THEN BEGIN 
               sState.oGroup->GetProperty, TRANSFORM=t 
               sState.oGroup->SetProperty, TRANSFORM=t#qmat 
               sState.oWindow->Draw, sState.oView 
           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 = sState.oWindow->PickData(sState.oView,$ 
                                                   sState.oMesh, $ 
                                                   [sEvent.x,sEvent.y],dataxyz) 
	           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 
 
                   sState.btndown = 4b 
	           WIDGET_CONTROL, sState.wDraw, /DRAW_MOTION 
               ENDIF ELSE BEGIN ; other mouse button. 
                   sState.btndown = 1b 
                   sState.oWindow->SetProperty, QUALITY=sState.dragq 
                   WIDGET_CONTROL, sState.wDraw, /DRAW_MOTION 
                   sState.oWindow->Draw, sState.oView 
               ENDELSE 
          ENDIF 
 
         ; Button motion. 
         IF (sEvent.type EQ 2) THEN BEGIN 
             IF (sState.btndown EQ 4b) THEN BEGIN ; Right mouse button. 
	         pick = sState.oWindow->PickData(sState.oView, $ 
                                                 sState.oMesh, $ 
                                                 [sEvent.x,sEvent.y], dataxyz) 
	         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 
 
             ENDIF 
        ENDIF 
 
        ; Button release. 
        IF (sEvent.type EQ 1) THEN BEGIN 
            IF (sState.btndown EQ 1b) THEN BEGIN 
      	        sState.oWindow->SetProperty, QUALITY=2 
      	        sState.oWindow->Draw, sState.oView 
            ENDIF 
            sState.btndown = 0b 
            WIDGET_CONTROL, sState.wDraw, DRAW_MOTION=0 
        ENDIF 
        WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY 
      END 
 
	  ELSE: BEGIN 
        END 
 
    ENDCASE 
END 
 
;---------------------------------------------------------------------------- 
PRO DECIMATE 
 
    xdim = 600 
    ydim = 360 
    decValue = 100 
 
    ; Create the widgets. 
    wBase = WIDGET_BASE(/COLUMN, XPAD=0, YPAD=0, $ 
                        TITLE="Polygonal Mesh Clipping and Decimation Example", $ 
                        /TLB_KILL_REQUEST_EVENTS) 
 
    wDraw = WIDGET_DRAW(wBase, XSIZE=xdim, YSIZE=ydim, UVALUE='DRAW', $ 
                        RETAIN=0, /EXPOSE_EVENTS, /BUTTON_EVENTS, $ 
                        GRAPHICS_LEVEL=2) 
    wGuiBase = WIDGET_BASE(wBase, /ROW) 
    wGuiBase1 = WIDGET_BASE(wGuibase, /COLUMN) 
 
	wFileSel = WIDGET_BUTTON(wGuiBase1, VALUE='Choose Model', UVALUE='FILESEL') 
 
    wStyleDrop = WIDGET_DROPLIST(wGuiBase1, VALUE=['Point','Wire','Solid'], $ 
    							 /FRAME, TITLE='Style', UVALUE='STYLE') 
 
    wOptions = WIDGET_BUTTON(wGuiBase1, MENU=2, VALUE="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') 
 
    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') 
 
    wCapping = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Capping") 
    wButton = WIDGET_BUTTON(wCapping, VALUE='Off', UVALUE='CAPPING_OFF') 
    wButton = WIDGET_BUTTON(wCapping, VALUE='Naive', UVALUE='CAPPING_NAIVE') 
    wButton = WIDGET_BUTTON(wCapping, VALUE='Smart', UVALUE='CAPPING_SMART') 
 
	;wFileSel = cw_filesel(wGuiBase1) 
 
    wGuiBase2 = WIDGET_BASE(wGuibase, /COLUMN, /FRAME) 
	wDec = WIDGET_SLIDER(wGuibase2, VALUE=100, $ 
		TITLE="Decimation Control (%)", UVALUE='DEC') 
	wCheckBase = WIDGET_BASE(wGuibase2, /COLUMN, /NONEXCLUSIVE) 
	wCheck1 = WIDGET_BUTTON(wCheckBase, VALUE='Allow vertex movement', $ 
		UVALUE='VERTEX_MOVE') 
 
    wGuiBase3 = WIDGET_BASE(wGuibase, /COLUMN, /FRAME) 
    wClipX1 = WIDGET_SLIDER(wGuibase3, VALUE=100, $ 
        TITLE="X Clip Control - (%)", UVALUE='XSLICE1') 
    wClipY1 = WIDGET_SLIDER(wGuibase3, VALUE=100, $ 
        TITLE="Y Clip Control - (%)", UVALUE='YSLICE1') 
    wClipZ1 = WIDGET_SLIDER(wGuibase3, VALUE=100, $ 
        TITLE="Z Clip Control - (%)", UVALUE='ZSLICE1') 
 
    wGuiBase4 = WIDGET_BASE(wGuibase, /COLUMN, /FRAME) 
    wClipX0 = WIDGET_SLIDER(wGuibase4, VALUE=100, $ 
        TITLE="X Clip Control + (%)", UVALUE='XSLICE0') 
    wClipY0 = WIDGET_SLIDER(wGuibase4, VALUE=100, $ 
        TITLE="Y Clip Control + (%)", UVALUE='YSLICE0') 
    wClipZ0 = WIDGET_SLIDER(wGuibase4, VALUE=100, $ 
        TITLE="Z Clip Control + (%)", UVALUE='ZSLICE0') 
 
 
    ; Status line. 
    wGuiBaseS = WIDGET_BASE(wGuibase, /COLUMN) 
    wLabel = WIDGET_LABEL(wGuiBaseS, /FRAME, /ALIGN_LEFT, $ 
                  VALUE="Left Mouse: Trackball    Right Mouse: Data Picking" ) 
    wLabel = WIDGET_LABEL(wGuiBaseS, /FRAME, /ALIGN_LEFT, $ 
                  VALUE="Choose a Model.", /DYNAMIC_RESIZE) 
 
    WIDGET_CONTROL, wBase, /REALIZE 
 
    ; Get the window id of the drawable. 
    WIDGET_CONTROL, wDraw, GET_VALUE=oWindow 
 
    ; Set default droplist items. 
    WIDGET_CONTROL, wStyleDrop, SET_DROPLIST_SELECT=2 
    WIDGET_CONTROL, wHide, SENSITIVE=0 
 
    ; Compute viewplane rect based on aspect ratio. 
    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 
 
    ; Create view. 
    oView = OBJ_NEW('IDLgrView', PROJECTION=2, EYE=3, ZCLIP=[1.4,-1.4],$ 
                    VIEWPLANE_RECT=myview, COLOR=[255,255,255]) 
 
    ; Create model. 
    oTop = OBJ_NEW('IDLgrModel') 
    oGroup = OBJ_NEW('IDLgrModel') 
    oTop->Add, oGroup 
 
 
    ; Create some lights. 
    oLight = OBJ_NEW('IDLgrLight', LOCATION=[2,2,2], TYPE=1) 
    oTop->Add, oLight 
    oLight = OBJ_NEW('IDLgrLight', TYPE=0, INTENSITY=0.5) 
    oTop->Add, oLight 
 
    ; Place the model in the view. 
    oView->Add, oTop 
 
    ; Create a trackball. 
    oTrack = OBJ_NEW('my_Trackball', [xdim/2, ydim/2.], xdim/2.) 
 
    ; Create a holder object for easy destruction. 
    oHolder = OBJ_NEW('IDL_Container') 
    oHolder->Add, oView 
    oHolder->Add, oTrack 
 
	oMesh = OBJ_NEW('IDLgrPolygon', STYLE=2, SHADING=1, $ 
		COLOR=[200,60,60]);, BOTTOM=[64,192,128]) 
 
	    ; Save state. 
    sState = {btndown: 0b,           $ 
			  dragq: 0,              $ 
			  movev: 0,              $ 
              oHolder: oHolder,	     $ 
              oTrack:oTrack,         $ 
              wDraw: wDraw,          $ 
              wLabel: wLabel,        $ 
              wHide: wHide,          $ 
              wShading: wShading,    $ 
              oWindow: oWindow,      $ 
              oView: oView,          $ 
              oGroup: oGroup,        $ 
              oMesh: oMesh,          $ 
              verts: PTR_NEW(),      $ 
              faces: PTR_NEW(),      $ 
              wDec: wDec,            $ 
              decValue: decValue,	 $ 
              capping: 0,            $ 
              xdim: xdim,            $ 
              ydim: ydim,            $ 
              wClipX0: wClipX0,      $ 
              wClipY0: wClipY0,      $ 
              wClipZ0: wClipZ0,      $ 
              wClipX1: wClipX1,      $ 
              wClipY1: wClipY1,      $ 
              wClipZ1: wClipZ1,      $ 
              xplane0: FLTARR(4),    $ 
              yplane0: FLTARR(4),    $ 
              zplane0: FLTARR(4),    $ 
              xplane1: FLTARR(4),    $ 
              yplane1: FLTARR(4),    $ 
              zplane1: FLTARR(4),    $ 
              xrange: FLTARR(2),     $ 
              yrange: FLTARR(2),     $ 
              zrange: FLTARR(2)      $ 
             } 
 
    WIDGET_CONTROL, wBase, SET_UVALUE=sState, /NO_COPY 
 
    XMANAGER, 'DECIMATE', wBase, /NO_BLOCK 
 
END