Muestre los rastros del puntero del mouse ... ¡del futuro!

24

Inspirado en este ejemplo de uso de d3js , le desafío a crear un lienzo (o su idioma de elección equivalente) en el que se mostrarán los rastros del puntero del mouse , con el siguiente giro:

El giro

Usted no debe mostrar los senderos de donde el puntero del ratón era , pero los "rastros" de donde va (podría) ser en el futuro.

Puedes hacerlo usando:

  1. Una máquina del tiempo, o

  2. Estimaciones probabilísticas basadas en movimientos previos del mouse

Supuestos

En caso de que no haya elegido la implementación de la máquina del tiempo, cuando el mouse no se mueve por más de un umbral de milisegundos, no puede mostrar ninguno de los senderos. (El valor del umbral depende de usted para elegir).

La imagen del cursor depende de usted y no tiene que ser la misma que la del cursor del sistema operativo (incluso puede dibujar círculos o puntos pequeños).

No se probará ninguna entrada malvada: puede asumir que los movimientos son suaves. La definición 'suave' para este caso es: si los movimientos del mouse fueran una función sobre los ejes xey del lienzo, sería una función continua.

Victorioso

La respuesta válida con la menor cantidad de caracteres en el código ganará. En caso de empate, ganará el que se publicó primero.

EDITAR: Ganará la respuesta válida con más votos a favor. En caso de empate, ganará el que se publicó primero. Puede ser creativo en la implementación o ser preciso con la predicción. Ya no soy el juez, todos lo somos :)

  • Una respuesta válida debe incluir una forma de jugar (¡prueba! Me refería a la prueba), ya sea en una herramienta en línea o en un compilador / intérprete / runtime / etc de descarga gratuita.
Jacob
fuente
2
Creo que esta pregunta podría ser más adecuada para un concurso de popularidad que un código de golf, porque es bastante subjetivo en cuanto a lo que califica como una predicción lo suficientemente buena. Recomendaría aclarar eso o cambiar la etiqueta. Sin embargo, se ve divertido.
isaacg
2
Tienes razón. Edité la pregunta y cambié la etiqueta.
Jacob
¡Es hora de que alguien implemente algoritmos de aprendizaje automático!
Ingo Bürk
66
Para fines de prueba, ¿a qué modelos de máquina del tiempo tiene acceso? ¿Y podemos usar bibliotecas estándar para interactuar con ellas?
Peter Taylor
1
Solo un matemático quejándose aquí: suave! = Continuo. De hecho, el movimiento salvaje y puntiagudo seguirá siendo continuo.
CompuChip

Respuestas:

33

Javascript

Mi programa predice la dirección del puntero usando el promedio del cambio angular en la dirección de los últimos 20 movimientos del mouse. También utiliza la variación del cambio angular para crear una "nube" de posibles ubicaciones y direcciones del puntero. Se supone que el color de cada puntero en la "nube" representa la probabilidad de que sea la nueva posición del puntero del mouse, donde los colores más oscuros representan una mayor probabilidad. La distancia de la nube de puntero delante del mouse se calcula utilizando la velocidad del movimiento del mouse. No hace las mejores predicciones, pero se ve bien.

Aquí hay un violín: http://jsfiddle.net/5hs64t7w/4/

Es interesante ver el aumento del tamaño de la nube de puntero. Se puede configurar cambiando la cloudSizevariable en la primera línea del programa. Aquí hay un violín con un tamaño de nube de 10: http://jsfiddle.net/5hs64t7w/5/

Utilicé estas fuentes para obtener fórmulas para la media circular y la varianza:
Media circular: http://en.wikipedia.org/wiki/Circular_mean
Variación circular: http://www.ebi.ac.uk/thornton-srv/software/ PROCHECK / nmr_manual / man_cv.html

