Using program/drawer functions in classes outside program

Hey,

I’ve played around with various creative coding frameworks (Processing, openFrameworks, ++) and OpenRNDR really looks interesting!

Anyway, for some stuff I really like to program in an OOP style with the objects drawing themselves (I picked up this habit in The Nature of Code book. In both Processing and openFrameworks the drawing functions are always available as “global” functions (I know Processing does some trickery behind the scenes here), yet in OpenRNDR this seems to not be the case (I’m sure for good reasons).

I just curious about what would be “good” style for doing this kind of programming in OpenRNDR? My current approach is to just have my objects have a reference to the program object like so:

class Ball(val program: Program) {
    val radius = 25.0
    var position = Vector2(program.width/2.0, program.height/2.0)
    var velocity = Vector2.uniform(-5.0, 5.5)

    fun iterate() {
        with (program) {
            position += velocity
            if (position.x + radius > width || position.x - radius < 0) {
                velocity = velocity.reflect(Vector2.UNIT_X)
            }
            if (position.y + radius > height || position.y - radius < 0) {
                velocity = velocity.reflect(Vector2.UNIT_Y)
            }
            
            drawer.circle(position, radius)
        }
    }
}

fun main() = application {
    program {
        val balls = (1..15).map { Ball(this) }
        extend {
            for (b in balls) b.iterate()
        }
    }
}

Is this a reasonable way to structure these OOP oriented programs? Or is there a smarter/easier/more convenient way to do this that I don’t know of? I’ve never really programmed much of Kotlin so I’m not that familiar with the idioms.

Otherwise I want to say that OpenRNDR seems like a really cool creative coding framework. Seem to strike a good balance between convenience, simplicity and flexibility. I’ve tried many of the frameworks, but usually one or more of the following frustrate me: can’t get external libraries to work, very ”heavy” to get started on a simple idea, struggling with cross platform, frustrating IDE (lack of autocomplete!). So I’m looking forward to looking more into OpenRNDR.

I also want mention Aren Davey who did a really good job of explaining where OpenRNDR fits in the creative coding landscape in this video. That’s what inspired me to give it a go.

Welcome to the forum @torb!

You could write it like this:

 class Ball(width: Int, height: Int) {
    val radius = 25.0
    var position = Vector2(width/2.0, height/2.0)
    var velocity = Vector2.uniform(-5.0, 5.5)

    // it's a good practice to separate behavior from representation
    // Daniel Shiffman tends to show examples like this as well
    fun update(width: Int, height: Int) {
        position += velocity
        if (position.x + radius > width || position.x - radius < 0) {
            velocity = velocity.reflect(Vector2.UNIT_X)
        }
        if (position.y + radius > height || position.y - radius < 0) {
            velocity = velocity.reflect(Vector2.UNIT_Y)
        }
    }

    // this is a very common practice
    fun draw(drawer: Drawer) {
        drawer.circle(position, radius)
    }
}

fun main() = application {
    program {
        val balls = (1..15).map { Ball(width, height) }
        
        extend {
            ball.forEach {
                it.update(width, height)
                it.draw(drawer)
            }
        }
    }
}
1 Like

Thanks for answering and thanks for the welcome!

Is there a specific reason to not keep a reference to program in the object? Is it more efficient to pass stuff to the functions? Or is it merely a style preference?

Since you mention the update and draw functions. Does OpenRNDR have the possibility of separate update and draw functions (with delta values for the update function) built in?

I would say that whether or not you keep a reference to the program is mainly a style preference.
There are built-in delta values in program (seconds, deltaTime, frameCount), but you could also just pass those to the update function yourself.