Code Sketch


atharva ghodke
By: Mhalsakant School
Category: Programming
// ============================================================
// FUTURISTIC HOLOGRAM ROBOT - WALK AND SAY HI
// KOJO / SCALA
// ============================================================

cleari()
originBottomLeft()

class Sketch {

  var t = 0.0
  var bob = 0.0
  var ringAngle = 0.0
  var eyeGlow = 0.0
  var coreGlow = 0.0
  var walkPhase = 0.0
  var waveAngle = 0.0

  var waving = false
  var walking = false

  var robotX = cwidth / 2.0
  var robotY = 205.0

  var targetX = robotX
  var targetY = robotY

  var lookX = 0.0
  var lookY = 0.0

  var hiTimer = 0
  var clicked = false
  var clickX = 0.0
  var clickY = 0.0

  def clamp(v: Double, lo: Double, hi: Double): Double =
    math.max(lo, math.min(hi, v))

  def alpha(v: Double): Int =
    clamp(v, 0.0, 255.0).toInt

  // ----------------------------------------------------------
  // BACKGROUND
  // ----------------------------------------------------------

  def drawBackground(c: CanvasDraw): Unit = {

    c.background(5, 10, 28)

    c.stroke(20, 75, 115, 65)
    c.strokeWeight(1)

    for (x <- 0 until cwidth.toInt by 35) {
      c.line(x, 55, x, cheight)
    }

    for (y <- 55 until cheight.toInt by 35) {
      c.line(0, y, cwidth, y)
    }

    for (i <- 0 until 45) {

      val px =
        (i * 97.0 + math.sin(t + i) * 12.0) % cwidth

      val py =
        60.0 +
        ((i * 61.0 + t * (12 + i % 5)) %
        (cheight - 70))

      val size = 2.0 + math.sin(t * 2.0 + i)

      c.noStroke()
      c.fill(0, 210, 255, 150)
      c.ellipse(px, py, size, size)
    }

    c.noStroke()
    c.fill(0, 130, 220, 35)
    c.ellipse(cwidth / 2.0, 75, 360, 45)

    c.fill(0, 190, 255, 25)
    c.ellipse(cwidth / 2.0, 75, 250, 25)
  }

  // ----------------------------------------------------------
  // HOLOGRAM PLATFORM
  // ----------------------------------------------------------

  def drawPlatform(c: CanvasDraw): Unit = {

    val cx = robotX
    val cy = 88.0

    c.noFill()
    c.stroke(0, 200, 255, 150)
    c.strokeWeight(2)

    c.ellipse(cx, cy, 190, 30)
    c.ellipse(cx, cy, 145, 22)

    c.stroke(0, 230, 255, 90)

    c.ellipse(
      cx, cy,
      220 + math.sin(t * 2.0) * 10.0,
      38
    )

    c.noStroke()
    c.fill(0, 190, 255, 80)
    c.ellipse(cx, cy, 90, 12)
  }

  // ----------------------------------------------------------
  // ENERGY RINGS
  // ----------------------------------------------------------

  def drawRings(c: CanvasDraw): Unit = {

    val cx = robotX
    val cy = robotY + bob

    c.noFill()
    c.strokeWeight(2)

    c.stroke(0, 230, 255, 130)

    c.ellipse(
      cx, cy + 20,
      170 + math.sin(ringAngle) * 12,
      38
    )

    c.stroke(100, 70, 255, 170)

    c.ellipse(
      cx, cy + 20,
      135,
      30 + math.cos(ringAngle) * 8
    )

    for (i <- 0 until 6) {

      val a = ringAngle + i * math.Pi / 3.0

      val px = cx + math.cos(a) * 83
      val py = cy + 20 + math.sin(a) * 18

      c.noStroke()
      c.fill(0, 240, 255, 230)
      c.ellipse(px, py, 7, 7)

      c.fill(0, 200, 255, 55)
      c.ellipse(px, py, 15, 15)
    }
  }

  // ----------------------------------------------------------
  // WALKING LEGS
  // ----------------------------------------------------------

  def drawLegs(c: CanvasDraw): Unit = {

    val bx = robotX
    val by = robotY + bob

    var leftStep = 0.0
    var rightStep = 0.0

    if (walking) {
      leftStep = math.sin(walkPhase) * 12
      rightStep = math.sin(walkPhase + math.Pi) * 12
    }

    c.stroke(120, 230, 255)
    c.strokeWeight(12)

    c.line(
      bx - 23, by - 35,
      bx - 30 + leftStep, by - 65
    )

    c.line(
      bx + 23, by - 35,
      bx + 30 + rightStep, by - 65
    )

    c.stroke(35, 130, 190)
    c.strokeWeight(5)

    c.line(
      bx - 30 + leftStep, by - 65,
      bx - 42 + leftStep, by - 75
    )

    c.line(
      bx + 30 + rightStep, by - 65,
      bx + 42 + rightStep, by - 75
    )

    c.noStroke()
    c.fill(0, 220, 255, 180)

    c.ellipse(bx - 42 + leftStep, by - 76, 20, 8)
    c.ellipse(bx + 42 + rightStep, by - 76, 20, 8)

    c.fill(30, 70, 125)
    c.stroke(0, 230, 255)
    c.strokeWeight(2)

    c.ellipse(bx - 27 + leftStep, by - 49, 15, 15)
    c.ellipse(bx + 27 + rightStep, by - 49, 15, 15)
  }

