RP
Ronaldo Persiano
Tue, Feb 26, 2019 3:26 PM
Sorry, I have not made myself clear. The additional vertices where
introduced to the polygons before calling any process in order to
evaluate the effect of the number of vertices. You may think that all
polygons are circle. My functions do not add any vertices to the input
polygons.
Parkinbot wrote
> Hmm, I would expect your example to have a key hole representation similar
> to
> this image:
> <http://forum.openscad.org/file/t887/FSP.png>
> So no additional vertices are introduced.
>
Sorry, I have not made myself clear. The additional vertices where
introduced to the polygons *before* calling any process in order to
evaluate the effect of the number of vertices. You may think that all
polygons are circle. My functions do not add any vertices to the input
polygons.
>
P
Parkinbot
Tue, Feb 26, 2019 6:15 PM
Sorry, I have not made myself clear. The additional vertices where
introduced to the polygons before calling any process in order to
evaluate the effect of the number of vertices.
Ok, but this relativates the following statement:
Ronaldo wrote
Besides, many triangles are very thin, almost degenerate, like the ones
just bellow the two holes at southwest. This may be a cause of troubles in
the render of sweeps.
and isn't that outcome a general problem of earcut?
Ronaldo wrote
which is now uploaded in my github repository in its last version.
I'll download and test it in the next days.
Ronaldo wrote
The irony I mentioned in the end of my last message is that anything we do
a partition of a polygon with holes in simple polygons to turn it
palatable to CGAL, the system will discard it and make its own
triangulation of the face.
Yeah, the main irony is indeed that OpenSCAD already has the capability to
triangulate polygons with holes, but it cannot be accessed from user space.
--
Sent from: http://forum.openscad.org/
Ronaldo wrote
> Sorry, I have not made myself clear. The additional vertices where
> introduced to the polygons *before* calling any process in order to
> evaluate the effect of the number of vertices.
Ok, but this relativates the following statement:
Ronaldo wrote
> Besides, many triangles are very thin, almost degenerate, like the ones
> just bellow the two holes at southwest. This may be a cause of troubles in
> the render of sweeps.
and isn't that outcome a general problem of earcut?
Ronaldo wrote
> which is now uploaded in my github repository in its last version.
I'll download and test it in the next days.
Ronaldo wrote
> The irony I mentioned in the end of my last message is that anything we do
> a partition of a polygon with holes in simple polygons to turn it
> palatable to CGAL, the system will discard it and make its own
> triangulation of the face.
Yeah, the main irony is indeed that OpenSCAD already has the capability to
triangulate polygons with holes, but it cannot be accessed from user space.
--
Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Tue, Feb 26, 2019 6:51 PM
Yeah, the main irony is indeed that OpenSCAD already has the capability to
triangulate polygons with holes, but it cannot be accessed from user
space.
I don't know if it is capable of dealing directly with polygons with holes
because there is no way to specify it except by boolean differences or by
FSP. And I don't care to access it directly. I would be glad that faces
with holes for polyhedron could be accepted in FSP format the same way they
are accepted by polygon() once we don't have any way to define polyhedron
faces by boolean differences.
>
> Parkinbot, wrote:
> Yeah, the main irony is indeed that OpenSCAD already has the capability to
> triangulate polygons with holes, but it cannot be accessed from user
> space.
>
I don't know if it is capable of dealing directly with polygons with holes
because there is no way to specify it except by boolean differences or by
FSP. And I don't care to access it directly. I would be glad that faces
with holes for polyhedron could be accepted in FSP format the same way they
are accepted by polygon() once we don't have any way to define polyhedron
faces by boolean differences.
>
RP
Ronaldo Persiano
Wed, Feb 27, 2019 12:10 AM
I would be glad that faces with holes for polyhedron could be accepted in
FSP format the same way they are accepted by polygon() once we don't have
any way to define polyhedron faces by boolean differences.
I was wrong! I have checked again and the primitive polyhedron is able to
deal with FSP ! There was a bug in my previous tests. So, we don't need to
do any partition, either by triangulation or polyHolePartition, to a FSP
and the only additional function needed to sweep polygons with holes is
polyHoles which is lighter than polyHolePartition() and much more efficient
than ear-cut triangulation.
Here is my previous sweep example working now directly with the FSP
generated by polyHoles.
use <polyHoles.scad>
use <sweep.scad> // this is my sweep version with sweep_polyhedron() and
lazyUnion()
// the face with holes
outer = circ(r=50,n=24); // the outer border
mH = circ_distrib(25, 4, 3, revert(circ(12,24))); // holes borders
// build the FSP for the face with holes
FSP = polyHoles(outer, mH);
// express it in a pdata format
FSP_PDat = [ FSP, [[for(i=[0:len(FSP)-1]) i]] ];
// The sweep
// main path
path = spiral(200,80,270,24);
// the sweep path transforms
ptrans = construct_transform_path(path);
// sweep the outer face and the holes without caps
// sweep_polyhedron function generates the pdata of the sweep
souter = sweep_polyhedron(outer, ptrans, caps=false);
shole0 = sweep_polyhedron(mH[0], ptrans, caps=false);
shole1 = sweep_polyhedron(mH[1], ptrans, caps=false);
shole2 = sweep_polyhedron(mH[2], ptrans, caps=false);
// transform FSP_PDat to put the holed caps in proper place
begCap = tranfPdata(ptrans[0], FSP_PDat) ;
endCap = tranfPdata(ptrans[len(ptrans)-1], FSP_PDat, rev=true) ;
// join evething in one polyhedron
difference() {
lazyUnion([souter, begCap, endCap, shole0, shole1, shole2]);
translate([-160,50,0]) cube([60,60,150]);
}
// helper funtions
function spiral(r,h,ang,n) =
[for(i=to_(n)) [rcos(iang/n), rsin(iang/n),ih/n] ];
function circ(r,n,a=360) = [for(i=to_(n)) r[cos(ia/n), sin(ia/n),0] ];
function T_(p,lp) = [for(pi=lp) pi+p ];
function circ_distrib(r,n,m,lp) = [for(i=to_(m)) rotz(i360/n,
T_([0,r,0],lp) ) ];
function tranfPdata(transf, pdata, rev=false) =
! rev ?
[ transform(transf, pdata[0]), pdata[1] ] :
[ transform(transf, pdata[0]), [for(f=pdata[1]) revert(f) ] ];
function rotz(a,lp) =
let( T = [ [cos(a), sin(a), 0],[-sin(a),cos(a),0],[0,0,1] ] )
lp[0][0]==undef? Tlp :[ for(pi=lp) Tpi ] ;
function revert(l) = !(len(l)>0)? [] : [ for(i=[len(l)-1:-1:0]) l[i] ];
function to_(n) = [0:n-1];
function transform(m, list) =
[for (p=list)let( q = mconcat(p,[1]) ) [q.x,q.y,q.z]/q[3]];
Ronaldo wrote:
> I would be glad that faces with holes for polyhedron could be accepted in
>> FSP format the same way they are accepted by polygon() once we don't have
>> any way to define polyhedron faces by boolean differences.
>
>
I was wrong! I have checked again and the primitive polyhedron is able to
deal with FSP ! There was a bug in my previous tests. So, we don't need to
do any partition, either by triangulation or polyHolePartition, to a FSP
and the only additional function needed to sweep polygons with holes is
polyHoles which is lighter than polyHolePartition() and much more efficient
than ear-cut triangulation.
Here is my previous sweep example working now directly with the FSP
generated by polyHoles.
use <polyHoles.scad>
use <sweep.scad> // this is my sweep version with sweep_polyhedron() and
lazyUnion()
// the face with holes
outer = circ(r=50,n=24); // the outer border
mH = circ_distrib(25, 4, 3, revert(circ(12,24))); // holes borders
// build the FSP for the face with holes
FSP = polyHoles(outer, mH);
// express it in a pdata format
FSP_PDat = [ FSP, [[for(i=[0:len(FSP)-1]) i]] ];
// The sweep
// main path
path = spiral(200,80,270,24);
// the sweep path transforms
ptrans = construct_transform_path(path);
// sweep the outer face and the holes without caps
// sweep_polyhedron function generates the pdata of the sweep
souter = sweep_polyhedron(outer, ptrans, caps=false);
shole0 = sweep_polyhedron(mH[0], ptrans, caps=false);
shole1 = sweep_polyhedron(mH[1], ptrans, caps=false);
shole2 = sweep_polyhedron(mH[2], ptrans, caps=false);
// transform FSP_PDat to put the holed caps in proper place
begCap = tranfPdata(ptrans[0], FSP_PDat) ;
endCap = tranfPdata(ptrans[len(ptrans)-1], FSP_PDat, rev=true) ;
// join evething in one polyhedron
difference() {
lazyUnion([souter, begCap, endCap, shole0, shole1, shole2]);
translate([-160,50,0]) cube([60,60,150]);
}
// helper funtions
function spiral(r,h,ang,n) =
[for(i=to_(n)) [r*cos(i*ang/n), r*sin(i*ang/n),i*h/n] ];
function circ(r,n,a=360) = [for(i=to_(n)) r*[cos(i*a/n), sin(i*a/n),0] ];
function T_(p,lp) = [for(pi=lp) pi+p ];
function circ_distrib(r,n,m,lp) = [for(i=to_(m)) rotz(i*360/n,
T_([0,r,0],lp) ) ];
function tranfPdata(transf, pdata, rev=false) =
! rev ?
[ transform(transf, pdata[0]), pdata[1] ] :
[ transform(transf, pdata[0]), [for(f=pdata[1]) revert(f) ] ];
function rotz(a,lp) =
let( T = [ [cos(a), sin(a), 0],[-sin(a),cos(a),0],[0,0,1] ] )
lp[0][0]==undef? T*lp :[ for(pi=lp) T*pi ] ;
function revert(l) = !(len(l)>0)? [] : [ for(i=[len(l)-1:-1:0]) l[i] ];
function to_(n) = [0:n-1];
function transform(m, list) =
[for (p=list)let( q = m*concat(p,[1]) ) [q.x,q.y,q.z]/q[3]];
P
Parkinbot
Wed, Feb 27, 2019 3:57 PM
I was wrong! I have checked again and the primitive polyhedron is able to
deal with FSP !
I tried to verify with OpenSCAD RC2, remembering that I had already checked
it when the thread started. I would say: polyhedron() does not interpret an
FSP. Or do you use another representation?
http://forum.openscad.org/file/t887/FSP_polyhedron.png
This is my code:
o = circle(r=100, z=100);
i = circle(r=10, z=100);
fsp = FSP(o,i);
color("red") polygon(vec2(fsp));
polyhedron(fsp, [Rg(2*len(fsp))]);
// line(fsp, color = "green");
function circle(r=10, N=3, z=0) = [for(i=[0:N]) let(a=360/Ni) r[cos(a),
sin(a),z/r]];
function vec2(P) = [for(p=P) [p[0], p[1]]];
function Rg(N) = [for(i=[0:N-1]) i];
function FSP(out, in) = [for(i=out) i, out[0], for(i=in) i, in[0], out[0]];
--
Sent from: http://forum.openscad.org/
Ronaldo wrote
> I was wrong! I have checked again and the primitive polyhedron is able to
> deal with FSP !
I tried to verify with OpenSCAD RC2, remembering that I had already checked
it when the thread started. I would say: polyhedron() does not interpret an
FSP. Or do you use another representation?
<http://forum.openscad.org/file/t887/FSP_polyhedron.png>
This is my code:
o = circle(r=100, z=100);
i = circle(r=10, z=100);
fsp = FSP(o,i);
color("red") polygon(vec2(fsp));
polyhedron(fsp, [Rg(2*len(fsp))]);
// line(fsp, color = "green");
function circle(r=10, N=3, z=0) = [for(i=[0:N]) let(a=360/N*i) r*[cos(a),
sin(a),z/r]];
function vec2(P) = [for(p=P) [p[0], p[1]]];
function Rg(N) = [for(i=[0:N-1]) i];
function FSP(out, in) = [for(i=out) i, out[0], for(i=in) i, in[0], out[0]];
--
Sent from: http://forum.openscad.org/
P
Parkinbot
Wed, Feb 27, 2019 4:09 PM
Sorry my fault, the inner polygon needs reversed orientation of course.
function FSP(out, in) = [for(i=out) i, out[0], for(i=[len(in)-1:-1:0])
in[i], in[0], out[0]];
Yeah, it works!!!! Great!
Sometimes it is a long journey to find out that everything was already
there.
http://forum.openscad.org/file/t887/fsp_poly.png
--
Sent from: http://forum.openscad.org/
Sorry my fault, the inner polygon needs reversed orientation of course.
function FSP(out, in) = [for(i=out) i, out[0], for(i=[len(in)-1:-1:0])
in[i], in[0], out[0]];
Yeah, it works!!!! Great!
Sometimes it is a long journey to find out that everything was already
there.
<http://forum.openscad.org/file/t887/fsp_poly.png>
--
Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Wed, Feb 27, 2019 5:36 PM
Don't be lazy and run a code with a full legal polyhedron!
A quarta, 27/02/2019, 16:14, Parkinbot rudolf@digitaldocument.de escreveu:
Don't be lazy and run a code with a full legal polyhedron!
A quarta, 27/02/2019, 16:14, Parkinbot <rudolf@digitaldocument.de> escreveu:
> Sorry my fault, the inner polygon needs reversed orientation of course.
>
> function FSP(out, in) = [for(i=out) i, out[0], for(i=[len(in)-1:-1:0])
> in[i], in[0], out[0]];
>
>
> Yeah, it works!!!! Great!
> Sometimes it is a long journey to find out that everything was already
> there.
>
> <http://forum.openscad.org/file/t887/fsp_poly.png>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
P
Parkinbot
Wed, Feb 27, 2019 6:46 PM
Don't be lazy and run a code with a full legal polyhedron!
You are right, better always do a full test. The following code is well with
F5/F12, but doesn't render properly with F6, when a cube is unioned.
o = circle(r=100, z=0);
i = circle(r=10, z=0);
o1 = circle(r=100, z=100);
i1 = circle(r=10, z=100);
P = concat(o, i, o1, i1);
F = [0, 3, 5, 4, 3, 0, 1, 2, 0]; // FSP lower
F1 = [6,8,7,6,9,10,11, 9,6]; // FSP upper
A = [[1,0,6], [1, 6, 7], [0, 2, 8], [0, 8, 6], [2, 1, 8], [1, 7, 8]]; //
inner
B = [[3,4,10], [3,10,9],[4, 11, 10], [4,5,11], [5, 3, 9], [5,9,11]]; //
outer
polyhedron(P, concat([F1, F], A, B));
cube(1);
function circle(r=10, N=3, z=0) = [for(i=[0:N-1]) let(a=360/Ni) r[cos(a),
sin(a),z/r]];
--
Sent from: http://forum.openscad.org/
Ronaldo wrote
> Don't be lazy and run a code with a full legal polyhedron!
You are right, better always do a full test. The following code is well with
F5/F12, but doesn't render properly with F6, when a cube is unioned.
o = circle(r=100, z=0);
i = circle(r=10, z=0);
o1 = circle(r=100, z=100);
i1 = circle(r=10, z=100);
P = concat(o, i, o1, i1);
F = [0, 3, 5, 4, 3, 0, 1, 2, 0]; // FSP lower
F1 = [6,8,7,6,9,10,11, 9,6]; // FSP upper
A = [[1,0,6], [1, 6, 7], [0, 2, 8], [0, 8, 6], [2, 1, 8], [1, 7, 8]]; //
inner
B = [[3,4,10], [3,10,9],[4, 11, 10], [4,5,11], [5, 3, 9], [5,9,11]]; //
outer
polyhedron(P, concat([F1, F], A, B));
cube(1);
function circle(r=10, N=3, z=0) = [for(i=[0:N-1]) let(a=360/N*i) r*[cos(a),
sin(a),z/r]];
--
Sent from: http://forum.openscad.org/
P
Parkinbot
Wed, Feb 27, 2019 7:17 PM
Of course, one can omit the union test, do F6 and export a proper STL. After
import Boolean operations will work.
import("FSP.stl");
cube(1);
So it is CGAL that is playing unfair. Haven't we had this result already
near thread start?
@Hans: It might be not a big thing to feed CGAL with a fully tesselated
version of a polyhedron.
--
Sent from: http://forum.openscad.org/
Of course, one can omit the union test, do F6 and export a proper STL. After
import Boolean operations will work.
import("FSP.stl");
cube(1);
So it is CGAL that is playing unfair. Haven't we had this result already
near thread start?
@Hans: It might be not a big thing to feed CGAL with a fully tesselated
version of a polyhedron.
--
Sent from: http://forum.openscad.org/
P
Parkinbot
Thu, Feb 28, 2019 5:49 PM
It's generally not lazyness but lack of time. I found time today to read the
paper more properly (a bit bony in its formalizm). After that I implemented
a FSP function, doing the same what your polyHoles does. Sorry to reinvent
the wheel, but it is so hard for me to read foreign code (you may call me a
code autist).
My code
- finds the points B_i with minimal x-value in each inner polygon and puts
them into a list A.
- selects the polygon n with minimal x-value from the list A, uses the
point B_n as bridge and excludes polygon n from the list A.
- finds the nearest point with smaler x value in the outer polygon with
respect to the bridge
- composes a new outer polygon using the bridge
- does recursion until list A is empty.
Not that I don't check if polygons are simple. Here is the code and a full
blown use case with four inner polygons:
http://forum.openscad.org/file/t887/fsp_FSP.png
// full example of FSP
o = circle(r=100, N=4);
i0 = T([-41, 25, 0], circle(r=20, N=3));
i1 = T([-40, -15, 0], circle(r=20, N=3));
i2 = T([45, -10, 0], circle(r=20, N=3));
i3 = T([0, 0, 0], circle(r=20, N=3));
X = FSP(o, [i0, i1, i2, i3]);
polygon(X);
line(X);
// outer: polygon, inner: list of polygons
function FSP(outer, inner, minXlist=undef) =
let(minXlist = minXlist?minXlist:MinXList(inner)) // all bridge point
x-values and indices
let(ibestP = minX(minXlist)) // get index of best polygon (i.e. with
lowest x-value)
let(bestinner = inner[minXlist[ibestP][2]]) // polygon to be bridged
let(minXlist_ = excludeElem(minXlist, ibestP)) // exclude best polygon from
bridge point list
let(validx = minXlist[ibestP]) // x-val and its index in best polygon
let(ibest = validx[1]) // index of bridge point in polygon
let(ibestinner = validx[2]) // index of polygon in inner list
let(bridge = inner[ibestinner][ibest]) // bridge point in polygon to be
bridged
let(obest = Nearest(outer, bridge)) // index of nearest point with <=x value
in outer polygon
let(N=len(bestinner))
let(fsp = [for(i=[0:obest]) outer[i], // outer polygon with bridge to inner
for(i=[ibest+N:-1:ibest]) bestinner[i%N],
for(i=[obest:len(outer)]) outer[i%len(outer)]])
let(ret = minXlist_==[]?fsp:FSP(fsp, inner, minXlist_)) // recursion until
minXlist empty
ret;
// P is polygon list. Returns list of minX-values, their indices and polygon
index
function MinXList(P) = [for(p=[0:len(P)-1]) let(m = minX(P[p])) [P[p][m][0],
m, p]];
// index of element with smallest value in first component (x value)
function minX(P) = let(N=len(P))
[for(i=1, min=0; i<N+1; min=P[i][0]<P[min][0]?i:min, i=i+1) if(i==N)
min][0];
// exclude element with idx from list
function excludeElem(list, idx) = [for(i=[0:len(list)-1]) if(i!=idx)
list[i]];
// get index of nearest point smaller x value in P wrt p
function Nearest(P, p) = let(N=len(P))
[for(i=1, min=0, val=0, val_=0; i<N+1;
min=P[i][0]<=p[0] && norm(P[i]-p)<norm(P[min]-p)?i:min, i=i+1)
if(i==N) min][0];
// translate polygon P
function T(x, P) = [for(p=P) p+x];
// circle polygon
function circle(r=10,N=3)=[for(i=[0:N-1]) let(a=360/Ni) r[cos(a),sin(a)]];
module line(P, r=1, color="red")
color(color)for(p=[0:len(P)-1])
hull()
{
translate(P[p]) sphere(r);
translate(P[(p+1)%len(P)]) sphere(r);
}
--
Sent from: http://forum.openscad.org/
It's generally not lazyness but lack of time. I found time today to read the
paper more properly (a bit bony in its formalizm). After that I implemented
a FSP function, doing the same what your polyHoles does. Sorry to reinvent
the wheel, but it is so hard for me to read foreign code (you may call me a
code autist).
My code
1. finds the points B_i with minimal x-value in each inner polygon and puts
them into a list A.
2. selects the polygon n with minimal x-value from the list A, uses the
point B_n as bridge and excludes polygon n from the list A.
3. finds the nearest point with smaler x value in the outer polygon with
respect to the bridge
4. composes a new outer polygon using the bridge
5. does recursion until list A is empty.
Not that I don't check if polygons are simple. Here is the code and a full
blown use case with four inner polygons:
<http://forum.openscad.org/file/t887/fsp_FSP.png>
// full example of FSP
o = circle(r=100, N=4);
i0 = T([-41, 25, 0], circle(r=20, N=3));
i1 = T([-40, -15, 0], circle(r=20, N=3));
i2 = T([45, -10, 0], circle(r=20, N=3));
i3 = T([0, 0, 0], circle(r=20, N=3));
X = FSP(o, [i0, i1, i2, i3]);
polygon(X);
line(X);
// outer: polygon, inner: list of polygons
function FSP(outer, inner, minXlist=undef) =
let(minXlist = minXlist?minXlist:MinXList(inner)) // all bridge point
x-values and indices
let(ibestP = minX(minXlist)) // get index of best polygon (i.e. with
lowest x-value)
let(bestinner = inner[minXlist[ibestP][2]]) // polygon to be bridged
let(minXlist_ = excludeElem(minXlist, ibestP)) // exclude best polygon from
bridge point list
let(validx = minXlist[ibestP]) // x-val and its index in best polygon
let(ibest = validx[1]) // index of bridge point in polygon
let(ibestinner = validx[2]) // index of polygon in inner list
let(bridge = inner[ibestinner][ibest]) // bridge point in polygon to be
bridged
let(obest = Nearest(outer, bridge)) // index of nearest point with <=x value
in outer polygon
let(N=len(bestinner))
let(fsp = [for(i=[0:obest]) outer[i], // outer polygon with bridge to inner
for(i=[ibest+N:-1:ibest]) bestinner[i%N],
for(i=[obest:len(outer)]) outer[i%len(outer)]])
let(ret = minXlist_==[]?fsp:FSP(fsp, inner, minXlist_)) // recursion until
minXlist empty
ret;
// P is polygon list. Returns list of minX-values, their indices and polygon
index
function MinXList(P) = [for(p=[0:len(P)-1]) let(m = minX(P[p])) [P[p][m][0],
m, p]];
// index of element with smallest value in first component (x value)
function minX(P) = let(N=len(P))
[for(i=1, min=0; i<N+1; min=P[i][0]<P[min][0]?i:min, i=i+1) if(i==N)
min][0];
// exclude element with idx from list
function excludeElem(list, idx) = [for(i=[0:len(list)-1]) if(i!=idx)
list[i]];
// get index of nearest point smaller x value in P wrt p
function Nearest(P, p) = let(N=len(P))
[for(i=1, min=0, val=0, val_=0; i<N+1;
min=P[i][0]<=p[0] && norm(P[i]-p)<norm(P[min]-p)?i:min, i=i+1)
if(i==N) min][0];
// translate polygon P
function T(x, P) = [for(p=P) p+x];
// circle polygon
function circle(r=10,N=3)=[for(i=[0:N-1]) let(a=360/N*i) r*[cos(a),sin(a)]];
module line(P, r=1, color="red")
color(color)for(p=[0:len(P)-1])
hull()
{
translate(P[p]) sphere(r);
translate(P[(p+1)%len(P)]) sphere(r);
}
--
Sent from: http://forum.openscad.org/