Really nice notebook! 👏
Regarding the question of opacity, I've wanted so badly to clean up my own answer to the question, so I made a notebook that draws a 2D point cloud and attempts to cleanly answer the question of how to compute opacity. I'm not certain it's the best/only way, but I'm content, to the extent that if you uniformly distribute points and set the nominal "opacity" slider to 1.0, it ends up exactly 100% filled!
https://observablehq.com/@rreusser/selecting-the-right-opacity-for-2d-point-clouds
(Edit: well, due to the antialiasing issue, it's tough to get it exactly, precisely filled by points unless you tweak the numbers just right, but I did spend some time and try to ensure I actually finally got it right. 😬)
This is fantastic, thanks so much for sharing! (And for all your other regl notebooks which I've learned so much from.) Can't wait to figure out how to build it in here.
Besides point size and alpha, in the larger versions of this--say, one point for each of 300M people--I tend to need some sliding scale where only the first few hundred thousand points render. In general that makes things yet more complicated; but on the antialiasing front it might actually make things a little simpler, because you can plot straight to uint8 colors with alpha clamped to a minimum of 1/255, but with an appropriate percentage of the points dropped altogether based on the target alpha.
All right, I've wired in the basic logic without worrying about the antialiasing issues yet. It's very nice that the only parameter you need is the desired total saturation, and you can then walk anything back from the number of points and arbitrary point size functions.
It's especially nice for the dot-density case because you can clearly see that changing from representing one person per point to representing 10 people per point doesn't change the appearance of the map at all!
Awesome! This is great. As for antialiasing, I think there are strictly four components: the point size function, the opacity function, increasing the point size by 0.5 in the vertex shader (to capture the + side of +/- 0.5 px), and then applying the SDF-style opacity in the fragment shader.
hi ben, thanks so much for this code. i notice that this isn't quite going to produce the fully random points in the polygon because you are are evenly spreading the points across the triangles. for complete randomness, i think you should do something like
for each point to generate:
1. randomly choose a triangle based, weighted by the triangles area
2. then randomly generate a point within that triangle.
It's not exactly spread evenly across tiles: the `share` variable looks at area and says 'this triangle is 15% the area of the enclosing polygons', and then I generate a fixed number of random points inside that triangle. (So if there were supposed to be 100 points in the overall polygon, 15 in this triangle). It's true that's not precisely random, because my code will be slightly more evenly demonstrated over triangles than a true random run--if you run it 50 times, there will always be exactly 15 points in that triangle. Probably the perfect way would be to generate a target number using a random number and the binomial distribution.
let how_many_points_do_i_get = randround(number_neededs[f_num] * share)