annotate java/src/uk/ac/qmul/eecs/ccmi/haptics/FalconHaptics.java @ 8:ea7885bd9bff tip

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