discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Voronoi Generator

N
NateTG
Tue, Apr 13, 2021 5:33 PM

Kenneth Sloan wrote

This is not correct for a “real” VD, but is a useful idea in many
practical
applications.  Often, the “sites” (the vertices of the DT) are samples
taken from a finite window.  In these cases, the VD cells at the edges are
“wrong”, because the sites which should have defined them are outside the
sample window.

...

Note that even closed Voronoi cells mat be “wrong”. Simply excluding
points
at infinity is not good enough!

It seems like this is creeping a little from generating Delaunay
triangulations and Voronoi tilings to dealing with applications.  If I
understand the description correctly, it seems like eliminating Delaunay
triangles which aren't contained by the sample window first and then only
using closed Voronoi cells should work most of the time.

This part of an earlier comment makes me think I'm missing something:

Kenneth Sloan wrote

If all you have is a collection of triangles, computing the Voronoi Point
corresponding to each triangle is easy - but finding the connections
between the points (that is, the triangles which share edges with each
other) can involve considerable search overhead.

It's not free, but if there's a list of N triangles with consistent winding
order, then we can make a sorted list of the 3N half-edges as a lookup table
in O(N log N) time.  Walking the perimeters of the Voronoi cells hits each
triangle 3 times, and each of the table look ups takes O(log N) time, so the
walking is O(N log N) too.

--
Sent from: http://forum.openscad.org/

Kenneth Sloan wrote > This is not correct for a “real” VD, but is a useful idea in many > practical > applications. Often, the “sites” (the vertices of the DT) are samples > taken from a finite window. In these cases, the VD cells at the edges are > “wrong”, because the sites which should have defined them are outside the > sample window. > > ... > > Note that even closed Voronoi cells mat be “wrong”. Simply excluding > points > at infinity is not good enough! It seems like this is creeping a little from generating Delaunay triangulations and Voronoi tilings to dealing with applications. If I understand the description correctly, it seems like eliminating Delaunay triangles which aren't contained by the sample window first and then only using closed Voronoi cells should work most of the time. This part of an earlier comment makes me think I'm missing something: Kenneth Sloan wrote > If all you have is a collection of triangles, computing the Voronoi Point > corresponding to each triangle is easy - but finding the connections > between the points (that is, the triangles which share edges with each > other) can involve considerable search overhead. It's not free, but if there's a list of N triangles with consistent winding order, then we can make a sorted list of the 3N half-edges as a lookup table in O(N log N) time. Walking the perimeters of the Voronoi cells hits each triangle 3 times, and each of the table look ups takes O(log N) time, so the walking is O(N log N) too. -- Sent from: http://forum.openscad.org/
KS
Kenneth Sloan
Tue, Apr 13, 2021 7:18 PM

By definition, all DT are contained in the "sampling window" (by which, I mean the window used to limit the original sites).

Given just the DT, it might be safe to:

a) find (and remove) all triangles which involve a site on the convex hull
b) eliminate all sites involved in those triangles
c) remove all triangles involving those sites
d) (perhaps) repeat b) and c) as needed for extra safety
e) generate Voronoi cells for the remaining  sites

I would still be on the lookout for "long, skinny" Voronoi cells.

For your proposed search - how would you sort the half edges?  It's a whole lot easier if your DT  code generates a data structure where:

a) points are linked (both ways) to edges
b) edges are linked (both ways) to triangles

In that case, every Voronoi cell corresponds to a point (site).  The point links directly to all of its incident edges.  Each edge links to its (1 or 2) triangles.

If you augment the data structure to include the coordinates of the circumcenter of the triangle, then you are home free.

So:

Triangle = 3 edges + 1 (circumcenter)
Edge = 2 points +  1 or 2 triangles  [if you prefer, a half-edge = 1 point + 0 or 1 triangle(s)]
Point = coordinates + list of edges

Now, to generate a Voronoi cell associated with a point:

for each Edge in the list of Edges associated with this Point
add a line segment from Triangle 1's circumcenter to Triangle 2's circumcenter
# special case when only 1 Triangle

and, to generate a full Voronoi Diagram (just the edges)

for each Edge in a master list of all Edges
add a line segment from Triangle 1's circumcenter to Triangle 2's circumcenter
# special case when only 1 Triangle

No search at all (you did all the work when creating the data structure above).

Of course, if you want to ANALYZE the Voronoi Cells, you might want:

Cell = site + list of Voronoi edges
Voronoi Edge = 2 Voronoi vertices
Voronoi Vertex = coordinates (optionally + list of all Voronoi Edges sharing this Voronoi Vertex).

If you want full generality, you can do all of this with one data structure that represents both the DT and the VD and simply requires different interpretation.

For example:

HalfEdge = 1 site + 1 DT + link to next HalfEdge surrounding this point + link to matching HalfEdge
DT = 3 HalfEdges + circumcenter

Generalizing to non-triangular "Delaunay Triangles" is left as an exerise (but not really recommended).

Note that you can also bundle HalfEdges into lists (instead of pairs) if you want to support non-manifold surfaces.

All of the above is from ancient memory - I apologize for any errors.  It's been more than a decade since I last actually wrote code for this (and the code has been working since then with no issues).
Alas, the last collection of code was in Java, which  may not help anyone writing in OpenSCAD.

Here's a recent example:

--
Kenneth Sloan
KennethRSloan@gmail.com
Vision is the art of seeing what is invisible to others.

On Apr 13, 2021, at 12:33, NateTG nate-openscadforum@pedantic.org wrote:

Kenneth Sloan wrote
This is not correct for a “real” VD, but is a useful idea in many practical
applications.  Often, the “sites” (the vertices of the DT) are samples
taken from a finite window.  In these cases, the VD cells at the edges are
“wrong”, because the sites which should have defined them are outside the
sample window.

...

Note that even closed Voronoi cells mat be “wrong”. Simply excluding points
at infinity is not good enough!
It seems like this is creeping a little from generating Delaunay triangulations and Voronoi tilings to dealing with applications.  If I understand the description correctly, it seems like eliminating Delaunay triangles which aren't contained by the sample window first and then only using closed Voronoi cells should work most of the time.

This part of an earlier comment makes me think I'm missing something:

Kenneth Sloan wrote
If all you have is a collection of triangles, computing the Voronoi Point corresponding to each triangle is easy - but finding the connections between the points (that is, the triangles which share edges with each other) can involve considerable search overhead.
It's not free, but if there's a list of N triangles with consistent winding order, then we can make a sorted list of the 3N half-edges as a lookup table in O(N log N) time.  Walking the perimeters of the Voronoi cells hits each triangle 3 times, and each of the table look ups takes O(log N) time, so the walking is O(N log N) too.

Sent from the OpenSCAD mailing list archive http://forum.openscad.org/ at Nabble.com.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

