fiore@3
|
1 #include "HapticManager.h"
|
fiore@3
|
2
|
fiore@3
|
3 #define NO_ERROR_SIMUL
|
fiore@3
|
4
|
fiore@3
|
5
|
fiore@3
|
6 void checkExceptions(void);
|
fiore@3
|
7 void stopExecution(char* msg);
|
fiore@3
|
8
|
fiore@3
|
9 bool movedNode = false;
|
fiore@3
|
10
|
fiore@3
|
11
|
fiore@3
|
12 void HLCALLBACK HapticManager::hlButton1CB(HLenum event, HLuint object,
|
fiore@3
|
13 HLenum thread, HLcache *cache,
|
fiore@3
|
14 void *userdata)
|
fiore@3
|
15 {
|
fiore@3
|
16 HapticManager *pThis = static_cast<HapticManager*>(userdata);
|
fiore@3
|
17 if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall
|
fiore@3
|
18 return;
|
fiore@3
|
19 if(event != HL_EVENT_1BUTTONDOWN)
|
fiore@3
|
20 return;
|
fiore@3
|
21
|
fiore@3
|
22 /* check if it was a double click or not */
|
fiore@3
|
23
|
fiore@3
|
24 clock_t time = clock();
|
fiore@3
|
25 if(pThis->clickStart == 0) // first click
|
fiore@3
|
26 pThis->clickStart = time;
|
fiore@3
|
27 else if(time < (pThis->clickStart + CLICK_INTERVAL))
|
fiore@3
|
28 pThis->doubleClick = true;
|
fiore@3
|
29
|
fiore@3
|
30 }
|
fiore@3
|
31
|
fiore@3
|
32 void HLCALLBACK HapticManager::hlButton2CB(HLenum event, HLuint object,
|
fiore@3
|
33 HLenum thread, HLcache *cache,
|
fiore@3
|
34 void *userdata){
|
fiore@3
|
35 HapticManager *pThis = static_cast<HapticManager*>(userdata);
|
fiore@3
|
36 if(event == HL_EVENT_2BUTTONDOWN){
|
fiore@3
|
37 pThis->stickyMode = !pThis->stickyMode;
|
fiore@3
|
38 pThis->executeCommand('g', pThis->stickyMode ? 1 : 0,0,0,0,0);
|
fiore@3
|
39 /* we change mode to stickyMode, thus dragging becomes not active */
|
fiore@3
|
40 if(pThis->stickyMode && pThis->springStatus == DRAGGING){
|
fiore@3
|
41 pThis->springStatus = STOP_DRAGGING;
|
fiore@3
|
42 pThis->secondClick = false;
|
fiore@3
|
43 }
|
fiore@3
|
44 #ifdef VIBRATION
|
fiore@3
|
45 // vibrate
|
fiore@3
|
46 if(pThis->vibrationStatus == NO_VIBRATION){
|
fiore@3
|
47 pThis->vibrationStatus = START_VIBRATION;
|
fiore@3
|
48 }
|
fiore@3
|
49 #endif
|
fiore@3
|
50 }
|
fiore@3
|
51 }
|
fiore@3
|
52
|
fiore@3
|
53 void HLCALLBACK HapticManager::hlTouchCB(HLenum event, HLuint object, HLenum thread,
|
fiore@3
|
54 HLcache *cache, void *userdata)
|
fiore@3
|
55 {
|
fiore@3
|
56 HapticManager *pThis = static_cast<HapticManager*>(userdata);
|
fiore@3
|
57 if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall
|
fiore@3
|
58 return;
|
fiore@3
|
59 if(pThis->lastMovedElement == object){
|
fiore@3
|
60 pThis->stillTouchingLastMovedElement = true;
|
fiore@3
|
61 }
|
fiore@3
|
62
|
fiore@3
|
63 try{
|
fiore@3
|
64 // check if the touched object is an edge
|
fiore@3
|
65 if(pThis->collectionsManager->isEdge(object)){
|
fiore@3
|
66 CollectionsManager::EdgeData & ed = pThis->collectionsManager->getEdgeDataFromID(object);
|
fiore@3
|
67 pThis->lastTouchedEdgeID = object;
|
fiore@3
|
68 pThis->lastTouchedEdgeIsUntouched = false;
|
fiore@3
|
69 pThis->lastTouchedEdgeStipplePattern = ed.stipplePattern;
|
fiore@3
|
70 // check if the proxy is still touching the edge's node, in that case only play the sound
|
fiore@3
|
71 HLboolean isTouching = HL_FALSE;
|
fiore@3
|
72 if(pThis->lastTouchedNodeID != HL_OBJECT_ANY){
|
fiore@3
|
73 hlGetShapeBooleanv(pThis->lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching);
|
fiore@3
|
74 }
|
fiore@3
|
75 /* play the sound and speech associated to the edge */
|
fiore@3
|
76 if((isTouching == HL_TRUE||pThis->stillTouchingLastMovedElement) && (object != pThis->attractToHapticId)){
|
fiore@3
|
77 if(!pThis->stickyMode)
|
fiore@3
|
78 pThis->executeCommand('p',ed.diagramId,0,0,0,0); // just plays the sound
|
fiore@3
|
79 }else{
|
fiore@3
|
80 pThis->executeCommand('s',ed.diagramId,0,0,0,0);
|
fiore@3
|
81 pThis->lastHighlightElementID = object;
|
fiore@3
|
82 pThis->executeCommand('t',ed.diagramId,0,0,0,0); // speaks the edge's name
|
fiore@3
|
83 }
|
fiore@3
|
84 }else{ // it's a node
|
fiore@3
|
85 pThis->lastTouchedNodeID = object;
|
fiore@3
|
86 CollectionsManager::NodeData & nd = pThis->collectionsManager->getNodeDataFromID(object);
|
fiore@3
|
87 pThis->lastTouchedNodePosition[0] = nd.x;
|
fiore@3
|
88 pThis->lastTouchedNodePosition[1] = nd.y;
|
fiore@3
|
89 pThis->lastTouchedNodePosition[2] = 0;
|
fiore@3
|
90 pThis->frictionDisabled = true;
|
fiore@3
|
91 pThis->executeCommand('s',nd.diagramId,0,0,0,0);
|
fiore@3
|
92 pThis->lastHighlightElementID = object;
|
fiore@3
|
93 pThis->executeCommand('t',nd.diagramId,0,0,0,0);
|
fiore@3
|
94 }
|
fiore@3
|
95 }catch(int){
|
fiore@3
|
96 stopExecution("Touch Call Back: Could not retrieve element from ID");
|
fiore@3
|
97 }
|
fiore@3
|
98 }
|
fiore@3
|
99
|
fiore@3
|
100 void HLCALLBACK HapticManager::hlUntouchCB(HLenum event, HLuint object, HLenum thread,
|
fiore@3
|
101 HLcache *cache, void *userdata)
|
fiore@3
|
102 {
|
fiore@3
|
103 HapticManager *pThis = static_cast<HapticManager*>(userdata);
|
fiore@3
|
104 if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall
|
fiore@3
|
105 return;
|
fiore@3
|
106
|
fiore@3
|
107 if(object == pThis->lastMovedElement){
|
fiore@3
|
108 pThis->lastMovedElement = HL_OBJECT_ANY;
|
fiore@3
|
109 pThis->stillTouchingLastMovedElement = false;
|
fiore@3
|
110 }
|
fiore@3
|
111 if(pThis->lastHighlightElementID == object){
|
fiore@3
|
112 pThis->lastHighlightElementID = HL_OBJECT_ANY;
|
fiore@3
|
113 pThis->executeCommand('u',0,0,0,0,0);
|
fiore@3
|
114 }
|
fiore@3
|
115 if(pThis->lastTouchedEdgeID == object){
|
fiore@3
|
116 pThis->lastTouchedEdgeIsUntouched = true;
|
fiore@3
|
117 }
|
fiore@3
|
118 }
|
fiore@3
|
119
|
fiore@3
|
120
|
fiore@3
|
121 void HLCALLBACK HapticManager::hlMotionCB(HLenum event, HLuint object, HLenum thread,
|
fiore@3
|
122 HLcache *cache, void *userdata)
|
fiore@3
|
123 {
|
fiore@3
|
124 HapticManager *pThis = static_cast<HapticManager*>(userdata);
|
fiore@3
|
125
|
fiore@3
|
126 /* plays the sound to acknowledge the user they're dragging an element around */
|
fiore@3
|
127 static int onceinawhile = 0;
|
fiore@3
|
128 if(pThis->springStatus == DRAGGING){
|
fiore@3
|
129 onceinawhile = (onceinawhile+1)%5;
|
fiore@3
|
130 if(!onceinawhile)
|
fiore@3
|
131 pThis->executeCommand('g',3,0,0,0,0);
|
fiore@3
|
132 }
|
fiore@3
|
133
|
fiore@3
|
134 /* the following code is to play element speech when taking a path through an edge starting from a connected node */
|
fiore@3
|
135
|
fiore@3
|
136 /* friction is disabled when we land on a node. If proxy ain't on a node, then just return */
|
fiore@3
|
137 /* else it would play every time we move along the edge even if not detouching from a node */
|
fiore@3
|
138 if(!pThis->frictionDisabled){
|
fiore@3
|
139 return;
|
fiore@3
|
140 }
|
fiore@3
|
141
|
fiore@3
|
142 /* calculate the distance between the node, the stylus was on, and the current proxy position after moving out from it*/
|
fiore@3
|
143 HLdouble proxyPosition[3];
|
fiore@3
|
144 hlGetDoublev(HL_DEVICE_POSITION,proxyPosition);
|
fiore@3
|
145 proxyPosition[0] -= pThis->lastTouchedNodePosition[0];
|
fiore@3
|
146 proxyPosition[1] -= pThis->lastTouchedNodePosition[1];
|
fiore@3
|
147 proxyPosition[2] -= pThis->lastTouchedNodePosition[2];
|
fiore@3
|
148 if(pThis->lastTouchedEdgeID != HL_OBJECT_ANY){
|
fiore@3
|
149 if(norm(proxyPosition) > EDGE_SPEECH_DISTANCE_WHEN_LEAVING_NODE){
|
fiore@3
|
150 pThis->frictionDisabled = false;
|
fiore@3
|
151 HLboolean isTouching;
|
fiore@3
|
152 hlGetShapeBooleanv(pThis->lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching);
|
fiore@3
|
153 if(isTouching == HL_TRUE){
|
fiore@3
|
154 try{
|
fiore@3
|
155 CollectionsManager::EdgeData & ed = pThis->collectionsManager->getEdgeDataFromID(pThis->lastTouchedEdgeID);
|
fiore@3
|
156 pThis->executeCommand('t',ed.diagramId,0,0,0,0);
|
fiore@3
|
157 pThis->lastTouchedEdgeID = ed.hapticId;
|
fiore@3
|
158 pThis->lastHighlightElementID = ed.hapticId;
|
fiore@3
|
159 pThis->executeCommand('s',ed.diagramId,0,0,0,0);
|
fiore@3
|
160 }catch(int){
|
fiore@3
|
161 stopExecution("Button 1 callback: Could not retrieve edge from ID");
|
fiore@3
|
162 }
|
fiore@3
|
163 }
|
fiore@3
|
164 }
|
fiore@3
|
165 }
|
fiore@3
|
166 }
|
fiore@3
|
167
|
fiore@3
|
168 #ifdef VIBRATION
|
fiore@3
|
169 void HLCALLBACK HapticManager::vibrationCB(HDdouble force[3], HLcache *cache, void *userdata){
|
fiore@3
|
170 static const hduVector3Dd direction( 0, 1, 0 );
|
fiore@3
|
171 HDdouble instRate;
|
fiore@3
|
172 static HDdouble timer = 0;
|
fiore@3
|
173
|
fiore@3
|
174 HapticManager *pThis = static_cast<HapticManager*>(userdata);
|
fiore@3
|
175
|
fiore@3
|
176 /* Use the reciprocal of the instantaneous rate as a timer. */
|
fiore@3
|
177 hdGetDoublev(HD_INSTANTANEOUS_UPDATE_RATE, &instRate);
|
fiore@3
|
178 timer += 1.0 / instRate;
|
fiore@3
|
179
|
fiore@3
|
180 /* Apply a sinusoidal force in the direction of motion. */
|
fiore@3
|
181 hduVecScale(force, direction, VIBRATION_AMPLITUDE * sin(timer * VIBRATION_FREQUENCY));
|
fiore@3
|
182 hdSetDoublev(HD_CURRENT_FORCE, force);
|
fiore@3
|
183 }
|
fiore@3
|
184
|
fiore@3
|
185 void HLCALLBACK HapticManager::beforeVibrationCB(HLcache *cache, void *userdata){}
|
fiore@3
|
186
|
fiore@3
|
187 void HLCALLBACK HapticManager::afterVibrationCB(HLcache *cache, void *userdata){}
|
fiore@3
|
188 #endif
|
fiore@3
|
189
|
fiore@3
|
190 void HapticManager::init(void) throw(HapticException){
|
fiore@3
|
191 HDErrorInfo error;
|
fiore@3
|
192
|
fiore@3
|
193 ghHD = hdInitDevice(HD_DEFAULT_DEVICE);
|
fiore@3
|
194 if (HD_DEVICE_ERROR(error = hdGetError())){
|
fiore@3
|
195 throw HapticException();
|
fiore@3
|
196 }
|
fiore@3
|
197
|
fiore@3
|
198 ghHLRC = hlCreateContext(ghHD);
|
fiore@3
|
199 hlMakeCurrent(ghHLRC);
|
fiore@3
|
200
|
fiore@3
|
201 // Enable optimization of the viewing parameters when rendering geometry for OpenHaptics.
|
fiore@3
|
202 hlEnable(HL_HAPTIC_CAMERA_VIEW);
|
fiore@3
|
203
|
fiore@3
|
204 hlTouchableFace(HL_FRONT);
|
fiore@3
|
205 wall1Id = hlGenShapes(1);
|
fiore@3
|
206 wall2Id = hlGenShapes(1);
|
fiore@3
|
207 attractToHapticId = HL_OBJECT_ANY;
|
fiore@3
|
208 lastTouchedNodeID = HL_OBJECT_ANY;
|
fiore@3
|
209 lastTouchedEdgeID = HL_OBJECT_ANY;
|
fiore@3
|
210 draggedElementID = HL_OBJECT_ANY;
|
fiore@3
|
211 lastHighlightElementID = HL_OBJECT_ANY;
|
fiore@3
|
212
|
fiore@3
|
213 // initialize effects
|
fiore@3
|
214 frictionFX = hlGenEffects(1);
|
fiore@3
|
215 vibrationFX = hlGenEffects(1);
|
fiore@3
|
216 attractionFX = hlGenEffects(1);
|
fiore@3
|
217 springFX = hlGenEffects(1);
|
fiore@3
|
218 displayList1 = 0;
|
fiore@3
|
219 displayList2 = 0;
|
fiore@3
|
220 hlEventd(HL_EVENT_MOTION_LINEAR_TOLERANCE,5);
|
fiore@3
|
221 hlEventd(HL_EVENT_MOTION_ANGULAR_TOLERANCE,5);
|
fiore@3
|
222
|
fiore@3
|
223 // add callbacks
|
fiore@3
|
224 hlAddEventCallback(HL_EVENT_2BUTTONDOWN,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton2CB,this);
|
fiore@3
|
225 hlAddEventCallback(HL_EVENT_2BUTTONUP,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton2CB,this);
|
fiore@3
|
226 hlAddEventCallback(HL_EVENT_1BUTTONUP,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton1CB,this);
|
fiore@3
|
227 hlAddEventCallback(HL_EVENT_1BUTTONDOWN,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton1CB,this);
|
fiore@3
|
228 hlAddEventCallback(HL_EVENT_TOUCH, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlTouchCB, this);
|
fiore@3
|
229 hlAddEventCallback(HL_EVENT_UNTOUCH, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlUntouchCB, this);
|
fiore@3
|
230 hlAddEventCallback(HL_EVENT_MOTION, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlMotionCB, this);
|
fiore@3
|
231
|
fiore@3
|
232 #ifdef VIBRATION
|
fiore@3
|
233 hlBeginFrame();
|
fiore@3
|
234 hlCallback(HL_EFFECT_START,(HLcallbackProc) beforeVibrationCB, this);
|
fiore@3
|
235 hlCallback(HL_EFFECT_STOP,(HLcallbackProc) afterVibrationCB, this);
|
fiore@3
|
236 hlCallback(HL_EFFECT_COMPUTE_FORCE, (HLcallbackProc) vibrationCB, this);
|
fiore@3
|
237 hlEndFrame();
|
fiore@3
|
238 #endif
|
fiore@3
|
239 }
|
fiore@3
|
240
|
fiore@3
|
241 HapticManager::ClickStatus HapticManager::getButton1Status(){
|
fiore@3
|
242 if(clickStart == 0){
|
fiore@3
|
243 return NO_CLICK;
|
fiore@3
|
244 }else if(doubleClick){
|
fiore@3
|
245 /* double click: set everything back to default value and return DOUBLE_CLICK */
|
fiore@3
|
246 clickStart = 0;
|
fiore@3
|
247 doubleClick = false;
|
fiore@3
|
248 return TWO_CLICK;
|
fiore@3
|
249 }else if(clock() >= clickStart + CLICK_INTERVAL || secondClick) {
|
fiore@3
|
250 /* one click and enough time elapsed, so that it cannot be a double click */
|
fiore@3
|
251 clickStart = 0;
|
fiore@3
|
252 return ONE_CLICK;
|
fiore@3
|
253 }
|
fiore@3
|
254 return NO_CLICK;
|
fiore@3
|
255 }
|
fiore@3
|
256
|
fiore@3
|
257 void HapticManager::doButton1Click(bool doubleClick){
|
fiore@3
|
258 /* DOUBLE CLICK*/
|
fiore@3
|
259 if(doubleClick){
|
fiore@3
|
260 if(lastTouchedNodeID != HL_OBJECT_ANY){
|
fiore@3
|
261 HLboolean isTouching = HL_FALSE;
|
fiore@3
|
262 hlGetShapeBooleanv(lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching); // priority to nodes
|
fiore@3
|
263 if(isTouching == HL_TRUE||lastTouchedNodeID == lastMovedElement){
|
fiore@3
|
264 try{
|
fiore@3
|
265 CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(lastTouchedNodeID);
|
fiore@3
|
266 executeCommand('i',nd.diagramId, 0,0,0,0);
|
fiore@3
|
267 }catch(int){
|
fiore@3
|
268 stopExecution("Button 1 callback: Could not retrieve node from ID");
|
fiore@3
|
269 }
|
fiore@3
|
270 }else{
|
fiore@3
|
271 if(lastTouchedEdgeID != HL_OBJECT_ANY){
|
fiore@3
|
272 hlGetShapeBooleanv(lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching);
|
fiore@3
|
273 if(isTouching == HL_TRUE||lastTouchedNodeID == lastMovedElement){
|
fiore@3
|
274 try{
|
fiore@3
|
275 CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(lastTouchedEdgeID);
|
fiore@3
|
276 executeCommand('i',ed.diagramId, 0,0,0,0);
|
fiore@3
|
277 }catch(int){
|
fiore@3
|
278 stopExecution("Button 1 callback: Could not retrieve edge from ID");
|
fiore@3
|
279 }
|
fiore@3
|
280 }
|
fiore@3
|
281 }
|
fiore@3
|
282 }
|
fiore@3
|
283 }
|
fiore@3
|
284 // this was a double click, thus clean up the data about the dragging
|
fiore@3
|
285 if(springStatus == DRAGGING){
|
fiore@3
|
286 springStatus = STOP_DRAGGING;
|
fiore@3
|
287 secondClick = false;
|
fiore@3
|
288 }
|
fiore@3
|
289 return;
|
fiore@3
|
290 }
|
fiore@3
|
291
|
fiore@3
|
292 /* - SINLGE CLICK - */
|
fiore@3
|
293 if(!secondClick){ // button 1 pressed for the first time
|
fiore@3
|
294 HLboolean isTouching;
|
fiore@3
|
295 /* if the stylus is touching a node then pick it up */
|
fiore@3
|
296 /* give priority to nodes */
|
fiore@3
|
297 if(lastTouchedNodeID != HL_OBJECT_ANY){
|
fiore@3
|
298 hlGetShapeBooleanv(lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching);
|
fiore@3
|
299 if(isTouching == HL_TRUE || (stillTouchingLastMovedElement&&lastMovedElement == lastTouchedNodeID)){
|
fiore@3
|
300 draggedElementID = lastTouchedNodeID;
|
fiore@3
|
301 try{
|
fiore@3
|
302 CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(lastTouchedNodeID);
|
fiore@3
|
303 executeCommand('c',nd.diagramId,0,0,0,0); // pick up command
|
fiore@3
|
304 }catch(int){
|
fiore@3
|
305 stopExecution("Button 1 callback: Could not retrieve node from ID");
|
fiore@3
|
306 }
|
fiore@3
|
307 return;
|
fiore@3
|
308 }
|
fiore@3
|
309 }
|
fiore@3
|
310 /* no node is currently touched, check the edges now */
|
fiore@3
|
311 if(collectionsManager->isEdge(lastTouchedEdgeID)){
|
fiore@3
|
312 hlGetShapeBooleanv(lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching);
|
fiore@3
|
313 if(isTouching == HL_TRUE||(stillTouchingLastMovedElement&&lastMovedElement == lastTouchedEdgeID)){
|
fiore@3
|
314 draggedElementID = lastTouchedEdgeID;
|
fiore@3
|
315 try{
|
fiore@3
|
316 CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(lastTouchedEdgeID);
|
fiore@3
|
317 executeCommand('c',ed.diagramId,0,0,0,0); // pick up command
|
fiore@3
|
318 }catch(int){
|
fiore@3
|
319 stopExecution("Button 1 callback: Could not retrieve edge from ID");
|
fiore@3
|
320 }
|
fiore@3
|
321 }
|
fiore@3
|
322 }
|
fiore@3
|
323 }else{ // button 1 pressed for the second time
|
fiore@3
|
324 if(springStatus != DRAGGING)
|
fiore@3
|
325 return;
|
fiore@3
|
326 springStatus = STOP_DRAGGING;
|
fiore@3
|
327 HLdouble leanPoint[3];
|
fiore@3
|
328 hlGetDoublev(HL_PROXY_POSITION,leanPoint);
|
fiore@3
|
329 leanPoint[2] = 1;
|
fiore@3
|
330 hduVector3Dd winCoordPoint;
|
fiore@3
|
331 toScreen(hduVector3Dd(leanPoint),winCoordPoint);
|
fiore@3
|
332
|
fiore@3
|
333 try{
|
fiore@3
|
334 if(collectionsManager->isNode(draggedElementID)){
|
fiore@3
|
335 CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(draggedElementID);
|
fiore@3
|
336 movedNode = true;
|
fiore@3
|
337 executeCommand('m',nd.diagramId,winCoordPoint[0],collectionsManager->getScreenHeight() - winCoordPoint[1],0,0);
|
fiore@3
|
338 }else if(collectionsManager->isEdge(draggedElementID)){ // double check necessary for the user might have changed the tab or closed the window before releasing the spring
|
fiore@3
|
339 springStart[2] = 1;
|
fiore@3
|
340 hduVector3Dd startWinCoordPoint;
|
fiore@3
|
341 toScreen(hduVector3Dd(springStart),startWinCoordPoint);//it's an edge, we also need the point where the motion has started
|
fiore@3
|
342 CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(draggedElementID);
|
fiore@3
|
343 executeCommand('m', ed.diagramId,
|
fiore@3
|
344 winCoordPoint[0],
|
fiore@3
|
345 collectionsManager->getScreenHeight() - winCoordPoint[1],
|
fiore@3
|
346 startWinCoordPoint[0],
|
fiore@3
|
347 collectionsManager->getScreenHeight() - startWinCoordPoint[1]
|
fiore@3
|
348 );
|
fiore@3
|
349 }
|
fiore@3
|
350 }catch(int){
|
fiore@3
|
351 stopExecution("Button 1 callback: Could not retrieve element from ID");
|
fiore@3
|
352 }
|
fiore@3
|
353 /* with second click we place the element and it becomes the last moved element */
|
fiore@3
|
354 lastMovedElement = draggedElementID;
|
fiore@3
|
355 secondClick = false;
|
fiore@3
|
356 }
|
fiore@3
|
357 }
|
fiore@3
|
358
|
fiore@3
|
359 void HapticManager::setAttractTo(jint hapticId){
|
fiore@3
|
360 if(attractionStatus == NO_ATTRACTION) // starts only if there is no previous attraction being overwritten
|
fiore@3
|
361 attractionStatus = START_ATTRACTION;
|
fiore@3
|
362 attractToHapticId = hapticId;
|
fiore@3
|
363 alreadyTouchingAttractingElement = false;
|
fiore@3
|
364 }
|
fiore@3
|
365
|
fiore@3
|
366 void HapticManager::pickUp(jint hapticId){
|
fiore@3
|
367 springStatus = START_DRAGGING;
|
fiore@3
|
368 secondClick = true;
|
fiore@3
|
369 hlGetDoublev(HL_PROXY_POSITION,springStart);
|
fiore@3
|
370 }
|
fiore@3
|
371
|
fiore@3
|
372 bool HapticManager::wasAlreadyTouchingAttractingElement(){
|
fiore@3
|
373 return alreadyTouchingAttractingElement;
|
fiore@3
|
374 }
|
fiore@3
|
375
|
fiore@3
|
376 void HapticManager::draw(void){
|
fiore@3
|
377 // Start haptic frame. (Must do this before rendering any haptic shapes.)
|
fiore@3
|
378 hlBeginFrame();
|
fiore@3
|
379
|
fiore@3
|
380 if(!doneOnce){
|
fiore@3
|
381 hlStartEffect(HL_EFFECT_FRICTION, frictionFX);
|
fiore@3
|
382 doneOnce = true;
|
fiore@3
|
383 }
|
fiore@3
|
384
|
fiore@3
|
385 if(wall){
|
fiore@3
|
386 // Plane shape.
|
fiore@3
|
387 hlPushAttrib(HL_MATERIAL_BIT|HL_TOUCH_BIT);
|
fiore@3
|
388 hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, wall1Id);
|
fiore@3
|
389 hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1);
|
fiore@3
|
390 hlMaterialf(HL_FRONT, HL_POPTHROUGH, 0 );
|
fiore@3
|
391 hlMaterialf(HL_FRONT, HL_STATIC_FRICTION, 0);
|
fiore@3
|
392 hlMaterialf(HL_FRONT, HL_DYNAMIC_FRICTION, 0);
|
fiore@3
|
393 hlTouchModel(HL_CONTACT);
|
fiore@3
|
394 hlTouchableFace(HL_FRONT);
|
fiore@3
|
395 drawPlane(-0.005);
|
fiore@3
|
396 hlEndShape();
|
fiore@3
|
397 #define SECOND_PLANE
|
fiore@3
|
398 #ifdef SECOND_PLANE
|
fiore@3
|
399 hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, wall2Id);
|
fiore@3
|
400 hlMaterialf(HL_BACK, HL_STIFFNESS, 1);
|
fiore@3
|
401 hlTouchableFace(HL_BACK);
|
fiore@3
|
402 hlMaterialf(HL_FRONT, HL_POPTHROUGH, 0 );
|
fiore@3
|
403 hlMaterialf(HL_FRONT, HL_STATIC_FRICTION, 0);
|
fiore@3
|
404 hlMaterialf(HL_FRONT, HL_DYNAMIC_FRICTION, 0);
|
fiore@3
|
405 drawPlane(0.005);
|
fiore@3
|
406 hlEndShape();
|
fiore@3
|
407 #endif
|
fiore@3
|
408 hlPopAttrib();
|
fiore@3
|
409 }
|
fiore@3
|
410
|
fiore@3
|
411 // Start a new haptic shape. Use the feedback buffer to capture OpenGL
|
fiore@3
|
412 // geometry for haptic rendering.
|
fiore@3
|
413
|
fiore@3
|
414 // draw nodes
|
fiore@3
|
415 int size = collectionsManager->getNodesNum();
|
fiore@3
|
416
|
fiore@3
|
417 for(int i=0; i< size;i++){
|
fiore@3
|
418 CollectionsManager::NodeData & nd = collectionsManager->getNodeData(i);
|
fiore@3
|
419
|
fiore@3
|
420 hlPushAttrib(HL_HINT_BIT);
|
fiore@3
|
421 hlHinti(HL_SHAPE_FEEDBACK_BUFFER_VERTICES, 3);
|
fiore@3
|
422 hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, nd.hapticId);
|
fiore@3
|
423 hlTouchModel(HL_CONSTRAINT);
|
fiore@3
|
424 hlTouchModelf(HL_SNAP_DISTANCE , 4.0f );
|
fiore@3
|
425 hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1.0);
|
fiore@3
|
426 hlMaterialf(HL_FRONT_AND_BACK, HL_DAMPING, 0);
|
fiore@3
|
427
|
fiore@3
|
428 hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION, 0);
|
fiore@3
|
429 hlMaterialf(HL_FRONT_AND_BACK, HL_DYNAMIC_FRICTION, 0);
|
fiore@3
|
430
|
fiore@3
|
431 //draw the point
|
fiore@3
|
432 glBegin(GL_POINTS);
|
fiore@3
|
433 glVertex3d(nd.x,nd.y,0);
|
fiore@3
|
434 glEnd();
|
fiore@3
|
435
|
fiore@3
|
436 // End the shape.
|
fiore@3
|
437 hlEndShape();
|
fiore@3
|
438 hlPopAttrib();
|
fiore@3
|
439
|
fiore@3
|
440 // saves the coorinates for a later use so that for the effect we don't need to cycle again
|
fiore@3
|
441 if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){
|
fiore@3
|
442 if(nd.hapticId == attractToHapticId){
|
fiore@3
|
443 attractToCoord[0] = nd.x;
|
fiore@3
|
444 attractToCoord[1] = nd.y;
|
fiore@3
|
445 attractToCoord[2] = 0;
|
fiore@3
|
446 }
|
fiore@3
|
447 }
|
fiore@3
|
448 }
|
fiore@3
|
449
|
fiore@5
|
450 /* draw lines. When a node is moved, edges are not drawn for the very first haptic frame after the shift. *
|
fiore@3
|
451 * This is to address the behaviour of the device which, after a n ode is moved cannot see the styilus *
|
fiore@3
|
452 * touching the edge. As a consequence of that in hlMotionCB() when moving away from a node along an edge *
|
fiore@3
|
453 * no edge name will be spoken and furthermore the pick up botton won't work as the device thinks it's not *
|
fiore@3
|
454 * touching anything. Not drawing the edge on the first frame, seems to address this issue. */
|
fiore@3
|
455 if(!movedNode) {
|
fiore@3
|
456 size = collectionsManager->getEdgesNum();
|
fiore@3
|
457 for(int i = 0; i<size; i++){
|
fiore@3
|
458 CollectionsManager::EdgeData & ed = collectionsManager->getEdgeData(i);
|
fiore@3
|
459 hlPushAttrib(HL_HINT_BIT|HL_MATERIAL_BIT);
|
fiore@3
|
460 hlHinti(HL_SHAPE_FEEDBACK_BUFFER_VERTICES, ed.getSize());
|
fiore@3
|
461
|
fiore@3
|
462 hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, ed.hapticId);
|
fiore@3
|
463 hlTouchModel(HL_CONSTRAINT);
|
fiore@3
|
464 hlTouchModelf(HL_SNAP_DISTANCE, ( (stickyMode && springStatus != DRAGGING && springStatus != START_DRAGGING) ? 20.0f : 3.5f ));
|
fiore@3
|
465 hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1);
|
fiore@3
|
466
|
fiore@3
|
467 if(ed.stipplePattern == 0xFFFF){
|
fiore@3
|
468 hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION, 0.5f);
|
fiore@3
|
469 }
|
fiore@3
|
470
|
fiore@3
|
471 glLineWidth(1.0);
|
fiore@3
|
472
|
fiore@3
|
473 glBegin(GL_LINES);
|
fiore@3
|
474 for(unsigned int j = 0; j < ed.getSize(); j++){
|
fiore@3
|
475 for(unsigned int k = j; k < ed.getSize(); k++){
|
fiore@3
|
476 if(ed.adjMatrix[j][k]){
|
fiore@3
|
477 if( k >= ed.nodeStart &&
|
fiore@3
|
478 (!stickyMode || (springStatus != RELEASED && springStatus != STOP_DRAGGING))
|
fiore@3
|
479 && attractionStatus != DOING_ATTRACTION && attractionStatus != START_ATTRACTION){ // not a line connecting two edges and stickyMode not enabled
|
fiore@3
|
480 /* it's drawing a line from a point to a node, thus it draws it a little shorter*/
|
fiore@3
|
481 double hypotenuse = sqrt( (ed.x[j] - ed.x[k])*(ed.x[j] - ed.x[k]) + (ed.y[j] - ed.y[k])*(ed.y[j] - ed.y[k]));
|
fiore@3
|
482 if(hypotenuse == 0)
|
fiore@3
|
483 continue;
|
fiore@3
|
484 double mySin = (ed.y[j] - ed.y[k])/hypotenuse;
|
fiore@3
|
485 double myCos = (ed.x[j] - ed.x[k])/hypotenuse;
|
fiore@3
|
486 glVertex3d(ed.x[k]+(EDGE_SHORTEND_VAL * myCos) , ed.y[k]+(EDGE_SHORTEND_VAL*mySin),0);
|
fiore@3
|
487 if(j >= ed.nodeStart) //its a direct edge connecting two nodes
|
fiore@3
|
488 glVertex3d(ed.x[j]+(EDGE_SHORTEND_VAL * -myCos),ed.y[j]+(EDGE_SHORTEND_VAL*-mySin),0);
|
fiore@3
|
489 else
|
fiore@3
|
490 glVertex3d(ed.x[j],ed.y[j],0);
|
fiore@3
|
491 }else{
|
fiore@3
|
492 glVertex3d(ed.x[j],ed.y[j],0);
|
fiore@3
|
493 glVertex3d(ed.x[k],ed.y[k],0);
|
fiore@3
|
494 }
|
fiore@3
|
495 }
|
fiore@3
|
496 }
|
fiore@3
|
497 }
|
fiore@3
|
498 glEnd();
|
fiore@3
|
499 hlEndShape();
|
fiore@3
|
500 hlPopAttrib();
|
fiore@3
|
501
|
fiore@3
|
502 // saves the coorinates for a later use so that for the effect we don't need to cycle again
|
fiore@3
|
503 if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){
|
fiore@3
|
504 if(ed.hapticId == attractToHapticId){
|
fiore@3
|
505 attractToCoord[0] = ed.attractPoint[0];
|
fiore@3
|
506 attractToCoord[1] = ed.attractPoint[1];
|
fiore@3
|
507 attractToCoord[2] = 0;
|
fiore@3
|
508 }
|
fiore@3
|
509 }
|
fiore@3
|
510 }
|
fiore@3
|
511 }else {
|
fiore@3
|
512 movedNode = false;
|
fiore@3
|
513 }
|
fiore@3
|
514 /* --- FORCES --- */
|
fiore@3
|
515 /* friction */
|
fiore@3
|
516 if(lastTouchedEdgeID != HL_OBJECT_ANY){
|
fiore@3
|
517 /* check if the edge is still there, it might not as the tab could be changed */
|
fiore@3
|
518 if(collectionsManager->isEdge(lastTouchedEdgeID)){
|
fiore@3
|
519 //hlGetShapeBooleanv(lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching); FIXME to remove
|
fiore@3
|
520 if(frictionDisabled||lastTouchedEdgeIsUntouched/*(isTouching == HL_FALSE)*/){
|
fiore@3
|
521 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0);
|
fiore@3
|
522 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0);
|
fiore@3
|
523 }else{
|
fiore@3
|
524 if(lastTouchedEdgeStipplePattern == 0xAAAA){ // dotted
|
fiore@3
|
525 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.4f);
|
fiore@3
|
526 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 1);
|
fiore@3
|
527 }else if(lastTouchedEdgeStipplePattern == 0xF0F0){ // dashed
|
fiore@3
|
528 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.6);
|
fiore@3
|
529 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.6);
|
fiore@3
|
530 }
|
fiore@3
|
531 }
|
fiore@3
|
532 }else{
|
fiore@3
|
533 lastTouchedEdgeID = HL_OBJECT_ANY;
|
fiore@3
|
534 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0);
|
fiore@3
|
535 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0);
|
fiore@3
|
536 }
|
fiore@3
|
537 hlUpdateEffect(frictionFX);
|
fiore@3
|
538 }
|
fiore@3
|
539
|
fiore@3
|
540 // spring
|
fiore@3
|
541 if(springStatus == START_DRAGGING){
|
fiore@3
|
542 hlPushAttrib(HL_EFFECT_BIT);
|
fiore@3
|
543 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.3);
|
fiore@3
|
544 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.5);
|
fiore@3
|
545
|
fiore@3
|
546 hduMatrix worldToView;
|
fiore@3
|
547 hduMatrix graphicToTouch;
|
fiore@3
|
548 hduMatrix touchToWorkspace;
|
fiore@3
|
549 hduMatrix WorldToDevice;
|
fiore@3
|
550 glMatrixMode(GL_MODELVIEW);
|
fiore@3
|
551 glGetDoublev(GL_MODELVIEW_MATRIX,worldToView);
|
fiore@3
|
552 hlGetDoublev(HL_VIEWTOUCH_MATRIX, graphicToTouch );
|
fiore@3
|
553 hlGetDoublev(HL_TOUCHWORKSPACE_MATRIX, touchToWorkspace );
|
fiore@3
|
554 WorldToDevice = worldToView * graphicToTouch * touchToWorkspace;
|
fiore@3
|
555
|
fiore@3
|
556 hduVector3Dd dst;
|
fiore@3
|
557 WorldToDevice.multVecMatrix(hduVector3Dd(springStart[0],springStart[1],0),dst);
|
fiore@3
|
558
|
fiore@3
|
559 hlEffectdv(HL_EFFECT_PROPERTY_POSITION, dst);
|
fiore@3
|
560 hlStartEffect(HL_EFFECT_SPRING, springFX);
|
fiore@3
|
561 hlPopAttrib();
|
fiore@3
|
562 springStatus = DRAGGING;
|
fiore@3
|
563 }else if(springStatus == STOP_DRAGGING){
|
fiore@3
|
564 hlStopEffect(springFX);
|
fiore@3
|
565 springStatus = RELEASED;
|
fiore@3
|
566 }
|
fiore@3
|
567
|
fiore@3
|
568 // attraction
|
fiore@3
|
569 HLboolean isTouching = HL_FALSE;
|
fiore@3
|
570 if(attractionStatus == DOING_ATTRACTION || attractionStatus == START_ATTRACTION){
|
fiore@3
|
571 /* we need to be sure we're currently touching the element */
|
fiore@3
|
572 if(attractToHapticId != HL_OBJECT_ANY){
|
fiore@3
|
573 hlGetShapeBooleanv(attractToHapticId,HL_PROXY_IS_TOUCHING,&isTouching);
|
fiore@3
|
574 }
|
fiore@3
|
575
|
fiore@3
|
576 // when touching the element we're getting attracted to, then shut the force down
|
fiore@3
|
577 if(isTouching == HL_TRUE){
|
fiore@3
|
578 if(attractionStatus == DOING_ATTRACTION){ // we finally reached the attracting node
|
fiore@3
|
579 attractionStatus = STOP_ATTRACTION;
|
fiore@3
|
580 }else{
|
fiore@3
|
581 attractionStatus = NO_ATTRACTION; // we were already touching the node we wanted to get attracted to
|
fiore@3
|
582 alreadyTouchingAttractingElement = true;
|
fiore@3
|
583 }
|
fiore@3
|
584 }
|
fiore@3
|
585 }
|
fiore@3
|
586
|
fiore@3
|
587 if(attractionStatus == STOP_ATTRACTION){
|
fiore@3
|
588 attractionStatus = NO_ATTRACTION;
|
fiore@3
|
589 hlStopEffect(attractionFX);
|
fiore@3
|
590 } else if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){
|
fiore@3
|
591 HLdouble devicePosition[3];
|
fiore@3
|
592 HDdouble direction[3];
|
fiore@3
|
593 hlGetDoublev(HL_DEVICE_POSITION,devicePosition);
|
fiore@3
|
594 direction[0] = attractToCoord[0] - devicePosition[0];
|
fiore@3
|
595 direction[1] = attractToCoord[1] - devicePosition[1];
|
fiore@3
|
596 direction[2] = attractToCoord[2] - devicePosition[2];
|
fiore@3
|
597 HDdouble directionNorm = norm(direction);
|
fiore@3
|
598 if(directionNorm != 0 ){
|
fiore@3
|
599 direction[0] /= directionNorm;
|
fiore@3
|
600 direction[1] /= directionNorm;
|
fiore@3
|
601 direction[2] /= directionNorm;
|
fiore@3
|
602 }
|
fiore@3
|
603 hlPushAttrib(HL_EFFECT_BIT);
|
fiore@3
|
604 hlEffectdv(HL_EFFECT_PROPERTY_DIRECTION, direction);
|
fiore@3
|
605 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.5f);
|
fiore@3
|
606 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.5f);
|
fiore@3
|
607 if(attractionStatus == START_ATTRACTION){
|
fiore@3
|
608 hlStartEffect(HL_EFFECT_CONSTANT, attractionFX);
|
fiore@3
|
609 attractionStatus = DOING_ATTRACTION;
|
fiore@3
|
610 }else{
|
fiore@3
|
611 hlUpdateEffect(attractionFX);
|
fiore@3
|
612 }
|
fiore@3
|
613 hlPopAttrib();
|
fiore@3
|
614 }
|
fiore@3
|
615 #ifdef VIBRATION
|
fiore@3
|
616 if(vibrationStatus == START_VIBRATION){
|
fiore@3
|
617 hlStartEffect(HL_EFFECT_CALLBACK, vibrationFX);
|
fiore@3
|
618 vibrationEnd = clock() + VIBRATION_DURATION;
|
fiore@3
|
619 vibrationStatus = DOING_VIBRATION;
|
fiore@3
|
620 }else if(vibrationStatus == DOING_VIBRATION){
|
fiore@3
|
621 if(clock() >= vibrationEnd){
|
fiore@3
|
622 hlStopEffect(vibrationFX);
|
fiore@3
|
623 vibrationStatus = NO_VIBRATION;
|
fiore@3
|
624 }
|
fiore@3
|
625 }
|
fiore@3
|
626 #endif
|
fiore@3
|
627 // End the haptic frame.
|
fiore@3
|
628 hlEndFrame();
|
fiore@3
|
629 }
|
fiore@3
|
630
|
fiore@3
|
631 void HapticManager::drawPlane(float zoffset){
|
fiore@3
|
632 GLuint displayList = (zoffset < 0) ? displayList1 : displayList2;
|
fiore@3
|
633
|
fiore@3
|
634 if (displayList){
|
fiore@3
|
635 glCallList(displayList);
|
fiore@3
|
636 }
|
fiore@3
|
637 else{
|
fiore@3
|
638 if(zoffset < 0){
|
fiore@3
|
639 displayList1 = glGenLists(1);
|
fiore@3
|
640 glNewList(displayList1, GL_COMPILE_AND_EXECUTE);
|
fiore@3
|
641 }else{
|
fiore@3
|
642 displayList2 = glGenLists(1);
|
fiore@3
|
643 glNewList(displayList2, GL_COMPILE_AND_EXECUTE);
|
fiore@3
|
644 }
|
fiore@3
|
645 glNewList(displayList, GL_COMPILE_AND_EXECUTE);
|
fiore@3
|
646 glPushAttrib(GL_ENABLE_BIT);
|
fiore@3
|
647 glPolygonOffset(1,1);
|
fiore@3
|
648 glEnable(GL_POLYGON_OFFSET_FILL);
|
fiore@3
|
649
|
fiore@3
|
650 glBegin(GL_QUADS);
|
fiore@3
|
651 glVertex3f(-5, -5, zoffset);
|
fiore@3
|
652 glVertex3f(5, -5, zoffset);
|
fiore@3
|
653 glVertex3f(5, 5, zoffset);
|
fiore@3
|
654 glVertex3f(-5, 5, zoffset);
|
fiore@3
|
655 glEnd();
|
fiore@3
|
656 glDisable(GL_POLYGON_OFFSET_FILL);
|
fiore@3
|
657 glPopAttrib();
|
fiore@3
|
658 glEndList();
|
fiore@3
|
659 }
|
fiore@3
|
660
|
fiore@3
|
661 }
|
fiore@3
|
662
|
fiore@3
|
663 HHD HapticManager::ghHD = HD_INVALID_HANDLE;
|
fiore@3
|
664 HHLRC HapticManager::ghHLRC = 0;
|
fiore@3
|
665 const double HapticManager::EDGE_SHORTEND_VAL = 0.06;
|
fiore@3
|
666 const unsigned int HapticManager::CLICK_INTERVAL = CLOCKS_PER_SEC/5;
|
fiore@3
|
667 const double HapticManager::EDGE_SPEECH_DISTANCE_WHEN_LEAVING_NODE = 0.03;
|
fiore@3
|
668 #ifdef VIBRATION
|
fiore@3
|
669 const HDdouble HapticManager::VIBRATION_AMPLITUDE = 0.88;//0.88;
|
fiore@3
|
670 const HDint HapticManager::VIBRATION_FREQUENCY = 170;//160;
|
fiore@3
|
671 const unsigned int HapticManager::VIBRATION_DURATION = CLOCKS_PER_SEC/10; // = 100 ms
|
fiore@3
|
672 #endif
|