Показуйте сліди вказівника миші… майбутнього!


24

Натхненний цим прикладом використання d3js , я закликаю вас створити полотно (або ваш еквівалент вибору мови), в якому будуть відображатися сліди вказівника миші , з наступним поворотом:

Твіст

Ви не повинні показувати сліди , де покажчик миші був , але «сліди» , де вона буде (може) бути в майбутньому.

Це можна зробити, скориставшись будь-яким:

  1. Машина часу, або

  2. Імовірнісні оцінки на основі попередніх рухів миші

Припущення

Якщо ви не вибрали впровадження машини часу, коли миша не рухається більше, ніж порогові мілісекунди, ви не зможете відобразити жодного із контурів. ( Порогове значення вибираєте самі).

Зображення курсору залежить від вас і не повинно бути однаковим курсором ОС (ви можете навіть намалювати прості маленькі кола або крапки).

Жодне зле введення не перевірятиметься: ви можете припустити, що рухи плавні. Визначення "гладкого" для цього випадку таке: якби рухи миші були функцією над віссю x і y полотна - це було б безперервною функцією.

Перемога

Дійсна відповідь з найменшими символами коду виграє. У разі нічиєї - переможе той, хто був розміщений першим.

РЕДАКТУЙТЕ: Дійсна відповідь з найбільшою кількістю результатів виграє. У разі нічиєї - переможе той, хто був розміщений першим. Ви можете бути творчими щодо впровадження або бути точними з прогнозом. Я вже не суддя, ми всі :)

  • Дійсна відповідь повинна містити спосіб для мене (тест! Я мав на увазі тест), або в Інтернеті, або у компіляторі / інтерпретаторі / програмі для виконання, який можна завантажити безкоштовно тощо.

2
Я думаю, що це питання може бути більш підходящим для змагань із популярністю, ніж кодовий гольф, оскільки це досить суб'єктивно щодо того, що кваліфікується як досить хороший прогноз. Я рекомендую уточнити це або змінити тег. Тим не менш, виглядає весело.
isaacg

2
Ти правий. Я відредагував питання і змінив тег.
Яків

Час комусь реалізувати алгоритми машинного навчання!
Інго Бюрк

6
До яких моделей машини часу ви маєте доступ для тестування? І чи можемо ми використовувати стандартні бібліотеки для взаємодії з ними?
Пітер Тейлор

1
Просто математик, який скуголить тут: гладко! = Безперервно. Насправді рух дикого колоска все ще буде безперервним.
CompuChip

Відповіді:


33

Javascript

Моя програма прогнозує напрямок вказівника, використовуючи середнє значення кутової зміни напрямку 20 останніх рухів миші. Він також використовує дисперсію кутової зміни для створення "хмари" можливих місць та напрямків вказівника. Колір кожного вказівника у "хмарі" повинен представляти ймовірність того, що це буде нове положення вказівника миші, де більш темні кольори представляють більшу ймовірність. Відстань хмари вказівника попереду миші обчислюється за допомогою швидкості руху миші. Це не найкращі прогнози, але це виглядає акуратно.

Ось загадка: http://jsfiddle.net/5hs64t7w/4/

Збільшення розміру хмари вказівника цікаво побачити. Його можна встановити, змінивши cloudSizeзмінну в першому рядку програми. Ось скрипка з розміром хмари 10: http://jsfiddle.net/5hs64t7w/5/

Я використовував ці джерела для отримання формул для кругової середньої та дисперсійної форми:
Кругова середня: http://en.wikipedia.org/wiki/Circular_mean
Кругова варіація : http://www.ebi.ac.uk/thornton-srv/software/ PROCHECK / nmr_manual / man_cv.html

Ось код, якщо хтось зацікавлений:

    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
Чи можете ви відображати послідовність вказівника миші (з фіксованою орієнтацією) замість вказівника, що вказує на змінний напрямок? Я очікував побачити "
мишові

Дуже приємно, але чи не є більш правдоподібним те, що вказівник повинен підніматися в майбутньому, коли він в даний час знижується? Імхо, програма повинна робити прямо навпаки, тому вона передбачає, що покажчик залишається на екрані.
Мадменйо

@ MennoGouw це не ідеально, але його досить прокляте добре
NimChimpsky

@nimchimpsky Просто кажучи, що ймовірність підйому миші вище, якщо миша в даний час знижується. Сама програма чудова.
Мадменйо

Як ви вважаєте, чи можна також використовувати звичну поведінку людини для роботи з мишею? Як круги, прямі лінії ... Це можна було б передбачити ще в майбутньому (обчислення радіуса кола після пари мешів і закінчення кола ще до того, як ви його склали)
Шафран

14

Java

Я вирішив скористатися машинним підходом часу. Виявляється, ключовим інгредієнтом машини часу є java.awt.Robot. Моя програма дозволяє переміщати мишу протягом 10 секунд. Через 10 секунд він повертається у часі і відтворює ваш рух миші, одночасно передбачуючи це.

введіть тут опис зображення

Ось код:

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

}

Код, дещо оптимізований Netbeans (позбувся попереджень): pastebin.com/E57LZ4zY
Kaz Wolfe

10

Ванільний Javascript

Просто для початку, ось простий прогноз, заснований на двох значеннях. Останні nпозиції миші запам'ятовуються та зберігаються у черзі, передбачення - це проста лінійна екстраполяція першого та останнього елемента у черзі.

Це лише код передбачення, повний код, включаючи демо-версію, можна побачити на 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;
}

Демонстрація містить коментар у передбаченні, що дозволяє замість цього використовувати два останні елементи в черзі для прогнозування. Робить результат більш "в режимі реального часу", але також менш "плавним".

Якщо хтось хоче скористатися boilerplate workдля реалізації іншого алгоритму прогнозування, не соромтеся. Це все одно не багато роботи.


Чи можете ви відображати вказівник миші замість лінії? Я очікував побачити "
мишові

У запитанні сказано, що це не повинно бути курсором;)
Ingo Bürk,

4

Javascript

Минуле - найкращий прогноз на майбутнє - і я, і, можливо, ще хтось

Моє рішення дуже просте. По-перше, ось >>> Fiddle! <<<

Все, що вона робить - це зміщення минулого сліду, так що це виглядає як майбутній слід. В основному жоден математик не бере участь (я знаю, досить нудно). Ви легко бачите помилки, особливо при переміщенні курсору по колах. Тому я зробив слід таким коротким;)

Кодекс:

<!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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.