Пример #1
0
void PackageCanvas::prepare_for_move(bool on_resize) {
  if (! on_resize) {
    DiagramCanvas::prepare_for_move(on_resize);
    
    Q3CanvasItemList l = collisions(TRUE);
    Q3CanvasItemList::ConstIterator it;
    Q3CanvasItemList::ConstIterator end = l.end();
    DiagramItem * di;
    BrowserNode * p = get_bn();
  
    for (it = l.begin(); it != end; ++it) {
      if ((*it)->visible() && // at least not deleted
	  !(*it)->selected() &&
	  ((di = QCanvasItemToDiagramItem(*it)) != 0) &&
	  di->move_with_its_package()) {
	BrowserNode * bn = di->get_bn();
	
	do
	  bn = (BrowserNode *) bn->parent();
	while (bn->get_type() != UmlPackage);
	
	if (bn == p) {
	  the_canvas()->select(*it);
	  di->prepare_for_move(FALSE);
	}
      }
    }
  }
}
Пример #2
0
Q3CanvasItem * UmlCanvas::collision(const QPoint & p) const
{
    Q3CanvasItemList l = collisions(p);
    Q3CanvasItemList::ConstIterator it;
    Q3CanvasItemList::ConstIterator end = l.end();
    ArrowCanvas * arrow = 0;

    for (it = l.begin(); it != end; ++it)
        if (((*it)->visible()) && // at least not deleted
            !isa_alien(*it) &&
            !isa_col_msg_dirs(*it)) {
            switch ((*it)->rtti()) {
            case RTTI_ARROW:
                if (arrow == 0)
                    arrow = (ArrowCanvas *) *it;

                break;

            case RTTI_LABEL:
                return (arrow == 0) ? *it : arrow;

            default:
                // isa DiagramCanvas
                return ((arrow == 0) ||
                        (small_element(((DiagramCanvas *) *it)->rect()) &&
                         ((DiagramCanvas *) *it)->attached_to(arrow)))
                       ? *it : arrow;
            }
        }

    return arrow;
}
Пример #3
0
void VerletShapeTests::sphereMissesCapsule() {
    // non-overlapping sphere and capsule
    float radiusA = 1.5f;
    float radiusB = 2.3f;
    float totalRadius = radiusA + radiusB;
    float halfHeightB = 1.7f;
    float axialOffset = totalRadius + 1.1f * halfHeightB;
    float radialOffset = 1.2f * radiusA + 1.3f * radiusB;
    
    // create points for the sphere + capsule
    VerletPoint points[3];
    for (int i = 0; i < 3; ++i) {
        points[i]._position = glm::vec3(0.0f);
    }

    // give the points to the shapes
    VerletSphereShape sphereA(radiusA, points);
    VerletCapsuleShape capsuleB(radiusB, points+1, points+2);
    capsuleB.setHalfHeight(halfHeightB);

    // give the capsule some arbitrary transform
    float angle = 37.8f;
    glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) );
    glm::quat rotation = glm::angleAxis(angle, axis);
    glm::vec3 translation(15.1f, -27.1f, -38.6f);
    capsuleB.setRotation(rotation);
    capsuleB.setTranslation(translation);

    CollisionList collisions(16);

    // walk sphereA along the local yAxis next to, but not touching, capsuleB
    glm::vec3 localStartPosition(radialOffset, axialOffset, 0.0f);
    int numberOfSteps = 10;
    float delta = 1.3f * (totalRadius + halfHeightB) / (numberOfSteps - 1);
    for (int i = 0; i < numberOfSteps; ++i) {
        // translate sphereA into world-frame
        glm::vec3 localPosition = localStartPosition + ((float)i * delta) * yAxis;
        sphereA.setTranslation(rotation * localPosition + translation);

        // sphereA agains capsuleB
        if (ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphere and capsule should NOT touch" << std::endl;
        }

        // capsuleB against sphereA
        if (ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphere and capsule should NOT touch" << std::endl;
        }
    }

    if (collisions.size() > 0) {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: expected empty collision list but size is " << collisions.size() << std::endl;
    }
}
Пример #4
0
/** create an animated cell from pixmaps at position row,col */
GraphicalCell::GraphicalCell( int row, int col , QCanvas *canvas)
	: QCanvasSprite( ImageTheme.cells, canvas )
{	
	collisions( true );
	setFrame( 0 );	
	move( _size*col, _size*row );
	setZ( CAN_GROUND );
	show();
}
Пример #5
0
void		load_game(void)
{
  update_background();
  update_content();
  collisions();
  update_stats();
  display_content();
  draw();
}
Пример #6
0
//=============================================================================
// Call repeatedly by the main message loop in WinMain
//=============================================================================
void Game::run(HWND hwnd)
{
    if(graphics == NULL)            // if graphics not initialized
        return;

    // calculate elapsed time of last frame, save in frameTime
    QueryPerformanceCounter(&timeEnd);
    frameTime = (float)(timeEnd.QuadPart - timeStart.QuadPart ) / (float)timerFreq.QuadPart;

    // Power saving code, requires winmm.lib
    // if not enough time has elapsed for desired frame rate
    if (frameTime < MIN_FRAME_TIME) 
    {
        sleepTime = (DWORD)((MIN_FRAME_TIME - frameTime)*1000);
        timeBeginPeriod(1);         // Request 1mS resolution for windows timer
        Sleep(sleepTime);           // release cpu for sleepTime
        timeEndPeriod(1);           // End 1mS timer resolution
        return;
    }

    if (frameTime > 0.0)
        fps = (fps*0.99f) + (0.01f/frameTime);  // average fps
    if (frameTime > MAX_FRAME_TIME) // if frame rate is very slow
        frameTime = MAX_FRAME_TIME; // limit maximum frameTime
    timeStart = timeEnd;

    // update(), ai(), and collisions() are pure virtual functions.
    // These functions must be provided in the class that inherits from Game.
    if (!paused)                    // if not paused
    {
        update();                   // update all game items
        ai();                       // artificial intelligence
        collisions();               // handle collisions
        input->vibrateControllers(frameTime); // handle controller vibration
    }
//for FPS on the title
	char windowtext[255];
	sprintf(windowtext,"frameTime = %f, FPS= %f",frameTime, fps);
	SetWindowText(hwnd, windowtext);

    renderGame();                   // draw all game items
//    input->readCoSntrollers();       // read state of controllers

    // if Alt+Enter toggle fullscreen/window
    if (input->isKeyDown(ALT_KEY) && input->wasKeyPressed(ENTER_KEY))
        setDisplayMode(graphicsNS::TOGGLE); // toggle fullscreen/window

    // if Esc key, set window mode
    if (input->isKeyDown(ESC_KEY))
        setDisplayMode(graphicsNS::WINDOW); // set window mode

    // Clear input
    // Call this after all key checks are done
    input->clear(inputNS::KEYS_PRESSED);
	
}
Пример #7
0
void VerletShapeTests::sphereMissesSphere() {
    // non-overlapping spheres of unequal size

    float radiusA = 7.0f;
    float radiusB = 3.0f;
    float alpha = 1.2f;
    float beta = 1.3f;
    glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f));
    float offsetDistance = alpha * radiusA + beta * radiusB;

    // create points for the sphere centers
    VerletPoint points[2];

    // give pointers to the spheres
    VerletSphereShape sphereA(radiusA, (points + 0));
    VerletSphereShape sphereB(radiusB, (points + 1));

    // set the positions of the spheres by slamming the points directly
    points[0]._position = origin;
    points[1]._position = offsetDistance * offsetDirection;

    CollisionList collisions(16);

    // collide A to B...
    {
        bool touching = ShapeCollider::collideShapes(&sphereA, &sphereB, collisions);
        if (touching) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphereA and sphereB should NOT touch" << std::endl;
        }
    }

    // collide B to A...
    {
        bool touching = ShapeCollider::collideShapes(&sphereB, &sphereA, collisions);
        if (touching) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphereA and sphereB should NOT touch" << std::endl;
        }
    }

    // also test shapeShape
    {
        bool touching = ShapeCollider::collideShapes(&sphereB, &sphereA, collisions);
        if (touching) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphereA and sphereB should NOT touch" << std::endl;
        }
    }

    if (collisions.size() > 0) {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: expected empty collision list but size is " << collisions.size() << std::endl;
    }
}
Пример #8
0
// ==================================================================
// WinMain内のメインのメッセージループで繰り返し呼び出される
// ==================================================================
void Game::run(HWND hwnd)
{
	if(graphics == NULL)  // グラフィックスが初期化されていない場合
		return;

	// エスケープキーで終了
	if(input->isKeyDown(ESC_KEY))
	{
		PostQuitMessage(0);
		return;
	}

	// 最後のフレームからの経過時間を計算、frameTimeに保存
	QueryPerformanceCounter(&timeEnd);
	frameTime = (float)(timeEnd.QuadPart - timeStart.QuadPart) / (float)timeFreq.QuadPart;

	// 省電力コード(winmm.libが必要)
	// 希望するフレームレートに対して経過時間が短い場合
	if(frameTime < MIN_FRAME_TIME)
	{
		sleepTime = (DWORD)((MIN_FRAME_TIME - frameTime) * 1000);
		timeBeginPeriod(1);  // 1ミリ秒の分解能をWindowsタイマーに要求
		Sleep(sleepTime);    // sleepTimeの間、CPUを解放
		timeEndPeriod(1);    // 1ミリ秒のタイマー分解能を終了
		return;
	}

	if(frameTime > 0.0)
		fps = (fps * 0.99f) + (0.01f / frameTime);  // 平均fps

	if(frameTime > MAX_FRAME_TIME)        // フレームレートが非常に遅い場合
		frameTime = MAX_FRAME_TIME;       // 最大frameTimeを制限

	timeStart = timeEnd;

	input->readControllers();             // コントローラの状態を読み取る

	// update()、ai()、collisions()は純粋仮想関数です
	// これらの関数は、Gameを継承しているクラス側で記述する必要があります
	if(!paused)                           // 一時停止中でない
	{
		update();                         // すべてのゲームアイテムを更新
		ai();                             // 人工知能
		collisions();                     // 衝突を処理
		input->vibrateControllers(frameTime);  //コントローラの振動を処理
	}
	renderGame();                         // すべてのゲームアイテムを描画

	// 入力をクリア
	// すべてのキーチェックが行われた後これを呼び出す
	input->clear(inputNS::KEYS_PRESSED);
}
Пример #9
0
/** add comments here */
GraphicalFightUnit::GraphicalFightUnit( QCanvas * canvas )
	: QCanvasSprite( (*ImageTheme.creatures[0])[0], canvas )
{
 	collisions( true );
 	setFrame( 0 );
	setZ( CAN_LORD );
	show();

	/*QBrush brush( yellow );
	_numBox = new QCanvasRectangle( canvas );
	_numBox->setBrush( brush );
	_numBox->collisions( true );
	_numBox->setZ( CAN_ARROW );
	_numBox->setSize( 50, 25 );
	_numBox->show();*/
}
Пример #10
0
void Floater::moveBy(double dx, double dy)
{
	if (!isEnabled())
		return;

	QCanvasItemList l = collisions(false);
	for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it)
	{
		CanvasItem *item = dynamic_cast<CanvasItem *>(*it);

		if (!noUpdateZ && item && item->canBeMovedByOthers())
			item->updateZ(this);

		if ((*it)->z() >= z())
		{
			if (item && item->canBeMovedByOthers() && collidesWith(*it))
			{
				if ((*it)->rtti() == Rtti_Ball)
				{
					//((Ball *)(*it))->setState(Rolling);
					(*it)->moveBy(dx, dy);
					if (game && game->hasFocus() && !game->isEditing() && game->curBall() == (Ball *)(*it))
							game->ballMoved();
				}
				else if ((*it)->rtti() != Rtti_Putter)
					(*it)->moveBy(dx, dy);
			}
		}
	}

	point->dontMove();
	point->move(x() + width(), y() + height());

	// this call must come after we have tested for collisions, otherwise we skip them when saving!
	// that's a bad thing
	QCanvasRectangle::moveBy(dx, dy);

	// because we don't do Bridge::moveBy();
	topWall->move(x(), y());
	botWall->move(x(), y() - 1);
	leftWall->move(x(), y());
	rightWall->move(x(), y());

	if (game && game->isEditing())
		game->updateHighlighter();
}
Пример #11
0
void FixedSet::Init(const std::vector<int>& elements) {
    int size = elements.size();
    std::vector<std::vector <int> > collisions(size);
    buckets_.resize(size);

    function_ = GenerateUniversalHashFunctor(PRIME_NUMBER);

    for (auto iterator = elements.begin(); iterator != elements.end(); ++iterator) {
        int element = AdjustElement(*iterator);
        int index = GetIndex(element);
        collisions[index].push_back(element);
    }

    for (int bucket_index = 0; bucket_index != size; ++bucket_index) {
        buckets_[bucket_index].Init(collisions[bucket_index]);
    }
}
Пример #12
0
void ShapeColliderTests::sphereMissesSphere() {
    // non-overlapping spheres of unequal size
    float radiusA = 7.f;
    float radiusB = 3.f;
    float alpha = 1.2f;
    float beta = 1.3f;
    glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.f, 2.f, 3.f));
    float offsetDistance = alpha * radiusA + beta * radiusB;

    SphereShape sphereA(radiusA, origin);
    SphereShape sphereB(radiusB, offsetDistance * offsetDirection);
    CollisionList collisions(16);

    // collide A to B...
    {
        bool touching = ShapeCollider::collideShapes(&sphereA, &sphereB, collisions);
        if (touching) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphereA and sphereB should NOT touch" << std::endl;
        }
    }

    // collide B to A...
    {
        bool touching = ShapeCollider::collideShapes(&sphereB, &sphereA, collisions);
        if (touching) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphereA and sphereB should NOT touch" << std::endl;
        }
    }

    // also test shapeShape
    {
        bool touching = ShapeCollider::collideShapes(&sphereB, &sphereA, collisions);
        if (touching) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphereA and sphereB should NOT touch" << std::endl;
        }
    }

    if (collisions.size() > 0) {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: expected empty collision list but size is " << collisions.size()
            << std::endl;
    }
}
Пример #13
0
void Game::Draw()
{
	//SFML is a polling based system, This is why we call pollEvent. 
	//If there is an event, it returns true, and is set equall to currentEvent.
	if(player1score > 9)
	{
		cout<<"Player 1 Wins!!! "<<endl;
		_gameState = GameOver;
	}
		if(player2score > 9)
	{
		cout<<"Player 2 Wins!!! "<<endl;
		_gameState = GameOver;
	}
	sf::Event event;
	while(ballMoving && !ballGoal){
		//cout<<"ball moving"<<endl;
		_mainWindow.clear(sf::Color(0,0,0));//Clears the stuff drawn to the screen.
		ball.move();
		playerUpdate();
		ball.update(_mainWindow);
		collisions();//check collisions with the ball.
		//		playerUpdate();
		_mainWindow.display();


		while(_mainWindow.pollEvent(event))
		{


			movement();//Update the movement of the paddles
			playerUpdate();
			ball.update(_mainWindow);
			_mainWindow.display();
			switch(event.type)
			{
			case sf::Event::Closed:
				_gameState = Game::Exiting;
				break;

			}

		}
	}

}
Пример #14
0
//=============================================================================
// Call repeatedly by the main message loop in WinMain
//=============================================================================
void Game::run(HWND hwnd)
{
    if(graphics == NULL)            // if graphics not initialized
        return;

    // calculate elapsed time of last frame, save in frameTime
    QueryPerformanceCounter(&timeEnd);
    frameTime = (float)(timeEnd.QuadPart - timeStart.QuadPart ) / 
                (float)timerFreq.QuadPart;

    // Power saving code, requires winmm.lib
    // if not enough time has elapsed for desired frame rate
    if (frameTime < MIN_FRAME_TIME) 
    {
        sleepTime = (DWORD)((MIN_FRAME_TIME - frameTime)*1000);
        timeBeginPeriod(1);         // Request 1mS resolution for windows timer
        Sleep(sleepTime);           // release cpu for sleepTime
        timeEndPeriod(1);           // End 1mS timer resolution
        return;
    }

    if (frameTime > 0.0)
        fps = (fps*0.99f) + (0.01f/frameTime);  // average fps

    if (frameTime > MAX_FRAME_TIME) // if frame rate is very slow
        frameTime = MAX_FRAME_TIME; // limit maximum frameTime

    timeStart = timeEnd;

    // update(), ai(), and collisions() are pure virtual functions.
    // These functions must be provided in the class that inherits from Game.
    if (!paused)                    // if not paused
    {
        update();                   // update all game items
        ai();                       // artificial intelligence
        collisions();               // handle collisions
        input->vibrateControllers(frameTime); // handle controller vibration
    }
    renderGame();                   // draw all game items
    input->readControllers();       // read state of controllers


    // Clear input
    // Call this after all key checks are done
    input->clear(inputNS::KEYS_PRESSED);
}
Пример #15
0
void PhysicsEntity::disableCurrentSelfCollisions() {
    CollisionList collisions(10);
    int numShapes = _shapes.size();
    for (int i = 0; i < numShapes; ++i) {
        const Shape* shape = _shapes.at(i);
        if (!shape) {
            continue;
        }
        for (int j = i+1; j < numShapes; ++j) {
            if (!collisionsAreEnabled(i, j)) {
                continue;
            }
            const Shape* otherShape = _shapes.at(j);
            if (otherShape && ShapeCollider::collideShapes(shape, otherShape, collisions)) {
                disableCollisions(i, j);
                collisions.clear();
            }
        }
    }
}
Пример #16
0
void FragmentCanvas::prepare_for_move(bool on_resize) {
  if (!on_resize) {
    DiagramCanvas::prepare_for_move(on_resize);
    
    QRect r = rect();
    Q3CanvasItemList l = collisions(TRUE);
    Q3CanvasItemList::ConstIterator it;
    Q3CanvasItemList::ConstIterator end = l.end();
    DiagramItem * di;
  
    for (it = l.begin(); it != end; ++it) {
      if ((*it)->visible() && // at least not deleted
	  !(*it)->selected() &&
	  ((di = QCanvasItemToDiagramItem(*it)) != 0) &&
	  r.contains(di->rect(), TRUE) &&
	  di->move_with(UmlFragment)) {
	the_canvas()->select(*it);
	di->prepare_for_move(FALSE);
      }
    }
  }
}
Пример #17
0
void Bullet::checkCollision()
{
    QCanvasItem* item;
    QCanvasItemList l=collisions(FALSE);
      for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
          item = *it;
          if ( (item->rtti()== 1500) && (item->collidesWith(this)) ) {
               Man* deadman = (Man*)item;
               if (deadman->frame() != 5) return;
               deadman->done();
	       emit score(10);
               setShotCount(shotcount+1);
               setAnimated(false);
               nobullets--;
               delete this;
               return;
          }
          else if ( (item->rtti()==1900) && (item->collidesWith(this)) ) {
               Helicopter* deadchopper = (Helicopter*) item;
               deadchopper->done();
	       emit score(50);
               setAnimated(false);
               nobullets--;
               delete this;
               return;
         }
      }
      //check shot is not out of bounds
     if ( (y() < 0) || (x() < 0) ||
          (y() > canvas()->height()) ||
          ( x() > canvas()->width()))  {
          setAnimated(false);
          nobullets--;
          delete this;
          return;
     }
}
Пример #18
0
//=============================================================================
// Call repeatedly by the main message loop in WinMain
//=============================================================================
void Game::run(HWND hwnd)
{
    if(graphics == NULL)            // if graphics not initialized
        return;

    // calculate elapsed time of last frame, save in frameTime
    QueryPerformanceCounter(&timeEnd);
    frameTime = (float)(timeEnd.QuadPart - timeStart.QuadPart ) / (float)timerFreq.QuadPart;

    // Power saving code, requires winmm.lib
    // if not enough time has elapsed for desired frame rate
    if (frameTime < MIN_FRAME_TIME)
    {
        sleepTime = (DWORD)((MIN_FRAME_TIME - frameTime)*1000);
        timeBeginPeriod(1);         // Request 1mS resolution for windows timer
        Sleep(sleepTime);           // release cpu for sleepTime
        timeEndPeriod(1);           // End 1mS timer resolution
        return;
    }

    if (frameTime > 0.0)
        fps = (fps*0.99f) + (0.01f/frameTime);  // average fps
    if (frameTime > MAX_FRAME_TIME) // if frame rate is very slow
        frameTime = MAX_FRAME_TIME; // limit maximum frameTime
    timeStart = timeEnd;

    // update(), ai(), and collisions() are pure virtual functions.
    // These functions must be provided in the class that inherits from Game.
    if (!paused)                    // if not paused
    {
        update();                   // update all game items
        ai();                       // artificial intelligence
        collisions();               // handle collisions
        input->vibrateControllers(frameTime); // handle controller vibration
    }
    renderGame();                   // draw all game items

    //check for console key
    if (input->getCharIn() == CONSOLE_KEY)
    {
        input->clearCharIn();       // clear last char
        console->showHide();
        paused = console->getVisible(); // pause game when console is visible
    }
    consoleCommand();               // process user entered console command

    input->readControllers();       // read state of controllers

    messageDialog->update();
    inputDialog->update();

    audio->run();                   // perform periodic sound engine tasks

    // if Alt+Enter toggle fullscreen/window
    if (input->isKeyDown(ALT_KEY) && input->wasKeyPressed(ENTER_KEY))
        setDisplayMode(graphicsNS::TOGGLE); // toggle fullscreen/window

    // if Esc key, set window mode
    if (input->isKeyDown(ESC_KEY))
        setDisplayMode(graphicsNS::WINDOW); // set window mode

    // if Pause key
    if (input->wasKeyPressed(VK_PAUSE))
        paused = !paused;

    // Clear input keys pressed
    // Call this after all key checks are done
    input->clear(inputNS::KEYS_PRESSED);
}
Пример #19
0
void ShapeColliderTests::sphereTouchesSphere() {
    // overlapping spheres of unequal size
    float radiusA = 7.f;
    float radiusB = 3.f;
    float alpha = 0.2f;
    float beta = 0.3f;
    glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.f, 2.f, 3.f));
    float offsetDistance = alpha * radiusA + beta * radiusB;
    float expectedPenetrationDistance = (1.f - alpha) * radiusA + (1.f - beta) * radiusB;
    glm::vec3 expectedPenetration = expectedPenetrationDistance * offsetDirection;

    SphereShape sphereA(radiusA, origin);
    SphereShape sphereB(radiusB, offsetDistance * offsetDirection);
    CollisionList collisions(16);
    int numCollisions = 0;

    // collide A to B...
    {
        bool touching = ShapeCollider::collideShapes(&sphereA, &sphereB, collisions);
        if (!touching) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphereA and sphereB should touch" << std::endl;
        } else {
            ++numCollisions;
        }

        // verify state of collisions
        if (numCollisions != collisions.size()) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: expected collisions size of " << numCollisions << " but actual size is " << collisions.size()
                << std::endl;
        }
        CollisionInfo* collision = collisions.getCollision(numCollisions - 1);
        if (!collision) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: null collision" << std::endl;
        }
    
        // penetration points from sphereA into sphereB
        float inaccuracy = glm::length(collision->_penetration - expectedPenetration);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad penetration: expected = " << expectedPenetration
                << " actual = " << collision->_penetration 
                << std::endl;
        }
    
        // contactPoint is on surface of sphereA
        glm::vec3 AtoB = sphereB.getPosition() - sphereA.getPosition();
        glm::vec3 expectedContactPoint = sphereA.getPosition() + radiusA * glm::normalize(AtoB);
        inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad contactPoint: expected = " << expectedContactPoint
                << " actual = " << collision->_contactPoint 
                << std::endl;
        }
    }

    // collide B to A...
    {
        bool touching = ShapeCollider::collideShapes(&sphereB, &sphereA, collisions);
        if (!touching) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphereA and sphereB should touch" << std::endl;
        } else {
            ++numCollisions;
        }
    
        // penetration points from sphereA into sphereB
        CollisionInfo* collision = collisions.getCollision(numCollisions - 1);
        float inaccuracy = glm::length(collision->_penetration + expectedPenetration);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad penetration: expected = " << expectedPenetration
                << " actual = " << collision->_penetration 
                << std::endl;
        }
    
        // contactPoint is on surface of sphereA
        glm::vec3 BtoA = sphereA.getPosition() - sphereB.getPosition();
        glm::vec3 expectedContactPoint = sphereB.getPosition() + radiusB * glm::normalize(BtoA);
        inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad contactPoint: expected = " << expectedContactPoint
                << " actual = " << collision->_contactPoint 
                << std::endl;
        }
    }
}
Пример #20
0
void ShapeColliderTests::capsuleTouchesCapsule() {
    // overlapping capsules
    float radiusA = 2.f;
    float halfHeightA = 3.f;
    float radiusB = 3.f;
    float halfHeightB = 4.f;

    float totalRadius = radiusA + radiusB;
    float totalHalfLength = totalRadius + halfHeightA + halfHeightB;

    CapsuleShape capsuleA(radiusA, halfHeightA);
    CapsuleShape capsuleB(radiusB, halfHeightB);

    CollisionList collisions(16);
    int numCollisions = 0;

    { // side by side
        capsuleB.setPosition((0.99f * totalRadius) * xAxis);
        if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
        if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    }

    { // end to end
        capsuleB.setPosition((0.99f * totalHalfLength) * yAxis);

        if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
        if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    }

    { // rotate B and move it to the side
        glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis);
        capsuleB.setRotation(rotation);
        capsuleB.setPosition((0.99f * (totalRadius + capsuleB.getHalfHeight())) * xAxis);

        if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
        if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    }

    { // again, but this time check collision details
        float overlap = 0.1f;
        glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis);
        capsuleB.setRotation(rotation);
        glm::vec3 positionB = ((totalRadius + capsuleB.getHalfHeight()) - overlap) * xAxis;
        capsuleB.setPosition(positionB);

        // capsuleA vs capsuleB
        if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    
        CollisionInfo* collision = collisions.getCollision(numCollisions - 1);
        glm::vec3 expectedPenetration = overlap * xAxis;
        float inaccuracy = glm::length(collision->_penetration - expectedPenetration);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad penetration: expected = " << expectedPenetration
                << " actual = " << collision->_penetration 
                << std::endl;
        }
    
        glm::vec3 expectedContactPoint = capsuleA.getPosition() + radiusA * xAxis;
        inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad contactPoint: expected = " << expectedContactPoint
                << " actual = " << collision->_contactPoint 
                << std::endl;
        }

        // capsuleB vs capsuleA
        if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    
        collision = collisions.getCollision(numCollisions - 1);
        expectedPenetration = - overlap * xAxis;
        inaccuracy = glm::length(collision->_penetration - expectedPenetration);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad penetration: expected = " << expectedPenetration
                << " actual = " << collision->_penetration 
                << std::endl;
        }
    
        expectedContactPoint = capsuleB.getPosition() - (radiusB + halfHeightB) * xAxis;
        inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad contactPoint: expected = " << expectedContactPoint
                << " actual = " << collision->_contactPoint 
                << std::endl;
        }
    }

    { // collide cylinder wall against cylinder wall
        float overlap = 0.137f;
        float shift = 0.317f * halfHeightA;
        glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis);
        capsuleB.setRotation(rotation);
        glm::vec3 positionB = (totalRadius - overlap) * zAxis + shift * yAxis;
        capsuleB.setPosition(positionB);

        // capsuleA vs capsuleB
        if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    
        CollisionInfo* collision = collisions.getCollision(numCollisions - 1);
        glm::vec3 expectedPenetration = overlap * zAxis;
        float inaccuracy = glm::length(collision->_penetration - expectedPenetration);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad penetration: expected = " << expectedPenetration
                << " actual = " << collision->_penetration 
                << std::endl;
        }
    
        glm::vec3 expectedContactPoint = capsuleA.getPosition() + radiusA * zAxis + shift * yAxis;
        inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad contactPoint: expected = " << expectedContactPoint
                << " actual = " << collision->_contactPoint 
                << std::endl;
        }
    }
}
Пример #21
0
void ShapeColliderTests::capsuleMissesCapsule() {
    // non-overlapping capsules
    float radiusA = 2.f;
    float halfHeightA = 3.f;
    float radiusB = 3.f;
    float halfHeightB = 4.f;

    float totalRadius = radiusA + radiusB;
    float totalHalfLength = totalRadius + halfHeightA + halfHeightB;

    CapsuleShape capsuleA(radiusA, halfHeightA);
    CapsuleShape capsuleB(radiusA, halfHeightA);

    CollisionList collisions(16);

    // side by side
    capsuleB.setPosition((1.01f * totalRadius) * xAxis);
    if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch"
            << std::endl;
    }
    if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch"
            << std::endl;
    }

    // end to end
    capsuleB.setPosition((1.01f * totalHalfLength) * xAxis);
    if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch"
            << std::endl;
    }
    if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch"
            << std::endl;
    }

    // rotate B and move it to the side
    glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis);
    capsuleB.setRotation(rotation);
    capsuleB.setPosition((1.01f * (totalRadius + capsuleB.getHalfHeight())) * xAxis);
    if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch"
            << std::endl;
    }
    if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch"
            << std::endl;
    }

    if (collisions.size() > 0) {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: expected empty collision list but size is " << collisions.size()
            << std::endl;
    }
}
Пример #22
0
void VerletShapeTests::capsuleMissesCapsule() {
    // non-overlapping capsules
    float radiusA = 2.0f;
    float halfHeightA = 3.0f;
    float radiusB = 3.0f;
    float halfHeightB = 4.0f;

    float totalRadius = radiusA + radiusB;
    float totalHalfLength = totalRadius + halfHeightA + halfHeightB;

    // create points for the shapes
    VerletPoint points[4];
    for (int i = 0; i < 4; ++i) {
        points[i]._position = glm::vec3(0.0f);
    }

    // give the points to the shapes
    VerletCapsuleShape capsuleA(radiusA, points+0, points+1);
    VerletCapsuleShape capsuleB(radiusB, points+2, points+3);
    capsuleA.setHalfHeight(halfHeightA);
    capsuleA.setHalfHeight(halfHeightB);

    CollisionList collisions(16);

    // side by side
    capsuleB.setTranslation((1.01f * totalRadius) * xAxis);
    if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch" << std::endl;
    }
    if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch" << std::endl;
    }

    // end to end
    capsuleB.setTranslation((1.01f * totalHalfLength) * xAxis);
    if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch" << std::endl;
    }
    if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch" << std::endl;
    }

    // rotate B and move it to the side
    glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis);
    capsuleB.setRotation(rotation);
    capsuleB.setTranslation((1.01f * (totalRadius + capsuleB.getHalfHeight())) * xAxis);
    if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch" << std::endl;
    }
    if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions))
    {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: capsule and capsule should NOT touch" << std::endl;
    }

    if (collisions.size() > 0) {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: expected empty collision list but size is " << collisions.size() << std::endl;
    }
}
Пример #23
0
//=============================================================================
// Call repeatedly by the main message loop in WinMain
//=============================================================================
void Game::run(HWND hwnd)
{
    if(graphics == NULL)            // if graphics not initialized
        return;

    // calculate elapsed time of last frame, save in frameTime
    QueryPerformanceCounter(&timeEnd);
    frameTime = (float)(timeEnd.QuadPart - timeStart.QuadPart ) / (float)timerFreq.QuadPart;

    // Power saving code, requires winmm.lib
    // if not enough time has elapsed for desired frame rate
    if (frameTime < MIN_FRAME_TIME) 
    {
        sleepTime = (DWORD)((MIN_FRAME_TIME - frameTime)*1000);
        timeBeginPeriod(1);         // Request 1mS resolution for windows timer
        Sleep(sleepTime);           // release cpu for sleepTime
        timeEndPeriod(1);           // End 1mS timer resolution
        return;
    }

    if (frameTime > 0.0)
        fps = (fps*0.99f) + (0.01f/frameTime);  // average fps
    if (frameTime > MAX_FRAME_TIME) // if frame rate is very slow
        frameTime = MAX_FRAME_TIME; // limit maximum frameTime
    timeStart = timeEnd;

	//PAUSE and UNPAUSE the game
	if(input->isStartPressed() && (currentState == OVERWORLD || currentState == BATTLE))
	{
		if(paused)
		{
			paused = false;

			if(currentState == BATTLE)
			{
				audio->stopCue(NOSTRINGS);
				audio->playCue(BATTLELOOP);
			}
			else if (currentState == OVERWORLD)
			{
				audio->stopCue(BATTLELOOP);
				audio->playCue(NOSTRINGS);
			}
		}
		else
		{
			audio->stopCue(NOSTRINGS);
			audio->stopCue(BATTLELOOP);
			paused = true;
		}
	}
    // update(), ai(), and collisions() are pure virtual functions.
    // These functions must be provided in the class that inherits from Game.
    if (!paused)                    // if not paused
    {
        update();                   // update all game items
        ai();                       // artificial intelligence
        collisions();               // handle collisions
        input->vibrateControllers(frameTime); // handle controller vibration
    }
    renderGame();                   // draw all game items
    input->readControllers();       // read state of controllers

	audio->run();                       // perform periodic sound engine tasks

    // if Alt+Enter toggle fullscreen/window
    if (input->isKeyDown(ALT_KEY) && input->wasKeyPressed(ENTER_KEY))
        setDisplayMode(graphicsNS::TOGGLE); // toggle fullscreen/window

    // if Esc key, set window mode
    //if (input->isKeyDown(ESC_KEY))
    //    setDisplayMode(graphicsNS::WINDOW); // set window mode

    // Clear input
    // Call this after all key checks are done
    input->clear(inputNS::KEYS_PRESSED);
}
Пример #24
0
void BouncyLogo::advance(int stage)
{
    switch ( stage ) {
      case 0: {
	double vx = xVelocity();
	double vy = yVelocity();

	if ( vx == 0.0 && vy == 0.0 ) {
	    // stopped last turn
	    initSpeed();
	    vx = xVelocity();
	    vy = yVelocity();
	}

	double nx = x() + vx;
	double ny = y() + vy;

	if ( nx < 0 || nx >= canvas()->width() )
	    vx = -vx; // 換反方向
	if ( ny < 0 || ny >= canvas()->height() )
	    vy = -vy; // 換反方向

	for (int bounce=0; bounce<4; bounce++) {
	    QCanvasItemList l=collisions(FALSE); // 沒有那麼精準的傳回所有有碰撞的canvas item
	    for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
		QCanvasItem *hit = *it;
		if ( hit->rtti()==logo_rtti && hit->collidesWith(this) ) {
		    switch ( bounce ) {
		      case 0:
			vx = -vx;
			break;
		      case 1:
			vy = -vy;
			vx = -vx;
			break;
		      case 2:
			vx = -vx;
			break;
		      case 3:
			// Stop for this turn
			vx = 0;
			vy = 0;
			break;
		    }
		    setVelocity(vx,vy);
		    break;
		}
	    }
	}

	if ( x()+vx < 0 || x()+vx >= canvas()->width() )
	    vx = 0;
	if ( y()+vy < 0 || y()+vy >= canvas()->height() )
	    vy = 0;

	setVelocity(vx,vy);
      } break;
      case 1:
	QCanvasItem::advance(stage);
	break;
    }
}
Пример #25
0
/**Moves the objects in the game*/
void Game::animate()
{
	//updates score
	gScene->removeItem(sAmount);
	score += 10;
	QString temp = QString::number(score);
	sAmount = new QGraphicsSimpleTextItem(temp);
	sAmount->setPos(400, 450);
	sAmount->setFont(fontT);
	sAmount->setZValue(2);
	gScene->addItem(sAmount);
		
	//moves all other things
	for(int l=0; l<things.size(); l++) {
		things[l]->move(yoshi->getLocx(), yoshi->getLocy());
		if(things[l]->type == Thing::kamekEnemy && things[l]->frame == 4) {
			int mod = levelCnt % 3;
			if(mod == 0 || levelCnt >= 3)
				mod = 3;
			if(things[l]->right) {
				int y = things[l]->getLocy();
				int x = things[l]->getLocx() +3;
				switch(rand()%mod){
					case 0:
						newMagic(x, y, 0);
						break;
					case 1:
						newBolt(x, y, 0);
						break;
					case 2:
						newWind(x, y, 0);
						break;
				}
			}
			else {
				int y = things[l]->getLocy();
				int x = things[l]->getLocx() -28;
				switch(rand()%mod){
					case 0:
						newMagic(x, y, 1);
						break;
					case 1:
						newBolt(x, y, 1);
						break;
					case 2:
						newWind(x, y, 1);
						break;
				}
			}
		}
		if(things[l]->del) {
			gScene->removeItem(things[l]);
			things.pop(l);
		}
	}
	if(timeCnt >= 1000) {
		
		interval -= (interval/16);
		timer->setInterval(interval);
		timeCnt = 0;
		goombaMax-=goombaMax*1/4;
		koopaMax-=koopaMax*1/4;
		kamekMax-=kamekMax*1/4;
		billMax-=billMax*1/4;
			
		background->move(0, 0);
		levelCnt++;
		gScene->removeItem(levelAmount);
		temp = QString::number(levelCnt);
		levelAmount = new QGraphicsSimpleTextItem(temp);
		levelAmount->setPos(400, 480);
		levelAmount->setFont(fontT);
		levelAmount->setZValue(2);
		gScene->addItem(levelAmount);
	}	
	else
		timeCnt++;
	
	//collisions
	collisions();
}
Пример #26
0
void SdDurationCanvas::menu(const QPoint & p) {
  Q3PopupMenu m(0);
  Q3CanvasItemList items = collisions(TRUE);
  Q3PtrList<SdDurationCanvas> l;
  Q3CanvasItemList::ConstIterator it;
  Q3CanvasItemList::ConstIterator end = items.end();
  
  for (it = items.begin(); it != end; ++it) {
    if ((*it)->visible()) {
      DiagramItem * di = QCanvasItemToDiagramItem(*it);
      
      if ((di != 0) &&
	  (di->type() == UmlActivityDuration) &&
	  (((SdDurationCanvas *) di)->support == support))
	l.append((SdDurationCanvas *) di);
    }
  }
  
  m.insertItem(new MenuTitle(TR("Activity bar"), m.font()), -1);
  m.insertSeparator();
  m.insertItem(TR("Upper"), 0);
  m.insertItem(TR("Lower"), 1);
  m.insertItem(TR("Go up"), 9);
  m.insertItem(TR("Go down"), 10);
  m.insertSeparator();
  m.insertItem((coregion) ? TR("Draw as activity bar") :  TR("Draw as a coregion"), 7);
  m.insertItem(TR("Edit drawing settings"), 2);
  m.insertSeparator();
  m.insertItem(TR("Select linked items"), 3);
  m.insertSeparator();
  m.insertItem(TR("Remove from diagram"), 4);
  m.insertSeparator();
  m.insertItem(TR("Cut here"), 5);
  if (!l.isEmpty())
    m.insertItem(TR("Merge juxtaposed activity bars"), 6);
  if (support->isaDuration())
    m.insertItem(TR("Collapse in parent bar"), 8);

  switch (m.exec(QCursor::pos())) {
  case 0:
    upper();
    modified();
    return;
  case 1:
    lower();
    modified();
    return;
  case 9:
    z_up();
    modified();	// call package_modified()
    return;
  case 10:
    z_down();
    modified();	// call package_modified()
    return;
  case 2:
    edit_drawing_settings();
    return;
  case 3:
    select_associated();
    break;
  case 4:
    delete_it();
    package_modified();
    break;
  case 5:
    cut(p);
    package_modified();
    break;
  case 6:
    merge(l);
    package_modified();
    break;
  case 7:
    coregion = !coregion;
    modified();
    return;
  case 8:
    {
      SdDurationCanvas * d = (SdDurationCanvas *) support;
      
      d->collapse(this);
      d->update_hpos(); // update sub duration and msg hpos
      d->update_self();
    }    
    package_modified();
    break;
  default:
    return;
  }
  
  canvas()->update();
}
Пример #27
0
void ShapeColliderTests::sphereTouchesCapsule() {
    // overlapping sphere and capsule
    float radiusA = 2.f;
    float radiusB = 1.f;
    float totalRadius = radiusA + radiusB;
    float halfHeightB = 2.f;
    float alpha = 0.5f;
    float beta = 0.5f;
    float radialOffset = alpha * radiusA + beta * radiusB;
    
    SphereShape sphereA(radiusA);
    CapsuleShape capsuleB(radiusB, halfHeightB);

    CollisionList collisions(16);
    int numCollisions = 0;

    {   // sphereA collides with capsuleB's cylindrical wall
        sphereA.setPosition(radialOffset * xAxis);

        if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphere and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    
        // penetration points from sphereA into capsuleB
        CollisionInfo* collision = collisions.getCollision(numCollisions - 1);
        glm::vec3 expectedPenetration = (radialOffset - totalRadius) * xAxis;
        float inaccuracy = glm::length(collision->_penetration - expectedPenetration);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad penetration: expected = " << expectedPenetration
                << " actual = " << collision->_penetration 
                << std::endl;
        }
    
        // contactPoint is on surface of sphereA
        glm::vec3 expectedContactPoint = sphereA.getPosition() - radiusA * xAxis;
        inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad contactPoint: expected = " << expectedContactPoint
                << " actual = " << collision->_contactPoint 
                << std::endl;
        }

        // capsuleB collides with sphereA
        if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and sphere should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    
        // penetration points from sphereA into capsuleB
        collision = collisions.getCollision(numCollisions - 1);
        expectedPenetration = - (radialOffset - totalRadius) * xAxis;
        inaccuracy = glm::length(collision->_penetration - expectedPenetration);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad penetration: expected = " << expectedPenetration
                << " actual = " << collision->_penetration 
                << std::endl;
        }
    
        // contactPoint is on surface of capsuleB
        glm::vec3 BtoA = sphereA.getPosition() - capsuleB.getPosition();
        glm::vec3 closestApproach = capsuleB.getPosition() + glm::dot(BtoA, yAxis) * yAxis;
        expectedContactPoint = closestApproach + radiusB * glm::normalize(BtoA - closestApproach);
        inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad contactPoint: expected = " << expectedContactPoint
                << " actual = " << collision->_contactPoint 
                << std::endl;
        }
    }
    {   // sphereA hits end cap at axis
        glm::vec3 axialOffset = (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis;
        sphereA.setPosition(axialOffset * yAxis);
        
        if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphere and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    
        // penetration points from sphereA into capsuleB
        CollisionInfo* collision = collisions.getCollision(numCollisions - 1);
        glm::vec3 expectedPenetration = - ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
        float inaccuracy = glm::length(collision->_penetration - expectedPenetration);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad penetration: expected = " << expectedPenetration
                << " actual = " << collision->_penetration 
                << std::endl;
        }
    
        // contactPoint is on surface of sphereA
        glm::vec3 expectedContactPoint = sphereA.getPosition() - radiusA * yAxis;
        inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad contactPoint: expected = " << expectedContactPoint
                << " actual = " << collision->_contactPoint 
                << std::endl;
        }

        // capsuleB collides with sphereA
        if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and sphere should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    
        // penetration points from sphereA into capsuleB
        collision = collisions.getCollision(numCollisions - 1);
        expectedPenetration = ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
        inaccuracy = glm::length(collision->_penetration - expectedPenetration);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad penetration: expected = " << expectedPenetration
                << " actual = " << collision->_penetration 
                << std::endl;
        }
    
        // contactPoint is on surface of capsuleB
        glm::vec3 endPoint;
        capsuleB.getEndPoint(endPoint);
        expectedContactPoint = endPoint + radiusB * yAxis;
        inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad contactPoint: expected = " << expectedContactPoint
                << " actual = " << collision->_contactPoint 
                << std::endl;
        }
    }
    {   // sphereA hits start cap at axis
        glm::vec3 axialOffset = - (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis;
        sphereA.setPosition(axialOffset * yAxis);
        
        if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: sphere and capsule should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    
        // penetration points from sphereA into capsuleB
        CollisionInfo* collision = collisions.getCollision(numCollisions - 1);
        glm::vec3 expectedPenetration = ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
        float inaccuracy = glm::length(collision->_penetration - expectedPenetration);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad penetration: expected = " << expectedPenetration
                << " actual = " << collision->_penetration 
                << std::endl;
        }
    
        // contactPoint is on surface of sphereA
        glm::vec3 expectedContactPoint = sphereA.getPosition() + radiusA * yAxis;
        inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad contactPoint: expected = " << expectedContactPoint
                << " actual = " << collision->_contactPoint 
                << std::endl;
        }

        // capsuleB collides with sphereA
        if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions))
        {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: capsule and sphere should touch"
                << std::endl;
        } else {
            ++numCollisions;
        }
    
        // penetration points from sphereA into capsuleB
        collision = collisions.getCollision(numCollisions - 1);
        expectedPenetration = - ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
        inaccuracy = glm::length(collision->_penetration - expectedPenetration);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad penetration: expected = " << expectedPenetration
                << " actual = " << collision->_penetration 
                << std::endl;
        }
    
        // contactPoint is on surface of capsuleB
        glm::vec3 startPoint;
        capsuleB.getStartPoint(startPoint);
        expectedContactPoint = startPoint - radiusB * yAxis;
        inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint);
        if (fabs(inaccuracy) > EPSILON) {
            std::cout << __FILE__ << ":" << __LINE__
                << " ERROR: bad contactPoint: expected = " << expectedContactPoint
                << " actual = " << collision->_contactPoint 
                << std::endl;
        }
    }
    if (collisions.size() != numCollisions) {
        std::cout << __FILE__ << ":" << __LINE__
            << " ERROR: expected " << numCollisions << " collisions but actual number is " << collisions.size()
            << std::endl;
    }
}