# How to draw batched lines with different style?

**URL:** <https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491>\
**Category:** How to?\
**Created:** [January 17, 2023, 10:28pm UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491 "2023-01-17T22:28:33Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![anoniim](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/anoniim/32/383_2.png) [@anoniim](https://openrndr.discourse.group/u/anoniim)\
**Post date:** [January 17, 2023, 10:28pm UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/1 "2023-01-17T22:28:33Z")

</div>

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](https://guide.openrndr.org/drawingBasics/drawingPrimitivesBatched.html) 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 `LineSegment`s. 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?

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [January 18, 2023, 9:15am UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/2 "2023-01-18T09:15:16Z")

</div>

Hi @anoniim!

You are right, [Drawing primitives batched | OPENRNDR GUIDE](https://guide.openrndr.org/drawingBasics/drawingPrimitivesBatched.html) 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.

```auto
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 {
            position(3)
            color(4)
        }, 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

                write(
                    (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?

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [January 18, 2023, 1:18pm UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/3 "2023-01-18T13:18:28Z")

</div>

There’s also a simpler way:

```auto
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?

---

<div class="post-metadata">

**Author:** ![anoniim](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/anoniim/32/383_2.png) [@anoniim](https://openrndr.discourse.group/u/anoniim)\
**Post date:** [January 19, 2023, 12:15am UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/4 "2023-01-19T00:15:32Z")

</div>

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!

```auto
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 `vertexBuffer`s when I hit the limits of this solution.

Thanks!

---

<div class="post-metadata">

**Author:** ![anoniim](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/anoniim/32/383_2.png) [@anoniim](https://openrndr.discourse.group/u/anoniim)\
**Post date:** [January 21, 2023, 6:43pm UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/5 "2023-01-21T18:43:10Z")

</div>

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

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.

---

<div class="post-metadata">

**Author:** ![anoniim](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/anoniim/32/383_2.png) [@anoniim](https://openrndr.discourse.group/u/anoniim)\
**Post date:** [February 20, 2023, 11:14pm UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/6 "2023-02-20T23:14:19Z")

</div>

I’ve hit another roadblock when I wanted to draw batched rectangles with different shade styles (gradient fill).

The `RectangleBatchBuilder` doesn’t support adding `shadeStyle` to individual rectangles (only seems to support the basic `stroke`, `strokeWeight` and `fill`).

There is `Drawer.rectangles(batch: RectangleBatch, count: Int)` where `RectangleBatch` constructor takes `geometry` and `drawStyle`

```auto
RectangleBatch(val geometry: VertexBuffer, val drawStyle: VertexBuffer)

```

but that is expecting the following format of `drawStyle`:

```auto
val drawStyleFormat = vertexFormat {
    attribute("fill", VertexElementType.VECTOR4_FLOAT32)
    attribute("stroke", VertexElementType.VECTOR4_FLOAT32)
    attribute("strokeWeight", VertexElementType.FLOAT32)
}

```

so I wouldn’t be able to set different shade style to individual rectangles that way either.

@abe, it seems like my only option is to use shaders as you suggested above. I recall you mentioning somewhere else on the forum that you might record a shader tutorial so I wonder whether that eventually happened? 😇

I looked at a few videos on YouTube and read a couple of articles so I have some basic idea how shaders work but would still appreciate any links to good tutorials or documentation to learn more.

I guess what I’ll have to do is to use `Drawer.vertexBuffer()` to draw my rectangles as custom geometry and set `shadeStyle` with `fragmentTransform` which creates the gradient.

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [February 21, 2023, 3:38pm UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/7 "2023-02-21T15:38:10Z")

</div>

Hi! @Alessandro and I have recorded now 11 videos but none yet about shaders. He actually suggested going into that topic 🙂

I guess if you use shaders you wouldn’t need multiple shadeStyles but one shader that produces different looks depending on the uniforms (or attributes) you pass to it.

How would you like the various gradients to differ? Are they all the same type (linear, radial)? Can the rectangles have various orientations? Is it animated? Trying to imagine how it looks like 🙂

Could you draw everything by layers? First everything that uses one shade style, then the next one, etc? (so you don’t need to change the style per rectangle).

---

<div class="post-metadata">

**Author:** ![dl33](https://avatars.discourse-cdn.com/v4/letter/d/dfb087/32.png) [@dl33](https://openrndr.discourse.group/u/dl33)\
**Post date:** [March 17, 2023, 12:39am UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/8 "2023-03-17T00:39:02Z")

</div>

Came here searching for the same thing, but for triangles. I looked into how `Drawer.rectangles {}` works, and apparently it uses the `DrawPrimitive.TRIANGLE` under the hood. But there is no similar wrapper for triangles! ☹

I think wrappers for all primitives would be quite helpful. Also, is there no easy way to implement at least a batched `shapes(shapes: List<Shape>, colors: List<ColorRGBa>)`?

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [March 17, 2023, 3:46pm UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/9 "2023-03-17T15:46:41Z")

</div>

Hi 🙂 Could you describe what you are trying to achieve?

What about using a [vertexBuffer](https://guide.openrndr.org/advancedDrawing/customRendering.html#vertex-buffers) to draw triangles?

About drawing shapes, is it the case that you are drawing many shapes with different colors and doing it one by one is not fast enough?

---

<div class="post-metadata">

**Author:** ![dl33](https://avatars.discourse-cdn.com/v4/letter/d/dfb087/32.png) [@dl33](https://openrndr.discourse.group/u/dl33)\
**Post date:** [March 17, 2023, 5:46pm UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/10 "2023-03-17T17:46:26Z")

</div>

Yep, exactly that. Vertex buffers seem like a too heavy-weight solution for such a simple task. Since there exists the `segments(segments, weights, colors)` wrapper, I’d very much prefer using a similar `shapes(...)` function. Or in the case of triangles, there could be a BatchBuilder like with circles or rectangles.

I guess for now I can write such a wrapper for triangles myself using the link you provided (thanks!).

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [March 18, 2023, 4:07pm UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/11 "2023-03-18T16:07:47Z")

</div>

I played with this approach to drawing triangles, but I’m not sure if it makes things faster. It provides a triangle drawer with a maximum number of triangles (to avoid resizing the buffer). Then one can draw a variable amount of triangles on every frame (up to the maximum). It allows to specify a color per vertex, although one could simplify it to specify a color per triangle.

# 3-Color triangle drawer

> **imports**
>
> ```auto
> import org.openrndr.WindowMultisample
> import org.openrndr.application
> import org.openrndr.draw.*
> import org.openrndr.extra.noise.Random
> import org.openrndr.extra.noise.uniform
> import org.openrndr.math.Vector2
> import org.openrndr.math.Vector4
> 
> ```

```auto
data class ColoredTriangle(
    val p0: Vector2, val p1: Vector2, val p2: Vector2,
    val c0: Vector4, val c1: Vector4, val c2: Vector4
)

```

```auto
class TriangleDrawer(private val drawer: Drawer, maxTriangles: Int) {
    private val geom = vertexBuffer(vertexFormat {
        position(3)
        color(4)
    }, 3 * maxTriangles)

    fun draw(tris: List<ColoredTriangle>) {
        geom.put {
            tris.forEach { tri ->
                write(tri.p0.x.toFloat(), tri.p0.y.toFloat(), 0f)
                write(tri.c0)
                write(tri.p1.x.toFloat(), tri.p1.y.toFloat(), 0f)
                write(tri.c1)
                write(tri.p2.x.toFloat(), tri.p2.y.toFloat(), 0f)
                write(tri.c2)
            }
        }
        val count = (tris.size * 3).coerceAtMost(geom.vertexCount)
        drawer.shadeStyle = shadeStyle {
            fragmentTransform = "x_fill = va_color;"
        }
        drawer.vertexBuffer(geom, DrawPrimitive.TRIANGLES, 0, count)
    }
}

```

```auto
fun main() {
    application {
        //configure { multisample = WindowMultisample.SampleCount(8) }
        program {
            val triangleDrawer = TriangleDrawer(drawer, 1000)
            val area = drawer.bounds.offsetEdges(-50.0)
            extend {
                val count = Random.int(1, 1000)
                triangleDrawer.draw(List(count) {
                    ColoredTriangle(
                        Vector2.uniform(area),
                        Vector2.uniform(area),
                        Vector2.uniform(area),
                        Vector4.uniform(0.0, 1.0),
                        Vector4.uniform(0.0, 1.0),
                        Vector4.uniform(0.0, 1.0)
                    )
                })
            }
        }
    }
}

```

The size of the triangles does affect the performance, so drawing a thousand overlapping translucent triangles is quite heavy. One can get a high frame rate by drawing smaller triangles with less overlap.

---

<div class="post-metadata">

**Author:** ![dl33](https://avatars.discourse-cdn.com/v4/letter/d/dfb087/32.png) [@dl33](https://openrndr.discourse.group/u/dl33)\
**Post date:** [March 18, 2023, 4:36pm UTC](https://openrndr.discourse.group/t/how-to-draw-batched-lines-with-different-style/491/12 "2023-03-18T16:36:38Z")

</div>

Just as I was writing about how I failed miserably, you come and save the day! 🙏

I am a total noob in low-level drawing, so all of my attempts to piece together the documentation, chatgpt advice and common sence were fruitless 🥲 But it turns out the only thing I needed is your `"x_fill = va_color;"` spell.

This code works perfectly fine for 10K triangles:

```kotlin
fun Drawer.triangles(triangles: List<Triangle>, colors: List<ColorRGBa>) = isolated {
    require(triangles.size == colors.size)
    val n = triangles.size
    val vb = vertexBuffer(vertexFormat {
        position(3)
        color(4)
    }, 3 * n)
    vb.put {
        for (i in 0 until n) {
            triangles[i].run {
                val c = colors[i]
                write(x1.xy0)
                write(c)
                write(x2.xy0)
                write(c)
                write(x3.xy0)
                write(c)
            }
        }
    }
    shadeStyle = shadeStyle {
        fragmentTransform = "x_fill = va_color;"
    }
    vertexBuffer(vb, DrawPrimitive.TRIANGLES)
    vb.destroy()
}

```

when being called like

```kotlin
extend {
    drawer.triangles(triangles, colors)
}

```

(probably worth noting that I’m changing the `colors` elements from `GlobalScope` coroutines)

Edit: “perfectly fine for 10K” non-intersecting trianlges. Still, perfectly fine for 1K intersecting semi-transparent triangles.

Edit 2: I see how `vertexBuffer{}` is called on every frame, but it doesn’t seem to cause performance issues. However, I just got an OutOfMemoryError, so I added `vb.destroy()` to prevent that.
