I've got a 3D solid, represented as the union of a set of polyhedral convex hulls. (Or a single convex, if that makes things easier.) I'd like to approximate that solid as the union of a set of spheres, in a way which minimizes both the number of spheres in the set and the error in the approximation. (The latter objective is deliberately vague: any reasonable error metric will do. Likewise, the way in which the objectives are combined is up in the air; either the number of spheres or the error metric could be constrained, or some function of the two could be minimized. I don't want to specify myself into a corner.)
The approximation does not need to entirely contain or be entirely contained by the original set. Each sphere may have an arbitrary radius.
This feels like the sort of problem that's NP-complete, and in any case unlikely to be practical using exact methods, so I'm assuming the solution lies in the realm of stochastic optimization. It feels like some variant of k-means might fit (assigning uncovered locations to their closest spheres, and refining the spheres to cover some of them), but I'm not sure how to handle multiply-covered locations, or how to find the local, not-necessarily-covering-everything optimum even for a single sphere. Also, for iterative methods efficiency is important, and doing 3D boolean operations is not going to be efficient.
The problem is not simple, but has been studied previously. The central concept
is the medial axis, which can be
viewed as a representation of an object by an infinite union of balls.
A key paper addressing your question is:
"The power crust, unions of balls, and the medial axis transform."
Nina Amenta, Sunghee Choi, Ravi Krishna Kolluri. Computational Geometry.
Volume 19, Issues 2–3, July 2001, Pages 127–153. (Journal link.)
(Images source: From Point Clouds to Power Crusts.)
A second paper is
Cazals, Frédéric, et al. "Greedy Geometric Algorithms for Collection of Balls, with Applications to Geometric Approximation and Molecular Coarse‐Graining." Computer Graphics Forum. Vol. 33. No. 6. 2014. (PDF download.)
whose 1st sentence is "Choosing balls which best approximate a 3D object is a non-trivial problem."!
Their primary application is to molecular models, which might be far
from your interests.
Hm, my best idea so far involves support vector machines. Turn your object into a whole bunch of (probably evenly spaced) points within and on the surface of the object. Train an SVDD model using a linear kernel (see libsvm for an SVDD implementation). The decision function of the model then represents an implicit surface defined by the support vectors of the model (and rho). Turning down the cost will get you more support vectors, turning it up gets you fewer.
Unfortunately, the nature of SVMs is such that the area covered by nearby support vectors will, uh, 'blob' together, sort of like this:
(sorry, my intuition for SVMs is entirely geometric/visual.)
Now, you don't have nice crisp spheres, but (massive hand waving!) hopefully the algorithm chose a useful distribution of centers for spheres.
Finally, you can concoct a function that computes error as a function of radii for spheres centers on all those points. Then just feed that into a nonlinear optimizer and tell it to minimize. Bam.
If you're willing to throw more CPU power at it, you could run another layer of error minimization over top of that one, which reruns the entire above process for different support vector costs, attempting to minimize some combination of error and cost. (Perhaps error/cost.)
This is what I came up with. This approach is more of an iterative 3D boolean operation so it might not be what you're looking for. The surface seems more difficult so I concentrated on that.
Overview
Basically add spheres inside the shape in positions that maximize the coverage of the surface. We convert the sphere into a 3D array of signed byte values. These values are points and will be gobbled up with spheres. We add one spheres at a time inside the object and then grow/shrink it in different directions to "eat" as many points as possible. The goal is to rack up as many points as possible per sphere. Points are earned by summing the points in the area of the sphere. With the addition of each sphere we count then count that area as used and set the Array values to 0.
(A) (B) ZZZZZZ (C) ZZZZZZ (D) ZZZZZZ (E) ZZZZZZ (F) ZZZZZZ
/\ ZX33XZ ZX33XZ ZX33XZ ZX33XZ ZX33XZ
/ \ ZX3223XZ ZX3223XZ ZX##23XZ ZX ##XZ ZX XZ
/ \ ZX321123XZ ZX321123XZ ZX####23XZ ZX ####XZ ZX XZ
| | ZX32111123XZ ZX32111123XZ ZX######23XZ ZX ######XZ ZX XZ
| | ZX32111133XZ ZX32111133XZ ZX######23XZ ZX ######XZ ZX XZ
| | ZX32222223XZ ZX##222223XZ ZX3####223XZ ZX3 ####3XZ ZX3 ##XZ
|------| ZX33333333XZ ZX##333333XZ ZX33##3333XZ ZX33 ##33XZ ZX33 ##XZ
X= -1 ZXXXXXXXXXXZ ZXXXXXXXXXXZ ZXXXXXXXXXXZ ZXXXXXXXXXXZ ZXXXXXXXXXXZ
Y= -2 ZZZZZZZZZZZZ ZZZZZZZZZZZZ ZZZZZZZZZZZZ ZZZZZZZZZZZZ ZZZZZZZZZZZZ
(A) The shape that we want to fill. (2D used here but 3D would be similar)
(B) Convert the shape in a 3D grid of points. Surface gets largest number and as you work to the center the numbers settle on low positive numbers(like 1); Outside the shape gets negative numbers; deep interior gets 1
(C) Add a small sphere. (we will grow it)
(D) Expand the sphere until we gobble up the maximum number of points
(E) Add the Next sphere and grow it.
(F) Add another sphere. This one is small.
Process
5) first break down the shape into a 3D block resolution.
10) Then give the most "points" to the blocks around the surface. High points with the block that actually touches the surface and lower points as you move inward or outward. As you go outward the points should quickly become negative as this will prevent protruding spheres. As you move inward from the surface the points should settle at 1 so that the central area would be all ones. These points can be setup in different ways to produce different results.
15) Find a location on the inside edge of the shape that is outside a sphere. While being near the edge is not required it does minimize the search area. If a location cannot be found goto 80. If a location cannot be found that is near
20) Draw a sphere with a zero radius inside the shape that is not covered
25) Move the sphere up/down until the points are maximized (simulated annealing)
26) Move the sphere in/out until the points are maximized
27) Move the sphere left/right until the points are maximized
28) Move the Top of the sphere up/down until the points are maximized (simulated annealing/hill climbing)
29) Move the Bottom the sphere up/down until the points are maximized
30) Move the Left side of the sphere in/out until the points are maximized
31) Move the Right side of the sphere in/out until the points are maximized
32) Move the Front of the sphere in/out until the points are maximized
50) If any changes in 25-32 then repeat (goto 25)
70) Subtract out the points from the last added sphere. Set all values to zero for internal values(positive numbers) and do not adjust external values(negative numbers). Goto 15.
80) (optional) Fill in an internal gaps. Basically visit each element to make sure it is less then or equal to 0. If positive then goto 20 with that element. Else, if none found then goto 85. Note:all outside values should be negative and all internal values that are in a sphere should be 0.
85) Finished
Notes
Since there would a grid of values, a 1000x1000x1000 workspace would consume up 1GB.
Not an exact solution
Could take lots of compute for higher resolutions.
For efficiency, smaller spheres can have their pixel ranges pre-recorded so that the sphere does not need to be calculated for each iteration.
A lower resolution(i.e. 500x500x500) version could be completed first and then the location and size of the spheres could be applied to a larger 1000x1000x1000. Also for very large shapes sub-sections could be solved.
A good start would be to develop an algorithm to:
1) Find the largest radius of an inscribed sphere.
2) Then consider the subtract volume
3) Approximate the subtract volume by a polyhedral.
4) Subdivide that polyhedral into smaller (finer) polyhedrals.
5) Redo step 1.
There might be some variations, but it seems to answer your question. As you can see, the error function decreases as the number of spheres increases, so you can't minimize both: that is a trade-off. But you can fix one and try to optimize another, e.g. fix the error function to be an acceptable tolerance, and minimize the number of spheres to do it, or vice versa.
Related
I'm looking for an algorithm for random close packing of spheres in 3D. The trick is that I'd like to pack spheres around a certain number of existing spheres. So for example, given somewhere between 100 and 1000 spheres in 3D (which have fixed positions and sizes; they may overlap, and may be different sizes), I'd like to pack spheres (all same size, positions can be chosen freely) around them (with no overlaps).
The metric for a good quality of packing is the packing density or void fraction. Essentially I'd like the fixed spheres and the packed spheres to occupy a compact volume of space (eg roughly ~spherical, or packed in layers around the fixed spheres) with as few voids in it as possible.
Is there an off the shelf algorithm that does this? How would you approach it in a way that balances speed of calculation with packing quality?
UPDATE Detail on packing density: this depends on what volume is chosen for the calculation. For this, we're looking to pack a certain number of layers of spheres around the fixed ones. Form a surface of points which are exactly a distance d to the surface of the closest fixed sphere; the packing density should be calculated within the volume enclosed by that surface. It's convenient if d = some multiple of the size of the packed spheres. (Assume we can place at least as many free spheres as needed to fill that volume; there may be excess ones, it doesn't matter where they're placed)
The fixed and all the variable spheres are all pretty similar sizes (let's say within 2x range from smallest to largest). In practice the degree of overlap of the fixed spheres is also limited: no fixed sphere is closer than a certain distance (around 0.2-0.3 diameters) of any other fixed sphere (so it is guaranteed that they are spread out, and/or only overlap a few neighbors rather than all overlapping each other)
Bounty posted!
Use a lattice where each point is equidistant by the diameter of the fill sphere. Any lattice shape, meeting the above definition will suffice.
Orient the translation and rotation of the lattice to minimize the center offsets of the fixed spheres to produce the world transform.
Fixed Pass 1:
Create a list of any lattice points within the fixed spheres radii + the diameter of the fill spheres.
For the latter keep the positional (origin - point) difference vectors in a list.
Flag in the lattice points(removal) in the list.
Lattice Pass 1:
Combine,i.e. re-base origin to overlap point(either a true overlap or extended to fill radius), any overlapping Fixed sphere's distance vectors. Storing the value on one side and flagging it on the other, to permit multiple overlaps.
This is where a decision is needed:
Optimize space over time, Computationally slow:
Add points from the adjusted origin radius + fill radius. Then iterating over lattice points moving one point at a time away from other points until all spacing conditions are met. If the lattice points implement spring logic, an optimal solution is produced, given enough iterations(N^2+ N). ?? Stop Here.... Done.
Pull the remaining points in lattice to fill the void:
Warp the lattice near(The size is as large as needed) each overlap point or origin, if no overlap exists pulling the points, to fill the gap.
Lattice Pass 2:
Add missing, i.e. no other point within fill radius + 1, and not near fixed sphere (other radius + fill radius) flagged points as removed. This should be a small amount of points.
Lattice Pass 3:
Adjust all lattice positions to move closer to the proper grid spacing. This will be monotonically decreasing the distances between points, limited to >= radius1 + radius2.
Repeat 3-4(or more) times. Applying a tiny amount of randomness(1 to -1 pixel max per dimension) bias offset to the first pass of the created points to avoid any equal spacing conflicts after the warp. If no suitable gap is created the solution may settle to a poorly optimized solution.
Each fill sphere is centered on a lattice grid point.
I can see many areas of improvement and optimization, but the point was to provide a clear somewhat fast algorithm that is good, but not guaranteed optimal.
Note the difference between 1 and 2:
Number 1 creates a sphere colliding with other spheres and requires all fills to move multiple times to resolve the collisions.
Number 2 only creates new spheres in empty spaces, and moves the rest inward to adapt, resulting in much faster convergence, since there are no collisions to resolve.
here is a problem that will turn your brain inside out, I'm trying to deal with it for a quite some time already.
Suppose you have sphere located in the origin of a 3d space. The sphere is segmented into a grid of equidistant points. The procedure that forms grid isn't that important but what seems simple to me is to use regular 3d computer graphics sphere generation procedure (The algorithm that forms the sphere described in the picture below)
Now, after I have such sphere (i.e. icosahedron of some degree) I need a computationally trivial procedure that will be capable to snap (an angle) of a random unit vector to it's closest icosahedron edge points. Also it is acceptable if the vector will be snapped to a center point of triangle that the vector is intersecting.
I would like to emphasise that it is important that the procedure should be computationally trivial. This means that procedures that actually create a sphere in memory and then involve a search among every triangle in sphere is not a good idea because such search will require access to global heap and ram which is slow because I need to perform this procedure millions of times on a low end mobile hardware.
The procedure should yield it's result through a set of mathematical equations based only on two values, the vector and degree of icosahedron (i.e. sphere)
Any thoughts? Thank you in advance!
============
Edit
One afterthought that just came to my mind, it seems that within diagram below step 3 (i.e. Project each new vertex to the unit sphere) is not important at all, because after bisection, projection of every vertex to a sphere would preserve all angular characteristics of a bisected shape that we are trying to snap to. So the task simplifies to identifying a bisected sub triangle coordinates that are penetrated by vector.
Make a table with 20 entries of top-level icosahedron faces coordinates - for example, build them from wiki coordinate set)
The vertices of an icosahedron centered at the origin with an
edge-length of 2 and a circumscribed sphere radius of 2 sin (2π/5) are
described by circular permutations of:
V[] = (0, ±1, ±ϕ)
where ϕ = (1 + √5)/2
is the golden ratio (also written τ).
and calculate corresponding central vectors C[] (sum of three vectors for vertices of every face).
Find the closest central vector using maximum of dot product (DP) of your vector P and all C[]. Perhaps, it is possible to reduce number of checks accounting for P components (for example if dot product of P and some V[i] is negative, there is no sense to consider faces being neighbors of V[i]). Don't sure that this elimination takes less time than direct full comparison of DP's with centers.
When big triangle face is determined, project P onto the plane of that face and get coordinates of P' in u-v (decompose AP' by AB and AC, where A,B,C are face vertices).
Multiply u,v by 2^N (degree of subdivision).
u' = u * 2^N
v' = v * 2^N
iu = Floor(u')
iv = Floor(v')
fu = Frac(u')
fv = Frac(v')
Integer part of u' is "row" of small triangle, integer part of v' is "column". Fractional parts are trilinear coordinates inside small triangle face, so we can choose the smallest value of fu, fv, 1-fu-fv to get the closest vertice. Calculate this closest vertex and normalize vector if needed.
It's not equidistant, you can see if you study this version:
It's a problem of geodesic dome frequency and some people have spent time researching all known methods to do that geometry: http://geo-dome.co.uk/article.asp?uname=domefreq, see that guy is a self labelled geodesizer :)
One page told me that the progression goes like this: 2 + 10·4N (12,42,162...)
You can simplify it down to a simple flat fractal triangle, where every triangle devides into 4 smaller triangles, and every time the subdivision is rotated 12 times around a sphere.
Logically, it is only one triangle rotated 12 times, and if you solve the code on that side, then you have the lowest computation version of the geodesic spheres.
If you don't want to keep the 12 sides as a series of arrays, and you want a lower memory version, then you can read about midpoint subdivision code, there's a lot of versions of midpoint subdivision.
I may have completely missed something. just that there isn't a true equidistant geodesic dome, because a triangle doesn't map to a sphere, only for icos.
I've got some point clouds – i.e. collection of 3d coordinates – whose underlying structures that I'd like to reconstruct, even just approximately, are 2D surfaces in the 3D space with either 0 (fig B), 1 (fig A) or 2 holes, or the disjoint union of two or more such structures (fig C).
Each surface is not a closed boundary to a 3D domain (my structures are 2 dimensional). Mathematically speaking, they are genus 0 2-manifolds with 1,2 or 3 boundary components or a disjoint union of such structures.
↑ "out" should be actually made of triangles; actual point clouds are not so obviously coming from spheres
[ As an example, think of the set of points defined as "sampled points on the surface of a mountain range whose distance from a air balloon is between 100 and 150 m". These points represent a surface that can either have 0 holes (the top of the mountain), 1 hole (the flank), 2 holes (a valley which is a basis for 2 mountains) ]
My expected output is a set of edges and triangular faces connecting the existing vertices. I'll use it for rough surface area estimates and for a graphic representation of my dataset. I don't need the resulting surface to be perfect: even topological artifacts (holes that shouldn't be there) can be acceptable if they are limited in number.
My point cloud has some features:
the density of point is somewhat constant, and if a set is made of non connected surfaces I should be able to tell them apart using a threshold
sometimes a "strip" of surface can narrow down to a single file of points, or even skip some points (see example C, left component)
I know there cannot be an exact reconstruction algorithm, but maybe there are canonical ways to minimise the total length of the edges, or the total surface area, or the crease angle between neighbouring faces: I'm not sure as I'm not from the computational geometry field.
If that helps, I'll be implementing this in Java, but it's the algorithm I'm interested in. I don't mind if the algorithm relies on parameters as long as it doesn't make assumptions that don't hold in my case.
Thanks!
When density is close to constant, i.e. space between points is similar, two methods are useful for triangulations: "Marching Cubes" and "Ball Pivot"
Imagine an enormous 3D grid (procedurally defined, and potentially infinite; at the very least, 10^6 coordinates per side). At each grid coordinate, there's a primitive (e.g., a sphere, a box, or some other simple, easily mathematically defined function).
I need an algorithm to intersect a ray, with origin outside the grid and direction entering it, against the grid's elements. I.e., the ray might travel halfway through this huge grid, and then hit a primitive. Because of the scope of the grid, an iterative method [EDIT: (such as ray marching) ]is unacceptably slow. What I need is some closed-form [EDIT: constant time ]solution for finding the primitive hit.
One possible approach I've thought of is to determine the amount the ray would converge each time step toward the primitives on each of the eight coordinates surrounding a grid cell in some modular arithmetic space in each of x, y, and z, then divide by the ray's direction and take the smallest distance. I have no evidence other than intuition to think this might work, and Google is unhelpful; "intersecting a grid" means intersecting the grid's faces.
Notes:
I really only care about the surface normal of the primitive (I could easily find that given a distance to intersection, but I don't care about the distance per se).
The type of primitive intersected isn't important at this point. Ideally, it would be a box. Second choice, sphere. However, I'm assuming that whatever algorithm is used might be generalizable to other primitives, and if worst comes to worst, it doesn't really matter for this application anyway.
Here's another idea:
The ray can only hit a primitive when all of the x, y and z coordinates are close to integer values.
If we consider the parametric equation for the ray, where a point on the line is given by
p=p0 + t * v
where p0 is the starting point and v is the ray's direction vector, we can plot the distance from the ray to an integer value on each axis as a function of t. e.g.:
dx = abs( ( p0.x + t * v.x + 0.5 ) % 1 - 0.5 )
This will yield three sawtooth plots whose periods depend on the components of the direction vector (e.g. if the direction vector is (1, 0, 0), the x-plot will vary linearly between 0 and 0.5, with a period of 1, while the other plots will remain constant at whatever p0 is.
You need to find the first value of t for which all three plots are below some threshold level, determined by the size of your primitives. You can thus vastly reduce the number of t values to be checked by considering the plot with the longest (non-infinite) period first, before checking the higher-frequency plots.
I can't shake the feeling that it may be possible to compute the correct value of t based on the periods of the three plots, but I can't come up with anything that isn't scuppered by the starting position not being the origin, and the threshold value not being zero. :-/
Basically, what you'll need to do is to express the line in the form of a function. From there, you will just mathematically have to calculate if the ray intersects with each object, as and then if it does make sure you get the one it collides with closest to the source.
This isn't fast, so you will have to do a lot of optimization here. The most obvious thing is to use bounding boxes instead of the actual shapes. From there, you can do things like use Octrees or BSTs (Binary Space Partitioning).
Well, anyway, there might be something I am overlooking that becomes possible through the extra limitations you have to your system, but that is how I had to make a ray tracer for a course.
You state in the question that an iterative solution is unacceptably slow - I assume you mean iterative in the sense of testing every object in the grid against the line.
Iterate instead over the grid cubes that the line intersects, and for each cube test the 8 objects that the cube intersects. Look to Bresenham's line drawing algorithm for how to find which cubes the line intersects.
Note that Bresenham's will not return absolutely every cube that the ray intersects, but for finding which primitives to test I'm fairly sure that it'll be good enough.
It also has the nice properties:
Extremely simple - this will be handy if you're running it on the GPU
Returns results iteratively along the ray, so you can stop as soon as you find a hit.
Try this approach:
Determine the function of the ray;
Say the grid is divided in different planes in z axis, the ray will intersect with each 'z plane' (the plane where the grid nodes at the same height lie in), and you can easily compute the coordinate (x, y, z) of the intersect points from the ray function;
Swipe z planes, you can easily determine which intersect points lie in a cubic or a sphere;
But the ray may intersects with the cubics/spheres between the z planes, so you need to repeat the 1-3 steps in x, y axises. This will ensure no intersection is left off.
Throw out the repeated cubics/spheres found from x,y,z directions searches.
I've asked some questions here and seen this geometric shape mentioned a few times among other geodesic shapes, but I'm curious how exactly would I generate one about a point xyz?
There's a tutorial here.
The essential idea is to start with an icosahedron (which has 20 triangular faces) and to repeatedly subdivide each triangular face into smaller triangles. At each stage, each new point is shifted radially so it is the correct distance from the centre.
The number of stages will determine how many triangles are generated and hence how close the resulting mesh will be to a sphere.
Here is one reference that I've used for subdivided icosahedrons, based on the OpenGL Red Book. The BSD-licensed source code to my iPhone application Molecules contains code for generating simple icosahedrons and loading them into a vertex buffer object for OpenGL ES. I haven't yet incorporated subdivision to improve the quality of the rendering, but it's in my plans.
To tesselate a sphere, most people sub-divide the points linearly, but that does not produce a rounded shape.
For a rounded tesselation, rotate the two points through a series of rotations.
Rotate the second point around z (by the z angle of point 1) to 0
Rotate the second point around y (by the y angle of point 1) to 0 (this logically puts point 1 at the north pole).
Rotate the second point around z to 0 (this logically puts point 1 on the x/y plane, which now becomes a unit circle).
Find the half-angle, compute x and y for the new 3rd point, point 3.
Perform the counter-rotations in the reverse order for steps 3), 2) and 1) to position the 3rd point to its destination.
There are also some mathematical considerations for values near each of the near-0 locations, such as the north and south pole, and the right-most and left-most, and fore-most and aft-most positions, so check those first and perform an additional rotation by pi/4 (45 degrees) if they're at those locations. This prevents floating point math libraries from freaking out and producing wildly out-of-character values for atan2() and other trig functions.
Hope this helps! :-)