-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementation of gradual bridging for holes #1467
Comments
I found the example confusing. There's no hole being supported by the extra bridging. The normal application is a counterbore screw where you need to end the counterbore, so the hole needs to shrink. It does seem like that idea would be a nice thing to add at some point. |
I agree that the example is a bit odd. Ideally I would have included a sliced model, but did not have time for it. Would it still be useful to include that? Indeed, the links I included are specifically for counterbore screw holes, but I personally experienced requiring this bridging procedure also for hexagonal nut pockets, or holes that happen to be in bridged section of my print. I personally would prefer keeping a module general enough for any hole. But specialized arguments may be added for the modules in Let me know if any assistance is needed. |
I don't know that a sliced model would help understanding the example. The thing that I don't understand is that the hole is not above a space and the only bridging I see is inside the hole. Hmm. Actually upon closer inspection I see that it's not inside the hole. It's hard to tell that. I'm not sure about the right API for a general implementation. Do you pass it two (convex) polygons giving the shape of a larger hole underneath and smaller hole above? What would a general interface look like? You have a lot more bridges than I've seen anyone use for this. Is it necessary/useful? It means the bridging section gets thicker. Could be a user parameter, I suppose. It seems like figuring out the right API is perhaps the hardest part. At the moment the dev focus is on bug fixes leading towards a stable 2.0 release, so we're trying to avoid being sidetracked by new features until after that release. But if you want to work on it, we can add the feature if you're able to get it to a reasonable state. Your preliminary code has z-fighting issues that you'd need to fix by adding extra amounts to the layers. |
To be clear, I was thinking you pass it polygons as paths, not as geometry, so like It seems like you'd normally want the module to actually also make the (bottom) hole. Certainly if it's called "bridge_hole" it should do that. If that doesn't make sense it should be "hole_support" or something like that. |
How would it work when fed an arbitrary shape? E.g. the smaller shape is a teardrop? I don't think the larger shape would matter too much, as long at the bridges span across the entire shape (but I might be wrong). API seems indeed the hardest part... |
Also the bottom bridges might not require to be rectangular. One might be able to fit more edges for better initial support. The code in the image:
|
Seems like a hard problem in its most general form |
Yes, I think the top hole shape is the one that matters. The outer hole matters only in that you have to make sure the bridges extend up to and probably slightly beyond that outer hole. There is polygon intersection code that I think should make that part easy. So one approach, limiting to orthogonal bridging, would be to pick a direction, examine the top shape and find its outermost point in that direction and create the two bridges that defines. Then go up a layer and do the orthogonal direction the same way. And then you can continue as may layers as needed. (How many layers are needed? Do you stop when the gap between the bridges and the hole is smaller than a threshold, which would presumably be an extrusion width?) I can see being able to implement this recursively where you pass a current "remaining hole" polygon, a bridge direction and the inner hole. Note that as long as the shape is convex, it doesn't matter what it looks like. There is the interesting notion of trying to force bridges to be parallel to segments of the hole, though. It seems like if you wanted a hexagonal hole you should be able to create that exactly with just three bridge pairs. But also it seems possibly better to use 2 layers of orthogonal bridging and then finish off with a layer featuring 4 "exact" bridges. I suppose this case could be detected because it would arise if you only had one segment left in the "hole" section. A user option could permit starting with more bridges at the bottom, but you run the risk that they intersect each other if the top hole is not small enough compared to the bottom hole, so that case would need to be detected. So yeah, being fully general requires some thought. Really nothing is fully general. So what requires thought is how to design an algorithm and API that is as general as possible without creating excessive complexity, but also without giving up to much if flexibility. |
This is a category of problem called "unsupported holes", which is basically any vertical hole that has its bottom edge offset from the build plate when 3D printing. There are techniques to account for this, depending on what surrounds the bottom of the hole (like a round hole or a hex-shaped hole). I usually refer to this document on best practices for 3D printed designs for guidance. It would be useful (and certainly would fill a need because I've had to do this many times in my own designs) to provide some cutouts for the most common use cases of unsupported holes, to accommodate a upside-down counterbores for bolts with a round head, square head, or hex head. A few days ago I was considering revising I would change
I'm not keen on the gradual bridging proposal that requires six layers to complete, because slicers can use variable layer height, and the layer locations are not predictable several layers in. A transition with two or three layers can work, though. |
I think if you want support structure it should be Before unsupported holes can be added, a totally separate module that makes unsupported holes needs to be written. It looks like that document just shows a round hole. It should be possible to write something more generic that handles arbitrary convex shapes, as discussed above. I admit I wasn't clear on the merit of the approach with tons of layers. Not sure everything in that document is true. Like horizontal axis threads are stronger than vertical ones due to layer lines. (You do have to omit part of the threads due to support.) I think we even implemented that in screw_hole. Not sure what refinement teardrop needs. The point at the bottom? What's the point of that? I think changing teardrop's shape (e.g. adding that bottom point) has the potential to be hard. (I rewrote teardrop a while back and it was really annoying because of the low $fn handling.) That point seems kind of silly for most screw-hole scale holes, where $fn will be too small to really resolve it. |
In my own library of code that I use outside of BOSL2, I already have some simple modules that make unsupported holes as well as horizontal holes. No rewrite is needed for teardrop because the shape isn't a teardrop, it's a special shape specifically for the purpose of a horizontal hole. I've never needed to use square heads, but I have needed hex-shaped holes for recessed bolts. Square head specs are published, for example here: https://www.aftfasteners.com/square-head-bolts-mechanical-technical-information/ A default layer height of 0.2 would cover 0.15 layer height for about two layers, but this could be a required parameter instead of one with a default. The refinement to the teardrop shape makes a difference for small horizontal holes, to avoid a flat surface at the bottom. The difference is significant because without it, I often need to drill the hole out a bit. For large holes, the little point at the bottom is always only 1 layer in height and not noticeable. The trapezoid at the top doesn't need to have 45° slopes; you can get away with 40° in all cases (and that's what the design guide I linked says), and the gap above needs to be at least 1 layer. |
Well, actually teardrop IS a special shape for the purpose of horizontal holes. That's why Revar wrote it. Second sentence of the description reads: Useful for extruding into 3D printable holes. It is also used for 3d printable bottom roundovers. Should probably open a separate issue to discuss that, though, as it's got nothing to do with bridged holes. Yeah, we have hex heads but not square heads. You can add square head screws if you like, but I still haven't recovered from working on the screws code and am not inclined to do it. You'd need to add a lookup table for the head thickness and sizes to _screw_info_english and code for actually creating square heads to screw_head. I would suggest the dimensions should be in fractions, rather than decimal. Don't know if square metric head info is easy to find or not. Generally the ideal way to find this stuff is in the right ASME or ISO document. I wonder if we should introduce a $layer_height parameter of some kind that can be set/used by modules, similar to $slop. The trouble with a default layer height is that you may forget the parameter exists, since you didn't specify it at first. If it's explicit you have more chance of seeing that you got it wrong if something changes. You'll never tell from looking at the model.... |
I know that's what the documentation says about teardrop, but in my experience that isn't the optimal shape for horizontal holes. It works for big holes but not for holes as small as 2mm, which I often use to make hinges with a piece of filament as the hinge pin. For holes that small you really need that little point at the bottom to ensure a rounded bottom. Looking further, I see there are metric square nuts but metric square-head bolts don't exist, they're all inch units. The head sizes all fit the standard non-metric wrench sizes 3/8, 1/2, 9/16, 5/8, 9/16, 3/4, etc. I agree layer height should be a required parameter, but it's a meaningless parameter if you're making a design for something other than FDM 3D printing. It may not matter at all for resin printing (I don't know). The documentation could say to set it to zero if it's irrelevant. |
I haven't done much with tiny holes like that. I did do filament hinge pins once and recall having variability on how easy it was to insert the filament. @revarbat might have more thoughts on the matter, as he has much broader printing experience than I do. I admit that I'm a little puzzled about the problem. If it's that the realized hole doesn't circumscribe the ideal hole wouldn't increasing hole diameter be the answer? Or to put it another way, why is the slicer making a hole smaller than what I asked for? But if it's the case that we sometimes need teardrops to have a point at the bottom, the answer is to elaborate teardrop to have a bottom-point option, not to introduce a second object that is exactly like teardrop except with a point. |
I checked my design with the filament hinge pin and it appears I have It sounds like you're thinking about this as a screw problem. Don't do that. It's a hole problem. My thinking was that you could have two paths, the bottom hole path and the top hole path. You can then work around the bottom hole, adding bridges and shrinking the remaining hole. The parameters are minimally: bottom path, top path, bottom height, and layer height. Is "span between walls" for making an unsupported rectangle with two opposing side walls missing? It's probably also desirable to include the total length and/or top hole length. (Probably both.) This framework would handle all sorts of cases that might include off-center stacked holes, holes with irregular shapes, and so on. I guess the question is how hard it is to implement. Can you elaborate on what goes wrong with more than 3 layers of support bridging? |
There are a lot of clearance guidances published, recommending as much as 0.5 mm for a loose fit. There are going to be horizontal tolerance variations as you print, arising from:
Printed vertical variations from the design arise mostly from:
These variations typically combine to cause 0.1 mm clearance horizontally to be a pretty tight fit. I find that 0.2 mm clearance for small holes is pretty snug. And the vertical variations are why the teardrop needs a little point at the bottom. Which brings me to your question about what's wrong with 3 layers of bridging. More than 3 layers would likely work fine if the layer height in the design equals the layer height being printed. It may not be in exactly the right place due to the first layer thickness offset, but close enough to work. Where it goes awry is when you have the design layer height smaller than the print layer height, in which case you'll get the first layer of bridging and maybe the second, but likely not the third. To compensate for this, when I release a design to the public that has holes, I assume a layer height of 0.3 mm because printing at a smaller layer height is going to hit all the bridges, and may print two layers on one bridge, but that's OK. |
I was thinking in terms of screws (or bolts) because the typical use case for unsupported holes is for counterbore holes with the counterbore on the underside. So I thought it would be best to give it parameters similar to screw_hole(). |
There's no reason the general implementation should be named after or designed based on a specialized application. For this to be used with screw holes, the screw() module I think will have to call it, so it doesn't matter what parameters the generic implementation uses for that application. Hmmm. Actually probably screw_head() has to call it. And so for a screw implementation, the length of "top" hole probably needs to be zero. (The top hole might be threaded.) The generic implementation should be considered in isolation to be the best designed generic implementation, with an API that makes sense generically. The original example above was a hole above a slot, so not a screw hole situation. So it sounds like the 3 layer restriction is not real. It's an artifact of wanting to make designs that are more easily portable across mismatched layer thicknesses. What that tells me is that an implementation of bridged holes should not adhere to a 3 layer restriction. That is, if using more layers can produce a better result in some case, it should be possible to produce the better result with more layers. But a layer max or layers-desired setting should allow the user to say use at most (exactly) 3 layers of bridging. I recall seeing bridges in prusaslicer that were two layers thick. Is that the norm? That would suggest that you might need 0.3mm layer thickness in the design when running at 0.15mm layer thickness. |
Those points are sensible. It should be a general solution to the problem unrelated to screws, and there is no need for a 3-layer restriction, because if it fails to print properly after 2 or 3 layers, it won't really matter to the printed result. I don't know of any norm for bridge thickness. A bridge is a line of plastic that has to bridge between two points. Whatever is printed on top of that isn't identified as a bridge in the slicer, because it has a surface underneath. PrusaSlicer does have a setting for printing thicker bridges, but that's thicker for only the bridging layer. |
I think I have something that tries to find the shortest bridges, but it requires the unsupported top hole to be convex. The bigger bottom hole can be any shape as long as it has at least four sides. Would those restriction be OK? I suppose a non-convex top hole could always have hull() applied to it inside the module for the analysis.
|
That sounds ideal. The top hole has to be convex for this method to even make sense, no? A bridge needs to touch two sides of the bottom hole. It also needs to touch the top hole, or it's not helping. That's impossible for concave points on the top polygon. So the only way this would work is if the concave regions are small enough that you can work on the hull of the top and ignore the concavities. I say let the user hull the input if they want this kind of thing. It seems like another requirement is possibly that the top hole polygon is contained within the bottom hole polygon, though the solution does still exist if this fails. In the extreme case the polygons are disjoint and you don't have any bridges at all. I'm also not sure about concave bottom hole. It seems like it may create some complications without adding much utility, so I wouldn't object to a convexity requirement there either. I think you'll need to use the internal function _region_region_intersections() to get the polygon segment indices. Be sure to set closed=false for the "region" which is the line. |
Well, my basic test case is a glued_circle() as the bottom hole. I think it is possible, but if it turns out to have weird results I can restrict it to convex. Is there a built-in function that tests if a point is on a line between two other points? It's easy enough to write but I'd rather not duplicate it if it exists. The reason I ask, is that polygon_line_intersection() already returns the intersection points, so all I'd need to do is test whether an intersection point is between any two adjacent points on the polygon to find the indices. That seems more efficient than all the stuff _regions_region_intersection seems to be doing, and the situation here is pretty simple. Edit: Looking further, it seems _regions_region_intersection isn't doing any math operation slower than norm(). I'll try that out first. Edit 2: Yes, I can work with _regions_region_intersection(). It seems to return the lower index point of the line segment that is intersected. |
Does performance seem to be a concern? It looks like the only extra stuff going on is the processing at the end which looks for repeated points, which might be significant if there are lots of points. Test if a point is on a line segment given by two other points: is_point_on_line(). You can run a list comprehension with line_intersection using the polygon segments and construct a list of the intersection segment indices and points. We did try to optimize all of this code though so I don't know if you'd get a big speed improvement. I guess there's the question of whether you're using a convex shape where weird stuff like self-touching is impossible and doesn't need to be checked for. Note that polygon_line_intersection is heavier than _region_region_intersection(). It calls split_region_at_region_crossings which in turn calls _region_region_intersections AND does additional post-processing. |
Just an update. I did come up with an algorithm, but I have set it aside for a while because it was giving me a headache. Too much instability for specific cases, partly arising from me not fully understanding how _region_region_intersections() should work when segments are collinear, as well as difficulty constructing new polygons without resorting to a slow intersection operation. I did decide that the optimal approach is likely not to greedily find the shortest bridges, but find the bridges that lop off the most area from the big hole. |
Is your feature request related to a problem? Please describe.
When putting holes on bridged sections, it is common to gradually bridge around the hole. (In FDM printing)
Describe the solution you'd like
Make a module that implements the negative of the hole (without the cylindrical part).
Describe alternatives you've considered
Example Code
Additional context
The result of the implementation:
Complete code in the image.
A Fusion360 plugin that does the same:
https://github.com/Finn2708/CounterboreBridging
A Hackaday article about the process:
https://hackaday.com/2020/05/17/look-ma-no-support-for-my-floating-holes/
The text was updated successfully, but these errors were encountered: