Code Sketch


bhavesh
By: Mhalsakant School
Category: Art
// 3D Vector & Projection Setup
case class Vector3D(x: Double, y: Double, z: Double)

val size = canvasBounds.width * 0.4
val viewAngleHalf = Math.toRadians(60.0 / 2.0)

def rotateX(p: Vector3D, angle: Double): Vector3D = {
  val rad = Math.toRadians(angle)
  Vector3D(p.x, p.y * Math.cos(rad) - p.z * Math.sin(rad), p.y * Math.sin(rad) + p.z * Math.cos(rad))
}

def rotateY(p: Vector3D, angle: Double): Vector3D = {
  val rad = Math.toRadians(angle)
  Vector3D(p.x * Math.cos(rad) + p.z * Math.sin(rad), p.y, -p.x * Math.sin(rad) + p.z * Math.cos(rad))
}

def rotateZ(p: Vector3D, angle: Double): Vector3D = {
  val rad = Math.toRadians(angle)
  Vector3D(p.x * Math.cos(rad) - p.y * Math.sin(rad), p.x * Math.sin(rad) + p.y * Math.cos(rad), p.z)
}

def project(p: Vector3D): (Double, Double, Double) = {
  val distance = size + 350
  val pz = p.z + distance
  val factor = size / (pz * Math.tan(viewAngleHalf))
  (p.x * factor, p.y * factor, pz)
}

// Colors including Navy Blue for Ashoka Chakra
val flagColors = Map(
  "SAFFRON" -> Color(255, 153, 51),
  "WHITE"   -> Color(255, 255, 255),
  "GREEN"   -> Color(19, 136, 8),
  "POLE"    -> Color(128, 128, 128),
  "CHAKRA"  -> Color(0, 0, 128) // Navy Blue
)

case class MiniBlock(cx: Double, cy: Double, cz: Double, s: Double, colorType: String) {
  val h = s / 2.0
  
  val vertices = Seq(
    Vector3D(cx - h, cy - h, cz - h), // 0
    Vector3D(cx + h, cy - h, cz - h), // 1
    Vector3D(cx + h, cy + h, cz - h), // 2
    Vector3D(cx - h, cy + h, cz - h), // 3
    Vector3D(cx - h, cy - h, cz + h), // 4
    Vector3D(cx + h, cy - h, cz + h), // 5
    Vector3D(cx + h, cy + h, cz + h), // 6
    Vector3D(cx - h, cy + h, cz + h)  // 7
  )

  val faces = Seq(
    ("FRONT",  Seq(4, 5, 6, 7), Vector3D(0, 0, 1)),
    ("BACK",   Seq(1, 0, 3, 2), Vector3D(0, 0, -1)),
    ("TOP",    Seq(3, 2, 6, 7), Vector3D(0, 1, 0)),
    ("BOTTOM", Seq(4, 5, 1, 0), Vector3D(0, -1, 0)),
    ("RIGHT",  Seq(5, 1, 2, 6), Vector3D(1, 0, 0)),
    ("LEFT",   Seq(0, 4, 7, 3), Vector3D(-1, 0, 0))
  )
}

val blockSpacing = 30.0
val blockSize = 28.0

// Construct Flag with Ashoka Chakra in the center white band
val flagBlocks = {
  val flagPart = for {
    x <- -1 to 2
    y <- -1 to 1
    z <- 0 to 0
  } yield {
    // Place Ashoka Chakra block right in the center of the white band
    val colorType = if (x == 0 && y == 0 && z == 0) {
      "CHAKRA"
    } else {
      y match {
        case 1  => "SAFFRON"
        case 0  => "WHITE"
        case -1 => "GREEN"
      }
    }
    MiniBlock(x * blockSpacing, y * blockSpacing, z * blockSpacing, blockSize, colorType)
  }

  val polePart = for {
    y <- -3 to 3
  } yield {
    MiniBlock(-2.0 * blockSpacing, y * blockSpacing, 0.0, blockSize, "POLE")
  }

  flagPart ++ polePart
}

// Animation Loop
animateWithState(0.0) { angle =>
  erasePictures()

  val rotX = angle * 0.3
  val rotY = angle * 0.5
  val rotZ = angle * 0.1

  var allRenderFaces = Seq.empty[(Seq[(Double, Double)], Color, Double)]

  flagBlocks.foreach { mini =>
    mini.faces.foreach { case (faceName, idxs, normal) =>
      
      val rotatedVerts = idxs.map { i =>
        val v = mini.vertices(i)
        val r = rotateZ(rotateY(rotateX(v, rotX), rotY), rotZ)
        project(r)
      }

      val rNormal = rotateZ(rotateY(rotateX(normal, rotX), rotY), rotZ)

      if (rNormal.z < 0) {
        val points2D = rotatedVerts.map { case (px, py, _) => (px, py) }
        val avgZ = rotatedVerts.map(_._3).sum / 4.0
        val color = flagColors(mini.colorType)

        allRenderFaces = allRenderFaces :+ (points2D, color, avgZ)
      }
    }
  }

  val sortedFaces = allRenderFaces.sortBy(_._3).reverse

  sortedFaces.foreach { case (pts, color, _) =>
    val facePic = Picture.fromVertexShape { p =>
      p.beginShape()
      pts.foreach { case (x, y) => p.vertex(x, y) }
      p.endShape()
    }
    facePic.setFillColor(color)
    facePic.setPenColor(Color(30, 30, 30))
    facePic.setPenThickness(1)
    draw(facePic)
  }

  angle + 1.0
}