Affichez les traces du pointeur de la souris… du futur!

24

Inspiré par cet exemple d'utilisation de d3js , je vous mets au défi de créer un canevas (ou votre équivalent de langue de choix) dans lequel les traces du pointeur de la souris seront affichées, avec la torsion suivante:

La torsion

Vous ne devriez pas afficher les pistes de l' endroit où le pointeur de la souris était , mais les « pistes » de l' endroit où il va (peut) être à l' avenir.

Vous pouvez le faire en utilisant soit:

  1. Une machine à voyager dans le temps, ou

  2. Estimations probabilistes basées sur les mouvements précédents de la souris

Hypothèses

Si vous n'avez pas choisi la mise en œuvre de la machine à temps, lorsque la souris ne bouge pas pendant plus de seuil millisecondes, vous pouvez afficher aucun des sentiers. ( Vous pouvez choisir la valeur seuil ).

L'image du curseur dépend de vous et ne doit pas nécessairement être la même que celle du curseur du système d'exploitation (vous pouvez même dessiner de petits cercles ou points simples).

Aucune mauvaise entrée ne sera testée: vous pouvez supposer que les mouvements sont fluides. La définition «lisse» pour ce cas est: si les mouvements de la souris étaient une fonction sur les axes x et y de la toile - ce serait une fonction continue.

Gagnant

La réponse valide avec le moins de caractères dans le code gagnera. En cas d'égalité - celui qui a été affiché en premier gagnera.

EDIT: La réponse valide avec le plus de votes positifs gagnera. En cas d'égalité - celui qui a été affiché en premier gagnera. Vous pouvez être créatif sur la mise en œuvre ou être précis avec la prédiction. Je ne suis plus juge, nous le sommes tous :)

  • Une réponse valide doit inclure un moyen pour moi de jouer avec (test! Je voulais dire test), soit sur un outil en ligne, soit sur un compilateur / interprète / runtime / etc librement téléchargeable.
Jacob
la source
2
Je pense que cette question pourrait être mieux adaptée à un concours de popularité qu'à un golf de code, car elle est assez subjective quant à ce qui constitue une prédiction suffisamment bonne. Je recommanderais de clarifier cela ou de changer la balise. Néanmoins, ça a l'air amusant.
isaacg
2
Tu as raison. J'ai édité la question et changé la balise.
Jacob
Il est temps pour quelqu'un de mettre en œuvre des algorithmes d'apprentissage automatique!
Ingo Bürk
6
À des fins de test, à quels modèles de machine à remonter le temps avez-vous accès? Et pouvons-nous utiliser des bibliothèques standard pour interfacer avec elles?
Peter Taylor
1
Juste un mathématicien pleurnichant ici: lisse! = Continu. En fait, le mouvement spikey sauvage sera toujours continu.
CompuChip

Réponses:

33

Javascript

Mon programme prédit la direction du pointeur en utilisant la moyenne du changement angulaire dans la direction des 20 derniers mouvements de la souris. Il utilise également la variance du changement angulaire pour créer un "nuage" d'emplacements et de directions possibles du pointeur. La couleur de chaque pointeur dans le "nuage" est censée représenter la probabilité qu'il s'agisse de la nouvelle position du pointeur de la souris, où des couleurs plus sombres représentent une plus grande probabilité. La distance du nuage de pointeurs devant la souris est calculée en utilisant la vitesse de déplacement de la souris. Il ne fait pas les meilleures prédictions mais il a l'air soigné.

Voici un violon: http://jsfiddle.net/5hs64t7w/4/

L'augmentation de la taille du nuage de pointeurs est intéressante à voir. Il peut être défini en modifiant la cloudSizevariable sur la première ligne du programme. Voici un violon avec une taille de nuage de 10: http://jsfiddle.net/5hs64t7w/5/

J'ai utilisé ces sources pour obtenir des formules de moyenne et de variance
circulaires: Moyenne circulaire: http://en.wikipedia.org/wiki/Circular_mean
Variation circulaire: http://www.ebi.ac.uk/thornton-srv/software/ PROCHECK / nmr_manual / man_cv.html

Voici le code si quelqu'un est intéressé:

    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;
    };
Crème pâtissière à la rhubarbe
la source
2
Pouvez-vous afficher une séquence de pointeur de souris (avec une orientation fixe) au lieu d'un pointeur pointant vers une direction variable? Je m'attendais à voir des "traces de souris" mais je n'en vois pas, haha
juste la moitié du
Très bien, mais n'est-il pas plus plausible que le pointeur monte à l'avenir alors qu'il descend actuellement? À mon humble avis, le programme devrait faire exactement le contraire pour qu'il prédit que le pointeur reste à l'écran.
Madmenyo
@MennoGouw ce n'est pas parfait mais son sacrément bon
NimChimpsky
@nimchimpsky Il suffit de dire que la probabilité que la souris monte est plus élevée si la souris descend actuellement. Le programme lui-même est génial.
Madmenyo
Pensez-vous qu'il est également possible d'utiliser le comportement humain habituel pour la manipulation de la souris? Comme des cercles, des lignes droites ... Celles-ci pourraient être prédites encore plus à l'avenir (calculer le rayon du cercle après quelques mesures et terminer le cercle avant même que vous ne l'ayez rédigé)
Safran
14

Java

J'ai décidé d'adopter l'approche de la machine à remonter le temps. Il s'avère que l'ingrédient clé d'une machine à voyager dans le temps est java.awt.Robot. Mon programme vous permet de déplacer votre souris pendant 10 secondes. Après les 10 secondes, il remonte dans le temps et recrée le mouvement de votre souris, tout en le prédisant parfaitement.

entrez la description de l'image ici

Voici le code:

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

}
Crème pâtissière à la rhubarbe
la source
Code légèrement optimisé par Netbeans (débarrassé des avertissements): pastebin.com/E57LZ4zY
Kaz Wolfe
10

Vanilla Javascript

Pour commencer, voici une prédiction simple basée sur deux valeurs. Les dernières positions de la nsouris sont mémorisées et conservées dans une file d'attente, la prédiction est une simple extrapolation linéaire du premier et du dernier élément de la file d'attente.

Ceci est juste le code de prédiction, le code complet incluant la démo peut être vu dans 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 démo contient un commentaire dans la prédiction qui vous permet d'utiliser à la place les deux derniers éléments de la file d'attente pour la prédiction. Rend le résultat plus "en temps réel", mais aussi moins "fluide".

Si quelqu'un veut utiliser le boilerplate workpour implémenter un algorithme de prédiction différent, n'hésitez pas. Ce n'est pas beaucoup de travail de toute façon.

Ingo Bürk
la source
Pouvez-vous afficher un pointeur de souris au lieu d'une ligne? Je m'attendais à voir des "traces de souris" mais je n'en vois pas, haha
juste la moitié du
La question dit qu'il ne doit pas être un curseur;)
Ingo Bürk
4

Javascript

Le passé est la meilleure prédiction pour l'avenir - moi, et probablement quelqu'un d'autre aussi

Ma solution est très simple. Voici d'abord le >>> Fiddle! <<<

Tout ce qu'il fait, c'est déplacer la piste passée, donc cela ressemble à la piste future. Fondamentalement, aucune math n'est impliquée (je sais, assez ennuyeux). Vous pouvez facilement voir les erreurs, en particulier lorsque vous déplacez le curseur en cercles. C'est pourquoi j'ai rendu le sentier si court;)

Le code:

<!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
la source
haha je viens de jeter un oeil à la date. Quoi qu'il en soit, j'aime ça
Felk