discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Unnecessary triangulation in linear_extrude

NH
nop head
Tue, Jan 5, 2016 9:27 AM

Hmm, seems to be a complete mess numerically: mixing fixed point, doubles
and presumably rationals in CGAL. Then the result gets converted to float
in the STL. Note that the STL vertices are all the same value in the
triangle prism example, so the difference is less than the resolution of a
float, not surprising.

This also explains my original observation that linear extruding a circle
matches a hull of circles better than cylinder. It isn't the triangulation
that is the issue, it is the vertex representation changing number format.
A workaround is to just make the cylinder a bit bigger as pointed out
before.

I think a program like this should have a solid number and consistent
number representation that doesn't change from version to version. When I
updated all my objects changed numerically. A few got degenerate triangles
and at least one became non manifold. When I review them with show edges I
found a lot look like patchwork quilts now. On the plus side replacing
minkowski with offset gets the render time down to 4 minutes from 20.

It is avoiding coincident surfaces that confuses newcomers the most and
gives CSG a bad name. In actual fact CGAL seems to handle coincident
surfaces fine it is the number conversions outside of it that screw up.

Once thing that surprises me is how does this work?:

big = 20;
small = 10.1;

cube([big, big, 10]);
translate([big - small / 2, big - small / 2, 10])
cube([small, small, 20], center = true);

10.1 can't be represented exactly in float, so I would expect the
translated cube not to line up exactly with the big one, but in fact it
does.


It has now become more of a puzzle to create clean objects. For example
building one object back to front and then rotating it got a better result
because the faces that needed to line up where then on zero before the
rotation. I expected this example to replicate the issue but it doesn't.

On 5 January 2016 at 02:14, Marius Kintel marius@kintel.net wrote:

On Jan 3, 2016, at 18:17 PM, nop head nop.head@gmail.com wrote:

$fn=32;
translate([10.1,0,0]) {
cylinder(r = 4, h= 20);
translate([0, 0, 100]) cube(); //dummy
}
translate([10.1,0,0]) cylinder(r = 4, h = 10);

In this case, what happens is this:

  • The first translate() first performs a CGAL union on the children, then
    transforms that while in CGAL
  • The second translate() performs the transform on double precision
    floating point mesh, then converts to CGAL
  • The final implicit union now has slightly different vertex positions for
    the same translation.

One workaround for such issues could be to force all calculations to be
kept in CGAL as long as we have a parent node for which we’re going to need
CGAL. Another workaround (as mentioned) could be to accumulate
transformations and applying them only when concrete vertices are needed.
Note that using 2D objects probably also causes similar issues, which are
a lot harder to avoid since all 2D operations are done in fixed point.

While chasing this, I’d strongly suggest that we try to refactor this to
allow for using other CSG libraries in the future.

Cheers,

