ptviewer._java


/* PTViewer	-	Interactive Viewer for Panoramic Images
   Copyright (C) 2000 - Helmut Dersch  der@fh-furtwangen.de
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

/*------------------------------------------------------------*/


import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.net.*;
import java.net.URLConnection;
import java.util.*;
import java.text.*;
import java.io.*;
import java.lang.reflect.*;
import java.lang.*;


public class ptviewer extends Applet implements Runnable{
	static final boolean debug = false;  		// Issue debug messages
	static final double HFOV_MIN 		= 10.5;
	static final double HFOV_MAX 		= 165.0;
	static final long TIME_PER_FRAME 	= 40;	    // Minimum frame time in auto-mode
	static final long ETERNITY 		= 100000000;// Number of frames which will never be rendered
	
	
	
	int quality = 2;			// Interpolators: 
						// 0 - always nn
						// 1 - nn for panning & autopanning, bil else
						// 2 - nn for panning, bil else
						// 3 - always bil


	boolean inited	= false;					// init() has been called

  	Color 		 	bgcolor = null;				// backgroundcolor

	long 	waittime = 0;						// Wait image is displayed at least that long
	long 	TimeOfLastDraw = 0;					// Time last frame was displayed
	boolean WaitDisplayed = false;				// Has wait image been displayed?
	
	Image 	view 	= null,						// Panorama Viewport
			dwait 	= null, 					// Display during download
			frame	= null,						// frame image
			offImage = null;					// offscreen image
	
	
	Graphics offGraphics = null;
	int offwidth=0, offheight=0;
	MemoryImageSource source = null;			// View is calculated here before display
	
	/* Dimension of applet window */
	int awidth = 320, aheight = 200; 			// just a guess, will be set later
	/** Dimension of viewer window */
	public int vwidth=0, vheight=0;				
	boolean vset = false;						// is vwidth/height fixed?
	int vx=0, vy=0;								// Position of viewer window
	int pwidth=0, pheight=0;					// Dimension of panoramic image
	
   	int[]   vdata 	= null;						// RGB data viewer window
   	byte[]  hs_vdata = null;					// hotspot viewer window
   	int[][] pdata 	= null;						// RGB data panorama image
   	boolean show_pdata = true;
   
   
   			
	boolean ready			= false;			// ready to render and display images
	boolean hsready			= false;			// ready to render and display hotspotimages
	boolean PanoIsLoaded	= false;			// panoramic image loaded and set up
   	boolean fatal			= false;			// a fatal error has occured
   	boolean mouseInWindow 	= true;				// true if mouse is in applet window
   	boolean mouseInViewer 	= true;				// true if mouse is in viewer window
   
   	boolean panning 		= false;			// mousebutton has been pressed
 	/** Indicates whether image needs to be rerendered at next <CODE>repaint()</CODE> call. */
   	public boolean dirty 	= true; 			// Image needs recalculation
   	boolean showhs			= false;			// Show Hotspot images?
   	
   	boolean showCoordinates	= false;			// Display mouse coordinates for editing
   	 
   	int oldx=0,oldy=0,newx=0,newy=0;			// Mouse coordinates	
   	
   	int ptcursor			= Frame.DEFAULT_CURSOR; // Cursor used in PTWindow for panning

   
   /** Current Pan Angle */
   	public double yaw 		= 0.0;	
   	/** Current horizontal field of view */			
   	public double hfov 		= 70.0;	
   	/** maximum horizontal field of view */			
	public double hfov_min 	= HFOV_MIN;		
	/** minimum horizontal field of view */	
	public double hfov_max 	= HFOV_MAX;			
   	/** tilt angle */			
   	public double pitch 	= 0.0;	
   	/** maximum tilt angle */			
   	public double pitch_max	= 90.0;	
   	/** minimum tilt angle */			
   	public double pitch_min	= -90.0;
   	/** maximum pan angle */    
  	public double yaw_max 	= 180.0; 
    /** minimum pan angle */		
   	public double yaw_min	= -180.0;			

	double autopan			= 0.0;				// pan increment
	double autotilt			= 0.0;				// tilt increment
	double zoom				= 1.0;				// Zoomfactor
	
	public double pan_steps	= 20.0;				// Number of steps for panning  one hfov

   	String filename 		= null;				// Name of panoramic image
   	String inits			= null;				// Commands to be executed on load.
	String MouseOverHS 		= null;				// Javascript function called by the applet when over hotspot
												// Syntax: MouseOverHS( int numhs );
	String GetView			= null;				// Javascript function called by the applet when view changes
												// Syntax: GetView( double pan, double tilt, double fov );

	
	int	   click_x = -1, click_y = -1;
	
	long frames				= 0;				// total number of rendered frames
	long lastframe			= 0;				// Stop autopanning after rendering this frame
	long ptimer				= 0;
	
   	Thread loadPano = null;
   	Thread ptviewerScript 	= null;
   	
   	String PTScript		 	= null;
 	
 	String PTViewer_Properties = null;


	// Region of Interests (high resolution inserts)
	
	boolean		loadAllRoi = true;			// load and insert all Roi images initially
	
	
	// Panoramic image descriptions
	
	int	   		CurrentPano	= -1;			// Currently loaded pano

   
    
    
    // Sender - Receiver stuff
    
    Hashtable sender = null;

	// Private image cache
	
	Thread 		preloadthread=null ;
	String 		preload=null;

	// Tile order in QTVR-file
	String 		order=null;
	
	
	// Constructors
	
	public ptviewer(){}
	
	public ptviewer(int[][] pd){
		pdata = pd;
		PanoIsLoaded = true;
		math_setLookUp( pdata );
		filename = "Pano";
	}
		 	    
    
	
	void initialize(){
		numhs = 0;
		curhs = -1;
		curshs = -1;
		numroi = 0;
		loadAllRoi = true;
		
		yaw 				= 0.0;				// Pan angle
		hfov 				= 70.0;				// horizontal field of view
		hfov_min 			= HFOV_MIN;			// maximum horizontal field of view
		hfov_max 			= HFOV_MAX;			// minimum horizontal field of view
		pitch 				= 0.0;				// tilt angle
		pitch_max			= 90.0;				// maximum tilt
		pitch_min			= -90.0;			// minimum tilt
		yaw_max 			= 180.0;     		// maximum pan
		yaw_min				= -180.0;			// minimum pan

		autopan				= 0.0;				// pan increment
		autotilt			= 0.0;				// tilt increment
		zoom				= 1.0;				// Zoomfactor
		
		pwidth				= 0;
		pheight				= 0;
		panning 			= false;
		lastframe			= 0;
		dirty 				= true; 
		showhs				= false;
		showCoordinates		= false;
		MouseOverHS			= null;
		GetView				= null;
		
		WaitDisplayed 		= false;
		pan_steps			= 20;
		order				= null;
	}
				
   
   	public void init(){
     	fatal = false;
		preloadthread 		= null;
		preload 			= null;
		ptcursor			= Frame.DEFAULT_CURSOR;
		

		file_init();
		math_init();
		pb_init();
		app_init();
		snd_init();
		shs_init();
		hs_init();
		
		sender = new Hashtable();

   		inited = true;
   		repaint();
		
		// Load property file
		
		/*
		byte[] bp = file_read( "PTViewer_Properties", null );
		if( bp != null ){
			PTViewer_Properties = new String( bp );
			bp = null;
		}
		*/

		initialize();
		ReadParameters( null );
		if( filename != null && filename.startsWith("ptviewer:") ){
			int n = Integer.parseInt( filename.substring( filename.indexOf(':')+1 ) );
			String p = myGetParameter(null, "pano" + n);
			if( p != null ){
				filename = null; // To reset PTViewer
				ReadParameters( myGetParameter(null, "pano" + n) );
			}
		}		
    }
	
	
 
   	public String getAppletInfo(){
      	return "PTViewer v. 2.1   H. Dersch, der@fh-furtwangen.de";
   	}

   	public void start(){
   		if(loadPano == null)
   		{
      		loadPano = new Thread(this);
      		loadPano.start();
      	}
   	}

   	public synchronized void stop()
 	{
 		int i;

 		
		stopThread(preloadthread); 	preloadthread 	= null;
		stopThread(loadPano); 		loadPano 		= null;

      	stopAutoPan();
      	panning = false;
      	
      	stopApplets(0);
      	 
  		ready = false;
 		hsready = false;
     		

		vdata 		= null; 
		hs_vdata	= null;
		view		= null;
    	if( !vset ){
   			vwidth 	= 0; 
   			vheight = 0;
   		}
       	offImage 	= null; 	
    }


	synchronized void PV_reset(){
  		int i;
   		
   		ready = false;
   		hsready= false;
   		
			    
		hs_dispose();
		roi_dispose();
				

		PanoIsLoaded = false;
 
 		filename = null;
		MouseOverHS = null;
		GetView		= null;
		
		
		pb_reset();
  		
 		inits = null;
 		order = null;
      		System.gc();
	}

	public synchronized void destroy(){
 		stopThread( ptviewerScript ); ptviewerScript = null;
  		
		PV_reset();  
		
		if(sender != null ){
			sender.clear();
			sender = null;
		}		


		vdata 	= null;
		hs_vdata= null;
	    source 	= null;
	    frame 	= null;
		view 	= null;
		dwait 	= null;
		pdata   = null;
	    
		math_dispose();
		shs_dispose();
		snd_dispose();
		
    	System.gc();
    }


		
 	/**
 	* Run threads to load and display the panoramic images and
 	* run ptviewer:commands.
 	*/
   	public void run(){
 
 		if( Thread.currentThread() == preloadthread && preload != null ){
			int n = getNumArgs( preload, ',' );

 			if( n > 0 ){
  				for(int i=0; i<n; i++){
  					String fname = getArg( i, preload, ',' ) ;
  					
  					if( fname != null && file_cachefiles && file_Cache != null &&
  						file_Cache.get( fname ) == null && fname != filename ){
  						file_read( fname, null);
  					}
  				}
  			}
   			return;
   		}
		
 
 		if( Thread.currentThread() == ptviewerScript ){
 			if( PTScript != null ){
 				PTViewerScript( PTScript );
 			}
 			return;
 		}
 				
  		
 		// Current thread is loadPano
 		
		
		ResetCursor();
 		 				
 		// Load panoramic image, if not done
        if( !PanoIsLoaded ){
        	show_pdata = true;
         	if(filename == null){
         		if(pwidth != 0) 
         			filename = "_PT_Grid"; // Display grid
         		else
         			show_pdata = false;
         	}
         	if( filename != null && filename.toLowerCase().endsWith(".mov")){ // Don't load qtvr files here
         		pdata = im_loadPano(null, pdata, pwidth, pheight);
         	}else
        		pdata = im_loadPano(filename, pdata, pwidth, pheight);
     		System.gc();
		}
		
		if( pdata == null ){
			fatal = true; repaint();
			return;
		}
  
		// Panorama is loaded now
		
		// Load QTVR panorama if there is one

		if( filename != null && filename.toLowerCase().endsWith(".mov")){
			try{
				String p = " {file=" + filename + "} ";
				if( order != null )
					p = p + "{order=" + order +"} ";
				Class c = Class.forName( "ptmviewer" );
	    		Constructor ac = c.getConstructor( new Class[]{ Class.forName("ptviewer"), String.class });
	    		Applet qtvr = (Applet) ac.newInstance( new Object[]{ this, p });
	    		qtvr.init();
	    		qtvr.start();
	    		qtvr = null;
	    		System.gc();
			}catch(Exception e){}
		}

        pheight = pdata.length;
        pwidth  = pdata[0].length;

  		// Set maximum and minimum tilt angles
        
        if( pheight != pwidth/2  ) { 
        	double f = (double)pheight / (double)pwidth * 180.0;
        	if( pitch_max > f ) pitch_max = f;
        	if( pitch_min < -f) pitch_min = -f;
        	
        }
        if( hfov > yaw_max - yaw_min ) hfov = yaw_max - yaw_min;
        

 
 		if( !PanoIsLoaded ) math_setLookUp( pdata );

 		finishInit( PanoIsLoaded );
 
   	}
 
 	void finishInit(boolean p){
  		if(!p)	
 			shs_setup(); // Set up static hotspots
               
   		ready = true;

		requestFocus();
		ResetCursor();
  		
  		repaint();
  		paint(getGraphics()); // Extra kick for IE	

 		// Load ROI Images
  		if( loadAllRoi && !PanoIsLoaded )
  			loadROI(0, numroi-1);
  		
  		if(!PanoIsLoaded)	
			hs_setup(pdata);
				
		hsready = true;
		
		PanoIsLoaded = true;
  		
   		if(autopan != 0.0)
  			lastframe = frames + ETERNITY;

  		if( inits!=null){
  			JumpToLink( inits, null );
  		}
		
       	repaint();
       	
       	SetupSounds();
       	
       	// Panorama is displayed now; start 
       	// download of more images in the background
     	if(preload != null && preloadthread == null){
      		preloadthread = new Thread(this);
      		try{
      			preloadthread.setPriority(Thread.MIN_PRIORITY);
      		}catch( SecurityException se ){
      		} 
      		preloadthread.start();
      	}

	} 		  	
 
   	public boolean mouseDown(Event evt, int x, int y){
   		if( x>=vx && x<vx+vwidth && y>=vy && y<vy+vheight ){
   			if( lastframe > frames ){
				stopThread( ptviewerScript ); ptviewerScript = null;
   				stopAutoPan();
     			oldx =  x;
      			oldy =  y;
   				return true;
   			}
   			if( showCoordinates ){
   				showStatus(DisplayHSCoordinates( x-vx, y-vy ));
   				showCoordinates = false;
   				return true;
   			}
   		}
 		
 		if( !panning && mouseInViewer ){
      		oldx =  x;
      		oldy =  y;
      		if( curhs<0 ){
      			panning 	= true;
      			if( evt.shiftDown() )
      				zoom = 1.0/1.03;
       			else if( evt.controlDown() )
       				zoom = 1.03;
       			else
      				zoom = 1.0;
      			repaint();
   				PVSetCursor( x,  y);
   			}
   		}

      	newx = x;
      	newy = y;
       	return true;
   	}

    public boolean mouseDrag(Event evt, int x, int y){
 		newx = x;               
      	newy = y;
 		if( mouseInViewer){  
 			panning = true;   
      		if( evt.shiftDown() )
      			zoom = 1.0/1.03;
      		else if( evt.controlDown() )
       			zoom = 1.03;
       		else
      			zoom = 1.0;
      		ResetCursor();
 		}
		repaint();
     	return true;
   	}


