# Circles, circles, circles, and did I say Circles?

**URL:** <https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460>\
**Category:** Patterns and Code\
**Created:** [August 6, 2021, 8:03pm UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460 "2021-08-06T20:03:23Z")\
**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 6, 2021, 8:03pm UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460/1 "2021-08-06T20:03:23Z")

</div>

@sorceror inspired me, with a video that showed an old Dr. Who video effect, basically warping circles… but I realized I didn’t have circle code (yet), so… first off:

```
var canvasHue = array(pixelCount)
var canvasSat = array(pixelCount)
var canvasVal = array(pixelCount)

var height = sqrt(pixelCount) // y axis
var width = sqrt(pixelCount) // x axis

function DrawPixel(x, y, hue, sat, val) {
  if (x < 0 || x > width-1 || y < 0 || y > height-1) { return; }
  canvasHue[x + y*height] = hue
  canvasSat[x + y*height] = sat
  canvasVal[x + y*height] = val
}

function DrawCircle(x0, y0,radius,fill,hue, sat, val) {
  var x = round(radius);
  line = x
  originalx = x
  if (fill == 1) {line = 0}
  if (fill == 2) {
    DrawCircle(x0, y0,radius,1,0,0,0)
    DrawCircle(x0, y0,radius,0,hue, sat, val)
  }
  for (xi = line; xi <=originalx; xi++){
    x = xi
    //hue += .3
    var y = 0;
    var radiusError = 1 - x;
    x0 = round(x0)
    y0 = round(y0)
    while (x >= y) {
      y1 = round(y)
      x1 = round(x)
      DrawPixel(x1 + x0, y1 + y0,hue, sat, val);
      DrawPixel(y1 + x0, x1 + y0,hue, sat, val);
      DrawPixel(-x1 + x0, y1 + y0,hue, sat, val);
      DrawPixel(-y1 + x0, x1 + y0,hue, sat, val);
      DrawPixel(-x1 + x0, -y1 + y0,hue, sat, val);
      DrawPixel(-y1 + x0, -x1 + y0,hue, sat, val);
      DrawPixel(x1 + x0, -y1 + y0,hue, sat, val);
      DrawPixel(y1 + x0, -x1 + y0,hue, sat, val);
      y += .25;
    
      if (radiusError < 0) {
        radiusError += 2 * y + 1;
      }
      else {
        x -= .25;
        radiusError+= 2 * (y - x + 1);
      }
    }
  }
};

export function beforeRender(delta) {
  resetTransform()
  scale(width,height)
  if (random(1000) > 999) {
    canvasclear()
  }
  if (random(100) > 97) {
    DrawCircle(random(height),random(width),random(height/2),floor(random(3)),random(1),1,random(1)+.5)
  }
}

function canvasclear() {
  canvasHue.mutate(wipe)
  canvasSat.mutate(wipe)
  canvasVal.mutate(wipe)
}

function wipe(){
  return 0
}

export function render2D(index,x,y) {
  x = floor(x)
  y = floor(y)
  pixel = x+y*height
  hsv(canvasHue[pixel],canvasSat[pixel],canvasVal[pixel])
}

```

This is the start of a canvas drawing API library… 3 pixelCount arrays to cover HSV (could be used for RGB/RYB/etc too), height/width assumption to be square, but easily changeable if not, scale the map to be roughly width/height integer (which while I could perhaps avoid that, makes the array math trivial) [but not yet handling negative numbers, like putting origin in center]

`DrawCircle(x0, y0, radius, fill, hue, sat, val)` is pretty self explanatory, except for fill, which is 0 to draw the circle but no fill, 1 to fill the center same color, 2 to wipe the center then draw the outline. Might tweak to handle more options, like second color (of which 2 is a special case)

More demos to come…

---

<div class="post-metadata">

