/** * <summary>Computes the new velocity of this agent.</summary> */ internal void computeNewVelocity() { orcaLines_.Clear(); double invTimeHorizonObst = 1.0 / timeHorizonObst_; /* Create obstacle ORCA lines. */ for (int i = 0; i < obstacleNeighbors_.Count; ++i) { Obstacle obstacle1 = obstacleNeighbors_[i].Value; Obstacle obstacle2 = obstacle1.next_; Vector2 relativePosition1 = obstacle1.point_ - position_; Vector2 relativePosition2 = obstacle2.point_ - position_; /* * Check if velocity obstacle of obstacle is already taken care * of by previously constructed obstacle ORCA lines. */ bool alreadyCovered = false; for (int j = 0; j < orcaLines_.Count; ++j) { if (RVOMath.det(invTimeHorizonObst * relativePosition1 - orcaLines_[j].point, orcaLines_[j].direction) - invTimeHorizonObst * radius_ >= -RVOMath.RVO_EPSILON && RVOMath.det(invTimeHorizonObst * relativePosition2 - orcaLines_[j].point, orcaLines_[j].direction) - invTimeHorizonObst * radius_ >= -RVOMath.RVO_EPSILON) { alreadyCovered = true; break; } } if (alreadyCovered) { continue; } /* Not yet covered. Check for collisions. */ double distSq1 = RVOMath.absSq(relativePosition1); double distSq2 = RVOMath.absSq(relativePosition2); double radiusSq = RVOMath.sqr(radius_); Vector2 obstacleVector = obstacle2.point_ - obstacle1.point_; double s = Vector2.Dot(-relativePosition1, obstacleVector) / RVOMath.absSq(obstacleVector); double distSqLine = RVOMath.absSq(-relativePosition1 - s * obstacleVector); Line line; if (s < 0.0 && distSq1 <= radiusSq) { /* Collision with left vertex. Ignore if non-convex. */ if (obstacle1.convex_) { line.point = new Vector2(0.0, 0.0); line.direction = RVOMath.normalize(new Vector2(-relativePosition1.y, relativePosition1.x)); orcaLines_.Add(line); } continue; } else if (s > 1.0 && distSq2 <= radiusSq) { /* * Collision with right vertex. Ignore if non-convex or if * it will be taken care of by neighboring obstacle. */ if (obstacle2.convex_ && RVOMath.det(relativePosition2, obstacle2.direction_) >= 0.0) { line.point = new Vector2(0.0, 0.0); line.direction = RVOMath.normalize(new Vector2(-relativePosition2.y, relativePosition2.x)); orcaLines_.Add(line); } continue; } else if (s >= 0.0 && s < 1.0 && distSqLine <= radiusSq) { /* Collision with obstacle segment. */ line.point = new Vector2(0.0, 0.0); line.direction = -obstacle1.direction_; orcaLines_.Add(line); continue; } /* * No collision. Compute legs. When obliquely viewed, both legs * can come from a single vertex. Legs extend cut-off line when * non-convex vertex. */ Vector2 leftLegDirection, rightLegDirection; if (s < 0.0 && distSqLine <= radiusSq) { /* * Obstacle viewed obliquely so that left vertex * defines velocity obstacle. */ if (!obstacle1.convex_) { /* Ignore obstacle. */ continue; } obstacle2 = obstacle1; double leg1 = Math.Sqrt(distSq1 - radiusSq); leftLegDirection = new Vector2(relativePosition1.x * leg1 - relativePosition1.y * radius_, relativePosition1.x * radius_ + relativePosition1.y * leg1) / distSq1; rightLegDirection = new Vector2(relativePosition1.x * leg1 + relativePosition1.y * radius_, -relativePosition1.x * radius_ + relativePosition1.y * leg1) / distSq1; } else if (s > 1.0 && distSqLine <= radiusSq) { /* * Obstacle viewed obliquely so that * right vertex defines velocity obstacle. */ if (!obstacle2.convex_) { /* Ignore obstacle. */ continue; } obstacle1 = obstacle2; double leg2 = Math.Sqrt(distSq2 - radiusSq); leftLegDirection = new Vector2(relativePosition2.x * leg2 - relativePosition2.y * radius_, relativePosition2.x * radius_ + relativePosition2.y * leg2) / distSq2; rightLegDirection = new Vector2(relativePosition2.x * leg2 + relativePosition2.y * radius_, -relativePosition2.x * radius_ + relativePosition2.y * leg2) / distSq2; } else { /* Usual situation. */ if (obstacle1.convex_) { double leg1 = Math.Sqrt(distSq1 - radiusSq); leftLegDirection = new Vector2(relativePosition1.x * leg1 - relativePosition1.y * radius_, relativePosition1.x * radius_ + relativePosition1.y * leg1) / distSq1; } else { /* Left vertex non-convex; left leg extends cut-off line. */ leftLegDirection = -obstacle1.direction_; } if (obstacle2.convex_) { double leg2 = Math.Sqrt(distSq2 - radiusSq); rightLegDirection = new Vector2(relativePosition2.x * leg2 + relativePosition2.y * radius_, -relativePosition2.x * radius_ + relativePosition2.y * leg2) / distSq2; } else { /* Right vertex non-convex; right leg extends cut-off line. */ rightLegDirection = obstacle1.direction_; } } /* * Legs can never point into neighboring edge when convex * vertex, take cutoff-line of neighboring edge instead. If * velocity projected on "foreign" leg, no constraint is added. */ Obstacle leftNeighbor = obstacle1.previous_; bool isLeftLegForeign = false; bool isRightLegForeign = false; if (obstacle1.convex_ && RVOMath.det(leftLegDirection, -leftNeighbor.direction_) >= 0.0) { /* Left leg points into obstacle. */ leftLegDirection = -leftNeighbor.direction_; isLeftLegForeign = true; } if (obstacle2.convex_ && RVOMath.det(rightLegDirection, obstacle2.direction_) <= 0.0) { /* Right leg points into obstacle. */ rightLegDirection = obstacle2.direction_; isRightLegForeign = true; } /* Compute cut-off centers. */ Vector2 leftCutOff = invTimeHorizonObst * (obstacle1.point_ - position_); Vector2 rightCutOff = invTimeHorizonObst * (obstacle2.point_ - position_); Vector2 cutOffVector = rightCutOff - leftCutOff; /* Project current velocity on velocity obstacle. */ /* Check if current velocity is projected on cutoff circles. */ double t = obstacle1 == obstacle2 ? 0.5 : (Vector2.Dot((velocity_ - leftCutOff), cutOffVector)) / RVOMath.absSq(cutOffVector); double tLeft = Vector2.Dot((velocity_ - leftCutOff), leftLegDirection); double tRight = Vector2.Dot((velocity_ - rightCutOff), rightLegDirection); if ((t < 0.0 && tLeft < 0.0) || (obstacle1 == obstacle2 && tLeft < 0.0 && tRight < 0.0)) { /* Project on left cut-off circle. */ Vector2 unitW = RVOMath.normalize(velocity_ - leftCutOff); line.direction = new Vector2(unitW.y, -unitW.x); line.point = leftCutOff + radius_ * invTimeHorizonObst * unitW; orcaLines_.Add(line); continue; } else if (t > 1.0 && tRight < 0.0) { /* Project on right cut-off circle. */ Vector2 unitW = RVOMath.normalize(velocity_ - rightCutOff); line.direction = new Vector2(unitW.y, -unitW.x); line.point = rightCutOff + radius_ * invTimeHorizonObst * unitW; orcaLines_.Add(line); continue; } /* * Project on left leg, right leg, or cut-off line, whichever is * closest to velocity. */ double distSqCutoff = (t <0.0 || t> 1.0 || obstacle1 == obstacle2) ? double.PositiveInfinity : RVOMath.absSq(velocity_ - (leftCutOff + t * cutOffVector)); double distSqLeft = tLeft < 0.0 ? double.PositiveInfinity : RVOMath.absSq(velocity_ - (leftCutOff + tLeft * leftLegDirection)); double distSqRight = tRight < 0.0 ? double.PositiveInfinity : RVOMath.absSq(velocity_ - (rightCutOff + tRight * rightLegDirection)); if (distSqCutoff <= distSqLeft && distSqCutoff <= distSqRight) { /* Project on cut-off line. */ line.direction = -obstacle1.direction_; line.point = leftCutOff + radius_ * invTimeHorizonObst * new Vector2(-line.direction.y, line.direction.x); orcaLines_.Add(line); continue; } if (distSqLeft <= distSqRight) { /* Project on left leg. */ if (isLeftLegForeign) { continue; } line.direction = leftLegDirection; line.point = leftCutOff + radius_ * invTimeHorizonObst * new Vector2(-line.direction.y, line.direction.x); orcaLines_.Add(line); continue; } /* Project on right leg. */ if (isRightLegForeign) { continue; } line.direction = -rightLegDirection; line.point = rightCutOff + radius_ * invTimeHorizonObst * new Vector2(-line.direction.y, line.direction.x); orcaLines_.Add(line); } int numObstLines = orcaLines_.Count; double invTimeHorizon = 1.0 / timeHorizon_; /* Create agent ORCA lines. */ for (int i = 0; i < agentNeighbors_.Count; ++i) { Agent other = agentNeighbors_[i].Value; Vector2 relativePosition = other.position_ - position_; Vector2 relativeVelocity = velocity_ - other.velocity_; double distSq = RVOMath.absSq(relativePosition); double combinedRadius = radius_ + other.radius_; double combinedRadiusSq = RVOMath.sqr(combinedRadius); Line line; Vector2 u; if (distSq > combinedRadiusSq) { /* No collision. */ Vector2 w = relativeVelocity - invTimeHorizon * relativePosition; /* Vector from cutoff center to relative velocity. */ double wLengthSq = RVOMath.absSq(w); double dotProduct1 = Vector2.Dot(w, relativePosition); if (dotProduct1 < 0.0 && RVOMath.sqr(dotProduct1) > combinedRadiusSq * wLengthSq) { /* Project on cut-off circle. */ double wLength = Math.Sqrt(wLengthSq); Vector2 unitW = w / wLength; line.direction = new Vector2(unitW.y, -unitW.x); u = (combinedRadius * invTimeHorizon - wLength) * unitW; } else { /* Project on legs. */ double leg = Math.Sqrt(distSq - combinedRadiusSq); if (RVOMath.det(relativePosition, w) > 0.0) { /* Project on left leg. */ line.direction = new Vector2(relativePosition.x * leg - relativePosition.y * combinedRadius, relativePosition.x * combinedRadius + relativePosition.y * leg) / distSq; } else { /* Project on right leg. */ line.direction = -new Vector2(relativePosition.x * leg + relativePosition.y * combinedRadius, -relativePosition.x * combinedRadius + relativePosition.y * leg) / distSq; } double dotProduct2 = Vector2.Dot(relativeVelocity, line.direction); u = dotProduct2 * line.direction - relativeVelocity; } } else { /* Collision. Project on cut-off circle of time timeStep. */ double invTimeStep = 1.0 / simulator.getTimeStep(); /* Vector from cutoff center to relative velocity. */ Vector2 w = relativeVelocity - invTimeStep * relativePosition; double wLength = RVOMath.abs(w); Vector2 unitW = w / wLength; line.direction = new Vector2(unitW.y, -unitW.x); u = (combinedRadius * invTimeStep - wLength) * unitW; } line.point = velocity_ + 0.5 * u; orcaLines_.Add(line); } int lineFail = linearProgram2(orcaLines_, maxSpeed_, prefVelocity_, false, ref newVelocity_); if (lineFail < orcaLines_.Count) { linearProgram3(orcaLines_, numObstLines, lineFail, maxSpeed_, ref newVelocity_); } }