yoppa.org


第7回 Processing実習 3 : Processingで画像データを扱う – 画像の分析・再合成

ProcessingのPImageクラスは、外部のビットマップ画像(Jpeg, GIF、PNGなど)をデータとしてプログラムに読み込むことができます。読み込んだ画像は単に画面に表示するだけでなく、色や明度やサイズを変更して表示することができます。さらには、画像に含まれる全てのピクセルの色情報を読み取り配列に格納することが可能です。そのデータをもとに別の画像を再生成することが可能となり、読み込んだ画像データの色情報をもとにした多彩な表現が可能となります。

今回はProcessingに画像を読み込んで、分析再合成することで、様々な表現の可能性について探っていきます。

スライド資料

サンプルプログラム

画像を表示

PImage img; //画像データ
void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}
void draw() {
  //画面に画像データを表示
  image(img, 0, 0);
}

位置とサイズを指定して、画像を表示

PImage img; //画像データ
void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}
void draw() {
  background(0); //背景
  //位置と大きさを指定して、画像データを表示
  image(img, width/4, height/4, width/2, height/2);
}

画像の明度を変更 (マウスX座標で明度が変化)

PImage img; //画像データ
void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}
void draw() {
  background(0);
  //マウスのX座標で明度を変化させる
  tint(map(mouseX, 0, width, 0, 255));
  //色を変更した画像を表示
  image(img, 0, 0);
}

画像の色を変更 (マウスの位置で色が変化)

PImage img; //画像データ
void setup() {
  size(800, 600);
  //画像を読み込み
  img = loadImage("photo.jpg");
}
void draw() {
  background(0);
  //色補正をリセット
  noTint();
  //元画像を左に表示
  image(img, 0, height/4, width/2, height/2);
  //画像のRGB値をマウスのY座標で変更
  float r = mouseX/float(width) * 255;
  float g = mouseY/float(height) * 255;
  float b = 255;
  tint(r, g, b);
  //色を変更した画像を表示
  image(img, width/2, height/4, width/2, height/2);
}

マウスの位置の色を取得

PImage img; //画像データ
void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}
void draw() {
  background(0);
  image(img, 0, 0);
  //マウスの位置のピクセルの色を取得
  color col = img.get(mouseX, mouseY);
  //取得した色で円を描画
  fill(col);
  ellipse(50, 50, 80, 80);
}

マウスの位置でモザイクの大きさが変化

PImage img; //画像データ
void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}
void draw() {
  background(0);
  noStroke();
  //モザイクの粒度をマウスのX座標で決定
  float step = map(mouseX, 0, width, 1, 800);
  //画面の幅と高さでループ
  for (int j = 0; j < height; j += step) {
    for (int i = 0; i < width; i += step) {
      //座標を指定して色を取得
      color c = img.get(i, j);
      //取得した色を塗りの色にして四角形を描画
      fill(c);
      rect(i, j, step, step);
    }
  }
}

マウスの位置でスキャンする高さを変更

PImage img; //画像データ
void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}
void draw() {
  background(0);
  noStroke();
  //画面の幅だけループ
  for (int i = 0; i < width; i++) {
    //現在のマウスのY座標の色を取得
    color c = img.get(i, mouseY);
    //取得した色を塗りの色に
    fill(c);
    //画面の高さいっぱいに幅1pixの長方形を描画
    rect(i, 0, 1, height);
  }
}

円の大きさと色で画像を再構成する、明度がサイズに対応

PImage img; //画像データ
void setup() {
  size(1280, 720);
  img = loadImage("photo.jpg");
  background(0); //背景
  noStroke(); //枠線は無し
}
void draw() {
  //色を取得する位置をランダムに決定
  int x = int(random(width));
  int y = int(random(height));
  //指定した場所の色を取得
  color col = img.get(x, y);
  fill(col, 127);
  //色の明度を円の直径にする
  float ellipseSize = map(brightness(col), 0, 255, 2, 50);
  //円を描画
  ellipse(x, y, ellipseSize, ellipseSize);
}

円の大きさと色で画像を再構成する、彩度がサイズに対応

PImage img; //画像データ
void setup() {
  size(1280, 720);
  img = loadImage("photo.jpg");
  background(0); //背景
  noStroke(); //枠線は無し
}
void draw() {
  for (int i = 0; i < 10; i++) {
    //色を取得する位置をランダムに決定
    int x = int(random(width));
    int y = int(random(height));
    //指定した場所の色を取得
    color col = img.get(x, y);
    fill(col, 127);
    //色の明度を円の直径にする
    float ellipseSize = map(saturation(col), 0, 255, 2, 50);
    //円を描画
    ellipse(x, y, ellipseSize, ellipseSize);
  }
}

四角形の大きさと角度で、画像を再構成する

PImage img; //画像データ
void setup() {
  size(1280, 720);
  img = loadImage("photo.jpg");
  background(0); //背景
  noStroke(); //枠線は無し
  rectMode(CENTER); //中心を原点に四角形を描画
}
void draw() {
  for (int i=0; i<100; i++) {
    //場所をランダムに決定
    int x = int(random(width));
    int y = int(random(height));
    //色を取得
    color col = img.get(x, y);
    fill(col, 127);
    pushMatrix();
    translate(x, y);
    //色の色相を回転角度に
    rotate(map(hue(col), 0, 255, 0, PI));
    //色の再度を四角形の幅に
    float w = map(saturation(col), 0, 255, 1, 40);
    rect(0, 0, w, 1.5);
    popMatrix();
  }
}

膨張するピクセル

ArrayList bubbles; //Bubbleクラスを格納するArrayList
PImage img; //色をピックアップするイメージ
int maxSize = 60; //円の最大サイズ(直径)
void setup() {
  //画面初期設定
  size(800, 600);
  frameRate(60);
  noStroke();
  //ArrayListの初期化
  bubbles = new ArrayList();
  //画像を読み込んで、画面の大きさにリサイズ
  //画像の名前は読み込んだ画像に変更する
  img = loadImage("photo.jpg");
  img.resize(width, height);
  //最初のきっかけの円を描画
  for (int i = 0; i < 10; i++) {
    PVector loc = new PVector(random(width), random(height));
    bubbles.add(new Bubble(loc));
  }
}
void draw() {
  background(0);
  //ArrayListに格納された数だけ、Bubbleを描画
  for (int i = 0; i < bubbles.size(); i++) {
    bubbles.get(i).draw();
  }
  //Bubbleの状態を更新
  for (int i = 0; i < bubbles.size(); i++) {
    //もしアクティブな状態だったら
    if (bubbles.get(i).isDead == false) {
      //円の周囲のピクセルの色を確認
      boolean expand = bubbles.get(i).checkPixel();
      //もしこれ以上膨張できない場合
      if (expand == false) {
        //新規にBubbleを生成
        PVector loc;
        //余白がみつかるまで繰り返し
        while (true) {
          loc = new PVector(random(width), random(height));
          color c = get(int(loc.x), int(loc.y));
          if ((red(c) + blue(c) + green(c)) == 0) break;
        }
        //余白に新規Bubbleを生成
        bubbles.add(new Bubble(loc));
        bubbles.get(i).isDead = true;
      }
    }
  }
}
//マウスクリックで初期化
void mouseClicked() {
  //ArrayListをクリア
  bubbles.clear();
  //きっかけの円を描画
  for (int i = 0; i < 100; i++) {
    PVector loc = new PVector(random(width), random(height));
    bubbles.add(new Bubble(loc));
  }
}
//Bubbleクラス
//円が膨張しながら空間を充填していく
class Bubble {
  float size; //円のサイズ(直径)
  float expandSpeed; //膨張スピード
  color circleColor; //円の色
  PVector location; //中心の位置
  boolean expand; //膨張中か否か
  boolean isDead; //活動している状態か否か
  //コンスタラクター
  Bubble(PVector _location) {
    location = _location; //位置を引数から取得
    //パラメータの初期値設定
    size = 0;
    expandSpeed = 4.0;
    expand = true;
    isDead = false;
    //読み込んだ画像から中心位置と同じピクセルの色を取得
    circleColor = img.get(int(location.x), int(location.y));
  }
  //円の描画
  void draw() {
    //もし膨張中なら
    if (expand == true) {
      //指定した速度で膨張
      size += expandSpeed;
    }
    //円を描画
    fill(circleColor);
    ellipse(location.x, location.y, size, size);
  }
  //円の周囲のピクセルの色を取得して、まだ膨張する余地があるかを判断する
  boolean checkPixel() {
    //次のフレームでのサイズを計算
    float nextSize = size + expandSpeed;
    for (float i = 0; i < TWO_PI; i += 0.01) {
      //円の周囲の座標を取得
      int x = int(cos(i) * nextSize / 2.0 + location.x);
      int y = int(sin(i) * nextSize / 2.0 + location.y);
      //取得した座標の直下のピクセルの色を取得
      color c = get(x, y);
      //色が黒意外、もしくは最大サイズを超えていたら、膨張を中止
      if ((red(c) + blue(c) + green(c)) > 0 || size > maxSize) {
        expand = false;
      }
    }
    //現在の膨張の状態を返す
    return expand;
  }
}