By definition, all DT are contained in the "sampling window" (by which, I mean the window used to limit the original sites). Given just the DT, it might be safe to: a) find (and remove) all triangles which involve a site on the convex hull b) eliminate all sites involved in those triangles c) remove all triangles involving those sites d) (perhaps) repeat b) and c) as needed for extra safety e) generate Voronoi cells for the remaining sites I would still be on the lookout for "long, skinny" Voronoi cells. For your proposed search - how would you sort the half edges? It's a whole lot easier if your DT code generates a data structure where: a) points are linked (both ways) to edges b) edges are linked (both ways) to triangles In that case, every Voronoi cell corresponds to a point (site). The point links directly to all of its incident edges. Each edge links to its (1 or 2) triangles. If you augment the data structure to include the coordinates of the circumcenter of the triangle, then you are home free. So: Triangle = 3 edges + 1 (circumcenter) Edge = 2 points + 1 or 2 triangles [if you prefer, a half-edge = 1 point + 0 or 1 triangle(s)] Point = coordinates + list of edges Now, to generate a Voronoi cell associated with a point: for each Edge in the list of Edges associated with this Point add a line segment from Triangle 1's circumcenter to Triangle 2's circumcenter # special case when only 1 Triangle and, to generate a full Voronoi Diagram (just the edges) for each Edge in a master list of all Edges add a line segment from Triangle 1's circumcenter to Triangle 2's circumcenter # special case when only 1 Triangle No search at all (you did all the work when creating the data structure above). Of course, if you want to ANALYZE the Voronoi Cells, you might want: Cell = site + list of Voronoi edges Voronoi Edge = 2 Voronoi vertices Voronoi Vertex = coordinates (optionally + list of all Voronoi Edges sharing this Voronoi Vertex). If you want full generality, you can do all of this with one data structure that represents both the DT and the VD and simply requires different interpretation. For example: HalfEdge = 1 site + 1 DT + link to next HalfEdge surrounding this point + link to matching HalfEdge DT = 3 HalfEdges + circumcenter Generalizing to non-triangular "Delaunay Triangles" is left as an exerise (but not really recommended). Note that you can also bundle HalfEdges into lists (instead of pairs) if you want to support non-manifold surfaces. All of the above is from ancient memory - I apologize for any errors. It's been more than a decade since I last actually wrote code for this (and the code has been working since then with no issues). Alas, the last collection of code was in Java, which may not help anyone writing in OpenSCAD. Here's a recent example: -- Kenneth Sloan KennethRSloan@gmail.com Vision is the art of seeing what is invisible to others. > On Apr 13, 2021, at 12:33, NateTG <nate-openscadforum@pedantic.org> wrote: > > Kenneth Sloan wrote > This is not correct for a “real” VD, but is a useful idea in many practical > applications. Often, the “sites” (the vertices of the DT) are samples > taken from a finite window. In these cases, the VD cells at the edges are > “wrong”, because the sites which should have defined them are outside the > sample window. > > ... > > Note that even closed Voronoi cells mat be “wrong”. Simply excluding points > at infinity is not good enough! > It seems like this is creeping a little from generating Delaunay triangulations and Voronoi tilings to dealing with applications. If I understand the description correctly, it seems like eliminating Delaunay triangles which aren't contained by the sample window first and then only using closed Voronoi cells should work most of the time. > > This part of an earlier comment makes me think I'm missing something: > > Kenneth Sloan wrote > If all you have is a collection of triangles, computing the Voronoi Point corresponding to each triangle is easy - but finding the connections between the points (that is, the triangles which share edges with each other) can involve considerable search overhead. > It's not free, but if there's a list of N triangles with consistent winding order, then we can make a sorted list of the 3N half-edges as a lookup table in O(N log N) time. Walking the perimeters of the Voronoi cells hits each triangle 3 times, and each of the table look ups takes O(log N) time, so the walking is O(N log N) too. > > > > > Sent from the OpenSCAD mailing list archive <http://forum.openscad.org/> at Nabble.com. > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org
RP
Ronaldo Persiano
Tue, Apr 13, 2021 7:41 PM

I have a data structure built from a vertices-and-faces description (VNF)
that ensures that all local adjacencies can be found in constant time.
It is constructed in O(n log n) (involves sorting and local searches) and
the adjacency access functions are inspired by the quad-edge structure
without the dual access functions. It requires a VNF representing a
topological 2-manifold without border. So to compute the VD from the DT I
needed to add a "border face", a face with a consistent widing representing
the outside world of the DT. The resulting VNF of the DT is then a
topological 2-manifold without border and easily dualizable (although a
strict dual would have a vertex associated to the border face). To cope
with the infinity edges of the VD far away vertices are added to the list
of circumcenters. In order to have closed VD cells, the far away vertices
are linked with additional edges and thus the VD itself may be represented
as a topological 2-manifold without border. The following picture shows the
results with the extra vertices of the VD closer enough to the point mass
for sake of illustration.

