BOSL2 icon indicating copy to clipboard operation
BOSL2 copied to clipboard

Are tags meant to not be useable when wrapped in attachable()?

Open jon-gilbert opened this issue 2 years ago • 19 comments

What's the expected behavior of models within attachable() that have a tag() applied to part of the model, when diff() is called for that tag upstream of the attachable() call?

The behavior I'm seeing is:

  • applying a tag to a partial shape within a module can be diff()'d upstream of that module
  • applying a tag to a partial shape within a module, when that shape is attachable, regardless of whether that shape is attached or not, cannot be diff()'d upstream of that module
[This may be probably better described as this long-ish, collapsed example.]
include <BOSL2/std.scad>

module test(d, l) {
    // cyl <- "remove" <- tube
    cyl(d=d, l=l, anchor=CENTER)
        attach(CENTER, CENTER)
            tag("remove")
                tube(id=d, wall=1, l=l, anchor=CENTER);
}

module atest(d, l) {
    // attachable <- cyl <- "remove" <- tube
    attachable(CENTER, 0, UP, d=d, h=l) {
        cyl(d=d, l=l, anchor=CENTER)
            attach(CENTER, CENTER)
                tag("remove")
                    tube(id=d, wall=1, l=l, anchor=CENTER);
        children();
    }
}

module invert_test(d, l) {
    // tube <- "" <- cyl
    // making this work should require that it be called underneath 
    // the "remove" tag before diffing
    tube(id=d, wall=1, l=l, anchor=CENTER)
        attach(CENTER, CENTER)
            tag("")
                cyl(d=d, l=l, anchor=CENTER);
}

module invert_atest(d, l) {
    // attachable <- tube <- "" <- cyl
    // making this work should require that it be called underneath 
    // the "remove" tag before diffing
    attachable(CENTER, 0, UP, d=d, h=l) {
        tube(id=d, wall=1, l=l, anchor=CENTER)
            attach(CENTER, CENTER)
                tag("")
                    cyl(d=d, l=l, anchor=CENTER);
        children();
    }
}



back_half(300)
xdistribute(spacing=15) {
    // tests 1 .. 4:
    // both modes, no diff(): all of these should have a diam of 12, nothing removed:
    test(10, 40); // ok
    
    atest(10, 40); // ok
    
    invert_test(10, 40); // ok
    
    invert_atest(10, 40); // ok

    
    // tests 5 .. 8:
    // both modes, diff()'d: all of these should have a diam of 10
    diff()
        test(10, 40); // ok

    diff()
        atest(10, 40); // ok

    diff()
        tag("remove")
            invert_test(10, 40); // ok
    
    diff()
        tag("remove")
            invert_atest(10, 40); // not-ok: the entire cyl is not present. the expectation 
                                  // is that the blank tag within invert_atest() will 
                                  // negate the "remove" tag applied, excepting the 
                                  // cylinder from the diff() operation.


    // tests 9 .. 12:
    // both modes, as a child of cuboid: none of these should have material removed, 
    // a diam of 12 imbedded into a cuboid
    cuboid(13, anchor=CENTER)
        test(10, 40); // ok
    
    cuboid(13, anchor=CENTER)
        atest(10, 40); // ok
    
    cuboid(13, anchor=CENTER)
        invert_test(10, 40); // ok
    
    cuboid(13, anchor=CENTER)
        invert_atest(10, 40); // ok


    // tests 13 .. 16:
    // both modes, as a child of a cuboid, diff()'d: these should 
    // have the cuboid, in which negative space is removed, inside of which 
    // is a cyl with a diam of 10: 
    diff()
        cuboid(13, anchor=CENTER)
            test(10, 40); // ok

    diff()
        cuboid(13, anchor=CENTER)
            atest(10, 40); // not-ok: the negative space between the cyl 
                           // and the cuboid is not removed; the cyl is 
                           // the correct diameter.

    diff()
        cuboid(13, anchor=CENTER)
            tag("remove")
                invert_test(10, 40); // ok
    
    diff()
        cuboid(13, anchor=CENTER)
            tag("remove")
                invert_atest(10, 40); // not-ok: the negative space is present;
                                      // the inner cyl above and below the cuboid 
                                      // is trucated; the cyl is the correct diameter


    // tests 17, 18:
    // attachable-only modes, as a child (*not* attached) of cuboid, diff()'d: 
    // these should have the cuboid, in which negative space is removed, inside of 
    // which is a cyl with a diam of 10:
    diff()
        cuboid(13, anchor=CENTER)
            attach(CENTER, CENTER)
                atest(10, 40); // not-ok: the negative space is not removed;
                               // the cyl has the correct diam 

    diff()
        cuboid(13, anchor=CENTER)
            attach(CENTER, CENTER)
                tag("remove")
                    invert_atest(10, 40); // not-ok: the negative space is present; 
                                          // the inner cyl above and below the 
                                          // cuboid is trucated; the cyl is 
                                          // the correct diameter (as in test 16).
}


