Drawing per-vertex colored meshes and variable thickness lines

Thickline

A class to draw variable width lines

apps4.WigglyLine2-2022-01-29-21.06.05

imports
import org.openrndr.KEY_ESCAPE
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.math.Vector2
import org.openrndr.shape.ShapeContour
import org.openrndr.extra.videoprofiles.gif
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
fun main() = application {
    configure {
        width = 400
        height = 400
    }
    program {
        val thickLine = ThickLine(
            200, ColorRGBa.WHITE, ColorRGBa.GRAY
        ) { t -> 10.0 - 10.0 * cos(t * PI * 2) }

        // extend(ScreenRecorder()) { gif() }

        extend {
            drawer.clear(ColorRGBa.WHITE)

            val thinLine = ShapeContour.fromPoints(List(50) {
                val a = seconds * 2 + it * 0.08
                Vector2(
                    sin(a + sin(a * 0.31)),
                    sin(a * 0.53 + sin(a * 0.73))
                ) * 160.0 + drawer.bounds.center
            }, false)

            thickLine.draw(drawer, thinLine)
        }
        keyboard.keyDown.listen {
            if(it.key == KEY_ESCAPE) {
                application.exit()
            }
        }
    }
}

/**
 * A class to draw [ShapeContour] objects as a thick line. The color is 
 * linearly interpolated from [color0] to [color1] from line start to end.
 * The thickness of the line is defined by a function that takes a normalized
 * `t` Double value as input and outputs the desired width. Get a uniform width 
 * using something like `{ t -> 10.0 }`
 */
class ThickLine(
    private val pointCount: Int,
    private val color0: ColorRGBa,
    private val color1: ColorRGBa,
    val width: (Double) -> Double
) {
    val geometry = vertexBuffer(vertexFormat {
        position(3)
        color(4)
    }, pointCount * 2)

    fun draw(drawer: Drawer, thinLine: ShapeContour) {
        drawer.shadeStyle = shadeStyle {
            fragmentTransform = "x_fill = va_color;"
        }
        geometry.put {
            for (i in 0 until pointCount) {
                val pc = i / (pointCount - 1.0)
                val color = color0.mix(color1, pc).toVector4()
                val pos = thinLine.position(pc)
                val normal = thinLine.normal(pc).normalized * width(pc) / 2.0
                write((pos + normal).vector3(z = 0.0))
                write(color)
                write((pos - normal).vector3(z = 0.0))
                write(color)
            }
        }
        drawer.vertexBuffer(geometry, DrawPrimitive.TRIANGLE_STRIP)
    }
}
1 Like