Lesson: vectors2.nim

← Back to all lessons

Run Code Download Code

Source Code

# ****************************************************************************************
#
#   raylib [vectors] lesson 2 - Drawing and Rotating Shapes
#
#   This lesson demonstrates:
#   - Defining a shape's vertices (points) using Vector2.
#   - The concept of "Model Space" (coordinates relative to the shape's own center).
#   - Rotating vertices using the `rotate` operator from the `raymath` module.
#   - Translating vertices to a "World Space" position.
#
# ****************************************************************************************

import raylib
import raymath
import math # Import Nim's standard math library for degToRad (pi/180)

const
  screenWidth = 800
  screenHeight = 450

type
  TriangleType = enum
    Isosceles, Equilateral, RightAngled

proc main =
  initWindow(screenWidth, screenHeight, "raylib [vectors] lesson 2 - Shapes and Rotation")
  setTargetFPS(60)

  var v1, v2, v3: Vector2

  var currentType = Isosceles

  # This is the position where we will draw our shape in the world.
  # This is "World Space".
  let shapePosition = Vector2(x: screenWidth / 2.0, y: screenHeight / 2.0)

  # A variable to hold the current rotation of our shape in degrees.
  var rotation: float32 = 0.0

  # Main game loop
  # --------------------------------------------------------------------------------------
  while not windowShouldClose(): # Detect window close button or ESC key
    # Update
    # ----------------------------------------------------------------------------------
    if isKeyPressed(Space):
      # Cycle through the triangle types
      # `.ord` gets the integer value of an enum (Isosceles=0, Equilateral=1, etc.).
      # `.high` gets the last enum member, so `.high.ord` is the highest index.
      # We add 1 to get the total count for the `mod` operator, which makes the
      # cycle wrap around to 0 when it goes past the end.
      # `cast` converts the resulting integer back to a TriangleType.
      currentType = cast[TriangleType]((currentType.ord + 1) mod (TriangleType.high.ord + 1))

    # LESSON 1: MODEL SPACE
    # We define our shape's vertices relative to its own center (0, 0).
    # This is "Model Space". It makes transformations like rotation much easier.
    if currentType == Isosceles:
      v1 = Vector2(x: 0.0, y: -25.0)
      v2 = Vector2(x: -25.0, y: 25.0)
      v3 = Vector2(x: 25.0, y: 25.0)
    elif currentType == RightAngled:
      v1 = Vector2(x: -25.0, y: -25.0)
      v2 = Vector2(x: 25.0, y: 25.0)
      v3 = Vector2(x: -25.0, y: 25.0)
    else: # Equilateral
      let side: float32 = 50.0
      let height = (sqrt(3.0) / 2.0) * side
      v1 = Vector2(x:  0.0, y: -height * 2.0/3.0)
      v2 = Vector2(x: -side / 2.0, y: height * 1.0/3.0)
      v3 = Vector2(x:  side / 2.0, y: height * 1.0/3.0)

    # Increment the rotation by 1 degree each frame.
    rotation += 1.0

    # LESSON 2: TRANSFORMATION (Rotate and Translate)
    # To draw our shape, we must transform each vertex from Model Space to World Space.
    # We do this in two steps for each vertex:
    # 1. Rotate the vertex around the origin (0,0).
    # 2. Translate (move) the rotated vertex to its final position on the screen.

    # The `rotate` operator takes a vector and an angle in RADIANS.
    # We use the `degToRad` function from the `math` module to convert our angle.
    let angleInRadians = degToRad(rotation)
    let transformedV1 = rotate(v1, angleInRadians) + shapePosition
    let transformedV2 = rotate(v2, angleInRadians) + shapePosition
    let transformedV3 = rotate(v3, angleInRadians) + shapePosition

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

    # drawText(text, posX, posY, fontSize, color)
    drawText("This triangle is defined by 3 vectors (vertices)!", 10, 10, 20, DarkGray)
    drawText("We rotate the vertices, then add the position vector.", 10, 40, 20, DarkGray)
    drawText("Press [Space] to switch triangle type.", 10, screenHeight - 30, 20, LightGray)

    # LESSON 3: DRAWING THE TRANSFORMED SHAPE
    # `drawTriangleLines` can take Vector2s directly to draw the shape.
    drawTriangleLines(transformedV1, transformedV2, transformedV3, Maroon)
    
    # We can also draw a small circle at the shape's origin to visualize the pivot point.
    # The `drawCircle` function is overloaded to accept a Vector2 for the center.
    drawCircle(shapePosition, 5, LightGray)
    endDrawing()
    # ------------------------------------------------------------------------------------
  # De-Initialization
  # --------------------------------------------------------------------------------------
  closeWindow() # Close window and OpenGL context
  # --------------------------------------------------------------------------------------

main()