<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Takepepe.com</title>
	<atom:link href="https://takepepe.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://takepepe.com</link>
	<description>Designer::develop</description>
	<lastBuildDate>Sun, 16 Jun 2013 15:40:24 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Processing #001 LeapMotion x Box2D</title>
		<link>https://takepepe.com/processing-001-leapmotion-x-box2d/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=processing-001-leapmotion-x-box2d</link>
		<comments>https://takepepe.com/processing-001-leapmotion-x-box2d/#comments</comments>
		<pubDate>Sun, 16 Jun 2013 10:32:37 +0000</pubDate>
		<dc:creator>Takepepe</dc:creator>
				<category><![CDATA[Processing]]></category>
		<category><![CDATA[Box2D]]></category>
		<category><![CDATA[LeapMotion]]></category>

		<guid isPermaLink="false">http://takepepe.com/?p=488</guid>
		<description><![CDATA[Processing #001 LeapMotion x Box2D from Takepepe on Vim [...]]]></description>
				<content:encoded><![CDATA[<p><iframe src="http://player.vimeo.com/video/68469264" width="720" height="405" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
<p><a href="http://vimeo.com/68469264">Processing #001 LeapMotion x Box2D</a> from <a href="http://vimeo.com/user16458913">Takepepe</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<p>ご無沙汰してます、Takepepeです。<br />
世間は梅雨、ついにクーラーをかけながらデモムービーを撮る季節に突入しました。<br />
すっかり月１更新になってしまった今日このごろ。反省しないと！</p>
<p>さて、今日は動画のとおり、Leapを使ってお絵描きするインスタレーションをProcessingで作成しました。<br />
今回のキモはLeapSDKをラップしているライブラリから提供される<br />
標準のジェスチャーや座標を、意図した値に変換するところです。</p>
<p>使用しているライブラリは以下</p>
<div class="gray-rect">
JBox2D : <a href="http://www.jbox2d.org" target="_blank">http://www.jbox2d.org</a><br />
BoxWrap2d : <a href="http://wiki.processing.org/w/BoxWrap2d" target="_blank">http://wiki.processing.org/w/BoxWrap2d</a><br />
LeapMotionP5 : <a href="https://github.com/mrzl/LeapMotionP5" target="_blank">https://github.com/mrzl/LeapMotionP5</a>
</div>
<p>コードはEclipseで書いていますので、このままProcessingのIDEにはっつけても動かないのでご注意を。<br />
OPENGLの恩恵を受けていないコードですが、レンダリングモードをOPENGLにしています。<br />
EclipseでProcessingを書く場合、OPENGLの使用にはcore.jarだけではなく<br />
他にも入れないといけない.jarがありますので適当にググってください。</p>
<p>いつもの様に、順をおって解説していきます。</p>
<ol>
<li>LeapPointerP5の作成</li>
<li>Box2Dの初期設定</li>
<li>LeapPointerP5の座標をBox2Dに落とし込む</li>
<li>両手の状態で描画判定</li>
</ol>
<h2>1.LeapPointerP5の作成</h2>
<p>P001っていうのはPAppletを継承したやつです。<br />
コンストラクタに引数として渡して保持しておけば、<br />
Processingの便利APIを外からでも使用できます。</p>
<p>update()は、そのP001からdraw()内で呼ばれるループ関数です。<br />
このクラスは、冒頭で述べたキモにあたる部分を担っています。</p>
<p>執筆時現在の開発環境での話ですが、<br />
前回解説したとおり、LeapはFrameオブジェクトからpointable（指か棒）を取得できますが、<br />
これらをロストした時のカバーを自分でやらなければいけません。</p>
<p>今回のインスタレーションでは、ポインターを１つに限定しています。<br />
getPointable()でそのポインターを固定する処理をして、<br />
ポインターが属する手と反対側の手が「グー」なら「つかんでいる」状態というのを<br />
publicなisGrabbed()から参照出来るようになっています。</p>
<h3>LeapPointerP5.java</h3>
<pre class="brush: java; title: ; notranslate">

import com.leapmotion.leap.Frame;
import com.leapmotion.leap.Pointable;
import com.leapmotion.leap.Hand;
import com.onformative.leap.LeapMotionP5;

public class LeapPointerP5 {
	
	private P001 p;
	
	private LeapMotionP5 leap;
	private Frame frame;
	private Pointable pointable;
	private Hand currentHand;
	private Hand contraryHand;
	
	private boolean isDoubleHand = false;
	private boolean isGrabbing = false;
	private float offset = 6.0f;
	private float x;
	private float y;
	
	public LeapPointerP5(P001 p,LeapMotionP5 leap){
		this.p = p;
		this.leap = leap;
		this.pointable = leap.getFrame().pointables().get(0);
	}

	//------------------------------------------------------------
	// @　Loop
	
	public void update(){
		
		frame = leap.getFrame();
		pointable = getPointable();
		setHands();
		setPosition();

	}
	
	//------------------------------------------------------------
	// @ private 
	
	private Pointable getPointable() {
		
		// 前のフレームで取得したPointableがある場合、前フレームで取得したPointableを返す。無ければ新しいPointableを返す。
		
		Pointable _pointable = frame.pointables().get(0);
		int count = frame.pointables().count();
		for(int i=0 ; i&lt;count ; i++){
			if(pointable.id() == frame.pointables().get(i).id()){
				_pointable = frame.pointables().get(i);
			}
		}
		return _pointable;
	}
	
	private void setHands(){
		
		// pointableの手を格納。手を2つ取得できた場合、反対側の手を格納
		
		if(pointable.isFinger()){
			currentHand = pointable.hand();
			if(frame.hands().count() == 2){
				contraryHand = frame.hands().get(1);
				if(currentHand == contraryHand){
					contraryHand = frame.hands().get(0);
				}
				isDoubleHand = true;
			}else{
				contraryHand = null;
				isDoubleHand = false;
			}
		}
	}
	
	private void setPosition(){
		
		// pointableから、pointerの座標を算出
		
		float px = pointable.tipPosition().getX()*offset;
		float py = pointable.tipPosition().getY()*offset;
		this.x += ((p.width/2 + px) - this.x)/10;
		this.y += ((p.height/0.6 - py) - this.y)/10;
	}
	
	
	//------------------------------------------------------------
	// @ getter
	
	public float getX(){
		return this.x;
	}
	
	public float getY(){
		return this.y;
	}
	
	public boolean isPointed(){
		boolean res = false;
		if(frame.pointables().count() != 0){
			res = true;
		}
		return res;
	}
	
	public boolean isGrabbed(){
		
		// 描画している手と反対側の手が存在しつつ、その手がグーの時trueを返す。
		
		boolean res = false;
		if(isDoubleHand){
			if(!isGrabbing){
				if(contraryHand.fingers().count() == 0){
					res = true;
					isGrabbing = true;
				}
			}else{
				res = true;
				if(contraryHand.fingers().count() == 5){
					res = false;
					isGrabbing = false;
				}
			}
		}
		return res;
	}
	
}

</pre>
<h2>2.Box2Dの初期設定</h2>
<p>ここからはPAppletのコードです。<br />
Box2Dはあんまり新しくはなく、日本語のドキュメントがかなり少ないため苦戦しました。<br />
Physicsという、ProcessingでBox2Dを使用するためのインスタンスを生成し、<br />
それに対して、jbox2dのクラス群を利用していく感じになります。<br />
createPointerBody()ではLeapPointerP5で定義した座標をBox2D上に落とし込むためのインスタンスを生成しています。</p>
<h3>P001.java(抜粋)</h3>
<pre class="brush: java; title: ; notranslate">
	private void createPysics(){
		float gravX = 0.0f;
		float gravY = -100.0f;
		float AABBWidth = 2*width;
		float AABBHeight = 2*height;
		float borderBoxWidth = width+11;
		float borderBoxHeight = height+11;
		physics = new Physics(this, width, height, gravX, gravY, AABBWidth, AABBHeight, borderBoxWidth, borderBoxHeight, pixelsPerMeter);
		physics.setDensity(110.1f);
		physics.setCustomRenderingMethod(this, &quot;myCustomRenderer&quot;);
	}
	
	private void createPointerBody(){
		
		// LeapPointerP5の座標を用いたpointerBodyを生成。
		
		pointerDef = new BodyDef();
		pointerBody = physics.getWorld().createBody(pointerDef);
		pointerCircleDef = new CircleDef();
		pointerCircleDef.radius = 0.5f;
		pointerCircleDef.friction = physics.getFriction();
		pointerCircleDef.restitution = physics.getRestitution();
		pointerCircleDef.isSensor = physics.getSensor();
		pointerBody.createShape(pointerCircleDef);
		pointerBody.setMassFromShapes();
		updatePointerBody();
	}
	
</pre>
<h2>3.LeapPointerP5の座標をBox2Dに落とし込む</h2>
<p>以下のupdatePointerBody()はdraw()内でコールされている関数です。<br />
getColor()は、フレームにあわせて虹色を返してくれる関数です。</p>
<h3>P001.java(抜粋)</h3>
<pre class="brush: java; title: ; notranslate">
	private void updatePointerBody(){
		
		// 描画中は描画が始まった段階のpalletColorを使用。描画中でない時は随時描画色を更新。
		
		UserData data = new UserData();
		if(isDrawing){
			data.color = palletColor;
		}else{
			data.color = getColor();
		}
		pointerBody.setUserData(data);
		
		// Box2Dの座標系に変換しつつpointerBodyを移動。
		
		float x = (pointer.getX() - width/2)/pixelsPerMeter;
		float y = (-pointer.getY() + height/2)/pixelsPerMeter;
		pointerBody.setPosition(new Vec2(x,y));
		
	}
</pre>
<h2>4.両手の状態で描画判定</h2>
<p>LeapPointerP5で反対の手が「グー」と判定された場合、描画中になります。<br />
まず、verticesというArrayListが生成され、<br />
「グー」の間、ポインターの座標をどんどんverticesにつっこんでいき、<br />
「パー」になった時、もしくは反対の手がなくなった時、<br />
描画が終了し、verticesを使用したCircleが新しいbodyとして追加されます。</p>
<h3>P001.java(抜粋)</h3>
<pre class="brush: java; title: ; notranslate">
	private void brushBody(){
		if(isDrawing != pointer.isGrabbed()){
			if(isDrawing){
				onEndDrawing();
			}else{
				onStartDrawing();
			}
		}
		if(isDrawing == pointer.isGrabbed() &amp;&amp; isDrawing){
			onProcessDrawing();
		}

		// 描画中であれば、ProcessingのAPIでラインを描く。（ verticesの数だけellipseを描く ）
		
		if(isDrawing){
			this.fill(palletColor);
			for(int i=0 ; i&lt;vertices.size() ; i++){
				this.ellipse(vertices.get(i).x*pixelsPerMeter+width/2, vertices.get(i).y*-pixelsPerMeter+height/2, 30 ,30);
			}
		}
	}
	
	private void onStartDrawing(){
		
		// 描画が開始した時の処理。 vertices に格納した座標を元に Box2D で body を生成。
		
		isDrawing = true;
		vertices = new ArrayList&lt;Vec2&gt;();
		palletColor = getColor();
	}
	
	private void onProcessDrawing(){
		
		// 描画中の処理。 vertices に格納した座標を格納。
		
		Vec2 point = physics.screenToWorld(pointer.getX(),pointer.getY());
		vertices.add(point);
	}
	
	private void onEndDrawing(){
		
		// 描画が終了した時の処理。verticesに格納した座標を元にBox2Dでbodyを生成。
		
		isDrawing = false;
		BodyDef bd = new BodyDef();
		Body body = physics.getWorld().createBody(bd);
		for(int i =0 ; i&lt;vertices.size() ; i++){
			CircleDef cd = new CircleDef();
			cd.radius = 0.5f;
			cd.density = physics.getDensity();
			cd.friction = physics.getFriction();
			cd.restitution = physics.getRestitution();
			cd.isSensor = physics.getSensor();
			cd.localPosition.set(vertices.get(i));
			body.createShape(cd);
			body.setMassFromShapes();
		}
		
		// 描画したBodyに色を持たせる。必要なメンバはUserDataクラスで定義しておく。
		
		UserData data = new UserData();
		data.color = palletColor;
		body.setUserData(data);
		vertices = null;
	}
</pre>
<h2>P001.java</h2>
<pre class="brush: java; title: ; notranslate">
import java.util.List;
import java.util.ArrayList;

import processing.core.PApplet;

import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.World;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.collision.CircleShape;
import org.jbox2d.collision.CircleDef;
import org.jbox2d.collision.Shape;
import org.jbox2d.collision.ShapeType;
import org.jbox2d.p5.Physics;

import com.onformative.leap.LeapMotionP5;

public class P001 extends PApplet{
	private static final long serialVersionUID = 0;
	
	// @ Box2D (JBox2D + BoxWrap2d)
	// JBox2D : http://www.jbox2d.org/
	// BoxWrap2d : http://wiki.processing.org/w/BoxWrap2d
	private Physics physics;
	private List&lt;Vec2&gt; vertices;
	private float pixelsPerMeter = 30;
	private BodyDef pointerDef;
	private Body pointerBody;
	private CircleDef pointerCircleDef;
	
	//  LeapMotionP5
	//  https://github.com/mrzl/LeapMotionP5
	//  auther @ mrzl
	private LeapMotionP5 leap;
	
	//  LeapPointerP5
	//  auther @ Takeppe
	private LeapPointerP5 pointer;
	
	private int palletColor;
	private boolean isDrawing = false;
	
	//------------------------------------------------------------
	// @ Setup
		
	public void setup(){
		size(2560,1390,OPENGL);
		frameRate(60);
		colorMode(HSB, 360, 100, 100);
		noStroke();
		
		leap = new LeapMotionP5(this);
		pointer = new LeapPointerP5(this,leap);
		
		createPysics();
		createPointerBody();
	}
	
	//------------------------------------------------------------
	// @ Box2D 
	
	private void createPysics(){
		float gravX = 0.0f;
		float gravY = -100.0f;
		float AABBWidth = 2*width;
		float AABBHeight = 2*height;
		float borderBoxWidth = width+11;
		float borderBoxHeight = height+11;
		physics = new Physics(this, width, height, gravX, gravY, AABBWidth, AABBHeight, borderBoxWidth, borderBoxHeight, pixelsPerMeter);
		physics.setDensity(110.1f);
		physics.setCustomRenderingMethod(this, &quot;myCustomRenderer&quot;);
	}
	
	private void createPointerBody(){
		
		// LeapPointerP5の座標を用いたpointerBodyを生成。
		
		pointerDef = new BodyDef();
		pointerBody = physics.getWorld().createBody(pointerDef);
		pointerCircleDef = new CircleDef();
		pointerCircleDef.radius = 0.5f;
		pointerCircleDef.friction = physics.getFriction();
		pointerCircleDef.restitution = physics.getRestitution();
		pointerCircleDef.isSensor = physics.getSensor();
		pointerBody.createShape(pointerCircleDef);
		pointerBody.setMassFromShapes();
		updatePointerBody();
	}
	
	public void myCustomRenderer(World world){
		
		// BoxWrap2d のカスタムレンダラーを使用。参照は以下。
		// http://wiki.processing.org/w/BoxWrap2d#Using_a_custom_renderer
		
		Body body;
		for (body = world.getBodyList(); body != null; body = body.getNext()) {
			Shape shape;
			for (shape = body.getShapeList(); shape != null; shape = shape.getNext()) {
				ShapeType st = shape.getType();
				if (st == ShapeType.POLYGON_SHAPE) {
				}else if (st == ShapeType.CIRCLE_SHAPE) {
					UserData data = (UserData)shape.getBody().getUserData();
					if(data != null){
						fill(data.color);
					}
					CircleShape circle = (CircleShape) shape;
					Vec2 pos = physics.worldToScreen(body.getWorldPoint(circle.getLocalPosition()));
					float radius = physics.worldToScreen(circle.getRadius());
					this.ellipseMode(CENTER);
					this.ellipse(pos.x, pos.y, radius*2, radius*2);
				}
			}
		}
	}
	
	//------------------------------------------------------------
	// @ Loop
	
	@Override
	public void draw() {
		this.background(0);
		pointer.update();
		updatePointerBody();
		brushBody();
	}
	
	private void updatePointerBody(){
		
		// 描画中は描画が始まった段階のpalletColorを使用。描画中でない時は随時描画色を更新。
		
		UserData data = new UserData();
		if(isDrawing){
			data.color = palletColor;
		}else{
			data.color = getColor();
		}
		pointerBody.setUserData(data);
		
		// Box2Dの座標系に変換しつつpointerBodyを移動。
		
		float x = (pointer.getX() - width/2)/pixelsPerMeter;
		float y = (-pointer.getY() + height/2)/pixelsPerMeter;
		pointerBody.setPosition(new Vec2(x,y));
		
	}
	
	private void brushBody(){
		if(isDrawing != pointer.isGrabbed()){
			if(isDrawing){
				onEndDrawing();
			}else{
				onStartDrawing();
			}
		}
		if(isDrawing == pointer.isGrabbed() &amp;&amp; isDrawing){
			onProcessDrawing();
		}

		// 描画中であれば、ProcessingのAPIでラインを描く。（ verticesの数だけellipseを描く ）
		
		if(isDrawing){
			this.fill(palletColor);
			for(int i=0 ; i&lt;vertices.size() ; i++){
				this.ellipse(vertices.get(i).x*pixelsPerMeter+width/2, vertices.get(i).y*-pixelsPerMeter+height/2, 30 ,30);
			}
		}
	}
	
	private void onStartDrawing(){
		
		// 描画が開始した時の処理。 vertices に格納した座標を元に Box2D で body を生成。
		
		isDrawing = true;
		vertices = new ArrayList&lt;Vec2&gt;();
		palletColor = getColor();
	}
	
	private void onProcessDrawing(){
		
		// 描画中の処理。 vertices に格納した座標を格納。
		
		Vec2 point = physics.screenToWorld(pointer.getX(),pointer.getY());
		vertices.add(point);
	}
	
	private void onEndDrawing(){
		
		// 描画が終了した時の処理。verticesに格納した座標を元にBox2Dでbodyを生成。
		
		isDrawing = false;
		BodyDef bd = new BodyDef();
		Body body = physics.getWorld().createBody(bd);
		for(int i =0 ; i&lt;vertices.size() ; i++){
			CircleDef cd = new CircleDef();
			cd.radius = 0.5f;
			cd.density = physics.getDensity();
			cd.friction = physics.getFriction();
			cd.restitution = physics.getRestitution();
			cd.isSensor = physics.getSensor();
			cd.localPosition.set(vertices.get(i));
			body.createShape(cd);
			body.setMassFromShapes();
		}
		
		// 描画したBodyに色を持たせる。必要なメンバはUserDataクラスで定義しておく。
		
		UserData data = new UserData();
		data.color = palletColor;
		body.setUserData(data);
		vertices = null;
	}
	
	private int getColor(){
		int hue = this.frameCount%360;
		return this.color(hue,100,100);
	}

	//------------------------------------------------------------
	// @ KeybordEvent
		
	@Override
	public void keyPressed(){
		
		// 初期化処理
		
		World world = physics.getWorld();
		if(keyCode == 32){
			Body body;
			for (body = world.getBodyList(); body != null; body = body.getNext()) {
				world.destroyBody(body);
			}
			physics.destroy();
			createPysics();
			createPointerBody();
		}
	}
	
}

</pre>
<p>余談ですが、先日Processingが2になりましたね！<br />
LeapSDKもバージョンアップがあった様子ですが、ちゃんと見れてません。<br />
（というか、ライブラリ頼みなのでバージョンあがっても対応できないという…むむむ）</p>
]]></content:encoded>
			<wfw:commentRss>https://takepepe.com/processing-001-leapmotion-x-box2d/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>LeapMotion #001 Starling</title>
		<link>https://takepepe.com/leapmotion-001-starling/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=leapmotion-001-starling</link>
		<comments>https://takepepe.com/leapmotion-001-starling/#comments</comments>
		<pubDate>Mon, 06 May 2013 11:50:42 +0000</pubDate>
		<dc:creator>Takepepe</dc:creator>
				<category><![CDATA[Flash]]></category>
		<category><![CDATA[LeapMotion]]></category>
		<category><![CDATA[Starling]]></category>

		<guid isPermaLink="false">http://takepepe.com/?p=426</guid>
		<description><![CDATA[LeapMotion #001 Starling from Takepepe on Vimeo. １ヶ月ぶりの [...]]]></description>
				<content:encoded><![CDATA[<p><iframe src="http://player.vimeo.com/video/65551480" width="720" height="405" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
<p><a href="http://vimeo.com/65551480">LeapMotion #001 Starling</a> from <a href="http://vimeo.com/user16458913">Takepepe</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<p>１ヶ月ぶりの投稿です。GW最終日、皆様いかがお過ごしですか？<br />
今回の投稿は、個人的に今年最も気になるガジェット「LeapMotion」についてです。</p>
<h6><a href="https://www.leapmotion.com/" target="_blank">・Leap Motion</a></h6>
<p><img src="http://takepepe.com/wp-content/uploads/2013/05/leap_logo.png" /></p>
<p>LeapMotionは端末の上1m四方空間内で、1ミリ単位で指やツール（ペン等）を検知するセンサーです。<br />
また、SDKレベルで数種類のジェスチャーを認識します。</p>
<p>つい先日、プレオーダーした人への発送延期がアナウンスされたばかりですが、<br />
自分は3月末にプレオーダー、デベロッパー登録したところ、2週間ほどで手元に届きました。<br />
発送延期の理由としては、完璧な状態として世に送り出したい、というような内容でした。<br />
7月22日が発送予定日とされていますが、実際どうなるかはまだわかりませんね。</p>
<p>まだ日本語のリソースが少ない状態ですので、コード解説にはいる前に<br />
DeveloperPortalの概要を写したものを交えて、簡単に解説したいと思います。</p>
<h2>Frame</h2>
<p>LeapMotionで値を取得するために使用するFrameオブジェクトの中には以下のものが含まれます。</p>
<h6>・Lists of tracking data</h6>
<div class="gray-rect">
Hands — すべての手<br />
Pointables — Pointableオブジェクトとして、すべての指とツール<br />
Fingers — すべての指<br />
Tools — すべてのツール<br />
Gestures — 開始、更新、終了のハンドラーを含むすべてのジェスチャー</div>
<h6>・Frame motion</h6>
<div class="gray-rect">
Rotation Axis — 回転軸を表現する方向ベクトル。<br />
Rotation Angle — 回転軸(右手の法則を使用して)のまわりで右回りの回転角。<br />
Rotation Matrix — 回転を表現するtransformマトリックス。<br />
Scale Factor — 拡大縮小を表現するファクター。<br />
Translation — 直線運動を表現するベクトル。
</div>
<h2>Hand model</h2>
<p>手について様々な情報を提供します。2本の手を認識しますが、右手左手の識別はしていません。</p>
<h6>・Hand attributes</h6>
<div class="gray-rect">
Palm Position — Leapの起点から計測した手のひら中心座標<br />
Palm Velocity — 秒速ミリメートル単位の、手のひらの移動速度<br />
Palm Normal —  手のひらの中心から、下方へ指す垂直方向のベクトル<br />
Direction — 手のひらの中心から、指へ向かうベクトル。<br />
Sphere Center — 手の屈曲に適当な球体の中心。(手でボールを持っているような感じ)<br />
Sphere Radius — 手の屈曲に適当な球体の半径。半径は手の形とともに変化します。
</div>
<h6>・Hand motion</h6>
<div class="gray-rect">
Rotation Axis — 回転軸を表現する方向ベクトル。<br />
Rotation Angle — 回転軸(右手の法則を使用して)のまわりで右回りの回転角。<br />
Rotation Matrix —回転を表現するtransformマトリックス。<br />
Scale Factor — 拡大縮小を表現するファクター。<br />
Translation — 直線運動を表現するベクトル。
</div>
<h6>・Finger and Tool lists</h6>
<p>検知した手に属する、指やツールの情報を取得できます。</p>
<div class="gray-rect">
Pointables — Pointableオブジェクトとして、すべての指とツール<br />
Fingers — すべての指<br />
Tools — すべてのツール
</div>
<h2>Finger and Tool models</h2>
<p>Leapはその視界内の指およびツールの両方を検知するおよび追跡します。</p>
<div class="gray-rect">
Length — オブジェクト(手から先端に及ぶ)の可視部の長さ。<br />
Width — オブジェクトの可視部の平均幅。<br />
Direction — オブジェクト(つまり基礎から先端まで)と同じ方角に指すユニット方向ベクトル。<br />
Tip Position — Leapの起点から計測した指先の座標<br />
Tip Velocity — 秒速ミリメートル単位の、指先の移動速度
</div>
<h2>Gestures</h2>
<p>Leapは特定の移動パターンをGesturesとして認識します。</p>
<div class="gray-rect">
Circle — 円をトレースする単一の指。<br />
Swipe — 手の直線運動。<br />
Key Tap — キーボード・キーを軽く打つかのような指の動作。<br />
Screen Tap — コンピューター・スクリーンを軽く打つかのような指の動作。
</div>
<h6>開発環境について</h6>
<p>現在サポートされている言語は、C++、C#、Objective-C、Java、Python、JavaScriptになります。<br />
DevelperPortalでは前述の概要の他に、各フレームワークに対応したライブラリの配布、<br />
ガイドライン、コミュニティ、アプリストア概要などが掲載されています。<br />
Leapに興味があるかたは是非覗いてみてください。</p>
<h6><a href="https://developer.leapmotion.com/documentation/guide/Leap_Overview">・Leap Motion Developer Portal</a></h6>
<h2>LeapMotion × Adobe AIR Starling</h2>
<p>
今回作成したデモについての解説です。<br />
AdobeAIRでStarlingのParticleSystemを使用して作成しています。
</p>
<h6><a href="http://gamua.com/starling/" target="_blank">・Starling Framework</a><br />
<a href="https://github.com/PrimaryFeather/Starling-Extension-Particle-System" target="_blank">・Starling-Extension-Particle-System</a></h6>
<p>
StarlingはStage3Dを使用しているのでapp.xmlに以下の設定を忘れずにしましょう。
</p>
<div class="gray-rect">
&lt;renderMode&gt;direct&lt;/renderMode&gt;<br />
&lt;depthAndStencil&gt;false&lt;/depthAndStencil&gt;
</div>
<p>
LeapをActionScriptで使用するために、以下のライブラリと付属のC++SDKをラップしているANEを使用します。<br />
導入方法については、下記ページの下部に書いてあるので、それぞれ開発環境にあったものを選んでください。<br />
自分はFlashBuilder4.7でした。
</p>
<h6><a href="https://github.com/logotype/LeapMotionAS3" target="_blank">・LeapMotionAS3</a></h6>
<p>
Starlingを使用したパーティクルのデモは<a href="http://clockmaker.jp/blog/" target="_blank">Clockmakerさん</a>や<a href="http://www.project-nya.jp/" target="_blank">にゃあプロジェクトさん</a>が充実していますので、<br />
そちらを参考にさせていただきました。
</p>
<h3>FL001.as</h3>
<pre class="brush: as3; title: ; notranslate">
package {
	
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageDisplayState;
	import flash.display.StageScaleMode;
	import flash.events.KeyboardEvent;
	import starling.core.Starling;
	
	[SWF(backgroundColor=&quot;#00164a&quot;, width=&quot;1440&quot;, height=&quot;900&quot;, frameRate=&quot;60&quot;)]
	
	public class FL001 extends Sprite {
		
		private var starling:Starling;
		
		public function FL001() {
			
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
			
			starling = new Starling(MainView, stage);
			starling.start();
			
		}
		
		private function onKeyDown(e:KeyboardEvent):void {
			
			if(stage.displayState == StageDisplayState.NORMAL){
				stage.displayState = StageDisplayState.FULL_SCREEN;
			} else {
				stage.displayState = StageDisplayState.NORMAL
			}
			
		}
		
		
	}
}

</pre>
<h3>MainView.as</h3>
<pre class="brush: as3; title: ; notranslate">
package {
	
	import flash.geom.Rectangle;
	import starling.core.Starling;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.events.ResizeEvent;
	import starling.extensions.ParticleDesignerPS;
	import starling.textures.Texture;
	import com.leapmotion.leap.LeapMotion;
	import com.leapmotion.leap.Pointable;
	import com.leapmotion.leap.events.LeapEvent;
	
	internal class MainView extends Sprite { 
		
		// 事前に「online particle editor」で.pexファイルとtexture.pngを用意しておきます
		// http://onebyonedesign.com/flash/particleeditor/
		
		[Embed(source = &quot;assets/particle.pex&quot;, mimeType = &quot;application/octet-stream&quot;)]
		private static var ParticleData:Class;
		
		[Embed(source = &quot;assets/texture.png&quot;)]
		private static var ParticleImage:Class;
		
		private var particles:Vector.&lt;ParticleDesignerPS&gt;;		
		private var count:int = 10;
		private var leap:LeapMotion;
		
		public function MainView() {
			addEventListener(Event.ADDED_TO_STAGE, onAddStage);
		}
		
		private function onAddStage(e:Event):void {
			
			setParticles();
			leap = new LeapMotion();
			leap.controller.addEventListener( LeapEvent.LEAPMOTION_FRAME, onLeapFrame );
			stage.addEventListener(ResizeEvent.RESIZE, onResizeStage);
			
		}
		
		private function setParticles():void{
			
			particles = new Vector.&lt;ParticleDesignerPS&gt;(10);
			for(var i:int = 0; i&lt;count ; i++){
				particles[i] = new ParticleDesignerPS( XML(new ParticleData()), Texture.fromBitmap(new ParticleImage()));
				particles[i].startSize = 100;
				particles[i].endSize = 100;
				particles[i].speed = 0;
				particles[i].start();
				Starling.juggler.add(particles[i]);
				addChild(particles[i]);
			}
			
		}
		
		private function onResizeStage(e:ResizeEvent):void {
			
			Starling.current.viewPort = new Rectangle(0, 0, e.width, e.height);
			stage.stageWidth = e.width;
			stage.stageHeight = e.height;
			
		}
		
		private function onLeapFrame(e:LeapEvent):void {
			
			var max:int = e.frame.pointables.length;
			for(var i:int = 0; i&lt;count ; i++){
				if( i &lt; max ){
					var pointable:Pointable = e.frame.pointables[i];
					particles[i].emitterX = (pointable.tipPosition.x)*3+stage.stageWidth/2;
					particles[i].emitterY = (pointable.tipPosition.y)*-3+stage.stageHeight;
					particles[i].gravityX = pointable.tipVelocity.x*-5;
					particles[i].gravityY = pointable.tipVelocity.y*5;
					particles[i].maxNumParticles = 50;
				}else{
					particles[i].maxNumParticles = 1;
				}
			}
			
		}
		
	}
}

</pre>
<p>１ヶ月いじった上で得た感想（というかぶちあたっている壁）です。</p>
<ol>
<li>指がLeapに向かって垂直に重なると検知できない。</li>
<li>細かなUI操作に向いていない</li>
<li>ジェスチャーはそのままでは利用しづらい</li>
</ol>
<p>
あくまで、現状公開されているSDKとライブラリを使用した上での感想です。<br />
概要を読む限りではワクワクするばかりですが、ちゃんとアプリケーションに落とし込むためには<br />
上記の問題をカバーしたフレームワークが必要だなと思いました。
</p>
<p>
今回の投稿はさらっと紹介しただけになってしまったので、<br />
次回はちょっとしたフレームワークと何か面白いネタで投稿できればいいなー。</p>
]]></content:encoded>
			<wfw:commentRss>https://takepepe.com/leapmotion-001-starling/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Flash x iOS #001 Away3D</title>
		<link>https://takepepe.com/flash-x-ios-001-away3d/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=flash-x-ios-001-away3d</link>
		<comments>https://takepepe.com/flash-x-ios-001-away3d/#comments</comments>
		<pubDate>Wed, 27 Mar 2013 16:21:20 +0000</pubDate>
		<dc:creator>Takepepe</dc:creator>
				<category><![CDATA[Flash]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Away3D]]></category>
		<category><![CDATA[frocessing]]></category>
		<category><![CDATA[OSC]]></category>
		<category><![CDATA[tuio]]></category>

		<guid isPermaLink="false">http://takepepe.com/?p=390</guid>
		<description><![CDATA[Flash x iOS #001 Away3D from Takepepe on Vimeo. 久々の投稿です [...]]]></description>
				<content:encoded><![CDATA[<p><iframe src="http://player.vimeo.com/video/62793330" width="720" height="405" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
<p><a href="http://vimeo.com/62793330">Flash x iOS #001 Away3D</a> from <a href="http://vimeo.com/user16458913">Takepepe</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<p>久々の投稿です。<br />
桜が満開ですね！今年は多忙につき花見はお預けな感じです。</p>
<p>今回はAway3Dを使ったデスクトップAIRアプリです。<br />
OSC通信でiPadから送られて来たタッチ座標と端末の傾きで、指の位置を3D空間に変換しています。<br />
レインボーな箱がウネウネとしています。特に意味はありません。</p>
<p>iPadからOSC送信するアプリはopenFrameWorksで作ったものです。<br />
早速見て行きましょう。</p>
<ol>
<li>iPadからOSC送信</li>
<li>FlashでOSC受信</li>
<li>受信した値から指の位置をシュミレート</li>
<li>レインボーな箱をウネウネ</li>
</ol>
<h2>1.iPadからOSC送信</h2>
<p>
iPadからOSC送信します。<br />
いつもの様にemptyExampleをコピペしたら、「iOS Application Target」の「Devices」を<br />
「iPad」か「Universal」にします。<br />
FI001::setup()で画面の向きをiPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_LEFT);<br />
にして、ホームボタンが右に来たときに左上が(0,0)になるようにします。</p>
<p>iPadでは同時に5つのタッチ座標が取得できます。<br />
5つの座標を格納できる「touchLoc」を用意し、タッチが5つに満たない場合、<br />
(-100,-100)を入れる様にしておき、常に5つ分の座標を送ります。
</p>
<p>
※Supported Intereface Orientationsを「Portrait」にしていますが、<br />
これを「LandscapeRight」にしながら、左上を(0,0)にする方法が分からず…知ってる人いたら教えてください！
</p>
<h3>FI001.cpp</h3>
<pre class="brush: cpp; title: ; notranslate">
void FI001::setup(){
    
    ofBackground(127,127,127);
    
    //iPadの向きの設定
    iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_LEFT);
    
    //加速度センサーを有効に
	ofxAccelerometer.setup();
    
    //ofxiPhoneCoreLocationインスタンスのコンパスを有効に
    location.startHeading();
    
    //OSC設定
    sender.setup( HOST, PORT );
	receiver.setup( MYPORT );
    
    //初期設定
    for (int i=0; i&lt;5; i++) {
        touchLoc[i].set(-100, -100);
    }
    
}

//--------------------------------------------------------------
void FI001::update(){
    
    ofxOscMessage m;
    m.setAddress(&quot;/iOSOSC&quot;);
    
    //タッチ位置の格納
    for (int i=0; i&lt;5; i++) {
        m.addIntArg(touchLoc[i].x);
        m.addIntArg(touchLoc[i].y);
    }
    
    //加速度
    m.addFloatArg(ofxAccelerometer.getForce().x);
    m.addFloatArg(ofxAccelerometer.getForce().y);
    m.addFloatArg(ofxAccelerometer.getForce().z);
    
    //X軸の傾き
    m.addFloatArg(ofxAccelerometer.getOrientation().y);
    
    //Y軸の傾き（コンパスの向き）
    m.addFloatArg(location.getMagneticHeading());
    
    //Z軸の傾き
    m.addFloatArg(ofxAccelerometer.getOrientation().x);
    
    sender.sendMessage( m );
    
}
</pre>
<h2>2.FlashでOSC受信</h2>
<h3>OSC.as</h3>
<p>
FlashでOSC通信をする場合、AIRアプリに限定される様です。（UDPConnectorあたり）<br />
ライブラリはtuio-as3を使っています。</p>
<h4>tuio-as3</h4>
<p><a href="https://code.google.com/p/tuio-as3-lib/" target="_blank">https://code.google.com/p/tuio-as3-lib/</a></p>
<p>
openFrameWorksから送られて来たOSCメッセージを連想配列にまとめて<br />
メインスレッド（FI001.as）に渡します。<br />
タッチ座標(-100,-100)が送られて来た場合、非表示の真偽値をセットします。
</p>
<pre class="brush: as3; title: ; notranslate">
package {
	
	import flash.display.*;
	import flash.geom.*;
	import org.tuio.connectors.UDPConnector;
	import org.tuio.osc.IOSCListener;
	import org.tuio.osc.OSCManager;
	import org.tuio.osc.OSCMessage;
	
	public class OSC implements IOSCListener {
		
		private var delegate:FI001;
		private var fingers:Vector.&lt;Object&gt;;
		private var accelerometer:Vector.&lt;Number&gt;;
		private var orientation:Vector.&lt;Number&gt;;
		private var oscManager:OSCManager;
		
		public function OSC (_delegate:FI001) {
			
			delegate = _delegate;
			
			//タッチ情報
			fingers = new Vector.&lt;Object&gt;(5,true);
			for(var i:int = 0 ; i&lt;5 ; i++){
				fingers[i] = new Object();
			}
			
			//加速度と傾き
			accelerometer = new Vector.&lt;Number&gt;(3,true);
			orientation = new Vector.&lt;Number&gt;(3,true);
			for(var j:int = 0 ; j&lt;3 ; j++){
				accelerometer[j] = 0;
				orientation[j] = 0;
			}
			
			// OSC設定　※ UDPConnectorはAIRアプリでのみ利用出来る
			var connectorIn:UDPConnector = new UDPConnector(&quot;192.168.11.20&quot;,8000);
			var connectorOut:UDPConnector = new UDPConnector(&quot;192.168.11.18&quot;,8001,false);
			oscManager = new OSCManager(connectorIn,connectorOut,true);
			oscManager.addMsgListener (this);
			
		}

		public function acceptOSCMessage (oscmsg:OSCMessage):void {
			
			//OSCメッセージを受信したら、delegateに連想配列を渡す
			
			if(oscmsg.address == &quot;/iOSOSC&quot;) {

				//タッチ情報
				for(var i:int = 0 ; i&lt;5 ; i++){
					fingers[i].x = oscmsg.arguments[i*2];
					fingers[i].y = oscmsg.arguments[i*2+1];
					fingers[i].visible = true;
					if(fingers[i].x &lt; 0){
						fingers[i].visible = false;
					}
				}
				
				//加速度と傾き
				accelerometer[0] = oscmsg.arguments[10];
				accelerometer[1] = oscmsg.arguments[11];
				accelerometer[2] = oscmsg.arguments[12];
				orientation[0] = oscmsg.arguments[13];
				orientation[1] = oscmsg.arguments[14];
				orientation[2] = oscmsg.arguments[15];
				
				//まとめ
				var data:Object = new Object();
				data.fingers = fingers;
				data.accelerometer = accelerometer;
				data.orientation = orientation;

				delegate.onMessage (data);
				
			}
		}
		
		public function sendOSCMessage ():void { }
		public function sendOSCBundle ():void { }

	}

}
</pre>
<h2>3.受信した値から指の位置をシュミレート</h2>
<h3>FI001.as</h3>
<p>
ここからAway3Dを使ってコーディングしていきます。<br />
Stage3Dを使ったAIRアプリは「app.xml」の</p>
<p class="gray-rect">
&lt;renderMode&gt;direct&lt;/renderMode&gt;<br />
&lt;depthAndStencil&gt;true&lt;/depthAndStencil&gt;
</p>
<p>を設定し忘れる「context3Dなんて知らないよ」って怒られるので注意。<br />
ブラウザで見るswfの場合は、htmlのwmodeをdirectにしましょう。<br />
Away3Dは現状で最新の「4.1.0 Alpha」を使います。
</p>
<h5>Away3D 4.1.0 Alpha</h5>
<p><a href="http://away3d.com/download/away3d_4.1.0_alpha" target="_blank">http://away3d.com/download/away3d_4.1.0_alpha</a></p>
<pre class="brush: as3; title: ; notranslate">
package {
	
	import away3d.cameras.Camera3D;
	import away3d.containers.Scene3D;
	import away3d.containers.View3D;
	import away3d.lights.DirectionalLight;
	import away3d.materials.lightpickers.StaticLightPicker;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Vector3D;
	import flash.system.System;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	
	[SWF(backgroundColor=&quot;#000717&quot;, width=&quot;1024&quot;, height=&quot;768&quot;, frameRate=&quot;60&quot;)]
	
	public class FI001 extends Sprite{
		
		// OSC通信で使うインスタンス
		private var osc:OSC;
		private var tf:TextField;
		
		// Away3Dでお決まりのインスタンス
		private var view:View3D;
		private var scene:Scene3D;
		private var camera:Camera3D;
		private var light:DirectionalLight;
		private static var ZERO:Vector3D = new Vector3D(0,0,0);
		
		// Away3Dで使うインスタンス
		private var fingers:Vector.&lt;Finger&gt;;
		
		// iPadの傾きを格納する変数
		private var ox:Number = 0;
		private var oy:Number = 0;
		private var oz:Number = 0;		

		public function FI001(){
			
			// ガベージコレクション設定（以下参考）
			// http://adclounge.jp/adobe-max-2011/new-small-features-in-flash-player-11/
			System.pauseForGCIfCollectionImminent(1);
			
			// OSCメッセージを受信したらデータを渡してくれるインスタンス
			osc = new OSC(this);
			
			// Away3Dの設定
			init3D();
			
			// 3Dオブジェクトの設定
			set3D();
			
			// デバッグ用テキスト（OSC受信した値表示用）
			setText();
			
		}
		
		private function init3D():void{
			
			// Away3Dの設定
			view = new View3D();
			scene = view.scene;
			light = new DirectionalLight();
			light.direction = new Vector3D(1, -1, 1);
			light.specular = 0.1;
			light.diffuse = 0.9;
			light.ambient = 0.1;
			scene.addChild(light);
			camera = view.camera;
			camera.z = -500;
			
			addChild(view);
			
		}
		
		private function set3D():void {
			
			var lightPicker:StaticLightPicker = new StaticLightPicker([light]);
			
			// タッチ座標に応じてCubeを表示するFingerインスタンス
			fingers = new Vector.&lt;Finger&gt;(5,true);
			for (var i:int = 0; i &lt; 5; i++) {
				var finger:Finger = new Finger();
				fingers[i] = finger;
				for(var j:int = 0 ; j&lt;fingers[i].cubes.length ; j++){
					scene.addChild(fingers[i].cubes[j].mesh);
					fingers[i].setLightPicker(lightPicker);
				}
			}
			
			// アニメーションループ
			addEventListener(Event.ENTER_FRAME, update, false, 0, true);
			
		}
		
		private function setText():void{
			
			// デバッグ用テキスト（OSC受信した値表示用）
			tf = new TextField();
			tf.textColor = 0xFFFFFF;
			tf.autoSize = TextFieldAutoSize.LEFT;
			addChild(tf);
			
		}
		
		private function update(e:Event):void {
			
			// アニメーションループ
			for (var i:int = 0; i &lt; 5; i++) {
				fingers[i].update();
			}
			view.render();
			
		}
		
		
		
		public function onMessage(_data:Object):void{
			
			// ローパスフィルターを通してセンサーの傾きを格納
			ox = ox*0.9 + (_data.orientation[0]+180)*0.1;
			oy = oy*0.9 + (_data.orientation[1])*0.1;
			oz = oz*0.9 + (_data.orientation[2]+180)*0.1;
			
			// 画面の中心
			var cx:Number = -stage.width/2;
			var cy:Number = -stage.height/2;
			
			for(var i:int = 0 ; i&lt;_data.fingers.length ; i++){
				
				var fx:Number = cx+_data.fingers[i].x;
				var fy:Number = cy+_data.fingers[i].y;
				var x:Number = -fx*Math.cos((oz)*Math.PI/180);
				var y:Number = -fy*Math.sin((ox)*Math.PI/180);
				var z:Number = fy*Math.cos((ox)*Math.PI/180);-fx*Math.cos((oy)*Math.PI/180);
				
				// iPadの傾きとタッチ位置から座標を特定
				fingers[i].vec.setTo(x,y,z);
				
				// 触っていなかったら非表示
				fingers[i].setVisible(_data.fingers[i].visible);
			}
			
			setMessageTet();
			
		}
		
		private function setMessageTet():void{
			
			// デバッグ用テキスト（OSC受信した値表示用）
			var str:String = &quot;&quot;;
			for(var i:int = 0 ; i&lt;fingers.length ; i++){
				str += &quot;finger&quot;+(i+1)+&quot;X:&quot;+fingers[i].vec.x+&quot;\n&quot;;
				str += &quot;finger&quot;+(i+1)+&quot;Y:&quot;+fingers[i].vec.y+&quot;\n&quot;;
			}
			str += &quot;orientationX:&quot;+ox+&quot;\n&quot;;
			str += &quot;orientationY:&quot;+oy+&quot;\n&quot;;
			str += &quot;orientationZ:&quot;+oz+&quot;\n&quot;;
			tf.text = str;
			
		}
		
	}
}
</pre>
<h2>4.レインボーな箱をウネウネ</h2>
<p>
メインスレッドで利用しているクラスは以下の通りです。<br />
レインボー色を出すのにfrocessingを使って楽しています。
</p>
<h5>frocessing</h5>
<p><a href="http://www.libspark.org/wiki/nutsu/Frocessing" target="_blank">http://www.libspark.org/wiki/nutsu/Frocessing</a></p>
<h3>Finger.as</h3>
<pre class="brush: as3; title: ; notranslate">
package {
	
	import flash.geom.Point;
	import flash.geom.Vector3D;
	import frocessing.color.ColorHSV;
	import away3d.materials.lightpickers.StaticLightPicker;
	
	public class Finger{
		
		public var vec:Vector3D;
		public var cubes:Vector.&lt;Cube&gt;;
		private var index:int = 0;
		private static var NUM:Number = 50;
		
		public function Finger(){
			
			// 指の疑似3次元座標
			vec = new Vector3D;
			
			// ウネウネするCube達
			cubes = new Vector.&lt;Cube&gt;(NUM);
			for(var i:int = 0; i&lt;NUM ; i++){
				var color:ColorHSV = new ColorHSV(360/NUM*i,0.4);
				cubes[i] = new Cube(1,1,1,color.value);
			}
			
		}
		
		public function setLightPicker(_lp:StaticLightPicker):void{
			
			// Cube達に光源設定
			for(var i:int = 0; i&lt;NUM ; i++){
				cubes[i].setLightPicker(_lp);
			}
			
		}
		
		public function setVisible(_visible:Boolean):void{
			
			// iPadを触っていない場合非表示
			for(var i:int = 0; i&lt;NUM ; i++){
				cubes[i].mesh.visible = _visible;
			}
			
		}
		
		
		
		public function update():void{
			
			// 操作するCubeのIndex
			index++;
			if(index&gt;=NUM){
				index = 0;
			}
			
			// 順番が回って来たCubeが触ってる場所に移動する
			cubes[index].setLife(100);
			cubes[index].setVector(vec.x,vec.y,vec.z);
			
			// Cube達を更新
			for(var i:int = 0; i&lt;NUM ; i++){
				cubes[i].update();
			}
			
		}
		
	}
}
</pre>
<h3>Cube.as</h3>
<pre class="brush: as3; title: ; notranslate">
package {

	import away3d.entities.Mesh;
	import away3d.materials.ColorMaterial;
	import away3d.materials.lightpickers.StaticLightPicker;
	import away3d.primitives.CubeGeometry;
	
	public class Cube{
		
		public var mesh:Mesh;
		private var life:Number = 0;
		private var isSurvive:Boolean = false;
		
		public function Cube(_width:Number,_height:Number,_depth:Number,_color:uint){
			
			// CubeMeshの生成
			var geo:CubeGeometry = new CubeGeometry(_width,_height,_depth);
			var mat:ColorMaterial = new ColorMaterial(_color,0.8);
			mesh = new Mesh(geo,mat);
			
			// 影を落とすかどうか
			mesh.castsShadows=true;

		}
		
		public function setVector(_x:Number,_y:Number,_z:Number):void{

			mesh.x = _x;
			mesh.y = _y;
			mesh.z = _z;
			
		}
		
		public function setLightPicker(_lp:StaticLightPicker):void{
			
			mesh.material.lightPicker = _lp;
			
		}
		
		public function setLife(_life:Number):void{
			
			life = _life;
			
		}
		
		public function update():void{
			
			mesh.rotationX = Math.sin(life*Math.PI/180)*500;
			mesh.rotationY = Math.cos(life*Math.PI/180)*500;
			mesh.rotationZ = Math.cos(life*Math.PI/180)*500;
			
			var scale:Number = Math.pow(life,4)/2000000;
			mesh.scaleX = scale;
			mesh.scaleY = scale;
			mesh.scaleZ = scale;
			
			life--;
			
		}
		
	}
}
</pre>
]]></content:encoded>
			<wfw:commentRss>https://takepepe.com/flash-x-ios-001-away3d/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>openFrameworks x Arduino #001</title>
		<link>https://takepepe.com/openframeworks-x-arduino-001-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=openframeworks-x-arduino-001-2</link>
		<comments>https://takepepe.com/openframeworks-x-arduino-001-2/#comments</comments>
		<pubDate>Fri, 15 Mar 2013 15:28:06 +0000</pubDate>
		<dc:creator>Takepepe</dc:creator>
				<category><![CDATA[Arduino]]></category>
		<category><![CDATA[openFrameworks]]></category>
		<category><![CDATA[ofArduino]]></category>
		<category><![CDATA[ofNoise]]></category>
		<category><![CDATA[ofxSimpleGuiToo]]></category>
		<category><![CDATA[PerlinNoise]]></category>

		<guid isPermaLink="false">http://takepepe.com/?p=332</guid>
		<description><![CDATA[openFrameworks x Arduino #001 from Takepepe on Vimeo. そ [...]]]></description>
				<content:encoded><![CDATA[<p><iframe src="http://player.vimeo.com/video/61887524" width="720" height="405" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
<p><a href="http://vimeo.com/61887524">openFrameworks x Arduino #001</a> from <a href="http://vimeo.com/user16458913">Takepepe</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<p>そろそろこのアバターの格好で記事投稿するのが辛くなり始める今日この頃。<br />
皆様いかがお過ごしでしょうか。</p>
<p>今回はopenFrameworksをArduinoでコントロールするネタです。（Arduinoはほぼオマケですが…）<br />
動画ではひらひら揺らめく面と、再生している音楽が同期しています。<br />
赤外線センサーに手を近づけて、面の揺らめきと音楽のスピード・音量をコントロールします。</p>
<p>ArduinoはINとOUTを備えたマイコンボードの統合開発環境です。<br />
一般的なPCやガジェットには無いセンサーをコンテンツに取り込むだけでなく、<br />
LED、モーターや家電のコントロールも出来る素敵インターフェースです。</p>
<p><img src="/wp-content/uploads/2013/03/arduino.png" alt="Arduino" /></p>
<h6>Arduino　<a href="http://www.arduino.cc/" target="_blank">http://www.arduino.cc/</a></h6>
<p>Arduinoはボードにプログラムを書き込み、電源を供給すればスタンドアロンでも動かす事ができます。<br />
Processing・openFrameworksでも簡単に相互通信できるAPIが用意されています。<br />
とても面白いプラットフォームなのでいろんな記事を調べてみてください。</p>
<p>いつもの様に制作過程の流れで説明していきます。</p>
<ol>
<li>Arduinoボードの設定</li>
<li>openFrameworksとArduinoをつなぐ</li>
<li>ofxSimpleGuiTooを追加</li>
<li>3D空間にofCircleを描く</li>
<li>PerlinNoiseでゆらゆら</li>
<li>音楽を再生</li>
<li>Arduinoの値を摘要</li>
</ol>
<h2>1.Arduinoボードの設定</h2>
<p>今回のインスタレーションでは赤外線測距モジュール(GP2Y0A21)を使っています。<br />
参考というか、出版されている書籍に載っているものをそのままですw<br />
「5章 レシピ2：距離を測りたい」</p>
<h6>Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ<br />
<a href="http://www.oreilly.co.jp/books/9784873114538/" target="_blank">http://www.oreilly.co.jp/books/9784873114538/</a></h6>
<p>まずは下記回路図の様にArduinoを組み立てます。</p>
<p><img src="/wp-content/uploads/2013/03/OA001_fzz.png" alt="Arduino" /></p>
<p>赤外線センサーから値が取れていることを確認します。<br />
以下のスケッチでは距離がcm単位でSerialに出力されます。</p>
<h3>OA001.ino</h3>
<pre class="brush: java; title: ; notranslate">


// アナログピン0番
const int sensorPin = 0;

// 閾値
const int threshold = 80;

void setup(){

	// 接続速度の設定
	Serial.begin(9600);

}

void loop(){

	int value = analogRead(sensorPin);
	if(value &gt; threshold){
		// cm単位に変換
		float range = (6787 / (value -3)) -4;
		Serial.println(range+&quot;cm&quot;);
	}

}
</pre>
<h2>2.openFrameworksとArduinoをつなぐ</h2>
<p>openFrameworksで値を取得する行程に入りますが、Arduinoでは先程のスケッチは使用しません。<br />
ArduinoボードにはStandardFirmataを書き込んでおきます。<br />
StandardFirmataは、色んなプラットフォームとArduinoを繋ぐものです。<br />
StandardFirmataのインストール方法とArduinoへの書き込み方法については割愛します。</p>
<p>openFrameworksのコードに移ります。
</p>
<h3>OA001.h</h3>
<pre class="brush: cpp; title: ; notranslate">
class OA001 : public ofBaseApp{

	public:
		void setup();
		void draw();

	private :
		ofArduino ard;
		float ardValue;
		float distance;
		void setupArduino(const int &amp; version);
		void analogPinChanged(const int &amp; pinNum);

}
</pre>
<h3>OA001.cpp</h3>
<pre class="brush: cpp; title: ; notranslate">
// Arduinoとの接続速度
#define SPEED 57600

//--------------------------------------------------------------
void OA001::setup(){

	// Arduinoに接続
	ard.connect(&quot;/dev/cu.usbmodemfd321&quot;, SPEED);
	ofAddListener(ard.EInitialized, this, &amp;OA001::setupArduino);

}

//--------------------------------------------------------------
void OA001::setupArduino(const int &amp; version) {
    
	// イベントリスナ削除
	ofRemoveListener(ard.EInitialized, this, &amp;OA001::setupArduino);
	
	// アナログピン0番からのレポートを取得
	ard.sendAnalogPinReporting(0, ARD_ANALOG);
	ofAddListener(ard.EAnalogPinChanged, this, &amp;OA001::analogPinChanged);
	
	//cout &lt;&lt; ard.getFirmwareName() &lt;&lt; endl;
	//cout &lt;&lt; &quot;firmata v&quot; &lt;&lt; ard.getMajorFirmwareVersion() &lt;&lt; &quot;.&quot; &lt;&lt; ard.getMinorFirmwareVersion() &lt;&lt; endl;

}

//--------------------------------------------------------------
void OA001::analogPinChanged(const int &amp; pinNum) {

	int value = ard.getAnalog(pinNum);
	cout &lt;&lt; value &lt;&lt; endl;

}

//--------------------------------------------------------------
void OA001::update(){

	// Arduinoを更新
	ard.update();

}
</pre>
<p>コンソールにセンサー値が出力されたら接続成功です。<br />
このセンサー値は近くに何も無い場合、150以下で推移、最も手を近づけた時600強の値が出ます。<br />
純粋なセンサー値とは別に、この回で使用しているローパスフィルターをかけながら<br />
distanceに変換後の値を入れていきます。</p>
<h3>OA001.cpp</h3>
<pre class="brush: cpp; title: ; notranslate">
void OA001::analogPinChanged(const int &amp; pinNum) {

	// そのままのセンサー値を格納
	ardValue = ard.getAnalog(pinNum);

	// センサー値を調整して格納
	if(ardValue &lt; 100){
		distance = 100;
	}else if(ardValue &gt; 700){
		distance = 700;
	}else{
		// ローパスフィルターを摘要
		distance = distance*0.9+ardValue*0.1;
	}

}
</pre>
<p>ここまででArduinoについて分からない場合は田所先生の講義資料を見て勉強してみてください。<br />
本当に素晴らしい講義資料ばかりを公開していただいています。感謝！</p>
<h6>yoppa.org 「第９回：openFrameworksとArduinoを連携する」<br />
<a href="http://yoppa.org/ma2_11/3383.html">http://yoppa.org/ma2_11/3383.html</a></h6>
<h2>3.ofxSimpleGuiTooを追加</h2>
<p>開発を進めるにあたり、アドオンのofxSimpleGuiTooを使います。<br />
とても便利で、作品を作るにあたり開発効率が良くなるので早期に取り入れます。<br />
先程取得したセンサー値も、簡単に視覚化することが出来ます。<br />
筆者はmemoさんのバージョンを使っています。</p>
<h6>ofxSimpleGuiToo<br />
<a href="https://github.com/memo/ofxSimpleGuiToo">https://github.com/memo/ofxSimpleGuiToo</a></h6>
<h3>OA001.h</h3>
<pre class="brush: cpp; title: ; notranslate">
class OA001 : public ofBaseApp{

	public:
		void setup();
		void draw();

	private :
		ofxSimpleGuiToo gui;
		void setupGUI();

}
</pre>
<h3>OA001.cpp</h3>
<pre class="brush: cpp; title: ; notranslate">

void OA001::setup(){

	// ofxSimpleGuiTooをセットアップ
	setupGUI();

}

//--------------------------------------------------------------
void OA001::setupGUI() {

	gui.setup();
    
	// GUIの見た目を設定
	gui.config-&gt;textColor = 0xFFFFFF;
	gui.config-&gt;buttonHeight = 20;
	gui.config-&gt;sliderHeight = 10;
	gui.config-&gt;titleHeight = 20;
	gui.config-&gt;fullActiveColor = 0x00aec3;
    
	// Arduinoのセンサー値
	gui.addTitle(&quot;ARDUINO&quot;);
	gui.addSlider(&quot;ardValue&quot;, ardValue, 0.0f, 700.0f);
	gui.addSlider(&quot;distance&quot;, distance, 0.0f, 700.0f);
	
	// 前回終了した時の設定で起動
	gui.loadFromXML();
    
	// 最初から表示させる
	gui.show();
    
}

//--------------------------------------------------------------
void OA001::draw(){

	// ofxSimpleGuiTooを描画
	gui.draw();

}

</pre>
<h2>4.3D空間にofCircleを描く</h2>
<p>まずは縦横100個のofCircleを画面のセンターに表示させます。<br />
ofRotateYとかに適当に値を入れると、3D空間にofCircleが描かれているのが確認出来ます。</p>
<h3>OA001.h</h3>
<pre class="brush: cpp; title: ; notranslate">
class OA001 : public ofBaseApp{

	public:
		void setup();
		void draw();

	private :
		float rotateX;
		float rotateY;
		float rotateZ;

		int count;
		float margin;
		float radius;
		int color;
		int alpha;

}
</pre>
<h3>OA001.cpp</h3>
<pre class="brush: cpp; title: ; notranslate">
void OA001::setup(){

	// 画面全体の回転値を初期化
	rotateX = 0.0f;
	rotateY = 0.0f;
	rotateZ = 0.0f;

	// ofCircleで使用する値を初期化
	count = 100;
	margin = 25.0f;
	radius = 5.0f;
	color = 255;
	alpha = 255;

}

//--------------------------------------------------------------
void OA001::draw(){

	// この段階のMatrixを保持
	ofPushMatrix();

	// 画面センターに移動
	ofTranslate(ofGetWidth()/2, ofGetHeight()/2);
    
	// 画面全体の回転値を摘要
	ofRotateX(rotateX);
	ofRotateY(rotateY);
	ofRotateZ(rotateZ);
    
	// ofCircleの描画
	int totalLen = (count-1)*margin;
	ofTranslate(totalLen/-2, totalLen/-2);
	ofSetColor(color, color, color, alpha);
	for(int i=0; i&lt;count; i++){
		for(int j=0; j&lt;count; j++){
			ofCircle(i*margin, j*margin, 0, radius);
		}
	}
    
	// 保持したMatrixに戻す
	ofPopMatrix();

}
</pre>
<h2>5.PerlinNoiseでゆらゆら</h2>
<p>ofNoiseはパターン化されたランダム値を取得する事が出来ます。<br />
ofNoiseからPerlinNoiseを取得するために以下記事を参考にしました。</p>
<h6>Noise &#8211; 【oF】openFrameworks<br />
<a href="https://sites.google.com/site/ofauckland/examples/noise">https://sites.google.com/site/ofauckland/examples/noise</a></h6>
<p>このPerlinNoiseで得たグレースケール値を、ofCircleの奥行きに摘要していきます。<br />
先程と同じ箇所を改変します。</p>
<h3>OA001.cpp</h3>
<pre class="brush: cpp; title: ; notranslate">
void OA001::draw(){

	// この段階のMatrixを保持
	ofPushMatrix();
    
	// 画面センターに移動
	ofTranslate(ofGetWidth()/2, ofGetHeight()/2);
    
	// 画面全体の回転値を摘要
	ofRotateX(rotateX);
	ofRotateY(rotateY);
	ofRotateZ(rotateZ);
    
	// ofCircleの描画
	int totalLen = (count-1)*margin;
	ofTranslate(totalLen/-2, totalLen/-2);
	ofSetColor(color.r*255, color.g*255, color.b*255, alpha);
	for(int i=0; i&lt;count; i++){
		for(int j=0; j&lt;count; j++){
			float a = i * xForce;
			float b = j * yForce;
			float c = ofGetFrameNum() / 60.0;
			float noise = ofNoise(a,b,c) * zForce;
			ofCircle(i*margin, j*margin, noise, radius);
		}
	}
    
	// 保持したMatrixに戻す
	ofPopMatrix();

}
</pre>
<p>ここまでの様子は以下の様になります。<br />
ofxSimpleGuiTooで各パラメーターを操作して遊んでみます。<br />
画面収録している分フレームレートが落ちていますが、実際は60fpsで動きます。</p>
<p><iframe src="http://player.vimeo.com/video/61872549" width="720" height="405" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
<p><a href="http://vimeo.com/61872549">openFrameworks x Arduino #001( Process )</a> from <a href="http://vimeo.com/user16458913">Takepepe</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<h2>6.音楽を再生</h2>
<p>ofSoundPlayerで音楽を取り込みます。<br />
openFrameworksで音楽をコントロールする、恐らく最も簡単な方法です。<br />
ofSoundPlayerのボリュームをPerlinNoiseに係数として与えることで、<br />
音に併せて揺らめきが変わることが確認出来ます。</p>
<h3>OA001.h</h3>
<pre class="brush: cpp; title: ; notranslate">
class OA001 : public ofBaseApp{

	public:
		void setup();

	private :
		ofSoundPlayer mySound;
		float speed;
		float volume;
		void setupMusic();

}
</pre>
<h3>OA001.cpp</h3>
<pre class="brush: cpp; title: ; notranslate">
void OA001::setupMusic() {
    
	// 音楽を読み込んでループ再生
	mySound.loadSound(&quot;sound.wav&quot;);
	mySound.setLoop(true);
	mySound.play();
    
}

//--------------------------------------------------------------
void OA001::update(){

	// ofSoundPlayerを設定
	mySound.setSpeed(speed);
	mySound.setVolume(volume);

}

</pre>
<h2>7.Arduinoの値を摘要</h2>
<p>冒頭で取得したArduinoの値をofMapで音楽の音量・スピードに摘要すれば完成です！</p>
<h3>OA001.cpp</h3>
<pre class="brush: cpp; title: ; notranslate">
void OA001::update(){
    
	// Arduinoのセンサー値をofSoundPlayerの設定値に変換
	speed = ofMap(distance, 150, 600, 0.1f, 3.0f);
	volume = ofMap(distance, 150, 600, 0.1f, 1.0f);
    
	// ofSoundPlayerを設定
	mySound.setSpeed(speed);
	mySound.setVolume(volume);
    

}
</pre>
]]></content:encoded>
			<wfw:commentRss>https://takepepe.com/openframeworks-x-arduino-001-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>openFrameworks x iOS #001</title>
		<link>https://takepepe.com/openframeworks-x-ios-001/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=openframeworks-x-ios-001</link>
		<comments>https://takepepe.com/openframeworks-x-ios-001/#comments</comments>
		<pubDate>Sun, 10 Mar 2013 07:00:00 +0000</pubDate>
		<dc:creator>Takepepe</dc:creator>
				<category><![CDATA[iOS]]></category>
		<category><![CDATA[openFrameworks]]></category>
		<category><![CDATA[Box2D]]></category>
		<category><![CDATA[OSC]]></category>

		<guid isPermaLink="false">http://takepepe.com/?p=292</guid>
		<description><![CDATA[openFrameworks x iOS #001 from Takepepe on Vimeo. 最近めっき [...]]]></description>
				<content:encoded><![CDATA[<p><iframe src="http://player.vimeo.com/video/61449234" width="720" height="405" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
<p><a href="http://vimeo.com/61449234">openFrameworks x iOS #001</a> from <a href="http://vimeo.com/user16458913">Takepepe</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<p>最近めっきり暖かくなりましたね。<br />
花粉が飛び始めたようですが、Takepepeまだ花粉症ではないのでどこ吹く風です。<br />
（花粉症の方すみません;）</p>
<p>今回は上の動画の様なものを作りました。<br />
openFrameworksで作成した複数のiOSアプリから、<br />
openFrameworksで作成したDesktopアプリケーションを操作します。<br />
通信には前回同様、OSCを使っています。</p>
<p>前回は１対１の一方通信でしたが、今回は１対複の通信になります。<br />
動画上では2台のiPhoneのタッチ座標を取得し、その座標をつなぐ線が描画されていますが、<br />
コード上はOSC接続している端末数に応じて点と線が増えていくものになっています。<br />
iPadやiOSシュミレーターでも確認できます。<br />
（引き出しに眠っているiPhone3GSが復活。捨てなくてよかった。）<br />
物理演算処理にはBox2Dを使用しています。</p>
<p>全体の流れを詳しく説明していきます。</p>
<ol>
<li>iOS端末からDesktopアプリケーションにOSC通信</li>
<li>iOS端末の数に応じたClientインスタンスの生成</li>
<li>Box2Dのセッティングと円を降らす処理</li>
<li>Clientメンバの座標間で線をひく</li>
</ol>
<h2>1.iOS端末からDesktopアプリケーションにOSC通信</h2>
<p>
まずはiOSアプリをopenFrameworksで作ります。<br />
アプリ単体で見ると、タッチした場所に円が描かれるだけの簡単なものになっています。
</p>
<h3>oscS.h (iOS端末のコード)</h3>
<pre class="brush: cpp; title: ; notranslate">

#define HOST &quot;192.168.10.191&quot;　// iMacのIPアドレス
#define PORT 8000 // 接続ポート

class oscS : public ofxiPhoneApp{
	
    public:
        void setup();
        void update();
        void draw();
        float getX(float mx);
        float getY(float my);
        void touchMoved(ofTouchEventArgs &amp; touch);
    
    private:
        ofxOscSender sender;
};
</pre>
<h3>oscS.mm (iOS端末のコード)</h3>
<pre class="brush: cpp; title: ; notranslate">

#include &quot;oscS.h&quot;
#include &quot;ofxOsc.h&quot;

//--------------------------------------------------------------
void oscS::setup(){
    
	ofxAccelerometer.setup();
    sender.setup( HOST, PORT );
	ofBackground(0, 0, 0);
    
}

//--------------------------------------------------------------
void oscS::update(){

}

//--------------------------------------------------------------
void oscS::draw(){
	ofSetColor(255, 255, 255);
    ofCircle(mouseX, mouseY, 10);
}

//--------------------------------------------------------------
float oscS::getX(float mx){
    int res = ofGetWidth();
    return mx/res;
}

//--------------------------------------------------------------
float oscS::getY(float my){
    int res = ofGetHeight();
    return my/res;
}

//--------------------------------------------------------------
void oscS::touchMoved(ofTouchEventArgs &amp; touch){
    ofxOscMessage m;
    m.setAddress( &quot;/mouse/position&quot; );
    m.addFloatArg( getX(touch.x) );
    m.addFloatArg( getY(touch.y) );
    sender.sendMessage( m );
}

</pre>
<h2>2.iOS端末の数に応じたClientインスタンスの生成</h2>
<p>
ここからはDesktopアプリケーションです。<br />
まずは「Client」クラスを作成します。<br />
このクラスはコンストラクタにiOS端末のipアドレスを引数で渡します。<br />
update関数が呼ばれるたび、タッチ座標を変換・更新します。
</p>
<h3>Client.h</h3>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;ofxOsc.h&quot;
class Client {
    public:
        Client(std::string ip);
        void update(ofxOscMessage message);
        void draw();
        ofPoint pos;
        string getIp();
    
    private:
        string ip;
};
</pre>
<h3>Client.cpp</h3>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;ofxOsc.h&quot;

//--------------------------------------------------------------
Client::Client(string _ip){
    ip = _ip;
}

//--------------------------------------------------------------
void Client::update(ofxOscMessage message){
    ofxOscMessage m = message;
    
    // Clientオブジェクトの更新
    if ( m.getAddress() == &quot;/mouse/position&quot; &amp;&amp; ip == m.getRemoteIp()){
        
        // 画面比率にあわせてタッチ座標を変換
        pos.x = ofGetWidth() * m.getArgAsFloat(0);
        pos.y = ofGetHeight() * m.getArgAsFloat(1);
    }
}

//--------------------------------------------------------------
void Client::draw(){
    ofCircle(pos.x, pos.y, 5);
}

//--------------------------------------------------------------
string Client::getIp(){
    return ip;
}
</pre>
<p>
メインスレッドでOSCメッセージを受信した際、そのメッセージが新しい端末からの場合、<br />
Clientインスタンスが生成されるようになっています。
</p>
<h3>oscR.h (抜粋)</h3>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;Client.h&quot;
#define PORT 8000
class oscR : public ofBaseApp{
    public:
        void setup();
        void update();
        void draw();
    
    private:
        void updateClient();
        ofxOscReceiver  receiver;
        vector &lt;Client *&gt; clients;

}
</pre>
<h3>oscR.cpp (抜粋)</h3>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;oscR.h&quot;
#include &quot;ofxOsc.h&quot;

//--------------------------------------------------------------
void oscR::setup(){

    receiver.setup( PORT );
    
}
//--------------------------------------------------------------
void oscR::update(){
    
    // OSCクライアントの更新
    updateClient();

}
//--------------------------------------------------------------
void oscR::updateClient(){
    while( receiver.hasWaitingMessages() ){
        ofxOscMessage m;
        receiver.getNextMessage( &amp;m );
        
        // 1つ目のOSCクライアント
        if(clients.size() == 0){
            clients.push_back(new Client(m.getRemoteIp()));
        }
        
        // 新しいOSCクライアントからのメッセージかどうか
        int flag = 1;
        for(int i=0 ; i&lt; clients.size() ; i++){
            if(clients[i]-&gt;getIp() == m.getRemoteIp()){
                flag = 0;
            }
        }
        
        // 新しいOSCクライアントだったら
        if(flag){
            clients.push_back(new Client(m.getRemoteIp()));
        }
        
        // Clientオブジェクトの更新
        int size = clients.size();
        for(int i=0 ; i&lt; size ; i++){
            clients[i]-&gt;update(m);
        }
    }
}
//--------------------------------------------------------------
void oscR::draw(){

    ofSetColor(50,200,255);
    ofFill();
    for(int i=0; i&lt;clients.size(); i++) {
        clients[i]-&gt;draw();
    }

}
</pre>
<h2>3.Box2Dのセッティングと円を降らす処理</h2>
<p>
次にBox2Dの実装です。<br />
updateでカウントアップし、circlesの数が100に達したら<br />
新しいofxBox2dCircleを作り、古いものは削除します。
</p>
<h3>oscR.h (抜粋)</h3>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;ofxBox2d.h&quot;
class oscR : public ofBaseApp{
    public:
        void setup();
        void update();
        void draw();
    
    private:
        void updateCircles();
        int count;
    
        ofxBox2d box2d;
        vector &lt;ofxBox2dCircle *&gt; circles;
    
};

</pre>
<h3>oscR.cpp (抜粋)</h3>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;ofxBox2d.h&quot;

//--------------------------------------------------------------
void oscR::setup(){

    count = 0;
    
    // Box2D設定
    box2d.init();
    box2d.setGravity(0, 0.1);
    box2d.createBounds(0, 0, ofGetWidth(), ofGetHeight());
    box2d.setFPS(10.0);
    box2d.setIterations(5, 5);
    
}

//--------------------------------------------------------------
void oscR::update(){
    
    // ofxBox2dCircleを更新
    updateCircles();
    box2d.update();

}

//--------------------------------------------------------------
void oscR::updateCircles(){
    count++;
    if(count &gt; 100){
        
        // ofxBox2dCircleの追加
        ofPoint pos;
        int r = ofRandom(10, 30);
        int x = ofRandom(r, ofGetWidth());
        pos.set(x,r);
        
        ofxBox2dCircle *c = new ofxBox2dCircle();
        c-&gt;setPhysics(1.0, 0.2, 0.2);
        c-&gt;setup(box2d.getWorld(),x,r,r);
        circles.push_back(c);
        
        // 100個以上になったら先頭のofxBox2dCircleを削除
        if(circles.size() &gt; 100){
            vector &lt;ofxBox2dCircle *&gt;::iterator it = circles.begin();
            (*it)-&gt;destroy();
            delete *it;
            it = circles.erase(it);
        }
        
        count = 0;
    }
}

//--------------------------------------------------------------
void oscR::draw(){

    ofNoFill();
    ofSetColor(255,255,255);
    for(int i=0; i&lt;circles.size(); i++) {
        circles[i]-&gt;draw();
    }

}
</pre>
<h2>4.Clientメンバの座標間で線をひく</h2>
<p>
最後にiOS端末から取得した座標でofxBox2dPolygonの線をひきます。<br />
ofPolylineに座標を追加し、ofxBox2dPolygonに代入します。<br />
画面上で線が見える様、draw関数ではofPolylineを描画しています。<span id="more-292"></span>
</p>
<h3>oscR.h (抜粋)</h3>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;ofxBox2d.h&quot;

class oscR : public ofBaseApp{
    public:
        void setup();
        void update();
        void draw();
    
    private:
        void updatePolyLine();
        ofPolyline drawing;
        ofxBox2dPolygon polyLine;
    
};

</pre>
<h3>oscR.cpp (抜粋)</h3>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;ofxBox2d.h&quot;

//--------------------------------------------------------------
void oscR::update(){
    
    // 複数のOSCクライアントでofxBox2dPolygonを更新
    updatePolyLine(); 
}

//--------------------------------------------------------------
void oscR::updatePolyLine(){
    int size = clients.size();
    
    // OSCクライアントが複数だったら
    if(size &gt;= 2){
        drawing.clear();
        for(int i=0 ; i&lt; size ; i++){
            // 座標を追加
            drawing.addVertex(clients[i]-&gt;pos.x, clients[i]-&gt;pos.y);
        }
        // 3点以上必要なので最後の座標を追加
        drawing.addVertex(clients[size-1]-&gt;pos.x, clients[size-1]-&gt;pos.y);
        
        // ofPolylineをofxBox2dPolygonに代入
        polyLine.clear();
        polyLine.setPhysics(0.0, 0.5, 0.5);
        polyLine.addVertexes(drawing);
        polyLine.create(box2d.getWorld());
        
    }
}

//--------------------------------------------------------------
void oscR::draw(){
 
    ofSetColor(50,100,255);
    if (drawing.size()){
        drawing.draw();
    }

}

</pre>
]]></content:encoded>
			<wfw:commentRss>https://takepepe.com/openframeworks-x-ios-001/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Processing x Android #001</title>
		<link>https://takepepe.com/processing-x-android-001/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=processing-x-android-001</link>
		<comments>https://takepepe.com/processing-x-android-001/#comments</comments>
		<pubDate>Sat, 02 Mar 2013 10:38:56 +0000</pubDate>
		<dc:creator>Takepepe</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Processing]]></category>

		<guid isPermaLink="false">http://takepepe.com/?p=256</guid>
		<description><![CDATA[Processing x Android #001 from Takepepe on Vimeo. 最近春一番 [...]]]></description>
				<content:encoded><![CDATA[<p><iframe src="http://player.vimeo.com/video/60888714" width="720" height="405" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe></p>
<p><a href="http://vimeo.com/60888714">Processing x Android #001</a> from <a href="http://vimeo.com/user16458913">Takepepe</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<p>最近春一番が吹き始めましたよ。<br />
  シーズンも終わりに近づき、悲しい限りです…。</p>
<p>今回はProcessing for Android について触れたいと思います。<br />
  Processing for AndroidはProcessingで比較的簡単にアプリをつくれる開発環境です。<br />
  ご存知のとおり、スマホ・タブレットにはたくさのセンサーが搭載されており、自分が使用しているNexus7には以下のセンサーが搭載されています。</p>
<p>・GPS<br />
  ・電子コンパス<br />
  ・光センサ<br />
  ・加速度センサ<br />
  ・ジャイロスコープ<br />
  ・NFC<br />
  ・磁気センサ</p>
<p>アイディア次第で何でも出来そうでワクワクしますね。特にNFCは今のところiOS端末には無いセンサーなので気になる人も多いのでは。<br />
  Processing for Androidなら、少ない手順でこれらにアクセス出来ます。</p>
<p>上の動画を見てのとおり、今回作成したアプリは端末の画面を見て遊ぶものではなく、センサー値やタッチ情報をiMacに送り、デスクトップで稼働しているProcessingを操作するものです。おおまかな流れを書いていきます。なお、開発環境の準備については良記事がたくさんありますので、そちらを参照してください。</p>
<ol>
<li>動作環境の準備</li>
<li>センサー値の取得</li>
<li>OSCによる通信</li>
<li>デスクトップアプリのスケッチ</li>
</ol>
<h2>1.動作環境の準備</h2>
<p>今回はWifi環境のもと、OSCでAndroid・Mac間の値のやり取りを行っています。<br />
  まずはMacのIPアドレスをメモしておきましょう。<br />
  あとは、同じネットワークにAndroid端末を接続しておきます。<br />
  MacからAndroidに値を送る場合はAndroidのIPアドレスもメモしておきましょう。</p>
<p>Processing for Androidで、Androidアプリが開発できる環境が整ったら、<br />
  必要なライブラリを開発環境にインストールします。</p>
<h3>ketai</h3>
<p><a href="https://code.google.com/p/ketai/" target="_blank">https://code.google.com/p/ketai/</a></p>
<p>Androidの各センサーや、イベント、OSの標準UI、SQLなど扱うことが出来るライブラリです。<br />
  多くの端末でテストしていないため動作保証は出来ませんが、自分のNexus7は問題なく使えました。<br />
  ライブラリの作者も以下の環境でテストしている様です。今回は「Ketai_v8」を使います。</p>
<p class="gray-rect">Test platforms : Android 4.1, 4.1.2, 4.0.2<br />
  Test Hardware: Nexus 7, Galaxy 3s, Nexus S, Transformer Prime</p>
<p>しかしながら、携帯から来ているらしいこのライブラリ名、違和感ありですね w（ケタイって）</p>
<h3>oscP5</h3>
<p><a href="http://www.sojamo.de/libraries/oscP5/" target="_blank">http://www.sojamo.de/libraries/oscP5/</a></p>
<p>OSCで値をやり取りするためにoscP5を使用します。<br />
  OSCはOpenSound Controlの略で、ネットワーク経由でリアルタイム通信が出来る通信プロトコルです。 <a href="http://ja.wikipedia.org/wiki/OpenSound_Control" target="_blank">http://ja.wikipedia.org/wiki/OpenSound_Control</a></p>
<h2>2.センサー値の取得</h2>
<p>さて、ここからコードに入ります。<br />
  が、その前に、先ほどダウンロードしたファイルに「examples」がありますので、いくつか気になるものをコンパイルしてみてください。</p>
<p>今回は「KetaiGesture」「KetaiSensor」の2つに焦点を絞っています。<br />
  まずはセンサーを有効にし、センサーイベントを受け取る関数を書きます。<br />
  今回有効にするのは「加速度センサ(Accelerometer)、ジャイロスコープ(Gyroscope)、電子コンパス(Orientation)、光センサ(Light)」です。</p>
<h5>Android.pde</h5>
<pre class="brush: java; title: ; notranslate">
void setup(){

	gesture = new KetaiGesture(this);
    sensor = new KetaiSensor(this);
    
    sensor.enableAccelerometer();
    sensor.enableGyroscope();
    sensor.enableOrientation();
    sensor.enableLight();
    sensor.start();
    
}

public boolean surfaceTouchEvent(android.view.MotionEvent event) {
  
  super.surfaceTouchEvent(event);
  return gesture.surfaceTouchEvent(event);
  
}

</pre>
<p><P>配列に値を入れるまえに、ローパスフィルターを通しています。<br />
  センサー値は手ぶれや様々な誤差から、思った通りのスムーズな値が得られません。<br />
  今回は以下の記事を参考にセンサー値を調整しています。</p>
<p>アンドロイドな日々 <a href="http://android.ohwada.jp/archives/334" target="_blank">http://android.ohwada.jp/archives/334</a></p>
<p>ピンチイベント、光センサーイベントの関数も書いておきます。</p>
<h5>Android.pde</h5>
<pre class="brush: java; title: ; notranslate">

void onAccelerometerEvent(float x, float y, float z){
	
    float AX = 0.9*GValue.get(0)+0.1*x;
    float AY = 0.9*GValue.get(1)+0.1*y;
    float AZ = 0.9*GValue.get(2)+0.1*z;
    
    AValue.set(0,AX);
    AValue.set(1,AY);
    AValue.set(2,AZ);
  
}

void onGyroscopeEvent(float x, float y, float z){
	
    float GX = 0.9*GValue.get(0)+0.1*x;
    float GY = 0.9*GValue.get(1)+0.1*y;
    float GZ = 0.9*GValue.get(2)+0.1*z;
    
    GValue.set(0,GX);
    GValue.set(1,GY);
    GValue.set(2,GZ);
  
}

void onOrientationEvent(float x, float y, float z){
	
    float OX = 0.9*OValue.get(0)+0.1*x;
    float OY = 0.9*OValue.get(1)+0.1*y;
    float OZ = 0.9*OValue.get(2)+0.1*z;
    
    OValue.set(0,OX);
    OValue.set(1,OY);
    OValue.set(2,OZ);
  
}

void onPinch(float x, float y, float d){
  
  pinchValue = d;
  
}

void onLightEvent(float v){
  
  lightValue = v;
  
}

</pre>
<h2>3.OSCによる通信</h2>
<p>今度はOSC通信に必要なインスタンスを準備します。まずはAndroid側。<br />
  1.でメモしておいた、iMacのネットワークアドレスをNetAddressインスタンスにセットします。<br />
  ポート番号は12000としておきます。Androidは12001に。<br />
  また、メニュー > Android > Sketch Permissions でINTERNETにチェックをいれておきます。</p>
<h5>Android.pde</h5>
<pre class="brush: java; title: ; notranslate">

void setup(){
  
  oscP5 = new OscP5(this,12001);
  iMacAddress = new NetAddress(&quot;192.168.10.191&quot;,12000); // IPアドレスは適宜変更
  
}

</pre>
<p>ここで新たに、デスクトップで稼働するスケッチを新規作成します。<br />
  これが後のセンサー値を受け取るデスクトップアプリになります。<br />
  こちらのスケッチでも、先程と同様にOscP5インスタンスとNetAddressインスタンスを作成します。<br />
  Androidのスケッチとはとポート番号が入れ替えになります。</p>
<h5>Desktop.pde</h5>
<pre class="brush: java; title: ; notranslate">

void setup(){
  
  oscP5 = new OscP5(this,12000);
  androidAddress = new NetAddress(&quot;192.168.10.221&quot;,12001); // IPアドレスは適宜変更
  
}


</pre>
<p>最後にAndroid側からOscMessageで値を送信し、<br />
  iMacで稼働しているアプリに値がちゃんと届いていることを確認します。</p>
<h5>Android.pde</h5>
<pre class="brush: java; title: ; notranslate">

void draw(){
  
  OscMessage sendValue = new OscMessage(&quot;/AndroidOSC&quot;);
  
  // ピンチ値
  sendValue.add(pinchValue);
  
  // 光センサー
  sendValue.add(lightValue);
  
  // 加速度センサー
  sendValue.add(AValue.get(0));
  sendValue.add(AValue.get(1));
  sendValue.add(AValue.get(2));
  
  // ジャイロスコープセンサー
  sendValue.add(GValue.get(0));
  sendValue.add(GValue.get(1));
  sendValue.add(GValue.get(2));
  
  // 電子コンパス
  sendValue.add(OValue.get(0));
  sendValue.add(OValue.get(1));
  sendValue.add(OValue.get(2));
  
  // OscMessageをiMacに送信
  oscP5.send(sendValue, iMacAddress);
  
}

</pre>
<h5>Desktop.pde</h5>
<pre class="brush: java; title: ; notranslate">

void oscEvent(OscMessage theOscMessage) {

	if(theOscMessage.checkAddrPattern(&quot;/AndroidOSC&quot;) == true) {
    	if(theOscMessage.checkTypetag(&quot;fffffffffff&quot;)) {
        	
            // ピンチ値
        	pinchValue = theOscMessage.get(0).floatValue();
            
            // 光センサー
            lightValue = theOscMessage.get(1).floatValue();
            
            // 加速度センサー
            AValue.set(0,theOscMessage.get(2).floatValue());
            AValue.set(1,theOscMessage.get(3).floatValue());
            AValue.set(2,theOscMessage.get(4).floatValue());
            
            // ジャイロスコープセンサー
            GValue.set(0,theOscMessage.get(5).floatValue());
            GValue.set(1,theOscMessage.get(6).floatValue());
            GValue.set(2,theOscMessage.get(7).floatValue());
            
            // 電子コンパス
            OValue.set(0,theOscMessage.get(8).floatValue());
            OValue.set(1,theOscMessage.get(9).floatValue());
            OValue.set(2,theOscMessage.get(10).floatValue());
            
    	}
	}
    
}

</pre>
<h2>4.デスクトップアプリのスケッチ</h2>
<p>ここまで来れば、もう完成間近です。<br />
  Processingでは3D表現も簡素なものであればすぐに出来ます。<br />
  あまり3Dソフトに慣れていないので、フリーのドロイド君を探し、Blendrでobj出力しました。<br />
  Processingに読み込む時、いろいろエラーが出た人は、書き出しオプションにチェックを入れて対処しましょう。<br />
  冒頭動画のソースコード全文は以下の様になっています。</p>
<h5>Desktop.pde</h5>
<pre class="brush: java; title: ; notranslate">

import saito.objloader.*;
import oscP5.*;
import netP5.*;

int W = 180;
int WIDTH = 720-W;
int HEIGHT = 405;
int NUM = 128;

OscP5 oscP5;
OBJModel objm;

float lightValue;
float scaleValue;
ArrayList&lt;Float&gt; AValue = new ArrayList&lt;Float&gt;();
ArrayList&lt;Float&gt; GValue = new ArrayList&lt;Float&gt;();
ArrayList&lt;Float&gt; OValue = new ArrayList&lt;Float&gt;();

void setup() {
  
    size(720, 405, OPENGL);
    noStroke();
    
    scaleValue = 40;
    for(int i = 0; i &lt; 3; i++){
      AValue.add(new Float(0));
      GValue.add(new Float(0));
      OValue.add(new Float(0));
    }
    oscP5 = new OscP5(this,12000);
    
    objm = new OBJModel(this);
    objm.load(&quot;Android3D.obj&quot;);
    setConsole();
    
}

void draw() {
  
  fill(0);
  noStroke();
  rect(0,0,WIDTH,HEIGHT);
  
  String info = &quot;&quot;;
  info += &quot;scaleValue:&quot;+scaleValue+&quot;\n&quot;;
  info += &quot;lightValue:&quot;+lightValue+&quot;\n&quot;;
  info += &quot;AccelerometerX:&quot;+AValue.get(0)+&quot;\n&quot;;
  info += &quot;AccelerometerY:&quot;+AValue.get(1)+&quot;\n&quot;;
  info += &quot;AccelerometerZ:&quot;+AValue.get(2)+&quot;\n&quot;;
  info += &quot;GyroscopeX:&quot;+GValue.get(0)+&quot;\n&quot;;
  info += &quot;GyroscopeY:&quot;+GValue.get(1)+&quot;\n&quot;;
  info += &quot;GyroscopeZ:&quot;+GValue.get(2)+&quot;\n&quot;;
  info += &quot;OrientationX:&quot;+OValue.get(0)+&quot;\n&quot;;
  info += &quot;OrientationY:&quot;+OValue.get(1)+&quot;\n&quot;;
  info += &quot;OrientationZ:&quot;+OValue.get(2)+&quot;\n&quot;;
  drawConsole(info);
  
  println(lightValue);
  ambientLight(200,100,100);
  directionalLight(255,255,100,-1,0,0);
  pointLight(100,200,255, 100, 100, 100);
  spotLight(lightValue,lightValue,lightValue, 100, 100, 1000, 0, 0, -1, PI, 2);
  
  translate(WIDTH/2,HEIGHT/2,1);
  rotateX(GValue.get(0)*-1);
  rotateY(GValue.get(1));
  rotateZ(GValue.get(2)*-1);
  scale(scaleValue);
  objm.draw();
  
}

void oscEvent(OscMessage theOscMessage) {
  if(theOscMessage.checkAddrPattern(&quot;/AndroidOSC&quot;) == true) {
    if(theOscMessage.checkTypetag(&quot;fffffffffff&quot;)) {
      float pinchValue = theOscMessage.get(0).floatValue();
      if(pinchValue&gt;2){
        scaleValue++;
      }
      if(pinchValue &lt; -2){
        scaleValue--;
      }
      if(scaleValue &lt; 1){
        scaleValue = 1;
      }
      lightValue = theOscMessage.get(1).floatValue();
      AValue.set(0,theOscMessage.get(2).floatValue());
      AValue.set(1,theOscMessage.get(3).floatValue());
      AValue.set(2,theOscMessage.get(4).floatValue());
      GValue.set(0,theOscMessage.get(5).floatValue());
      GValue.set(1,theOscMessage.get(6).floatValue());
      GValue.set(2,theOscMessage.get(7).floatValue());
      OValue.set(0,theOscMessage.get(8).floatValue());
      OValue.set(1,theOscMessage.get(9).floatValue());
      OValue.set(2,theOscMessage.get(10).floatValue());
    }
  }
}

</pre>
<h5>Android.pde</h5>
<pre class="brush: java; title: ; notranslate">
import ketai.net.*;
import ketai.ui.*;
import ketai.sensors.*;
import oscP5.*;
import netP5.*;

KetaiGesture gesture;
KetaiSensor sensor;
public OscP5 oscP5;
NetAddress iMacAddress;

float pinchValue;
float lightValue;
ArrayList&lt;Float&gt; AValue = new ArrayList&lt;Float&gt;();
ArrayList&lt;Float&gt; GValue = new ArrayList&lt;Float&gt;();
ArrayList&lt;Float&gt; OValue = new ArrayList&lt;Float&gt;();

void setup(){
  size(displayWidth, displayHeight);
  orientation(LANDSCAPE);
  
  oscP5 = new OscP5(this,12001);
  iMacAddress = new NetAddress(&quot;192.168.10.191&quot;,12000);
  
  pinchValue = 0;
  lightValue = 0;
  for(int i = 0; i &lt; 3; i++){
    AValue.add(new Float(0));
    GValue.add(new Float(0));
    OValue.add(new Float(0));
  }
  
  gesture = new KetaiGesture(this);
  sensor = new KetaiSensor(this);
  sensor.enableLight();
  sensor.enableAccelerometer();
  sensor.enableGyroscope();
  sensor.enableOrientation();
  sensor.start();
  
}

public boolean surfaceTouchEvent(android.view.MotionEvent event) {
  
  super.surfaceTouchEvent(event);
  return gesture.surfaceTouchEvent(event);
  
}

void draw(){
  
  pinchValue += (1-pinchValue)/5;
  
  String info = &quot;&quot;;
  info += &quot;AccelerometerX:&quot;+AValue.get(0)+&quot;\n&quot;;
  info += &quot;AccelerometerY:&quot;+AValue.get(1)+&quot;\n&quot;;
  info += &quot;AccelerometerZ:&quot;+AValue.get(2)+&quot;\n&quot;;
  info += &quot;GyroscopeX:&quot;+GValue.get(0)+&quot;\n&quot;;
  info += &quot;GyroscopeY:&quot;+GValue.get(1)+&quot;\n&quot;;
  info += &quot;GyroscopeZ:&quot;+GValue.get(2)+&quot;\n&quot;;
  info += &quot;OrientationX:&quot;+OValue.get(0)+&quot;\n&quot;;
  info += &quot;OrientationY:&quot;+OValue.get(1)+&quot;\n&quot;;
  info += &quot;OrientationZ:&quot;+OValue.get(2)+&quot;\n&quot;;
  
  OscMessage sendValue = new OscMessage(&quot;/AndroidOSC&quot;);
  sendValue.add(pinchValue);
  sendValue.add(lightValue);
  sendValue.add(AValue.get(0));
  sendValue.add(AValue.get(1));
  sendValue.add(AValue.get(2));
  sendValue.add(GValue.get(0));
  sendValue.add(GValue.get(1));
  sendValue.add(GValue.get(2));
  sendValue.add(OValue.get(0));
  sendValue.add(OValue.get(1));
  sendValue.add(OValue.get(2));
  oscP5.send(sendValue, iMacAddress);
  
  background(0);
  textSize(20);
  fill(255);
  text(info,50,50);
  
}

void onAccelerometerEvent(float x, float y, float z){
  
  float AX = 0.9*GValue.get(0)+0.1*x;
  float AY = 0.9*GValue.get(1)+0.1*y;
  float AZ = 0.9*GValue.get(2)+0.1*z;
  
  AValue.set(0,AX);
  AValue.set(1,AY);
  AValue.set(2,AZ);
  
}

void onGyroscopeEvent(float x, float y, float z){
  
  float GX = 0.9*GValue.get(0)+0.1*x;
  float GY = 0.9*GValue.get(1)+0.1*y;
  float GZ = 0.9*GValue.get(2)+0.1*z;
  
  GValue.set(0,GX);
  GValue.set(1,GY);
  GValue.set(2,GZ);
  
}

void onOrientationEvent(float x, float y, float z){
  
  float OX = 0.9*OValue.get(0)+0.1*x;
  float OY = 0.9*OValue.get(1)+0.1*y;
  float OZ = 0.9*OValue.get(2)+0.1*z;
  
  OValue.set(0,OX);
  OValue.set(1,OY);
  OValue.set(2,OZ);
  
}

void onLightEvent(float v){
  
  lightValue = v;
  
}

void onPinch(float x, float y, float d){
  
  pinchValue = d;
  
}

</pre>
<h5>Console.pde</h5>
<pre class="brush: java; title: ; notranslate">

import processing.video.*;
Capture capture;
PFont font;

void setConsole(){
  font = loadFont(&quot;Helvetica-12.vlw&quot;);
  capture = new Capture(this, 640,480,30);
  capture.start();
}

void drawConsole(String str){
  float H = W*0.75;
  int ts = 11;
  float X = width-W;
  float Y = height-H;
  if (capture.available()){
    capture.read();
    image(capture, X, Y, W, H);
  }
  
  fill(17);
  rect(X,0,W,Y);
  
  stroke(51);
  line(X,0,X,height);
  noStroke();
  
  fill(255);
  textSize(ts);
  textFont(font, ts);
  text(str,X+5,ts+5);
  text(str,X+5,ts+5);
}


</pre>
]]></content:encoded>
			<wfw:commentRss>https://takepepe.com/processing-x-android-001/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>canvas #001 WebRTC</title>
		<link>https://takepepe.com/canvas-001-webrtc/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=canvas-001-webrtc</link>
		<comments>https://takepepe.com/canvas-001-webrtc/#comments</comments>
		<pubDate>Tue, 19 Feb 2013 03:37:27 +0000</pubDate>
		<dc:creator>Takepepe</dc:creator>
				<category><![CDATA[html5]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://takepepe.com/?p=230</guid>
		<description><![CDATA[こんにちは。今日は東京でも雪が降っています。 ゲレンデ好きでも、日常の雪は勘弁して欲しいところ。 前回、Fla [...]]]></description>
				<content:encoded><![CDATA[<p><script type="text/javascript" src="http://jsdo.it/blogparts/eW16q/js"></script></p>
<p>こんにちは。今日は東京でも雪が降っています。<br />
  ゲレンデ好きでも、日常の雪は勘弁して欲しいところ。</p>
<p>前回、Flashでカメラ映像にDisplacementMapフィルターを適用する記事を書きました。<br />
  今回は同様の表現をcanvasで実装していきます。<br />
  js.do.itにアップしている上のデモはPCのChromeで見て下さい。（v.24.0以降）<br />
  また、再生の際にカメラへのアクセス許可が求められるので、許可してください。（ページ上部）</p>
<h3>ながれ</h3>
<ol>
<li>WebRTCでカメラにアクセス</li>
<li>マップ用canvasの用意</li>
<li>レイヤー配置</li>
<li>レンダリング</li>
</ol>
<h2>1.WebRTCでカメラにアクセス</h2>
<p>設定無しに、ウェブカメラにアクセス出来るメジャーブラウザはいまのところChromeのみなので、<br />
Chrome閲覧を想定したコードになっています。(2013.2.19現在)<br />
  WebRTCはマイクからの音声も拾えるようになっています。</p>
<h5>Main.js</h5>
<pre class="brush: jscript; title: ; notranslate">
  function setCamera(){
		
		if(navigator.webkitGetUserMedia) {
			navigator.webkitGetUserMedia( {video:true}, succeedUseCamera, failedUseCamera );
		}else{
			browserError();
		}
		
	}
  </pre>
<h2>2.マップ用canvasの用意</h2>
<p>前回同様、マップ画像はスクリプト上で生成します。<br />
  グラデーションの書き方はActionScriptとは違うので調べてみてください。<br />
  レンズにはまる様、サイズ調整などしています。見辛くてすみません。</p>
<h5>Main.js</h5>
<pre class="brush: jscript; title: ; notranslate">
  function setMap(){
		
		map = document.createElement('canvas');
		map.width = WIDTH;
		map.height = HEIGHT;
		map_ctx = map.getContext('2d');
		map_ctx.beginPath();
		
		var grad = map_ctx.createRadialGradient(WIDTH*0.5*offset,HEIGHT*0.5*offset,0,WIDTH*0.5*offset,HEIGHT*0.5*offset,WIDTH*0.5*offset);
		grad.addColorStop(0,'red'); 
		grad.addColorStop(1,'blue');
		map_ctx.fillStyle = grad;
		map_ctx.rect(0,50, WIDTH*offset,HEIGHT*offset);
		map_ctx.fill();
		
	}
  </pre>
<h2>3.レイヤー配置</h2>
<p>必要なレイヤーを順番に配置していきます。<br />
  カメラ映像の映写が始まるまで、レンズが外れたような見た目にならないよう、リソースの画像も敷いています。<br />
  マップ用canvasとvideoタグは配置する必要はありません。</p>
<h5>Main.js</h5>
<pre class="brush: jscript; title: ; notranslate">
  function setCanvas(){
		
		lens = new Image();
		lens.src = &quot;http://jsrun.it/assets/1/Y/a/h/1Yahi.png&quot;;
		
		frame = new Image();
		frame.src = &quot;http://jsrun.it/assets/n/P/B/W/nPBWU.png&quot;;
		
		mirror = document.createElement('canvas');
		mirror_ctx = mirror.getContext('2d');
		
		c = document.createElement('canvas');
		ctx = c.getContext('2d');
		
		lens.width = mirror.width = frame.width = c.width = WIDTH;
		lens.height = mirror.height = frame.height = c.height = HEIGHT;
		
		var objBody = document.getElementsByTagName(&quot;body&quot;).item(0);
		objBody.appendChild(lens);
		objBody.appendChild(c);
		objBody.appendChild(frame);
		
	}
  </pre>
<h2>4.レンダリング</h2>
<p>ActionScriptと違い、現状ではcanvasのAPIやCSSでブレンドモードの設定は出来ません。<br />
  ここで、以下のライブラリを利用させていただきました。</p>
<p><a href="https://github.com/Phrogz/context-blender" target="_blank">context-blender.js @Author Phrogz</a></p>
<p>また、DisplacementMapFilterもcanvasのAPIにはないため、こちらも外部ライブラリに頼りです。</p>
<p><a href="http://www.soundstep.com/blog/2012/04/25/javascript-displacement-mapping/" target="_blank">displacement.js @Author Romuald</a></p>
<h5>Main.js</h5>
<pre class="brush: jscript; title: ; notranslate">
  function draw(){
		
		ctx.clearRect(0, 0, WIDTH, HEIGHT);
		ctx.drawImage(lens, 0, 0, WIDTH, HEIGHT);
		
		mirror_ctx.drawImage(video, 0, 0, WIDTH, HEIGHT);
		mirror_ctx.setTransform(-offset, 0, 0, offset, WIDTH*offset, 50);
		
		var filter = new filters.DisplacementMap(
		mirror,
		map,
		mirror,
		new filters.Point(0,30),
		0,
		-30,
		filters.ColorChannel.RED,
		filters.ColorChannel.RED);
		filter.draw();
		
		mirror_ctx.blendOnto( ctx, 'overlay');
		
	}
  </pre>
<p>結果として、かなり重い実装になってしまいました。<br />
  DisplacementMapFilterの処理に時間がかかっているので、次回のjavascript関連投稿は<br />
  この処理をホヤホヤのCSS Shadersで実装したいと思います。</p>
]]></content:encoded>
			<wfw:commentRss>https://takepepe.com/canvas-001-webrtc/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Flash #001 Media</title>
		<link>https://takepepe.com/flash-001/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=flash-001</link>
		<comments>https://takepepe.com/flash-001/#comments</comments>
		<pubDate>Fri, 15 Feb 2013 13:56:13 +0000</pubDate>
		<dc:creator>Takepepe</dc:creator>
				<category><![CDATA[Flash]]></category>

		<guid isPermaLink="false">http://takepepe.com/?p=195</guid>
		<description><![CDATA[flash on 2013-2-15 &#8211; wonderfl build flash online  [...]]]></description>
				<content:encoded><![CDATA[<p><script type="text/javascript" src="http://wonderfl.net/blogparts/t0eb/js"></script></p>
<p class="ttlBpWonderfl" style="width: 465px; margin: 0; text-align: right; font-size: 11px;"><a href="http://wonderfl.net/c/t0eb" title="flash on 2013-2-15">flash on 2013-2-15 &#8211; wonderfl build flash online</a></p>
<p>Takepepeはスノボが大好きです。<br />
  痛いのが嫌なので、パウダー派です。冬最高！</p>
<p>SNSなどに利用している自分のアバター画像はスノボウェアの自分…<br />
  ではなく、中身はそこら辺にあった毛布を帽子に突っ込んで撮影しましたw</p>
<p>iPhoneで撮影したら、レンズにバッチリ自分も写っていたため、Photoshopで編集することに。<br />
  静止画には風景を合成して入れていますが、今回はここにウェブカムの映像を映してそれっぽく変形させます。<br />
  ActionScriptには便利なフィルターがたくさんあるので、そいつでグニャっとね。</p>
<p>おおまかな流れは以下</p>
<ol>
<li>画像を読み込んで各レイヤーを配置</li>
<li>カメラ映像を映す</li>
<li>変形に使うフォースマップを用意</li>
<li>映写レイヤーを変形</li>
<li>ミラー反転も忘れずに</li>
</ol>
<h2>1.画像を読み込んで各レイヤーを配置</h2>
<p>swfに画像を埋め込んだり、同ドメイン内の外部画像を読み込む場合はここは飛ばしていいです。<br />
  Wonderflに投稿して自サーバーの画像を読み込む場合はクロスドメインポリシーファイルをサイトルートなどに設置する必要があります。</p>
<h5>crossdomain.xml</h5>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;cross-domain-policy&gt;
  &lt;allow-access-from domain=&quot;wonderfl.kayac.com&quot; /&gt;
  &lt;allow-access-from domain=&quot;wonderfl-swf.kayac.com&quot; /&gt;
  &lt;allow-access-from domain=&quot;*.wonderfl.net&quot; /&gt;
&lt;/cross-domain-policy&gt;
</pre>
<h5>Main.as</h5>
<pre class="brush: as3; title: ; notranslate">
public function Main() {

	// クロスドメインポリシーファイルの読み込み
	Security.loadPolicyFile( &quot;http://takepepe.com/crossdomain.xml&quot; ) ;
    
}

private function setAvator():void{

	lens = new Bitmap (lensBmd);
    addChild(lens);
    
    screenBmd = new BitmapData(465,465,true,0x00000000);
    screen = new Bitmap(screenBmd);
    screen.blendMode = &quot;overlay&quot;; //ブレンドモード設定
    addChild(screen);
    
    frame = new Bitmap (frameBmd);
    addChild(frame);
    
}
</pre>
<p>レンズ下地、映写レイヤー、フレームの順に重ねたら、映写レイヤーにはこの段階でblendModeを設定しておきます。<br />
  &#8220;overlay&#8221;で、レンズっぽくなります。</p>
<h2>2.カメラ映像を映す</h2>
<p>このVideoインスタンスはaddChildする必要はありません。<br />
  VideoとCameraのインスタンス設定は以下の箇所です。</p>
<h5>Main.as</h5>
<pre class="brush: as3; title: ; notranslate">
private function setCamera():void{

	camera = Camera.getCamera();
    camera.setMode(WIDTH,HEIGHT,30,true);
    camera.addEventListener(ActivityEvent.ACTIVITY,onActiveCamera);
    video = new Video(WIDTH,HEIGHT);
    video.attachCamera(camera);
    
}
</pre>
<h2>3.変形に使うフォースマップを用意</h2>
<p>赤ければ赤いほど、下方向にピクセルが移動する力が働いています。<br />
  インタラクションでグラデーションを動かせると、ActionScriptでしか出来ない表現ができるようになります。<br />
  Flashをクリックするとグラデーションの表示・非表示が切り替わります。<br />
  フィルターの設定は4で。</p>
<p>    <script type="text/javascript" src="http://wonderfl.net/blogparts/cP1H/js"></script></p>
<p class="ttlBpWonderfl" style="width: 465px; margin: 0; text-align: right; font-size: 11px;"><a href="http://wonderfl.net/c/cP1H" title="Flash #001 Media'">Flash #001 Media&#8217; &#8211; wonderfl build flash online</a></p>
<h2>4.映写レイヤーを変形</h2>
<p>X軸の変形は必要ないので、適当な値を入れています。</p>
<h5>Main.as</h5>
<pre class="brush: as3; title: ; notranslate">
private function update(e:Event):void{
	
    var mapPoint:Point = new Point(0, 0); 
    var componentX:uint = BitmapDataChannel.GREEN;
    var componentY:uint = BitmapDataChannel.RED;
    var forceX:Number = 0;
    var forceY:Number = 200; 
    var my_filter:DisplacementMapFilter = new DisplacementMapFilter(transformer, mapPoint, componentX, componentY, forceX, forceY, &quot;color&quot;, 0, 0);
    screen.filters = new Array(my_filter);
    
}
</pre>
<h2>5.ミラー反転も忘れずに</h2>
<p>鏡面に映っている表現なので、映写の際にMatrixで反転させます。</p>
<h5>Main.as</h5>
<pre class="brush: as3; title: ; notranslate">
private function draw(e:Event):void{
	
    var matrix:Matrix = new Matrix();
    matrix.scale(-1, 1);
    matrix.translate(screenBmd.width, 0);
    screenBmd.draw(video);
			
}
</pre>
<p>全ソースは<a href="http://wonderfl.net/c/t0eb" target="_blank">Wonderfl</a>を参照してください。<br />
  次回はWebRTCとcanvasで同様の表現に挑戦したいと思います。</p>
]]></content:encoded>
			<wfw:commentRss>https://takepepe.com/flash-001/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress #001</title>
		<link>https://takepepe.com/wordpress-001/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=wordpress-001</link>
		<comments>https://takepepe.com/wordpress-001/#comments</comments>
		<pubDate>Fri, 15 Feb 2013 04:00:57 +0000</pubDate>
		<dc:creator>Takepepe</dc:creator>
				<category><![CDATA[Server]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://takepepe.com/?p=147</guid>
		<description><![CDATA[記念すべき第一回目の投稿です。 このブログでは、新しめのことから、ちょっと流行遅れのことまでいろいろ取り上げて [...]]]></description>
				<content:encoded><![CDATA[<p>記念すべき第一回目の投稿です。<br />
  このブログでは、新しめのことから、ちょっと流行遅れのことまでいろいろ取り上げていきたいと思います。少し古くても、アイデア次第ではまだまだ面白くなる技術は多数あります。</p>
<p>Google先生と共に技術を蓄えてきたたわけですが、もちろんGoogle先生だけでなく、偉大な先人たちのおかげで多くのことを学びました。改めて敬意を表するとともに、私と同じように、これから独学でWEBデザイン・メディアアートに挑戦しようとしている皆さんの役にたてればと思い、ブログを立ち上げました。というわけで第一回目の投稿は、当ブログでも利用しているWordPressに関する記事です。</p>
<p>ご存知のとおりWordPressはPHPで構成されたブログシステムで、配布されているテーマやプラグインで自前のブログを短時間で簡単に作れてしまいます。多くのレンタルサーバーではボタン一発でインストールが出来たりと、ハードルは低いです。WordPressのインストール方法や便利なプラグインの紹介は既に良記事がたくさんあるので、そちらを参考にしてください。</p>
<h2>「Takepepe.com」TOPに用いている表現</h2>
<p>このブログでは記事を投稿する際にアイキャッチ画像（以下サムネイル）が選択されていると、ひとつなぎになったグレースケール画像がPHPで生成されるようになっています。わざわざPHPで画像を生成しなくても、CSSやcanvasで実装出来そうですが、今回はこの方法をとっています。具体的な流れは以下の通りです。</p>
<ol>
<li>記事を投稿する際に、サムネイル生成関数を呼ぶ</li>
<li>生成関数で輝度・彩度の調整をして合成</li>
<li>TOP用のWordPressテンプレートを作成</li>
</ol>
<p>ブログを手っ取り早く立ち上げたかったため、デフォルトのテーマを直にいじりました。<br />
  バックアップなどは自己責任でお願いします</p>
<h3>1.記事を投稿する際に、サムネイル生成関数を呼ぶ</h3>
<p>post.php に数行追加します。<br />
  生成関数は2に記述します。</p>
<h5>「wp-admin/post.php」を編集</h5>
<pre class="brush: php; title: ; notranslate">
  
  
// ファイル上部に追加
require_once('./makeTopThum.php');

////// 〜中略〜 //////

// ファイル末に追加
if(has_post_thumbnail($post_id)){ //サムネイルがあった場合
	
	//「makeTopThum.php」に記述されている関数
	makeTopThum(get_the_post_thumbnail($post_id));
	
}

</pre>
<h3>2.生成関数で輝度・彩度の調整をして合成</h3>
<p>PHPで画像にフィルターなどかけながら8枚の画像を1つにまとめます。<br />
  画像パスの取得はもっと良い方法がありそうですが、とりあえず。<br />
  画像生成はそれなりにサーバー負荷がかかりますので、クライアントワーク等に使用する際は気をつけてください。</p>
<h6>Before</h6>
<p>  <img src="/wp-content/uploads/2013/02/WordPress_001.jpg" width="80" /></p>
<h6>After</h6>
<p>  <img src="/wp-content/uploads/2013/02/WordPress_001-top.jpg" width="644" /></p>
<h5>「wp-admin/makeTopThum.php」を新規作成</h5>
<pre class="brush: php; title: ; notranslate">
&lt;?php
function makeTopThum($_img){
	
    $THUM_WIDTH = 160; //サムネイル幅
    $THUM_HEIGHT = 90; //サムネイル高さ
    $NUM = 8; //画像数
	$WIDTH = ($THUM_WIDTH+1)*$NUM; //出力する画像の幅
	$HEIGHT = $THUM_HEIGHT+1; //出力する画像の高さ
	
    
	//出力先の画像を作成
	$dst = imagecreatetruecolor($WIDTH, $HEIGHT);
	
	//白で塗る
	$white = imagecolorallocate($dst, 0xFF, 0xFF, 0xFF);
	imagefill($dst, 0, 0, $white);
	
	//アイキャッチ画像のパス
	preg_match_all(&quot;/&lt;img [^&gt;]*src\s*=\s*[\&quot;']?([^\&quot;'&gt; ]+)/i&quot;, $_img, $mt);
	$srcs = array_map('htmlspecialchars_decode', $mt[1]);
	$src = $srcs[0];
	
	//画像名の取得
	$index = mb_strrpos($src,&quot;/&quot;);
	$name = substr($src, $index+1, -4);
	
	//画像ディレクトリの取得
	$index = mb_strrpos($src,&quot;/&quot;);
	$dir = $_SERVER['DOCUMENT_ROOT'];
	$dir .= substr($src, 19, $index-18);
	
	//新画像のパス
	$new = $dir.$name.&quot;-top.jpg&quot;;
	
	//画像を8つ並べる
	$arr = array(200,150,100,50,0,50,100,150); //輝度配列
	for($i = 0; $i&lt;$NUM ;$i++){
		$thum = imagecreatefromjpeg($src);
		imagefilter($thum, IMG_FILTER_BRIGHTNESS, $arr[$i]); //輝度調整
		imagefilter($thum, IMG_FILTER_GRAYSCALE); //グレースケール変換
		imagecopy($dst, $thum, $i*($THUM_WIDTH+1), 0, 0, 0, $THUM_WIDTH, $THUM_HEIGHT);　//画像配置
		imagedestroy($thum);
	}
	
	//画像保存
	imagejpeg($dst, $new, 100);
	imagedestroy($dst);
	
}

?&gt;
</pre>
<h3>3.TOP用のWordPressテンプレートを作成</h3>
<p>最後にTOP用のテンプレートを作成して、CSSを調整すれば完成です。</p>
<h5>「wp-content/themes/(利用しているテーマ)/content-top.php」を新規作成</h5>
<pre class="brush: php; title: ; notranslate">


&lt;?php

$title = get_the_title(); //タイトル
$date = get_the_date(); //投稿日
$url = get_permalink(); //パーマリンク

//アイキャッチ画像のパス
$img = get_the_post_thumbnail();

//統合画像のパスに変換
preg_match_all(&quot;/&lt;img [^&gt;]*src\s*=\s*[\&quot;']?([^\&quot;'&gt; ]+)/i&quot;, $img, $mt);
$srcs = array_map('htmlspecialchars_decode', $mt[1]);
$src = $srcs[0];
$path = substr($src, 19, -4).&quot;-top.jpg&quot;;

echo &quot;&lt;a href=\&quot;$url\&quot; style=\&quot;background:url($path);\&quot;&gt;&lt;span class=\&quot;title\&quot;&gt;$title&lt;span class=\&quot;date\&quot;&gt;$date&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&quot;;

?&gt;


</pre>
<p>デザイナーの人も、サーバーでPhotoshopみたいに画像を編集出来ることを知っていれば、CMS経由のコンテンツデザインの幅がぐっと広がります。是非、挑戦してみてください。</p>
]]></content:encoded>
			<wfw:commentRss>https://takepepe.com/wordpress-001/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

 Served from: takepepe.com @ 2026-04-07 05:06:19 by W3 Total Cache -->