CAN Generative Typography

Amnon Owed 라는 작가가 Creative Applications Network 에 게시한 글 'Generative Typography with Processing – Tutorial' 에 함께 첨부된 Processing 예제입니다. 이 글에 따르면 튜토리얼은 코드를 사용하여 타이포그라피의 창조적 가능성을 탐색하는 방법을 보여주기 위해 만들어졌습니다. Type을 기본형으로 하여 다양한 기술과 알고리즘으로 그래픽을 얻어내는 팁들을 담고 있습니다.

Link

Generative Typography with Processing – Tutorial
Generative Typography – github

수업 영상

원하는 폰트 고르기

PFont.list()
내 운영체제에 저장된 폰트의 목록을 String Array 형태로 반환한다.
Processing
// 사용할 수 있는 폰트목록을 console에 출력한다
String[] fontList = PFont.list();
printArray(fontList);
createFont( name, size )
원하는 폰트를 Processing에서 사용할 수 있도록 PFont 데이터 형식으로 변환한다.
textFont( which )
이후에 그려지는 text를 parameter로 넣은 폰트로 지정한다.
Processing
PFont font;
    
void setup() {
    // Canvas 사이즈를 정한다
    size(1280, 720);
    
    // 사용할 수 있는 폰트목록을 console에 출력한다
    String[] fontList = PFont.list();
    printArray(fontList);
    
    // 원하는 폰트를 PFont로 얻어서 지정한다
    font = createFont("Georgia", 400);
    textFont(font);
    
    // text를 상단 왼쪽을 기준으로 정렬되게 한다
    textAlign(LEFT, TOP);
    
    // Canvas에 원하는 text를 그린다
    background(255);
    fill(0);
    text("Hello!", 0, 0);
}

Type 형태대로 그래픽이 그려질 좌표 얻기

get( x, y )
parameter로 입력한 x, y 좌표 픽셀의 색상값을 얻어 Processing의 color 데이터 타입으로 반환합니다.
Processing
PFont font;
    
void setup() {
    // Canvas 사이즈를 정한다
    size(1280, 720);
    
    // 원하는 폰트를 PFont로 얻어서 지정한다
    font = createFont("Georgia", 400);
    textFont(font);
    
    // text를 상단 왼쪽을 기준으로 정렬되게 한다
    textAlign(LEFT, TOP);
    
    // Canvas에 원하는 text를 그린다
    background(255);
    fill(0);
    text("Hello!", 0, 0);

    // 캔버스 전체(가로 0~width, 세로 0~height)에 10px 간격으로 x,y 지점을 찾는다
    for(int x=0; x<width; x += 10){
      for(int y=0; y<height; y += 10){
        
        // 해당 좌표 픽셀의 색깔이 원하는 색깔이 맞는지 판별한다
        // 맞다면 중괄호 안의 코드를 실행한다
        if( get(x, y) == color(0) ){
        
          // 그려질 원의 색깔을 정한다
          fill(255, 0, 0);
          
          // 해당 좌표에 가로세로 10, 10, 크기의 원을 그린다
          ellipse(x, y, 10, 10);
          
        }
        
      }
    }
    
}

표현할 그래픽 분리하기

프로세싱엔 마치 레이어처럼 이미지를 분리해서 그리는 PGraphics라는 데이터타입이 있다. 텍스트 실루엣을 PGraphics에 그린후 숨겨놓고 실루엣의 좌표만 얻어서 그래픽을 표현할 수 있다.

createGraphics( width, height )
입력한 가로세로 크기의 레이어(PGraphics)를 생성한다. 이 레이어 안에 그리기 위해서는 함수 앞에 'PGraphics이름.' 을 붙여야한다. 예를 들어 PGraphics의 이름이 pg라면 사각형을 그릴 때 pg.rect(100, 40, 50, 50) 과 같이 작성한다.
PGraphics이름.beginDraw()
PGraphics 안에 그래픽을 그리기 시작한다.
PGraphics이름.endDraw()
PGraphics 안에 그래픽을 그리기는 것을 끝낸다.
Processing
PFont font;
PGraphics pg;
    
void setup() {
    // Canvas 사이즈를 정한다
    size(1280, 720);
    
    // Canvas와 같은 크기로 PGraphics를 생성한다.
    pg = createGraphics(width, height);
    
    // 원하는 폰트를 PFont로 얻어서 지정한다
    font = createFont("Georgia", 400);
    
    // PGraphics에 원하는 text를 그린다
    pg.beginDraw();
    
    pg.background(255);
    
    pg.textFont(font);
    pg.textAlign(LEFT, TOP);
    
    pg.fill(0);
    pg.text("Hello!", 0, 0);
    
    pg.endDraw();
    
    // Canvas 의 배경을 칠한다
    background(255);
    
    // 캔버스 전체(가로 0~width, 세로 0~height)에 10px 간격으로 x,y 지점을 찾는다
    for(int x=0; x<width; x += 10){
      for(int y=0; y<height; y += 10){
        
        // 해당 좌표 픽셀의 색깔이 원하는 색깔이 맞는지 판별한다
        // 맞다면 중괄호 안의 코드를 실행한다
        if( pg.get(x, y) == color(0) ){
        
          // 그려질 원의 색깔을 정한다
          fill(255, 0, 0);
          
          // 해당 좌표에 가로세로 10, 10, 크기의 원을 그린다
          ellipse(x, y, 10, 10);
          
        }
        
      }
    }
    
}

각 그래픽 입자를 객체로 독립시키기

Processing
PFont font;
PGraphics pg;

// Particle 배열을 만든다
Particle[] particle;

// 생성될 Particle의 처음 번호를 변수 담아준다
int particleNum = 0;
    
void setup() {
    // Canvas 사이즈를 정한다
    size(1280, 720);
    
    // Canvas와 같은 크기로 PGraphics를 생성한다.
    pg = createGraphics(width, height);
    
    // 원하는 폰트를 PFont로 얻어서 지정한다
    font = createFont("Georgia", 400);
    
    // PGraphics에 원하는 text를 그린다
    pg.beginDraw();
    pg.background(255);
    pg.textFont(font);
    pg.textAlign(LEFT, TOP);
    pg.fill(0);
    pg.text("Hello!", 0, 0);
    pg.endDraw();
    
     // Canvas 의 배경을 칠한다
    background(255);
    
    // Particle 배열 안에 최대로 담을 수 있는 공간을 만들어준다
    // particle이 Canvas 안에 10px 간격으로 생기니 가로세로를 10으로 나눠 곱한다
    particle = new Particle[width/10 * height/10];
    
    // 캔버스 전체(가로 0~width, 세로 0~height)에 10px 간격으로 x,y 지점을 찾는다
    for(int x=0; x<width; x += 10){
      for(int y=0; y<height; y += 10){
        
        // 해당 좌표 픽셀의 색깔이 원하는 색깔이 맞는지 판별한다
        // 맞다면 중괄호 안의 코드를 실행한다
        if( pg.get(x, y) == color(0) ){
        
          // 실루엣이 있는 좌표에 Particle을 생성한다
          particle[particleNum] = new Particle(x, y, 10, color(255,0,0));
          
          // 생성된 Particle을 그려준다
          particle[particleNum].display();
          
          // 다음 Particle을 생성하기 위해 번호를 1 올려준다
          particleNum ++;
        }
        
      }
    }
    
}
Processing tab - Particle
// Particle 객체를 만든다

class Particle{
  // 필요한 변수를 생성한다
  float x;
  float y;
  float size;
  color c;
  
  // new 키워드를 통해서 각 Particle을 생성할때 어떻게 변수를 받을지 작성한다
  Particle(float x_, float y_, float size_, color c_){
    x = x_;
    y = y_;
    size = size_;
    c = c_;
  }
  
  // Particle을 시각화할 함수를 만든다
  void display(){
    fill(c);
    ellipse(x, y, size, size);
  }
}

애니메이션 만들기

Processing
PFont font;
PGraphics pg;

// Particle 배열을 만든다
Particle[] particle;

// 생성될 Particle의 처음 번호를 변수 담아준다
int particleNum = 0;
    
void setup() {
    // Canvas 사이즈를 정한다
    size(1280, 720);
    
    // Canvas와 같은 크기로 PGraphics를 생성한다.
    pg = createGraphics(width, height);
    
    // 원하는 폰트를 PFont로 얻어서 지정한다
    font = createFont("Georgia", 400);
    
    // PGraphics에 원하는 text를 그린다
    pg.beginDraw();
    pg.background(255);
    pg.textFont(font);
    pg.textAlign(LEFT, TOP);
    pg.fill(0);
    pg.text("Hello!", 0, 0);
    pg.endDraw();
    
    // Particle 배열 안에 최대로 담을 수 있는 공간을 만들어준다
    // particle이 Canvas 안에 10px 간격으로 생기니 가로세로를 10으로 나눠 곱한다
    particle = new Particle[width/10 * height/10];
    
    // 캔버스 전체(가로 0~width, 세로 0~height)에 10px 간격으로 x,y 지점을 찾는다
    for(int x=0; x<width; x += 10){
      for(int y=0; y<height; y += 10){
        
        // 해당 좌표 픽셀의 색깔이 원하는 색깔이 맞는지 판별한다
        // 맞다면 중괄호 안의 코드를 실행한다
        if( pg.get(x, y) == color(0) ){
        
          // 실루엣이 있는 좌표에 Particle을 생성한다
          particle[particleNum] = new Particle(x, y, 10, color(255,0,0));
          
          // 다음 Particle을 생성하기 위해 번호를 1 올려준다
          particleNum ++;
        }
        
      }
    }
    
}


// 애니메이션 효과를 주기위해 display함수를 draw에 작성한다
void draw(){
  background(255);
  for(int i=0; i<particleNum; i++){
    particle[i].display();
  }
}

