Het is alweer tijd voor les 3 van Apps om in te lijsten. Deze week gaan we kijken naar de mogelijkheden van de muis.

De vorige keer hebben we gekeken naar if-statements, kleurgebruik, for-loops en random. Deze week bouwen we voort op deze elementen maar introduceren we daarbij interactiviteit met een gebruiker.

Interactiviteit

Interactiviteit is iets wat moeilijk te bereiken is met andere creatieve media, dus leren hoe je op een goede manier een respons kan krijgen uit een gebruiker en hoe zij op een boeiende manier kunnen spelen met je werk is iets wat het bestuderen waard is.

De makkelijkste vorm van interactiviteit is de muis, omdat iedereen weet hoe een muis werkt. Op Android tablets geld een aanraking ook meteen als muis, dus dat is een mooie bonus.

De muis gebruiken in Processing is makkelijk, zo is de positie van de muis op te vragen door middel van mouseX en mouseY.

void setup(){
  size(300,300);
}

void draw(){
    background(0);
    ellipse(mouseX,mouseY,20,20);  
}

Zoals je ziet volgt de cirkel nu keurig de muis. Dit is het meest simpele voorbeeld dat je kan maken met mouseX en mouseY dus laten we het uitbouwen tot een klein tekenprogrammaatje.

Een tekenprogrammaatje

Voor een tekenprogramma kun je, zoals we in de vorige les lieten zien, de background uit de vorige code weghalen. Op die manier worden de cirkels niet van het scherm gehaald en ontstaat er een slinger van cirkels.

void setup(){
  size(300,300);
}

void draw(){
    ellipse(mouseX,mouseY,20,20);  
}

Nou is een cirkel met slingers best kunstig. Maar wat als we wat meer een traditionele lijn zouden willen tekenen? Processing heeft een line-functie maar het probleem is dat je een begin en eindpunt voor de lijn nodig hebt.

Dus hoe weet je waar je muis de vorige frame was? mouseX en mouseY geven alleen aan waar de muis op dit moment uithangt. Gelukkig heeft Processing ook variabelen waar de muis de vorige frame uithing namelijk pmouseX en pmouseY. Zo kunnen we een programma maken dat lijnen kan tekenen.

void setup(){
  size(300,300);
  background(0);
}

void draw(){
    stroke(255);
    line(pmouseX, pmouseY, mouseX, mouseY);
}

Dus we weten dat we iets kunnen doen met de positie van de muis, maar met een muis kun je ook klikken. Dus hoe doe je daar iets mee?

Als je kijkt in de Processing Reference zie je onder input heel wat opties met de muis. Daar zitten ook verschillende manieren in om het klikken van de muis te gebruiken, maar wij richten ons op mousePressed.

MousePressed is net als mouseX en mouseY een variabele, maar in plaats van een getal is mousePressed een boolean. Een boolean is een variabele die true of false is, net zoiets als een lichtknopje dat aan of uit kan. Door een if statement te gebruiken kun je acties koppelen aan een muisklik.

void setup(){
  size(300,300);
  
}

void draw(){
  if (mousePressed == true) {
    println("de muis wordt ingedrukt!");
  }
}

Op deze manier zou je het tekenprogramma kunnen voorzien van een mogelijkheid om het scherm leeg te maken.

void setup() {
  size(300, 300);
  background(0);
}

void draw() {
  if (mousePressed == true) {
    background(0);
  }

  stroke(255);
  line(pmouseX, pmouseY, mouseX, mouseY);
}

Wees niet te verlegen om de variabelen van de muis voor onlogische dingen te gebruiken. Dit leidt vaak tot interessante resultaten. Bijvoorbeeld als je de kleuren van je lijn koppelt aan de positie van de muis.

void setup() {
  size(300, 300);
  background(0);
  strokeWeight(4);
}

void draw() {
  if (mousePressed == true) {
    background(0);
  }

  stroke(mouseX,mouseX,mouseY);
  line(pmouseX, pmouseY, mouseX, mouseY);
}

Reproduceren interactief werk

Nu dat we een idee hebben van de basis, laten we net als vorige week kijken naar een bestaand kunstwerk en dat reproduceren. Dit keer is dat Primitives van Alexander Christiaan Jakobs.

Net als vorige keer breken we het werk eerst af in simpele elementen en stappen om te zien wat het werkt nou eigenlijk doet.

  • verschillende vormen
  • verschillende kleuren
  • vormen geplaatst in een raster
  • Als je met de muis beweegt over een vorm verandert het

