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:
ArrayListpolygons; 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
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: