How to draw batched lines with different style?

Hello, I’m trying to draw a bunch o lines, each with a different style. If I understand correctly, to draw straight lines in OPENRNDR, I should use the LineSegment class.

I read in the docs that for other primitive shapes I can use

  • static approach: drawer.<<primitive>>Batch { }
  • dynamic approach: drawer.<<primitive>>s { }

which gives me <<primitive>>BatchBuilder where I can set the style and create the primitive object using build functions.

However, there doesn’t seem to be any drawer.lineSegments() function gives me LineSegmentBatchBuilder. As far as I can see, all lineSegments overloads take a list of vectors or LineSegments. In fact, the LineSegmentBatchBuilder class does not seem to exist.

If I use drawer.lineSegment() to draw each line separately, the performance drops quite quickly for relatively small number of lines.

How can I draw batched lines each with a different style, please?

Hi @anoniim!

You are right, Drawing primitives batched | OPENRNDR GUIDE only seems to show circles, rectangles and points, not lines.

Maybe that could be added to the framework based on the other 3 examples…

Meanwhile, I found this program among my sketches.

import org.openrndr.application
import org.openrndr.draw.DrawPrimitive
import org.openrndr.draw.shadeStyle
import org.openrndr.draw.vertexBuffer
import org.openrndr.draw.vertexFormat
import org.openrndr.extra.noise.Random
import org.openrndr.extra.noise.simplex3D
import org.openrndr.extra.noise.uniform
import org.openrndr.extra.noise.withVector2Output

fun main() = application {
    configure {
        width = 800
        height = 800
    program {
        // create a buffer and specify it's format and size.
        val geometry = vertexBuffer(vertexFormat {
        }, 2 * 210000)

        // create an area with some padding around the edges
        val area = drawer.bounds.offsetEdges(-50.0)

        val n = simplex3D.withVector2Output()
        // populate the vertex buffer.
        geometry.put {
            for (i in 0 until geometry.vertexCount / 2) {
                val p = area.uniform()
                write(p.vector3(z = 0.0)) // start
                write(Random.vector4(0.0, 1.0)) // color

                    (p + n(5, p.x * 0.01, p.y * 0.01, 0.0) * 5.0)
                        .vector3(z = 0.0)
                ) // end
                write(Random.vector4(0.0, 1.0)) // color

        extend {
            // shader using the color attributes from our buffer
            drawer.shadeStyle = shadeStyle {
                fragmentTransform = "x_fill = va_color;"
            drawer.vertexBuffer(geometry, DrawPrimitive.LINES)

Maybe it helps you get started?

There’s also a simpler way:

import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extra.noise.Random
import org.openrndr.extra.noise.uniform
import org.openrndr.math.Vector4

fun main() = application {
    program {
        val count = 5000
        val segs = List(count) {
            drawer.bounds.uniform(50.0).vector3(z = 0.0)
        val thicks = List(count) {
            Random.double(1.0, 8.0)
        val colors = List(count) {
            ColorRGBa.fromVector(Vector4.uniform(0.0, 1.0))
        extend {
            drawer.lineSegments(segs, thicks, colors)

But I haven’t measured the performance, and I don’t know why the number of colors and thicknesses equals the number of vertices.

Maybe you can test and say how the performance compares?

Thanks @abe, you are very helpful as always!

The following lineSegments overload is sufficient for what I need. I should have really noticed it the first time I was looking at it!

fun lineSegments(segments: List<Vector3>, weights: List<Double>, colors: List<ColorRGBa>)

I played with it a bit (and checked the implementation of that function) and noticed a couple of things

  • The size of the vector list must be even. It crashes with ArrayIndexOutOfBounds otherwise
  • The odd items from weights and colors are ignored, only the even items are used for the corresponding line

There is no overload for Vector2, and it’s far less convenient than the BatchBuilder, but does the job! It also performs well even with large number of lines.

I’ll look into vertexBuffers when I hit the limits of this solution.


One more caveat for people who draw in 2D like me :slight_smile:

Since the only lineSegments() overload that allows setting colors takes Vector3, drawStyle.lineCap is not taken into account. Line caps are only applied when drawing lines in 2D.