日本・中国の文様辞典、視覚デザイン研究所編、視覚デザイン研究所、2000年、ISBN4-88108-150-0.
ここでは、タートルグラフィックスのライブラリを用いて、いくつかの割付文様をどのように描画するかに ついて、具体的なプログラムとプログラミング手法について、説明していきます。 以下のプログラムの構造は、大きな部分では同じになっています。
青海波は、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 |