# Rotation functions?

**URL:** <https://forum.electromage.com/t/rotation-functions/1462>\
**Category:** Feature Ideas + Requests\
**Created:** [August 9, 2021, 3:19pm UTC](https://forum.electromage.com/t/rotation-functions/1462 "2021-08-09T15:19:08Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Scruffynerf](https://forum.electromage.com/letter_avatar_proxy/v4/letter/s/34f0e0/32.png) [@Scruffynerf](https://forum.electromage.com/u/Scruffynerf)\
**Post date:** [August 9, 2021, 3:19pm UTC](https://forum.electromage.com/t/rotation-functions/1462/1 "2021-08-09T15:19:09Z")

</div>

Discussion in the [Circle](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460) post lead to an issue with rotation. Given a formula like @zranger1 's that define a given shape based on shader style math, typically a rotation would be a separate function

@wizard, since you’ve put the map rotation functions into the firmware, is it possible to expose similar functions for a given (x,y[,z]) and given angle? So we could have pointRotateZ(x,y,angle) and get back an array of x and y?

We can do it in usercode but I suspect it’s the exact same math you’re doing already and faster.

ObInterestingMath: [Let's remove Quaternions from every 3D Engine (An Interactive Introduction to Rotors from Geometric Algebra) - Marc ten Bosch](https://marctenbosch.com/quaternions/)  
(Which removes quarterions entirely and just uses vectors to define rotation!)

---

<div class="post-metadata">

**Author:** ![wizard](https://forum.electromage.com/user_avatar/forum.electromage.com/wizard/32/4_2.png) [@wizard](https://forum.electromage.com/u/wizard)\
**Post date:** [August 9, 2021, 4:10pm UTC](https://forum.electromage.com/t/rotation-functions/1462/2 "2021-08-09T16:10:35Z")

</div>

I do plan on applying transforms with the map walking function. That means you could run through the map from multiple transforms.

But you are looking for coordinate transformation on arbitrary coordinates?

> <https://stackoverflow.com/questions/2259476/rotating-a-point-about-another-point-2d/2259502#2259502>

The math behind the pixel map coordinate transformations is a bit more involved and uses 4x4 transformation matrices. I found [this article](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Matrix_math_for_the_web) very helpful.

If you had to transform 3 points, I’d use the simple function.

---

<div class="post-metadata">

**Author:** ![Scruffynerf](https://forum.electromage.com/letter_avatar_proxy/v4/letter/s/34f0e0/32.png) [@Scruffynerf](https://forum.electromage.com/u/Scruffynerf)\
**Post date:** [August 9, 2021, 6:42pm UTC](https://forum.electromage.com/t/rotation-functions/1462/3 "2021-08-09T18:42:44Z")

</div>

Agreed:

rotate point `(px, py)` around point `(ox, oy)` by angle theta:

`p'x = cos(theta) * (px-ox) - sin(theta) * (py-oy) + ox`

`p'y = sin(theta) * (px-ox) + cos(theta) * (py-oy) + oy`

That will work for our purposes, just calculate the rotated x+y values before using zranger’s functions should work, I think. And for a given rotation of a shape, you could precalculate the sine/cosine just once per shape. So pretty quick.

---

<div class="post-metadata">

**Author:** ![Scruffynerf](https://forum.electromage.com/letter_avatar_proxy/v4/letter/s/34f0e0/32.png) [@Scruffynerf](https://forum.electromage.com/u/Scruffynerf)\
**Post date:** [August 10, 2021, 12:27pm UTC](https://forum.electromage.com/t/rotation-functions/1462/4 "2021-08-10T12:27:09Z")

</div>

> [@Scruffynerf](#):
>
> `p'x = cos(theta) * (px-ox) - sin(theta) * (py-oy) + ox`  
> `p'y = sin(theta) * (px-ox) + cos(theta) * (py-oy) + oy`

turns out to be even simpler for your code, @zranger1

```
shapetheta = theta + objects[i][6] // shape's angle, so each can be different
px = cos(shapetheta) * (x-objects[i][0]) - sin(shapetheta) * (y-objects[i][1])
py = sin(shapetheta) * (x-objects[i][0]) + cos(shapetheta) * (y-objects[i][1])
d = shapeSdf[objects[i][5]](px,py,objectSize);

```

You were already doing most of the origin relative math (e.g. x-objects[i][0]), so very easy to add.

> **Shape code, with shape rotation added**
>
> ```
> // adds per shape rotation 
> 
> // Global Variables
> var maxObjects = 4;
> var numObjects = 4;
> export var objectSize = 0.21;
> export var speed = 0.18;
> var numShapes = 4;
> var shapeSdf = array(numShapes)
> var shapeCompare = array(2);
> var filled = 1;
> var lineWidth = 0.04;
> 
> var theta;
> 
> shapeCompare[0] = (f) => (abs(f) > lineWidth); // unfilled shapes
> shapeCompare[1] = (f) => (f > lineWidth); // filled shapes
> 
> shapeSdf[0] = circle;
> shapeSdf[1] = square;
> shapeSdf[2] = triangle;
> shapeSdf[3] = hexagon;
> 
> // signed distance functions for various shapes, adapted for 2D. 
> // Math from https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
> function circle(x,y,r) {
> return hypot(x,y) - r;
> }
> 
> function square(x,y,size) {
> dx = abs(x) - size; d1 = max(dx,0);
> dy = abs(y) - size; d2 = max(dy,0);
> return min(max(dx, dy), 0.0) + hypot(d1,d2);
> }
> 
> function triangle(x,y,r) {
> return max((abs(x) * 0.866025) - (y * 0.5), y) - r / 2;
> }
> 
> function hexagon(x,y,r){
> x = abs(x); y = abs(y);
> return max((x * 0.5 + y * 0.866025),x) - r;
> }
> 
> // array of object vectors
> var objects = array(maxObjects);
> 
> // UI
> export function sliderSize(v) {
> objectSize = 0.4 * v;
> }
> 
> export function sliderSpeed(v) {
> speed = v;
> }
> 
> export function sliderFilled(v) {
> filled = (v >= 0.5);
> }
> 
> // allocate memory for object vectors
> function createObjects() {
> for (var i = 0; i < maxObjects; i++) {  
> objects[i] = array(8);
> }
> }
> 
> // create object vector with a random position, direction, speed, color
> function initObjects() {
> var hue = random(1);
> for (var i = 0; i < numObjects; i++) {
> var b = objects[i];  
>     
> b[0] = random(1); // x pos
> b[1] = random(1); // y pos
> 
> b[2] = random(0.2); // x velocity
> b[3] = random(0.2); // y velocity
> 
> b[4] = hue; // color
> b[5] = i % numShapes; // shape
> b[6] = random(1)*PI2 // rotation angle
> hue += 0.619033
> }
> }
> 
> // move objects and bounce them off "walls"
> function bounce() {
> for (var i = 0; i < numObjects; i++) {
> var b = objects[i];
>     
> // move object
> b[0] += b[2] * speed;
> b[1] += b[3] * speed;
> 
> // bounce off walls by flipping vector element sign when we hit.
> if (b[0] < 0) { b[0] = 0; b[2] = -b[2]; continue; } 
> if (b[1] < 0) { b[1] = 0; b[3] = -b[3]; continue; }
> 
> if (b[0] > 1) { b[0] = 1; b[2] = -b[2]; continue; }
> if (b[1] > 1) { b[1] = 1; b[3] = -b[3]; continue; }
> }
> }
> 
> createObjects();
> initObjects();
> 
> export function beforeRender(delta) {
> bounce();
> theta = PI2 * time(0.1);
> 
> // uncomment the block below to rotate entire scene around its
> // center
> /*
> 
> resetTransform();
> translate(-0.5,-0.5);  
> rotate(theta);  
> translate(0.5,0.5);
> */  
> }
> 
> export function render2D(index,x,y) {
> var d;
> var v = 0;
> 
> for (var i = 0; i < numObjects; i++) {
> shapetheta = theta + objects[i][6]
> px = cos(shapetheta) * (x-objects[i][0]) - sin(shapetheta) * (y-objects[i][1])
> py = sin(shapetheta) * (x-objects[i][0]) + cos(shapetheta) * (y-objects[i][1])
> d = shapeSdf[objects[i][5]](px,py,objectSize);
> if (shapeCompare[filled](d)) continue;
>     
> v = 1-(d/0.04);
> s = 1.5-abs(d)/objectSize
> h = objects[i][4]-d;      
> break;
> }
>      
> hsv(h, s, v*v*v)
> }
> 
> ```

**added** I see your concern about speed vs number of objects

I tweaked the above to cache the sin/cos in the shape pre render, and then just pull it once.

With 8 squares, depending on size/fill, it goes as slow as 12fps or so… probably more way to optimize it, but not bad at all.

Added:  
`if (abs(sx) > objectSize * 1.1 || abs(sy) > objectSize * 1.1) continue;`  
more than doubled the framerate by avoiding the check if the point is well outside of the size, which cuts down on scanning every pixel for each shape, even if the shape is well away from the pixel, so there is no way it’s close enough. 25-28fps (varies based on size/filled/etc)

> **8 squares rotating for FPS purposes, some caching done**
>
> ```
> // adds per shape rotation 
> 
> // Global Variables
> var maxObjects = 8;
> var numObjects = 8;
> export var objectSize = 0.21;
> export var speed = 0.18;
> var numShapes = 4;
> var shapeSdf = array(numShapes)
> var shapeCompare = array(2);
> var filled = 1;
> var lineWidth = 0.04;
> 
> var theta;
> 
> shapeCompare[0] = (f) => (abs(f) > lineWidth); // unfilled shapes
> shapeCompare[1] = (f) => (f > lineWidth); // filled shapes
> 
> shapeSdf[0] = circle;
> shapeSdf[1] = square;
> shapeSdf[2] = triangle;
> shapeSdf[3] = hexagon;
> 
> // signed distance functions for various shapes, adapted for 2D. 
> // Math from https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
> function circle(x,y,r) {
> return hypot(x,y) - r;
> }
> 
> function square(x,y,size) {
> dx = abs(x) - size; d1 = max(dx,0);
> dy = abs(y) - size; d2 = max(dy,0);
> return min(max(dx, dy), 0.0) + hypot(d1,d2);
> }
> 
> function triangle(x,y,r) {
> return max((abs(x) * 0.866025) - (y * 0.5), y) - r / 2;
> }
> 
> function hexagon(x,y,r){
> x = abs(x); y = abs(y);
> return max((x * 0.5 + y * 0.866025),x) - r;
> }
> 
> // array of object vectors
> var objects = array(maxObjects);
> 
> // UI
> export function sliderSize(v) {
> objectSize = 0.4 * v;
> }
> 
> export function sliderSpeed(v) {
> speed = v;
> }
> 
> export function sliderFilled(v) {
> filled = (v >= 0.5);
> }
> 
> // allocate memory for object vectors
> function createObjects() {
> for (var i = 0; i < maxObjects; i++) {  
> objects[i] = array(9);
> }
> }
> 
> // create object vector with a random position, direction, speed, color
> function initObjects() {
> var hue = random(1);
> for (var i = 0; i < numObjects; i++) {
> var b = objects[i];  
>     
> b[0] = random(1); // x pos
> b[1] = random(1); // y pos
> 
> b[2] = random(0.2); // x velocity
> b[3] = random(0.2); // y velocity
> 
> b[4] = hue; // color
> b[5] = 1 // shape
> b[6] = random(1)*PI2 // rotation
> b[7] = sin(b[6])
> b[8] = cos(b[6])
> hue += 0.619033
> }
> }
> 
> // move objects and bounce them off "walls"
> function bounce() {
> for (var i = 0; i < numObjects; i++) {
> var b = objects[i];
>     
> // move object
> b[0] += b[2] * speed;
> b[1] += b[3] * speed;
> 
> // bounce off walls by flipping vector element sign when we hit.
> if (b[0] < 0) { b[0] = 0; b[2] = -b[2]; continue; } 
> if (b[1] < 0) { b[1] = 0; b[3] = -b[3]; continue; }
> 
> if (b[0] > 1) { b[0] = 1; b[2] = -b[2]; continue; }
> if (b[1] > 1) { b[1] = 1; b[3] = -b[3]; continue; }
> }
> }
> 
> createObjects();
> initObjects();
> 
> export function beforeRender(delta) {
> bounce();
> theta = PI2 * time(0.1);
> for (var i = 0; i < numObjects; i++) {
> shapetheta = theta + objects[i][6]
> objects[i][7] = sin(shapetheta)
> objects[i][8] = cos(shapetheta)
> }
> // uncomment the block below to rotate entire scene around its
> // center
> /*
> 
> resetTransform();
> translate(-0.5,-0.5);  
> rotate(theta);  
> translate(0.5,0.5);
> */  
> }
> 
> export function render2D(index,x,y) {
> var d;
> var v = 0;
> for (var i = 0; i < numObjects; i++) {
> sx = x-objects[i][0]
> sy = y-objects[i][1]
> if (abs(sx) > objectSize * 1.1 || abs(sy) > objectSize * 1.1) continue;
> sinshape = objects[i][7]
> cosshape = objects[i][8]
> px = cosshape * sx - sinshape * sy
> py = sinshape * sx + cosshape * sy
> d = shapeSdf[objects[i][5]](px,py,objectSize);
> if (shapeCompare[filled](d)) continue;
>     
> v = 1-(d/0.04);
> s = 1.5-abs(d)/objectSize
> h = objects[i][4]-d;      
> break;
> }
>      
> hsv(h, s, v*v*v)
> }
> 
> ```

Follow up, cause my GF came home and immediately proclaimed the running pattern was ‘80s kid show’, so I guess I’d better share the code, and add it to the pattern collectionn.

> **80s kid show**
>
> ```
> // 80s kid show, by Scruffynerf, with huge chunks of code by @zranger
> // v1.0ish, but certainly hackable for more fun. 8-2021
> 
> // Global Variables
> var maxObjects = 16;
> var objectProperties = 12
> export var numObjects = 10;
> export var objectSize = 0.21;
> export var speed = 0.18;
> export var filled = 1;
> export var lineWidth = 0.04;
> export var bounds = 1;
> export var spin = 1;
> export var flat = 1;
> export var whichShapes = 2;
> var old = 2
> var theta;
> var numShapes = 5;
> var shapeSdf = array(numShapes)
> var shapeCompare = array(2);
> shapeCompare[0] = (f) => (abs(f) > lineWidth); // unfilled shapes
> shapeCompare[1] = (f) => (f > lineWidth); // filled shapes
> 
> shapeSdf[0] = circle;
> shapeSdf[1] = square;
> shapeSdf[2] = triangle;
> shapeSdf[3] = hexagon;
> shapeSdf[4] = hexstar;
> 
> // signed distance functions for various shapes, adapted for 2D. 
> // Math from https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
> function circle(x,y,r) {
> return hypot(x,y) - r;
> }
> 
> function square(x,y,size) {
> dx = abs(x) - size; d1 = max(dx,0);
> dy = abs(y) - size; d2 = max(dy,0);
> return min(max(dx, dy), 0.0) + hypot(d1,d2);
> }
> 
> function triangle(x,y,r) {
> return (max((abs(x) * 0.866025) - (y * 0.5), y) - r / 2);
> }
> 
> function hexagon(x,y,r){
> x = abs(x); y = abs(y);
> return max((x * 0.5 + y * 0.866025),x) - r;
> }
> 
> function hexstar(x,y,r) {
> x = abs(x); y = abs(y); 
> // the vector of scary constants is just a grab bag of precalculated values.
> // 1.73205 is the square root of 3. 0.866025 is half that
> // these constants are used in a lot of hexagonal sdf things
> // because on a hex with flat to flat radius of 0.5, the
> // distance between the points is 0.5/sqrt(3)
> dot = 2 * min(-0.5*x + 0.866025 * y,0);
> x -= dot * -0.5; y -= dot * 0.866025;
>   
> dot = 2 * min(0.866025*x + -0.5 * y,0);
> x -= dot * 0.866025; y -= dot * -0.5;
>   
> x -= clamp(x, r * 0.5773502692, r * 1.7320508076);
> y -= r;
> result = hypot(x,y);
> // TODO - could be more efficient here. I know I've got a fast bit masking
> // signum function around somewhere.
> return (y > 0) ? result : -result;
> }
> 
> // array of object vectors
> var objects = array(maxObjects);
> 
> // UI
> export function sliderFloaters(v) {
> numObjects = floor(maxObjects * v);
> }
> 
> export function sliderType(v) {
> old = whichShapes;
> whichShapes = floor((numShapes) * v);
> if (old != whichShapes){ initObjects(); }
> }
> export function sliderSize(v) {
> objectSize = 0.4 * v;
> }
> 
> export function sliderSpeed(v) {
> speed = v;
> }
> 
> export function sliderFilled(v) {
> filled = (v >= 0.5);
> }
> 
> export function sliderLine(v) {
> lineWidth = 0.04 + 0.05* v
> }
> 
> export function sliderCutoff(v) {
> bounds = 1 + v
> }
> 
> export function sliderSpin(v) {
> spin = (v >= 0.5);
> }
> 
> export function sliderFlat(v) {
> flat = (v <= 0.5);
> }
> 
> // allocate memory for object vectors
> function createObjects() {
> for (var i = 0; i < maxObjects; i++) {  
> objects[i] = array(objectProperties);
> }
> }
> 
> // create object vector with a random position, direction, speed, color, etc
> function initObjects() {
> var hue = random(1);
> for (var i = 0; i < maxObjects; i++) {
> var b = objects[i];  
>     
> b[0] = random(1); // x pos
> b[1] = random(1); // y pos
> 
> b[2] = random(0.2); // x velocity
> b[3] = random(0.2); // y velocity
> 
> b[4] = hue; // color
>     
> if (whichShapes == numShapes){
> b[5] = floor(random(numShapes)) // random shape mix
> } else {
> b[5] = whichShapes // specific shape
> }
> 
> b[6] = random(1)*PI2 // rotation
> b[7] = sin(b[6])
> b[8] = cos(b[6])
> 
> b[9] = random(2)+1 // object variation in size
> b[10] = random(1) // timing of growth cycle
> b[11] = 1 // current size caching so we save the final size per render
> hue += 0.4
> }
> }
> 
> // move objects and bounce them off "walls"
> function bounce() {
> for (var i = 0; i < numObjects; i++) {
> var b = objects[i];
>     
> // move object
> b[0] += b[2] * speed;
> b[1] += b[3] * speed;
> 
> // bounce off walls by flipping vector element sign when we hit.
> if (b[0] < 0) { b[0] = 0; b[2] = -b[2]; continue; } 
> if (b[1] < 0) { b[1] = 0; b[3] = -b[3]; continue; }
> if (b[0] > 1) { b[0] = 1; b[2] = -b[2]; continue; }
> if (b[1] > 1) { b[1] = 1; b[3] = -b[3]; continue; }
> }
> }
> 
> createObjects();
> initObjects();
> 
> function swaptwo(){
> s1 = floor(random(numObjects))
> s2 = floor(random(numObjects))
> for (i = 0; i < objectProperties; i++){
> swap = objects[s1][i]
> objects[s1][i] = objects[s2][i]
> objects[s2][i] = swap
> }
> }
> 
> export function beforeRender(delta) {
> bounce();
> theta = PI2 * time(.1);
> for (var i = 0; i < numObjects; i++) {
> shapetheta = theta + objects[i][6]
> objects[i][7] = sin(shapetheta)
> objects[i][8] = cos(shapetheta)
> objects[i][11] = objects[i][9] * objectSize * (.5+wave(time(objects[i][10])))
> }
> 
> // swap 2 floaters randomly once in a random while, so the same one isn't always on top 
> if (random(1) > .98){
> swaptwo();
> }
>   
> if (spin){
> resetTransform();
> translate(-0.5,-0.5);  
> rotate(theta);  
> translate(0.5,0.5);
> }
> }
> 
> export function render2D(index,x,y) {
> var d,h,s,v;
> for (var i = 0; i < numObjects; i++) {
> shapesize = objects[i][11]
> sx = x-objects[i][0]
> sy = y-objects[i][1]
> if (abs(sx) > shapesize * bounds || abs(sy) > shapesize * bounds ) continue;
> sinshape = objects[i][7]
> cosshape = objects[i][8]
> px = cosshape * sx - sinshape * sy
> py = sinshape * sx + cosshape * sy
> d = shapeSdf[objects[i][5]](px,py,shapesize);
> if (shapeCompare[filled](d)) continue;
>     
> if (flat){ 
> v = 1; 
> s = 1;
> } else {
> v = 1-(1.3*d/lineWidth)
> s = 1.5-abs(d)/shapesize*bounds
> }
> h = objects[i][4];      
> break;
> }
> hsv(h, s, v*v)
> }
> 
> ```

Some notes before I forget:  
The Shape slider has a trick to cause a reinit if you slide it. Save the old value before you set the new, and if it’s different after, then the slider was moved, so reinitialize the shapes.

Lots of sliders that are really switches, but no UI yet so it’s a bit messy.  
Only 5 shape types so far, more would be nice, maxing the slider makes a random mix instead of one type.  
The cutoff adjusts how far the shape looks out for points to include, so if you adjust it, you can make more lumpy shapes (blobs)  
More floaters, and less cutoff, is slower but hopefully not too slow  
Linewidth is adjustable for some thicker lines.  
Flat is more “basic” h,1,1 coloring, while non-flat is more edgy/fill looking. The colors tend to group (adding .4 so it’ll roll every 5 shapes) which I liked the look of, and the color scheme tends to be a nice contrasty mix that way, even though it’s picking a random starting color.

I could add more and more to this… it’s got so much we can do.

---

<div class="post-metadata">

**Author:** ![zranger1](https://forum.electromage.com/user_avatar/forum.electromage.com/zranger1/32/301_2.png) [@zranger1](https://forum.electromage.com/u/zranger1)\
**Post date:** [August 11, 2021, 3:03am UTC](https://forum.electromage.com/t/rotation-functions/1462/5 "2021-08-11T03:03:54Z")

</div>

Nice, @scruffynerf! You caught the good optimizations – the early out when pixels are completely outside an object’s bounding box is excellent! I like the 80s TV look too!

Here’s the final “good” Hexstar function, along with the relatively fast signum function I finally managed to find - swap it in if you like.

One of the oddities of the ported shader functions, including the n-gon one, is that they don’t produce shapes of even remotely equal size. ( I don’t think anybody in the shaderverse ever tested quite this way. 🙂 )

Sometimes the size parameter means the distance to a flat side, sometimes to a point. I’ve tried to standardize a little as I go. This version produces a hex star the same size as everything else…

```auto
function signum(a) {
  return (a > 0) - (a < 0)
}

function hexStar(x,y,r) {
  // rescale to pointy parts of star
  x = abs(x*1.73205); y = abs(y*1.73205); 
  dot = 2 * min(-0.5*x + 0.866025 * y,0);
  x -= dot * -0.5; y -= dot * 0.866025;
  
  dot = 2 * min(0.866025*x + -0.5 * y,0);
  x -= dot * 0.866025; y -= dot * -0.5;
  
  x -= clamp(x, r * 0.57735, r * 1.73205);
  y -= r;
  return signum(y) * hypot(x,y) / 1.73205;
}

```

---

<div class="post-metadata">

**Author:** ![zranger1](https://forum.electromage.com/user_avatar/forum.electromage.com/zranger1/32/301_2.png) [@zranger1](https://forum.electromage.com/u/zranger1)\
**Post date:** [August 16, 2021, 12:25am UTC](https://forum.electromage.com/t/rotation-functions/1462/6 "2021-08-16T00:25:54Z")

</div>

And here’s a port of iq’s n-sided regular polygon signed distance function. Original GLSL source is below. Have fun drawing! I can sense a Github page collecting these things in the near future…

```auto
function signum(a) {
  return (a > 0) - (a < 0)
}

// draw regular polygon of height 'r' with 'sides' sides.
function nSidedPolygon(x,y,r,sides) {
  var x1,y1,bn,he;  
  var an = PI2/sides;
  
// size algorithm needs a little adjustment for triangles. I 
// commented this out, since there's already a faster stand-alone
// triangle function. Just uncomment if you want it!  
// r -= (sides == 3) * r / 3;
  he = r * tan(0.5*an);

// swap x and y coordinates and flip signs as needed 
// (if you want to reverse the y orientation, use 'x = -y')  
  tmp = -x; x = y; y = tmp;
  
// calculate angle needed to rotate to first sector  
  var bn = an * floor((atan2(y,x) + 0.5*an)/an);
  
// when porting GLSL matrix operations remember that
// matrices are initialized in column major order. This
// has given me more than one headache since directX
// does it the other way...
  c = cos(bn); s = sin(bn);
  x1 = (c * x) + (s * y);
  y1 = (c * y) - (s * x);
  
  return hypot(x1 - r, y1 - clamp(y1,-he,he)) * signum(x1 - r)
}

```

// iq’s GLSL code, from [Shader - Shadertoy BETA](https://www.shadertoy.com/view/MtKcWW)

```auto
#define N 7

// signed distance to a regular n-gon
float sdNGon( in vec2 p, in float r )
{
    // these 2 lines can be precomputed
    float an = 6.2831853/float(N);
    float he = r*tan(0.5*an);
    
    // rotate to first sector
    p = -p.yx; // if you want the corner to be up
    float bn = an*floor((atan(p.y,p.x)+0.5*an)/an);    
    vec2 cs = vec2(cos(bn),sin(bn));
    p = mat2(cs.x,-cs.y,cs.y,cs.x)*p;

    // side of polygon
    return length(p-vec2(r,clamp(p.y,-he,he)))*sign(p.x-r);
}

```

---

<div class="post-metadata">

**Author:** ![Scruffynerf](https://forum.electromage.com/letter_avatar_proxy/v4/letter/s/34f0e0/32.png) [@Scruffynerf](https://forum.electromage.com/u/Scruffynerf)\
**Post date:** [August 18, 2021, 12:45am UTC](https://forum.electromage.com/t/rotation-functions/1462/7 "2021-08-18T00:45:29Z")

</div>

The [version of n-poly](https://www.shadertoy.com/view/3tSGDy) that also does stars is pretty clean too. Easy enough to make that one do double duty (ie star or normal poly)

I admit now I want all sorts of other shapes.  
Hearts, Spades, Clubs, Diamonds, etc etc.

(Not that I expect you to do all the porting, of course… )

---

<div class="post-metadata">

**Author:** ![zranger1](https://forum.electromage.com/user_avatar/forum.electromage.com/zranger1/32/301_2.png) [@zranger1](https://forum.electromage.com/u/zranger1)\
**Post date:** [August 18, 2021, 3:04am UTC](https://forum.electromage.com/t/rotation-functions/1462/8 "2021-08-18T03:04:07Z")

</div>

Tomorrow, I’ll start a github repository that has a markdown page where we can keep these things and I’ll port more of 'em as I can. Work is unfortunately interfering with my recreational programming this week, as it sometimes does.

---

<div class="post-metadata">

**Author:** ![Scruffynerf](https://forum.electromage.com/letter_avatar_proxy/v4/letter/s/34f0e0/32.png) [@Scruffynerf](https://forum.electromage.com/u/Scruffynerf)\
**Post date:** [August 18, 2021, 1:29pm UTC](https://forum.electromage.com/t/rotation-functions/1462/9 "2021-08-18T13:29:41Z")

</div>

Sounds good. If we port all of the IQ examples, and the ones in PixelSpirit, that’s the majority of SDFs I’ve found. Absolutely, the building blocks of making whatever shapes we want, scalable, and usable both in render() as live math on the fly and usable for buffered creation too.

I’m imagining a random image creator similar to the way the Deck builds up, but making random combos… Lots of options like symmetry, rings, and more. But let’s get a good SDF collection first.

Added:

Imagine an led display like this:

> <https://twitter.com/MattVerkuijlen/status/1366036329847533568>

---

<div class="post-metadata">

**Author:** ![zranger1](https://forum.electromage.com/user_avatar/forum.electromage.com/zranger1/32/301_2.png) [@zranger1](https://forum.electromage.com/u/zranger1)\
**Post date:** [August 18, 2021, 11:04pm UTC](https://forum.electromage.com/t/rotation-functions/1462/10 "2021-08-18T23:04:07Z")

</div>

Link to SDF reference below:

It’s dead simple right now, but it’s a place to start – Send me code here or via pr on github, and we can grow it pretty quickly. I’ll make a separate thread for it when we’re a bit farther along.

[2D SDF Reference](https://github.com/zranger1/PixelblazePatterns/blob/master/Toolkit/sdf2d.md)  
[Toolkit Main Page](https://github.com/zranger1/PixelblazePatterns/tree/master/Toolkit)

---

<div class="post-metadata">

**Author:** ![Scruffynerf](https://forum.electromage.com/letter_avatar_proxy/v4/letter/s/34f0e0/32.png) [@Scruffynerf](https://forum.electromage.com/u/Scruffynerf)\
**Post date:** [August 19, 2021, 1:44am UTC](https://forum.electromage.com/t/rotation-functions/1462/11 "2021-08-19T01:44:44Z")

</div>

Yeah, we’re way off topic now

> **[2D SDF Combination](https://www.ronja-tutorials.com/post/035-2d-sdf-combination/)**
>
> In the last tutorial we learned how to create and move simple shapes with signed distance functions. In this one we will learn how to combine several shapes to make more complex distance fields. I learned most of the techniques described here from a...

In additional to IQ’s own stuff on SDF combinations.

> **[Signed Distance Fields Part 1: Unsigned Distance Fields](https://shaderfun.com/2018/03/23/signed-distance-fields-part-1-unsigned-distance-fields/)**
>
> Welcome to the first blog post on shaderfun.com. It probably won’t be all shaders, and probably not always fun, but I promise lots of interesting code and stuff. Signed distance fields are so…

^ Good discussion that shows why this is good for lowres LEDs.

Some good visualized bits:

> **[Distance Fields](https://prideout.net/blog/distance_fields/)**
>
> Distance fields, coordinate fields, and marching parabolas.

More good code:  
[http://mercury.sexy/hg\_sdf/](http://mercury.sexy/hg_sdf/)

And

> **[GitHub - patriciogonzalezvivo/lygia: lygia, it's a granular and multi-language...](https://github.com/patriciogonzalezvivo/lygia)**
>
> lygia, it's a granular and multi-language shader library designed for performance and flexibility - GitHub - patriciogonzalezvivo/lygia: lygia, it's a granular and multi-language shader lib...

Also related, and curious where PB fits in this…

 ![E8wt-a5XIAczKWm](https://forum.electromage.com/uploads/default/original/2X/6/6496caa5a721d5e76bef45661ecabb5dc0add310.jpeg)

As Patricio of BookOfShaders/PixelSpiritDeck/Lygia is taking old monitors, adding a Pi, and making pseudo LED displays

> <https://twitter.com/patriciogv/status/1422903636531814409>

It’s all converging.

Audio +shaders:

> **[AudioReactiveVisuals/S01 at main · NERDDISCO/AudioReactiveVisuals](https://github.com/NERDDISCO/AudioReactiveVisuals/tree/main/S01)**
>
> main/S01

And a video Playlist of their progress:  
https://www.youtube.com/embed/videoseries?list=PLpHvK863SFUCUEzfsBzGJHOY08wvLQgJV&wmode=transparent&rel=0&autohide=1&showinfo=1&enablejsapi=1

And a p5.js animated port of the PSDeck, again, totally imagining a led version

> **[GitHub - b2renger/p5js\_pixel-spirit-deck\_shaders: Recode of the pixel spirit...](https://github.com/b2renger/p5js_pixel-spirit-deck_shaders)**
>
> Recode of the pixel spirit deck for p5js with animated cards : https://patriciogonzalezvivo.github.io/PixelSpiritDeck/

And another audio reactive one potentially

> <https://twitter.com/lg3bass/status/1340857043222228992>

Adapted to more interactive/reactive items here:

> **[Pixel Spirit Deck Table with guest Colin Evoy Sebestyen — VDMX - MAC VJ SOFTWARE](https://vdmx.vidvox.net/blog/pixel-spirit-deck-table)**
>
> A few weeks ago Patricio Gonzalez Vivo  (creator of the infamous Book of Shaders , one of the best online resources for learning GLSL) released a new project known as the Pixel Spirit Deck, a set of tarot cards where each image includes...