   	public boolean mouseUp(Event evt, int x, int y)
   	{
  		newx = x;               
      	newy = y;
		panning = false;
   		zoom = 1.0;
   		if(hsready){
  			if( curshs >= 0 ){
   				int i;
 				for(i=0; i<numshs; i++)
  					if( shs_active[i] )
   						gotoSHS( i );
   			}
   			else if( curhs >= 0 ) {
   				int i;
   				gotoHS( curhs );
   				for(i=curhs+1; i<numhs && curhs != -1; i++)
   					if( hs_link[i] == curhs )
   						gotoHS( i );
   				if( curhs < 0 ) return true;
   			}
   			PVSetCursor( x,  y);
   			
   			click_x = x;
   			click_y = y;
   		}
		return true;
   	}

   	public boolean mouseEnter(Event evt, int x, int y){
       	mouseInWindow = true;
       	mouseInViewer = is_inside_viewer(x,y);
   		PVSetCursor( x,  y);
      	return true;
   	}

    public boolean mouseExit(Event evt, int x, int y){
      	mouseInWindow = mouseInViewer = false;
      	panning = false;
   		zoom = 1.0;
   		ResetCursor();
      	return true;
   	}

   	public boolean keyDown(Event evt, int key)
   	{ 
   		if( !ready ) return true;
   		  		
   		switch( key )
   		{
   			case Event.UP:	panUp();
         					break;
         	case Event.DOWN:panDown();
         					break;
         	case Event.LEFT:panLeft();
         					break;
         	case Event.RIGHT:panRight();
         					break;
         	case '+': 
         	case '>':   	
			case 'A': 
        	case 'a': 
        	case '.': 
        	case '=':		ZoomIn();
 							break;
			case '-':
			case '<':   	
			case 'Z':
			case 'z':   	
			case ',':
			case '_':   	ZoomOut();
     						break;
			case ' ':		toggleHS();
							break;
			case 'i':
			case 'I':		showStatus(getAppletInfo());
							break;
			case 'v':		// Print View information
							showStatus("pan = " + (double)((int)(yaw*100.0))/100 + "; tilt = " + (double)((int)(pitch*100.0))/100 + "; fov = " +  (double)((int)(hfov*100.0))/100 + "");
							break;
			case 'p':		// Print path to document
			case 'P':		showStatus( ptgetPath() );
							break;
			case 'u':		// Print URL to document
			case 'U':		showStatus( getDocumentBase().toString() );
							break;
			case 'h':		// Print Mousecoordinates
							showCoordinates = true;
							showStatus("Click Mouse to display X/Y Coordinates");
							break;
			case '\n':		if(!hsready) break;
							if( curshs >= 0 ) {
								int i;
								for(i=0; i<numshs; i++)
									if( shs_active[i] )
										gotoSHS( i );
							}
							else if( !panning && curhs >= 0 ) {
								int i;
								gotoHS( curhs );
								for(i=curhs+1; i<numhs && curhs != -1; i++)
									if( hs_link[i]==curhs )
										gotoHS( i );
								if( curhs < 0 ) return true;
							}
							break;
     	}
     	return true;
   	}


 	public boolean mouseMove(Event evt, int x, int y){
       	mouseInViewer = is_inside_viewer(x,y);
   		if(mouseInWindow){
			newx = x;               
      		newy = y;
      		
      	}
      	PVSetCursor(x,y);
       	return true;		
   	}

	
	void PVSetCursor(int x, int y){
		int i;
		
		if(!mouseInWindow){ // outside applet window
			ResetCursor();
			return;
		}
		
		if( !ready ){
			i = -1;
		}else
			i = OverStaticHotspot( x, y );
		
		if( i!= curshs ){ // Hotspot status has changed
			curshs = i;
		
			if( curshs >= 0 ){ // We entered a static hotspot
     			try{
					((Frame)getParent()).setCursor( Frame.HAND_CURSOR );
         		}catch(Exception e){}
 				curhs = -1;
 				repaint();
 				return;
 			}
 			else{	// We just left a static hotspot
 				ResetCursor();
 				repaint();
  			}
 		}
 		
 		// Leave if we're over a static hotspot
 		if( curshs >= 0 ) return;

		if( panning || lastframe>frames || !mouseInViewer){
			curhs = -1;
			ResetCursor();
			return;
		}
		
		if( !hsready ){
			i = -1;
		}else
			i = OverHotspot( x-vx, y-vy );
			
		if( i!= curhs ){ // hotspot status has changed
			curhs = i;
      		if( curhs >= 0 ){ // we entered the hotspot
      			try{
					((Frame)getParent()).setCursor( Frame.HAND_CURSOR );
     				if(hsready){
     					showStatus(hs_name[curhs]);
     					hs_exec_popup( curhs );
      					repaint();
       					sendHS();
       				}
       				return;
        		}catch(Exception e){}
     		}
      		else{ // we left the hotspot
      			ResetCursor();
      			repaint();
      			showStatus("");
      			sendHS();
      			return;
      		}
      	}
 		if( curhs >= 0 ) return;

		ResetCursor();
 	}
 	
 	void ResetCursor(){
      	try{
			if(mouseInViewer){
				if( !ready ){
					((Frame)getParent()).setCursor( Frame.WAIT_CURSOR );
					return;
				}
      			if( ((Frame)getParent()).getCursorType() != ptcursor ){
      				((Frame)getParent()).setCursor( ptcursor );	
      			}
      		}else{
       			if( ((Frame)getParent()).getCursorType() != Frame.DEFAULT_CURSOR  ){
      				((Frame)getParent()).setCursor( Frame.DEFAULT_CURSOR );	
      			}
      		}
      	}
 		catch(Exception e){}
 	}
 			

	void sendView()
	{
		if(GetView != null && ready && loadPano!=null) // && !panning && !(lastframe>frames) 
			executeJavascriptCommand( GetView + "(" + yaw + "," + pitch + "," + hfov + ")");
	}
	
	void sendHS()
	{
      	if( MouseOverHS != null && ready && loadPano!=null)
      		executeJavascriptCommand( MouseOverHS + "(" + curhs + ")" );
  	}
 


	public void update(Graphics g) {	paint(g);	}


	public synchronized void paint(Graphics g){
 		if( !inited ) return;
  		
       	if(fatal){
         	setBackground(Color.red);
         	g.clearRect(0,0,size().width,size().height);
         	return;
      	}
      	
      	
 		if( offImage == null ){
 			awidth = size().width; aheight = size().height;
 			if( !vset || offwidth == 0 ){
       			offwidth = size().width; offheight = size().height;
       		}
  			offImage 	 = createImage(offwidth, offheight);
      		offGraphics  = offImage.getGraphics();
 		}
		

		
		if( !ready || System.currentTimeMillis() < ptimer){
			if(dwait != null){
				if( bgcolor != null && !WaitDisplayed){
					setBackground( bgcolor );
					offGraphics.clearRect(0, 0, offwidth, offheight );
				}
				if( !WaitDisplayed ){
					if( waittime != 0 )
						ptimer = System.currentTimeMillis() + waittime;
					WaitDisplayed = true;
				}
				offGraphics.drawImage(dwait,
							(offwidth  - dwait.getWidth(null))/2,
							(offheight - dwait.getHeight(null))/2,this);
				
				pb_draw( offGraphics, offwidth, offheight);
				
				g.drawImage(offImage,0,0,this);
				
				if( ready ){
					try{ Thread.sleep(20);}				
					catch (InterruptedException e) {return;}
					repaint();
				}
			}else{
				if( bgcolor != null ){
					setBackground( bgcolor );
				}
        		g.clearRect(0, 0, size().width, size().height );
        		if( percent != null && percent[0] > 0 )
        			g.drawString("Loading Image..." + percent[0] + "% complete", 30, size().height/2);
        		else
        			g.drawString("Loading Image...", 30, size().height/2);
         	}
        	return;
        } 
        
        // At this point the panoramic image is loaded

      	// Initialize viewer window
     	
        if( vdata == null){
        	if( vwidth == 0 ) 	vwidth  = size().width;
        	if( vheight == 0 ) 	vheight = size().height;
        	
        	// Set some vwidth/vheight related data
  
        	while( math_fovy(hfov, vwidth, vheight) > pitch_max - pitch_min ){
        		hfov /= 1.03;
        	}
 			double fovy2 = math_fovy(hfov, vwidth, vheight)/2.0;
        
        	if( pitch > pitch_max - fovy2 && pitch_max != 90.0) pitch = 0.0;
        	if( pitch < pitch_min + fovy2 && pitch_min != -90.0) pitch = 0.0;

        	vdata 	= new int[vwidth * vheight ];
        	hs_vdata= new byte[vwidth * vheight];
        	
        	if( filename != null && filename.toLowerCase().endsWith(".mov") )
				for(int i=0; i<hs_vdata.length; i++) hs_vdata[i] = (byte)0;
			else
				for(int i=0; i<hs_vdata.length; i++) hs_vdata[i] = (byte)0xff;
				
        	dirty	= true;
       		source 	= new MemoryImageSource(vwidth, vheight, vdata, 0, vwidth);
        	source.setAnimated(true);
   		
 			if( view == null ){
   				view 	= createImage(source);
  			}
		}   		

      	

			
    	if( panning ){
   			double scale = 1.0 / 2000.0 * hfov / 70.0 * 320.0 / vwidth;
			gotoView( yaw 	+ scale * (double)((newx - oldx)*(newx - oldx)) * (newx > oldx? 1.0 : -1.0) , 
					  pitch + scale * (double)((oldy - newy)*(oldy - newy)) * (oldy > newy? 1.0 : -1.0) , 
					  hfov 	* zoom );
		}
    	
    	if( lastframe>frames ){
			gotoView( yaw + autopan, 
						  pitch + autotilt, 
						  hfov * zoom );
		}
		
		if(hsready){
			if( hs_drawWarpedImages(pdata, curhs, showhs) ){
				dirty = true;
			}
		}
        	
      	if( dirty ){// image needs recalculation

            if( lastframe>frames ){
      			long delay = System.currentTimeMillis() - TimeOfLastDraw;
      			if( delay < TIME_PER_FRAME ){
      				try{ Thread.sleep( TIME_PER_FRAME - delay );}				
					catch (InterruptedException e) {return;}
				}
			}
      			
            TimeOfLastDraw = System.currentTimeMillis() ;
           	
           	// Clear image
			int i; for(i = 0;  i < vdata.length; i++) vdata[i] = 0;
 		
 			// Call extension applets if any
 			for(i=0; i<app_properties.size(); i++){
 				Applet a = (Applet)applets.get( app_properties.elementAt(i) );
 				if( a != null &&
 					sender != null && 
 					sender.get( a ) != null ){ //Applet is registered as sender
					String s = a.getAppletInfo();
					if( dirty && s != null && s.equals("topFrame")  ){ // Paint image into viewer window
						a.paint(null);
					}
				}
			}

  			if( dirty && show_pdata ){ // filename != null && !filename.toLowerCase().endsWith(".mov")){
				switch( quality ){
					case 0: math_extractview(pdata, vdata, hs_vdata, vwidth, hfov, yaw, pitch, false);
							dirty = false;
							break;
					case 1: if( panning  || lastframe>frames)
								math_extractview(pdata, vdata, hs_vdata, vwidth, hfov, yaw, pitch, false);
							else{
								math_extractview(pdata, vdata, hs_vdata, vwidth, hfov, yaw, pitch, true);
								System.gc();
								dirty = false;
							}
							break;
					case 2: if( panning )
								math_extractview(pdata, vdata, hs_vdata, vwidth, hfov, yaw, pitch, false);
							else{
								math_extractview(pdata, vdata, hs_vdata, vwidth, hfov, yaw, pitch, true);
								System.gc();
								dirty = false;
							}
							break;
					case 3: math_extractview(pdata, vdata, hs_vdata, vwidth, hfov, yaw, pitch, true);
							dirty = false;
							break;
				}
			}				
					
		
			hs_setCoordinates(vwidth, vheight, pwidth, pheight, 
							yaw, pitch, hfov);
			sendView();
			frames++;
             
       		source.newPixels();
         }
       	
      	// draw image
 		
 		// Set cursor if image has moved
 		if( panning || lastframe>frames )	
      			PVSetCursor(newx, newy);
 
     			
      	offGraphics.drawImage(view,vx,vy,this);
      	
      	// Hotspot images
      	
       	if(hsready)
       		hs_draw(offGraphics, vx, vy, vwidth, vheight, curhs, showhs);
 
       		
 		if( frame != null )      	
      		offGraphics.drawImage(frame,
      							offwidth  - frame.getWidth(null),
      							offheight - frame.getHeight(null),
      							this);
		
		if( ready ){
			shs_draw( offGraphics );
		}
		
		{
			Enumeration en = sender.elements();
			while( en.hasMoreElements() ){
				try{
					Applet ext = (Applet) en.nextElement();
					if( ext.getAppletInfo() != "topFrame"){
						ext.paint(offGraphics);
					}
				}catch( Exception e1){}
			}
		}
		
    	g.drawImage(offImage,0,0,this);
    	
    }
 	
 
 
	public void loadROI( int first, int last ){
		int i;
		for(i=first; i<=last; i++){
			loadROI(i);
		}
	}
	
	
	public void loadROI( int i ){
 		if( i < numroi && !roi_loaded[i] ){
  			Image r = null;
 			r = loadImage( roi_im[i] );
   			if(r != null){
  				ptinsertImage( pdata,  roi_xp[i], roi_yp[i], r, (pheight+99)/100 );

   				// Update warped hotspots
   				if( hsready ){
   					int k;
   					for(k=0; k<numhs; k++){
   						if( (hs_imode[k] & IMODE_WARP) > 0 ){ // warped hotspot
 							int w =  (int)hs_up[k];
							int h =  (int)hs_vp[k];
							int xp = (int)hs_xp[k] - w/2;
							int yp = (int)hs_yp[k] - h/2;
  							im_extractRect( pdata, xp, yp, (int[])hs_him[k], w, 0, h, w, h );
        				}
        			}
   				}	
 				roi_loaded[i] = true;
  				r = null;
   			}
   		}
  	}
		
     	



	
	String DisplayHSCoordinates( int xv, int yv ){
		double[] x = math_view2pano( xv, yv, vwidth, vheight,
                            	   pwidth, pheight,
                            	   yaw, pitch, hfov);
		
		x[0] =  ((double) Math.rint( x[0] * 100000.0 / (double)pwidth ))/ 1000.0;
		x[1] =  ((double) Math.rint( x[1] * 100000.0 / (double)pheight))/ 1000.0;
		
		return ("X = " + x[0] + "; Y = " + x[1]);		
	}
	
	
	

