bool HitBox::IsCollision(Vector2 offset, HitBox *pHitBox, Vector2 hitBoxOffset, CollisionParameter *pParam) const { bool isCollision = false; Vector2 overlapCompensation = Vector2(0, 0); if (pHitBox != NULL) { for (unsigned int i = 0; i < collidableObjectList.size(); i++) { CollidableObject *pCollidableObject1 = collidableObjectList[i]; for (unsigned int j = 0; j < pHitBox->collidableObjectList.size(); j++) { CollidableObject *pCollidableObject2 = pHitBox->collidableObjectList[j]; CollisionParameter tempParam; if (CollisionExists(pCollidableObject1, offset + overlapCompensation, pCollidableObject2, hitBoxOffset, &tempParam) && fabs(tempParam.OverlapDistance) > 0.0001) { for (unsigned int k = 0; k < tempParam.OverlapEntryList.size(); k++) { pParam->OverlapEntryList.push_back(tempParam.OverlapEntryList[k]); } overlapCompensation += tempParam.OverlapAxis * tempParam.OverlapDistance; isCollision = true; } } } } pParam->OverlapDistance = overlapCompensation.Length(); pParam->OverlapAxis = overlapCompensation.Normalize(); return isCollision; }
Number TransformGizmo::getTransformPlaneAngle() { Ray gizmoRay; gizmoRay.origin = getConcatenatedMatrix().getPosition(); gizmoRay.direction = localTransformPlane * -1; Vector3 gizmoIntersect = gizmoRay.planeIntersectPoint(transformPlane, transformPlaneDistance); gizmoIntersect = planeMatrix.Inverse() * gizmoIntersect; Ray ray = targetScene->projectRayFromCameraAndViewportCoordinate(targetCamera, coreInput->getMousePosition()); Vector3 mouseIntersect = ray.planeIntersectPoint(transformPlane, transformPlaneDistance); mouseIntersect = planeMatrix.Inverse() * mouseIntersect; Vector2 planePosition; if(localTransformPlane.x > 0) { planePosition.x = mouseIntersect.z - gizmoIntersect.z; planePosition.y = mouseIntersect.y - gizmoIntersect.y; } else if(localTransformPlane.y > 0.0) { planePosition.x = mouseIntersect.x - gizmoIntersect.x; planePosition.y = mouseIntersect.z - gizmoIntersect.z; } else if(localTransformPlane.z > 0.0) { planePosition.x = mouseIntersect.x - gizmoIntersect.x; planePosition.y = mouseIntersect.y - gizmoIntersect.y; } planePosition.Normalize(); return atan2(planePosition.x, planePosition.y); }
float Vector2::AngleBetween(Vector2 v) const { if (this->Magnitude() == 0 || v.Magnitude() == 0) { return 0; } Vector2 left = this->Normalize(); Vector2 right = v.Normalize(); if (left == right) { return 0; } float dot = left.Dot(right); // Floating points check if (dot > 1.0f) { dot = 1.0f; } else if (dot < -1.0f) { dot = -1.0f; } float rot = acos(dot); // http://stackoverflow.com/questions/11022446/direction-of-shortest-rotation-between-two-vectors // Use cross vector3 to determine direction Vector3 cross = Vector3(left.x, left.y).Cross(Vector3(right.x, right.y)); if (cross.z > 0) { return -rot; } else { return rot; } }
void Object::Move(int64 diff) { if(!target) return; Vector2 to(target->getX(), target->getY()); Vector2 cur(x, y); Vector2 goingTo (to - cur); Vector2 norm (goingTo.Normalize()); double deltaMovement = (double)(getMoveSpeed()) * 0.000001f*diff; float xx = norm.X * deltaMovement; float yy = norm.Y * deltaMovement; x+= xx; y+=yy; /* If the target was a simple point, stop when it is reached */ if(target->isSimpleTarget() && distanceWith(target) < deltaMovement*3) { if(++curWaypoint >= waypoints.size()) { setTarget(0); } else { setTarget(waypoints[curWaypoint].toTarget()); } } }
void GalaxianReturnToFormationState::Update(Galaxian* a_galaxian, float a_fDeltaTime) { m_deltaTimeMap[a_galaxian] += a_fDeltaTime; if(m_referanceMap[a_galaxian]->m_state == EntityState::REMOVED) { m_referanceMap[a_galaxian] = m_formation->GetFirstGalaxianInFormation(); } Vector2 referancePosition = m_referanceMap[a_galaxian]->GetPosition(); int xOffset = a_galaxian->GetPositionInFormation().x - m_referanceMap[a_galaxian]->GetPositionInFormation().x; int yOffset = a_galaxian->GetPositionInFormation().y - m_referanceMap[a_galaxian]->GetPositionInFormation().y; Vector2 targetPosition = referancePosition + Vector2(xOffset * (GalaxianFormation::SPRITE_WIDTH+12), yOffset * (GalaxianFormation::SPRITE_HEIGHT+6)); if(a_galaxian->GetPosition().Distance(targetPosition) > 5) { Vector2 direction = targetPosition - a_galaxian->GetPosition(); direction.Normalize(); a_galaxian->Move(direction * a_fDeltaTime * 200); } else { a_galaxian->SetPosition(targetPosition); a_galaxian->SetDirection(Vector2(0,1)); a_galaxian->ChangeState(GalaxianInFormationState::getInstance()); } }
//----------------------------------------------------------------------------------------------------------------------------------- void Camera::Update(DX::StepTimer const& timer) { // If the camera is fixed we should not do any more updating if (m_cameraMode == CameraMode::kFixed) { return; } KeyboardInput& keyboard = ScreenManager::GetKeyboardInput(); Vector2 diff = Vector2::Zero; if (keyboard.IsKeyDown(Keyboard::Keys::Left)) { diff.x = -1; } if (keyboard.IsKeyDown(Keyboard::Keys::Right)) { diff.x = 1; } if (keyboard.IsKeyDown(Keyboard::Keys::Up)) { diff.y = -1; } if (keyboard.IsKeyDown(Keyboard::Keys::Down)) { diff.y = 1; } if (diff != Vector2::Zero) { diff.Normalize(); m_position += diff * (float)timer.GetElapsedSeconds() * m_panSpeed; } }
Vector2 Physics_Response(const float dt, const Vector2& src, const Vector2& contact, const Vector2& velocity, const float restitution/* = 2.0f*/) { if (dt == 0.0f) { return Vector2(0.f, 0.f); } Vector2 normal = contact - src; normal.Normalize(); Vector2 vel_dt = velocity * dt; SGE::Graphics_DebugLine(src, src + vel_dt, 0xAAAAFF); SGE::Graphics_DebugLine(contact, src, 0x00FF00); float dot = SGE::Math_Dot(vel_dt, normal); Vector2 A = (normal * restitution) * dot; SGE::Graphics_DebugLine(contact, contact + A, 0xFFFFFF); Vector2 X = vel_dt - A; SGE::Graphics_DebugLine(src, src + X, 0xFF0000); Vector2 response_vel = X * (1.f/dt); return response_vel; }
void Object::Move(int64 diff) { if (!target) { direction = Vector2(); return; } currentUpwardDisplacement = map->getHeightAtLocation(getPosition().X, getPosition().Y); Vector2 to(target->getX(), target->getY()); Vector2 cur(x, y); Vector2 goingTo (to - cur); direction = goingTo.Normalize(); double deltaMovement = (double)(getMoveSpeed()) * 0.000001f*diff; float xx = direction.X * deltaMovement; float yy = direction.Y * deltaMovement; x+= xx; y+= yy; /* If the target was a simple point, stop when it is reached */ if(target->isSimpleTarget() && distanceWith(target) < deltaMovement*3) { if(++curWaypoint >= waypoints.size()) { setTarget(0); } else { setTarget(waypoints[curWaypoint].toTarget()); } } }
void Worm::MoveInDirection(Vector2& direction, float distance) { direction.Normalize(); Vector2 newPoint = Vector2( m_head->GetPosition() + (direction * distance) ); m_head->SetPosition(newPoint); m_tail->moveTo(newPoint, distance); }
Vector2 Evasion::CalcVelocity() { Vector2 offset = mActor->GetPosition() - mTarget->GetPosition(); float distance = offset.Length(); float timeInterval = std::min(0.1f, distance / mActor->GetMaxVelocity()); Vector2 v = mActor->GetPosition() - mTarget->PredictFuturePosition(timeInterval); // Inefficient!!! v.Normalize(); v = mActor->GetMaxVelocity() * v; v -= mActor->GetVelocity(); // Limit v.Normalize(); v = mActor->GetMaxVelocity() * v; return v; }
Vector2 Vector2::RandomXY() { Vector2 v; v.X = (float)(Random::Instance->NextDouble() - 0.5); v.Y = (float)(Random::Instance->NextDouble() - 0.5); v.Normalize(); return v; }
void BatMan::AliveUpdate() { Vector2 vec = (_playerRef.GetCollider().Center() - _collider.Center());//normalizeで変な値になってる vec = vec.Normalize(); _pos.x += vec.x * 2; _collider.pos = _pos + Vector2(_cameraRef.OffsetX(), 0); _walkFrame++; }
Vector2 Flee::CalcVelocity() { Vector2 v = mActor->GetPosition() - mTarget; v.Normalize(); v = mActor->GetMaxVelocity() * v; v -= mActor->GetVelocity(); return v; }
void FieldCharacter::UpdateDirection(Vector2 directionVector) { directionVector = directionVector.Normalize(); // We'll snap the player's direction vector according to // the nearest direction for which we have an animation. double angleToHorizontal = acos(directionVector.GetX()); // acos() only returns values from 0 to pi, // so to get the full circle we need to check // whether we're in the bottom two quandrants, // and change the angle to account for this if so. if (directionVector.GetY() > 0) { angleToHorizontal = 2 * M_PI - angleToHorizontal; } if (angleToHorizontal <= M_PI / 8 || angleToHorizontal > M_PI * 15 / 8) { SetSpriteDirection(FieldCharacterDirectionSide); SetDirection(CharacterDirectionRight); } else if (angleToHorizontal > M_PI / 8 && angleToHorizontal <= M_PI * 3 / 8) { SetSpriteDirection(FieldCharacterDirectionDiagonalUp); SetDirection(CharacterDirectionRight); } else if (angleToHorizontal > 3 * M_PI / 8 && angleToHorizontal <= M_PI * 5 / 8) { SetSpriteDirection(FieldCharacterDirectionUp); } else if (angleToHorizontal > 5 * M_PI / 8 && angleToHorizontal <= M_PI * 7 / 8) { SetSpriteDirection(FieldCharacterDirectionDiagonalUp); SetDirection(CharacterDirectionLeft); } else if (angleToHorizontal > 7 * M_PI / 8 && angleToHorizontal <= M_PI * 9 / 8) { SetSpriteDirection(FieldCharacterDirectionSide); SetDirection(CharacterDirectionLeft); } else if (angleToHorizontal > 9 * M_PI / 8 && angleToHorizontal <= M_PI * 11 / 8) { SetSpriteDirection(FieldCharacterDirectionDiagonalDown); SetDirection(CharacterDirectionLeft); } else if (angleToHorizontal > 11 * M_PI / 8 && angleToHorizontal <= M_PI * 13 / 8) { SetSpriteDirection(FieldCharacterDirectionDown); } else if (angleToHorizontal > 13 * M_PI / 8 && angleToHorizontal <= M_PI * 15 / 8) { SetSpriteDirection(FieldCharacterDirectionDiagonalDown); SetDirection(CharacterDirectionRight); } }
void SegmentShape2D::SetDirection(Vector2 value) { if (value != Vector2::Zero) value.Normalize(); if (GetDirection() != value) { direction = value; revision = -1; } }
void Sun::finalAttack() { Vector2 playerPos(world_->getPlayer().x(), world_->getPlayer().y()); Vector2 dir = playerPos - position_; dir.Normalize(); dir *= 760.0f; velocity_ = dir; phase_time_ = 6000000; }
void VNode::addRandomizedChildren(const Vector2& startPoint, const Vector2& targetPoint) { static const float fudgeFactor = 0.3f; Vector2 orientation = targetPoint - startPoint; orientation.Normalize(); orientation.X = MathUtil::RandomFloatInRange( orientation.X - fudgeFactor, orientation.X + fudgeFactor ); orientation.Y = MathUtil::RandomFloatInRange( orientation.Y - fudgeFactor, orientation.Y + fudgeFactor ); // n times... m_children.push_back( new VNode(startPoint, orientation) ); }
void Ball::Update() { // store our last position Vector2 lastPosition = position; // update our current position using velocity position += velocity * Monocle::deltaTime; // check collisions against the paddles Collider* collider = Collide("Paddle"); if (collider) { Debug::Log("Ball hit an entity tagged with 'Paddle'"); position = lastPosition; Vector2 diff = position - collider->GetEntity()->position; diff.Normalize(); diff *= velocity.GetMagnitude(); velocity = diff; // Calculate panning float pan = ((collider->GetEntity()->position.x / Graphics::GetVirtualWidth()) - 0.5) * 2.0; if (sfxWall) sfxWall->Play(1,1.0,pan); // Play it with panning! (STEREO, baby :D) } // if we hit the top or bottom of the screen if (position.y < 0 || position.y > 600) { position = lastPosition; velocity.y *= -1; // Calculate panning float pan = ((position.x / Graphics::GetVirtualWidth()) - 0.5) * 2.0; if (sfxWall) sfxWall->Play(1,1.0,pan,2.0); // Play it higher, you won't even notice it's the same ;D } // if we go off the left side of the screen if (position.x < 0) { SendNoteToScene("BallOffLeft"); } // if we go off the right side of the screen if (position.x > 800) { SendNoteToScene("BallOffRight"); } }
void StreamEmitter::Tick(GameData* _GD) { spawnTimer += _GD->m_dt; while (spawnTimer > 1.f / rate) { bool particleSpawned = false; //Iterates through the particle list for (Particle* particle : myParticles) { if (!particle->isAlive()) { Vector2 particlePos = m_pos; //Sets the direction of travel of the particle to downwards Vector2 particleDir = Vector2(0, 1); //Sets the direction of travel for the particle particlePos = Vector2::Transform(particlePos, m_worldMat); particlePos.Normalize(); particleDir.Normalize(); //Spawn the particle with the values calculated above particle->Spawn(life, m_pos, particleDir); particleSpawned = true; spawnTimer -= 1.f / rate; break; } } break; } //Sort the particles using the function declared above to make sure particles are being added and removed in the correct order myParticles.sort(compareParticles); Emitter::Tick(_GD); }
int IntrLine2Segment2<Real>::Classify (Real* afS, Vector2<Real>* pkDiff, Vector2<Real>* pkDiffN) { // The intersection of two lines is a solution to P0+s0*D0 = P1+s1*D1. // Rewrite this as s0*D0 - s1*D1 = P1 - P0 = Q. If D0.Dot(Perp(D1)) = 0, // the lines are parallel. Additionally, if Q.Dot(Perp(D1)) = 0, the // lines are the same. If D0.Dot(Perp(D1)) is not zero, then // s0 = Q.Dot(Perp(D1))/D0.Dot(Perp(D1)) // produces the point of intersection. Also, // s1 = Q.Dot(Perp(D0))/D0.Dot(Perp(D1)) Vector2<Real> kDiff = m_pkSegment->Origin - m_pkLine->Origin; if (pkDiff) { *pkDiff = kDiff; } Real fD0DotPerpD1 = m_pkLine->Direction.DotPerp(m_pkSegment->Direction); if (Math<Real>::FAbs(fD0DotPerpD1) > Math<Real>::ZERO_TOLERANCE) { // lines intersect in a single point if (afS) { Real fInvD0DotPerpD1 = ((Real)1.0)/fD0DotPerpD1; Real fDiffDotPerpD0 = kDiff.DotPerp(m_pkLine->Direction); Real fDiffDotPerpD1 = kDiff.DotPerp(m_pkSegment->Direction); afS[0] = fDiffDotPerpD1*fInvD0DotPerpD1; afS[1] = fDiffDotPerpD0*fInvD0DotPerpD1; } return IT_POINT; } // lines are parallel kDiff.Normalize(); if (pkDiffN) { *pkDiffN = kDiff; } Real fDiffNDotPerpD1 = kDiff.DotPerp(m_pkSegment->Direction); if (Math<Real>::FAbs(fDiffNDotPerpD1) <= Math<Real>::ZERO_TOLERANCE) { // lines are colinear return IT_SEGMENT; } // lines are parallel, but distinct return IT_EMPTY; }
int IntrSegment2Segment2<Real>::Classify (Real* s, Vector2<Real>* diff, Vector2<Real>* diffN) { // The intersection of two lines is a solution to P0+s0*D0 = P1+s1*D1. // Rewrite this as s0*D0 - s1*D1 = P1 - P0 = Q. If D0.Dot(Perp(D1)) = 0, // the lines are parallel. Additionally, if Q.Dot(Perp(D1)) = 0, the // lines are the same. If D0.Dot(Perp(D1)) is not zero, then // s0 = Q.Dot(Perp(D1))/D0.Dot(Perp(D1)) // produces the point of intersection. Also, // s1 = Q.Dot(Perp(D0))/D0.Dot(Perp(D1)) Vector2<Real> originDiff = mSegment1->Center - mSegment0->Center; if (diff) { *diff = originDiff; } Real D0DotPerpD1 = mSegment0->Direction.DotPerp(mSegment1->Direction); if (Math<Real>::FAbs(D0DotPerpD1) > Math<Real>::ZERO_TOLERANCE) { // Lines intersect in a single point. if (s) { Real invD0DotPerpD1 = ((Real)1)/D0DotPerpD1; Real diffDotPerpD0 = originDiff.DotPerp(mSegment0->Direction); Real diffDotPerpD1 = originDiff.DotPerp(mSegment1->Direction); s[0] = diffDotPerpD1*invD0DotPerpD1; s[1] = diffDotPerpD0*invD0DotPerpD1; } return IT_POINT; } // Lines are parallel. originDiff.Normalize(); if (diffN) { *diffN = originDiff; } Real diffNDotPerpD1 = originDiff.DotPerp(mSegment1->Direction); if (Math<Real>::FAbs(diffNDotPerpD1) <= Math<Real>::ZERO_TOLERANCE) { // Lines are colinear. return IT_SEGMENT; } // Lines are parallel, but distinct. return IT_EMPTY; }
void Player::Update() { Entity::Update(); if (Input::IsMouseButtonHeld(MOUSE_BUTTON_LEFT)) { Vector2 dir = Input::GetWorldMousePosition() - position; dir.Normalize(); velocity += force * dir * Monocle::deltaTime; } position += velocity * Monocle::deltaTime; Game::GetScene()->GetCamera()->position = position; //Graphics::SetCameraPosition(position); }
Vector2 AIFleeComponent::calculateForce(AIMovementComponent::MovementParameters& params) { // Should we use our specific flee target, or the one provided? Point2F target = params.target; if (!mFleeTargetObject.isNull()) { target = mFleeTargetObject->getPosition(); } Vector2 direction = params.ownerPosition - target; direction.Normalize(); Vector2 desiredVelocity = direction * params.ownerMaxSpeed; return desiredVelocity - params.ownerVelocity; }
void GameObject::update(double deltaTime) { if (moveBy != Vector2::Zero) { Vector2 dir = moveBy; dir.Normalize(); dir *= deltaTime * 100; moveBy -= dir; move(dir); if ((Vector2::Distance(position, waypoint) <= Globals::WAYPOINT_TOLERANCE)) { moveBy = Vector2::Zero; } } }
Vector2 Actor::GetDirectionVector() const { Vector2 r = Deku2D::Const::Math::V2_ZERO; for (int i = 0; i < 4; i++) { if (directions_[i]) { r += directionToVector[i]; } } if (r.x * r.y != 0.0f) { r.Normalize(); } return r; }
void FrogShip::update(double deltaTime, PlayerShip* player, vector<Bullet*>& liveBullets) { GameObject::updateDebug(deltaTime); timeAlive += deltaTime; double percent = timeAlive / TIME_TO_CLIMAX; percent = 1 - cos(percent*XM_PIDIV2); double rt = 1 - percent; position = camera.screenToWorld(float(rt*rt)*startPos + 2 * float(rt*percent)*controlPoint + float(percent*percent)*climaxPos); if (percent > .25) { // start aiming at player Vector2 dirToPlayer = player->getCenter() - position; dirToPlayer.Normalize(); float angleToPlayer = atan2(dirToPlayer.y, dirToPlayer.x) + XM_PIDIV2; dirToPlayer = Vector2(cos(angleToPlayer), sin(angleToPlayer)); //dirToPlayer += Vector2(cos(XM_PIDIV2), sin(XM_PIDIV2)); Vector2 currentRot(cos(rotation), sin(rotation)); currentRot += (dirToPlayer - currentRot) * float(deltaTime * rotationSpeed); setRotation(atan2(currentRot.y, currentRot.x)); if (timeAlive >= TIME_TO_FIRE && closeEnough(dirToPlayer, currentRot)) { for (auto const& weapon : weaponSystems) { weapon->timeSinceFired += deltaTime; weapon->updatePosition(position); if (weapon->ready()) { weapon->fired = true; Bullet* bullet = weapon->launchBullet(player->getCenter()); liveBullets.push_back(bullet); } } } } if (position.y > camera.screenToWorld(Vector2(0, float(camera.viewportHeight + 120))).y) { reset(); } }
void NavMesh::Grow(Vertices& vertices, float amount) { std::vector<Vector2> normals; normals.resize(vertices.size(), Vector2(0,0)); for(NavTriangle* t : triangles) { for (int i=0; i<3; i++) { if (!t->neighbors[i]) { int index0 = t->corners[i]; int index1 = t->corners[i<2 ? i + 1 : 0]; Vector2 direction = vertices[index1] - vertices[index0]; Vector2 normal = { -direction.y, direction.x }; normal.Normalize(); normals[index0] += normal; normals[index1] += normal; } } } for (int i=0; i<vertices.size(); i++) { vertices[i] -= normals[i].Normalized() * amount; } }
void GhostEnemy::Update(double dt) { // Get a direction to the target Vector2 moveVector = Vector3(m_target.x, m_target.y) - m_transforms.Translation; if (moveVector != Vector2::ZERO_VECTOR) { moveVector.Normalize(); } // Set the length to move moveVector = moveVector * m_moveSpeed * dt; m_transforms.Translation += moveVector; // Update the weapon if (m_weapon != NULL) { m_weapon->Update(dt); } // Shoot when possitble m_attacked = Attack(); }
void NavMesh::AddPoly(std::vector<double>& points, std::vector<int>& segments, std::vector<double>& holes, std::vector<Vector2>& polyPoints) { int index = (int)points.size()/2; Vector2 holePosition = 0; for (int i=0; i<polyPoints.size(); i++) { points.push_back(polyPoints[i].x); points.push_back(polyPoints[i].y); segments.push_back(index + i); segments.push_back(index + ((i==polyPoints.size()-1) ? 0 : i + 1)); holePosition += polyPoints[i]; } holePosition *= (1.0f / polyPoints.size()); for (int i=0; i<polyPoints.size(); i++) { Vector2 toCenter = holePosition - polyPoints[i]; toCenter.Normalize(); holes.push_back(polyPoints[i].x + toCenter.x * 4); holes.push_back(polyPoints[i].y + toCenter.y * 4); } }
MinBox2<Real>::MinBox2 ( int numPoints, const Vector2<Real>* points, Real epsilon, Query::Type queryType, bool isConvexPolygon ) { // Get the convex hull of the points. Vector2<Real>* hullPoints = 0; if ( isConvexPolygon ) { hullPoints = ( Vector2<Real>* )points; } else { ConvexHull2<Real> hull( numPoints, ( Vector2<Real>* )points, epsilon, false, queryType ); int hullDim = hull.GetDimension(); int hullNumSimplices = hull.GetNumSimplices(); const int* hullIndices = hull.GetIndices(); if ( hullDim == 0 ) { mMinBox.Center = points[0]; mMinBox.Axis[0] = Vector2<Real>::UNIT_X; mMinBox.Axis[1] = Vector2<Real>::UNIT_Y; mMinBox.Extent[0] = ( Real )0; mMinBox.Extent[1] = ( Real )0; return; } if ( hullDim == 1 ) { ConvexHull1<Real>* hull1 = hull.GetConvexHull1(); hullIndices = hull1->GetIndices(); mMinBox.Center = ( ( Real )0.5 ) * ( points[hullIndices[0]] + points[hullIndices[1]] ); Vector2<Real> diff = points[hullIndices[1]] - points[hullIndices[0]]; mMinBox.Extent[0] = ( ( Real )0.5 ) * diff.Normalize(); mMinBox.Extent[1] = ( Real )0.0; mMinBox.Axis[0] = diff; mMinBox.Axis[1] = -mMinBox.Axis[0].Perp(); delete0( hull1 ); return; } numPoints = hullNumSimplices; hullPoints = new1<Vector2<Real> >( numPoints ); for ( int i = 0; i < numPoints; ++i ) { hullPoints[i] = points[hullIndices[i]]; } } // The input points are V[0] through V[N-1] and are assumed to be the // vertices of a convex polygon that are counterclockwise ordered. The // input points must not contain three consecutive collinear points. // Unit-length edge directions of convex polygon. These could be // precomputed and passed to this routine if the application requires it. int numPointsM1 = numPoints - 1; Vector2<Real>* edges = new1<Vector2<Real> >( numPoints ); bool* visited = new1<bool>( numPoints ); int i; for ( i = 0; i < numPointsM1; ++i ) { edges[i] = hullPoints[i + 1] - hullPoints[i]; edges[i].Normalize(); visited[i] = false; } edges[numPointsM1] = hullPoints[0] - hullPoints[numPointsM1]; edges[numPointsM1].Normalize(); visited[numPointsM1] = false; // Find the smallest axis-aligned box containing the points. Keep track // of the extremum indices, L (left), R (right), B (bottom), and T (top) // so that the following constraints are met: // V[L].X() <= V[i].X() for all i and V[(L+1)%N].X() > V[L].X() // V[R].X() >= V[i].X() for all i and V[(R+1)%N].X() < V[R].X() // V[B].Y() <= V[i].Y() for all i and V[(B+1)%N].Y() > V[B].Y() // V[T].Y() >= V[i].Y() for all i and V[(T+1)%N].Y() < V[T].Y() Real xmin = hullPoints[0].X(), xmax = xmin; Real ymin = hullPoints[0].Y(), ymax = ymin; int LIndex = 0, RIndex = 0, BIndex = 0, TIndex = 0; for ( i = 1; i < numPoints; ++i ) { if ( hullPoints[i].X() <= xmin ) { xmin = hullPoints[i].X(); LIndex = i; } if ( hullPoints[i].X() >= xmax ) { xmax = hullPoints[i].X(); RIndex = i; } if ( hullPoints[i].Y() <= ymin ) { ymin = hullPoints[i].Y(); BIndex = i; } if ( hullPoints[i].Y() >= ymax ) { ymax = hullPoints[i].Y(); TIndex = i; } } // Apply wrap-around tests to ensure the constraints mentioned above are // satisfied. if ( LIndex == numPointsM1 ) { if ( hullPoints[0].X() <= xmin ) { xmin = hullPoints[0].X(); LIndex = 0; } } if ( RIndex == numPointsM1 ) { if ( hullPoints[0].X() >= xmax ) { xmax = hullPoints[0].X(); RIndex = 0; } } if ( BIndex == numPointsM1 ) { if ( hullPoints[0].Y() <= ymin ) { ymin = hullPoints[0].Y(); BIndex = 0; } } if ( TIndex == numPointsM1 ) { if ( hullPoints[0].Y() >= ymax ) { ymax = hullPoints[0].Y(); TIndex = 0; } } // The dimensions of the axis-aligned box. The extents store width and // height for now. mMinBox.Center.X() = ( ( Real )0.5 ) * ( xmin + xmax ); mMinBox.Center.Y() = ( ( Real )0.5 ) * ( ymin + ymax ); mMinBox.Axis[0] = Vector2<Real>::UNIT_X; mMinBox.Axis[1] = Vector2<Real>::UNIT_Y; mMinBox.Extent[0] = ( ( Real )0.5 ) * ( xmax - xmin ); mMinBox.Extent[1] = ( ( Real )0.5 ) * ( ymax - ymin ); Real minAreaDiv4 = mMinBox.Extent[0] * mMinBox.Extent[1]; // The rotating calipers algorithm. Vector2<Real> U = Vector2<Real>::UNIT_X; Vector2<Real> V = Vector2<Real>::UNIT_Y; bool done = false; while ( !done ) { // Determine the edge that forms the smallest angle with the current // box edges. int flag = F_NONE; Real maxDot = ( Real )0; Real dot = U.Dot( edges[BIndex] ); if ( dot > maxDot ) { maxDot = dot; flag = F_BOTTOM; } dot = V.Dot( edges[RIndex] ); if ( dot > maxDot ) { maxDot = dot; flag = F_RIGHT; } dot = -U.Dot( edges[TIndex] ); if ( dot > maxDot ) { maxDot = dot; flag = F_TOP; } dot = -V.Dot( edges[LIndex] ); if ( dot > maxDot ) { maxDot = dot; flag = F_LEFT; } switch ( flag ) { case F_BOTTOM: if ( visited[BIndex] ) { done = true; } else { // Compute box axes with E[B] as an edge. U = edges[BIndex]; V = -U.Perp(); UpdateBox( hullPoints[LIndex], hullPoints[RIndex], hullPoints[BIndex], hullPoints[TIndex], U, V, minAreaDiv4 ); // Mark edge visited and rotate the calipers. visited[BIndex] = true; if ( ++BIndex == numPoints ) { BIndex = 0; } } break; case F_RIGHT: if ( visited[RIndex] ) { done = true; } else { // Compute box axes with E[R] as an edge. V = edges[RIndex]; U = V.Perp(); UpdateBox( hullPoints[LIndex], hullPoints[RIndex], hullPoints[BIndex], hullPoints[TIndex], U, V, minAreaDiv4 ); // Mark edge visited and rotate the calipers. visited[RIndex] = true; if ( ++RIndex == numPoints ) { RIndex = 0; } } break; case F_TOP: if ( visited[TIndex] ) { done = true; } else { // Compute box axes with E[T] as an edge. U = -edges[TIndex]; V = -U.Perp(); UpdateBox( hullPoints[LIndex], hullPoints[RIndex], hullPoints[BIndex], hullPoints[TIndex], U, V, minAreaDiv4 ); // Mark edge visited and rotate the calipers. visited[TIndex] = true; if ( ++TIndex == numPoints ) { TIndex = 0; } } break; case F_LEFT: if ( visited[LIndex] ) { done = true; } else { // Compute box axes with E[L] as an edge. V = -edges[LIndex]; U = V.Perp(); UpdateBox( hullPoints[LIndex], hullPoints[RIndex], hullPoints[BIndex], hullPoints[TIndex], U, V, minAreaDiv4 ); // Mark edge visited and rotate the calipers. visited[LIndex] = true; if ( ++LIndex == numPoints ) { LIndex = 0; } } break; case F_NONE: // The polygon is a rectangle. done = true; break; } } delete1( visited ); delete1( edges ); if ( !isConvexPolygon ) { delete1( hullPoints ); } }