Code Sketch


Navya Tembhurne
By: Mhalsakant School
Category: Art
// ============================================================
// ULTRA 3D ANIMATED UNIVERSE - KOJO
// ============================================================

cleari()
setBackground(Color(2, 2, 18))


// ============================================================
// 3D VECTOR
// ============================================================

case class Vec3(x: Double, y: Double, z: Double)


// ============================================================
// PLANET DATA
// ============================================================

case class Planet(
  name: String,
  orbit: Double,
  radius: Double,
  r: Int,
  g: Int,
  b: Int,
  speed: Double
)

val planets = Seq(
  Planet("SUN",     0,   40, 255, 215, 50,  0.0),
  Planet("Mercury", 80,   6, 180, 180, 180, 4.0),
  Planet("Venus",   125,  9, 235, 170, 100, 2.2),
  Planet("Earth",   175, 12, 40, 130, 255, 1.5),
  Planet("Mars",    225,  9, 235, 60,  45, 1.1),
  Planet("Jupiter", 290, 22, 215, 160, 70, 0.6),
  Planet("Saturn",  365, 18, 235, 190, 130, 0.45),
  Planet("Uranus",  430, 14, 70, 210, 220, 0.30),
  Planet("Neptune", 490, 13, 50, 100, 255, 0.20)
)


// ============================================================
// CAMERA
// ============================================================

val camera = 600.0
val centerZ = 650.0


// ============================================================
// ROTATION
// ============================================================

def rotateX(p: Vec3, a: Double): Vec3 = {
  Vec3(
    p.x,
    p.y * Math.cos(a) - p.z * Math.sin(a),
    p.y * Math.sin(a) + p.z * Math.cos(a)
  )
}


def rotateY(p: Vec3, a: Double): Vec3 = {
  Vec3(
    p.x * Math.cos(a) + p.z * Math.sin(a),
    p.y,
    -p.x * Math.sin(a) + p.z * Math.cos(a)
  )
}


// ============================================================
// PERSPECTIVE
// ============================================================

def project(p: Vec3): (Double, Double, Double) = {

  val depth =
    Math.max(150.0, p.z + camera)

  val scale =
    camera / depth

  (
    p.x * scale,
    p.y * scale,
    scale
  )
}


// ============================================================
// BACKGROUND STARS
// ============================================================

val stars =
  (1 to 160).map { _ =>
    (
      randomDouble(
        -canvasBounds.width / 2,
        canvasBounds.width / 2
      ),
      randomDouble(
        -canvasBounds.height / 2,
        canvasBounds.height / 2
      ),
      randomDouble(1, 3)
    )
  }


// ============================================================
// DRAW STARS
// ============================================================

def drawStars(time: Double): Unit = {

  stars.zipWithIndex.foreach {

    case ((x, y, r), i) =>

      val brightness =
        90 +
        120 *
        Math.abs(
          Math.sin(time * 0.08 + i)
        )

      val star =
        Picture.circle(r)

      star.setFillColor(
        Color(
          255,
          255,
          255,
          brightness.toInt
        )
      )

      star.setPenColor(noColor)
      star.setPosition(x, y)

      draw(star)
  }
}


// ============================================================
// NEBULA
// ============================================================

def drawNebula(time: Double): Unit = {

  val n1 =
    Picture.circle(260)

  n1.setFillColor(
    Color(90, 20, 180, 18)
  )

  n1.setPenColor(noColor)

  n1.setPosition(
    -260 + Math.sin(time * 0.01) * 30,
    130
  )

  draw(n1)


  val n2 =
    Picture.circle(220)

  n2.setFillColor(
    Color(20, 80, 220, 18)
  )

  n2.setPenColor(noColor)

  n2.setPosition(
    300,
    -160 + Math.cos(time * 0.01) * 30
  )

  draw(n2)
}


// ============================================================
// ORBIT
// ============================================================

def drawOrbit(
  radius: Double,
  tilt: Double,
  rotation: Double
): Unit = {

  val points =
    (0 to 80).map { i =>

      val a =
        2 * Math.PI * i / 80

      var p =
        Vec3(
          radius * Math.cos(a),
          0,
          radius * Math.sin(a)
        )

      p = rotateY(p, rotation)
      p = rotateX(p, tilt)

      val q =
        project(
          Vec3(
            p.x,
            p.y,
            p.z + centerZ
          )
        )

      (q._1, q._2)
    }


  val orbit =
    Picture.fromVertexShape { v =>

      v.beginShape()

      points.foreach {
        case (x, y) =>
          v.vertex(x, y)
      }

      v.endShape()
    }


  orbit.setPenColor(
    Color(120, 100, 255, 70)
  )

  orbit.setPenThickness(1)

  draw(orbit)
}


// ============================================================
// ASTEROID BELT
// ============================================================