	// Return number of hotspot under x/y coordinate
	// or -1, if there is none
	
	int OverHotspot( int x, int y ){
		int i;
		int[] cp;
							
		if( !hsready  || x < 0 || x >= vwidth || y < 0 || y >= vheight )
			return -1;
		
		/*
		cp = math_int_view2pano( x, y, vwidth, vheight,
                             pwidth, pheight,
                             yaw, pitch, hfov);

		i=( pdata[cp[1]][cp[0]] >>> 24 );
		cp = null;
		*/
		i = hs_vdata[y*vwidth + x] & 0xff;
		
		if( filename != null && filename.toLowerCase().endsWith(".mov")){ // qtvr, use other syntax
			if( i == 0 )
				return -1;
			else
				return i-1;
		}
		
		
		if( i!=0xff && i < numhs ){
			// System.out.println("HS " + i);
			return i;
		}
		
		if( hs_image != null )
			return -1;
			

		for( i = 0; i < numhs; i++){
			if( hs_visible[i] && hs_mask[i] == null   &&
								 hs_link[i] == -1	  &&
								 hs_up[i] == NO_UV	  &&
								 hs_vp[i] == NO_UV	  &&
								 x < hs_xv[i] + HSIZE && 
								 x > hs_xv[i] - HSIZE &&
								 y < hs_yv[i] + HSIZE &&
								 y > hs_yv[i] - HSIZE ){
				return i;
			}
		}
		return -1;
	}


	


	/** Wait while autopanning */
	public void waitWhilePanning(){
		while( lastframe>frames ){
			try{
				Thread.sleep( 200 );
			}catch(Exception e){
				return;
			}
		}
		return;
	}
	
	// Some public functions to script the applet
	/** Zoom in 3% */
	public void ZoomIn()		{ gotoView( yaw, 	pitch, 			 hfov/1.03 	); } 
	/** Zoom out 3% */
	public void ZoomOut()		{ gotoView( yaw, 	pitch, 			 hfov*1.03 	); } 
	/** Tilt up 5 degrees */
	public void panUp()			{ gotoView( yaw, 	pitch+hfov/pan_steps, hfov 		); } 
 	/** Tilt down 5 degrees */
 	public void panDown()		{ gotoView( yaw, 	pitch-hfov/pan_steps, hfov 		); } 
	/** Pan left 5 degrees */
	public void panLeft()		{ gotoView( yaw-hfov/pan_steps,pitch, 	 hfov 		); } 
 	/** Pan right 5 degrees */
 	public void panRight()		{ gotoView( yaw+hfov/pan_steps,pitch, 	 hfov 		); } 

 	/** Show Hotspot Images */
 	public void showHS()		{ showhs = true; 	repaint(); } 
 	/** Hide Hotspot Images */
 	public void hideHS()		{ showhs = false;	repaint(); } 
 	/** Toggle Visibility of Hotspot Images */
 	public void toggleHS()		{ showhs = !showhs;	repaint(); } 
 	/** Are Hotspot Images visible? */
 	public boolean isVisibleHS(){ return showhs; } 

 	/** Return current pan angle */
 	public double pan()			{ return yaw; }		
 	/** Return current tilt angle */
 	public double tilt()		{ return pitch; }	
 	/** Return current field of view angle */
 	public double fov()			{ return hfov; }	
 	
 	/**
 	* Moves from a specific position to another position using a specified amount of frames
    * @param p0 Pan angle of starting view
    * @param p1 Pan angle of target view
    * @param t0 Tilt angle of starting view
    * @param t1 Tilt angle of target view
    * @param f0 Field of View angle of starting view
    * @param f1 Field of View of target view
    * @param nframes the number of frames
    */
	public void moveFromTo( double p0, double p1, double t0, double t1, double f0, double f1, int nframes )
	{
		double dp = 0.0; 
		double dt = (t1-t0)/(double)nframes;
		double z = Math.pow(f1/f0, 1.0/(double)nframes);
		double df = (f1-f0)/(double)nframes;
		
		if( Math.abs(p1-p0) < 180.0 || yaw_max != 180.0 || yaw_min != -180.0)
			dp = (p1-p0)/(double)nframes;
		else if( p1 > p0 )
			dp = (p1-p0-360.0)/(double)nframes;
		else if( p1 < p0 )
			dp = (p1-p0+360.0)/(double)nframes;
			 
		gotoView( p0, t0, f0 );
		lastframe = frames + nframes;
		startAutoPan( dp, dt, z );
		
	}

 	/**
 	* Moves from the current position to another position using a specified amount of frames
    * @param pan Pan angle of target view
    * @param tilt Tilt angle of target view
    * @param fov Field of View of target view
    * @param nframes the number of frames
    */
	public void moveTo( double pan, double tilt, double fov, int nframes )
	{
		moveFromTo( yaw, pan, pitch, tilt, hfov, fov, nframes );
	}

	/**
 	* Starts autopanning.
    * @param p Pan angle increment per frame
    * @param t Tilt angle increment per frame
    * @param z Field of View angle factor per frame
    */
	public void startAutoPan( double p, double t, double z )
	{
		autopan = p; autotilt = t; zoom = z; 
		if( lastframe <= frames )
			lastframe = frames + ETERNITY;
		repaint();
	}

	/**
 	* Stops autopanning. Also stops ongoing <CODE>moveTo()</CODE>
 	* or <CODE>moveFromTo()</CODE> processes.
	*/
	public void stopAutoPan()
	{
		lastframe = 0; autopan = 0.0; autotilt = 0.0; zoom = 1.0; 
	}
	
	/**
 	* Returns true if autopanning, or ongoing <CODE>moveTo()</CODE>
 	* or <CODE>moveFromTo()</CODE> processes.
	*/
	public boolean getAutoPan(){ return lastframe > frames; }
	
	
	
	/**
 	* Jump to specific position
    * @param pan Pan angle 
    * @param tilt Tilt angle
    * @param fov Field of View angle
	*/	
	public void gotoView( double pan, double tilt, double fov ){
		if( pan == yaw && tilt == pitch && fov == hfov ) return;

		while( pan > 180.0 ) pan -= 360.0;
		while( pan <-180.0 ) pan += 360.0;
		
		
		double f  = math_fovy( fov,  vwidth, vheight)/2.0;  

		if(tilt > pitch_max - f && pitch_max != 90.0) 
			tilt =  pitch_max - f;
		else if(tilt > pitch_max)
			tilt = pitch_max;
		else if( tilt < pitch_min + f && pitch_min != -90.0) 
			tilt =  pitch_min + f;
		else if(tilt < pitch_min)
			tilt = pitch_min;
		
		// Check and correct for yaw_max/min
		
		if(yaw_max != 180.0 || yaw_min != -180.0){
			double x[]; 
			double xl, xr;
			
			// check left edge
			
			x = math_view2pano( 0, ( pitch > 0.0 ? 0 : vheight-1), vwidth, vheight,
                            	 pwidth, pheight,
                            	 pan, tilt, fov);
			xl = x[0];

			x = math_view2pano( vwidth-1, ( pitch > 0.0 ? 0 : vheight-1), vwidth, vheight,
                            	 pwidth, pheight,
                            	 pan, tilt, fov);
			xr = x[0];
			
			x = null;
			
			if( (xr - xl) > (yaw_max-yaw_min)/360.0 * (double)pwidth ) return;
			
			if( xl < (yaw_min + 180.0) / 360.0 * (double) pwidth ){ // too far to the right
				if( lastframe > frames ){ // autopanning
					autopan *= -1.0;
				}
				pan +=  yaw_min - ( xl/(double)pwidth * 360.0 - 180.0);
			}
				
			if( xr > (yaw_max + 180.0) / 360.0 * (double) pwidth ){
				if(lastframe > frames ){
					autopan *= -1.0;
				}
				pan -= ( xr/(double)pwidth * 360.0 - 180.0) - yaw_max ;
			}
		}
			
		if( 2.0 * f <=  pitch_max - pitch_min 
			&& fov <= hfov_max
			&& fov >= hfov_min
			&& fov <= yaw_max - yaw_min
			&& tilt <= pitch_max
			&& tilt >= pitch_min
			&& pan  <= yaw_max
			&& pan  >= yaw_min){
						
			if( pan != yaw || tilt != pitch || fov != hfov){
				yaw = pan; pitch = tilt; hfov = fov;
				dirty = true;
				repaint();
				return;
			}
		}
		
		// If we reach this point, then there is no change
		// We have probably reached the end of an autopan
		stopAutoPan();
		
	}

	/**
	* Jump to the url-link specified in a hotspot
    * @param n The list number of the hotspot
	*/
	public void gotoHS( int n ){
		if( n < 0 || n >= numhs )
			return;
			
		JumpToLink( hs_url[n], hs_target[n] );
	}
	
	/**
	* Jump to the url-link specified in a static hotspot
    * @param n The list number of the static hotspot
	*/
	void gotoSHS( int n ){
		if( n < 0 || n >= numshs )
			return;
			
		JumpToLink( shs_url[n], shs_target[n] );
	}
	
	void JumpToLink( String url, String target )
	{
		URL u;
		
		if(url == null )
			return;
		
		if( url.startsWith("ptviewer:") ){
			executePTViewerCommand( url.substring( url.indexOf(':')+1 ));
			return;
		}
		
		if( url.startsWith("javascript:") ){
			executeJavascriptCommand( url.substring( url.indexOf(':')+1 ));
			return;
		}
				
		try{
			u = new URL(getDocumentBase(),url );
		}
		catch(MalformedURLException mue){
			System.err.println("URL " + url + " ill-formed");
			return;
		}
		
		if( target == null )
			getAppletContext().showDocument(u);
		else
			getAppletContext().showDocument(u, target );
		 
	}

	
	/** Load a new panoramic image from a list.
    * @param k The list number of the panorama
    * @param pan Pan angle
    * @param tilt Tilt angle
    * @param fov Field of view angle
	*/
	public synchronized void newPanoFromList( int k, double pan, double tilt, double fov ){
		loadPanoFromList(  k );	
		yaw = pan; pitch = tilt; hfov = fov;
		repaint();
		start();
	}

	
	/** Load a new panoramic image from a list.
    * @param k The list number of the panorama
	*/
	public synchronized void newPanoFromList( int k ){
		loadPanoFromList( k );
		repaint();
		start();
	}


	
	void loadPanoFromList( int k ){
		String p = myGetParameter(null, "pano" + k);
 		if( p == null) return;
		stop();
		PV_reset(); 
 		initialize();
		
		CurrentPano = k;
 		ReadParameters( p );
	}
	
	public void newPano( String p ){
		stop();
		PV_reset(); 
 		initialize();

 		ReadParameters( p );
		repaint();
		start();
	}
	
	// For QTVR compatibility	
	public void SetURL( String url ){
		newPano( "{file=" + url + "}" );
	}
		
	
	
	// Read parameters for panorama "p"
	
	void ReadParameters( String p ){
 		String s;
 		int i,k;
 		
 		if( p == null ) { // read only first time
			s = myGetParameter( p, "bgcolor");		if( s!=null )	bgcolor = new Color( Integer.parseInt(s,16) );
			s = myGetParameter( p, "barcolor");		if( s!=null )	pb_color = new Color( Integer.parseInt(s,16) );
			s = myGetParameter( p, "bar_x");		if( s!=null )	pb_x = Integer.parseInt(s);
			s = myGetParameter( p, "bar_y");		if( s!=null )	pb_y = Integer.parseInt(s);
			s = myGetParameter( p, "bar_width");	if( s!=null )	pb_width = Integer.parseInt(s);
			s = myGetParameter( p, "bar_height");	if( s!=null )	pb_height = Integer.parseInt(s);
			s = myGetParameter( p, "maxarray"); 	if( s!=null )	im_maxarray = Integer.parseInt(s);
			s = myGetParameter( p, "view_width");	if( s!=null )	{ vwidth  = Integer.parseInt(s); vset = true; }
			s = myGetParameter( p, "view_height");	if( s!=null )	{ vheight = Integer.parseInt(s); vset = true; }
			s = myGetParameter( p, "view_x");		if( s!=null )	vx = Integer.parseInt(s);
			s = myGetParameter( p, "view_y");		if( s!=null )	vy = Integer.parseInt(s);
			s = myGetParameter( p, "preload");		if( s!=null )	preload = s;
			s = myGetParameter( p, "cache");		if( s!=null )	{ if( s.equalsIgnoreCase("false") ) file_cachefiles = false; }
			s = myGetParameter( p, "cursor");		if( s!=null )	{ if( s.equalsIgnoreCase("CROSSHAIR") ) ptcursor = Frame.CROSSHAIR_CURSOR;
																	  else if( s.equalsIgnoreCase("MOVE"))  ptcursor = Frame.MOVE_CURSOR; }
			s = myGetParameter( p, "grid_bgcolor");	if( s!=null )   grid_bgcolor = Integer.parseInt(s,16);
			s = myGetParameter( p, "grid_fgcolor");	if( s!=null )   grid_fgcolor = Integer.parseInt(s,16);

		}
		
		s = myGetParameter( p, "quality");		if( s!=null )	{ quality = Integer.parseInt(s); 
																	  if( quality < 0 ) quality = 0;
																	  if( quality > 3 ) quality = 3;
																}
		s = myGetParameter( p, "inits");		if( s!=null )	inits = s;
		s = myGetParameter( p, "tiltmin");		if( s!=null )	{ double x = Double.valueOf(s).doubleValue();
																 if( x > -90.0 && x < 0.0 ) pitch_min = x;}
		s = myGetParameter( p, "tiltmax");		if( s!=null )	{ double x = Double.valueOf(s).doubleValue();
																 if( x <  90.0 && x > 0.0 ) pitch_max = x;}
		s = myGetParameter( p, "tilt");			if( s!=null )	{ double x = Double.valueOf(s).doubleValue();
																if( x >=  pitch_min && x <= pitch_max ) pitch = x;}
		s = myGetParameter( p, "panmax");		if( s!=null )	yaw_max =  Double.valueOf(s).doubleValue();
		s = myGetParameter( p, "panmin");		if( s!=null )	yaw_min =  Double.valueOf(s).doubleValue();
		s = myGetParameter( p, "pan");			if( s!=null )	{ double x = Double.valueOf(s).doubleValue();
																if( x>=yaw_min && x<=yaw_max ) yaw = x;}
		s = myGetParameter( p, "fovmax");		if( s!=null )	{ double x = Double.valueOf(s).doubleValue();
																if( x <= HFOV_MAX ) hfov_max = x > (yaw_max - yaw_min) ? yaw_max - yaw_min : x; }
		s = myGetParameter( p, "fovmin");		if( s!=null )	hfov_min = Double.valueOf(s).doubleValue();
		s = myGetParameter( p, "fov");			if( s!=null )	{ double x = Double.valueOf(s).doubleValue();
																if( x <= hfov_max && x >= hfov_min ) hfov = x; }
		s = myGetParameter( p, "wait");			if( s!=null )	{ dwait = null; dwait = loadImage( s ); update(getGraphics()); }
		s = myGetParameter( p, "auto");			if( s!=null )	autopan =  Double.valueOf(s).doubleValue();
		s = myGetParameter( p, "mousehs"); 		if( s!=null )	MouseOverHS = s;
		s = myGetParameter( p, "getview"); 		if( s!=null )	GetView = s;
		s = myGetParameter( p, "frame");		if( s!=null )	{ frame = null; frame = loadImage( s );}
		s = myGetParameter( p, "waittime");		if( s!=null )	waittime = Integer.parseInt(s);
		s = myGetParameter( p, "hsimage");		if( s!=null )	hs_image = s;
		s = myGetParameter( p, "pwidth");		if( s!=null )	pwidth  = Integer.parseInt(s); 
		s = myGetParameter( p, "pheight");		if( s!=null )	pheight = Integer.parseInt(s);
		s = myGetParameter( p, "loadAllRoi");	if( s!=null )	{ if( s.equalsIgnoreCase("false") ) loadAllRoi = false; }
		s = myGetParameter( p, "file"); 		if( s!=null ) 	filename = s;
		s = myGetParameter( p, "order");		if( s!=null )	order = s;
		
		for(i=0; i<=hotspots.size(); i++){
			s=myGetParameter( p, "hotspot" + i);
			if( s != null){
				if(i<hotspots.size()){ // Discard hotspots
					hotspots.setSize(i);
				}
				hotspots.addElement(s);
			}
		}
		
		numroi = 0;
		for( k=0; myGetParameter( p, "roi" + k)!=null; k++) ;
		if( k > 0){
			roi_allocate(k);
			for(i=0; i<numroi; i++){
				s = myGetParameter( p, "roi" + i);
				if( s!=null )
					ParseROILine( s, i );
			}
		}

		for(i=0; i<=shotspots.size(); i++){
			s=myGetParameter( p, "shotspot" + i);
			if( s != null){
				if(i<shotspots.size()){ // Discard static hotspots
					shotspots.setSize(i);
				}
				shotspots.addElement(s);
			}
		}


		for(i=0; i<=sounds.size(); i++){
			s=myGetParameter( p, "sound" + i);
			if( s != null){
				if(i<sounds.size()){ // Discard sounds
					sounds.setSize(i);
				}
				sounds.addElement(s);
			}
		}
		
		for(i=0; i<=app_properties.size(); i++){
			s=myGetParameter( p, "applet" + i);
			if( s != null){
				if(i<app_properties.size()){ // Discard applets
					stopApplets(i);
					app_properties.setSize(i);
				}
				app_properties.addElement(s);
			}
		}

		

	}
	



					
 	void executeJavascriptCommand( String s ){
 		if( s != null ){
 			try{
 				Class c = Class.forName("netscape.javascript.JSObject");
 				Object win = c.getMethod("getWindow", new Class[]{ Applet.class }).invoke( c, new Object[] {this} );
 				c.getMethod("eval", new Class[]{ String.class }).invoke( win, new Object[]{s} );
   			}catch(Exception e){}
 		}
 	}
 
 	void executePTViewerCommand( String s ){
		stopThread(ptviewerScript); 
 		ptviewerScript = new Thread(this );
 		PTScript = s;
 		ptviewerScript.start();
 	}


 	void PTViewerScript( String s ){
 		int n = getNumArgs( s, ';' );

 		if( n > 0 ){
 			int i;
 			for(i=0; i<n; i++){
 				String c = stripWhiteSpace( getArg( i, s, ';' ) );
 				if( c.equals("loop()") ){
 					i = -1;
 				}else{
 					PTViewerCommand( c );
 				}
 			}
 		}
 	}



 	// Parser to read and execute PTViewer commands
 	void PTViewerCommand( String s )
 	{
  		String args		= s.substring( s.indexOf('(')+1, s.indexOf(')') );
  		
		if		( s.startsWith("ZoomIn"))		ZoomIn();
		else if ( s.startsWith("ZoomOut"))		ZoomOut();
		else if ( s.startsWith("panUp"))		panUp();
		else if ( s.startsWith("panDown"))		panDown();
		else if ( s.startsWith("panLeft"))		panLeft();
		else if ( s.startsWith("panRight"))		panRight();
		else if ( s.startsWith("showHS"))		showHS();
		else if ( s.startsWith("hideHS"))		hideHS();
		else if ( s.startsWith("toggleHS"))		toggleHS();
		else if ( s.startsWith("gotoView"))		{
													if( getNumArgs( args ) != 3 ) return;
													gotoView( Double.valueOf(getArg(0, args)).doubleValue(),
														  	  Double.valueOf(getArg(1, args)).doubleValue(),
														  	  Double.valueOf(getArg(2, args)).doubleValue() );
												}
		else if ( s.startsWith("startAutoPan"))	{
													if( getNumArgs( args ) != 3 ) return;
													startAutoPan( Double.valueOf(getArg(0, args)).doubleValue(),
														  	  Double.valueOf(getArg(1, args)).doubleValue(),
														  	  Double.valueOf(getArg(2, args)).doubleValue() );
												}
		else if ( s.startsWith("stopAutoPan"))	stopAutoPan();
		else if ( s.startsWith("newPanoFromList")){
													if( getNumArgs( args ) == 1 )
														newPanoFromList( Integer.parseInt(args) );
													else if( getNumArgs( args ) == 4 ){
														newPanoFromList( Integer.parseInt(getArg(0, args)),
															Double.valueOf(getArg(1, args)).doubleValue(),
															Double.valueOf(getArg(2, args)).doubleValue(),
															Double.valueOf(getArg(3, args)).doubleValue() );
													}
												}
		else if ( s.startsWith("newPano"))	 	 newPano( args );
		else if ( s.startsWith("SetURL"))	 	 SetURL( args );
		else if ( s.startsWith("PlaySound"))	 PlaySound( Integer.parseInt(args) );
		else if ( s.startsWith("moveFromTo")){
													if( getNumArgs( args ) != 7 ) return;
													moveFromTo( Double.valueOf(getArg(0, args)).doubleValue(),
														  	  Double.valueOf(getArg(1, args)).doubleValue(),
														  	  Double.valueOf(getArg(2, args)).doubleValue(),
														  	  Double.valueOf(getArg(3, args)).doubleValue(),
														  	  Double.valueOf(getArg(4, args)).doubleValue(),
														  	  Double.valueOf(getArg(5, args)).doubleValue(),
														  	  Integer.valueOf(getArg(6, args)).intValue() );
											}
		else if ( s.startsWith("moveTo"))	{												
													if( getNumArgs( args ) != 4 ) return;
													moveTo(   Double.valueOf(getArg(0, args)).doubleValue(),
														  	  Double.valueOf(getArg(1, args)).doubleValue(),
														  	  Double.valueOf(getArg(2, args)).doubleValue(),
														  	  Integer.valueOf(getArg(3, args)).intValue() );
											}
		else if ( s.startsWith("DrawSHSImage"))	 DrawSHSImage( Integer.parseInt(args) );
		else if ( s.startsWith("HideSHSImage"))	 HideSHSImage( Integer.parseInt(args) );
		else if ( s.startsWith("DrawHSImage"))	 DrawHSImage( Integer.parseInt(args) );
		else if ( s.startsWith("HideHSImage"))	 HideHSImage( Integer.parseInt(args) );
		else if ( s.startsWith("ToggleHSImage")) ToggleHSImage( Integer.parseInt(args) );
		else if ( s.startsWith("ToggleSHSImage"))ToggleSHSImage( Integer.parseInt(args) );
		else if ( s.startsWith("waitWhilePanning"))	 waitWhilePanning();
		else if ( s.startsWith("startApplet"))	 startApplet( Integer.parseInt(args) );
		else if ( s.startsWith("stopApplet"))	 stopApplet( Integer.parseInt(args) );
		else if ( s.startsWith("loadROI"))		 if(getNumArgs( args ) == 2 ) {
													loadROI( Integer.valueOf(getArg(0, args)).intValue(),
															 Integer.valueOf(getArg(1, args)).intValue() );
												 }else{	 
													loadROI( Integer.parseInt(args) );
 												 } 
 	}
 
	
	