[image: Delaunay&Voronoi.PNG]

That data structure also makes it possible to implement the so-called
subdivision methods (like Catmull-Clark, Doo-Sabin and Loop's algorithm)
for VNFs without border with a worst case order of O(n log n). Its space
requirements are O(n).

It seems like this is creeping a little from generating Delaunay
triangulations and Voronoi tilings to dealing with applications.  If I
understand the description correctly, it seems like eliminating Delaunay
triangles which aren't contained by the sample window first and then only
using closed Voronoi cells should work most of the time.

This part of an earlier comment makes me think I'm missing something:

Kenneth Sloan wrote
If all you have is a collection of triangles, computing the Voronoi Point
corresponding to each triangle is easy - but finding the connections
between the points (that is, the triangles which share edges with each
other) can involve considerable search overhead.

It's not free, but if there's a list of N triangles with consistent
winding order, then we can make a sorted list of the 3N half-edges as a
lookup table in O(N log N) time.  Walking the perimeters of the Voronoi
cells hits each triangle 3 times, and each of the table look ups takes
O(log N) time, so the walking is O(N log N) too.

I have a data structure built from a vertices-and-faces description (VNF) that ensures that all local adjacencies can be found in constant time. It is constructed in O(n log n) (involves sorting and local searches) and the adjacency access functions are inspired by the quad-edge structure without the dual access functions. It requires a VNF representing a topological 2-manifold *without border*. So to compute the VD from the DT I needed to add a "border face", a face with a consistent widing representing the outside world of the DT. The resulting VNF of the DT is then a topological 2-manifold without border and easily dualizable (although a strict dual would have a vertex associated to the border face). To cope with the infinity edges of the VD far away vertices are added to the list of circumcenters. In order to have closed VD cells, the far away vertices are linked with additional edges and thus the VD itself may be represented as a topological 2-manifold without border. The following picture shows the results with the extra vertices of the VD closer enough to the point mass for sake of illustration. [image: Delaunay&Voronoi.PNG] That data structure also makes it possible to implement the so-called subdivision methods (like Catmull-Clark, Doo-Sabin and Loop's algorithm) for VNFs without border with a worst case order of O(n log n). Its space requirements are O(n). > It seems like this is creeping a little from generating Delaunay > triangulations and Voronoi tilings to dealing with applications. If I > understand the description correctly, it seems like eliminating Delaunay > triangles which aren't contained by the sample window first and then only > using closed Voronoi cells should work most of the time. > > This part of an earlier comment makes me think I'm missing something: > > Kenneth Sloan wrote > If all you have is a collection of triangles, computing the Voronoi Point > corresponding to each triangle is easy - but finding the connections > between the points (that is, the triangles which share edges with each > other) can involve considerable search overhead. > > It's not free, but if there's a list of N triangles with consistent > winding order, then we can make a sorted list of the 3N half-edges as a > lookup table in O(N log N) time. Walking the perimeters of the Voronoi > cells hits each triangle 3 times, and each of the table look ups takes > O(log N) time, so the walking is O(N log N) too. >
RP
Ronaldo Persiano
Tue, Apr 13, 2021 7:47 PM

Kenneth Sloan kennethrsloan@gmail.com wrote:

All of the above is from ancient memory - I apologize for any errors.
It's been more than a decade since I last actually wrote code for this (and
the code has been working since then with no issues).
Alas, the last collection of code was in Java, which  may not help anyone
writing in OpenSCAD.

Did you use Guibas&Stolfi quad-edge for that?

Kenneth Sloan <kennethrsloan@gmail.com> wrote: > All of the above is from ancient memory - I apologize for any errors. > It's been more than a decade since I last actually wrote code for this (and > the code has been working since then with no issues). > Alas, the last collection of code was in Java, which may not help anyone > writing in OpenSCAD. > > > Did you use Guibas&Stolfi quad-edge for that?
N
NateTG
Wed, Apr 14, 2021 3:26 PM

...
For your proposed search - how would you sort the half edges? ...

Obviously more sophisticated data structures have advantages, but sorting by
index in the point list works just fine, since the half edge that pairs with
(a,b) is (b,a).  Generation of something like:

http://forum.openscad.org/file/t2140/delaunay_voronoi.png

Is dominated by time spent on generating the triangulation.  (I was a little
surprised about the loose triangle on the upper left, but it makes sense
that that can happen.)

--
Sent from: http://forum.openscad.org/

... For your proposed search - how would you sort the half edges? ... Obviously more sophisticated data structures have advantages, but sorting by index in the point list works just fine, since the half edge that pairs with (a,b) is (b,a). Generation of something like: <http://forum.openscad.org/file/t2140/delaunay_voronoi.png> Is dominated by time spent on generating the triangulation. (I was a little surprised about the loose triangle on the upper left, but it makes sense that that can happen.) -- Sent from: http://forum.openscad.org/
KR
Kenneth R Sloan
Wed, Apr 14, 2021 6:06 PM

Not most recently.  I did implement G&S a Long, Long time ago - but later
decided it was of more theoretical interest than practical.  My current
implementation is simpler.  Its beginnings were an attempt to rewrite Joe
O’Rourke’s C code in Java - mostly for pedagogic purposes.

Like so much code that I have written for classroom demos, I ended up using
it in real projects.

On Tue, Apr 13, 2021 at 14:48 Ronaldo Persiano rcmpersiano@gmail.com
wrote:

Kenneth Sloan kennethrsloan@gmail.com wrote:

All of the above is from ancient memory - I apologize for any errors.
It's been more than a decade since I last actually wrote code for this (and
the code has been working since then with no issues).
Alas, the last collection of code was in Java, which  may not help anyone
writing in OpenSCAD.

