Coordinate Library

Another in library series…

Based on discussion in Pentagonal icositetrahedron

Still under construction, posting early version for eyeballs and feedback.
only 2d so far, does R/A from x/y and also stores re-origin-ed x,y (so 0,0 is center)

// Coordinate Library v0.5 alpha  by ScruffyNerf
// caches the various coordinate systems, 
// so you can convert only once, and then have it precalc-ed on subsequent calls
// first up, 2d Cartesian (x,y) to Polar (r,a)

var coordinates = array(pixelCount)
var calc = true

var coordinatetypes = 4
var _r = 0
var _a = 1
var _x = 2
var _y = 3

// preset all arrays, makes testing easier... (if you don't cache and create arrays every time, memory runs out)
for (i = 0; i < pixelCount; i++) {
  coordinates[i] = array(coordinatetypes)
}

function coordinatecache(index,x,y){
  x = x - .5
  y = y - .5
  coordinates[index][_r] = sqrt(x*x + y*y)
  coordinates[index][_a] = atan2(y,x)
  coordinates[index][_x] = x
  coordinates[index][_y] = y
  if (index == pixelCount - 1) { // last pixel, we're all cached
    calc = false;   // if you comment out this line, you can compare cached speed to doing math every time.
  }
  return
}

export function beforeRender(delta) {
  t1 = wave(time(.15))
}

export function render2D(index,x,y) {
  if (calc) { coordinatecache(index,x,y)}
  r = coordinates[index][_r]
  a = coordinates[index][_a]
  if (r <= .4 && r >= .3 && (a >= (t1 * PI2) - PI) ){
      h = t1 + index/pixelCount
      hsv(h, 1, 1)
  }
  else {
    hsv(0,0,0)
  }
}

based on my quick test (commenting out the calc = false line)
I go from ~86FPS to 56FPS on a v3 with 256 pixels (16x16)
That’s a 50% difference in speed (30FPS more over 56FPS), just by no longer doing the math functions (Yes, there are some extra things in there, like setting array values, so it’s not all math, but it’s mostly the math, if I comment out all of the rest but the math, it goes to only 64FPS… that’s still more than 20FPS difference)

additional math/speed tweaks:
replace index/pixelCount with y and gain another FPS or 2.
replace sqrt calc with with hypot(x,y)… still only 57 FPS, so slightly faster.
I also removed assigning s and v, and even rearranged logic, to just always set hsv(0,0,0) first before the if(), vs in the else at the end, the else is just slightly faster I think.

Need to add 3d, and maybe other ‘one time’ calculations that are useful…
reference Spherical coordinate system - Wikipedia
I’ll definitely add cylindrical coords (r,a,z) because we have a lot of people with those (wrapped coils, and so on). This will also have a conversion the other way, so potentially you could map whatever was easiest and then convert.

1 Like

I’d prefer an API in the render that looked like this:

polarAngle(x, y, z)
polarDistance(x, y, z)
polarAzimuth(x, y, z)

The cache implementation are details a pattern shouldn’t need to worry about.

This would be much easier and efficient with proper hash table lookups, so you may need to pass in the index to “cheat” as a table with x,y,z lookups will perform terribly I suppose. And map data isn’t mutable so I guess it’s not that bad.

Having the full pixel map data available as a global would go a long way to simplifying this.

1 Like

I think my method can give you that format of function call (it’s really already an array lookup, but it can be functionalized), but using x,y,z is so cumbersome. We have index already. Using x,y,z is either 3 indexes (or requires some hash method) and makes the index potentially larger. But I’ll look into it. Can you give me an example of non pixel usage?

I agree, if the map was global,this would be a one time setup. But since the only place we have the data is render, that’s why I did it this way.

Hash seems awkward. Let’s say we limit x,y,z to 0…1. An array value is an integer, so 0-32767, but even then setting an array(32767) is a memory killer. But let’s say we could hash 3 0…1 values down to 16bit… That’s only 5 bits, which is just 31 values, or 1/32th increments. So I think hashing is out.

We could push multiple values into a bigger array, but again, pulling them out is likely not much of a savings in time. You’d eat memory and gain much less speed.

Any ideas on how to effectively cache function(x,y,z) is welcome.

I had a brainstorm on how to make all of this “self-caching”, so you could use functions and so long as the library was added, you’d be good without any code in your render functions. I need to flesh it out, but it’s just taking advantage of globals and having the index be the hash consistently.

Following up, this works, but it looks like reduces the framerate. I’m going to experiment more.

Followup2: with a one line check at top of render, I can maintain a good frame rate. Because the only place we have pixel info is in render, if I consolidate the check there, it’s only checking for a single “are we cached” variable once per pixel. If I move the check to per “function”, then it’s going to have to check multiple times per pixel, just to see if we’re cached. If we had pixel data in Beforerender, we could do the check for caching there, and do it once per frame instead

Given this… If it’s a scaling issue of once per frame, once per pixel, and once per function, and once per frame isn’t available (yet), I’m not sure moving the cache check out further makes sense. It’s down to one line, and if you are going to use functions available, like radius() or theta(), putting in a single line at the start of render seems minor.

I’m grinning widely, as I just figured out how to implement a framebuffer into this library, using a bit of a cheat. The problem with doing an index-ed array is that the x/y can be all over and unless you search thru the x array and the y array, you don’t know where the pixels are… , so you don’t have a smooth x+y*h type framebuffer to just paint on.

But if I build that x+y*h style buffer and just populate it with the index (so at framebuffer point x,y, pixel index #, which is at that spot, is returned), then I can use it either way. So you can do things like “draw a red rectangle here” and also do "color all of radius X’
and drawing in the framebuffer during beforeRender and then directly in the Render (and adding in the framebuffer data) both work.

it’s a bit outside of the coord library specs, but… it’s so useful.