애니메이션 1

Processing tab - Particle
// Particle 객체를 만든다

class Particle{
  // 필요한 변수를 생성한다
  float x;
  float y;
  float size;
  color c;
  
  // new 키워드를 통해서 각 Particle을 생성할때 어떻게 변수를 받을지 작성한다
  Particle(float x_, float y_, float size_, color c_){
    x = x_;
    y = y_;
    size = size_;
    c = c_;
  }
  
  // Particle을 시각화할 함수를 만든다
  void display(){
    // 각 Particle의 x좌표를 움직여 전광판같은 효과를 준다
    x++;
    if(x > width) x = 0;
    
    fill(c);
    ellipse(x, y, size, size);
  }
}

애니메이션 2

Processing tab - Particle
// Particle 객체를 만든다

class Particle{
  // 필요한 변수를 생성한다
  
  // 본래위치
  float x;
  float y;
  
  // 움직인 위치
  float moveX;
  float moveY;
  
  float size;
  color c;
  
  // new 키워드를 통해서 각 Particle을 생성할때 어떻게 변수를 받을지 작성한다
  Particle(float x_, float y_, float size_, color c_){
    x = x_;
    y = y_;
    size = size_;
    c = c_;
    
    moveX = x;
    moveY = y;
  }
  
  // Particle을 시각화할 함수를 만든다
  void display(){
    // 랜덤으로 움직인 값을 임시로 담아둔다
    float tempX = moveX + random(-3, 3);
    float tempY = moveY + random(-3, 3);
    
    // 움직인 값이 일정범위를 넘어가면 다시 계산해서 경계를 넘어가지 않게한다
    while(dist(tempX, tempY, x, y) > 10){
      tempX = moveX + random(-3, 3);
      tempY = moveY + random(-3, 3);
    }
    
    // 경계를 넘지않는 값을 얻으면 값을 담는다
    moveX = tempX;
    moveY = tempY;
    
    fill(c);
    ellipse(moveX, moveY, size, size);
  }
}

애니메이션 3

Processing tab - Particle
// Particle 객체를 만든다

class Particle{
  // 필요한 변수를 생성한다
  
  // 본래위치
  float x;
  float y;
  
  // 움직인 위치
  float moveX;
  float moveY;
  
  float size;
  color c;
  
  // new 키워드를 통해서 각 Particle을 생성할때 어떻게 변수를 받을지 작성한다
  Particle(float x_, float y_, float size_, color c_){
    x = x_;
    y = y_;
    size = size_;
    c = c_;
    
    moveX = x;
    moveY = y;
  }
  
  // Particle을 시각화할 함수를 만든다
  void display(){
    // frameCount에 따라 sin값에 변화를 주는데 
    // x,y위치에따라 조금씩 다르게하여 파동을 만든다
    moveX = x + sin(frameCount*0.1 + x*0.01) * 5;
    moveY = y + cos(frameCount*0.1 + y*0.01) * 5;
    
    fill(c);
    ellipse(moveX, moveY, size, size);
  }
}

애니메이션 4

Processing tab - Particle
// Particle 객체를 만든다

class Particle{
  // 필요한 변수를 생성한다
  
  float x;
  float y;
  
  //최대 크기와 현재크기
  float maxSize;
  float size;
  
  color c;
  
  // new 키워드를 통해서 각 Particle을 생성할때 어떻게 변수를 받을지 작성한다
  Particle(float x_, float y_, float size_, color c_){
    x = x_;
    y = y_;
    maxSize = size_;
    
    // 각 Particle 이 0~최대크기 사이에서 랜덤하게 시작하게한다
    size = maxSize * random(0, 1);
    
    c = c_;
    
  }
  
  // Particle을 시각화할 함수를 만든다
  void display(){
    // 매프레임마다 0.1씩 커지게한다
    size += 0.1;
    
    // 최대크기에 다다르면 0으로 돌아간다
    if(size > maxSize) size = 0;
    
    fill(c);
    ellipse(x, y, size, size);
  }
}

이미지, PDF 파일로 내보내기

이미지파일로 저장하기

saveFrame( filename )
filename대로 현재 프레임을 이미지파일로 저장한다.
ex) saveFrame( "image.jpg" )
Processing
import processing.pdf.*;

boolean record;

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

void draw() {
  // Draw something good here
  background(255);
  ellipse(width/2, height/2, 200, 200);
}

// Use a keypress so thousands of files aren't created
void mousePressed() {
  saveFrame( "image.jpg" );
}

PDF 저장하기

Processing
import processing.pdf.*;

boolean record;

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

void draw() {
  if (record) {
    // Note that #### will be replaced with the frame number. Fancy!
    beginRecord(PDF, "frame-####.pdf"); 
  }

  // Draw something good here
  background(255);
  ellipse(width/2, height/2, 200, 200);

  if (record) {
    endRecord();
	record = false;
  }
}

// Use a keypress so thousands of files aren't created
void mousePressed() {
  record = true;
}