Tuesday, April 6, 2021

HOW-TO make a fish scale / mermaid / bun pattern

 


A friend of mine prompted me to make some "nautical" bookmarks, and that drifted off towards fish. I like fish - largely for their dynamic aggregate behaviors (my thesis had to do with flocking behaviors of dinosaurs, not that different from tropical fish, at least in my simulation). 

So, I had this idea of making an organic-looking fish scale design. It was super quick, and it turned out pretty nice, according to me and the people on Twitter who liked my post. This will be a high-level description of how to make something like the above pattern, along with tips on things to try changing.

Step 1: Generate a "grid" of points

If you were here last time for my flow field how-to, you'll recognize this step. I've got a library, or maybe a toolbox, of routines that I like to turn to when I make my plotter drawings. Robert Bridson wrote a short, but super useful (and readable!) paper about generating points that feel organic, but have nice distributions. Read it and come back, it's worth it, and it's short!

https://www.cct.lsu.edu/~fharhad/ganbatte/siggraph2007/CD2/content/sketches/0250.pdf

I'm going to trust you read the paper, but the takeaway is that you make a grid where each square in the grid is sized so that you know that no two points that you generate can be in a single grid cell. And then you can check a neighborhood in O(1) time, which is like the best time. 

Bridson's paper doesn't assume you're working in 2D, but I don't know about you, I do almost all of my drawings in 2D. Sometimes 3D, so it's good to think about a 3D grid, but a 2D grid gets the job done 99% of the time.

So, if you're ok with 2D, there's this variation, which is what I actually use for my plottering:

http://extremelearning.com.au/an-improved-version-of-bridsons-algorithm-n-for-poisson-disc-sampling/

This is faster, and a little bit more tightly packed, which seems good for my stuff. If you decide it's not for you, and you use Bridson Classic, that's cool. The improved version walks the edge of a disc around each point, generating points as tightly as it can, which you'd think might lead to some hints of repeating patterns, but I find it looks good.

Step 2: Sort your points in x

I'm going to trust that you've got this one. Take the points generated by Bridson, and sort them based on their x-coordinate. You could do any other sorting, but what I'm doing here is basically putting all of the points on a slanty surface in x AND Z slanting away from the viewer, so that the points on the right look farther away than the points on the left.

Mix it up, though. Maybe you can do something fancy to get some "wiggle" in the scales. Or an Ouroboros pattern where the scales loop around. Let me know what you come up with.

Step 2a: Let's draw some debug circles


This is just drawing a circle around each point with the radius I used for the Bridson algorithm. You can see that the points are all pretty tightly packed. Nearly a triangle grid, but with some noise in there that makes it feel more interesting.

The thing we're going to do next is some hidden line / hidden surface elimination, which kids for the past 25 years plus have been able to do with hardware z-buffering. But we're not going to do that, because I'm writing in Python, and vector art feels like it's not meant to be all pixel-shader-y. 

If you disagree, maybe you can do something more efficient than I'm doing here, but I'm going to walk the class through use of "The Painter's Algorithm", which is what we had when I was your age.

Step 3: Let's draw some circles one segment at a time

If you looked at that above illustration and reproduced it with a circle primitive in your drawing environment, that's fine. I actually drew it as a "polyline", a series of vertices connected by straight lines. I used a lot of vertices (around 45 for each circle), which is pretty good. Bear in mind that my target is a 6 inch by 1 inch bookmark, so I can get away with not being pixel-perfect.

I'm not here to teach you trigonometry, but a for loop and some sines and cosines, and you can get a polyline that plots to look like a circle.

But not so fast!

We've sorted our circles so that we'll be drawing the circles from left to right. I've got a routine that takes one of these polyline representations of a circle, and "clips" it against another circle, so that any vertices in the polyline that are inside the second circle get removed. It's a simple function, and I don't want to load this post up with code, but since you asked so kindly:


Yeah, that's a picture of code, which makes it hard to just cut and paste. I'm not sorry. You get syntax highlighting this way.

You can probably see what's going on here, I pass in a "path", our polyline of vertices. Also a center and a radius which describe the circle we're clipping against. I keep consecutive sets of verts in "current_path" until I run out, or until I hit a vert that's inside the second disk, at which point, I move "current_path" into "out_paths", and carry on. At the end, I return each little snipped up piece of the polyline. Maybe I could have been smarter, knowing that my path might end up having 0, 1, or 2 pieces, but I was lazy, and this seemed easy.

With this routine, I can take each of the circles to the left (which, recall, are "in front") of this circle and "hide" the parts of the circle that are obscured. It's almost like I'm some sort of oil painter, hence "Painter's Algorithm".

Except that a painter that uses an opaque paint (Bob Ross) would work from the back and move forward, so it's maybe not a perfect analogy. We're sort of manually cutting up each new circle and only drawing the pieces not obscured by those to the left/forward of it.

I could be clever, use the Bridson grid approach to do an O(1) neighborhood check to find previously drawn circles to clip against, but again, I was lazy when i did this, and I just clip against every circle I've already drawn. It's quick enough for what I need, right now.

Step 4: You're done

That's all there is to it, just draw circles, clipped against earlier circles. I thought about doing fancier masking, drawing a bitmap to a temporary buffer, and then clipping my polylines against that - I might do that, too. 

Or, you could do some fancy Signed Distance Field (SDF) stuff, which would be pretty similar to what I did, above. But SDFs are cool, so maybe there's some neat effects you could get from doing it that way.

Step 5: You're still here?

If you turn the picture sideways, maybe it looks like steamed buns or xiao long bao (soup dumplings). 



Maybe not. But if you like the image of dumplings and/or buns, stretching off to the horizon, maybe my work here is done.

Sunday, April 4, 2021

HOW-TO make a flow field drawing on a plotter

 


In January of 2021, I took part in a month-long daily set of challenges to draw things on my plotter. One of the challenges was "curves", so I set out to draw a bunch of non-overlapping curves. I call this technique a "flow field", and it's inspired by a number of things I've seen elsewhere, and I'm sure other people have similar approaches, but this article talks about how I've been making these kinds of drawings, including some things I've learned as I go along.

Step 1: Generate a "grid" of points

My first step is to make a (roughly) evenly spaced grid of points, using Bridson's algorithm. I like this implementation here: http://extremelearning.com.au/an-improved-version-of-bridsons-algorithm-n-for-poisson-disc-sampling/ which uses Bridson's technique of keeping an array of buckets for generated points, but then walking uniformly around a "live" point to generate new points where possible. Quick, easy, and a useful tool for your toolbox.

You could use any other technique for generating your points; a square grid, a hex grid, or something else.

Step 2: For each point, generate a random direction

This is pretty straightforward - at each of the points in the grid (from step 1, above), pick a random direction. You could do this by picking a heading in degrees between 0 and 360 or in radians between 0 and 2*pi. That works, and it's basically fine, but I prefer to generate a random vector of unit length, by picking x and y values from -1 to 1 and throwing out x,y pairs that are outside of the circle. If they're inside the circle, I scale x and y so that the magnitude of the vector is 1.

This is a little more work, and you could get the same effect by picking a random heading and then using sine and cosine to get x and y. One thing that's nice about generating vectors this way is that it's easy to use the same ideas to make a random 3d vector on the unit sphere. Or 5d or whatever. If you have need for such things.

Step 3: Smooth the directions

I don't know how important this step is, I think it's worth doing, but I don't know how much work is worth doing here. What I do is loop for a couple (maybe 5?) times over all the points from step 1, and adjust the directions by a weighted average of the nearby points' directions.

This is a place where it's handy to have the directions as vectors; you can sum a bunch of direction vectors, scale them as you need, maybe normalize on the way out, and you've got a well-behaved vector sum. It's trickier to make sure that angle measures wrap around properly, and doing weighted sums is harder.

So, I do some smoothing to get points sort of lining up with their neighbors.

Step 4: Trace some paths

Now you want to generate a set of starting points. Maybe use Bridson or whatever you used in step 1. Or use something else.

For each starting point, create a path, a series of points, stepping in the average direction of the nearby neighbors. I use an inverse square falloff to weight the nearby points, but different ways of determining the local direction are possible. Take a step, find the local direction, take another step. If you go past a maximum number of steps, probably stop. If you go off the edge of the paper, stop.

I have noticed that there's a strong possibility of having paths getting dense on the page, to the point that when I physically plot the lines on paper, I bleed through the paper (with felt-tipped pens) or tear holes in the paper (with ball-point pens). So, one thing that I do is keep track of areas that I've visited, and if my new path is close to an already-drawn path, then I stop my new path.



Variations

Some ideas that I've played around with, and some ideas I haven't tried:
  • Vary the color - I've picked a different color for different paths. Maybe other color selection algorithms would be useful. If you're drawing to a screen with more than 256 colors, you can be pretty flexible. With plotters, you might have a much more limited set of options.

  • Use a mask for where the paths can be - there's no real reason to use a rectangle as your boundary. I used a pair of circles here to stop tracing my paths. You could use Signed Distance Fields or other ways of determining a boundary.
  • non-unit direction vectors - All of my directions are normalized to be length 1. I do a weighted average of the nearby direction vectors, and again, normalize to be length 1. Maybe don't do that? You could have paths that have "velocity" and "momentum" in a way that these paths don't. Maybe that's interesting?
  • Other forces - you could have other ways of influencing the direction that a path moves, by simulating gravity, or by generating a twist around your points.
  • Other generating shapes - I use points, but you could use squares or donuts, or other shapes.

This is all pretty high level, but gives some ideas for how you might generate your own flow field drawings. Have fun!

Friday, April 2, 2021

Dose 1 of the Pfizer Vaccine

This is a post that might be relevant to a few people for a short amount of time. I just got my first dose of the Pfizer COVID-19 vaccine, and wanted to share a little bit of the experience.

Qualifying

Washington State has been running a "Phase Finder" service for a few weeks, and I guess that the service has been shut down, as the ramping up of availability has made administering the service of knowing who's eligible less useful. So that's good, I guess.