**Author:** ![sorceror](https://forum.electromage.com/user_avatar/forum.electromage.com/sorceror/32/735_2.png) [@sorceror](https://forum.electromage.com/u/sorceror)\
**Post date:** [August 6, 2021, 9:09pm UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460/2 "2021-08-06T21:09:20Z")

</div>

I don’t have it wired up anymore, but here’s a distance field circle implementation, with an inner radius for solid fill and outer radius that fades out to black. Looks like it could use some updating for new features like hypot().

Some things might make sense if you know that it’s for a square matrix wrapped around a cylinder, with a light barrier between the top and bottom parts. I’ll make a video next time I have it wired up!

```auto
var br1 = 1.5/16.0
export function sliderBallRadius1(v) {
    br1 = v * (2.0/16.0)
}

var br2 = 2.0/16.0
export function sliderBallRadius2(v) {
    br2 = v * (3.0/16.0)
}

export function beforeRender(delta) {
  th = time(0.071)
  bx = 1.0 - time(0.013)
  by = triangle(time(0.017)) * (1.0-br2) // ((sin(time(0.037)*PI2) * (1.0-br2)) + 1.0) / 2.0
}

export function render2D(index,x,y) {
  h = (y < 0.5) ? th : by
  s = 1
  v = 0
  
  dy = abs(y-by) 
  if (dy < br2) {
    mx = x
    bx2 = bx
    if ((bx < br2) || (bx > (1.0-br2))) {
      bx2 = ((bx2 + 0.5 ) % 1.0)
      mx = ((mx + 0.5 ) % 1.0)
    }
    
    dx = abs(mx-bx2)
    
    if (dx < br2) {
      distance = sqrt(dx*dx + dy*dy)
      if (distance <= br1) {
        v = 1
        s = distance / br1
      } else if (distance < br2) {
        v = 1.0 - ((distance-br1) / (br2-br1))
      }
    }
  }
  
  hsv(h, s, v*v)
}

```

---

<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 6, 2021, 10:38pm UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460/3 "2021-08-06T22:38:17Z")

</div>

Interesting, has a bit of a 3d look on the ball, thanks to the fade and glow.

---

<div class="post-metadata">

**Author:** ![sorceror](https://forum.electromage.com/user_avatar/forum.electromage.com/sorceror/32/735_2.png) [@sorceror](https://forum.electromage.com/u/sorceror)\
**Post date:** [August 7, 2021, 6:00am UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460/4 "2021-08-07T06:00:32Z")

</div>

Oh right, it’s not a solid fill inside, it fades to white there via `s = distance / br1`.

Thanks for giving it a spin!

---

<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 7, 2021, 11:55am UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460/5 "2021-08-07T11:55:07Z")

</div>

Inspired again by @sorceror, I present Soap Bubbles aka Fizzy Lifting

> **v0.5 - static bubbles**
>
> ```
> // SoapBubbles v0.5 by Scruffynerf
> var canvasHue = array(pixelCount)
> var canvasSat = array(pixelCount)
> var canvasVal = array(pixelCount)
> var canvasAge = array(pixelCount)
> var canvasType = array(pixelCount)
> 
> var height = sqrt(pixelCount) // y axis
> var width = sqrt(pixelCount) // x axis
> 
> function DrawPixel(x, y, hue, sat, val, special) {
> if (x < 0 || x > width-1 || y < 0 || y > height-1) { return }
> index = x + y*height
> canvasHue[index] = hue
> canvasSat[index] = sat
> canvasVal[index] = val
> canvasAge[index] = 99
> canvasType[index] = special
> }
> 
> function DrawCircle(x0, y0, radius, fill, hue, sat, val, hue2, sat2, val2) {
> var x = round(radius);
> line = x
> originalx = x
> originalsat = sat
> special = floor(random(3))
> if (fill == 1 || fill == 3) {line = 0}
> if (fill == 2) {
> DrawCircle(x0, y0,radius,1,hue2,sat2,val2)
> DrawCircle(x0, y0,radius,0,hue, sat, val)
> }
> for (xi = line; xi <=originalx; xi++){
> if (fill == 3) {
> sat = lerp(0,originalsat*.99,(xi)/(originalx))
> }
> x = xi
> var y = 0;
> var radiusError = 1 - x;
> x0 = round(x0)
> y0 = round(y0)
> while (x >= y) {
> x1 = round(x)
> y1 = round(y)
> DrawPixel(x1 + x0, y1 + y0,hue, sat, val, special);
> DrawPixel(y1 + x0, x1 + y0,hue, sat, val, special);
> DrawPixel(-x1 + x0, y1 + y0,hue, sat, val, special);
> DrawPixel(-y1 + x0, x1 + y0,hue, sat, val, special);
> DrawPixel(-x1 + x0, -y1 + y0,hue, sat, val, special);
> DrawPixel(-y1 + x0, -x1 + y0,hue, sat, val, special);
> DrawPixel(x1 + x0, -y1 + y0,hue, sat, val, special);
> DrawPixel(y1 + x0, -x1 + y0,hue, sat, val, special);
> y += .25;
>     
> if (radiusError < 0) {
> radiusError += 2 * y + 1;
> }
> else {
> x -= .25;
> radiusError+= 2 * (y - x + 1);
> }
> }
> }
> }
> 
> export function beforeRender(delta) {
> resetTransform()
> scale(width,height)
> if (random(1) > .99) {
> radius = random(height/4)+2
> fill = 3
> DrawCircle(random(height-radius*1.5)+radius*.75,random(width-radius*1.5)+radius*.75,radius,fill,random(1),1,1)
> }
> canvasfade()
> }
> 
> function canvasfade() {
> canvasVal.mutate(fade)
> }
> 
> /*
> function canvasclear() {
> canvasHue.mutate(wipe)
> canvasSat.mutate(wipe)
> canvasVal.mutate(wipe)
> }
> function wipe(){
> return 0
> }
> */
> 
> function fade(value, index, array){
> canvasAge[index] -= random(1)
> if (canvasType[index] == 0){
> pop = canvasSat[index]
> } else if (canvasType[index] == 1) {
> pop = 1-canvasSat[index]
> } else {
> pop = canvasVal[index]
> }
> if (canvasAge[index] < 30*(pop) ){
> canvasSat[index] = canvasSat[index] * .95
> return value*canvasSat[index]
> } else {
> return value 
> }
> }
> 
> function lerp(a, b, t) {
> return a * (1-t) + b * t
> }
> 
> export function render2D(index,x,y) {
> x = floor(x)
> y = floor(y)
> pixel = x+y*height
> hsv(canvasHue[pixel],canvasSat[pixel],canvasVal[pixel])
> }
> 
> ```

They have some depth, thanks to the sat changes, and they pop in 3 ways, either inside to out, outside to in, or just fizzle. This version doesn’t have any sliders (yet), nor does it have them rising into view, then popping. (I’ve figured out how I think I can do that, using additional height in the canvas, so we draw ‘offscreen’, then either copy the array up a row, or perhaps use translate to move the viewing window down (which would look like the bubbles were rising) [way cleaner/faster, but have to handle how to smoothly move back up, or maybe I shift-copy the array only then when I hit bottom, then move back to zero?]

So I just did a straight copy (mutate the various arrays upwards by one row), no translation. I still like the idea, but this works, so… I’m definitely going to use this technique again… ideas come to mind, like the classic ‘road race’…

> **v0.7 of Soap Bubbles - now more of a Fizzy Lifting pattern**
>
> ```
> // v0.7 of Soap Bubbles - now more of a Fizzy Lifting pattern - by Scruffynerf
> var canvasHue = array(pixelCount*2)
> var canvasSat = array(pixelCount*2)
> var canvasVal = array(pixelCount*2)
> var canvasAge = array(pixelCount*2)
> var canvasType = array(pixelCount*2)
> 
> var height = sqrt(pixelCount) // y axis
> var width = sqrt(pixelCount) // x axis
> 
> function DrawPixel(x, y, hue, sat, val, special) {
> if (x < 0 || x > width-1 || y < 0 || y > (2*height)-1) { return }
> cindex = x + y*height
> canvasHue[cindex] = hue
> canvasSat[cindex] = sat
> canvasVal[cindex] = val
> canvasAge[cindex] = 60 + ((2*height)-y)*6
> canvasType[cindex] = special
> }
> 
> function DrawCircle(x0, y0, radius, fill, hue, sat, val, hue2, sat2, val2) {
> var x = round(radius);
> line = x
> originalx = x
> originalsat = sat
> special = floor(random(3))
> if (fill == 1 || fill == 3) {line = 0}
> if (fill == 2) {
> DrawCircle(x0, y0,radius,1,hue2,sat2,val2)
> DrawCircle(x0, y0,radius,0,hue, sat, val)
> }
> for (xi = line; xi <=originalx; xi++){
> if (fill == 3) {
> sat = lerp(0,originalsat*.99,(xi)/(originalx))
> }
> x = xi
> var y = 0;
> var radiusError = 1 - x;
> x0 = round(x0)
> y0 = round(y0)
> while (x >= y) {
> x1 = round(x)
> y1 = round(y)
> DrawPixel(x1 + x0, y1 + y0,hue, sat, val, special);
> DrawPixel(y1 + x0, x1 + y0,hue, sat, val, special);
> DrawPixel(-x1 + x0, y1 + y0,hue, sat, val, special);
> DrawPixel(-y1 + x0, x1 + y0,hue, sat, val, special);
> DrawPixel(-x1 + x0, -y1 + y0,hue, sat, val, special);
> DrawPixel(-y1 + x0, -x1 + y0,hue, sat, val, special);
> DrawPixel(x1 + x0, -y1 + y0,hue, sat, val, special);
> DrawPixel(y1 + x0, -x1 + y0,hue, sat, val, special);
> y += .25;
>     
> if (radiusError < 0) {
> radiusError += 2 * y + 1;
> }
> else {
> x -= .25;
> radiusError+= 2 * (y - x + 1);
> }
> }
> }
> }
> 
> var timer
> export var speed
> 
> export function sliderSpeed(v){
> speed = 400 * v
> }
> 
> export function beforeRender(delta) {
> timer = timer + delta
> resetTransform()
> scale(width,height)
> if (random(1) > .98) {
> radius = random(height/4)+2
> fill = 3
> DrawCircle(random(width-radius*1.5)+radius*.75,random(height)-radius-1+height,radius,fill,random(1),1,1)
> }
> canvasfade()
> if (timer > speed){
> timer = timer - speed
> canvasscrollup()
> }
> }
> 
> function canvasfade() {
> canvasVal.mutate(fade)
> }
> 
> function canvasscrollup(){
> canvasHue.mutate(scrollup)
> canvasSat.mutate(scrollup)
> canvasVal.mutate(scrollup)
> canvasAge.mutate(scrollup)
> canvasType.mutate(scrollup)
> }
> 
> function scrollup(value, index, array){
> if (index < (pixelCount*2)-height){
> return array[index+height]
> }
> }
> 
> function fade(value, index, array){
> if (index < pixelCount){ 
> canvasAge[index] -= random(10 * (100/speed) * ((pixelCount-index)/pixelCount) )
> if (canvasType[index] == 0){
> pop = canvasSat[index]
> } else if (canvasType[index] == 1) {
> pop = 1-canvasSat[index]
> } else {
> pop = canvasVal[index]
> }
> if (canvasAge[index] < 90*(pop) ){
> canvasSat[index] = canvasSat[index] * .95
> return value*canvasSat[index]
> } else {
> return value 
> }
> }
> return value
> }
> 
> function lerp(a, b, t) {
> return a * (1-t) + b * t
> }
> 
> export function render2D(index,x,y) {
> x = floor(x)
> y = floor(y)
> pixel = x+y*(height)
> if (pixel >=0 && pixel < pixelCount) { 
> hsv(canvasHue[pixel],canvasSat[pixel],canvasVal[pixel])
> }
> }
> 
> ```

Absolutely needs more tweaking, and fine tuning, but not bad for a Saturday morning’s LED creation

 ![68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f776174747061642d6d656469612d736572766963652f53746f7279496d6167652f474f6c357761707551714a6356513d3d2d3637363439333032392e313537363961363937336564366166333938343430353337303833392e6a7067](https://forum.electromage.com/uploads/default/original/2X/d/d7cfb7e52249b390f1dcf658abe426ca4d7c8136.jpeg)

![you-got-fingerprints-on-your-leds-when-you-ran-my-pattern-good-day-sir](https://forum.electromage.com/uploads/default/original/2X/2/249752d8561f5cb659c61a6ab5ab2b4a3c0af48f.jpeg)

---

<div class="post-metadata">

**Author:** ![sorceror](https://forum.electromage.com/user_avatar/forum.electromage.com/sorceror/32/735_2.png) [@sorceror](https://forum.electromage.com/u/sorceror)\
**Post date:** [August 7, 2021, 8:37pm UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460/6 "2021-08-07T20:37:20Z")

</div>

Works great, the bubbles are very bubbly! I like the Age and Type buffers, great ideas! I am pondering how to reconcile this classic framebuffer+blitting approach with shader techniques.

---

<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 8, 2021, 7:37pm UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460/7 "2021-08-08T19:37:01Z")

</div>

Just to muddy the water even more, here’s something I’m working on that uses signed distance functions to draw various antialiased geometric shapes, filled and unfilled. This is mostly a testbed for the distance functions, so shading and so forth are pretty basic (um… slightly silly?) atm.

> **Geometry Testbed #1**
>
> ```auto
> 
> // 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
> 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++) {
> d = shapeSdf[objects[i][5]](x-objects[i][0],y-objects[i][1],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)
> }
> 
> ```

---

<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 8, 2021, 8:04pm UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460/8 "2021-08-08T20:04:30Z")

</div>

To be clear, I only implemented a most basic version of the Midpoint circle algorithm, and I’d love to replace it with something that does any polygon, arcs, etc. As I said up top, I really want to do a pattern like the DrWho one, and realized that I needed some form of circle first. (But of course have spent my time on other stuff instead)

@zranger1 these are cool, but I’m not quite sure how to do a conversion from [the 2d code](https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm) to the signed functions you created, can you walk thru one, and maybe do a new one?  
These blow my circle code out of the water: they do fills, they do variable widths… so now I want use these (not just for circles), but still not quite ‘getting it’. So if I wanted to rotate the triangle (and only the triangle), I could either figure out how to rotate the points, OR I guess I could rotate the canvas and rotate back (ugly!)

---

<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 8, 2021, 11:37pm UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460/9 "2021-08-08T23:37:53Z")

</div>

Ha… I didn’t even know he had 2d functions – I went from the 3D.  
Will do another shape one this evening to show what I did… I may actually be wrong!

---

<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 8, 2021, 11:41pm UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460/10 "2021-08-08T23:41:23Z")

</div>

And actually, they don’t blow your circle code out of the water at all. Apples and oranges. I timed yours, and DrawCircle() is really fast, especially if you use the simpler fills. Also, the cool bubble popping effect is much harder to do on the non-frame buffer version.

Short answer: We need both. Geometry is an underexplored area on the Pixelblaze, and I look forward to seeing all the cool tools and techniques we come up with!

---

<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 9, 2021, 3:27am UTC](https://forum.electromage.com/t/circles-circles-circles-and-did-i-say-circles/1460/11 "2021-08-09T03:27:48Z")

</div>

Ok… here’s my first cut at annotating the GLSL-\>Pixelblaze porting process for the basic square sdf, and a new hexagonal star function. This is just the distance function code, but you can swap it into the demo pattern I posted earlier to see what it looks like.

Tomorrow, I’ll plug this into the demo pattern , and start dealing with rotation.

```auto
// iq's GLSL code
// note that this version permits independent x and y side length
float sdBox( in vec2 p, in vec2 b )
{
    vec2 d = abs(p)-b;
    
    // this is GLSL being sneaky about how it handles vectors -- the expression max(d,0)
    // is actually doing something like max(max(d.x,d.y),0)).  
    return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
}

// Pixelblaze version
// incoming x and y coords are the difference between the object's center and the
// pixel being evaluated.   
// size is the side length of the square we want to draw.
function square(x,y,size) {

  // The two lines below compute the various parts of d = abs(p)-b, and max(d,0.0) from
  // the original, where p is a vector containing the x and y coords, and b holds the side lengths
  // of the rectangle we're checking. I force the side lengths to be the same for simplicity.
  
  dx = abs(x) - size; d1 = max(dx,0); // how far are x and y from the nearest edge?
  dy = abs(y) - size; d2 = max(dy,0);
  
  // Here we put everything together for the final result. This whole thing works just
  // like the GLSL version. I just broke up the vector operations and reordered things
  // a little. 
  return min(max(dx, dy), 0.0) + hypot(d1,d2);
}

```

Now for a new one. A hexagonal star seems like it’d be an interesting thing to have!

```auto
// iq's GLSL code
float sdHexagram( in vec2 p, in float r )
{
    const vec4 k = vec4(-0.5,0.8660254038,0.5773502692,1.7320508076);
    p = abs(p);
    p -= 2.0*min(dot(k.xy,p),0.0)*k.xy;
    p -= 2.0*min(dot(k.yx,p),0.0)*k.yx;
    p -= vec2(clamp(p.x,r*k.z,r*k.w),r);
    return length(p)*sign(p.y);
}

// Pixelblaze version
function hexStar(x,y,r) {
  // rescale and take absolute value of pixel coords
  // original is apparently scaled to a 2*unit size
  x = abs(x*2); y = abs(y*2); 
  
  // 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;
}

```
