Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding chapter "Advanced Topics/Custom Programs" #108

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Treide1
Copy link

@Treide1 Treide1 commented Jun 8, 2023

This PR adds the chapter 11_Advanced_Topics/C135_Custom_programs.kt
The chapter explains how to inherit from ProgramImplementation to write a custom program that adds to the scope of the normal program { ... } block.

This is useful for more experienced OPENRNDR users, that want to reuse code with minimal effort.

@hamoid
Copy link
Member

hamoid commented Jun 12, 2023

Thank you! I will review this today :-)

@edwinRNDR edwinRNDR requested a review from hamoid June 13, 2023 07:00
@hamoid
Copy link
Member

hamoid commented Jun 13, 2023

Finally I got into trying how this look like:

image

image

@hamoid
Copy link
Member

hamoid commented Jun 13, 2023

Thank you very much for your contribution! Super nice to have one more person writing!!!

I think I could tweak some sentences so they match the tone of other parts of the guide, but before getting into that... I was wondering if you are aware of ApplicationPreload?

I wonder if that is a simpler approach to achieve part of what you can do with your approach (which may be more flexible, but also a bit more verbose).

An example I use to add screenshots, video recording and ESC=quit to all sketches:

package org.openrndr

import org.openrndr.extensions.Screenshots
import org.openrndr.ffmpeg.ScreenRecorder

/**
 * Preload class to inject functionality into all sketches
 */

class Preload : ApplicationPreload() {
//    override fun onConfiguration(configuration: Configuration) {
//        configuration.width = 1200
//        configuration.height = 600
//    }

    override fun onProgramSetup(program: Program) {
        val screenRecorder = ScreenRecorder().apply {
            outputToVideo = false
            frameClock = false
        }
        program.extend(screenRecorder)
        program.extend(Screenshots())
        program.keyboard.keyDown.listen {
            when {
                it.key == KEY_ESCAPE -> program.application.exit()
                it.name == "v" && it.modifiers.isEmpty() -> {
                    screenRecorder.outputToVideo = !screenRecorder.outputToVideo
                    program.application.configuration.vsync =
                        !screenRecorder.outputToVideo
                    println("ScreenRecorder: ${if (screenRecorder.outputToVideo) "ON" else "OFF"}")
                }
            }
        }
    }
}

Do you think it's worth including both approaches in the guide?

@hamoid
Copy link
Member

hamoid commented Jun 13, 2023

In https://github.com/openrndr/openrndr-guide/wiki we had planned a section for reusing code. Maybe both approaches could be listed there, together with others like creating a library, or sharing a source folder across multiple projects. What do you think?

@Treide1
Copy link
Author

Treide1 commented Jun 14, 2023

Hi Abe, I was not aware of ApplicationPreload and couldn't figure out how to use one's custom subclass of that.

The guide page should be adjusted for sure :D
Feel free to change the wording and some of the weird formating.

@hamoid
Copy link
Member

hamoid commented Jun 14, 2023

Hi! One actually doesn't need to subclass, but just put that class (the example I shared) in a file, for example in src/main/kotlin/org/openrndr/Preload.kt. When a program starts it searches for ApplicationPreload instances and uses them if found. You can see it mentioned in the log in Idea when running any program.

If @edwinRNDR agrees, I think I could do this:

  1. merge this as it is.
  2. do a tiny bit of clean up / wording.
  3. next, move it into a "reusing code" section, where we can mention:
  • How to use ApplicationPreload (used by all sketches of one project)
  • How to extend Program (your example) (available to sketches of one project)
  • How to use https://github.com/edwinRNDR/openrndr-library-template (reuse code across projects)
  • How to include an external src/ folder (reuse code across projects)

@Treide1
Copy link
Author

Treide1 commented Jun 14, 2023

In https://github.com/openrndr/openrndr-guide/wiki we had planned a section for reusing code. Maybe both approaches could be listed there, together with others like creating a library, or sharing a source folder across multiple projects. What do you think?

I see the following situation:

  • People start OPENRNDR and write code they (eventually) use over and over
  • They find the page of the guide discussing code reuse
  • They move their common parts (declarations, executed code) into the reusable part
  • They probably want to change their existing code to now rely on the reusable
    • This should take little effort (in the case of myProgram a change of one outer function name and deleting moved code)
    • This should be side effect free to other projects

-> Future projects:

  • A decision is made which common part should be used: none (standard program), reusable (myProgram) or maybe even another reusable (myVideoBasedProgram, myShapeBasedProgram)

Depending on how experimental/stable the code is and if the intention is more for personal use/community use of the written code,
I would recommend either a ProgramImplementation or writing it as a library.

In my opinion, more options would be more confusing than helpful.

@hamoid
Copy link
Member

hamoid commented Jun 14, 2023

That's a good point. More options can be confusing.

It's tricky because each option has its benefits.

shared src folder vs library-template

I think including a src folder with shared code in a project is very easy to understand and implement by anyone. It may be better to use the library-template, but it has a drawback: to edit the code one needs to open a separate project, change the code, then publish to maven local (instead of just editing the code inside the open project). Benefits: is that it is easier to share with others via jitpack and build times are better. I'm ok with not showing how to use the shared src folder.

ApplicationPreload vs extending Program

Extending Program is more flexible, but the code can be confusing for people not experienced with Kotlin. Also, it requires more code than using ApplicationPreload. For example, to make all programs quit on ESC,

package org.openrndr

class Preload : ApplicationPreload() {
    override fun onProgramSetup(program: Program) {
        program.keyboard.keyDown.listen {
            if(it.key == KEY_ESCAPE) program.application.exit()
        }
    }
}

vs something like this:

class MyProgram(private val init: suspend MyProgram.() -> Unit) : ProgramImplementation(suspend = false) {
    override suspend fun setup() {
        super.setup()
        init()
        keyboard.keyDown.listen {
            if(it.key == KEY_ESCAPE) program.application.exit()
        }
    }
}
fun ApplicationBuilder.myProgram(
    init: suspend MyProgram.() -> Unit
): MyProgram {
    program = MyProgram(init)
    return program as MyProgram
}

It is very nice though that with the second approach you can easily choose if you want to use program or myProgram. But even after 3 years there are still parts of this code I'm not familiar with.

That's why I would go for showing both alternatives and suggest using ApplicationPreload until you are comfortable with the more advanced approach.

I'm concerned that by suggesting code that is harder to understand it may scare some people away, so it's nice to provide a simpler solution as well.

What do you think?

@Treide1
Copy link
Author

Treide1 commented Jun 14, 2023

I was not thinking for the people unfamiliar with Kotlin. As an Android Dev, I am maybe too used to it :D
Showing how to use Preload upfront is the better choice.

It also makes sense as this allows for reuse within one IntelliJ project, which is the logical unit of project/bundle of apps.
And also goes well with binding a src folder.

My only concern with the Preload class is that it enforces a org/openrndr/ path.
That feels a bad odd, as specifically this part of your project has the same package name as the library you are using ... but isn't that library 🤔

I would encourage creating a library only in the context of orx/orml - this way people are more keen on contributing back if they have something worthy of a lib

@hamoid
Copy link
Member

hamoid commented Jun 15, 2023

Thanks for the feedback :) I agree with your concern regarding the location of the Preload file. I guess one could place it anywhere, but it would complain about package not matching the location.

So I'll do as I mentioned above: merge, clean up, convert into a "reusing code" section and continue with the plan in the wiki.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants