<?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 &#187; Processing</title>
	<atom:link href="https://takepepe.com/category/processing/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>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>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

 Served from: takepepe.com @ 2026-04-11 08:19:10 by W3 Total Cache -->