2010-03-06 17 views
12

Sto osservando un modo semplice e programmatico per rilevare se l'utente ha disegnato o meno una forma circolare. Sto lavorando in C, ma sono felice di lavorare con lo pseudo-codice. Un po 'di Google utilizza un numero di metodi (auspicabilmente) eccessivamente complessi.Rilevamento di un semplice gesto circolare

Sto monitorando le coordinate del mouse come float e ho creato una serie di vettori per tracciare il movimento del mouse nel tempo. In sostanza sto cercando di rilevare quando un cerchio è stato disegnato e quindi di eliminare tutti i dati di movimento non associati a quel cerchio.

Ho un idea di base di come questo potrebbe essere realizzato:

traccia tutti i movimenti che utilizzano una funzione polling. Ogni volta che la funzione viene interrogata, viene memorizzata la posizione corrente del mouse. Qui, passiamo in rassegna i dati storici di posizione e facciamo un approssimativo 'scatto alla posizione' per confrontare le due posizioni. Se la nuova posizione si trova a una distanza sufficiente da una vecchia posizione, rimuoviamo tutti i dati storici prima della vecchia posizione.

Mentre questo funziona in teoria, è un casino nella pratica. Qualcuno ha qualche suggerimento? Punti bonus se il metodo suggerito può rilevare se è stato disegnato in senso orario o antiorario.

risposta

6

In base alla funzione di rilevamento/polling, che spinge le coppie flottanti su una pila. Questo deve essere fatto su un normale intervallo di tempo.

  1. Effettuare una ricerca basata sulla soglia per due voci uguali nell'elenco. Ora hai due indici nel tuo stack; la prima e la seconda voci uguali. Consideralo come una linea.
  2. Ottieni la differenza assoluta negli indici. Quindi dividere per due e ottenere le coordinate di questo punto. (Centro della linea.)
  3. Hai due punti: quindi puoi ottenere il raggio del cerchio, ottenendo la distanza tra i due punti divisi per due.
  4. Dividere il numero del passaggio 2 per 2, ora hai i quarti.

    Se la linea al passaggio 1 è verticale e il primo punto della linea è in alto: Se il primo quarto è a sinistra del punto centrale, il cerchio è stato disegnato in senso antiorario. Se il primo quarto è a destra del punto centrale, il cerchio è stato disegnato in senso orario. Se il primo punto della linea si trova in basso, all'indietro (cioè ccw => cw e cw => ccw)

    Se la linea al passaggio 1 è orizzontale e il primo punto dell'elenco è a sinistra: Se il primo quarto è sopra il punto centrale, il cerchio è stato disegnato in senso antiorario. Se il primo quarto è inferiore al punto centrale, il cerchio è stato disegnato in senso orario. Se il primo punto della linea è a destra, retromarcia.

  5. Verificare se si tratta di un cerchio: itera su tutte le coppie di coordinate e calcola la distanza dal punto centrale. Modificare la soglia delle distanze consentite dalla distanza calcolata e la distanza effettiva dal punto centrale.

Nei passaggi 2 e 4 è possibile modificare ulteriormente questo algoritmo prendendo la media di più indici se l'intervallo di temporizzazione è molto basso (polling veloce). Ad esempio: ci sono 30 coppie nella matrice, quindi le coppie medie a 0, 1 e 28, 29 per ottenere il punto superiore. Fai lo stesso per tutti gli altri punti.

Spero che sia abbastanza facile.

+0

Se il rilevamento dei gesti è l'argomento principale, in realtà andrei con una sorta di evento che attiva un timer. Attualmente sto lavorando su gesti personalizzati usando Qt e io uso la pressione del pulsante di un mouse come punto di partenza per la raccolta di campioni. Questo timer verrà quindi arrestato quando si verifica un altro evento specifico (ad esempio rilasciando il pulsante del mouse). Durante il punto di inizio e di fine del tempo, è possibile rilevare se il mouse si sta muovendo e raccogliere le posizioni del cursore. In questo modo avrai meno dati poveri più insiemi di punti ben definiti ciascuno descrivendo un movimento dall'inizio alla fine. – rbaleksandar

0

Non ho provato questo, ma l'idea è venuta in mente leggendo la tua domanda, così potrebbe anche condividere con voi:

Sto assumendo il cerchio deve essere redatto entro un ragionevole lasso di tempo, data una costante "frequenza di campionamento" del mouse che lascerebbe una matrice di dimensioni note di vettori 2D (punti). Aggiungili tutti e dividi per il conteggio dei vettori 2D per ottenere una stima del punto "centro" dell'array. Quindi forma i vettori da questo punto centrale ai punti nell'array e fai i prodotti punto (normalizzandoli per lunghezza del vettore), assicurandoti che il segno dei prodotti punto rimanga identico per un intervallo di punti significa che tutti i punti si muovono tutti nello stesso direzione, un segno positivo indicherà il movimento in senso antiorario, il negativo è esattamente l'opposto. Se l'angolo accumulato supera 2 PI, è stato disegnato un movimento circolare.

Buona fortuna.

+0

Usare il tempo per limitare il movimento è una buona idea. Lo sto anche usando. Se un utente vuole disegnare un cerchio, la sua intuizione gli dirà di farlo nel miglior modo possibile, quindi la probabilità che si verifichino loop di confine o alcuni strani movimenti non circolari si riduce un po '. Il problema è che il tempo potrebbe sembrare ok per un utente, ma essere completamente fuori per un altro, rendendo questa soluzione tutt'altro che perfetta. – rbaleksandar

4

Sei decisamente sulla strada giusta. IMHO. Fondamentalmente è necessario confrontare ciascun punto del mouse con il punto del mouse precedente e calcolare l'angolo tra di essi (come previsto su un cerchio unitario in cui il primo punto è all'origine). Per questo si può usare la formula:

double angle = atan2(y2 - y1, x2 - x1) * 180/PI; 

if (angle < 0) 
    angle += 360; 

cosa si finisce con è che per il movimento in senso orario, l'angolo scorrerà in una direzione positiva, mentre per il movimento in senso antiorario l'angolo si pedala in una direzione negativa. È possibile capire se l'angolo di corrente è maggiore o minore di quella precedente con la seguente logica:

if (angle2 > 270 && angle1 < 90) 
{ 
    angle1 += 360 
} 
else if (angle1 > 270 && angle2 < 90) 
{ 
    angle2 += 360 
} 

bool isPositive = (angle2-angle1 > 0); 

Se si ottiene un certo numero di vettori di tutto con gli angoli che stanno aumentando (isPositive è vero, diciamo, 10 volte), si può presumere che venga disegnato un cerchio in senso orario; se la tendenza è negativa (isPositive è false 10 volte) è un cerchio in senso antiorario. :)

+0

Bello ma questo in realtà può essere applicato a qualsiasi forma in cui i punti del bordo vanno in senso orario/antiorario. Puoi disegnare un quadrato, un triangolo ecc. Con la stessa logica e forse ottenere ancora un cerchio. Ciò è dovuto al movimento della mano e del mouse. È altamente improbabile che l'utente possa disegnare una linea perfetta (come un lato di un quadrato, ad esempio), quindi le curve sono il risultato probabile che si tradurrà in un falso positivo. Ovviamente non è un problema così grande se si devono rilevare solo cerchi e nient'altro che segue lo stesso percorso logico. – rbaleksandar

Problemi correlati