Code Sketch


bhavesh or yadnesh using gemeni
By: Mhalsakant School
Category: Programming
// --- ISRO MISSION: CURVED RIGHT LAUNCH & SLOW START WITH NEW MESSAGE ---
cleari()
originBottomLeft()

val gravity = Vector2D(0, -0.08)
var waveAngle = 0.0
var chakraRotation = 0.0

val planetList = Array("Mercury", "Venus", "Earth", "Moon", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune")
var selectedPlanet = "Mars"
var isZoomed = false

var isTimerRunning = false
var countdownTimer = 10.0
var isLaunched = false
var isLanded = false

var doorOpenProgress = 0.0  
var astronautStep = 0.0     

var targetX = (cwidth / 2.0) + 80.0
var targetY = (cheight / 2.0) + 30.0

// ??????? ?????????? ????? (??????)
var rocketX = 80.0
var rocketY = 60.0
var launchProgress = 0.0 // ??? ????? ??? ???? ???????? ?????????

val tricolorPalette = Array(
    cm.rgb(255, 153, 51),  
    cm.rgb(255, 255, 255), 
    cm.rgb(18, 136, 7)     
)

class Particle(x0: Double, y0: Double, col: Color, seed: Boolean) {
    var location = Vector2D(x0, y0)
    private var velocity = if (seed) Vector2D(0, random(5, 11)) else randomExplosionVector
    private var acceleration = Vector2D(0, 0)
    var lifespan = 255.0
    private var exploded = false

    def randomExplosionVector: Vector2D = {
        val rv1 = Vector2D(randomNormalDouble, randomNormalDouble)
        val r1 = randomDouble(2, 4)
        rv1 * r1
    }

    def applyForce(force: Vector2D) { acceleration += force }
    def shouldExplode: Boolean = !exploded && lifespan <= 0
    def checkExplode() { if (seed && velocity.y < 0) lifespan = 0 }
    def isDead: Boolean = if (seed) exploded else lifespan <= 0
    def explode() { exploded = true }

    def step() {
        velocity += acceleration
        location += velocity
        if (!seed) {
            lifespan -= 5
            velocity *= 0.95
        }
        acceleration *= 0
        checkExplode()
    }

    def view(canvas: CanvasDraw) {
        canvas.stroke(col)
        if (seed) canvas.strokeWeight(4) else canvas.strokeWeight(2)
        canvas.point(location.x, location.y)
    }
}

class Firework() {
    private val fireworkColor = tricolorPalette(random(tricolorPalette.length))
    private val firework = new Particle(random(cwidth), 0, fireworkColor, true)
    private val particles = ArrayBuffer.empty[Particle]

    def done: Boolean = firework.isDead && particles.isEmpty

    def step() {
        particles.filterInPlace(p => !p.isDead)

        if (!firework.isDead) {
            firework.applyForce(gravity)
            firework.step()

            if (firework.shouldExplode) {
                repeat(80) {
                    particles.append(new Particle(firework.location.x, firework.location.y, fireworkColor, false))
                }
                firework.explode()
            }
        }

        repeatFor(particles) { p => p.applyForce(gravity) }
    }

    def view(canvas: CanvasDraw) {
        firework.view(canvas)
        repeatFor(particles) { p => p.view(canvas) }
    }
}

val fireworks = ArrayBuffer.empty[Firework]

def updateState() {
    fireworks.filterInPlace(f => !f.done)
    if (randomDouble(1) < 0.08) fireworks.append(new Firework())
    repeatFor(fireworks) { f => f.step() }

    waveAngle += 0.08
    chakraRotation += 0.05

    if (isTimerRunning && countdownTimer > 0) {
        countdownTimer -= 0.03
        if (countdownTimer <= 0) {
            countdownTimer = 0
            isTimerRunning = false
            isLaunched = true
            launchProgress = 0.0
        }
    }

    // ? ???? ?????? ??? ??? ??????? ?????? ????? (Bezier / Curved Path)
    if (isLaunched && !isLanded) {
        launchProgress += 0.006 // ??? ?????
        if (launchProgress >= 1.0) {
            launchProgress = 1.0
            isLanded = true
        }

        // ?????? ????? ?????????? (???? ????????? ????? ?????? ???)
        val startX = 80.0
        val startY = 60.0
        val controlX = cwidth / 2.0 + 150.0 // ?????? ?????? ??????? ??????? ?????
        val controlY = cheight / 2.0 - 50.0

        val u = launchProgress
        val tt = u * u
        val uu = (1.0 - u) * (1.0 - u)

        rocketX = uu * startX + 2 * (1.0 - u) * u * controlX + tt * targetX
        rocketY = uu * startY + 2 * (1.0 - u) * u * controlY + tt * targetY
    }

    if (isLanded) {
        if (doorOpenProgress < 1.0) {
            doorOpenProgress += 0.02
        } else if (astronautStep < 1.0) {
            astronautStep += 0.02
        }
    }
}

onMouseClick { (x, y) =>
    if (x >= 20 && x <= 110 && y >= 15 && y <= 50) {
        if (!isTimerRunning && !isLaunched) {
            isTimerRunning = true
            countdownTimer = 10.0
        }
    } 
    else if (x <= 130 && y >= cheight - 320 && y <= cheight - 50) {
        val index = ((cheight - 50 - y) / 28).toInt
        if (index >= 0 && index < planetList.length) {
            selectedPlanet = planetList(index)
            isZoomed = true
            targetX = cwidth / 2.0 + 80.0
            targetY = cheight / 2.0 + 30.0
            isLanded = false
            isLaunched = false
            doorOpenProgress = 0.0
            astronautStep = 0.0
            rocketX = 80.0
            rocketY = 60.0
            launchProgress = 0.0
        }
    }
    else {
        if (isZoomed && !isLaunched) {
            targetX = x.toDouble
            targetY = y.toDouble
        } else if (!isZoomed && x > 140) {
            isZoomed = true
        }
    }
}

def drawPlanetList(canvas: CanvasDraw) {
    canvas.fill(15, 20, 35, 200)
    canvas.stroke(100, 100, 150)
    canvas.strokeWeight(1)
    canvas.rect(10, cheight - 330, 120, 275)

    canvas.fill(255, 153, 51)
    canvas.text("ALL PLANETS", 20, cheight - 70)

    var yPos = cheight - 95.0
    repeatFor(planetList) { pName =>
        if (pName == selectedPlanet) {
            canvas.fill(18, 136, 7)
            canvas.rect(12, yPos - 12, 116, 22)
            canvas.fill(255, 255, 255)
        } else {
            canvas.fill(200, 200, 200)
        }
        canvas.text(s"? $pName", 22, yPos)
        yPos -= 28.0
    }
}

def drawSolarSystem(canvas: CanvasDraw) {
    val cx = cwidth / 2.0 + 80
    val cy = cheight / 2.0 + 20

    canvas.fill(255, 140, 0)
    canvas.stroke(255, 215, 0)
    canvas.strokeWeight(3)
    canvas.ellipse(cx - 160, cy, 65, 65)

    canvas.noFill()
    canvas.stroke(80, 80, 110)
    canvas.strokeWeight(1)
    val rList = Array(70, 100, 130, 150, 180, 220, 260, 290, 320)
    repeatFor(rList) { r =>
        canvas.ellipse(cx - 160, cy, r * 2, r * 1.2)
    }

    canvas.fill(255, 255, 255)
    canvas.text("CLICK ANY PLANET TO ZOOM & AIM!", cx - 100, cheight - 40)
}

def drawZoomedPlanet(canvas: CanvasDraw) {
    val px = cwidth / 2.0 + 80
    val py = cheight / 2.0 + 20

    selectedPlanet match {
        case "Jupiter" =>
            canvas.fill(210, 150, 100)
            canvas.ellipse(px, py, 220, 220)
            canvas.fill(170, 100, 60)
            canvas.rect(px - 108, py - 30, 216, 22)
            canvas.rect(px - 105, py + 20, 210, 18)
            canvas.fill(180, 50, 40)
            canvas.ellipse(px + 40, py - 40, 42, 28)
        case "Saturn" =>
            canvas.fill(220, 190, 140)
            canvas.ellipse(px, py, 180, 180)
            canvas.stroke(210, 170, 110)
            canvas.strokeWeight(8)
            canvas.noFill()
            canvas.ellipse(px, py, 300, 65)
        case "Mars" =>
            canvas.fill(210, 70, 50)
            canvas.ellipse(px, py, 190, 190)
            canvas.fill(160, 40, 30)
            canvas.ellipse(px - 30, py + 30, 40, 30)
        case "Earth" =>
            canvas.fill(30, 120, 220)
            canvas.ellipse(px, py, 200, 200)
            canvas.fill(50, 160, 60)
            canvas.ellipse(px - 30, py + 20, 70, 50)
        case _ =>
            canvas.fill(190, 195, 200)
            canvas.ellipse(px, py, 200, 200)
            canvas.fill(150, 155, 160)
            canvas.ellipse(px - 40, py + 30, 35, 28)
    }

    canvas.fill(255, 255, 255)
    canvas.text(s"TARGET PLANET: $selectedPlanet", px - 65, py + 130)

    if (!isLanded) {
        canvas.stroke(255, 0, 0)
        canvas.strokeWeight(2)
        canvas.noFill()
        canvas.ellipse(targetX, targetY, 24, 24)
        canvas.line(targetX - 18, targetY, targetX + 18, targetY)
        canvas.line(targetX, targetY - 18, targetX, targetY + 18)
    }
}

def drawEarthAndLaunchUI(canvas: CanvasDraw) {
    canvas.fill(30, 120, 220)
    canvas.stroke(10, 60, 160)
    canvas.strokeWeight(2)
    canvas.ellipse(50, -20, 180, 180)

    canvas.fill(220, 20, 20)
    canvas.stroke(255, 255, 255)
    canvas.strokeWeight(2)
    canvas.rect(20, 15, 90, 35)

    canvas.fill(255, 255, 255)
    canvas.text("LAUNCH", 32, 28)

    if (isTimerRunning) {
        val sec = countdownTimer.toInt
        val msec = ((countdownTimer - sec) * 100).toInt

        canvas.fill(255, 215, 0)
        if (sec <= 3) {
            canvas.text(f"COUNTDOWN: 0$sec%d:$msec%02d MS", 125, 30)
        } else {
            canvas.text(f"COUNTDOWN: $sec%02d SEC", 125, 30)
        }
    } else if (isLaunched && !isLanded) {
        canvas.fill(51, 204, 51)
        // ???? ?????: ??? ??? ?????, ??? ??? ????????????
        canvas.text("GOODBYE ISRO, GOODBYE ASTRONAUTS!", 125, 30)
    }
}

def drawDetailedFlag(canvas: CanvasDraw) {
    if (isLanded) {
        val fx = targetX - 30.0
        val fy = targetY + 25.0
        val fw = 90.0
        val fh = 18.0

        canvas.stroke(220, 220, 220)
        canvas.strokeWeight(3)
        canvas.line(fx, fy + fh * 3 + 10, fx, fy - 20)

        val stepSize = 3.0
        var x = 0.0
        while (x < fw) {
            val yOffset = math.sin(waveAngle + (x * 0.04)) * 5.0
            
            canvas.stroke(255, 153, 51)
            canvas.strokeWeight(stepSize + 1)
            canvas.line(fx + x, fy + fh * 2 + yOffset, fx + x, fy + fh * 3 + yOffset)

            canvas.stroke(255, 255, 255)
            canvas.line(fx + x, fy + fh + yOffset, fx + x, fy + fh * 2 + yOffset)

            canvas.stroke(18, 136, 7)
            canvas.line(fx + x, fy + yOffset, fx + x, fy + fh + yOffset)

            x += stepSize
        }

        val chakraX = fx + fw / 2.0
        val chakraY = fy + fh + (fh / 2.0) + (math.sin(waveAngle + ((fw / 2.0) * 0.04)) * 5.0)
        val radius = 7.0

        canvas.stroke(0, 0, 128)
        canvas.strokeWeight(1)
        canvas.noFill()
        canvas.ellipse(chakraX, chakraY, radius * 2, radius * 2)

        for (i <- 0 until 24) {
            val angle = chakraRotation + (i * (2 * math.Pi / 24))
            val xEnd = chakraX + math.cos(angle) * radius
            val yEnd = chakraY + math.sin(angle) * radius
            canvas.line(chakraX, chakraY, xEnd, yEnd)
        }
    }
}

def drawISROMissileAndAstronaut(canvas: CanvasDraw) {
    val rx = rocketX
    val ry = rocketY

    if (isLaunched && !isLanded) {
        canvas.fill(200, 200, 200, 150)
        canvas.noStroke()
        canvas.ellipse(rx - 8, ry - 20, random(12, 18), random(12, 18))

        canvas.fill(255, 69, 0)
        canvas.ellipse(rx, ry - 12, 10, 20)
    }

    canvas.fill(240, 240, 240)
    canvas.stroke(100, 100, 100)
    canvas.strokeWeight(1)
    canvas.rect(rx - 15, ry, 30, 45)

    canvas.fill(255, 153, 51)
    canvas.line(rx - 15, ry + 45, rx, ry + 60)
    canvas.line(rx + 15, ry + 45, rx, ry + 60)

    canvas.fill(0, 51, 102)
    canvas.text("ISRO", rx - 12, ry + 32)

    if (isLanded) {
        val doorWidth = 14.0 * (1.0 - doorOpenProgress)
        canvas.fill(50, 50, 50)
        canvas.rect(rx - 7, ry + 8, 14, 20)

        canvas.fill(180, 180, 180)
        canvas.rect(rx - 7, ry + 8, doorWidth, 20)

        if (doorOpenProgress > 0.5) {
            canvas.stroke(150, 150, 150)
            canvas.strokeWeight(3)
            canvas.line(rx - 3, ry + 8, rx + 22, ry - 15)
        }

        // ??? ???????????? (???! ??? ?? ????!)
        if (astronautStep > 0) {
            val ax = rx + 12 + (astronautStep * 22.0)
            val ay = ry - 5 - (astronautStep * 10.0)

            canvas.fill(200, 200, 200)
            canvas.rect(ax - 7, ay, 4, 14)

            canvas.fill(255, 255, 255)
            canvas.stroke(80, 80, 80)
            canvas.strokeWeight(1)
            canvas.rect(ax - 6, ay, 12, 16)

            canvas.fill(255, 255, 255)
            canvas.ellipse(ax, ay + 20, 14, 14)

            canvas.fill(30, 144, 255)
            canvas.ellipse(ax + 2, ay + 21, 8, 7)

            canvas.stroke(255, 255, 255)
            canvas.strokeWeight(3)
            canvas.line(ax + 6, ay + 10, ax + 15, ay + 18)
            canvas.line(ax - 6, ay + 8, ax - 12, ay + 2)

            canvas.line(ax - 3, ay, ax - 6, ay - 10)
            canvas.line(ax + 3, ay, ax + 6, ay - 10)

            if (astronautStep >= 0.8) {
                // "???!" ?????
                canvas.fill(255, 255, 255)
                canvas.rect(ax + 10, ay + 26, 35, 18)
                canvas.fill(0, 0, 0)
                canvas.text("???!", ax + 14, ay + 30)

                // "?? ????!" ?????
                canvas.fill(255, 153, 51)
                canvas.rect(ax - 10, ay + 48, 70, 22)
                canvas.fill(255, 255, 255)
                canvas.text("?? ????! ??", ax - 6, ay + 53)
            }
        }
    }
}

def viewState(canvas: CanvasDraw) {
    canvas.fill(5, 5, 20, 50)
    canvas.noStroke()
    canvas.rect(0, 0, cwidth, cheight)

    repeatFor(fireworks) { f => f.view(canvas) }

    drawPlanetList(canvas)

    if (!isZoomed) {
        drawSolarSystem(canvas)
    } else {
        drawZoomedPlanet(canvas)
    }

    drawEarthAndLaunchUI(canvas)
    
    drawDetailedFlag(canvas)
    drawISROMissileAndAstronaut(canvas)

    canvas.fill(255, 153, 51)
    canvas.text("INDIAN ISRO ASTRONAUT MISSION", 150, cheight - 20)
}

animateWithCanvasDraw { canvas =>
    updateState()
    viewState(canvas)
}