Skip to content

CuriousNikhil/k5-compose

Repository files navigation

k5-compose

k5-compose is a sketchy port of P5.js for Jetpack Compose Desktop.

aweseome kotlin

This library provides you a playground to play with your sketches so you don't have to worry about maintaining/remembering states and setting up the animations etc. You can focus on creating awesome sketches, creating generative art. This library also provides you necessary physics and math functions which are ported from p5.js.

Say for example you can do something like this in just 20 lines of code -

Moving Vehicle code K5 Sketch
carbon fastest_gif

Few examples...

parametric eq particles js gravitation game of life automaton
parametric.mov
particles_js.mov
gravitation.mov
game_of_life.mov
Starfield Rain drops Fractal tree Fireworks
starfield.mov
rain.mov
fractal_tree.mov
fireworks.mov
Circle waves CircleGrid danceYarn (by @elye_project) landscapeInspection (by @elye_project)
circle-waves.mov
hearts.mov
dancingYarnMouse.mov
landscapeInspection.mov

Also don't forget to check awesome examples and blog posts by few contributors

Click on the link to go to the code. Code explains the things in details. Try playing with those by tweaking values and running on your own. πŸ˜„ (I have added videos instead of gifs just so you can view these without loosing any frames πŸ˜‰)

Generative Art with K5-Compose

All the examples can be found here

Blackhole 10Print Circle Loop
image image image
Threads with Perlin-Noise Phyllotaxis Mandlebrot (by @elye_project)
image image image
Perlin noise1d (by @elye_project) Perlin noise2d (by @elye_project) Ribbon
image image image
Circle Packing
Screenshot 2022-03-05 at 11 55 13 PM

Getting started

In order to understand using this library for creating experiments, I would recommend to go through the Nature of Code book by Daniel Shiffman - https://natureofcode.com/book/. This will give you the overall knowledge about how a physics system works in simplest way in p5/k5 or in general computer world.
However, you could start digging into it right away by checking examples.

  1. Create your Jetpack Compose Desktop project. Add the following dependency in your build.gradle along with the compose dependencies
implementation("me.nikhilchaudhari:k5-compose:{latest-version}")
  1. Use k5 dsl construct to create K5 sketch
fun main() = k5{

  // Initialise your variables, objects

  show { drawScope ->
    // use drawScope to draw the shapes
  }  
}
  1. Use library apis for calculations and you are good to go! :p

No need to manage/remember states, animation or anything. Just focus on your logic to design sketch rest of the things are handled by the library.

How do I do that?

k5

It's very easy, you have to use k5{...} builder in order to initialise your k5-sketch. The things you define in here will be initialised only once.

fun main() = k5{
  // you can define all your properties, variables, vectors here.
    val position = Vector2D(20f, 20f)
    
    // Say you want to have a control over your shape and few other parameters like, force, acceleration, mass etc. You can use classes to represent your shapes.
    val spaceShip = SpaceShip(position, size) // some data class representing spaceship
    
  //...
}

You can pass size param in k5()

  fun main() = k5(size = Size(800f, 500f)) { ... }

And there are more number of configurations you can do to k5-compose playground which will be applied to the Window{..} composable. You can check API docs.

show

Once you have initialised your necessary stuff which is going to change while running the frame of animation, you have to draw your shape/sketch in the show{...} function. show function gives you a canvas drawscope which you can used to draw into the k5 compose playground.

Note: Whatever you pass in show{...} lambda will run for every frame. So if you want to update the values you've initialised and draw the sketch every frame you can pass those things in the lambda. For example -

fun main() = k5{
  val vehiclePosition = Vector2D(20f, 20f)

  show{ drawScope ->
      
      //update your vehicle position, 
      vehiclePosition.add(Vector2D.randomVector() * 2f)
      
       drawScope.drawCircle(color = Color.White, radius = 30f, center = vehiclePosition.toOffSet())
  }
}

You can apply all the compose Modifiers to the playground like changing background, color and taking keyboard and mouse pointer input.

    show(modifier = Modifier
        .background(Color.Yellow)
        .pointerMoveFilter(
            onMove = {
                //use mouse pointer values here
                false
            }
        )
    ) {
        // Draw your sketch
    }

showWithControls

If you want to add some interactivity with controls for your sketch, you can use showWithControls(){..}. You can add readily available Jetpack Compose elements like Slider, Checkbox, Toggle, Button, etc. And use State to change, update, take the inputs to your k5 sketch.

