Code Sketch


Tricolour With Generative Art
By: Anay Narendra Kamat
Category: Art
cleari

val faces = 5
val radius = 150
val points = 50

setBackground(cm.darkSlateGray)

val theta = List.iterate(0.0, points)(_ + (2 * Math.PI / points))
val radiusValues = theta.map { thetaValue =>
    Math.cos(Math.PI / faces) / Math.cos((thetaValue % (2 * Math.PI / faces)) - Math.PI / faces)
}
val distances =
    radiusValues.sliding(2, 1).map { values =>
        Math.abs(values(1) - values(0))
    }.toList

val cummulativeDistances = distances.scan(0.0)(_ + _).map(_.toDouble).toList

val coordinates = (radiusValues.map(_ * radius), theta, cummulativeDistances).zipped.toList

def square = Picture.rect(60, 60)
    .thatsFilledWith(red)
    .thatsStrokeColored(black)
    .thatsTranslated(-30, -30)

def angleRange(angle: Double): (Boolean, Boolean, Boolean, Boolean, Boolean) =
    (
        ((45 * 0) until (45 * 1) - 5).contains(angle.toDegrees.toInt),
        ((45 * 1) - 5 until (45 * 3) + 30).contains(angle.toDegrees.toInt),
        ((45 * 3) + 30 until (45 * 5) - 10).contains(angle.toDegrees.toInt),
        ((45 * 5) - 10 until (45 * 7) + 30).contains(angle.toDegrees.toInt),
        ((45 * 7) until (45 * 9)).contains(angle.toDegrees.toInt),
    )

def squareAtShapePoint(t: Double)(state: (Double, Double, Double)) = {
    val (r, angle, d) = state
    val color: Color = angleRange(angle) match {
        case (true, _, _, _, _) => white
        case (_, true, _, _, _) => cm.orange
        case (_, _, true, _, _) => white
        case (_, _, _, true, _) => green
        case (_, _, _, _, true) => white
    }
    val (x, y) = (r * Math.cos(angle), r * Math.sin(angle))
    square
        .thatsRotated(-(d.toDegrees + t)).thatsTranslated(x, y)
        .thatsFilledWith(color)
}

def toCoordinate(data: (Double, Double, Double)): (Double, Double) = {
    val (r, angle, d) = data
    (r * Math.cos(angle), r * Math.sin(angle))
}

def pentagonAtCenter = Picture.fromPath { p =>
    val (startX, startY) = toCoordinate(coordinates.head)
    p.moveTo(startX, startY)
    coordinates.tail.map(toCoordinate).foreach {
        case (x, y) => p.lineTo(x, y)
    }
    p.closePath()
}

def firstHalf(t: Double) =
    coordinates.take(25).map(squareAtShapePoint(t) _)

def secondHalf(t: Double) =
    coordinates.drop(25).map(squareAtShapePoint(t) _)

def fromLeft(t: Double) = picStack(
    picStack(secondHalf(t)),
    picStack(firstHalf(t)),
)

def fromRight(t: Double) = picStack(
    picStack(firstHalf(t)),
    picStack(secondHalf(t)),
)

val cb = canvasBounds

animateWithState(0.0) { s =>
    erasePictures()
    val shapeToShowOnlyRightPart = new java.awt.Rectangle(0, cb.getMinY.toInt, cb.width.toInt / 2, cb.getHeight.toInt)
    val shapeToShowOnlyLeftPart = new java.awt.Rectangle(cb.getMinX.toInt, cb.getMinY.toInt, cb.width.toInt / 2, cb.getHeight.toInt)
    val properPatternOnRight =
        fromLeft(s).withClipping(shapeToShowOnlyRightPart).withPenColor(noColor)
    val properPatternOnLeft =
        fromRight(s).withClipping(shapeToShowOnlyLeftPart).withPenColor(noColor)
    picStack(
        pentagonAtCenter
            .thatsFilledWith(white)
            .withPenColor(noColor),
        properPatternOnRight,
        properPatternOnLeft,
        chakra.thatsRotated(-s*0.3)
    ).draw
    s + 2
}

def chakraLine =
    trans(0, 4) *
        fillColor(blue) *
        penColor(blue) *
        penThickness(0.5) ->
        Picture.fromPath { s =>
            s.moveTo(0, 0)
            s.lineTo(-1, 20)
            s.lineTo(0, 50)
            s.lineTo(1, 20)
            s.closePath()
        }

def chakraOuterCircle =
    penThickness(4) *
        penColor(blue) ->
        Picture.circle(54)

def chakraInnerCircle =
    penThickness(0.5) *
        fillColor(blue) *
        penColor(blue) ->
        Picture.circle(4)

def chakraLines = repeatRotatePicture(23, chakraLine, 15)

def chakra =
    picStack(
        chakraLines,
        chakraOuterCircle,
        chakraInnerCircle
    )

def repeatRotatePicture(step: Int, picture: Picture, angle: Int): Picture =
    if (step > 0) {
        picStack(
            picture,
            rot(angle) -> repeatRotatePicture(step - 1, picture, angle)
        )
    }
    else {
        picture
    }