# Add more particle distributions to the spray engine

requested to merge deiflou/krita:deiflou/spray_distribution into master

This MR originates after some discussion on this KA thread.

The MR has two parts:

1. The spray brush seems to not distribute the particles correctly in the 2D space/spray area. To explain the issue clearly, I'll use as an example the simple uniform random distribution. Internally the spray engine distributes the particles using polar coordinates. It generates 2 random values, one for the angle and another for the distance, both relative to the spray area center. The issue is that even if a uniform distribution is used (the `gaussian` option is not checked) the particles appear more concentrated towards the center of the spray area. This is an issue with the radial distribution (the angular one is ok). The reason is that particles are uniformly distributed in the distance from the center, but that means that every circumference at any radius will get roughly the same amount of particles. Since the circumferences at lower radius have smaller perimeter, that means that they will have larger particle density. The probability of a particle ending at a radius `r'` should be the ratio of perimeter at `r'` to the total spray area, which ends up being `2r'`. Taking the cdf of that pdf and applying the inverse transform sampling follows that one should not use uniform values as a base specific distribution (normal, uniform, etc.) but the square root of that. To solve this I had to made some custom distributions, the reason being that the random distributions in std or boost take an engine that produces uniform values as a parameter and then they "shape" that value into the specific distribution, but we need to take that uniform random value and take the square root before it is used by the specific distribution to get the new value so that spatially in 2D the distribution conforms to the specific distribution. An issue is that just changing the way that works would break the old brushed, so I added a "center-biased spread (legacy)" checkbox in the ui to alter which method to use. I don't like having that option but I cannot think of another way of not breaking compatibility with old brushes.

2. The other part just brings some new features. In that KA thread the user wants more control over how the particles are distributed. So I added the following:

• Changed the ui of the spray area panel a bit (move some widgets around mainly)
• Now one can change the distribution of the angle and the distance.
• I implemented 4 different distributions:
• Uniform
• Gaussian - Uses a normal distribution. Now one can change the standard deviation.
• Cluster based - This is like the deviation option in CSP. It uses a normal distribution under the hood
• Curve based - One can make a custom probability density function using the curve widget. It allows to set the number of times the curve should repeat to prevent having a lot of points
• For angular distribution I only expose "uniform" and "curve based", since the others are not periodic and look weird here
• I added `operator()`, `min` and `max` member functions to the random source class so that it can be used as an engine by the random distribution classes.

A (little outdated) video showcasing the features:

## Test Plan

• Play with the new options
• Make sure that one can store/load the options corretly to/from a new brush
• Make sure the old brushes behave as before

## Formalities Checklist

• I confirmed this builds.
• I confirmed Krita ran and the relevant functions work.
• I tested the relevant unit tests and can confirm they are not broken. (If not possible, don't hesitate to ask for help!)
• I made sure my commits build individually and have good descriptions as per KDE guidelines.
• I made sure my code conforms to the standards set in the HACKING file.
• I can confirm the code is licensed and attributed appropriately, and that unattributed code is mine, as per KDE Licensing Policy.
Edited by Deif Lou