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.
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!
No comments:
Post a Comment