val asteroids =
  (1 to 45).map { i =>

    (
      245 + randomDouble(-18, 18),
      randomDouble(1, 3),
      randomDouble(0, Math.PI * 2)
    )
  }


def drawAsteroids(
  time: Double,
  tilt: Double,
  rotation: Double
): Unit = {

  asteroids.zipWithIndex.foreach {

    case ((distance, radius, start), i) =>

      val angle =
        start + time * (0.4 + i * 0.003)

      var p =
        Vec3(
          distance * Math.cos(angle),
          randomDouble(-8, 8),
          distance * Math.sin(angle)
        )

      p = rotateY(p, rotation)
      p = rotateX(p, tilt)

      val q =
        project(
          Vec3(
            p.x,
            p.y,
            p.z + centerZ
          )
        )

      val asteroid =
        Picture.circle(
          Math.max(
            1.0,
            radius * q._3
          )
        )

      asteroid.setFillColor(
        Color(150, 130, 120)
      )

      asteroid.setPenColor(noColor)

      asteroid.setPosition(
        q._1,
        q._2
      )

      draw(asteroid)
  }
}


// ============================================================
// MOON
// ============================================================

def drawMoon(
  time: Double,
  tilt: Double,
  rotation: Double,
  earthX: Double,
  earthY: Double,
  earthScale: Double
): Unit = {

  val moonAngle =
    time * 3.0

  val moonDistance =
    24 * earthScale

  val mx =
    earthX +
    moonDistance *
    Math.cos(
      Math.toRadians(moonAngle)
    )

  val my =
    earthY +
    moonDistance *
    Math.sin(
      Math.toRadians(moonAngle)
    ) * 0.45

  val moon =
    Picture.circle(
      Math.max(2, 4 * earthScale)
    )

  moon.setFillColor(
    Color(210, 210, 210)
  )

  moon.setPenColor(noColor)

  moon.setPosition(mx, my)

  draw(moon)
}


// ============================================================
// SUN
// ============================================================

def drawSun(
  x: Double,
  y: Double,
  r: Double,
  time: Double
): Unit = {

  val pulse =
    1.0 +
    0.10 *
    Math.sin(time * 0.18)


  val outer =
    Picture.circle(
      r * 3.4 * pulse
    )

  outer.setFillColor(
    Color(255, 50, 0, 25)
  )

  outer.setPenColor(noColor)
  outer.setPosition(x, y)

  draw(outer)


  val glow =
    Picture.circle(
      r * 2.2 * pulse
    )

  glow.setFillColor(
    Color(255, 160, 0, 75)
  )

  glow.setPenColor(noColor)
  glow.setPosition(x, y)

  draw(glow)


  val sun =
    Picture.circle(
      r * pulse
    )

  sun.setFillColor(
    Color(255, 235, 100)
  )

  sun.setPenColor(
    Color(255, 255, 255)
  )

  sun.setPenThickness(2)

  sun.setPosition(x, y)

  draw(sun)


  val label =
    Picture.text(
      "SUN",
      12
    )

  label.setPenColor(
    Color(255, 220, 50)
  )

  label.setPosition(
    x - 15,
    y - r - 20
  )

  draw(label)
}


// ============================================================
// COMET
// ============================================================

def drawComet(
  time: Double,
  tilt: Double,
  rotation: Double
): Unit = {

  val angle =
    Math.toRadians(
      time * 0.9
    )

  val distance =
    330.0

  var p =
    Vec3(
      distance * Math.cos(angle),
      -60,
      distance * Math.sin(angle)
    )

  p = rotateY(p, rotation)
  p = rotateX(p, tilt)

  val q =
    project(
      Vec3(
        p.x,
        p.y,
        p.z + centerZ
      )
    )


  // Tail
  val tail =
    Picture.ellipse(
      45 * q._3,
      8 * q._3
    )

  tail.setFillColor(
    Color(80, 180, 255, 90)
  )

  tail.setPenColor(noColor)

  tail.setPosition(
    q._1 - 22,
    q._2
  )

  draw(tail)


  // Head
  val head =
    Picture.circle(
      Math.max(3, 6 * q._3)
    )

  head.setFillColor(
    Color(220, 250, 255)
  )

  head.setPenColor(noColor)

  head.setPosition(
    q._1,
    q._2
  )

  draw(head)
}


// ============================================================
// SHOOTING STAR
// ============================================================

def drawShootingStar(
  time: Double
): Unit = {

  val cycle =
    time % 300

  val progress =
    cycle / 300.0

  val x =
    -canvasBounds.width / 2 +
    progress *
    canvasBounds.width * 1.5

  val y =
    canvasBounds.height / 2 -
    progress *
    canvasBounds.height

  val star =
    Picture.circle(4)

  star.setFillColor(
    Color(255, 255, 255)
  )

  star.setPenColor(noColor)

  star.setPosition(x, y)

  draw(star)


  val tail =
    Picture.ellipse(
      55,
      3
    )

  tail.setFillColor(
    Color(120, 200, 255, 100)
  )

  tail.setPenColor(noColor)

  tail.setPosition(
    x - 30,
    y + 20
  )

  draw(tail)
}