Aquí está el código si alguien está interesado:

    var cloudSize = 3;

    var canvas = document.getElementById('canvas_element');
    var c = canvas.getContext('2d');
    var prevX = -1;
    var prevY = -1;
    var curX = -1;
    var curY = -1;
    var distance = 0;
    var direction = 0;

    function drawMouse(x, y, angle, gray){
        var grayVal = Math.round(gray*255);
        var grayString = "rgb(" + grayVal + "," + grayVal +"," + grayVal + ")";
        c.fillStyle = grayString;
        c.strokeStyle = grayString;
        c.lineWidth = 1;
        c.beginPath();
        c.moveTo(x, y);
        c.lineTo(x + 16*Math.cos(angle + Math.PI/2.0 + Math.PI/8.0), y + 16*Math.sin(angle + Math.PI/2.0 + Math.PI/8.0));
        c.moveTo(x, y);
        c.lineTo(x + 16*Math.cos(angle + Math.PI/2.0 - Math.PI/8.0), y + 16*Math.sin(angle + Math.PI/2.0 - Math.PI/8.0));
        c.lineTo(x + 16*Math.cos(angle + Math.PI/2.0 + Math.PI/8.0), y + 16*Math.sin(angle + Math.PI/2.0 + Math.PI/8.0));
        c.stroke();
        c.fill();
        c.beginPath();
        c.moveTo(x, y);
        c.lineTo(x + 24*Math.cos(angle + Math.PI/2), y + 24*Math.sin(angle + Math.PI/2));
        c.stroke();
    }

    function sum(array){
        var s = 0.0;
        for(var i=0; i<array.length; i++){
            s += array[i];
        }
        return s;
    }

    var sins = [];
    var coss = [];
    var lengths = [];
    var times = [];
    var index = 0;
    var limit = 20;
    var variance = 0;
    var prevTime = new Date().getTime();
    function updateDistanceAndDirection(x, y){
        var angle = Math.atan2(prevY - curY, prevX - curX);
        sins[index] = Math.sin(angle);
        coss[index] = Math.cos(angle);
        lengths[index] = Math.sqrt((curX-prevX)*(curX-prevX) + (curY-prevY)*(curY-prevY));
        var time = new Date().getTime();
        times[index] = time - prevTime;

        variance = 1.0 - Math.sqrt(sum(coss)*sum(coss)+sum(sins)*sum(sins))/sins.length;

        direction = Math.atan2(1/sins.length*sum(sins),1/coss.length*sum(coss));
        var speed = sum(lengths)/(sum(times)/200);
        distance = Math.min(Math.max(40, speed), 100);
        prevTime = time;
        index = (index+1)%limit;
    }

    function drawMice(count){
        c.clearRect(0, 0, canvas.width, canvas.height);

        for(var i=count; i>=0; i--){
            var dir = direction + i*variance;
            drawMouse(curX - distance*Math.cos(dir), curY - distance*Math.sin(dir), dir - Math.PI/2, i/count);
            dir = direction - i*variance;
            drawMouse(curX - distance*Math.cos(dir), curY - distance*Math.sin(dir), dir - Math.PI/2, i/count);
        }
    }

    canvas.onmousemove = function (event) {
        curX = event.clientX;
        curY = event.clientY;

        updateDistanceAndDirection(curX, curY);

        drawMice(cloudSize);

        prevX = curX;
        prevY = curY;
    };
Natillas de ruibarbo
fuente
2
¿Se puede mostrar una secuencia de puntero del mouse (con orientación fija) en lugar de un puntero que apunta a una dirección variable? Esperaba ver "rastros del mouse" pero no puedo ver ninguno, jaja
solo el
Muy bien, pero ¿no es más plausible que el puntero suba en el futuro cuando actualmente baja? En mi opinión, el programa debe hacer exactamente lo contrario para que prediga que el puntero permanece en la pantalla.
Madmenyo
@MennoGouw no es perfecto pero es bastante bueno
NimChimpsky
@nimchimpsky Solo digo que la probabilidad de que el mouse suba es mayor si el mouse se cae actualmente. El programa en sí es genial.
Madmenyo
¿Crees que también es posible usar el comportamiento humano habitual para el manejo del mouse? Como círculos, líneas rectas ... Esto podría predecirse aún más en el futuro (calculando el radio del círculo después de un par de medidas y terminando el círculo incluso antes de dibujarlo)
Azafrán
14

