yoppa.org


Processingで画像データを扱う

※こちらの内容は既に古い情報となっています。新しく書き直した記事を参照してください。

画像を表示する

  • Processingで画像を表示するためには、画像ファイルをそのスケッチのある階層に「data」フォルダを作成し、その中に画像ファイルを入れる
  • Jepg、GIF、PNG形式に対応している
  • 画像を表示するには、image() 関数を用いる
    • image(画像ファイル名, x, y); – 座標(x, y)を左上にして、画像を表示
    • image(画像ファイル名, x, y, width, height); – 座標(x, y)を左上にして、幅(width)と高さ(height)を指定して画像を表示

画像を表示

PImage img;
void setup() {
  size(640, 426);
  img = loadImage("photo.jpg");
}
void draw() {
  image(img, 0, 0);
}

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

PImage img;
void setup() {
  size(640, 426);
  img = loadImage("photo.jpg");
}
void draw() {
  background(0);
  image(img, width/4, height/4, width/2, height/2);
}

画像の色や透明度を変更する

  • image() で表示した画像の、色や透明度を操作することができる
  • tint() 関数
    • tint(gray) – グレースケール画像のレベルを変更
    • tint(gray, alpha) – グレースケール画像のレベルと透明度を変更
    • tint(red, green, blue) – カラー画像のRGBの値を変更
    • tint(red, green, blue, alpha) – カラー画像のRGBの値と透明度を変更

画像の色を変更してみる

イメージのレベルを変更

PImage img;
void setup() {
  size(640, 426);
  img = loadImage("photo.jpg");
}
void draw() {
  background(0);
  noTint();
  image(img, 0, 0);
  tint(102);
  image(img, width/2, 0);
}

イメージのカラーを変更

PImage img;
void setup() {
  size(640, 426);
  img = loadImage("photo.jpg");
}
void draw() {
  background(0);
  noTint();
  image(img, 0, 0);
  tint(31, 127, 255);
  image(img, width/2, 0);
}

イメージの透明度(アルファ)を変更

PImage img;
void setup() {
  size(640, 426);
  img = loadImage("photo.jpg");
}
void draw() {
  background(0);
  for(int i = 0; i < 8; i++) {
    int xpos = i * width / 8;
    fill(0);
    rect(xpos, 0, width, height);
    tint(255,255,255, i * 32);
    image(img, xpos, 0);
  }
}

ピクセルの情報を取得

  • 表示した画像の情報をピクセル単位取得することができる
  • 取得した画像の情報をもとに、より高度なイメージの操作、イメージの情報をもとにした表現を行うことが可能となる
  • ピクセル情報の取得には、get() 関数を用いる
  • get関数
    • get() – ウィンドウ内のピクセル全てを取得、イメージを返す
    • get(x, y) – 指定した座標、(x, y)のピクセルを取得、色の値を返す
    • get(x, y, width, hegiht) – 指定した座標、(x, y) から幅(width)、高さ(height)を指定してイメージを取得

get()を使用した例

マウスの位置のピクセルの色を取得 – 取得したRGBの値で左上に3つの正方形を描く

PImage img;
void setup() {
  size(640, 426);
  stroke(255, 102);
  img = loadImage("photo.jpg");
}
void draw() {
  background(0);
  image(img, 0, 0);
  noStroke();
  fill(0);
  rect(20, 20, 60, 20);
  color c = get(mouseX, mouseY);
  fill(red(c), 0, 0);
  rect(20, 20, 20, 20);
  fill(0, green(c), 0);
  rect(40, 20, 20, 20);
  fill(0, 0, blue(c));
  rect(60, 20, 20, 20);
  stroke(255, 102);
  line(mouseX, 0, mouseX, height);
  line(0, mouseY, width, mouseY);
}

画像をモザイク化する

  • loadPixels() – 現在画面に表示されているピクセル情報を記録する
    • loadPixels() で格納したピクセルの色情報は、pixels[] 配列に格納される
  • pixels[] 配列を一定間隔で読みだすことで、画像をモザイク化する
PImage img;
int mosaicWidth = 30;
int mosaicHeight = 20;
void setup() {
  size(640, 426);
  noStroke();
  img = loadImage("photo.jpg");
}
void draw() {
  background(0);
  image(img, 0, 0);
  loadPixels();
  for(int j = 0; j < height; j+=mosaicHeight) {  
    for(int i = 0; i < width; i+=mosaicWidth) {  
      color c = pixels[j * width + i];
      fill(c);
      rect(i, j, mosaicWidth, mosaicHeight);
    }
  }
}
void mouseDragged() {
  mosaicWidth = mouseX / 4 + 10;
  mosaicHeight = mouseY / 4 + 10;
}

画像ファイルをスキャンする

  • loadPixels() を活用して、画像の特定のy座標の色だけをとりだして、バーコード状に並べる
PImage img;
void setup() {
  size(640, 426);
  stroke(255, 102);
  img = loadImage("photo.jpg");
  image(img, 0, 0);
  loadPixels();
}
void draw() {
  for(int i = 0; i < width; i++) {  
    color c = pixels[width * mouseY + i];
    stroke(c);
    line(i, 0, i, height);
  }
  stroke(255, 102);
  line(0, mouseY, width, mouseY);
}

フラクタルフォトモザイク

  • モザイクのひとつひとつが同じ写真になっている
  • モザイク状に配置した写真の明度をtintで変化させている
PImage img;
int mosaicWidth = 30;
int mosaicHeight = 20;
void setup() {
  size(640, 426);
  noStroke();
  background(0);
  img = loadImage("photo.jpg");
  image(img, 0, 0);
  loadPixels();
}
void draw() {
  for(int j = 0; j < height; j+=mosaicHeight) {  
    for(int i = 0; i < width; i+=mosaicWidth) {  
      color c = pixels[j * width + i];
      tint(red(c), green(c), blue(c));
      image(img, i, j, mosaicWidth, mosaicHeight);
    }
  }
}

画像をピクセレイト

  • 画像のピクセルの色情報をもとに、画像を再描画する – ピクセレイト

円の大きさと色で、画像を再構成する

PImage img;
int mosaicSize = 12;
void setup() {
  size(640, 426);
  noStroke();
  img = loadImage("photo.jpg");
  image(img, 0, 0);
  loadPixels();
}
void draw() {
  background(0);
  for(int j = 0; j < height; j+=mosaicSize) {  
    for(int i = 0; i < width; i+=mosaicSize) {  
      color c = pixels[j * width + i];
      fill(c, 127);
      ellipse(i, j, brightness(c)/6.0, brightness(c)/6.0);
    }
  }
}

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

PImage img;
int mosaicSize = 6;
void setup() {
  size(640, 426);
  noStroke();
  img = loadImage("photo.jpg");
  image(img, 0, 0);
  loadPixels();
  rectMode(CENTER);
  smooth();
}
void draw() {
  background(0);
  for(int j = 0; j < height; j+=mosaicSize) {  
    for(int i = 0; i < width; i+=mosaicSize) {  
      color c = pixels[j * width + i];
      fill(c, 127);
      pushMatrix();
      translate(i, j);
      rotate(brightness(c));
      rect(0, 0, brightness(c)/6.0, 2);
      popMatrix();
    }
  }
}

画像ファイルのデータを利用した表現

  • Jared Tarbell, box.fitting.imageの紹介
  • ソースファイルを解析してみる
  • 画像ファイルを入れかえてみる

サンプルファイルのダウンロード

今日とりあげた全てのサンプルは下記からダウンロードしてください。


ActionScript応用:Tweenライブラリの利用(1)

今日の内容

  • トウィーンを扱うライブラリを使用して、高度なアニメーション表現に調整
  • ライブラリにはTweenMaxを使用する
  • 様々なイージング関数を試してみる

Tweenライブラリ

TweenMax のダウンロード

TweenMaxを利用したインタラクティブなムービーの作成:基本

マウスをクリックした場所にTween

  • 必ずダウンロードしたフォルダ「greensock-tweening-platform-as3」の中にある「com」フォルダを、作成するflaファイルと同じ場所に置く
    • この中にあるAS3のライブラリを参照します
  • 画面にムービークリップを一つ配置する
  • ムービークリップのインスタンス名を「mc」に
  • タイムラインの第1フレームにスクリプトを記入
  • まず最初に、Tweenライブラリをインポートする
    • import com.greensock.*;

[code language=”javascript”]
import com.greensock.*;

this.stage.addEventListener(MouseEvent.CLICK, clickHandler);

function clickHandler(e:MouseEvent):void {
TweenLite.to(this.mc, 1, {x:mouseX, y:mouseY});
}
[/code]

いろいろなイージング関数を試してみる

  • あらたにイージングのためのライブラリをインポートする
    • import com.greensock.easing.*;
  • TweenLiteの指定にイージングを追加する

指数関数によるイースイン・アウト

[code language=”javascript”]
import com.greensock.*;
import com.greensock.easing.*;

this.stage.addEventListener(MouseEvent.CLICK, clickHandler);

function clickHandler(e:MouseEvent):void {
TweenLite.to(this.mc, 1, {x:mouseX, y:mouseY, ease:Expo.easeInOut});
}
[/code]

