提交RVO寻路

This commit is contained in:
PC-20230316NUNE\Administrator
2024-02-22 19:25:13 +08:00
parent 2b467e56ad
commit 8f2fb1010c
100 changed files with 34628 additions and 30888 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d2ce9624f28d4a21895096978accd208
timeCreated: 1708498100

View File

@@ -0,0 +1,8 @@
using RVO;
namespace Plugins.JNGame.Sync.Frame.AStar.RVO2
{
public class JNRVO2Simulator : Simulator
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f6cb8cf568a74351b80ac824ae622734
timeCreated: 1708509872

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 0950c0aa028d5c747b6a5b42e0707b3c
folderAsset: yes
timeCreated: 1509520805
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,730 @@
/*
* Agent.cs
* RVO2 Library C#
*
* Copyright 2008 University of North Carolina at Chapel Hill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Please send all bug reports to <geom@cs.unc.edu>.
*
* The authors may be contacted via:
*
* Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
* Dept. of Computer Science
* 201 S. Columbia St.
* Frederick P. Brooks, Jr. Computer Science Bldg.
* Chapel Hill, N.C. 27599-3175
* United States of America
*
* <http://gamma.cs.unc.edu/RVO2/>
*/
using System;
using System.Collections.Generic;
namespace RVO
{
/**
* <summary>Defines an agent in the simulation.</summary>
*/
internal class Agent
{
internal IList<KeyValuePair<float, Agent>> agentNeighbors_ = new List<KeyValuePair<float, Agent>>();
internal IList<KeyValuePair<float, Obstacle>> obstacleNeighbors_ = new List<KeyValuePair<float, Obstacle>>();
internal IList<Line> orcaLines_ = new List<Line>();
internal Vector2 position_;
internal Vector2 prefVelocity_;
internal Vector2 velocity_;
internal int id_ = 0;
internal int maxNeighbors_ = 0;
internal float maxSpeed_ = 0.0f;
internal float neighborDist_ = 0.0f;
internal float radius_ = 0.0f;
internal float timeHorizon_ = 0.0f;
internal float timeHorizonObst_ = 0.0f;
internal bool needDelete_ = false;
private Vector2 newVelocity_;
/**
* <summary>Computes the neighbors of this agent.</summary>
*/
internal void computeNeighbors(Simulator simulator)
{
obstacleNeighbors_.Clear();
float rangeSq = RVOMath.sqr(timeHorizonObst_ * maxSpeed_ + radius_);
simulator.kdTree_.computeObstacleNeighbors(this, rangeSq);
agentNeighbors_.Clear();
if (maxNeighbors_ > 0)
{
rangeSq = RVOMath.sqr(neighborDist_);
simulator.kdTree_.computeAgentNeighbors(this, ref rangeSq);
}
}
/**
* <summary>Computes the new velocity of this agent.</summary>
*/
internal void computeNewVelocity(Simulator simulator)
{
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 / simulator.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>Inserts an agent neighbor into the set of neighbors of this
* agent.</summary>
*
* <param name="agent">A pointer to the agent to be inserted.</param>
* <param name="rangeSq">The squared range around this agent.</param>
*/
internal void insertAgentNeighbor(Agent agent, ref float rangeSq)
{
if (this != agent)
{
float distSq = RVOMath.absSq(position_ - agent.position_);
if (distSq < rangeSq)
{
if (agentNeighbors_.Count < maxNeighbors_)
{
agentNeighbors_.Add(new KeyValuePair<float, Agent>(distSq, agent));
}
int i = agentNeighbors_.Count - 1;
while (i != 0 && distSq < agentNeighbors_[i - 1].Key)
{
agentNeighbors_[i] = agentNeighbors_[i - 1];
--i;
}
agentNeighbors_[i] = new KeyValuePair<float, Agent>(distSq, agent);
if (agentNeighbors_.Count == maxNeighbors_)
{
rangeSq = agentNeighbors_[agentNeighbors_.Count - 1].Key;
}
}
}
}
/**
* <summary>Inserts a static obstacle neighbor into the set of neighbors
* of this agent.</summary>
*
* <param name="obstacle">The number of the static obstacle to be
* inserted.</param>
* <param name="rangeSq">The squared range around this agent.</param>
*/
internal void insertObstacleNeighbor(Obstacle obstacle, float rangeSq)
{
Obstacle nextObstacle = obstacle.next_;
float distSq = RVOMath.distSqPointLineSegment(obstacle.point_, nextObstacle.point_, position_);
if (distSq < rangeSq)
{
obstacleNeighbors_.Add(new KeyValuePair<float, Obstacle>(distSq, obstacle));
int i = obstacleNeighbors_.Count - 1;
while (i != 0 && distSq < obstacleNeighbors_[i - 1].Key)
{
obstacleNeighbors_[i] = obstacleNeighbors_[i - 1];
--i;
}
obstacleNeighbors_[i] = new KeyValuePair<float, Obstacle>(distSq, obstacle);
}
}
/**
* <summary>Updates the two-dimensional position and two-dimensional
* velocity of this agent.</summary>
*/
internal void update(Simulator simulator)
{
velocity_ = newVelocity_;
position_ += velocity_ * simulator.timeStep_;
}
/**
* <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. */
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>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>Solves a two-dimensional linear program subject to linear
* constraints defined by lines and a circular constraint.</summary>
*
* <param name="lines">Lines defining the linear constraints.</param>
* <param name="numObstLines">Count of obstacle lines.</param>
* <param name="beginLine">The line on which the 2-d linear program
* failed.</param>
* <param name="radius">The radius of the circular constraint.</param>
* <param name="result">A reference to the result of the linear program.
* </param>
*/
private void linearProgram3(IList<Line> lines, int numObstLines, int beginLine, float radius, ref Vector2 result)
{
float distance = 0.0f;
for (int i = beginLine; i < lines.Count; ++i)
{
if (RVOMath.det(lines[i].direction, lines[i].point - result) > distance)
{
/* Result does not satisfy constraint of line i. */
IList<Line> projLines = new List<Line>();
for (int ii = 0; ii < numObstLines; ++ii)
{
projLines.Add(lines[ii]);
}
for (int j = numObstLines; j < i; ++j)
{
Line line;
float determinant = RVOMath.det(lines[i].direction, lines[j].direction);
if (RVOMath.fabs(determinant) <= RVOMath.RVO_EPSILON)
{
/* Line i and line j are parallel. */
if (lines[i].direction * lines[j].direction > 0.0f)
{
/* Line i and line j point in the same direction. */
continue;
}
else
{
/* Line i and line j point in opposite direction. */
line.point = 0.5f * (lines[i].point + lines[j].point);
}
}
else
{
line.point = lines[i].point + (RVOMath.det(lines[j].direction, lines[i].point - lines[j].point) / determinant) * lines[i].direction;
}
line.direction = RVOMath.normalize(lines[j].direction - lines[i].direction);
projLines.Add(line);
}
Vector2 tempResult = result;
if (linearProgram2(projLines, radius, new Vector2(-lines[i].direction.y(), lines[i].direction.x()), true, ref result) < projLines.Count)
{
/*
* This should in principle not happen. The result is by
* definition already in the feasible region of this
* linear program. If it fails, it is due to small
* floating point error, and the current result is kept.
*/
result = tempResult;
}
distance = RVOMath.det(lines[i].direction, lines[i].point - result);
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c89cdc06092e4cfab700ca0a1b02396a
timeCreated: 1708509745

View File

@@ -0,0 +1,678 @@
/*
* KdTree.cs
* RVO2 Library C#
*
* Copyright 2008 University of North Carolina at Chapel Hill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Please send all bug reports to <geom@cs.unc.edu>.
*
* The authors may be contacted via:
*
* Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
* Dept. of Computer Science
* 201 S. Columbia St.
* Frederick P. Brooks, Jr. Computer Science Bldg.
* Chapel Hill, N.C. 27599-3175
* United States of America
*
* <http://gamma.cs.unc.edu/RVO2/>
*/
using System.Collections.Generic;
using System;
namespace RVO
{
/**
* <summary>Defines k-D trees for agents and static obstacles in the
* simulation.</summary>
*/
internal class KdTree
{
/**
* <summary>Defines a node of an agent k-D tree.</summary>
*/
private struct AgentTreeNode
{
internal int begin_;
internal int end_;
internal int left_;
internal int right_;
internal float maxX_;
internal float maxY_;
internal float minX_;
internal float minY_;
}
/**
* <summary>Defines a pair of scalar values.</summary>
*/
private struct FloatPair
{
private float a_;
private float b_;
/**
* <summary>Constructs and initializes a pair of scalar
* values.</summary>
*
* <param name="a">The first scalar value.</returns>
* <param name="b">The second scalar value.</returns>
*/
internal FloatPair(float a, float b)
{
a_ = a;
b_ = b;
}
/**
* <summary>Returns true if the first pair of scalar values is less
* than the second pair of scalar values.</summary>
*
* <returns>True if the first pair of scalar values is less than the
* second pair of scalar values.</returns>
*
* <param name="pair1">The first pair of scalar values.</param>
* <param name="pair2">The second pair of scalar values.</param>
*/
public static bool operator <(FloatPair pair1, FloatPair pair2)
{
return pair1.a_ < pair2.a_ || !(pair2.a_ < pair1.a_) && pair1.b_ < pair2.b_;
}
/**
* <summary>Returns true if the first pair of scalar values is less
* than or equal to the second pair of scalar values.</summary>
*
* <returns>True if the first pair of scalar values is less than or
* equal to the second pair of scalar values.</returns>
*
* <param name="pair1">The first pair of scalar values.</param>
* <param name="pair2">The second pair of scalar values.</param>
*/
public static bool operator <=(FloatPair pair1, FloatPair pair2)
{
return (pair1.a_ == pair2.a_ && pair1.b_ == pair2.b_) || pair1 < pair2;
}
/**
* <summary>Returns true if the first pair of scalar values is
* greater than the second pair of scalar values.</summary>
*
* <returns>True if the first pair of scalar values is greater than
* the second pair of scalar values.</returns>
*
* <param name="pair1">The first pair of scalar values.</param>
* <param name="pair2">The second pair of scalar values.</param>
*/
public static bool operator >(FloatPair pair1, FloatPair pair2)
{
return !(pair1 <= pair2);
}
/**
* <summary>Returns true if the first pair of scalar values is
* greater than or equal to the second pair of scalar values.
* </summary>
*
* <returns>True if the first pair of scalar values is greater than
* or equal to the second pair of scalar values.</returns>
*
* <param name="pair1">The first pair of scalar values.</param>
* <param name="pair2">The second pair of scalar values.</param>
*/
public static bool operator >=(FloatPair pair1, FloatPair pair2)
{
return !(pair1 < pair2);
}
}
/**
* <summary>Defines a node of an obstacle k-D tree.</summary>
*/
private class ObstacleTreeNode
{
internal Obstacle obstacle_;
internal ObstacleTreeNode left_;
internal ObstacleTreeNode right_;
};
/**
* <summary>The maximum size of an agent k-D tree leaf.</summary>
*/
private const int MAX_LEAF_SIZE = 10;
private Agent[] agents_;
private AgentTreeNode[] agentTree_;
private ObstacleTreeNode obstacleTree_;
/**
* <summary>Builds an agent k-D tree.</summary>
*/
internal void buildAgentTree(Simulator simulator)
{
if (agents_ == null || agents_.Length != simulator.agents_.Count)
{
agents_ = new Agent[simulator.agents_.Count];
for (int i = 0; i < agents_.Length; ++i)
{
agents_[i] = simulator.agents_[i];
}
agentTree_ = new AgentTreeNode[2 * agents_.Length];
for (int i = 0; i < agentTree_.Length; ++i)
{
agentTree_[i] = new AgentTreeNode();
}
}
if (agents_.Length != 0)
{
buildAgentTreeRecursive(0, agents_.Length, 0);
}
}
/**
* <summary>Builds an obstacle k-D tree.</summary>
*/
internal void buildObstacleTree(Simulator simulator)
{
obstacleTree_ = new ObstacleTreeNode();
IList<Obstacle> obstacles = new List<Obstacle>(simulator.obstacles_.Count);
for (int i = 0; i < simulator.obstacles_.Count; ++i)
{
obstacles.Add(simulator.obstacles_[i]);
}
obstacleTree_ = buildObstacleTreeRecursive(simulator,obstacles);
}
/**
* <summary>Computes 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>
*/
internal void computeAgentNeighbors(Agent agent, ref float rangeSq)
{
queryAgentTreeRecursive(agent, ref rangeSq, 0);
}
/**
* <summary>Computes 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>
*/
internal void computeObstacleNeighbors(Agent agent, float rangeSq)
{
queryObstacleTreeRecursive(agent, rangeSq, obstacleTree_);
}
/**
* <summary>Queries 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>
*/
internal bool queryVisibility(Vector2 q1, Vector2 q2, float radius)
{
return queryVisibilityRecursive(q1, q2, radius, obstacleTree_);
}
internal int queryNearAgent(Vector2 point, float radius)
{
float rangeSq = float.MaxValue;
int agentNo = -1;
queryAgentTreeRecursive(point, ref rangeSq, ref agentNo, 0);
if (rangeSq < radius*radius)
return agentNo;
return -1;
}
/**
* <summary>Recursive method for building an agent k-D tree.</summary>
*
* <param name="begin">The beginning agent k-D tree node node index.
* </param>
* <param name="end">The ending agent k-D tree node index.</param>
* <param name="node">The current agent k-D tree node index.</param>
*/
private void buildAgentTreeRecursive(int begin, int end, int node)
{
agentTree_[node].begin_ = begin;
agentTree_[node].end_ = end;
agentTree_[node].minX_ = agentTree_[node].maxX_ = agents_[begin].position_.x_;
agentTree_[node].minY_ = agentTree_[node].maxY_ = agents_[begin].position_.y_;
for (int i = begin + 1; i < end; ++i)
{
agentTree_[node].maxX_ = Math.Max(agentTree_[node].maxX_, agents_[i].position_.x_);
agentTree_[node].minX_ = Math.Min(agentTree_[node].minX_, agents_[i].position_.x_);
agentTree_[node].maxY_ = Math.Max(agentTree_[node].maxY_, agents_[i].position_.y_);
agentTree_[node].minY_ = Math.Min(agentTree_[node].minY_, agents_[i].position_.y_);
}
if (end - begin > MAX_LEAF_SIZE)
{
/* No leaf node. */
bool isVertical = agentTree_[node].maxX_ - agentTree_[node].minX_ > agentTree_[node].maxY_ - agentTree_[node].minY_;
float splitValue = 0.5f * (isVertical ? agentTree_[node].maxX_ + agentTree_[node].minX_ : agentTree_[node].maxY_ + agentTree_[node].minY_);
int left = begin;
int right = end;
while (left < right)
{
while (left < right && (isVertical ? agents_[left].position_.x_ : agents_[left].position_.y_) < splitValue)
{
++left;
}
while (right > left && (isVertical ? agents_[right - 1].position_.x_ : agents_[right - 1].position_.y_) >= splitValue)
{
--right;
}
if (left < right)
{
Agent tempAgent = agents_[left];
agents_[left] = agents_[right - 1];
agents_[right - 1] = tempAgent;
++left;
--right;
}
}
int leftSize = left - begin;
if (leftSize == 0)
{
++leftSize;
++left;
++right;
}
agentTree_[node].left_ = node + 1;
agentTree_[node].right_ = node + 2 * leftSize;
buildAgentTreeRecursive(begin, left, agentTree_[node].left_);
buildAgentTreeRecursive(left, end, agentTree_[node].right_);
}
}
/**
* <summary>Recursive method for building an obstacle k-D tree.
* </summary>
*
* <returns>An obstacle k-D tree node.</returns>
*
* <param name="obstacles">A list of obstacles.</param>
*/
private ObstacleTreeNode buildObstacleTreeRecursive(Simulator simulator,IList<Obstacle> obstacles)
{
if (obstacles.Count == 0)
{
return null;
}
ObstacleTreeNode node = new ObstacleTreeNode();
int optimalSplit = 0;
int minLeft = obstacles.Count;
int minRight = obstacles.Count;
for (int i = 0; i < obstacles.Count; ++i)
{
int leftSize = 0;
int rightSize = 0;
Obstacle obstacleI1 = obstacles[i];
Obstacle obstacleI2 = obstacleI1.next_;
/* Compute optimal split node. */
for (int j = 0; j < obstacles.Count; ++j)
{
if (i == j)
{
continue;
}
Obstacle obstacleJ1 = obstacles[j];
Obstacle obstacleJ2 = obstacleJ1.next_;
float j1LeftOfI = RVOMath.leftOf(obstacleI1.point_, obstacleI2.point_, obstacleJ1.point_);
float j2LeftOfI = RVOMath.leftOf(obstacleI1.point_, obstacleI2.point_, obstacleJ2.point_);
if (j1LeftOfI >= -RVOMath.RVO_EPSILON && j2LeftOfI >= -RVOMath.RVO_EPSILON)
{
++leftSize;
}
else if (j1LeftOfI <= RVOMath.RVO_EPSILON && j2LeftOfI <= RVOMath.RVO_EPSILON)
{
++rightSize;
}
else
{
++leftSize;
++rightSize;
}
if (new FloatPair(Math.Max(leftSize, rightSize), Math.Min(leftSize, rightSize)) >= new FloatPair(Math.Max(minLeft, minRight), Math.Min(minLeft, minRight)))
{
break;
}
}
if (new FloatPair(Math.Max(leftSize, rightSize), Math.Min(leftSize, rightSize)) < new FloatPair(Math.Max(minLeft, minRight), Math.Min(minLeft, minRight)))
{
minLeft = leftSize;
minRight = rightSize;
optimalSplit = i;
}
}
{
/* Build split node. */
IList<Obstacle> leftObstacles = new List<Obstacle>(minLeft);
for (int n = 0; n < minLeft; ++n)
{
leftObstacles.Add(null);
}
IList<Obstacle> rightObstacles = new List<Obstacle>(minRight);
for (int n = 0; n < minRight; ++n)
{
rightObstacles.Add(null);
}
int leftCounter = 0;
int rightCounter = 0;
int i = optimalSplit;
Obstacle obstacleI1 = obstacles[i];
Obstacle obstacleI2 = obstacleI1.next_;
for (int j = 0; j < obstacles.Count; ++j)
{
if (i == j)
{
continue;
}
Obstacle obstacleJ1 = obstacles[j];
Obstacle obstacleJ2 = obstacleJ1.next_;
float j1LeftOfI = RVOMath.leftOf(obstacleI1.point_, obstacleI2.point_, obstacleJ1.point_);
float j2LeftOfI = RVOMath.leftOf(obstacleI1.point_, obstacleI2.point_, obstacleJ2.point_);
if (j1LeftOfI >= -RVOMath.RVO_EPSILON && j2LeftOfI >= -RVOMath.RVO_EPSILON)
{
leftObstacles[leftCounter++] = obstacles[j];
}
else if (j1LeftOfI <= RVOMath.RVO_EPSILON && j2LeftOfI <= RVOMath.RVO_EPSILON)
{
rightObstacles[rightCounter++] = obstacles[j];
}
else
{
/* Split obstacle j. */
float t = RVOMath.det(obstacleI2.point_ - obstacleI1.point_, obstacleJ1.point_ - obstacleI1.point_) / RVOMath.det(obstacleI2.point_ - obstacleI1.point_, obstacleJ1.point_ - obstacleJ2.point_);
Vector2 splitPoint = obstacleJ1.point_ + t * (obstacleJ2.point_ - obstacleJ1.point_);
Obstacle newObstacle = new Obstacle();
newObstacle.point_ = splitPoint;
newObstacle.previous_ = obstacleJ1;
newObstacle.next_ = obstacleJ2;
newObstacle.convex_ = true;
newObstacle.direction_ = obstacleJ1.direction_;
newObstacle.id_ = simulator.obstacles_.Count;
simulator.obstacles_.Add(newObstacle);
obstacleJ1.next_ = newObstacle;
obstacleJ2.previous_ = newObstacle;
if (j1LeftOfI > 0.0f)
{
leftObstacles[leftCounter++] = obstacleJ1;
rightObstacles[rightCounter++] = newObstacle;
}
else
{
rightObstacles[rightCounter++] = obstacleJ1;
leftObstacles[leftCounter++] = newObstacle;
}
}
}
node.obstacle_ = obstacleI1;
node.left_ = buildObstacleTreeRecursive(simulator,leftObstacles);
node.right_ = buildObstacleTreeRecursive(simulator,rightObstacles);
return node;
}
}
private void queryAgentTreeRecursive(Vector2 position, ref float 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)
{
float distSq = RVOMath.absSq(position - agents_[i].position_);
if (distSq < rangeSq)
{
rangeSq = distSq;
agentNo = agents_[i].id_;
}
}
}
else
{
float distSqLeft = RVOMath.sqr(Math.Max(0.0f, agentTree_[agentTree_[node].left_].minX_ - position.x_)) + RVOMath.sqr(Math.Max(0.0f, position.x_ - agentTree_[agentTree_[node].left_].maxX_)) + RVOMath.sqr(Math.Max(0.0f, agentTree_[agentTree_[node].left_].minY_ - position.y_)) + RVOMath.sqr(Math.Max(0.0f, position.y_ - agentTree_[agentTree_[node].left_].maxY_));
float distSqRight = RVOMath.sqr(Math.Max(0.0f, agentTree_[agentTree_[node].right_].minX_ - position.x_)) + RVOMath.sqr(Math.Max(0.0f, position.x_ - agentTree_[agentTree_[node].right_].maxX_)) + RVOMath.sqr(Math.Max(0.0f, agentTree_[agentTree_[node].right_].minY_ - position.y_)) + RVOMath.sqr(Math.Max(0.0f, position.y_ - 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>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>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, float rangeSq, ObstacleTreeNode node)
{
if (node != null)
{
Obstacle obstacle1 = node.obstacle_;
Obstacle obstacle2 = obstacle1.next_;
float agentLeftOfLine = RVOMath.leftOf(obstacle1.point_, obstacle2.point_, agent.position_);
queryObstacleTreeRecursive(agent, rangeSq, agentLeftOfLine >= 0.0f ? node.left_ : node.right_);
float distSqLine = RVOMath.sqr(agentLeftOfLine) / RVOMath.absSq(obstacle2.point_ - obstacle1.point_);
if (distSqLine < rangeSq)
{
if (agentLeftOfLine < 0.0f)
{
/*
* 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.0f ? node.right_ : 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(Vector2 q1, Vector2 q2, float radius, ObstacleTreeNode node)
{
if (node == null)
{
return true;
}
Obstacle obstacle1 = node.obstacle_;
Obstacle obstacle2 = obstacle1.next_;
float q1LeftOfI = RVOMath.leftOf(obstacle1.point_, obstacle2.point_, q1);
float q2LeftOfI = RVOMath.leftOf(obstacle1.point_, obstacle2.point_, q2);
float invLengthI = 1.0f / RVOMath.absSq(obstacle2.point_ - obstacle1.point_);
if (q1LeftOfI >= 0.0f && q2LeftOfI >= 0.0f)
{
return queryVisibilityRecursive(q1, q2, radius, node.left_) && ((RVOMath.sqr(q1LeftOfI) * invLengthI >= RVOMath.sqr(radius) && RVOMath.sqr(q2LeftOfI) * invLengthI >= RVOMath.sqr(radius)) || queryVisibilityRecursive(q1, q2, radius, node.right_));
}
if (q1LeftOfI <= 0.0f && q2LeftOfI <= 0.0f)
{
return queryVisibilityRecursive(q1, q2, radius, node.right_) && ((RVOMath.sqr(q1LeftOfI) * invLengthI >= RVOMath.sqr(radius) && RVOMath.sqr(q2LeftOfI) * invLengthI >= RVOMath.sqr(radius)) || queryVisibilityRecursive(q1, q2, radius, node.left_));
}
if (q1LeftOfI >= 0.0f && q2LeftOfI <= 0.0f)
{
/* One can see through obstacle from left to right. */
return queryVisibilityRecursive(q1, q2, radius, node.left_) && queryVisibilityRecursive(q1, q2, radius, node.right_);
}
float point1LeftOfQ = RVOMath.leftOf(q1, q2, obstacle1.point_);
float point2LeftOfQ = RVOMath.leftOf(q1, q2, obstacle2.point_);
float invLengthQ = 1.0f / RVOMath.absSq(q2 - q1);
return point1LeftOfQ * point2LeftOfQ >= 0.0f && RVOMath.sqr(point1LeftOfQ) * invLengthQ > RVOMath.sqr(radius) && RVOMath.sqr(point2LeftOfQ) * invLengthQ > RVOMath.sqr(radius) && queryVisibilityRecursive(q1, q2, radius, node.left_) && queryVisibilityRecursive(q1, q2, radius, node.right_);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 843e733cda984e1f9609e15ea17aae14
timeCreated: 1708509745

View File

@@ -0,0 +1,43 @@
/*
* Line.cs
* RVO2 Library C#
*
* Copyright 2008 University of North Carolina at Chapel Hill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Please send all bug reports to <geom@cs.unc.edu>.
*
* The authors may be contacted via:
*
* Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
* Dept. of Computer Science
* 201 S. Columbia St.
* Frederick P. Brooks, Jr. Computer Science Bldg.
* Chapel Hill, N.C. 27599-3175
* United States of America
*
* <http://gamma.cs.unc.edu/RVO2/>
*/
namespace RVO
{
/**
* <summary>Defines a directed line.</summary>
*/
public struct Line
{
public Vector2 direction;
public Vector2 point;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 561d472d76e84e58bf85b3a4500de88e
timeCreated: 1708509745

View File

@@ -0,0 +1,48 @@
/*
* Obstacle.cs
* RVO2 Library C#
*
* Copyright 2008 University of North Carolina at Chapel Hill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Please send all bug reports to <geom@cs.unc.edu>.
*
* The authors may be contacted via:
*
* Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
* Dept. of Computer Science
* 201 S. Columbia St.
* Frederick P. Brooks, Jr. Computer Science Bldg.
* Chapel Hill, N.C. 27599-3175
* United States of America
*
* <http://gamma.cs.unc.edu/RVO2/>
*/
namespace RVO
{
/**
* <summary>Defines static obstacles in the simulation.</summary>
*/
internal class Obstacle
{
internal Obstacle next_;
internal Obstacle previous_;
internal Vector2 direction_;
internal Vector2 point_;
internal int id_;
internal bool convex_;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5b53ac8ae25f4c2b9045a0e4d598eda3
timeCreated: 1708509745

View File

@@ -0,0 +1,192 @@
/*
* RVOMath.cs
* RVO2 Library C#
*
* Copyright 2008 University of North Carolina at Chapel Hill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Please send all bug reports to <geom@cs.unc.edu>.
*
* The authors may be contacted via:
*
* Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
* Dept. of Computer Science
* 201 S. Columbia St.
* Frederick P. Brooks, Jr. Computer Science Bldg.
* Chapel Hill, N.C. 27599-3175
* United States of America
*
* <http://gamma.cs.unc.edu/RVO2/>
*/
using System;
namespace RVO
{
/**
* <summary>Contains functions and constants used in multiple classes.
* </summary>
*/
public struct RVOMath
{
/**
* <summary>A sufficiently small positive number.</summary>
*/
internal const float RVO_EPSILON = 0.00001f;
/**
* <summary>Computes the length of a specified two-dimensional vector.
* </summary>
*
* <param name="vector">The two-dimensional vector whose length is to be
* computed.</param>
* <returns>The length of the two-dimensional vector.</returns>
*/
public static float abs(Vector2 vector)
{
return sqrt(absSq(vector));
}
/**
* <summary>Computes the squared length of a specified two-dimensional
* vector.</summary>
*
* <returns>The squared length of the two-dimensional vector.</returns>
*
* <param name="vector">The two-dimensional vector whose squared length
* is to be computed.</param>
*/
public static float absSq(Vector2 vector)
{
return vector * vector;
}
/**
* <summary>Computes the normalization of the specified two-dimensional
* vector.</summary>
*
* <returns>The normalization of the two-dimensional vector.</returns>
*
* <param name="vector">The two-dimensional vector whose normalization
* is to be computed.</param>
*/
public static Vector2 normalize(Vector2 vector)
{
return vector / abs(vector);
}
/**
* <summary>Computes the determinant of a two-dimensional square matrix
* with rows consisting of the specified two-dimensional vectors.
* </summary>
*
* <returns>The determinant of the two-dimensional square matrix.
* </returns>
*
* <param name="vector1">The top row of the two-dimensional square
* matrix.</param>
* <param name="vector2">The bottom row of the two-dimensional square
* matrix.</param>
*/
internal static float det(Vector2 vector1, Vector2 vector2)
{
return vector1.x_ * vector2.y_ - vector1.y_ * vector2.x_;
}
/**
* <summary>Computes the squared distance from a line segment with the
* specified endpoints to a specified point.</summary>
*
* <returns>The squared distance from the line segment to the point.
* </returns>
*
* <param name="vector1">The first endpoint of the line segment.</param>
* <param name="vector2">The second endpoint of the line segment.
* </param>
* <param name="vector3">The point to which the squared distance is to
* be calculated.</param>
*/
internal static float distSqPointLineSegment(Vector2 vector1, Vector2 vector2, Vector2 vector3)
{
float r = ((vector3 - vector1) * (vector2 - vector1)) / absSq(vector2 - vector1);
if (r < 0.0f)
{
return absSq(vector3 - vector1);
}
if (r > 1.0f)
{
return absSq(vector3 - vector2);
}
return absSq(vector3 - (vector1 + r * (vector2 - vector1)));
}
/**
* <summary>Computes the absolute value of a float.</summary>
*
* <returns>The absolute value of the float.</returns>
*
* <param name="scalar">The float of which to compute the absolute
* value.</param>
*/
internal static float fabs(float scalar)
{
return Math.Abs(scalar);
}
/**
* <summary>Computes the signed distance from a line connecting the
* specified points to a specified point.</summary>
*
* <returns>Positive when the point c lies to the left of the line ab.
* </returns>
*
* <param name="a">The first point on the line.</param>
* <param name="b">The second point on the line.</param>
* <param name="c">The point to which the signed distance is to be
* calculated.</param>
*/
internal static float leftOf(Vector2 a, Vector2 b, Vector2 c)
{
return det(a - c, b - a);
}
/**
* <summary>Computes the square of a float.</summary>
*
* <returns>The square of the float.</returns>
*
* <param name="scalar">The float to be squared.</param>
*/
internal static float sqr(float scalar)
{
return scalar * scalar;
}
/**
* <summary>Computes the square root of a float.</summary>
*
* <returns>The square root of the float.</returns>
*
* <param name="scalar">The float of which to compute the square root.
* </param>
*/
internal static float sqrt(float scalar)
{
return (float)Math.Sqrt(scalar);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bd22ac7d066645e1bd1e8b9ca83a11a5
timeCreated: 1708509745

View File

@@ -0,0 +1,936 @@
/*
* Simulator.cs
* RVO2 Library C#
*
* Copyright 2008 University of North Carolina at Chapel Hill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Please send all bug reports to <geom@cs.unc.edu>.
*
* The authors may be contacted via:
*
* Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
* Dept. of Computer Science
* 201 S. Columbia St.
* Frederick P. Brooks, Jr. Computer Science Bldg.
* Chapel Hill, N.C. 27599-3175
* United States of America
*
* <http://gamma.cs.unc.edu/RVO2/>
*/
using System;
using System.Collections.Generic;
using System.Threading;
namespace RVO
{
/**
* <summary>Defines the simulation.</summary>
*/
public class Simulator
{
/**
* <summary>Defines a worker.</summary>
*/
private class Worker
{
private ManualResetEvent doneEvent_;
private int end_;
private int start_;
/**
* <summary>Constructs and initializes a worker.</summary>
*
* <param name="start">Start.</param>
* <param name="end">End.</param>
* <param name="doneEvent">Done event.</param>
*/
internal Worker(int start, int end, ManualResetEvent doneEvent)
{
start_ = start;
end_ = end;
doneEvent_ = doneEvent;
}
internal void config(int start, int end)
{
start_ = start;
end_ = end;
}
/**
* <summary>Performs a simulation step.</summary>
*
* <param name="obj">Unused.</param>
*/
internal void step(Simulator simulator)
{
for (int index = start_; index < end_; ++index)
{
simulator.agents_[index].computeNeighbors(simulator);
simulator.agents_[index].computeNewVelocity(simulator);
}
doneEvent_.Set();
}
/**
* <summary>updates the two-dimensional position and
* two-dimensional velocity of each agent.</summary>
*
* <param name="obj">Unused.</param>
*/
internal void update(Simulator simulator)
{
for (int index = start_; index < end_; ++index)
{
simulator.agents_[index].update(simulator);
}
doneEvent_.Set();
}
}
internal IDictionary<int, int> agentNo2indexDict_;
internal IDictionary<int, int> index2agentNoDict_;
internal IList<Agent> agents_;
internal IList<Obstacle> obstacles_;
internal KdTree kdTree_;
internal float timeStep_;
private Agent defaultAgent_;
private ManualResetEvent[] doneEvents_;
private Worker[] workers_;
private int numWorkers_;
private int workerAgentCount_;
private float globalTime_;
public void delAgent(int agentNo)
{
agents_[agentNo2indexDict_[agentNo]].needDelete_ = true;
}
void updateDeleteAgent()
{
bool isDelete = false;
for (int i = agents_.Count - 1; i >= 0; i--)
{
if (agents_[i].needDelete_)
{
agents_.RemoveAt(i);
isDelete = true;
}
}
if (isDelete)
onDelAgent();
}
static int s_totalID = 0;
/**
* <summary>Adds a new agent with default properties to the simulation.
* </summary>
*
* <returns>The number of the agent, or -1 when the agent defaults have
* not been set.</returns>
*
* <param name="position">The two-dimensional starting position of this
* agent.</param>
*/
public int addAgent(Vector2 position)
{
if (defaultAgent_ == null)
{
return -1;
}
Agent agent = new Agent();
agent.id_ = s_totalID;
s_totalID++;
agent.maxNeighbors_ = defaultAgent_.maxNeighbors_;
agent.maxSpeed_ = defaultAgent_.maxSpeed_;
agent.neighborDist_ = defaultAgent_.neighborDist_;
agent.position_ = position;
agent.radius_ = defaultAgent_.radius_;
agent.timeHorizon_ = defaultAgent_.timeHorizon_;
agent.timeHorizonObst_ = defaultAgent_.timeHorizonObst_;
agent.velocity_ = defaultAgent_.velocity_;
agents_.Add(agent);
onAddAgent();
return agent.id_;
}
void onDelAgent()
{
agentNo2indexDict_.Clear();
index2agentNoDict_.Clear();
for (int i = 0; i < agents_.Count; i++)
{
int agentNo = agents_[i].id_;
agentNo2indexDict_.Add(agentNo, i);
index2agentNoDict_.Add(i, agentNo);
}
}
void onAddAgent()
{
if (agents_.Count == 0)
return;
int index = agents_.Count - 1;
int agentNo = agents_[index].id_;
agentNo2indexDict_.Add(agentNo, index);
index2agentNoDict_.Add(index, agentNo);
}
/**
* <summary>Adds a new agent to the simulation.</summary>
*
* <returns>The number of the agent.</returns>
*
* <param name="position">The two-dimensional starting position of this
* agent.</param>
* <param name="neighborDist">The maximum distance (center point to
* center point) to other agents this agent takes into account in the
* navigation. The larger this number, the longer the running time of
* the simulation. If the number is too low, the simulation will not be
* safe. Must be non-negative.</param>
* <param name="maxNeighbors">The maximum number of other agents this
* agent takes into account in the navigation. The larger this number,
* the longer the running time of the simulation. If the number is too
* low, the simulation will not be safe.</param>
* <param name="timeHorizon">The minimal amount of time for which this
* agent's velocities that are computed by the simulation are safe with
* respect to other agents. The larger this number, the sooner this
* agent will respond to the presence of other agents, but the less
* freedom this agent has in choosing its velocities. Must be positive.
* </param>
* <param name="timeHorizonObst">The minimal amount of time for which
* this agent's velocities that are computed by the simulation are safe
* with respect to obstacles. The larger this number, the sooner this
* agent will respond to the presence of obstacles, but the less freedom
* this agent has in choosing its velocities. Must be positive.</param>
* <param name="radius">The radius of this agent. Must be non-negative.
* </param>
* <param name="maxSpeed">The maximum speed of this agent. Must be
* non-negative.</param>
* <param name="velocity">The initial two-dimensional linear velocity of
* this agent.</param>
*/
public int addAgent(Vector2 position, float neighborDist, int maxNeighbors, float timeHorizon, float timeHorizonObst, float radius, float maxSpeed, Vector2 velocity)
{
Agent agent = new Agent();
agent.id_ = s_totalID;
s_totalID++;
agent.maxNeighbors_ = maxNeighbors;
agent.maxSpeed_ = maxSpeed;
agent.neighborDist_ = neighborDist;
agent.position_ = position;
agent.radius_ = radius;
agent.timeHorizon_ = timeHorizon;
agent.timeHorizonObst_ = timeHorizonObst;
agent.velocity_ = velocity;
agents_.Add(agent);
onAddAgent();
return agent.id_;
}
/**
* <summary>Adds a new obstacle to the simulation.</summary>
*
* <returns>The number of the first vertex of the obstacle, or -1 when
* the number of vertices is less than two.</returns>
*
* <param name="vertices">List of the vertices of the polygonal obstacle
* in counterclockwise order.</param>
*
* <remarks>To add a "negative" obstacle, e.g. a bounding polygon around
* the environment, the vertices should be listed in clockwise order.
* </remarks>
*/
public int addObstacle(IList<Vector2> vertices)
{
if (vertices.Count < 2)
{
return -1;
}
int obstacleNo = obstacles_.Count;
for (int i = 0; i < vertices.Count; ++i)
{
Obstacle obstacle = new Obstacle();
obstacle.point_ = vertices[i];
if (i != 0)
{
obstacle.previous_ = obstacles_[obstacles_.Count - 1];
obstacle.previous_.next_ = obstacle;
}
if (i == vertices.Count - 1)
{
obstacle.next_ = obstacles_[obstacleNo];
obstacle.next_.previous_ = obstacle;
}
obstacle.direction_ = RVOMath.normalize(vertices[(i == vertices.Count - 1 ? 0 : i + 1)] - vertices[i]);
if (vertices.Count == 2)
{
obstacle.convex_ = true;
}
else
{
obstacle.convex_ = (RVOMath.leftOf(vertices[(i == 0 ? vertices.Count - 1 : i - 1)], vertices[i], vertices[(i == vertices.Count - 1 ? 0 : i + 1)]) >= 0.0f);
}
obstacle.id_ = obstacles_.Count;
obstacles_.Add(obstacle);
}
return obstacleNo;
}
/**
* <summary>Clears the simulation.</summary>
*/
public void Clear()
{
agents_ = new List<Agent>();
agentNo2indexDict_ = new Dictionary<int, int>();
index2agentNoDict_ = new Dictionary<int, int>();
defaultAgent_ = null;
kdTree_ = new KdTree();
obstacles_ = new List<Obstacle>();
globalTime_ = 0.0f;
timeStep_ = 0.1f;
SetNumWorkers(0);
}
/**
* <summary>Performs a simulation step and updates the two-dimensional
* position and two-dimensional velocity of each agent.</summary>
*
* <returns>The global time after the simulation step.</returns>
*/
public float doStep()
{
updateDeleteAgent();
if (workers_ == null)
{
workers_ = new Worker[numWorkers_];
doneEvents_ = new ManualResetEvent[workers_.Length];
workerAgentCount_ = getNumAgents();
for (int block = 0; block < workers_.Length; ++block)
{
doneEvents_[block] = new ManualResetEvent(false);
workers_[block] = new Worker(block * getNumAgents() / workers_.Length, (block + 1) * getNumAgents() / workers_.Length, doneEvents_[block]);
}
}
if (workerAgentCount_ != getNumAgents())
{
workerAgentCount_ = getNumAgents();
for (int block = 0; block < workers_.Length; ++block)
{
workers_[block].config(block * getNumAgents() / workers_.Length, (block + 1) * getNumAgents() / workers_.Length);
}
}
kdTree_.buildAgentTree(this);
for (int block = 0; block < workers_.Length; ++block)
{
workers_[block].step(this);
}
for (int block = 0; block < workers_.Length; ++block)
{
doneEvents_[block].Reset();
workers_[block].update(this);
}
globalTime_ += timeStep_;
return globalTime_;
}
/**
* <summary>Returns the specified agent neighbor of the specified agent.
* </summary>
*
* <returns>The number of the neighboring agent.</returns>
*
* <param name="agentNo">The number of the agent whose agent neighbor is
* to be retrieved.</param>
* <param name="neighborNo">The number of the agent neighbor to be
* retrieved.</param>
*/
public int getAgentAgentNeighbor(int agentNo, int neighborNo)
{
return agents_[agentNo2indexDict_[agentNo]].agentNeighbors_[neighborNo].Value.id_;
}
/**
* <summary>Returns the maximum neighbor count of a specified agent.
* </summary>
*
* <returns>The present maximum neighbor count of the agent.</returns>
*
* <param name="agentNo">The number of the agent whose maximum neighbor
* count is to be retrieved.</param>
*/
public int getAgentMaxNeighbors(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].maxNeighbors_;
}
/**
* <summary>Returns the maximum speed of a specified agent.</summary>
*
* <returns>The present maximum speed of the agent.</returns>
*
* <param name="agentNo">The number of the agent whose maximum speed is
* to be retrieved.</param>
*/
public float getAgentMaxSpeed(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].maxSpeed_;
}
/**
* <summary>Returns the maximum neighbor distance of a specified agent.
* </summary>
*
* <returns>The present maximum neighbor distance of the agent.
* </returns>
*
* <param name="agentNo">The number of the agent whose maximum neighbor
* distance is to be retrieved.</param>
*/
public float getAgentNeighborDist(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].neighborDist_;
}
/**
* <summary>Returns the count of agent neighbors taken into account to
* compute the current velocity for the specified agent.</summary>
*
* <returns>The count of agent neighbors taken into account to compute
* the current velocity for the specified agent.</returns>
*
* <param name="agentNo">The number of the agent whose count of agent
* neighbors is to be retrieved.</param>
*/
public int getAgentNumAgentNeighbors(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].agentNeighbors_.Count;
}
/**
* <summary>Returns the count of obstacle neighbors taken into account
* to compute the current velocity for the specified agent.</summary>
*
* <returns>The count of obstacle neighbors taken into account to
* compute the current velocity for the specified agent.</returns>
*
* <param name="agentNo">The number of the agent whose count of obstacle
* neighbors is to be retrieved.</param>
*/
public int getAgentNumObstacleNeighbors(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].obstacleNeighbors_.Count;
}
/**
* <summary>Returns the specified obstacle neighbor of the specified
* agent.</summary>
*
* <returns>The number of the first vertex of the neighboring obstacle
* edge.</returns>
*
* <param name="agentNo">The number of the agent whose obstacle neighbor
* is to be retrieved.</param>
* <param name="neighborNo">The number of the obstacle neighbor to be
* retrieved.</param>
*/
public int getAgentObstacleNeighbor(int agentNo, int neighborNo)
{
return agents_[agentNo2indexDict_[agentNo]].obstacleNeighbors_[neighborNo].Value.id_;
}
/**
* <summary>Returns the ORCA constraints of the specified agent.
* </summary>
*
* <returns>A list of lines representing the ORCA constraints.</returns>
*
* <param name="agentNo">The number of the agent whose ORCA constraints
* are to be retrieved.</param>
*
* <remarks>The halfplane to the left of each line is the region of
* permissible velocities with respect to that ORCA constraint.
* </remarks>
*/
public IList<Line> getAgentOrcaLines(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].orcaLines_;
}
/**
* <summary>Returns the two-dimensional position of a specified agent.
* </summary>
*
* <returns>The present two-dimensional position of the (center of the)
* agent.</returns>
*
* <param name="agentNo">The number of the agent whose two-dimensional
* position is to be retrieved.</param>
*/
public Vector2 getAgentPosition(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].position_;
}
/**
* <summary>Returns the two-dimensional preferred velocity of a
* specified agent.</summary>
*
* <returns>The present two-dimensional preferred velocity of the agent.
* </returns>
*
* <param name="agentNo">The number of the agent whose two-dimensional
* preferred velocity is to be retrieved.</param>
*/
public Vector2 getAgentPrefVelocity(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].prefVelocity_;
}
/**
* <summary>Returns the radius of a specified agent.</summary>
*
* <returns>The present radius of the agent.</returns>
*
* <param name="agentNo">The number of the agent whose radius is to be
* retrieved.</param>
*/
public float getAgentRadius(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].radius_;
}
/**
* <summary>Returns the time horizon of a specified agent.</summary>
*
* <returns>The present time horizon of the agent.</returns>
*
* <param name="agentNo">The number of the agent whose time horizon is
* to be retrieved.</param>
*/
public float getAgentTimeHorizon(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].timeHorizon_;
}
/**
* <summary>Returns the time horizon with respect to obstacles of a
* specified agent.</summary>
*
* <returns>The present time horizon with respect to obstacles of the
* agent.</returns>
*
* <param name="agentNo">The number of the agent whose time horizon with
* respect to obstacles is to be retrieved.</param>
*/
public float getAgentTimeHorizonObst(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].timeHorizonObst_;
}
/**
* <summary>Returns the two-dimensional linear velocity of a specified
* agent.</summary>
*
* <returns>The present two-dimensional linear velocity of the agent.
* </returns>
*
* <param name="agentNo">The number of the agent whose two-dimensional
* linear velocity is to be retrieved.</param>
*/
public Vector2 getAgentVelocity(int agentNo)
{
return agents_[agentNo2indexDict_[agentNo]].velocity_;
}
/**
* <summary>Returns the global time of the simulation.</summary>
*
* <returns>The present global time of the simulation (zero initially).
* </returns>
*/
public float getGlobalTime()
{
return globalTime_;
}
/**
* <summary>Returns the count of agents in the simulation.</summary>
*
* <returns>The count of agents in the simulation.</returns>
*/
public int getNumAgents()
{
return agents_.Count;
}
/**
* <summary>Returns the count of obstacle vertices in the simulation.
* </summary>
*
* <returns>The count of obstacle vertices in the simulation.</returns>
*/
public int getNumObstacleVertices()
{
return obstacles_.Count;
}
/**
* <summary>Returns the count of workers.</summary>
*
* <returns>The count of workers.</returns>
*/
public int GetNumWorkers()
{
return numWorkers_;
}
/**
* <summary>Returns the two-dimensional position of a specified obstacle
* vertex.</summary>
*
* <returns>The two-dimensional position of the specified obstacle
* vertex.</returns>
*
* <param name="vertexNo">The number of the obstacle vertex to be
* retrieved.</param>
*/
public Vector2 getObstacleVertex(int vertexNo)
{
return obstacles_[vertexNo].point_;
}
/**
* <summary>Returns the number of the obstacle vertex succeeding the
* specified obstacle vertex in its polygon.</summary>
*
* <returns>The number of the obstacle vertex succeeding the specified
* obstacle vertex in its polygon.</returns>
*
* <param name="vertexNo">The number of the obstacle vertex whose
* successor is to be retrieved.</param>
*/
public int getNextObstacleVertexNo(int vertexNo)
{
return obstacles_[vertexNo].next_.id_;
}
/**
* <summary>Returns the number of the obstacle vertex preceding the
* specified obstacle vertex in its polygon.</summary>
*
* <returns>The number of the obstacle vertex preceding the specified
* obstacle vertex in its polygon.</returns>
*
* <param name="vertexNo">The number of the obstacle vertex whose
* predecessor is to be retrieved.</param>
*/
public int getPrevObstacleVertexNo(int vertexNo)
{
return obstacles_[vertexNo].previous_.id_;
}
/**
* <summary>Returns the time step of the simulation.</summary>
*
* <returns>The present time step of the simulation.</returns>
*/
public float getTimeStep()
{
return timeStep_;
}
/**
* <summary>Processes the obstacles that have been added so that they
* are accounted for in the simulation.</summary>
*
* <remarks>Obstacles added to the simulation after this function has
* been called are not accounted for in the simulation.</remarks>
*/
public void processObstacles()
{
kdTree_.buildObstacleTree(this);
}
/**
* <summary>Performs a visibility query between the two specified points
* with respect to the obstacles.</summary>
*
* <returns>A boolean specifying whether the two points are mutually
* visible. Returns true when the obstacles have not been processed.
* </returns>
*
* <param name="point1">The first point of the query.</param>
* <param name="point2">The second point of the query.</param>
* <param name="radius">The minimal distance between the line connecting
* the two points and the obstacles in order for the points to be
* mutually visible (optional). Must be non-negative.</param>
*/
public bool queryVisibility(Vector2 point1, Vector2 point2, float radius)
{
return kdTree_.queryVisibility(point1, point2, radius);
}
public int queryNearAgent(Vector2 point, float radius)
{
if (getNumAgents() == 0)
return -1;
return kdTree_.queryNearAgent(point, radius);
}
/**
* <summary>Sets the default properties for any new agent that is added.
* </summary>
*
* <param name="neighborDist">The default maximum distance (center point
* to center point) to other agents a new agent takes into account in
* the navigation. The larger this number, the longer he running time of
* the simulation. If the number is too low, the simulation will not be
* safe. Must be non-negative.</param>
* <param name="maxNeighbors">The default maximum number of other agents
* a new agent takes into account in the navigation. The larger this
* number, the longer the running time of the simulation. If the number
* is too low, the simulation will not be safe.</param>
* <param name="timeHorizon">The default minimal amount of time for
* which a new agent's velocities that are computed by the simulation
* are safe with respect to other agents. The larger this number, the
* sooner an agent will respond to the presence of other agents, but the
* less freedom the agent has in choosing its velocities. Must be
* positive.</param>
* <param name="timeHorizonObst">The default minimal amount of time for
* which a new agent's velocities that are computed by the simulation
* are safe with respect to obstacles. The larger this number, the
* sooner an agent will respond to the presence of obstacles, but the
* less freedom the agent has in choosing its velocities. Must be
* positive.</param>
* <param name="radius">The default radius of a new agent. Must be
* non-negative.</param>
* <param name="maxSpeed">The default maximum speed of a new agent. Must
* be non-negative.</param>
* <param name="velocity">The default initial two-dimensional linear
* velocity of a new agent.</param>
*/
public void setAgentDefaults(float neighborDist, int maxNeighbors, float timeHorizon, float timeHorizonObst, float radius, float maxSpeed, Vector2 velocity)
{
if (defaultAgent_ == null)
{
defaultAgent_ = new Agent();
}
defaultAgent_.maxNeighbors_ = maxNeighbors;
defaultAgent_.maxSpeed_ = maxSpeed;
defaultAgent_.neighborDist_ = neighborDist;
defaultAgent_.radius_ = radius;
defaultAgent_.timeHorizon_ = timeHorizon;
defaultAgent_.timeHorizonObst_ = timeHorizonObst;
defaultAgent_.velocity_ = velocity;
}
/**
* <summary>Sets the maximum neighbor count of a specified agent.
* </summary>
*
* <param name="agentNo">The number of the agent whose maximum neighbor
* count is to be modified.</param>
* <param name="maxNeighbors">The replacement maximum neighbor count.
* </param>
*/
public void setAgentMaxNeighbors(int agentNo, int maxNeighbors)
{
agents_[agentNo2indexDict_[agentNo]].maxNeighbors_ = maxNeighbors;
}
/**
* <summary>Sets the maximum speed of a specified agent.</summary>
*
* <param name="agentNo">The number of the agent whose maximum speed is
* to be modified.</param>
* <param name="maxSpeed">The replacement maximum speed. Must be
* non-negative.</param>
*/
public void setAgentMaxSpeed(int agentNo, float maxSpeed)
{
agents_[agentNo2indexDict_[agentNo]].maxSpeed_ = maxSpeed;
}
/**
* <summary>Sets the maximum neighbor distance of a specified agent.
* </summary>
*
* <param name="agentNo">The number of the agent whose maximum neighbor
* distance is to be modified.</param>
* <param name="neighborDist">The replacement maximum neighbor distance.
* Must be non-negative.</param>
*/
public void setAgentNeighborDist(int agentNo, float neighborDist)
{
agents_[agentNo2indexDict_[agentNo]].neighborDist_ = neighborDist;
}
/**
* <summary>Sets the two-dimensional position of a specified agent.
* </summary>
*
* <param name="agentNo">The number of the agent whose two-dimensional
* position is to be modified.</param>
* <param name="position">The replacement of the two-dimensional
* position.</param>
*/
public void setAgentPosition(int agentNo, Vector2 position)
{
agents_[agentNo2indexDict_[agentNo]].position_ = position;
}
/**
* <summary>Sets the two-dimensional preferred velocity of a specified
* agent.</summary>
*
* <param name="agentNo">The number of the agent whose two-dimensional
* preferred velocity is to be modified.</param>
* <param name="prefVelocity">The replacement of the two-dimensional
* preferred velocity.</param>
*/
public void setAgentPrefVelocity(int agentNo, Vector2 prefVelocity)
{
agents_[agentNo2indexDict_[agentNo]].prefVelocity_ = prefVelocity;
}
/**
* <summary>Sets the radius of a specified agent.</summary>
*
* <param name="agentNo">The number of the agent whose radius is to be
* modified.</param>
* <param name="radius">The replacement radius. Must be non-negative.
* </param>
*/
public void setAgentRadius(int agentNo, float radius)
{
agents_[agentNo2indexDict_[agentNo]].radius_ = radius;
}
/**
* <summary>Sets the time horizon of a specified agent with respect to
* other agents.</summary>
*
* <param name="agentNo">The number of the agent whose time horizon is
* to be modified.</param>
* <param name="timeHorizon">The replacement time horizon with respect
* to other agents. Must be positive.</param>
*/
public void setAgentTimeHorizon(int agentNo, float timeHorizon)
{
agents_[agentNo2indexDict_[agentNo]].timeHorizon_ = timeHorizon;
}
/**
* <summary>Sets the time horizon of a specified agent with respect to
* obstacles.</summary>
*
* <param name="agentNo">The number of the agent whose time horizon with
* respect to obstacles is to be modified.</param>
* <param name="timeHorizonObst">The replacement time horizon with
* respect to obstacles. Must be positive.</param>
*/
public void setAgentTimeHorizonObst(int agentNo, float timeHorizonObst)
{
agents_[agentNo2indexDict_[agentNo]].timeHorizonObst_ = timeHorizonObst;
}
/**
* <summary>Sets the two-dimensional linear velocity of a specified
* agent.</summary>
*
* <param name="agentNo">The number of the agent whose two-dimensional
* linear velocity is to be modified.</param>
* <param name="velocity">The replacement two-dimensional linear
* velocity.</param>
*/
public void setAgentVelocity(int agentNo, Vector2 velocity)
{
agents_[agentNo2indexDict_[agentNo]].velocity_ = velocity;
}
/**
* <summary>Sets the global time of the simulation.</summary>
*
* <param name="globalTime">The global time of the simulation.</param>
*/
public void setGlobalTime(float globalTime)
{
globalTime_ = globalTime;
}
/**
* <summary>Sets the number of workers.</summary>
*
* <param name="numWorkers">The number of workers.</param>
*/
public void SetNumWorkers(int numWorkers)
{
numWorkers_ = numWorkers;
if (numWorkers_ <= 0)
{
int completionPorts;
ThreadPool.GetMinThreads(out numWorkers_, out completionPorts);
}
workers_ = null;
workerAgentCount_ = 0;
}
/**
* <summary>Sets the time step of the simulation.</summary>
*
* <param name="timeStep">The time step of the simulation. Must be
* positive.</param>
*/
public void setTimeStep(float timeStep)
{
timeStep_ = timeStep;
}
/**
* <summary>Constructs and initializes a simulation.</summary>
*/
protected Simulator()
{
Clear();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bb91eae09aaa4ddd9584412824b4ba8c
timeCreated: 1708509745

View File

@@ -0,0 +1,197 @@
/*
* Vector2.cs
* RVO2 Library C#
*
* Copyright 2008 University of North Carolina at Chapel Hill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Please send all bug reports to <geom@cs.unc.edu>.
*
* The authors may be contacted via:
*
* Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
* Dept. of Computer Science
* 201 S. Columbia St.
* Frederick P. Brooks, Jr. Computer Science Bldg.
* Chapel Hill, N.C. 27599-3175
* United States of America
*
* <http://gamma.cs.unc.edu/RVO2/>
*/
using System;
using System.Globalization;
namespace RVO
{
/**
* <summary>Defines a two-dimensional vector.</summary>
*/
public struct Vector2
{
internal float x_;
internal float y_;
/**
* <summary>Constructs and initializes a two-dimensional vector from the
* specified xy-coordinates.</summary>
*
* <param name="x">The x-coordinate of the two-dimensional vector.
* </param>
* <param name="y">The y-coordinate of the two-dimensional vector.
* </param>
*/
public Vector2(float x, float y)
{
x_ = x;
y_ = y;
}
/**
* <summary>Returns the string representation of this vector.</summary>
*
* <returns>The string representation of this vector.</returns>
*/
public override string ToString()
{
return "(" + x_.ToString(new CultureInfo("").NumberFormat) + "," + y_.ToString(new CultureInfo("").NumberFormat) + ")";
}
/**
* <summary>Returns the x-coordinate of this two-dimensional vector.
* </summary>
*
* <returns>The x-coordinate of the two-dimensional vector.</returns>
*/
public float x()
{
return x_;
}
/**
* <summary>Returns the y-coordinate of this two-dimensional vector.
* </summary>
*
* <returns>The y-coordinate of the two-dimensional vector.</returns>
*/
public float y()
{
return y_;
}
/**
* <summary>Computes the dot product of the two specified
* two-dimensional vectors.</summary>
*
* <returns>The dot product of the two specified two-dimensional
* vectors.</returns>
*
* <param name="vector1">The first two-dimensional vector.</param>
* <param name="vector2">The second two-dimensional vector.</param>
*/
public static float operator *(Vector2 vector1, Vector2 vector2)
{
return vector1.x_ * vector2.x_ + vector1.y_ * vector2.y_;
}
/**
* <summary>Computes the scalar multiplication of the specified
* two-dimensional vector with the specified scalar value.</summary>
*
* <returns>The scalar multiplication of the specified two-dimensional
* vector with the specified scalar value.</returns>
*
* <param name="scalar">The scalar value.</param>
* <param name="vector">The two-dimensional vector.</param>
*/
public static Vector2 operator *(float scalar, Vector2 vector)
{
return vector * scalar;
}
/**
* <summary>Computes the scalar multiplication of the specified
* two-dimensional vector with the specified scalar value.</summary>
*
* <returns>The scalar multiplication of the specified two-dimensional
* vector with the specified scalar value.</returns>
*
* <param name="vector">The two-dimensional vector.</param>
* <param name="scalar">The scalar value.</param>
*/
public static Vector2 operator *(Vector2 vector, float scalar)
{
return new Vector2(vector.x_ * scalar, vector.y_ * scalar);
}
/**
* <summary>Computes the scalar division of the specified
* two-dimensional vector with the specified scalar value.</summary>
*
* <returns>The scalar division of the specified two-dimensional vector
* with the specified scalar value.</returns>
*
* <param name="vector">The two-dimensional vector.</param>
* <param name="scalar">The scalar value.</param>
*/
public static Vector2 operator /(Vector2 vector, float scalar)
{
return new Vector2(vector.x_ / scalar, vector.y_ / scalar);
}
/**
* <summary>Computes the vector sum of the two specified two-dimensional
* vectors.</summary>
*
* <returns>The vector sum of the two specified two-dimensional vectors.
* </returns>
*
* <param name="vector1">The first two-dimensional vector.</param>
* <param name="vector2">The second two-dimensional vector.</param>
*/
public static Vector2 operator +(Vector2 vector1, Vector2 vector2)
{
return new Vector2(vector1.x_ + vector2.x_, vector1.y_ + vector2.y_);
}
/**
* <summary>Computes the vector difference of the two specified
* two-dimensional vectors</summary>
*
* <returns>The vector difference of the two specified two-dimensional
* vectors.</returns>
*
* <param name="vector1">The first two-dimensional vector.</param>
* <param name="vector2">The second two-dimensional vector.</param>
*/
public static Vector2 operator -(Vector2 vector1, Vector2 vector2)
{
return new Vector2(vector1.x_ - vector2.x_, vector1.y_ - vector2.y_);
}
/**
* <summary>Computes the negation of the specified two-dimensional
* vector.</summary>
*
* <returns>The negation of the specified two-dimensional vector.
* </returns>
*
* <param name="vector">The two-dimensional vector.</param>
*/
public static Vector2 operator -(Vector2 vector)
{
return new Vector2(-vector.x_, -vector.y_);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3e3cacbab9ea44c3a3f2778d7d63d56f
timeCreated: 1708509745