// sizing comparisons:
down(15)
    xdistribute(spacing=15) {
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
    }

image

What I'm trying to get is a module that yields an attachable shape that comes coupled with portions that may be pre-tagged for removal by an upstream caller. Think of an axle that comes with a pre-calculated axle-bore that only needs one attachable step. A contrived example would be something like:

diff()
   cuboid(40, anchor=CENTER)
      attach(CENTER, CENTER)
         axle(d=10, l=50);

...in which axle() would come coupled with a cylinder wrapped with a tube that is tagged for removal. Today I'm doing something akin to:

diff()
   cuboid(40, anchor=CENTER) {
      attach(CENTER, CENTER)
         tag("remove")
            axle(d=10.2, l=50);
      attach(CENTER, CENTER)
            axle(d=10, l=50);
   }

...but this is cumbersome.

It's clear I can do this with modules that aren't attachable(), but wrapping the shape and the tag with attachable() removes the tag within the yielded model. Is this intended? There's no $tag removal side-effect listed in attachable() or attach(), but I also don't see an explicit point where $tag would have been removed within attachable(), so I'm a little confused how these two are meant to interact.

(I don't know if this classifies as a bug per se; and while it might be classified a documentation issue, it feels really more like a general question than anything else, and if that's not kosher then I already apologize.)

jon-gilbert avatar Aug 06 '23 17:08 jon-gilbert

Can you post an example specifically of the broken code that's not doing what you want and explanation about what the exact desired outcome is?

adrianVmariano avatar Aug 06 '23 18:08 adrianVmariano

Er. There's a lengthy example set along with expectations in the above post (though it's hidden under "[This may be probably better described as this long-ish, collapsed example]" text; so maybe that doesn't work in github after all). I'll duplicate it in this comment here:

include <BOSL2/std.scad>

module test(d, l) {
    // cyl <- "remove" <- tube
    cyl(d=d, l=l, anchor=CENTER)
        attach(CENTER, CENTER)
            tag("remove")
                tube(id=d, wall=1, l=l, anchor=CENTER);
}

module atest(d, l) {
    // attachable <- cyl <- "remove" <- tube
    attachable(CENTER, 0, UP, d=d, h=l) {
        cyl(d=d, l=l, anchor=CENTER)
            attach(CENTER, CENTER)
                tag("remove")
                    tube(id=d, wall=1, l=l, anchor=CENTER);
        children();
    }
}

module invert_test(d, l) {
    // tube <- "" <- cyl
    // making this work should require that it be called underneath 
    // the "remove" tag before diffing
    tube(id=d, wall=1, l=l, anchor=CENTER)
        attach(CENTER, CENTER)
            tag("")
                cyl(d=d, l=l, anchor=CENTER);
}

module invert_atest(d, l) {
    // attachable <- tube <- "" <- cyl
    // making this work should require that it be called underneath 
    // the "remove" tag before diffing
    attachable(CENTER, 0, UP, d=d, h=l) {
        tube(id=d, wall=1, l=l, anchor=CENTER)
            attach(CENTER, CENTER)
                tag("")
                    cyl(d=d, l=l, anchor=CENTER);
        children();
    }
}



back_half(300)
xdistribute(spacing=15) {
    // tests 1 .. 4:
    // both modes, no diff(): all of these should have a diam of 12, nothing removed:
    test(10, 40); // ok
    
    atest(10, 40); // ok
    
    invert_test(10, 40); // ok
    
    invert_atest(10, 40); // ok

    
    // tests 5 .. 8:
    // both modes, diff()'d: all of these should have a diam of 10
    diff()
        test(10, 40); // ok

    diff()
        atest(10, 40); // ok

    diff()
        tag("remove")
            invert_test(10, 40); // ok
    
    diff()
        tag("remove")
            invert_atest(10, 40); // not-ok: the entire cyl is not present. the expectation 
                                  // is that the blank tag within invert_atest() will 
                                  // negate the "remove" tag applied, excepting the 
                                  // cylinder from the diff() operation.


    // tests 9 .. 12:
    // both modes, as a child of cuboid: none of these should have material removed, 
    // a diam of 12 imbedded into a cuboid
    cuboid(13, anchor=CENTER)
        test(10, 40); // ok
    
    cuboid(13, anchor=CENTER)
        atest(10, 40); // ok
    
    cuboid(13, anchor=CENTER)
        invert_test(10, 40); // ok
    
    cuboid(13, anchor=CENTER)
        invert_atest(10, 40); // ok


    // tests 13 .. 16:
    // both modes, as a child of a cuboid, diff()'d: these should 
    // have the cuboid, in which negative space is removed, inside of which 
    // is a cyl with a diam of 10: 
    diff()
        cuboid(13, anchor=CENTER)
            test(10, 40); // ok

    diff()
        cuboid(13, anchor=CENTER)
            atest(10, 40); // not-ok: the negative space between the cyl 
                           // and the cuboid is not removed; the cyl is 
                           // the correct diameter.

    diff()
        cuboid(13, anchor=CENTER)
            tag("remove")
                invert_test(10, 40); // ok
    
    diff()
        cuboid(13, anchor=CENTER)
            tag("remove")
                invert_atest(10, 40); // not-ok: the negative space is present;
                                      // the inner cyl above and below the cuboid 
                                      // is trucated; the cyl is the correct diameter


    // tests 17, 18:
    // attachable-only modes, as a child (*not* attached) of cuboid, diff()'d: 
    // these should have the cuboid, in which negative space is removed, inside of 
    // which is a cyl with a diam of 10:
    diff()
        cuboid(13, anchor=CENTER)
            attach(CENTER, CENTER)
                atest(10, 40); // not-ok: the negative space is not removed;
                               // the cyl has the correct diam 

    diff()
        cuboid(13, anchor=CENTER)
            attach(CENTER, CENTER)
                tag("remove")
                    invert_atest(10, 40); // not-ok: the negative space is present; 
                                          // the inner cyl above and below the 
                                          // cuboid is trucated; the cyl is 
                                          // the correct diameter (as in test 16).
}


// sizing comparisons:
down(15)
    xdistribute(spacing=15) {
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
        %cyl(d=10, h=10);
    }

image

jon-gilbert avatar Aug 06 '23 19:08 jon-gilbert

As far as desired outcomes, I'd have expected that this:

module atest(d, l) {
    // attachable <- cyl <- "remove" <- tube
    attachable(CENTER, 0, UP, d=d, h=l) {
        cyl(d=d, l=l, anchor=CENTER)
            attach(CENTER, CENTER)
                tag("remove")
                    tube(id=d, wall=1, l=l, anchor=CENTER);
        children();
    }
}
diff()
      cuboid(13, anchor=CENTER)
          attach(CENTER, CENTER)
              atest(10, 40); 

... would produce a block with a 12-diameter hole through it's long axis, and a cylinder with a diam of 10 and a length of 40 in its middle. Instead, it yields:

image

I can get the desired outcome by not using a module that produces an attachable shape, like this:

include <BOSL2/std.scad>

module test(d, l) {
    // cyl <- "remove" <- tube
    cyl(d=d, l=l, anchor=CENTER)
        attach(CENTER, CENTER)
            tag("remove")
                tube(id=d, wall=1, l=l, anchor=CENTER);
}

diff()
  cuboid(13, anchor=CENTER)
      test(10, 40); 

image

...but that cylinder within the cuboid isn't attachable.

jon-gilbert avatar Aug 06 '23 19:08 jon-gilbert

Sorry, I didn't see the collapsed section. It does work. I didn't know that was possible in github, so I wasn't really alert to that possibility.

