Code Sketch


BHA,,ve,sh,ArT///?
By: Mhalsakant School
Category: Art
// --- GRAND INDEPENDENCE DAY CELEBRATION WITH STUDENTS, TEACHER & SOLDIERS ---
cleari()
originBottomLeft()

// ?. "?? ?? ??" ????????????? ??????? ????? (Full Anthem Notes)
setNoteInstrument(Instrument.PIANO)
val fullJanaGanaTune = Array(
    (53, 200), (55, 200), (57, 200), (57, 200), (57, 200), (57, 200), (57, 200), 
    (57, 200), (57, 200), (55, 200), (57, 200), (58, 200),
    (57, 200), (57, 200), (57, 200), (55, 200), (55, 200), (55, 200), (53, 200), (55, 200), (53, 300),
    (55, 200), (57, 200), (55, 200), (53, 300),
    (53, 200), (60, 200), (60, 200), (60, 200), (60, 200), (60, 200), (60, 200),
    (60, 200), (58, 200), (60, 200), (58, 200), (57, 200), (55, 200), (57, 200), (58, 300),
    (57, 200), (57, 200), (57, 200), (57, 200), (55, 200), (58, 200), (57, 200),
    (55, 200), (55, 200), (53, 200), (55, 200), (53, 300),
    (53, 200), (55, 200), (57, 200), (57, 200), (57, 200), (55, 200), (57, 200), (58, 300),
    (57, 200), (55, 200), (57, 200), (53, 200), (55, 200), (53, 300),
    (53, 200), (55, 200), (57, 200), (57, 200), (57, 200), (57, 200), (57, 200),
    (55, 200), (57, 200), (58, 200), (57, 200), (55, 200), (53, 300),
    (60, 250), (58, 250), (60, 300),
    (58, 250), (57, 250), (58, 300),
    (57, 250), (55, 250), (57, 300),
    (53, 200), (53, 200), (55, 200), (55, 200), (57, 200), (57, 200), (58, 400)
)
var tuneIndex = 0

// ????????????? ??????? (Letter by Letter)
val fullAnthemText = 
    "Jana Gana Mana Adhinayaka Jaya He\n" +
    "Bharata Bhagya Vidhata\n" +
    "Punjaba Sindhu Gujarata Maratha\n" +
    "Dravida Utkala Banga\n" +
    "Vindhya Himachala Yamuna Ganga\n" +
    "Uchhala Jaladhi Taranga\n" +
    "Tava Subha Name Jage\n" +
    "Tava Subha Asisa Mange\n" +
    "Gahe Tava Jaya Gatha\n" +
    "Jana Gana Mangala Dayaka Jaya He\n" +
    "Bharata Bhagya Vidhata\n" +
    "Jaya He, Jaya He, Jaya He\n" +
    "Jaya Jaya Jaya Jaya He!"

var charIndex = 0

def playJanaGanaStep() {
    if (tuneIndex < fullJanaGanaTune.length) {
        val (note, duration) = fullJanaGanaTune(tuneIndex)
        playNote(note, duration)
        tuneIndex = (tuneIndex + 1) % fullJanaGanaTune.length
    }
}

val gravity = Vector2D(0, -0.08)
var waveAngle = 0.0
var chakraRotation = 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() {
        playJanaGanaStep()
        exploded = true
        
        // ?????? ???? ??????
        if (charIndex < fullAnthemText.length) {
            charIndex = math.min(charIndex + 3, fullAnthemText.length)
        }
    }

    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)
            p.step()
        }
    }

    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
}

// ?. ??????? ???? ??? ???? ????
def drawWavingTricolorFlag(canvas: CanvasDraw) {
    val fw = 150.0
    val fh = 26.0
    val fx = (cwidth - fw) / 2.0 + 130.0
    val fy = cheight / 2.0

    // ????????? (Flag Pole)
    canvas.stroke(200, 200, 200)
    canvas.strokeWeight(5)
    canvas.line(fx, fy + fh * 3 + 15, fx, fy - 180)

    val stepSize = 4.0
    var x = 0.0
    while (x < fw) {
        val yOffset = math.sin(waveAngle + (x * 0.03)) * 8.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.03)) * 8.0)
    val radius = 10.0

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

    canvas.strokeWeight(1)
    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 drawSingleSoldier(canvas: CanvasDraw, sx: Double, sy: Double) {
    canvas.fill(45, 80, 45)
    canvas.stroke(20, 50, 20)
    canvas.strokeWeight(1)
    canvas.rect(sx - 10, sy, 20, 32) // ????

    canvas.fill(240, 195, 150)
    canvas.ellipse(sx, sy + 42, 15, 15) // ?????

    canvas.fill(30, 60, 30)
    canvas.ellipse(sx, sy + 47, 18, 12) // ???????

    canvas.stroke(45, 80, 45)
    canvas.strokeWeight(4)
    canvas.line(sx - 10, sy + 28, sx - 15, sy + 8) // ???? ???

    canvas.line(sx + 10, sy + 28, sx + 18, sy + 40) // ??????? ???? ???
    canvas.line(sx + 18, sy + 40, sx + 6, sy + 47)

    canvas.stroke(30, 50, 30)
    canvas.strokeWeight(5)
    canvas.line(sx - 5, sy, sx - 5, sy - 20) // ???
    canvas.line(sx + 5, sy, sx + 5, sy - 20)
}

// ?. ??? ???????? (Teacher in Saree)
def drawTeacher(canvas: CanvasDraw, tx: Double, ty: Double) {
    // ???? (Orange/Saffron Saree)
    canvas.fill(255, 128, 0)
    canvas.stroke(200, 80, 0)
    canvas.strokeWeight(1)
    
    // ?????? ?????????? ?????
    canvas.rect(tx - 10, ty, 20, 35)

    // ??? ??? ?????
    canvas.fill(240, 195, 150)
    canvas.ellipse(tx, ty + 45, 16, 16)

    // ??? (Hair)
    canvas.fill(30, 20, 10)
    canvas.ellipse(tx, ty + 50, 18, 10)

    // ??????? / ???????? ??? (Folded Hands)
    canvas.stroke(240, 195, 150)
    canvas.strokeWeight(3)
    canvas.line(tx, ty + 25, tx, ty + 33)

    // ???
    canvas.stroke(50, 50, 50)
    canvas.strokeWeight(4)
    canvas.line(tx - 4, ty, tx - 4, ty - 18)
    canvas.line(tx + 4, ty, tx + 4, ty - 18)
}

// ?. ? ?????????? (School Students in Uniform)
def drawStudent(canvas: CanvasDraw, stx: Double, sty: Double, isBoy: Boolean) {
    // ????? ????????? (White Shirt + Blue Pant/Skirt)
    canvas.fill(255, 255, 255) // ?????? ????
    canvas.stroke(180, 180, 180)
    canvas.strokeWeight(1)
    canvas.rect(stx - 8, sty + 10, 16, 18)

    // ??? ????? ??????
    canvas.fill(0, 51, 102) // ?????? ????
    canvas.rect(stx - 8, sty, 16, 10)

    // ?????
    canvas.fill(240, 195, 150)
    canvas.ellipse(stx, sty + 34, 14, 14)

    // ???
    canvas.fill(20, 20, 20)
    if (isBoy) {
        canvas.ellipse(stx, sty + 38, 14, 8)
    } else {
        canvas.ellipse(stx, sty + 38, 16, 12) // ??????? ???? ???
    }

    // ??????? ???
    canvas.stroke(240, 195, 150)
    canvas.strokeWeight(3)
    canvas.line(stx + 8, sty + 22, stx + 14, sty + 30)
    canvas.line(stx + 14, sty + 30, stx + 4, sty + 36)

    // ???
    canvas.stroke(0, 51, 102)
    canvas.strokeWeight(4)
    canvas.line(stx - 4, sty, stx - 4, sty - 15)
    canvas.line(stx + 4, sty, stx + 4, sty - 15)
}

// ?. ??????? ????? ????? ?????? (Group Assembly)
def drawGroupAssembly(canvas: CanvasDraw) {
    val baseX = (cwidth / 2.0) - 10.0
    val baseY = (cheight / 2.0) - 120.0

    // ?. ????????? ??? (?????? ??????)
    drawSingleSoldier(canvas, baseX + 100, baseY)
    drawSingleSoldier(canvas, baseX + 130, baseY)
    drawSingleSoldier(canvas, baseX + 160, baseY)

    // ?. ???????? (????????)
    drawTeacher(canvas, baseX + 50, baseY - 5)

    // ?. ?????? ???? ??? ???? (?????? ??????)
    drawStudent(canvas, baseX - 10, baseY - 10, true)   // ????? ?
    drawStudent(canvas, baseX + 12, baseY - 10, false)  // ????? ?
    drawStudent(canvas, baseX - 30, baseY - 10, true)  // ????? ?
}

// ?. ?????-???-????? ??????????
def drawLetterByLetterAnthem(canvas: CanvasDraw) {
    val visibleSubstring = fullAnthemText.substring(0, charIndex)
    val lines = visibleSubstring.split("\n")
    
    val startX = 20.0
    var startY = cheight - 80.0

    for (i <- lines.indices) {
        if (i == lines.length - 1) {
            canvas.fill(255, 153, 51) // ????? ???
        } else {
            canvas.fill(220, 220, 220) // ?????? ???
        }
        canvas.text(lines(i), startX, startY)
        startY -= 20.0
    }
}

def viewState(canvas: CanvasDraw) {
    canvas.fill(10, 10, 25, 45)
    canvas.noStroke()
    canvas.rect(0, 0, cwidth, cheight)

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

    // ??????????
    drawLetterByLetterAnthem(canvas)

    // ?????? ????
    drawWavingTricolorFlag(canvas)

    // ??????????, ?????? ??? ?????
    drawGroupAssembly(canvas)
}

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