MengeCrowdSim / Menge

The source code for the Menge crowd simulation framework
Apache License 2.0
138 stars 64 forks source link

Linear program #130

Closed dkal3 closed 5 years ago

dkal3 commented 5 years ago

Hi, I study the code from the ORCA algorithm. I try to understand the functions linearProgram1,linearProgram2 and linearProgram3 from ORCAAgent.cpp. I understand the brief description but when I want to deal with it in detail, I am confusing. In linearProgram1, you calculate
const float denominator = det(lines[lineNo]._direction, lines[i]._direction); const float numerator = det(lines[i]._direction, lines[lineNo]._point - lines[i]._point);

Which variables we put inside det. For example why numerator could not be like const float numerator = det(lines[i]._direction, lines[lineNo]._point);

Below in the code, there is a condition if (optVelocity * lines[lineNo]._direction > 0.0f). Why we put this inside the condition

I am sorry for asking so many things. I would be appreciate if you could give me a mathematical type that you follow for the linear Programs , to understand it better. Thank you in advance!!!

curds01 commented 5 years ago

You have my deepest sympathies. The author of that code is incredibly gifted -- his gift is in taking math and rigorously reducing it to efficient code. However efficient code is not necessarily synonymous with easily understood. :)

This weekend I'll walk through the code and see if I can't give you an explanation of what you're seeing there. Sorry for the slight delay.

curds01 commented 5 years ago

Turns out, I have the time now. :)

I've taken the code out of the source and put a copied-and-pasted version down below with line numbers for reference. But first the overview.

In this case, we're optimizing constraints around the current velocity. So, when linearProgram1 is applying the circular constraint, it is a circle around the origin.

The simple way to think about this function is that we have a line that we're going to clip with a circle and a set of halfspaces. The line to be clipped is drawn from lines and is at index lineNo. We first clip it with a circle, and then clip it with all of the other lines in lines before lineNo. Once we've clipped, we'll either do a simple directional optimization or a nearest point optimization -- based on the provided optVelocity.

So, with that overview in mind:

Some notation:

Algorithm overivew

1.  const float dotProduct = lines[lineNo]._point * lines[lineNo]._direction;
2.    const float discriminant = sqr(dotProduct) + sqr(radius) - absSq(lines[lineNo]._point);
3.    if (discriminant < 0.0f) {
4.      return false;
5.    }
6.    const float sqrtDiscriminant = std::sqrt(discriminant);
7.    float tLeft = -dotProduct - sqrtDiscriminant;
8.    float tRight = -dotProduct + sqrtDiscriminant;
9.    for (size_t i = 0; i < lineNo; ++i) {
10.     const float denominator = det(lines[lineNo]._direction, lines[i]._direction);
11.      const float numerator = det(lines[i]._direction, lines[lineNo]._point - lines[i]._point);
12.      if (std::fabs(denominator) <= Menge::EPS) {
13.        if (numerator < 0.0f) {
14.          return false;
15.        } else {
16.          continue;
17.        }
18.      }
19.      const float t = numerator / denominator;
20.      if (denominator >= 0.0f) {
21.        tRight = std::min(tRight, t);
22.      } else {
23.        tLeft = std::max(tLeft, t);
24.      }
25.      if (tLeft > tRight) {
26.        return false;
27.      }
28.    }
29.    if (directionOpt) {
30.      if (optVelocity * lines[lineNo]._direction > 0.0f) {
31.        result = lines[lineNo]._point + tRight * lines[lineNo]._direction;
32.      } else {
33.        result = lines[lineNo]._point + tLeft * lines[lineNo]._direction;
34.      }
35.    } else {
36.      const float t = lines[lineNo]._direction * (optVelocity - lines[lineNo]._point);
37.      if (t < tLeft) {
38.        result = lines[lineNo]._point + tLeft * lines[lineNo]._direction;
39.      } else if (t > tRight) {
40.        result = lines[lineNo]._point + tRight * lines[lineNo]._direction;
41.      } else {
42.        result = lines[lineNo]._point + t * lines[lineNo]._direction;
43.      }
44.    }
45.    return true;
dkal3 commented 5 years ago

You are amazing!!! Thank you so much for your detailed answer!!!

dkal3 commented 5 years ago

I don't have another question! We can close the issue! Thank you very much!

curds01 commented 5 years ago

The nice thing is, I can use this to upgrade the documentation.

dkal3 commented 5 years ago

Hi again!! You have done amazing job with the documentation! Can I ask something? I am a bit confused with the determinant and which parameters we put inside. The result of the determinant what does it show? Thank you in advance!!!

curds01 commented 5 years ago

So, there's two things about det(). I'll see if I can't clarify it.

  1. det() as a mathematical function:
    • Generally, this is the definition of the determinant of a 2x2 matrix:
      |a c|
      det|   | = ad - bc
      |b d|

      This is where the name comes from. The function is invoked as det(u, v) which implicitly computes:

   |u₁ v₁|
det|     | = u₁v₂ - u₂v₁
   |u₂ v₂|
  1. det() as how it is typically used in Menge.
    • The first application is a sneaky mechanism for computing u⋅v⟂ what all you have is u and v. This works because if v = [v₁ v₂], then v⟂= [v₂ -v₁]. Therefore, u⋅v⟂ = u₁v₂ - u₂v₁ = det(u, v).
    • It can also be used to determine the sine of the angle between vectors u and v. This works by thinking of the 2D vector v = [v₁ v₂] as the 3D vector v = [v₁ v₂ 0]. Given u, v ∈ ℜ³, |u ⨯ v| = |u||v|sin(θ). If |u| = |v| = 1 then |u ⨯ v| = sin(θ). If you multiply out the cross product, you get the vector |[0, 0, u₁v₂ - u₂v₁]| = u₁v₂ - u₂v₁ = sin(θ). So, for unit normals in ℜ², det(u, v) will give you the sin(θ) between u and v which is enough to determine whether v lies clockwise or counter-clockwise of u. (And if you really care about the angle, you can call acos(det(u, v)).

I hope this clarifies det().

dkal3 commented 5 years ago

Yes it is clear now!! For one more time, thank you a lot!!!