Skip to content

Instantly share code, notes, and snippets.

@stuaxo
Last active March 14, 2021 19:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stuaxo/fa38f23f7ac4d7ddee7b9bca8b5af349 to your computer and use it in GitHub Desktop.
Save stuaxo/fa38f23f7ac4d7ddee7b9bca8b5af349 to your computer and use it in GitHub Desktop.
Shoebot design notes.

Nodebox API notes:

nodeboxes graphics API has lots of small functions that are combined into classes such as BezierPath:

https://github.com/nodebox/nodebox-pyobjc/blob/master/nodebox/graphics/bezier.py

Runner class (in console.py):

In nodebox, the runner class manages the lifecycle of the bot. This means, handling FRAME, and PAGENUM (which are the same).

Functions call the runner in different ways, to run it normally, or do things like render to to movie files.

Reflections:

  • A runner class is a good idea, though not necassarily the same as this, we have an asyncio version in one of our jupyter experiments.
  • console.py is a confusing name for this.

Jupyter notebook experiments:

toybot-scenegraph

  • prototype architecture based on a scenegraph.
  • doesn't render anything, just outputs Paths made of Points.

Cairo - Canvas and intermediate output - 10.ipynb

  • Based on building starting with a runner like Nodebox, but async
  • Web API for updating source, which should be simpler than shoebots stdin / socket based API
  • Unplanned colour API, was experimenting with a scheme with flexible channels.

General:

  • use ADRs
  • Can the underneath part work as a library not a framework allow extensions to be imported as libraries too ?

Wishlist:

Logging

  • Use logging properly throughout
  • lifecycle (current frame), live reloading
  • ability to override initial state FRAME, size, stroke, fill etc (maybe this is theme support)

API needs:

Data / state / minimum API needs:

Questions:

  • Layers?
  • Background:
    • is background a layer ?

TransformData

  • ???? where something is.
  • should it also contain the matrix stack ???

ColorData:

  • min requirements for color ?
  • channels ? and values ?
  • what does gegl do ?

ColorableData:

  • fill ?
  • stroke ?
  • Should this be just colours or have something like cairos targets with patterns ?
  • name this better

FontData:

  • fontname
  • size

PathData:

  • closed flag
  • list of PathElements

PathElementData:

pathoperations: measure: - path_element_point_at(path_element: PathElementData) - path_length(path: PathData) - path_element_point_at(path_element: PathElementData) - path_point_at(path: PathData) transform: ... ? generates a new path with all or some paths transformed.
effect: (is a path effect a transformation) (think about how this would work). generates a new path that may have different points. boolean ops: (are these effects - quite probably)

Backend API needs equivilents to:

Text

  • text(...) Should there be a way of doing text spans? Maybe you can pass them to text(..)

Fonts:

  • fontnames
  • font

Path:

  • render_path(path: PathData, colorabledata: ColorableData)
  • render_text(???)
  • ??? render_image(???)

References - Nodebox Architecture Chat:

http://support.nodebox.net/discussions/nodebox-1/91-how-does-shoebot-1-decide-to-render-a-single-image-or-run-an-animation

Home → NodeBox 1 →
How does Shoebot 1 decide to render a single image or run an animation ?
Stu's Avatar
Stu

May 03, 2020 @ 12:35 PM


How does Nodebox 1 decide whether a bot is an animation or a still image.

In our Shoebot, I think we toggle on whether there is a "draw" function, but I've seen other info that it might be calling speed() with a number > 0 that enables it.

This should help me remove some of the less sane code in Shoebot :)

1 Posted by karstenwo on May 03, 2020 @ 01:37 PM
karstenwo's Avatar

In file console.py, in class NodeboxRunner method _check_animation() is self-explanatory.

See also usages of this method.

2 Posted by Stu on May 03, 2020 @ 06:27 PM
Stu's Avatar

Ah, great - I was thinking that having speed with a default of None might be a way of implementing this.

Reassuring to see I was thinking along similar lines.

This is definitely enough to write some unit tests for Shoebot so our implementation is compatible.

3 Posted by Stu on May 03, 2020 @ 06:32 PM
Stu's Avatar

While you're here: the canvas vs context distinction always confused me, is it that the graphics "context" is the current state, of graphics, while canvas is the thing it paints on ?

4 Posted by karstenwo on May 03, 2020 @ 07:31 PM
karstenwo's Avatar

Hey, I only keep it alive ;-) and write some new stuff; occasionally.

...graphics "context" is the current state

Yes. It's in nodebox/graphics/__init__.py and should be adaptable to Shoe with minimal changes

...while canvas is the thing it paints on...
Yes. And it has connections to the Cocoa framework

If you look in my fork here

https://github.com/karstenw/nodebox-pyobjc/blob/7dbbb1d3d1263d9a08faf22e11910bd5f9b4a817/nodebox/graphics/cocoa.py#L15

you'l see all the Cocoa functions that are used.

5 Posted by Stu on May 03, 2020 @ 09:31 PM
Stu's Avatar

Thanks.

I'll probably implement the same logic, but with tests, the code is pretty different + I'm working to gradually remove the weirder code (that I added about 7 years ago!).

BTW, for Shoebot I've started to add unit tests, including some assertions around image correctness, there stuff you can find useful

https://github.com/shoebot/shoebot/tree/master/tests/unittests

BTW, is _ctx when running a nodebox script, the same context ?

For the longest time, I've been working on this + not really understanding the nodebox codebase :) (I may be main dev, but I didn't start this project).

I feel like if I worker on it for long enough the codebase would end up looking like Nodeboxes exactly, but for now I'm happy to fix up the bugs and weirdness + maybe get it to the next release.

6 Posted by karstenwo on May 03, 2020 @ 10:30 PM
karstenwo's Avatar

_ctx is the connection to the canvas for ximported libs. It's the Context() that is created when a nodebox script is run.

class NodeBoxRunner(object):
    
    def __init__(self):
        ...
        self.canvas = graphics.Canvas()
        self.context = graphics.Context(self.canvas, self.namespace)
...

    def _initNamespace(self, frame=1):
        self.canvas.clear()
        self.namespace.clear()
        # Add everything from the namespace
        for name in graphics.__all__:
            self.namespace[name] = getattr(graphics, name)
        for name in util.__all__:
            self.namespace[name] = getattr(util, name)
        # Add everything from the context object
        self.namespace["_ctx"] = self.context
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment