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:
; <None>
;
; Keywords:
; <None>
;
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