Java

Decidí adoptar el enfoque de máquina del tiempo. Resulta que el ingrediente clave de una máquina del tiempo es java.awt.Robot. Mi programa te permite mover el mouse durante 10 segundos. Después de los 10 segundos, retrocede en el tiempo y recrea el movimiento del mouse, mientras lo predice perfectamente.

ingrese la descripción de la imagen aquí

Aquí está el código:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;


public class TimeMachine extends JPanel implements MouseMotionListener {

    Timer timer;
    int time = 10;
    java.util.Timer taskTimer;
    ArrayList<Point> mousePoints;
    ArrayList<Long> times;
    Robot robot;
    int width, height;
    ArrayList<Point> drawMousePoints;

    public TimeMachine(){
        width = 500;
        height = 500;
        drawMousePoints = new ArrayList<Point>();

        robot = null;
        try{
            robot = new Robot();
        }
        catch(Exception e){
            System.out.println("The time machine malfunctioned... Reverting to 512 BC");
        }
        mousePoints = new ArrayList<Point>();
        times = new ArrayList<Long>();

        taskTimer = new java.util.Timer();

        ActionListener al = new ActionListener(){
            public void actionPerformed(ActionEvent e){
                time--;
                if(time == 0)
                    rewind();
                repaint();
            }
        };
        timer = new Timer(1000, al);
        start();
    }

    public void paint(Graphics g){
        g.clearRect(0, 0, width, height);
        g.drawString("Time Machine activiates in: " + time, 15, 50);
        for(int i=0; i<drawMousePoints.size(); i++){
            Point drawMousePoint = drawMousePoints.get(i);
            drawMouse(drawMousePoint.x-getLocationOnScreen().x, drawMousePoint.y-getLocationOnScreen().y, g, Color.BLACK, Color.LIGHT_GRAY, (double)i/drawMousePoints.size());
        }
    }

    public void drawMouse(int x, int y, Graphics g, Color line, Color fill, double alpha){
        Graphics2D g2d = (Graphics2D)g;
        g2d.setColor(new Color(fill.getRed(), fill.getGreen(), fill.getBlue(), (int)Math.max(Math.min(alpha*255, 255), 0)));
        g2d.fillPolygon(new int[]{x, x, x+4, x+8, x+10, x+7, x+12}, new int[]{y, y+16, y+13, y+20, y+19, y+12, y+12}, 7);

        g2d.setColor(new Color(line.getRed(), line.getGreen(), line.getBlue(), (int)Math.max(Math.min(alpha*255, 255), 0)));
        g2d.drawLine(x, y, x, y + 16);
        g2d.drawLine(x, y+16, x+4, y+13);
        g2d.drawLine(x+4, y+13, x+8, y+20);
        g2d.drawLine(x+8, y+20, x+10, y+19);
        g2d.drawLine(x+10, y+19, x+7, y+12);
        g2d.drawLine(x+7, y+12, x+12, y+12);
        g2d.drawLine(x+12, y+12, x, y);
    }

    public void start(){
        timer.start();
        prevTime = System.currentTimeMillis();
        mousePoints.clear();
    }

    public void rewind(){
        timer.stop();
        long timeSum = 0;
        for(int i=0; i<times.size(); i++){
            timeSum += times.get(0);
            final boolean done = i == times.size()-1;
            taskTimer.schedule(new TimerTask(){
                public void run(){
                    Point point = mousePoints.remove(0);
                    drawMousePoints.clear();
                    drawMousePoints.addAll(mousePoints.subList(0, Math.min(mousePoints.size(), 30)));
                    robot.mouseMove(point.x, point.y);
                    repaint();
                    if(done)
                        System.exit(0);
                }
            }, timeSum);
        }
    }

    long prevTime = 0;
    public void record(MouseEvent m){
        if(timer.isRunning()){
            long time = System.currentTimeMillis();
            mousePoints.add(new Point(m.getXOnScreen(), m.getYOnScreen()));
            times.add((time-prevTime)/10);
            prevTime = time;
        }
    }