  // ----------------------------------------------------------
  // ROBOT BODY
  // ----------------------------------------------------------

  def drawBody(c: CanvasDraw): Unit = {

    val bx = robotX
    val by = robotY + bob

    c.fill(20, 85, 145)
    c.stroke(0, 225, 255)
    c.strokeWeight(2)

    c.ellipse(bx - 39, by + 28, 27, 30)
    c.ellipse(bx + 39, by + 28, 27, 30)

    c.fill(15, 35, 72)
    c.stroke(40, 210, 255)
    c.strokeWeight(3)

    c.ellipse(bx, by + 5, 78, 88)

    c.fill(20, 70, 115)
    c.stroke(80, 230, 255)

    c.ellipse(bx, by + 12, 53, 59)

    val glow = alpha(140 + math.sin(coreGlow) * 90)

    c.noStroke()
    c.fill(0, 200, 255, 45)
    c.ellipse(bx, by + 12, 53, 53)

    c.fill(0, 230, 255, glow)
    c.ellipse(bx, by + 12, 30, 30)

    c.fill(210, 255, 255, 240)
    c.ellipse(bx, by + 12, 13, 13)

    c.stroke(0, 230, 255, 200)
    c.strokeWeight(2)

    c.line(bx - 20, by + 28, bx - 10, by + 20)
    c.line(bx + 20, by + 28, bx + 10, by + 20)
    c.line(bx - 19, by - 8, bx - 10, by - 3)
    c.line(bx + 19, by - 8, bx + 10, by - 3)

    c.noStroke()
    c.fill(110, 255, 255)

    for (i <- 0 until 3) {
      c.ellipse(bx - 9 + i * 9, by - 20, 4, 4)
    }
  }

  // ----------------------------------------------------------
  // ARMS AND WAVING
  // ----------------------------------------------------------

  def drawArms(c: CanvasDraw): Unit = {

    val bx = robotX
    val by = robotY + bob

    val swing =
      if (waving) math.sin(waveAngle) * 28
      else math.sin(t * 2.0) * 5

    c.stroke(65, 190, 240)
    c.strokeWeight(11)

    c.line(bx - 38, by + 28, bx - 56, by + 3)
    c.line(bx - 56, by + 3, bx - 65, by - 17)

    c.fill(20, 90, 155)
    c.stroke(0, 225, 255)
    c.strokeWeight(2)

    c.ellipse(bx - 56, by + 3, 17, 17)
    c.ellipse(bx - 65, by - 17, 18, 18)

    val handX = bx + 70
    val handY = by + 7 + swing

    c.stroke(65, 190, 240)
    c.strokeWeight(11)

    c.line(bx + 38, by + 28, bx + 56, by + 4)
    c.line(bx + 56, by + 4, handX, handY)

    c.fill(20, 90, 155)
    c.stroke(0, 225, 255)
    c.strokeWeight(2)

    c.ellipse(bx + 56, by + 4, 17, 17)

    c.fill(100, 245, 255, 220)
    c.ellipse(handX, handY, 21, 21)

    c.stroke(120, 255, 255)
    c.strokeWeight(2)

    for (i <- 0 until 3) {
      c.line(
        handX + 3, handY + 2,
        handX + 13, handY + 5 + i * 5
      )
    }
  }

  // ----------------------------------------------------------
  // ROBOT HEAD AND FACE
  // ----------------------------------------------------------

  def drawHead(c: CanvasDraw): Unit = {

    val hx = robotX
    val hy = robotY + bob + 65

    c.fill(20, 80, 130)
    c.stroke(0, 220, 255)
    c.strokeWeight(2)

    c.rect(hx - 12, hy - 34, 24, 15)

    c.noStroke()
    c.fill(0, 150, 255, 35)
    c.ellipse(hx, hy, 94, 76)

    c.fill(15, 35, 68)
    c.stroke(70, 220, 255)
    c.strokeWeight(3)

    c.rect(hx - 39, hy - 29, 78, 58)

    c.fill(5, 20, 42)
    c.stroke(0, 130, 210)

    c.rect(hx - 31, hy - 21, 62, 41)

    val eye = alpha(170 + math.sin(eyeGlow) * 75)

    c.noStroke()
    c.fill(0, 220, 255, 65)

    c.ellipse(hx - 15 + lookX, hy + 5 + lookY, 23, 23)
    c.ellipse(hx + 15 + lookX, hy + 5 + lookY, 23, 23)

    c.fill(80, 255, 255, eye)

    c.ellipse(hx - 15 + lookX, hy + 5 + lookY, 11, 11)
    c.ellipse(hx + 15 + lookX, hy + 5 + lookY, 11, 11)

    c.fill(255, 255, 255, 240)

    c.ellipse(hx - 15 + lookX, hy + 5 + lookY, 4, 4)
    c.ellipse(hx + 15 + lookX, hy + 5 + lookY, 4, 4)

    c.stroke(0, 220, 255)
    c.strokeWeight(2)

    c.line(hx - 10, hy - 10, hx + 10, hy - 10)

    c.stroke(80, 210, 255)
    c.strokeWeight(3)

    c.line(hx, hy + 29, hx, hy + 43)

    c.noStroke()
    c.fill(0, 240, 255, eye)
    c.ellipse(hx, hy + 47, 10, 10)

    c.fill(0, 230, 255, 45)
    c.ellipse(hx, hy + 47, 21, 21)

    c.fill(20, 100, 155)
    c.stroke(0, 220, 255)
    c.strokeWeight(2)

    c.rect(hx - 46, hy - 12, 8, 24)
    c.rect(hx + 38, hy - 12, 8, 24)

    c.noStroke()
    c.fill(120, 255, 255)

    c.ellipse(hx - 42, hy, 4, 10)
    c.ellipse(hx + 42, hy, 4, 10)

    // HI SPEECH BUBBLE
    if (hiTimer > 0) {

      c.fill(0, 30, 65, 230)
      c.stroke(0, 240, 255)
      c.strokeWeight(2)

      c.rect(hx + 42, hy + 28, 66, 34)

      c.noStroke()
      c.fill(120, 255, 255)

      c.text("HI!", hx + 59, hy + 40)
    }
  }

  // ----------------------------------------------------------
  // HUD
  // ----------------------------------------------------------

  def drawHUD(c: CanvasDraw): Unit = {

    val x = 30.0
    val y = cheight - 55.0

    c.noStroke()
    c.fill(0, 220, 255)

    c.text("HOLOGRAM SYSTEM", x, y)
    c.text("ROBOT STATUS: ONLINE", x, y - 22)

    c.fill(100, 220, 255, 160)

    for (i <- 0 until 12) {
      val h = 5 + ((i * 7 + t.toInt) % 18)
      c.rect(x + i * 12, y - 48, 6, h)
    }

    c.fill(0, 220, 255, 220)
    c.text("CLICK ANYWHERE TO MOVE AND SAY HI", 20, 25)
  }

  // ----------------------------------------------------------
  // CLICK EFFECT
  // ----------------------------------------------------------

  def drawClickEffect(c: CanvasDraw): Unit = {

    if (clicked) {

      val radius = 15 + (t * 18 % 55)

      c.noFill()
      c.stroke(0, 230, 255, 180)
      c.strokeWeight(2)

      c.ellipse(clickX, clickY, radius * 2, radius * 2)

      c.stroke(120, 80, 255, 130)

      c.ellipse(
        clickX, clickY,
        radius * 1.4,
        radius * 1.4
      )

      if (radius > 65) {
        clicked = false
      }
    }
  }

  // ----------------------------------------------------------
  // SETUP
  // ----------------------------------------------------------

  def setup(c: CanvasDraw): Unit = {
    c.background(5, 10, 28)
  }

  // ----------------------------------------------------------
  // MAIN ANIMATION LOOP
  // ----------------------------------------------------------

  def drawLoop(c: CanvasDraw): Unit = {

    t += 0.04
    ringAngle += 0.045
    eyeGlow += 0.08
    coreGlow += 0.06

    drawBackground(c)

    // Click to choose destination
    if (isMousePressed) {

      targetX = clamp(
        mouseX.toDouble,
        60.0,
        cwidth - 60.0
      )

      targetY = clamp(
        mouseY.toDouble,
        160.0,
        280.0
      )

      clickX = mouseX.toDouble
      clickY = mouseY.toDouble

      clicked = true
      waving = true
      waveAngle = 0.0
      hiTimer = 100
    }

    // Move towards the target
    val distanceX = targetX - robotX

    walking = math.abs(distanceX) > 2.0

    if (walking) {

      robotX += clamp(distanceX, -2.0, 2.0)

      walkPhase += 0.25
    }

    // Smooth vertical movement
    val distanceY = targetY - robotY

    robotY += clamp(distanceY, -1.0, 1.0)

    // Floating animation
    bob = math.sin(t * 2.0) * 3.0

    // Eyes follow the target
    lookX = clamp(
      (targetX - robotX) * 0.035,
      -5.0,
      5.0
    )

    lookY = clamp(
      (targetY - robotY) * 0.025,
      -4.0,
      4.0
    )

    // Wave animation
    if (waving) {

      waveAngle += 0.22

      if (waveAngle > 12.0) {
        waving = false
        waveAngle = 0.0
      }
    }

    if (hiTimer > 0) {
      hiTimer -= 1
    }

    // Draw everything
    drawPlatform(c)
    drawRings(c)
    drawLegs(c)
    drawBody(c)
    drawArms(c)
    drawHead(c)
    drawClickEffect(c)
    drawHUD(c)
  }
}

// ============================================================
// START ROBOT
// ============================================================

val sketch = new Sketch
canvasSketch(sketch)