// ============================================================
// PLANET
// ============================================================

def drawPlanet(
  planet: Planet,
  time: Double,
  tilt: Double,
  rotation: Double
): Unit = {

  val angle =
    Math.toRadians(
      time * planet.speed
    )


  var p =
    if (planet.orbit == 0)

      Vec3(
        0,
        0,
        centerZ
      )

    else

      Vec3(
        planet.orbit *
          Math.cos(angle),

        0,

        planet.orbit *
          Math.sin(angle) +
          centerZ
      )


  p =
    rotateY(
      Vec3(
        p.x,
        p.y,
        p.z - centerZ
      ),
      rotation
    )


  p =
    Vec3(
      p.x,
      p.y,
      p.z + centerZ
    )


  p =
    rotateX(
      Vec3(
        p.x,
        p.y,
        p.z - centerZ
      ),
      tilt
    )


  p =
    Vec3(
      p.x,
      p.y,
      p.z + centerZ
    )


  val q =
    project(p)


  val x = q._1
  val y = q._2
  val scale = q._3

  val radius =
    planet.radius * scale


  if (planet.name == "SUN") {

    drawSun(
      x,
      y,
      radius,
      time
    )

  } else {

    // Planet Glow
    val glow =
      Picture.circle(
        radius * 1.7
      )

    glow.setFillColor(
      Color(
        planet.r,
        planet.g,
        planet.b,
        40
      )
    )

    glow.setPenColor(noColor)
    glow.setPosition(x, y)

    draw(glow)


    // Planet Body
    val body =
      Picture.circle(radius)

    body.setFillColor(
      Color(
        planet.r,
        planet.g,
        planet.b
      )
    )

    body.setPenColor(noColor)
    body.setPosition(x, y)

    draw(body)


    // Saturn Ring
    if (planet.name == "Saturn") {

      val ring1 =
        Picture.ellipse(
          radius * 3.0,
          radius * 0.8
        )

      ring1.setPenColor(
        Color(240, 210, 160, 180)
      )

      ring1.setPenThickness(2)
      ring1.setPosition(x, y)

      draw(ring1)


      val ring2 =
        Picture.ellipse(
          radius * 2.4,
          radius * 0.55
        )

      ring2.setPenColor(
        Color(180, 150, 110, 130)
      )

      ring2.setPenThickness(1)
      ring2.setPosition(x, y)

      draw(ring2)
    }


    // Earth Moon
    if (planet.name == "Earth") {

      drawMoon(
        time,
        tilt,
        rotation,
        x,
        y,
        scale
      )
    }


    // Planet Label
    val label =
      Picture.text(
        planet.name,
        10
      )

    label.setPenColor(
      Color(210, 220, 255)
    )

    label.setPosition(
      x - planet.name.length * 3,
      y - radius - 14
    )

    draw(label)
  }
}


// ============================================================
// MAIN ANIMATION
// ============================================================

animateWithState(0.0) { time =>

  erasePictures()


  // ------------------------------------------
  // Deep Space
  // ------------------------------------------

  drawStars(time)

  drawNebula(time)


  // ------------------------------------------
  // 3D Rotation
  // ------------------------------------------

  val rotation =
    time * 0.006

  val tilt =
    Math.toRadians(28)


  // ------------------------------------------
  // Orbits
  // ------------------------------------------

  planets.tail.foreach { p =>

    drawOrbit(
      p.orbit,
      tilt,
      rotation
    )
  }


  // ------------------------------------------
  // Asteroid Belt
  // ------------------------------------------

  drawAsteroids(
    time,
    tilt,
    rotation
  )


  // ------------------------------------------
  // Comet
  // ------------------------------------------

  drawComet(
    time,
    tilt,
    rotation
  )


  // ------------------------------------------
  // Shooting Star
  // ------------------------------------------

  drawShootingStar(time)


  // ------------------------------------------
  // Z Sorting
  // ------------------------------------------

  val sorted =
    planets
      .map { p =>

        val a =
          Math.toRadians(
            time * p.speed
          )

        val z =
          if (p.orbit == 0)

            centerZ

          else

            p.orbit *
              Math.sin(a) +
              centerZ

        (p, z)
      }
      .sortBy(_._2)


  // ------------------------------------------
  // Render Planets
  // ------------------------------------------

  sorted.foreach {

    case (p, _) =>

      drawPlanet(
        p,
        time,
        tilt,
        rotation
      )
  }


  // ------------------------------------------
  // Animation Speed
  // ------------------------------------------

  time + 1.0
}