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