Home >> Columns >> Patterns


Javaでの整数を使ったアルゴリズム


P−1.文様の歴史、分類、修飾譜について

☆日本の文様の分類

☆日本の文様の歴史について

☆割付文様一覧

☆文様の修飾譜

☆参考文献

日本・中国の文様辞典、視覚デザイン研究所編、視覚デザイン研究所、2000年、ISBN4-88108-150-0.


P−2.割付文様の描画方法

ここでは、タートルグラフィックスのライブラリを用いて、いくつかの割付文様をどのように描画するかに ついて、具体的なプログラムとプログラミング手法について、説明していきます。 以下のプログラムの構造は、大きな部分では同じになっています。

☆青海波(単純版)の描画方法

青海波は、goBackは単に戻るまでのことしかやっていません。実数の誤差によって、 drawArcを繰り返すことで、微妙に右肩上がりになっています。


	Turtle  turtle;
	
	public void start( ) {
		// ここにあなたのコードを書きます
		turtle = new Turtle( this );
		for ( int i=0; i < 20; i++ ) {
			fillingLine( 8 );
			goBack( 8 );
			drawArc( 2, (i%2==0) ? 60 : -60 , 2 );
			turtle.rotate( (i%2==0) ?-150 : -30 );
		}
	}

	void goBack( int n ) {
		turtle.rotate( 150 );
		for ( int i=0; i < n ; i++ ) {
			drawArc( 2, -120, 2 );
			turtle.rotate( 120 );
		}
		turtle.rotate( -60 );
	}
	
	void fillingLine( int n ) {
		turtle.penup( );
		turtle.rotate( 30 );
		for ( int i=0; i < n ; i++ ) {
			turtle.pendown( );
			drawArc( 2, 120, 2 );
			turtle.penup( );
			turtle.rotate( -120 );
		}
		turtle.rotate( -30 );
	}
	
	void drawArc( int step, int diff, int anglestep ) { 
		if ( diff >= 0 ) {
			for ( int  angle = 0; angle < diff; angle += anglestep ) {
				turtle.forward( step );
				turtle.rotate( anglestep );				
			}
		} else {
			for ( int angle = 0; angle < -diff; angle += anglestep ) {
				turtle.rotate( -anglestep );
				turtle.back( step );
				
			}
		}
	}
	
	

☆七宝紋の描画方法

七宝紋で塗りつぶしをしようと思ったら、arc sine, arc cosineやarc tangent(それぞれJavaではMath.asin, Math.acos, Math.atan2)を使い、1ラインごとに描く必要があると思います。トライしてみて下さい。 七宝紋も、青海波と同じで、drawArcの実数誤差によって、微妙に右肩上がりになっています。

	
	Turtle  turtle;
	
	public void start( ) {
		turtle = new Turtle( this );
		for ( int i=0; i < 10; i++ ) {
			fillingLine( 10 );
			goBack( 10 );
		}
	}
	
	void goBack( int n ) {
		turtle.penup( );
		turtle.rotate( 180 );
		for ( int i=0; i < n; i++ ) {
			drawArc( 2, -180, 2 );
			turtle.rotate( 180 );
		}
		turtle.rotate( -90 );
		drawArc( 2, 180, 2 );
		turtle.rotate( 90 );
		turtle.pendown( );
	}
	
	void fillingLine( int n ) {
		for ( int i=0; i < n ; i++ ) {
			makeShippou( );
			turtle.penup( );
			drawArc( 2, 180, 2 );
			turtle.rotate( -180 );
			turtle.pendown( );
		}
	}

	void makeShippou( ) {
		for ( int i=0; i < 4; i++ ) {
			drawLeaf( );
		}
	}
	
	void drawLeaf( ) {
		drawArc( 2, 90, 2 );
		turtle.rotate( 90 );
		drawArc( 2, 90, 2 );
	}
	
	void drawArc( int step, int diff, int anglestep ) { 
		if ( diff >= 0 ) {
			for ( int  angle = 0; angle < diff; angle += anglestep ) {
				turtle.forward( step );
				turtle.rotate( anglestep );				
			}
		} else {
			for ( int angle = 0; angle < -diff; angle += anglestep ) {
				turtle.rotate( -anglestep );
				turtle.back( step );
				
			}
		}
	}
	
	

