Wyświetl ślady wskaźnika myszy… przyszłości!

24

Zainspirowany tym przykładem użycia d3js , wzywam cię do stworzenia kanwy (lub twojego odpowiednika języka), w którym będą wyświetlane ślady wskaźnika myszy , z następującymi zmianami:

Twist

Nie powinieneś wyświetlać śladów miejsca, w którym znajdował się wskaźnik myszy , ale „śladów” tego, gdzie będzie (mógł) być w przyszłości.

Możesz to zrobić za pomocą:

  1. Wehikuł czasu lub

  2. Szacunki probabilistyczne na podstawie poprzednich ruchów myszy

Założenia

W przypadku, gdy nie wybrałeś implementacji wehikułu czasu, gdy mysz nie porusza się dłużej niż przez milisekundy progowe , nie możesz wyświetlić żadnego śladu. (Wartość progowa zależy od ciebie).

Obraz kursora zależy od Ciebie i nie musi być taki sam jak kursor systemu operacyjnego (możesz nawet narysować zwykłe małe kółka lub kropki).

Nie będzie testowany żaden zły wkład: możesz założyć, że ruchy są płynne. „Gładka” definicja w tym przypadku brzmi: gdyby ruchy myszy były funkcją na osi xiy płótna - byłaby to funkcja ciągła.

Zwycięski

Wygra poprawna odpowiedź z najmniejszą liczbą znaków w kodzie. W przypadku remisu wygra ten, który został ogłoszony jako pierwszy.

EDYCJA: Prawidłowa odpowiedź z największą liczbą głosów zwycięży. W przypadku remisu wygra ten, który został ogłoszony jako pierwszy. Możesz być kreatywny przy implementacji lub być precyzyjny z przewidywaniami. Nie jestem już sędzią, wszyscy jesteśmy :)

  • Prawidłowa odpowiedź musi zawierać sposób, w jaki mogę się bawić (test! Miałem na myśli test), albo w narzędziu online, albo w darmowym kompilatorze / tłumaczu / środowisku uruchomieniowym / itp.
Jakub
źródło
2
Myślę, że to pytanie może lepiej pasować do konkursu popularności niż w golfa kodowego, ponieważ jest dość subiektywne, co kwalifikuje się jako wystarczająco dobra prognoza. Polecam wyjaśnienie tego lub zmianę tagu. Niemniej jednak wygląda zabawnie.
isaacg
2
Masz rację. Zredagowałem pytanie i zmieniłem tag.
Jacob
Czas, aby ktoś zaimplementował algorytmy uczenia maszynowego!
Ingo Bürk
6
Do jakich testów masz dostęp do jakich modeli wehikułu czasu? I czy możemy używać standardowych bibliotek do łączenia się z nimi?
Peter Taylor
1
Tylko matematyk marudzi tutaj: gładki! = Ciągły. W rzeczywistości dziki kolczasty ruch będzie nadal ciągły.
CompuChip

Odpowiedzi:

33

JavaScript

Mój program przewiduje kierunek wskaźnika za pomocą średniej zmiany kątowej w kierunku ostatnich 20 ruchów myszy. Wykorzystuje również wariancję zmiany kątowej, aby utworzyć „chmurę” możliwych lokalizacji i kierunków wskaźnika. Kolor każdego wskaźnika w „chmurze” ma reprezentować prawdopodobieństwo, że jest to nowa pozycja wskaźnika myszy, w której ciemniejsze kolory oznaczają większe prawdopodobieństwo. Odległość chmury wskaźnika przed myszą obliczana jest na podstawie prędkości ruchu myszy. Nie robi najlepszych prognoz, ale wygląda ładnie.

Oto skrzypce: http://jsfiddle.net/5hs64t7w/4/

Zwiększenie rozmiaru chmury wskaźnika jest interesujące. Można to ustawić, zmieniając cloudSizezmienną w pierwszym wierszu programu. Oto skrzypce o rozmiarze chmury 10: http://jsfiddle.net/5hs64t7w/5/

