2022-04-07 - object frame in OpenSCAD

recently i was asked to help with creating a mode for 3d printing. as usual, i took 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 Hermann Minkowski (namely 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:

random input shape

importing model

that one was simple. well… almost. as i wrote before 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.

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 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.

internal part 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.

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:

complete shape

and here's top view, with original shape mocked, to show proper spacing.

top view with element mock

you can download STL file of a generated model as well.

final notes

here's a github project 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.

blog/2022/04/07/2022-04-07_-_object_frame_in_openscad.txt · Last modified: 2022/04/07 11:59 by basz
Back to top
Valid CSS Driven by DokuWiki Recent changes RSS feed Valid XHTML 1.0