Exiba as trilhas do ponteiro do mouse ... do futuro!


24

Inspirado neste exemplo de uso do d3js , desafio você a criar uma tela (ou seu idioma equivalente) na qual as trilhas do ponteiro do mouse serão exibidas, com o seguinte toque:

The Twist

Você não deve exibir as trilhas de onde estava o ponteiro do mouse , mas as "trilhas" de onde estará (poderá) no futuro.

Você pode fazer isso usando:

  1. Uma máquina do tempo, ou

  2. Estimativas probabilísticas baseadas em movimentos anteriores do mouse

Suposições

Caso você não tenha escolhido a implementação da máquina do tempo, quando o mouse não se mover por mais do que o limite de milissegundos, você não poderá exibir nenhuma das trilhas. (O valor limite depende de você escolher).

A imagem do cursor é sua e não precisa ser a mesma do cursor do sistema operacional (você pode desenhar pequenos círculos ou pontos simples).

Nenhuma entrada ruim será testada: você pode assumir que os movimentos são suaves. A definição 'suave' para este caso é: se os movimentos do mouse fossem uma função sobre os eixos xey da tela - seria uma função contínua.

Ganhando

A resposta válida com o mínimo de caracteres no código vencerá. Em caso de empate - o que foi lançado primeiro ganhará.

EDIT: A resposta válida com o maior número de votos será vencedora. Em caso de empate - o que foi lançado primeiro ganhará. Você pode ser criativo na implementação ou ser preciso com a previsão. Eu não sou mais o juiz, todos somos :)

  • Uma resposta válida deve incluir uma maneira de brincar (test! Quis dizer test), em uma ferramenta on-line ou em um compilador / intérprete / tempo de execução / etc.

2
Acho que essa pergunta pode ser mais adequada a um concurso de popularidade do que a um código de golfe, porque é bastante subjetivo sobre o que qualifica como uma previsão suficientemente boa. Eu recomendaria esclarecer isso ou alterar a tag. No entanto, parece divertido.
Isaacg

2
Você está certo. Editei a pergunta e mudei a tag.
Jacob

Hora de alguém implementar algoritmos de aprendizado de máquina!
Ingo Bürk

6
Para fins de teste, a quais modelos de máquina do tempo você tem acesso? E podemos usar bibliotecas padrão para interagir com elas?
Peter Taylor

11
Apenas um matemático choramingando aqui: suave! = Contínuo. De fato, o movimento espigão selvagem ainda será contínuo.
CompuChip

Respostas:


33

Javascript

Meu programa prevê a direção do ponteiro usando a média da mudança angular na direção dos últimos 20 movimentos do mouse. Ele também usa a variação da alteração angular para criar uma "nuvem" de possíveis locais e direções do ponteiro. A cor de cada ponteiro na "nuvem" deve representar a probabilidade de ser a nova posição do ponteiro do mouse, onde cores mais escuras representam uma maior probabilidade. A distância da nuvem do ponteiro à frente do mouse é calculada usando a velocidade do movimento do mouse. Não faz as melhores previsões, mas parece limpo.

Aqui está um violino: http://jsfiddle.net/5hs64t7w/4/

É interessante ver o aumento do tamanho da nuvem de ponteiros. Pode ser definido alterando a cloudSizevariável na primeira linha do programa. Aqui está um violino com um tamanho de nuvem de 10: http://jsfiddle.net/5hs64t7w/5/

Usei essas fontes para obter fórmulas para média circular e variação:
Média Circular: http://en.wikipedia.org/wiki/Circular_mean
Variação Circular: http://www.ebi.ac.uk/thornton-srv/software/ PROCHECK / nmr_manual / man_cv.html

Aqui está o código, se alguém estiver interessado:

    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;
    };

2
Você pode exibir uma sequência de ponteiro do mouse (com orientação fixa) em vez de um ponteiro apontando para direção variável? Eu estava esperando para ver "rastros do mouse", mas não consigo ver nenhuma, haha
justhalf

Muito bom, mas não é mais plausível que o ponteiro suba no futuro quando estiver atualmente inativo? No entanto, o programa deve fazer exatamente o oposto para que ele preveja que o ponteiro permanece na tela.
Madmenyo

@MennoGouw não é perfeito mas é bom bem danado
Nim Chimpsky

@nimchimpsky Só de dizer que a probabilidade de o mouse subir é maior se o mouse cair no momento. O programa em si é ótimo.
Madmenyo

Você acha que também é possível usar o comportamento humano usual para o manuseio do mouse? Como círculos, linhas retas ... Estes poderiam ser previstos ainda mais no futuro (o cálculo do raio do círculo depois de alguns mesures, e terminando o círculo antes mesmo de você redigiu)
Saffron

14

Java

Decidi adotar a abordagem da máquina do tempo. Acontece que o principal ingrediente de uma máquina do tempo é o java.awt.Robot. Meu programa permite que você mova o mouse por 10 segundos. Após os 10 segundos, ele volta no tempo e recria o movimento do mouse, prevendo-o perfeitamente.

insira a descrição da imagem aqui

Aqui está o 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);
    }

}

Código levemente otimizado pelo Netbeans (se livrou dos avisos): pastebin.com/E57LZ4zY
Kaz Wolfe

10

Javascript de baunilha

Apenas para começar, aqui está uma previsão simples com base em dois valores. As últimas nposições do mouse são memorizadas e mantidas em uma fila, a previsão é uma extrapolação linear simples do primeiro e do último elemento na fila.

Este é apenas o código de previsão, o código completo, incluindo a demonstração, pode ser visto em 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;
}

A demonstração contém um comentário na previsão que permite que você use os dois últimos elementos na fila para a previsão. Torna o resultado mais "em tempo real", mas também menos "suave".

Se alguém quiser usar o boilerplate workpara implementar um algoritmo de previsão diferente, fique à vontade. De qualquer forma, não é muito trabalho.


Você pode exibir um ponteiro do mouse em vez de uma linha? Eu estava esperando para ver "rastros do mouse", mas não consigo ver nenhuma, haha
justhalf

A pergunta diz que não precisa ser um cursor;)
Ingo Bürk

4

Javascript

O passado é a melhor previsão para o futuro - eu, e provavelmente outra pessoa também

Minha solução é muito simples. Primeiro, aqui está o >>> Fiddle! <<<

Tudo o que faz é mudar a trilha do passado, para que pareça a trilha do futuro. Basicamente, nenhuma matemática está envolvida (eu sei, muito chata). Você pode ver facilmente os erros, especialmente ao mover o cursor em círculos. Foi por isso que fiz a trilha tão curta;)

O 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>

haha, acabei de dar uma olhada na data. Seja como for, eu gosto
Felk
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.