☆亀甲紋の描画方法

このプログラムだけ、enumという構文が使われています。これは、列挙型と呼ばれるもので、 要は整数の定数のように使います。ただし、C/C++と異なり、Javaでは具体的に整数のどの値を 記号に割り当てるということはできません。あくまでも、記号として使うだけになっています。


	enum Direction { Right, Left, DownRight, DownLeft };
	

上記の記述は、Directionという列挙型を定義し、4つの記号を定義しています。これらは、 文様を描いた後、タートルを次の地点に行かせるときに、goNextメソッドへの指示として 使われています。それぞれ、Rightが「右へ」、Leftが「左へ」、DownRightが「右下へ」、 DownLeftが「左下へ」ということを示しています。goNextメソッドでは、その指示を受け取り、 タートルをDirectionが示す方向へ移動させています。


	Turtle turtle;
	
	enum Direction { Right, Left, DownRight, DownLeft };
	
	public void start( ) {
		turtle = new Turtle( this );
		int vertical = 20;
		int horizontal = 20;
		int size = 30;
		for ( int i=0; i < vertical; i ++ ) {
			fillingLine( size, horizontal );
			goBack( size, horizontal );
			goNext( size, (i%2==0) ? Direction.DownRight : Direction.DownLeft );
		}
	}

	void goBack( int size, int n ) {
		for ( int i=0; i < n; i++ ) {
			goNext( size, Direction.Left );
		}
	}
	
	void fillingLine( int size, int n ) {
		for ( int i=0; i < n; i++ ) {
			makeKomochiHexagon( size );
			goNext( size, Direction.Right );
		}
	}
	
	void goNext( int size, Direction d ) {
		turtle.penup( );
		turtle.rotate( (d==Direction.Right || d == Direction.DownRight) ? 120 : -120 );
		turtle.forward( size );
		turtle.rotate( (d==Direction.Right || d == Direction.DownLeft ) ? -60 : 60 );
		turtle.forward( size );
		turtle.rotate( (d==Direction.Right ) ? -60 : (d==Direction.Left ) ? 60 : 180 );
		turtle.pendown( );
	}
	
	void makeHexagon( int size ) {
		for ( int i=0;  i < 6; i++ ) {
			turtle.forward( size );
			turtle.rotate( 60 );
		}
	}
	
	void makeKomochiHexagon( int size ) {
		for ( int j=0;  j <= 1; j++ ) {
			turtle.penup( );
			turtle.rotate( 60 );
			turtle.forward( size * j / 5 );
			turtle.rotate( -60 );
			turtle.pendown( );
	
			for ( int i=0;  i < 6; i++ ) {
				turtle.forward( size * (5-j) / 5 );
				turtle.rotate( 60 );
			}
			turtle.penup( );
			turtle.rotate( -120 );
			turtle.forward( size * j / 5);
			turtle.rotate( 120 );
			turtle.pendown( );
		}
	}

	

☆檜垣紋の描画方法


	Turtle  turtle;
	
	public void start( ) {
		turtle = new Turtle( this );
		turtle.rotate( -45 );
		for ( int i=0; i < 10; i++ ) {
			fillingLine( 20, 10 );
			goBack( 20, 10 );
		}
	}
	
	void goBack( int size, int n ) {
		turtle.penup( );
		turtle.rotate( 90 );
		for ( int i=0; i < n; i++ ) {
			turtle.left( size * 2 );
			turtle.back( size * 2 );
		}
		turtle.back( size );
		turtle.right( size );
		turtle.rotate( -90 );
		turtle.pendown( );
	}
	
	void fillingLine( int size, int n ) {
		for ( int i=0; i < n ; i++ ) {
			makeHigaki( size );
			turtle.penup( );
			turtle.right( size * 3 );
			turtle.rotate( -90 );
			turtle.pendown( );
		}
	}
	
	void makeHigaki( int size ) {
		for ( int i=0; i < 10; i++ ) {
			turtle.forward( (i%2==0) ? size : size * 2 );
			turtle.rotate( (i != 5 ) ? 90 : 0 );
		}
	}

	

☆麻の葉紋の描画方法

これは、三角形の真ん中の三本線を描くために、√3を利用しています。そのため、実数で動いています。


	Turtle  turtle;
	
	public void start( ) {
		turtle = new Turtle( this );
		int size = 40;
		for ( int i=0; i < 20; i++ ) {
			fillingLine( size, 10 );
			turtle.penup( );
			goBack( size, 10 );
			turtle.rotate( (i%2==0) ?120 : -120 );
			turtle.forward( size );
			turtle.rotate( (i%2==0) ? -120 : 120 );
			turtle.pendown( );
		}
	}
	
	void goBack( int size, int n ) {
		for ( int i=0; i < n; i++ ) {
			turtle.rotate( -60 );
			turtle.forward( size );
			turtle.rotate( -60 );
			turtle.forward( size );
			turtle.rotate( 120 );
		}
	}

	void fillingLine( int size, int n ) {
		for ( int i=0; i < n; i++ ) {
			makeAsanoha( size );
			turtle.penup( );
			turtle.rotate( 60 );
			turtle.forward( size );
			turtle.rotate( 60 );
			turtle.forward( size );
			turtle.rotate( -120 );
			turtle.pendown( );
		}
	}
	
	void makeAsanoha( int size ) {
		for ( int i=0; i < 6; i++ ) {
			turtle.forward( size );
			turtle.rotate( ( i < 3 ) ? 120 : -120 );
		}
		double distance = 1.0 / Math.sqrt( 3 )  * size;
		turtle.rotate( 30 );
		for ( int i=0; i < 6; i++ ) {
			turtle.forward( (i <= 1 || i == 3) ? distance : -distance );
			turtle.rotate( (i == 0) ? -60 : (i==2) ? 120 : (i==4 ) ? -60 : 0 );
	   }
	   turtle.rotate( -60 );
	   for ( int i=0; i < 6; i++ ) {
			turtle.forward( (i <= 1 || i == 3) ? distance : -distance );
			turtle.rotate( (i == 0) ? -60 : (i==2) ? 120 : (i==4 ) ? -60 : 0 );
	   }
	   turtle.rotate( 30 );
	}

	

☆工字繋ぎの描画方法

工字繋ぎは、配列(Lecture 9を参照)を用いて、1つの割付文様を描いています。 「工」の字の左下から始まり、上の線を描くまでを配列にしています(pattern)。 また、描いた後に何度タートルを回転させるかも配列にしています(angles)。 まず、patternの値は、次の図を見て下さい。

	
	□□□□□
	□ □ □
	  □
	  □
	□ □ □
	□□□□□
		

左下の隅から、上に行くようにして、四角形の外側をなぞるようにしていくと、四角形の一辺の 個数が数えられます。2, 1, 1, 1, 4, 1, 1, 1, 2, 5という形になります。このように直立した形の場合は、 90度と-90度で良いのですが、斜めに寝かせていますので、描画した後に回転させる角度として、 60度、120度、-60度、-120度を使っています。

inoutがわかりにくいかも知れませんが、内側に少し小さい文様を描くために用いられています。 マイナスの場合は、本来の線に対して内側にあることで、短くなっていることを示し、プラスの場合は、 本来の線よりも長くなっていることを示します。

