fiore@5
|
1 /*
|
fiore@5
|
2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
|
fiore@5
|
3
|
fiore@5
|
4 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
|
fiore@5
|
5
|
fiore@5
|
6 This program is free software: you can redistribute it and/or modify
|
fiore@5
|
7 it under the terms of the GNU General Public License as published by
|
fiore@5
|
8 the Free Software Foundation, either version 3 of the License, or
|
fiore@5
|
9 (at your option) any later version.
|
fiore@5
|
10
|
fiore@5
|
11 This program is distributed in the hope that it will be useful,
|
fiore@5
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
fiore@5
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
fiore@5
|
14 GNU General Public License for more details.
|
fiore@5
|
15
|
fiore@5
|
16 You should have received a copy of the GNU General Public License
|
fiore@5
|
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
fiore@5
|
18 */
|
fiore@5
|
19
|
fiore@5
|
20 #include "HapticManager.h"
|
fiore@5
|
21
|
fiore@5
|
22 using namespace HAPI;
|
fiore@5
|
23
|
fiore@5
|
24 // Render a sphere using OpenGL calls.
|
fiore@5
|
25 void drawSphere() {
|
fiore@5
|
26 GLUquadricObj* pObj = gluNewQuadric();
|
fiore@5
|
27 gluSphere(pObj, 0.0010, 10, 10);
|
fiore@5
|
28 gluDeleteQuadric(pObj );
|
fiore@5
|
29 }
|
fiore@5
|
30
|
fiore@5
|
31 HapticManager::HapticManager(CollectionsManager * cManager,
|
fiore@5
|
32 void (*func)(
|
fiore@5
|
33 const jchar cmd,
|
fiore@5
|
34 const jint ID,
|
fiore@5
|
35 const jdouble startx,
|
fiore@5
|
36 const jdouble starty,
|
fiore@5
|
37 const jdouble endx,
|
fiore@5
|
38 const jdouble endy)
|
fiore@5
|
39 )
|
fiore@5
|
40 : executeCommand(func), cm(cManager), force_effect(0), last_touched_point(NULL),
|
fiore@5
|
41 last_touched_line(NULL),magnetic_mode(LOOSE), pickup_mode(RELEASED), object_unselected(false),
|
fiore@5
|
42 magnetic_mode_changed(false), pickedup_point(NULL), pickedup_line(NULL) {}
|
fiore@5
|
43
|
fiore@5
|
44
|
fiore@5
|
45 bool HapticManager::init (void){
|
fiore@5
|
46
|
fiore@5
|
47 hd = new AnyHapticsDevice();
|
fiore@5
|
48
|
fiore@5
|
49 // The haptics renderer to use.
|
fiore@5
|
50 hd->setHapticsRenderer( new HAPI::GodObjectRenderer() );
|
fiore@5
|
51
|
fiore@5
|
52 /* init the device */
|
fiore@5
|
53 if(hd->initDevice() != HAPIHapticsDevice::SUCCESS){
|
fiore@5
|
54 return false;
|
fiore@5
|
55 }
|
fiore@5
|
56
|
fiore@5
|
57 /* enable the device ( forces and positions will be updated) */
|
fiore@5
|
58 hd->enableDevice();
|
fiore@5
|
59
|
fiore@5
|
60 return true;
|
fiore@5
|
61 }
|
fiore@5
|
62
|
fiore@5
|
63 /* draws the visual diagram every time it's called. The haptic diagram is redrawn only if *
|
fiore@5
|
64 * something has changed in the diagram ( redraw_haptic_scene = true ) as, unlike openGL, *
|
fiore@5
|
65 * it doesn't need to be drawn at each frame but only once */
|
fiore@5
|
66 void HapticManager::drawDiagram(bool redraw_haptics_scene, bool pickup, int _attract_to ){
|
fiore@5
|
67
|
fiore@5
|
68 /* STOP_ATTRACTION means the object has been reached (or eventually deleted). The attraction *
|
fiore@5
|
69 * force must therefore be stopped and a redrawing of the haptic forces is necessary */
|
fiore@5
|
70 if(attraction_mode == STOP_ATTRACTION){
|
fiore@5
|
71 attraction_mode = NO_ATTRACTION;
|
fiore@5
|
72 attract_to = NO_ID;
|
fiore@5
|
73 redraw_haptics_scene = true;
|
fiore@5
|
74 }
|
fiore@5
|
75
|
fiore@5
|
76 /* _attract_to is different from NO_ID there is no attraction going on (attraction_mode = NO_ATTRACTION)*
|
fiore@5
|
77 * It differs from STOP_ATTRACTION in that the haptic scene doesn't have to be repainted */
|
fiore@5
|
78 if(_attract_to != NO_ID){
|
fiore@5
|
79 attract_to = _attract_to;
|
fiore@5
|
80 attraction_mode = ACTIVE;
|
fiore@5
|
81 redraw_haptics_scene = true;
|
fiore@5
|
82 }
|
fiore@5
|
83
|
fiore@5
|
84 if(magnetic_mode_changed){
|
fiore@5
|
85 redraw_haptics_scene = true;
|
fiore@5
|
86 magnetic_mode_changed = false;
|
fiore@5
|
87 }
|
fiore@5
|
88
|
fiore@5
|
89 if(pickup){
|
fiore@5
|
90 pickup_mode = START_DRAGGING;
|
fiore@5
|
91 redraw_haptics_scene = true;
|
fiore@5
|
92 }
|
fiore@5
|
93
|
fiore@5
|
94 /* wash before use */
|
fiore@5
|
95 if(redraw_haptics_scene){
|
fiore@5
|
96 point_set.clear();
|
fiore@5
|
97 line_set.clear();
|
fiore@5
|
98 point_id_map.clear();
|
fiore@5
|
99 line_id_map.clear();
|
fiore@5
|
100 }
|
fiore@5
|
101
|
fiore@5
|
102 /* --- draw the edges --- */
|
fiore@5
|
103 glPushAttrib(GL_ENABLE_BIT);
|
fiore@5
|
104 glColor3f(1.0f,0.0f,0.0f); // Red
|
fiore@5
|
105 glLineWidth(2.0);
|
fiore@5
|
106 glDisable(GL_LIGHTING);
|
fiore@5
|
107 glEnable(GL_COLOR_MATERIAL);
|
fiore@5
|
108 int numEdges = cm->getEdgesNum();
|
fiore@5
|
109 for ( int i = 0; i < numEdges; i++){
|
fiore@5
|
110 /* draw the edge in openGL */
|
fiore@5
|
111 CollectionsManager::EdgeData & ed = cm->getEdgeData(i);
|
fiore@5
|
112 glPushAttrib(GL_ENABLE_BIT);
|
fiore@5
|
113 glLineStipple(1, ed.stipplePattern);
|
fiore@5
|
114 glEnable(GL_LINE_STIPPLE);
|
fiore@5
|
115 glBegin(GL_LINES);
|
fiore@5
|
116 for(unsigned int j = 0; j < ed.getSize(); j++){
|
fiore@5
|
117 for(unsigned int k = j; k < ed.getSize(); k++){
|
fiore@5
|
118 if(ed.adjMatrix[j][k]){
|
fiore@5
|
119 glVertex3d(ed.x[j]*2,ed.y[j]*GRAPHIC_SCALE,0);
|
fiore@5
|
120 glVertex3d(ed.x[k]*2,ed.y[k]*GRAPHIC_SCALE,0);
|
fiore@5
|
121 }
|
fiore@5
|
122 }
|
fiore@5
|
123 }
|
fiore@5
|
124 glEnd();
|
fiore@5
|
125 glPopAttrib();
|
fiore@5
|
126 /* update haptics edge line set if a change in the collection occurred */
|
fiore@5
|
127 if(redraw_haptics_scene){
|
fiore@5
|
128 /* build the vector with the edges*/
|
fiore@5
|
129 for(unsigned int j = 0; j < ed.getSize(); j++){
|
fiore@5
|
130 for(unsigned int k = j; k < ed.getSize(); k++){
|
fiore@5
|
131 if(ed.adjMatrix[j][k]){
|
fiore@5
|
132 line_set.push_back(Collision::LineSegment (
|
fiore@5
|
133 Vec3(ed.x[j],ed.y[j],0),
|
fiore@5
|
134 Vec3(ed.x[k],ed.y[k],0)
|
fiore@5
|
135 ));
|
fiore@5
|
136 }
|
fiore@5
|
137 }
|
fiore@5
|
138 }
|
fiore@5
|
139
|
fiore@5
|
140 for(vector< HAPI::Collision::LineSegment>::iterator itr = line_set.begin(); itr != line_set.end(); itr++){
|
fiore@5
|
141 line_id_map.insert(pair<Collision::LineSegment*,int>(&(*itr),ed.hapticId));
|
fiore@5
|
142 }
|
fiore@5
|
143 }
|
fiore@5
|
144 }
|
fiore@5
|
145 glPopAttrib();
|
fiore@5
|
146
|
fiore@5
|
147 /* --- draw the nodes --- */
|
fiore@5
|
148 int numNodes = cm->getNodesNum();
|
fiore@5
|
149 glColor3f(1.0f, 1.0f, 1.0f); // white
|
fiore@5
|
150 for( int i = 0; i < numNodes; i++){
|
fiore@5
|
151 /* draw the nodes in openGL */
|
fiore@5
|
152 glPushMatrix();
|
fiore@5
|
153 CollectionsManager::NodeData &nd = cm->getNodeData(i);
|
fiore@5
|
154 glTranslated(nd.x*GRAPHIC_SCALE,nd.y*GRAPHIC_SCALE, 0);
|
fiore@5
|
155 drawSphere();
|
fiore@5
|
156 glPopMatrix();
|
fiore@5
|
157
|
fiore@5
|
158 /* update haptics node line set if a change in the collection occurred */
|
fiore@5
|
159 if(redraw_haptics_scene){
|
fiore@5
|
160 point_set.push_back(Vec3(nd.x,nd.y,0));
|
fiore@5
|
161 point_id_map.insert(pair<Collision::Point*,int>(&(point_set.back()),nd.hapticId)) ;
|
fiore@5
|
162 }
|
fiore@5
|
163 }
|
fiore@5
|
164
|
fiore@5
|
165 /* --- update the haptic force if a change in the collection occurred --- */
|
fiore@5
|
166 if(redraw_haptics_scene){
|
fiore@5
|
167 /* reset the effects and set up new one, otherwise they would accumulate */
|
fiore@5
|
168 hd->clearEffects();
|
fiore@5
|
169 /* first lines */
|
fiore@5
|
170 if(!line_set.empty()){
|
fiore@5
|
171 /* force according to the current attraction_mode */
|
fiore@5
|
172 int force_factor = (magnetic_mode == STICKY) ? LINE_FORCE_FACTOR_STICKY : LINE_FORCE_FACTOR_LOOSE;
|
fiore@5
|
173 /* if the user is dragging or looking for an object, the force *
|
fiore@5
|
174 * factor is always loose until she drops or find the object */
|
fiore@5
|
175 if(pickup_mode == DRAGGING || pickup_mode == START_DRAGGING || attraction_mode == ACTIVE){
|
fiore@5
|
176 force_factor = LOOSE;
|
fiore@5
|
177 }
|
fiore@5
|
178
|
fiore@5
|
179 /* update the force */
|
fiore@5
|
180 force_effect.reset( new HapticShapeConstraint(new HapticLineSet( line_set, 0 ),force_factor));
|
fiore@5
|
181 hd->addEffect(force_effect.get());
|
fiore@5
|
182
|
fiore@5
|
183 /* recalculate last_touched_line if it was != NULL as the pointed *
|
fiore@5
|
184 object is now destroyed after clear() and replaced with a new one */
|
fiore@5
|
185 if(last_touched_line != NULL){
|
fiore@5
|
186 last_touched_line = NULL;
|
fiore@5
|
187 vector<Collision::LineSegment>::iterator itr;
|
fiore@5
|
188 Vec3 closest_point, normal, tex_coord;
|
fiore@5
|
189 for( itr=line_set.begin(); itr != line_set.end(); itr++ ){
|
fiore@5
|
190 itr->closestPoint(proxy_pos,closest_point,normal,tex_coord);
|
fiore@5
|
191 if(pointsDistance(closest_point,proxy_pos) < LINE_COLLISION_THRESHOLD){
|
fiore@5
|
192 last_touched_line = &(*itr);
|
fiore@5
|
193 break;
|
fiore@5
|
194 }
|
fiore@5
|
195 }
|
fiore@5
|
196 if(last_touched_line == NULL){
|
fiore@5
|
197 /* touched line was != NULL and now is NULL. It means it has been deleted *
|
fiore@5
|
198 * while being touched, therefore an unselect command must be issued */
|
fiore@5
|
199 object_unselected = true;
|
fiore@5
|
200 }
|
fiore@5
|
201 }
|
fiore@5
|
202 }else if(last_touched_line != NULL){
|
fiore@5
|
203 /* if last_touuched_line was != null it means an edge has been deleted *
|
fiore@5
|
204 * which was being touched, therefore un unselect command must be issued */
|
fiore@5
|
205 object_unselected = true;
|
fiore@5
|
206 /* if there are no lines there cannot be a last touched one */
|
fiore@5
|
207 last_touched_line = NULL;
|
fiore@5
|
208 }
|
fiore@5
|
209
|
fiore@5
|
210 /* now points */
|
fiore@5
|
211 if(!point_set.empty()){
|
fiore@5
|
212 /* update the force */
|
fiore@5
|
213 force_effect.reset( new HapticShapeConstraint(new HapticPointSet( point_set, 0 ),POINT_FORCE_FACTOR ) );
|
fiore@5
|
214 hd->addEffect(force_effect.get());
|
fiore@5
|
215
|
fiore@5
|
216 /* recalculate lastTouchedNode if it was != NULL as the pointed object is now destroyed after clear() */
|
fiore@5
|
217 if(last_touched_point != NULL){
|
fiore@5
|
218 last_touched_point = NULL;
|
fiore@5
|
219 vector<Collision::Point>::iterator itr;
|
fiore@5
|
220 for(itr=point_set.begin(); itr != point_set.end(); itr++ ){
|
fiore@5
|
221 if(pointsDistance(itr->position,proxy_pos) < POINT_COLLISION_THRESHOLD ){
|
fiore@5
|
222 last_touched_point = &(*itr);
|
fiore@5
|
223 break;
|
fiore@5
|
224 }
|
fiore@5
|
225 }
|
fiore@5
|
226 if(last_touched_point == NULL){
|
fiore@5
|
227 /* touched point was != NULL and now is NULL. It means it has been deleted *
|
fiore@5
|
228 * while being touched, therefore an unselect command must be issued */
|
fiore@5
|
229 object_unselected = true;
|
fiore@5
|
230 }
|
fiore@5
|
231 }
|
fiore@5
|
232 }else if(last_touched_point != NULL){
|
fiore@5
|
233 /* if last_touuched_point was != null it means a node has been deleted *
|
fiore@5
|
234 * which was being touched, therefore un unselect command must be issued */
|
fiore@5
|
235 object_unselected = true;
|
fiore@5
|
236 /* if there are no points there cannot be a last touched one */
|
fiore@5
|
237 last_touched_point = NULL;
|
fiore@5
|
238 }
|
fiore@5
|
239
|
fiore@5
|
240 if(pickup_mode == START_DRAGGING && attraction_mode == NO_ATTRACTION){
|
fiore@5
|
241 force_effect.reset(new HapticSpring(proxy_pos,SPRING_FORCE_FACTOR));
|
fiore@5
|
242 hd->addEffect(force_effect.get());
|
fiore@5
|
243 /* spring force is initialized not pickup_mode goes to DRAGGING*/
|
fiore@5
|
244 pickup_mode = DRAGGING;
|
fiore@5
|
245 }else if(attraction_mode == ACTIVE){
|
fiore@5
|
246 bool found = false;
|
fiore@5
|
247 /* let's look for the object into the nodes */
|
fiore@5
|
248 for(map<HAPI::Collision::Point*,int>::iterator itr = point_id_map.begin();itr != point_id_map.end(); itr++){
|
fiore@5
|
249 if( (*itr).second == attract_to ){
|
fiore@5
|
250 force_effect.reset(new HapticSpring(((*itr).first)->position,SPRING_FORCE_FACTOR));
|
fiore@5
|
251 hd->addEffect(force_effect.get());
|
fiore@5
|
252 found = true;
|
fiore@5
|
253 break;
|
fiore@5
|
254 }
|
fiore@5
|
255 }
|
fiore@5
|
256 /* if not found look into the edges */
|
fiore@5
|
257 if(!found){
|
fiore@5
|
258 for(map<HAPI::Collision::LineSegment*,int>::iterator itr = line_id_map.begin();itr != line_id_map.end(); itr++){
|
fiore@5
|
259 if( (*itr).second == attract_to ){
|
fiore@5
|
260 HAPI::Collision::LineSegment* line_ptr = (*itr).first;
|
fiore@5
|
261 force_effect.reset(new HapticSpring(
|
fiore@5
|
262 midPoint(line_ptr->start, line_ptr->end),
|
fiore@5
|
263 SPRING_FORCE_FACTOR));
|
fiore@5
|
264 hd->addEffect(force_effect.get());
|
fiore@5
|
265 found = true;
|
fiore@5
|
266 break;
|
fiore@5
|
267 }
|
fiore@5
|
268 }
|
fiore@5
|
269 }
|
fiore@5
|
270
|
fiore@5
|
271 if(!found){
|
fiore@5
|
272 /* element has been deleted before user could reach it: go back to normal */
|
fiore@5
|
273 attraction_mode = STOP_ATTRACTION;
|
fiore@5
|
274 }
|
fiore@5
|
275
|
fiore@5
|
276 }
|
fiore@5
|
277 /* just deallocate the memory for the last force effect */
|
fiore@5
|
278 force_effect.reset();
|
fiore@5
|
279 /* tranfer all the forces to the device */
|
fiore@5
|
280 hd->transferObjects();
|
fiore@5
|
281 }
|
fiore@5
|
282 }
|
fiore@5
|
283
|
fiore@5
|
284
|
fiore@5
|
285 void HapticManager::drawCursor(){
|
fiore@5
|
286 HAPIHapticsRenderer *hr = hd->getHapticsRenderer();
|
fiore@5
|
287 if( hr ) {
|
fiore@5
|
288 /* save the proxy pos in a global variable */
|
fiore@5
|
289 proxy_pos = static_cast< HAPIProxyBasedRenderer * >(hr)->getProxyPosition();
|
fiore@5
|
290
|
fiore@5
|
291 glPushMatrix();
|
fiore@5
|
292 glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT);
|
fiore@5
|
293 glTranslatef( (GLfloat)proxy_pos.x*GRAPHIC_SCALE,
|
fiore@5
|
294 (GLfloat)proxy_pos.y*GRAPHIC_SCALE,
|
fiore@5
|
295 (GLfloat)proxy_pos.z );
|
fiore@5
|
296 glEnable(GL_COLOR_MATERIAL);
|
fiore@5
|
297 glColor3f(0.0f, 0.5f, 1.0f);// Blue
|
fiore@5
|
298 drawSphere();
|
fiore@5
|
299 glPopAttrib();
|
fiore@5
|
300 glPopMatrix();
|
fiore@5
|
301 }
|
fiore@5
|
302 }
|
fiore@5
|
303
|
fiore@5
|
304 bool HapticManager::checkNodeCollision(){
|
fiore@5
|
305 /* if the proxy goes far enough from the last touched node, then *
|
fiore@5
|
306 * lastTouched node can be set back to NULL */
|
fiore@5
|
307 bool do_unselect = false;
|
fiore@5
|
308 if(last_touched_point != NULL){
|
fiore@5
|
309 if(pointsDistance(last_touched_point->position, proxy_pos) > POINT_COLLISION_THRESHOLD){
|
fiore@5
|
310 last_touched_point = NULL;
|
fiore@5
|
311 do_unselect = true;
|
fiore@5
|
312 }
|
fiore@5
|
313 }
|
fiore@5
|
314
|
fiore@5
|
315 /* find the closest point among those closer than POINT_COLLISION_THRESHOLD */
|
fiore@5
|
316 vector<Collision::Point>::iterator itr;
|
fiore@5
|
317 HAPI::Collision::Point* found_point = NULL;
|
fiore@5
|
318 double min_dist = POINT_COLLISION_THRESHOLD;
|
fiore@5
|
319 double dist;
|
fiore@5
|
320 for(itr=point_set.begin(); itr != point_set.end(); itr++ ){
|
fiore@5
|
321 if((dist = pointsDistance(itr->position,proxy_pos)) < min_dist ){
|
fiore@5
|
322 found_point = &(*itr);
|
fiore@5
|
323 min_dist = dist;
|
fiore@5
|
324 }
|
fiore@5
|
325 }
|
fiore@5
|
326
|
fiore@5
|
327 if(found_point != NULL){
|
fiore@5
|
328 /* if the last touched point is the same, it means the cursor was *
|
fiore@5
|
329 * already on it therefore do nothing as the node name is uttered only when *
|
fiore@5
|
330 * the proxy comes across it and not at each frame (it would make a mess) */
|
fiore@5
|
331 if(last_touched_point != found_point){
|
fiore@5
|
332 last_touched_point = found_point;
|
fiore@5
|
333 /* Setting last_touched_line to NULL, means untouching the eventual touching line. *
|
fiore@5
|
334 * When a node is touched, the connected lines are indeed untouched as only one object *
|
fiore@5
|
335 * at time can be touching the position proxy */
|
fiore@5
|
336 last_touched_line = NULL;
|
fiore@5
|
337 int point_id = point_id_map[last_touched_point];
|
fiore@5
|
338 /* select the node for eventual highlighting int the java tree */
|
fiore@5
|
339 executeCommand(SELECT_CMD,point_id,0,0,0,0);
|
fiore@5
|
340 /* utter the name of the node */
|
fiore@5
|
341 executeCommand(SPEAK_NAME_CMD,point_id,0,0,0,0);
|
fiore@5
|
342 if(attraction_mode == ACTIVE && point_id == attract_to){
|
fiore@5
|
343 /* we reached the attracting node, stop the attraction force */
|
fiore@5
|
344 attraction_mode = STOP_ATTRACTION;
|
fiore@5
|
345 }
|
fiore@5
|
346 }
|
fiore@5
|
347 return true;
|
fiore@5
|
348 }
|
fiore@5
|
349
|
fiore@5
|
350 if(do_unselect)
|
fiore@5
|
351 executeCommand(UNSELECT_CMD,0,0,0,0,0);
|
fiore@5
|
352
|
fiore@5
|
353 return false;
|
fiore@5
|
354 }
|
fiore@5
|
355
|
fiore@5
|
356 bool HapticManager::checkEdgeCollision(){
|
fiore@5
|
357 Vec3 closest_point, normal, tex_coord;
|
fiore@5
|
358
|
fiore@5
|
359 /* id is taken from the last touched line is overwritten. To understand why suppose we have an edge *
|
fiore@5
|
360 * made of two lines A and B. When going from one line to another we don't have to utter the name (see *
|
fiore@5
|
361 * comment below. last_touched_line goes from a to NULL before being set to B. If lastTouchedLineId is *
|
fiore@5
|
362 * calculated before checking for NULL-ness it will become -1 and it won't be useful anymore to avoid *
|
fiore@5
|
363 * uttering the edge name when going from A to B. In this way instead proxy goes from A to B, the id is *
|
fiore@5
|
364 * to A's, last_touched_line becomes NULL and then it becomes B, but the name is not uttered because id *
|
fiore@5
|
365 * is the same. This is a very long comment. I want to finish the line to make it look better. bye bye */
|
fiore@5
|
366 int lastTouchedLineId = (last_touched_line == NULL) ? - 1 : line_id_map[last_touched_line];
|
fiore@5
|
367
|
fiore@5
|
368 bool do_unselect = false;
|
fiore@5
|
369 /* if the proxy goes far enough from the last touched line, then last_touched_line can be set back to NULL */
|
fiore@5
|
370 if(last_touched_line != NULL){
|
fiore@5
|
371 last_touched_line->closestPoint(proxy_pos,closest_point,normal,tex_coord);
|
fiore@5
|
372 if(pointsDistance(closest_point, proxy_pos) > LINE_COLLISION_THRESHOLD){
|
fiore@5
|
373 last_touched_line = NULL;
|
fiore@5
|
374 do_unselect = true;
|
fiore@5
|
375 }
|
fiore@5
|
376 }
|
fiore@5
|
377
|
fiore@5
|
378 /* find the closest line to the proxy among those closer than LINE_COLLISION_THRESHOLD */
|
fiore@5
|
379 vector<Collision::LineSegment>::iterator itr;
|
fiore@5
|
380 HAPI::Collision::LineSegment* found_line = NULL;
|
fiore@5
|
381 double min_dist = LINE_COLLISION_THRESHOLD;
|
fiore@5
|
382 double dist;
|
fiore@5
|
383 for( itr=line_set.begin(); itr != line_set.end(); itr++ ){
|
fiore@5
|
384 itr->closestPoint(proxy_pos,closest_point,normal,tex_coord);
|
fiore@5
|
385 if((dist = pointsDistance(closest_point,proxy_pos)) < min_dist){
|
fiore@5
|
386 found_line = &(*itr);
|
fiore@5
|
387 min_dist = dist;
|
fiore@5
|
388 }
|
fiore@5
|
389 }
|
fiore@5
|
390
|
fiore@5
|
391 if(found_line != NULL){
|
fiore@5
|
392 last_touched_line = found_line;
|
fiore@5
|
393 /* proxy can touch only one object at time either line or point */
|
fiore@5
|
394 last_touched_point = NULL;
|
fiore@5
|
395 /* An edge can be broken into several lines. Such lines will map to the same edge id *
|
fiore@5
|
396 * If the proxy detouches from a line but immediately touches another line mapped to *
|
fiore@5
|
397 * the same id, it just means the proxy is going aling the line and therefore no *
|
fiore@5
|
398 * name must be uttered again (the name must be uttered when touching an edge and *
|
fiore@5
|
399 * not when touching each line. We keep track of the last touched id and if it's the *
|
fiore@5
|
400 * as te new touched line id, then nothing is uttered, for we're on the same edge */
|
fiore@5
|
401 if(lastTouchedLineId != line_id_map[last_touched_line]){
|
fiore@5
|
402 int line_id = line_id_map[last_touched_line];
|
fiore@5
|
403 /* select the node for eventual highlighting int the java tree */
|
fiore@5
|
404 executeCommand(SELECT_CMD,line_id,0,0,0,0);
|
fiore@5
|
405 /* utter the name of the edge */
|
fiore@5
|
406 executeCommand(SPEAK_NAME_CMD,line_id,0,0,0,0);
|
fiore@5
|
407 if(attraction_mode == ACTIVE && line_id == attract_to){
|
fiore@5
|
408 /* we reached the attracting node, stop the attraction force */
|
fiore@5
|
409 attraction_mode = STOP_ATTRACTION;
|
fiore@5
|
410 }
|
fiore@5
|
411 }
|
fiore@5
|
412 return true;
|
fiore@5
|
413 }
|
fiore@5
|
414
|
fiore@5
|
415 if(do_unselect){
|
fiore@5
|
416 executeCommand(UNSELECT_CMD,0,0,0,0,0);
|
fiore@5
|
417 }
|
fiore@5
|
418
|
fiore@5
|
419 return false;
|
fiore@5
|
420 }
|
fiore@5
|
421
|
fiore@5
|
422 bool HapticManager::checkUnselection(void){
|
fiore@5
|
423 if(object_unselected){
|
fiore@5
|
424 object_unselected = false;
|
fiore@5
|
425 executeCommand(UNSELECT_CMD,0,0,0,0,0);
|
fiore@5
|
426 return true;
|
fiore@5
|
427 }
|
fiore@5
|
428 return false;
|
fiore@5
|
429 }
|
fiore@5
|
430
|
fiore@5
|
431 bool HapticManager::checkButtons(void){
|
fiore@5
|
432 HAPIInt32 status = hd->getButtonStatus();
|
fiore@5
|
433 /* handle the buttons. Buttons can be pressed one at time. If a button is pressed all the *
|
fiore@5
|
434 * other buttons (either when pressed or released) are ignored untill the button is released */
|
fiore@5
|
435 if(pressed_button == NO_BUTTON){
|
fiore@5
|
436 if(status & FRONT_BUTTON){
|
fiore@5
|
437 /* front button pressed, change how magnetic the lines are */
|
fiore@5
|
438 pressed_button = FRONT_BUTTON;
|
fiore@5
|
439 /* switch from sticky to loose and vice versa */
|
fiore@5
|
440 magnetic_mode = (magnetic_mode == STICKY) ? LOOSE : STICKY;
|
fiore@5
|
441 magnetic_mode_changed = true;
|
fiore@5
|
442 /* make a sound according to the new attraction mode */
|
fiore@5
|
443 executeCommand(PLAY_SOUND_CMD, (magnetic_mode == STICKY) ? STICKY_MODE_SOUND : LOOSE_MODE_SOUND ,0,0,0,0);
|
fiore@5
|
444 return true;
|
fiore@5
|
445 }else if(status & REAR_BUTTON){
|
fiore@5
|
446 pressed_button = REAR_BUTTON;
|
fiore@5
|
447 /* rear button pressed: First time it picks up an object, second time it drops it */
|
fiore@5
|
448 if(pickup_mode == RELEASED){ // pickup mode was released, then now user picked up an object
|
fiore@5
|
449 if(last_touched_point != NULL){
|
fiore@5
|
450 pickedup_point = last_touched_point;
|
fiore@5
|
451 last_dragging_pos = pickedup_point->position; // last_dragging_pos used in checkMotion
|
fiore@5
|
452 /* send a pickup command to the Java thread which will in turn issue another pickup *
|
fiore@5
|
453 * command to this thread (pickup = true in drawDiagram) if the lock could be granted */
|
fiore@5
|
454 executeCommand(PICKUP_CMD,point_id_map[last_touched_point],0,0,0,0);
|
fiore@5
|
455 }else if(last_touched_line != NULL){
|
fiore@5
|
456 pickedup_line = last_touched_line;
|
fiore@5
|
457 pickup_line_pos = proxy_pos; // the point where the line was picked up
|
fiore@5
|
458 last_dragging_pos = proxy_pos;// last_dragging_pos used in checkMotion
|
fiore@5
|
459 executeCommand(PICKUP_CMD,line_id_map[last_touched_line],0,0,0,0);
|
fiore@5
|
460 }/* else user picked up thin air. Do nothing. */
|
fiore@5
|
461 }else if(pickup_mode == DRAGGING){ // pickup mode was dragging, then now user dropped an object
|
fiore@5
|
462 pickup_mode = RELEASED;
|
fiore@5
|
463 Vec3 & new_position = hapticToScreenSpace(Vec3(proxy_pos),
|
fiore@5
|
464 cm->getScreenWidth(),
|
fiore@5
|
465 cm->getScreenHeight());
|
fiore@5
|
466 if(pickedup_point){
|
fiore@5
|
467 executeCommand(MOVE_CMD,point_id_map[pickedup_point],new_position.x,new_position.y,0,0);
|
fiore@5
|
468 pickedup_point = NULL;
|
fiore@5
|
469 }else{
|
fiore@5
|
470 hapticToScreenSpace(pickup_line_pos,
|
fiore@5
|
471 cm->getScreenWidth(),
|
fiore@5
|
472 cm->getScreenHeight());
|
fiore@5
|
473 executeCommand(MOVE_CMD,
|
fiore@5
|
474 line_id_map[pickedup_line],
|
fiore@5
|
475 new_position.x,
|
fiore@5
|
476 new_position.y,
|
fiore@5
|
477 pickup_line_pos.x,
|
fiore@5
|
478 pickup_line_pos.y);
|
fiore@5
|
479 pickedup_line = NULL;
|
fiore@5
|
480 }
|
fiore@5
|
481 return true;
|
fiore@5
|
482 }
|
fiore@5
|
483 } else if(status & ( LEFT_BUTTON | RIGHT_BUTTON )){ // either left or right button pressed
|
fiore@5
|
484 pressed_button = (status == LEFT_BUTTON) ? LEFT_BUTTON : RIGHT_BUTTON;
|
fiore@5
|
485 if(last_touched_point != NULL){ // priority to nodes
|
fiore@5
|
486 executeCommand(SPEAK_INFO_CMD,point_id_map[last_touched_point], 0,0,0,0);
|
fiore@5
|
487 return true;
|
fiore@5
|
488 }
|
fiore@5
|
489 if(last_touched_line != NULL){
|
fiore@5
|
490 executeCommand(SPEAK_INFO_CMD,line_id_map[last_touched_line], 0,0,0,0);
|
fiore@5
|
491 return true;
|
fiore@5
|
492 }
|
fiore@5
|
493 }
|
fiore@5
|
494 } else if ( /* deactivate the buttons */
|
fiore@5
|
495 (pressed_button == FRONT_BUTTON && !(status & FRONT_BUTTON)) ||
|
fiore@5
|
496 (pressed_button == REAR_BUTTON && !(status & REAR_BUTTON)) ||
|
fiore@5
|
497 (pressed_button == LEFT_BUTTON && !(status & LEFT_BUTTON)) ||
|
fiore@5
|
498 (pressed_button == RIGHT_BUTTON && !(status & RIGHT_BUTTON)))
|
fiore@5
|
499 {
|
fiore@5
|
500 pressed_button = NO_BUTTON;
|
fiore@5
|
501 }
|
fiore@5
|
502 return false;
|
fiore@5
|
503 }
|
fiore@5
|
504
|
fiore@5
|
505 bool HapticManager::checkMotion(void){
|
fiore@5
|
506 if(pickup_mode != DRAGGING)
|
fiore@5
|
507 return false;
|
fiore@5
|
508 if(pointsDistance(proxy_pos,last_dragging_pos) > DRAGGING_SOUND_THRESHOLD){
|
fiore@5
|
509 last_dragging_pos = proxy_pos;
|
fiore@5
|
510 executeCommand(PLAY_SOUND_CMD,DRAGGING_SOUND,0,0,0,0);
|
fiore@5
|
511 return true;
|
fiore@5
|
512 }
|
fiore@5
|
513 return false;
|
fiore@5
|
514 }
|
fiore@5
|
515
|
fiore@5
|
516 void HapticManager::dispose(void){
|
fiore@5
|
517 hd->releaseDevice();
|
fiore@5
|
518 delete hd;
|
fiore@5
|
519 }
|
fiore@5
|
520
|
fiore@5
|
521
|
fiore@5
|
522 /* static constants initialization */
|
fiore@5
|
523 const int HapticManager::NO_ID = 0;
|
fiore@5
|
524
|
fiore@5
|
525 const int HapticManager::GRAPHIC_SCALE = 2;
|
fiore@5
|
526 const int HapticManager::SPRING_FORCE_FACTOR = 200;
|
fiore@5
|
527 const int HapticManager::LINE_FORCE_FACTOR_STICKY = 2000;
|
fiore@5
|
528 const int HapticManager::LINE_FORCE_FACTOR_LOOSE = 100;
|
fiore@5
|
529 const int HapticManager::POINT_FORCE_FACTOR = 20;
|
fiore@5
|
530 const double HapticManager::POINT_COLLISION_THRESHOLD = 0.0025;
|
fiore@5
|
531 const double HapticManager::LINE_COLLISION_THRESHOLD = 0.0025;
|
fiore@5
|
532 const double HapticManager::DRAGGING_SOUND_THRESHOLD = 0.01;
|
fiore@5
|
533
|
fiore@5
|
534 const int HapticManager::LOOSE_MODE_SOUND = 0;
|
fiore@5
|
535 const int HapticManager::STICKY_MODE_SOUND = 1;
|
fiore@5
|
536 const int HapticManager::DRAGGING_SOUND = 3;
|
fiore@5
|
537
|
fiore@5
|
538 const char HapticManager::MOVE_CMD = 'm';
|
fiore@5
|
539 const char HapticManager::PLAY_SOUND_CMD = 'g';
|
fiore@5
|
540 const char HapticManager::SPEAK_NAME_CMD = 't';
|
fiore@5
|
541 const char HapticManager::PICKUP_CMD = 'c';
|
fiore@5
|
542 const char HapticManager::SPEAK_INFO_CMD = 'i';
|
fiore@5
|
543 const char HapticManager::SELECT_CMD = 's';
|
fiore@5
|
544 const char HapticManager::UNSELECT_CMD = 'u';
|
fiore@5
|
545
|
fiore@5
|
546 const HAPIInt32 HapticManager::FRONT_BUTTON = (1 << 2);
|
fiore@5
|
547 const HAPIInt32 HapticManager::LEFT_BUTTON = (1 << 1);
|
fiore@5
|
548 const HAPIInt32 HapticManager::RIGHT_BUTTON = (1 << 3);
|
fiore@5
|
549 const HAPIInt32 HapticManager::REAR_BUTTON = (1 << 0);
|
fiore@5
|
550 const HAPIInt32 HapticManager::NO_BUTTON = 0;
|