annotate 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
rev   line source
f@0 1 /*
f@0 2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
f@0 3
f@0 4 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
f@0 5
f@0 6 This program is free software: you can redistribute it and/or modify
f@0 7 it under the terms of the GNU General Public License as published by
f@0 8 the Free Software Foundation, either version 3 of the License, or
f@0 9 (at your option) any later version.
f@0 10
f@0 11 This program is distributed in the hope that it will be useful,
f@0 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
f@0 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f@0 14 GNU General Public License for more details.
f@0 15
f@0 16 You should have received a copy of the GNU General Public License
f@0 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
f@0 18 */
f@0 19
f@0 20
f@0 21 package uk.ac.qmul.eecs.ccmi.haptics;
f@0 22
f@0 23 import java.awt.Dimension;
f@0 24 import java.awt.Toolkit;
f@0 25 import java.awt.geom.Line2D;
f@0 26 import java.io.File;
f@0 27 import java.io.IOException;
f@0 28 import java.net.URL;
f@0 29 import java.util.ArrayList;
f@0 30 import java.util.BitSet;
f@0 31 import java.util.HashMap;
f@0 32 import java.util.ListIterator;
f@0 33
f@0 34 import uk.ac.qmul.eecs.ccmi.utils.OsDetector;
f@0 35 import uk.ac.qmul.eecs.ccmi.utils.PreferencesService;
f@0 36 import uk.ac.qmul.eecs.ccmi.utils.ResourceFileWriter;
f@0 37
f@0 38 class FalconHaptics extends Thread implements Haptics {
f@0 39 static Haptics createInstance(HapticListenerThread listener) {
f@0 40 if(OsDetector.has64BitJVM()){// no 64 native library supported yet
f@0 41 return null;
f@0 42 }
f@0 43
f@0 44 if(OsDetector.isWindows()){
f@0 45 /* create a directory for the dll distributed with HAPI library */
f@0 46 String libDir = PreferencesService.getInstance().get("dir.libs", System.getProperty("java.io.tmpdir"));
f@0 47 File hapiDir = new File(libDir,"HAPI");
f@0 48 hapiDir.mkdir();
f@0 49
f@0 50 /* try to load .dll's. First copy it in the home/ccmi_editor_data/lib directory */
f@0 51 String[] dlls = {"HAPI/pthreadVC2.dll","HAPI/FreeImage.dll","HAPI/freeglut.dll",
f@0 52 "HAPI/H3DUtil_vc9.dll","HAPI/HAPI_vc9.dll","FalconHaptics.dll"};
f@0 53 ResourceFileWriter fileWriter = new ResourceFileWriter();
f@0 54 for(String dll : dlls){
f@0 55 URL url = OmniHaptics.class.getResource(dll);
f@0 56 fileWriter.setResource(url);
f@0 57 fileWriter.writeOnDisk(libDir, dll);
f@0 58 String path = fileWriter.getFilePath();
f@0 59 try{
f@0 60 if(path == null)
f@0 61 throw new UnsatisfiedLinkError(dll+" missing");
f@0 62 System.load( path );
f@0 63 }catch(UnsatisfiedLinkError e){
f@0 64 System.err.println(e.getMessage());
f@0 65 e.printStackTrace();
f@0 66 return null;
f@0 67 }
f@0 68 }
f@0 69 }else{
f@0 70 return null;
f@0 71 }
f@0 72
f@0 73 FalconHaptics falcon = new FalconHaptics("FalconHaptics");
f@0 74 falcon.hapticListener = listener;
f@0 75 /* start up the listener which immediately stops, waiting for commands */
f@0 76 if(!falcon.hapticListener.isAlive())
f@0 77 falcon.hapticListener.start();
f@0 78 /* start up the haptics thread which issues commands from the java to the c++ thread */
f@0 79 falcon.start();
f@0 80 synchronized(falcon){
f@0 81 try {
f@0 82 falcon.wait();
f@0 83 }catch (InterruptedException ie) {
f@0 84 throw new RuntimeException(ie); // must never happen
f@0 85 }
f@0 86 }
f@0 87 if(falcon.initFailed)
f@0 88 return null;
f@0 89 else
f@0 90 return falcon;
f@0 91 }
f@0 92
f@0 93 private FalconHaptics(String threadName){
f@0 94 super(threadName);
f@0 95
f@0 96 nodes = new HashMap<String,ArrayList<Node>>();
f@0 97 edges = new HashMap<String,ArrayList<Edge>>();
f@0 98 currentNodes = Empties.EMPTY_NODE_LIST;
f@0 99 currentEdges = Empties.EMPTY_EDGE_LIST;
f@0 100
f@0 101 attractTo = 0;
f@0 102 }
f@0 103
f@0 104 private native int initFalcon(int width, int height) throws IOException ;
f@0 105
f@0 106 @Override
f@0 107 public void addNewDiagram(String diagramName) {
f@0 108 ArrayList<Node> cNodes = new ArrayList<Node>(30);
f@0 109 ArrayList<Edge> cEdges = new ArrayList<Edge>(30);
f@0 110 nodes.put(diagramName, cNodes);
f@0 111 edges.put(diagramName, cEdges);
f@0 112
f@0 113 synchronized(this){
f@0 114 currentNodes = cNodes;
f@0 115 currentEdges = cEdges;
f@0 116 collectionsChanged = true;
f@0 117 }
f@0 118 }
f@0 119
f@0 120 @Override
f@0 121 public synchronized void switchDiagram(String diagramName) {
f@0 122 if(!nodes.containsKey(diagramName))
f@0 123 throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramName);
f@0 124
f@0 125 currentNodes = nodes.get(diagramName);
f@0 126 currentEdges = edges.get(diagramName);
f@0 127 collectionsChanged = true;
f@0 128 }
f@0 129
f@0 130 @Override
f@0 131 public synchronized void removeDiagram(String diagramNameToRemove,
f@0 132 String diagramNameOfNext) {
f@0 133 if(!nodes.containsKey(diagramNameToRemove))
f@0 134 throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramNameToRemove);
f@0 135
f@0 136 nodes.remove(diagramNameToRemove);
f@0 137 edges.remove(diagramNameToRemove);
f@0 138
f@0 139 if(diagramNameOfNext == null){
f@0 140 currentNodes = Empties.EMPTY_NODE_LIST;
f@0 141 currentEdges = Empties.EMPTY_EDGE_LIST;
f@0 142 }else {
f@0 143 if(!nodes.containsKey(diagramNameOfNext))
f@0 144 throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramNameOfNext);
f@0 145 currentNodes = nodes.get(diagramNameOfNext);
f@0 146 currentEdges = edges.get(diagramNameOfNext);
f@0 147 }
f@0 148 collectionsChanged = true;
f@0 149 }
f@0 150
f@0 151 @Override
f@0 152 public synchronized void addNode(double x, double y, int nodeHashCode, String diagramName) {
f@0 153 Node n = new Node(x,y,nodeHashCode, nodeHashCode);
f@0 154 if(diagramName == null){
f@0 155 currentNodes.add(n);
f@0 156 }else{
f@0 157 nodes.get(diagramName).add(n);
f@0 158 }
f@0 159 collectionsChanged = true;
f@0 160 }
f@0 161
f@0 162 @Override
f@0 163 public synchronized void removeNode(int nodeHashCode, String diagramName) {
f@0 164 ListIterator<Node> itr = (diagramName == null) ? currentNodes.listIterator() : nodes.get(diagramName).listIterator();
f@0 165 while(itr.hasNext()){
f@0 166 Node n = itr.next();
f@0 167 if(n.diagramId == nodeHashCode){
f@0 168 itr.remove();
f@0 169 collectionsChanged = true;
f@0 170 break;
f@0 171 }
f@0 172 }
f@0 173 }
f@0 174
f@0 175 @Override
f@0 176 public synchronized void moveNode(double x, double y, int nodeHashCode,
f@0 177 String diagramName) {
f@0 178 ArrayList<Node> iterationList = (diagramName == null) ? currentNodes : nodes.get(diagramName);
f@0 179 for(Node n : iterationList){
f@0 180 if(n.diagramId == nodeHashCode){
f@0 181 n.x = x;
f@0 182 n.y = y;
f@0 183 collectionsChanged = true;
f@0 184 break;
f@0 185 }
f@0 186 }
f@0 187 }
f@0 188
f@0 189 @Override
f@0 190 public synchronized void addEdge(int edgeHashCode, double[] xs, double[] ys,
f@0 191 BitSet[] adjMatrix, int nodeStart, int stipplePattern,
f@0 192 Line2D attractLine, String diagramName) {
f@0 193 /* find the mid point of the line of attraction */
f@0 194 double pX = Math.min(attractLine.getX1(), attractLine.getX2());
f@0 195 double pY = Math.min(attractLine.getY1(), attractLine.getY2());
f@0 196 pX += Math.abs(attractLine.getX1() - attractLine.getX2())/2;
f@0 197 pY += Math.abs(attractLine.getY1() - attractLine.getY2())/2;
f@0 198
f@0 199 Edge e = new Edge(edgeHashCode,edgeHashCode, xs, ys, adjMatrix, nodeStart, stipplePattern, pX, pY);
f@0 200 /* add the edge reference to the edges list */
f@0 201 if(diagramName == null){
f@0 202 currentEdges.add(e);
f@0 203 }else{
f@0 204 edges.get(diagramName).add(e);
f@0 205 }
f@0 206 collectionsChanged = true;
f@0 207 }
f@0 208
f@0 209 @Override
f@0 210 public synchronized void updateEdge(int edgeHashCode, double[] xs, double[] ys,
f@0 211 BitSet[] adjMatrix, int nodeStart, Line2D attractLine,
f@0 212 String diagramName) {
f@0 213
f@0 214 for(Edge e : currentEdges){
f@0 215 if(e.diagramId == edgeHashCode){
f@0 216 e.xs = xs;
f@0 217 e.ys = ys;
f@0 218 e.size = xs.length;
f@0 219 e.adjMatrix = adjMatrix;
f@0 220 e.nodeStart = nodeStart;
f@0 221 // find the mid point of the line of attraction
f@0 222 double pX = Math.min(attractLine.getX1(), attractLine.getX2());
f@0 223 double pY = Math.min(attractLine.getY1(), attractLine.getY2());
f@0 224 pX += Math.abs(attractLine.getX1() - attractLine.getX2())/2;
f@0 225 pY += Math.abs(attractLine.getY1() - attractLine.getY2())/2;
f@0 226 e.attractPointX = pX;
f@0 227 e.attractPointY = pY;
f@0 228 }
f@0 229 }
f@0 230 collectionsChanged = true;
f@0 231 }
f@0 232
f@0 233 @Override
f@0 234 public synchronized void removeEdge(int edgeHashCode, String diagramName) {
f@0 235 ListIterator<Edge> itr = (diagramName == null) ? currentEdges.listIterator() : edges.get(diagramName).listIterator();
f@0 236 while(itr.hasNext()){
f@0 237 Edge e = itr.next();
f@0 238 if(e.diagramId == edgeHashCode){
f@0 239 itr.remove();
f@0 240 collectionsChanged = true;
f@0 241 break;
f@0 242 }
f@0 243 }
f@0 244 collectionsChanged = true;
f@0 245 }
f@0 246
f@0 247 @Override
f@0 248 public synchronized void attractTo(int elementHashCode) {
f@0 249 attractTo = elementHashCode;
f@0 250 }
f@0 251
f@0 252 @Override
f@0 253 public synchronized void pickUp(int elementHashCode) {
f@0 254 pickUp = true;
f@0 255 }
f@0 256
f@0 257 @Override
f@0 258 public void setVisible(boolean visible) {
f@0 259 // falcon haptics window cannot be made invisible
f@0 260 }
f@0 261
f@0 262 @Override
f@0 263 public synchronized void dispose(){
f@0 264 shutdown = true;
f@0 265 /* wait for the haptic thread to shut down */
f@0 266 try {
f@0 267 wait();
f@0 268 } catch (InterruptedException e) {
f@0 269 throw new RuntimeException(e);
f@0 270 }
f@0 271 }
f@0 272
f@0 273 @Override
f@0 274 public void run() {
f@0 275 /* get the screen size which will be passed to init methos in order to set up a window
f@0 276 * for the haptic with the same size as the swing one
f@0 277 */
f@0 278 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
f@0 279
f@0 280 int screenWidth = (int)screenSize.getWidth();
f@0 281 int screenHeight = (int)screenSize.getHeight();
f@0 282 currentNodes.size();
f@0 283 try {
f@0 284 initFalcon(screenWidth * 5 / 8,screenHeight * 5 / 8);
f@0 285 } catch (IOException e) {
f@0 286 throw new RuntimeException();// OMNI haptic device doesn't cause any exception
f@0 287 }
f@0 288 }
f@0 289
f@0 290 /* the diagram currently selected */
f@0 291 private ArrayList<Node> currentNodes;
f@0 292 private ArrayList<Edge> currentEdges;
f@0 293
f@0 294 /* maps with all the diagrams in the editor */
f@0 295 private HashMap<String,ArrayList<Node>> nodes;
f@0 296 private HashMap<String,ArrayList<Edge>> edges;
f@0 297
f@0 298 private HapticListenerThread hapticListener;
f@0 299 private boolean initFailed;
f@0 300 private boolean collectionsChanged;
f@0 301 private boolean pickUp;
f@0 302 private int attractTo;
f@0 303 private boolean shutdown;
f@0 304 }