Пример #1
0
 public void TestDisconnectReturnsNull()
 {
     GeometryCollection geometry = new GeometryCollection();
     Line line = new Line(new Vector2(-1, 0), new Vector2(1, 0));
     geometry.Add(line);
     geometry.Add(new Vector2(2, 0));
     BoundParticle particle = new BoundParticle(line, false, 0.5, 0, 0);
     Path path = PathFinding.PathTo(particle, new Vector2(2, 0), geometry, 0.1f, 0.1f);
     Assert.IsNull(path);
 }
Пример #2
0
 public void TestBowlSwing()
 {
     GeometryCollection geometry = new GeometryCollection();
     Line line = new Line(new Vector2(1, 0), new Vector2(-1, 0));
     geometry.Add(line);
     geometry.Add(new Line(new Vector2(-2, 1), new Vector2(-1, 0)));
     geometry.Add(new Line(new Vector2(2, 1), new Vector2(1, 0)));
     // Shape looks like \_._/*
     BoundParticle particle = new BoundParticle(line, false, 0.5, 0, 0);
     Path path = PathFinding.PathTo(particle, new Vector2(2, 1), geometry, 0.1f, -0.2f);
     Assert.IsNotNull(path);
 }
Пример #3
0
 public void TestBowlSlowdown()
 {
     GeometryCollection geometry = new GeometryCollection();
     Line line = new Line(new Vector2(1, 0), new Vector2(-1, 0));
     geometry.Add(line);
     geometry.Add(new Line(new Vector2(-2, 1), new Vector2(-1, 0)));
     geometry.Add(new Line(new Vector2(2, 1), new Vector2(1, 0)));
     // Shape looks like \_._/*
     BoundParticle particle = new BoundParticle(line, false, 0.5, 0, 0);
     Path path = PathFinding.PathTo(particle, new Vector2(2, 1), geometry, 0.1f, -0.1f);
     // Exact answer: √(80+120√2)-√40
     AssertExtra.AreApproximate(9.4775213623, path.totalTime);
 }
Пример #4
0
        public static Path PathTo(BoundParticle start, Point end, GeometryCollection geometry, float accel, float gravity)
        {
            // construct a new critical point
            Point newPoint = start.position();
            Line line1 = new Line(newPoint, start.boundTo.p1);
            Line line2 = new Line(newPoint, start.boundTo.p2);
            geometry.Add(line1);
            geometry.Add(line2);
            geometry.Remove(start.boundTo);

            Queue<PathCriticalPoint> bfs = new Queue<PathCriticalPoint>();
            // map time function for two attached points
            Dictionary<PathCriticalPoint, PointFunction> dict = new Dictionary<PathCriticalPoint, PointFunction>();
            PathCriticalPoint state1 = new PathCriticalPoint(line1, true);
            PathCriticalPoint state2 = new PathCriticalPoint(line2, true);
            dict[state1] = new PointFunction(0);
            dict[state2] = new PointFunction(0);
            bfs.Enqueue(state1);
            bfs.Enqueue(state2);
            // bfs will simply search through steps, and will not continue a branch if it doesn't improve anything
            while (bfs.Count > 0)
            {
                var head = bfs.Dequeue();
                // TODO: currently not going back to previously visited points, as the logic isn't correct for it
                var currPoint = head.firstPoint ? head.line.p1 : head.line.p2;
                foreach (var nextLine in geometry.LinesAttachedTo(currPoint).Where(l => !l.Equals(head.line)))
                {
                    var nextPoint = (nextLine.p1.Equals(currPoint)) ? nextLine.p2 : nextLine.p1;
                    var nextState = new PathCriticalPoint(nextLine, nextLine.p1.Equals(nextPoint));
                    PointFunction timings = new PointFunction(currPoint, nextPoint, dict[head], accel, gravity);
                    WorkWithTimings(end, geometry, bfs, dict, nextState, timings);
                    // also attempt to return along that path
                    var nextState2 = new PathCriticalPoint(nextLine, nextLine.p1.Equals(currPoint));
                    PointFunction timings2 = PointFunction.Return(currPoint, nextPoint, dict[head], accel, gravity);
                    WorkWithTimings(end, geometry, bfs, dict, nextState2, timings2);
                }
            }
            Path answer = null;
            if (ContainsAnyState(dict, end, geometry, 0))
            {
                answer = BestOfAnyState(dict, end, geometry, 0);
            }
            geometry.Remove(newPoint);
            geometry.Add(start.boundTo);
            return answer;
        }
Пример #5
0
        public BoundParticle FirstCollision(Particle particle)
        {
            var v = particle.velocity;
            var p = particle.position;
            if (v.X == 0 && v.Y == 0) return null;
            BoundParticle collidedWith = null;
            double minT = Double.MaxValue;
            foreach (Line line in lines)
            {
                // calculate t, the amount of time it would take to collide
                // p+v*t=line.p1+(line.p2-line.p1)*g
                // p.x+v.x*t=line.p1.x+(line.p2.x-line.p1.x)*g
                // p.y+v.y*t=line.p1.y+(line.p2.y-line.p1.y)*g

                // (line.p1.x+(line.p2.x-line.p1.x)*g)*v.y-(line.p1.y+(line.p2.y-line.p1.y)*g)*v.x=p.x*v.y-p.y*v*x
                // g=(p.x*v.y-p.y*v.x-v.y*line.p1.x+line.p1.y*v.x)/((line.p2.x-line.p1.x)*v.y-(line.p2.y-line.p1.y)*v.x)
                double g = (p.X*v.Y-p.Y*v.X-v.Y*line.p1.v.Position.X+line.p1.v.Position.Y*v.X)/((line.p2.v.Position.X-line.p1.v.Position.X)*v.Y-(line.p2.v.Position.Y-line.p1.v.Position.Y)*v.X);
                double t = (line.p1.v.Position.Y + (line.p2.v.Position.Y - line.p1.v.Position.Y) * g - p.Y) / v.Y;
                if(t>=0 && g>=0 && g<=1 && t<minT && t<=1)
                {
                    minT = t;
                    double gv = Vector2.Dot(v, (Vector2)line.p2 - line.p1) / (((Vector2)line.p2 - line.p1).LengthSquared());
                    double ga = Vector2.Dot(particle.gravity, line) / ((Vector2)line).LengthSquared();
                    collidedWith = new BoundParticle(line, v.CrossProduct(line) > 0, g, gv, ga);
                }
            }
            return collidedWith;
        }
Пример #6
0
 internal void Update()
 {
     mouseListener.Update();
     var keystate = Keyboard.GetState();
     if(keystate.IsKeyDown(Keys.Z) && !playback && isBound) // TODO: we can improve this to work even when unbound
     {
         var target = geometry.SnapToClosePoint(mouseListener.Transform(Mouse.GetState()));
         recording = PathFinding.PathTo(player2, target, geometry, ACCEL, GRAVITY);
         if (recording != null)
         {
             timeInRecording = 0;
             recordingPointer = new Particle(recording.absolutePosAt(0), Vector2.Zero, Vector2.Zero);
             playback = true;
         }
     }
     if (!playback)
     {
         if (!isBound)
         {
             var collision = geometry.FirstCollision(player);
             if (collision != null)
             {
                 isBound = true;
                 player2 = collision;
             }
         }
         if (isBound)
         {
             bool upsideDown = (((Vector2)player2.boundTo).X < 0) != player2.onCW;
             if (upsideDown)
             {
                 isBound = false;
                 player.position = player2.boundTo.p1 + Vector2.Multiply(player2.boundTo, (float)player2.g);
                 player.velocity = Vector2.Multiply(player2.boundTo, (float)player2.gv) + player.gravity;
                 player.position += player.velocity;
                 player.velocity += player.gravity;
             }
             else
             {
                 player2.g += player2.gv;
                 player2.gv += player2.ga;
                 if (keystate.IsKeyDown(Keys.A))
                 {
                     player2.gv += ACCEL * ((player2.onCW) ? 1 : -1) / ((Vector2)player2.boundTo).Length();
                 }
                 if (keystate.IsKeyDown(Keys.D))
                 {
                     player2.gv -= ACCEL * ((player2.onCW) ? 1 : -1) / ((Vector2)player2.boundTo).Length();
                 }
                 if (player2.g > 1 || player2.g < 0)
                 {
                     var exitingPoint = (player2.g < 0) ? player2.boundTo.p1 : player2.boundTo.p2;
                     var nextList = geometry.LinesAttachedTo(exitingPoint).Where(l => l != player2.boundTo);
                     nextList = nextList.Where(l => (((((Vector2)player2.boundTo).CrossProduct(l) < 0) != player2.onCW) != (player2.boundTo.p1 == exitingPoint)) != ((exitingPoint == l.p1) == (exitingPoint == player2.boundTo.p1)));
                     if (nextList.Count() > 0)
                     {
                         var next = nextList.OrderBy(l => Math.Abs(((Vector2)player2.boundTo).CrossProduct(l)) / ((Vector2)l).Length()).Last();
                         var multiplier = Vector2.Dot(next, player2.boundTo) / (((Vector2)next).Length() * ((Vector2)player2.boundTo).Length()) * ((Vector2)player2.boundTo).Length() / ((Vector2)next).Length();
                         if ((exitingPoint == next.p1) == (exitingPoint == player2.boundTo.p1))
                         {
                             player2.onCW = !player2.onCW;
                         }
                         player2.boundTo = next;
                         player2.g = (next.p1 == exitingPoint) ? 0 : 1;
                         player2.gv *= multiplier;
                         player2.ga = Vector2.Dot(player.gravity, (Vector2)next) / (((Vector2)next).LengthSquared());
                     }
                     else
                     {
                         isBound = false;
                         player.position = player2.boundTo.p1 + Vector2.Multiply(player2.boundTo, (float)player2.g);
                         player.velocity = Vector2.Multiply(player2.boundTo, (float)player2.gv);
                     }
                 }
             }
         }
         else
         {
             // order of this matters
             player.position += player.velocity;
             player.velocity += player.gravity;
         }
     }
 }
Пример #7
0
 public void TestMultipleFlatLines()
 {
     int n = 10; // half the total number of points
     GeometryCollection geometry = new GeometryCollection();
     Line line = new Line(new Vector2(-1, 0), new Vector2(1, 0));
     geometry.Add(line);
     MathExp.Geometry.Point target = null;
     for(int i=1; i<n; i++)
     {
         MathExp.Geometry.Point rightMostPoint = new Vector2(i + 1, 0);
         Line leftLine = new Line(new Vector2(i, 0), rightMostPoint);
         Line rightLine = new Line(new Vector2(-i, 0), new Vector2(-i - 1, 0));
         geometry.Add(leftLine);
         geometry.Add(rightLine);
         target = rightMostPoint;
     }
     BoundParticle particle = new BoundParticle(line, false, 0.5, 0, 0);
     Path path = PathFinding.PathTo(particle, target, geometry, 0.1f, 0.1f);
     AssertExtra.AreApproximate(path.totalTime, 2 * Math.Sqrt(10 * n));
     for (float t = 0; t < path.totalTime; t += 0.01f)
     {
         Vector2 pos = path.absolutePosAt(t);
         AssertExtra.AreApproximate(0, pos.Y);
         if (t < Math.Sqrt(10*n))
         {
             // when speeding up to approach
             AssertExtra.AreApproximate(0.1 * t * t / 2, pos.X);
         }
         else
         {
             // when slowing down to stop
             double g = t - Math.Sqrt(10*n);
             AssertExtra.AreApproximate(-0.1 * g * g / 2 + Math.Sqrt(10*n) * 0.1 * g + 0.5*n, pos.X);
         }
     }
 }
Пример #8
0
 public void TestSingleFlatLine()
 {
     GeometryCollection geometry = new GeometryCollection();
     Line line = new Line(new Vector2(-1, 0), new Vector2(1, 0));
     geometry.Add(line);
     BoundParticle particle = new BoundParticle(line, false, 0.5, 0, 0);
     Path path = PathFinding.PathTo(particle, new Vector2(1, 0), geometry, 0.1f, 0.1f);
     AssertExtra.AreApproximate(2 * Math.Sqrt(10), path.totalTime);
     for (float t = 0; t < path.totalTime; t += 0.01f)
     {
         Vector2 pos = path.absolutePosAt(t);
         AssertExtra.AreApproximate(0, pos.Y);
         if (t < Math.Sqrt(10))
         {
             // when speeding up to approach
             AssertExtra.AreApproximate(0.1 * t * t / 2, pos.X);
         }else
         {
             // when slowing down to stop
             double g = t - Math.Sqrt(10);
             AssertExtra.AreApproximate(-0.1 * g * g / 2 + Math.Sqrt(10)*0.1*g+0.5, pos.X);
         }
     }
 }