;+

FUNCTION fly,cube,R=r,CLICK=click,UNDER=under

; PURPOSE:	Cursor-controlled flight around 3D object
;
; METHOD:	Window x-y coordinates of cursor define viewpoint relative to
;		intrinsic x-y coordinates of object. I.e. move cursor to
;		left side (-ve x-axis of window) to view object from its
;		-ve x-axis side, as defined by its intrinsic coordinates.
;
; USAGE:	WHILE fly(cube) DO drawit
;		"drawit" (which is defined by the user and can have arbitrary
;		arguments) should draw the object.
;
; ARGUMENTS:	Cube = [xmin,ymin,zmin,xmax,ymax,zmax] defines the extent in
;		data coordinates of the data cube which is rotated.
;		The system variable !P.T3D is also used. It is assumed
;		to be unset on initial entry. It is set by fly for
;		the duration of the flying to enable the 3D->2D projection.
;
; KEYWORDS:	R=r defines distance of viewpoint from centre of unit cube
;		to which data is mapped.  Default is infinity (i.e. no
;		perspective effect). R=2 or R=3 helps in interpretation.
;		*NOTE* that SURFACE and SHADE_SURF do not work with
;		perspective. Must use default R=infinity with them.
;		/CLICK to specify wait for left button click before redrawing
;		image, rather than redrawing whenever the cursor moves.
;		Recommmended for complex images (~> few hundred line segments).
;		Note also that although in SunView image motion follows cursor
;		if it has moved since last time cursor was read, in Xwindows
;		it seems to need an event (wiggle mouse or press button)
;		after the cursor routine is entered.
;		/UNDER to enable flying under the cube. It is disabled by
;		default because viewpoint is quite sensitive to cursor
;		position with /UNDER and it can get confusing as to whether
;		you are looking at it from above or below. Without it you
;		are restricted to looking down on the cube.
;
; EXAMPLE:	Try fly_demo.pro
;
; BUGS:		The method of cursor control is not very intuitive. Should
;		be changed to make cube rotate in (or against) direction
;		of motion of cursor.
;
; COMMON:	References dbcom, to know whether to call flash.
;
; AUTHOR:	ANM 910325
;		ANM 911017 Modified theta treatment, added double-buffering
;			   stuff.
;		ANM 911021 Add UNDER keyword, use !P.T3D to determine initial
;			   entry, rather than INIT parameter.
;		ANM 911022 Rotate about centre of base of cube rather than
;			   center of cube (TR=[.5,.5,.0] rather than [.5,.5,.5])
;-

COMMON dbcom,w ; double-buffering routines common
IF N_ELEMENTS(w) EQ 0 THEN w=-1
IF w GE 0 THEN BEGIN	; double buffering is in effect
	flash & pix = !D.WINDOW & WSET,w ; flush the image, get real window
ENDIF

IF N_ELEMENTS(click) EQ 0 THEN click=0

IF NOT !P.T3D THEN BEGIN
  PRINT,'Move the cursor to the x-y position in the window to view object'
  PRINT,'from that side e.g. cursor on left to view from -ve x-axis.'
  IF click THEN PRINT,'Click left button to redraw image.'
  PRINT,'Hit right button to exit.'
  !X.S=[-cube(0),1.0]/(cube(3)-cube(0))
  !Y.S=[-cube(1),1.0]/(cube(4)-cube(1))
  !Z.S=[-cube(2),1.0]/(cube(5)-cube(2))
  cx = -1.0/3 & cy = SQRT(3)*cx
  IF KEYWORD_SET(under) THEN BEGIN cx = 0.5*cx & cy = 0.5*cy & END
  cx=(cx+1)/2 & cy=(cy+1)/2 & TVCRS,cx,cy,/NORM
  ; initial cx,cy correspond to theta=60 phi=240
  IF w GE 0 THEN WSET,pix
  !P.T3D = 1 ; do after TVCRS
END ELSE BEGIN
  IF w EQ -1 THEN EMPTY
  CURSOR,cx,cy,/NORM,2-click;;;/CHANGE ; select viewpoint
  IF w GE 0 THEN WSET,pix
  IF !ERR EQ 4 THEN BEGIN !P.T3D=0 & RETURN,0 & END ; exit loop on right button
ENDELSE

cx = 2*cx-1 & cy = 2*cy-1 ; put origin in centre
phi = ATAN(cy,cx)*180/!PI ; spherical coordinates of viewpoint
;;theta = ASIN(SQRT(MIN([cx^2+cy^2,1.0])))*180/!PI
; above is too sensitive near theta=90. Also cant look from below. Try linear:
theta=90*SQRT(MIN([cx^2+cy^2,1.0])) & IF KEYWORD_SET(under) THEN theta=2*theta
T3D,/RESET,TR=-[.5,.5,.0],SCALE=[1,1,1]/SQRT(3)
T3D,ROT=[-90,-90-phi,0] & T3D,ROT=[90-theta,0,0]
IF N_ELEMENTS(r) GT 0 THEN T3D,PERS=r
T3D,TR=[.5,.5,.0]
ERASE
RETURN,1
END