Onecontinuous big source of inspiration for me has been Processing's Discover Tab, where you can browse sketches from different users and see what they have come up with.
Recently one in particular caught my eye, that allows you to draw brush strokes in a style reminiscent of the far east. The behaviour of these brush strokes felt very fluid and responsive, so I dug little deeper and had a look at the code (you can view the code for every sketch on there!).
This blog post's purpose is to explain and adapt this code for p5js. BUN divides his tutorial into 6 parts, where each part introduces new behaviour and comlexity to the brush stroke. The first part is very simple, we simply draw circles at the cursor's location and go from there:
The second step will actually improve the drawing behaviour significantly by implementing Hooke's Law. Essentially, we're not drawing the ellipses at the exact mouse coordinates anymore, but are applying a modifiers to the coordinates. It will almost feel like the drawn ellipses are dragged behind the mouse cursor, as if they were attached to a spring, and once you stop moving the cursor, it will actually overshoot and then snap back ellastically to a resting position. I'm not a physics whizz, but I will try my best at explaining the concept.
Picture an imaginary spring, where one end is attached to the circle that we're drawing, and the other end is connected to your cursor. This spring has an ellastic behaviour, when you begin dragging one end, meanwhile the other end, to which our circle is attached, will start dragging behind it. The faster you drag one end the more this spring will be stretched out and the harder the snap-back will be when you come to a rest. The trick here is to somehow simulate this behaviour, and it can be done with Hooke's Law.
Where x is the amount of displacement from the beginning of dragging the spring, and k is a parameter characteristic of the spring. We can actually choose this parameter, which simply signifies the elasticity of the spring. In BUN's code, we first calculate the velocity of our cursor while it is moving (velocity being a vector quantity that defines the distance traveled over some period of time).
The amount of displacement is simply the distance between the moment we pressed the mouse to where we moved the mouse, and will be the x parameter in Hooke's formula. For now well simply choose k to be 0.5 for a balanced behaviour (k should be between 0 and 1). One thing to note here, is that we need to calculate the formula for both x and y coordinates, since we're moving on a 2D plane:
This is because we ignored one variable that occurs in the physical world: friction. Friction, like the spring parameter, is a variable between 0 and 1, where values closer to 1 designated less friction and values closer to zero designate more friction. We simply multiply our velocity by this friction parameter. And we will obtain a nice behaviour. The code should look like this now:
In the third part of the tutorial, we begin making some changes to make the drawn circles look more like strokes, rather than just circles. Generally, in chinese, japanese and korean calligraphy, symbols can be drawn in different styles and different speeds, where the speed at which the calligraphy is made is a characteristic feature. When drawn slow, strokes tend to be thicker and heavier; when drawn faster, less pressure is put on the strokes and they tend to be thinner. This is what BUN tries to achieve in this part of the tutorial, drawn circles should be smaller (thinner), in the middle portion of the stroke, the start and end points of the stroke should be thicker, as more pressure is put on the brush.
In part 4, we actually make the stroke look much more like a stroke than just ellipses. It's actually very impressive how stacking these simple rules allow us to simulate such complex behaviour. Here, instead of drawing separate circles, we'll draw lines between the points where we would have drawn circles instead, this can be done by simply adding an intermediary variable that stores the previous circles coordinates.
However you can see that the different line segments that make up the stroke can look a little discontinuous and jagged when drawn at higher speeds. Part 5 tries to ammend this by splitting up the line segments into multiple smaller chunks to smooth these portions.
We could already stop here, but the last part will put a finishing touch on our brush strokes and make them look much more realistic. In part 6 we draw multiple lines offset from each by a tiny amount and make them have variable thickness:
And this is about it! Many thanks to BUN for sharing his code, and I recommend giving him a follow on OpenProcessing or on Twitter to keep up with his creations! There are actually many different algorithms for simulating brush strokes, which I might cover when I learn more about them! Cheers, if you enjoyed this post consider giving me a follow on Twitter and sharing this, until next time!
Implementing real time fluid simulation in pure Javascript was a long desire of me and has been sitting in my todo list from a long time on. However from various reasons i never found enough time to start working on it. I know it's nothing new and there are quite a few implementations done on canvas already (maybe the best known is this: ), but i wanted to do mine without to relay to others work and to use the core logic as found on the original paper.
For me one of the most inspiring real-time 2D fluid simulations based on Navier Stokes equations was that created by Memo Akten, originally implemented in Processing (a Java based programming language, very suitable for digital artists for visual experimentation), lately ported to OpenFrameworks (a C++ framework) then implemented in AS 3.0 by Eugene Zatepyakin =248.
We can think of fluids as a collection of cells or boxes (in 3D), each one interacting with it's neighbor. Each cell has some basic properties like velocity and density and the transformation taking place in one cell is affecting the properties of neighbor cells (all of these happening million times per second).
Being such a complex operation, computers (even the most powerful ones) can not simulate real time the fluid dynamics. For this we need to make some compromise. We'll break the fluid up into a reasonable number of cells and try to do several interactions per second. That's what Jos Stam's approaching is doing great: sacrifice the fluid accuracy for a better visual presentation, more suitable for running real time. The best way to improve the simulation fluency is to reduce the solver iteration number. We'll talk about this a little bit later.
First we are staring by initializing some basic properties like the number of cells, the length of the timestamp, the amount of diffusion and viscosity etc. Also we need a density array and a velocity array and for each of them we set up a temporary array to switch the values between the new and old ones. This way we can store the old values while computing the new ones. Once we are done with the iteration we need to reset all the values to the default ones. For efficiency purposes we'll use single dimensional array over double ones.
Because some of the functions needs to be accessible only from main script i separated the internal (private) functions from the external (public) functions. But because Javascript is a prototypical language i used the object prototype method to extend it. That's why some of the methods are declared with prototype and others as simple functions. However these aren't accessible from the outside of FS namespace, which is declared as a global namespace. For this technique please see this great article Learning JavaScript Design Patterns.
The basic structure of our solver is as follows. We start with some initial state for the velocity and the density and then update its values according to events happening in the environment.In our prototype we let the user apply forces and add density sources with the mouse (or we can extend this to touching devices too). There are many other possibilities this can be extended.
This means the amount of fluid going in has to be exactly equal with the amount of fluid going out, otherwise may happens that the net inflow in some cells to be higher or lower than the net inflow of the neighbor cells. This may cause the simulation to react completely chaotic. This operation runs through all the cells and fixes them up to maintain in equilibrium.
We assume that the fluid is contained in a box with solid walls: no flow should exit the walls. This simply means that the horizontal component of the velocity should be zero on the vertical walls, while the vertical component of the velocity should be zero on the horizontal walls. So in fact every velocity in the layer next to the outer layer is mirrored. The following code checks the boundaries of fluid cells.
We are calling these functions on each frame on the main drawing method. For rendering the cells i used canvas putImageData() method which accepts as parameter an image data, whose values are in fact the fluid solver values obtained from the density array. My first attempt was to generate a particle field, associate to each particle density, velocity etc. (so all the data necessarily making a fluid simulation), connect each particle in a single linked list, knowing this is a much faster method for constructing an array, instead using the traditional one. Then using a fast and efficient method to trace a line, best known as Extremely Fast Line Algorithm or Xiaolin Wu's line algorithm with anti aliasing ( =67) i thought i could gain more FPS for a smoother animation, but turned out being a very buggy implementation, so for the moment i abandoned the idea, but i will revised once i will have more time. Anyway i don't swipe out this code part. You can find some utility functions too for my attempting, like implementing the AS3 Bitmap and BitmapData classes.
3a8082e126