====== 2022-04-07 - object frame in OpenSCAD ====== recently i was asked to help with creating a mode for 3d printing. as usual, i took [[wp>OpenSCAD]] for the job. the model was a fairly straight-forward extrusion of a flat SVG file, with some minor adjustments here and there. however there was a catch -- what was also needed was a "slot" for this figure, to but put in, with a nice, rounded tops. oh -- and one more thing -- there are indentations on sides, so that it is easier to took grab models out from the "slot". also nice, and rounded. with a hand from [[wp>Hermann Minkowski]] (namely [[wp>Minkowski addition]]) it turned out to be fairly easy to code, yet computationally intensive to execute. let's get started! ===== input model ===== the tricky part, that got me thinking in a first place, was what the shape was very complex. it was not a mechanical model i'm used to work with, but instead an artistic shape, that had no square angles anywhere. more over the model was also a work-in-progress, and thus a subject to change. this meant that if i try to hardcode sth like shape's external border, not only i'd do a lot of extra work, but also i'd have to throw the whole thing out of the window, on a very first change to the input model. the original model is both copyrighted and not public, so for the sake of discussion, let's use following, random hand drawing of mine, as an input: {{:blog:2022:04:07:random_shape.png?400|random input shape}} ===== importing model ===== that one was simple. well... almost. as i wrote before [[2022-04-07 - importing svg in openscad|OpenSCAD have issues with some SVGs during importing]]. so the final code is: eps = 0.01; module model_import(file) { offset(eps) import(file); } model_import("random_shape.svg"); so we now have contour of the input shape ready. {{:blog:2022:04:07:step_1_top.png?400|top view of the imported shape}} ===== extending ===== as a next step we need to increase perimeters of the model, by wall thickness + some spacing. eps = 0.01; wall = 2; wall_h = 4; spacing = 1; module model_import(file) { offset(eps) import(file); } module model_slot_base(name) { module extended_base(r) { minkowski() { model_import(name); circle(r); } } module frame(wall) { difference() { extended_base(spacing + wall/2 + 0.5); #extended_base(spacing + wall/2); } } frame(wall); } model_slot_base("random_shape.svg"); the idea is to have [[wp>Minkowski addition]] of the input shape (SVG import) and a circle. this (effectively) extend side walls. by marking internal model in red we can see external base marked in yellow... and this is the only part that actually stays. for the sake of visibility i've increased spacing in above code from ''0.5'' to ''4''. {{:blog:2022:04:07:step_2_top_small.png?400|internal part}} {{:blog:2022:04:07:step_2_top_thick.png?400|external part (yellow) with subtraction of internal one (red)}} ===== cut-ins for fingers ===== as a next step we had to remove some part of the wall, to make space to allow grabbing the model with ones' fingers. eps = 0.01; wall = 2; wall_h = 4; spacing = 1; module model_import(file) { offset(eps) import(file); } module shape_placeholder() { linear_extrude(2*wall) model_import("random_shape.svg"); } module model_slot_base(name) { module extended_base(r) { minkowski() { model_import(name); circle(r); } } module frame(wall) { difference() { extended_base(spacing + wall/2 + 0.5); extended_base(spacing + wall/2); } } module cut_in_frame() { difference() { // model frame(wall); // side wall cuts children(); } } cut_in_frame() children(); } module model_slot(name) { intersection() { model_slot_base(name) #children(); // cut-off for the bottom roundings // we're only interested in the top of the model translate([-50, -50, 0]) cube(300*[1,1,1]); } } %shape_placeholder(); model_slot("random_shape.svg") { // some location(s) that shall be removed from the overall shape: // bottom translate([40, -10, 0]) square([60, 20]); // top translate([60, 110, 0]) square([50, 20]); } that's the bottom most part, where two squares are removed from the perimeters created in a step before. again, for readability these are marked in red. {{:blog:2022:04:07:step_3_top.png?400|removed parts for fingers}} ===== walls and roundings ===== so how to create walls out from it? that's simple now -- we need to ''linear_extrude()'' the 2D shape we've created so far and then again use Minkowski addition of the shape, with the sphere. last but not least, we remove bottom part with ''intersection()'' of the shape and ''cube()''. so we get: eps = 0.01; wall = 2; wall_h = 4; spacing = 1; module model_import(file) { offset(eps) import(file); } module shape_placeholder() { linear_extrude(2*wall) model_import("random_shape.svg"); } module model_slot_base(name) { module extended_base(r) { minkowski() { model_import(name); circle(r); } } module frame(wall) { difference() { extended_base(spacing + wall/2 + 0.5); extended_base(spacing + wall/2); } } module cut_in_frame() { difference() { // model frame(wall); // side wall cuts children(); } } cut_in_frame() children(); } module model_slot(name) { intersection() { minkowski() { linear_extrude(wall_h) model_slot_base(name) children(); sphere(r=wall/2, $fn=20); // $fn here determines compilation times! } // cut-off for the bottom roundings // we're only interested in the top of the model translate([-50, -50, 0]) cube(300*[1,1,1]); } } %shape_placeholder(); model_slot("random_shape.svg") { // some location(s) that shall be removed from the overall shape: // bottom translate([40, -10, 0]) square([60, 20]); // top translate([60, 110, 0]) square([50, 20]); } so we finally got this: {{:blog:2022:04:07:complete.png?600|complete shape}} and here's top view, with original shape mocked, to show proper spacing. {{:blog:2022:04:07:final_shape_with_element_mock.png?400|top view with element mock}} you can download {{:blog:2022:04:07:frame.stl.gz| STL file of a generated model}} as well. ===== final notes ===== here's a github project [[https://github.com/el-bart/soft_playground/tree/master/openscad_svg_frame|containing both the input model and final OpenSCAD code]] if you'd like to play around. Minkowski addition is an excellent tool to work with difficult shapes, creating roundings, etc... it does however come with at a price. for any non-trivial shape this is not only computationally expensive but also OpenSCAD is not able to utilize multiple threads to compute it. for the above example, when sphere's ''$fn'' (TL;DR -- parameter controlling number of facets of round objects) was left default (around ''10'' for sphere of this size) it computed for almost 8 minutes. when i increased it to ''20'', to smooth the edges, i've ended up with 37 minutes of compilation! so what do i think about the transformation? i love it, but i'd strongly suggest to use with care. if possible to solve the issue otherwise, i'd suggest to try that one first.