	/** Draw static hotspot image
    * @param n The list number of the static hotspot
	*/
	public synchronized void DrawSHSImage( int n )
	{
		if( n >= 0 && n < numshs && shs_imode[n] != IMODE_ALWAYS){
			shs_imode[n] = IMODE_ALWAYS;
			repaint();
		}
	}
	
	/** Hide static hotspot image
    * @param n The list number of the static hotspot
	*/
	public synchronized void HideSHSImage( int n )
	{
		if( n >= 0 && n < numshs && shs_imode[n] != IMODE_NORMAL){
			shs_imode[n] = IMODE_NORMAL;
			repaint();
		}
	}

	/** Toggle visibility of static hotspot image
    * @param n The list number of the static hotspot
	*/
	public synchronized void ToggleSHSImage( int n ){
		if( n >= 0 && n < numshs ){
			if( shs_imode[n] != IMODE_NORMAL )
				HideSHSImage( n );
			else if( shs_imode[n] != IMODE_ALWAYS ) 
				DrawSHSImage( n );
		}
	}
		


	/** Draw hotspot image
    * @param n The list number of the hotspot
	*/
	public synchronized void DrawHSImage( int n ){
		if( n >= 0 && n < numhs && (hs_imode[n] & IMODE_ALWAYS) == 0){
			hs_imode[n] |= IMODE_ALWAYS;
			repaint();
		}
	}
	
	/** Hide hotspot image
    * @param n The list number of the hotspot
	*/
	public synchronized void HideHSImage( int n ){
		if( n >= 0 && n < numhs && (hs_imode[n] & IMODE_ALWAYS) != 0){
			hs_imode[n] &= ~IMODE_ALWAYS;
			repaint();
		}
	}
	
	/** Toggle visibility of hotspot image
    * @param n The list number of the hotspot
	*/
	public synchronized void ToggleHSImage( int n ){
		if( n >= 0 && n < numhs ){
			if((hs_imode[n] & IMODE_ALWAYS) != 0)
				 HideHSImage( n );
			else if( (hs_imode[n] & IMODE_ALWAYS) == 0)
				 DrawHSImage( n );
		}
	}
		
	
	/** The current horizontal and relative mouse coordinates 
	* in the panoramic image. 0 - left, 100 - right.
	*/
	public double get_x() { 
	   	int[] cp;
	   	double result = -1.0;
	   	
	   	if( click_x >= 0 && click_y >= 0 ){
	   		cp = math_int_view2pano( click_x - vx, click_y - vy, vwidth, vheight,
                            	 pwidth, pheight,
                            	 yaw, pitch, hfov);
			result =  cp[0]*100.0/(double)pwidth;
		}
		cp = null;
		
		return result;
	}

	/** The current vertical and relative mouse coordinates 
	* in the panoramic image. 0 - top, 100 - bottom.
	*/
	public double get_y() {
	   	int[] cp;
	   	double result = -1.0;
	   	
	   	if( click_x >= 0 && click_y >= 0 ){
	   		cp = math_int_view2pano( click_x - vx, click_y - vy, vwidth, vheight,
                            	 pwidth, pheight,
                            	 yaw, pitch, hfov);
			result =  cp[1]*100.0/(double)pheight;
		}

		cp = null;
		click_x = -1; click_y = -1;
		return result;
	}

	/** The list number of the current panoramic image 	*/
	public int getPanoNumber() { return CurrentPano; }
	
  	
	
	/** Specify an applet to communicate with.
	* This applet's <CODE>paint()</CODE> method will
	* be called whenever the view changes.
    * @param a the applet.
 	*/
 	public  void startCommunicating(Applet a){
  		synchronized(sender){
 			if( a!= null )
 				sender.put( a, a );
			else
				sender.clear();
		}
		dirty = true;
		repaint();
	}
	
	/** Stop communicationg with applet.
    * @param a the applet.
 	*/
 	public void stopCommunicating(Applet a){
 		if( a!= null ){
 			synchronized(sender){
 				sender.remove( a );
 			}
 			dirty = true;
			repaint();
		}
	}


	// Get path to document, but without leading A:/C: etc.
	
	private String ptgetPath(){
		String s = getDocumentBase().getFile();
		int i = s.indexOf(':');
		if(  i != -1  && i+1 < s.length()){
			return s.substring(i+1);
		}
		i = s.indexOf('|');
		if(  i != -1  && i+1 < s.length()){
			return s.substring(i+1);
		}
		
		return s;
	}
	
	void stopThread( Thread t ){
		if(t != null && t.isAlive()){
			try{
				t.checkAccess();
				t.stop();
			}catch( SecurityException  e ){
				t.destroy();
			}
		}
	}

void ptinsertImage( int[][]pd, int xi, int yi, Image im, int ntiles ){
	if( im == null ) return;
	int w = im.getWidth(null), h = im.getHeight(null);
	if(ntiles > h) ntiles = h;
	int ht = (h + ntiles -1) / ntiles;

	int[] idata = new int[ w * ht  ];
	int i, sheight;
	PixelGrabber pg;
	for(i=0; i<ntiles; i++){
			
		sheight = (ht + i * ht > h ? h - i*ht : ht);

		pg = new PixelGrabber( im, 0, i * ht, 
								   w, sheight, idata, 0, w );
        try { pg.grabPixels(); } 
        catch (InterruptedException e) { return; }
			
		im_insertRect( pd, xi, yi + i * ht, idata, w, 0, 0, w, sheight );
		dirty = true;
		repaint();
	}
	idata = null;
}  	
	

boolean is_inside_viewer(int x, int y){
	if( x>= vx && y >= vy && x < vx + vwidth && y < vy + vheight )
		return true;
	return false;
} 	
 	
	




Back