-Marius


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Hmm, seems to be a complete mess numerically: mixing fixed point, doubles and presumably rationals in CGAL. Then the result gets converted to float in the STL. Note that the STL vertices are all the same value in the triangle prism example, so the difference is less than the resolution of a float, not surprising. This also explains my original observation that linear extruding a circle matches a hull of circles better than cylinder. It isn't the triangulation that is the issue, it is the vertex representation changing number format. A workaround is to just make the cylinder a bit bigger as pointed out before. I think a program like this should have a solid number and consistent number representation that doesn't change from version to version. When I updated all my objects changed numerically. A few got degenerate triangles and at least one became non manifold. When I review them with show edges I found a lot look like patchwork quilts now. On the plus side replacing minkowski with offset gets the render time down to 4 minutes from 20. It is avoiding coincident surfaces that confuses newcomers the most and gives CSG a bad name. In actual fact CGAL seems to handle coincident surfaces fine it is the number conversions outside of it that screw up. Once thing that surprises me is how does this work?: big = 20; small = 10.1; cube([big, big, 10]); translate([big - small / 2, big - small / 2, 10]) cube([small, small, 20], center = true); 10.1 can't be represented exactly in float, so I would expect the translated cube not to line up exactly with the big one, but in fact it does. ​ It has now become more of a puzzle to create clean objects. For example building one object back to front and then rotating it got a better result because the faces that needed to line up where then on zero before the rotation. I expected this example to replicate the issue but it doesn't. On 5 January 2016 at 02:14, Marius Kintel <marius@kintel.net> wrote: > > On Jan 3, 2016, at 18:17 PM, nop head <nop.head@gmail.com> wrote: > > > > $fn=32; > > translate([10.1,0,0]) { > > cylinder(r = 4, h= 20); > > translate([0, 0, 100]) cube(); //dummy > > } > > translate([10.1,0,0]) cylinder(r = 4, h = 10); > > > In this case, what happens is this: > > * The first translate() first performs a CGAL union on the children, then > transforms that while in CGAL > * The second translate() performs the transform on double precision > floating point mesh, then converts to CGAL > * The final implicit union now has slightly different vertex positions for > the same translation. > > One workaround for such issues could be to force _all_ calculations to be > kept in CGAL as long as we have a parent node for which we’re going to need > CGAL. Another workaround (as mentioned) could be to accumulate > transformations and applying them only when concrete vertices are needed. > Note that using 2D objects probably also causes similar issues, which are > a lot harder to avoid since all 2D operations are done in fixed point. > > While chasing this, I’d strongly suggest that we try to refactor this to > allow for using other CSG libraries in the future. > > Cheers, > > -Marius > > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >
DM
doug moen
Tue, Jan 5, 2016 1:19 PM

Regarding your big/small program: it makes sense that the cubes line up.
The specific value of 'small' doesn't matter, because you don't try to
compute that value in two different ways. What matters, for this program,
is that floats have a binary representation, so division by two always
returns an exact result, never an inexact approximation. You compute
small/2 in two ways, via centre=true, and via small/2, and those values
exactly match.

On Tuesday, 5 January 2016, nop head nop.head@gmail.com wrote:

Hmm, seems to be a complete mess numerically: mixing fixed point, doubles
and presumably rationals in CGAL. Then the result gets converted to float
in the STL. Note that the STL vertices are all the same value in the
triangle prism example, so the difference is less than the resolution of a
float, not surprising.

This also explains my original observation that linear extruding a circle
matches a hull of circles better than cylinder. It isn't the triangulation
that is the issue, it is the vertex representation changing number format.
A workaround is to just make the cylinder a bit bigger as pointed out
before.

I think a program like this should have a solid number and consistent
number representation that doesn't change from version to version. When I
updated all my objects changed numerically. A few got degenerate triangles
and at least one became non manifold. When I review them with show edges I
found a lot look like patchwork quilts now. On the plus side replacing
minkowski with offset gets the render time down to 4 minutes from 20.

It is avoiding coincident surfaces that confuses newcomers the most and
gives CSG a bad name. In actual fact CGAL seems to handle coincident
surfaces fine it is the number conversions outside of it that screw up.

Once thing that surprises me is how does this work?:

big = 20;
small = 10.1;

cube([big, big, 10]);
translate([big - small / 2, big - small / 2, 10])
cube([small, small, 20], center = true);

10.1 can't be represented exactly in float, so I would expect the
translated cube not to line up exactly with the big one, but in fact it
does.


It has now become more of a puzzle to create clean objects. For example
building one object back to front and then rotating it got a better result
because the faces that needed to line up where then on zero before the
rotation. I expected this example to replicate the issue but it doesn't.

On 5 January 2016 at 02:14, Marius Kintel <marius@kintel.net
javascript:_e(%7B%7D,'cvml','marius@kintel.net');> wrote:

On Jan 3, 2016, at 18:17 PM, nop head <nop.head@gmail.com

$fn=32;
translate([10.1,0,0]) {
cylinder(r = 4, h= 20);
translate([0, 0, 100]) cube(); //dummy
}
translate([10.1,0,0]) cylinder(r = 4, h = 10);

In this case, what happens is this:

  • The first translate() first performs a CGAL union on the children, then
    transforms that while in CGAL
  • The second translate() performs the transform on double precision
    floating point mesh, then converts to CGAL
  • The final implicit union now has slightly different vertex positions
    for the same translation.

One workaround for such issues could be to force all calculations to be
kept in CGAL as long as we have a parent node for which we’re going to need
CGAL. Another workaround (as mentioned) could be to accumulate
transformations and applying them only when concrete vertices are needed.
Note that using 2D objects probably also causes similar issues, which are
a lot harder to avoid since all 2D operations are done in fixed point.

While chasing this, I’d strongly suggest that we try to refactor this to
allow for using other CSG libraries in the future.

Cheers,

-Marius


OpenSCAD mailing list
Discuss@lists.openscad.org
javascript:_e(%7B%7D,'cvml','Discuss@lists.openscad.org');
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Regarding your big/small program: it makes sense that the cubes line up. The specific value of 'small' doesn't matter, because you don't try to compute that value in two different ways. What matters, for this program, is that floats have a binary representation, so division by two always returns an exact result, never an inexact approximation. You compute small/2 in two ways, via centre=true, and via small/2, and those values exactly match. On Tuesday, 5 January 2016, nop head <nop.head@gmail.com> wrote: > Hmm, seems to be a complete mess numerically: mixing fixed point, doubles > and presumably rationals in CGAL. Then the result gets converted to float > in the STL. Note that the STL vertices are all the same value in the > triangle prism example, so the difference is less than the resolution of a > float, not surprising. > > This also explains my original observation that linear extruding a circle > matches a hull of circles better than cylinder. It isn't the triangulation > that is the issue, it is the vertex representation changing number format. > A workaround is to just make the cylinder a bit bigger as pointed out > before. > > I think a program like this should have a solid number and consistent > number representation that doesn't change from version to version. When I > updated all my objects changed numerically. A few got degenerate triangles > and at least one became non manifold. When I review them with show edges I > found a lot look like patchwork quilts now. On the plus side replacing > minkowski with offset gets the render time down to 4 minutes from 20. > > It is avoiding coincident surfaces that confuses newcomers the most and > gives CSG a bad name. In actual fact CGAL seems to handle coincident > surfaces fine it is the number conversions outside of it that screw up. > > Once thing that surprises me is how does this work?: > > big = 20; > small = 10.1; > > cube([big, big, 10]); > translate([big - small / 2, big - small / 2, 10]) > cube([small, small, 20], center = true); > > 10.1 can't be represented exactly in float, so I would expect the > translated cube not to line up exactly with the big one, but in fact it > does. > > > ​ > It has now become more of a puzzle to create clean objects. For example > building one object back to front and then rotating it got a better result > because the faces that needed to line up where then on zero before the > rotation. I expected this example to replicate the issue but it doesn't. > > > On 5 January 2016 at 02:14, Marius Kintel <marius@kintel.net > <javascript:_e(%7B%7D,'cvml','marius@kintel.net');>> wrote: > >> > On Jan 3, 2016, at 18:17 PM, nop head <nop.head@gmail.com >> <javascript:_e(%7B%7D,'cvml','nop.head@gmail.com');>> wrote: >> > >> > $fn=32; >> > translate([10.1,0,0]) { >> > cylinder(r = 4, h= 20); >> > translate([0, 0, 100]) cube(); //dummy >> > } >> > translate([10.1,0,0]) cylinder(r = 4, h = 10); >> > >> In this case, what happens is this: >> >> * The first translate() first performs a CGAL union on the children, then >> transforms that while in CGAL >> * The second translate() performs the transform on double precision >> floating point mesh, then converts to CGAL >> * The final implicit union now has slightly different vertex positions >> for the same translation. >> >> One workaround for such issues could be to force _all_ calculations to be >> kept in CGAL as long as we have a parent node for which we’re going to need >> CGAL. Another workaround (as mentioned) could be to accumulate >> transformations and applying them only when concrete vertices are needed. >> Note that using 2D objects probably also causes similar issues, which are >> a lot harder to avoid since all 2D operations are done in fixed point. >> >> While chasing this, I’d strongly suggest that we try to refactor this to >> allow for using other CSG libraries in the future. >> >> Cheers, >> >> -Marius >> >> >> _______________________________________________ >> OpenSCAD mailing list >> Discuss@lists.openscad.org >> <javascript:_e(%7B%7D,'cvml','Discuss@lists.openscad.org');> >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >> > >
NH
nop head
Tue, Jan 5, 2016 3:05 PM

Yes thinking about it, it was probably another case of translates being
done in different number representations.

On 5 January 2016 at 13:19, doug moen doug@moens.org wrote:

Regarding your big/small program: it makes sense that the cubes line up.
The specific value of 'small' doesn't matter, because you don't try to
compute that value in two different ways. What matters, for this program,
is that floats have a binary representation, so division by two always
returns an exact result, never an inexact approximation. You compute
small/2 in two ways, via centre=true, and via small/2, and those values
exactly match.

On Tuesday, 5 January 2016, nop head nop.head@gmail.com wrote:

Hmm, seems to be a complete mess numerically: mixing fixed point, doubles
and presumably rationals in CGAL. Then the result gets converted to float
in the STL. Note that the STL vertices are all the same value in the
triangle prism example, so the difference is less than the resolution of a
float, not surprising.

This also explains my original observation that linear extruding a circle
matches a hull of circles better than cylinder. It isn't the triangulation
that is the issue, it is the vertex representation changing number format.
A workaround is to just make the cylinder a bit bigger as pointed out
before.

I think a program like this should have a solid number and consistent
number representation that doesn't change from version to version. When I
updated all my objects changed numerically. A few got degenerate triangles
and at least one became non manifold. When I review them with show edges I
found a lot look like patchwork quilts now. On the plus side replacing
minkowski with offset gets the render time down to 4 minutes from 20.

It is avoiding coincident surfaces that confuses newcomers the most and
gives CSG a bad name. In actual fact CGAL seems to handle coincident
surfaces fine it is the number conversions outside of it that screw up.

Once thing that surprises me is how does this work?:

big = 20;
small = 10.1;

cube([big, big, 10]);
translate([big - small / 2, big - small / 2, 10])
cube([small, small, 20], center = true);

10.1 can't be represented exactly in float, so I would expect the
translated cube not to line up exactly with the big one, but in fact it
does.


It has now become more of a puzzle to create clean objects. For example
building one object back to front and then rotating it got a better result
because the faces that needed to line up where then on zero before the
rotation. I expected this example to replicate the issue but it doesn't.

On 5 January 2016 at 02:14, Marius Kintel marius@kintel.net wrote:

On Jan 3, 2016, at 18:17 PM, nop head nop.head@gmail.com wrote:

$fn=32;
translate([10.1,0,0]) {
cylinder(r = 4, h= 20);
translate([0, 0, 100]) cube(); //dummy
}
translate([10.1,0,0]) cylinder(r = 4, h = 10);

In this case, what happens is this:

  • The first translate() first performs a CGAL union on the children,
    then transforms that while in CGAL
  • The second translate() performs the transform on double precision
    floating point mesh, then converts to CGAL
  • The final implicit union now has slightly different vertex positions
    for the same translation.

One workaround for such issues could be to force all calculations to
be kept in CGAL as long as we have a parent node for which we’re going to
need CGAL. Another workaround (as mentioned) could be to accumulate
transformations and applying them only when concrete vertices are needed.
Note that using 2D objects probably also causes similar issues, which
are a lot harder to avoid since all 2D operations are done in fixed point.

While chasing this, I’d strongly suggest that we try to refactor this to
allow for using other CSG libraries in the future.

Cheers,

-Marius


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Yes thinking about it, it was probably another case of translates being done in different number representations. On 5 January 2016 at 13:19, doug moen <doug@moens.org> wrote: > Regarding your big/small program: it makes sense that the cubes line up. > The specific value of 'small' doesn't matter, because you don't try to > compute that value in two different ways. What matters, for this program, > is that floats have a binary representation, so division by two always > returns an exact result, never an inexact approximation. You compute > small/2 in two ways, via centre=true, and via small/2, and those values > exactly match. > > > On Tuesday, 5 January 2016, nop head <nop.head@gmail.com> wrote: > >> Hmm, seems to be a complete mess numerically: mixing fixed point, doubles >> and presumably rationals in CGAL. Then the result gets converted to float >> in the STL. Note that the STL vertices are all the same value in the >> triangle prism example, so the difference is less than the resolution of a >> float, not surprising. >> >> This also explains my original observation that linear extruding a circle >> matches a hull of circles better than cylinder. It isn't the triangulation >> that is the issue, it is the vertex representation changing number format. >> A workaround is to just make the cylinder a bit bigger as pointed out >> before. >> >> I think a program like this should have a solid number and consistent >> number representation that doesn't change from version to version. When I >> updated all my objects changed numerically. A few got degenerate triangles >> and at least one became non manifold. When I review them with show edges I >> found a lot look like patchwork quilts now. On the plus side replacing >> minkowski with offset gets the render time down to 4 minutes from 20. >> >> It is avoiding coincident surfaces that confuses newcomers the most and >> gives CSG a bad name. In actual fact CGAL seems to handle coincident >> surfaces fine it is the number conversions outside of it that screw up. >> >> Once thing that surprises me is how does this work?: >> >> big = 20; >> small = 10.1; >> >> cube([big, big, 10]); >> translate([big - small / 2, big - small / 2, 10]) >> cube([small, small, 20], center = true); >> >> 10.1 can't be represented exactly in float, so I would expect the >> translated cube not to line up exactly with the big one, but in fact it >> does. >> >> >> ​ >> It has now become more of a puzzle to create clean objects. For example >> building one object back to front and then rotating it got a better result >> because the faces that needed to line up where then on zero before the >> rotation. I expected this example to replicate the issue but it doesn't. >> >> >> On 5 January 2016 at 02:14, Marius Kintel <marius@kintel.net> wrote: >> >>> > On Jan 3, 2016, at 18:17 PM, nop head <nop.head@gmail.com> wrote: >>> > >>> > $fn=32; >>> > translate([10.1,0,0]) { >>> > cylinder(r = 4, h= 20); >>> > translate([0, 0, 100]) cube(); //dummy >>> > } >>> > translate([10.1,0,0]) cylinder(r = 4, h = 10); >>> > >>> In this case, what happens is this: >>> >>> * The first translate() first performs a CGAL union on the children, >>> then transforms that while in CGAL >>> * The second translate() performs the transform on double precision >>> floating point mesh, then converts to CGAL >>> * The final implicit union now has slightly different vertex positions >>> for the same translation. >>> >>> One workaround for such issues could be to force _all_ calculations to >>> be kept in CGAL as long as we have a parent node for which we’re going to >>> need CGAL. Another workaround (as mentioned) could be to accumulate >>> transformations and applying them only when concrete vertices are needed. >>> Note that using 2D objects probably also causes similar issues, which >>> are a lot harder to avoid since all 2D operations are done in fixed point. >>> >>> While chasing this, I’d strongly suggest that we try to refactor this to >>> allow for using other CSG libraries in the future. >>> >>> Cheers, >>> >>> -Marius >>> >>> >>> _______________________________________________ >>> OpenSCAD mailing list >>> Discuss@lists.openscad.org >>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>> >> >> > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > >
MK
Marius Kintel
Tue, Jan 5, 2016 4:31 PM

On Jan 5, 2016, at 04:27 AM, nop head nop.head@gmail.com wrote:
I think a program like this should have a solid number and consistent number representation that doesn't change from version to version.

I agree in principle - it’s just a bit utopic in reality : /
Our goal thus far has been to gradually move away from the slow CGAL operations whenever we find a good alternative. We also work around bugs in CGAL by using other libraries to perform certain operations (e.g. triangulations). This leaves us with a mashup of underlying libraries.

Anyway, as a quick test I tried to defer applying transformations until after the conversion to CGAL. That improved some of your cases.
We also perform some vertex quantizing, which I think is responsible for the remaining issues. I’ll try to temporarily remove that and see if I can construct a clean mesh using your examples.

Another idea would be to force vertex quantizing into a fixed point representation every time we convert between mesh representations. There are some challenges related to that; first that vertex quantizing is not topology preserving, secondly that it may come at a performance penalty as we’d need to quantize intermediate results from CGAL (which probably means reconstructing said CGAL objects).

If you have other variants of similar behavior, please post as they could serve as good candidates for test cases.

-Marius

> On Jan 5, 2016, at 04:27 AM, nop head <nop.head@gmail.com> wrote: > I think a program like this should have a solid number and consistent number representation that doesn't change from version to version. I agree in principle - it’s just a bit utopic in reality : / Our goal thus far has been to gradually move away from the slow CGAL operations whenever we find a good alternative. We also work around bugs in CGAL by using other libraries to perform certain operations (e.g. triangulations). This leaves us with a mashup of underlying libraries. Anyway, as a quick test I tried to defer applying transformations until after the conversion to CGAL. That improved some of your cases. We also perform some vertex quantizing, which I think is responsible for the remaining issues. I’ll try to temporarily remove that and see if I can construct a clean mesh using your examples. Another idea would be to force vertex quantizing into a fixed point representation every time we convert between mesh representations. There are some challenges related to that; first that vertex quantizing is not topology preserving, secondly that it may come at a performance penalty as we’d need to quantize intermediate results from CGAL (which probably means reconstructing said CGAL objects). If you have other variants of similar behavior, please post as they could serve as good candidates for test cases. -Marius