fiore@3: #include "HapticManager.h" fiore@3: fiore@3: #define NO_ERROR_SIMUL fiore@3: fiore@3: fiore@3: void checkExceptions(void); fiore@3: void stopExecution(char* msg); fiore@3: fiore@3: bool movedNode = false; fiore@3: fiore@3: fiore@3: void HLCALLBACK HapticManager::hlButton1CB(HLenum event, HLuint object, fiore@3: HLenum thread, HLcache *cache, fiore@3: void *userdata) fiore@3: { fiore@3: HapticManager *pThis = static_cast(userdata); fiore@3: if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall fiore@3: return; fiore@3: if(event != HL_EVENT_1BUTTONDOWN) fiore@3: return; fiore@3: fiore@3: /* check if it was a double click or not */ fiore@3: fiore@3: clock_t time = clock(); fiore@3: if(pThis->clickStart == 0) // first click fiore@3: pThis->clickStart = time; fiore@3: else if(time < (pThis->clickStart + CLICK_INTERVAL)) fiore@3: pThis->doubleClick = true; fiore@3: fiore@3: } fiore@3: fiore@3: void HLCALLBACK HapticManager::hlButton2CB(HLenum event, HLuint object, fiore@3: HLenum thread, HLcache *cache, fiore@3: void *userdata){ fiore@3: HapticManager *pThis = static_cast(userdata); fiore@3: if(event == HL_EVENT_2BUTTONDOWN){ fiore@3: pThis->stickyMode = !pThis->stickyMode; fiore@3: pThis->executeCommand('g', pThis->stickyMode ? 1 : 0,0,0,0,0); fiore@3: /* we change mode to stickyMode, thus dragging becomes not active */ fiore@3: if(pThis->stickyMode && pThis->springStatus == DRAGGING){ fiore@3: pThis->springStatus = STOP_DRAGGING; fiore@3: pThis->secondClick = false; fiore@3: } fiore@3: #ifdef VIBRATION fiore@3: // vibrate fiore@3: if(pThis->vibrationStatus == NO_VIBRATION){ fiore@3: pThis->vibrationStatus = START_VIBRATION; fiore@3: } fiore@3: #endif fiore@3: } fiore@3: } fiore@3: fiore@3: void HLCALLBACK HapticManager::hlTouchCB(HLenum event, HLuint object, HLenum thread, fiore@3: HLcache *cache, void *userdata) fiore@3: { fiore@3: HapticManager *pThis = static_cast(userdata); fiore@3: if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall fiore@3: return; ccmi-guest@8: fiore@3: if(pThis->lastMovedElement == object){ fiore@3: pThis->stillTouchingLastMovedElement = true; fiore@3: } fiore@3: fiore@3: try{ fiore@3: // check if the touched object is an edge fiore@3: if(pThis->collectionsManager->isEdge(object)){ fiore@3: CollectionsManager::EdgeData & ed = pThis->collectionsManager->getEdgeDataFromID(object); fiore@3: pThis->lastTouchedEdgeID = object; fiore@3: pThis->lastTouchedEdgeIsUntouched = false; ccmi-guest@8: fiore@3: pThis->lastTouchedEdgeStipplePattern = ed.stipplePattern; fiore@3: // check if the proxy is still touching the edge's node, in that case only play the sound fiore@3: HLboolean isTouching = HL_FALSE; fiore@3: if(pThis->lastTouchedNodeID != HL_OBJECT_ANY){ fiore@3: hlGetShapeBooleanv(pThis->lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching); fiore@3: } fiore@3: /* play the sound and speech associated to the edge */ fiore@3: if((isTouching == HL_TRUE||pThis->stillTouchingLastMovedElement) && (object != pThis->attractToHapticId)){ fiore@3: if(!pThis->stickyMode) fiore@3: pThis->executeCommand('p',ed.diagramId,0,0,0,0); // just plays the sound fiore@3: }else{ fiore@3: pThis->executeCommand('s',ed.diagramId,0,0,0,0); fiore@3: pThis->lastHighlightElementID = object; fiore@3: pThis->executeCommand('t',ed.diagramId,0,0,0,0); // speaks the edge's name fiore@3: } fiore@3: }else{ // it's a node fiore@3: pThis->lastTouchedNodeID = object; fiore@3: CollectionsManager::NodeData & nd = pThis->collectionsManager->getNodeDataFromID(object); fiore@3: pThis->lastTouchedNodePosition[0] = nd.x; fiore@3: pThis->lastTouchedNodePosition[1] = nd.y; fiore@3: pThis->lastTouchedNodePosition[2] = 0; fiore@3: pThis->frictionDisabled = true; fiore@3: pThis->executeCommand('s',nd.diagramId,0,0,0,0); fiore@3: pThis->lastHighlightElementID = object; fiore@3: pThis->executeCommand('t',nd.diagramId,0,0,0,0); fiore@3: } fiore@3: }catch(int){ fiore@3: stopExecution("Touch Call Back: Could not retrieve element from ID"); fiore@3: } fiore@3: } fiore@3: fiore@3: void HLCALLBACK HapticManager::hlUntouchCB(HLenum event, HLuint object, HLenum thread, fiore@3: HLcache *cache, void *userdata) fiore@3: { fiore@3: HapticManager *pThis = static_cast(userdata); fiore@3: if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall fiore@3: return; ccmi-guest@8: fiore@3: if(object == pThis->lastMovedElement){ fiore@3: pThis->lastMovedElement = HL_OBJECT_ANY; fiore@3: pThis->stillTouchingLastMovedElement = false; fiore@3: } fiore@3: if(pThis->lastHighlightElementID == object){ fiore@3: pThis->lastHighlightElementID = HL_OBJECT_ANY; fiore@3: pThis->executeCommand('u',0,0,0,0,0); fiore@3: } ccmi-guest@8: fiore@3: if(pThis->lastTouchedEdgeID == object){ fiore@3: pThis->lastTouchedEdgeIsUntouched = true; fiore@3: } fiore@3: } fiore@3: fiore@3: fiore@3: void HLCALLBACK HapticManager::hlMotionCB(HLenum event, HLuint object, HLenum thread, fiore@3: HLcache *cache, void *userdata) fiore@3: { fiore@3: HapticManager *pThis = static_cast(userdata); fiore@3: fiore@3: /* plays the sound to acknowledge the user they're dragging an element around */ fiore@3: static int onceinawhile = 0; fiore@3: if(pThis->springStatus == DRAGGING){ fiore@3: onceinawhile = (onceinawhile+1)%5; fiore@3: if(!onceinawhile) fiore@3: pThis->executeCommand('g',3,0,0,0,0); fiore@3: } fiore@3: fiore@3: /* the following code is to play element speech when taking a path through an edge starting from a connected node */ fiore@3: fiore@3: /* friction is disabled when we land on a node. If proxy ain't on a node, then just return */ fiore@3: /* else it would play every time we move along the edge even if not detouching from a node */ fiore@3: if(!pThis->frictionDisabled){ fiore@3: return; fiore@3: } fiore@3: fiore@3: /* calculate the distance between the node, the stylus was on, and the current proxy position after moving out from it*/ fiore@3: HLdouble proxyPosition[3]; fiore@3: hlGetDoublev(HL_DEVICE_POSITION,proxyPosition); fiore@3: proxyPosition[0] -= pThis->lastTouchedNodePosition[0]; fiore@3: proxyPosition[1] -= pThis->lastTouchedNodePosition[1]; fiore@3: proxyPosition[2] -= pThis->lastTouchedNodePosition[2]; fiore@3: if(pThis->lastTouchedEdgeID != HL_OBJECT_ANY){ fiore@3: if(norm(proxyPosition) > EDGE_SPEECH_DISTANCE_WHEN_LEAVING_NODE){ fiore@3: pThis->frictionDisabled = false; fiore@3: HLboolean isTouching; fiore@3: hlGetShapeBooleanv(pThis->lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching); fiore@3: if(isTouching == HL_TRUE){ fiore@3: try{ fiore@3: CollectionsManager::EdgeData & ed = pThis->collectionsManager->getEdgeDataFromID(pThis->lastTouchedEdgeID); fiore@3: pThis->executeCommand('t',ed.diagramId,0,0,0,0); fiore@3: pThis->lastTouchedEdgeID = ed.hapticId; fiore@3: pThis->lastHighlightElementID = ed.hapticId; fiore@3: pThis->executeCommand('s',ed.diagramId,0,0,0,0); fiore@3: }catch(int){ fiore@3: stopExecution("Button 1 callback: Could not retrieve edge from ID"); fiore@3: } fiore@3: } fiore@3: } fiore@3: } fiore@3: } fiore@3: fiore@3: #ifdef VIBRATION fiore@3: void HLCALLBACK HapticManager::vibrationCB(HDdouble force[3], HLcache *cache, void *userdata){ fiore@3: static const hduVector3Dd direction( 0, 1, 0 ); fiore@3: HDdouble instRate; fiore@3: static HDdouble timer = 0; fiore@3: fiore@3: HapticManager *pThis = static_cast(userdata); fiore@3: fiore@3: /* Use the reciprocal of the instantaneous rate as a timer. */ fiore@3: hdGetDoublev(HD_INSTANTANEOUS_UPDATE_RATE, &instRate); fiore@3: timer += 1.0 / instRate; fiore@3: fiore@3: /* Apply a sinusoidal force in the direction of motion. */ fiore@3: hduVecScale(force, direction, VIBRATION_AMPLITUDE * sin(timer * VIBRATION_FREQUENCY)); fiore@3: hdSetDoublev(HD_CURRENT_FORCE, force); fiore@3: } fiore@3: fiore@3: void HLCALLBACK HapticManager::beforeVibrationCB(HLcache *cache, void *userdata){} fiore@3: fiore@3: void HLCALLBACK HapticManager::afterVibrationCB(HLcache *cache, void *userdata){} fiore@3: #endif fiore@3: fiore@3: void HapticManager::init(void) throw(HapticException){ fiore@3: HDErrorInfo error; fiore@3: fiore@3: ghHD = hdInitDevice(HD_DEFAULT_DEVICE); fiore@3: if (HD_DEVICE_ERROR(error = hdGetError())){ fiore@3: throw HapticException(); fiore@3: } fiore@3: fiore@3: ghHLRC = hlCreateContext(ghHD); fiore@3: hlMakeCurrent(ghHLRC); fiore@3: fiore@3: // Enable optimization of the viewing parameters when rendering geometry for OpenHaptics. fiore@3: hlEnable(HL_HAPTIC_CAMERA_VIEW); fiore@3: fiore@3: hlTouchableFace(HL_FRONT); fiore@3: wall1Id = hlGenShapes(1); fiore@3: wall2Id = hlGenShapes(1); fiore@3: attractToHapticId = HL_OBJECT_ANY; fiore@3: lastTouchedNodeID = HL_OBJECT_ANY; fiore@3: lastTouchedEdgeID = HL_OBJECT_ANY; fiore@3: draggedElementID = HL_OBJECT_ANY; fiore@3: lastHighlightElementID = HL_OBJECT_ANY; fiore@3: fiore@3: // initialize effects fiore@3: frictionFX = hlGenEffects(1); fiore@3: vibrationFX = hlGenEffects(1); fiore@3: attractionFX = hlGenEffects(1); fiore@3: springFX = hlGenEffects(1); fiore@3: displayList1 = 0; fiore@3: displayList2 = 0; fiore@3: hlEventd(HL_EVENT_MOTION_LINEAR_TOLERANCE,5); fiore@3: hlEventd(HL_EVENT_MOTION_ANGULAR_TOLERANCE,5); fiore@3: fiore@3: // add callbacks fiore@3: hlAddEventCallback(HL_EVENT_2BUTTONDOWN,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton2CB,this); fiore@3: hlAddEventCallback(HL_EVENT_2BUTTONUP,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton2CB,this); fiore@3: hlAddEventCallback(HL_EVENT_1BUTTONUP,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton1CB,this); fiore@3: hlAddEventCallback(HL_EVENT_1BUTTONDOWN,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton1CB,this); fiore@3: hlAddEventCallback(HL_EVENT_TOUCH, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlTouchCB, this); fiore@3: hlAddEventCallback(HL_EVENT_UNTOUCH, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlUntouchCB, this); fiore@3: hlAddEventCallback(HL_EVENT_MOTION, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlMotionCB, this); fiore@3: fiore@3: #ifdef VIBRATION fiore@3: hlBeginFrame(); fiore@3: hlCallback(HL_EFFECT_START,(HLcallbackProc) beforeVibrationCB, this); fiore@3: hlCallback(HL_EFFECT_STOP,(HLcallbackProc) afterVibrationCB, this); fiore@3: hlCallback(HL_EFFECT_COMPUTE_FORCE, (HLcallbackProc) vibrationCB, this); fiore@3: hlEndFrame(); fiore@3: #endif fiore@3: } fiore@3: fiore@3: HapticManager::ClickStatus HapticManager::getButton1Status(){ fiore@3: if(clickStart == 0){ fiore@3: return NO_CLICK; fiore@3: }else if(doubleClick){ fiore@3: /* double click: set everything back to default value and return DOUBLE_CLICK */ fiore@3: clickStart = 0; fiore@3: doubleClick = false; fiore@3: return TWO_CLICK; fiore@3: }else if(clock() >= clickStart + CLICK_INTERVAL || secondClick) { fiore@3: /* one click and enough time elapsed, so that it cannot be a double click */ fiore@3: clickStart = 0; fiore@3: return ONE_CLICK; fiore@3: } fiore@3: return NO_CLICK; fiore@3: } fiore@3: fiore@3: void HapticManager::doButton1Click(bool doubleClick){ fiore@3: /* DOUBLE CLICK*/ fiore@3: if(doubleClick){ fiore@3: if(lastTouchedNodeID != HL_OBJECT_ANY){ fiore@3: HLboolean isTouching = HL_FALSE; fiore@3: hlGetShapeBooleanv(lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching); // priority to nodes fiore@3: if(isTouching == HL_TRUE||lastTouchedNodeID == lastMovedElement){ fiore@3: try{ fiore@3: CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(lastTouchedNodeID); fiore@3: executeCommand('i',nd.diagramId, 0,0,0,0); fiore@3: }catch(int){ fiore@3: stopExecution("Button 1 callback: Could not retrieve node from ID"); fiore@3: } fiore@3: }else{ fiore@3: if(lastTouchedEdgeID != HL_OBJECT_ANY){ fiore@3: hlGetShapeBooleanv(lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching); fiore@3: if(isTouching == HL_TRUE||lastTouchedNodeID == lastMovedElement){ fiore@3: try{ fiore@3: CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(lastTouchedEdgeID); fiore@3: executeCommand('i',ed.diagramId, 0,0,0,0); fiore@3: }catch(int){ fiore@3: stopExecution("Button 1 callback: Could not retrieve edge from ID"); fiore@3: } fiore@3: } fiore@3: } fiore@3: } fiore@3: } fiore@3: // this was a double click, thus clean up the data about the dragging fiore@3: if(springStatus == DRAGGING){ fiore@3: springStatus = STOP_DRAGGING; fiore@3: secondClick = false; fiore@3: } fiore@3: return; fiore@3: } fiore@3: fiore@3: /* - SINLGE CLICK - */ fiore@3: if(!secondClick){ // button 1 pressed for the first time fiore@3: HLboolean isTouching; fiore@3: /* if the stylus is touching a node then pick it up */ fiore@3: /* give priority to nodes */ fiore@3: if(lastTouchedNodeID != HL_OBJECT_ANY){ fiore@3: hlGetShapeBooleanv(lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching); fiore@3: if(isTouching == HL_TRUE || (stillTouchingLastMovedElement&&lastMovedElement == lastTouchedNodeID)){ fiore@3: draggedElementID = lastTouchedNodeID; fiore@3: try{ fiore@3: CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(lastTouchedNodeID); fiore@3: executeCommand('c',nd.diagramId,0,0,0,0); // pick up command fiore@3: }catch(int){ fiore@3: stopExecution("Button 1 callback: Could not retrieve node from ID"); fiore@3: } fiore@3: return; fiore@3: } fiore@3: } fiore@3: /* no node is currently touched, check the edges now */ fiore@3: if(collectionsManager->isEdge(lastTouchedEdgeID)){ fiore@3: hlGetShapeBooleanv(lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching); fiore@3: if(isTouching == HL_TRUE||(stillTouchingLastMovedElement&&lastMovedElement == lastTouchedEdgeID)){ fiore@3: draggedElementID = lastTouchedEdgeID; fiore@3: try{ fiore@3: CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(lastTouchedEdgeID); fiore@3: executeCommand('c',ed.diagramId,0,0,0,0); // pick up command fiore@3: }catch(int){ fiore@3: stopExecution("Button 1 callback: Could not retrieve edge from ID"); fiore@3: } fiore@3: } fiore@3: } fiore@3: }else{ // button 1 pressed for the second time fiore@3: if(springStatus != DRAGGING) fiore@3: return; fiore@3: springStatus = STOP_DRAGGING; fiore@3: HLdouble leanPoint[3]; fiore@3: hlGetDoublev(HL_PROXY_POSITION,leanPoint); fiore@3: leanPoint[2] = 1; fiore@3: hduVector3Dd winCoordPoint; fiore@3: toScreen(hduVector3Dd(leanPoint),winCoordPoint); fiore@3: fiore@3: try{ fiore@3: if(collectionsManager->isNode(draggedElementID)){ fiore@3: CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(draggedElementID); fiore@3: movedNode = true; fiore@3: executeCommand('m',nd.diagramId,winCoordPoint[0],collectionsManager->getScreenHeight() - winCoordPoint[1],0,0); fiore@3: }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: springStart[2] = 1; fiore@3: hduVector3Dd startWinCoordPoint; fiore@3: toScreen(hduVector3Dd(springStart),startWinCoordPoint);//it's an edge, we also need the point where the motion has started fiore@3: CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(draggedElementID); fiore@3: executeCommand('m', ed.diagramId, fiore@3: winCoordPoint[0], fiore@3: collectionsManager->getScreenHeight() - winCoordPoint[1], fiore@3: startWinCoordPoint[0], fiore@3: collectionsManager->getScreenHeight() - startWinCoordPoint[1] fiore@3: ); fiore@3: } fiore@3: }catch(int){ fiore@3: stopExecution("Button 1 callback: Could not retrieve element from ID"); fiore@3: } fiore@3: /* with second click we place the element and it becomes the last moved element */ fiore@3: lastMovedElement = draggedElementID; fiore@3: secondClick = false; fiore@3: } fiore@3: } fiore@3: fiore@3: void HapticManager::setAttractTo(jint hapticId){ fiore@3: if(attractionStatus == NO_ATTRACTION) // starts only if there is no previous attraction being overwritten fiore@3: attractionStatus = START_ATTRACTION; fiore@3: attractToHapticId = hapticId; fiore@3: alreadyTouchingAttractingElement = false; fiore@3: } fiore@3: fiore@3: void HapticManager::pickUp(jint hapticId){ fiore@3: springStatus = START_DRAGGING; fiore@3: secondClick = true; fiore@3: hlGetDoublev(HL_PROXY_POSITION,springStart); fiore@3: } fiore@3: fiore@3: bool HapticManager::wasAlreadyTouchingAttractingElement(){ fiore@3: return alreadyTouchingAttractingElement; fiore@3: } fiore@3: fiore@3: void HapticManager::draw(void){ fiore@3: // Start haptic frame. (Must do this before rendering any haptic shapes.) fiore@3: hlBeginFrame(); fiore@3: fiore@3: if(!doneOnce){ fiore@3: hlStartEffect(HL_EFFECT_FRICTION, frictionFX); fiore@3: doneOnce = true; fiore@3: } fiore@3: fiore@3: if(wall){ fiore@3: // Plane shape. fiore@3: hlPushAttrib(HL_MATERIAL_BIT|HL_TOUCH_BIT); fiore@3: hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, wall1Id); fiore@3: hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1); fiore@3: hlMaterialf(HL_FRONT, HL_POPTHROUGH, 0 ); fiore@3: hlMaterialf(HL_FRONT, HL_STATIC_FRICTION, 0); fiore@3: hlMaterialf(HL_FRONT, HL_DYNAMIC_FRICTION, 0); fiore@3: hlTouchModel(HL_CONTACT); fiore@3: hlTouchableFace(HL_FRONT); fiore@3: drawPlane(-0.005); fiore@3: hlEndShape(); fiore@3: #define SECOND_PLANE fiore@3: #ifdef SECOND_PLANE fiore@3: hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, wall2Id); fiore@3: hlMaterialf(HL_BACK, HL_STIFFNESS, 1); fiore@3: hlTouchableFace(HL_BACK); fiore@3: hlMaterialf(HL_FRONT, HL_POPTHROUGH, 0 ); fiore@3: hlMaterialf(HL_FRONT, HL_STATIC_FRICTION, 0); fiore@3: hlMaterialf(HL_FRONT, HL_DYNAMIC_FRICTION, 0); fiore@3: drawPlane(0.005); fiore@3: hlEndShape(); fiore@3: #endif fiore@3: hlPopAttrib(); fiore@3: } fiore@3: fiore@3: // Start a new haptic shape. Use the feedback buffer to capture OpenGL fiore@3: // geometry for haptic rendering. fiore@3: fiore@3: // draw nodes fiore@3: int size = collectionsManager->getNodesNum(); fiore@3: fiore@3: for(int i=0; i< size;i++){ fiore@3: CollectionsManager::NodeData & nd = collectionsManager->getNodeData(i); fiore@3: fiore@3: hlPushAttrib(HL_HINT_BIT); fiore@3: hlHinti(HL_SHAPE_FEEDBACK_BUFFER_VERTICES, 3); fiore@3: hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, nd.hapticId); fiore@3: hlTouchModel(HL_CONSTRAINT); fiore@3: hlTouchModelf(HL_SNAP_DISTANCE , 4.0f ); fiore@3: hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1.0); fiore@3: hlMaterialf(HL_FRONT_AND_BACK, HL_DAMPING, 0); fiore@3: fiore@3: hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION, 0); fiore@3: hlMaterialf(HL_FRONT_AND_BACK, HL_DYNAMIC_FRICTION, 0); fiore@3: fiore@3: //draw the point fiore@3: glBegin(GL_POINTS); fiore@3: glVertex3d(nd.x,nd.y,0); fiore@3: glEnd(); fiore@3: fiore@3: // End the shape. fiore@3: hlEndShape(); fiore@3: hlPopAttrib(); fiore@3: fiore@3: // saves the coorinates for a later use so that for the effect we don't need to cycle again fiore@3: if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){ fiore@3: if(nd.hapticId == attractToHapticId){ fiore@3: attractToCoord[0] = nd.x; fiore@3: attractToCoord[1] = nd.y; fiore@3: attractToCoord[2] = 0; fiore@3: } fiore@3: } fiore@3: } fiore@3: fiore@5: /* draw lines. When a node is moved, edges are not drawn for the very first haptic frame after the shift. * fiore@3: * This is to address the behaviour of the device which, after a n ode is moved cannot see the styilus * fiore@3: * touching the edge. As a consequence of that in hlMotionCB() when moving away from a node along an edge * fiore@3: * no edge name will be spoken and furthermore the pick up botton won't work as the device thinks it's not * fiore@3: * touching anything. Not drawing the edge on the first frame, seems to address this issue. */ fiore@3: if(!movedNode) { fiore@3: size = collectionsManager->getEdgesNum(); fiore@3: for(int i = 0; igetEdgeData(i); fiore@3: hlPushAttrib(HL_HINT_BIT|HL_MATERIAL_BIT); fiore@3: hlHinti(HL_SHAPE_FEEDBACK_BUFFER_VERTICES, ed.getSize()); fiore@3: fiore@3: hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, ed.hapticId); fiore@3: hlTouchModel(HL_CONSTRAINT); fiore@3: hlTouchModelf(HL_SNAP_DISTANCE, ( (stickyMode && springStatus != DRAGGING && springStatus != START_DRAGGING) ? 20.0f : 3.5f )); fiore@3: hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1); fiore@3: fiore@3: if(ed.stipplePattern == 0xFFFF){ fiore@3: hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION, 0.5f); fiore@3: } fiore@3: fiore@3: glLineWidth(1.0); fiore@3: fiore@3: glBegin(GL_LINES); fiore@3: for(unsigned int j = 0; j < ed.getSize(); j++){ fiore@3: for(unsigned int k = j; k < ed.getSize(); k++){ fiore@3: if(ed.adjMatrix[j][k]){ fiore@3: if( k >= ed.nodeStart && fiore@3: (!stickyMode || (springStatus != RELEASED && springStatus != STOP_DRAGGING)) fiore@3: && attractionStatus != DOING_ATTRACTION && attractionStatus != START_ATTRACTION){ // not a line connecting two edges and stickyMode not enabled fiore@3: /* it's drawing a line from a point to a node, thus it draws it a little shorter*/ fiore@3: 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: if(hypotenuse == 0) fiore@3: continue; fiore@3: double mySin = (ed.y[j] - ed.y[k])/hypotenuse; fiore@3: double myCos = (ed.x[j] - ed.x[k])/hypotenuse; fiore@3: glVertex3d(ed.x[k]+(EDGE_SHORTEND_VAL * myCos) , ed.y[k]+(EDGE_SHORTEND_VAL*mySin),0); fiore@3: if(j >= ed.nodeStart) //its a direct edge connecting two nodes fiore@3: glVertex3d(ed.x[j]+(EDGE_SHORTEND_VAL * -myCos),ed.y[j]+(EDGE_SHORTEND_VAL*-mySin),0); fiore@3: else fiore@3: glVertex3d(ed.x[j],ed.y[j],0); fiore@3: }else{ fiore@3: glVertex3d(ed.x[j],ed.y[j],0); fiore@3: glVertex3d(ed.x[k],ed.y[k],0); fiore@3: } fiore@3: } fiore@3: } fiore@3: } fiore@3: glEnd(); fiore@3: hlEndShape(); fiore@3: hlPopAttrib(); fiore@3: fiore@3: // saves the coorinates for a later use so that for the effect we don't need to cycle again fiore@3: if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){ fiore@3: if(ed.hapticId == attractToHapticId){ fiore@3: attractToCoord[0] = ed.attractPoint[0]; fiore@3: attractToCoord[1] = ed.attractPoint[1]; fiore@3: attractToCoord[2] = 0; fiore@3: } fiore@3: } fiore@3: } fiore@3: }else { fiore@3: movedNode = false; fiore@3: } fiore@3: /* --- FORCES --- */ fiore@3: /* friction */ fiore@3: if(lastTouchedEdgeID != HL_OBJECT_ANY){ fiore@3: /* check if the edge is still there, it might not as the tab could be changed */ fiore@3: if(collectionsManager->isEdge(lastTouchedEdgeID)){ ccmi-guest@8: if(frictionDisabled||lastTouchedEdgeIsUntouched){ ccmi-guest@8: hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0); // none fiore@3: hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0); fiore@3: }else{ fiore@3: if(lastTouchedEdgeStipplePattern == 0xAAAA){ // dotted fiore@3: hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.4f); fiore@3: hlEffectd(HL_EFFECT_PROPERTY_GAIN, 1); fiore@3: }else if(lastTouchedEdgeStipplePattern == 0xF0F0){ // dashed fiore@3: hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.6); fiore@3: hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.6); ccmi-guest@8: }else{ ccmi-guest@8: hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0); // solid ccmi-guest@8: hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0); fiore@3: } fiore@3: } fiore@3: }else{ fiore@3: lastTouchedEdgeID = HL_OBJECT_ANY; fiore@3: hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0); fiore@3: hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0); fiore@3: } fiore@3: hlUpdateEffect(frictionFX); fiore@3: } fiore@3: fiore@3: // spring fiore@3: if(springStatus == START_DRAGGING){ fiore@3: hlPushAttrib(HL_EFFECT_BIT); fiore@3: hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.3); fiore@3: hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.5); fiore@3: fiore@3: hduMatrix worldToView; fiore@3: hduMatrix graphicToTouch; fiore@3: hduMatrix touchToWorkspace; fiore@3: hduMatrix WorldToDevice; fiore@3: glMatrixMode(GL_MODELVIEW); fiore@3: glGetDoublev(GL_MODELVIEW_MATRIX,worldToView); fiore@3: hlGetDoublev(HL_VIEWTOUCH_MATRIX, graphicToTouch ); fiore@3: hlGetDoublev(HL_TOUCHWORKSPACE_MATRIX, touchToWorkspace ); fiore@3: WorldToDevice = worldToView * graphicToTouch * touchToWorkspace; fiore@3: fiore@3: hduVector3Dd dst; fiore@3: WorldToDevice.multVecMatrix(hduVector3Dd(springStart[0],springStart[1],0),dst); fiore@3: fiore@3: hlEffectdv(HL_EFFECT_PROPERTY_POSITION, dst); fiore@3: hlStartEffect(HL_EFFECT_SPRING, springFX); fiore@3: hlPopAttrib(); fiore@3: springStatus = DRAGGING; fiore@3: }else if(springStatus == STOP_DRAGGING){ fiore@3: hlStopEffect(springFX); fiore@3: springStatus = RELEASED; fiore@3: } fiore@3: fiore@3: // attraction fiore@3: HLboolean isTouching = HL_FALSE; fiore@3: if(attractionStatus == DOING_ATTRACTION || attractionStatus == START_ATTRACTION){ fiore@3: /* we need to be sure we're currently touching the element */ fiore@3: if(attractToHapticId != HL_OBJECT_ANY){ fiore@3: hlGetShapeBooleanv(attractToHapticId,HL_PROXY_IS_TOUCHING,&isTouching); fiore@3: } fiore@3: fiore@3: // when touching the element we're getting attracted to, then shut the force down fiore@3: if(isTouching == HL_TRUE){ fiore@3: if(attractionStatus == DOING_ATTRACTION){ // we finally reached the attracting node fiore@3: attractionStatus = STOP_ATTRACTION; fiore@3: }else{ fiore@3: attractionStatus = NO_ATTRACTION; // we were already touching the node we wanted to get attracted to fiore@3: alreadyTouchingAttractingElement = true; fiore@3: } fiore@3: } fiore@3: } fiore@3: fiore@3: if(attractionStatus == STOP_ATTRACTION){ fiore@3: attractionStatus = NO_ATTRACTION; fiore@3: hlStopEffect(attractionFX); fiore@3: } else if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){ fiore@3: HLdouble devicePosition[3]; fiore@3: HDdouble direction[3]; fiore@3: hlGetDoublev(HL_DEVICE_POSITION,devicePosition); fiore@3: direction[0] = attractToCoord[0] - devicePosition[0]; fiore@3: direction[1] = attractToCoord[1] - devicePosition[1]; fiore@3: direction[2] = attractToCoord[2] - devicePosition[2]; fiore@3: HDdouble directionNorm = norm(direction); fiore@3: if(directionNorm != 0 ){ fiore@3: direction[0] /= directionNorm; fiore@3: direction[1] /= directionNorm; fiore@3: direction[2] /= directionNorm; fiore@3: } fiore@3: hlPushAttrib(HL_EFFECT_BIT); fiore@3: hlEffectdv(HL_EFFECT_PROPERTY_DIRECTION, direction); fiore@3: hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.5f); fiore@3: hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.5f); fiore@3: if(attractionStatus == START_ATTRACTION){ fiore@3: hlStartEffect(HL_EFFECT_CONSTANT, attractionFX); fiore@3: attractionStatus = DOING_ATTRACTION; fiore@3: }else{ fiore@3: hlUpdateEffect(attractionFX); fiore@3: } fiore@3: hlPopAttrib(); fiore@3: } fiore@3: #ifdef VIBRATION fiore@3: if(vibrationStatus == START_VIBRATION){ fiore@3: hlStartEffect(HL_EFFECT_CALLBACK, vibrationFX); fiore@3: vibrationEnd = clock() + VIBRATION_DURATION; fiore@3: vibrationStatus = DOING_VIBRATION; fiore@3: }else if(vibrationStatus == DOING_VIBRATION){ fiore@3: if(clock() >= vibrationEnd){ fiore@3: hlStopEffect(vibrationFX); fiore@3: vibrationStatus = NO_VIBRATION; fiore@3: } fiore@3: } fiore@3: #endif fiore@3: // End the haptic frame. fiore@3: hlEndFrame(); fiore@3: } fiore@3: fiore@3: void HapticManager::drawPlane(float zoffset){ fiore@3: GLuint displayList = (zoffset < 0) ? displayList1 : displayList2; fiore@3: fiore@3: if (displayList){ fiore@3: glCallList(displayList); fiore@3: } fiore@3: else{ fiore@3: if(zoffset < 0){ fiore@3: displayList1 = glGenLists(1); fiore@3: glNewList(displayList1, GL_COMPILE_AND_EXECUTE); fiore@3: }else{ fiore@3: displayList2 = glGenLists(1); fiore@3: glNewList(displayList2, GL_COMPILE_AND_EXECUTE); fiore@3: } fiore@3: glNewList(displayList, GL_COMPILE_AND_EXECUTE); fiore@3: glPushAttrib(GL_ENABLE_BIT); fiore@3: glPolygonOffset(1,1); fiore@3: glEnable(GL_POLYGON_OFFSET_FILL); fiore@3: fiore@3: glBegin(GL_QUADS); fiore@3: glVertex3f(-5, -5, zoffset); fiore@3: glVertex3f(5, -5, zoffset); fiore@3: glVertex3f(5, 5, zoffset); fiore@3: glVertex3f(-5, 5, zoffset); fiore@3: glEnd(); fiore@3: glDisable(GL_POLYGON_OFFSET_FILL); fiore@3: glPopAttrib(); fiore@3: glEndList(); fiore@3: } fiore@3: fiore@3: } fiore@3: fiore@3: HHD HapticManager::ghHD = HD_INVALID_HANDLE; fiore@3: HHLRC HapticManager::ghHLRC = 0; fiore@3: const double HapticManager::EDGE_SHORTEND_VAL = 0.06; fiore@3: const unsigned int HapticManager::CLICK_INTERVAL = CLOCKS_PER_SEC/5; fiore@3: const double HapticManager::EDGE_SPEECH_DISTANCE_WHEN_LEAVING_NODE = 0.03; fiore@3: #ifdef VIBRATION fiore@3: const HDdouble HapticManager::VIBRATION_AMPLITUDE = 0.88;//0.88; fiore@3: const HDint HapticManager::VIBRATION_FREQUENCY = 170;//160; fiore@3: const unsigned int HapticManager::VIBRATION_DURATION = CLOCKS_PER_SEC/10; // = 100 ms fiore@3: #endif