~seh999/jcog/proto3

« back to all changes in this revision

Viewing changes to spacetime/src/opencog/spacetime/control/ToolSystem.java

  • Committer: SeH
  • Date: 2009-09-19 22:59:48 UTC
  • Revision ID: seh999@gmail.com-20090919225948-q3ab80xa57i74mm6
start of major jReality refactoring

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 *
 
3
 * This file is part of jReality. jReality is open source software, made
 
4
 * available under a BSD license:
 
5
 *
 
6
 * Copyright (c) 2003-2006, jReality Group: Charles Gunn, Tim Hoffmann, Markus
 
7
 * Schmies, Steffen Weissmann.
 
8
 *
 
9
 * All rights reserved.
 
10
 *
 
11
 * Redistribution and use in source and binary forms, with or without
 
12
 * modification, are permitted provided that the following conditions are met:
 
13
 *
 
14
 * - Redistributions of source code must retain the above copyright notice, this
 
15
 *   list of conditions and the following disclaimer.
 
16
 *
 
17
 * - Redistributions in binary form must reproduce the above copyright notice,
 
18
 *   this list of conditions and the following disclaimer in the documentation
 
19
 *   and/or other materials provided with the distribution.
 
20
 *
 
21
 * - Neither the name of jReality nor the names of its contributors nor the
 
22
 *   names of their associated organizations may be used to endorse or promote
 
23
 *   products derived from this software without specific prior written
 
24
 *   permission.
 
25
 *
 
26
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
27
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
28
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
29
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 
30
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
31
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
32
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
33
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
34
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
35
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
36
 * POSSIBILITY OF SUCH DAMAGE.
 
37
 *
 
38
 */
 
39
 
 
40
 
 
41
package opencog.spacetime.control;
 
42
 
 
43
import java.util.Collection;
 
44
import java.util.Collections;
 
45
import java.util.HashMap;
 
46
import java.util.HashSet;
 
47
import java.util.Iterator;
 
48
import java.util.LinkedList;
 
49
import java.util.List;
 
50
import java.util.Set;
 
51
import java.util.WeakHashMap;
 
52
import java.util.Map.Entry;
 
53
 
 
54
import opencog.math.Rn;
 
55
import opencog.spacetime.control.AxisState;
 
56
import opencog.spacetime.control.InputSlot;
 
57
import opencog.spacetime.control.Tool;
 
58
import opencog.spacetime.control.ToolContext;
 
59
import opencog.spacetime.control.config.ToolSystemConfiguration;
 
60
import opencog.spacetime.control.tool.AnimatorTool;
 
61
import opencog.spacetime.space.Geometry;
 
62
import opencog.spacetime.space.SpaceComponent;
 
63
import opencog.spacetime.space.SpaceNode;
 
64
import opencog.spacetime.space.SpacePath;
 
65
import opencog.spacetime.space.Viewer;
 
66
import opencog.spacetime.space.data.DoubleArray;
 
67
import opencog.spacetime.space.pick.AABBPickSystem;
 
68
import opencog.spacetime.space.pick.PickResult;
 
69
import opencog.spacetime.space.pick.PickSystem;
 
70
import opencog.spacetime.space.pick.PosWHitFilter;
 
71
import opencog.spacetime.util.Input;
 
72
import opencog.spacetime.util.LoggingSystem;
 
73
import opencog.spacetime.util.RenderTrigger;
 
74
import opencog.spacetime.util.Secure;
 
75
import opencog.spacetime.util.SystemProperties;
 
76
 
 
77
 
 
78
/**
 
79
 * 
 
80
 * TODO: document this
 
81
 * 
 
82
 * @author weissman
 
83
 *  
 
84
 */
 
85
public class ToolSystem implements ToolEventReceiver {
 
86
 
 
87
    static WeakHashMap<Viewer, ToolSystem> globalTable = new WeakHashMap<Viewer, ToolSystem>();
 
88
    
 
89
        /**
 
90
         * If <i>v</i> has a tool system already associated to it, return it. Otherwise allocate a default one
 
91
         * @param v
 
92
         * @return
 
93
         */
 
94
    public static ToolSystem toolSystemForViewer(Viewer v)      {
 
95
                synchronized (globalTable) {
 
96
                        ToolSystem sm = (ToolSystem) globalTable.get(v);
 
97
                        if (sm != null) return sm;
 
98
                        LoggingSystem.getLogger(ToolSystem.class).warning("Viewer has no tool system, allocating default");
 
99
                        sm = new ToolSystem(v, null, null);
 
100
                        globalTable.put(v,sm);
 
101
                        return sm;
 
102
                }
 
103
        }
 
104
        
 
105
        /**
 
106
         * This method just looks up and returns the possibly null toolsystem associated to viewer
 
107
         * @param v
 
108
         * @return
 
109
         */
 
110
         public static ToolSystem getToolSystemForViewer(Viewer v)      {
 
111
                 synchronized (globalTable) {
 
112
                         ToolSystem sm = (ToolSystem) globalTable.get(v);
 
113
                         return sm;
 
114
                 }
 
115
        }
 
116
        
 
117
        public static void setToolSystemForViewer(Viewer v, ToolSystem ts)      {
 
118
                synchronized (globalTable) {
 
119
                        ToolSystem sm = (ToolSystem) globalTable.get(v);
 
120
                        if (sm != null) throw new IllegalStateException("Viewer already has tool system "+sm);
 
121
                        globalTable.put(v,ts);
 
122
                }
 
123
        }
 
124
        
 
125
        private static void unsetToolSystem(ToolSystem ts) {
 
126
                synchronized (globalTable) {
 
127
                        for (Iterator<Entry<Viewer, ToolSystem>> it = globalTable.entrySet().iterator(); it.hasNext(); ) {
 
128
                                Entry<Viewer, ToolSystem> e = it.next();
 
129
                                if (e.getValue() == ts) it.remove();
 
130
                        }
 
131
                }
 
132
        }
 
133
        
 
134
        private RenderTrigger renderTrigger;
 
135
 
 
136
        protected final LinkedList<ToolEvent> compQueue = new LinkedList<ToolEvent>();
 
137
 
 
138
        private final LinkedList<ToolEvent> triggerQueue = new LinkedList<ToolEvent>();
 
139
        private final HashMap<Tool, List<SpacePath>> toolToPath = new HashMap<Tool, List<SpacePath>>();
 
140
        private List<PickResult> pickResults = Collections.emptyList();
 
141
        private PosWHitFilter hitFilter;
 
142
 
 
143
        private SpacePath emptyPickPath=new SpacePath();
 
144
 
 
145
        protected Viewer viewer;
 
146
        private ToolContextImpl toolContext;
 
147
        protected DeviceManager deviceManager;
 
148
        private ToolManager toolManager;
 
149
        private SlotManager slotManager;
 
150
        private PickSystem pickSystem;
 
151
        private ToolUpdateProxy updater;
 
152
        private ToolEventQueue eventQueue;
 
153
        ToolSystemConfiguration config;
 
154
        private PickResult pickResult;
 
155
        private static InputSlot pointerSlot = InputSlot.getDevice("PointerTransformation");
 
156
 
 
157
        protected boolean executing;
 
158
 
 
159
        private final Object KEY=new Object();
 
160
        
 
161
        private class ToolContextImpl implements ToolContext {
 
162
 
 
163
                InputSlot sourceSlot;
 
164
                ToolEvent event;
 
165
                private SpacePath rootToLocal;
 
166
                private SpacePath rootToToolComponent;
 
167
                private Tool currentTool;
 
168
 
 
169
                boolean rejected;
 
170
 
 
171
                public Viewer getViewer() {
 
172
                        return viewer;
 
173
                }
 
174
 
 
175
                public InputSlot getSource() {
 
176
                        return event.getInputSlot();
 
177
                }
 
178
 
 
179
                public DoubleArray getTransformationMatrix(InputSlot slot) {
 
180
                        return deviceManager.getTransformationMatrix(slot);
 
181
                }
 
182
 
 
183
                public AxisState getAxisState(InputSlot slot) {
 
184
                        return deviceManager.getAxisState(slot);
 
185
                }
 
186
 
 
187
                public long getTime() {
 
188
                        return event.getTimeStamp();
 
189
                }
 
190
 
 
191
                private void setRootToLocal(SpacePath rootToLocal) {
 
192
                        this.rootToLocal = rootToLocal;
 
193
                        rootToToolComponent = null;
 
194
                }
 
195
 
 
196
                public SpacePath getRootToLocal() {
 
197
                        return rootToLocal;
 
198
                }
 
199
 
 
200
                public SpacePath getRootToToolComponent() {
 
201
                        if (rootToToolComponent == null) {
 
202
                                LinkedList<SpaceNode> list = new LinkedList<SpaceNode>();
 
203
                                for (Iterator<SpaceNode> i = rootToLocal.reverseIterator(); i.hasNext(); ) {
 
204
                                        SpaceNode cp = i.next();
 
205
                                        if (!(cp instanceof SpaceComponent))
 
206
                                                continue;
 
207
                                        if (((SpaceComponent) cp).getTools().contains(currentTool)) {
 
208
                                                list.addFirst(cp);
 
209
                                                while (i.hasNext())
 
210
                                                        list.addFirst((SpaceNode) i.next());
 
211
                                        }
 
212
                                }
 
213
                                rootToToolComponent = SpacePath.fromList(list);
 
214
                        }
 
215
                        return rootToToolComponent;
 
216
                }
 
217
 
 
218
                public PickResult getCurrentPick() {
 
219
                        if (pickResult == null) {
 
220
                                performPick();
 
221
                                pickResult = pickResults.isEmpty() ? null : (PickResult) pickResults.get(0);                            
 
222
                        }
 
223
                        return pickResult;
 
224
                }
 
225
 
 
226
                public List<PickResult> getCurrentPicks() {
 
227
                        if (pickResults == null) {
 
228
                                performPick();
 
229
                        }
 
230
                        return pickResults;
 
231
                }
 
232
 
 
233
                private void setCurrentTool(Tool currentTool) {
 
234
                        this.currentTool = currentTool;
 
235
                }
 
236
 
 
237
                public void reject() {
 
238
                        rejected=true;
 
239
                }
 
240
 
 
241
                boolean isRejected() {
 
242
                        return rejected;
 
243
                }
 
244
 
 
245
                public SpacePath getAvatarPath() {
 
246
                        return ToolSystem.this.getAvatarPath();
 
247
                }
 
248
 
 
249
                public PickSystem getPickSystem() {
 
250
                        return ToolSystem.this.getPickSystem();
 
251
                }
 
252
 
 
253
                public Object getKey() {
 
254
                        return KEY;
 
255
                }
 
256
        };
 
257
 
 
258
        private static ToolSystemConfiguration loadConfiguration() {
 
259
            ToolSystemConfiguration config;
 
260
            try {
 
261
              String toolFile = Secure.getProperty(SystemProperties.TOOL_CONFIG_FILE);
 
262
              config = ToolSystemConfiguration.loadConfiguration(
 
263
                  Input.getInput(toolFile)
 
264
              );
 
265
              LoggingSystem.getLogger(ToolSystem.class).config("Using toolconfig="+toolFile);
 
266
            } catch (Exception e1) {
 
267
              config = ToolSystemConfiguration.loadDefaultConfiguration();
 
268
            }
 
269
            return config;
 
270
          }
 
271
        /**
 
272
         * 
 
273
         * @param viewer the viewer
 
274
         * @param config the config
 
275
         * @param renderTrigger a rendertrigger to synch or null - the ToolSystem does not take care of
 
276
         * setting/removing the triggers viewer and scene root (on initialize/dispose)
 
277
         */
 
278
        public ToolSystem(Viewer viewer, ToolSystemConfiguration config, RenderTrigger renderTrigger) {
 
279
                toolContext = new ToolContextImpl();
 
280
                toolManager = new ToolManager();
 
281
                eventQueue = new ToolEventQueue(this);
 
282
                if (config == null) config = loadConfiguration();
 
283
                this.config = config;
 
284
                this.viewer = viewer;
 
285
                deviceManager = new DeviceManager(config, eventQueue, viewer);
 
286
                slotManager = new SlotManager(config);
 
287
                updater = new ToolUpdateProxy(this);
 
288
                this.renderTrigger = renderTrigger;
 
289
                // this code moved over from the ToolSystemViewer constructor
 
290
            setPickSystem(new AABBPickSystem());
 
291
            // provide a reasonable default empty pick path
 
292
            emptyPickPath = new SpacePath();
 
293
            emptyPickPath.push(viewer.getSceneRoot());
 
294
        }
 
295
 
 
296
        private class MouseOverSupport implements Tool {
 
297
 
 
298
                List<InputSlot> activation = Collections.emptyList(); //Collections.singletonList(InputSlot.getDevice("EnablePointerHit"));
 
299
                List<InputSlot> pointer = Collections.singletonList(InputSlot.getDevice("PointerTransformation"));
 
300
                InputSlot trigger = InputSlot.getDevice("PointerHit");
 
301
                
 
302
                
 
303
                SpacePath rootPath;
 
304
                int useCount=0;
 
305
                
 
306
                void mouseOverToolAdded() {
 
307
                        if (useCount == 0) {
 
308
                                //System.out.println("Enabling mouse over support");
 
309
                                addToolImpl(this, rootPath);
 
310
                        }
 
311
                        useCount++;
 
312
                }
 
313
                
 
314
                void mouseOverToolRemoved() {
 
315
                        useCount--;
 
316
                        if (useCount == 0) {
 
317
                                //System.out.println("Disabling mouse over support");
 
318
                                removeToolImpl(this, rootPath);
 
319
                        }
 
320
                }
 
321
                
 
322
                private MouseOverSupport(SpacePath root) {
 
323
                        rootPath=new SpacePath(root);
 
324
                }
 
325
                
 
326
                public void activate(ToolContext tc) {
 
327
                }
 
328
 
 
329
                public void deactivate(ToolContext tc) {
 
330
                }
 
331
 
 
332
                public List<InputSlot> getActivationSlots() {
 
333
                        return activation;
 
334
                }
 
335
 
 
336
                public List<InputSlot> getCurrentSlots() {
 
337
                        return pointer;
 
338
                }
 
339
 
 
340
                public String getDescription(InputSlot slot) {
 
341
                        return "foo";
 
342
                }
 
343
 
 
344
                public String getDescription() {
 
345
                        return "dummy tool to enable mouse over";
 
346
                }
 
347
                
 
348
                boolean hasHit=false;
 
349
                SpacePath lastPath=null;
 
350
                int ignoreCnt=12;
 
351
                public void perform(ToolContext tc) {
 
352
                        if (ignoreCnt > 0) {
 
353
                                ignoreCnt--;
 
354
                                return;
 
355
                        }
 
356
                        SpacePath newP = null;
 
357
                        boolean hits = false;
 
358
                        PickResult p = tc.getCurrentPick();
 
359
                        if (p!=null && p.getPickPath() != null && p.getPickPath().getLastElement() instanceof Geometry) {
 
360
                                newP = new SpacePath(p.getPickPath());
 
361
                                hits = true;
 
362
                        }
 
363
                        if (!hits) {
 
364
                                if (!hasHit) {
 
365
                                        // nothing to do...
 
366
                                } else {
 
367
                                        hasHit = false;
 
368
                                        lastPath = null;
 
369
                                        fireNoMoreHit();
 
370
                                }
 
371
                        } else {
 
372
                                if (hasHit) {
 
373
                                        if (lastPath.isEqual(newP)) {
 
374
                                                // same hit, nothing to do
 
375
                                        }
 
376
                                        else {
 
377
                                                lastPath = newP;
 
378
                                                // fire hit lost
 
379
                                                fireNoMoreHit();
 
380
                                                // then fire hit again
 
381
                                                fireHit();
 
382
                                        }
 
383
                                } else {
 
384
                                        lastPath = newP;
 
385
                                        hasHit = true;
 
386
                                        fireHit();
 
387
                                }
 
388
                                        
 
389
                        }
 
390
                }
 
391
 
 
392
                private void fireNoMoreHit() {
 
393
                        eventQueue.addEvent(new ToolEvent(this, deviceManager.getSystemTime(), trigger, AxisState.ORIGIN));
 
394
                }
 
395
 
 
396
                private void fireHit() {
 
397
                        eventQueue.addEvent(new ToolEvent(this, deviceManager.getSystemTime(), trigger, AxisState.PRESSED));
 
398
                }
 
399
 
 
400
                @Override
 
401
                public int hashCode() {
 
402
                        return 31;
 
403
                }
 
404
 
 
405
                @Override
 
406
                public boolean equals(Object obj) {
 
407
                        if (this == obj)
 
408
                                return true;
 
409
                        if (obj == null)
 
410
                                return false;
 
411
                        if (getClass() != obj.getClass())
 
412
                                return false;
 
413
                        throw new IllegalStateException("Duplicate MouseOverSupport!");
 
414
                }
 
415
                
 
416
        }
 
417
        
 
418
        private boolean initialized;
 
419
 
 
420
        private MouseOverSupport mouseOverSupport;
 
421
        public void initializeSceneTools() {
 
422
                if (initialized) {
 
423
                        LoggingSystem.getLogger(this).warning("already initialized!");
 
424
                        return;
 
425
                }
 
426
                initialized=true;
 
427
                toolManager.cleanUp();
 
428
                
 
429
                // register animator
 
430
                SpacePath rootPath = new SpacePath();
 
431
                rootPath.push(viewer.getSceneRoot());
 
432
                addTool(AnimatorTool.getInstanceImpl(KEY), rootPath);
 
433
                
 
434
                // enable mouse over support
 
435
                mouseOverSupport = new MouseOverSupport(rootPath);
 
436
                
 
437
                if (emptyPickPath.getLength() == 0) {
 
438
                        emptyPickPath.push(viewer.getSceneRoot());
 
439
                }
 
440
                if (pickSystem != null) {
 
441
                        pickSystem.setSceneRoot(viewer.getSceneRoot());
 
442
                }
 
443
                updater.setSceneRoot(viewer.getSceneRoot());
 
444
                eventQueue.start();
 
445
        }
 
446
 
 
447
        public void processToolEvent(ToolEvent event) {
 
448
                synchronized (mutex) {
 
449
                        if (disposed) return;
 
450
                        executing=true;
 
451
                }
 
452
                compQueue.add(event);
 
453
                int iterCnt=0;
 
454
                do {
 
455
                        iterCnt++;
 
456
                        processComputationalQueue();
 
457
                        processTriggerQueue();
 
458
                        List<ToolEvent> l = deviceManager.updateImplicitDevices();
 
459
                        if (l.isEmpty()) break;
 
460
                        compQueue.addAll(l);
 
461
                        if (iterCnt > 5000) {
 
462
                                //throw new IllegalStateException("recursion in tool system!");
 
463
                                LoggingSystem.getLogger(this).warning("may be stuck in endless loop");
 
464
                                iterCnt = 0;
 
465
                        }
 
466
                } while (true);
 
467
                // handle newly added/removed tools
 
468
                synchronized (mutex) {
 
469
                        if (!toolsChanging.isEmpty()) {
 
470
                                final List<Pair> l = new LinkedList<Pair>(toolsChanging);
 
471
                                toolsChanging.clear();
 
472
                                for (Iterator<Pair> i = l.iterator(); i.hasNext(); ) {
 
473
                                        Pair p = i.next();
 
474
                                        i.remove();
 
475
                                        if (p.added) {
 
476
                                                addToolImpl(p.tool, p.path);
 
477
                                        } else {
 
478
                                                removeToolImpl(p.tool, p.path);
 
479
                                        }
 
480
                                }
 
481
                        }
 
482
                        executing=false;
 
483
                }
 
484
                if (event.getInputSlot() == InputSlot.getDevice("SystemTime")) {
 
485
                        deviceManager.setSystemTime(event.getTimeStamp());
 
486
                        if (renderTrigger != null) {
 
487
                                renderTrigger.finishCollect();
 
488
                                renderTrigger.startCollect();
 
489
                        }
 
490
                }
 
491
        }
 
492
 
 
493
        protected void processComputationalQueue() {
 
494
                while (!compQueue.isEmpty()) {
 
495
                        ToolEvent event = (ToolEvent) compQueue.removeFirst();
 
496
                        deviceManager.evaluateEvent(event, compQueue);
 
497
                        if (isTrigger(event) && !event.isConsumed()) triggerQueue.add(event);
 
498
                }
 
499
        }
 
500
 
 
501
        private boolean isTrigger(ToolEvent event) {
 
502
                InputSlot slot = event.getInputSlot();
 
503
                boolean ret = slotManager.isActiveSlot(slot) || slotManager.isActivationSlot(slot);
 
504
                return ret;
 
505
        }
 
506
 
 
507
        protected void processTriggerQueue() {
 
508
                if (triggerQueue.isEmpty())     return;
 
509
 
 
510
                HashSet<Tool> activatedTools = new HashSet<Tool>();
 
511
                HashSet<Tool> deactivatedTools = new HashSet<Tool>();
 
512
                HashSet<Tool> stillActiveTools = new HashSet<Tool>();
 
513
 
 
514
                SpacePath pickPath = null;
 
515
 
 
516
                for (ToolEvent event : triggerQueue) { //Iterator iter = triggerQueue.iterator(); iter.hasNext();) {
 
517
//                      ToolEvent event = (ToolEvent) iter.next();
 
518
                        toolContext.event = event;
 
519
                        InputSlot slot = event.getInputSlot();
 
520
                        toolContext.sourceSlot = slot;
 
521
                        pickResults = null;
 
522
                        pickResult = null;
 
523
                        
 
524
                        AxisState axis = deviceManager.getAxisState(slot);
 
525
 
 
526
                        boolean noTrigger = true;
 
527
 
 
528
                        if (axis != null && axis.isPressed()) { // possible activation:
 
529
 
 
530
                                Set<Tool> candidatesForPick = new HashSet<Tool>(slotManager.getToolsActivatedBySlot(slot));
 
531
 
 
532
                                Set<Tool> candidates = new HashSet<Tool>();
 
533
 
 
534
                                // TODO: see if activating more than one Tool for an axis
 
535
                                // makes sense...
 
536
                                for (Tool candidate : candidatesForPick) {
 
537
                                        if (!toolManager.needsPick(candidate)) //throw new Error();
 
538
                                                LoggingSystem.getLogger(this).warning("Something wrong with pick candidates\n");
 
539
                                }
 
540
                                if (!candidatesForPick.isEmpty()) {
 
541
                                        // now we need a pick path
 
542
                                        if (pickPath == null)
 
543
                                                pickPath = calculatePickPath();
 
544
                                        int level = pickPath.getLength();
 
545
                                        do {
 
546
                                                Collection<Tool> selection = toolManager.selectToolsForPath(pickPath, level--, candidatesForPick);
 
547
                                                if (selection.isEmpty()) continue;
 
548
                                                LoggingSystem.getLogger(this).finer("selected pick tools:" + selection);
 
549
                                                for (Tool tool : selection)   {
 
550
                                                        registerActivePathForTool(pickPath, tool);
 
551
                                                }
 
552
                                                candidates.addAll(selection);
 
553
                                                // now all Tools in the candidates list need to be
 
554
                                                // processed=activated
 
555
                                                activateToolSet(candidates);
 
556
                                        } while (candidates.isEmpty() && level > 0);
 
557
                                        activatedTools.addAll(candidates);
 
558
                                        noTrigger = candidates.isEmpty();
 
559
                                }
 
560
                        }
 
561
                        if (axis != null && axis.isReleased()) { // possible deactivation
 
562
                                Set<Tool> deactivated = findDeactivatedTools(slot);
 
563
                                deactivatedTools.addAll(deactivated);
 
564
                                deactivateToolSet(deactivated);
 
565
                                noTrigger = deactivated.isEmpty();
 
566
                        }
 
567
 
 
568
                        // process all active tools NEW: only if no tool was (de)activated
 
569
                        if (noTrigger) {  //activatedTools.isEmpty() && deactivatedTools.isEmpty()
 
570
                                Set<Tool> active = slotManager.getActiveToolsForSlot(slot);
 
571
                                stillActiveTools.addAll(active);
 
572
                                processToolSet(active);
 
573
                        }
 
574
                }
 
575
                triggerQueue.clear();
 
576
                // NEW: this is now obsolete
 
577
                // // don't update used slots for deactivated tools!
 
578
                // stillActiveTools.removeAll(deactivatedTools);
 
579
                slotManager.updateMaps(stillActiveTools, activatedTools, deactivatedTools);
 
580
        }
 
581
 
 
582
        private void registerActivePathForTool(SpacePath pickPath, Tool tool) {
 
583
                List<SpacePath> ap = Collections.singletonList(
 
584
                                pickPath.getLastElement() instanceof Geometry ? pickPath.popNew() : pickPath
 
585
                                                );
 
586
                toolToPath.put(tool, ap);
 
587
        }
 
588
 
 
589
        private double[] pointerTrafo = new double[16];
 
590
 
 
591
        private double[] currentPointer = new double[16];
 
592
 
 
593
        protected final Object mutex=new Object();
 
594
 
 
595
        private SpacePath avatarPath;
 
596
 
 
597
        private boolean disposed;
 
598
 
 
599
        private void performPick() {
 
600
                if (pickSystem == null) {
 
601
                        pickResults = Collections.emptyList();
 
602
                        return;
 
603
                }
 
604
                pointerSlot = InputSlot.getDevice("PointerTransformation");
 
605
                currentPointer = deviceManager.getTransformationMatrix(pointerSlot).toDoubleArray(currentPointer);
 
606
                //if (Rn.equals(pointerTrafo, currentPointer)) return;
 
607
                Rn.copy(pointerTrafo, currentPointer);
 
608
                double[] to = new double[] { -pointerTrafo[2], -pointerTrafo[6],
 
609
                                -pointerTrafo[10], -pointerTrafo[14] };
 
610
                double[] from = new double[] { pointerTrafo[3], pointerTrafo[7],
 
611
                                pointerTrafo[11], pointerTrafo[15] };
 
612
                try {
 
613
                        AABBPickSystem.getFrustumInterval(from, to, viewer);
 
614
                } catch (IllegalStateException e) {
 
615
                        // no camera?
 
616
                }
 
617
                pickResults =  pickSystem.computePick(from, to);
 
618
                if (!SystemProperties.isPortal) {
 
619
                        if (hitFilter == null)
 
620
                                hitFilter = new PosWHitFilter(viewer);
 
621
                        hitFilter.update();
 
622
                        // throw out pick results whose NDC w-coordinate is negative
 
623
                        AABBPickSystem.filterList(hitFilter, from, to, pickResults);                    
 
624
                }
 
625
//              if (pickResults.size() != 0) {
 
626
//                      System.err.println(SystemProperties.hostname+" Got "+pickResults.size()+" picks");
 
627
//                      System.err.println(SystemProperties.hostname+" picked "+pickResults.get(0).getPickPath().getLastComponent().getName());
 
628
//              }
 
629
        }
 
630
 
 
631
        private SpacePath calculatePickPath() {
 
632
                performPick();
 
633
                if (pickResults.isEmpty()) {
 
634
                        return emptyPickPath;
 
635
                }
 
636
                PickResult result = (PickResult) pickResults.get(0);
 
637
                LoggingSystem.getLogger(this).fine("ToolSystem.calculatePickPath() <HIT>");
 
638
                return result.getPickPath();
 
639
        }
 
640
 
 
641
        /**
 
642
         * calls perform(ToolContext tc) for all tools in the given Set
 
643
         * removes Tools from the set if the tool rejected the activation.
 
644
         * 
 
645
         * @param toolSet
 
646
         * @return false if the current level of tools rejected the context...
 
647
         */
 
648
        private void activateToolSet(Set<Tool> toolSet) {
 
649
                for (Iterator<Tool> iter = toolSet.iterator(); iter.hasNext();) {
 
650
                        Tool tool = iter.next();
 
651
                        toolContext.setCurrentTool(tool);
 
652
                        toolContext.event.device=slotManager.resolveSlotForTool(tool, toolContext.sourceSlot);
 
653
                        if (toolContext.event.device == null) {
 
654
                                LoggingSystem.getLogger(this).warning("activate: resolving "+toolContext.sourceSlot+" failed: "+tool.getClass().getName());
 
655
                        }
 
656
                        for (SpacePath path : getActivePathsForTool(tool)) {
 
657
                                toolContext.setRootToLocal(path);
 
658
                                tool.activate(toolContext);
 
659
                                if (toolContext.isRejected()) {
 
660
                                        iter.remove();
 
661
                                        toolContext.rejected=false;
 
662
                                }
 
663
                        }
 
664
                }
 
665
        }
 
666
 
 
667
        /**
 
668
         * calls perform(ToolContext tc) for all tools in the given Set
 
669
         * 
 
670
         * @param toolSet
 
671
         */
 
672
        private void processToolSet(Set<Tool> toolSet) {
 
673
                for (Tool tool : toolSet) {
 
674
                        toolContext.setCurrentTool(tool);
 
675
                        toolContext.event.device=slotManager.resolveSlotForTool(tool, toolContext.sourceSlot);
 
676
                        for (SpacePath path : getActivePathsForTool(tool)) {
 
677
                                toolContext.setRootToLocal(path);
 
678
                                tool.perform(toolContext);
 
679
                        }
 
680
                }
 
681
        }
 
682
 
 
683
        private List<SpacePath> getActivePathsForTool(Tool tool) {
 
684
                List<SpacePath> l = toolToPath.get(tool);
 
685
                if (l == null) return Collections.emptyList();
 
686
                return l;
 
687
        }
 
688
 
 
689
        /**
 
690
         * calls perform(ToolContext tc) for all tools in the given Set
 
691
         * 
 
692
         * @param toolSet
 
693
         */
 
694
        private void deactivateToolSet(Set<Tool> toolSet) {
 
695
                for (Tool tool : toolSet) {
 
696
                        toolContext.setCurrentTool(tool);
 
697
                        toolContext.event.device=slotManager.resolveSlotForTool(tool, toolContext.sourceSlot);
 
698
                        if (toolContext.event.device == null) {
 
699
                                LoggingSystem.getLogger(this).warning("deavtivate: resolving "+toolContext.sourceSlot+" failed: "+tool.getClass().getName());
 
700
                        }
 
701
                        for (SpacePath path : getActivePathsForTool(tool)) {
 
702
                                toolContext.setRootToLocal(path);
 
703
                                tool.deactivate(toolContext);
 
704
                        }
 
705
                }
 
706
        }
 
707
 
 
708
        /**
 
709
         * the given slot must have AxisState.isReleased() == true
 
710
         * this is garanteed in processTrigger...
 
711
         * 
 
712
         * @param slot
 
713
         * @return
 
714
         */
 
715
        private Set<Tool> findDeactivatedTools(InputSlot slot) {
 
716
                return slotManager.getToolsDeactivatedBySlot(slot);
 
717
        }
 
718
 
 
719
        public void setPickSystem(PickSystem pickSystem) {
 
720
                this.pickSystem = pickSystem;
 
721
                if (pickSystem != null) {
 
722
                        pickSystem.setSceneRoot(viewer.getSceneRoot());
 
723
                }
 
724
        }
 
725
 
 
726
        public PickSystem getPickSystem() {
 
727
                return pickSystem;
 
728
        }
 
729
 
 
730
        public void setAvatarPath(SpacePath p) {
 
731
                avatarPath=p;
 
732
                deviceManager.setAvatarPath(avatarPath);
 
733
        }
 
734
 
 
735
        public SpacePath getAvatarPath() {
 
736
                return avatarPath != null ? avatarPath : viewer.getCameraPath();
 
737
        }
 
738
 
 
739
        public void dispose() {
 
740
                synchronized (mutex) {
 
741
                        disposed=true;
 
742
                        while (executing)
 
743
                                try {
 
744
                                        mutex.wait(1);
 
745
                                } catch (InterruptedException e) {
 
746
                                        // TODO Auto-generated catch block
 
747
                                        e.printStackTrace();
 
748
                                }
 
749
                }
 
750
                System.out.println("event queue shut down done...");
 
751
                deviceManager.dispose();
 
752
                eventQueue.dispose();
 
753
                updater.dispose();
 
754
                AnimatorTool.disposeInstance(KEY);
 
755
                unsetToolSystem(this); // remove from the viewer->tool-system table
 
756
        }
 
757
 
 
758
        final List<Pair> toolsChanging = new LinkedList<Pair>();
 
759
 
 
760
        protected static class Pair {
 
761
                final Tool tool;
 
762
                final SpacePath path;
 
763
                final boolean added;
 
764
                Pair(Tool tool, SpacePath path, boolean added) {
 
765
                        this.path=path;
 
766
                        this.tool=tool;
 
767
                        this.added=added;
 
768
                }
 
769
        }
 
770
 
 
771
        void addTool(Tool tool, SpacePath path) {
 
772
                synchronized (mutex) {
 
773
                        if (disposed) return;
 
774
                        if (executing)
 
775
                                toolsChanging.add(new Pair(tool, path, true));
 
776
                        else addToolImpl(tool, path);
 
777
                }
 
778
        }
 
779
 
 
780
        void removeTool(Tool tool, SpacePath path) {
 
781
                synchronized (mutex) {
 
782
                        if (disposed) return;
 
783
                        if (executing)
 
784
                                toolsChanging.add(new Pair(tool, path, false));
 
785
                        else removeToolImpl(tool, path);
 
786
                }
 
787
        }
 
788
 
 
789
        /**
 
790
         * TODO: sync
 
791
         * @param tool
 
792
         * @param path
 
793
         */
 
794
        void addToolImpl(Tool tool, SpacePath path) {
 
795
                boolean first = toolManager.addTool(tool, path);
 
796
                if (!toolManager.needsPick(tool)) {
 
797
                        List<SpacePath> l = toolToPath.get(tool);
 
798
                        if (l == null) {
 
799
                                l = new LinkedList<SpacePath>();
 
800
                                toolToPath.put(tool, l);
 
801
                        }
 
802
                        try {
 
803
                                l.add(path);
 
804
                        } catch (UnsupportedOperationException e) {
 
805
                                System.out.println("try adding to sigleton: "+tool);
 
806
                        }
 
807
                }
 
808
                if (first) {
 
809
                        slotManager.registerTool(tool);
 
810
                        if (tool.getActivationSlots().contains(InputSlot.POINTER_HIT)) {
 
811
                                mouseOverSupport.mouseOverToolAdded();
 
812
                        }
 
813
                }
 
814
                LoggingSystem.getLogger(this).info(
 
815
                                "first=" + first + " tool=" + tool + "   path=" + path);
 
816
        }
 
817
 
 
818
        /**
 
819
         * TODO: sync
 
820
         * @param tool
 
821
         * @param path
 
822
         */
 
823
        void removeToolImpl(Tool tool, SpacePath path) {
 
824
                boolean last = toolManager.removeTool(tool, path);
 
825
                for (SpacePath activePath : getActivePathsForTool(tool)) {
 
826
                        if (path.isEqual(activePath)) {
 
827
                                ToolEvent te = new ToolEvent(this, -1, InputSlot.getDevice("remove"), null,     null);
 
828
                                toolContext.setCurrentTool(tool);
 
829
                                toolContext.setRootToLocal(path);
 
830
                                toolContext.event = te;
 
831
                                tool.deactivate(toolContext);
 
832
                                toolToPath.remove(tool);
 
833
                        }
 
834
                }
 
835
                if (last) {
 
836
                        slotManager.unregisterTool(tool);
 
837
                        if (tool.getActivationSlots().contains(InputSlot.POINTER_HIT)) {
 
838
                                mouseOverSupport.mouseOverToolRemoved();
 
839
                        }
 
840
                }
 
841
                LoggingSystem.getLogger(this).info(
 
842
                                "last=" + last + " tool=" + tool + " path=" + path);
 
843
        }
 
844
 
 
845
        public SpacePath getEmptyPickPath() {
 
846
                return emptyPickPath;
 
847
        }
 
848
 
 
849
        public void setEmptyPickPath(SpacePath emptyPickPath) {
 
850
                if (emptyPickPath != null) {
 
851
                        if (emptyPickPath.getFirstElement().getName() != viewer.getSceneRoot().getName())
 
852
                                throw new IllegalArgumentException("empty pick path must start at scene root!");
 
853
                        if (emptyPickPath.getFirstElement() != viewer.getSceneRoot()) {
 
854
                                LoggingSystem.getLogger(this).warning("Strange situation: same names but different scene roots");
 
855
                        }
 
856
                        this.emptyPickPath = emptyPickPath;
 
857
                } else {
 
858
                        this.emptyPickPath = new SpacePath();
 
859
                        this.emptyPickPath.push(viewer.getSceneRoot());
 
860
                }
 
861
        }
 
862
 
 
863
    public RenderTrigger getRenderTrigger() {
 
864
        return renderTrigger;
 
865
    }
 
866
 
 
867
        public Object getKey() {
 
868
                return KEY;
 
869
        }
 
870
 
 
871
}