Lo primero que tenemos que hacer es decidir cómo detectar si estamos dibujando el primer o segundo trazo de la X. Aunque se puede hacer de muchas maneras en este caso utilizaremos un intervalo de tiempo. Se dibuja el primer trazo (se pulsa, se arrastra y se suelta el botón) y se espera al segundo trazo. Si se tarda mucho en hacer el segundo trazo entendemos que no estamos "escribiendo" una X si no que hemos hecho una raya y un rato después otra.
De cada trazo nos quedaremos sólo con el primer y último punto .
Para hacer lo anterior, en un JPanel, simplemente tenemos que agregarle un MouseListener para procesar los eventos del ratón.
Si se produce un evento pressed o entered llamaremos al método:
private void pressedOEntered(MouseEvent e) {
long tiempo = (System.nanoTime() - tiempoPrimeraRecta)/1000000;
System.out.println("Tiempo: " + tiempo);
if (tiempo > MAX_TIEMPO_ENTRE_RECTAS) {
// Es la primera recta
x0 = e.getX();
y0 = e.getY();
primeraRecta = true;
System.out.println("x0, y0: " + x0 + ", " + y0);
}
else {
// Es la segunda recta
x0p = e.getX();
y0p = e.getY();
primeraRecta = false;
System.out.println("x0p, y0p: " + x0p + ", " + y0p);
}
}
Si se produce un evento released o exited llamaremos al
método:
private void releasedOExited(MouseEvent e) {
if (primeraRecta) {
x1 = e.getX();
y1 = e.getY();
tiempoPrimeraRecta = System.nanoTime();
System.out.println("x1, y1: " + x1 + ", " + y1);
}
else {
x1p = e.getX();
y1p = e.getY();
System.out.println("x1p, y1p: " + x1p + ", " + y1p);
comprobarX();
}
}
En el momento que se finaliza el segundo trazo llamaremos al método que comprueba si es o no una X. La comprobación que realizamos es la siguiente:
- Son dos rectas con pendientes de signo distinto. No permitimos una recta totalmente vertical (pendiente infinita) ni X con rectas con la misma pendiente:
- El punto de corte pertenece al dominio de los dos trazados:
La pendiente de una recta es su tangente:
El método que las calcula para nuestras rectas es:
private double getPendiente(double x0, double y0, double x1, double y1) {
double pendiente;
if (x0 < x1)
pendiente = (y1-y0)/(x1-x0);
else if (x0 > x1)
pendiente = (y0-y1)/(x0-x1);
else
throw new IllegalArgumentException("Pendiente infinita");
return pendiente;
}
Calculamos el punto b de cada recta partiendo de que ya tenemos las pendientes: y = mx + b => b = y – mx.
Una vez calculadas las rectas resolvemos el sistema de ecuaciones para calcular el punto de corte:
Quedando el método:
private void comprobarX() {
try {
double m = getPendiente(x0,y0,x1,y1);
System.out.println("Pendiente 1: " + m);
double mp = getPendiente(x0p,y0p,x1p,y1p);
System.out.println("Pendiente 2: " + mp);
if (m * mp >0)
System.out.println("No es X, pendientes del mismo signo");
else {
// Tenemos que ver si se cortan, y=mx+b => b = y-mx => b = y0-m * x0
double b = y0 - m * x0;
double bp = y0p - mp * x0p;
// y = mx + b, y=mp x + bp => mx+b = mp x + bp => x = (bp - b)/(m-mp), y= (m bp - mp b) / (m - mp)
double corteX = (bp - b)/(m - mp);
double corteY = (m * bp - mp * b) / (m - mp);
System.out.println("Punto de corte: " + corteX + ", " + corteY);
// Vemos si el punto de corte esta dentro del dominio de los segmentos trazados
if ( (Math.min(x0,x1) < corteX && Math.max(x0,x1) > corteX) && (Math.min(x0p, x1p) < corteX && Math.max(x0p,x1p)> corteX) && (Math.min(y0,y1) < corteY && Math.max(y0,y1) > corteY) && (Math.min(y0p, y1p) < corteY && Math.max(y0p,y1p)> corteY)) {
System.out.println("¡¡¡¡¡ Es x !!!!");
}
else
System.out.println("No es x");
}
}
catch(IllegalArgumentException iae) {
// No vale;
System.out.print("Pendiente infinita, no vale como X");
}
}
El código java del ejemplo completo: