Kappaleen otsikko ei ala kovin motivoivasti, mutta noise-metodin avulla
tapa käy järkeen. Jos maltat tämän kappaleen loppuun olet jo pitkällä
generatiivisten tekniikoiden osalta.
Jos haluaisit piirtää suoran viivan Processingilla mieleesi tulee
varmastikin suoraviivaisin ratkaisu: line-metodi.
line(0, height/2, width, height/2);
Entä jos haluaisit piirtää viivan yksi piste kerrallaan? Viivan
piirtämiseen tästä ei ole hyötyä, mutta piste kerrallaan piirtäminen
mahdollistaa pisteiden manipuloinnin esimerkiksi noise-metodin avulla.
Tarmokasta näppäimistötyöskentelyä vaativa brute force -tekniikka
näyttäisi ehkä tältä:
Näin hankalaan tapaan ei onneksi tarvitse sortua. Apuun tulevat
johdannossa mainitut toistorakenteet! Ajatuksena on toistaa koodinpätkää
niin pitkään, kun jokin ehto täyttyy. Ehdot ilmaistaan samaan tapaan,
kuin ehtolauseiden (if ja else) parissa. Tarkastele seuraavaa
esimerkkiä:
int x = 0;//luo kokonaislukutyyppinen muuttuja x ja aseta arvoksi 0
while(x < width){//toista koodilohkoa jos x on pienempi kuin width
point(x, height/2);//piirrä piste
x = x + 1;//kasvata muuttujan x arvoa yhdellä
}
Loistavaa, nyt olet taas lähtöpisteessä! Mutta mikä tässä tavassa oli
vaivan arvoista? Ensiksi, pääsit tutustumaan while-looppiin, joka tulee
osoittautumaan korvaamattomaksi työkaluksi ohjelmoinnissa. Toiseksi, nyt
voit helposti lähteä manipuloimaan yksittäisiä pisteitä viivassa.
Otetaan esimerkiksi matematiikasta tuttu sini-funktio. sin(x) palauttaa
x-muuttujasta riippuen aina arvon välillä -1, 1. Kokeillaan muokata
viiva-ohjelmassa piirrettävän pisteen y-koordinaattia sini-funktion
arvoilla:
int x = 0;// luo kokonaislukutyyppinen muuttuja x ja aseta arvoksi 0
float sin_seed = 0;while(x < width){// toista koodilohkoa jos x on pienempi kuin width
float y = sin(sin_seed)* 10;// muuttuja y saa arvoja välillä -10 - 10
point(x, height/2 + y);// piirä piste
x = x + 1;// kasvata muuttujan x arvoa yhdellä
sin_seed = sin_seed + 0.1;}
Toistorakenteet
Tässä kappaleessa jatketaan toistorakenteiden parissa ja esitellään
Processingin noise-metodi. Käytit äskeisesssä kappaleessa while-looppia
viivan ja sinikäyrän piirtämiseen. Tässä kappaleessa piirrät
while-loopin ja noise-metodin avulla aaltoilevan käyrän.
Seuraavassa esimerkissä piirretään jokaisella draw-metodin kutsulla
while-loopin avulla käyrä ikkunan poikki. Seuraavalla draw-metodin
kutsulla piirretään käyrä uudestaan hieman eri muotoisena. Draw-metodia
toistettaessa syntyy vaikutelma aaltoilevasta pinnasta.
While-looppi
While-looppi muistuttaa
rakenteeltaan hyvin paljon if-lausetta. While-komennon jälkeen
määritellään suluissa ehto, jonka on oltava tosi, jotta sitä seuraavan
lohkon sisällä oleva koodi suoritetaan. Erona if-lauseeseen on se, että
while-lauseen lohkon sisältöä suoritetaan niin kauan kunnes ehto ei
enää päde. Siinä missä if-lauseen lohko suoritetaan kerran, while-lohko
voidaan suorittaa vaikka kuinka monta kertaa - jopa ikuisesti!
Huom!
voiddraw(){// while-looppia suoritetaan niin kauan, kuin ehto on tosi:
// koska 0 on aina 0, jatkuu suoritus tässä tapauksessa ikuisesti
while(0 == 0){
rect(50, 100, 50, 50);}
ellipse(200, 200, 30, 30);// tätä komentoa ei koskaan päästä suorittamaan
}
Ylläoleva koodi on varoittava esimerkki siitä, mitä tapahtuu, kun
while-loopin ehto on aina tosi. Kun yritämme ajaa ylläolevan ohjelman,
IDE:n kääntäjä varoittaa meille, että ellipse-metodi on "unreachable
code" eli ohjelma sisältää koodia, jota ei missään tilanteessa päästä
suorittamaan. While-looppi siis jatkuu ikuisesti.
Noise-aalto
Aloitetaan aaltoanimaation kirjoittaminen piirtämällä jälleen
while-loopin ja point-metodin avulla suora jana ikkunan poikki.
voidsetup(){
size(500, 500);}voiddraw(){int x = 0;// luodaan kokonaislukumuuttuja x, asetetaan alkuarvoksi 0
// While-looppia suoritetaan niin kauan kun ehto on tosi.
// Tässä tapauksessa niin kauan kun x on pienempi kuin width eli 500
while(x < width){
point(x, 250);// piirretään piste kohtaan (x,250)
x++;// kasvatetaan x:n arvoa yhdellä
// x++ tarkoittaa samaa kuin x = x + 1
}// Kun while-looppi on käyty läpi, on x == 500.
// Draw-metodin alussa muuttuja luodaan uudestaan ja asetetaan nollaan.
}
Olisi ollut helpompaa kirjoittaa ainoastaan line(0, 250, 500, 250),
mutta työ on vaivan arvoista kun lisätään mukaan noise-metodi. Käytä
seuraavaksi muuttujaa x noise-metodin parametrina ja käytä saatua arvona
pisteen y-koordinaattina. Muuttuja x kasvaa jokaisella kierroksella
yhdellä. Jos muuttujan arvo jaetaan 100:lla, saadaan noise-metodille
sopiva 0.01 muutos jokaisella kierroksella.
Muista, että noise palauttaa aina arvon välillä 0-1. Jos haluat käyttää
noisen antamaa arvoa esimerkiksi y-koordinaattina, tulee välillä 0-1
oleva arvo skaalata sopivalle välille. Skaalaus onnistuu mukavasti
Processingin map-metodilla:
map(syöte, alkuperäinen minimi, alkuperäinen maksimi, uusi minimi, uusi maksimi)
Käytännössä map-metodin käyttö voisi näyttää tältä: (muuntaa välillä
0-1 olevan x:n arvon välille 200-400):
map(x, 0, 1, 200, 400)
voidsetup(){
size(500, 500);}voiddraw(){float x = 0;// luodaan liukulukumuuttuja x, asetetaan alkuarvoksi 0
// While-looppia suoritetaan niin kauan kun ehto on tosi.
// Tässä tapauksessa niin kauan kun x on pienempi kuin width eli 500
while(x < width){float y = map(noise(x/100), 0, 1, 150, 300);// x/100 kasvaa 0.01 jokaisella kierroksella, mikä sopii hyvin noise-metodin parametriksi
// skaalataan map-metodilla välillä 0-1 oleva arvo välille 150-300
point(x, y);// piirretään piste kohtaan (x,y)
x++;// kasvatetaan x:n arvoa yhdellä
// x++ tarkoittaa samaa kuin x = x + 1
}}
Huomaat, että ohjelma generoi erilaisen käyrän jokaisella kerralla kun
ohjelma ajetaan. Käyrä pysyy kuitenkin paikoillaan. Kun ohjelma on
käynnissä, käyrä piirretään yhä uudestaan ja uudestaan draw-metodissa.
Noise-metodia kutsutaan aina samoilla arvoilla, joten käyrä piirtyy
samanlaisena. Tehdään seuraavaksi viimeinen vaihe ja lisätään
noise-metodin parametreihin hieman variaatiota jokaisella draw-metodin
kutsulla.
Jokaisella draw-metodin kutsulla x saa arvot välillä 0-500. Kun x
jaetaan sadalla, noise-metodia kutsutaan samoilla arvoilla välillä 0-5,
0.01 välein. Jos jokaisella draw-metodin kutsulla halutaan piirtää
hieman erilainen kuvaaja, tulee noise-metodia kutsua myös eri arvoilla.
Tämä onnistuu kätevästi antamalla noise-metodille toinen parametri x:n
lisäksi! Eli:
Jos toista parametria kasvatetaan hieman draw-metodin lopussa, saadaan
aikaiseksi hieman erilainen kuvaaja jokaisella draw-metodin kierrokselta
ja lopputulos näyttää aaltoilevalta:
float y = 0;// luodaan liukulukumuuttuja y, asetetaan alkuarvoksi 0
voidsetup(){
size(500, 500);}voiddraw(){
background(255);float x = 0;// luodaan liukulukumuuttuja x, asetetaan alkuarvoksi 0
// While-looppia suoritetaan niin kauan kun ehto on tosi.
// Tässä tapauksessa niin kauan kun x on pienempi kuin width eli 500
while(x < width){float n = noise(x/100, y);// kutsutaan noise-metodia kahdella parametrilla. Ensimmäinen parametri saa arvoja väillä 0-5, 0.01 välein.
// Toinen parametri pysyy samana, kunnes sitä draw-metodin lopussa kasvatetaan hieman.
float y = map(n, 0, 1, 150, 300);// skaalataan map-metodilla välillä 0-1 oleva arvo välille 150-300
point(x, y);// piirretään piste kohtaan (x,y)
x++;// kasvatetaan x:n arvoa yhdellä
}
y += 0.02;// Kasvatetaan y:n arvoa hieman
}
Loistavaa! Voisit kokeilla seuraavaksi esimerkiksi taustan häivyttämistä
draw-metodin alussa background-metodin sijaan ja hakea inspiraatiota
loppuprojektia varten.
Seuraavassa kappaleessa käsitellään for-looppia, joka toiminnaltaan on
while-looppia vastaava, mutta suoraviivaistaa rakenteen syntaksia. Tee
seuraavaksi alla olevat tehtävät while-loopin toiminnasta ja siirry
seuraavaan lukuun.
int i = 0;while(i<10){
print("Tämä tulostuu kymmenen kertaa");}
Miksi alla oleva koodi ei tulosta kymmentä kertaa tekstiä?
For-looppi
For-looppi on while-looppia
näppärämpi tapa toistaa asioita. Se koostuu täsmälleen samoista
palasista: laskurimuuttujan luomisesta, ehdosta ja laskurin
kasvatuksesta. Vain syntaksi on eri. Alla on esimerkki while- ja
for-loopeista, jotka molemmat tekevät saman asian:
int i = 0;while(i < 10){// while-looppi suoritetaan kymmenen kertaa
rect(10*i, 10*i, 10*i, 10*i);
i++;}
for(int i = 0; i < 10; i++){// for-looppi suoritetaan kymmenen kertaa
rect(10*i, 10*i, 10*i, 10*i);}
Sisäkkäiset for-loopit
Koska edellisessä kappaleessa tehtiin onnistuneesti aaltoileva animaatio
while-loopin ja noisen avulla, jatketaan tässä kappaleessa tutulla
linjalla – mutta tehdään samalla vaivalla ainakin parikymmentä
aaltoilevaa kuvaajaa!
Aloitetaan piirtämällä paikallaan pysyvä noise-käyrä for loopilla:
voidsetup(){
size(500, 500);}voiddraw(){// For loop käy läpi arvot välillä 0-500)
for(float x = 0; x < width; x++){float y = map(noise(x/100), 0, 1, 0, 100);// skaalataan map-metodilla välillä 0-1 oleva arvo välille 0-100
point(x, y);// piirretään piste kohtaan (x,y)
}}
Tämä on jo tuttua edellisen kappaleen perusteella. Ainoa ero on
for-rakenne while-rakenteen sijaan.
Nyt ohjelma piirtää yhden käyrän. Jos haluaisit piirtää vaikkapa 20
käyrää, voisit kopioida for loopin 20 kertaa peräkkäin. Pidemmän päälle
saman koodin kopiointi käy kuitenkin vaivalloiseksi ja arvojen
muuttaminen jokaisesta kahdestakymmenestä toistorakenteesta olisi
työlästä ja virhealtista. Onneksi voit luoda toisen toistorakenteen
edellisen ympärille! Katso seuraavaa esimerkkiä:
voidsetup(){
size(500, 500);}voiddraw(){// row käy läpi arvot 0, 20, 40, 60, ..., 460, 480
for(float row = 0; row < width; row += 20){// For loop käy läpi arvot välillä 0-500. Sisempi for-loop käydään läpi 25 kertaa
for(float x = 0; x < width; x++){float y = map(noise(x/100), 0, 1, 0, 100);// skaalataan map-metodilla välillä 0-1 oleva arvo välille 0-100
point(x, row + y);// piirretään piste kohtaan (x,y)
}}}
Sisempi for-loop toistetaan nyt 25 kertaa, eli ruudulle piirretään
yhteensä 25 kuvaajaa. Kaikki kuvaajat ovat ikävästi samanlaisia, joten
käytetään row-muuttujaa noise-metodin toisena parametrina:
voidsetup(){
size(500, 500);}voiddraw(){// row käy läpi arvot 0, 20, 40, 60, ..., 460, 480
for(float row = 0; row < width; row += 20){// For loop käy läpi arvot välillä 0-500. Sisempi for-loop käydään läpi 25 kertaa
for(float x = 0; x < width; x++){float y = map(noise(x/100, row/100), 0, 1, 0, 100);// skaalataan map-metodilla välillä 0-1 oleva arvo välille 0-100
point(x, row + y);// piirretään piste kohtaan (x,y)
}}}
Hienoa, mutta kuva pysyy edelleen paikallaan. Lisätään noise-muuttujalle
vielä kolmas parametri ja käytetään samaa kikkaa kuin edellisessä
kappaleessa animaation tuottamiseksi. Luodaan muuttuja z, käytetään
z-muuttujaa noise-metodin kolmantena parametrina ja kasvatetaan
muuttujan arvoa hieman draw-metodin lopussa.
float z = 0;voidsetup(){
size(500, 500);}voiddraw(){
background(255);// row käy läpi arvot 0, 20, 40, 60, ..., 460, 480
for(float row = 0; row < width; row += 20){// For loop käy läpi arvot välillä 0-500. Sisempi for-loop käydään läpi 25 kertaa
for(float x = 0; x < width; x++){float y = map(noise(x/100, row/100, z), 0, 1, 0, 100);// skaalataan map-metodilla välillä 0-1 oleva arvo välille 0-100
point(x, row + y);// piirretään piste kohtaan (x,y)
}}
z += 0.02;}
Loistavaa! Voit kokeilla erilaisia värimaailmoja sekä eri arvoja
noise-metodille.