import processing.serial.*;
import jklabs.monomic.*;
Monome m; // New instance of monome called m
int goButtonX, goButtonY; // Position of go button
int recordButtonX, recordButtonY; // Position of record button
int recordButtonSize, goButtonSize = 50; // Diamater of both buttons
int statusBoxX, statusBoxY; // Position of status box
int statusBoxWidth, statusBoxHeight; // Dimensions of the status box
int numberOfCoords = 0; // Number of recorded coordinates
int counter = 0; // Used during authentication to count the remaining keystrokes
color statusBoxColour, statusBoxRecordColour, statusBoxAuthColour; // Colour of the satus box;
color recordButtonColour, goButtonColour; // Colour of both buttons
color recordButtonHighlight, goButtonHighlight; // Colour of both buttons when highlighted
boolean goButtonOver = false; // Boolean var to denote if the go button is rolled over
boolean recordButtonOver = false; // As above for record button
boolean Record = false; // Boolean var to denote whether the monome is recording keystrokes
boolean RecordedOK = false;
boolean Auth = false; // Boolean var to denote whether the monome is authenticating a user
int storedCoords[][] = new int[10][3]; // 2d array to store coordinates
int arrayPlace = 0; // Counter var for handling the array
void setup() { // Method run once at startup
background(102); // Set the background colour
size(200, 200); // Size of the convas
smooth(); // Smooth edges
recordButtonColour = color(255,100,100); // Colour of the record button
recordButtonHighlight = color(255,140,140); // Colour of the record button when highlighted
goButtonColour = color(132,255,132); // Go button colour
goButtonHighlight = color(179,255,179); // Colour of the go button when highlighted
statusBoxColour = color(204); // Status box colour
statusBoxRecordColour = color(255,100,100); // Status box colour when recording
statusBoxAuthColour = color(132,255,132); // Status box colour when authenticating
goButtonX = 130; // X and Y coordinates for the go button
goButtonY = 70;
recordButtonX = 20; // X and Y coordinates for the record button
recordButtonY = 70;
goButtonSize = 50; // Dimensions of the two buttons
recordButtonSize = 50;
statusBoxX = 14; // X and Y coords for the status box
statusBoxY = 168;
statusBoxWidth = 170; // dimensions of the status box
statusBoxHeight = 20;
m = new MonomeOSC(this); // Tell the monome m to use the OSC protocol to communicate
PFont fontA = loadFont("Ziggurat-HTF-Black-32.vlw"); // Select the font for words
PFont fontB = loadFont("ArialMT-15.vlw"); // Second font for words
textFont(fontA, 20); // Select the size for wods
fill(0); // Select the colour
text("Pattern-Based", 16,25 ); // Words, with coordinates
fill(51); // Change colour
text("Authentication", 13, 48);
textFont(fontA, 15); // Change font size
fill(204);
text("Record", 17, 138);
text("Pattern", 14, 158);
fill(204);
text("Attempt", 120, 138);
text("Pattern", 123, 158);
textFont(fontB, 15); // Change font size
}
void draw() // Looping method running constantly
{
update(mouseX, mouseY); // Call method update with current x and y coordinates of the mouse
if(recordButtonOver) { // If mouse is over the record button
fill(recordButtonHighlight); // Then set the corect colour
}
else { // If the mouse is NOT over the record button
fill(recordButtonColour); // Set the correct colour
}
stroke(0); // Line border colour
rect(recordButtonX, recordButtonY, recordButtonSize, recordButtonSize); // Draw the rectangle
if(goButtonOver) { // As above but for the go button
fill(goButtonHighlight);
}
else {
fill(goButtonColour);
}
stroke(0);
rect(goButtonX, goButtonY, goButtonSize, goButtonSize);
fill(statusBoxColour);
if(Record) fill(statusBoxRecordColour);
if(Auth) fill(statusBoxAuthColour); // Colour when authenticating
rect(statusBoxX, statusBoxY, statusBoxWidth, statusBoxHeight);
if(Record) {
fill(100);
text("Recording", statusBoxX+52, statusBoxY+16);
}
if(RecordedOK && !Auth) {
fill(100);
text ("Recorded "+numberOfCoords+" keystrokes", statusBoxX+10, statusBoxY+16);
}
if(Auth) {
fill(100);
text("Authenticate Now", statusBoxX+30, statusBoxY+16);
}
if (!Record && !RecordedOK && !Auth) {
fill(100);
text("Ready", statusBoxX+64, statusBoxY+16);
}
}
void update(int x, int y) // Method to update the colours of the buttons
{
if( overRect(recordButtonX, recordButtonY, recordButtonSize, recordButtonSize) ) { // Check to see if the mouse is over the record button
recordButtonOver = true; // Set the boolean var to true
goButtonOver = false;
}
else if ( overRect(goButtonX, goButtonY, goButtonSize, goButtonSize) ) { // Check to see if the mouse is over the go button
recordButtonOver = false;
goButtonOver = true; // Set the boolean var to true
}
else { // Is the mouse is not over any button
recordButtonOver = goButtonOver = false; // Set both vars to false
}
}
void mousePressed() // When the mouse has been pressed
{
if(recordButtonOver) { // If the boolean mouse over var is true
recordButtonCommand(); // Run the method asociated with the record button
}
if(goButtonOver) { // As above but for go button
goButtonCommand();
}
}
boolean overRect(int x, int y, int width, int height) // Method to check to see if the mouse is over a rectangle
{
if (mouseX >= x && mouseX <= x+width && // If the mouse is within the boundaries of a rectangle
mouseY >= y && mouseY <= y+height) {
return true; // return true
}
else {
return false; // If not, then return false
}
}
void recordButtonCommand() { // Actions to perform when the record button is pressed
numberOfCoords = 0; // Reset the number of coords
if (Record == true) { // Set the record var to true
if (numberOfCoords == 0)
m.lightsOff(); // Turn all of the monome lights off
Record = false;
for (int i=0; i < 10; i++) // Count the number of recorded coordinates
{
if (storedCoords[i][0] != -2) numberOfCoords++;
}
if (numberOfCoords == 0) println("Error: Must record atleast one key press");
else {
println("Stored "+numberOfCoords+" Presses");
RecordedOK = true; // Recorded OK
}
}
else if (!Auth) { // Check to see that we are not already trying to authenticate
arrayPlace = 0;
for (int i=0; i < 10; i++) // Fill the array with -2s (no longer -1 because -1 is used to represent consecutive keystrokes on the same key
for (int j=0; j < 3; j++)
storedCoords[i][j] = -2;
Record = true;
RecordedOK = false;
m.lightsOn(); // Turn all the monome lights on
}
else {
println("Error: Can not record whilst authenticating");
}
}
void goButtonCommand() { // Actions to perform when the go button is pressed
if (Record) println("Error: Can not authenticate whilst recording");
else if (!RecordedOK) println("Error: Must record a pattern before attempting to authenticate");
else {
prettyEffect();
Auth=!Auth;
counter = numberOfCoords;
}
}
void monomeReleased(int x, int y) {
if (!Record) m.setValue(x,y,!m.isLit(x,y)); // Invert that key's LED state (ie on->off, off->on)
if (arrayPlace == 10) {
recordButtonCommand();// Increment our array counter
arrayPlace++;
}
}
void monomePressed(int x, int y) { // Actions to perform when a monome key press is detected
m.setValue(x,y,!m.isLit(x,y)); // Invert that key's LED state (ie on->off, off->on)
if (Record == true) { // If we are currently recording
println("pressed: "+x+","+y+" Recorded"); // Echo the X Y coords of the keypress for debug reasons
storedCoords[arrayPlace][0] = x; // Store the X coordinate
storedCoords[arrayPlace][1] = y; // Store the Y coordinate
if (arrayPlace != 0) { // Check for 0 because can't have a relatie angle for the first key press
int Angle = (int)(relativePosition(storedCoords[arrayPlace-1][0],storedCoords[arrayPlace-1][1],x,y)); // Convert from double to int and store as Angle
storedCoords[arrayPlace][2] = Angle; // Store angle in the array
println("angle: "+storedCoords[arrayPlace][2]); // Print the angle
}
arrayPlace++;
//for (int i=0; i < storedCoords.length; i++) { // Each time a keystroke is recorded, output the entire array to show each recorded
// cordinate so far. Debug reasons.
// println("Stored X: "+storedCoords[i][0]+" , Stored Y: "+storedCoords[i][1]);
//}
}
else if (Auth == true) { // If we are in authentication mode
// int counter = numberOfCoords; // Assign value to counter
counter--; // Decrement the value of counter with each key stroke.
if (counter == 0) Auth = false; // When we have pressed the same number of keys as was recorded, stop authenticating
}
else { // If NOT recording, just show the echo the keypress to screen.
println("pressed: "+x+","+y);
}
}
double relativePosition(int x1, int y1, int x2, int y2)
{
if (x1 == x2 && y1 == y2) return -1; // If the coordinates are identical, return -1
if (y2 >= y1) { // Ie if the movement is in the top right quadrant. Less than or equal to, to cover the 0 degrees event (directly above)
int deltax = x2-x1; // a
int deltay = y2-y1; // b
// c is the DISTANCE between the two points, or the hyponenuse of the triangle.
double c = sqrt((deltax*deltax)+(deltay*deltay)); // a^2 + b^2 = c^2
// println("c: "+c);
double angle = deltax/c;
// println("angle: "+angle);
angle =
90-
Math.
toDegrees(Math.
asin(angle
));
// toDegrees converts from radians to degrees
angle =
Math.
round(angle
);
// round the angle
return angle;
}
if (y2 < y1) { // Ie if the movement is in the bottom left quadrant
int deltax = x2-x1; // a
int deltay = y2-y1; // b
// c is the DISTANCE between the two points, or the hyponenuse of the triangle.
double c = sqrt((deltax*deltax)+(deltay*deltay)); // a^2 + b^2 = c^2
// println("c: "+c);
double angle = deltax/c;
// println("angle: "+angle);
angle =
270+
Math.
toDegrees(Math.
asin(angle
));
// toDegrees converts from radians to degrees
angle =
Math.
round(angle
);
// round the angle
return angle;
}
return -1; // Return -1 in any other case (should never get called)
}
void prettyEffect() { // A method simply to vertically scroll a pattern. Acts as a visual cue to the use.
try {
for (int i = 0; i<8; i++) {
m.setCol(i, new int[]{
1,1,1,1,1,1,1,1 }
);
Thread.
currentThread().
sleep(40);
}
for (int i = 0; i<8; i++) {
m.setCol(i, new int[]{
0,0,0,0,0,0,0,0 }
);
Thread.
currentThread().
sleep(40);
}
}
println("Fail");
}
}