Did you use Guibas&Stolfi quad-edge for that?


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

--
-Kenneth Sloan

Not most recently. I did implement G&S a Long, Long time ago - but later decided it was of more theoretical interest than practical. My current implementation is simpler. Its beginnings were an attempt to rewrite Joe O’Rourke’s C code in Java - mostly for pedagogic purposes. Like so much code that I have written for classroom demos, I ended up using it in real projects. On Tue, Apr 13, 2021 at 14:48 Ronaldo Persiano <rcmpersiano@gmail.com> wrote: > > > Kenneth Sloan <kennethrsloan@gmail.com> wrote: > >> All of the above is from ancient memory - I apologize for any errors. >> It's been more than a decade since I last actually wrote code for this (and >> the code has been working since then with no issues). >> Alas, the last collection of code was in Java, which may not help anyone >> writing in OpenSCAD. >> >> >> > Did you use Guibas&Stolfi quad-edge for that? > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org > -- -Kenneth Sloan
C
caterpillar
Fri, Apr 16, 2021 10:38 AM

I tried to implement tri_delaunay and tri_delaunay2voronoi.

http://forum.openscad.org/file/t1825/tri_vro.jpg

//
https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay.scad
use <experimental/tri_delaunay.scad>;
//
https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay2voronoi.scad
use <experimental/tri_delaunay2voronoi.scad>;
use <hull_polyline2d.scad>;

points = [for(i = [0:20]) rands(-100, 100, 2)];

color("black")
for(p = points) {
translate(p)
circle(2);
}

%draw(tri_delaunay(points, ret = "TRI_SHAPES"));

d = tri_delaunay(points, ret = "DELAUNAY");
#draw(tri_delaunay2voronoi(d));

module draw(pointsOfTriangles) {
for(t = pointsOfTriangles) {
hull_polyline2d(concat(t, [t[0]]));
}
}


https://openhome.cc

Sent from: http://forum.openscad.org/

I tried to implement tri_delaunay and tri_delaunay2voronoi. <http://forum.openscad.org/file/t1825/tri_vro.jpg> // https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay.scad use <experimental/tri_delaunay.scad>; // https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay2voronoi.scad use <experimental/tri_delaunay2voronoi.scad>; use <hull_polyline2d.scad>; points = [for(i = [0:20]) rands(-100, 100, 2)]; color("black") for(p = points) { translate(p) circle(2); } %draw(tri_delaunay(points, ret = "TRI_SHAPES")); d = tri_delaunay(points, ret = "DELAUNAY"); #draw(tri_delaunay2voronoi(d)); module draw(pointsOfTriangles) { for(t = pointsOfTriangles) { hull_polyline2d(concat(t, [t[0]])); } } ----- https://openhome.cc -- Sent from: http://forum.openscad.org/
KS
Kenneth Sloan
Fri, Apr 16, 2021 6:26 PM

Looks good.

To test numerical stability, try it on regular grids with many co-linear points.  A  hexagonal grid is good - a square grid is more challenging.  Start with 4 points at the vertices of a square and ramp  up from there.  Also challenging  is many points on (say) the x-axis, with 1 point  some distance away on the y axis.

Also, test with a wide  range of distances - change your uniform distribution over x and y to a Gaussian distribution.

--
Kenneth Sloan
KennethRSloan@gmail.com
Vision is the art of seeing what is invisible to others.

On Apr 16, 2021, at 05:38, caterpillar caterpillar@openhome.cc wrote:

I tried to implement tri_delaunay and tri_delaunay2voronoi.

// https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay.scad https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay.scad
use <experimental/tri_delaunay.scad>;
// https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay2voronoi.scad https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay2voronoi.scad
use <experimental/tri_delaunay2voronoi.scad>;
use <hull_polyline2d.scad>;

points = [for(i = [0:20]) rands(-100, 100, 2)];

color("black")
for(p = points) {
translate(p)
circle(2);
}

%draw(tri_delaunay(points, ret = "TRI_SHAPES"));

d = tri_delaunay(points, ret = "DELAUNAY");
#draw(tri_delaunay2voronoi(d));

module draw(pointsOfTriangles) {
for(t = pointsOfTriangles) {
hull_polyline2d(concat(t, [t[0]]));
}
}
https://openhome.cc https://openhome.cc/
Sent from the OpenSCAD mailing list archive http://forum.openscad.org/ at Nabble.com.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Looks good. To test numerical stability, try it on regular grids with many co-linear points. A hexagonal grid is good - a square grid is more challenging. Start with 4 points at the vertices of a square and ramp up from there. Also challenging is many points on (say) the x-axis, with 1 point some distance away on the y axis. Also, test with a wide range of distances - change your uniform distribution over x and y to a Gaussian distribution. -- Kenneth Sloan KennethRSloan@gmail.com Vision is the art of seeing what is invisible to others. > On Apr 16, 2021, at 05:38, caterpillar <caterpillar@openhome.cc> wrote: > > I tried to implement tri_delaunay and tri_delaunay2voronoi. > > > > // https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay.scad <https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay.scad> > use <experimental/tri_delaunay.scad>; > // https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay2voronoi.scad <https://github.com/JustinSDK/dotSCAD/blob/master/src/experimental/tri_delaunay2voronoi.scad> > use <experimental/tri_delaunay2voronoi.scad>; > use <hull_polyline2d.scad>; > > points = [for(i = [0:20]) rands(-100, 100, 2)]; > > color("black") > for(p = points) { > translate(p) > circle(2); > } > > %draw(tri_delaunay(points, ret = "TRI_SHAPES")); > > d = tri_delaunay(points, ret = "DELAUNAY"); > #draw(tri_delaunay2voronoi(d)); > > module draw(pointsOfTriangles) { > for(t = pointsOfTriangles) { > hull_polyline2d(concat(t, [t[0]])); > } > } > https://openhome.cc <https://openhome.cc/> > Sent from the OpenSCAD mailing list archive <http://forum.openscad.org/> at Nabble.com. > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org