第6回 openFrameworks – IoT計測データのビジュアライズ、中間展示のヒント

今回は、中間展示に向けてデータビジュアライズについて実践的に学んでいきます。まず初めに、データビジュアライゼーションの様々な手法を紹介し、どのような用途にどういったビジュアライズの手法が適しているのか考えていきます。その中でも特に時系列のデータのビジュアライズに適した、Hans Roslingによる時系列に沿ってアニメーションするバブルチャート「Trendalyzer」の手法を紹介します。

後半は、前回のワークショップで設置した植物の育成データのIoT計測機器の計測データを実際に活用して、openFrameweroksを利用した様々なビジュアライゼーションに挑戦します。

スライド資料

サンプルプログラム

今回のサンプルプログラムは、WSのGithubリポジトリのweek06を参照してください。


第6回: 画像の分析・再合成 (画像データ、ピクセル、ラスタ画像)

ProcessingのPImageクラスは、外部のビットマップ画像(Jpeg, GIF、PNGなど)をデータとしてプログラムに読み込むことができます。読み込んだ画像は単に画面に表示するだけでなく、色や明度やサイズを変更して表示することができます。さらには、画像に含まれる全てのピクセルの色情報を読み取り配列に格納することが可能です。そのデータをもとに別の画像を再生成することが可能となり、読み込んだ画像データの色情報をもとにした多彩な表現が可能となります。

今回はProcessingに画像を読み込んで、分析再合成することで、様々な表現の可能性について探っていきます。

スライド資料

サンプルファイル

課題: Proessingに読み込んだ画像ファイルのデータで表現する

  • PImageに読み込んだ画像ファイルの情報から、新たなイメージを生成してください
    • 読み込む画像は自由です

課題の提出方法

  • emailで提出
    • To : tadokoro+teu17@gmail.com
    • Subject: 課題4
    • 作成したプログラムの実行結果をスクリーンキャプチャーしてメールに添付
    • 本文に以下の内容を記入して提出
    • 学籍番号
    • 氏名
    • 締切: 次回の講義開始時間 (6月5日 16:45) まで

第6回 Processing実習 2 : Visual Harmony – アルゴリズムによるアニメーション表現

今回は、より複雑なアニメーションに挑戦します。たくさんの図形を、一定の手続き(= アルゴリズム)にそって動かしてみます。動きのアルゴリズムはいろいろありますが、今回はその一例として、三角関数(sin, cos , tan)を使用した動きをとりあげます。三角関数の定義は、直角三角形の角度とそれを取り囲む辺の比率で定義されます。しかし、同時に円運動を座標に変換する仕組みとしても活用可能です。この仕組みを応用してアニメーションするとどうなるか、三角関数を用いたアニメーションで美しいパターン「ビジュアルハーモニー」を生成する手法を紹介します。

スライド資料

サンプルプログラム

sin関数による動き

PVector location;
PVector velocity;

void setup() {
  size(800, 800);
  frameRate(60);
  background(0);
  noStroke();
  velocity = new PVector(4, 0);
  location = new PVector(0, 0);
}

void draw() {
  background(0);
  translate(width / 2.0, height / 2.0);
  location.add(velocity);
  location.y = sin(frameCount/20.0)*height/4.0;
  fill(0, 127, 255);
  ellipse(location.x, location.y, 10, 10);
  if (location.x > width/2) {
    location.x = -width/2;
  }
}

円運動

PVector location;
float angle, velocity, radius;

void setup() {
    size(800, 800);
    frameRate(60);
    background(0);
    noStroke();
    angle = 0.0; //現在の角度
    velocity = 2.0; //角速度
    radius = height / 4.0; //半径
    location = new PVector(0, 0);
}

void draw() {
    background(0);
    translate(width / 2.0, height / 2.0);
    location.x = cos(radians(angle)) * radius;
    location.y = sin(radians(angle)) * radius;
    fill(0, 127, 255);
    ellipse(location.x, location.y, 10, 10);
    angle += velocity; //角速度から角度を更新
}

配列をつかった沢山の円運動

int NUM = 32;
PVector[] location = new PVector[NUM];
float angle, velocity, radius;

void setup() {
    size(800, 800);
    frameRate(60);
    background(0);
    noStroke();
    angle = 0.0;
    velocity = 2.0;
    radius = height / 4.0;
    for (int i = 0; i < NUM; i++) {
        location[i] = new PVector(0, 0);
    }
}

void draw() {
    background(0);
    translate(width / 2.0, height / 2.0);
    for (int i = 0; i < NUM; i++) {
        location[i].x = cos(radians(angle) / NUM * (i+1)) * radius;
        location[i].y = sin(radians(angle) / NUM * (i+1)) * radius;
        fill(0, 127, 255);
        ellipse(location[i].x, location[i].y, 10, 10);
    }
    angle += velocity;
}

ビジュアルハーモニー 01 - 基本

int NUM = 32;
PVector[] location = new PVector[NUM];
float angle, velocity, radius;

void setup() {
    size(800, 800);
    frameRate(60);
    background(0);
    noFill();
    angle = 0.0;
    velocity = 2.0;
    radius = height / 4.0;
    for (int i = 0; i < NUM; i++) {
        location[i] = new PVector(0, 0);
    }
}

void draw() {
    background(0);
    translate(width / 2.0, height / 2.0);
    for (int i = 0; i < NUM; i++) {
        location[i].x = cos(radians(angle) / NUM * (i+1)) * radius;
        location[i].y = sin(radians(angle) / NUM * (i+1)) * radius;
        stroke(0, 127, 255);
        ellipse(location[i].x, location[i].y, 4 * i, 4 * i);
    }
    angle += velocity;
}

ビジュアルハーモニー 02 - 円の数を増やしてみる

int NUM = 256;
PVector[] location = new PVector[NUM];
float angle, velocity, radius;

void setup() {
    size(800, 800, P3D);
    frameRate(60);
    background(0);
    noFill();
    angle = 0.0;
    velocity = 2.0;
    radius = height / 4.0;
    for (int i = 0; i < NUM; i++) {
        location[i] = new PVector(0, 0);
    }
}

void draw() {
    background(0);
    translate(width / 2.0, height / 2.0);
    for (int i = 0; i < NUM; i++) {
        location[i].x = cos(radians(angle) / NUM * (i+1)) * radius;
        location[i].y = sin(radians(angle) / NUM * (i+1)) * radius;
        stroke(0, 127, 255);
        float diameter = i * (height / 2) / float(NUM);
        ellipse(location[i].x, location[i].y, diameter, diameter);
    }
    angle += velocity;
}

ビジュアルハーモニー 03 - 周波数の比率を変化させる