I was looking at one of the fail examples, invert_atest(), and it looks like the reason for its behavior is that the "remove" tag applies to the attachable object as a unit. It's had to be 100% sure about things here---as I said this code is like voodoo---but the test for whether an object is displayed or not happens in attachable() so getting different behavior would be a big change. Basically children(0) of attachable gets a test for whether it should be displayed or not, and whether to apply a special color (e.g. from color_this), so if it's got a remove tag and is under a diff() then it will not get run, so you can't make things spring back into existence by changing the tags inside.

So (1) does this make sense and (2) does this explain all of your issues?

I started wondering what would happen if there was an option to attachable() to always show the child.

module tagpass_attachable(
    anchor, spin, orient,
    size, size2, shift,
    r,r1,r2, d,d1,d2, l,h,
    vnf, path, region,
    extent=true,
    cp=[0,0,0],
    offset=[0,0,0],
    anchors=[],
    two_d=false,
    axis=UP,override,
    geom
) {
    dummy1 =
        assert($children==2, "attachable() expects exactly two children; the shape to manage, and the union of all attachment candidates.")
        assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Got: ",anchor))
        assert(is_undef(spin)   || is_vector(spin,3) || is_num(spin), str("Got: ",spin))
        assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient));
    anchor = first_defined([$anchor_override, anchor, CENTER]);
    spin =   default(spin,   0);
    orient = is_def($anchor_override)? UP : default(orient, UP);
    region = !is_undef(region)? region :
        !is_undef(path)? [path] :
        undef;
    geom = is_def(geom)? geom :
        attach_geom(
            size=size, size2=size2, shift=shift,
            r=r, r1=r1, r2=r2, h=h,
            d=d, d1=d1, d2=d2, l=l,
            vnf=vnf, region=region, extent=extent,
            cp=cp, offset=offset, anchors=anchors,
            two_d=two_d, axis=axis, override=override
        );
    m = _attach_transform(anchor,spin,orient,geom);
    multmatrix(m) {
        $parent_anchor = anchor;
        $parent_spin   = spin;
        $parent_orient = orient;
        $parent_geom   = geom;
        $parent_size   = _attach_geom_size(geom);
        $attach_to   = undef;
        $anchor_override=undef;
        //if (_is_shown())
            _color($color) children(0);
        if (is_def($save_color)) {
            $color=$save_color;
            $save_color=undef;
            children(1);
        }
        else children(1);
    }
}

For cases where you want tags inside the object to be respected you could try using the above code and seeing if anything breaks. I fear that a lot of the tagging infrastructure has been built up in this manner. ("WTF did it do that? Hmmm....this seems to fix it.") I tested a lot of cases and did a lot of head scratching when trying to get the things you see in the examples and tutorial to work. The question for this is basically whether the attachable object gets shown incorrectly at some point. If this feature seems actually useful and not a source of breakage we can implement it cleanly with some kind of option instead of a whole extra module. One problem this could exacerbate is exponential explosion in run time, because it would mean going deeper down the tree before things cut cut off.

adrianVmariano avatar Aug 06 '23 19:08 adrianVmariano

I was looking at one of the fail examples, invert_atest(), and it looks like the reason for its behavior is that the "remove" tag applies to the attachable object as a unit.

Yeah, no doubt: I don't really see anything I'm doing getting structured with something like the invert_atest() - maybe there are cases where I'd want to remove everything within the module except for a portion of the shape, though I try to keep things as straightforward as I can and that seems backwards to me - but I did want to see what it did and how it reacted like the other attempts. I imagine mostly I'd try to do a "here is the base shape you asked for, and it comes coupled with another shape, that you must clear out with diff(), in order for the base shape to make sense. Maybe that coupled, removable shape is nothing; maybe it is a buffer of 0.02 the diameter of the base shape; either way, it's on you, the caller, to clear it", so more in line with the atest() examples above.

Your explanation makes sense; and, it seems to explain the behavior. It might be an idea to note this in the side effects of attachable()'s documentation; I can cobble together a PR if that helps.

The tagpass_attachable() provided above seems to work well (in this very specific and probably overly-niche scenario), and gets me what I'm looking for. I don't really use colors so no loss for me there, but I one-hundred percent get the unintended effects of a change like that. I might implement it internally with this project - I'd have to give it some serious thought and testing - though it might make updates to future BOSL2 stuff a little brittle.

Obviously I wouldn't say "no" to having that ability officially implemented within attachable() gated with an option if it makes sense and doesn't break the magic of the feature. OTOH, I strongly suspect there are other things that need doing ;)

Thanks!

jon-gilbert avatar Aug 06 '23 20:08 jon-gilbert

Note that implementing this is trivial. And colors should actually still work correctly. The question of whether it's generally useful is there, though. It would have to be an option because if it was the default behavior, everything would just get shown and tags wouldn't work any more. Basically your "last" object can't be one that was created with this option in force.

adrianVmariano avatar Aug 06 '23 21:08 adrianVmariano

The question of whether it's generally useful is there, though.

nod That's understandable. I would find it extremely useful, and while I can't really speak for other BOSL2 consumers, I suspect I might be in the minority 😁

jon-gilbert avatar Aug 06 '23 21:08 jon-gilbert

It might in fact be useful enough to do. I vaguely recall needing to call attachable twice somewhere to work around this limitation. It would be nice if I could find that example to see if this would in fact address the issue. It's somewhere in BOSL2.... BOSL2 is big enough that I think there are tons of features or components whose use is "minority" but that doesn't mean they aren't worth having.

The main issues I can see are that if you supply children that aren't themselves attachable then the children won't respond properly to any tags at all, and the previously mentioned concern with possibly increasing growth in the tree of children that get evaluated.

adrianVmariano avatar Aug 06 '23 21:08 adrianVmariano

I for one would find this useful as an option. My use case is that I want to create modular attachables comprised of multiple other attachables that include cutouts for things like screw holes, which need to be cut through other attachable parts. Right now I'm implementing this as two different versions of the same attachable so I can tag however I like after the fact, but it would be really nice to be able to just create one attachable with pre-tagged bits according to whatever options I pass in.

To give a concrete example (the one I'm working on at the moment), I have an attachable that is the combination of four BOSL2 cylinders spaced correctly to serve as Mini-ITX motherboard spacers in a computer case. Originally I wanted to optionally include remove-tagged screw holes as part of the whole attachable, so that I could position the bottom of the standoffs to the top of the motherboard tray, but also bore the correct screw holes through the tray - itself a separate attachable - and the standoffs in more or less one step with a diff() after add()ing the two parts together. Hopefully that all makes sense. I can probably generate a little example script if that would make things clearer.

Apologies if I'm misunderstanding anything or this doesn't actually apply to what I'm trying to do. CAD isn't really my "thing" and I certainly still haven't come anywhere close to fully grokking how any of this works. I've been on a years-long quest to find the most sustainable CAD-as-code option for my personal projects, and OpenSCAD+BOSL2 plus another project called SolidPython seems to be as close as I've come to realizing that so far. In any case, if there's a better way to accomplish what I'm trying to do with features that already exist, please let me know.

mgalvey avatar Dec 06 '23 03:12 mgalvey

I haven't tried to fully understand what you're doing, but first answer this question: does the above tagpass_attachable() solve your problem?

In never actually implemented it (would be as an option to attachable, not a separate module) because it wasn't clear it was actually useful in a real situation.

adrianVmariano avatar Dec 06 '23 21:12 adrianVmariano

Based on the comments in this thread, I believe it would, but I haven't had a chance to test with the module version yet. I'll need to figure out how to either include and make use of that custom module from my SolidPython code, or (more likely) generate a minimal example from my existing code and drop down to native OpenSCAD to test with the module. I'll make some time to figure that out once I'm done with this current project.

mgalvey avatar Dec 06 '23 22:12 mgalvey

I don't know what SolidPython is doing, but you can just add the above code to attachments.scad in your copy of BOSL2, or just simply insert it into your OpenSCAD code wherever is convenient. There's nothing special or magical about it. You do need both regular attachable and the modified one, because the modified one will break stuff if it's dropped in as a total replacement.

If it does solve your problem, great. It's very easy to add this, and you'll be able to supply a use case example for why the feature is useful. :) Otherwise, we'll have to dig deeper. The attachments stuff is very tricky because of how OpenSCAD works.

adrianVmariano avatar Dec 06 '23 23:12 adrianVmariano

@mgalvey Did you ever figure out if this would help your issue?

adrianVmariano avatar Jan 16 '24 03:01 adrianVmariano

