Lesson: graphics2.nim

← Back to all lessons

Run Code Download Code

Source Code

# ****************************************************************************************
#
#   raylib [graphics] lesson 2 - Drawing Polygons
#
#   This lesson demonstrates how to generate and draw regular polygons.
#
#   We will build on the previous lesson by:
#   - Creating a procedure to generate vertices for any regular polygon.
#   - Storing these vertices in a sequence.
#   - Using a loop to draw the polygon from its vertices.
#   - Cycling through different polygon types with user input.
#
# ****************************************************************************************

import raylib
import raymath
import rlgl # raylib's OpenGL abstraction layer, for matrix transformations
import math
import strformat

const
  screenWidth = 800
  screenHeight = 600

type
  Polygon = object
    name: string
    vertices: seq[Vector2]

# LESSON 1: GENERATING POLYGON VERTICES
# This procedure creates the vertices for a regular polygon with a given
# number of sides and radius. The vertices are calculated by finding points
# on a circle.
proc generateRegularPolygon(sides: int, radius: float32): seq[Vector2] =
  var vertices: seq[Vector2] = @[]
  # We add an angle offset of -PI/2 to ensure the first vertex is at the top,
  # which often looks better for shapes like triangles.
  let angleStep = TAU / sides.float # TAU is 2*PI
  let angleOffset = -PI / 2.0

  for i in 0 ..< sides:
    let angle = i.float * angleStep + angleOffset
    vertices.add(Vector2(x: cos(angle) * radius, y: sin(angle) * radius))
  
  return vertices

proc main =
  initWindow(screenWidth, screenHeight, "raylib [graphics] lesson 2 - Polygons")
  setTargetFPS(60)

  # LESSON 2: DEFINING OUR POLYGONS
  # We create a list of polygons to cycle through.
  let polygons = [
    Polygon(name: "Triangle", vertices: generateRegularPolygon(3, 50)),
    Polygon(name: "Square", vertices: generateRegularPolygon(4, 50)),
    Polygon(name: "Pentagon", vertices: generateRegularPolygon(5, 50)),
    Polygon(name: "Hexagon", vertices: generateRegularPolygon(6, 50))
  ]
  var currentPolygonIndex = 0

  var rotation: float32 = 0.0
  let font = getFontDefault()

  # Main game loop
  # --------------------------------------------------------------------------------------
  while not windowShouldClose():
    # Update
    # ----------------------------------------------------------------------------------
    rotation += 0.5 # Increase rotation angle every frame

    if isKeyPressed(Space):
      currentPolygonIndex = (currentPolygonIndex + 1) mod polygons.len

    let currentPolygon = polygons[currentPolygonIndex]

    # Draw
    # ------------------------------------------------------------------------------------
    beginDrawing()
    clearBackground(RayWhite)

    # Use the same transformation stack as the previous lesson.
    pushMatrix()
    translatef(screenWidth / 2, screenHeight / 2, 0)
    rotatef(rotation, 0, 0, 1)

    # LESSON 3: DRAWING THE POLYGON
    # Instead of a specific `drawTriangle` function, we can loop through the
    # vertices and draw lines between them. This works for any polygon!
    for i in 0 ..< currentPolygon.vertices.len:
      let v1 = currentPolygon.vertices[i]
      # The modulo operator (%) makes the index wrap around, so the last
      # vertex connects back to the first one.
      let v2 = currentPolygon.vertices[(i + 1) mod currentPolygon.vertices.len]
      drawLine(v1, v2, 2.0, DarkGray)
    
    # We can also use raylib's `drawPolygonLinesEx` for a simpler approach.
    # drawPolygonLinesEx(currentPolygon.vertices[0].addr, currentPolygon.vertices.len, 2.0, DarkGray)

    popMatrix()

    # --- Draw UI and Explanations ---
    drawText(font, "Drawing Polygons", 
        Vector2(x: 20, y: 20), 30.0, 1.0, DarkGray)
    drawText(font, "We generate vertices by finding points on a circle.", 
        Vector2(x: 20, y: 70), 20.0, 1.0, Gray)
    drawText(font, "This allows us to create any regular polygon.", 
        Vector2(x: 20, y: 100), 20.0, 1.0, Gray)

    let currentName = currentPolygon.name
    drawText(font, fmt"Current Shape: {currentName}", 
        Vector2(x: 20, y: 150), 20.0, 1.0, Maroon)
    drawText(font, "Press [Space] to cycle shapes.", 
        Vector2(x: 20, y: screenHeight - 40), 20.0, 1.0, LightGray)

    endDrawing()
    # ------------------------------------------------------------------------------------

  # De-Initialization
  # --------------------------------------------------------------------------------------
  closeWindow()
  # --------------------------------------------------------------------------------------

main()