I got an email yesterday afternoon, telling me that I now qualify - anybody 16 years old or older with two or more "comorbidities" (eesh, I hate that word, I'd prefer something like "health factors") is good for the vaccine.

A friend of mine pointed me at https://www.covidwa.com/ as a website that helps people find places that might have doses. You can look for standby opportunities, filter by county, and other goodness. It refreshes frequently, so it's worth just having open and looking at through the day.

I found an appointment at the Arlington Municipal Airport. A hot tip I got on Twitter was to find the most red-leaning neighborhood within your radius (whatever that means, how far you want to drive, where you are eligible, whatever), and that's probably where the vaccines are in least demand, so perhaps with highest supply. Arlington is ~25 miles further out into the exurbs from my house, so that worked out.

Appointment

My appointment was at 9:05 this morning, the appointment signup sheet having slots every five minutes. I was given an address to show up at, which looked like a random point on the perimeter of the airfield. Turns out, the address was specific enough to let my GPS lead me to a gate where they let in a line of cars. I think I was around car #20 in the line, which they started letting through the gate at a little before 9:00. If you've ever lined up for a car-ferry, or if you've ever been at a concert or gone to a state fair where people direct you through traffic cone ad hoc traffic paths, this was a lot like that. More smooth than most of those, by a little bit.

They asked for my photo ID and my appointment confirmation (which I had printed out - good), and then gave me a questionnaire (what race are you? what ethnicity? do you have a fever?). I happened to have a pen with me, which meant I didn't have to share a pen with somebody else. Probably not a big deal. If I had thought to bring a clipboard to fill out the form, that might have been nice, too.

I was wearing a facemask with red sequins, and a jacket with red sequins. I got a few remarks about how festive I looked. This was, honestly, the biggest day for about a year, yeah I'm going to dress up.

They split us into three lanes to speed up the pipeline. Jab! Quick and painless. Then they funneled us into a waiting lane where we'd sit for 15 minutes, just in case there was some side effect that made us feel anything less than OK. 

They gave us vaccine cards, with little photocopied information sheets (with "Pfizer" spelled incorrectly) to help us fill out our own cards. They told us that we'd get emailed to schedule our second shot. So, I've been refreshing my inbox, getting ready for that.

So far, no aches or pains to speak of. My arm doesn't feel sore. I've checked, and the bandage on my arm suggests this wasn't just a dream I had.

Friday, February 5, 2021

Racetrack Update

 

You may not recall, but a while ago, I drew some paths using Voronoi Regions (look back to Genuary Day 26). 



I was pleased with the output, but a little perplexed that the hairpin turn on the NE corner of the westernmost track seemed to be too sharp a turn. More specifically, if the notional "cars" were going from the fairly vanilla hexagon at the far N of the track, into the "diamond" shaped pentagon, with the pointy bit to the south, on to the flat hexagon, back to the diamond pentagon. 

My code should not generate tracks based on previous regions - what's going on?

I've got all the code from the various works, and each of those used a seed string, so I could reproduce the calculations that I considered suspect. So, I dug back into the code, made a copy, and (re)enabled some debugging information, along with drawing a lot of dots.


One thing I discovered in my spline code was that I was asking for 10 points on my curve, but my generators were evaluating from 0 to 10, inclusive. This caused some of my curve data to overshoot the intended data.

Another thing I discovered was that my tracks (the blue curve, here) were based on the generating points for the voronoi regions. I'm not doing anything fancy like using the centerpoint of the edge between regions, to ensure that the curve would be centered on that edge. I'm basically just drawing a polyline from generating point to generating point, but then I round off the corners.

What's important(?) to recognize here is that that generated polyline may cross between regions along the voronoi edge between those regions, or it might not.

If you look super carefully at the "flat hexagon" and the "apparent pentagon" to its west, there's a tiny little edge connecting them, which is what my track layout algorithm used. But the line connecting the generating points crosses back into the diamond pentago on its way from the flat hexagon to the "apparent pentagon" (which is actually a hexagon, because the 6th side is tiny, and the flat hexagon is a heptagon).

I'm sure you were as curious as I was about why the tracks weren't working. Now they are. Although I suspect the drawings are just about indistinguishable. But now they're right, and we understand why.

Sunday, January 31, 2021

Genuary 2021 Day 31: "Oblique Strategies"

 


I like the idea of Brian Eno's cards for jarring your brain to be more creative, to get out of its rut. I found an online Eno Oblique Strategy oracle, and it told me:

"Don't be afraid of things because they're easy to do."

Which I played around with for a while. I remembered the old vector arcade Star Wars game that allowed you to select your level and get a bonus if you selected anything but the easiest difficulty. But no shame in playing on "easy". Some people prefer to experience the story.



So, I rendered my own version of the level selection screen, using my ray marcher. I added in some "reflection" operators, so the four X-Wing cannons are actually a single piece of geometry. The 3 Death Stars are, in fact, 3 separate items in the scene hierarchy.

I then rendered a starfield as 4 separate Bridson blue noise pointsets. I drew the different pointsets with different colors - I think this gives some satisfying clustering, and looks like a SF vision of how stars look, even if it's not realistic.

I then used my Hershey Font renderer to draw some text. I put in the Eno prompt to give the player some reassurance.

I composited the starfield, the rendered scene, and the text together in GIMP.

Tools Used: drawSVG, my ray marcher, GIMP, Hershey Futura L font

Languages Used: Python

Development Time: ~3 hours

Drawing Time: ~1.5 hours (for the rendered bits, negligible for the rest)

What's Generative Here: only the starfield. Everything else was done by hand.

Saturday, January 30, 2021

Genuary 2021 Revisiting the Penrose Tiles (Day 29)

 



I wasn't super satisfied with my Penrose Tile drawing from Day 29, so I modified it (perhaps imperceptibly), and had another go at it. It's better, but I think there's even more to be done.

Also, I wanted to describe my approach, which comes, in part from the discussion here: http://www.math.ubc.ca/~cass/courses/m308-02b/projects/schweber/penrose.html talking about beginning with a known good tiling and then subdividing from there.


I won't explain them here, (read the above linked page for more detail) but I'm using two different shapes of rhombuses, "tall" and "short". In the earlier version, my seed pattern was a "wheel" or flower shape made up of 10 "tall" rhombuses arrayed around a point at the center of the page.

I was dissatisfied with this for a couple of reasons:

  1. It's not "generative" enough - it's some natural origin of some pattern, lining up with the center of the page. Bleh.
  2. There's too much symmetry. Penrose tiles are supposed to be this magnificent non-periodic pattern, but sitting right at the center of the screen is a 5-fold rotational axis of symmetry. Bleh. Not just a rotational axis, but 5 different planes of mirror symmetry.
So, I went in to modify the generator in two ways to fight this to some degree:
  1. My new generator wouldn't use a symmetric "wheel", it would generate all possible patterns of rhombuses coming together at a vertex, and choose one. Also, rotate it, which is easy.
  2. The new generator should also offset the center of the page, relative to the center of the "wheel". This is also easy. Indeed, my earlier Genuary code was repurposed from some earlier code where I was making laser cut jigsaw puzzles, and that code had an offset. I deleted the offset to make porting my old code to my Genuary codebase easier. Putting it back is not a big deal.

This is what a wheel looks like. The blue triangles are from "Tall" rhombuses, and red are from "Short". I didn't make up the names. Note that there are edge labelings that force the triangles to alternate, to keep from falling into a degenerate, boring, tiling.

I am already happy at this point that my wheel is asymmetric, look at all that asymmetry. Mission accomplished.



Subdividing all of those triangles once.

I won't describe the subdivision here (read the linked page), but you can see that the tall triangles turn into a tall and a short triangle and the short triangles turn into a tall and two short triangles. And, whaaaat? We now have a symmetric red star inside a symmetric blue ring. There's asymmetry outside the blue ring, but we have to move a ways to get there.


Subdividing a second time, the symmetry hasn't gone away, nor will it. 


It's harder to be certain about the symmetry as the subdivisions increase, but you can feel its presence.


Ok, but if I shift it off the side of the page, it's not so bad, right?


Zooming in (and subdividing to maintain approximately the same level of detail). The point of symmetry is still staring at me from the top-middle of the page, like the eye of Sauron, in the middle of the red spot on Jupiter. Checkmate.

Let's see how I did, relative to my objectives:
  1. More "generative"? Check, I guess? I like the fact that my seed "wheel" has more variety to it. And I'm rotating and centering in ways that mean that the next one I generate will be somewhat different from this drawing.
  2. Less symmetry? Meh, let's give that a C. The rotational center is not at the center of the page, so that's good. I could actually modify the code to ensure that my recenter-and-zoom code moved the rotational center off the page, that wouldn't be too hard.
Another way to carry this forward would be to throw out the "wheel" seed altogether and just start with a rhombus and zoom in to that. Or even a triangle.

I really should give it another go - the zooming wouldn't be too hard, and I'm happy with the output in other respects.

One other thing that I should revisit for the next pass is my "edgepool" system - it's doing a pretty good job of accumulating paths and giving them back to me in a left-to-right draw order, which makes the page fill up in a sane order, mostly. This also does a good job of lessening (perhaps not optimizing) the pen up movements, which makes the drawing more efficient. So that's all good. But a few things I could do:
  1. Right now, the paths are sorted based on the x-coordinate (or "abscissa") of vertex 0 on the path. I had considered checking to see if the last vertex is lower in x, and if so, flipping, which for this drawing wouldn't do anything, as all the drawn shapes are closed (unless you're clipping off the edge of the page, but that's not important right now). What I could do, instead, is to find the minimum x for all the points on the path, and use that to sort. This would avoid a little bit of "swiss cheese" effect where I'm filling the page with things and some things almost entirely to the right of some other things draw first. This would fill more evenly
  2. Don't just sort on x, also sort on y. I don't think this would have a lot of effect, as floating point, it'll getcha 99.99% of the time. But if there were three paths with "the same" x value, maybe draw them top to bottom, rather than middle, top, bottom, or some other weird order.
  3. A variant on the above would be to bucket the paths in x, and then within a bucket, sort in y. This would work around the floating point issue where things aren't ever going to match their x coordinates exactly. I could even just round the (x, y) pairs to integers, and it'd be pretty close to this.
I'm stepping away from this for a little bit to work on revisiting a different piece. Maybe I'll post about that. Maybe I'll post about coming back to this. Stay tuned.






Genuary 2021 Day 30: Replicate a Natural Concept

 


I drew a double pendulum today, using some math pointed out by the Coding Train "Coding Challenge #93: Double Pendulum" video: https://www.youtube.com/watch?v=uWzPe_S-RVE

I recall at some point when I was in high school, knowing a little bit about the Mandelbrot Set and a little bit about chaos, thinking that a double pendulum graph would be cool to explore. 

It's kind of cool to see how the red bob ("bob 1") is jerked around by the blue bob ("bob 2"), yielding non periodic motion, but it's surprising to me to see how smooth bob 2's motion looks; the path looks like a sine wave to my eye. It's probably a little sharper, but hard to tell.

Interesting direction to go with this: instead of just a single plot of x versus time, what if we had multiple configurations in parallel, and compress the pendulum x value into a color? So, maybe time would extend to the right in the image, and the image x would represent some continuously varying set of initial conditions.


Tools Used: drawSVG Python Library

Languages Used: Python

Development Time: ~1 hours

Drawing Time: ~2 minutes

What's Generative Here: the mass and length m1 and L1 are chosen to be 1.0 each, but the mass and length m2 and L2 are chosen randomly. Similarly the initial pose of the double pendulum is randomly generated.


Friday, January 29, 2021

Genuary 2021 Day 29: "Any shape, none can touch"

 "Penrose Tiling"


For this one, I dug around in my hard drive, and found an old bit of code that generated Penrose tilings. The code starts with a configuration of triangles, a "wheel" of isosceles triangles, and then subdivides them based on the subdivision rules here: http://www.math.ubc.ca/~cass/courses/m308-02b/projects/schweber/penrose.html This guarantees a valid Penrose tiling without having to do the hard job of extending the tiling from a seed rhombus.

I subdivided a few times to get the density I liked, and then inset each rhombus by 10% so that things wouldn't touch.





Tools Used: AxiDraw, Pentel .5mm pen, old Penrose Tiling code I had written previously

Languages Used: Python

Development Time: ~1.5 hours

Drawing Time: ~10 minutes

What's Generative Here: Not a lot, really - the initial configuration was selected by hand. Maybe the subdivision rules can be considered generative. I want to revisit this with a random zoom into the initial configuration (which is what the original code did, but I didn't port the random bits).








Thursday, January 28, 2021

Genuary 2021 Day 28: "Use Sound"


"Clash of the Piano Rolls"

For this one, I took 4 MIDI files and extracted the note_on data using the "mido" Python library. I cut out a random 60 second portion from each, and plotted the notes. I also drew a line for Middle C, to help orient you.

The four songs, in case you can't read piano rolls, are:

  1. "Popcorn" by Hot Butter
  2. "Never Gonna Give You Up" by Rick Astley
  3. "Tarzan Boy" by Baltimora
  4. "Take on Me" by A-ha

I think my parsing of the Rick Astley song must be wrong; it looks like I'm never finding "note off" messages. Damn you, Rick Astley!

I'm curious to play around with making a markov chain out of the percussion tracks. There's a lot of little notes going on down there.





Tools Used: AxiDraw, Pentel .5mm pen, mido Python library

Languages Used: Python

Development Time: ~1 hours

Drawing Time: ~40 minutes

What's Generative Here: I picked a random ordering of 4 songs, and picked a random portion from each, and then drew the song data (in random colors).

 




Wednesday, January 27, 2021

Genuary 2021 Day 27: "Monochrome Gradients without Lines"

"Rubber Duck-lish?"




Monochrome, yes. Gradients, um, ok. Without lines, well...

I interpreted the "without lines" to mean that I shouldn't be drawing straight lines or line segments. So, the only primitives I used was the Circle, which I used to halftone an image. 

I first used my ray marcher to render an image of a donut plus two spheres. I used a Smooth Union operator ("Smooth Union Operator" is the name of my easy listening jazz trio). You can see a falloff - or "gradient" in the shading of the donut and the top sphere, so that's something to work with. The second sphere is below and to the right, mostly hidden. The camera angle and positions of the spheres, as well as rotation of the overall thing is largely procedural, so I didn't have a lot of control over it. There was an initial configuration that looked uncomfortably anatomical, so I adjusted some values to get this configuration.



I then switched over to plotterworld, generated a blue noise point grid (of course), then for each of the points, I sampled the raster image, giving me a gray value from 0 to 1, 0 = black, 1 = white. I wrote a small function that turned a gray value into a list of circle radii that would give me darkness options. I had a list of candidate radii, and I greedily output radii that would approximate the gray fraction. Black = all radii, white = no radii, 40% might be the biggest circle alone, 50% might be the biggest circle and the second smallest circle.

I was somewhat clever this time, and I sorted my points by X, so that I drew from left to right:













Tools Used: My ray marcher, AxiDraw, Pentel .5mm pen

Languages Used: Python

Development Time: ~1 hour

Drawing Time: ~100 minutes

What's Generative Here: The camera position, the orientation, and the basic geometry of the donut + spheres are all randomly generated. I then use a random blue noise pointset to sample that image for halftoning.


Tuesday, January 26, 2021

Genuary 2021 Day 26: 2D Perspective

"Random Regional Racetracks"


 
I wanted to do something quick today, and it didn't turn out quite as quick as I intended. I had wanted to do something based on the loop of a racetrack, and I had some algorithmic ideas in the back of my head that I wanted to try out.

So, first, I generated a Bridson blue noise grid, of course. I used scipy's Voronoi package to generate Voronoi regions for each of the points. I then did some data structure munging to build a way of knowing what regions neighbor each other. 

Then, I used this neighbor table to generate paths of connected regions that looped back to their beginning within a given window of path lengths. Each time I found a loop, I stored that, and made subsequent length windows smaller, until I got an empty path for my query. (Not that I couldn't make more paths, clearly I could, just that particular query started from a point that led to a dead end.)

For each list of regions, I drew a path, using a cubic bezier curve to round out the edges, and letting drawSVG close the curves for me.

Looking at the raster(color) version, they look more like lakes than they look like racetracks - I would like to fatten up the paths, which wouldn't be too hard to do, just offset left and right perpendicular to the track path segments.

Another thing I got around to doing was to add a sorted draw path function to my edgepool class, so the Voronoi grid drew mostly left to right.





Tools Used: AxiDraw, Pentel .5mm pen
Languages Used: Python
Development Time: ~2 hours
Drawing Time: ~5 minutes
What's Generative Here: I start with a random blue noise point set, generate the voronoi graph for those points, and then trace closed paths on those voronoi cells.





Monday, January 25, 2021

Genuary 2021 Day 25 "Make a grid of permutations of something"

"Random Duets of Something"


The prompt says to make a grid of permutations of something. My first thought was to take the word "Something", break it into chunks and shuffle those chunks, like THSOINMEG. Totally would have technically, literally been permutations of something.

Instead, I took the things mentioned in "Something" by the Beatles, and selected random 2-permutations of those, and assigned them to random 2-permutations of the Beatles themselves, and envisioned that as a little duet. 

Also, I drew a couple random flowers (epicycloids + circles for easy botany) to make it more suitable for a Summer Of Love art mood.

I drew the text using the "Futura L" Hershey Font, and my own hershey font renderer. I think the text came out OK - it's totally legible at this size, but I wouldn't want to go much smaller with a 0.5mm pen.




Tools Used: AxiDraw, Pentel .5mm pen
Languages Used: Python
Development Time: ~1 hour
Drawing Time: ~10 minutes
What's Generative Here: The flower positions, rotations, and color were selected randomly. Each duet is a random selection of 2-permutations of phrases from "Something" combined with random 2-permutations of Beatles.








Sunday, January 24, 2021

Genuary 2021 Day 24 "500 Lines"



This one started out as being a simple variant of my "no loops" piece, starting with a Bridson blue noise point set, doing a Delaunay triangulation on that to get a tri mesh, then taking the edges of that mesh to generate 500 polylines.

I then added quadratic Bezier curves to round off the pointy parts of those polylines, and the result is surprisingly pleasing. The raster version has a little bit of a Keith Haring style.

I still ought to make a tool to sort my polylines by x to decrease the amount of time the AxiDraw pen spends moving around when drawing a piece like this.



Tools Used: AxiDraw, Pentel 5mm pen
Languages Used: Python
Development Time: ~2 hours
Drawing Time: ~5 minutes
What's Generative Here: The squiggly lines are generated from edges of a triangular mesh that's generated from a random blue noise point cloud.
 


Saturday, January 23, 2021

Genuary 2021 Day 23: ", no gradient" (variant)


"More Colorful Ripples. Chance of Gradient"




I decided to revisit this one, to try a few things. I kept the basic ideas, but altered the implementation. First up, instead of randomly positioning the color strings, I placed the color strings in an RGB cube, and randomly oriented that cube (generativity!) within the canvas.

I then replaced my square sampling of the SDF field with a triangular mesh, based on a Delaunay triangulation of a Bridson blue noise point set.

This allowed me / required me to rewrite my "Marching Cubes" implementation to be a "Marching Triangles" implementation, except that I guess "Marching Triangles" is the name of a different algorithm. Rewriting my marching squares actually simplified things.

I then did my ripples rendering - first with a version that just colored the segment of the ripple based on the closest color string, which was kind of interesting.

But then I rewrote it to do a weighted sum based on the inverse distance to each of the color strings. Which I think violates the "no gradient" part of the prompt. Bah! I'm not beholden to prompts! They serve me, not the other way around! Also, this is a variant, I feel like I have even more flexibility than the prime work.

I added an "edge pool" class, which collects line segments and accumulates them into long polylines. This made the ripple drawing a lot faster. And it invited me to draw the underlying triangle mesh, which is kind of interesting in the raster picture, but overwhelming when drawn on the AxiDraw.