monome / norns

norns is many sound instruments.
http://monome.org
GNU General Public License v3.0
633 stars 147 forks source link

screen abstractions #1278

Closed tyleretters closed 1 year ago

tyleretters commented 3 years ago

is there any utility in my contributing in a small screen abstraction suite?

presently writing text requires three lines of code (not counting setup with font_face, font_size, etc.):

screen.level(15)
screen.move(64, 32)
screen.text("hell world")

the abstraction would be something like:

draw.text(64, 32, "hello world", 15)

other abstractions would follow for rectangles, lines, etc.

reference:

tehn commented 3 years ago

i would consider, perhaps instead, overloading screen.text

ie

screen.text("hello") screen.text("hello",20,30) -- go to 20,30 first screen.text("hello",20,30,1) -- go to 20,30 and brightness 1)

what do you think?

i'm concerned about having two different screen/draw function tables, but if there are substantially more cases like this we can consider it

ngwese commented 3 years ago

Personally I think adding helper functions like this -if desired- should be part of a more broad identification of a goal or specific problem we are trying to solve. If the concern is about making things simpler or more accessible for people I would argue that making an API wider with multiple ways of solving the same problem does not really make it more approachable.

One particular problem I see with wrapping up several screen functions is that it is a leaky abstraction because it bundles together operations which have side effects which change the behavior of subsequent screen functions - in this case setting the brightness.

I would posit that adding functionality akin to a traditional graph where settings like origin, line width, brightness, etc. could be pushed and pop’ed off a stack as a set would help more.

tyleretters commented 3 years ago

I see with wrapping up several screen functions is that it is a leaky abstraction because it bundles together operations which have side effects

yeeeep. closing issue.

pq commented 3 years ago

Personally I think adding helper functions like this -if desired- should be part of a more broad identification of a goal or specific problem we are trying to solve.

+1

having said, i 100% agree that something should be done here.

looking, for example, at @tyleretters softclock experiments, https://github.com/tyleretters/softclock/blob/bde136a6056dfe47a48e310e9b056010a172e541/softclock.lua#L81-L86

  screen.level(15)
  screen.aa(0)
  screen.font_size(8)
  screen.font_face(0)
  screen.move(1, 8)
  screen.text(params:get("clock_tempo") .. " BPM")

it's a little hilarious how atomic the screen functions are and how many loc it takes to write some text.

as for side effects, i think they're pretty insidious even in the more atomic case and it'd be great to get straight on desired behavior. like @ngwese says a stack seems right here. it's just a question of where it's implemented and who's responsibility it is to manage it. i think that would fall out of a general conversation driven by use cases, etc.

tyleretters commented 3 years ago

another input: after i closed this i remembered that the new rotate functions do clean up after themselves:

https://github.com/monome/norns/blob/main/lua/core/screen.lua#L257


what does a "stack" mean in this context?


one solution is to make a default text display buffer thingymajig. you just feed it strings and it automatically populates the screen for you.

pq commented 3 years ago

what does a "stack" mean in this context?

i just mean something to support a notion of scope. in practice this would ensure that user and system screen state are distinct and don't interfere with each other.

ngwese commented 3 years ago

@pq thanks clarifying the stack comment, indeed it was in reference to push/pop operation for transform and ideally graphics state like line width, brightness, etc. i'd expect that pushing and poping the state would be a script author concern but also useful to the menu system and text/file select ui code.

to be clear i'm not against abstractions, i'm advocating that producing a good api (particularly when it comes to stuff like screen which consists of highly composable operations) comes out of having a clear goal/problem one it trying to address. in the absence of a clear goal i'd argue that less is more.

looking at the stuff i find myself reaching for (as an example, not necessarily a representative person). i think of more abstractions to draw shapes, say polygons (and a few other things in https://love2d.org/wiki/love.graphics). i often have to stop and remember whether i need to fill or stroke or both a rectangle to get the right result. i wonder if i have to close a shape before i can fill it... i often defensively set level, font, and font size because i can't remember if it was changed elsewhere... a quick way to draw parameters (outside of the menu) would be nice (label, value, unit) in a more compact form on screen to provide quick access to certain critical parameters. drawing the currently selected value from a list of values would be nice as well.

the underlying theme of the above is emulating aspects of other parts of the norns ui but with the ability to make some tweaks and place it where i'd like it. implementing this stuff is not necessarily hard but as soon at one want to allow placement anywhere on the screen or reuse in general one immediately has to contend with managing "graphics state" so that things draw correctly --- and then do the math to draw in screen space vs local space. ...this brings me back to being able to manage graphics state as a stack would help people build their own abstractions just a much as it would help us build abstractions for others.

tangentially there are already some [high level] abstractions in the code base. namely ui.lua and graph.lua - if those are not getting used much then it is worth asking the question as to why. are they too high level? are they too prescriptive visually? do people just not know about them because we don't do have easily accessible documentation/examples which go along with the abstractions?

tyleretters commented 3 years ago

i didn't know about ui.lua

awwaiid commented 2 years ago

Perhaps a bit silly, but we could have all the screen functions return screen and then it can be optionally written in a chained style.

screen
  .level(15)
  .aa(0)
  .font_size(8)
  .font_face(0)
  .move(1, 8)
  .text(params:get("clock_tempo") .. " BPM")