yoppa.org


多摩美 - “iTamabi” – iPhoneアプリ開発プロジェクト 2010

openFrameworks for iPhone:お絵描きアプリを作る

今日の内容

  • ドロー(お絵描き)アプリをつくる
  • 簡単なサンプルから徐々に複雑なサンプルへと進めていきます

ドロー系アプリのサンプル

  • FatTag Deluxe Katsu Edition! http://fffff.at/fattag-deluxe-katsu-edition
    • いろいろな写真を背景にタギング(落書き)することができる
    • スプレーで描いた際のインクの垂れなどをリアルに再現

ドローアプリを作る

ステップ1:タッチした軌跡に円を描く

  • touchDownとtouchMoveした際のX座標とY座標を取得
  • 取得した座標の場所に円を描いていく
  • 円の連なりで線を描いていく

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);

ofPoint touchLoc; //画面をタッチしている場所
bool drawing; //ドロー中かどうか
};
[/code]

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

//————————————————————–
void testApp::setup(){
//iPhone基本設定
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);

//横位置で起動
iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);

//画面の基本設定
ofBackground(255, 255, 255);
ofSetBackgroundAuto(false);
drawing = false;
}

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

}

//————————————————————–
void testApp::draw(){
//もし現在ドロー中だったら
if (drawing) {
//色を黒に
ofSetColor(0, 0, 0);
//タッチしている場所に円を描く
ofCircle(touchLoc.x, touchLoc.y, 10);
}
}

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

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){
//ドロー中に
drawing = true;
//タッチしている場所を記録
touchLoc.x = touch.x;
touchLoc.y = touch.y;
}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){
//タッチしている場所を記録
touchLoc.x = touch.x;
touchLoc.y = touch.y;
}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){
//ドローを終了
drawing = false;
}

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

}

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

}

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

}

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

}

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

}
[/code]

ステップ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);

ofPoint touchLoc; //現在タッチしている場所
ofPoint lastTouchLoc; //ひとつ前のタッチしている場所
bool drawing; //ドロー中かどうか
};
[/code]

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

//————————————————————–
void testApp::setup(){
//iPhone基本設定
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);

//横位置で起動
iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);

//画面基本設定
ofSetBackgroundAuto(false);
ofBackground(255, 255, 255);
ofSetLineWidth(3);
ofEnableSmoothing();

drawing = false;
}

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

}

//————————————————————–
void testApp::draw(){
//もしドロー中だったら
if (drawing) {
ofSetColor(0, 0, 0);
//ひとつ前のタッチした座標から現在の座標まで線を描く
ofLine(lastTouchLoc.x, lastTouchLoc.y, touchLoc.x, touchLoc.y);
}
}

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

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){
//タッチしている場所を記録
touchLoc.x = touch.x;
touchLoc.y = touch.y;
}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){
//ドロー中に
drawing = true;
//ひとつ前の座標を記録
lastTouchLoc = touchLoc;
//タッチしている場所を記録
touchLoc.x = touch.x;
touchLoc.y = touch.y;
}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){
//ドローを終了
drawing = false;
}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
ofSetColor(255, 255, 255);
ofRect(0, 0, ofGetWidth(), ofGetHeight());
}

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

}

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

}

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

}

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

}
[/code]

ステップ3:線の太さを変化させる

  • 前のタッチした座標から、現在の座標までの移動した距離を測定
  • 移動した距離に応じて線の太さを変化させる
    • 移動距離が長いときは細い線で
    • 移動距離が短いときには太い線で
  • 筆で書いたような効果

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);

ofPoint touchLoc; //現在タッチしている場所
ofPoint lastTouchLoc; //ひとつ前のタッチしている場所
bool drawing; //ドロー中かどうか
};
[/code]

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

//————————————————————–
void testApp::setup(){
//iPhone基本設定
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);

//横位置で起動
iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);

//画面基本設定
ofSetBackgroundAuto(false);
ofBackground(255, 255, 255);
ofEnableSmoothing();
drawing = false;
}

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

}

//————————————————————–
void testApp::draw(){
//ドロー中だったら
if (drawing) {
//前のタッチした座標と現在の座標の距離を算出
float dist = ofDist(lastTouchLoc.x, lastTouchLoc.y, touchLoc.x, touchLoc.y);
//線の太さを決定
float lineWidth = 14 – dist * 0.5;
//4ピクセルよりは細くならないように
if (lineWidth < 4) {
lineWidth = 4;
}
ofSetColor(0, 0, 0);
//算出した太さで円を描く
ofCircle(touchLoc.x, touchLoc.y, lineWidth);
}
}

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

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){
//タッチしている場所を記録
touchLoc.x = touch.x;
touchLoc.y = touch.y;
}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){
//ドロー中に
drawing = true;
//ひとつ前の座標を記録
lastTouchLoc = touchLoc;
//タッチしている場所を記録
touchLoc.x = touch.x;
touchLoc.y = touch.y;
}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){
//ドローを終了
drawing = false;
}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
//ダブルタップで画面をリセット
drawing = false;
ofSetColor(255, 255, 255);
ofRect(0, 0, ofGetWidth(), ofGetHeight());
}

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

}

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

}

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

}

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

}
[/code]

ステップ4:よりなめらかな線に

  • 線が細くなった際に、線が切れぎれになってしまう問題に対処
  • 座標を一気に移動するのではなく、補完していくことで、なめらかな線にしている

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);

ofPoint touchLoc; //現在タッチしている場所
ofPoint lastTouchLoc; //ひとつ前のタッチしている場所
bool drawing; //ドロー中かどうか
float interpolate; //補完の割合

};
[/code]

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

//————————————————————–
void testApp::setup(){
//iPhone基本設定
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);

//横位置で起動
iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);

//画面基本設定
ofSetBackgroundAuto(false);
ofBackground(255, 255, 255);
ofEnableSmoothing();
drawing = false;

//補完の割合を設定
interpolate = 0.2;
}

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

}

//————————————————————–
void testApp::draw(){
if (drawing) {

//前のタッチした座標と現在の座標の距離を算出
float dist = ofDist(lastTouchLoc.x, lastTouchLoc.y, touchLoc.x, touchLoc.y);
//線の太さを決定
float lineWidth = 16 – dist * 0.2;
//6ピクセルよりは細くならないように
if (lineWidth < 6) {
lineWidth = 6;
}
ofSetColor(0, 0, 0);
//座標を一定の割合で補完している
lastTouchLoc += (touchLoc – lastTouchLoc) * interpolate;
//円を描く
ofFill();
ofCircle(lastTouchLoc.x, lastTouchLoc.y, lineWidth);
ofNoFill();
ofCircle(lastTouchLoc.x, lastTouchLoc.y, lineWidth);
}
}

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

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){
//タッチしている場所を記録
touchLoc.x = touch.x;
touchLoc.y = touch.y;
//ひとつ前の座標を記録
lastTouchLoc = touchLoc;
}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){
//ドロー中に
drawing = true;

//タッチしている場所を記録
touchLoc.x = touch.x;
touchLoc.y = touch.y;
}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){
//ドローを終了
drawing = false;
}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
//ダブルタップで画面をリセット
drawing = false;
ofSetColor(255, 255, 255);
ofRect(0, 0, ofGetWidth(), ofGetHeight());
}

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

}

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

}

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

}

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

}
[/code]

ステップ5:文字で描く

  • フォントを読みこんで、文字によって線を描く

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);

ofPoint touchLoc;
ofPoint lastTouchLoc;
float interpolate;
bool drawing;
ofTrueTypeFont myFont;
string letters;
float size;
int counter;
};
[/code]

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

//————————————————————–
void testApp::setup(){
//iPhone基本設定
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);

//横位置で起動
iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);

//画面基本設定
ofSetBackgroundAuto(false);
ofBackground(255, 255, 255);
ofEnableSmoothing();
drawing = false;

//表示する文字列を設定
letters = "hello iphone!!";
counter = 0;
interpolate = 0.2;
}

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

}

//————————————————————–
void testApp::draw(){
//文字で線を描く
float dist = ofDist(lastTouchLoc.x, lastTouchLoc.y, touchLoc.x, touchLoc.y);
float fontSize = dist;
if (fontSize < 12) {
fontSize = 12;
}
myFont.loadFont("Georgia.ttf", dist);
ofSetColor(0, 0, 0);
string newLetter = letters.substr(counter, 1);
size = myFont.stringWidth(newLetter);

if (dist > size) {
float angle = atan2(touchLoc.x – lastTouchLoc.x, lastTouchLoc.y – touchLoc.y);

ofPushMatrix();
ofTranslate(touchLoc.x, touchLoc.y);
ofRotateZ(angle * 180.0 / PI – 90);
myFont.drawString(newLetter, 0, 0);
ofPopMatrix();

counter++;
if (counter > letters.size()) {
counter = 0;
}
lastTouchLoc = touchLoc;
}

}

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

}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){
//タッチしている場所を記録
touchLoc.x = touch.x;
touchLoc.y = touch.y;
//ひとつ前の座標を記録
lastTouchLoc = touchLoc;
}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){
//ドロー中に
drawing = true;

//タッチしている場所を記録
touchLoc.x = touch.x;
touchLoc.y = touch.y;
}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){
//ドローを終了
drawing = false;
}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
//ダブルタップで画面をリセット
drawing = false;
ofSetColor(255, 255, 255);
ofRect(0, 0, ofGetWidth(), ofGetHeight());
}

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

}

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

}

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

}

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

}
[/code]

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

今回の授業で紹介したプログラムのソースコードは以下からダウンロードしてください。