BIT-101
Bill Gates touched my MacBook Pro
Points are a core data type in the wire library. Every shape has a list of 3d points, which generally make up paths by joining up pairs of points in segments.
But rendering points all by themselves is also an interesting technique. All you have to do is call shape.RenderPoints(radius)
. This loops through the point array, projecting each 3d point to a 2d point and drawing a circle of the given radius at that location. Of course, that radius is also scaled via perspective, so the final rendered radius will vary depending on the point’s z value. Here’s an example. I can create a sphere and draw all its segments by calling sphere.Stroke()
:
With the same sphere, I can just call RenderPoints
instead and get this:
Here, I put a heavy blur on to make the points show up a bit better.
You can even do strokes and points:
You can also just create an empty shape and add points to it:
shape := wire.NewShape()
for range 5000 {
shape.AddXYZ(
random.FloatRange(-300, 300),
random.FloatRange(-300, 300),
random.FloatRange(-300, 300),
)
}
In fact, there’s a method to do that even more easily:
shape := wire.NewShape()
for range 5000 {
shape.AddRandomPointInBox(600, 600, 600)
}
But why stop with boxes? There’s a similar method for creating random points in a sphere. But I went one further than that and just made a method that lets you say how many random points you want in that sphere and it does it all for you. Just specify the radius of the sphere and the number of points.
shape := wire.RandomInnerSphere(400, 5000)
This leads us in an obvious direction - what about random points ON a sphere? Got you covered:
shape := wire.RandomSurfaceSphere(400, 5000)
I want to take a quick break and look at some of this sphere code because it’s super useful even if you don’t use this library. How do you create random points on the surface of a sphere? It’s a harder question than it seems at first glance. A naive method would be generate a point at an x, y, z of radius, 0, 0
, then rotate it around the y-axis at a random angle between 0 and 2 * PI. Then rotate it around the z-axis at a random angle from -PI to +PI. Or something like that. But that will leave a concentration of points at the poles, rather than having them evenly distributed. Other methods will leave other artifacts.
I’ve done this a number of times and always spend too much time looking it up. So I made sure I bookmarked a good reference for it this time.
https://mathworld.wolfram.com/SpherePointPicking.html
And here’s the actual code that does it in the lib (yes, some room for optimization here, now that I look at it!):
func RandomPointOnSphere(radius float64) *Point {
u := random.FloatRange(-1, 1)
t := random.Angle() // returns a number from 0 to 2*PI
x := math.Sqrt(1-u*u) * math.Cos(t)
y := math.Sqrt(1-u*u) * math.Sin(t)
z := u
return NewPoint(x*radius, y*radius, z*radius)
}
Getting a random point INSIDE a sphere is another interesting problem. You might try multiplying the radius
value by a random value from 0 to 1. But if you do that, here’s what you get:
While the points ARE evenly distributed along the radius, they wind up being more cramped near the center of the sphere, and more sparse near the outer edge. It’s a neat effect in itself, but if you want an even distribution, you have a problem. The solution is to multiply radius
by the square cube root of a random value from 0 to 1. (See note at end of post.) This will have the effect of adding a bias to larger radii, evening out the visual distribution.
func RandomPointInSphere(radius float64) *Point {
u := random.FloatRange(-1, 1)
t := random.Angle()
x := math.Sqrt(1-u*u) * math.Cos(t)
y := math.Sqrt(1-u*u) * math.Sin(t)
z := u
radius = math.Pow(random.Float(), 1.0/3.0) * radius
return NewPoint(x*radius, y*radius, z*radius)
}
One more note about that problem of clustering points around the poles of a sphere, I actually run into the same thing rendering a non-random sphere. Look at the first couple of animations at the top of this post and you’ll see what I mean. The rings at the two poles have the same number of points in them as the points on the equator, so the poles render a bit more bright than the rest of the sphere. I could use the same trick I used here for random points, but after actually trying it, I found that it looks worse. It feels like the poles are artificially sparse. But the bright poles look kind of normal. To me anyway. So I’m keeping it.
There are other random point functions for creating random points on or in a cylinder or a torus. And I’ll probably continue to add other similar functions for other random point placements.
The code for these is interesting too, but I’ll leave it to you to examine the code if you so desire.
There’s one more challenge on some of these that I’ll be tackling soon. It has to do with points on various surfaces. You might notice that on the cylinder example, there are no points on the caps of the cylinder, just on the body. Putting points on the caps is no problem, but how many points to put there? Basically I’ll have to figure out the surface area of the body of the cylinder, and then the surface area of each cap itself, then distribute the number of points accordingly. If each cap contributes 10% of the surface of the cylinder, then each one should get 10% of the points, and the body gets 80%.
There will be a similar challenge for a box. The left and right sides will get a certain percentage each, top and bottom another percentage, and front and back another. Not rocket science, but I haven’t gotten around to coding these cases up yet.
A cone presents another set of challenges I haven’t even started to think about yet. But it should be interesting.
Well that’s a bit about rendering 3d points. Hopefully some interesting stuff even if you don’t use the library.
Earlier I said that to correctly distribute random points within a sphere, you should take the square root of a random value between 0 and 1, and multiply that by the radius.
Thankfully, I was corrected on this point. You use square root for 2d. For a sphere, you’ll want to use the cube root. I fixed the text and code and image above. But it’s worth looking at the different methods and what they create.
Multiplying by a random number:
Multiplying by the square root of a random number:
Multiplying by the cube root of a random number:
Obviously the last one is “correct” as far as making an even distribution.
In fact, here’s an animation moving between a single random number and the 16th root of a random number, which effectively hollows out the sphere. Mmmm… math!
Comments? Best way to shout at me is on Mastodon