Code Sketch
Navya Tembhurne
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
}