view java/src/uk/ac/qmul/eecs/ccmi/haptics/FalconHaptics.java @ 1:e3935c01cde2 tip

moved license of PdPersistenceManager to the beginning of the file
author Fiore Martin <f.martin@qmul.ac.uk>
date Tue, 08 Jul 2014 19:52:03 +0100
parents 78b7fc5391a2
children
line wrap: on
line source
/*  
 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
  
 Copyright (C) 2011  Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)

 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
*/  


package uk.ac.qmul.eecs.ccmi.haptics;

import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.geom.Line2D;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.ListIterator;

import uk.ac.qmul.eecs.ccmi.utils.OsDetector;
import uk.ac.qmul.eecs.ccmi.utils.PreferencesService;
import uk.ac.qmul.eecs.ccmi.utils.ResourceFileWriter;

class FalconHaptics extends Thread implements Haptics {
	static Haptics createInstance(HapticListenerThread listener) {
		if(OsDetector.has64BitJVM()){// no 64 native library supported yet
			return null;
		}
		
		if(OsDetector.isWindows()){
			/* create a directory for the dll distributed with HAPI library */
			String libDir = PreferencesService.getInstance().get("dir.libs", System.getProperty("java.io.tmpdir"));
			File hapiDir = new File(libDir,"HAPI");
			hapiDir.mkdir();
			
			/* try to load .dll's. First copy it in the home/ccmi_editor_data/lib directory  */
			String[] dlls = {"HAPI/pthreadVC2.dll","HAPI/FreeImage.dll","HAPI/freeglut.dll",
					"HAPI/H3DUtil_vc9.dll","HAPI/HAPI_vc9.dll","FalconHaptics.dll"}; 
			ResourceFileWriter fileWriter = new ResourceFileWriter();
			for(String dll : dlls){
				URL url = OmniHaptics.class.getResource(dll);
				fileWriter.setResource(url);
				fileWriter.writeOnDisk(libDir, dll);
				String path = fileWriter.getFilePath();
				try{
					if(path == null)
						throw new UnsatisfiedLinkError(dll+" missing");
					System.load( path );
				}catch(UnsatisfiedLinkError e){
					System.err.println(e.getMessage());
					e.printStackTrace();
					return null; 
				}
			}
		}else{
			return null;
		}
		
		FalconHaptics falcon = new FalconHaptics("FalconHaptics");
		falcon.hapticListener = listener;
		/* start up the listener which immediately stops, waiting for commands */
		if(!falcon.hapticListener.isAlive())
			falcon.hapticListener.start();
		/* start up the haptics thread which issues commands from the java to the c++ thread */
		falcon.start();
		synchronized(falcon){
			try {
				falcon.wait();
			}catch (InterruptedException ie) {
				throw new RuntimeException(ie); // must never happen 
			}
		}
		if(falcon.initFailed)
			return null;
		else 
			return falcon;
	}
	
	private FalconHaptics(String threadName){
		super(threadName);
		
		nodes = new HashMap<String,ArrayList<Node>>();
		edges = new HashMap<String,ArrayList<Edge>>();
		currentNodes = Empties.EMPTY_NODE_LIST;
		currentEdges = Empties.EMPTY_EDGE_LIST;
		
		attractTo = 0;
	}
	
	private native int initFalcon(int width, int height) throws IOException ;
	
	@Override
	public void addNewDiagram(String diagramName) {
		ArrayList<Node> cNodes = new ArrayList<Node>(30);
		ArrayList<Edge> cEdges = new ArrayList<Edge>(30);
		nodes.put(diagramName, cNodes);
		edges.put(diagramName, cEdges);
		
		synchronized(this){
			currentNodes = cNodes;
			currentEdges = cEdges;
			collectionsChanged = true;
		}
	}

	@Override
	public synchronized void switchDiagram(String diagramName) {
		if(!nodes.containsKey(diagramName))
			throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramName);
		