    public static void main(String[] args){

        TimeMachine timeMachine = new TimeMachine();

        JFrame frame = new JFrame("Time Machine");
        frame.setSize(timeMachine.width, timeMachine.height);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        frame.addMouseMotionListener(timeMachine);

        frame.add(timeMachine);
    }

    public void mouseDragged(MouseEvent m) {
        record(m);
    }

    public void mouseMoved(MouseEvent m) {
        record(m);
    }

}
Natillas de ruibarbo
fuente
Código ligeramente optimizado por Netbeans (se deshizo de las advertencias): pastebin.com/E57LZ4zY
Kaz Wolfe
10

Javascript vainilla

Solo para comenzar, aquí hay una predicción simple basada en dos valores. Las últimas nposiciones del mouse se memorizan y se mantienen en una cola, la predicción es una simple extrapolación lineal del primer y último elemento de la cola.

Este es solo el código de predicción, el código completo que incluye la demostración se puede ver en this fiddle:

function predict(trail) {
    var b = trail.pop(),
        a = trail[0],
        d = {
            x: b.x - a.x,
            y: b.y - a.y
        },
        m = Math.sqrt( d.x * d.x + d.y * d.y );

    d.x = 5 * d.x / m;
    d.y = 5 * d.y / m;

    var predictions = [];
    for(var i = 1; i <= 10; i++) {
        predictions.push({
            x: b.x + i * d.x,
            y: b.y + i * d.y
        });
    }

    return predictions;
}

La demostración contiene un comentario en la predicción que le permite utilizar los dos últimos elementos en la cola para la predicción. Hace que el resultado sea más "en tiempo real", pero también menos "fluido".

Si alguien quiere usar el boilerplate workpara implementar un algoritmo de predicción diferente, siéntase libre. No es mucho trabajo de todos modos.

Ingo Bürk
fuente
¿Se puede mostrar un puntero del mouse en lugar de una línea? Esperaba ver "rastros del mouse" pero no puedo ver ninguno, jaja
solo el
La pregunta dice que no tiene que ser un cursor;)
Ingo Bürk
4

Javascript

El pasado es la mejor predicción para el futuro : yo y probablemente también alguien más

Mi solución es muy simple. Primero, aquí está el >>> Fiddle! <<<

Todo lo que hace es cambiar el camino pasado, para que parezca el camino futuro. Básicamente no hay matemáticas involucradas (lo sé, bastante aburrido). Puede ver fácilmente los errores, especialmente al mover el cursor en círculos. Es por eso que hice el camino tan corto;)

El código:

<!DOCTYPE html>
<html>
    <head>
        <style type="text/css">
            .cursor {
                width: 12px;
                height: 19px;
                position: absolute;
                background-image: url(https://i.imgur.com/h8imKBP.png);
            }
        </style>
        <script type="text/javascript">

            var x, y;
            window.onmousemove = function(e) {x=e.clientX; y=e.clientY;}

            var p = [0,0,0,0,0,0,0,0,0,0];
            window.setInterval(function() {
                p.shift();
                p.push([x, y]);
                var diff = [x-p[0][0], y-p[0][1]];
                for (var i = 0; i < 10; i++) {
                    var e = document.getElementById(i);
                    e.style.left = (p[9-i][0]+diff[0])+"px";
                    e.style.top = (p[9-i][1]+diff[1])+"px";
                }
            }, 10);

        </script>
    </head>
    <body>
    <div id="0" class="cursor"></div>
    <div id="1" class="cursor"></div>
    <div id="2" class="cursor"></div>
    <div id="3" class="cursor"></div>
    <div id="4" class="cursor"></div>
    <div id="5" class="cursor"></div>
    <div id="6" class="cursor"></div>
    <div id="7" class="cursor"></div>
    <div id="8" class="cursor"></div>
    <div id="9" class="cursor"></div>
    </body>
</html>
Felk
fuente
jaja Acabo de echar un vistazo a la fecha. Lo que sea, me gusta
Felk