Brewhouse/hardware/CAD/Enclosure/ProjectCase.scad
2023-02-02 15:26:02 -05:00

851 lines
31 KiB
OpenSCAD

/*
////////////////////////////////////////////////////
See my other designs:
https://www.thingiverse.com/steeveeet/designs
////////////////////////////////////////////////////
This file is designed to be entirely customisable.
The easiest way to use this is to copy the top sections (Basic and Advanced Customisation) into a new file
and add `include <U_Box.scad>;` at the top of that file. That will bring all the functionality of this file into a new file
so that you can define the box specific value for it.
There are 3 main sections to this file:
*** Basic Customisation ***
Top section defines the variables for the box -
these are all exposed through the Customizer UI.
The last section (display settings) is used to control what is displayed
(and thus exported to STL when the time comes)
*** Advanced Customisation ***
The configuratino fo the end panels are here.
These modules define the shapes to use to cut out holes from the front and back panels,
and to add (text) decoration to the front and back panels
In the PCB mount locations you'll find an array of point constructed
from the rectangle defined by the PCB Width and Height defined in the UI.
If you wish to ovverride this (if you need more than 4 mounting points,
or they are not in a rectangle) simply construct that array in directly and the model will update
*** Implementation ***
This section contains the modules and functions that are used to actually construict the parts of the box
It's unlikely you will need to change this, though I'd be very interested if you find that you do!
////////////////////////////////////////////////////
*/
/********************************************************************
*** Basic Customisation ***
*********************************************************************/
/* [Case Settings] */
Length = 80;
Width = 60;
Height = 30;
WallThickness = 2; // [1:5]
FilletRadius = 2; // [0.1:12]
// Decorations or ventilation holes
VentType = "VentHoles"; // [None, Decorations, VentHoles:Ventilation Holes]
// Width of vent/decoration holes
VentWidth = 1.5;
/* [Fastening Options] */
// Methods of securing the closure of the box
FasteningType = "Push"; // [Screw:Screw Fit, Push:Push Fit]
// Number for fastenings along thge edge
NumberOfFastenings = 2; // [0:10]
// Thickness of the fastening tabs
FasteningTabThickness = 2; // [1:5]
// Diameter of fastener hole for screw fitting
FasteningHoleDiameter = 2; // [0.1:5]
/* [End Panels] */
BackPanelType = "IntegratedWithBase"; // [None, IntegratedWithTop:Integrated With Top, IntegratedWithBase:Integrated With Base, Discrete]
FrontPanelType = "IntegratedWithBase"; // [None, IntegratedWithTop:Integrated With Top, IntegratedWithBase:Integrated With Base, Discrete]
// Panel Text Type
EmbossPanelDecorations = 1; // [0:Demboss, 1:Emboss]
// End Panel Tolerance
PanelTolerance = 0.9;
// Use example panel decorations
examplePanels = 1; // [0:No, 1:Yes]
/* [PCB Mounts] */
// Show PCB Ghost
ShowPCB = 1; // [0:No, 1:Yes]
// Add PCB Mounts
PCBMounts = 1; // [0:No, 1:Yes]
// Back left corner X position
PCBPosX = 10;
// Back left corner Y position
PCBPosY = 10;
// PCB Length
PCBLength = 40;
// PCB Width
PCBWidth = 30;
// Mount height
MountHeight = 10;
// Mount diameter
MountDia = 8;
// Hole diameter
MountHoleDia = 2.5;
/* [Display Settings] */
// Print Layout
PrintLayout = 0; // [0:No, 1:Yes]
// Top Case
ShowTCase = 0; // [0:No, 1:Yes]
// Bottom Case
ShowBCase = 1; // [0:No, 1:Yes]
// Front panel
ShowFPanel = 1; // [0:No, 1:Yes]
// Back panel
ShowBPanel = 1; // [0:No, 1:Yes]
/* [Hidden] */
// Case color
CaseColour = "Teal";
// End Panel color
PanelColour = "Aqua";
// Fillet Smootheness
Resolution = 50; // [4:100]
/************************************************************
*** Advanced Customisation ***
************************************************************/
{
//////////////////////////////////////////
/////////// Panel Configuration //////////
//////////////////////////////////////////
module FrontPanelCutouts(){
if (examplePanels)
{
SquareHole ([10,5],[10,10],1);
CylinderHole([40,10],5);
}
}
module FrontPanelDecoration(){
if (examplePanels)
{
LText([7.5,17.5], "On/Off");
CText([40,10], 5, "12345");
}
}
module BackPanelCutouts(){
if (examplePanels)
{
SquareHole ([5,0],[10,10],1);
CylinderHole([10,10],10);
}
}
module BackPanelDecoration(){
if (examplePanels)
{
LText([17.5,5], "Power");
}
}
//////////////////////////////////////////
/////////// PCB Mount locations //////////
//////////////////////////////////////////
pcbMountLocations = [[0,0], [PCBWidth,0], [0,PCBLength], [PCBWidth,PCBLength] ];
}
/************************************************************
*** Implementation ***
************************************************************/
{
//////////////////////////////
/////////// Helpers //////////
//////////////////////////////
$fn = Resolution;
////////// CONSTANT: Thickness of end panels //////////
function PanelThickness() = WallThickness;
////////// CONSTANT: Helper to define how much space is needed for end panel ribs, depending on type //////////
function SpaceForEndPanel(type) =
type == "IntegratedWithTop" ? PanelThickness() :
type == "IntegratedWithBase" ? PanelThickness() :
type == "Discrete" ? 2*WallThickness + PanelThickness() + PanelTolerance :
0; // None
////////// CONSTANT: How much of the length of the case will be taken up by support ribs for the end panels //////////
function SpaceForEndPanels() = 2*max(SpaceForEndPanel(FrontPanelType), SpaceForEndPanel(BackPanelType));
////////// CONSTANT: Vector describing exterior dimensions of the case //////////
function CaseDims() = [Length, Width, Height];
////////// CONSTANT: The clear space before fittings for end panels //////////
function InteriorDims() = [Length - SpaceForEndPanels(), Width - 2*WallThickness, Height - 2*WallThickness];
////////// Calculated size of tabs. Default to 8mm, but reduce if a small length or height //////////
function TabDiameter() =
min(Height/2, // Don't collide with Vent Holes in reduced height boxes
min(InteriorDims().x/2, // Don't collide with other tabs in reduced length boxes
20)); // Sensible max height
//////////////////////////////////////////////////
/////////// Maths functions & Utilities //////////
//////////////////////////////////////////////////
////////// Utility to work out how many items you might fit ion a given length //////////
function numItemsAlongLength(length, itemSpacing) = ceil(length / itemSpacing);
// Helper functions to calculate the bounding box of an array of vec2s
function bbox(v) = [minimum(v), maximum(v)];
{
// Return the maximum of all the "elem"th elemtns in a vector of vectors
function minimumElem(v, elem, i = 0) =
(i < len(v) - 1) ?
min(v[i][elem], minimumElem(v, elem, i+1)) :
v[i][elem];
// Return the maximum of all the "elem"th elemtns in a vector of vectors
function maximumElem(v, elem, i = 0) =
(i < len(v) - 1) ?
max(v[i][elem], maximumElem(v, elem, i+1)) :
v[i][elem];
function minimum(v) =
[minimumElem(v, 0), minimumElem(v, 1)];
function maximum(v) =
[maximumElem(v, 0), maximumElem(v, 1)];
}
///// Operator: distribute copies of the children evenly along a vector //////////
module distribute(start, end, numItems){
vec = end-start;
length = norm(vec);
normVec = vec/length;
itemSpacing = length / (2*numItems);
// Even number of tabs
for (i=[0: 1: numItems-1])
{
tabLoc = start + (itemSpacing * (1 + 2*i)) * normVec;
translate([tabLoc.x, tabLoc.y, tabLoc.z])
{
children();
}
}
}
/////////// Generic rounded box - Aligned along the X axis //////////
module RoundBox(dims, fillet = 0, center = false){
if (fillet > 0){
// Calculate the correct offset depending on whether the cube should be offset
// Split the total height between cubeDims & cylHeight
cylHeight = dims.x/2;
// The fillet radius must (at most) be half either Y, or Z, but we cannot have cubeDims Y or Z equal to zero
cappedFillet = min(min(fillet, dims.y/2.001), dims.z/2.001);
cubeDims = [
dims.x/2,
dims.y - 2*cappedFillet,
dims.z - 2*cappedFillet,
];
offset = center ? [0,0,0] : dims/2;
translate(offset){
minkowski()
{
rotate([0, 90, 0]){
cylinder(r = cappedFillet, h = cylHeight, center = true);
}
cube(cubeDims, center = true);
}
}
} else {
cube(dims, center = center);
}
}// End of RoundBox Module
/////////// Generic hollow rounded box - Aligned along the X axis //////////
module HollowRoundBox(dims, thickness, fillet = 0, center = false){
offset = center ? [0,0,0] : dims/2;
translate(offset){
difference() {
RoundBox(dims, fillet, true);
RoundBox([dims.x * 1.1, dims.y - 2*thickness, dims.z - 2*thickness], fillet, true);
}
}
}// End of RoundBox Module
////////////////////////////////////////////////////////
////////////////////// Main Case ///////////////////////
////////////////////////////////////////////////////////
module PanelRibs(panelType, xMultiplier = 1)
{
// For discrete panels, define the ribs to hold them in place
discreteRibDims = [WallThickness, CaseDims().y - 2*WallThickness, CaseDims().z - 2*WallThickness];
// Effective thickness of the discrete panel, including tolerance
discretePanelThickness = PanelTolerance + WallThickness;
// Location for slot for discrete panels
slotLocation = CaseDims().x/2 - (discreteRibDims.x + discretePanelThickness/2);
// Offset (from slotLocation) for retaining ribs
slotOffset = (discreteRibDims.x + discretePanelThickness)/2;
// For integrated panels, the rib can be smaller
integratedRibDims = [WallThickness/2, CaseDims().y - 2*WallThickness, CaseDims().z - 2*WallThickness];
// For integrated panels, the rib can be smaller
integratedRibOffset = (WallThickness + PanelTolerance/2) + integratedRibDims.x/2;
// Front Panel
if(panelType == "Discrete")
{
// Add ribs for supporting discrete panel
translate([xMultiplier * (slotLocation + slotOffset), 0, 0]){
HollowRoundBox(discreteRibDims, WallThickness, FilletRadius, true);
}
translate([xMultiplier * (slotLocation - slotOffset), 0, 0]){
HollowRoundBox(discreteRibDims, WallThickness, FilletRadius, true);
}
}
else if(panelType == "IntegratedWithThis"){
// Add a supporting rib for the panel integrated with this half
translate([xMultiplier * (CaseDims().x/2 - WallThickness/2), 0, 0]){
HollowRoundBox([WallThickness, CaseDims().y - 2*WallThickness, CaseDims().z - 2*WallThickness], PanelTolerance/2, FilletRadius, true);
}
}
else if(panelType == "IntegratedWithOther"){
// Add a supporting rib for the panel integrated with the other half
translate([xMultiplier * (CaseDims().x/2 - integratedRibOffset), 0, 0]){
HollowRoundBox(integratedRibDims, WallThickness/2, FilletRadius, true);
}
}
}
////////////////////////////////// Case //////////////////////////////////
module Case(dims, thickness, fillet = 0, fPanelType = "None", bPanelType = "None"){
// Hull with integrated panels (where appropriate)
union(){
// Decorated hull
difference()
{
// Main hull
union() {
// Main Shell
HollowRoundBox(dims, thickness, fillet, true);
// Add ribs for supporting the end panels
PanelRibs(fPanelType, 1);
PanelRibs(bPanelType, -1);
}
// Remove the top
translate([0, 0, Height / 2]){
cube([dims.x*1.1, dims.y*1.1, dims.z], true);
}
// Add side decoration / vents
if (VentType != "None")
{
union()
{
overlap = VentWidth; //make sure that we cleanly overlap the top/bottom/side of the case
decDims = [
VentType == "VentHoles" ?
(thickness + fillet) + overlap :
thickness/2 + overlap,
VentWidth,
(dims.z / 4) + overlap
];
padding = dims.x/20; // (half) the gap between sets of vents (and between vents and end)
ventFillet = VentWidth/3;
// Coords for vents
xStart = padding;
xEnd = InteriorDims().x/2 - padding;
yPos = VentType == "VentHoles" ?
InteriorDims().y/2 + decDims.x/2 - fillet : // Calculated from the inside wall
CaseDims().y/2 - decDims.x/2 + overlap; // Calculated from the ouside wall
zPos = -0.5*(dims.z - decDims.z) - overlap;
numVents = numItemsAlongLength(xEnd-xStart, VentWidth+thickness);
distribute([xStart, yPos, zPos], [xEnd, yPos, zPos], numVents)
{
rotate([0, 0, 90])
RoundBox(decDims, ventFillet, true);
}
distribute([-xStart, yPos, zPos], [-xEnd, yPos, zPos], numVents)
{
rotate([0, 0, 90])
RoundBox(decDims, ventFillet, true);
}
distribute([xStart, -yPos, zPos], [xEnd, -yPos, zPos], numVents)
{
rotate([0, 0, 90])
RoundBox(decDims, ventFillet, true);
}
distribute([-xStart, -yPos, zPos], [-xEnd, -yPos, zPos], numVents)
{
rotate([0, 0, 90])
RoundBox(decDims, ventFillet, true);
}
}
}
}
}
}
////////////////////////////////// Fixing tabs //////////////////////////////////
module Tab(radius, thickness){
difference(){
union(){
rotate([90, 0, 0])
{
cylinder(r = radius, thickness, $fn = 6);
}
}
// Chamfer
translate([0, -radius/2, -2*radius/3]){
rotate([45, 0, 0]){
cube([2*radius, radius, radius], center=true);
}
}
// Tolerance cut
tolerance = 0.1;
tolBox = [2*radius, 2*thickness, radius];
translate([0, (thickness-tolerance), tolBox.z/2]){
cube(tolBox, center=true);
}
}
}
////////////////////////////////// Case with tabs //////////////////////////////////
module CaseWithTabs(dims, thickness, fillet = 0, fPanelType = "None", bPanelType = "None"){
cylRad = TabDiameter()/2;
holeDia = FasteningHoleDiameter;
// Calculate the number of tabs and their location based on the length of the box.
// Assume a gap of TabDiameter on each side of each tab before spawning a new tab
numItems = NumberOfFastenings;
// For push fitting, offset the registration sphere so it's not a complete hemisphere. Makes it easier to close.
// Setting this to zero will result in a complete hemisphere
sphereOffset = 0.125*holeDia;
difference(){
union(){
Case(dims, thickness, fillet, fPanelType, bPanelType);
// Tabs
distribute(
[InteriorDims().x/2, InteriorDims().y/2, 0],
[-InteriorDims().x/2, InteriorDims().y/2, 0],
numItems)
{
Tab(cylRad, FasteningTabThickness);
}
if (FasteningType == "Push"){
// Add registration spheres
distribute(
[InteriorDims().x/2, -InteriorDims().y/2 - sphereOffset, -cylRad/3],
[-InteriorDims().x/2, -InteriorDims().y/2 - sphereOffset, -cylRad/3],
numItems)
{
difference(){
sphere(d = holeDia);
// Ensure the sphere doesn't stick out of the other side of the wall!
translate([0,-(holeDia - 2.*sphereOffset)/2,0])
{
cube(holeDia, true);
}
}
}
}
}
if (FasteningType == "Push"){
// Add cutouts for registration spheres
distribute(
[InteriorDims().x/2, InteriorDims().y/2 + sphereOffset, cylRad/3],
[-InteriorDims().x/2, InteriorDims().y/2 + sphereOffset, cylRad/3],
numItems
)
{
sphere(d = holeDia);
}
}
else if (FasteningType == "Screw"){
// Screw fitting so add the case holes
distribute(
[InteriorDims().x/2, InteriorDims().y/2, cylRad/3],
[-InteriorDims().x/2, InteriorDims().y/2, cylRad/3],
numItems
)
{
rotate([90, 0, 0]){
cylinder(d = holeDia, 3.0*thickness, center=true);
}
}
distribute(
[InteriorDims().x/2, -InteriorDims().y/2, -cylRad/3],
[-InteriorDims().x/2, -InteriorDims().y/2, -cylRad/3],
numItems)
{
rotate([90, 0, 0]){
cylinder(d = holeDia, 3.0*thickness, center=true);
}
}
}
}
}
/////////////////////////////////////////////////////////
////////////////////// PCB Mounts ///////////////////////
/////////////////////////////////////////////////////////
/////////////////////// PCB Mount /////////////////////////////
module PCBMount(){
FilletRadius = 2;
color(CaseColour)
union(){
difference(){
// Main cylinder
cylinder(d = MountDia, MountHeight);
// central hole
cylinder(d = MountHoleDia, MountHeight*1.01);
// Chamfer to help centre screws
chamferRad = (MountDia-MountHoleDia)/4;
rotate_extrude(){
translate([MountHoleDia / 2, (MountHeight-chamferRad)*1.01, 0]){
difference(){
square(chamferRad);
translate([chamferRad,0,0])
circle(chamferRad, $fn=4);
}
}
}
}
// Fillet
rotate_extrude(){
translate([MountDia / 2, 0, 0]){
difference(){
square(FilletRadius);
translate([FilletRadius,FilletRadius,0])
circle(FilletRadius);
}
}
}
}
}
module PCBMounts(){
if (PCBMounts)
{
translate(-InteriorDims()/2 + [PCBPosX, PCBPosY, 0]){
if (ShowPCB)
{
// PCB Ghost
pcbGhostPadding = 5;
bbox = bbox(pcbMountLocations);
w = bbox[1][0] - bbox[0][0];
l = bbox[1][1] - bbox[0][1];
translate(-[pcbGhostPadding, pcbGhostPadding, -(MountHeight+1)]){
% square([l + 2*pcbGhostPadding, w + 2*pcbGhostPadding]);
}
}
for(footLocation = pcbMountLocations)
{
translate([footLocation.y, footLocation.x, 0])
{
PCBMount();
}
}
}
}
}
/////////////////////////////////////////////////////////
////////////////////// End Panels ///////////////////////
/////////////////////////////////////////////////////////
/////////////// Circular hole helper //////////////////////
// loc = location of centre
// d = diameter of holes
module CylinderHole(loc, d){
translate([loc.x, loc.y, -PanelThickness()*0.1]){
cylinder(d = d, PanelThickness() * 1.2, $fn = Resolution);
}
}
/////////////// Rectangular hole helper /////////////////////
// loc = location of bottom left corner
// sz = dimensions of box
// f = radius of fillet
module SquareHole(loc, sz, f){
translate([loc.x, loc.y, -PanelThickness()*0.1]){
rotate([-90, -90, 0])
{
RoundBox([PanelThickness() * 1.2, sz.x, sz.y], f);
}
}
}
/////////////// Text helper /////////////////////
// loc = location of bottom left corner
// content = text to print
// sz = size of text
// ft = font to use
module LText(loc, content, sz = min(Width,Height)/10, ft = "Arial Black"){
translate([loc.x, loc.y, PanelThickness()]){
linear_extrude(height = 1, center = true){
text(content, size = sz, font = ft);
}
}
}
/////////////// Arc Text helper /////////////////////
// loc = location of the centre of the arc
// content = text to print
// rad = radius of arc
// a0 = Start angle (default 0 - west)
// a1 = End angle (default 180 - east)
// sz = size of text
// ft = font to use
/////////////////////////////////////////////////////
module CText(loc, r, content, a0=0, a1=180, sz = min(Width,Height)/10, ft = "Arial Black"){
angleStep = (a1-a0) / (len(content)-1);
translate([loc.x, loc.y, PanelThickness()])
for (i = [0: len(content) - 1] ) {
rotate([0, 0, 90 - (i * angleStep)])
translate([0, r, 0]) {
linear_extrude(height = 1, center = true){
text(content[i], font = ft, size = sz, valign = "baseline", halign = "center");
}
}
}
}
////////////////////// End Panel //////////////////////
module Panel(){
panelDims = [PanelThickness(), InteriorDims().y - PanelTolerance, InteriorDims().z - PanelTolerance];
cutout = true;
difference(){
color(PanelColour)
RoundBox(panelDims, FilletRadius, true);
// Translate so that bottom left corner of panel is "origin" for controls
translate(-panelDims/2){
rotate([90, 0, 90]){
children(0);
}
}
if (!EmbossPanelDecorations){
color(CaseColour){
translate(-panelDims/2){
rotate([90, 0, 90]){
children(1);
}
}
}
}
}
if (EmbossPanelDecorations){
color(CaseColour){
translate(-panelDims/2){
rotate([90, 0, 90]){
children(1);
}
}
}
}
}
/////////////////////////////////////////////////////////////////////////
////////////////////// Main Scene Build /////////////////////////////////
/////////////////////////////////////////////////////////////////////////
printLayoutPadding = 10;
// Utility function to establish whether the panel is integated with This half or the Other
function panelType(type, this, other) =
type == this ? "IntegratedWithThis" :
type == other ? "IntegratedWithOther" :
type;
// Utility to work out the offset from the edge of the case depending on panel type.
function panelOffset(type) =
type == "Discrete" ?
(WallThickness + PanelThickness()/2 + PanelTolerance/2) : // Discrete
PanelThickness()/2; // Integrated (or None, in which case the translate doesn't matter)
///////////////////////// Calculate the transforms for each part, depening on whether we're visualising or printing /////////////////////////
BaseTranslate = !PrintLayout ?
CaseDims() / 2:
CaseDims() / 2;
BaseRotate = !PrintLayout ?
[0,0,0]:
[0,0,0];
TopTranslate = !PrintLayout ?
[CaseDims().x/2, CaseDims().y/2, CaseDims().z/1.95] :
[CaseDims().x/2, CaseDims().y*1.5 + printLayoutPadding, CaseDims().z/2];
TopRotate = !PrintLayout ?
[180, 0, 0] :
[0,0,0];
FPanelTranslate = (!PrintLayout || FrontPanelType != "Discrete") ?
FrontPanelType == "IntegratedWithTop" ?
TopTranslate + [CaseDims().x/2 - panelOffset(FrontPanelType) - 0.01, 0, 0] :
BaseTranslate + [CaseDims().x/2 - panelOffset(FrontPanelType) - 0.01, 0, 0] :
[CaseDims().x + CaseDims().z/2 + printLayoutPadding, CaseDims().y/2, PanelThickness()/2];
FPanelRotate = (!PrintLayout || FrontPanelType != "Discrete") ?
FrontPanelType == "IntegratedWithTop" ?
TopRotate + [180,0,0]:
BaseRotate + [0,0,0]:
[0,-90,0];
BPanelTranslate = (!PrintLayout || BackPanelType != "Discrete") ?
BackPanelType == "IntegratedWithTop" ?
TopTranslate + [-(CaseDims().x/2 - panelOffset(BackPanelType) - 0.01), 0, 0] :
BaseTranslate + [-(CaseDims().x/2 - panelOffset(BackPanelType) - 0.01), 0, 0] :
[CaseDims().x + CaseDims().z/2 + printLayoutPadding, CaseDims().y*1.5 + printLayoutPadding, PanelThickness()/2];
BPanelRotate = (!PrintLayout || BackPanelType != "Discrete") ?
BackPanelType == "IntegratedWithTop" ?
TopRotate + [180,0,180]:
BaseRotate + [0,0,180]:
[0,-90,0];
PCBTranslate = !PrintLayout ? CaseDims() / 2 : [0,0,0];
PCBRotate = !PrintLayout ? [0,0,0]: [0,0,0];
if (ShowTCase)
{
union()
{
translate(TopTranslate){
rotate(TopRotate){
color(CaseColour){
CaseWithTabs(CaseDims(), WallThickness, FilletRadius,
fPanelType = panelType(FrontPanelType, "IntegratedWithTop", "IntegratedWithBase"),
bPanelType = panelType(BackPanelType, "IntegratedWithTop", "IntegratedWithBase"));
}
}
}
// TODO: translated (but don't rotate) the panel with the case - also works for print mode
// means we have to have the panel translation in local coords (at least not for discrete mode)
if (FrontPanelType == "IntegratedWithTop")
{
translate(FPanelTranslate) {
rotate(FPanelRotate){
Panel(){
FrontPanelCutouts();
FrontPanelDecoration();
}
}
}
}
if (BackPanelType == "IntegratedWithTop")
{
translate(BPanelTranslate) {
rotate(BPanelRotate){
Panel(){
BackPanelCutouts();
BackPanelDecoration();
}
}
}
}
}
}
if (ShowBCase) {
union()
{
translate(BaseTranslate){
rotate(BaseRotate){
color(CaseColour){
CaseWithTabs(CaseDims(), WallThickness, FilletRadius,
fPanelType = panelType(FrontPanelType, "IntegratedWithBase", "IntegratedWithTop"),
bPanelType = panelType(BackPanelType, "IntegratedWithBase", "IntegratedWithTop"));
}
PCBMounts();
}
}
if (FrontPanelType == "IntegratedWithBase")
{
translate(FPanelTranslate) {
rotate(FPanelRotate){
Panel(){
FrontPanelCutouts();
FrontPanelDecoration();
}
}
}
}
if (BackPanelType == "IntegratedWithBase")
{
translate(BPanelTranslate) {
rotate(BPanelRotate){
Panel(){
BackPanelCutouts();
BackPanelDecoration();
}
}
}
}
}
}
if (FrontPanelType == "Discrete" && ShowFPanel)
{
translate(FPanelTranslate) {
rotate(FPanelRotate){
Panel(){
FrontPanelCutouts();
FrontPanelDecoration();
}
}
}
}
if (BackPanelType == "Discrete" && ShowBPanel)
{
translate(BPanelTranslate) {
rotate(BPanelRotate){
Panel(){
BackPanelCutouts();
BackPanelDecoration();
}
}
}
}
}