Printable panel blanks for the rc2014

 

In a recent point I talked about my rc2014 Power Base project. It was the biggest project I yet attempted in OpenSCAD, and I learned a lot from it. Still, there were a few things that really bothered me about the end result. 

  • The repository is a complete mess, with a commit message to match.
  • All of the work is in foot2.scad, an utterly inscrutable name for anyone who wants to replicate the project.
  • The foot2.scad file is also gigantic, making it really hard to work on when multiple related subcomponents need to be modified.
  • It doesn't have a lot of reusability for future projects, because of it's aforementioned Bigness of Single File.

I wanted to get back to it for this year's Retro Challenge in the rc2014 category. That was, of course, before getting lost in skew issues with the Voron Zero, and my dishwasher completely dying on me. Since I was recovering from updating my COVID and Flu vaccination status for this year, I finally found the time to think about this some more.

A thing that seems to happen to me is that I get focused on a particular project until I either get to acceptable completion point, or some key issue appears that I don't have the money, time, or self-confidence to overcome. Often, entire projects just get dropped then, leaving a bunch of useless crap around my house. 

I didn't want this to happen to the Power Base. Sometimes, it helps restarting a project if I can focus on a different, smaller problem that can build that momentum back up. Since I now have a Rear Panel Kit for the rc2014, making some covers for that seemed like the perfect opportunity. 

Sorry to be intentionally vague here (they're keeping their work low-key at the time of this post), but I was also helping someone with their own printable panel covers in SCAD. Reviewing their code made me really want to model my own take on the implementation. Not to replace their effort, but as a platform for me to refine my own OpenSCAD skills.

Recreating the panel covers would be a good test bed for some of the refactoring ideas I had since I "finished" with the Power Base:

  • Break up a big file into multiple smaller library files
  • Have some sort of "preview" or "main" file for actually rendering STLs
  • Use vectors (arrays) to make parameters less dizzying

The first one is really straightforward, but only because I've put in a lot of work in previous models defining a kind of code style for OpenSCAD that breaks things up really clearly for me. Often, I make a base 2D shape first, so I put that in an *Outline() module:

module rearPanelCoverBlankOutline() {}

This only creates a 2D outline that needs to be extruded to a set height. The module name is intentionally long, so we can remove words from it as we get closer to the part that we want. Usually, the 3D version of the shape relies on combining multiple 2D and 3D shapes, so it gets its own module too:

module rearPanelCoverBlank() {}

There's also a "namespace" in the module name, which helps functionally relate it together for people reading the code. OpenSCAD has no language level namespace mechanism, so we're doing this as a workaround.

Once that's done, it's pretty easy to refactor out modules to separate files by just grabbing all of those in the same "namespace". A straightforward method would be to name the file after the namespace, rearPanelCover.scad, or even pluralizing it to denote it as the library for inclusion (rearPanelCovers.scad). The pluralization doesn't have any language significance, but it may help signal to humans "look at this!".

Inclusion is pretty easy after that:

include <path/to/the/file.scad>

With a large component library, it can become really confusing to see a huge, flat directory of files. "Where do I even start!?" is something I ask myself a lot. For this reason, I like to structure my larger projects to help draw the eyes of humans. 

Since there are multiple rear panel cover types -- blanks, keystones, round holes for 3.5mm jacks or DC barrel jacks, etc. -- it makes sense to lean on more files, and leverage more directories to organize them. 

rearPanelCovers/
├── rearPanelCoverBlank.scad
└── rearPanelCoverKeystone.scad

This works, but there's a big catch. Library files should not actually render any modules in an OpenSCAD component library. You really only want those to provide code, rather than a model. That way, when you include the library in a much bigger rendering file, it doesn't start rendering odd things in odd places.

In programmer speak, we want to separate library code from application code. Our components are our library, and rendering those modules is our application. So, the solution is to create another file, appropriately called main.scad:

rearPanelCovers/
├── main.scad
├── rearPanelCoverBlank.scad
└── rearPanelCoverKeystone.scad

By doing this, we can separate out the rendering pretty easily:

include <rearPanelCoverBlank.scad>;
include <rearPanelCoverKeystone.scad>;

Select = 0; // [0:rearPanelCoverBlank, 1:rearPanelCoverKeystone]

if (Select==0) {
    rearPanelCoverBlank();
} else if (Select==1) {
    keyStoneCoverPlate();
}

As a bonus, the directory structure allows us to render STLs into each component directory without making our head spin:

rearPanelCovers/
├── main.scad
├── rearPanelCoverBlank.scad
├── rearPanelCoverKeystone.scad
└── STLs
    ├── rearPanelCoversBlank.stl
    └── rearPanelCoversKeystone.stl

As a bonus, the main.scad filename immediately draws the eye, as it's a convention in my programming languages to have a main-dot-something file as the primary execution point. I don't know how well this would scale, and it's definitely a "works for me" kind of solution, but it solves all of my concerns. 

One of the problems I had with the Power Base is the ever growing list of parameters I had for my modules. They eventually got so huge I couldn't read them in one line. 

module rearPanelCoverBlank(basePlateX, basePlateY, basePlateZ, tabID, tabOD, tabInsetY) {}

The OpenSCAD core library often uses vectors (arrays) for modules like cube() or operations like translate(). This functionally groups:

module rearPanelCoverBlank(basePlate, tab) {}

Furthermore, you can set defaults to make it easier to call the module:

module rearPanelCoverBlank(basePlate=[42,22,3], tab=[4,7,2.5]) {}

Even better, once you have the vectors, you can easily pass them to other modules:

module rearPanelCoverBlank(basePlate=[42,22,3], tab=[4,7,2.5]) {
        linear_extrude(3)
            rearPanelBlankOutline(basePlate, tab);
}

Given this is OpenSCAD, sometimes the math can get quite dense and difficult to read. This is helped in no way when using vectors as parameters. If we need to extract only one value from the vector for an operation, the syntax gets ugly:

module rearPanelCoverBlank(basePlate=[42,22,3], tab=[4,7,2.5]) {
        linear_extrude(basePlate[3]) // Ugly, but works.
            rearPanelBlankOutline(basePlate, tab);
}

A solution to this was to expand the vector internally to the module. Not because this makes it faster or better, it just makes it easier to read. I highly prize readable code, because I don't want to spend an hour trying to figure out why something doesn't work because of a misplaced or ignored square bracket:

module rearPanelCoverBlank(basePlate=[42,22,3], tab=[4,7,2.5]) {
        BasePlateX=basePlate[0];
        BasePlateY=basePlate[1];
        BasePlateZ=basePlate[2];
        tabID=tab[0];
        tabOD=tab[1];
        tabYInset=tab[2];

        linear_extrude(BasePlateZ) // Much clearer!
            rearPanelBlankOutline(basePlate, tab); // Doesn't prevent us from using vectors in the future!
}

This works because "variables" aren't really variables in OpenSCAD. They're really more like constants that can be overridden based on scope. In the above example, BasePlateZ doesn't get modified outside of the rearPanelCoverBlank() module. It cannot change any globally defined BasePlateZ. Any child scope, however, will get that constant as defined in the calling module. If a module in that child scope then overrides BasePlateZ, it will only be that new value for any of that scope's child scopes. 

In theory, we can still use globally defined constants and they should still work with the above file structure. 

This wasn't a complicated modeling project, but it allowed me to test out some other ideas I hope to use on the existing Power Base SCAD going forward. Hopefully, that'll make it easier for other people to print their own Power Base. You can find all of the SCAD and STLs on the Github repo for the Power Base.