Code Sketch


Spiral of square tiles with Greek pattern in two colors
By: Mike
Category: Art
// The plane can be tiled with a grid of squares. The only other regular 
// polygons that can do so are hexagons and equilateral triangles
// (and the latter only work if half of them are oriented "upside-down").
// 
// This script shows how the turtle can cover the plane with square tiles
// by winding around an initial square in a continuous spiral. The tiles are
// filled by a Greek pattern, and their outlines are not drawn.
//  
//  Why not design a more interesting pattern? Make your own pattern
//  a similar size to the square, and remember when drawing a tile that
//  the turtle starts and ends in the bottom-left corner, facing north!

clear()

// Length of the sides of the squares we're tiling
val tileSide: Double = 100 

// How many times does the spiral loop completely around the initial square?
val loops = 3 

// How fast will the turtle move? 1000 means normal speed, 500 is double-speed,
// 100 means ten times faster than normal, 0 for almost-instant drawing!
val speed = 400

// The pattern works best in two colors, why not pick your own?
// You can use the utility functions color(r, g, b) or color(value)
// where value can be a hex color code like used for web design.
// Or just right-click in the Script Editor, and select "Choose Color"!
val firstColor = red
val secondColor = blue

setPenColor(firstColor) //start with first color
// We'll use a Boolean (true/false) to keep track of which color is selected
// firstColorSelected will be true if the pen is set to the 1st color
// firstColorSelected will be false if the pen is set to the 2nd color
// We use "var" not "val" because the pen selection can change!
var firstColorSelected = true

def switchColor{ 
    if (firstColorSelected) {
        setPenColor(secondColor) //if 1st color selected, switch to 2nd color
    } else {
        setPenColor(firstColor) //if 1st color not selected, switch to 1st color
    }    
     //if firstColorSelected was true, now make it false, and vice versa
    firstColorSelected = !firstColorSelected
}

// The pattern used is intricate, and treats each tile as a 22x22 grid.
// The lengths of the sides of the small squares of that grid will form
// the  fundamental unit of length for the pattern.
val gridSide = tileSide/22 

// The pattern instructions are stored as lists: one for lengths to move (in
// small grid units) and one for the angle to turn through afterwards.
val patternLengths = List(2, 7, 14, 10, 6 , 4, 2, 6, 10, 14, 7, 2)
val patternAngles = List(90, 270, 270, 270, 270, 90, 90, 90, 90, 90, 270, 0)

def drawPattern {
    penDown
    // There are 12 instructions in each list and numbering starts at 0
    // So we need to work in order through entries 0 to 11.
    // If your list had 20 entries, you'd need to work through from 0 to 19.
    for (j <- 0 to 11) { 
        //patternLengths(j) finds the item in position j of the length list
        forward(patternLengths(j)*gridSide)
        right(patternAngles(j))    
    }
    // You may find it hard to keep track of where your pattern leaves the
    // turtle. Although it's slow, we can trace back our steps by undoing
    // each instruction in reverse order - last command first, turn left not 
    // right, move back not forward. This will leave you in the position
    // and heading when drawPattern was called. Alternatively we can use
    // savePosHe and restorePosHe to put the turtle back: but it's fun to
    // watch the turtle tile the plane in one continuous motion, no "jumping"!
    for (j <- 0 to 11) {
        // patternAngles.reverse is just the patternAngles list in reverse
        left(patternAngles.reverse(j))
        back(patternLengths.reverse(j)*gridSide)        
    }
    penUp // only drawPattern should leave a trail in this program
}

// In your own tile pattern, remember to start and end in the bottom-left,
// facing north, and that the tile's dimensions are tileSide x tileSide.
def drawTile {
    //first move into bottom center, facing north, then draw the pattern
    right()
    forward(tileSide/2)
    left    
    drawPattern
    
    //the pattern can be interlocked with an alternate color copy of itself
    //drawn with the turtle starting at top center, facing south
    forward(tileSide)
    right(180)
    switchColor
    drawPattern
    
    //finally move back into bottom left, facing north
    forward(tileSide)
    right()
    forward(tileSide/2)
    right()
}

// Each time the spiral winds around, it draws four sides ("arms") around 
// the existing tiles. To draw an arm we need to know how many squares long
// it should be. We will draw each tile, starting and finishing in the 
// bottom-left corner of its square, and move into position for the next tile.
// Finally the turtle turns to face the direction of the next arm.
// 
// To draw each tile consistently we must start and end facing north. So it's
// helpful to know the orientation of each arm, measured by the angle the turtle
// has to turn through to face north.

def drawArm(tiles: Int, orientation: Double) {
    repeat(tiles) {
        left(orientation) // now facing north
        drawTile //starts and finishes in bottom-left of tile's square
        right(orientation) //now facing in direction of arm again
        forward(tileSide) //moves to bottom-left of next square
        }
    right(90) // arms wrap around clockwise, so turn right to start next arm
}

// We wind each loop of the spiral clockwise just by drawing its four arms.
// Arms have different lengths, and orientations change by 90 degrees.
// We use the the number of squares in the shortest (first) arm to specify
// how large to draw the loop.
def windSpiral(shortArm: Int) {
    //initial position is ready to start tile above what will be bottom-left
    //head north along left side (unusually short as bottom-left is in 4th arm)
    //finish in position to start the top-left tile
    drawArm(shortArm, 0) 
    //draw top-left tile, head east along top side
    //finish in position to start top-right tile
    drawArm(shortArm + 1, 90)
    //draw top-right tile, head south along right side
    //finish in position to start bottom-right tile
    drawArm(shortArm + 1, 180)
    //draw bottom-right tile, head west along bottom side
    //draw all bottom row tiles, including the bottom-left (so unusually long)
    //finish in position to the left of the bottom-left tile, facing north, so
    //in correct position and orientation for the northward arm of next loop
    drawArm(shortArm + 2, 270)
}

//with all the definitions complete, let's start tiling!

setAnimationDelay(speed) // speed up turtle
penUp // turtle should only leave a trace when in the pattern-drawing phase

drawTile // draw the initial tile

//move to bottom-left of square to the left of the initial one, face north, so 
//in correct position and orientation for the northward arm of the 1st loop
left() 
forward(tileSide)
right()

// Now wind the spiral clockwise around the initial square!
// With each loop added around, the size of the shortest arm increases by 2.
for (i <- 1 to 2*loops by 2) { //as loops have shortest side 1, 3, 5, 7, etc
    windSpiral(i)
}