I recall this being discussed a while back and I'm not sure about what the
ultimate conclusion was, and I can't remember what the issue was that gave rise to the change. But it seems like the RC3 version has the behavior that if(false) returns an empty object rather than nothing. This is different from what happened in 2019.05, it seems, as shown by this example which breaks in RC3 but works in 2019. chamfer = false; // Set to true to get chamfer intersection(){ cube(10); if (chamfer) translate([0,-10,12]) rotate([-45,0,0])cube(30); } When chamfer is false I expect a cube but instead I get nothing because the intersection is with an empty object. Note that in the list context: [1,2,if (false) 27, 3, 4] the conditional produces nothing, not an empty list, so the new behavior makes the openscad language less internally consistent. Is there some advantage of the new behavior? Note that if this change is staying, it should be highlighted as a language change from the 2019 version. -- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
I don't think it returns an empty object in 2019.05. (Check the CSG
tree.) What I think is happening is that intersection in 2019.05
ignores the empty object.
Having it return nothing would break things in a different way. want_sphere = false; module foo() { children(1); } foo() { cube(); if (want_sphere) sphere(); cylinder(); } should emit either a sphere or nothing, depending on how
want_sphere is set. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
-- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
In reply to this post by JordanBrown
On 12/25/2020 12:12 PM, Jordan Brown
wrote:
I don't think it returns an empty object in 2019.05. (Check the CSG tree.) I mis-wrote. I don't think it returns *nothing* in 2019.05. In both 2019.05 and the current build, if(false) yields an empty group() in the CSG tree. As do echo(), for()-that-never-runs, and empty modules. The difference is in what intersection() and difference() do with children that don't contain any solid-generators. 2019.05's difference() and intersection() ignore children that don't contain anything that generates a solid. Thus echo, if(false), "translate([0,0,0]);", empty modules, et cetera are all ignored. An intersection or difference of a cube or other solid, that yields nothing, is *not* ignored. The current build respects those "super empty" structures. What I think is happening is that intersection in 2019.05 ignores the empty object. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
How do you view the CSG tree?
I noticed that the example you posted works the same in both versions, so that does imply that it returns an empty object and that the question is maybe what intersection or difference do with empty objects. Here's a simple example: difference(){ union() {}; cube(); } The above code produces a cube in 2019.05 and nothing in RC3. To me the old behavior makes more sense: if you haven't specified an object then ignore it rather than "no object" being the same as the empty set. I could see leaving the current behavior but introducing a nothing() object that lets you not return geometry. I could also see changing difference and intersection to the old behavior and having a nothing() object that acts like the empty set. But maybe this second case doesn't work because the change fixed a bug? It seems like "if" is a weaker tool if it always has to return geometry that is acted upon. JordanBrown wrote > On 12/25/2020 12:12 PM, Jordan Brown wrote: >> I don't think it returns an empty object in 2019.05. (Check the CSG >> tree.) > > I mis-wrote. I don't think it returns *nothing* in 2019.05. In both > 2019.05 and the current build, if(false) yields an empty group() in the > CSG tree. As do echo(), for()-that-never-runs, and empty modules. > > The difference is in what intersection() and difference() do with > children that don't contain any solid-generators. > > 2019.05's difference() and intersection() ignore children that don't > contain anything that generates a solid. > Thus echo, if(false), "translate([0,0,0]);", empty modules, et cetera > are all ignored. > An intersection or difference of a cube or other solid, that yields > nothing, is *not* ignored. > > The current build respects those "super empty" structures. > >> What I think is happening is that intersection in 2019.05 ignores the >> empty object. >> >> Having it return nothing would break things in a different way. >> >> want_sphere = false; >> >> module foo() { >> children(1); >> } >> >> foo() { >> cube(); >> if (want_sphere) sphere(); >> cylinder(); >> } >> >> should emit either a sphere or nothing, depending on how want_sphere >> is set. >> >> >> _______________________________________________ >> OpenSCAD mailing list >> > Discuss@.openscad >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > > > _______________________________________________ > OpenSCAD mailing list > Discuss@.openscad > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org -- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
Administrator
|
adrianv wrote
> How do you view the CSG tree? Design/Display-CSG-tree ----- OpenSCAD Admin - email* me if you need anything, or if I've done something stupid... * on the Forum, click on my MichaelAtOz label, there is a link to email me. Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work. Obviously inclusion of works of previous authors is not included in the above. -- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
OpenSCAD Admin - email* me if you need anything, or if I've done something stupid...
* on the Forum, click on my MichaelAtOz label, there is a link to email me. Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work. Obviously inclusion of works of previous authors is not included in the above. |
In reply to this post by JordanBrown
Now that I know how to look at CSG trees I did some inspection.
It looks like some objects have changed behavior. echo and assert give group(); in 2019.05 but give nothing in RC3. if(false) and for(never) give group(); in both versions. And I discovered something really bad: it's now a warning to write a for statement that doesn't run. This is a terrible idea. It's going to break a bunch of base cases and degenerate cases all over the place. JordanBrown wrote > On 12/25/2020 12:12 PM, Jordan Brown wrote: >> I don't think it returns an empty object in 2019.05. (Check the CSG >> tree.) > > I mis-wrote. I don't think it returns *nothing* in 2019.05. In both > 2019.05 and the current build, if(false) yields an empty group() in the > CSG tree. As do echo(), for()-that-never-runs, and empty modules. > > The difference is in what intersection() and difference() do with > children that don't contain any solid-generators. > > 2019.05's difference() and intersection() ignore children that don't > contain anything that generates a solid. > Thus echo, if(false), "translate([0,0,0]);", empty modules, et cetera > are all ignored. > An intersection or difference of a cube or other solid, that yields > nothing, is *not* ignored. > > The current build respects those "super empty" structures. > >> What I think is happening is that intersection in 2019.05 ignores the >> empty object. >> >> Having it return nothing would break things in a different way. >> >> want_sphere = false; >> >> module foo() { >> children(1); >> } >> >> foo() { >> cube(); >> if (want_sphere) sphere(); >> cylinder(); >> } >> >> should emit either a sphere or nothing, depending on how want_sphere >> is set. >> >> >> _______________________________________________ >> OpenSCAD mailing list >> > Discuss@.openscad >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > > > _______________________________________________ > OpenSCAD mailing list > Discuss@.openscad > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org -- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
I think it is only a warning in for() if the range is literal. So a loop that will never run with any input variables is a warning but one that sometimes doesn't run is OK. On Sat, 26 Dec 2020 at 00:14, adrianv <[hidden email]> wrote: Now that I know how to look at CSG trees I did some inspection. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
In reply to this post by adrianv
On Fri, Dec 25, 2020 at 10:14 AM adrianv <[hidden email]> wrote: I recall this being discussed a while back and I'm not sure about what the The change was introduced by https://github.com/openscad/openscad/pull/3342 which was a fix for at least 4 existing issues: On Fri, Dec 25, 2020 at 2:38 PM Jordan Brown <[hidden email]> wrote:
Jordan is correct here, the old version basically had no concept of empty geometry and treated it the same as "no geometry" (eg an echo or assert node without children). So I guess the issue is now whether or not an untaken conditional should be counted as empty geometry or not geometry at all. Without enabling the experimental feature of "lazy-union", a group essentially implies a union. So an empty group is treated much like "union() {}" which means "the empty set" of geometry. Hans _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
I think a false if or an empty for() should not create empty geometry, just as echo() doesn't. It is more backwards compatible and you can always force empty geometry with else union(); On Sat, 26 Dec 2020 at 14:40, Hans L <[hidden email]> wrote:
_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
I think it's a strong argument that you can always insert empty geometry if
you need it, but there's no way to remove empty geometry once it's been created, so I agree with nophead that false if and empty for should behave like echo and assert and not create any geometry. Even though Jordan pointed out how either choice can break backwards compatibility, my feeling is that the current version (where for and if generate empty geometry) breaks more things than the alternative, and the fixes for those broken things are more painful, since you have to repeat blocks of code instead of just adding an "else union();" nophead wrote > I think a false if or an empty for() should not create empty geometry, > just > as echo() doesn't. It is more backwards compatible and you can always > force > empty geometry with else union(); > > On Sat, 26 Dec 2020 at 14:40, Hans L < > thehans@ > > wrote: > >> >> On Fri, Dec 25, 2020 at 10:14 AM adrianv < > avm4@ > > wrote: >> >>> I recall this being discussed a while back and I'm not sure about what >>> the >>> ultimate conclusion was, and I can't remember what the issue was that >>> gave >>> rise to the change. >>> >> >> The change was introduced by >> https://github.com/openscad/openscad/pull/3342 >> which was a fix for at least 4 existing issues: >> https://github.com/openscad/openscad/issues/666 >> https://github.com/openscad/openscad/issues/3311 >> https://github.com/openscad/openscad/issues/3312 >> https://github.com/openscad/openscad/issues/3416 >> >> On Fri, Dec 25, 2020 at 2:38 PM Jordan Brown < >> > openscad@.maileater >> wrote: >> >>> On 12/25/2020 12:12 PM, Jordan Brown wrote: >>> >>> I don't think it returns an empty object in 2019.05. (Check the CSG >>> tree.) >>> >>> >>> I mis-wrote. I don't think it returns *nothing* in 2019.05. In both >>> 2019.05 and the current build, if(false) yields an empty group() in the >>> CSG >>> tree. As do echo(), for()-that-never-runs, and empty modules. >>> >>> The difference is in what intersection() and difference() do with >>> children that don't contain any solid-generators. >>> >>> 2019.05's difference() and intersection() ignore children that don't >>> contain anything that generates a solid. >>> Thus echo, if(false), "translate([0,0,0]);", empty modules, et cetera >>> are >>> all ignored. >>> An intersection or difference of a cube or other solid, that yields >>> nothing, is *not* ignored. >>> >> >> Jordan is correct here, the old version basically had no concept of >> empty >> geometry and treated it the same as "no geometry" (eg an echo or assert >> node without children). >> So I guess the issue is now whether or not an untaken conditional should >> be counted as empty geometry or not geometry at all. >> >> Without enabling the experimental feature of "lazy-union", a group >> essentially implies a union. So an empty group is treated much like >> "union() {}" which means "the empty set" of geometry. >> >> Hans >> >> _______________________________________________ >> OpenSCAD mailing list >> > Discuss@.openscad >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >> > > _______________________________________________ > OpenSCAD mailing list > Discuss@.openscad > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org -- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
In reply to this post by JordanBrown
On 12/25/2020 12:38 PM, Jordan Brown
wrote:
An intersection or difference of a cube or other solid, that yields nothing, is *not* ignored. Correction: I swear that I tested that, but today I test it and 2019.05 *does* ignore such an empty set. This yields two cubes: module nothing1() { } module nothing2() { intersection() { cube(); translate([10,0,0]) cube(); } } intersection() { cube(); nothing1(); } translate([20,0,0]) { intersection() { cube(); nothing2(); } } _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
In reply to this post by adrianv
[ I'm not sure whether my commentary
below really adds value. However, it helped me to work through
some of the issues and think about the issues in a somewhat
structured way, so I'll send it out. The TL;DR version is that
there probably needs to be a kind of nothingness that is between
"absolutely nothing" and "an empty set". ]
On 12/26/2020 7:02 AM, adrianv wrote: I think it's a strong argument that you can always insert empty geometry if you need it, but there's no way to remove empty geometry once it's been created, so I agree with nophead that false if and empty for should behave like echo and assert and not create any geometry. Even though Jordan pointed out how either choice can break backwards compatibility, my feeling is that the current version (where for and if generate empty geometry) breaks more things than the alternative, and the fixes for those broken things are more painful, since you have to repeat blocks of code instead of just adding an "else union();" It's hard to talk about different kinds of "nothing", and terms like "object" are overloaded, so I'll define a couple of terms for what I'm about to say: When I say "nothing", it means that there is absolutely nothing generated. Comments generate nothing. Assignments generate nothing. The program "", the program consisting of no text at all, generates nothing. When I say "set", I mean a (possibly empty) collection of points. When I say "empty set", it means that there are operations, but that the result contains no points. Examples include union() {} and intersection() { cube(); translate([10,0,0]) cube() }. When I say "module-invocation-like construct", I mean anything that looks like "xxx(...);" or "xxx(...) { ... }". As a special case, I include "if(...) { ... } else { ... }". --- I think the next section describes fact, not opinion. --- In 2019.05:
In RC3:
Note that RC3 has made two changes:
--- Now for opinion --- I think that the change to intersection and difference is
correct. It matches mathematics, and it avoids perverse results
in (admittedly contrived) cases. For instance, in 2019.05: module foo(d) { intersection() { cube(); intersection() { cube(); translate([d,0,0]) cube(); } } } will yield part of a cube if d is -1 through 1, but will yield a full cube if d is outside that range. In particular, foo(0.999) will yield a very thin slice of a cube, but foo(1.001) will yield a full cube. (The behavior of foo(1) is left as a question for philosophers.)
I have mixed feelings about the change to echo() and assert(). On the one hand, it's intuitively sensible. These are clearly not attempts to describe geometry. On the other hand, they introduce perverse cases. echo() produces nothing, but if(true)echo() produces an empty set. A module consisting only of an invocation of echo() produces an empty set. children(i) where the specified child is an echo yields nothing. Yeah, I'll have to come down on the side of "no" on that one. I
can't even find a clear way to describe the behavior of echo().
It's not nothing, but it's not an empty set. (But see below for
an idea.)
As for the "failing if yields nothing" proposal, I have to come
down solidly against. (But again, see below.) Although again it's
intuitively sensible, it makes a hash of what $children means and
the indexing of children. What does this print? (Note that
rands(0,1,1)[0] yields a random number between 0 and 1.) module count() { echo($children); echo($children); } count() { if (rands(0,1,1)[0] > 0.5) { cube(); } } Before "failing if yields nothing", you don't need to evaluate
the children to determine the value of $children. With it, you
have to evaluate them to determine whether or not they generate
nothing. And if you recursively apply nothingness (so that an if
whose body yields nothing, yields nothing, and so a module whose
body yields nothing, yields nothing) then you have to evaluate
arbitrarily deeply. What if some of that evaluation is erroneous
when done in the wrong context? value = -1; module foo() { echo($children); if (value >= 0) { children(0); } } function safe_sqrt(v) = assert(v >= 0) sqrt(v); foo() { if (safe_sqrt(value) > 1) { cube(); } } When do you calculate the value of $children? Before doing anything with the module? What if the children's behavior depends on $ variables set by the module? Do you re-evaluate $children each time it's used? Do you re-evaluate the child list each time children() is used? module perverse() { $mychildren = $children; echo($children); } perverse() { if ($mychildren == 0) { cube(); } } That's purposely perverse, but I've had programs that had children that conditionally supplied geometry depending on $ variables set by the parent. ---module foo() { for ($i=[0:3]) { children(); } } foo() { if ($i != 0) { cube(); } } The best idea I come up with to comply with some of the intuitively desirable behavior of echo() and failing if() is to define a new kind of nothingness. Call it NULL.
(Note: I suspect that PR#3555 may effectively implement some of this, though defined in terms of counting nodes in the AST tree for $children and children() purposes.) HOWEVER, I'll point out that "null" and similar concepts have been very difficult subjects in other languages. C programmers often mess up the difference between NULL and empty strings. JavaScript programmers are treated to something like five different kinds of nothingness - undefined, null, {}, [], and "" - and confusion involving them is common. Programmers in many languages often have trouble with the idea of arrays with no entries, without even bringing in the concept of NULL. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
I agree that operations should interact correctly with empty sets.
Current behavior of RC3 is that echo() and assert() return your NULL. They behave like children but generate no geometry. Note that echo is only actually run if the child is actually instantiated. module foo() { intersection(){ children(0); children(1); } } foo() { cube(); echo("child 1"); cylinder(); } Above code produces a cube and displays "child 1". If you swap the echo and the cylinder then you get the intersection of cube with cylinder and nothing is printed. This is arguably somewhat confusing. Consider this: foo() { cube(); cylinder(); assert(false, "fail!"); } The assert never runs. Also arguably confusing. But this behavior is consistent between versions. It seems like the truth is that passing assert or echo as children is just weird and confusing. Is there any use in doing this? My proposal, which is consistent with your suggestion, is that false if and empty for should behave the same as echo, namely that they behave as your NULL, which already exists, like it or not. And I think the behavior of echo (and assert) is far more confusing than my proposed behavior for if() and for() would be. However, in thinking carefully about this I wonder if any of the options really avoids confusion. It seems like the main case where this matters with the standard language and with empty if or for is for intersection(), because an actual empty object eliminates your model, and there's no clean way to fix it. With union or difference it doesn't matter. (Is there any way to add an "everything" object to openscad?) But for user written modules, a variety of things may happen. Consider a module spread() that places its child i at position [10*i,0,0]. If you include echo() as a child you get a blank space. And the same thing happens (in both language versions) if you pass "if (false) cube();" This seems like unexpected behavior to me. But the only way around it is to give the module the ability to identify modules as NULL. And this starts to get messy. Most modules will want to count the non-null children and skip all the NULL ones...which amounts to treating NULL children as nonexistent. Does this end up any different than actually not passing the children at all? JordanBrown wrote > The best idea I come up with to comply with some of the intuitively > desirable behavior of echo() and failing if() is to define a new kind of > nothingness. Call it NULL. > > * echo() and failing if(), among others, would yield NULL. > * NULL is not nothing. It counts as a child. (So all module-like > invocations yield children, avoiding the $children problem described > above.) > * Intersection and difference would ignore NULL children. > * Ideally, many operations on NULL would yield NULL. > o A module whose contents all yield NULL (or with no contents at > all) would yield NULL. > o An "if" whose children all yield NULL (or with no children) > would yield NULL. > o children(i), where the corresponding child is NULL, would yield > NULL. > * However, boolean operations on NULL should probably yield empty > sets. In particular, union() {} should yield an empty set. > intersection() { NULL } would ignore the NULL child, and would then > yield an empty set. > > That seems to address most of the concerns. The fact that echo() counts > as a child might cause confusion for some. > > (Note: I suspect that PR#3555 may effectively implement some of this, > though defined in terms of counting nodes in the AST tree for $children > and children() purposes.) -- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
Keep in mind that echo and assert can both have children. They only yield "NULL" if no geometry child nodes are given. This was partly addressed for another issue: https://github.com/openscad/openscad/issues/3131 which is different since the 2019 release. In the 2019 release, assert would render children if given, but echo would not. Now they both do. That's why I mentioned in my first post in this thread "(eg an echo or assert node **without children**).", [emphasis now added] but may have been too subtle on that point. So if you want to use echo or assert in a way that doesn't affect the child count then you can nest your actual geometry as children of them (probably preferably on the first element[s]) foo() { assert(false, "fail!") cube(); cylinder(); } On Sat, Dec 26, 2020 at 6:53 PM adrianv <[hidden email]> wrote: I agree that operations should interact correctly with empty sets. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
In reply to this post by adrianv
[ Resend from correct address. ]
On 12/26/2020 4:52 PM, adrianv wrote: Current behavior of RC3 is that echo() and assert() return your NULL. They behave like children but generate no geometry. And intersection() in particular ignores children with no geometry. Note that echo is only actually run if the child is actually instantiated. Yes. None of the children are run unless instantiated. Violating that risks running them in cases where their parents know they won't work. It seems like the truth is that passing assert or echo as children is just weird and confusing. Somewhat. But they obey the general rule: they run when you say children(i) and refer to them. Is there any use in doing this? You might leave an echo as a placeholder for future work. Even for a selective parent, it might make sense. Consider for instance a decorated_cube() module that builds a cube and and translates and rotates each of its six children to be on its faces.union() { part1(); echo("need to write part 2"); } decorated_cube() { face1(); face2(); echo("need to design face 3"); ... } My proposal, which is consistent with your suggestion, is that false if and empty for should behave the same as echo, namely that they behave as your NULL, Yes, that's what I was thinking. which already exists, like it or not. Well, in the RC, which is 95% existing but not 100%. (I think my primary contribution to this discussion might be in giving it a name.) And I think the behavior of echo (and assert) is far more confusing than my proposed behavior for if() and for() would be. Perhaps. One of the things that was making my head hurt was that echo("foo"); and if (true) echo("foo"); currently behave differently in the RC, as children of
intersection(). Similarly that a module that exists only to wrap
around echo() behaves differently than a plain echo(). Even stranger is that plain echo generates NULL but generates an empty set.if (false) echo("foo"); However, in thinking carefully about this I wonder if any of the options really avoids confusion. Nope :-(. It seems like the main case where this matters with the standard language and with empty if or for is for intersection(), because an actual empty object eliminates your model, and there's no clean way to fix it. Intersecting with an *actual* empty-set object (say, the intersection of two disjoint objects) absolutely *should* eliminate your model. YAFIYGI. The question is what constructs should yield empty sets. With union or difference it doesn't matter. With difference it matters a little, because the first child is special (it's positive). difference() { if (false); cube(); } yields a cube in 2019.05 and an empty set in RC3. But for user written modules, a variety of things may happen. Consider a module spread() that places its child i at position [10*i,0,0]. Yep. Exactly the confusing case I was thinking of. But the only way around it is to give the module the ability to identify modules as NULL. But you can't know that it's NULL until you instantiate it, and then it's too late. I think Very Bad Things(tm) will happen if you try to have any of these constructs not yield children. The rule needs to be simple: they get evaluated when and if their parents say to evaluate them. You have to just understand that they *do* generate children. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
In reply to this post by thehans
On 12/26/2020 6:12 PM, Hans L wrote:
You just made my head explode. Now there are brains and blood all over my keyboard. Concur that echo() and assert() should behave the same with respect to their children. (Slight inclination towards "it's an error", but not enough to fight.) _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
In reply to this post by JordanBrown
JordanBrown wrote
>> It seems like the truth is that passing assert or echo as children is >> just weird and confusing. > > Somewhat. But they obey the general rule: they run when you say > children(i) and refer to them. Yes, everything makes sense if you understand the technical details. But for a casual user it's going to be surprising and puzzling, I think. The note that assert and echo take children is interesting. It makes sense...but I don't think I realized it. (Even though that's how assert and echo act in let() statements. In fact, you have to use assert with children in functions if you want to avoid dummy variables. The same is true of echo...but I usually only insert it for debugging so I'm not bothered by the dummy vars.) >> Is there any use in doing this? > > You might leave an echo as a placeholder for future work. > > union() { > part1(); > echo("need to write part 2"); > } I think in my own work I'd be more inclined to stick a cube() or something in as a placeholder than text. Or just a comment. >> My proposal, which is consistent with your suggestion, is that false if >> and >> empty for should behave the same as echo, namely that they behave as your >> NULL, > > Yes, that's what I was thinking. > >> which already exists, like it or not. > > Well, in the RC, which is 95% existing but not 100%. (I think my > primary contribution to this discussion might be in giving it a name.) What is the missing 5%? >> And I think the behavior of echo (and assert) is far more confusing than >> my proposed behavior for if() and for() would be. > > Perhaps. One of the things that was making my head hurt was that > > echo("foo"); > > and > > if (true) echo("foo"); > > currently behave differently in the RC, as children of intersection(). > Similarly that a module that exists only to wrap around echo() behaves > differently than a plain echo(). > > Even stranger is that plain echo generates NULL but > > if (false) echo("foo"); > > generates an empty set. Wouldn't these things be "fixed" by having if statements not create geometry? >> However, in thinking carefully about this I wonder if any of the options >> really avoids confusion. > > Nope :-(. > >> It seems like the main case where this matters with the standard language >> and with empty if or for is for intersection(), because an actual empty >> object eliminates your model, and there's no clean way to fix it. > > Intersecting with an *actual* empty-set object (say, the intersection of > two disjoint objects) absolutely *should* eliminate your model. > YAFIYGI. The question is what constructs should yield empty sets. > >> With union or difference it doesn't matter. > > With difference it matters a little, because the first child is special > (it's positive). > > difference() { > if (false); > cube(); > } > > yields a cube in 2019.05 and an empty set in RC3. I'm in agreement that intersection with empty set should give the empty set. The problem is that there's no way to specify the complement of the empty set. So I can't do intersection(){ if (condition) thing(); else everything(); otherthing(); } Being able to do that would solve the problem with intersections in a simple manner. I assume that it's impossible to implement the everything() object. For difference, yes, it matters what the first object is, and your example does give a case where things change. But it's not an example that seems like it has a use case. I can't think of a situation where you want A-B in one case and B in another case. It's just not likely. It's also simple because you've got only one possible meaningful condition, whether A is enabled or not. Consider an intersection like: intersection(){ mainthing(); if (parm1) thing1(); if (parm2) thing2(); if (parm3) thing3(); if (parm4) thing4(); } Now if I need to handle this it explodes into a real mess because I have to consider every combination of the 4 variables separately, so I need 2^4=16 cases instead of 4 cases. Presumably the only realistic way to solve this is to write an "everything" module that is a superset of mainthing(), or do something like intersection(){ mainthing(); if (parm1) thing1(); else mainthing(); if (parm2) thing2(); else mainthing(); if (parm3) thing3(); else mainthing(); if (parm4) thing4(); else mainthing(); } Of course, the other solution is to avoid intersection() for cases like this and use difference() instead. Then there's no problem. >> But for user written modules, a variety of things may happen. Consider a >> module spread() that places its child i at position [10*i,0,0]. > > Yep. Exactly the confusing case I was thinking of. > >> But the only way around it is to give the module the ability to identify >> modules as NULL. > > But you can't know that it's NULL until you instantiate it, and then > it's too late. Yeah. At least this is not a change from the old behavior. That is, the spread module behaves the same in the RC3 as it does in the 2019 version. In either case, the empty geometry or the NULL object get treated as a child that renders as nothing, and you get a gap. I'm left wondering if it really matters whether if(false) and empty for return an empty set or a NULL, since most of the time it will give the same result anyway. -- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
[ Sigh. Not my day. First I sent this
from the wrong address, then when I went to resend it from the
right address I resent the one of Adrian's messages instead of
this one. ]
On 12/26/2020 6:58 PM, adrianv wrote: JordanBrown wroteIt seems like the truth is that passing assert or echo as children is just weird and confusing.Somewhat. But they obey the general rule: they run when you say children(i) and refer to them.Yes, everything makes sense if you understand the technical details. But for a casual user it's going to be surprising and puzzling, I think. Yes. What I'm saying is that if it's going to be surprising and puzzling, at least there should be a simple rule. (And I think that all variations that have gotten past the first level of discussion have that simple rule, that all module-like invocations yield children.) I think in my own work I'd be more inclined to stick a cube() or something in as a placeholder than text. Or just a comment. A comment won't be enough if you're leaving a placeholder for child #3 of 6. which already exists, like it or not.Well, in the RC, which is 95% existing but not 100%. (I think my primary contribution to this discussion might be in giving it a name.)What is the missing 5%? I wasn't saying that the functionality wasn't in the RC... but rather that the RC is an RC, not a release, so doesn't 100% exist. Behavior in a non-release can change; behavior in a release should normally remain constant. (But: the RC has NULL functionality for echo, assert, intersection, and difference, but not for if, for, and modules. Hans has a PR for if.) [ if(true)echo() and if(false)echo() are different from echo() ] Well, by having failing if not generate geometry - having it generate NULL - and by having successful if (or else) yield whatever its contents yield (which might or might not be NULL). I'm in agreement that intersection with empty set should give the empty set. The problem is that there's no way to specify the complement of the empty set. So I can't do NULL would address this particular need, because a failing if would yield NULL, and intersection would be defined to ignore NULL. So for intersection purposes, NULL would be sort of like everything(). For difference, yes, it matters what the first object is, and your example does give a case where things change. But it's not an example that seems like it has a use case. Agreed. It only has a confusion case, where somebody has put an echo() as the first child to a difference. I'm left wondering if it really matters whether if(false) and empty for return an empty set or a NULL, since most of the time it will give the same result anyway. Intersection is the big one. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
In RC4, if (false) doesn't create a group in the tree, so intersection works
the way it did before. JordanBrown wrote > [ Sigh. Not my day. First I sent this from the wrong address, then > when I went to resend it from the right address I resent the one of > Adrian's messages instead of this one. ] > > On 12/26/2020 6:58 PM, adrianv wrote: >> JordanBrown wrote >>>> It seems like the truth is that passing assert or echo as children is >>>> just weird and confusing. >>> Somewhat. But they obey the general rule: they run when you say >>> children(i) and refer to them. >> Yes, everything makes sense if you understand the technical details. But >> for a casual user it's going to be surprising and puzzling, I think. > > Yes. > > What I'm saying is that if it's going to be surprising and puzzling, at > least there should be a simple rule. > > (And I think that all variations that have gotten past the first level > of discussion have that simple rule, that all module-like invocations > yield children.) > >> I think in my own work I'd be more inclined to stick a cube() or >> something in as a placeholder than text. Or just a comment. > > A comment won't be enough if you're leaving a placeholder for child #3 of > 6. > >>>> which already exists, like it or not. >>> Well, in the RC, which is 95% existing but not 100%. (I think my >>> primary contribution to this discussion might be in giving it a name.) >> What is the missing 5%? > > I wasn't saying that the functionality wasn't in the RC... but rather > that the RC is an RC, not a release, so doesn't 100% exist. Behavior in > a non-release can change; behavior in a release should normally remain > constant. > > (But: the RC has NULL functionality for echo, assert, intersection, and > difference, but not for if, for, and modules. Hans has a PR for if.) > >> [ if(true)echo() and if(false)echo() are different from echo() ] >> Wouldn't these things be "fixed" by having if statements not create >> geometry? > > Well, by having failing if not generate geometry - having it generate > NULL - and by having successful if (or else) yield whatever its contents > yield (which might or might not be NULL). > >> I'm in agreement that intersection with empty set should give the >> empty set. The problem is that there's no way to specify the >> complement of the empty set. So I can't do >> intersection(){ >> if (condition) thing(); else everything(); >> otherthing(); >> } > > NULL would address this particular need, because a failing if would > yield NULL, and intersection would be defined to ignore NULL. So for > intersection purposes, NULL would be sort of like everything(). > >> For difference, yes, it matters what the first object is, and your >> example >> does give a case where things change. But it's not an example that seems >> like it has a use case. > > Agreed. It only has a confusion case, where somebody has put an echo() > as the first child to a difference. > > >> I'm left wondering if it really matters whether if(false) and empty >> for return an empty set or a NULL, since most of the time it will give >> the same result anyway. > > Intersection is the big one. > > > _______________________________________________ > OpenSCAD mailing list > Discuss@.openscad > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org -- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
Free forum by Nabble | Edit this page |