Użyłem tych źródeł, aby uzyskać wzory na średnią i wariancję kołową:
Środek kołowy: http://en.wikipedia.org/wiki/Circular_mean
Okólnik wariancji: http://www.ebi.ac.uk/thornton-srv/software/ PROCHECK / nmr_manual / man_cv.html

Oto kod, jeśli ktoś jest zainteresowany:

    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;
    };
Krem Rabarbarowy
źródło
2
Czy potrafisz wyświetlić sekwencję wskaźnika myszy (ze stałą orientacją) zamiast wskaźnika wskazującego zmienny kierunek? Spodziewałem się zobaczyć „ślady myszy”, ale nie widzę żadnego, haha
tylko
Bardzo fajnie, ale czy nie jest bardziej prawdopodobne, że wskaźnik powinien podnieść się w przyszłości, gdy obecnie spada? Imho, program powinien zrobić dokładnie odwrotnie, więc przewiduje, że wskaźnik pozostanie na ekranie.
Madmenyo,
@MennoGouw nie jest idealny, ale jest cholernie dobry
NimChimpsky
@nimchimpsky Powiedzenie, że prawdopodobieństwo myszki w górę jest wyższe, jeśli mysz aktualnie spadnie. Sam program jest świetny.
Madmenyo
Czy uważasz, że do obsługi myszy można również wykorzystać zwykłe ludzkie zachowanie? Podobnie jak koła, linie proste ... Można je przewidzieć jeszcze bardziej w przyszłości (obliczanie promienia koła po kilku pomiarach i dokończenie koła nawet przed jego szkicowaniem)
Saffron
14

Jawa

Zdecydowałem się na podejście oparte na maszynie czasu. Okazuje się, że kluczowym składnikiem wehikułu czasu jest java.awt.Robot. Mój program pozwala poruszać myszą przez 10 sekund. Po 10 sekundach cofa się w czasie i odtwarza ruch myszy, jednocześnie doskonale ją przewidując.

wprowadź opis zdjęcia tutaj

Oto kod:

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

}
Krem Rabarbarowy
źródło
Kod nieco zoptymalizowany przez Netbeans (pozbył się ostrzeżeń): pastebin.com/E57LZ4zY
Kaz Wolfe
10

Waniliowy Javascript

Aby rozpocząć, oto prosta prognoza oparta na dwóch wartościach. Ostatnie npozycje myszy są zapamiętywane i przechowywane w kolejce, przewidywanie to prosta liniowa ekstrapolacja pierwszego i ostatniego elementu w kolejce.

To tylko kod prognozy, pełny kod wraz z wersją demonstracyjną można zobaczyć w 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;
}

Demo zawiera komentarz w prognozie, który pozwala zamiast tego użyć dwóch ostatnich elementów w kolejce do prognozy. Sprawia, że ​​wynik jest bardziej „w czasie rzeczywistym”, ale także mniej „płynny”.

Jeśli ktoś chce użyć boilerplate workdo implementacji innego algorytmu predykcji, nie krępuj się. To i tak nie jest dużo pracy.

Ingo Bürk
źródło
Czy możesz wyświetlić wskaźnik myszy zamiast linii? Spodziewałem się zobaczyć „ślady myszy”, ale nic nie widzę, haha
tylko
Pytanie mówi, że to nie musi być kursor;)
Ingo Bürk
4

JavaScript

Przeszłość jest najlepszą prognozą na przyszłość - ja i prawdopodobnie także ktoś inny

Moje rozwiązanie jest bardzo proste. Po pierwsze, oto >>> skrzypce! <<<

Wszystko, co robi, to przesuwanie przeszłego szlaku, więc wygląda jak szlak przyszłości. Zasadniczo nie jest zaangażowana matematyka (wiem, dość nudna). Możesz łatwo zobaczyć błędy, szczególnie podczas przesuwania kursora w kółko. Dlatego tak krótko skróciłem szlak;)

Kod:

<!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
źródło
haha Właśnie spojrzałem na datę. Cokolwiek mi się podoba
Felk