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