Zoals je ziet zijn er een aantal delen hetzelfde als bij 1025 Farben. Je hebt te maken met een raster en willekeurige kleuren. Dus dit zijn onderdelen die we gedeeltelijk kunnen over nemen.

aq_block_39

Muispositie berekenen

Het moeilijkste aan dit kunstwerk is het bekijken wanneer er een nieuw vakje op het scherm getekend moet worden, dus daar gaan we mee beginnen. Dit vereist enige denkwerk om zo goed mogelijk te doen.

De raster waarin de vormen getekend worden zijn allemaal vierkant van vorm, laten we in onze reproductie zeggen dat het 30 bij 30 pixels is. Maar hoe controleer je nou op welk vierkant je muis uithangt? Door mouseX en mouseY weet je alleen de pixel waarop de muis uithangt dus als je dat door if statments zou willen berekenen wordt het iets als het onderstaande.

//snap je niet zo goed wat hier staat
//bekijk http://processing.org/reference/if.html
//en http://processing.org/reference/logicalAND.html

if(mouseX >= 60 && mouseX <= 90){
    if(mouseY >= 30 && mouseY <= 60){
        println("we zitten op het 3e vakje op de 2e         rij!");
    }
}

Dit is heel veel code om voor elk vierkant te schrijven. Daarnaast is het probleem dat om het moment dat je meer of minder vierkantjes wil je veel code moet aanpassen.

Een betere oplossing is als je het raster waarin de vormen worden getekend een eigen co├Ârdinaten stelsel meegeeft, a la zeeslagje. Op die manier kunnen we de positie van het muis terug rekenen naar de vorm op het scherm die moet veranderen.

aq_block_47

Nu kunnen we heel snel het juiste co├Ârdinaat vinden door de positie van de muis te delen door de breedte van het vakje.

//Eerst geven we aan hoe groot een blokje in het raster is
int cellSize=30;


void setup() {
  //We spelen een beetje vals door de grootte van het canvas makkelijk deelbaar maken door de grote van een vorm
  size(600, 600);

  //Voor het tekenen van "nette" afbeeldingen
  smooth();

  //Een zwarte achtergrond
  background(0);
}

void draw() {
  //Hier rekenen we uit in welk rastervierkantje de muis uithangt
  int mouseXArea = floor(mouseX/cellSize);  
  int mouseYArea = floor(mouseY/cellSize);
  println(" mouseXArea: "+mouseXArea +" mouseYArea: "+mouseYArea)
}

Als mouseX nu 180 is weeten mouseXArea dat we in het zesde vakje van links zitten. Het probleem is dat als mouseX 183 is, mouseXarea denkt dat we in vakje 8.1 inzitten. Omdat we een heel getal terug willen hebben en niet een notatie als "vakje 8.1" ronden we alle getallen af naar beneden, dit kan door de functie floor. De code is dan als volgt.

//Eerst geven we aan hoe groot een blokje in het raster is
int cellSize=30;


void setup() {
  //We spelen een beetje vals door de grootte van het canvas makkelijk deelbaar maken door de grote van een vorm
  size(600, 600);

  //Voor het tekenen van "nette" afbeeldingen
  smooth();

  //Een zwarte achtergrond
  background(0);
}

void draw() {
  //Hier rekenen we uit in welk rastervierkantje de muis uithangt
  int mouseXArea = floor(mouseX/cellSize);  
  int mouseYArea = floor(mouseY/cellSize);

  fill(random(255), random(255), random(255));
  rect(mouseXArea*cellSize, mouseYArea*cellSize, cellSize, cellSize);

  println("de muis zit in vakje "+mouseXArea+" bij "+mouseYArea);
}

We hebben dus nu een methode om te berekenen welke vorm op het scherm opnieuw getekend moet worden, en we zetten voorlopig een vierkant neer met een willekuerige kleur als vorm.

Dit is al leuk, maar als je muis stil staat dan blijft het geheel knipperen. Dat is niet handig, dus we hebben een check nodig die kijkt of de muis niet al in hetzelfde vakje aanwezig was.

Om dit te doen hebben we een if statement die kijkt of de muis zich in een ander vakje bevindt op de horizontale OF verticale regel. in een van de eerdere voorbeeld lieten we stilletjes al && in een if statment zien, wat kijkt of beide zaken die je wil controleren waar zijn. In ons geval hoeven ze niet allebei waar zijn, mag wel, daarvoor gebruiken we | | , wat zoveel betekend als OR.

Hier mee kunnen we aan een if statement beide onderdelen controleert en als een van de vergelijkingen waar is dat de code wordt uitgevoerd. Dus zo kunnen we kijken of de muis zich op een ander horizontaal OF verticaal vakje bevindt.

//Eerst geven we aan hoe groot een blokje in het raster is
int cellSize=30;


void setup() {
  //We spelen een beetje vals door de grootte van het canvas makkelijk deelbaar maken door de grote van een vorm
  size(600, 600);

  //Voor het tekenen van "nette" afbeeldingen
  smooth();

  //Een zwarte achtergrond
  background(0);
}

void draw() {
  //Hier rekenen we uit in welk rastervierkantje de muis uithangt
  int mouseXArea = floor(mouseX/cellSize);  
  int mouseYArea = floor(mouseY/cellSize);
//We kijken nu ook naar de positie van de muis tijdens het vorige frame
  int pmouseXArea = floor(pmouseX/cellSize);  
  int pmouseYArea = floor(pmouseY/cellSize);

  if (mouseXArea != pmouseXArea || mouseYArea != pmouseYArea) {
    fill(random(255), random(255), random(255));
    rect(mouseXArea*cellSize, mouseYArea*cellSize, cellSize, cellSize);
  }
}

In bovenstaande code staat nog iets wat we nog niet eerder hebben behandeld, namelijk de !=. != betekend zoveel als niet gelijk aan. Dus als mouseXArea en pmouseXArea niet gelijk zijn of hetzelfde geld voor mouseYArea en pmouseYArea, dan teken je een nieuw vierkantje.

//Eerst geven we aan hoe groot een blokje in het raster is
int cellSize=30;

void setup() {
  //We spelen een beetje vals door de grootte van het canvas makkelijk deelbaar maken door de grote van een vorm
  size(600, 600);

  //Voor het tekenen van "nette" afbeeldingen
  smooth();

  //Een zwarte achtergrond
  background(0);
  noStroke();
  
  //vergeet ellipseMode niet toe te voegen 
  //http://processing.org/reference/ellipseMode_.html
  ellipseMode(CORNER);
}

void draw() {
  //Hier rekenen we uit in welk rastervierkantje de muis uithangt
  int mouseXArea = floor(mouseX/cellSize);  
  int mouseYArea = floor(mouseY/cellSize);
  int pmouseXArea = floor(pmouseX/cellSize);  
  int pmouseYArea = floor(pmouseY/cellSize);

  int shape = floor(random(2));
  if (mouseXArea != pmouseXArea || mouseYArea != pmouseYArea) {
    
    //Nu tekenen we er dus altijd een zwart vakje achter, zodat cirkels die over een vierkant heen
    //vallen niet nog het vierkant er achter laten zien
    fill(0);
    rect(mouseXArea*cellSize, mouseYArea*cellSize, cellSize, cellSize);
    
    fill(random(255), random(255), random(255));
  
    if(shape ==0){
    rect(mouseXArea*cellSize, mouseYArea*cellSize, cellSize, cellSize);
    } else if(shape == 1){
      ellipse(mouseXArea*cellSize, mouseYArea*cellSize, cellSize, cellSize);
    }
  }
}

Mocht je meer vormen willen toevoegen, dan kan dit natuurlijk altijd. We hebben hier wat voorbeeld code waar in we de andere vormen van Primitives uitleggen. Maar mocht je niet zoveel interesse hebben in vormen, dan kun je ook verder zonder de uitleg te lezen. De belangrijkste toevoeging is de functie arc die je in staat stelt delen van een cirkel te tekenen. Let daarbij op dat Processing gebruikt maakt van radianen als notering om het start en eindpunt van de cirkel aan te geven. Mocht je dat onhandig vinden dan kun je met randians-functie gewoon het 360 graden systeem gebruiken.

Het scherm aan het begin vullen

Dus nu kunnen we op de juiste manier nieuwe vormen laten verschijnen en over oude vormen heen tekenen. Maar we beginnen met een leeg scherm. Dat is natuurlijk niet hoe het originele werkt begint.

Gelukkig is het vullen van het scherm niet heel moeilijk en bestaat het uit de code die we al hebben geschreven in draw te combineren met de code van vorige week met 1025 Farben. Daar vulde we ook een scherm in een rastervorm. Hier hebben we naast het vierkant echter ook andere vormen.