The jonki

呼ばれて飛び出てじょじょじょじょーんき

【Android】Androidで描くベジェ曲線

とりあえずベジェ曲線Android上で書きます。別にAndroidじゃなくてもいいけど。

最初に実行例を見せるとこんな感じ。とりあえず最初なので3点を与えたときのベジェ曲線


解説

ベジェ曲線とは何ぞやという方はまずこちら様のサイトを一読。
中学生でもわかるベジェ曲線


何となく頭のイメージをこんな感じで作る。要はt:(1-t)にどんどん内分していけばいいわけですね。塾講師をしていた時を思い出しながらソースに落とします。

ソース
/**
 * 
 */
package practice.android.net.joki;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.opengl.GLSurfaceView;

/**
 * @author jonki
 *
 */
public class GLRendererBezier implements GLSurfaceView.Renderer {

	private float sw, sh;
	
	private final int VERTICES_NUM = 50;
	
	private final Point p0 = new Point(-0.f, -0.8f);
	private final Point p1 = new Point(-0.6f, 0.5f);
	private final Point p2 = new Point(0.7f, 0.6f);
	private Point p3 = new Point();
	private Point p4 = new Point();
	private Point p5 = new Point();
	
	@Override
	public void onDrawFrame(GL10 gl10) {
		gl10.glClearColor(0.f, 0.f, 0.f, 1.f);
		gl10.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
		
		gl10.glMatrixMode(GL10.GL_PROJECTION);
		gl10.glLoadIdentity();
		
		gl10.glMatrixMode(GL10.GL_MODELVIEW);
		gl10.glLoadIdentity();
		
		float[] lineVertices = new float[VERTICES_NUM * 2];	//!< 頂点数×座標分(xとyの2つ)
		for(int i = 0; i < VERTICES_NUM * 2; i += 2)
		{
			//!< tは0~1で動くよ
			float t = (float)(i) / 2.f / (float)VERTICES_NUM;
			
			//!< 内分点を計算していく。*演算子をオーバーロードした方がいいが、今回は勉強のため冗長な書き方
			
			//!< p0とp1をt:(1-t)に内分する点
			p3.x = t * p1.x + (1.f - t) * p0.x;
			p3.y = t * p1.y + (1.f - t) * p0.y;
			
			//!< p1とp2をt:(1-t)に内分する点
			p4.x = t * p2.x + (1.f - t) * p1.x;
			p4.y = t * p2.y + (1.f - t) * p1.y;
			
			//!< p3とp4をt:(1-t)に内分する点
			p5.x = t * p4.x + (1.f - t) * p3.x;
			p5.y = t * p4.y + (1.f - t) * p3.y;
			
			//!< xとyを順番に詰め込んでいく
			lineVertices[i] = p5.x;
			lineVertices[i+1] = p5.y;
		}
		
		//!< ベジエ曲線の軌跡がわかりやすいよう点で描く
		//!< 固定座標なので毎回転送するのは無駄なので、アプリではvertex buffer objectは使うべき
		{
			FloatBuffer fb = ByteBuffer.allocateDirect(lineVertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
			fb.put(lineVertices);
			fb.position(0);
			gl10.glPointSize(4.f);
			gl10.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			gl10.glVertexPointer(2, GL10.GL_FLOAT, 0, fb);
			gl10.glColor4f(1.f, 0.f, 0.f, 1.f);
			gl10.glDrawArrays(GL10.GL_POINTS, 0, VERTICES_NUM);
		}
		
		//!< 基準となる制御点を描く
		//!< 固定座標なので毎回転送するのは無駄なので、アプリではvertex buffer objectは使うべき
		{
			float[] baseLineVertices = new float[]{
					p0.x, p0.y,
					p1.x, p1.y,
					p2.x, p2.y
			};
			FloatBuffer fb = ByteBuffer.allocateDirect(baseLineVertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
			fb.put(baseLineVertices);
			fb.position(0);
			gl10.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			gl10.glVertexPointer(2, GL10.GL_FLOAT, 0, fb);
			gl10.glColor4f(0.f, 0.f, 1.f, 1.f);
			gl10.glDrawArrays(GL10.GL_LINE_STRIP, 0, 3);
		}
	}

	@Override
	public void onSurfaceChanged(GL10 gl10, int width, int height) {
		sw = (float)width;
		sh = (float)height;
		aspect = sw / sh;
		gl10.glViewport(0, 0, width, height);
	}

	@Override
	public void onSurfaceCreated(GL10 gl10, EGLConfig arg1) {
	}
	
	class Point
	{
		public Point()
		{
			this.x = 0.f;
			this.y = 0.f;
		}
		public Point(float x, float y)
		{
			this.x = x;
			this.y = y;
		}
		public float x;
		public float y;
	}
}