int NUM = 256;
PVector[] location = new PVector[NUM];
float angle, velocity, radius;

void setup() {
    size(800, 800, P3D);
    frameRate(60);
    background(0);
    noFill();
    angle = 0.0;
    velocity = 2.0;
    radius = height / 4.0;
    for (int i = 0; i < NUM; i++) {
        location[i] = new PVector(0, 0);
    }
}

void draw() {
    background(0);
    translate(width / 2.0, height / 2.0);
    for (int i = 0; i < NUM; i++) {
        location[i].x = cos(radians(angle) / NUM * (i+1) * 1.2) * radius;
        location[i].y = sin(radians(angle) / NUM * (i+1) * 1.5) * radius;
        stroke(0, 127, 255);
        float diameter = i * (height / 2) / float(NUM);
        ellipse(location[i].x, location[i].y, diameter, diameter);
    }
    angle += velocity;
}

第5回 openFrameworks JSONデータの読み込みと表示 – Quick, Draw! をビジュアライズしてみる ofxJSON

今回はデータビジュアライゼーションの続きとして、JSON形式のデータを読み込んでビジュアライズを試みます。せっかくデータを読み込むなら「クリエイティブ」で巨大なデータを活用しようということで、今回は、Google AI Experimentsチームが最近公開した、Quick, Draw!でユーザーが実際に描いたイラストのデータを使用してみます。このデータは「face」「ant」などの出題された単語からユーザーが画面上で描いた膨大な量のイラストのデータを蓄積したもので、1.5億件以上のイラストデータが公開されています。また、イラストを描いた際の描画の順番などのデータなども残されており様々なビジュアライズの可能性を秘めています。

まず始めに、JSONデータの基本構造と、openFrameworksでJSONを扱うためのアドオンofxJSONの使い方を簡単に説明し、その後、Quick, Draw! の実際のデータを解析しながら様々なビジュアライズを試みます。

スライド資料

サンプルプログラム

今回のサンプルプログラムは、WSのGithubリポジトリのweek05を参照してください。

このサンプルに読み込むQuick, Draw! の公開データは、下記のリンクからSimplified drawings filesをダウンロードして使用してください。


第5回: ビジュアルハーモニー (アルゴリズム、三角関数)

今回は、より複雑なアニメーションに挑戦します。たくさんの図形を、一定の手続き(= アルゴリズム)にそって動かしてみます。動きのアルゴリズムはいろいろありますが、今回はその一例として、三角関数(sin, cos , tan)を使用した動きをとりあげます。三角関数の定義は、直角三角形の角度とそれを取り囲む辺の比率で定義されます。しかし、同時に円運動を座標に変換する仕組みとしても活用可能です。この仕組みを応用してアニメーションするとどうなるか、三角関数を用いたアニメーションで美しいパターン「ビジュアルハーモニー」を生成する手法を紹介します。

スライド資料

サンプルプログラム

課題

課題: 「ビジュアルハーモニー」を表現する

  • sin関数による動きで、オリジナルの「ビジュアルハーモニー」を表現してみる
    • 周波数を変化させてみる
    • 動かす形を変えてみる
    • 色を変化させてみる
    • 3Dで表現してみる (上級編)
    • …etc.

サンプルコード: 以下のサンプルが課題のベースになります。

int NUM = 256;
PVector[] location = new PVector[NUM];
float phase, velocity, radius;

void setup() {
  size(800, 800, P3D);
  frameRate(60);
  background(0);
  noFill();
  phase = 0.0;
  velocity = 2.0;
  radius = height / 4.0;
  for (int i = 0; i < NUM; i++) {
    location[i] = new PVector(0, 0);
  }
}

void draw() {
  background(0);
  translate(width / 2.0, height / 2.0);
  for (int i = 0; i < NUM; i++) {
    location[i].x = cos(radians(phase) / NUM * (i+1)) * radius;
    location[i].y = sin(radians(phase) / NUM * (i+1)) * radius;
    stroke(0, 127, 255);
    float diameter = i * (height / 2) / float(NUM);
    ellipse(location[i].x, location[i].y, diameter, diameter);
  }
  phase += velocity;
}

課題の提出方法

  • emailで提出
    • To : tadokoro+teu17@gmail.com
    • Subject: 課題3
    • 作成したプログラムの実行結果をスクリーンキャプチャーしてメールに添付
    • 本文に以下の内容を記入して提出
    • 学籍番号
    • 氏名
    • 締切: 次回の講義開始時間 (5月29日 16:45) まで

第5回 Processing実習 1 : 動きを生みだす – アニメーションとベクトル

今回からいよいよ動きのある表現(= アニメーション)について扱っていきます。アニメーションを実現するには、まず時間を扱う基本構造を知る必要があります。Processingでは、setup(), draw() という2つのブロックにわけて、初期化と更新を行うことでアニメーションを実現しています。まず始めはこの基本構造について理解します。次に、これから動きを扱う際に、向きと大きさをもった「ベクトル」という概念を理解します。ベクトルを理解することで、位置や運動を整理して記述することが可能となります。最後に、この基本構造をベクトルを活用して簡単なアニメーションを作成します。

スライド資料

サンプルコード

サンプルコード 1

//初期化関数
void setup() {
  size(800, 600);
  frameRate(12);   //書き換え頻度の設定
  background(0);
}
//メインループ
void draw() {
  float diameter = random(100);
  noStroke();
  fill(random(255), random(255), random(255));
  ellipse(random(width), random(height), diameter, diameter);
}

サンプルコード 2

float locationX, locationY; //円の中心位置を格納する変数
float velocityX, velocityY; //円の速度を格納する変数
void setup() {
    size(800, 600); //800x600 pixelの画面を生成
    frameRate(60); //フレームレート
    locationX = width/2; //円の初期位置X
    locationY = height/2; //円の初期位置Y
    velocityX = 3; //円の初期位置X
    velocityY = 2; //円の初期位置Y
}

void draw() {
    background(0); //背景を描画
    locationX = locationX + velocityX; //円のX座標を更新
    locationY = locationY + velocityY; //円のY座標を更新
    noStroke(); //枠線なし
    fill(0, 127, 255); //塗りの色
    ellipse(locationX, locationY, 20, 20); //指定した位置に円を描画
}

サンプルコード 3

float locationX, locationY; //円の中心位置を格納する変数
float velocityX, velocityY; //円の速度を格納する変数

void setup() {
    size(800, 600); //800x600 pixelの画面を生成
    frameRate(60);  //フレームレート
    locationX = width/2; //円の初期位置X
    locationY = height/2; //円の初期位置Y
    velocityX = 3;  //円の初期位置X
    velocityY = 2;  //円の初期位置Y
}

void draw() {
    background(0); //背景を描画
    locationX = locationX + velocityX; //円のX座標を更新
    locationY = locationY + velocityY; //円のY座標を更新
    noStroke(); //枠線なし
    fill(0, 127, 255); //塗りの色
    //指定した位置に円を描画
    ellipse(locationX, locationY, 20, 20); 

    if (locationX < 0 || locationX > width) {
        //もし画面の左端、または右端に到達したら
        //X方向のスピードを反転
        velocityX = velocityX * -1;
    }
    if (locationY < 0 || locationY > height) {
        //もし画面の下端、または上端に到達したら
        //Y方向のスピードを反転
        velocityY = velocityY * -1; 
    }
}

サンプルコード 4

PVector location; //円の中心位置を格納する変数
PVector velocity; //円の速度を格納する変数
void setup() {
    size(800, 600); //800x600 pixelの画面を生成
    frameRate(60); //フレームレート
    location = new PVector(random(width), random(height));
    velocity = new PVector(random(-10, 10), random(-10, 10));
}

void draw() {
    background(0); //背景を描画
    //位置のベクトルに速度のベクトルを加算、次の位置になる
    location.add(velocity); 

    noStroke();
    fill(0, 127, 255);
    ellipse(location.x, location.y, 20, 20);
    if (location.x < 0 || location.x > width) {
        velocity.x = velocity.x * -1;
    }
    if (location.y < 0 || location.y > height) {
        velocity.y = velocity.y * -1;
    }
}

サンプルコード 5

int NUM = 100; //配列の数
//位置のベクトルの配列
PVector[] location = new PVector[NUM];
//速度のベクトルの配列
PVector[] velocity = new PVector[NUM];

void setup() {
  size(800, 600); //800x600pixelの画面を生成
  frameRate(60); //フレームレート
  noStroke(); //枠線なし
  fill(0, 127, 255); //塗りの色
  for (int i = 0; i < NUM; i++) { //配列の数だけ繰り返し
    //位置のベクトルの初期設定
    location[i] = new PVector(random(width), random(height));
    //速度のベクトルの初期設定
    velocity[i] = new PVector(random(-4, 4), random(-4, 4));
  }
}

void draw() {
  background(15); //背景を描画
  for (int i = 0; i < NUM; i++) { //配列の数だけ繰り返し
    //指定した位置に円を描画
    ellipse(location[i].x, location[i].y, 20, 20);
    //位置のベクトルに速度のベクトルを加算、次の位置になる
    location[i].add(velocity[i]);
    //もし画面の左端、または右端に到達したら
    if ((location[i].x > width) || (location[i].x < 0)) {
      velocity[i].x *= -1; //X方向のスピードを反転
    }
    //もし画面の下端、または上端に到達したら
    if ((location[i].y > height) || (location[i].y < 0)) {
      velocity[i].y *= -1; //Y方向のスピードを反転
    }
  }
}

サンプルコード 6

int NUM = 500; //配列の数
//位置のベクトルの配列
PVector[] location = new PVector[NUM];
//速度のベクトルの配列
PVector[] velocity = new PVector[NUM];
//塗りの色の配列
color[] col = new color[NUM];
//円の大きさ(直径)の配列
float[] diameter = new float[NUM];

void setup() {
  size(800, 600); //800x600pixelの画面を生成
  frameRate(60); //フレームレート
  noStroke();
  for (int i = 0; i < NUM; i++) { //配列の数だけ繰り返し
    //位置のベクトルの初期設定
    location[i] = new PVector(random(width), random(height));
    //速度のベクトルの初期設定
    velocity[i] = new PVector(random(-4, 4), random(-4, 4));
    //色の初期設定
    col[i] = color(random(255), random(255), random(255), 192);
    //大きさの初期設定
    diameter[i] = random(3, 40);
  }
}

void draw() {
  background(15); //背景を描画
  //配列の数だけ繰り返し
  for (int i = 0; i < NUM; i++) {
    fill(col[i]); //色を指定
    //指定した位置に円を描画
    ellipse(location[i].x, location[i].y, diameter[i], diameter[i]);
    //位置のベクトルに速度のベクトルを加算、次の位置になる
    location[i].add(velocity[i]);
    //もし画面の左端、または右端に到達したら
    if ((location[i].x > width) || (location[i].x < 0)) {
      velocity[i].x *= -1; //X方向のスピードを反転
    }
    //もし画面の下端、または上端に到達したら
    if ((location[i].y > height) || (location[i].y < 0)) {
      velocity[i].y *= -1; //Y方向のスピードを反転
    }
  }
}

第4回 openFrameworks – Addonについて ofxGui, ofxCsv

Addon(アドオン)とは、openFrameworksに機能を拡張するためのライブラリーやフレームワークです。processingのLibrariesのように、openFrameworks単体ではできなかった様々な機能を実現することが可能となります。Addonは、oF本体の開発者以外でも独自に開発して追加することが可能であり、繰り返し用いる機能や、CやC++で記述された既存のライブラリーをopenFrameworksに統合することが可能となります。今回はAddonの導入を、まずofxGuiというプロジェクトにGUIを追加するアドオンで行います。ofxGuiを使用することで、例えば周囲の環境が異なる場所でインスタレーションを設置する時など、すぐにパラメータを最適な状態に調整して保存することができ、とても便利です。

後半は、このWSの主題である “Creative Visualization” の核心に徐々に迫っていきます。まず今回はオープンに公開されたデータをopenFrameworksに読み込んでビジュアライズする基礎を学びます。オープンに公開されたデータは、様々な形式でフォーマットされています。代表的なもので、CSV(コンマ区切りテキスト)、XML(マークアップ言語)、JSON(JavaScriptのオブジェクト書式)などがあげられます。今回はこの中から、CSVを読み込む手法について取り上げます。CSVを読み込むための、ofxCsvというAddonを用いてデータの読み込みと視覚化を、空港の位置データをサンプルにして実習していきます。

スライド資料

サンプルプログラム

今回のサンプルプログラムは、WSのGithubリポジトリのweek04を参照してください。


第4回: アニメーション基礎 (ベクトル、運動方程式)

今回からいよいよ動きのある表現(= アニメーション)について扱っていきます。アニメーションを実現するには、まず時間を扱う基本構造を知る必要があります。Processingでは、setup(), draw() という2つのブロックにわけて、初期化と更新を行うことでアニメーションを実現しています。まず始めはこの基本構造について理解します。次に、これから動きを扱う際に、向きと大きさをもった「ベクトル」という概念を理解します。ベクトルを理解することで、位置や運動を整理して記述することが可能となります。最後に、この基本構造をベクトルを活用して簡単なアニメーションを作成します。

スライド資料

サンプルプログラム

本日とりあげたサンプルプログラムは以下からダウンロードしてください。

課題

今日とりあげた最後のサンプルを元に、以下のスケッチを作成してください。

  • それぞれの円の色をランダムに
  • それぞれの円の大きさをランダムに

以下のサンプルがベースになります。

int NUM = 100;
PVector[] location = new PVector[NUM];
PVector[] velocity = new PVector[NUM];

void setup() {
  size(800, 600);
  frameRate(60);
  noStroke();
  fill(0, 127, 255);
  for (int i = 0; i < NUM; i++) {
    location[i] = new PVector(random(width), random(height));
    velocity[i] = new PVector(random(-4, 4), random(-4, 4));
  }
}

void draw() {
  background(15);
  for (int i = 0; i < NUM; i++) {
    ellipse(location[i].x, location[i].y, 20, 20);
    location[i].add(velocity[i]);
    if ((location[i].x > width) || (location[i].x < 0)) {
      velocity[i].x *= -1;
    }
    if ((location[i].y > height) || (location[i].y < 0)) {
      velocity[i].y *= -1;
    }
  }
}

課題の提出方法

  • emailで提出
    • To: tadokoro+teu17@gmail.com
    • Subject: 課題2
  • 本文に以下の内容を記入して提出
    • 学籍番号
    • 氏名
    • 作成したソースコード (.pde) を本文にコピー&ペースト
  • 締切: 次回の講義開始時間 (5月22日 16:45) まで

第3回 openFrameworks + OOP オブジェクト指向プログラミング入門

openFrameworksは、C++というプログラミング言語をベースにしています。C++はその原型となるC言語に様々な機能を拡張しています。その中でも代表的な機能が「オブジェクト指向」というプログラミングのパラダイム(プログラムの見方)です。これは、プログラムをデータとその振舞が結び付けられたオブジェクトの集まりとして構成するプログラミングの手法で、現在では、Java、Ruby、Python、Swift、JavaScript、C#といった様々な言語において根幹となる考え方となっています。

今回は、まず上下左右に規則性無く動きまわる「ランダムウォーク」する点を通常の手法で作成した後、そのランダムウォークする点を1つのオブジェクトとして作成し、さらにそれを大量に生成して同時に動かしてみます。そのプログラミングの課程で、openFrameworksにおけるオブジェクト指向プログラミングの考え方と方法について学んでいきます。

スライド資料

サンプルプログラム

今回のサンプルプログラムは、WSのGithubリポジトリのweek03を参照してください。