fillingPatternというメソッドが定義されていますが、これは組み合わさった「工」の字を2つ描く ために設けたものです。2つずつ描きながら、横方向に進んでいきます。


	Turtle  turtle;
	
	public void start( ) {
		turtle = new Turtle( this );
		turtle.rotate( 45 );
		for ( int i=0; i < 10; i++ ) {
			 fillingLine( 10, 10 );
			 goBack( 10, 10 );
		}
	}

	void goBack( int size, int n ) {
		turtle.penup( );
		turtle.setDirection( turtle.getDirection( )-30 );
		turtle.left( size * 6 * n );
		turtle.setDirection( turtle.getDirection( )+30 );
		turtle.back( size * 6 );
		turtle.pendown( );
	}
	
	void fillingLine( int size, int n ) {
		for ( int i=0; i < n; i++ ) {
			fillingPattern( size );
		}
	}

	void fillingPattern( int size ) {
		for ( int i = 0; i < 2; i++ ) {
			makePattern( size );
			turtle.penup( );
			turtle.forward(  (i==0) ? size * 3 : -size * 3 );
			turtle.setDirection( turtle.getDirection( )-30 );
			turtle.right(  size*3 );
			turtle.setDirection( turtle.getDirection( )+30 );
			turtle.pendown( );
		}
	 }
	
	 void makePattern( int size ) {
		int pattern [ ] = { 2,   1,   1, 1, 4, 1, 1, 1, 2, 5 };
		int angles[ ]   = { 60, 120, -120, -60, -120, -60, 60, 120, 60, 120 };
		int inout[ ]   = { -2,   -2,  -2,  2,  2, 2, -2, -2, -2, -2 };
		int offset = size / 10;

		// parent
		/*
		for ( int i=0; i < pattern.length*2; i++ ) {
			turtle.forward( size * pattern[ i % pattern.length ] );
			turtle.rotate( angles[ i % pattern.length ] );
		}
		*/

		// child
		turtle.penup( );
		turtle.forward( offset );
		turtle.setDirection( turtle.getDirection( )+30 );
		turtle.right( offset );
		turtle.setDirection( turtle.getDirection( )-30 );
		turtle.pendown( );

		for ( int i=0; i < pattern.length*2; i++ ) {
			turtle.forward( size * pattern[ i % pattern.length ] + offset*inout[ i % pattern.length ] );
			turtle.rotate( angles[ i % pattern.length ] );
		}

		turtle.penup( );
		turtle.back( offset );
		turtle.setDirection( turtle.getDirection( )+30 );
		turtle.left( offset );
		turtle.setDirection( turtle.getDirection( )-30 );
		turtle.pendown( );
	}

	

☆蜀江(単純版)の描画方法


	Turtle  turtle;
	
	public void start( ) {
		turtle = new Turtle( this );
		for ( int i=0; i<8; i++ ) {
			fillingLine( 20, 10 );
			goBack( 20, 10 );
		}
	}
	
	void goBack( int size, int n ) {
		turtle.penup( );
		turtle.rotate( -90 );
		for ( int i=1; i <= n+1; i++ ) {
			for ( int j=1; j <= 4; j++ ) {
				turtle.forward( size );
				turtle.rotate( (i <= n && j % 4 >= 2 || i > n && j <= 2 ) ? 45 : -45 );
			}
			if ( i == n ) { 
				turtle.rotate( -135 ); }
		}
		turtle.rotate( -135 );
		turtle.pendown( );
	}
	
	void fillingLine( int size, int n ) {
		for ( int i=1; i <= n; i++ ) {
			makeShokukou( size );
			turtle.penup( );
			turtle.rotate( 135 );
			for ( int j=1; j<=4; j++ ) {
				turtle.forward( size );
				turtle.rotate( (j==3) ? 45 : -45 );
			}
			turtle.rotate( -45 );
			turtle.pendown( );
		}
	}
	
	void makeShokukou( int size ) {
		for ( int i=1; i<=8; i++ ) {
			turtle.forward( size );
			if ( i == 1 || i == 3 ) {
				for ( int j=1; j <= 4; j++ ) {
					turtle.rotate( -90 );
					turtle.forward( size );
				}
			}
			turtle.rotate( 45 );
		}
	}
	

☆立涌の描画方法

すいません。ちょっと三角関数を使って角度を決めているので、 位置的に揃えられそうになかったものですから、setLocationとsetAngleを使っています。 しかも、setAngleでなぜこの角度になるのかも、まあ試行錯誤の結果です。

	
	Turtle turtle;

	public void start( ) {
		turtle = new Turtle( this );
		for ( int i=1, x=50; i<=20; i++ ){
			turtle.penup( );
			turtle.setLocation( x, 500 ); 
			turtle.setAngle( ( i % 2 == 0) ? 23 : -23 );
			turtle.pendown( );
			drawTatewaku( i % 2 == 0 );
			x += (i %2 ==0) ? 40 : 30;
		}
	}
	
	void drawTatewaku( boolean isRight ) {
		for ( int i=0; i<100; i++ ) {
			turtle.forward( 5 );
			double angle = Math.sin( Math.toRadians( i * 10 ) ) * 4;
			if ( isRight ) { angle = -angle; }
			turtle.rotate( angle );
		}
	}
	

<<Integers ⋏ Return to Columns