Apologies for the delay on my part. I'm in the process of rebuilding the system I had my CAD environment hosted on due to a drive failure last month. I do intend to come back around to this to test whether the posted module results in the functionality I'm looking for, but I can't make any promises on how long it'll be before I get there. In the meantime, please don't feel obliged to hold this issue open on my account.

In addition to the rebuild effort, I have some other things that will likely keep me busy for the rest of this week, but I've set a reminder for myself to devote some time to this next week.

mgalvey avatar Jan 17 '24 18:01 mgalvey

We've had issues open for months or years. If we close the issue without a resolution, though, the matter will be forgotten.

adrianVmariano avatar Jan 17 '24 22:01 adrianVmariano

Sorry for the extended delay on this; as often happens in life, other things crept up and demanded my attention, but I finally found some time last night and confirmed that tagpass_attachable does indeed enable the functionality I was looking for. Having it as a configurable option for the existing attachable functionality would be very helpful for future projects. I've put together a minimal example of what I was trying to do. Given the following code and screenshot:

attachable(anchor = BOT, anchors = [named_anchor(name = "botcent", orient = DOWN, pos = [0, 0, 0], spin = 0)]) {
	cylinder($fn = 180, anchor = BOT, d = 6, h = 6) {
		tag(tag = "remove") {
			recolor(c = [1, 0, 0]) {
				position(from = TOP) {
					screw_hole($fn = 180, anchor = TOP, l = 10, spec = "M3x0.50", thread = false, tolerance = "normal");
				}
			}
		}
	}
	union() {
		children();
	}
}

standoff

I wanted the red part to cut not only through the standoff, but also whatever other parts it was attached to. This is useful because it enables me to make one object and accomplish both the creation of a new feature on an existing part, and also the cutting out of necessary screw holes with a simple call to diff or tag_diff. Using the current standard attachable doesn't allow this because tags don't survive being wrapped in it. However, when I use tagpass_attachable, everything works as desired:

tag_diff(tag = "") {
	cuboid(anchor = BOT, size = [15, 15, 3]) {
		position(from = TOP) {
			tagpass_attachable(anchor = "botcent", anchors = [named_anchor(name = "botcent", orient = DOWN, pos = [0, 0, 0], spin = 0)]) {
				cylinder($fn = 180, anchor = BOT, d = 6, h = 6) {
					tag(tag = "remove") {
						recolor(c = [1, 0, 0]) {
							position(from = TOP) {
								screw_hole($fn = 180, anchor = TOP, l = 10, spec = "M3x0.50", thread = false, tolerance = "normal");
							}
						}
					}
				}
				union() {
					children();
				}
			}
		}
	}
}

standoff-plate-tdiff-tagpass standoff-plate-tdiff-tagpass-top

So please consider this a formal request to have this functionality added as an option to attachable(), and thank you for your patience while I dawdled on testing it out.

mgalvey avatar Feb 08 '24 20:02 mgalvey

Ok. This is in the PR. I noticed that you have a weird (to me) style of always using named arguments. This I think looks a little funny in cases like tag(tag="foo") instead of tag("foo"). But also for position() the named argument made no sense. (I think it was just inherited from attach in a copy-paste operation.) So I renamed it from "from" to "at". Your code will still work, but with deprecation messages. Just a heads up.

The new option to attachable is use_child_tags=true.

adrianVmariano avatar Feb 09 '24 02:02 adrianVmariano

That probably has something to do with my using SolidPython to generate the code; if some specific params it's using are deprecated, I may have to nudge that project to pull in newer versions of BOSL2. I find the native OpenSCAD language to be extremely hard to follow in general, to the point that I'd rather not use it if that was my only option. That's something that may change someday, but for now, I just don't have the willpower to knuckle down and learn it well enough that I become fluent. I just don't use it frequently enough to justify that level of effort (yet).

In any case, style guide type recommendations may be well received over at the SolidPython project, but I don't really want to make them myself, since I avoid the native language and therefore have no authority to tell them how they should be doing things.

mgalvey avatar Feb 09 '24 03:02 mgalvey

AH, if it's autogenerated code it makes more sense what they're doing.

adrianVmariano avatar Feb 09 '24 04:02 adrianVmariano

The name of the option for this new feature has changed. It is now expose_tags=true. (There were two many different children around, making the other name potentially confusing.)

adrianVmariano avatar Feb 18 '24 21:02 adrianVmariano