Implement Scaling

Objective: Implement Scaling

Let's begin by reviewing our objective.

Create a Python script with a function draw_shape(size) that draws a regular equilateral triangle. The script should call this function twice: once to draw the triangle with a side length of 50 pixels, and a second time to draw the same triangle with a side length of 100 pixels, both originating from the same starting point (e.g., (0,0)).

Check Spec: Run Tests

Next, we'll run pytest to see the failing tests. This confirms the engineering specification we need to meet.

Run the tests in your terminal:

pytest

Test Results:

  • test_draw_shape_function_logic
  • test_draw_scaled_triangles_at_origin_orchestration

Implement: main.py

Now, let's build the solution by following the TODO comments in the skeleton code. Each implementation slide covers all TODOs for a single file, referencing the most relevant documentation sections to review for the task.

Step by step checklist:

  1. In the draw_shape function, command the turtle t to draw an equilateral triangle with the given size. This involves repeating a forward movement and a left turn three times.
  2. In the draw_scaled_triangles_at_origin function, instantiate a turtle object.
  3. Using the turtle object, call the draw_shape function to draw the first triangle with a size of 50.
  4. Reset the turtle's state to prepare for drawing the second triangle from the origin by lifting the pen, moving to the home position, and lowering the pen.
  5. Using the same turtle object, call the draw_shape function again to draw the second triangle with a size of 100.

The following documentation sections are going to be helpful:

  • Moving the Turtle: Translation
  • Resizing Shapes: Scaling
  • Building Blocks: Functions
  • Quick Reference: Key Turtle & Math Functions

Validate: Run Tests Again

With the code in place, let's run the tests again to validate our work.

Run the tests in your terminal:

pytest

Test Results:

  • test_draw_shape_function_logic
  • test_draw_scaled_triangles_at_origin_orchestration All tests passed!

Documentation

Python Turtle & Generative Art: Key Concepts

This document provides a quick reference for the Python concepts and turtle library commands used in the Generative Techniques blueprint.

1. Controlling Turtle's Direction: Absolute Headings

The turtle's heading determines the direction it faces.

  • turtle.setheading(angle): Sets the turtle's orientation to an absolute angle.
    • 0 degrees: East (right)
    • 90 degrees: North (up)
    • 180 degrees: West (left)
    • 270 degrees: South (down) This is different from turtle.left() or turtle.right(), which turn the turtle relative to its current direction.
import turtle
t = turtle.Turtle()

# Face East and draw
t.setheading(0)
t.forward(50)

# Face North and draw
t.setheading(90)
t.forward(50)

# Face West and draw
t.setheading(180)
t.forward(50)

Diagram showing a turtle at the origin with arrows indicating 0, 90, 180, 270 degrees and their corresponding directions (East, North, West, South).

2. Moving the Turtle: Translation

Translation involves moving a shape from one position to another without changing its orientation or size.

  • turtle.penup(): Lifts the pen, so the turtle moves without drawing.
  • turtle.goto(x, y): Moves the turtle to an absolute position (x, y) on the screen. The center of the screen is (0,0).
  • turtle.pendown(): Puts the pen down, so the turtle draws when it moves.
  • turtle.home(): Moves the turtle to the origin (0,0) and sets its heading to 0 degrees (East). Useful for resetting position.
import turtle
t = turtle.Turtle()

def draw_square(size):
    for _ in range(4):
        t.forward(size)
        t.left(90)

# Draw a square at the origin
draw_square(50)

# Translate and draw another square
t.penup()
t.goto(100, 50) # Move to new position
t.pendown()
draw_square(50)

# Reset position and orientation
t.penup()
t.home()
t.pendown()

GIF showing a square being drawn, then the turtle (pen up) moving to a new location, and an identical square being drawn there.

3. Resizing Shapes: Scaling

Scaling changes the size of a shape. This can be achieved by passing a size-modifying parameter to a drawing function.

  • Define a drawing function that accepts a size parameter.
  • Call the function with different size values to draw scaled versions of the shape.
import turtle
t = turtle.Turtle()

def draw_triangle(side_length):
    for _ in range(3): # Equilateral triangle
        t.forward(side_length)
        t.left(120)

# Draw a small triangle
draw_triangle(50)

# Reset position (optional, for clarity if drawing from same origin)
t.penup()
t.home() # Go to (0,0), face East
t.pendown()

# Draw a larger triangle
draw_triangle(100)

Image showing two equilateral triangles, one small and one large, both drawn from the same origin point (0,0). The smaller triangle is inside the larger one.

4. Rotation

4.1. Basic Rotation with Turtle Heading

For simple rotations where the shape is drawn relative to the turtle's current orientation:

  • Use turtle.setheading(angle) to set the turtle's absolute orientation before drawing.
  • Alternatively, use turtle.left(angle) or turtle.right(angle) for relative turns.
import turtle
t = turtle.Turtle()

def draw_rectangle(width, height):
    for _ in range(2):
        t.forward(width)
        t.left(90)
        t.forward(height)
        t.left(90)

# Draw a rectangle
t.setheading(0) # Base along x-axis (East)
draw_rectangle(100, 50)

