All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.j3d.utils.behaviors.sensor.SensorEventAgent Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistribution of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any
 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
 * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or
 * intended for use in the design, construction, operation or
 * maintenance of any nuclear facility.
 *
 */

package com.sun.j3d.utils.behaviors.sensor ;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.media.j3d.Sensor;
import javax.media.j3d.Transform3D;

import com.sun.j3d.utils.timer.J3DTimer;

/**
 * This class works in conjunction with the SensorButtonListener
 * and SensorReadListener interfaces to support an event-driven
 * model of sensor interaction.  Java 3D defines sensors as delivering
 * continuous input data which must be polled to retrieve their values, but in
 * practice it is often convenient to structure application code to respond to
 * events such as button state transitions.
 * 

* Listeners registered with this class are invoked when its * dispatchEvents method is called. This is usually called from * the processStimulus method of a Behavior, but may * also be called directly from the pollAndProcessInput method of * an event-driven implementation of InputDevice. In either case * the device is still polled by the Java 3D input device scheduling thread to * get its current values; however, in the former, dispatchEvents * is called from the behavior scheduler thread regardless of whether any new * events are available, while in the latter, the InputDevice * implementation may choose to call dispatchEvents only if new * events are actually generated. *

* Button events are generated by changes in sensor button state, from pressed * to released and vice versa. Button state changes are examined with each * invocation of the dispatchEvents method. Events are * distributed to interested parties through the button listener interface * using the pressed and released callbacks. *

* The dragged method is not necessarily called in response to a * motion event generated by a sensor. dispatchEvents will call * dragged whenever any button assigned to the listener is down * and has not changed state since the last time it was called. If * dispatchEvents is called in the processStimulus * of a Behavior, then dragged may be called even if * the sensor value has not changed. This is as a consequence of the core * Java 3D API definition of sensors as continuous devices. *

* Like dragged, the read method of * SensorReadListener is not necessarily invoked in response to a * real event. It is called by dispatchEvents whenever a button * listener has not been called for that sensor. This usually means that no * buttons are down, but clients are free to leave a button listener null, or * to explicitly bind a null button listener to a button so that button's * events are ignored. The sensor value has not necessarily changed since the * last read callback. *

* A mutual exclusion policy can be applied between button * listeners when they are grouped in an array mapped to the sensor's * buttons. If multiple sensor buttons are held down at the same time, * then a listener in the array is invoked only for the button that was * depressed first. The read callback is separated from the * pressed, released, and dragged * callbacks in a separate interface in order to support this policy. *

* The events passed to the listeners are ephemeral; they are only * valid until the listener has returned. This is done to avoid * allocating large numbers of mostly temporary objects, especially for * behaviors that wake up every frame. If a listener needs to retain the * event it must be copied using the SensorEvent(SensorEvent) * constructor. *

* It is safe to add and remove listeners in response to a callback. * * @see SensorEvent * @see SensorButtonListener * @see SensorReadListener * @since Java 3D 1.3 */ public class SensorEventAgent { private long t0 = 0 ; private Object source = null ; private SensorEvent e = new SensorEvent() ; // List of SensorBinding objects and corresponding array. private List bindingsList = new ArrayList() ; private SensorBinding[] bindings = new SensorBinding[0] ; // Indicates that lists must be converted to arrays. Need to do this // to allow listeners to add and remove themselves or other listeners // safely during event dispatch. private boolean listsDirty = false ; /** * Create a SensorEventAgent to generate and dispatch * sensor events to registered listeners. * * @param source reference to the originating object for inclusion in * generated SensorEvents; intended to refer to the * instantiating Behavior but may be any reference, or null */ public SensorEventAgent(Object source) { this.source = source ; } /** * This class contains all the button and read listeners registered * with a sensor. */ private static class SensorBinding { Sensor sensor = null ; int[] buttons = null ; Transform3D read = null ; // List of SensorButtonBinding objects and corresponding array. List buttonBindingsList = new ArrayList() ; SensorButtonBinding[] buttonBindings = new SensorButtonBinding[0] ; // List of SensorReadListener objects and corresponding array. List readBindingsList = new ArrayList() ; SensorReadListener[] readBindings = new SensorReadListener[0] ; SensorBinding(Sensor sensor) { this.sensor = sensor ; buttons = new int[sensor.getSensorButtonCount()] ; read = new Transform3D() ; } void updateArrays() { buttonBindings = (SensorButtonBinding[])buttonBindingsList.toArray (new SensorButtonBinding[buttonBindingsList.size()]) ; readBindings = (SensorReadListener[])readBindingsList.toArray (new SensorReadListener[readBindingsList.size()]) ; } @Override public String toString() { String s = new String() ; s = "sensor " + sensor + "\nbutton listener arrays:\n" ; for (int i = 0 ; i < buttonBindingsList.size() ; i++) s = s + ((SensorButtonBinding)buttonBindingsList.get(i)) ; s = s + "read listeners:\n" ; for (int i = 0 ; i < readBindingsList.size() ; i++) s = s + " " + ((SensorReadListener)readBindingsList.get(i)) + "\n" ; return s ; } } /** * This class contains an array of SensorButtonListener * implementations, one for each sensor button. This array is used to * support a mutual exclusion callback policy. There may be multiple * instances of this class associated with a single sensor. */ private static class SensorButtonBinding { int buttonsHandled = 0 ; boolean[] prevButtons = null ; boolean multiButton = false ; SensorButtonListener[] listeners = null ; SensorButtonBinding(SensorButtonListener[] listeners, boolean multiButtonEnable) { prevButtons = new boolean[listeners.length] ; this.listeners = new SensorButtonListener[listeners.length] ; for (int i = 0 ; i < listeners.length ; i++) { prevButtons[i] = false ; this.listeners[i] = listeners[i] ; } this.multiButton = multiButtonEnable ; } @Override public String toString() { String s = new String() ; s = " length " + listeners.length + ", mutual exclusion " + (!multiButton) + "\n" ; for (int i = 0 ; i < listeners.length ; i++) s = s + " " + (listeners[i] == null? "null" : listeners[i].toString()) + "\n" ; return s ; } } /** * Look up the sensor listeners bound to the given sensor. */ private SensorBinding getSensorBinding(Sensor sensor) { for (int i = 0 ; i < bindingsList.size() ; i++) { SensorBinding sb = (SensorBinding)bindingsList.get(i) ; if (sb.sensor == sensor) return sb ; } return null ; } /** * Creates a binding of the specified sensor button to the given * SensorButtonListener implementation. * * @param sensor the sensor with the button to be bound * @param button the index of the button to be bound on the specified * sensor; may range from 0 to * (sensor.getSensorButtonCount() - 1) * @param buttonListener the SensorButtonListener * implementation that will be invoked for the sensor's button */ public synchronized void addSensorButtonListener (Sensor sensor, int button, SensorButtonListener buttonListener) { if (sensor == null) throw new NullPointerException("\nsensor is null") ; if (button >= sensor.getSensorButtonCount()) throw new ArrayIndexOutOfBoundsException ("\nbutton " + button + " >= sensor button count " + sensor.getSensorButtonCount()) ; SensorBinding sb = getSensorBinding(sensor) ; if (sb == null) { sb = new SensorBinding(sensor) ; bindingsList.add(sb) ; } SensorButtonListener[] listeners = new SensorButtonListener[sb.buttons.length] ; // Assign only the specified button; others remain null. listeners[button] = buttonListener ; SensorButtonBinding sbb = new SensorButtonBinding(listeners, true) ; sb.buttonBindingsList.add(sbb) ; listsDirty = true ; } /** * Creates a binding from all the buttons on the specified sensor to * the given SensorButtonListener implementation. If * multiple sensor buttons are held down at the same time, the press * and release callbacks are called for each button in the order that * they occur. This allows actions to be bound to combinations of * button presses, but is also convenient for listeners that don't * care which button was pressed. * * @param sensor the sensor to be bound * @param buttonListener the SensorButtonListener * implementation that will be called for all button events */ public synchronized void addSensorButtonListener (Sensor sensor, SensorButtonListener buttonListener) { if (sensor == null) throw new NullPointerException("\nsensor is null") ; SensorBinding sb = getSensorBinding(sensor) ; if (sb == null) { sb = new SensorBinding(sensor) ; bindingsList.add(sb) ; } SensorButtonListener[] listeners = new SensorButtonListener[sb.buttons.length] ; // All buttons are bound to the same listener. for (int i = 0 ; i < sb.buttons.length ; i++) listeners[i] = buttonListener ; SensorButtonBinding sbb = new SensorButtonBinding(listeners, true) ; sb.buttonBindingsList.add(sbb) ; listsDirty = true ; } /** * Creates a binding of the specified sensor to the given array of * SensorButtonListener implementations. The array index * of the listener indicates the index of the sensor button to which * it will be bound. *

* This method enforces a mutually exclusive callback policy * among the listeners specified in the array. If multiple sensor * buttons are held down at the same time, callbacks are invoked only * for the button that was depressed first. * * @param sensor the sensor to be bound * @param buttonListeners array of implementations of * SensorButtonListener; array entries may be null or * duplicates but the array length must equal the sensor's button * count */ public synchronized void addSensorButtonListeners (Sensor sensor, SensorButtonListener[] buttonListeners) { if (sensor == null) throw new NullPointerException("\nsensor is null") ; SensorBinding sb = getSensorBinding(sensor) ; if (sb == null) { sb = new SensorBinding(sensor) ; bindingsList.add(sb) ; } if (sb.buttons.length != buttonListeners.length) throw new IllegalArgumentException ("\nbuttonListeners length " + buttonListeners.length + " must equal sensor button count " + sb.buttons.length) ; SensorButtonBinding sbb = new SensorButtonBinding(buttonListeners, false) ; sb.buttonBindingsList.add(sbb) ; listsDirty = true ; } /** * Gets the SensorButtonListener implementations bound to * the given sensor and button. * * @param sensor the sensor of interest * @param button the button of interest * @return array of SensorButtonListener implementations * bound to the given sensor and button, or null */ public SensorButtonListener[] getSensorButtonListeners(Sensor sensor, int button) { if (sensor == null) throw new NullPointerException("\nsensor is null") ; if (button >= sensor.getSensorButtonCount()) throw new ArrayIndexOutOfBoundsException ("\nbutton " + button + " >= sensor button count " + sensor.getSensorButtonCount()) ; SensorBinding sb = getSensorBinding(sensor) ; if (sb == null) return null ; ArrayList listeners = new ArrayList() ; for (int i = 0 ; i < sb.buttonBindingsList.size() ; i++) { SensorButtonBinding sbb = (SensorButtonBinding)sb.buttonBindingsList.get(i) ; if (sbb.listeners[button] != null) listeners.add(sbb.listeners[button]) ; } if (listeners.size() == 0) return null ; else return (SensorButtonListener[])listeners.toArray (new SensorButtonListener[listeners.size()]) ; } /** * Remove the SensorButtonListener from the given SensorBinding. */ private void removeSensorButtonListener (SensorBinding sb, SensorButtonListener listener) { Iterator i = sb.buttonBindingsList.iterator() ; while (i.hasNext()) { int instanceCount = 0 ; SensorButtonBinding sbb = (SensorButtonBinding)i.next() ; for (int j = 0 ; j < sbb.listeners.length ; j++) { if (sbb.listeners[j] == listener) sbb.listeners[j] = null ; else if (sbb.listeners[j] != null) instanceCount++ ; } if (instanceCount == 0) { i.remove() ; } } listsDirty = true ; } /** * Remove the given SensorButtonListener binding from the * specified sensor. * * @param sensor the sensor from which to remove the listener * @param listener the listener to be removed */ public synchronized void removeSensorButtonListener (Sensor sensor, SensorButtonListener listener) { if (sensor == null) throw new NullPointerException("\nsensor is null") ; SensorBinding sb = getSensorBinding(sensor) ; if (sb == null) return ; removeSensorButtonListener(sb, listener) ; if (sb.buttonBindingsList.size() == 0 && sb.readBindingsList.size() == 0) removeSensorBinding(sensor) ; listsDirty = true ; } /** * Remove the given SensorButtonListener from all sensors. * * @param listener the listener to remove */ public synchronized void removeSensorButtonListener (SensorButtonListener listener) { Iterator i = bindingsList.iterator() ; while (i.hasNext()) { SensorBinding sb = (SensorBinding)i.next() ; removeSensorButtonListener(sb, listener) ; if (sb.buttonBindingsList.size() == 0 && sb.readBindingsList.size() == 0) { i.remove() ; } } listsDirty = true ; } /** * Creates a binding of the specified sensor to the given * SensorReadListener. The read listener is invoked * every time dispatchEvents is called and a button * listener is not invoked. * * @param sensor the sensor to be bound * @param readListener the SensorReadListener * implementation */ public synchronized void addSensorReadListener (Sensor sensor, SensorReadListener readListener) { if (sensor == null) throw new NullPointerException("\nsensor is null") ; SensorBinding sb = getSensorBinding(sensor) ; if (sb == null) { sb = new SensorBinding(sensor) ; bindingsList.add(sb) ; } sb.readBindingsList.add(readListener) ; listsDirty = true ; } /** * Gets the SensorReadListeners bound to the specified * sensor. * * @param sensor the sensor of interest * @return array of SensorReadListeners bound to the * given sensor, or null */ public SensorReadListener[] getSensorReadListeners(Sensor sensor) { if (sensor == null) throw new NullPointerException("\nsensor is null") ; SensorBinding sb = getSensorBinding(sensor) ; if (sb == null) return null ; else if (sb.readBindingsList.size() == 0) return null ; else return (SensorReadListener[])sb.readBindingsList.toArray (new SensorReadListener[sb.readBindingsList.size()]) ; } /** * Remove the SensorReadListener from the given SensorBinding. */ private void removeSensorReadListener (SensorBinding sb, SensorReadListener listener) { Iterator i = sb.readBindingsList.iterator() ; while (i.hasNext()) { if (((SensorReadListener)i.next()) == listener) i.remove() ; } listsDirty = true ; } /** * Remove the given SensorReadListener binding from the * specified sensor. * * @param sensor the sensor from which to remove the listener * @param listener the listener to be removed */ public synchronized void removeSensorReadListener (Sensor sensor, SensorReadListener listener) { if (sensor == null) throw new NullPointerException("\nsensor is null") ; SensorBinding sb = getSensorBinding(sensor) ; if (sb == null) return ; removeSensorReadListener(sb, listener) ; if (sb.buttonBindingsList.size() == 0 && sb.readBindingsList.size() == 0) removeSensorBinding(sensor) ; listsDirty = true ; } /** * Remove the given SensorReadListener from all sensors. * * @param listener the listener to remove */ public synchronized void removeSensorReadListener (SensorReadListener listener) { Iterator i = bindingsList.iterator() ; while (i.hasNext()) { SensorBinding sb = (SensorBinding)i.next() ; removeSensorReadListener(sb, listener) ; if (sb.buttonBindingsList.size() == 0 && sb.readBindingsList.size() == 0) { i.remove() ; } } listsDirty = true ; } /** * Remove all sensor listeners bound to the given sensor. */ public synchronized void removeSensorBinding(Sensor sensor) { Iterator i = bindingsList.iterator() ; while (i.hasNext()) { SensorBinding sb = (SensorBinding)i.next() ; if (sb.sensor == sensor) { i.remove() ; break ; } } listsDirty = true ; } /** * Returns an array of references to all sensors that have been bound * to listeners. * @return an array of sensors, or null if no sensors have been bound */ public Sensor[] getSensors() { if (bindingsList.size() == 0) return null ; Sensor[] s = new Sensor[bindingsList.size()] ; for (int i = 0 ; i < bindingsList.size() ; i++) s[i] = ((SensorBinding)bindingsList.get(i)).sensor ; return s ; } /** * Copies binding lists to arrays for event dispatch. This allows * listeners to add or remove themselves or other listeners safely. */ private synchronized void updateArrays() { bindings = (SensorBinding[])bindingsList.toArray (new SensorBinding[bindingsList.size()]) ; for (int i = 0 ; i < bindings.length ; i++) { bindings[i].updateArrays() ; } } /** * Reads all sensor button state and dispatches events to registered * button and read listeners. This method is intended to be called from * the processStimulus implementation of a * Behavior or the pollAndProcessInput method of * an event-driven implementation of InputDevice. */ public void dispatchEvents() { long t1 = t0 ; t0 = J3DTimer.getValue() ; if (listsDirty) { updateArrays() ; listsDirty = false ; } // Loop through all sensor bindings. for (int k = 0 ; k < bindings.length ; k++) { SensorBinding sb = bindings[k] ; Sensor s = sb.sensor ; Transform3D read = sb.read ; int[] buttons = sb.buttons ; int dragButton = 0 ; boolean callReadListeners = true ; boolean callDraggedListener = false ; // Get this sensor's readings. s.getRead(read) ; s.lastButtons(buttons) ; // Dispatch button listeners. for (int j = 0 ; j < sb.buttonBindings.length ; j++) { SensorButtonBinding sbb = sb.buttonBindings[j] ; for (int i = 0 ; i < buttons.length ; i++) { if (sbb.listeners[i] == null) continue ; // Check for button release. if (sbb.prevButtons[i]) { if (buttons[i] == 0) { e.set(source, SensorEvent.RELEASED, s, read, buttons, i, t0, t1) ; sbb.listeners[i].released(e) ; sbb.prevButtons[i] = false ; sbb.buttonsHandled-- ; } else { callDraggedListener = true ; dragButton = i ; } callReadListeners = false ; } // Check for button press. // Ignore multiple button presses if not enabled; // otherwise, one listener is bound to all buttons. else if (buttons[i] == 1) { if (sbb.buttonsHandled == 0 || sbb.multiButton) { e.set(source, SensorEvent.PRESSED, s, read, buttons, i, t0, t1) ; sbb.listeners[i].pressed(e) ; sbb.prevButtons[i] = true ; sbb.buttonsHandled++ ; callReadListeners = false ; } } } if (callDraggedListener) { // One drag event even if multiple buttons down. // Called after all pressed() and released() calls. e.set(source, SensorEvent.DRAGGED, s, read, buttons, SensorEvent.NOBUTTON, t0, t1) ; sbb.listeners[dragButton].dragged(e) ; } } // Dispatch read listeners. if (callReadListeners) { e.set(source, SensorEvent.READ, s, read, buttons, SensorEvent.NOBUTTON, t0, t1) ; for (int r = 0 ; r < sb.readBindings.length ; r++) { sb.readBindings[r].read(e) ; } } } } @Override public String toString() { String s = "SensorEventAgent@" + Integer.toHexString(hashCode()) ; s += "\nsensor bindings:\n\n" ; for (int i = 0 ; i < bindingsList.size() ; i++) { s += ((SensorBinding)bindingsList.get(i)).toString() + "\n" ; } return s ; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy