Since we can add grains with physical properties, we can start to work on the hard part: particle selection, collision and animation. However, to do that, we need a way to make the objects created in a stroke to be persistent, since after the paintop is done all objects created during the operation are destroyed.
To do that, we had to find a way to hold a collection of particles and then spread it after the pouring. Thus, I'm using Krita's Annotations, a feature that make possible to add aditional information to an image, like a comment, or, in my case, the collection of particles.
With that, we can save the image state after the creation/pouring of the grains and load them in a way that make it possible to start the usual painting operations.
However, since the number of particles grows really fast, the pouring mode of the brush will just add the particles and save them in a collection that will be converted in an annotation. This annotation will be reloaded when the user start the spread mode, where we only modify the particles positions and don't add any new particle on the canvas.
Another feature that we made these days is a private grid partition of the image. This is necessary since the number of particles grows really fast (in a common pouring painting we add from 5000 to 100000 grains), so to speed up particle selection and neighborhood detection, we are using two data structures that holds the particle indices associated with a grid and create a neighborhood relation between them, so we have to retrieve only those inside and near that grid cell.
For instance, given a image, its associated grid is created by taking the canvas width and height and dividing each by the number of rows/columns disired. If the mouse is in the position (gx,gy), we will search the grid index
by taking its position, multiplying by the number of grids and dividing it by the image size:
grid_x_position = number_of_horizontal_grids * gx / image_width
doing the same for the grid vertical position. With that, we initialize a neighborhood relation structure where we will seach for particles to select and do collision, if need to be.
We could search for these particles in the eight neighbors of the cell where the mouse is, but in practice we can reduce it by half, based on commutativity relation between neighbors. Suppose that (gx, gy) are not boundary cells. Them, since a particle i is neighbor of particle j, we only need to search for neighbors in one way only, in our case, the lower-right half of the neighborhood.
|Neighborhood search space restriction.|
Witha that, when the mouse moves, we can apply the forces on each particle based on the mouse speed and acceleration and after that, we animate them to have the dynamic feel. For now, the grid creation is based on the image size, but I'm doing the grid customization widget so the user can indicate the number of grids that suits his/hers needs better.
Along with that, I have more few things to fix yet, but I'm enumerating them and soon we will have more details of the situation. Until next time!