$fn = 64; // --- Parameters --- cover_width = 80; // Total width of the outlet cover cover_height = 90; // Total height of the outlet cover cover_depth = 12; // Thickness of the outer frame frame_thickness = 12; // Width of the stone arch frame grid_bar_width = 4; // Thickness of the portcullis bars grid_bar_depth = 4; // Depth of the portcullis bars cable_gap = 25; // Opening height at the bottom for cables num_v_bars = 5; // Number of vertical bars num_h_bars = 4; // Number of horizontal bars // --- Modules --- // Inner arch cutout shape used for framing and grid bounding module inner_shape(thickness, y_bleed=0) { inner_w = cover_width - 2 * frame_thickness; straight_h = cover_height - cover_width / 2; union() { translate([0, -y_bleed, 0]) cube([inner_w, straight_h + y_bleed, thickness]); translate([inner_w / 2, straight_h, 0]) cylinder(d=inner_w, h=thickness); } } module portcullis_cover() { union() { // Main Frame (Stone Arch) difference() { // Outer shape union() { cube([cover_width, cover_height - cover_width/2, cover_depth]); translate([cover_width/2, cover_height - cover_width/2, 0]) cylinder(d=cover_width, h=cover_depth); } // Inner cutout (y_bleed ensures a clean cut at the bottom) translate([frame_thickness, 0, -1]) inner_shape(cover_depth + 2, y_bleed=1); } // Portcullis Grid (Flush with the back for easy, support-free 3D printing) translate([0, 0, 0]) { intersection() { // Bounding volume to perfectly trim the grid inside the arch translate([frame_thickness, 0, 0]) inner_shape(grid_bar_depth, y_bleed=0); // Grid bars union() { // Vertical bars v_spacing = (cover_width - 2*frame_thickness - grid_bar_width) / (num_v_bars - 1); for (i = [0 : num_v_bars - 1]) { bar_x = frame_thickness + i * v_spacing; translate([bar_x, cable_gap, 0]) cube([grid_bar_width, cover_height, grid_bar_depth]); } // Horizontal bars h_spacing = (cover_height - cable_gap - grid_bar_width) / num_h_bars; for (i = [0 : num_h_bars - 1]) { bar_y = cable_gap + grid_bar_width/2 + i * h_spacing; translate([frame_thickness, bar_y, 0]) cube([cover_width - 2*frame_thickness, grid_bar_width, grid_bar_depth]); } } } // Spikes at the bottom of vertical bars (pointing down into the cable gap) v_spacing = (cover_width - 2*frame_thickness - grid_bar_width) / (num_v_bars - 1); for (i = [0 : num_v_bars - 1]) { bar_x = frame_thickness + i * v_spacing; translate([bar_x + grid_bar_width/2, cable_gap, grid_bar_depth/2]) rotate([90, 0, 0]) cylinder(d1=grid_bar_width, d2=0, h=min(8, cable_gap)); } } } } // Render the object portcullis_cover();