View Javadoc

1   /**
2    * 
3    */
4   package org.ximtec.igesture.core.composite;
5   
6   import java.util.ArrayList;
7   import java.util.Iterator;
8   import java.util.List;
9   import java.util.Set;
10  import java.util.logging.Level;
11  import java.util.logging.Logger;
12  
13  import org.sigtec.ink.Note;
14  import org.ximtec.igesture.core.Gesture;
15  import org.ximtec.igesture.core.GestureSample;
16  import org.ximtec.igesture.core.GestureSample3D;
17  import org.ximtec.igesture.io.IDeviceManager;
18  import org.ximtec.igesture.util.Constant;
19  import org.ximtec.igesture.util.RecordedGesture3DTool;
20  import org.ximtec.igesture.util.additions3d.RecordedGesture3D;
21  
22  /**
23   * This class represents a two-fold constraint. First the gestures have to be performed concurrently and secondly, they have 
24   * to be performed in each others vicinity. The minimum and maximum distance has to be defined. This distance is the diagonal
25   * of the rectangular bounding box of the gestures for 2D gestures, for 3D gestures it is the diagonal of each facet of a 
26   * cubic bounding space.
27   * 
28   * @author Bjorn Puype, bpuype@gmail.com
29   *
30   */
31  public class ProximityConcurrencyConstraint extends ConcurrencyConstraint{
32  
33  	private static final Logger LOGGER = Logger.getLogger(ProximityConcurrencyConstraint.class.getName());
34  	private static final String ERROR_MESSAGE_NO_TYPE = "The device type must be specified for a proximity based constraint in order\n" +
35  								"to ensure that the coordinates of the gestures can be logically compared.\n" +
36  								"Please select a device type.";
37  	private static final String LOGGER_MESSAGE_NO_TYPE = "Proximity based constraints must specify the device type. " +
38  								"The gesture class was not added.";
39  
40  	private static final String ERROR_MESSAGE_WRONG_TYPE = "The gestures created by a device of the specified type cannot be logically compared\n" +
41  								"with the other gestures in the constraint!\n" +
42  								"Please specify a device which creates gestures of type: ";
43  	private static final String LOGGER_MESSAGE_WRONG_TYPE = "The gesture type (e.g. 2D, 3D) of all composing gestures must match. The gesture class was not added.";
44  	
45  	public enum Config{
46  		MIN_DISTANCE, MAX_DISTANCE, DISTANCE_UNIT
47  	}
48  	
49  	private final static String MIN_DISTANCE = "0";
50  	private final static String MAX_DISTANCE = "10";
51  	private final static String DISTANCE_UNIT = Constant.CM;
52  
53  	private double minDistance;
54  	private double maxDistance;
55  	private String distanceUnit;
56  
57  	public ProximityConcurrencyConstraint() {
58  		super();
59  		DEFAULT_CONFIGURATION.put(Config.MIN_DISTANCE.name(), MIN_DISTANCE);
60  		DEFAULT_CONFIGURATION.put(Config.MAX_DISTANCE.name(), MAX_DISTANCE);
61  		DEFAULT_CONFIGURATION.put(Config.DISTANCE_UNIT.name(), DISTANCE_UNIT);
62  		setterMapping.put(Config.DISTANCE_UNIT.name(), "setDistanceUnit");
63  		setterMapping.put(Config.MIN_DISTANCE.name(), "setMinDistance");
64  		setterMapping.put(Config.MAX_DISTANCE.name(), "setMaxDistance");
65  		setMinDistance(MIN_DISTANCE);
66  		setMaxDistance(MAX_DISTANCE);
67  		setDistanceUnit(DISTANCE_UNIT);
68  	}
69  	
70  	/* (non-Javadoc)
71  	 * @see org.ximtec.igesture.core.composite.Constraint#addGestureClass(java.lang.String)
72  	 */
73  	@Override
74  	public void addGestureClass(String gestureClass) {
75  		IllegalArgumentException e = new IllegalArgumentException(ERROR_MESSAGE_NO_TYPE);
76  		LOGGER.log(Level.SEVERE,LOGGER_MESSAGE_NO_TYPE,e);
77  		throw e;
78  	}
79  
80  	/* (non-Javadoc)
81  	 * @see org.ximtec.igesture.core.composite.Constraint#addGestureClass(java.lang.String, int)
82  	 */
83  	@Override
84  	public void addGestureClass(String gestureClass, int user) {
85  		IllegalArgumentException e = new IllegalArgumentException(ERROR_MESSAGE_NO_TYPE);
86  		LOGGER.log(Level.SEVERE,LOGGER_MESSAGE_NO_TYPE,e);
87  		throw e;
88  	}
89  
90  	/* (non-Javadoc)
91  	 * @see org.ximtec.igesture.core.composite.Constraint#addGestureClass(java.lang.String, java.lang.String, java.util.Set<String>)
92  	 */
93  	@Override
94  	public void addGestureClass(String gestureClass, String deviceType, Set<String> devices) {
95  		String myGestureType = ConstraintTool.getGestureType(deviceType);
96  		
97  		if(gestures.isEmpty() || (!gestures.isEmpty() && ConstraintTool.getGestureType(gestures.get(0).getDeviceType()).equals(myGestureType)))
98  		{
99  			DefaultConstraintEntry entry = new DefaultConstraintEntry(gestureClass, deviceType, devices); 
100 			gestures.add(entry);
101 			propertyChangeSupport.fireIndexedPropertyChange(PROPERTY_GESTURES, gestures.indexOf(entry), null, entry);
102 		}
103 		else
104 		{
105 			IllegalArgumentException e = new IllegalArgumentException(ERROR_MESSAGE_WRONG_TYPE+ConstraintTool.getGestureType(gestures.get(0).getDeviceType()));
106 			LOGGER.log(Level.SEVERE,LOGGER_MESSAGE_WRONG_TYPE,e);
107 			throw e;
108 		}
109 	}
110 
111 	/* (non-Javadoc)
112 	 * @see org.ximtec.igesture.core.composite.Constraint#addGestureClass(java.lang.String, int, java.lang.String, java.util.Set<String>)
113 	 */
114 	@Override
115 	public void addGestureClass(String gestureClass, int user, String deviceType, Set<String> devices) {
116 		String myGestureType = ConstraintTool.getGestureType(deviceType);
117 		
118 		if(gestures.isEmpty() || (!gestures.isEmpty() && ConstraintTool.getGestureType(gestures.get(0).getDeviceType()).equals(myGestureType)))
119 		{
120 			DefaultConstraintEntry entry = new DefaultConstraintEntry(gestureClass, deviceType, devices);
121 			gestures.add(entry);
122 			propertyChangeSupport.fireIndexedPropertyChange(PROPERTY_GESTURES, gestures.indexOf(entry), null, entry);
123 		}
124 		else
125 		{
126 			IllegalArgumentException e = new IllegalArgumentException(ERROR_MESSAGE_WRONG_TYPE+ConstraintTool.getGestureType(gestures.get(0).getDeviceType()));
127 			LOGGER.log(Level.SEVERE,LOGGER_MESSAGE_WRONG_TYPE,e);
128 			throw e;
129 		}
130 	}
131 	
132 	/**
133 	 * Get the minimum distance between two gestures.
134 	 */
135 	public double getMinDistance() {
136 		return minDistance;
137 	}
138 	
139 	/**
140 	 * Set the minimum distance between two gestures.
141 	 */
142 	public void setMinDistance(String minDistance) throws NumberFormatException{
143 		this.minDistance = Double.parseDouble(minDistance);
144 	}
145 	
146 	/**
147 	 * Get the maximum distance between two gestures.
148 	 */
149 	public double getMaxDistance() {
150 		return maxDistance;
151 	}
152 	
153 	/**
154 	 * Set the maximum distance between two gestures.
155 	 */
156 	public void setMaxDistance(String maxDistance) throws NumberFormatException{
157 		this.maxDistance = Double.parseDouble(maxDistance);
158 	}
159 	
160 	/**
161 	 * Get the distance unit.
162 	 * @see org.ximtec.igesture.core.composite.Constraint
163 	 */
164 	public String getDistanceUnit() {
165 		return distanceUnit;
166 	}
167 	
168 	/**
169 	 * Set the distance unit.
170 	 * @see org.ximtec.igesture.core.composite.Constraint
171 	 */
172 	public void setDistanceUnit(String distanceUnit) {
173 		this.distanceUnit = distanceUnit;
174 	}
175 	
176 	/* (non-Javadoc)
177 	 * @see org.ximtec.igesture.core.composite.DefaultConstraint#validateConditions(java.util.List)
178 	 */
179 	@Override
180 	public boolean validateConditions(List<Gesture<?>> gestures, IDeviceManager manager) {
181 		
182 		boolean conditionsValid = super.validateConditions(gestures, manager);
183 		
184 		/* distance check */
185 		if(conditionsValid) // if previous conditions hold
186 		{
187 			boolean same = true;
188 			Gesture<?> gest = gestures.get(0);
189 			if(gest instanceof GestureSample)
190 			{
191 				List<Note> notes = new ArrayList<Note>();
192 				for (Iterator<Gesture<?>> iterator = gestures.iterator(); iterator.hasNext();) {
193 					Gesture<?> gesture = iterator.next();
194 					if(gesture instanceof GestureSample)
195 					{
196 						notes.add(((GestureSample)gesture).getGesture());
197 					}
198 					else
199 					{
200 						same = false;
201 						break;
202 					}
203 					
204 				}
205 				if(same)
206 				{
207 					conditionsValid = ConstraintTool.isBoundsDiagonalValid(notes,minDistance,maxDistance);
208 				}
209 //				else
210 //					throw new RuntimeException("Gestures need to be of same type. 2D and 3D gestures cannot be mixed in proximity based constraints");
211 				
212 			}
213 			else if(gest instanceof GestureSample3D)
214 			{
215 				List<Note> notesXY = new ArrayList<Note>();
216 				List<Note> notesYZ = new ArrayList<Note>();
217 				List<Note> notesXZ = new ArrayList<Note>();
218 				for (Iterator<Gesture<?>> iterator = gestures.iterator(); iterator.hasNext();) {
219 					Gesture<?> gesture = iterator.next();
220 					if(gesture instanceof GestureSample3D)
221 					{
222 						RecordedGesture3D record = ((GestureSample3D)gesture).getGesture();
223 						List<Gesture<Note>> notes = RecordedGesture3DTool.splitToPlanes(record);
224 						notesXY.add(notes.get(0).getGesture());
225 						notesYZ.add(notes.get(1).getGesture());
226 						notesXZ.add(notes.get(2).getGesture());
227 					}
228 					else
229 					{
230 						same = false;
231 						break;
232 					}
233 					
234 				}
235 				if(same)
236 				{					
237 					conditionsValid =  ConstraintTool.isBoundsDiagonalValid(notesXZ,minDistance,maxDistance);
238 					if(conditionsValid)
239 					{
240 						conditionsValid = ConstraintTool.isBoundsDiagonalValid(notesYZ,minDistance,maxDistance);
241 						if(conditionsValid)
242 							conditionsValid = ConstraintTool.isBoundsDiagonalValid(notesXY,minDistance,maxDistance);
243 					}					
244 				}
245 //				else
246 //					throw new ProximityException();
247 				
248 			}
249 		}
250 		
251 		return conditionsValid;
252 	}
253 	
254 	public String toString()
255 	{
256 		return ProximityConcurrencyConstraint.class.getSimpleName();
257 	}
258 }