monome / norns

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

Add support for flipping grid #1095

Closed simonvanderveldt closed 2 years ago

simonvanderveldt commented 4 years ago

When creating a grid interface that doesn't start from the top-left it would be nice to be able to have a way to flip the grid so one doesn't constantly have to manipulate the values returned by key presses to get the correct value. So instead of top-left returning 1,1 it would return 1,8 when flipping around the horizontal axes. This would remove a lot of noise from scripts.

There are probably a lot of examples, but one them can be found here https://github.com/tehn/awake/blob/076bf89c82c3d1db4b7722ac706237f0e916f056/awake.lua#L363

tehn commented 4 years ago

honestly i can't see this as a common use case. a huge majority of scripts are totally legible with x/y as-is (ie, like LCD screens). flipping the y axis makes sense in rare cases--- such as those you mention where vertical indicates pitch. put that's a very tiny subset and to accommodate this as a low-level function it'd add a bunch of extra logic for remapping.

my strong preference would be to have y-flip (or x-flip, etc) implemented per script with helper functions, which would be incredibly light weight comparatively. but feel free to continue this discussion for best practices and examples...

grid rotation, however, is implemented, though there is no UI and it must be added per-script as a param. #644

simonvanderveldt commented 4 years ago

Hmm, that's surprising, this seemed like no-brainer to me. Also surprised that it would be a small subset of scripts this would be useful for, scripts that count up from the bottom seem rather common? I might be missing something I guess.

In any case, IMHO this is something we should support out of the box, it's very basic functionality. The way it currently is feels very clumsy/clunky, not nice when writing a script that counts up from the bottom.

What would make the difference that this would be easy in Lua but would be more difficult/problematic when implemented in C? Wouldn't it do nothing when it's not enabled and only do something when enabled? And isn't this also supported by serialosc?

grid rotation, however, is implemented, though there is no UI and it must be added per-script as a param. #644

I looked into rotation, but funnily enough I have the same feeling about rotation as you have about flipping. It seems not really relevant to me, I guess it might be useful for scripts that run vertically? Which seems much more niche than flipping. I guess that illustrates the point that use cases will differ :) In any case, despite not really seeing the need for rotation just like flipping I think it should be supported out of the box to give everyone a nice and clean scripting experience.

P.S. I think rotation would make more sense as user option to be able to rotate the grid 180 degrees for positioning purposes than as a script option.

tehn commented 4 years ago

re: grid rotation. this is incredibly relevant, implemented immediately when the grid came out 15 years ago: often due to gear layout, you need the "cable" to come out of a different direction. in fact, rotation was originally called "cable" back in 2006, to indicate which way the cable was coming out. when the option is missing, people ask for it, continually.

re: flipping. it's really just a preference of standard screen layout coordinates (as is, ie, VGA) vs. cartesian (y flip, as you propose). this has never come up until today. i understand that for a small set of scripts this makes more intuitive sense... but most scripts don't have mapping preference (please point out more examples if you can think of them?)

here's how to make the flip very easily in lua with two lines:

grid.key = function(x,y,z) user_grid_key(x,8-y,z) end
user_grid_led = function(x,y,z) grid.led(x,8-y,z) end

and then just define the "user" functions as you normally would the default handler and function.

the reason this is better in the lua layer: it avoids complexity in the c layer, stays efficient without branching... or extra function pointers to variants. also there still are 16x16 grids out there so inverting y is conditional on grid size, so more complexity.

catfact commented 4 years ago

or even

local g = grid.connect()
local flip_x = function(n=16) 
  g:led = function(x,y,z)  _norns.grid_set_led(self.dev, n+1-x, y, val) end
  g:key = <etc>..
end
local flip_y = function(m=8) 
  g:led = function(x,y,z)  _norns.grid_set_led(self.dev, x, m+1-y, val) end
end
local flip_xy = function(m=16, n=8) 
  g:led = function(x,y,z)  _norns.grid_set_led(self.dev, n+1-x, m+1-y, val) end
end
local reset_flip = function()
  g:led = function(x,y,z)  _norns.grid_set_led(self.dev, x, y, val) end
end

then you don't have to change any calls to g:led in the script, and this could be easily split out into a library. (should be really, since it calls system functions.)

to make this a robust core library would be more work, b/c you would really want to automatically handle grid size and rotation. (i'd bake it into the Grid module i guess.)

anyway i agree that it's better done in the lua layer. whether it should be a high-level method for grid objects is just a question of expedience.

as for my opinion? i actually would rather see the index math written in the script directly: it's simple enough, and a modal behavior toggle for each axis would be confusing to me at least when reading other people's scripts. (but, not a big deal either.)

tehn commented 4 years ago

@catfact i totally forgot that the global state resets between scripts, so this is a smart way to hack in this sort of functionality without added structural complexity

@simonvanderveldt i'll put together a small PR that implements this for you

tehn commented 4 years ago

@simonvanderveldt apologies for my preemptive gusto--- i just spend a couple minutes looking at the core grid library and it's not as straightforward as i thought, and i have other priorities. if you want to spend time adding this functionality we can review it

or, if it's good enough, i believe ezra's solution above is very elegant for a per-script flip

tehn commented 2 years ago

now that we have the MODs system (which is designed for global specialized features) i'm going to close this