Exemple #1
0
        static BallState Detect(ProcessId pidA, BallState stateA, ProcessId pidB, BallState stateB, BallState lastStateB)
        {
            if (pidA == pidB)
            {
                return(stateB);
            }

            var a = new Point2(stateA.X, stateA.Y);
            var b = new Point2(stateB.X, stateB.Y);

            var avel = new Vector2(stateA.VelX.MetresPerSecond, stateA.VelY.MetresPerSecond);
            var bvel = new Vector2(stateB.VelX.MetresPerSecond, stateB.VelY.MetresPerSecond);

            Vector2 collision = a - b;
            double  distance  = collision.Length;

            if (distance == 0.0)
            {
                // hack to avoid div by zero
                collision = new Vector2(1.0, 0.0);
                distance  = 1.0;
            }
            if (distance > stateA.BallSize && distance > stateB.BallSize)
            {
                return(stateB);
            }

            // Get the components of the velocity vectors which are parallel to the collision.
            // The perpendicular component remains the same for both fish
            collision = collision / distance;
            double aci = Vector2.Dot(avel, collision);
            double bci = Vector2.Dot(bvel, collision);

            // Solve for the new velocities using the 1-dimensional elastic collision equations.
            // Turns out it's really simple when the masses are the same.
            double acf = bci;
            double bcf = aci;

            // Replace the collision velocity components with the new ones
            var navel = avel + collision * (acf - aci);
            var nbvel = bvel + collision * (bcf - bci);

            SetPosAndVelMsg.Tell(pidA, a.X, a.Y, navel.DX * m / s, navel.DY * m / s);
            SetPosAndVelMsg.Tell(pidB, lastStateB.X, lastStateB.Y, nbvel.DX * m / s, nbvel.DY * m / s);

            return(lastStateB);
        }
        static BallState Detect(ProcessId pidA, BallState stateA, ProcessId pidB, BallState stateB, BallState lastStateB)
        {
            if (pidA == pidB) return stateB;

            var a = new Point2(stateA.X, stateA.Y);
            var b = new Point2(stateB.X, stateB.Y);

            var avel = new Vector2(stateA.VelX.MetresPerSecond, stateA.VelY.MetresPerSecond);
            var bvel = new Vector2(stateB.VelX.MetresPerSecond, stateB.VelY.MetresPerSecond);

            Vector2 collision = a - b;
            double distance = collision.Length;
            if (distance == 0.0)
            {              
                // hack to avoid div by zero
                collision = new Vector2(1.0, 0.0);
                distance = 1.0;
            }
            if (distance > stateA.BallSize && distance > stateB.BallSize)
            {
                return stateB;
            }

            // Get the components of the velocity vectors which are parallel to the collision.
            // The perpendicular component remains the same for both fish
            collision = collision / distance;
            double aci = Vector2.Dot(avel, collision);
            double bci = Vector2.Dot(bvel, collision);

            // Solve for the new velocities using the 1-dimensional elastic collision equations.
            // Turns out it's really simple when the masses are the same.
            double acf = bci;
            double bcf = aci;

            // Replace the collision velocity components with the new ones
            var navel = avel + collision * (acf - aci);
            var nbvel = bvel + collision * (bcf - bci);

            SetPosAndVelMsg.Tell(pidA, a.X, a.Y, navel.DX*m/s, navel.DY*m/s);
            SetPosAndVelMsg.Tell(pidB, lastStateB.X, lastStateB.Y, nbvel.DX*m/s, nbvel.DY*m/s);

            return lastStateB;
        }
        /// <summary>
        /// Spawn a ball process and observe its state
        /// </summary>
        void SpawnBall(int id)
        {
            var element = CreateBallUI();

            // Setup function that creates a random initial state for the
            // ball and passes on the bounds of the window
            Func <BallState> setup = () =>
                                     BallState.CreateRandom()
                                     .With(
                BoxWidth: box.ActualWidth,
                BoxHeight: box.ActualHeight
                );

            // Spawn the ball process
            kill(User["ball-" + id]);
            var pid = spawn <BallState, BallMsg>("ball-" + id, setup, BallProcess.Inbox);

            // Subscribe the ball process to the update process's state
            var sub = observeState <DateTime>(update).Subscribe(par(TimeMsg.Tell, pid));

            // Subscribe to the state changes of the ball so we can update the view
            observeState <BallState>(pid)
            .ObserveOn(DispatcherScheduler.Current)
            .Subscribe(state =>
            {
                element.Visibility = Visibility.Visible;
                Canvas.SetLeft(element, state.X);
                Canvas.SetTop(element, box.ActualHeight - state.Y);
            },
                       () =>
            {
                box.Children.Remove(element);
                sub.Dispose();
            }
                       );

            // The resolver subscribes to the state changes of the ball
            observeState <BallState>(pid).Subscribe(state => tell(resolver, Tuple(pid, state)));
        }
 static BallState ImpulseRight(BallState state) => 
     state.With(
         X:    state.BoxWidth - state.BallSize, 
         VelX: state.VelX * -state.SurfaceImpulse);
 static bool HasCollidedWithRightWall(BallState state) =>
     state.X > (state.BoxWidth - state.BallSize);
 static BallState ImpulseLeft(BallState state) => 
     state.With(
         X:    0, 
         VelX: state.VelX * -state.SurfaceImpulse);
 static bool HasCollidedWithCeiling(BallState state) =>
     state.Y > state.BoxHeight;
 static bool HasCollidedWithLeftWall(BallState state) =>
     state.X < 0;
 /// <summary>
 /// Ball inbox
 /// This is the entry point for messages sent to a ball.  It looks for the message-type
 /// in the message-map; if found it invokes the message-inbox with the message and state.
 /// </summary>
 public static BallState Inbox(BallState state, BallMsg msg) =>
     match(messageMap, msg.Tag, 
         Some: fn => fn(msg)(state), 
         None: () => state
         );
 static bool HasCollidedWithCeiling(BallState state) =>
 state.Y > state.BoxHeight;
 static BallState ImpulseCeiling(BallState state) =>
 state.With(
     Y:    state.BoxHeight,
     VelX: state.VelX * state.SurfaceImpulse,
     VelY: state.VelY * -state.SurfaceImpulse);
 /// <summary>
 /// Find the relative time-step
 /// </summary>
 static Time DeltaTime(DateTime now, BallState state) =>
 now - state.Time;
 static BallState ImpulseRight(BallState state) =>
 state.With(
     X:    state.BoxWidth - state.BallSize,
     VelX: state.VelX * -state.SurfaceImpulse);
 static BallState ImpulseGround(BallState state) =>
 state.With(
     Y:    state.BallSize,
     VelX: state.VelX * state.SurfaceImpulse,
     VelY: state.VelY * -state.SurfaceImpulse);
 static BallState ImpulseLeft(BallState state) =>
 state.With(
     X:    0,
     VelX: state.VelX * -state.SurfaceImpulse);
 static bool HasCollidedWithRightWall(BallState state) =>
 state.X > (state.BoxWidth - state.BallSize);
 static bool HasCollidedWithLeftWall(BallState state) =>
 state.X < 0;
 static BallState ImpulseGround(BallState state) => 
     state.With(
         Y:    state.BallSize,
         VelX: state.VelX * state.SurfaceImpulse,
         VelY: state.VelY * -state.SurfaceImpulse);
 /// <summary>
 /// Find the relative time-step
 /// </summary>
 static Time DeltaTime(DateTime now, BallState state) =>
     now - state.Time;
 static BallState ImpulseCeiling(BallState state) =>
     state.With(
         Y:    state.BoxHeight,
         VelX: state.VelX * state.SurfaceImpulse,
         VelY: state.VelY * -state.SurfaceImpulse);
 static bool HasCollidedWithGround(BallState state) =>
     state.Y < state.BallSize;
 /// <summary>
 /// Ball inbox
 /// This is the entry point for messages sent to a ball.  It looks for the message-type
 /// in the message-map; if found it invokes the message-inbox with the message and state.
 /// </summary>
 public static BallState Inbox(BallState state, BallMsg msg) =>
 match(messageMap, msg.Tag,
       Some: fn => fn(msg)(state),
       None: () => state
       );
 static bool HasCollidedWithGround(BallState state) =>
 state.Y < state.BallSize;