diff --git a/rvo2/index.d.ts b/rvo2/index.d.ts new file mode 100644 index 0000000000..88ee40cd96 --- /dev/null +++ b/rvo2/index.d.ts @@ -0,0 +1,581 @@ +// Type definitions for rvo2 +// Project: https://github.com/TNOCS/rvo2 +// Definitions by: Erik Vullings +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare namespace rvo2 { + + class Line { + /** + * A point on the directed line. + */ + point: Vector2; + + /** + * The direction of the directed line. + */ + direction: Vector2; + } + + /** + * The Agent class has no public members or methods. + * + * @class Agent + */ + class Agent { } + + /** + * The KdTree class has no public members or methods. + * + * @class KdTree + */ + class KdTree { } + + /** + * The Obstacle class has no public members or methods. + * + * @class Obstacle + */ + class Obstacle { } + + /** + * A simple two dimensional vector with an x and y position. + * + * @class Vector2 + */ + class Vector2 { + /** + * Creates an instance of Vector2. + * + * @param {number} x + * @param {number} y + * + * @memberOf Vector2 + */ + constructor(x: number, y: number); + + /** + * Returns the x position. + * + * @returns {number} + * + * @memberOf Vector2 + */ + x(): number; + /** + * Returns the y position. + * + * @returns {number} + * + * @memberOf Vector2 + */ + y(): number; + /** + * Add two vectors. + * + * @param {Vector2} v + * @returns {Vector2} + * + * @memberOf Vector2 + */ + add(v: Vector2): Vector2; + /** + * Subtract two vectors. + * + * @param {Vector2} v + * @returns {Vector2} + * + * @memberOf Vector2 + */ + sub(v: Vector2): Vector2; + /** + * Multiplies the vector by a scalar. + * + * @param {number} s + * @returns {Vector2} + * + * @memberOf Vector2 + */ + mul(s: number): Vector2; + } + + /** + * An array of Vector2 vectors. + * + * @class vectorvector + */ + class vectorvector { + constructor(size?: number); + + [index: number]: Vector2; + } + + /** + * Simulation engine. + * + * @class sim + */ + class RVOSimulator { + constructor(); + + /** + * Constructs a simulator instance and sets the default + * properties for any new agent that is added. + * + * @param {number} timeStep The time step of the simulation. + * Must be positive. + * @param {number} 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 {number} 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 {number} 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 {number} 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 {number} radius The default radius of a new agent. + * Must be non-negative. + * @param {number} maxSpeed The default maximum speed of a new agent. + * Must be non-negative. + * @param {Vector2} velocity The default initial two-dimensional linear + * velocity of a new agent (optional). + */ + constructor(timeStep: number, neighborDist: number, maxNeighbors: number, + timeHorizon: number, timeHorizonObst: number, radius: number, + maxSpeed: number, velocity?: Vector2); + + /** + * Sets the time step of the simulation. + * + * @param {number} timeStep The time step of the simulation. + * Must be positive. + * + * @memberOf sim + */ + setTimeStep(timeStep: number): void; + + /** + * Lets the simulator perform a simulation step and updates the + * two-dimensional position and two-dimensional velocity of each agent. + * + * @memberOf RVOSimulator + */ + doStep(): void; + + /** + * Sets the default properties for any new agent that is added. + * @param {number} 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 {number} 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 {number} 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 {number} 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 {number} radius The default radius of a new agent. + * Must be non-negative. + * @param {number} maxSpeed The default maximum speed of a new agent. + * Must be non-negative. + * @param {Vector2} velocity The default initial two-dimensional linear + * velocity of a new agent (optional). + */ + setAgentDefaults(neighborDist: number, maxNeighbors: number, timeHorizon: number, timeHorizonObst: number, radius: number, maxSpeed: number, velocity?: Vector2): void; + /** + * Adds a new agent with default properties to the simulation. + * + * @param {Vector2} position The two-dimensional starting position of this agent. + + * @returns {number} The number of the agent, or RVO::RVO_ERROR when the * agent defaults have not been set. + * + * @membeim + */ + addAgent(position: Vector2): number; + + /** + * Adds a new agent to the simulation. + * + * @param {Vector2} position The two-dimensional starting position of + * this agent. + * @param {number} 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 + * of the simulation. If the number is too + * low, the simulation will not be safe. + * Must be non-negative. + * @param {number} 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 + * safe. + * @param {number} 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. + * be positive. + * @param {number} timeHorizonObst The minimal amount of time for which this + * agevelocities that are computed by the + * simulation are safe with respect to + * obst. 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 {number} radius The radius of this agent. + * Must be non-negative. + * @param {number} maxSpeed The maximum speed of this agent. + * Must be non-negative. + * @param {number} velocity The initial two-dimensional linear velocity + * of this agent (optional). + * @returns {number} The number of the agent. + */ + addAgent(position: Vector2, neighborDist: number, maxNeighbors: number, + timeHorizon: number, timeHorizonObst: number, radius: number, + maxSpeed: number, velocity: Vector2): number; + + /** + * Adds a new obstacle to the simulation. + * + * To add a "negative" obstacle, e.g. a bounding polygon around + * the environment, the vertices should be listed in clockwise order. + * + * @param {vectorvector} vertices List of the vertices of the polygonal + * obstacle in counterclockwise order. + * @returns {number} The number of the first vertex of the obstacle, + * or RVO::RVO_ERROR when the number of vertices is less than two. + */ + addObstacle(vertices: vectorvector): number; + + /** + * Returns the position of agent at the specified index. + * + * @param {number} index Index of the agent. Must be >= 0. + * @returns {Vector2} Position of the agent. + * + * @memberOf sim + */ + getAgentPosition(index: number): Vector2; + + /** + * Sets the two-dimensional preferred velocity of a specified agent. + * @param {number} agentNo The number of the agent whose two-dimensional + * preferred velocity is to be modified. + * @param {Vector2} prefVelocity The replacement of the two-dimensional + * preferred velocity. + */ + setAgentPrefVelocity(agentNo: number, prefVelocity: Vector2): void; + + /** + * Returns the specified agent neighbor of the specified agent. + * + * @param {number} agentNo The number of the agent whose agent + * neighbor is to be retrieved. + * @param {number} neighborNo The number of the agent neighbor to be + * retrieved. + * @returns The number of the neighboring agent. + */ + getAgentAgentNeighbor(agentNo: number, neighborNo: number): number; + + /** + * Returns the maximum neighbor count of a specified agent. + * + * @param {number} agentNo The number of the agent whose maximum neighbor count is + * to be retrieved. + * @return {number} The present maximum neighbor count of the agent. + */ + getAgentMaxNeighbors(agentNo: number): number; + + /** + * Returns the maximum speed of a specified agent. + * + * @param {number} agentNo The number of the agent whose maximum speed is to be retrieved. + * @return {number} The present maximum speed of the agent. + */ + getAgentMaxSpeed(agentNo: number): number; + + /** + * Returns the maximum neighbor distance of a specified agent. + * @param {number} agentNo The number of the agent whose maximum + * neighbor distance is to be retrieved. + * @return The present maximum neighbor distance of the agent. + */ + getAgentNeighborDist(agentNo: number): number; + + /** + * Returns the count of agent neighbors taken into account to + * compute the current velocity for the specified agent. + * + * @param {number} agentNo The number of the agent whose count of agent + * neighbors is to be retrieved. + * @return {number} The count of agent neighbors taken into account to compute + * the current velocity for the specified agent. + */ + getAgentNumAgentNeighbors(agentNo: number): number; + + /** + * Returns the count of obstacle neighbors taken into account + * to compute the current velocity for the specified agent. + * + * @param {number} agentNo The number of the agent whose count of + * obstacle neighbors is to be retrieved. + * @return {number} The count of obstacle neighbors taken into account to + * compute the current velocity for the specified agent. + */ + getAgentNumObstacleNeighbors(agentNo: number): number; + + + /** + * Returns the count of ORCAconstraints used to compute + * + * the current velocity for the specified agent. + * + * @param {number} agentNo The number of the agent whose count of ORCA + * constraints is to be retrieved. + * + * @return {number} The count of ORCAconstraints used to compute the current + * + * velocity for the specified agent. + */ + getAgentNumORCALines(agentNo: number): number; + + /** + * Returns the specified obstacle neighbor of the specified agent. + * + * @param {number} agentNo The number of the agent whose obstacle + * neighbor is to be retrieved. + * @param {number} neighborNo The number of the obstacle neighbor to be + * retrieved. + * @return {number} The number of the first vertex of the neighboring obstacle edge. + */ + getAgentObstacleNeighbor(agentNo: number, neighborNo: number): number; + + /** + * Returns the specified ORCA constraint of the specified agent. + * + * The halfplane to the left of the line is the region of + * permissible velocities with respect to the specified + * ORCAconstraint. + * + * @param {number} agentNo The number of the agent whose ORCA constraint is to be retrieved. + * + * @param {number} lineNo The number of the ORCAconstraint to be retrieved. + * @return {number} A line representing the specified ORCAconstraint. + * + * + */ + getAgentORCALine(agentNo: number, lineNo: number): Line; + + /** + * Returns the two-dimensional position of a specified + * agent. + * @param {number} agentNo The number of the agent whose two-dimensional position + * is to be retrieved. + * @return {number} The present two-dimensional position of the (center of the) agent. + */ + getAgentPosition(agentNo: number): Vector2; + + /** + * Returns the two-dimensional preferred velocity of a + * specified agent. + * @param {number} agentNo The number of the agent whose two-dimensional + * preferred velocity is to be retrieved. + * @return {number} The present two-dimensional preferred velocity of the agent. + */ + getAgentPrefVelocity(agentNo: number): Vector2; + + /** + * Returns the radius of a specified agent. + * @param {number} agentNo The number of the agent whose radius is to be retrieved. + * @return {number} The present radius of the agent. + */ + getAgentRadius(agentNo: number): number; + + /** + * Returns the time horizon of a specified agent. + * @param {number} agentNo The number of the agent whose time horizon is to be retrieved. + * @return {number} The present time horizon of the agent. + */ + getAgentTimeHorizon(agentNo: number): number; + + /** + * Returns the time horizon with respect to obstacles of a specified agent. + * + * @param {number} agentNo The number of the agent whose time horizon + * with respect to obstacles is to be retrieved. + * @return {number} The present time horizon with respect to obstacles of the + * agent. + */ + getAgentTimeHorizonObst(agentNo: number): number; + + /** + * Returns the two-dimensional linear velocity of a + * specified agent. + * @param {number} agentNo The number of the agent whose two-dimensional linear + * velocity is to be retrieved. + * @return {Vector2} The present two-dimensional linear velocity of the agent. + */ + getAgentVelocity(agentNo: number): Vector2; + + /** + * Returns the global time of the simulation. + * + * @return {number} The present global time of the simulation (zero initially). + */ + getGlobalTime(): number; + + /** + * Returns the count of agents in the simulation. + * + * @return {number} The count of agents in the simulation. + */ + getNumAgents(): number; + + /** + * Returns the count of obstacle vertices in the simulation. + * + * @return {number} The count of obstacle vertices in the simulation. + */ + getNumObstacleVertices(): number; + + /** + * Returns the two-dimensional position of a specified obstacle vertex. + * @param {number} vertexNo The number of the obstacle vertex to be retrieved. + * @return {Vector2} The two-dimensional position of the specified obstacle vertex. + */ + getObstacleVertex(vertexNo: number): Vector2; + + /** + * Returns the number of the obstacle vertex succeeding the specified obstacle vertex in its polygon. + * + * @param {number} vertexNo The number of the obstacle vertex whose + * successor is to be retrieved. + * @return {number} The number of the obstacle vertex succeeding the specified + * obstacle vertex in its polygon. + */ + getNextObstacleVertexNo(vertexNo: number): number; + + /** + * Returns the number of the obstacle vertex preceding the specified obstacle + * vertex in its polygon. + * + * @param {number} vertexNo The number of the obstacle vertex whose + * predecessor is to be retrieved. + * @return {number} The number of the obstacle vertex preceding the specified + * obstacle vertex in its polygon. + */ + getPrevObstacleVertexNo(vertexNo: number): number; + + /** + * Returns the time step of the simulation. + * + * @return {number} The present time step of the simulation. + */ + getTimeStep(): number; + + /** + * Processes the obstacles that have been added so that they are accounted for in + * the simulation. + * + * Obstacles added to the simulation after this function has + * been called are not accounted for in the simulation. + */ + processObstacles(): void; + + /** + * Performs a visibility query between the two specified points with respect to + * the obstacles + * + * @param {number} point1 The first point of the query. + * @param {number} point2 The second point of the query. + * @param {number} 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. + * @return {number} A boolean specifying whether the two points are mutually + * visible. Returns true when the obstacles have not been processed. + */ + queryVisibility(point1: Vector2, point2: Vector2, radius?: number): number; + } + + /** + * Computes the length of a specified two-dimensional vector. + * + * @param {Vector2} v The two-dimensional vector whose length is to be computed. + * @returns {number} The length of the two-dimensional vector. + * + * @memberOf sim + */ + function abs(v: Vector2): number; + + /** + * Computes the squared length of a specified two-dimensional vector. + * @param {Vector2} v The two-dimensional vector whose squared length is to be computed. + * @returns {number} The squared length of the two-dimensional vector. + */ + function absSq(v: Vector2): number; + + /** + * Computes the determinant of a two-dimensional square matrix with + * rows consisting of the specified two-dimensional vectors. + * @param {Vector2} v1 The top row of the two-dimensional square matrix. + * @param {Vector2} v2 The bottom row of the two-dimensional square matrix. + * @returns {number} The determinant of the two-dimensional square matrix. + */ + function det(v1: Vector2, v2: Vector2): number; + + /** + * Computes the normalization of the specified two-dimensional vector. + * @param {Vector2} vector The two-dimensional vector whose normalization + * is to be computed. + * returns {Vector2} The normalization of the two-dimensional vector. + */ + function normalize(vector: Vector2): Vector2 + +} + +export = rvo2; \ No newline at end of file diff --git a/rvo2/rvo2-tests.ts b/rvo2/rvo2-tests.ts new file mode 100644 index 0000000000..80906011e9 --- /dev/null +++ b/rvo2/rvo2-tests.ts @@ -0,0 +1,137 @@ +import { Vector2, vectorvector, RVOSimulator, absSq, normalize } from './index'; + +// Some examples of vector functions. +const v1 = new Vector2(-1, 2); +const v2 = new Vector2(1, 2); +const v3 = v1.mul(3); // -3, 6 +const v4 = v1.sub(v2); // -2, 0 +const v5 = v1.add(v2); // 0, 4 + +function setupScenario(sim: RVOSimulator, goals: vectorvector) { + sim.setTimeStep(0.25); + /* Specify the default parameters for agents that are subsequently added. */ + sim.setAgentDefaults(15, 10, 5, 5, 2, 2); + + /* + * Add agents, specifying their start position, and store their goals on the + * opposite side of the environment. + */ + for (var i = 0; i < 5; ++i) { + for (var j = 0; j < 5; ++j) { + var index = sim.addAgent(new Vector2(55 + i * 10, 55 + j * 10)); + goals[index] = new Vector2(-75, -75); + + index = sim.addAgent(new Vector2(-55 - i * 10, 55 + j * 10)); + goals[index] = new Vector2(75, -75); + + index = sim.addAgent(new Vector2(55 + i * 10, -55 - j * 10)); + goals[index] = new Vector2(-75, 75); + + index = sim.addAgent(new Vector2(-55 - i * 10, -55 - j * 10)); + goals[index] = new Vector2(75, 75); + } + } + + /* + * Add (polygonal) obstacles, specifying their vertices in counterclockwise + * order. + */ + const obstacle1 = new vectorvector(4) + , obstacle2 = new vectorvector(4) + , obstacle3 = new vectorvector(4) + , obstacle4 = new vectorvector(4); + + obstacle1[0] = new Vector2(-10, 40); + obstacle1[1] = new Vector2(-40, 40); + obstacle1[2] = new Vector2(-40, 10); + obstacle1[3] = new Vector2(-10, 10); + + obstacle2[0] = new Vector2(10, 40); + obstacle2[1] = new Vector2(10, 10); + obstacle2[2] = new Vector2(40, 10); + obstacle2[3] = new Vector2(40, 40); + + obstacle3[0] = new Vector2(10, -40); + obstacle3[1] = new Vector2(40, -40); + obstacle3[2] = new Vector2(40, -10); + obstacle3[3] = new Vector2(10, -10); + + obstacle4[0] = new Vector2(-10, -40); + obstacle4[1] = new Vector2(-10, -10); + obstacle4[2] = new Vector2(-40, -10); + obstacle4[3] = new Vector2(-40, -40); + + sim.addObstacle(obstacle1); + sim.addObstacle(obstacle2); + sim.addObstacle(obstacle3); + sim.addObstacle(obstacle4); + + /* Process the obstacles so that they are accounted for in the simulation. */ + sim.processObstacles(); +} + +function updateVisualization(sim: RVOSimulator) +{ + /* Output the current global time. */ + const time = sim.getGlobalTime(); + // console.log(`Time: ${time}`); + /* Output the current position of all the agents. */ + for (let i = 0; i < sim.getNumAgents(); ++i) { + const p = sim.getAgentPosition(i); + // console.log(`#${i}) x: ${p.x()}, y: ${p.y()}`); + } +} + +function setPreferredVelocity(sim: RVOSimulator, goals: vectorvector) { + for (var i = 0; i < sim.getNumAgents(); i++) { + const delta = goals[i].sub(sim.getAgentPosition(i)); + + /* + * Perturb a little to avoid deadlocks due to perfect symmetry. + */ + const angle = Math.random() * 2.0 * Math.PI; + const dist = Math.random() * 0.0001; + + var goalVector = new Vector2(delta.x() + dist * Math.cos(angle), delta.y() + dist * Math.sin(angle)); + + if (absSq(goalVector) > 1.0) { + goalVector = normalize(goalVector); + } + + sim.setAgentPrefVelocity(i, goalVector); + } +} + +function reachedGoal(sim: RVOSimulator, goals: vectorvector) { + /* Check if all agents have reached their goals. */ + for (var i = 0; i < sim.getNumAgents(); ++i) { + const dist = sim.getAgentPosition(i).sub(goals[i]); + if (absSq(dist) > 400) { // 400 <= 20 x 20 + return false; + } + } + return true; +} + +function main(debug = false) { + const nbrAgents = 100; + const sim = new RVOSimulator(); + const goals = new vectorvector(nbrAgents); + + setupScenario(sim, goals); + + // sim.addObstacle(vv); + var i = 0; + do { + if (++i % 10 === 0) { + // console.log('TIME: ' + i); + } + setPreferredVelocity(sim, goals); + if (debug) { updateVisualization(sim); } + sim.doStep(); + } while (!reachedGoal(sim, goals)); + + // console.log('Done'); +} + +main(); diff --git a/rvo2/tsconfig.json b/rvo2/tsconfig.json new file mode 100644 index 0000000000..bf6778173d --- /dev/null +++ b/rvo2/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": false, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "rvo2-tests.ts" + ] +}