fun main() = k5 {

val numbers = mutableStateOf(0.3f)

// Here Slider is pure Jetpack Compose element
showWithControls(controls = {
      Text("Number of squares")
      Slider(
          value = numbers.value,
          onValueChange = { numbers.value = it }
      )
  }){ drawScope ->

      // Use the numbers value here
      // Draw your sketch.
  }

Few examples here and here. Which looks something like this - video

image

This adds 2/3rd of your specified width to the k5 compose playground window.

Few handy Apis

noLoop

Here, the vehiclePosition is constantly updated by adding a random vector to it's previous position and then a new circle is drawn on the playground based on the updated position. Simple, right?

Let's say you don't want to keep your sketch in loop. Let's say you want to draw it only once. You can use noLoop() method in k5{...}.

Playground size

You can use dimensInt or dimensFloat properties available in k5{...} to get the size of the playground. You can pass the size in k5() as well but there are few density display metrics related issues in using floating point values. So to avoid any miscalculations, these Size values can be used to get the precise height and width of your playground.

fun main() = k5 {

    // Use noLoop to draw your content only once
    noLoop()

    show {
        // this will be drawn only once
    }

}

How do I use math and physics functions?

To use and understand mathematics and physics I would recommend Nature Of Code book and Video series by Daniel Shiffman. Or you can go through the examples section in the repo.

Vectors

Vector2D is data class - a vector representing any vector quantity in 2D plane. You can create a simple vector like val acceleration = Vector2D(x = 2f, y = 3f), this means that the acceleration vector's x component is 2f and y component is 3f.

To perform vector operations there are extensions and operators available to give you all the necessary vector algebra APIs. You can take a look at those methods here in the API Docs.

Few helper methods available to create vectors from angle and also to create random vectors -

Random Vector You can create random unit vector by using static method randomVector of Vector2D class. This creates a random unit vector.

val position = Vector2D.randomVector()

Vector from angle If you want to create a vector using any angle you can use fromAngle static method. For ex - the below code will create a vector with angle as PI radians and length of 2. (means x = 2 * cos(angle), y = 2 * sin(angle))

val position = Vector2D.fromAnAngle(angle = PI, length = 2f)

toOffset There's another handy method to convert your vector to the Offset type since you need to pass Offset types while drawing in Jetpack Compose. So you can convert Vector2D into Offset. Using toOffset on vector value.

 val position = Vector2D(2f, 5f)
    position.toOffSet()

Random

You can use the Random functions available in Kotlin by default. To quickly generate any random number within particular range, there are helper extensions available over ClosedRange for any Number data types.

For ex - (-12f..-8f).random() or (1..40).random() or (1.0..10.0).random() etc

If you want ot generate randomGaussian or a random number with your custom set seed value, apis are available to set the seed for randomness etc. You can check it here. And you can use k5Random(api doc) and randomGaussian(api doc) functions to generate random values.

Noise

Noise is used a lot in p5 to generate a smooth randomness in numbers. This library contains Perlin noise helper methods which will generate noise values. You can check it here.

There are three methods available for generating noise values - noise1D(x: Double), fun noise2D(x: Double, y: Double) and fun noise3D(x: Double, y: Double, z: Double)

If you don't know what noise is please take a look here

Trigonometry

You could of course use the basic kotlin.math trigonometric functions but just to keep it in handy this library has extensions functions over Float values. So you can just convert any float value to trigonometric function value over this float.

  val tan = 1f.tan()
  val cos = 0.2f.cos()
  val sin = 0.1f.sin()

  val atan = 1f.atan()
  val acos = 0.2f.acos()
  val asin = 0.1f.asin()

You can check few more trigonometric functions here

Angle The default angle measurement is in radians. If you want to change the angles to be measured in the degrees, then you can set the angleMode to degrees. And degress will be used to measure the angle and in all the functions related to angles.

angleMode = AngleMode.DEGREES

Calculations

There are certain calculations that are related to vector, numbers etc which are required when you write physics system in a 2D environment. Those few methods are directly ported from p5.js. You can find some functions like lerp, map, norm, constrain etc. here

Contributors

Contribution Guide

PRs are welcomed! Please check contribution guide here

License

Licensed under Apache License, Version 2.0 here