		currentNodes = nodes.get(diagramName);
		currentEdges = edges.get(diagramName);
		collectionsChanged = true;
	}

	@Override
	public synchronized void removeDiagram(String diagramNameToRemove,
			String diagramNameOfNext) {
		if(!nodes.containsKey(diagramNameToRemove))
			throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramNameToRemove);
		
		nodes.remove(diagramNameToRemove);
		edges.remove(diagramNameToRemove);
		
		if(diagramNameOfNext == null){
			currentNodes = Empties.EMPTY_NODE_LIST;	
			currentEdges = Empties.EMPTY_EDGE_LIST;
		}else {
			if(!nodes.containsKey(diagramNameOfNext))
				throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramNameOfNext);
			currentNodes = nodes.get(diagramNameOfNext);
			currentEdges = edges.get(diagramNameOfNext);
		}
		collectionsChanged = true;
	}

	@Override
	public synchronized void addNode(double x, double y, int nodeHashCode, String diagramName) {
		Node n = new Node(x,y,nodeHashCode, nodeHashCode);
		if(diagramName == null){
			currentNodes.add(n);
		}else{
			nodes.get(diagramName).add(n);
		}
		collectionsChanged = true;
	}

	@Override
	public synchronized void removeNode(int nodeHashCode, String diagramName) {
		ListIterator<Node> itr = (diagramName == null) ? currentNodes.listIterator() : nodes.get(diagramName).listIterator();
		while(itr.hasNext()){
			Node n = itr.next();
			if(n.diagramId == nodeHashCode){
				itr.remove();
				collectionsChanged = true;
				break;
			}
		}
	}

	@Override
	public synchronized void moveNode(double x, double y, int nodeHashCode,
			String diagramName) {
		ArrayList<Node> iterationList = (diagramName == null) ? currentNodes : nodes.get(diagramName); 
		for(Node n : iterationList){
			if(n.diagramId == nodeHashCode){
				n.x = x;
				n.y = y;
				collectionsChanged = true;
				break;
			}
		}
	}

	@Override
	public synchronized void addEdge(int edgeHashCode, double[] xs, double[] ys,
			BitSet[] adjMatrix, int nodeStart, int stipplePattern,
			Line2D attractLine, String diagramName) {
		/* find the mid point of the line of attraction */
		double pX = Math.min(attractLine.getX1(), attractLine.getX2());
		double pY = Math.min(attractLine.getY1(), attractLine.getY2());
		pX += Math.abs(attractLine.getX1() -  attractLine.getX2())/2;
		pY += Math.abs(attractLine.getY1() -  attractLine.getY2())/2;
		
		Edge e = new Edge(edgeHashCode,edgeHashCode, xs, ys, adjMatrix, nodeStart, stipplePattern, pX, pY);
		/* add the edge reference to the edges list */
		if(diagramName == null){
			currentEdges.add(e);
		}else{
			edges.get(diagramName).add(e);
		}
		collectionsChanged = true;
	}

	@Override
	public synchronized void updateEdge(int edgeHashCode, double[] xs, double[] ys,
			BitSet[] adjMatrix, int nodeStart, Line2D attractLine,
			String diagramName) {
		
		for(Edge e : currentEdges){
			if(e.diagramId == edgeHashCode){
				e.xs = xs;
				e.ys = ys;
				e.size = xs.length;
				e.adjMatrix = adjMatrix;
				e.nodeStart = nodeStart;
				// find the mid point of the line of attraction
				double pX = Math.min(attractLine.getX1(), attractLine.getX2());
				double pY = Math.min(attractLine.getY1(), attractLine.getY2());
				pX += Math.abs(attractLine.getX1() -  attractLine.getX2())/2;
				pY += Math.abs(attractLine.getY1() -  attractLine.getY2())/2;
				e.attractPointX = pX;
				e.attractPointY = pY;
			}
		}
		collectionsChanged = true;
	}

	@Override
	public synchronized void removeEdge(int edgeHashCode, String diagramName) {
		ListIterator<Edge> itr = (diagramName == null) ? currentEdges.listIterator() : edges.get(diagramName).listIterator();
		while(itr.hasNext()){
			Edge e = itr.next();
			if(e.diagramId == edgeHashCode){
				itr.remove();
				collectionsChanged = true;
				break;
			}
		}
		collectionsChanged = true;
	}

	@Override
	public synchronized void attractTo(int elementHashCode) {
		attractTo = elementHashCode;
	}

	@Override
	public synchronized void pickUp(int elementHashCode) {
		pickUp = true;
	}

	@Override
	public void setVisible(boolean visible) {
		// falcon haptics window cannot be made invisible 
	}

	@Override
	public synchronized void dispose(){
		shutdown = true;
		/* wait for the haptic thread to shut down */
		try {
			wait();
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
	}
	
	@Override
	public void run() {
		/* get the screen size which will be passed to init methos in order to set up a window
		 * for the haptic with the same size as the swing one
		 */
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

		int screenWidth = (int)screenSize.getWidth();
		int screenHeight = (int)screenSize.getHeight();
		currentNodes.size();
		try {
			initFalcon(screenWidth * 5 / 8,screenHeight * 5 / 8);
		} catch (IOException e) {
			throw new RuntimeException();// OMNI haptic device doesn't cause any exception
		}
	}
	
	/* the diagram currently selected */
	private ArrayList<Node> currentNodes;
	private ArrayList<Edge> currentEdges;

	/* maps with all the diagrams in the editor */
	private HashMap<String,ArrayList<Node>> nodes;
	private HashMap<String,ArrayList<Edge>> edges;
	
	private HapticListenerThread hapticListener;
	private boolean initFailed;
	private boolean collectionsChanged;
	private boolean pickUp;
	private int attractTo;
	private boolean shutdown;
}