Pioneer3ATSim::Pioneer3ATSim():WheeledBaseSim(0.34,0.25,0.1,0.1) { speed=0; rotSpeed=0; //constructor: create the physical representation of the robot PrismaticPart *top=new PrismaticPart; PrismaticPart *bod=new PrismaticPart; double topVertex[20][2]= {{0.176776695,-0.176776695},{0.216506351,-0.125},{0.241481457,-0.064704761}, {0.25,0},{0.241481457,0.064704761},{0.216506351,0.125}, {0.176776695,0.176776695},{0.132,0.193},{-0.037,0.193}, {-0.037,0.168},{-0.184,0.168},{-0.216506351,0.125}, {-0.241481457,0.064704761},{-0.25,0},{-0.241481457,-0.064704761}, {-0.216506351,-0.125},{-0.184,-0.168},{-0.037,-0.168}, {-0.037,-0.193},{0.132,-0.193}}; double body[8][2]= {{0.2,0.0825},{0.16,0.1325},{-0.16,0.1325},{-0.2,0.0825}, {-0.2,-0.0825},{-0.16,-0.1325},{0.16,-0.1325},{0.2,-0.0825}}; vector<Vector2D> list; int i; for(i=0;i<20;i++)list.push_back(Vector2D(topVertex[i][0],topVertex[i][1])); top->setPolygonalBase(list); top->setHeight(0.005); //5mm top->setRelativePosition(Vector3D(0,0,0.275)); top->setColor(0.1,0.1,0.1); (*this)+=top; list.clear(); for(int i=0;i<8;i++)list.push_back(Vector2D(body[i][0],body[i][1])); bod->setPolygonalBase(list); bod->setHeight(0.19); //190mm bod->setRelativePosition(Vector3D(0,0,0.085)); bod->setColor(1.0,0.1,0.1); (*this)+=bod; //las ruedas no se pueden añadir hasta no tener un mecanismo de exclusión de detección CylindricalPart *wheel1=new CylindricalPart(wheel_width,wheel_radius); wheel1->setColor(0.1,0.1,0.1); wheel1->setRelativeOrientation(X_AXIS,-PI/2); CylindricalPart *wheel2=new CylindricalPart(wheel_width,wheel_radius); wheel2->setColor(0.1,0.1,0.1); wheel2->setRelativeOrientation(X_AXIS,-PI/2); CylindricalPart *wheel3=new CylindricalPart(wheel_width,wheel_radius); wheel3->setColor(0.1,0.1,0.1); wheel3->setRelativeOrientation(X_AXIS,PI/2); CylindricalPart *wheel4=new CylindricalPart(wheel_width,wheel_radius); wheel4->setColor(0.1,0.1,0.1); wheel4->setRelativeOrientation(X_AXIS,PI/2); wheel1->setRelativePosition(Vector3D(large/2,width/2,wheel_radius+0.001)); wheel2->setRelativePosition(Vector3D(-large/2,width/2,wheel_radius+0.001)); wheel3->setRelativePosition(Vector3D(large/2,-width/2,wheel_radius+0.001)); wheel4->setRelativePosition(Vector3D(-large/2,-width/2,wheel_radius+0.001)); (*this)+=wheel1; (*this)+=wheel2; (*this)+=wheel3; (*this)+=wheel4; }
void CASW_Target_Dummy::PaintHealthBar( CASWHud3DMarineNames *pSurface ) { char pText[64]; Q_snprintf( pText, sizeof( pText ), "DPS: %.2f", GetDPS() ); pSurface->PaintGenericText( GetRenderOrigin() + Vector( -20, -10, 0), pText, Color( 140, 240, 240, 255 ), 1.0f, Vector2D( 0, -6 ) ); Q_snprintf( pText, sizeof( pText ), "DAMAGE:%.2f", GetDamageTaken() ); pSurface->PaintGenericText( GetRenderOrigin() + Vector( -20, -10, 0), pText, Color( 140, 240, 240, 255 ), 1.0f, Vector2D( 0, -5 ) ); Q_snprintf( pText, sizeof( pText ), "LAST:%.2f", m_flLastHit.Get() ); pSurface->PaintGenericText( GetRenderOrigin() + Vector( -20, -10, 0), pText, Color( 140, 240, 240, 255 ), 1.0f, Vector2D( 0, -4 ) ); }
Vector2D GamePlayObject::CollisionCenter() { return Vector2D(x + CollisionOffset.x + (collisionWidth/2), y + CollisionOffset.y + (collisionHeight/2)); }
void Sprite::SetScaleX(double x) { SetScale(Vector2D(x, _scaleDimensions.GetY())); }
void Sprite::CalcCenterFrame() { double x = (_frameDimensions.GetX() * _scaleDimensions.GetX()) / 2; double y = (_frameDimensions.GetY() * _scaleDimensions.GetY()) / 2; _center = Vector2D(x, y); }
void Object::stop(){ velocity = Vector2D(0, 0); }
void Sprite::SetCenterX(int x) { SetCenter(Vector2D(x, _center.GetY())); }
Vector2D operator/ (const Vector2D &a, float divisor) { assert(divisor != 0.0f); return Vector2D(a.x / divisor, a.y / divisor); }
Vector2D operator+ (const Vector2D &a, const Vector2D &b) { return Vector2D(a.x + b.x, a.y + b.y); }
Vector2D operator- (const Vector2D &a, const Vector2D &b) { return Vector2D(a.x - b.x, a.y - b.y); }
Vector2D operator* (float scalar, const Vector2D &a) { return Vector2D(a.x * scalar, a.y * scalar); }
void VideoDisplay::OnMouseLeave(wxMouseEvent& event) { mouse_pos = Vector2D(); if (tool) tool->OnMouseEvent(event); }
void GameplayFrame::DrawGame(Core::Graphics& graphics) { PROFILE("Total Draw"); myBackground.DrawBackdrop(graphics); graphics.SetColor( RGB( 255, 255, 255)); char buf[50]; sprintf_s(buf, "%f", debugTime); graphics.DrawString(35,980, buf); graphics.DrawString(5, 980, "FPS:"); healthBar.DrawHealth(graphics); fuelGauge.DrawPlayerFuel(graphics); graphics.SetColor(RGB(51,51,51)); boundaries.Draw(graphics); { PROFILE("Draw Planets"); creator.DrawPlanets(graphics); } graphics.SetColor( RGB( 255, 255, 255)); { PROFILE("Draw Enemies"); tester.drawShips(graphics); } { PROFILE("Draw Particles"); myParticles.drawEffect(graphics); } myText.drawText(graphics); graphics.SetColor(RGB(255,255,255)); Vector2D fuel(fuelStation[1].x+(fuelStation[3].x-fuelStation[1].x)/4.5f, (fuelStation[0].y + ((fuelStation[2].y-fuelStation[0].y)/2.5f))); gameText.drawText(graphics, 4.0f, RGB(255,255,255), "Fueling", 20.0f, 5.0f, 0, fuel); gameText.drawText(graphics, 4.0f, RGB(255,255,255), "Station", 20.0f, 5.0f, 0, Vector2D(fuel.x, fuel.y + 35.0f)); myCash.DrawCash(graphics, 5.0f); healthDrop.DrawHealthPickup(graphics); graphics.SetColor( RGB(255, 255, 255)); graphics.DrawString(30, 100, "PRESS I TO OPEN/CLOSE THE LIST OF INSTRUCTIONS."); DebugFrameMemory(graphics); graphics.SetColor( RGB( 255, 100, 0)); { ranger.Draw(graphics); } //rapidFirePowerup.drawPowerup(graphics); if(alreadyKilled) { char buf[50]; sprintf_s(buf, "You survived for %i seconds!", (int)totalTime); Core::RGB textColor = RGB(255, 149, 0); gameText.drawText(graphics, 5.0f, textColor, "GAME OVER...", 30.0f, 5.0f, 0, Vector2D(250.0f, 250.0f)); gameText.drawText(graphics, 5.0f, textColor, buf, 30.0f, 5.0f, 0, Vector2D(680.0f, 250.0f)); graphics.SetColor(RGB(255,100,0)); graphics.DrawString(700, 500, "Game over, you have died and as thus you are"); graphics.DrawString(700, 520, "reading this screen. If you wish to try again"); graphics.DrawString(700, 540, "simply press the space bar to reset the game. Yep..."); graphics.DrawString(700, 560, "why are you still reading this?? I mean I"); graphics.DrawString(700, 580, "don't care but you are kind of wasting your"); graphics.DrawString(700, 600, "time at this point."); if(Core::Input::IsPressed(32)) { resetFrame(); } } if(paused && upgradesPause) { armorUpgrade.DrawUpgrade(graphics, mouse, 3.0f); healthDropUpgrade.DrawUpgrade(graphics, mouse, 3.0f); bulletsUpgrade.DrawUpgrade(graphics, mouse, 3.0f); fuelUpgrade.DrawUpgrade(graphics, mouse, 3.0f); } else if(paused && instructionsPause) { graphics.DrawString(825, 430, " (Press I to close/open the list of instructions.)"); graphics.DrawString(800, 450, " Instructions: use up, down, W or S to move the ship. A and D"); graphics.DrawString(800, 470, "or left and right to rotate the spaceship. Use the left mouse"); graphics.DrawString(800, 490, "to fire your turret. Keep in mind that only ten bullets can"); graphics.DrawString(800, 510, "be on the screen at once and that the sun in the background"); graphics.DrawString(800, 530, "is simply for ambiance."); graphics.DrawString(800, 570, " Upgrades: Use the cash that you have earned to purchase"); graphics.DrawString(800, 590, "the upgrades. To open the upgrade menu press R. You can"); graphics.DrawString(800, 610, "view individual upgrade information by hovering over the tiles."); graphics.DrawString(800, 650, "Fueling: Your fuel will slowly decrease as time goes on...because"); graphics.DrawString(800, 670, "you know.....space....well anyway drive over the aptly named"); graphics.DrawString(800, 690, "'FUELING STATION' to fuel your ship. You can't, however, fire"); graphics.DrawString(800, 710, "your turret while you are fueling, because that is illegal."); graphics.DrawString(800, 750, "Damage: When an enemy ship collides with you, you'll take"); graphics.DrawString(800, 770, "some damage. If you have the armor upgrades then your armor"); graphics.DrawString(800, 790, "will absorb the first chuncks of damage. Each ship has a"); graphics.DrawString(800, 810, "one second spawn delay from being able to damage your ship"); graphics.DrawString(800, 830, "as well as a three second delay from the game starting to"); graphics.DrawString(800, 850, "being able to damage your spacecraft."); } }
bool GameplayFrame::UpdateGame(float dt) { PROFILE("Total Update"); mouse = Vector2D((float)(Core::Input::GetMouseX()), (float)(Core::Input::GetMouseY())); myBackground.UpdateAsteroid(dt); if((Input::IsPressed(82) && pauseToggleCooldown <= 0) && !instructionsPause && !alreadyKilled) { paused = !paused; upgradesPause = !upgradesPause; pauseToggleCooldown = .5f; } else if((Input::IsPressed(73) && pauseToggleCooldown <= 0) && !upgradesPause && !alreadyKilled) { paused = !paused; instructionsPause = !instructionsPause; pauseToggleCooldown = .5f; } else if(pauseToggleCooldown > 0) { pauseToggleCooldown -= dt; } float time = (!paused) ? dt: 0; Timer profileTimer; profiler.newFrame(); debugTime = 1/(counter.getLastTimeInterval()); profileTimer.startTimer(); { PROFILE("Ship Update"); ranger.update(time, SCREEN_HEIGHT, SCREEN_WIDTH, fuelStation); } { PROFILE("Planets Update"); creator.UpdatePlanets(time); } { PROFILE("Enemies Update"); tester.updateShips(time, ranger.position, ranger.bullets,10); } { PROFILE("Particles Update"); myParticles.updateEffect(time); } if(!paused && !alreadyKilled) { totalTime += time; } if(!alreadyKilled) { fuelGauge.UpdateFuel(time, ranger.isFueling()); healthBar.UpdateArmor(time); } healthDrop.UpdateHealthPickup(time, ranger.position); if(Input::IsPressed(Input::KEY_ESCAPE)) { return true; } if(healthBar.getHealth() <= 0 || fuelGauge.getFuel()<= 0) { if(!alreadyKilled) { ranger.setLife(false); myParticles.addEffect(1, ranger.position, 500, 350.0f, 3.0f, RGB(242, 165, 22)); shipDeathTimer -= dt; alreadyKilled = (shipDeathTimer <= 0); } } myText.updateText(time); return false; }
void Object::moveLeft(){ velocity = Vector2D(-1, 0) * speed; }
Vector2D operator+ (const Vector2D* &a, const Vector2D &b) { return Vector2D(a->x + b.x, a->y + b.y); }
void Object::moveRight(){ velocity = Vector2D(1, 0) * speed; }
Level1_03::Level1_03( float r, int entrance, Alonebot* p, FMOD::System* fsys, FMOD::Channel* sfx ) : Level( r, entrance, p, fsys, sfx ) { // Inititalize area and size of level area = 1; levelWidth = 1152; levelHeight = 480; // Initialize backgrounds and music bgMusic = "..\\music\\beat171-restless.mp3"; background = LoadTexture( std::string( "..\\anims\\bg\\bg_03.png" ) ); backbackground = LoadTexture( std::string( "..\\anims\\bg\\bbg_03.png" ) ); // Create player, placement depending upon the entrance taken switch( entrance ) { case 0: player = new Alonebot( 20.f, 260.f, p->dir, p ); break; case 1: player = new Alonebot( levelWidth - 38.f, 316.f, p->dir, p ); break; case 2: player = new Alonebot( 20.f, 116.f, p->dir, p ); break; case 3: player = new Alonebot( levelWidth - 38.f, 116.f, p->dir, p ); break; } // Add static level objects to array objects.push_back( new Box( 0.f, 376.f, levelWidth ) ); // Ground floor objects.push_back( new Box( 0.f, 0.f, levelWidth ) ); // Ceiling for( int i=6; i<10; i++ ) { // Left staircase objects.push_back( new Box( 0.f, 224.f + 16.f * i, 24 * i ) ); } for( int i=0; i<4; i++ ) { // Middle staircase objects.push_back( new Box( 535.f - 24 * i, 320.f + 16.f * i, 48 * ( i + 2 ) ) ); } objects.push_back( new Box( 0.f, 176.f, levelWidth / 3 + 19 ) ); objects.push_back( new Box( levelWidth * 2.f / 3.f, 176.f, levelWidth / 3 ) ); objects.push_back( new Dropbox( 418.f, 204.f, 64 ) ); objects.push_back( new Dropbox( 520.f, 256.f, 128 ) ); objects.push_back( new Dropbox( 689.f, 204.f, 64 ) ); objects.push_back( new Box( 0.f, 0.f, 16, 112 ) ); // Walls objects.push_back( new Box( 0.f, 176.f, 16, 80 ) ); objects.push_back( new Box( levelWidth - 16.f, 0.f, 16, 112 ) ); objects.push_back( new Box( levelWidth - 16.f, 176.f, 16, 136 ) ); // Add doors doors.push_back( new Door( 0.f, 112.f, 1, false, 5, 0 ) ); // Top left door doorToppers.push_back( new Door( 0.f, 112.f, 1, true ) ); objects.push_back( new Box( 0.f, 112.f, 16, 4 ) ); doors.push_back( new Door( 0.f, 256.f, 1, false, 4, 1 ) ); // Bottom left door doorToppers.push_back( new Door( 0.f, 256.f, 1, true ) ); objects.push_back( new Box( 0.f, 256.f, 16, 4 ) ); doors.push_back( new Door( levelWidth - 32.f, 112.f, -1, false, 7, 2 ) );// Top right door doorToppers.push_back( new Door( levelWidth - 32.f, 112.f, -1, true ) ); objects.push_back( new Box( levelWidth - 16.f, 112.f, 16, 4 ) ); doors.push_back( new Door( levelWidth - 32.f, 312.f, -1, false, 7, 0 ) );// Bottom right door doorToppers.push_back( new Door( levelWidth - 32.f, 312.f, -1, true ) ); objects.push_back( new Box( levelWidth - 16.f, 312.f, 16, 4 ) ); // Add enemies enemies.push_back( new Skeleton( Vector2D( 333.f, 334.f ), -1 ) ); enemies.push_back( new Skeleton( Vector2D( 803.f, 334.f ), -1 ) ); // Start the heart player->init( fmodSystem ); heartBeat.start( ); // Try to center the camera cameraX = player->pos.x - 320; cameraY = player->pos.y - 240; }
void Sprite::SetPosition(double x, double y) { SetPosition(Vector2D(x, y)); }
Vector2D Vector2D::operator +(const Vector2D& other) const { return Vector2D(this->x + other.x, this->y + other.y); }
void Sprite::SetCenterY(int y) { SetCenter(Vector2D(_center.GetX(), y)); }
Vector2D Vector2D::operator -(const Vector2D& other) const { return Vector2D(this->x - other.x, this->y - other.y); }
void Sprite::SetScaleY(double y) { SetScale(Vector2D(_scaleDimensions.GetX(), y)); }
void CauldronEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity) { Vector2D recoilVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), 200.0f ); targetEntity->giveRepulsion(false, recoilVector, 0.1f); }
//----------------------------------------------------------------------------- // Compute the bounding box's center, size, and basis //----------------------------------------------------------------------------- void C_EntityDissolve::ComputeRenderInfo( mstudiobbox_t *pHitBox, const matrix3x4_t &hitboxToWorld, Vector *pVecAbsOrigin, Vector *pXVec, Vector *pYVec ) { // Compute the center of the hitbox in worldspace Vector vecHitboxCenter; VectorAdd( pHitBox->bbmin, pHitBox->bbmax, vecHitboxCenter ); vecHitboxCenter *= 0.5f; VectorTransform( vecHitboxCenter, hitboxToWorld, *pVecAbsOrigin ); // Get the object's basis Vector vec[3]; MatrixGetColumn( hitboxToWorld, 0, vec[0] ); MatrixGetColumn( hitboxToWorld, 1, vec[1] ); MatrixGetColumn( hitboxToWorld, 2, vec[2] ); // vec[1] *= -1.0f; Vector vecViewDir; VectorSubtract( CurrentViewOrigin(), *pVecAbsOrigin, vecViewDir ); VectorNormalize( vecViewDir ); // Project the shadow casting direction into the space of the hitbox Vector localViewDir; localViewDir[0] = DotProduct( vec[0], vecViewDir ); localViewDir[1] = DotProduct( vec[1], vecViewDir ); localViewDir[2] = DotProduct( vec[2], vecViewDir ); // Figure out which vector has the largest component perpendicular // to the view direction... // Sort by how perpendicular it is int vecIdx[3]; SortAbsVectorComponents( localViewDir, vecIdx ); // Here's our hitbox basis vectors; namely the ones that are // most perpendicular to the view direction *pXVec = vec[vecIdx[0]]; *pYVec = vec[vecIdx[1]]; // Project them into a plane perpendicular to the view direction *pXVec -= vecViewDir * DotProduct( vecViewDir, *pXVec ); *pYVec -= vecViewDir * DotProduct( vecViewDir, *pYVec ); VectorNormalize( *pXVec ); VectorNormalize( *pYVec ); // Compute the hitbox size Vector boxSize; VectorSubtract( pHitBox->bbmax, pHitBox->bbmin, boxSize ); // We project the two longest sides into the vectors perpendicular // to the projection direction, then add in the projection of the perp direction Vector2D size( boxSize[vecIdx[0]], boxSize[vecIdx[1]] ); size.x *= fabs( DotProduct( vec[vecIdx[0]], *pXVec ) ); size.y *= fabs( DotProduct( vec[vecIdx[1]], *pYVec ) ); // Add the third component into x and y size.x += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pXVec ) ); size.y += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pYVec ) ); // Bloat a bit, since the shadow wants to extend outside the model a bit size *= 2.0f; // Clamp the minimum size Vector2DMax( size, Vector2D(10.0f, 10.0f), size ); // Factor the size into the xvec + yvec (*pXVec) *= size.x * 0.5f; (*pYVec) *= size.y * 0.5f; }
void Object::moveUp(){ velocity = Vector2D(0, -1) * speed; }
Vector2D Angle::getN() const { return Vector2D( cosf( ang ), sinf( ang ) ); }
void Object::moveDown(){ velocity = Vector2D(0, 1) * speed; }
void KingRatEntity::animate(float delay) { if (age <= 0.0f) { age += delay; return; } if (isAgonising) { if (h < -0.01f) { isDying = true; game().addCorpse(x, y, deathFrame); if (dyingSound != SOUND_NONE) SoundManager::getInstance().playSound(dyingSound); } else { frame = dyingFrame; hVelocity -= 700.0f * delay; h += hVelocity * delay; } return; } EnemyEntity::animate(delay); if (specialState[SpecialStateIce].active) delay *= specialState[SpecialStateIce].param1; float timerMult = 1.0f; if (hp <= hpMax / 4) { creatureSpeed = KING_RAT_SPEED * 1.4f; timerMult = 0.7f; } else if (hp <= hpMax / 2) { creatureSpeed = KING_RAT_SPEED * 1.2f; timerMult = 0.85f; } else { creatureSpeed = KING_RAT_SPEED; } if (state == 5) { velocity.x *= 0.965f; velocity.y *= 0.965f; } else { if (velocity.x > 0.1f) sprite.setScale(-1.0f, 1.0f); else if (velocity.x < -0.1f) sprite.setScale(1.0f, 1.0f); } timer -= delay; if (timer <= 0.0f) { if (state == 0) { state = 1; // generate rats generateGreenRats(); SoundManager::getInstance().playSound(SOUND_KING_RAT_1); timer = 2.0f; velocity.x = 0.0f; velocity.y = 0.0f; } else if (state == 1) // generate rats { // to normal or berserk if (hp < hpMax / 4 && !hasBeenBerserk) { hasBeenBerserk = true; timer = 12.0f; state = 6; velocity = Vector2D(KING_RAT_BERSERK_SPEED); SoundManager::getInstance().playSound(SOUND_KING_RAT_2); } else { timer = 7.0f * timerMult; velocity = Vector2D(creatureSpeed); state = 2; } } else if (state == 2) // normal -> rush { state = 3; // angry timer = 1.0f; velocity.x = 0.0f; velocity.y = 0.0f; SoundManager::getInstance().playSound(SOUND_KING_RAT_2); } else if (state == 3) // normal -> rush { state = 4; // rush timer = 12.0f; float tan = (game().getPlayer()->getX() - x) / (game().getPlayer()->getY() - y); float angle = atan(tan); if (game().getPlayer()->getY() > y) setVelocity(Vector2D(sin(angle) * KING_RAT_RUNNING_SPEED, cos(angle) * KING_RAT_RUNNING_SPEED)); else setVelocity(Vector2D(-sin(angle) * KING_RAT_RUNNING_SPEED, -cos(angle) * KING_RAT_RUNNING_SPEED)); } else if (state == 5) // wall impact { // to normal or berserk if (hp < hpMax / 4 && !hasBeenBerserk) { hasBeenBerserk = true; timer = 12.0f; state = 6; velocity = Vector2D(KING_RAT_BERSERK_SPEED); SoundManager::getInstance().playSound(SOUND_KING_RAT_2); } else { timer = 7.0f * timerMult; velocity = Vector2D(creatureSpeed); state = 0; } } else if (state == 6) { timer = 7.0f * timerMult; velocity = Vector2D(creatureSpeed); state = 0; } } if (state == 6) { berserkDelay -= delay; if (berserkDelay <= 0.0f) { berserkDelay = 0.6f + (rand()%10) / 20.0f; SoundManager::getInstance().playSound(SOUND_KING_RAT_2); setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(),KING_RAT_BERSERK_SPEED )); } } frame = 0; if (state == 1) frame = 3; else if (state == 3 || state == 6) { frame = 3; //0; int r = ((int)(age * 10.0f)) % 2; if (r == 0) sprite.setScale(-1.0f, 1.0f); else sprite.setScale(1.0f, 1.0f); } else if (state == 4) { int r = ((int)(age * 7.5f)) % 4; if (r == 1) frame = 1; else if (r == 3) frame = 2; } else if (state == 5) { frame = 0; } else { int r = ((int)(age * 5.0f)) % 4; if (r == 1) frame = 1; else if (r == 3) frame = 2; } z = y + 48; }
TEST(Vector2DTest, angle) { EXPECT_FLOAT_EQ(0.0f, Vector2D(1, 0).angle()); EXPECT_FLOAT_EQ(M_PI * 2 / 8, Vector2D(1, 1).angle()); EXPECT_FLOAT_EQ(M_PI * 2 / 4, Vector2D(0, 1).angle()); }