# Reset, rotate, and draw again
t.penup()
t.goto(0,0)
t.pendown()
t.setheading(270) # Rotated 90 degrees clockwise (or -90)
draw_rectangle(100, 50)

Image showing a rectangle drawn horizontally (heading 0°), and an identical rectangle rotated 90 degrees clockwise (heading 270°), both originating from the same point.

4.2. Advanced Rotation with Trigonometry

For precise rotation of points around an origin (typically (0,0)):

  • Import the math module: import math
  • Convert degrees to radians: math.radians(degrees) (Trigonometric functions in math use radians).
  • Use math.cos(radians) and math.sin(radians) to calculate coordinates.
  • Rotation Formulas (for counter-clockwise rotation of point (x, y) around origin (0,0) by angle_rad):
    • new_x = x * math.cos(angle_rad) - y * math.sin(angle_rad)
    • new_y = x * math.sin(angle_rad) + y * math.cos(angle_rad)
import math

# Original point
x1, y1 = 100, 0
angle_deg = 60

# Convert angle to radians
angle_rad = math.radians(angle_deg)

# Calculate new coordinates
x2 = x1 * math.cos(angle_rad) - y1 * math.sin(angle_rad)
y2 = x1 * math.sin(angle_rad) + y1 * math.cos(angle_rad)

print(f"Original: ({x1}, {y1})")
print(f"Rotated by {angle_deg} degrees: ({x2}, {y2})")

Diagram showing a 2D coordinate plane with a point P and its rotation P' around the origin, illustrating the trigonometric relationship.

5. Building Blocks: Functions

Functions help organize code, make it reusable, and improve readability.

  • Defining a function:
    def function_name(parameter1, parameter2):
        # code block
        # ...
        return result # Optional: sends a value back
    
  • Functions that return values: Use the return statement to send a result back to the part of the code that called the function.
  • Calling a function: Use the function name followed by parentheses (), providing arguments if the function has parameters.
def calculate_area(length, width):
    """Calculates the area of a rectangle."""
    area = length * width
    return area # Send the calculated area back

def demonstrate_calculation():
    rect_length = 8
    rect_width = 5
    
    # Call the function and store the returned value
    calculated_area = calculate_area(rect_length, rect_width)
    
    # Use the returned value
    print(f"The area is: {calculated_area}") # Output: The area is: 40

6. Artistic Alchemy: Combining Transformations

Combine translation, scaling, and rotation within loops to create complex and interesting patterns.

  1. Create a drawing function for a base element, accepting transformation parameters (offset x, offset y, scale factor, rotation angle).

    # Example: BASE_SIZE = 20
    def draw_transformed_square(t, x_offset, y_offset, scale_factor, rotation_angle):
        t.penup()
        t.goto(x_offset, y_offset) # Translation
        t.setheading(rotation_angle) # Rotation
        t.pendown()
        
        side_length = BASE_SIZE * scale_factor # Scaling
        for _ in range(4):
            t.forward(side_length)
            t.left(90)
        t.penup()
    
  2. Use a loop to call this function multiple times, varying the parameters systematically based on the loop counter.

    import turtle
    # Assume draw_transformed_square and BASE_SIZE are defined
    
    pen = turtle.Turtle()
    pen.speed(0) # Fastest
    
    num_elements = 5
    for i in range(num_elements):
        # Vary parameters based on 'i'
        x = i * 30          # Translate x
        y = i * 20          # Translate y
        scale = 1 + (i * 0.2) # Increase scale
        angle = i * 15        # Increase rotation
    
        draw_transformed_square(pen, x, y, scale, angle)
    

GIF showing a sequence of squares being drawn, each progressively larger, more rotated, and shifted, creating a spiral or fanning pattern.

7. Quick Reference: Key Turtle & Math Functions

  • turtle.Turtle(): Creates a new turtle object.
  • turtle.Screen(): Gets the drawing screen object.
  • turtle.done(): Starts the turtle graphics event loop (keeps window open).
  • turtle.exitonclick(): Closes the window when clicked.
  • turtle.speed(speed): Sets the turtle's speed (0 is fastest).
  • turtle.hideturtle(): Makes the turtle cursor invisible.
  • turtle.pensize(width): Sets the pen thickness.
  • turtle.forward(distance): Moves the turtle forward.
  • turtle.backward(distance): Moves the turtle backward.
  • turtle.left(angle): Turns the turtle left by angle degrees (relative).
  • turtle.right(angle): Turns the turtle right by angle degrees (relative).
  • turtle.setheading(angle): Sets the turtle's absolute orientation (0=East, 90=North, 180=West, 270=South).
  • turtle.penup(): Lifts the pen.
  • turtle.pendown(): Lowers the pen.
  • turtle.goto(x, y): Moves the turtle to absolute coordinates (x, y).
  • turtle.home(): Moves turtle to (0,0) and sets heading to 0.
  • math.radians(degrees): Converts an angle from degrees to radians.
  • math.cos(radians): Returns the cosine of an angle in radians.
  • math.sin(radians): Returns the sine of an angle in radians.