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á!

A bejegyzés trackback címe:

https://processing.blog.hu/api/trackback/id/tr735079885

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása