Wednesday, January 6, 2021

Genuary 2021 Day 6: "Triangle Subdivision"

This prompt sent me back to copying BASIC programs out of the old "Creative Computing" magazine on our family's Apple II. I dimly recall keying in a program that divided a triangle recursively, giving a heightfield. Our Apple II had 64 kilobytes of RAM, which a good chunk was given over to DOS and the BASIC overhead, so there wasn't a lot of room for fancy data structures or very complicated geometry. Then again, the hi-resolution graphics screen was 280x192 pixels, or less if you were picky about color.

This is a throwback to the output of that program, even though I only recall the basic algorithm. I pick random elevations for the corners of the triangle, and then subdivide the triangle up into four sub-triangles of half the width, and perturb the new vertices based on the parent triangle's vertices. This is similar to the "Diamond/Square" terrain generation algorithm.

After creating a triangular heightfield, I generate lines by projecting each x,y,z world point into a sx, sy point on the page. To honor the hacky BASIC program, I did this by multiplying each x, y, z component by a vector I thought would look nice. No matrix multiplies, per se, no look-vectors. I could rewrite this to use that, but meh.

Another thing this doesn't have is colors (the flat part to the right is maybe water, which could be blue), which would lead me to break my paths into zero or more land parts and zero or more water parts, which could totally be done, but again meh - especially since I was planning to draw this on my AxiDraw, which I wasn't going to load different pens into, so monochrome it is.

And still one more thing that this could have, but doesn't, is hidden surface elimination. I think the original Apple II program implemented this by retaining an array in screen-x that remembered the highest value that had been drawn at that value of X, thus not going "beneath" the horizon. I've used this many times before, I refer to it as "The Joy Division Algorithm". I could do something like that here, but since I'm dealing in a bigger space than the Apple II was, I'd want to do my clipping against the vector path information, rather than some quantized pixel positions. Again, this particular seed value doesn't benefit from this, so I skipped it.


Tools Used: AxiDraw, DrawSVG

Languages Used: Python3

Development Time: ~90 minutes

Drawing Time: ~3 minutes

What's Generative Here: The heightfield is generated using RNG. This might have been the first generative algorithm I ever played with, as a kid.











Tuesday, January 5, 2021

Genuary 2021 Day 5: "Code Golf"


The full prompt text is:
Do some code golf! How little code can you write to make something interesting? Share the sketch and its code together if you can.

I pushed myself to make something small, but without taking a lot of time to make it smaller than my first working version. I started with a version that just printed a string to the console:



which outputs this:

                  🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲  🏌             ⛳                                  🌳🌳🌳🌳🌳🌳🌳           🌴🌴

(depending on how good your Unicode support is)

I then extended that to output a PNG:


Which takes the previous code's output and renders it to a PNG using PIL and a Unicode font. Not minimal, but relatively tight.


Tools Used: um, Unicode?

Languages Used: Python3

Development Time: ~90 minutes

What's Generative Here: I place 3 different forests of size and position specified by a random number generator. I then place the golfer and hole at random places.












 


Monday, January 4, 2021

Genuary 2021 Day 4: Small Areas of Symmetry

 


600 small spiders, each with left/right symmetry of the pose of their legs.

I've wanted to draw something based on a spiral on my AxiDraw for a bit, so here we are. 

For each spider, I generate a list of four numbers [-1, 1] that describe how "forward" one leg is. I then use that for both left and right legs to draw lines for the legs. I was thinking that I'd randomly let some spiders be L/R symmetric, others be rotationally symmetric, but the rotation code didn't work the first time, so rather than debug it, I just fell back to L/R symmetry.

The spiral is a simple archimedean spiral: r = k * theta, but I'm trying to take steps of roughly uniform distance along that spiral, so my step in theta is based on figuring out the radius of a circle going through the current point, and dividing a target step distance by that. This math was hard for Monday morning brain.

I started drawing this on the AxiDraw Mini, and estimated that each spider took around 4 seconds to draw, which didn't feel like a lot, but there are a lot of spiders, so I added a counter to my spider drawing loop, which counted 600 spiders exactly, which is unexpected.

I've been drawing on normal printer paper, folded over four ways to fit on my AxiDraw Mini, which sometimes led to the paper bowing upward during the print. This time, I ripped the paper down to 4.25" x 5.5", which reduced bowing. I think the better solution will be to get some drawing paper, which will have a more interesting texture and a little more thickness. 

I should also try to draw to a smaller region; this image is 15cm x 10cm as generated, but my AxiDraw Mini drew off the edge of the paper, bumped into the binder clips, and drew off the edge of the board. So, maybe 12cm x 10cm next time. 

Tools Used: AxiDraw Mini, InkScape, DrawSVG

Languages Used: Python3

Development Time: ~60 minutes

Plotting Time: ~60 minutes

What's Generative Here: for each spider, for each leg, that leg's pose is generated using a random number generator







Sunday, January 3, 2021

Genuary 2021 Day 3: "Make Something Human"

 


Maybe I'll call this "84 Little Computer Friends"

I was kind of dreading this prompt, as humans are complex and difficult to draw. So, with thanks to Randall Munroe and Rich Burlew, I set out to draw a bunch of simple humans. I've got an idea for a picture I want to render someday of a hundred, a thousand superheroes, all just standing around. This is not that picture. Or maybe it is. Well, no. 

I used the Python drawSvg library to draw this image as a SVG,  and then I used the AxiDraw to draw the SVG from within InkScape. The pen-and-ink version lost the racial diversity in the original image, which might be a theme. If you want.

Each stick figure has their own proportions, based around an average stick figure that I drew on some graph paper.


Tools Used: AxiDraw Mini, InkScape, DrawSVG

Languages Used: Python3 (SVG?)

Development Time: ~90 minutes

What's Generative Here: each stick figure's proportions, pose, and skin tone are generated using a random number generator




Saturday, January 2, 2021

Genuary 2021 Day 2: "Rule 30"

 


"Rule 30" here, refers to one of Stephen Wolfram's elementary (1d) cellular automata. If your cell's state is related to the state of the cells in the previous generation to the left and right and it's own previous state, that's 3 different bits of input for the current state. That's 2^3 = 8 possible input combinations. Each combination needs to be specified by your rule for whether that leads to an "alive" or "dead" state in the new generation, so 2^8 = 256 possible 1d cellular automata if you only look one cell left or right.

In this rendering, I (ab)used my ray marcher, and created a material that evaluated the cellular automaton for rule 30. I converted the x,y position where the ray hit the floor plane into an integer x,y value. I used x to represent the spatial dimension of the CA, and y to represent the generation number, where x,y = (0, 0) was a "seed" value of "alive", and y=0, x != 0 was seeded to "dead".

I maintained a table to cache my previously evaluated values of whether (x,y) was alive or dead. I imagined requiring around 30 generations of data, which amounted to around 1000 bits of information in my table, so not out of control.

If the (x,y) value was "alive" in my table, I colored it a sort of teal color. If it was not alive, I marched back in time to see how long it had been since that x-value was alive, with an upper limit of 10. Each step back in time faded the teal color towards the base color (which is a pale gray, which I chose because maybe it looks nicer than a harsh pure white). 

Tools Used: my ray marcher

Languages Used:  Python3

Evaluation Time: 337 seconds

Development Time: ~90 minutes


Friday, January 1, 2021

Triple Nested Loop, part 2


 Same as before (same colors, same camera angle), just turned on the metallic reflective material. I don't have a good intuition about how shiny metal links reflect - some of those reflections look weird to me. Also, it kind of looks like some Terminator-esque living metal all merging into one cohesive blob.

I'm still bothered that the shadow on the hex tile ground is out of frame to the bottom.

Genuary 2021 Day 1: "Triple Nested Loop"



I'm taking a crack at doing "Genuary 2021", a series of prompts for generating something each day, based on a series of prompts. January 1st's prompt is "Triple Nested Loop", which I took some liberties with and rendered this set of Borromean Rings. Which are mutually nested loops. Sort of.

This is rendered with my ray marcher (using sphere marching) that I built over the past week. It's written in Python 3, using multithreading and a worker queue.

What's procedural about this? I let the script pick a random point for the camera, and the links are a randomly generated color.

I tried making a version of this where there were repeated instances of these rings, but I got impatient with the rendering.