バウント効果によるイースアウト

[code language=”javascript”]
import com.greensock.*;
import com.greensock.easing.*;

this.stage.addEventListener(MouseEvent.CLICK, clickHandler);

function clickHandler(e:MouseEvent):void {
TweenLite.to(this.mc, 1, {x:mouseX, y:mouseY, ease:Bounce.easeOut});
}
[/code]

ばねの動きによるイースアウト

[code language=”javascript”]
import com.greensock.*;
import com.greensock.easing.*;

this.stage.addEventListener(MouseEvent.CLICK, clickHandler);

function clickHandler(e:MouseEvent):void {
TweenLite.to(this.mc, 1, {x:mouseX, y:mouseY, ease:Elastic.easeOut});
}
[/code]

位置+サイズの変更

[code language=”javascript”]
import com.greensock.*;
import com.greensock.easing.*;

this.stage.addEventListener(MouseEvent.CLICK, clickHandler);

function clickHandler(e:MouseEvent):void {
var size:Number = Math.random() * 200;
TweenLite.to(this.mc, 1, {x:mouseX, y:mouseY, width:size, height:size, ease:Elastic.easeOut});
}
[/code]

TintPluginの利用:色の変化

ランダムに色が変化する

[code language=”javascript”]
import com.greensock.*;
import com.greensock.easing.*;
import com.greensock.plugins.*;

TweenPlugin.activate([TintPlugin]);

this.stage.addEventListener(MouseEvent.CLICK, clickHandler);

function clickHandler(e:MouseEvent):void {
var scale:Number = Math.random() * 4.0;
var color:Number = Math.random() * 0xffffff;
import com.greensock.plugins.*;
TweenLite.to(this.mc, 1, {x:mouseX, y:mouseY, scaleX:scale, scaleY:scale, tint:color, ease:Elastic.easeOut});
}
[/code]

Tweenの連鎖:onCompleteの利用

Tweenが完了すると、すぐに新たなTweenを適用する

[code language=”javascript”]
import com.greensock.*;
import com.greensock.easing.*;
import com.greensock.plugins.*;

TweenPlugin.activate([TintPlugin]);

this.stage.addEventListener(MouseEvent.CLICK, clickHandler);

function clickHandler(e:MouseEvent):void {
myTween();
}

function myTween():void {
var scale:Number=Math.random()*4.0;
var color:Number=Math.random()*0xffffff;
var toX:Number=Math.random()*stage.stageWidth;
var toY:Number=Math.random()*stage.stageHeight;
import com.greensock.plugins.*;
TweenLite.to(this.mc, 1, {x:toX, y:toY, scaleX:scale, scaleY:scale, tint:color, ease:Elastic.easeOut, onComplete:myTween});
}
[/code]


openFrameworks for iPhone:サウンドの録音・再生

今日の内容

  • サウンドファイルの再生「Buddha Machine」的なアプリを作ってみる
  • サウンドの録音と再生を使用した音アプリに挑戦
  • 録音した音のデータをインタラクティブに操作する

サウンドファイルの再生

サンプル1:サウンドファイルの再生

  • オリジナルな"Buddha Machine"的アプリを制作できる雛形
  • サウンドファイル(caf形式)と表示する画像を入れ替えれば、そのままアプリにできるテンプレートを以下に掲載
  • 音ファイル(caf形式)は、「プロジェクトのフォルダ」→「bin」→「data」→「mySound.caf」に入れる
  • caf形式のフォーマット – リニアPCM、16bit リトルエンディアン符号付き整数、22050Hz
  • 表示する画像ファイル(PNG形式)は、「プロジェクトのフォルダ」→「bin」→「data」→「myScreen.png」に入れる
  • PNGファイルノフォーマット – PNG 320 x 480 pixel


ファイルの配置

testApp.h
[code language=”cpp”]
#pragma once

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"
#include "ofxALSoundPlayer.h"

class testApp : public ofxiPhoneApp {

public:
void setup();
void update();
void draw();
void exit();

void touchDown(ofTouchEventArgs &touch);
void touchMoved(ofTouchEventArgs &touch);
void touchUp(ofTouchEventArgs &touch);
void touchDoubleTap(ofTouchEventArgs &touch);

void lostFocus();
void gotFocus();
void gotMemoryWarning();
void deviceOrientationChanged(int newOrientation);

//表示する画面イメージ
ofImage image;

//OpenALを利用して音を再生するプレーヤー
ofxALSoundPlayer synth;
};
[/code]

testApp.mm
[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);

//画面イメージを読みこみ
image.loadImage("myScreen.png");

//ループ再生するcafファイルをロードして、Synthにわりあて
synth.loadLoopingSound("mySound.caf");
//読み込んだcafファイルを再生
synth.play();
}

//————————————————————–
void testApp::update(){

}

//————————————————————–
void testApp::draw(){
//画面を表示
image.draw(0, 0);
}

//————————————————————–
void testApp::exit(){

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::lostFocus(){

}

//————————————————————–
void testApp::gotFocus(){

}

//————————————————————–
void testApp::gotMemoryWarning(){

}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){

}
[/code]

サウンドを録音・再生する (サンプリング&プレイバック)

  • あらかじめ録音したサウンドファイルを再生するのではなく、iPhoneのマイク入力から音を録音したり、録音した音を再生するには、SoundStreamを使用する必要がある
  • SoundStreamは、音のサンプルのデータ自体を数値として配列に記憶し、それを再生することができる機能 → サンプリング&プレイバック

サンプリグ (標本化)とは

  • 時間的に連続した信号を一定の間隔をおいて測定することにより、離散的な(連続でない)データとして収集すること
  • 時間的に連続した信号 = アナログ信号
  • 離散的な信号 = デジタル信号

サンプリング周波数と量子化ビット数

  • サンプリング周波数 – 1秒間にいくつのサンプルを使用してサンプリングするか、単位はHz
  • 量子化ビットレイト – AD変換の際に信号を何段階の数値で表現するか、単位はbit

iPhoneシミュレータでサンプリング&プレイバックのプレビューを行う際には「Audio MIDI 設定で」下記の設定にする

  • サンプリング周波数、44.1KHz
  • 量子化ビットレイト – 24bit

Sound Streamの機能

SoundStreamを使用するにはまず ofSoundStreamSetup() で初期設定を行う、通常は、setup() の中で指定

  • ofSoundStreamSetup(int nOutputs, int nInputs, ofSimpleApp * OFSA, int sampleRate, int bufferSize, int nBuffers)
    • サウンドの録音再生の初期設定を行う
    • int nOutput – 再生する音のチャンネル数
    • int nInput – 録音する音のチャンネル数
    • ofSimpleApp * OFSA – 呼び出し元のofSimpleApp (普通は、this でOK)
    • int sampleRate – サンプリングレイト
    • int bufferSize – 音をバッファ(格納)するサイズ

Sound Streamに関連するイベント

testApp では、ofSoundStreamSetup() の設定すると音の入出力に関するイベントが発生する

  • audioReceived(float * input, int bufferSize, int nChannels)
    • 音が入力(録音)される際に発生するイベント
    • float * input – 入力された音のサンプルの配列
    • int bufferSize – バッファサイズ、一度に格納されるサンプルの長さ
    • int nChannels – チャンネル数
  • audioRequested(float * output, int bufferSize, int nChannels)
    • 音が出力(再生)される際に発生するイベント
    • float * output – 音として出力するサウンドのサンプルの配列
    • int bufferSize – バッファサイズ、一度に格納されるサンプルの長さ
    • int nChannels – チャンネル数

サンプル2:サンプリング&プレイバック

  • 音を録音して再生するという「サンプリング&プレイバック」の機能をシンプルに実現してみる
  • 画面をタッチ(touchDown)すると録音、タッチしている指を離すと(touchUp)録音した音を再生するように設計
  • 録音した音の波形を画面に表示する

testApp.h
[code language=”cpp”]
#pragma once

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"

//録音・再生する音の長さ
#define LENGTH 44100 * 3

class testApp : public ofxiPhoneApp{

public:
void setup();
void update();
void draw();

void touchDown(ofTouchEventArgs &touch);
void touchMoved(ofTouchEventArgs &touch);
void touchUp(ofTouchEventArgs &touch);
void touchDoubleTap(ofTouchEventArgs &touch);

void audioReceived( float * input, int bufferSize, int nChannels );
void audioRequested( float * output, int bufferSize, int nChannels );

float buffer[LENGTH]; //オーディオバッファ
int sampleRate; //サンプリングレイト
int recPos; //レコード位置
int playPos; //再生位置
int mode; //現在のモード、0:off, 1:recording, 2:play
};
[/code]

testApp.mm
[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
//iPhone基本設定
ofRegisterTouchEvents(this);
ofBackground(0, 0, 0);
ofSetFrameRate(60);

//横位置で使用
ofxiPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);

//サンプリングレイトの設定
sampleRate = 44100;

//サウンド録音再生の初期化
ofSoundStreamSetup(1, 1, this, sampleRate, LENGTH, 4);

//再生モードに
mode = 2;

//録音、再生の位置を先頭に
recPos = 0;
playPos = 0;
}

//————————————————————–
void testApp::update(){
}

//————————————————————–
void testApp::draw(){
ofSetColor(255, 255, 255);

if (mode == 1) {
//録音モードの場合、recordingの表示をして、背景を青に
ofDrawBitmapString("recording", 10, 20);
ofBackground(255, 0, 0);
} else if (mode == 2) {
//再生モードの場合、playingの表示をして、背景を赤に
ofDrawBitmapString("playing", 10, 20);
ofBackground(0, 0, 255);
}

//画面の幅と録音サンプル数の比率を計算
int ratio = LENGTH / ofGetWidth();

//画面の横幅にあわせて、波形を描画
for (int i = 0; i < LENGTH; i+=ratio){
ofLine(i/ratio,ofGetHeight()/2,i/ratio,ofGetHeight()/2+buffer[i]*100.0f);
}
}

//————————————————————–
//オーディオ入力の処理
void testApp::audioReceived(float * input, int bufferSize, int nChannels){
//もし録音モードだったら
if (mode == 1) {
//バッファされたサンプルの数だけ処理
for (int i = 0; i < bufferSize*nChannels; i++){
//録音位置が設定した最大の長さに逹っしていなければ
if(recPos<LENGTH){
//バッファにサウンド入力を設定
buffer[recPos] = input[i];
//録音位置を進める
recPos++;
} else {
//もし最大位置を越えていたら、最初に戻る(ループ録音)
recPos = 0;
}

}
}
}

//————————————————————–
//オーディオ再生の処理
void testApp::audioRequested(float * output, int bufferSize, int nChannels){
//もし再生モードだったら
if (mode == 2) {
//バッファされたサンプル数だけ処理
for (int i = 0; i < bufferSize*nChannels; i++) {
//再生位置が設定した最大の長さに逹っしていなければ
if(playPos<LENGTH){
//バッファに格納したサウンドを再生
output[i] = buffer[playPos];
//再生位置を進める
playPos++;
} else {
//もし最大位置を越えていたら、最初に戻る(ループ再生)
playPos = 0;
}
}
}
}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){
//画面をタッチすると、録音モードへ
mode = 1;
recPos = 0;
}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){
//画面をから指を離すと、再生モードへ
mode = 2;
playPos = 0;
}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}
[/code]

サンプル3:録音した音をインタラクティブに操作する

  • 録音した音のピッチや音量を変化できるようにする
  • 簡易版"Sampletoy" – 参考:Sampletoy

  • 録音したサンプルを再生する際に工夫を加えてみる
  • そのままの状態で録音開始
  • 画面をタッチすると再生
  • 画面をタッチしながら横に移動すると、音のピッチが変化
  • 画面をタッチしながら縦に移動すると、音量が変化

testApp.h
[code language=”cpp”]
#pragma once

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"

//録音・再生する音の長さ
#define LENGTH 44100 * 3

class testApp : public ofxiPhoneApp{

public:
void setup();
void update();
void draw();

void touchDown(ofTouchEventArgs &touch);
void touchMoved(ofTouchEventArgs &touch);
void touchUp(ofTouchEventArgs &touch);
void touchDoubleTap(ofTouchEventArgs &touch);

void audioReceived( float * input, int bufferSize, int nChannels );
void audioRequested( float * output, int bufferSize, int nChannels );

float buffer[LENGTH]; //オーディオバッファ
int sampleRate; //サンプリングレイト
int recPos; //レコード位置
float playPos; //再生位置
int mode; //現在のモード、0:off, 1:recording, 2:play
float audioLevel; //音量
float playSpeed; //再生スピード(ピッチを変更できるようFloat型で)
};
[/code]

testApp.mm
[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
//iPhone基本設定
ofRegisterTouchEvents(this);
ofBackground(0, 0, 0);
ofSetFrameRate(60);

//横位置で使用
ofxiPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);

//サンプリングレイトの設定
sampleRate = 44100;

//サウンド録音再生の初期化
ofSoundStreamSetup(1, 1, this, sampleRate, LENGTH, 4);

//録音モードに
mode = 1;

//録音、再生の位置を先頭に
recPos = 0;
playPos = 0;

playSpeed = 1.0;

}

//————————————————————–
void testApp::update(){
}

//————————————————————–
void testApp::draw(){
ofSetColor(255, 255, 255);

if (mode == 1) {
//録音モードの場合、recordingの表示をして、背景を青に
ofDrawBitmapString("recording", 10, 20);
ofBackground(255, 0, 0);
} else if (mode == 2) {
//再生モードの場合、playingの表示をして、背景を赤に
ofDrawBitmapString("playing", 10, 20);
ofDrawBitmapString("pos = " + ofToString(playSpeed, 3), 10, 40);
ofBackground(0, 0, 255);
}

//画面の幅と録音サンプル数の比率を計算
int ratio = LENGTH / ofGetWidth();

//画面の横幅にあわせて、波形を描画
for (int i = 0; i < LENGTH; i+=ratio){
ofLine(i/ratio,ofGetHeight()/2,i/ratio,ofGetHeight()/2+buffer[i]*100.0f);
}
}

//————————————————————–
//オーディオ入力の処理
void testApp::audioReceived(float * input, int bufferSize, int nChannels){
//もし録音モードだったら
if (mode == 1) {
//バッファされたサンプルの数だけ処理
for (int i = 0; i < bufferSize*nChannels; i++){
//録音位置が設定した最大の長さに逹っしていなければ
if(recPos<LENGTH){
//バッファにサウンド入力を設定
buffer[recPos] = input[i];
//録音位置を進める
recPos++;
}else {
//もし最大位置を越えていたら、最初に戻る(ループ再生)
recPos = 0;
}
}
}
}

//————————————————————–
//オーディオ再生の処理
void testApp::audioRequested(float * output, int bufferSize, int nChannels){
//もし再生モードだったら
if (mode == 2) {
//バッファされたサンプル数だけ処理
for (int i = 0; i < bufferSize*nChannels; i++) {
//再生位置が設定した最大の長さに逹っしていなければ
if(playPos<LENGTH){
//バッファに格納したサウンドを再生
output[i] = buffer[int(playPos)] * audioLevel;
//音程にあわせて、再生位置を移動する
playPos += playSpeed;
} else {
//もし最大位置を越えていたら、最初に戻る(ループ再生)
playPos = 0;
}
}
}
}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){
//画面をから指を離すと、再生モードへ
mode = 2;
playPos = 0;

//音量を設定
audioLevel = (ofGetHeight() – touch.y) / ofGetHeight() * 3.0;
//音程を設定
playSpeed = touch.x / ofGetWidth() * 2.0;
}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){
//音量を設定
audioLevel = (ofGetHeight() – touch.y) / ofGetHeight() * 3.0;
//音程を設定
playSpeed = touch.x / ofGetWidth() * 2.0;
}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){
//画面をタッチすると、録音モードへ
mode = 1;
recPos = 0;
}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}
[/code]

サンプルファイルのダウンロード

今回の授業で紹介した全てのサンプルは、下記のリンクからダウンロード可能です


ArduinoとProcessingの連携2:大きな値を送信する、データの流れを視覚化する

ArduinoとProcessingの連携2:大きな値を送信する、データの流れを視覚化する

今日の内容

先週の課題の講評会

センサーからの入力を視覚化する – ArduinoとProcessingの連携のつづき

  • 大きな値を送受信する – 大きな値 → 0〜1023 (1024段階 = 210 = 10bit)
  • 先週のように、0〜255にマッピングするのではなく、全ての値が反映されるように送受信する方法を学ぶ – ビット演算の使用
  • データの流れを視覚化する
  • ノイズの影響などを除去しながらデータが変化する様子を視覚化する

先週の課題の講評会

先週の課題:

  • Generative Gestaltungのコードを活用して、Arduinoにとりつけたセンサーや可変抵抗、スイッチなどを操作すると形態や動きが変化するプログラムをProcessingで作成する。
  • 使用するセンサーは自由に選択してください。温度センサー、光センサー、可変抵抗以外のものでもOK
  • 複数のセンサー、可変抵抗を組み合せても構いません

大きな値を送受信する

先週の授業でのArduino → Processingのデータの送受信

  • Arduino – 0 〜 1023 (1024段階、210)
  • Serialで一度に送出できる範囲 – 0 〜 255 (256段階、28)
  • Arduinoの値をSerialにあわせるため、map() 関数で値を補正 – map(val, 0, 1023, 0, 255);
  • 値の解像度が1/4になってしまっていた – できることなら全ての値を送りたい

今日用いる手法:値を分割して送信する

  • 値を8bitずつで分解、2回にわけて値を送出する
  • bit演算を用いて、分解、再合成を行う

ビット(bit)とは

  • ビット (bit) は、デジタルコンピュータが扱うデータの最小単位 – “binary digit” の略
  • 2通りの状態しか表現できない – “0” または “1”

例:3bit – 3桁のbit

  • 3bitで表現できる数 → 0 〜 7 の8通り
  • bitで表現できる数は、2の乗数で計算できる
  • 3bit = 2^3 = 8

8bit = 1byte

  • 00000000 から 11111111
  • 10進数の数値にすると、0 から 255
  • Serial通信で一度に送れる数値は、1byte つまり 0 (00000000) 〜 255 (11111111)

Arduino – Processingの通信の流れ

  • 1024 = 16bit → ArduinoのAnalog inの精度
  • 255 = 8bit → Serial で一度に送出できる値
  • Arduinoの入力値16bitを、8bit のかたまり2つに分解する
  • 2回にわけて8bit( = 1byte) ずつSerialで送信

例:入力の値が、950 (0000001110110110)だったら

16bitの列から上位の8bitをとりだすには? – bit シフトを行う – 各桁を左または右に移動する

  • 右方向に1bitシフト
  • 0000001110110110 >> 1 = 0000000111011011
  • 右に1桁移動

16bitの列から上位の8bitを取り出すには

  • 右方向に8bitシフト
  • 0000001110110110 >> 8 = 0000000000000011
  • 右に8桁移動 → 上位の8桁をとりだした状態
  • 10進数で表現すると
  • 950 >> 8 = 3

16bitの列から下位の8bitをとりだすには?

  • bit マスクを行う – bit単位のAND計算

例:下位8bitだけをとりだす

まとめ – 16bitの数値の、上位 8bit と下位 8bit を分解するには

  • 上位 8bit:8桁 bitシフトを行う
  • 下位 8bit:下位 8bit をマスクする

例:入力が 950 (0000001110110110) の場合

  • 上位8bit:950 >> 8 = 3 (00000011)
  • 下位8bit:950 & 255 = 182 (110110110)

それぞれを、Serialで送信してあげればよい

以上の手順を、ArduinoとProcessing側双方で実装してみる

Arduino側

//アナログ入力の数を定義する
#define NUM 1

//アナログ入力の値を格納する配列
int val[NUM]; 

void setup() {
  //シリアル通信の開始
  Serial.begin(9600);
}

void loop() {
  //アナログ入力の数だけ繰り返し
  for(int i=0; i 0){
    //アナログ入力の数だけ繰り返し
    for(int i=0; i> 8, BYTE);
      //下位 8bit を送出
      Serial.print(val[i] & 255, BYTE);
    }
    //合図用データを読み込んでバッファ領域を空にする
    Serial.read(); 
  }
}

Processing側

import processing.serial.*;

//アナログ入力の数を指定
int NUM = 6;

//Serialクラスのインスタンス
Serial myPort;

//Serialより読み込んだデータを格納する配列
int[] val = new int[NUM]; 

void setup() {
  //画面を生成
  size(400, 400);
  //ポートの名前を取得
  String portName = Serial.list()[1];
  //Serialクラスをインスタンス化
  myPort = new Serial(this, portName, 9600);
}

void draw() {
  //背景を黒に
  background(0);
  //塗りを白に
  fill(255);
  //入力の数だけ繰り返し
  for(int i=0; i

データの流れを視覚化する

データの流れを視覚化する1

データからノイズを除去する

  • センサーからの入力値には、意図しない信号(ノイズ)がのってしまう場合がある
  • Processing側の工夫で、急激な入力信号の変化は除去したい
  • 実際の値の変化を、指定した値でeasingする → なめらかな変化となる

Arduino側

//アナログ入力の数を定義する
#define NUM 1

//アナログ入力の値を格納する配列
    int val[NUM]; 

void setup() {
    //シリアル通信の開始
    Serial.begin(9600);
}

void loop() {
    //アナログ入力の数だけ繰り返し
    for(int i=0; i 0) {
    //アナログ入力の数だけ繰り返し
    for(int i=0; i> 8, BYTE);
        //下位 8bit を送出
        Serial.print(val[i] & 255, BYTE);
    }
    //合図用データを読み込んでバッファ領域を空にする
    Serial.read();
    }
}

Processing側

import processing.serial.*;

//アナログ入力の数を指定
int NUM = 1;

//現在走査しているX座標
int x;
//なめらかさ
float easing = 0.05;
//なめらかに変換した値
float easedVal;
//現在Serial信号を受信しているか
boolean running = false;

//Serialクラスのインスタンス
Serial myPort;

//Serialより読み込んだデータを格納する配列
int[] val = new int[NUM]; 

void setup() {
    //画面を生成
    size(1024, 480);
    //フレームレート
    frameRate(30);
    //ポートの名前を取得
    String portName = Serial.list()[1];
    //Serialクラスをインスタンス化
    myPort = new Serial(this, portName, 9600);
    //背景を黒に
    background(255);
}

void draw() {
    //もし現在信号を受信していたら
    if(running) {
    //入力値から目標とする値をマッピング
    float targetVal = map(val[0], 0, 1024, 0, height/2);
    //目標値をなめらかに変換
    easedVal += (targetVal - easedVal) * easing;
    //グラフの描画
    //白い線で前の値を消す
    stroke(255);
    line(x, 0, x, height);
    //色を指定
    stroke(63,127,255);
    //現在走査している場所を線で示す
    line(x+1, 0, x+1, height);
    //なめらかにしていない値を描画
    line(x, height/2, x, targetVal);
    //なめらかにした値を描画
    line(x, height, x, easedVal + height/2);
    //X座標の更新
    x++;
    //もし画面の端まできたら、最初から
    if(x > width) {
        x = 0;
    }
    } 
    else {
    //まだ信号を受信していなかったら
    //画面をクリックするようにメッセージを出す
    background(255);
    fill(0);
    text("click on screen", 10, 20);
    }
}

//シリアル入力を検出した際に発生するイベント
void serialEvent(Serial p) {
    //もし、バッファーにアナログ入力の数の2倍(上位8bitと下位8bit)あれば
    if(myPort.available() > NUM * 2 - 1) {
    //入力の数だけ繰り返し
    for(int i=0; i

データの流れを視覚化する 2

  • 視覚的な部分をもう少し工夫してみる
  • データを横にスキャンするのではなく、円形にスキャンしてみる

Arduino側のプログラムは同じ
Processing側

import processing.serial.*;

//アナログ入力の数を指定
int NUM = 1;

//半径
float radius;
//現在の角度
float angle;
//現在Serial信号を受信しているか
boolean running = false;

//Serialクラスのインスタンス
Serial myPort;

//Serialより読み込んだデータを格納する配列
int[] val = new int[NUM]; 

void setup() {
    //画面を生成
    size(640, 640);
    colorMode(HSB,360,100,100,100);
    frameRate(15);
    strokeWeight(3);
    //ポートの名前を取得
    String portName = Serial.list()[1];
    //Serialクラスをインスタンス化
    myPort = new Serial(this, portName, 9600);
    //背景を黒に
    background(0);
    //スムージング
    smooth();
}

void draw() {
    if(running) {
    //入力された値から円の半径をマッピング
    float radius = map(val[0], 0, 1023, 0, height/2);
    //円の中心点を指定
    int middleX = width/2;
    int middleY = height/2;
    //三角関数を仕様して角度から座標を算出
    float x = middleX + cos(angle) * height/2;
    float y = middleY + sin(angle) * height/2;
    //黒い線で前の値を消す
    stroke(0,0,0);
    line(middleX, middleY, x, y);
    x = middleX + cos(angle) * radius;
    y = middleY + sin(angle) * radius;
    //値によって色相を変化させる
    float h = map(val[0], 0, 1023, 180, 360);
    //算出した色相で線を描く
    stroke(h, 75, 100);
    line(middleX, middleY, x, y);
    //角度の更新
    angle += 0.01;
    } 
    else {
    //まだ信号を受信していなかったら
    //画面をクリックするようにメッセージを出す
    background(0);
    fill(0,0,100);
    text("click on screen", 10, 20);
    }
}

//シリアル入力を検出した際に発生するイベント
void serialEvent(Serial p) {
    //もし、バッファーにアナログ入力の数の2倍(上位8bitと下位8bit)あれば
    if(myPort.available() > NUM * 2 - 1) {
    //入力の数だけ繰り返し
    for(int i=0; i

サンプルコードのダウンロード

今回の授業で紹介した全てのサンプルは、下記のリンクからダウンロード可能です


進歩

DSC_7344

openFrameworksの開発者Zach Liebermanさんが来日していて、渋谷で飲んでいるということで、集まりに参加させていただいた。感謝。oF本にサインしていただく。これで、先日多摩美でレクチャーしたときにいただいたTheo Watsonさんのサインとあわせて、openFrameworks主要開発者のサインが揃った。あとは、Arturo Castroさんのサインが揃えばコンプリートか。

ブログには毎回書かなくなったけど、ラニンニングを継続中。週に3日くらいのペースで1時間走る感じ。半年くらい前に始めた当初は1時間で8kmくらいしか走れなかったのだけど、最近ついに1時間で10kmを走りきることができるようになった。ひとつ大きな壁を突破した心境。

人間40歳が目前に迫るくらいの年齢になると、進歩することが少なくなってくる。いろいろな面で。なので、こうして自分の記録を更新できたということ自体が楽しい。今後はこのペースで毎回安定して走ることができるように継続していきたい。


Processingによる3Dプログラミング

今日の内容

Processingで3DCGプログラミング

  • 3Dの座標系
  • OpenGLとは
  • 2Dのアニメーションを3Dに拡張してみる
  • 3D座標で図形を描く
  • 視点の移動
  • 3Dのを用いた高度なアニメーション

コンピュータで3Dを表現するには

コンピュータ画面で3Dを表現したい

  • コンピュータのディスプレイは、2D (縦横に並んだピクセル)
  • 奥行は擬似的に表現するしかない
  • 画面に、立体や空間などの3次元の存在を投影して描画する = 3DCG (3次元グラフィックス)

2次元平面に3次元の存在を投影するには、様々な数学的な知識が必要

  • Processingではこうした演算を自動的に行うことが可能
  • 3次元の座標系をそのまま使用できる
  • 高速な表示のためのライブラリ(OpenGL)も標準で使用可


コンピュータ画面での3Dを表示する

3D空間の座標系

X (幅)、 Y (高さ) に加えて、奥行を表現する座標軸 Z が加わる。


3Dの座標系

3Dプログラミング基本

まず2Dの図形を回転するプログラムを作成してみる

  • translateで画面の中心を座標の原点に
  • 以下の処理を繰り返す
    • 背景描画
    • rotateで回転
    • rectを描画
    • 回転する角度を更新
float rot=0;
void setup(){
    size(400,400);
    frameRate(60);
    smooth();
    fill(63,127,255);
    stroke(255);
    //四角形を中心を原点に描画するモードに
    rectMode(CENTER);
}
void draw(){
    background(0);
    //画面の中心に原点(0,0)を移動する
    translate(width/2,height/2);
    //画面の中心を軸にrotだけ回転
    rotate(rot);
    //四角を描く
    rect(0,0,200,200);
    //角度を更新
    rot += 0.06;
}

現状の回転する四角形のプログラム

  • 2D的な視点では平面上で回転している、これを3D的な視点に変更してみる
  • 現状では、Z軸を中心軸としてXY平面上に置いてある物体が回転している
  • では軸をZ軸ではなく、他の軸(X軸、Y軸)にすると果してどうなるのか

回転する軸を指定してrotateする関数

rotateX(angle);  // X軸を中心に回転
rotateY(angle);  // Y軸を中心に回転軸を中心に回転
rotateZ(angle);  // Z軸を中心に回転軸を中心に回転

3D座標を用いたプログラミングをする際の注意点

レンダラー (描画の際の方式) を指定する必要がある

  1. P3D – Processing専用の3D描画エンジン
  2. OpenGL – 3Dグラフィックスのためのプログラムインターフェイス、高速に3D画像を描画できる

P3Dを使用する場合

  • size関数に以下の指定をする
size (幅, 高さ, P3D); // size関数にP3Dの指定

OpenGLを使用する場合

  • プログラムの先頭でOpenGLのライブラリを読み込み
  • size関数にOPENGLの指定をする
import processing.opengl.*; // OpenGLライブラリの読み込み
size(幅, 高さ, OPENGL);     // size関数にOPENGLの指定

OpenGLを使用した3次元空間での回転

x軸を中心に回転

import processing.opengl.*;
float rot=0;
void setup(){
    size(400,400,OPENGL);
    frameRate(60);
    fill(63,127,255);
    stroke(255);
    rectMode(CENTER);
}
void draw(){
    background(0);
    translate(width/2,height/2);
    //X軸を中心に回転させる
    rotateX(rot);
    rect(0,0,200,200);
    rot += 0.06;
}

x,y,z軸をそれぞれ回転

import processing.opengl.*;
// x, y, z それぞれの軸での回転角度
float rotX, rotY, rotZ;
void setup(){
    size(400,400,OPENGL);
    frameRate(60);
    fill(63,127,255);
    stroke(255);
    rectMode(CENTER);
}
void draw(){
    background(0,0,20);
    translate(width/2,height/2);
    //X軸を中心に回転
    rotateX(rotX);
    //Y軸を中心に回転
    rotateY(rotY);
    //Z軸を中心に回転
    rotateZ(rotZ);
    //四角形を描く
    rect(0,0,200,200);
    //それぞれの軸の回転角度を更新
    rotX += 0.02;
    rotY += 0.03;
    rotZ += 0.05;
}

3D図形の描画デモ

四角形を3D空間にタイル状に敷き詰める

  • for文を2重に入れ子にして、X,Y方向にグリッド状に四角形を敷きつめていく
  • ただし個々の四角形はすこしだけ斜めに傾けて立体感を強調する
  • マウスの位置によって、座標全体を回転してみる
  • まずは単純に描画してみる
import processing.opengl.*;
void setup() {
  size(400, 400, OPENGL);
  noStroke();
  fill(255, 190);
}
void draw() {
  background(0);
  translate(width / 2, height / 2, 0);
  //マウスの位置で座標全体を回転する
  rotateX(mouseX / 200.0);
  rotateY(mouseY / 100.0);
  //四角形描画を中心を原点に
  rectMode(CENTER);
  //敷きつめるタイルの一片の長さ
  int dim = 18;
  //XY平面を正方形でタイリング
  for (int i = -height / 2; i < height / 2; i += dim) {
    for (int j = -width / 2; j < width / 2; j += dim) {
      pushMatrix();
      translate(i, j);
      rotateX(radians(30));
      rotateY(radians(30));
      rect(0, 0, dim, dim);
      popMatrix();
    }
  }
}

ライティング

Processinには様々なライティングの方法を用いることができる

  • ambientLight() – 環境光
  • directionalLight() – 一定方向から差し込む平行光
  • pointLight() – 点光源
  • spotLight() – スポットライト

様々な光を活用しながら、複雑なライティングをしてみる

import processing.opengl.*;
void setup() {
  size(400, 400, OPENGL);
  noStroke();
  fill(255, 190);
}
void draw() {
  //環境光
  ambientLight(63, 31, 31);
  //平行光
  directionalLight(255, 255, 255, -1, 0, 0);
  //点光源
  pointLight(63, 127, 255, mouseX, mouseY, 200);
  //スポットライト
  spotLight(100, 100, 100, mouseX, mouseY, 200, 0, 0, -1, PI, 2);
  background(0);
  translate(width / 2, height / 2, -20);
  rotateX(mouseX / 200.0);
  rotateY(mouseY / 100.0);
  int dim = 18;
  for (int i = -height / 2; i < height / 2; i += dim * 1.4) {
    for (int j = -width / 2; j < width / 2; j += dim * 1.4) {
      pushMatrix();
      translate(i, j);
      rotateX(radians(30));
      rotateY(radians(30));
      box(dim, dim, dim);
      popMatrix();
    }
  }
}

カメラ

Processingでは何も指定していない時にはカメラ(視点)の場所は、中心点(0, 0, 0)。しかし、camera() 関数を使用することで、視点をコントロールすることができる

camera() 関数

  • camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ)
    • eyeX, eyeY, eyeZ – 視点の位置
    • centerX, centerY, centerZ – 注視する中心位置
    • upX, upY, upZ – カメラの向き

マウスの位置でカメラの位置をコントロールしてみる

import processing.opengl.*;
void setup() {
  size(400, 400, OPENGL);
  noStroke();
  fill(255, 190);
}
void draw() {
  background(0);
  ambientLight(63, 31, 31);
  directionalLight(255, 255, 255, -1, 0, 0);
  pointLight(63, 127, 255, mouseX, mouseY, 200);
  spotLight(100, 100, 100, mouseX, mouseY, 200, 0, 0, -1, PI, 2);
  //カメラを定義、マウスの位置でカメラの位置が変化する
  camera(mouseX, mouseY, 200, width / 2.0, height / 2.0, 0, 0, 1, 0);
  translate(width / 2, height / 2, -20);
  int dim = 18;
  for (int i = -height / 2; i < height / 2; i += dim * 1.4) {
    for (int j = -width / 2; j < width / 2; j += dim * 1.4) {
      pushMatrix();
      translate(i, j);
      rotateX(radians(30));
      rotateY(radians(30));
      box(dim, dim, dim);
      popMatrix();
    }
  }
}

3D空間でのアニメーション

少しずつスピードを変化させながら回転する立方体を作成してみる

import processing.opengl.*;
float a;
int NUM = 128;
float offset = PI / NUM;
color[] colors = new color[NUM];
void setup() {
  size(400, 400, OPENGL);
  noStroke();
  colorMode(HSB, 360, 100, 100, 100);
  frameRate(30);
  for (int i = 0; i < NUM; i++) {
    colors[i] = color(i * 2 + 100, 70, 100, 25);
  }
  lights();
}
void draw() {
  background(0);
  ambientLight(63, 31, 31);
  directionalLight(255, 255, 255, -1, 0, 0);
  pointLight(63, 127, 255, mouseX, mouseY, 200);
  spotLight(100, 100, 100, mouseX, mouseY, 200, 0, 0, -1, PI, 2);
  translate(width / 2, height / 2, -20);
  rotateX(mouseX / 200.0);
  rotateY(mouseY / 100.0);
  for (int i = 0; i < NUM; i++) {
    pushMatrix();
    fill(colors[i]);
    rotateY(a + offset * i);
    rotateX(a / 2 + offset * i);
    rotateZ(a / 3 + offset * i);
    box(width / 2);
    popMatrix();
  }
  a += 0.01;
}

サンプルファイルのダウンロード

今日とりあげた全てのサンプルは下記からダウンロードしてください。


ActionScript (3) : ムービープレーヤーを作る

本日の内容

  • ActionScript 3.0 を用いたより実践的なプログラミングに挑戦する
  • より具体的な機能をもったユーザーインタフェイスを作成してみる
  • ムービプレーヤーを作成する
    • Webで動画を表示・操作することのできる、簡単なムービプレーヤーを作成する
    • コンポーネントを効果的に使用すると、意外と容易に作成可能

完成イメージ

制作テンプレートのダウンロード

まず最初に制作のためのテンプレートを下記からダウンロードしてください。

ムービープレーヤーの作成手順

FLVPlayerの配置
  • ウィンドウ → コンポーネント を開く
  • Videoの中からFLVPlaybackを選択して、ステージ上に配置する
  • FLVPlayqバックを選択して、プロパティウィンドウのパラメータのタブを選択して下記のように設定する
    • インスタンス名:theVideo
    • autoPlay : true
    • scaleMode : noScale
    • skin : パラメータのコラムを選択した状態で、右側の虫めがねのアイコンをクリックする
      • skinの選択画面が表示されるので、リストから SkinUnderPlayStopSeekMuteVol.swf を選択
    • source : パラメータのコラムを選択した状態で、右側の虫めがねのアイコンをクリックする
      • 再生するムービーファイルのコンテンツパスを設定する画面が表示される
      • Popeye_forPresiden768K_000.flv を指定する
  • この状態で一旦ムービーのプレビューをしてみる
    • 画面上でオープニングのムービーが再生されるはず
シーン選択ボタンの配置
  • コンポーネントウィンドウを開く (ウィンドウ → コンポーネント)
  • User Intreface から Buttonを選択
  • ステージ上に、6つボタンを配置する
  • それぞれのボタンのパラメータを次のように設定する
    • インスタンス名: scene0、Label : Scene 0
    • インスタンス名: scene1、Label : Scene 1
    • インスタンス名: scene2、Label : Scene 2
    • インスタンス名: scene3、Label : Scene 3
    • インスタンス名: scene4、Label : Scene 4
    • インスタンス名: scene5、Label : Scene 5
スクリプトの記述
  • actionsのレイヤーに移動して、フレーム内に下記のフレームスクリプトを記述する

[code language=”javascript”]
//それぞれの動画の再生ボタンのリスナーを設定
this.scene0.addEventListener(MouseEvent.CLICK, showScene0);
this.scene1.addEventListener(MouseEvent.CLICK, showScene1);
this.scene2.addEventListener(MouseEvent.CLICK, showScene2);
this.scene3.addEventListener(MouseEvent.CLICK, showScene3);
this.scene4.addEventListener(MouseEvent.CLICK, showScene4);
this.scene5.addEventListener(MouseEvent.CLICK, showScene5);

//動画0を再生
function showScene0(event:MouseEvent):void
{
this.theVideo.source = "Popeye_forPresiden768K_000.flv";
}

//動画1を再生
function showScene1(event:MouseEvent):void
{
this.theVideo.source = "Popeye_forPresiden768K_001.flv";
}

//動画2を再生
function showScene2(event:MouseEvent):void
{
this.theVideo.source = "Popeye_forPresiden768K_002.flv";
}

//動画3を再生
function showScene3(event:MouseEvent):void
{
this.theVideo.source = "Popeye_forPresiden768K_003.flv";
}

//動画4を再生
function showScene4(event:MouseEvent):void
{
this.theVideo.source = "Popeye_forPresiden768K_004.flv";
}

//動画5を再生
function showScene5(event:MouseEvent):void
{
this.theVideo.source = "Popeye_forPresiden768K_005.flv";
}
[/code]

完成!
  • 画面タイトルなどをデザインしたら完成!

ArduinoとProcessingの連携1 : センサーの情報を視覚化する

今日の内容

  • ArduinoとProcessingを連携する
  • Arduinoのアナログインにセンサーからの情報を入力
    • 可変抵抗
    • 光センサー
    • 温度センサー
  • ProcessingからArduinoの情報を取得してリアルタイムに情報を視覚化
  • 今後のBio Media Artの肝となってくる部分!!

ArduinoとProcessinの連携方法

  • Serial 通信による連携
  • Funnelによる連携
  • この授業では、Serial通信のほうを採用
  • 参考として、Funnelを使用した方法も紹介

シリアル通信:全体の構成

サンプル1:可変抵抗の値を送信

  • まずはシンプルな構成で連携をテスト
  • ブレッドボードとArduinoの配線
    • 可変抵抗1つ
    • ArduinoのAnalog in 0に抵抗値を接続
    • USBケーブルで、ArduinoをPCに接続
  • Arduino
    • Analog in の値を取得 – analogRead()
    • シリアルで値を送信 – Serial.print()
  • Processing
    • シリアルポートに接続
    • シリアルポートから値を読み取る
  • 可変抵抗器 (ポテンショメーター)
    • 抵抗値を変更することができる抵抗器
    • 今回は、10KΩの可変抵抗器を用意
  • 可変抵抗器の抵抗値をとりだす
  • 全体の配線例
  • Arduino側のプログラム
    • setup() – 初期設定
      • シリアル通信を開始
    • loop() – メインループ
      • Analog in 0 の値を読み取る
      • Analog in の値の範囲は、0〜1024
      • Serialで一度に出力できる範囲は、0〜255 (1 BYTE)
      • 入力値を、0〜255の範囲にマッピング (map関数)
      • シリアルで補正した値を送信
      • 一定間隔静止して、繰り返す
int in0; //アナログ入力0
int outByte; //シリアルで出力する値

void setup(){
    //シリアル通信開始
    Serial.begin(9600);
}

void loop(){
  //アナログ入力0番ピンの値を読み取り(0~1023)
  in0 = analogRead(0);
  //値を、0〜255の範囲にマップ
  outByte = map(in0, 0, 1023, 0, 255); 
  //シリアルでoutByteを送信(BYTEフォーマット)
  Serial.print(outByte,BYTE);
  //1秒間に10回ループ
  delay(100);
}
  • Processing側プログラム
    • setup() – 初期設定
      • シリアルポートを設定
      • Serialクラスを初期化
    • serialEvent() – シリアルイベント
      • シリアル通信でデータを受信した際に発生するイベント
      • read() で値を取得する (0〜255)
      • 取得した値をモニタする – println()
    • draw() – 描画
      • 取得した値を背景のグレースケールのレベルに対応させている
import processing.serial.*;

// Serialクラスのインスタンス
Serial myPort;
// シリアルポートから取得したデータ(Byte)
int inByte;

void setup()
{
    size(640, 640);
    // Macのシリアルのリストの最初のポートがFTDIアダプタのポート
    String portName = Serial.list()[0];
    // ポートとスピードを設定して、Serialクラスを初期化、
    myPort = new Serial(this, portName, 9600);
}

void draw()
{
    // シリアルから取得した値を背景色に設定
    background(inByte);
}

void serialEvent(Serial p){
    // 設定したシリアルポートからデータを読み取り
    inByte = myPort.read();
}
  • 完成!
  • 可変抵抗の値を変化させると、背景色の濃度が変化する

サンプル2:複数の値を送信

  • 複数のセンサーからの値を取得するには
  • Arduinoには、Analog in が最大5つまで使用できる
  • 複数のAnalog入力の値を取得してみる
  • まずは可変抵抗でテスト
  • ブレットボードとArduinoの配線例
  • ArduinoとProcessinを連携する際の注意
  • Arduinoからのシリアル出力は、値が順番に送られる
    • 例:入力が3つ(byte0, byte1, byte2)の場合
    • byte0, byte1, byte2, byte0, byte1, byte2, ….
  • Processingの受信方法を工夫しないと、値がずれてしまう場合がある
  • 例:
    • 012012012012… – 想定した値
    • 120120120120… – 順番がずれた値
  • 順番がずれてしまわないように、Processing側で値のセットを受け取ったら読み込み完了の合図を送る
  • 同期のイメージ
  • Arduino
int in0, in1, in2; //アナログ入力0
int outByte0, outByte1, outByte2; //シリアルで出力する値

void setup(){
  //シリアル通信開始
  Serial.begin(9600);
}

void loop(){
  //アナログ入力0〜2番ピンの値を読み取り(0~1023)
  in0 = analogRead(0);
  in1 = analogRead(1);
  in2 = analogRead(2);

  //値を、0〜255の範囲にマップ
  outByte0 = map(in0, 0, 1023, 0, 255); 
  outByte1 = map(in1, 0, 1023, 0, 255); 
  outByte2 = map(in2, 0, 1023, 0, 255); 

  //Processingと同期
  if(Serial.available()>0){
    //シリアルでoutByteを送信(BYTEフォーマット)
    Serial.print(outByte0,BYTE);
    Serial.print(outByte1,BYTE);
    Serial.print(outByte2,BYTE);
  }

  //1秒間に10回ループ
  delay(100);
}
  • Processing
import processing.serial.*;

// Serialクラスのインスタンス
Serial myPort;
// シリアルポートから取得したデータ(Byte)
int inByte0, inByte1, inByte2;

void setup()
{
  size(640, 640);
  // Macのシリアルのリストの最初のポートがFTDIアダプタのポート
  String portName = Serial.list()[0];
  // ポートとスピードを設定して、Serialクラスを初期化、
  myPort = new Serial(this, portName, 9600);
  noStroke();
  smooth();
}

void draw()
{
  background(inByte0);
  fill(inByte1);
  float diameter = float(inByte2)/255.0*width;
  ellipse(width/2, height/2, diameter, diameter); 
}

void serialEvent(Serial p){
  //Arduinoから送られてきたデータが3個来たら
  if(myPort.available()>2){
    //順番にデータを読み込む
    inByte0 = myPort.read();
    inByte1 = myPort.read();
    inByte2 = myPort.read();
    //Arduinoへ読み込み完了の合図を送る
    myPort.write(255);
  }
}

//マウスが押されたら通信開始
void mousePressed(){
  //バッファ領域を空にする
  myPort.clear();
  //合図用データを送る
  myPort.write(255);
}
  • 可変抵抗の設定で、背景色、描画色、円の大きさが変化する

サンプル3:光センサーを使う

  • 可変抵抗でなくても、変化するアナログ値であれば、なんでも利用可能
  • 可変抵抗を各種センサーに入れ替えてみる
    • 光センサー (CdSセル)
    • 測距センサー (赤外線センサー)
    • 感圧センサー
    • 振動センサー
    • 曲げセンサー
    • 温度センサー
    • 傾きセンサー
  • まずは、光センサーを使用してみます
  • Arduinoと、Processingのプログラムは、そのまま流用可能
  • ブレットボードとArduinoの配線例
  • センサー部分拡大

サンプル4:温度センサーを使う

  • 温度センサーをつかって、温度に反応するようにしてみる
  • LM35DZ – 温度センサー、0℃で0V、20℃で200mVになる
  • ブレットボードとArduinoの配線例

課題:センサーの値を視覚化する

  • Generative Gestaltungのコードを活用して、Arduinoにとりつけたセンサーや可変抵抗、スイッチなどを操作すると形態や動きが変化するプログラムをProcessingで作成する。
  • 使用するセンサーは自由に選択してください。温度センサー、光センサー、可変抵抗以外のものでもOK
  • 複数のセンサー、可変抵抗を組み合せても構いません
  • 次回の月曜の授業まで!

課題のサンプル

  • Generative Gestaltungの”M_1_2_01″を改造して作成
  • マウスのX座標によって変化する変数”faderX”を、シリアル入力の値に変更
  • 色も微妙に変化させている
  • Arduino側のコードは、サンプル1と一緒
int in0; //アナログ入力0
int outByte; //シリアルで出力する値

void setup(){
    //シリアル通信開始
    Serial.begin(9600);
}

void loop(){
  //アナログ入力0番ピンの値を読み取り(0~1023)
  in0 = analogRead(0);
  //値を、0〜255の範囲にマップ
  outByte = map(in0, 0, 1023, 0, 255); 
  //シリアルでoutByteを送信(BYTEフォーマット)
  Serial.print(outByte,BYTE);
  //1秒間に10回ループ
  delay(100);
}
  • Processing側コード
  • Generative Gestaltung M_1_2_01 を改造
import processing.serial.*;
import processing.pdf.*;
boolean savePDF = false;

int actRandomSeed = 0;
int count = 360;


// Serialクラスのインスタンス
Serial myPort;
// シリアルポートから取得したデータ(Byte)
int inByte;

void setup()
{
  size(800, 800);
  colorMode(HSB, 360,100,100,100);
  cursor(CROSS);
  smooth();
  // Macのシリアルのリストの最初のポートがFTDIアダプタのポート
  String portName = Serial.list()[0];
  // ポートとスピードを設定して、Serialクラスを初期化、
  myPort = new Serial(this, portName, 9600);

}


void draw() {
  if (savePDF) beginRecord(PDF, timestamp()+".pdf");
  background(0);
  noStroke();

  float faderX = inByte/255.0;

  randomSeed(actRandomSeed);
  float angle = radians(360/float(count));
  for (int i=0; i

openFrameworks for iPhone:時計をつくる

今日の内容

  • iPhoneで時計をつくってみる
  • openFrameworksで、現在の時刻の取得は簡単に実現可能
  • あとは、その数値を使って、いろいろな表現を試す

時刻の取得

  • openFrameworksでは、現在の時間、分、秒を取得する関数が用意されている
  • だたし、この時間はiPhone (シミュレータの場合はMac) の時計を参照しているので、常に正確とは限らない
  • 時刻を取得する関数
    • ofGetHours(); – 「時」を取得 (0〜24)
    • ofGetMinutes(); – 「分」を取得 (0〜60)
    • ofGetSeconds(); – 「秒」を取得 (0〜60)

サンプル1:時刻を表示する

  • まずは簡単に、取得した時刻を文字で表示してみる

testApp.mm
[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);
ofBackground(0,0,0);
}

//————————————————————–
void testApp::update(){

}

//————————————————————–
void testApp::draw(){
//秒の取得
int s = ofGetSeconds();
//分の取得
int m = ofGetMinutes();
//時の取得
int h = ofGetHours();

//文字で表示する
ofSetColor(255, 255, 255);
string time = ofToString(h, 0) + ":" + ofToString(m, 0) + ":" + ofToString(s, 0);
ofDrawBitmapString(time, 10, 20);
}

//————————————————————–
void testApp::exit(){

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::lostFocus(){

}

//————————————————————–
void testApp::gotFocus(){

}

//————————————————————–
void testApp::gotMemoryWarning(){

}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){

}
[/code]

サンプル2:デジタル時計

  • フォントを読み込んで、きれいな文字で表示
  • 時、分、秒ともに、値が10以下の時は、左に0を追加して2桁表示にする

testApp.h
[code language=”cpp”]
#pragma once

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"

class testApp : public ofxiPhoneApp {

public:
void setup();
void update();
void draw();
void exit();

void touchDown(ofTouchEventArgs &touch);
void touchMoved(ofTouchEventArgs &touch);
void touchUp(ofTouchEventArgs &touch);
void touchDoubleTap(ofTouchEventArgs &touch);

void lostFocus();
void gotFocus();
void gotMemoryWarning();
void deviceOrientationChanged(int newOrientation);
ofTrueTypeFont verdana;

};
[/code]

testApp.mm
[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);
ofBackground(0,0,0);
//フォントを読み込み
verdana.loadFont("verdana.ttf",36, true, true);
}

//————————————————————–
void testApp::update(){

}

//————————————————————–
void testApp::draw(){
string s, m, h;
ofSetColor(255, 255, 255);

//秒を読み込み、10以下の場合には左に0づめで2桁に
if (ofGetSeconds()<10) {
s = "0"+ofToString(ofGetSeconds(), 0);
} else {
s = ofToString(ofGetSeconds(), 0);
}
//分を読み込み、10以下の場合には左に0づめで2桁に
if (ofGetMinutes()<10) {
m = "0"+ofToString(ofGetMinutes(), 0);
} else {
m = ofToString(ofGetMinutes(), 0);
}
//時を読み込み、10以下の場合には左に0づめで2桁に
if (ofGetHours()<10) {
h = "0"+ofToString(ofGetHours(), 0);
} else {
h = ofToString(ofGetHours(), 0);
}

//読み込んだフォントで、時刻を表示
string time = h + ":" + m + ":" + s;
verdana.drawString(time, 50, ofGetHeight()/2);
}

//————————————————————–
void testApp::exit(){

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::lostFocus(){

}

//————————————————————–
void testApp::gotFocus(){

}

//————————————————————–
void testApp::gotMemoryWarning(){

}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){

}
[/code]

サンプル3:アナログ時計(簡易版)

  • 取得した時刻から、アナログ時計を作成する
  • 時針、分針、秒針のそれぞれの角度を求める
  • 時針:12時間で360°変化する。1時間あたりの変化は、360 / 12 = 30°
    • 時刻から時針の角度を求める
    • 現在の時刻(時) x 30
  • 分針:60分で360°変化する。1時間あたりの変化は、360 / 60 = 6°
    • 時刻から分針の角度を求める
    • 現在の時刻(分) x 6
  • 秒針:60秒で360°変化する。1時間あたりの変化は、360 / 60 = 6°
    • 時刻から秒針の角度を求める
    • 現在の時刻(秒) x 6

testApp.mm
[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);
ofBackground(0, 0, 0);
ofEnableSmoothing();
}

//————————————————————–
void testApp::update(){

}

//————————————————————–
void testApp::draw(){
//時刻を取得
int s = ofGetSeconds();
int m = ofGetMinutes();
int h = ofGetHours()%12;

//時計の大きさを設定
float clockSize = ofGetWidth()/2 – 20;

//背景に円を描く
ofSetColor(255, 255, 255);
ofNoFill();
ofTranslate(ofGetWidth()/2, ofGetHeight()/2);
ofSetLineWidth(3);
ofSetCircleResolution(64);
ofCircle(0, 0, clockSize);

//秒針
ofPushMatrix();
ofRotateZ(s*6.0);
ofSetLineWidth(1);
ofLine(0, 0, 0, -clockSize);
ofPopMatrix();

//分
ofPushMatrix();
ofRotateZ(m*6.0);
ofSetLineWidth(2);
ofLine(0, 0, 0, -clockSize);
ofPopMatrix();

//時針
ofPushMatrix();
ofRotateZ(h*30.0);
ofSetLineWidth(4);
ofLine(0, 0, 0, -clockSize*0.75);
ofPopMatrix();
}

//————————————————————–
void testApp::exit(){

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::lostFocus(){

}

//————————————————————–
void testApp::gotFocus(){

}

//————————————————————–
void testApp::gotMemoryWarning(){

}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){

}
[/code]

サンプル4:アナログ時計(完成版)

  • 作成したプログラムの表示と実際のアナログ時計と比べてみる
  • なにか違いはないだろうか?
    • 分が切り替わった瞬間の動き
    • 時間が切り替わった瞬間の動き
  • 実際の時計
    • 時針、分針も常にゆっくりと動いている
    • 時刻が変化した瞬間に動くのではなく、ゆっくりと移動して、気がつくと角度が変化している
  • 実際の分針
    • 秒の影響を受けている
    • 毎秒あたり、1/60の影響 (1分 = 60秒なので)
    • m = m + (s/60);
  • 実際の時針
    • 分の影響を受けている
    • 毎分あたり、1/60の影響 (1時間 = 60分なので)
    • h = h + (m/60);
  • 分針と時針の補正をしたアナログ時計に修正する
  • 文字盤を追加
    • 角度を変更しながら、一定間隔に目盛を刻んでいく
    • 小さな目盛:6度づつ
    • 大きな目盛:30度づつ

testApp.mm
[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);
ofBackground(0, 0, 0);
ofEnableSmoothing();
}

//————————————————————–
void testApp::update(){

}

//————————————————————–
void testApp::draw(){
//時刻を取得
//秒を取得
float s = ofGetSeconds();
//秒の影響を加えた、分の算出
float m = ofGetMinutes() + (s/60.0);
//分の影響を加えた、時の算出
float h = ofGetHours()%12 + (m/60);

//時計の大きさ
float clockSize = ofGetWidth()/2 – 20;

//座標全体を中心に移動
ofTranslate(ofGetWidth()/2, ofGetHeight()/2);

//時計の背景
ofSetColor(127,127,127);
ofFill();

//分の目盛を描く
for (int i=0; i<60; i++) {
ofRotateZ(6);
ofCircle(clockSize, 0, 2);
}

//時の目盛を描く
for (int i=0; i<12; i++) {
ofRotateZ(30);
ofCircle(clockSize, 0, 4);
}

ofSetColor(255, 255, 255);
ofNoFill();

//秒針
ofPushMatrix();
ofRotateZ(s*6.0);
ofSetLineWidth(1);
ofLine(0, 0, 0, -clockSize);
ofPopMatrix();

//分
ofPushMatrix();
ofRotateZ(m*6.0);
ofSetLineWidth(2);
ofLine(0, 0, 0, -clockSize);
ofPopMatrix();

//時針
ofPushMatrix();
ofRotateZ(h*30.0);
ofSetLineWidth(4);
ofLine(0, 0, 0, -clockSize*0.75);
ofPopMatrix();
}

//————————————————————–
void testApp::exit(){

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::lostFocus(){

}

//————————————————————–
void testApp::gotFocus(){

}

//————————————————————–
void testApp::gotMemoryWarning(){

}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){

}
[/code]

サンプル5:大きさによる時間の表現

  • 時刻の数値をより自由な発想で使用してみる
  • 時、分、秒、ミリ秒の値で半径を変化させながら、円を描いてみる
  • 大きさによる時計

testApp.mm
[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);
ofBackground(0, 0, 0);
ofEnableSmoothing();
}

//————————————————————–
void testApp::update(){

}

//————————————————————–
void testApp::draw(){
//時刻を取得
int ms = ofGetElapsedTimeMillis() % 1000;
int s = ofGetSeconds();
int m = ofGetMinutes();
int h = ofGetHours();

//円の最大の大きさを設定
float circleSize = ofGetHeight()/8;

ofSetColor(127, 127, 127);
//ミリ秒
ofCircle(ofGetWidth()/2, ofGetHeight()/8*7, ms/1000.0*circleSize);
//秒
ofCircle(ofGetWidth()/2, ofGetHeight()/8*5, s/60.0*circleSize);
//分
ofCircle(ofGetWidth()/2, ofGetHeight()/8*3, m/60.0*circleSize);
//時
ofCircle(ofGetWidth()/2, ofGetHeight()/8, h/24.0*circleSize);}

//————————————————————–
void testApp::exit(){

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::lostFocus(){

}

//————————————————————–
void testApp::gotFocus(){

}

//————————————————————–
void testApp::gotMemoryWarning(){

}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){

}
[/code]

サンプル6:位置による時間の表現

  • 時、分、秒、ミリ秒の値で位置を変化させながら、直線を引いてみる

testApp.h
[code language=”cpp”]
#pragma once

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"

class testApp : public ofxiPhoneApp {

public:
void setup();
void update();
void draw();
void exit();

void touchDown(ofTouchEventArgs &touch);
void touchMoved(ofTouchEventArgs &touch);
void touchUp(ofTouchEventArgs &touch);
void touchDoubleTap(ofTouchEventArgs &touch);

void lostFocus();
void gotFocus();
void gotMemoryWarning();
void deviceOrientationChanged(int newOrientation);

ofTrueTypeFont verdana;
};
[/code]

testApp.mm
[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);
ofBackground(0, 0, 0);
ofEnableSmoothing();
ofSetBackgroundAuto(false);
ofEnableAlphaBlending();

verdana.loadFont("verdana.ttf",12, false, true);
}

//————————————————————–
void testApp::update(){

}

//————————————————————–
void testApp::draw(){
//背景
ofSetColor(0, 0, 0, 15);
ofRect(0, 0, ofGetWidth(), ofGetHeight());

//時刻を取得
int ms = ofGetElapsedTimeMillis() % 1000;
int s = ofGetSeconds();
int m = ofGetMinutes();
int h = ofGetHours();

//枠
ofSetColor(63, 63, 63);
ofSetLineWidth(1);
ofLine(0, ofGetHeight()/4, ofGetWidth(), ofGetHeight()/4);
ofLine(0, ofGetHeight()/2, ofGetWidth(), ofGetHeight()/2);
ofLine(0, ofGetHeight()/4*3, ofGetWidth(), ofGetHeight()/4*3);

//線の位置で時間を表現
ofSetColor(255, 255, 255);
ofSetLineWidth(3);
//ミリ秒
verdana.drawString(ofToString(ms, 0), ms/1000.0*ofGetWidth()+4, ofGetHeight()/8+6);
ofLine(ms/1000.0*ofGetWidth(), 0, ms/1000.0*ofGetWidth(), ofGetHeight()/4);
//秒
verdana.drawString(ofToString(s, 0), s/60.0*ofGetWidth()+4, ofGetHeight()/8*3+6);
ofLine(s/60.0*ofGetWidth(), ofGetHeight()/4, s/60.0*ofGetWidth(), ofGetHeight()/2);
//分
verdana.drawString(ofToString(m, 0), m/60.0*ofGetWidth()+4, ofGetHeight()/8*5+6);
ofLine(m/60.0*ofGetWidth(), ofGetHeight()/2, m/60.0*ofGetWidth(), ofGetHeight()/4*3);
//時
verdana.drawString(ofToString(h, 0), h/24.0*ofGetWidth()+4, ofGetHeight()/8*7+6);
ofLine(h/24.0*ofGetWidth(), ofGetHeight()/4*3, h/24.0*ofGetWidth(), ofGetHeight());
}

//————————————————————–
void testApp::exit(){

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::lostFocus(){

}

//————————————————————–
void testApp::gotFocus(){

}

//————————————————————–
void testApp::gotMemoryWarning(){

}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){

}
[/code]

サンプルファイルのダウンロード

今日とりあげた全てのサンプルは、下記よりダウンロードすることができます。


ハーフマラソンへの道

11月にハーフマラソンを走るという目標ができたので、週2、3回は1時間くらい走って徐々に長距離を走る体力をつけていきたい。というわけで、昨日はティップネスのトレッドミル(ランニングマシン)で、10km/hのペースでどこまで走れるかトライしてみた。

できれば、1時間10km/h、1kmあたり6分ちょうどのペースで、そのまま1時間走りたいというのが目標。ところが、いざ走ってみると、最初の5kmは快調だったのだが、6km過ぎくらいからかなりバテてきた。7km過ぎからだいぶ辛くなり8kmをちょっと過ぎた地点でリタイア。10kmまではまだまだトレーニングが足りないみたいだ。当面は、1時間で10kmを走るというのを目標に、練習していきたい。

久しぶりに体重測ったら、ちょっとだけ痩せてた。おそらく本人にしか気付かない微妙な変化なんだけど…