Coracle compose

A new version of Coracle but non-portable, targeting Android Compose only. Since Coracle was first written Compose has become the default user interface toolkit for Android, Coracle compose aims to keep the original simple syntax intact by hiding the Compose implementation details.

An example Coracle compose drawing:

class TestDrawing: Drawing() {  
  
    override fun draw(drawScope: DrawScope) {  
        super.draw(drawScope)  
        background(Color.Gray)  
        stroke(Color.Green)  
        line(0, random(height), width, random(height))  
    }  
}

The above test drawing displayed in a Compose screen:

Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->  
    Column(  
        modifier = Modifier.padding(innerPadding)  
    ){  
        CoracleCanvas(  
            modifier = Modifier.fillMaxSize(),  
            drawing = TestDrawing()  
        )  
    }  
}

Status

Under active development.

Demo drawing


package orllewin.coraclecompose.drawings  
  
import androidx.compose.ui.graphics.Color  
import androidx.compose.ui.graphics.drawscope.DrawScope  
import androidx.compose.ui.unit.dp  
import orllewin.coracle.Drawing  
import orllewin.coracle.Line  
import orllewin.coracle.Vector  
  
/**  
 * A port of https://orllewin.uk/graphics/coracle/Avoid+closest 
 */
 class AvoidClosest: Drawing() {  
  
    val worldColour = Color(0xff121212)  
    val nucleusColour = Color(0xFF222C2C)  
    val cellColour = Color(0x55c7e5f2)  
    val cellRadius = 80.dp  
    val nucleusRadius = 15.dp  
    val midpointRadius = 4.dp  
    private val cells = mutableListOf<Cell>()  
  
    override fun setup(width: Int, height: Int) {  
        super.setup(width, height)  
  
        repeat(100){  
            cells.add(Cell())  
        }  
    }  
  
    override fun draw(drawScope: DrawScope) {  
        super.draw(drawScope)  
        background(worldColour)  
  
        cells.forEach { boid ->  
            boid.update().draw()  
        }  
  
    }  
  
    inner class Cell{  
  
        private val maxSpeed = 1.5f  
        private var location = Vector.randomPosition(width, height)  
        private var velocity = Vector(0, 0)  
  
        fun update(): Cell {  
  
            var closestDistance = Float.MAX_VALUE  
            var closestIndex = -1  
  
            cells.forEachIndexed { index, other ->  
                if(other != this){  
                    val distance = location.distance(other.location)  
                    if(distance < closestDistance){  
                        closestIndex = index  
                        closestDistance = distance  
                    }  
                }  
            }  
  
            val closest = cells[closestIndex]  
  
            //While we've got the reference to closest cell, draw the relationship:  
            val line = Line(location.x, location.y, closest.location.x, closest.location.y)  
            val midpoint = line.midpoint()  
  
            stroke(Color.White)  
            line(line)  
  
            fill(Color.White)  
            circle(midpoint, midpointRadius.toPx())  
  
            var direction = location.direction(closest.location)  
            direction.normalize()  
            direction *= -0.2f  
  
            velocity += (direction)  
            velocity.limit(maxSpeed)  
            location += (velocity)  
  
            if (this.location.x > width + cellRadius.toPx()) this.location.x = -cellRadius.toPx()  
            if (this.location.x < -cellRadius.toPx()) this.location.x = width.toFloat() + cellRadius.toPx()  
            if (this.location.y > height + cellRadius.toPx()) this.location.y = -cellRadius.toPx()  
            if (this.location.y < -cellRadius.toPx()) this.location.y = height.toFloat() + cellRadius.toPx()  
  
            return this  
        }  
  
        fun draw(){  
            fill(cellColour)  
            circle(location.x, location.y, (cellRadius.toPx().toInt()/2))  
  
            fill(nucleusColour)  
            circle(location.x, location.y, nucleusRadius.toPx().toInt())  
        }  
    }  
}