日本・中国の文様辞典、視覚デザイン研究所編、視覚デザイン研究所、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 |