2014.03.14.
14:26

Írta: harsanyireka

Win / Kinect + Processing + SimpleOpenni

kinectproc_1355933045_1394803534.png_1464x624

http://sensorbreakers.wordpress.com/2012/12/19/win-kinect-processing-simpleopenni/

 

Szólj hozzá!

2013.10.13.
23:42

Írta: harsanyireka

Leckék angolul

http://www.learningprocessing.com/examples/

Letölthető példa kódokkal.

Szólj hozzá!

2013.10.13.
23:40

Írta: harsanyireka

Generatív grafika

http://www.generative-gestaltung.de/code

A könyvhöz tartozó példa kódok letölthetőek innen. A lap alján találhatóak a szükséges könyvtárak linkjei is.

Szólj hozzá!

2013.02.25.
14:20

Írta: harsanyireka

PVector

A vektor olyan értékek gyűjtőhelye amik egy relatív helyzetet határoznak meg a térben. A két pont közé rajzolt nyíl a pontok közötti távolságot jelentik: 

Bevezetés

Az első példában nem alkalmazunk még vektort, csak egy olyan mozgó, visszapattanó "labdát" rajzolunk, ami a következő tulajdonságokkal rendelkezik:

- van helyzete, x és y koordinátával

- sebessége, xseep és yspeed változókkal

- gyorsulása, xacceleration és yacceleration változókkal

- cél helyzete, xtarget és ytarget változóval

- hat rá a szél, xwind és ywind változókkal

- és hat rá a surlódás, xfriction és yfriction változókkal

2 dimenzióban ábrázoljuk, de ha 3d-ben akarnánk akkora z tengelyhez is be kellene vezetnünk a változókat.

float x = 100;
float y = 100;
float xspeed = 1;
float yspeed = 3.3;

void setup() {
  size(200,200);
  smooth();
  background(255);
}

void draw() {
  noStroke();
  fill(255,10);
  rect(0,0,width,height);
  
  // pozíció változtatása a sebességgel:
  x = x + xspeed;
  y = y + yspeed;

  // visszapattanás beállítása:
  if ((x > width) || (x < 0)) {
    xspeed = xspeed * -1;
  }
  if ((y > height) || (y < 0)) {
    yspeed = yspeed * -1;
  }

  // x,y helyzetben megjelenítjük a labdát:
  stroke(0);
  fill(175);
  ellipse(x,y,16,16);
}

  

Ezen float változók helyett használhatunk PVectort is, akár fizikai szimulációhoz is jó. Most csak 2d-ben nézzük meg.

Mit jelentenek a vektorok?

Definíciója szerint a két pont közötti távolságot jelenti. Néhány példa a vektrok (mozgás) átfordítására. Korábban ezt a módszert használtuk ha el akartunk mozdítani egy pontot - meghatároztuk mennyivel mozduljon el x és y irányban:

Ezt lecserélhetjük vektorra, megmondhatjuk egyalakzatnak hogy A pontból B pontba mozduljon, az úgynevezett pixel-sebesség (pixel velocity) érték hozzáadásával:

location = location + velocity

Ha a sebességet egy vektorral adjuk meg, akkor a pozíciót is azzal kell meghatározni? Egyrészt a pozíció egy szimpla pont a térben. Másrészt meghatározható egy útvonal részeként is, ebben az esetben már vektroként is értelmezhetjük és létrehozhatunk egy olyan osztályt a mozgásnak, ami jóval kényelmesebb megoldás, mint egyes pontokat meghatározni. 

Határozzunk meg értékeket a pozíciónak és a sebességnek. A pattogólabda példában ezt így csináltuk:

location --> x,y
velocity --> xspeed,yspeed

Ugyanezt megtehetjükegy osztályon belül is:

class PVector {

  float x;
  float y;

  PVector(float x_, float y_) {
    x = x_;
    y = y_;
  }

}

DE a cél pozíció értékeket (float x és y helyett) megadhatjuk vektorban is:

PVector location = new PVector(100,100);
PVector velocity = new PVector(1,3.3);

Ezek után már algoritmust is írhatunk ezekhez az értékekhez, a korábban használt x=x+speed helyett. A pillanatnyi gyorsulás értéket hozáadjuk a pillanatnyi pozícióhoz:

 location = location + velocity;

Habár a + művelet csak primitív értékekkel működik Processingben. Így afenti sort nem tudja értelmezni, ezért a meghatározott függvényekkel tudunk matematikai műveleteket elvégezni: http://processing.org/reference/PVector.html 

Vektorok összeadása: add()

u = (5,2) 
v = (3,4)

(Vektor jelölése félkövér betűvel, vagy a betű feletti nyíllal. Valós számok dőlt betűvel.)


Minden vektornak két eleme van, x és y érték. Ha két vektort össze akarunk adni akkor az x és az y értékeiket kell összeadnunk:

w = u + v esetén:

wx = ux + vx 
wy = uy + vy

ebben az esetben: 

wx = 5 + 3 
wy = 2 + 4

w = (8,6)

PVector-ok összeadása esetén az add() függvényt tudjuk erre használni. Pontszintaxissal működik. Szintaxisa.

Az előző pattogó labda példa átírása vektor összeadással:

Example: Bouncing Ball with PVector!

// float helyett vektort használunk:
PVector location;
PVector velocity;

void setup() {
  size(200,200);
  smooth();
  background(255);
  location = new PVector(100,100);
  velocity = new PVector(2.5,5);
}

void draw() {
  noStroke();
  fill(255,10);
  rect(0,0,width,height);
  
  // pillanatnyi sebességet a pozícióhoz adjuk:
  location.add(velocity);

  // A vektorok értékeit pont szintaxissal érjük el, 
  // (location.x, velocity.y, stb.)
  if ((location.x > width) || (location.x < 0)) {
    velocity.x = velocity.x * -1;
  }
  if ((location.y > height) || (location.y < 0)) {
    velocity.y = velocity.y * -1;
  }

  // kör megjelenítése x pozícióban:
  stroke(0);
  fill(175);
  ellipse(location.x,location.y,16,16);
}

                       

Az ellipse() fügvényben nem tudsz paraméternek megadni PVectort, mivel neki x és y érték is kell, ezért azokat az értékeket ki kell szedni, így:

ellipse(location.x,location.y,16,16);

Ugyanez igaz a for ciklusra is. A pl a location és a velocity vektor esetén:

if ((location.x > width) || (location.x < 0)) {
  velocity.x = velocity.x * -1;
}

Még több algebra

        

Vektorokról: http://aries.ektf.hu/~hz/pdf-tamop/pdf-01/html/ch01.html 

Példa: Vektorok kivonása, egér mozgást követő vonal, a középpontja fix:

void setup() {
  size(200,200);
  smooth();
}

void draw() {
  background(255);
  
  // két PVector, az egyik az egér helyzete a másik az ablak közepe:
  PVector mouse = new PVector(mouseX,mouseY);
  PVector center = new PVector(width/2,height/2);
  
  // PVector-ok kivonása
  mouse.sub(center);
  /*
// PVector osztás: // az eredeti méret fele lesz ha ezt is bele tesszük: mouse.mult(0.5);
*/
// ábrázoljuk a vektort, vonal kirajzolása: 
translate(width/2,height/2);
line(0,0,mouse.x,mouse.y);
}

Vektor hossza: mag()

Pitagorasz tétel segítségével tudjuk meghatározni, amit a következő képlettel írható le:

||v|| = sqrt(vx*vx + vy*vy)

PVector-nál a mag() függvény felel meg ennek, pontszintaxissal működik. A következő példa balfelső sarokba rajzol egy a vektor aktuális hosszának megfelelő szélességű téglalapot:

void setup() {
  size(200,200);
  smooth();
}

void draw() {
  background(255);
  
  PVector mouse = new PVector(mouseX,mouseY);
  PVector center = new PVector(width/2,height/2);
  mouse.sub(center);

//a vektor hosszát változóként meghatározzuk: float m = mouse.mag(); fill(0); rect(0,0,m,10); translate(width/2,height/2); line(0,0,mouse.x,mouse.y); }

Normalizálás

Szabványosítja a vektort, egységnyi hosszúra alakítja. 

A nagyságával osztjuk el a vektrort ilyenkor. (De 0-val nem oszthatjuk el! Nem működik.)

Példa:

void setup() {
  size(200,200);
  smooth();
}

void draw() {
  background(255);
  
  PVector mouse = new PVector(mouseX,mouseY);
  PVector center = new PVector(width/2,height/2);
  mouse.sub(center);
  
  // normalizáljuk a vektort, majd megszorozzuk 50-nel  
  // Nem számít hol van az egér,mert fix lesz a vektor hossza,
  // a normalizálásnak köszönhetően.
  mouse.normalize();
  mouse.mult(50);

  translate(width/2,height/2);
  line(0,0,mouse.x,mouse.y);
  
}

Mozgás

A pattogó labdánál meghatároztuk a labda helyzetét és sebességét és ezeket adtuk össze hogy megkapjuk a labda aktuális pozícióját.

location.add(velocity);

Ezután kirajzoltuk a labdát a megfelelő helyen:

ellipse(location.x,location.y,16,16);

Setupban és loopban határoztuk meg ezeket. Most ezeket tegyük át egy osztályba! (OOP) A két változót két vektorként defíniáljuk!

class Mover {
  
  PVector location;
  PVector velocity;
void update() {
///aktuálispozícióba mozgatás location.add(velocity); } void display() {
////megjelenítés stroke(0); fill(175); ellipse(location.x,location.y,16,16); } }

Ebből a kódból még hiányzik egy fontos rész, a konstruktor! Ugyanazon a néven, függvényben megadjuk:

 Mover() {
    location = new PVector(random(width),random(height));
    velocity = new PVector(random(-2,2),random(-2,2));
  }

Ugyanebben a "mozgó" osztályban határozzuk meg mi történjen ha az ibjektum az ablakunk széléhez ér! Ha kimegy az egyik oldalon jöjjön be a másikon egyből:      

void checkEdges() {

    if (location.x > width) {
      location.x = 0;
    } else if (location.x < 0) {
      location.x = width;
    }

    if (location.y > height) {
      location.y = 0;
    } else if (location.y < 0) {
      location.y = height;
    }

  }

Most hogy kész van az osztály, már hivatkozhatunk rá. Deklaráljuk:

Mover mover;

Setupban:

 mover = new Mover();

Drawban:

mover.update(); 
mover.checkEdges();
mover.display();

TELJES PÉLDA

// Mover objektum deklarálása
Mover mover;

void setup() {
  size(200,200);
  smooth();
  background(255);
  // Mover objektum létre hozása
  mover = new Mover(); 
}

void draw() {
  noStroke();
  fill(255,10);
  rect(0,0,width,height);
  
  // fügvények meghívása Mover objektumhoz:
  mover.update();
  mover.checkEdges();
  mover.display(); 
}

class Mover {

  //két PVectors: location-pozíció, velocity-sebesség:
  PVector location;
  PVector velocity;

  Mover() {
    location = new PVector(random(width),random(height));
    velocity = new PVector(random(-2,2),random(-2,2));
  }

  void update() {
    // mozgás:pozíció+sebesség
    location.add(velocity);
  }

  void display() {
    stroke(0);
    fill(175);
    ellipse(location.x,location.y,16,16);
  }

  void checkEdges() {

    if (location.x > width) {
      location.x = 0;
    } else if (location.x < 0) {
      location.x = width;
    }
    
    if (location.y > height) {
      location.y = 0;
    } else if (location.y < 0) {
      location.y = height;
    }

  }

}

Ennél érdekesebb mozgást úgy érhetünk el ha a gyorsulásnak is megadunk egy PVektort, és ezt hozzáadjuk a sebességhez:  

  velocity.add(acceleration);
  location.add(velocity);

De használhatunk algoritmusokat is. Lehet a gyorsulás egyenletes vagy random, zajos vagy egérmozgásnak megfelelő is akár.

         

The source code contained in this tutorial is also available for download.

forrás: http://processing.org/learning/pvector/ 

 

3 komment

2013.02.21.
11:26

Írta: harsanyireka

PShape

Mi az a PShape?

Az elején megtanultuk hogyan rajzoljunk egyszerű "primitív" formákat: négyzetet, ellipszist, vonalat, pontot, háromszöget, stb.

rect(x,y,w,h);
ellipse(x,y,w,h);
line(x1,y1,x2,y2);
triangle(x1,y1,x2,y2,x3,y3);

Ennél haladóbb megoldás, amikor abeginShape() és endShape() függvények segítségével pontokat határozunk meg, ésígyrajzolunk ki egy poligont (sokszöget):

beginShape();
vertex(x1,y1);
vertex(x2,y2);
vertex(x3,y3);
vertex(x4,y4);
// stb;
endShape();

Sokkal bonolultabb formákat is létrehozhatunk hacsoportosítjuk és egyosztályba rendezzük ezeket a rajzoló függvényeket:

class MyWackyShape {
  // Néhány válozó
  
  // Konstruktor*

  // Néhány függvény

  // Jelenítsük meg a formát!
  void display() {
    rect(x,y,w,h);
    ellipse(x,y,w,h);
    beginShape();
    vertex(x1,y1);
    vertex(x2,y2);
    vertex(x3,y3);
    vertex(x4,y4);
    // stb;
    endShape();
  }
}

*"A konstruktort tekinthetjük egy olyan speciális függvénynek, melyben lévő kód akkor fut le, amikor az adott osztályból létrehozunk egy példányt. A konstruktor éppen ezért jól felhasználható olyan esetekben, amikor egy-egy példány létrejöttekor szükséges valamilyen osztályon belüli inicializálást elvégezni, például egy belső változónak kezdeti értéket adni." (http://www.softwareonline.hu/art1902/konstruktorok.html)

Mindazonáltal a fentieken túl van egy másik, gyorsabb lehetőség is Processingben, ez a PShape. A PImage-hez hasonlóan működik. Először meghatározzuk a PShape objektumot, utána loadShape()-pel betöltjük az SVG vagy OBJ kiterjesztésű fájlunkat:

void setup() {
  size(100, 100);
  // data mappában kell lennie a "bot.svg"fájlnak!
s = loadShape("bot.svg");
//// s = loadShape("bot.obj");
} 

void draw() {
shape(s, 10, 10, 80, 80);
}

Primitív PShape

Kezdjük a legegyszerűbb formával. Ha szimplán a draw() metódust használjuk akkor a következő képpen írhatunk egy egérmozgást követő négyzetet:

void setup() {
size(400,400);
}
void draw() {
background(51);
stroke(255);
fill(127);
rect(mouseX,mouseY,100,50);
}

Ehhez képest ha PShape-etakarunk használni, akkor egy olyan változót kell létrehoznunk, ami tárolja a szín és a méter információt. Először inicializálni kell a PShape-et a createShape() metódussal. Ez tartalmazza azokata paramétereket, amik a PShape-re vonatkoznak. Először a fajtáját adjuk meg (ELLIPSE, RECT, ARC, TRIANGLE, SPHERE, BOX, LINE, GROUP, stb.), utána  az x és y koordinátáit, majd a szélességét és magasságát, és mindezt a setup-on belül tesszük:

PShape rectangle;

void setup() {  
size(400,400); rectangle = createShape(RECT,mouseX,mouseY,100,50); }

De fenti kód nem működik mégsem, ennek oka hogy amikor elindul a program a mousex és mousey 0,0-ként definiálódik, így is marad, mivel setup-ba írtuk. Mit kell tennünk? Fix helyen defíniáljuk a négyzetet és utána draw-ban 2d transzformáció segítségével mozgatjuk (translate, rotate, stb.). És ha azt akarjuk hogy a négyzetet a  közepénél lehessen mozgatni akkor úgy rajzoljuk ki hogy oda essen a 0,0 koordináta:

PShape rectangle;

void setup() {  
size(400,400); rectangle = createShape(RECT,-50,-25,100,50); }
void draw() {
  background(51);
  translate(mouseX,mouseY);
  shape(rectangle);
}

A négyzet színeit is meghatározhatjuk pontszintaxissal. Ha ezt is dinamikusan szeretnénk változtatni akkor értelem szerűen draw-ba kell tenni (példa kommentben).

PShape rectangle;
void setup() {  
  rectangle = createShape(RECT,-50,-25,100,50);
  rectangle.stroke(255);  
  rectangle.strokeWeight(4);
  rectangle.fill(127);
}
void draw() {
  background(51);
  translate(mouseX,mouseY);
////rectangle.fill(map(mouseX, 0, width, 0, 255));
shape(rectangle);
}

        

Egyéni PShape formák

Alapvetően sokszöget a beginShape() és endShape() fügvényekkel is rajzolhatunk a csúcsok meghatározásával:

void draw() {
  background(51);
  translate(mouseX, mouseY);
  fill(102);
  stroke(255);
  strokeWeight(2);
  beginShape();
  vertex(0, -50);
  vertex(14, -20);
  vertex(47, -15);
  vertex(23, 7);
  vertex(29, 40);
  vertex(0, 25);
  vertex(-29, 40);
  vertex(-23, 7);
  vertex(-47, -15);
  vertex(-14, -20);
  endShape(CLOSE);
}

Ha PShape-et használunk ezeket a funkciókat a createShape() és az end() váltja fel:

PShape star;

void setup() {
  star = createShape();  // beginShape(); helyett

  // sarkok meghatározása marad itt

  star.end(CLOSE);       // endShape(CLOSE); helyett
}

Hozzunk létre egy star nevű PShape objektumot! Határozzuk meg a színét!

void setup() {
  // Létrehozzuk a formát:
  star = createShape();

  // színeket beállítjuk:
  star.fill(102);
  star.stroke(255);
  star.strokeWeight(2);

  // sarkokat megadjuk:
  star.vertex(0, -50);
  star.vertex(14, -20);
  star.vertex(47, -15);
  star.vertex(23, 7);
  star.vertex(29, 40);
  star.vertex(0, 25);
  star.vertex(-29, 40);
  star.vertex(-23, 7);
  star.vertex(-47, -15);
  star.vertex(-14, -20);

  // Kész a formánk:
  star.end(CLOSE);
}

      

Több PShape egyszerre

A hagyományos megoldása ennek, hogy egy osztályban határozzuk meg a formát és utána több darabot jelenítünk meg belőle egy tömb és a translate() segítségével, például:

void display() {
  pushMatrix();
  translate(x, y);
  fill(102);
  stroke(255);
  strokeWeight(2);
  beginShape();
  vertex(0, -50);
  vertex(14, -20);
  vertex(47, -15);
  vertex(23, 7);
  vertex(29, 40);
  vertex(0, 25);
  vertex(-29, 40);
  vertex(-23, 7);
  vertex(-47, -15);
  vertex(-14, -20);
  endShape(CLOSE);
  popMatrix();
}

/////////////////////////////////
void draw() {
  background(51);
  for (int i = 0; i < stars.length; i++) {
    stars[i].display(); 
  }
}

Ugyanez PShape-pel jóval gyorsabban megy, render szempontból is. Itt szintén egy osztályon belül hozzuk létre a formát. Ennek szintaxisa:

class Polygon {
  PShape s;
  
  void display() {
    shape(s);
  }
}

A diplay függvényt majd draw-ban hívjuk meg. A PShape-et a konstruktorban kell inicializálni, közvetlenül az osztályban. Mivel minden objektum azonos PShape-ként jelenik meg. 


  Star() {
    // létrehozzuk aformát:
    s = createShape();
    star.beginShape();
    // beállítjuk a színeket
    star.fill(102);
    star.stroke(255);
    star.strokeWeight(2);
    // megadjuk a vertexeket:
    star.vertex(0, -50);
    star.vertex(14, -20);
    star.vertex(47, -15);
    star.vertex(23, 7);
    star.vertex(29, 40);
    star.vertex(0, 25);
    star.vertex(-29, 40);
    star.vertex(-23, 7);
    star.vertex(-47, -15);
    star.vertex(-14, -20);
    star.endShape(CLOSE);
  }

Ennek a módszernek akkor van értelme, ha minden objektum saját, egy algoritmus általgenerált geometriával rendelkezik. Mindazonáltal azonos PShape-kéntjelenítjükmeg őket, még jobb ha konstruktorral hivatkozunk a tulajdonságokra. Nézzük meg hogy működikez! Hozzunk létre egy "Polygon" osztályt ami egy PShape-re hivatkozik:

class Polygon {
  PShape s;
  
  void display() {
    shape(s);
  }
}

A fenti kódban a formát az objektum konstuktorában hoztuk létre. Egy másik módja ennek hogy paraméterben adjuk meg a formát: 

Polygon(PShape s_) {
    s = s_;
  }

Ahhoz hogy ez működjön, a PShape-et ott kell meghívni ahol az objektumot létrehoztuk. E a setup-on belül így fest:

Polygon poly;                   
// Polygon típusú objektum void setup() { PShape star = createShape();
// Először létrehoztuk a PShape-et
star.beginShape(); star.noStroke(); star.fill(0, 127); star.vertex(0, -50); star.vertex(14, -20); star.vertex(47, -15); star.vertex(23, 7); star.vertex(29, 40); star.vertex(0, 25); star.vertex(-29, 40); star.vertex(-23, 7); star.vertex(-47, -15); star.vertex(-14, -20); star.endShape(CLOSE); poly = new Polygon(star);
// Azután a poligon objektumot adjuk meg,
// a PShape-re hivatkozva }

Ez egy nagyon rugalmas megközelítés. Például ha van egy PShape-ekből álló tömbböd, akkor több poligont is létrehozhatsz random PShape-ekkel. Egy egyszerű példa erre:

ArrayList polygons;
PShape[] shapes = new PShape[2];                       
//a PShape tömbb void setup() { size(640, 360, P2D); smooth();
// két Pshape van shapes[0] = createShape(ELLIPSE,0,0,100,100); shapes[1] = createShape(RECT,0,0,100,100); polygons = new ArrayList(); for (int i = 0; i < 25; i++) { int selection = int(random(shapes.length));
// Random értéket használunk Polygon p = new Polygon(shapes[selection]);
// ugyanazt a PShape-t használjuk poligonok létrehozására polygons.add(p); } }

                     

PÉLDA: http://www.learningprocessing.com/examples/chapter-22/example-22-2/    

plusz Processing/Examples/Topics/Create Shapes/ PolygonPShape példák

poligon1.jpg

Egyszerűbb PShape-ek

A krábban használt formákat is tehetjük PShap-be: POINT, LINE, TRIANGLE, TRIANGLE_FAN, TRIANGLE_STRIP, QUAD, QUAD_STRIP stb. pl:

PShape s = createShape(QUAD_STRIP);

Ha a típusa nincs meghatározva, akkor lehet szabálytalan forma is, mint pl. a csillag a korábbi példában. A PShape-et útvonalként (path) is használhatjuk ha nem zárjuk le a végén. Egy példa erre, szinusz hullámot tartalmaz a PShape:

PShape path = createShape();
  path.beginShape();
  float x = 0;
  //útvonal meghatározása szinusz hullámként
  for (float a = 0; a < TWO_PI; a += 0.1) {
    path.vertex(x,sin(a)*100);
    x+= 5;
  }
  // Ha útvonalként használjuk nem kell bezárni a végén!
  path.endShape();     
  

Path PShape példa az Exaples-ben.


A PShape tartalmazza a beginContour() és endContour() metódust, amik lehetővé teszik hogy egy formát kivonjunk egy másikból, így egy lukas formát kapunk. A példában két négyzetet használink ehhez:

PShape s = createShape();
s.beginShape();

// a külsőforma
s.beginContour();
s.vertex(-100,-100);
s.vertex(100,-100);
s.vertex(100,100);
s.vertex(-100,100);
s.vertex(-100,-100);
s.endContour();

// a belső forma
s.beginContour();
s.vertex(-10,-10);
s.vertex(10,-10);
s.vertex(10,10);
s.vertex(-10,10);
s.vertex(-10,-10);
s.endContour();

// a forma bezárása:
s.endShape();

Examples/Topics/CreateShapes/ BeginEndContour példa.

PShape-ek csoportosítása

Szülő-Gyerek (Parent-Child) viszonnyal tudjuk a fomrákat csoportosítani. Úgy hogy a fő PShape nevével hívjuk meg pontszintaxissal ahozzá tartozó formákat:

// Szülő Pshape:
PShape alien = createShape(GROUP);

// Két forma létrehozása
PShape head = createShape(ELLIPSE, 0, 0, 50, 50);
PShape body = createShape(RECT, 0, 50, 50, 100);

// A szülő-csoporthoz adjuk a két gyerek-formát:
alien.addChild(head);
alien.addChild(body);

// Kirajzoljuk:
translate(width/2, height/2);
shape(alien);

Examples/Topics/CreateShapes/ GroupPShape példa.               

Fájl betöltése PShape-be

2d formákhoz betölthetünk SVG fájlt, 3d formához OBJ fájlt a loadShape() meghívásával:

PShape svg;

void setup() {
  size(640, 360, P2D);
  svg = loadShape("star.svg");
}

void draw() {
  background(255);
  shape(svg);
}

A PShape csúcspontjainak valós-idejű irányítása

A vertex-eket dinamikusan is változtathatjuk a getVertex() és setVertex() fügvényekkel.  Egy for ciklussal tudunk végighaladni a csúcspontokon a getVertexCount() segítségével, amit egy PVector objektumot ad vissza, így ezt kell manipulálnunk, pont szintaxissal tudjuk meghívni a pontjainak x és y koordinátáit:

for (int i = 0; i < s.getVertexCount(); i++) {
  PVector v = s.getVertex(i);
  v.x += random(-1,1);
  v.y += random(-1,1);
  s.setVertex(i,v.x,v.y);
}

Examples/Topics/CreateShapes/ WigglePShape példa.

PShapehez kapcsolódó metódusok: http://processing.org/reference/PShape.html 

      

forrás:

http://processing.org/learning/pshape/

Szólj hozzá!

2013.02.13.
16:08

Írta: harsanyireka

Trigonometria

A grafikus programozás legfőbb része a trigonometria, ami a síkgeometriában a a derékszügű háromszög oldalai és szögei közötti összefüggéseket vizsgálja. 

A könnyebb megjegyezhetőség kedvéért mozaikszavakat is alkalmazhatunk:

hypotenuse-átfogó, opposite-ellentétes (a szöggel szemközti), adjacent-szomszédes

  • soh - szinusz
  • cah - koszinusz
  • toa - tangens

  • szinusz függvény (sin) az α szöggel szemben lévő a befogó és a c átfogó hányadosa,
  • koszinusz függvény (cos) az α szög melletti b befogó és a c átfogó hányadosa,
  • tangens függvény az α szöggel szemben lévő a befogó és a szög melletti bbefogó hányadosa.

      
\sin \alpha=\frac{a}{c},    \cos \alpha=\frac{b}{c},    \tan \alpha=\frac{a}{b}=\frac{\sin \alpha}{\cos \alpha}

         

Egy másik módszer a trigonometria megértéséhez az egység sugarú kör ábrája (ábra: Unit Circle). Ebben az esetben polárkoordináta rendszert használunk a korábban használt Descartes-féle helyett. "A polárkoordinátázás a sík egyfajta görbevonalú bekoordinátázása. Koncentrikus körök és sugárirányú egyenesek alkotják a hálózatot". Egy pont helyzetét nem x,y koordináa szerint adjuk meg hanem r és θ adatok alapján, ahol az r a kör sugarát és a θ (théta) a forgás szöge. A szöget jobb középső szélen kezdjük mérni és az óramutató járásával ellentétes irányban mérjük. (Lásd radián ábra). Az egységnyi kör (Unit Circle) diagramban a p pont 45º-nál helyezkedik el ami PI/4 értéknek felel meg radiánban megadva. 180º =  PI . A PI értéke megközelítőleg 3.142.

A polárkoordináta rendszerben tehát nem fokban mérjük a szögeket hanem radiánban. A forgás ívhosszát kiszámíthatjuk  r*θ képlettel, a kör sugarát meg kell szorozni a szögével. De általában ezt a rendszert a pixel koordinátáinak meghatározására szogtuk használni (pl. ha körbe akarunk forgatni egy pontot az origóhoz képest).

"A fenti definíciók (sin, cos, tang egyenletek) csak 0 és 90° között (0 és π/2 radián között) értelmezhetők. Az egységsugarú kört alkalmazva a definíció kiterjeszthető az összes pozitív és negatív argumentumra (l. trigonometrikus függvények). A trigonometrikus függvények periodikus függvények, 180° (π radián) vagy 360° (2π radián) periodicitással. Ez azt jelenti, hogy ismétlődnek a fenti értékekkel." Tehát periodikus hullámként jelenik meg a szinusz és a koszinusz.

A körvonalon fekvő p pont egy háromszögre illyesztehtő, ahol a sugár az átfogó, így alkalmazhatjuk a Pithagorsz tételt. Így tudsz átjárni a két koordináta rendszer között:

  x = cosine(theta) * radius
  y = sine(theta) * radius

Ez a Processingben a következő képpen néz ki:

  float x = cos(radians(angle)) * radius;
   float y = sin(radians(angle)) * radius;

Nefelejtsd el hogy a polárkoordináta rendszerben radiánt használunk még a Descartes-félében fokot! A kettő közötti váltás a következő egyenlettel lehetséges:

  theta = angle*pi/180
  angle = theta*180/pi

De Processingben van egy kényelmesebb megoldás is erre:

  theta = radians(angle)
  angle = degrees(theta)

            

SZINTAXISOK:

Trigonometry

acos()

asin()

atan()

atan2()

cos()

degrees()

radians()

sin()

tan()

Ebene polarkoordinaten.PNG

x = r \cdot\cos \varphi \,

y = r \cdot\sin \varphi \,

Ahol a szöget PI-ben adjuk meg! A sugarat ki tudjuk számolni a Pitagorasz tétellel:

r = \sqrt{x^2 + y^2} \,

 

       

Radián és fok viszonya:

Degree-Radian_Conversion.png

     

Szögfüggvények:

Összefüggések: szinusz (sin), koszinusz (cos), tangens (tg), kotangens (ctg), -ezek a periodikus függvények-  és szekáns (sec), koszekáns (csc):

Fájl:Circle-trig6.svg

\begin{align}
\cos(x) &= \sin\left( x + \frac {\pi} {2}\right)\\
\mathrm{tg}(x) &= \frac{\sin(x)}{\cos(x)} &\quad \mathrm{ctg}(x)&= \frac{\cos(x)}{\sin(x)} = \frac{1}{\mathrm{tg}(x)}\\
\sec(x) &= \frac{1}{\cos(x)} &\quad \csc(x)&= \frac{1}{\sin(x)}
\end{align}

        

Trigonometrikus függvények ábrázolása:

http://www.akg.hu/matek/fuggvenyek/sinus.html animáció itt.

               

         

PÉLDA

A következő példa azt mutatja be hogyan függössze az egységsugarú kör aszinusz hullámmal:

trigonometria.jpg

/** Sine Console
* Processing: Creative Coding and
* Computational Art
* By Ira Greenberg */

float px, py, px2, py2;
float angle, angle2;
float radius = 50;
float frequency = 2;
float frequency2 = 2;
float x, x2;

// létrehozunk egy font objektumot
PFont myFont;

void setup(){
  size(600, 200);
  background (127);
  // egy rendszer font-ból generáljuk a betűnket
  myFont = createFont("verdana", 12);
  textFont(myFont);
}

void draw(){
  background (127);
  noStroke();
  fill(255);
  ellipse(width/8, 75, radius*2, radius*2);
  // a kör körül forgatjuk a négyzetet
  px = width/8 + cos(radians(angle))*(radius);
  py = 75 + sin(radians(angle))*(radius);
  rectMode(CENTER);
  fill(0);
  //kirajzoljuk a négyzetet
  rect (px, py, 5, 5);
  stroke(100);
  line(width/8, 75, px, py);
  stroke(200);

  // ismét 0-ra inicializálunk hogy elkerüljük,
  // a villogást az újra rajzolás alatt
  angle2 = 0;

  // kirajzoljuk a statikus görbét - y = sin(x)
  for (int i = 0; i< width; i++){
    px2 = width/8 + cos(radians(angle2))*(radius);
    py2 = 75 + sin(radians(angle2))*(radius);
    point(width/8+radius+i, py2);
    angle2 -= frequency2;
  }

  // a sinus görbe mentén kis ellipszist küldünk
  // hogy illusztráljuk a kapcsolatot a kör és a
// hullám között:
  noStroke();
  ellipse(width/8+radius+x, py, 5, 5);
  angle -= frequency;
  x+=1;

  // amikor a kis ellipszis az ablakunk végéhez ér
  // újra inicializálunk néhány változót:
  if (x>= width-60) {
    x = 0;
    angle = 0;
  }

  // változó értékű, dinamikus vonallal kirajzoljuk
// a hullámot:   stroke(50);   line(px, py, width/8+radius+x, py);   // kiküldünk pár számítást az ablakba   text("y = sin x", 35, 185);   text("px = " + px, 105, 185);   text("py = " + py, 215, 185); }

 

                                

forrás:

http://hu.wikipedia.org/wiki/Trigonometria

http://processing.org/learning/trig/

http://hu.wikipedia.org/wiki/Trigonometrikus_f%C3%BCggv%C3%A9nyek

http://hu.wikipedia.org/wiki/Trigonometrikus_azonoss%C3%A1gok

http://hu.wikipedia.org/wiki/Pol%C3%A1rkoordin%C3%A1ta-rendszer

http://en.wikipedia.org/wiki/Radian

 

 

 

 

 

 

Szólj hozzá!

2013.01.30.
13:54

Írta: harsanyireka

Stringek, kirajzolt szövegek, transzformációk

A String osztály

Már használtunk stringet pl. képfájl betöltésére vagy szöveg kiírására, ezek kódjai:

println("printing some text to the message window!");  
PImage img = loadImage("filename.jpg");                

A string karaktert vagy karakterláncot jelent. A karakterek értéket is tárolnak, tehát a string is adat. 

         

Ha nem lenne string osztályunk akkor ilyen kódot kellene írnunk:

char[] sometext={'H','e','l','l','o',' ','W','o','r','l','d'};

De mivel Processingben van, ezét a következő formát használhatjuk:

String sometext = "How do I make String? Type some characters 
between quotation marks!"; //a kódot egy sorba írd!

Különböző metódusokkal működik:

  

charAt()

Szimpla karater ad vissza, az index szám jelöli hogy a stringnek hanyadik elemét akarjuk kiolvasni. Arra vigyázz hogy az első elemet 0-val, a másodikat 1-gyel stb. lehet elérni!

String message = "some text here.";
char c = message.charAt(3);
println(c);                // az eredmény: 'e'

  

length()

Nem összekeverendő a tömbb hossz paraméterével. A kerek zárójel is arra utal hogy ez nem egy paraméter hanem egy függvény.

String message = "This String is 34 characters long.";
println(message.length());

      

toUpperCase(), toLowerCase() 

Átváltoztathatjuk a stiongünket nagybetűkre és kisbetkre ezekkel.

String uppercase = message.toUpperCase();
println(uppercase); 

Mivel a string egy állandó, tehát értéke nem változik, ezért ha változtatni akarjuk akkor mindig létre kell hoznunk egy új stringet. Ebben a példában a a  toUpperCase a sting negybetűs másolatát küldi vissza.

      

equals()

== operátorral tehetjük a stringet valamivelegyenlővé, a szimpla = deklarálásra szolgál!

String one = "hello";
String two = "hello";
println(one == two);

A harmadik sorban az objektumok memória címét hasonlítjuk össze (technikailag), mivel ugyanazta stringet tartalmazzaa kétérték megegyezik, egyébként a végeredmény hamis (false) lenne.  

Az equal() függvény biztosítja hogy leellenőrizhessük hogy a két string ugyanazt a karakterláncot tartalmazza-e, figyelmen kívül hagyva hogy hol tároljuk az adatot.

String one = "hello";
String two = "hello";
println(one.equals(two));

Habár mindkét kód ugyanazt csinálja, biztonságosabb az equalt() használni, mivel attól függően hogy a kódban hol hozzuk létre string objektumot, az == nem mindigműködik. 

Stingeket egymáshoz is fűzhetünk a + operátorral, számok esetében ez természetesen összeadást jelent. 

String helloworld = "Hello" + "World";

Változókat is hozzá fűzhetünk stringekhez:

int x = 10;
String message = "The value of x is: " + x;

                         

Szöveg megjelenítése

Ennek legegyszerűbb modja hakiprinteljük őket az üzenet ablakban. Ezt csináljuk amikor nyomon akarjuk követni az adatunkat, pl. ha a horizontális egérmozgást akarjuk tudni akkor ezt írjuk le:

println(mouseX);

De akár leíró szöveget is kiírhatunk a kódban általunk gondolt helyen:

println("Kiírjuk az egér pozícióját!");

        

Ebben az esetben a szövegünk még nem a megjelenítő ablakban van, hogyan tudnánk ezt is elérni? A következő pár lépéssel:

1. Deklaráljunk egy PFont típusú objektumot.

PFont f;

2. Hozzuk létre a betűket a nevére hivatkozva a createFont() függvénnyel. Ez ugyanúgy egyszer fut le minta setup(). Éppúgy mint a képeknél, memóriába töltjük az adatot, tehát nem célszerű draw() függvénbe tenni. 

Az elérhető rendszer-betűtipusokat (mindenkinek van, nem kell telepíteniük) egy kóddal ki tudod printelni, ezután innen tudsz választani:

size(200, 200);
String[] fontList = PFont.list();
println(fontList);

Ezután a következő szintaxissal tudod alkalmazni a betű-nevedhez a paramétereket: típus, mérte, anti-alising legyen (true) vagy ne (false):

f = createFont("Arial",16,true); 
// Arial, 16 pont, anti-aliasing be

3. Specifikáld a betűt a textFont() segítségével, két paramétere van, a betű-változód neve és a mérete (utóbbi opcionális és változtatható). Ez a sima text()-hez képest egy bitmap képet eredményez, így dinamikusan növelhető aszöveg mérete. P2D render használatával feljavíthatod a kép minőségét és teljesítményt is. A P3D render pixeleket eredményezhet.

textFont(f,36);

4. Szín megadása fill()-lel:

fill(255);

5. A szöveget a text() függvény meghívásával tudjuk megjeleníteni. Három paraméterrel rendelkezik, éppúgy mint a formák: a megjelenítendő szöveg, x és y koordinátaák.

text("Hello Strings!",10,100);

               

A teljes kód:

sting1.jpg

PFont f;                          
  
void setup() {
  size(200,200);
  f = createFont("Arial",16,true); 
}

void draw() {
  background(255);
  textFont(f,16);                 
  fill(0);                        
  text("Hello Strings!",10,100); 
}

   

Menüből is létrehozhatunk betűt: "Tools" --> "Create Font." Ez létrehoz egy  VLW betű fájlt a data mappádban, amit PFont objektumba be tudsz tölteni loadFont() függvénnyel.

f = loadFont("ArialMT-16.vlw");

     

Szöveg animálása

Nézzünk meg néhány hasznos függvényt a fentiek mellé!

   

textAlign()

A sortördelést határozhatjuk meg vele: balra zárt (LEFT), középre zárt (CENTER) és jobbra zárt (RIGHT).

PFont f;

void setup() {
  size(400,200);
  f = createFont("Arial",16,true); 
}

void draw() {
  background(255);

  stroke(175);
  line(width/2,0,width/2,height);

  textFont(f);       
  fill(0);

  textAlign(CENTER);
  text("This text is centered.",width/2,60); 

  textAlign(LEFT);
  text("This text is left aligned.",width/2,100); 

  textAlign(RIGHT);
  text("This text is right aligned.",width/2,140); 

               

textWidth()

Kiszámolja és visszaküldi egy karakter vagy szöveg string szélességét.

Csináljuk egy balról jobbra mozgó feliratot! Ha tudjuk a kezdőpont x koordinátáját és a szöveg szélességét, valamint az ablakunk méretét, akkor ki tudjuk számolni az animáció végének x koordinátáját, ahol méglátszik a teljes szöveg. 

Először setup()-ban inicializáljuk a szöveget, betűtipust és méretet, x és y koordinátát:

// Főcim animálása
String headline = "Akármi lehet a főcím";
PFont f;  // globális változó: f
float x;  // szöveg vzsintes pozíciója

void setup() {
  f = createFont("Arial",16,true);  // betű betöltése
  x = width; // inicializáljuk a szöveg jobb szélét
}

A draw()-ban megjelenítjüka szöveget az adott helyen:

textFont(f,16);        
textAlign(LEFT);
text(headline,x,180); 

Változtatjuk az x pozició értékét hogy létrejöjjön az animáció, mivel balra kell csússzon az értéket csökkentjük:

x = x - 3;

Most egy kicsit bonyolultabb rész jön. Mivel a szövegünk balra van zárva, ezért nem elég ha x kisebb mint 0 egyenlettel akarjuk eltűntetni az ablakból (mint formáknál) mivel ilyenkormég egy része belóg a képe. Ehelyett az egyenletünk úgy néz ki hogy x kisebb mint a nullából kivont szöveg-szélesség (alább a kód):

float w = textWidth(headline);
if (x < -w) {
  x = width; 
}

   

A teljes kód:

sting2.jpg

// string tömbbe tesszük a szöveget:
String[] headlines = {
  "Processing a köbön", 
  "Akármi lehet a főcím",
  };

PFont f;  // f globális betű változó
float x;  // szöveg vizszintes helyzte
int index = 0;

void setup() {
  size(400,200);
  f = createFont("Arial",16,true);  
  // jobboldali szöveg eltűntetés inicializálása
  x = width; 
}

void draw() {
  background(255);
  fill(0);

  // szöveg megjelenítése x poziciónál
  textFont(f,16);        
  textAlign(LEFT);
  text(headlines[index],x,180); 

  // animáció sebessége:
  x = x - 3;

  // ha x kisebb minta negatv szélesség 
  // tűnjön el a szöveg:
  float w = textWidth(headlines[index]);
  if (x < -w) {
    x = width; 
    index = (index + 1) % headlines.length;
  }
}

   

Ezeken kívül még a  textLeading()textMode()textSize() függvények tartoznak a témához. 

                  

Szöveg forgatása

Transzformációt és forgatást is érvényesíthetünk szövegre. Tanslate() használatakor a koordinátarendszerünket helyezzük alrébb, így a fomra x,y értéke ugyanaz maradhat:

grid moved with arrow showing motion

A pushMatrix() és popMatrix() függvényekkel jelezzük hogy a transzformációt melyik parancssorokra akarjuk értelmezni (amennyiben nem az egész kódra akarjuk). A példában a kirajzolt szürke négyszöget eltoljuk translate()-tel ráccsal együtt 60,80 koordinátához és kékre színezzük, amint látod ennek helye megegyezik a másodiknak kirajzolt piros négyzettel, amit az eredeti koordinátarendszerben rajzoltunk ki, csak a kezdőpontját helyeztük át, mivel minkét négyszög elfás,a színeik összekeverednek, így egy lila jön létre:

string3.jpg

void setup()
{
  size(200, 200);
  background(255);
  noStroke();

  // eredeti szürke négyzet kirajzolása:
  fill(192);
  rect(20, 20, 40, 40);
  
  // az átlátszó piros nyégyzet megváltoztatott koordinátával: 
  fill(255, 0, 0, 128);
  rect(20 + 60, 20 + 80, 40, 40);
  
  // az átlátszó kék négyzet a rács alrébb tolásával:
  fill(0, 0, 255, 128);
  pushMatrix();
  translate(60, 80);
  rect(20, 20, 40, 40);
  popMatrix();
}

 A forgatás ugyanígy működik, paraméternek megadjuk hogy milyen mértékegységben és mennyivel akarjuk forgatni:

  pushMatrix();
  rotate(radians(45));
  fill(0);
  rect(40, 40, 40, 40);
  popMatrix();

 Nagyítás vagy kicsinyítés:

  pushMatrix();
  scale(2.0);
  rect(20, 20, 40, 40);
  popMatrix();

 

Az előzőek alapján forgassuk körbe a középpont körül a középre igazított szövegünket:

string4.jpg

PFont f;
String message = "ez a szöveg pörög";
float theta;

void setup() {
  size(200, 200);
  f = createFont("Arial",20,true);
}

void draw() { 
  background(255);
  fill(0); 
  textFont(f);                     // szöveg beállítása
  translate(width/2,height/2);    // középre transzformálása
  rotate(theta);       // elforgatása theta-változó értékével
  textAlign(CENTER);            
  text(message,0,0);            
  theta += 0.05;                // forgatás növelése
}

     

Szöveg kirajzolása karakterenként

Ebben az esetben a karaktereket egyenként mozgathatod, színezheted, stb. Ehhez létre kell hoznod egy tömbböt amibe beolvasod a karaktereket egyenként charAt() függvénnyel, és innen fogod őket kirajzolni úgy hogy a pozíciójuk x tengelyen mindig 10-zel több. A fent említett módokon hozd létre a PFont objektumodat, töltsdbele a szöveget, határozd meg aszöveg tulajdonságait és draw()-ba írd a karakter számláló for ciklust!

PFont f;
String message = "Each character is not written individually.";
void setup() {
size(400, 200);
f = createFont("Arial",20,true);
}
void draw() { 
background(255);
fill(0);
textFont(f);
// egy tömbként jelenik meg a szöveg
text(message,10,height/2);
}

Ebben az esetben egyben jelent meg a szöveged, mivel nincs kiolvasva karakterenként. Írjuk át:

PFont f;
String message = "Each character is written individually.";
void setup() {
size(400, 200);
f = createFont("Arial",20,true);
}
void draw() { 
background(255);
fill(0);
textFont(f);

int x = 10; 
for (int i = 0; i < message.length(); i++) {
// i++ ugyanaz mint i=i+1 // charAt() egyesével beolvassa a karaktereket: text(message.charAt(i),x,height/2); // 10 pixelközönként kiírja a karaktereket: x += 10; }

}

Ebben az esetbenmár karakterenkéntírtuk ki a szöveget,de nagyon szétesik, igazítsuk meg textWidth() fügvénnyel, ami kiolvassa az adott karakter (i változóval meghatározott) szélességét (pixelben) és hozzáadja az x-hez (ami10 pixel):

PFont f;
String message = "Each character is written individually.";

void setup() {
  size(400, 150);
  f = createFont("Arial",20,true);
}

void draw() { 
  background(255);
  fill(0);
  textFont(f); 
int x = 10; //első karakter itt lesz
//kiolvassuk a karaktereket sorban egy for ciklussal:
// for (int i = 0; i < message.length(); i++) { textSize(random(12,36)); // 12-36 közötti random érték text(message.charAt(i),x,height/2); // textWidth() a betűköz helyes beállítására: x += textWidth(message.charAt(i)); } noLoop(); }

       

Most már tudjuk effektezni is a karaktereket egyenként! Akövetkező példában, ha nyomva tartod a bal egér gombot, akkor összerázódnak a betűk, azaz pozíciót változtatnak:

PFont f;
String message = "click mouse to shake it up";
// Letter objektum tömb, letters névvel:
//Az osztályt később meghatározzuk majd! Letter[] letters; void setup() { size(260, 200); // betű betöltése f = createFont("Arial",20,true); textFont(f); // adott string hosszúságú tömbb létrehozása: letters = new Letter[message.length()]; // Letters inicializálása x pozícióra int x = 16; for (int i = 0; i < message.length(); i++) { letters[i] = new Letter(x,100,message.charAt(i)); x += textWidth(message.charAt(i)); } } void draw() { background(255); for (int i = 0; i < letters.length; i++) { // betűk megjelenítése letters[i].display(); // ha egérgomb nyomva, a betűk rázkódnak // ha nincs,akkor eredetipozícióba kerülnek if (mousePressed) { letters[i].shake(); } else { letters[i].home(); } } } // Az osztály ami minden egyes betűre jellemző class Letter { char letter; // jegyezze meg az eredeti pozícióját: float homex,homey; // és a pillanatnyit is: float x,y; Letter (float x_, float y_, char letter_) { homex = x = x_; homey = y = y_; letter = letter_; } // betűk megjelentése void display() { fill(0); textAlign(LEFT); text(letter,x,y); } // mozgasd a betűket random void shake() { x += random(-2,2); y += random(-2,2); } // kerüljenek vissza a betűk eredeti pozícióba void home() { x = homex; y = homey; } }

       

A karakterről karakterre módszer lehetővé teszi hogy görbére illesszük a szöveget. Első lépésként fűzzünk fel négyzeteket egy körvonalra, ehhez trigonometriát kell alkalmaznunk. 

 

Példa:

PFont f;
// A kör sugara:
float r = 100;
// a dobozok szélessége és magassága:
float w = 40;
float h = 40;

void setup() {
  size(320, 320);
  smooth();
}

void draw() {
  background(255);
  
  // Rajzoljunk egy kört az ablak közepére
  translate(width / 2, height / 2); //eltoltuk a rácsot
  noFill();
  stroke(0);
  // Az ablakunk közzépontj a köré is,
// innen rajzolunk r sugarú kört: ellipse(0, 0, r*2, r*2); // 10 dobozunk legyen: int totalBoxes = 10; // Visszakövetjük a pozíciójukat a görbe mentén: float arclength = 0; // for ciklus az összes dobozhoz: for (int i = 0; i < totalBoxes; i++) { // a dobozok felének szélességével toljuk el őket: arclength += w/2; // A fok radiánban megadva,
// a pozíció osztva a sugárral: float theta = arclength / r; pushMatrix(); // derékszögű koordináta átalakítása: translate(r*cos(theta), r*sin(theta)); // doboz elforgatása: rotate(theta); // doboz megjelenítése: fill(0,100); rectMode(CENTER); rect(0,0,w,h); popMatrix(); // ismét elmozdítjuk a dobozt: arclength += w/2; } }

Ezek után a dobozokon belülre helyezzünk egy-egy betűt. Mivel a betűk szélessége változó ismét a textWidth()-et hívjuk segítségül.

// A megjelenítendő szöveg:
String message = "text along a curve";

PFont f;
// a kör sugara:
float r = 100;

void setup() {
  size(320, 320);
  f = createFont("Georgia",40,true);
  textFont(f);
  // Középre zárjuk a szöveget:
  textAlign(CENTER);
  smooth();
}

void draw() {
  background(255);

  // középre toljuk a koordináta rendszert:
// kört rajzolunk: translate(width / 2, height / 2); noFill(); stroke(0); ellipse(0, 0, r*2, r*2); // Visszakövetjük a pozíciót a görbe mentén: float arclength = 0; // For ciklus az összes dobozhoz: for (int i = 0; i < message.length(); i++) { // leellenőrizzük minden karakter szélességét: char currentChar = message.charAt(i); float w = textWidth(currentChar); // alrébb tesszük a dobozok szélességével: arclength += w/2; // radiánsban megadott szög:
// dobozok szélessége osztva a kör sugarával: // a kör bal szélén kezdjük, PI-től indul: float theta = PI + arclength / r; pushMatrix(); // derékszögű koordináta átalakítása: translate(r*cos(theta), r*sin(theta)); // doboz elforgtása: rotate(theta+PI/2); // 90 fokkal elforgatjuk // karakter megjelenítése fill(0); text(currentChar,0,0); popMatrix(); // eltolása a megfelelő pozícióhoz: arclength += w/2; } }

 

A fokok PI-vel meghatározott értékei:

          

forrás: http://processing.org/learning/text/

http://processing.org/learning/transform2d/ 

http://processing.org/reference/pushMatrix_.html

 

 

 

Szólj hozzá!

2013.01.21.
16:15

Írta: harsanyireka

Képek és pixelek

A digitális képek nem mások mint adatok, R G B értékek által meghatározott részek egy pixelekből álló rácshálón. Kreatív gondolkodással és a kód alapú pixel manipuláció szeretetével ezeket az információkat számtalan módon megjeleníthetjük. Ezt a leckét a formák rajzolásából való kitörésnek szenteljük, képeket használunk fel processing-grafikák létrehozására.. 

    

Kezdő lépések

Remélhetőleg már ismered az egyszerű adat típusokat és tudod is használni őket, pl. a floatot és az integert, amik a számítógép memóriájában eltárolt értékek. Az objektum és osztály használatot is ismerheted egy korábbi leckéből.

A Processingben már előre defíniált osztályok is vannak így ezekehez már nem kell kódot írnunk, az egyik ilyen a PImage, ami kép betöltésére és megjelenítésére alkalmas. 

// PImage típusú változó deklarálása:
PImage img;	

void setup() {
  size(320,240);
  // új PImage objektumba kép fájl betöltése:
  img = loadImage("mysummervacation.jpg");
}

void draw() {
  background(0);
  // Kép kirajzolása 0,0 koordinátára
  image(img,0,0);
}

A PImage használata megegyezik a magadáltal írt osztályok használatával. A változója img néven már deklarálva van, ezért ezt kell használnod. Másodszor, új hivatkozást a loadImage() metódussal hozhatsz létre, aminek egy argumentuma van, egy stringel/szöveggel megjelelölt fájl név - ezt a képet betölti majd a memóriába. A képet amit használsz a sketch mappán belül található data nevű mappába kell tenni. Ezt megteheted úgy is, hogy a Sketc/Add file menüt használod vagy a Sketch/Show sketch folder menüvel hagyományos úton másolod be a képedet ide.

Ezeket a kiterjesztéseket tudod használni: GIF, JPG, TGA, PNG.

    

A fönti példában nem használunk konstruktort (new PImage() stb.) ennek ellenére a legtöbb objektum-alapú példában konstruktor szükséges az objektum létrehozásához. Két példaezekre: 

Spaceship ss = new Spaceship();
Flower flr = new Flower(25);

és

PImage img = loadImage("file.jpg");

    

Valójában a loadImage() függvény végzi el a konstruktor munkáját. egy új hivatkozást küld vissza a PImage-en belül meghatározott fájlnévvel.

Üres kép létrenozásáraa createImage() függvényt használhatjuk. Pl egy 200x200 pixel méretű RGB képet így adhatunk meg: 

PImage img = createImage(200,200,RGB);

A képbetöltés elég lassú feladat, de mivel a setup() csak egyszer futle a legelején, elég egyszer kivárni ezt. Ha draw()-ba tesszük "Out of Memory" hibaüzenetet kaphatunk.

Ha betöltöttük a képünket, akkor image() függvénnyel tudjuk megjeleníteni, aminek 3 argumnetuma van: a kép, valamint az x és az y pozíciója. Opcionálisan további két paraméter is megadhatóhaát akarjuk méretezni a képünket. 

image(img,10,20,90,60);

        

Képszűrők

 Ezek hasznosak ha pl meg akarod változtatni a képed szín értékeit, sötétségét vagy átlátszóságát, stb. Ehhez a tint() függvényt használhatod ami megegyezik a formáknál használt fill() függvénnyel. A tint() paraméterének azt a színértéket kell megadnod amit a képed minden egyespixelére alkalmazni akarsz.

A következő példában két képet maniplálunk, a kutya lesz a háttérbenés ez fog majd előtűnni. Első lépés a képek betöltése:

PImage sunflower = loadImage("sunflower.jpg");
PImage dog = loadImage("dog.jpg");
background(dog);

Ha a tint()paraméterének egy számot adunk meg akkor az a világosságára vonatkozik majd. 

// Eredeti állapotot látjuk, mivel 255 a max érték:
tint(255);  
image(sunflower,0,0);

      

//Sötétebb lett a képünk:
tint(100);  
image(sunflower,0,0);

     

A második paraméter az alfa csatornáját állítja a képünknek:

// 50%-ben átlátszó a képünk:
tint(255,127);  
image(sunflower,0,0);

    

Három paraméter esetén az RGB színértékekra vonatkoznak a számok:

// Nincs benne piros(0), csak kék(255) és zöld(200) szín:
tint(0,200,255);
image(sunflower,0,0);

    

Ha ehhez hozzáteszünk még egy negyedik paramétert az az alfa csatornát állítja, pont mint a második példánál. colorMode() függvénnyel szabályozhatjuk a tint() értékeit ugyanúgy mint a fill()-nél, egy előző leckében.

// Pirosra szineztük a képet és átlátszóvá tettük:
tint(255,0,0,100);
image(sunflower,0,0);

     

Pixel, pixel és még több pixel

Mint láthatadd, a rajzolás vagy képelőállítás alapfeltétele hogy meghívsz egy (mások által már megírt) függvényt, vonalat rajzolsz, formákat színezel ki, vagy bitmap képet manipulálsz. Néha azonban a megjelenő pixelekkel közvetlenül akarunk foglalkozni. Ezeknek szintén x és y koordinátán meghatározható pozíciója van, valamint szín értéket tárolnak lineáris sorrendben.

 

Nézzünk meg egy konkrét példát. Rajzoljunk ki pixelenként egy képet, ahol a pixelek színértékei a szürke skálán random változnak. A pixelek számát egy tömbben tároljuk el és egy for ciklussal haladunk végig sorban rajtuk:

size(200, 200);
//Mielőtt a pixelekkel foglalkozunk:
loadPixels();  
// az egyes pixeleket ciklussal számoljuk:
for (int i = 0; i < pixels.length; i++) {
  // 0-255 közötti random értéket rendelünk rand változónkhoz:
  float rand = random(255);
  // A random értékből szürkeárnyalatos színt kapunk:
  color c = color(rand);
  // ezt hozzárendeljük a pixelünkhöz:
  pixels[i] = c;
}
// Miután befejeztük a pixellel való munkát
updatePixels(); 

pixel1.jpg

Mint láthatod két függvényt hívtunk meg a példában, az egyik jelzi a Processingnek hogy most pixeleketfogunk manipulálni - loadPixels();

A másikkat a folyamat végén hívjuk meg amikor befejeztük a pixel manipulálást és meg akarjuk jeleníteni - updatePixels();

Mivel a fenti példában random értéket használtunk, nem fontos hogy x és y koordinátákat használjunk a pixelek meghívásánál, de néha ez is szükségeslehet. Vegyünk egy konkrét példát, állítsuk be minden páros oszlopot színezzünk fehérre és minden páratlant feketére! Hogyan tudjuk ezt egy egydimenziós tömbbel létrehozni? Honnan tudjuk hogy melyik pixel hol helyezkedik el?

  1. Létrehozhatunk egy képet általunk megadott szélességgel és magassággal (pixelben adjuk meg).
  2. Így már tudjuk hogy a pixeleket tartalmazó tömbbünk lemeinek száma a szélesség szorozva a magassággal. 
  3. Egy adott pixel koordinátáit is ki tudjuk számolni: x + (y*szélesség):

 

Ismét for ciklust használunk, azzal a különbséggel hogy most az oszlopokon belül számláljuk a sorokat:

size(200, 200);
loadPixels();  
// oszlopokat számláló ciklus:
for (int x = 0; x < width; x++) {
  // sorokat számláló ciklus:
  for (int y = 0; y < height; y++) {
    // 1-dimenziós helyét (y) megkeressüka pixelünknek:
    int loc = x + y * width;
    if (x % 2 == 0) { 
// ha párososzlopban vagyunk: pixels[loc] = color(255); } else {
// ha páratlan oszlopban vagyunk: pixels[loc] = color(0); } } } updatePixels();

pixel2.jpg

Bevezetés a képfeldolgozásba

Az előzőekben megnéztünk pár példát hogyan állítsunk be pixel értékeket tetszőlegesen. Most megnézzük ugyanezt PImage() használatán keresztül is. Pszeudokódal leírva ezt fogjuk csinálni:

  1. PImage objektumba betöltjük a képet
  2. Kiolvassuk belőle minden egyes pixel színét és ezt megjelenítjük

A PImange osztály a következő adatokat tárolja a képeel kapcsolatban: szélesség, magasság, színkezelés. Éppúgy mint egyéb felhasználó-definiált osztályoknál pont-szintaxis használatávalérhetjük ezeket el. 

PImage img = createImage(320,240,RGB);  
println(img.width);  // 320-at ad vissza
println(img.height); // 240-et ad vissza
img.pixels[0] = color(255,0,0); 
// a kép első pixelét pirosra állítja

Mivel ezekehez az adatokhoz hozzáférünk pixelenként, az értékek szerint ezeket a pixeleket meg is tudjuk jeleníteni:

pixel3.jpg

void setup() {
  size(200, 200);
  img = loadImage("sunflower.jpg");
}

void draw() {
  loadPixels(); 
  // Mivel el akarjuk érni a képünk pixeleit:  
  img.loadPixels(); 
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      int loc = x + y*width;
      
      // A red(), green(), blue() fügvényekkel 
// olvassuk ki a 3 színértéket: float r = red(img.pixels[loc]); float g = green(img.pixels[loc]); float b = blue(img.pixels[loc]); // Képfeldolgozás // Ha az RGB értéket akarjuk beállítani azt a
// megjelenítő ablakba küldés előtt kell
// megtennünk: // A kép pixelét beállítjuk megjelenítendő pixelnek: pixels[loc] = color(r,g,b); } } updatePixels(); }

        

Ha csak megjeleníteni akarunk egy képet akkor a kódot egyszerűsíthetjünk, mert ennél a pixelek RGB értékeihez is hozzáfértünk. Most az x y koordináta meghatározására használtunk algoritmust, de egyebeket is felfedezhetünk. Mielőtt tovább lépünk, hangsúlyozom hogy a fenti példa azért működik mert a kijelző ablak mérete megegyezik a kép méretével. Ha nem így lenne, akkor két helymeghatározás kalkulációt kellene elvégezni,egyet a forrás képen a másikat a megjelenített képen:

int imageLoc = x + y*img.width; 
int displayLoc = x + y*width;

Második kép szűrő, saját "színárnyalat" létrehozása

A tint() függvényt már használtuk korábban színezésre és alfa állítására. A pixelről pixelre módszer lehetővé teszi algoritmusok felfedezését képek átszínezésére. Példánkban a világosság értékkel kísérletezünk, ezt változtatjuk, dinamikusan növeljük vagy csökkentjük az egér vízszintes mozgatásával. Ehhez for ciklust használunk:

for (int x = 0; x < img.width; x++) {
  for (int y = 0; y < img.height; y++ ) {
    // 1D pixel helyzetének meghatározása
    int loc = x + y*img.width;
    // az R,G,B érték kiolvasása a képből
    float r = red   (img.pixels[loc]);
    float g = green (img.pixels[loc]);
    float b = blue  (img.pixels[loc]);
    // világosság változása egér koordináta alapján
    float adjustBrightness = ((float) mouseX / width)*8.0;
    r *= adjustBrightness;
    g *= adjustBrightness;
    b *= adjustBrightness;
    // RGB érték bekényszerítéseconstrain-nel 0-255 közé
    r = constrain(r,0,255);
    g = constrain(g,0,255);
    b = constrain(b,0,255);
    //új szín definiálása és kijelzése az ablakban
    color c = color(r,g,b);
    pixels[loc] = c;
  }
}

pixel4.jpg

Ebben a példában minden pixelértéket egyszerre változtatunk, de megtehetjök pl. hogy az egértől való távolságuk szerint (x,y) manipuláljuk őket:

for (int x = 0; x < img.width; x++) {
  for (int y = 0; y < img.height; y++ ) {
    // 1D pixel helyzetének meghatározása
    int loc = x + y*img.width;
    // R,G,B érték kiolvasásaa képből
    float r = red   (img.pixels[loc]);
    float g = green (img.pixels[loc]);
    float b = blue  (img.pixels[loc]);
    // Avilágosságot változtató érték meghatározása, 
    // az egértől való távolsághoz képest
    float distance = dist(x,y,mouseX,mouseY);
    float adjustBrightness = (50-distance)/50;  
    r *= adjustBrightness;
    g *= adjustBrightness;
    b *= adjustBrightness;
    // RGB érték bekényszerítése 0-255 közé
    r = constrain(r,0,255);
    g = constrain(g,0,255);
    b = constrain(b,0,255);
    // új szín definiálása és megjelenítése az ablakban
    color c = color(r,g,b);
    pixels[loc] = c;
  }
}

pixel5.jpg

Egyéb PImage objektumok írása

Eddigi képfeldolgozópéldáink kiolvasták a pixelek információit és a képet is pixelenként jelenítették meg közvetlenül az ablakban. Mindazonáltal néha kényelmesebb új pixelt írni a cél képbe amit aztán image() függvénnyel megjeleníthetünk. Határérték (Threshold) szűrő segítségével demonstrálom az utóbbit. 

A határérték szűrő minden pixelt két állapotban jelenít meg vagy fekete vagy fehér, attól függően hogy a megadott küszöbértékhez képest milyen értéke van. Ha a pixel világosság értéke nagyobb a küszöbértéknél akkor fehér pixel lesz megjelenítve, ha alacsonyabb akkor fekete. A kódban ez az érték100 lesz most:

pixel6.jpg

PImage source;       // forrás kép
PImage destination;  // cél kép

void setup() {
  size(200, 200);
  source = loadImage("sunflower.jpg");  
  // a célképet üres képként definiáljuk aminek mérete 
// megegyezik a forrás kép méretével: destination=createImage(source.width,source.height,RGB); } void draw() { float threshold = 127; // mindkét képnek megnézzük a pixeleit: source.loadPixels(); destination.loadPixels(); for (int x = 0; x < source.width; x++) { for (int y = 0; y < source.height; y++ ) { int loc = x + y*source.width; // világosság érték megvizsgálása: if (brightness(source.pixels[loc]) > threshold) { destination.pixels[loc] = color(255); // fehét } else { destination.pixels[loc] = color(0); // fekete } } } // a cél kép pixelét megváltoztatjuk: destination.updatePixels(); // és megjelenítjük: image(destination,0,0); }

De pixel programozás nélkül is létrehozhatjuk a fenti effektet a Processing filter() nevű függvényével. Ez azonban saját filter írásánálnem használható, csak ilyen (threshold) előre defíniált függvényeknél.

// Kép rajzolása:
image(img,0,0);
// határérték szűrőt alkalmazzuk az ablakon
// 0.5 küszöbérték 50% világosság értéket jelent
filter(THRESHOLD,0.5);

2. szint, pixel-csoport

Kezdjük azzal hogy két pixelt kiveszünk a forrás képből, a második az első mellett balra helyezkedik el:

Első pixel koordinátái x,y:

int loc = x + y*img.width;
color pix = img.pixels[loc];

A bal oldali szomszédja x-1,y koordinátával rendelkezik:

int leftLoc = (x-1) + y*img.width;
color leftPix = img.pixels[leftLoc];

Ezután a két pixel közötti különbséget beállítjuk egy új színnek:

float diff = abs(brightness(pix) - brightness(leftPix));
pixels[loc] = color(diff);

       

TELJES ALGORITMUS:

// Mivel a baloldali szomszédja kell a pixelnek
// az első oszlopot átugorjuk
for (int x = 1; x < width; x++) {
  for (int y = 0; y < height; y++ ) {
    // pixel helye és színe
    int loc = x + y*img.width;
    color pix = img.pixels[loc];

    // baloldali-pixel helye és színe
    int leftLoc = (x-1) + y*img.width;
    color leftPix = img.pixels[leftLoc];

    //új szín, értéke a két pixel különbsége:
    float diff = abs(brightness(pix) - brightness(leftPix));
    pixels[loc] = color(diff);
  }
}

pixel7.jpg  

Ez egy egyszer perem-érzékelő algoritmus. Ha a szomszédos pixel értékhez képest a különbség nagy, akkor valószínűleg az egy alakzat széle, ezt fehérrel rajzolja ki, atöbbitfeketével, agy jön létre ez a táblaképre krétával rajzolt hatás. 

Jóval kifinomultabb algoritmust is létrehozhatunk ha egyszerre több pixelt vizsgálunk. Minden pixelnek 8 szomszédja van:

Ezt az algoritmust gyakran "térbeli spirálnak" nevezik. A folyamat a bemeneti és a szomszédos pixel súlyozott középértékét (számtani közepét) használja a kimeneti pixel kiszámítására. Más szóval, az új pixel egy adott terület pixeleinek a függvénye. Különbözőszámú szomszédos pixeleket használhatunk, pl. 3x3, 5x5 mátrixot, stb.

Különböző súlyozású pixelek különféle effekteket eredményeznek. Pl.kiélseíthetünk egy képet ha a szomszédos pixel értékéből kivonunk és növeljük a középső pixel értékét. Blurt úgy hozhatunk lére hogy a szomszédos pixelek átlagértékét vesszük. (Aspirális mátrixban az érték 1-et adhat ki). (példa: http://celebrate.digitalbrain.com/celebrate/accounts/banhegyesi/web/stat2/205/)

Példa:

Élesítés:
-1   -1   -1
-1    9   -1
-1   -1   -1

Blur:
1/9  1/9  1/9
1/9  1/9  1/9
1/9  1/9  1/9

          

A következő példa 2D tömbb segítségével egy spirált hoz létre eltárolja a pixel súlyozott átlagát egy 3x3 mátrixban:

pixel8.jpg  

PImage img;
int w = 80;

// Spirál létrehozása különböző
// különböző mátrixokkal lehetséges 

float[][] matrix = { { -1, -1, -1 },
                     { -1,  9, -1 },
                     { -1, -1, -1 } }; 

void setup() {
  size(200, 200);
  frameRate(30);
  img = loadImage("sunflower.jpg");
}

void draw() {
  // Mivel csak a kép egy részével dolgozunk,
  // először az egész képet a háttérbe küldjük
  image(img,0,0);
  // A kis négyzet amin belül feldolgozzuk a képet:
  int xstart = constrain(mouseX-w/2,0,img.width);
  int ystart = constrain(mouseY-w/2,0,img.height);
  int xend = constrain(mouseX+w/2,0,img.width);
  int yend = constrain(mouseY+w/2,0,img.height);
  int matrixsize = 3;
  loadPixels();
  // Ciklus amivel a pixeleken végigmegyünk:
  for (int x = xstart; x < xend; x++) {
    for (int y = ystart; y < yend; y++ ) {
      // convolution()-be töltjük a pixelek helyét (x,y) 
      // ami egy új színértéket ad vissza,ezt megjelenítjük: 
      color c = convolution(x,y,matrix,matrixsize,img);
      int loc = x + y*img.width;
      pixels[loc] = c;
    }
  }
  updatePixels();

  stroke(0);
  noFill();
  rect(xstart,ystart,w,w);
}

color convolution(int x,int y,float[][] matrix,int matrixsize,PImage img){
  float rtotal = 0.0;
  float gtotal = 0.0;
  float btotal = 0.0;
  int offset = matrixsize / 2;
  // ciklus a spirális mátrixhoz:
  for (int i = 0; i < matrixsize; i++){
    for (int j= 0; j < matrixsize; j++){
      // amelyik pixelt teszteljük:
      int xloc = x+i-offset;
      int yloc = y+j-offset;
      int loc = xloc + img.width*yloc;
      // hogy ne lépjünk ki a pixel tömbből bekényszerítjük: 
      loc = constrain(loc,0,img.pixels.length-1);
      // Spirál kiszámítása
      // a szomszédos pixeleket összeadjuk és megszorozzuk 
// spirális mátrix értékekkel: rtotal += (red(img.pixels[loc]) * matrix[i][j]); gtotal += (green(img.pixels[loc]) * matrix[i][j]); btotal += (blue(img.pixels[loc]) * matrix[i][j]); } } // RGB érték bekényszerítése: rtotal = constrain(rtotal,0,255); gtotal = constrain(gtotal,0,255); btotal = constrain(btotal,0,255); // az eredményként kapott színt visszaküldjük: return color(rtotal,gtotal,btotal); }


Kép megjelenítése

Gondolhatnád hogy ezeket a képeket Photoshopban is csinálhatnád. Ami a Processing előnye hogy valós időben hozhatsz vele létre generált vagy interaktív képeket.

A következő két példában forma kirajzoló algoritmus fogunk használni. Egy PImage objektum kép pixeleibőlkiválasztunk színeket, a képet magát nem jelenítjük meg, csak adatbázisként szolgál, így számtalan kreatív képet hozhatunk létre.

Az első példában random köröket rajzolunk ki amiket az adott helyen lévő kép-pixel színével töltünk ki. Ez egyfajta pointilista stílust eredményez.

pixel9.jpg

PImage img;
int pointillize = 16;

void setup() {
  size(200,200);
  img = loadImage("sunflower.jpg");
  background(0);
  smooth();
}

void draw() {
  // pixel kiválasztása random:
  int x = int(random(img.width));
  int y = int(random(img.height));
  int loc = x + y*img.width;
  
  // RGB érték megnézése a képben:
  loadPixels();
  float r = red(img.pixels[loc]);
  float g = green(img.pixels[loc]);
  float b = blue(img.pixels[loc]);
  noStroke();
  
  // elipszis kirajzolása adott helyen adott színnel:
  fill(r,g,b,100);
  ellipse(x,y,pointillize,pointillize);
}

A következő példában egy két dimenziós képből nyerünk adatot és ennek felhasználásával négyzeteket rakunk ki egy három dimenziós térben. A négyzetek pozícióját egér koordinátákhoz rendeljük.

 

pixel10.jpg



PImage img; // forrás kép int cellsize = 2; // rács celláinak kiterjedése int cols, rows; // oszlopok és sorok száma a rendszerünkben void setup() { size(200, 200, P3D); img = loadImage("sunflower.jpg"); // kép betöltése cols = width/cellsize; // oszlopok kiszámítása rows = height/cellsize; // sorok kiszámítása } void draw() { background(0); loadPixels(); // for ciklus oszlopokhoz for ( int i = 0; i < cols;i++) { // for ciklus sorokhoz for ( int j = 0; j < rows;j++) { int x = i*cellsize + cellsize/2; // x érték int y = j*cellsize + cellsize/2; // y érték int loc = x + y*width; // pixel tömb x,y értéke color c = img.pixels[loc];
// a hozzá tartozó szín kiolvasása // egér x tengelyéhez rendeljük a négyzetek z értékét
// és a világosság értéket: float z=(mouseX/(float)width)*brightness(img.pixels[loc])-100.0; // elhelyezés az adott koordinátára, kitöltés és
// körvonalszín beállítása, és négyzetek kirajzolása: pushMatrix(); translate(x,y,z); fill(c); noStroke(); rectMode(CENTER); rect(0,0,cellsize,cellsize); popMatrix(); } } }

 

           

forrás: http://processing.org/learning/pixels/ 

Szólj hozzá!

2013.01.21.
15:16

Írta: harsanyireka

Görbék rajzolása

Ez a fejezet három görbe-típust tartalmaz: ív, görbe és Bézier görbe.

Ívek

Ez a legegyszerűbben rajzolható görbe. A Processing ezt egy ellipszis részeként defíniálja. Ezt a függvényt a következő paraméterekkel tudod meghívni:

arc(x, y, width, height, start, stop);

Az első négy ugyanaz mint az ellipse() függvénynél: először meghatározzuk a pozicióját és méretét annak a doboznak amibe kirajzoljuk és az utolsó két paraméter az ív kezdő és végpontját határozzák meg. Aszögeket fokban határozzuk meg és óra járásal megegyező irányba adjuk meg. PI egyenlő 180 fokkal.

gorbe1.jpg

void setup()
{
  size(300, 200);
  background(255);
  smooth();
  
  rectMode(CENTER); // behatároló négyzetek
  stroke(128);
  rect(35, 35, 50, 50);
  rect(105, 35, 50, 50);
  rect(175, 35, 50, 50);
  rect(105, 105, 100, 50);
  
  stroke(0);
  arc(35, 35, 50, 50, 0, PI / 2.0); // kör alsó negyede 
  arc(105, 35, 50, 50, -PI, 0);  // kör felső fele
  arc(175, 35, 50, 50, -PI / 6, PI / 6); // 60°-os szelet
  arc(105, 105, 100, 50, PI / 2, 3 * PI / 2); // 180°-os szelet
}

Görbék

A curve() függvénnyel olyan görbét is létrehozhatunk ami nem egy ellipszis része, ehhez meghatározzuk a kezdő és végpont x,y koordinátáját és két kontroll pontot ami meghatározza a görbe irányát és ívét. Szintaxisa:

curve(cpx1, cpy1, x1, y1, x2, y2, cpx2, cpy2);

cpx1, cpy1 - első kontroll pont helyzete

x1, y1 - görbe kezdő pontja

x2, y2 - görbe végpontja

cpx2, cpy2 - második kontroll pont helyzete

              

A következő példában a kontroll pontokat pirossal és a görbe végpontjaik kékkel jelenítjük meg:

gorbe2.jpg

void setup()
{
  size(200, 200);
  background(255);
  smooth();
  stroke(0);
  curve(40, 40, 80, 60, 100, 100, 60, 120);
  
  noStroke();
  fill(255, 0, 0);
  ellipse(40, 40, 3, 3);
  fill(0, 0, 255, 192);
  ellipse(100, 100, 3, 3);
  ellipse(80, 60, 3, 3);
  fill(255, 0, 0);
  ellipse(60, 120, 3, 3);  
}

Hogyan befolyásolják a kontroll pontok a görbe ívét? 
A görbe kezdő pontjának érintője/tangense párhuzamos a görbe végpontja és az első kontrollpont által határolt egyenessel. A görbe végpontjának érintője pedig a görbe kezdőpontja és a második kontroll pont által határolt egyenessel párhuzamos:

Folytonos kontrollpontos görbék

Ezt több pont meghatározásával tehetjük meg a curveVertex() fügvény segítségével. Ha ezt használod egy forma rajzolására akkor az elején a beginShape() a végén az endShape() függvényt meg kell hívnod.

A következő példában a görbe kezdő és végpontja egyúttal a kontrollpont is. 

gorbe3.jpg

void setup()
{
  int[ ] coords = {
    40, 40, 80, 60, 100, 100, 60, 120, 50, 150
  };
  int i;
  
  size(200, 200);
  background(255);
  smooth();

  noFill();
  stroke(0);
  beginShape();
  curveVertex(40, 40); // az első kontroll pont
  curveVertex(40, 40); // a görbe kezdő pontja
  curveVertex(80, 60);
  curveVertex(100, 100);
  curveVertex(60, 120);
  curveVertex(50, 150); // a görbe végpontja
  curveVertex(50, 150); // az utolsó kontroll pont
  endShape();
  
  // tömbök segítségével rövidítjük a kódok sokaságát;
  // és kirajzoljuk az ellipszist
  fill(255, 0, 0);
  noStroke();
  for (i = 0; i < coords.length; i += 2)
  {
    ellipse(coords[i], coords[i + 1], 3, 3);
  }
  
}

          

A köztes pontok érintői így néznek ki .

curvevertex2.png

Bézier görbék

Ezek sokkal szebb ívűek, de ugyanazok a paraméterei és a bezier() függvény meghívásával hozhatod őket létre.

bezier(x1, y1, cpx1, cpy1, cpx2, cpy2, x2, y2);

x1, y1 - görbe kezdő pontjának koordinátái

cpx1, cpy1 - első kontroll pont koordinátái

cpx2, cpy2 - második kontroll pont koordinátái

x2, y2 - görbe végpontjának koordinátái

                 

A következő kódban pirossal jelöltük a kontroll pontokat és kékkel a görbe végpontjait.

Screen Shot 2013-01-21 at 2.52.59 PM.png

void setup( )
{
  size(150, 150);
  background(255);
  smooth();

  ellipse(50, 75, 5, 5); // görbe végpontjai
  ellipse(100, 75, 5, 5);
  fill(255, 0, 0);
  ellipse(25, 25, 5, 5);  // kontroll pontok
  ellipse(125, 25, 5, 5); 
  noFill();
  stroke(0);
  bezier(50, 75, 25, 25, 125, 25, 100, 75);
}

A most rajzolt görbénk érintőit így néznek ki:

bezier_with_lines.png      

bővebben.: http://hu.wikipedia.org/wiki/B%C3%A9zier_g%C3%B6rbe

Folytonos Bézier görbe

Ennek létrehozásához bezierVertex() függvényt kell használnunk, a beginShape() és az endShape() parancsok meghívásával. A kezdő pontot vertex(startX, startY) paranccsal hozzuk létre, ezután a bezierVertex()-szel megadjuk a kontroll pontokat és a végpontot.

bezierVertex(cpx1, cpy1, cpx2, cpy2, x, y);

cpx1, cpy1 - első kontroll pont koordinátái

cpx2, cpy2 - második kintroll pont koordinátái

x, y - a következő pont rajta a görbén

              

Nézzünk rá példákat:

Screen Shot 2013-01-21 at 3.05.13 PM.png

 

void setup( )
{
  size(150, 150);
  background(255);
  smooth();
  // nem mutatja a kontroll pontokat
  noFill();
  stroke(0);
  beginShape();
  vertex(50, 75); // első pont
  bezierVertex(25, 25, 125, 25, 100, 75);
  endShape();
}

Írjuk át a kódot hogy folytonos görbénk legyen:

Screen Shot 2013-01-21 at 3.07.51 PM.png

     

beginShape();
vertex(30, 70); // az első pont
bezierVertex(25, 25, 100, 50, 50, 100);
bezierVertex(50, 140, 75, 140, 120, 120);
endShape();

Ez még nem annyira sima:

nonsmooth_bezier.pngEzért a második ívrész első kontroll pontját alrébb kell tennünk:

smooth_bezier.png

Screen Shot 2013-01-21 at 3.13.28 PM.png

beginShape();
vertex(30, 70); // az első pont
bezierVertex(25, 25, 100, 50, 50, 100);
bezierVertex(20, 130, 75, 140, 120, 120);
endShape();

       

forrás: http://processing.org/learning/curves/ 

Szólj hozzá!

2013.01.17.
15:45

Írta: harsanyireka

Referencia, operátorok, adat típusok

Mivel a Processing ugyanazt a programnyelvet használja mint az arduino, a referenciát itt találjátok:

http://harsanyireka.blog.hu/2012/07/04/referencia_1_vezerlo_strukturak

http://harsanyireka.blog.hu/2012/07/04/tovabbi_szintaxisok

http://harsanyireka.blog.hu/2012/07/04/referencia_3_osszehasonlito_operatorok

http://harsanyireka.blog.hu/2012/07/05/referencia_4_aritmetikai_operatorok

http://harsanyireka.blog.hu/2012/08/01/referencia_5_boolean_operatorok

http://harsanyireka.blog.hu/2012/08/01/referencia_402 pointerek

http://harsanyireka.blog.hu/2012/08/05/referencia_861 bitenkénti operátorok

http://harsanyireka.blog.hu/2012/08/10/referencia_665 növelő/csökkentő operátorok

http://harsanyireka.blog.hu/2012/10/17/referencia_9_allandok

http://harsanyireka.blog.hu/2012/11/08/referencia_10_adat_tipusok

PROCESSING REF ANGOLUL: http://processing.org/reference/#

Szólj hozzá!

süti beállítások módosítása