3次元グラフ

C言語による最新アルゴリズム事典 (ソフトウェアテクノロジー)

C言語による最新アルゴリズム事典 (ソフトウェアテクノロジー)

C言語による最新アルゴリズム事典で紹介されている、3次元グラフをJavaで描いてみました。
y = f( x, z )の関数を3次元のグラフで表現するというものです。

以下は、y = exp( - ( x * x + z * z ) ) * cos( 10 * sqrt( ( x * x + z * z ) )を描いた結果です。

以下は、これを描くためのJavaコードです。
主要なアルゴリズムの箇所は、事典の内容をそのままです。
描画メソッド(move_()、draw()とpaint())のみ自前で用意しています。

import java.util.*;
import java.awt.*;
import javax.swing.*;

/**
 * 三次元グラフのウィンドウ
 */
public class ThreeDimensionGraph extends JFrame {

	/**
	 * メインルーチン
	 */
	public static void main( String arsg ) {
		// 三次元グラフのウィンドウを起動する。
		new ThreeDimensionGraph();
	}
	
	/**
	 * 三次元グラフのウィンドウを起動する。
	 */
	public ThreeDimensionGraph() {
		// 閉じるボタンのクリック時、ウィンドウを破棄する。
		setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
		// ウィンドウのサイズを設定する。
		setSize(1000, 600);
		// ウィンドウを表示する。
		setVisible( true );
		
		// 描画アルゴリズムを実行する。
		drawFunction();
	}
	
	//////////////////////////////////////////////
	// 描画アルゴリズム START
	//////////////////////////////////////////////

	private static final double Xmin = -1;
	private static final double Ymin = -1;
	private static final double Zmin = -1;
	private static final double Xmax = 1;
	private static final double Zmax = 1;
	private static final double Ymax = 1;

	/**
	 * 描画アルゴリズムを実行する。
	 */
	private void drawFunction() {
		double x, y, z;
		int i, ix, iz;
		boolean ok, ok1;
		double lowerhorizon = new double[241];
		double upperhorizon[] = new double[241];
		
		for ( i = 0; i <= 240; ++i ) {
			lowerhorizon[i] = Float.MAX_VALUE;
			upperhorizon[i] = Float.MIN_VALUE;
		}
		for ( iz = 0; iz <= 20; ++iz ) {
			z = Zmin + ( Zmax - Zmin ) / 20 * iz;
			ok1 = false;
			for ( ix = 0; ix <= 200; ++ix ) {
				x = Xmin + ( Xmax - Xmin ) / 200 * ix;
				i = ix + 2 * ( 20 - iz );
				y = 30 * ( func( x, z ) - Ymin ) / ( Ymax - Ymin ) + 5 * iz;
				
				ok = false;
				if ( y < lowerhorizon[i] ) {
					lowerhorizon[i] = y;
					ok = true;
				}
				if ( y > upperhorizon[i] ) {
					upperhorizon[i] = y;
					ok = true;
				}
				if ( ok && ok1 ) draw( i, y );
				else move_( i, y );
				ok1 = ok;
			}
		}
	}
	
	//////////////////////////////////////////////
	// 描画アルゴリズム END
	//////////////////////////////////////////////

	/**
	 * 描画対象の関数
	 */
	private double func( double x, double z ) {
		double r2;
		r2 = x * x + z * z;
		return Math.exp( -r2 ) * Math.cos( 10 * Math.sqrt( r2 ) );
	}
	
	// ペンの座標
	private Coordinate pen;
	// 線分のリスト
	private java.util.List lines = new ArrayList();
	
	/**
	 * 線分のリストの内容を描画する。
	 */
	public void paint( Graphics g ) {
		// 表示内容をクリアする。
		g.clearRect( 0, 0, 1000, 600 );
		// リスト中のすべての線分を表示する。
		// うまく表示できるように座標の補正を行う。
		for ( Line line : lines ) {
			g.drawLine( adjust( line.start().x() ), 600 - adjust( line.start().y() ), adjust( line.end().x() ),600 -  adjust( line.end().y() ) );
		}
	}
	
	/**
	 * double型の座標値を補正し、int型にする。
	 * @param d 補正前の座標値
	 * @return 補正後の座標値
	 */
	private int adjust( double d ) {
		return (int) ( d * 4.0 );
	}
	
	/**
	 * 線分をリストに追加する。
	 */
	private void draw( double x, double y ) {
		// ペンの座標から指定した座標までの線分をリストに追加する。
		Coordinate temp = new Coordinate().x( x ).y( y );
		lines.add( new Line().start( pen ).end( temp ) );
		// ペンの座標を指定した座標に変更する。
		pen = new Coordinate().x( x ).y( y );
	}

	/**
	 * ペンの座標を指定した座標に変更する。
	 * @param x X座標
	 * @param y Y座標
	 */
	private void move_( double x, double y ) {
		pen = new Coordinate().x( x ).y( y );
	}
	
	private Container contentPane() {
		return getContentPane();
	}
}

/**
 * 線分
 */
class Line {
	private Coordinate start;	// 始点の座標
	private Coordinate end;		// 終点の座標
	
	/**
	 * 始点の座標を設定する。
	 * @param start 始点の座標
	 * @return 自分
	 */
	public Line start( Coordinate start ) {
		this.start = start;
		return this;
	}
	
	/**
	 * 終点の座標を設定する。
	 * @param end 終点の座標
	 * @return 自分
	 */
	public Line end( Coordinate end ) {
		this.end = end;
		return this;
	}
	
	/**
	 * 始点の座標を取得する。
	 * @return 始点の座標
	 */
	public Coordinate start() {
		return start;
	}
	
	/**
	 * 終点の座標を取得する。
	 * @return 終点の座標
	 */
	public Coordinate end() {
		return end;
	}
}

/**
 * 座標
 */
class Coordinate {
	
	private double x;	// X座標
	private double y;	// Y座標
	
	/**
	 * X座標を設定する。
	 * @param x X座標
	 * @return 自分
	 */
	public Coordinate x( double x ) {
		this.x = x;
		return this;
	}
	
	/**
	 * Y座標を設定する。
	 * @param y Y座標
	 * @return 自分
	 */
	public Coordinate y( double y ) {
		this.y = y;
		return this;
	}
	
	/**
	 * X座標を取得する。
	 * @param X座標
	 */
	public double x() {
		return x;
	}
	
	/**
	 * Y座標を取得する。
	 * @param Y座標
	 */
	public double y() {
		return y;
	}
}

何かに使えないだろうか・・・。