Drawing text

OPENRNDR comes with support for rendering bitmap text, but for plotting purposes we are more interested in font contours.

Working with text contours

To load the vector data of a font file use the loadFace() method, then call the .glyphForCharacter() method to obtain a Shape representing a character.

../media/text-004.jpg

import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.font.loadFace
import org.openrndr.extra.composition.composition
import org.openrndr.extra.composition.drawComposition
import org.openrndr.extra.shapes.rectify.rectified
import org.openrndr.shape.LineSegment

fun main() = application {
    program {
        val face = loadFace("data/fonts/default.otf")
        val shape = face.glyphForCharacter(character = '8').shape(1.0)
        
        val myDesign = drawComposition {
            // Center the shape
            translate(drawer.bounds.center - shape.bounds.center)
            fill = null
            
            // Draw each contour found in the character '8' with a different color
            shape.contours.forEach {
                contour(it)
            }
        }
        
        extend {
            drawer.clear(ColorRGBa.WHITE)
            drawer.composition(myDesign)
        }
    }
}

This example visualizes normal vectors around the contour of the character ‘8’, evenly spaced every 10 pixels.

../media/text-005.jpg

import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.font.loadFace
import org.openrndr.extra.composition.composition
import org.openrndr.extra.composition.drawComposition
import org.openrndr.extra.shapes.rectify.rectified
import org.openrndr.shape.LineSegment

fun main() = application {
    program {
        val face = loadFace("data/fonts/default.otf")
        val shape = face.glyphForCharacter('8').shape(1.0)
        
        // Map each contour in the shape to a list of LineSegment,
        // then combine the resulting lists into one list by calling `.flatten()`.
        val normals = shape.contours.map { c ->
            // Work with rectified contours so `t` values are evenly spaced.
            val rc = c.rectified()
            val stepCount = (c.length / 10).toInt()
            List(stepCount) {
                val t = it / stepCount.toDouble()
                LineSegment(rc.position(t) + rc.normal(t) * 5.0, rc.position(t) + rc.normal(t) * 20.0)
            }
        }.flatten()
        
        val myDesign = drawComposition {
            translate(drawer.bounds.center - shape.bounds.center)
            lineSegments(normals)
        }
        extend {
            drawer.clear(ColorRGBa.WHITE)
            drawer.composition(myDesign)
        }
    }
}

edit on GitHub