/** * <summary>Recursive method for computing the obstacle neighbors of the * specified agent.</summary> * * <param name="agent">The agent for which obstacle neighbors are to be * computed.</param> * <param name="rangeSq">The squared range around the agent.</param> * <param name="node">The current obstacle k-D node.</param> */ private void queryObstacleTreeRecursive(Agent agent, KInt rangeSq, ObstacleTreeNode node) { if (node != null) { Obstacle obstacle1 = node.obstacle_; Obstacle obstacle2 = obstacle1.next_; KInt agentLeftOfLine = RVOMath.leftOf(obstacle1.point_, obstacle2.point_, agent.position_); queryObstacleTreeRecursive(agent, rangeSq, agentLeftOfLine >= 0 ? node.left_ : node.right_); if (RVOMath.absSq(obstacle2.point_ - obstacle1.point_) == 0) { return; } KInt distSqLine = RVOMath.sqr(agentLeftOfLine) / RVOMath.absSq(obstacle2.point_ - obstacle1.point_); if (distSqLine < rangeSq) { if (agentLeftOfLine < 0) { /* * Try obstacle at this node only if agent is on right side of * obstacle (and can see obstacle). */ agent.insertObstacleNeighbor(node.obstacle_, rangeSq); } /* Try other side of line. */ queryObstacleTreeRecursive(agent, rangeSq, agentLeftOfLine >= 0 ? node.right_ : node.left_); } } }
public Vector2 GetVelocityCollisionFree(Vector2 currentlocation, Vector2 currentVelocity) // { IList <Agent> tmpAgents = getAgents(); // currentAgent = createAgent(robotId); if (tmpAgents.Count > 0) { float rangeSq = RVOMath.sqr(timeHorizonObst * maxSpeed + radius); IList <Obstacle> tmpObstacle = getObstacles(currentlocation, rangeSq); currentAgent.position_ = currentlocation; // currentAgent.prefVelocity_ = currentVelocity; // kdTree_ = new KdTree(); kdTree_.buildAgentTree(tmpAgents); currentAgent.computeNeighbors(kdTree_, tmpObstacle); // currentAgent.computeNewVelocity(); // return(currentAgent.newVelocity_); // } else { return(new Vector2(0, 0)); } }
private IList <Obstacle> getObstacles(Vector2 currentlocation, float rangeSq) { List <Obstacle> temp = new List <Obstacle>(); for (int i = 0; i < walls.Count; i++) { Obstacle obstacle1 = walls[i]; Obstacle obstacle2 = obstacle1.next_; float agentLeftOfLine = RVOMath.leftOf(obstacle1.point_, obstacle2.point_, currentlocation); float distSqLine = RVOMath.sqr(agentLeftOfLine) / RVOMath.absSq(obstacle2.point_ - obstacle1.point_); if (distSqLine < rangeSq) { if (agentLeftOfLine < 0.0f) { temp.Add(walls[i]); } } } return(temp); }
/** * <summary>Computes the neighbors of this agent.</summary> */ internal void computeNeighbors() { obstacleNeighbors_.Clear(); KInt rangeSq = RVOMath.sqr(timeHorizonObst_ * maxSpeed_ + radius_); Simulator.Instance.kdTree_.computeObstacleNeighbors(this, rangeSq); agentNeighbors_.Clear(); if (maxNeighbors_ > 0) { rangeSq = RVOMath.sqr(neighborDist_); Simulator.Instance.kdTree_.computeAgentNeighbors(this, ref rangeSq); } }
private void queryAgentTreeRecursive(KInt2 position, ref KInt rangeSq, ref int agentNo, int node) { if (agentTree_[node].end_ - agentTree_[node].begin_ <= MAX_LEAF_SIZE) { for (int i = agentTree_[node].begin_; i < agentTree_[node].end_; ++i) { KInt distSq = RVOMath.absSq(position - agents_[i].position_); if (distSq < rangeSq) { rangeSq = distSq; agentNo = agents_[i].id_; } } } else { KInt distSqLeft = RVOMath.sqr(ReduceMax(agentTree_[agentTree_[node].left_].minx, position.IntX)) + RVOMath.sqr(ReduceMax(position.IntX, agentTree_[agentTree_[node].left_].maxx)) + RVOMath.sqr(ReduceMax(agentTree_[agentTree_[node].left_].miny, position.IntY)) + RVOMath.sqr(ReduceMax(position.IntY, agentTree_[agentTree_[node].left_].maxy)); KInt distSqRight = RVOMath.sqr(ReduceMax(agentTree_[agentTree_[node].right_].minx, position.IntX)) + RVOMath.sqr(ReduceMax(position.IntX, agentTree_[agentTree_[node].right_].maxx)) + RVOMath.sqr(ReduceMax(agentTree_[agentTree_[node].right_].miny, position.IntY)) + RVOMath.sqr(ReduceMax(position.IntY, agentTree_[agentTree_[node].right_].maxy)); if (distSqLeft < distSqRight) { if (distSqLeft < rangeSq) { queryAgentTreeRecursive(position, ref rangeSq, ref agentNo, agentTree_[node].left_); if (distSqRight < rangeSq) { queryAgentTreeRecursive(position, ref rangeSq, ref agentNo, agentTree_[node].right_); } } } else { if (distSqRight < rangeSq) { queryAgentTreeRecursive(position, ref rangeSq, ref agentNo, agentTree_[node].right_); if (distSqLeft < rangeSq) { queryAgentTreeRecursive(position, ref rangeSq, ref agentNo, agentTree_[node].left_); } } } } }
/** * <summary>Computes the neighbors of this agent.</summary> */ internal void computeNeighbors(KdTree kdTree_, IList <Obstacle> obstacles) { obstacleNeighbors_.Clear(); agentNeighbors_.Clear(); float rangeSq = RVOMath.sqr(timeHorizonObst_ * maxSpeed_ + radius_); foreach (var obstacle in obstacles) { insertObstacleNeighbor(obstacle, rangeSq); //mozliwy powod bledu ze wzgledu na rangeSq // kdTree.computeObstacleNeighbors(this, rangeSq); } if (maxNeighbors_ > 0) { rangeSq = RVOMath.sqr(neighborDist_); kdTree_.computeAgentNeighbors(this, ref rangeSq); // foreach (var agent in agents) // insertAgentNeighbor(agent, ref rangeSq); //mozliwy powod bledu ze wzgledu na rangeSq // co z obstacle_next // kdTree.computeAgentNeighbors(this, ref rangeSq); } }
/** * <summary>Recursive method for computing the agent neighbors of the * specified agent.</summary> * * <param name="agent">The agent for which agent neighbors are to be * computed.</param> * <param name="rangeSq">The squared range around the agent.</param> * <param name="node">The current agent k-D tree node index.</param> */ private void queryAgentTreeRecursive(Agent agent, ref KInt rangeSq, int node) { if (agentTree_[node].end_ - agentTree_[node].begin_ <= MAX_LEAF_SIZE) { for (int i = agentTree_[node].begin_; i < agentTree_[node].end_; ++i) { agent.insertAgentNeighbor(agents_[i], ref rangeSq); } } else { KInt distSqLeft = RVOMath.sqr(ReduceMax(agentTree_[agentTree_[node].left_].minx, agent.position_.IntX)) + RVOMath.sqr(ReduceMax(agent.position_.IntX, agentTree_[agentTree_[node].left_].maxx)) + RVOMath.sqr(ReduceMax(agentTree_[agentTree_[node].left_].miny, agent.position_.IntY)) + RVOMath.sqr(ReduceMax(agent.position_.IntY, agentTree_[agentTree_[node].left_].maxy)); KInt distSqRight = RVOMath.sqr(ReduceMax(agentTree_[agentTree_[node].right_].minx, agent.position_.IntX)) + RVOMath.sqr(ReduceMax(agent.position_.IntX, agentTree_[agentTree_[node].right_].maxx)) + RVOMath.sqr(ReduceMax(agentTree_[agentTree_[node].right_].miny, agent.position_.IntY)) + RVOMath.sqr(ReduceMax(agent.position_.IntY, agentTree_[agentTree_[node].right_].maxy)); if (distSqLeft < distSqRight) { if (distSqLeft < rangeSq) { queryAgentTreeRecursive(agent, ref rangeSq, agentTree_[node].left_); if (distSqRight < rangeSq) { queryAgentTreeRecursive(agent, ref rangeSq, agentTree_[node].right_); } } } else { if (distSqRight < rangeSq) { queryAgentTreeRecursive(agent, ref rangeSq, agentTree_[node].right_); if (distSqLeft < rangeSq) { queryAgentTreeRecursive(agent, ref rangeSq, agentTree_[node].left_); } } } } }
/** * <summary>Recursive method for querying the visibility between two * points within a specified radius.</summary> * * <returns>True if q1 and q2 are mutually visible within the radius; * false otherwise.</returns> * * <param name="q1">The first point between which visibility is to be * tested.</param> * <param name="q2">The second point between which visibility is to be * tested.</param> * <param name="radius">The radius within which visibility is to be * tested.</param> * <param name="node">The current obstacle k-D node.</param> */ private bool queryVisibilityRecursive(KInt2 q1, KInt2 q2, KInt radius, ObstacleTreeNode node) { if (node == null) { return(true); } Obstacle obstacle1 = node.obstacle_; Obstacle obstacle2 = obstacle1.next_; KInt q1LeftOfI = RVOMath.leftOf(obstacle1.point_, obstacle2.point_, q1); KInt q2LeftOfI = RVOMath.leftOf(obstacle1.point_, obstacle2.point_, q2); KInt LengthI = RVOMath.absSq(obstacle2.point_ - obstacle1.point_); // KInt invLengthI = 1.0f / RVOMath.absSq(obstacle2.point_ - obstacle1.point_); if (q1LeftOfI >= 0 && q2LeftOfI >= 0) { return(queryVisibilityRecursive(q1, q2, radius, node.left_) && ((RVOMath.sqr(q1LeftOfI) / LengthI >= RVOMath.sqr(radius) && RVOMath.sqr(q2LeftOfI) / LengthI >= RVOMath.sqr(radius)) || queryVisibilityRecursive(q1, q2, radius, node.right_))); } if (q1LeftOfI <= 0 && q2LeftOfI <= 0) { return(queryVisibilityRecursive(q1, q2, radius, node.right_) && ((RVOMath.sqr(q1LeftOfI) / LengthI >= RVOMath.sqr(radius) && RVOMath.sqr(q2LeftOfI) / LengthI >= RVOMath.sqr(radius)) || queryVisibilityRecursive(q1, q2, radius, node.left_))); } if (q1LeftOfI >= 0 && q2LeftOfI <= 0) { /* One can see through obstacle from left to right. */ return(queryVisibilityRecursive(q1, q2, radius, node.left_) && queryVisibilityRecursive(q1, q2, radius, node.right_)); } KInt point1LeftOfQ = RVOMath.leftOf(q1, q2, obstacle1.point_); KInt point2LeftOfQ = RVOMath.leftOf(q1, q2, obstacle2.point_); KInt LengthQ = RVOMath.absSq(q2 - q1); // KInt invLengthQ = 1.0f / RVOMath.absSq(q2 - q1); return(point1LeftOfQ * point2LeftOfQ >= 0 && RVOMath.sqr(point1LeftOfQ) / LengthQ > RVOMath.sqr(radius) && RVOMath.sqr(point2LeftOfQ) / LengthQ > RVOMath.sqr(radius) && queryVisibilityRecursive(q1, q2, radius, node.left_) && queryVisibilityRecursive(q1, q2, radius, node.right_)); }
/** * <summary>Recursive method for computing the agent neighbors of the * specified agent.</summary> * * <param name="agent">The agent for which agent neighbors are to be * computed.</param> * <param name="rangeSq">The squared range around the agent.</param> * <param name="node">The current agent k-D tree node index.</param> */ private void queryAgentTreeRecursive(Agent agent, ref float rangeSq, int node) { if (agentTree_[node].end_ - agentTree_[node].begin_ <= MAX_LEAF_SIZE) { for (int i = agentTree_[node].begin_; i < agentTree_[node].end_; ++i) { agent.insertAgentNeighbor(agents_[i], ref rangeSq); } } else { float distSqLeft = RVOMath.sqr(Math.Max(0.0f, agentTree_[agentTree_[node].left_].minX_ - agent.position_.x_)) + RVOMath.sqr(Math.Max(0.0f, agent.position_.x_ - agentTree_[agentTree_[node].left_].maxX_)) + RVOMath.sqr(Math.Max(0.0f, agentTree_[agentTree_[node].left_].minY_ - agent.position_.y_)) + RVOMath.sqr(Math.Max(0.0f, agent.position_.y_ - agentTree_[agentTree_[node].left_].maxY_)); float distSqRight = RVOMath.sqr(Math.Max(0.0f, agentTree_[agentTree_[node].right_].minX_ - agent.position_.x_)) + RVOMath.sqr(Math.Max(0.0f, agent.position_.x_ - agentTree_[agentTree_[node].right_].maxX_)) + RVOMath.sqr(Math.Max(0.0f, agentTree_[agentTree_[node].right_].minY_ - agent.position_.y_)) + RVOMath.sqr(Math.Max(0.0f, agent.position_.y_ - agentTree_[agentTree_[node].right_].maxY_)); if (distSqLeft < distSqRight) { if (distSqLeft < rangeSq) { queryAgentTreeRecursive(agent, ref rangeSq, agentTree_[node].left_); if (distSqRight < rangeSq) { queryAgentTreeRecursive(agent, ref rangeSq, agentTree_[node].right_); } } } else { if (distSqRight < rangeSq) { queryAgentTreeRecursive(agent, ref rangeSq, agentTree_[node].right_); if (distSqLeft < rangeSq) { queryAgentTreeRecursive(agent, ref rangeSq, agentTree_[node].left_); } } } } }
/** * <summary>Solves a two-dimensional linear program subject to linear * constraints defined by lines and a circular constraint.</summary> * * <returns>The number of the line it fails on, and the number of lines * if successful.</returns> * * <param name="lines">Lines defining the linear constraints.</param> * <param name="radius">The radius of the circular constraint.</param> * <param name="optVelocity">The optimization velocity.</param> * <param name="directionOpt">True if the direction should be optimized. * </param> * <param name="result">A reference to the result of the linear program. * </param> */ private int linearProgram2(IList <Line> lines, float radius, Vector2 optVelocity, bool directionOpt, ref Vector2 result) { if (directionOpt) { /* * Optimize direction. Note that the optimization velocity is of * unit length in this case. */ result = optVelocity * radius; } else if (RVOMath.absSq(optVelocity) > RVOMath.sqr(radius)) { /* Optimize closest point and outside circle. */ result = RVOMath.normalize(optVelocity) * radius; } else { /* Optimize closest point and inside circle. */ result = optVelocity; } for (int i = 0; i < lines.Count; ++i) { if (RVOMath.det(lines[i].direction, lines[i].point - result) > 0.0f) { /* Result does not satisfy constraint i. Compute new optimal result. */ Vector2 tempResult = result; if (!linearProgram1(lines, i, radius, optVelocity, directionOpt, ref result)) { result = tempResult; return(i); } } } return(lines.Count); }
/** * <summary>Computes the new velocity of this agent.</summary> */ internal void computeNewVelocity() { orcaLines_.Clear(); float invTimeHorizonObst = 1.0f / 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. */ float distSq1 = RVOMath.absSq(relativePosition1); float distSq2 = RVOMath.absSq(relativePosition2); float radiusSq = RVOMath.sqr(radius_); Vector2 obstacleVector = obstacle2.point_ - obstacle1.point_; float s = (-relativePosition1 * obstacleVector) / RVOMath.absSq(obstacleVector); float distSqLine = RVOMath.absSq(-relativePosition1 - s * obstacleVector); Line line; if (s < 0.0f && distSq1 <= radiusSq) { /* Collision with left vertex. Ignore if non-convex. */ if (obstacle1.convex_) { line.point = new Vector2(0.0f, 0.0f); line.direction = RVOMath.normalize(new Vector2(-relativePosition1.y(), relativePosition1.x())); orcaLines_.Add(line); } continue; } else if (s > 1.0f && 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.0f) { line.point = new Vector2(0.0f, 0.0f); line.direction = RVOMath.normalize(new Vector2(-relativePosition2.y(), relativePosition2.x())); orcaLines_.Add(line); } continue; } else if (s >= 0.0f && s < 1.0f && distSqLine <= radiusSq) { /* Collision with obstacle segment. */ line.point = new Vector2(0.0f, 0.0f); 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.0f && distSqLine <= radiusSq) { /* * Obstacle viewed obliquely so that left vertex * defines velocity obstacle. */ if (!obstacle1.convex_) { /* Ignore obstacle. */ continue; } obstacle2 = obstacle1; float leg1 = RVOMath.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.0f && distSqLine <= radiusSq) { /* * Obstacle viewed obliquely so that * right vertex defines velocity obstacle. */ if (!obstacle2.convex_) { /* Ignore obstacle. */ continue; } obstacle1 = obstacle2; float leg2 = RVOMath.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_) { float leg1 = RVOMath.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_) { float leg2 = RVOMath.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.0f) { /* Left leg points into obstacle. */ leftLegDirection = -leftNeighbor.direction_; isLeftLegForeign = true; } if (obstacle2.convex_ && RVOMath.det(rightLegDirection, obstacle2.direction_) <= 0.0f) { /* 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. */ float t = obstacle1 == obstacle2 ? 0.5f : ((velocity_ - leftCutOff) * cutOffVector) / RVOMath.absSq(cutOffVector); float tLeft = (velocity_ - leftCutOff) * leftLegDirection; float tRight = (velocity_ - rightCutOff) * rightLegDirection; if ((t < 0.0f && tLeft < 0.0f) || (obstacle1 == obstacle2 && tLeft < 0.0f && tRight < 0.0f)) { /* 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.0f && tRight < 0.0f) { /* 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. */ float distSqCutoff = (t <0.0f || t> 1.0f || obstacle1 == obstacle2) ? float.PositiveInfinity : RVOMath.absSq(velocity_ - (leftCutOff + t * cutOffVector)); float distSqLeft = tLeft < 0.0f ? float.PositiveInfinity : RVOMath.absSq(velocity_ - (leftCutOff + tLeft * leftLegDirection)); float distSqRight = tRight < 0.0f ? float.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; float invTimeHorizon = 1.0f / 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_; float distSq = RVOMath.absSq(relativePosition); float combinedRadius = radius_ + other.radius_; float 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. */ float wLengthSq = RVOMath.absSq(w); float dotProduct1 = w * relativePosition; if (dotProduct1 < 0.0f && RVOMath.sqr(dotProduct1) > combinedRadiusSq * wLengthSq) { /* Project on cut-off circle. */ float wLength = RVOMath.sqrt(wLengthSq); Vector2 unitW = w / wLength; line.direction = new Vector2(unitW.y(), -unitW.x()); u = (combinedRadius * invTimeHorizon - wLength) * unitW; } else { /* Project on legs. */ float leg = RVOMath.sqrt(distSq - combinedRadiusSq); if (RVOMath.det(relativePosition, w) > 0.0f) { /* 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; } float dotProduct2 = relativeVelocity * line.direction; u = dotProduct2 * line.direction - relativeVelocity; } } else { /* Collision. Project on cut-off circle of time timeStep. */ float invTimeStep = 1.0f / TimeStep; /* Vector from cutoff center to relative velocity. */ Vector2 w = relativeVelocity - invTimeStep * relativePosition; float 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.5f * u; orcaLines_.Add(line); } int lineFail = linearProgram2(orcaLines_, maxSpeed_, prefVelocity_, false, ref newVelocity_); if (lineFail < orcaLines_.Count) { linearProgram3(orcaLines_, numObstLines, lineFail, maxSpeed_, ref newVelocity_); } }
/** * <summary>Solves a one-dimensional linear program on a specified line * subject to linear constraints defined by lines and a circular * constraint.</summary> * * <returns>True if successful.</returns> * * <param name="lines">Lines defining the linear constraints.</param> * <param name="lineNo">The specified line constraint.</param> * <param name="radius">The radius of the circular constraint.</param> * <param name="optVelocity">The optimization velocity.</param> * <param name="directionOpt">True if the direction should be optimized. * </param> * <param name="result">A reference to the result of the linear program. * </param> */ private bool linearProgram1(IList <Line> lines, int lineNo, float radius, Vector2 optVelocity, bool directionOpt, ref Vector2 result) { float dotProduct = lines[lineNo].point * lines[lineNo].direction; float discriminant = RVOMath.sqr(dotProduct) + RVOMath.sqr(radius) - RVOMath.absSq(lines[lineNo].point); if (discriminant < 0.0f) { /* Max speed circle fully invalidates line lineNo. */ return(false); } float sqrtDiscriminant = RVOMath.sqrt(discriminant); float tLeft = -dotProduct - sqrtDiscriminant; float tRight = -dotProduct + sqrtDiscriminant; for (int i = 0; i < lineNo; ++i) { float denominator = RVOMath.det(lines[lineNo].direction, lines[i].direction); float numerator = RVOMath.det(lines[i].direction, lines[lineNo].point - lines[i].point); if (RVOMath.fabs(denominator) <= RVOMath.RVO_EPSILON) { /* Lines lineNo and i are (almost) parallel. */ if (numerator < 0.0f) { return(false); } continue; } float t = numerator / denominator; if (denominator >= 0.0f) { /* Line i bounds line lineNo on the right. */ tRight = Math.Min(tRight, t); } else { /* Line i bounds line lineNo on the left. */ tLeft = Math.Max(tLeft, t); } if (tLeft > tRight) { return(false); } } if (directionOpt) { /* Optimize direction. */ if (optVelocity * lines[lineNo].direction > 0.0f) { /* Take right extreme. */ result = lines[lineNo].point + tRight * lines[lineNo].direction; } else { /* Take left extreme. */ result = lines[lineNo].point + tLeft * lines[lineNo].direction; } } else { /* Optimize closest point. */ Vector2 v = (optVelocity - lines[lineNo].point); float v2 = lines[lineNo].direction * v; float t = lines[lineNo].direction * (optVelocity - lines[lineNo].point); if (t < tLeft) { result = lines[lineNo].point + tLeft * lines[lineNo].direction; } else if (t > tRight) { result = lines[lineNo].point + tRight * lines[lineNo].direction; } else { result = lines[lineNo].point + t * lines[lineNo].direction; } } return(true); }
/** * <summary>Computes the new velocity of this agent.</summary> */ internal void computeNewVelocity() { orcaLines_.Clear(); //KInt invTimeHorizonObst = 1 / timeHorizonObst_; KInt tempradius = radius_ / timeHorizonObst_; /* Create obstacle ORCA lines. */ for (int i = 0; i < obstacleNeighbors_.Count; ++i) { Obstacle obstacle1 = obstacleNeighbors_[i].Value; Obstacle obstacle2 = obstacle1.next_; KInt2 relativePosition1 = obstacle1.point_ - position_; KInt2 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(relativePosition1 / timeHorizonObst_ - orcaLines_[j].point, orcaLines_[j].direction) - tempradius >= 0 && RVOMath.det(relativePosition2 / timeHorizonObst_ - orcaLines_[j].point, orcaLines_[j].direction) - tempradius >= 0) { alreadyCovered = true; break; } } if (alreadyCovered) { continue; } /* Not yet covered. Check for collisions. */ KInt distSq1 = RVOMath.absSq(relativePosition1); KInt distSq2 = RVOMath.absSq(relativePosition2); KInt radiusSq = RVOMath.sqr(radius_); KInt2 obstacleVector = obstacle2.point_ - obstacle1.point_; KInt s = (-RVOMath.Dot(relativePosition1, obstacleVector)) / RVOMath.absSq(obstacleVector); KInt distSqLine = RVOMath.absSq(-relativePosition1 - s * obstacleVector); Line line = new Line(); if (s < 0 && distSq1 <= radiusSq) { /* Collision with left vertex. Ignore if non-convex. */ if (obstacle1.convex_) { line.point = KInt2.zero; line.direction = RVOMath.normalize(KInt2.ToInt2(-relativePosition1.IntY, relativePosition1.IntX)); orcaLines_.Add(line); } continue; } else if (s > 1 && 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) { line.point = KInt2.zero; line.direction = RVOMath.normalize(KInt2.ToInt2(-relativePosition2.IntY, relativePosition2.IntX)); orcaLines_.Add(line); } continue; } else if (s >= 0 && s < 1 && distSqLine <= radiusSq) { /* Collision with obstacle segment. */ line.point = KInt2.zero; 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. */ KInt2 leftLegDirection, rightLegDirection; if (s < 0 && distSqLine <= radiusSq) { /* * Obstacle viewed obliquely so that left vertex * defines velocity obstacle. */ if (!obstacle1.convex_) { /* Ignore obstacle. */ continue; } obstacle2 = obstacle1; KInt leg1 = RVOMath.sqrt(distSq1 - radiusSq); leftLegDirection = KInt2.ToInt2(relativePosition1.IntX * leg1 - relativePosition1.IntY * radius_, relativePosition1.IntX * radius_ + relativePosition1.IntY * leg1) / distSq1; rightLegDirection = KInt2.ToInt2(relativePosition1.IntX * leg1 + relativePosition1.IntY * radius_, -relativePosition1.IntX * radius_ + relativePosition1.IntY * leg1) / distSq1; if (isover(leftLegDirection) || isover(rightLegDirection)) { UnityEngine.Debug.LogError("!!!"); } } else if (s > 1 && distSqLine <= radiusSq) { /* * Obstacle viewed obliquely so that * right vertex defines velocity obstacle. */ if (!obstacle2.convex_) { /* Ignore obstacle. */ continue; } obstacle1 = obstacle2; KInt leg2 = RVOMath.sqrt(distSq2 - radiusSq); leftLegDirection = KInt2.ToInt2(relativePosition2.IntX * leg2 - relativePosition2.IntY * radius_, relativePosition2.IntX * radius_ + relativePosition2.IntY * leg2) / distSq2; rightLegDirection = KInt2.ToInt2(relativePosition2.IntX * leg2 + relativePosition2.IntY * radius_, -relativePosition2.IntX * radius_ + relativePosition2.IntY * leg2) / distSq2; if (isover(leftLegDirection) || isover(rightLegDirection)) { Debug.LogError("!!!"); } } else { /* Usual situation. */ if (obstacle1.convex_) { KInt leg1 = RVOMath.sqrt(distSq1 - radiusSq); leftLegDirection = KInt2.ToInt2(relativePosition1.IntX * leg1 - relativePosition1.IntY * radius_, relativePosition1.IntX * radius_ + relativePosition1.IntY * leg1) / distSq1; if (isover(leftLegDirection)) { Debug.LogError("!!!"); } } else { /* Left vertex non-convex; left leg extends cut-off line. */ leftLegDirection = -obstacle1.direction_; if (isover(leftLegDirection)) { Debug.LogError("!!!"); } } if (obstacle2.convex_) { KInt leg2 = RVOMath.sqrt(distSq2 - radiusSq); rightLegDirection = KInt2.ToInt2(relativePosition2.IntX * leg2 + relativePosition2.IntY * radius_, -relativePosition2.IntX * radius_ + relativePosition2.IntY * leg2) / distSq2; if (isover(rightLegDirection)) { Debug.LogError("!!!"); } } else { /* Right vertex non-convex; right leg extends cut-off line. */ rightLegDirection = obstacle1.direction_; if (isover(rightLegDirection)) { Debug.LogError("!!!"); } } } /* * 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) { /* Left leg points into obstacle. */ leftLegDirection = -leftNeighbor.direction_; if (isover(leftLegDirection)) { Debug.LogError("!!!"); } isLeftLegForeign = true; } if (obstacle2.convex_ && RVOMath.det(rightLegDirection, obstacle2.direction_) <= 0) { /* Right leg points into obstacle. */ rightLegDirection = obstacle2.direction_; isRightLegForeign = true; if (isover(rightLegDirection)) { Debug.LogError("!!!"); } } /* Compute cut-off centers. */ KInt2 leftCutOff = (obstacle1.point_ - position_) / timeHorizonObst_; KInt2 rightCutOff = (obstacle2.point_ - position_) / timeHorizonObst_; KInt2 cutOffVector = rightCutOff - leftCutOff; /* Project current velocity on velocity obstacle. */ /* Check if current velocity is projected on cutoff circles. */ KInt sqvalue = RVOMath.absSq(cutOffVector); KInt t = KInt.ToInt(KInt.divscale / 2); if (obstacle1 != obstacle2) { if (sqvalue == 0) { t = KInt.MaxValue; } else { t = RVOMath.Dot((velocity_ - leftCutOff), cutOffVector) / sqvalue; } } KInt tLeft = RVOMath.Dot((velocity_ - leftCutOff), leftLegDirection); KInt tRight = RVOMath.Dot((velocity_ - rightCutOff), rightLegDirection); if ((t < 0 && tLeft < 0) || (obstacle1 == obstacle2 && tLeft < 0 && tRight < 0)) { /* Project on left cut-off circle. */ KInt2 unitW = RVOMath.normalize((velocity_ - leftCutOff)); line.direction = KInt2.ToInt2(unitW.IntY, -unitW.IntX); line.point = leftCutOff + radius_ * unitW / timeHorizonObst_; orcaLines_.Add(line); continue; } else if (t > 1 && tRight < 0) { /* Project on right cut-off circle. */ KInt2 unitW = RVOMath.normalize((velocity_ - rightCutOff)); line.direction = KInt2.ToInt2(unitW.IntY, -unitW.IntX); line.point = rightCutOff + radius_ * unitW / timeHorizonObst_; orcaLines_.Add(line); continue; } /* * Project on left leg, right leg, or cut-off line, whichever is * closest to velocity. */ KInt distSqCutoff = (t < 0 || t > 1 || obstacle1 == obstacle2) ? KInt.MaxValue : RVOMath.absSq(velocity_ - (leftCutOff + t * cutOffVector)); KInt distSqLeft = tLeft < 0 ? KInt.MaxValue : RVOMath.absSq(velocity_ - (leftCutOff + tLeft * leftLegDirection)); KInt distSqRight = tRight < 0 ? KInt.MaxValue : RVOMath.absSq(velocity_ - (rightCutOff + tRight * rightLegDirection)); if (distSqCutoff <= distSqLeft && distSqCutoff <= distSqRight) { /* Project on cut-off line. */ line.direction = -obstacle1.direction_; line.point = leftCutOff + radius_ * KInt2.ToInt2(-line.direction.IntY, line.direction.IntX) / timeHorizonObst_; orcaLines_.Add(line); continue; } if (distSqLeft <= distSqRight) { /* Project on left leg. */ if (isLeftLegForeign) { continue; } line.direction = leftLegDirection; line.point = leftCutOff + radius_ * KInt2.ToInt2(-line.direction.IntY, line.direction.IntX) / timeHorizonObst_; orcaLines_.Add(line); continue; } /* Project on right leg. */ if (isRightLegForeign) { continue; } line.direction = -rightLegDirection; line.point = rightCutOff + radius_ * KInt2.ToInt2(-line.direction.IntY, line.direction.IntX) / timeHorizonObst_; orcaLines_.Add(line); } int numObstLines = orcaLines_.Count; //KInt invTimeHorizon = 1 / timeHorizon_; /* Create agent ORCA lines. */ for (int i = 0; i < agentNeighbors_.Count; ++i) { Agent other = agentNeighbors_[i].Value; KInt2 relativePosition = other.position_ - position_; KInt2 relativeVelocity = velocity_ - other.velocity_; KInt distSq = RVOMath.absSq(relativePosition); KInt combinedRadius = radius_ + other.radius_; KInt combinedRadiusSq = RVOMath.sqr(combinedRadius); Line line = new Line(); KInt2 u; if (distSq > combinedRadiusSq) { /* No collision. */ KInt2 w = relativeVelocity - relativePosition / timeHorizon_; /* Vector from cutoff center to relative velocity. */ KInt wLengthSq = RVOMath.absSq(w); KInt dotProduct1 = RVOMath.Dot(w, relativePosition); if (dotProduct1 < 0 && RVOMath.sqr(dotProduct1) > combinedRadiusSq * wLengthSq) { /* Project on cut-off circle. */ KInt wLength = RVOMath.sqrt(wLengthSq); if (wLength == 0) { continue; } KInt2 unitW = w / wLength; line.direction = KInt2.ToInt2(unitW.IntY, -unitW.IntX); u = (combinedRadius / timeHorizon_ - wLength) * unitW; } else { /* Project on legs. */ KInt leg = RVOMath.sqrt(distSq - combinedRadiusSq); if (RVOMath.det(relativePosition, w) > 0) { /* Project on left leg. */ line.direction = KInt2.ToInt2(relativePosition.IntX * leg - relativePosition.IntY * combinedRadius, relativePosition.IntX * combinedRadius + relativePosition.IntY * leg) / distSq; } else { /* Project on right leg. */ line.direction = -KInt2.ToInt2(relativePosition.IntX * leg + relativePosition.IntY * combinedRadius, -relativePosition.IntX * combinedRadius + relativePosition.IntY * leg) / distSq; } KInt dotProduct2 = RVOMath.Dot(relativeVelocity, line.direction); u = dotProduct2 * line.direction - relativeVelocity; } } else { /* Collision. Project on cut-off circle of time timeStep. */ //KInt invTimeStep = 1 / Simulator.Instance.timeStep_; /* Vector from cutoff center to relative velocity. */ KInt2 w = relativeVelocity - relativePosition / Simulator.Instance.timeStep_; KInt wLength = RVOMath.abs(w); if (wLength == 0) { continue; } KInt2 unitW = w / wLength; line.direction = KInt2.ToInt2(unitW.IntY, -unitW.IntX); u = (combinedRadius / Simulator.Instance.timeStep_ - wLength) * unitW; } line.point = velocity_ + u / 2; orcaLines_.Add(line); } int lineFail = linearProgram2(orcaLines_, maxSpeed_, prefVelocity_, false, ref newVelocity_); if (lineFail < orcaLines_.Count) { linearProgram3(orcaLines_, numObstLines, lineFail, maxSpeed_, ref newVelocity_); } }