/* [Main settings] */ // path to the tab icon icon = "icons/classes/Guardian.svg"; // number of tabs tabs = 4; // index of the tab, 0 for first tab. must be smaller than number of tabs index = 0; // generate a card for each tab index for testing overlap test_tabs = false; /* [Dimensions] */ // horizontal size of the card horizontal = 87.0; // vertical size of the card (not including the tab) vertical = 61.0; // width of the tab's top (excluding the slope) tab_width = 16.0; // height of the tab tab_height = 10.0; // angle of the slope. lower angle means steeper slope, i.e. 0 makes square tabs tab_angle = 45; // [0:89] // height of the icon (will be resized). Must be smaller than tab_height target_icon_height = 9.0; // width of the icon. If set to zero, will keep aspect, but may cause it to be off center if it isn't square target_icon_width = 0.0; base_color = "black"; icon_color = "lime"; /* [Hidden] */ $fa = 1; $fs = 0.4; module outer_tab(tab_height = 10, tab_width = 16, tab_angle = 45, flip = false) { slope_width = tab_height * tan(tab_angle); tab = ([ [ 0, 0 ], [ 0, tab_height ], [ tab_width, tab_height ], [ tab_width + slope_width, 0 ], ]); if (flip) { translate([ tab_width + slope_width, 0, 0 ]) mirror([ 1, 0, 0 ]) polygon(tab); } else { polygon(tab); } } module inner_tab(tab_height = 10, tab_width = 16, tab_angle = 45) { slope_width = tab_height * tan(tab_angle); tab = ([ [ 0, 0 ], [ slope_width, tab_height ], [ tab_width + slope_width, tab_height ], [ tab_width + 2 * slope_width, 0 ], ]); polygon(tab); } module card(index = 0, thickness = 0.6, icon = "", icon_thickness = 0.4, horizontal = 87, vertical = 61, tabs = 4, tab_height = 10, tab_width = 16, tab_angle = 45, target_icon_height = 9, target_icon_width = 9) { assert(0 <= index && index < tabs, str("Tab index out of range. Index: ", index, " Tabs: ", tabs)); assert(target_icon_height <= tab_height, str("Icon larger than tab!")); is_inner = index > 0 && index < tabs - 1; tab_spacing = (horizontal - tabs * tab_width) / (tabs - 1); assert(tab_spacing >= 0, "Card width is too small for the number and width of tabs."); position = index * (tab_width + tab_spacing); slope_width = tab_height * tan(tab_angle); indent = index == 0 ? 0 : (tab_width + tab_spacing) * index - slope_width; color(base_color) linear_extrude(thickness) { square([ horizontal, vertical ]); translate([ indent, vertical, 0 ]) { if (is_inner) inner_tab(tab_height, tab_width, tab_angle); else outer_tab(tab_height, tab_width, tab_angle, index == tabs - 1); } } if (icon != "") { // If no icon width is given, we assume the icon is square. icon_width = (target_icon_width == 0 ? target_icon_height : target_icon_width); icon_x_offset = (index > 0 ? slope_width : 0) + tab_width / 2 - icon_width / 2; icon_y_offset = tab_height / 2 - target_icon_height / 2; translate([ indent + icon_x_offset, vertical + icon_y_offset, thickness ]) color(icon_color) linear_extrude(icon_thickness) resize([ target_icon_width, target_icon_height, 0 ], auto = true) offset(delta = 0.001) import(icon); } } if (test_tabs) { for (idx = [0:tabs - 1]) { translate([ 0, 0, idx ]) card(index = idx, icon = icon, horizontal = horizontal, vertical = vertical, tab_width = tab_width, tabs = tabs, tab_height = tab_height, tab_angle = tab_angle, target_icon_height = target_icon_height, target_icon_width = target_icon_width); } } else { card(index, icon = icon, horizontal = horizontal, vertical = vertical, tab_width = tab_width, tabs = tabs, tab_height = tab_height, tab_angle = tab_angle, target_icon_height = target_icon_height, target_icon_width = target_icon_width); }