$fn = $preview ? 32 : 128; // Cartridge base dimensions cartridge_width = 102; cartridge_length = 133; cartridge_height = 21; // Tape guide dimensions tape_guide_width = 6; tape_guide_depth = 16; tape_guide_slope = tape_guide_width / tape_guide_depth; // Track dimensions track_front_inset = 8; track_back_inset = 4; track_side_inset = 6; track_radius = 1; track_taper_length = 12; // Foot dimensions foot_width = 2.5; foot_height = 1; foot_front_inset = 8; foot_back_inset = 4; foot_side_inset = 6; // Fillets back_vertical_fillet_radius = 4; side_vertical_fillet_radius = 16; front_vertical_fillet_radius = 5; horizontal_fillets_radius = 1; // Windows on front wall window_vertical_inset = 2; window_side_inset = 14; window_depth = 6; // 13 left_window_width = 20; right_window_width = 20; window_gap = 5; roller_window_depth = 8; roller_window_offset = 3.5; // Emulated gap between top and bottom half gap_height = 1; gap_depth = 0.3; gap_elevation = 14; // Grips grip_wedge_width = 2.5; grip_count = 5; // Tape roller_inset = 0; roller_radius = 0; roller_position = [12, 79.5]; tape_line_position = [2, 14]; tape_height = 6.4; // NFC Coin nfc_coin_depth = 1; nfc_coin_diameter = 25; nfc_coin_tolerance = 0.1; cartridge(); module cartridge() { difference() { base(); windows(); grips(); gap(); undercut(); nfc_coin(); } tape(); } module base() { translate([0, 0, horizontal_fillets_radius]) minkowski() { linear_extrude(cartridge_height - 2 * horizontal_fillets_radius) offset(-horizontal_fillets_radius) footprint(); sphere(horizontal_fillets_radius); } tracks(); feet(); } module windows() { rectangular_windows(); roller_window(); } module rectangular_windows() { translate([ 0, window_side_inset, window_vertical_inset ]) { window_height = cartridge_height - 2 * window_vertical_inset; cube([window_depth, left_window_width, window_height]); translate([ 0, left_window_width + window_gap, ]) cube([window_depth, right_window_width, window_height]); } } module roller_window() { translate([ -roller_window_depth, roller_position.y - roller_window_offset, ]) cylinder(h=cartridge_height, r=12); } module grips() { for(i=[0:grip_count-1]) { translate([cartridge_length - i * grip_wedge_width - back_vertical_fillet_radius, 0]) { grip_wedge(); translate([0, cartridge_width]) mirror([0, 1]) grip_wedge(); } } } module grip_wedge() { linear_extrude(gap_elevation) polygon([ [0, 0], [0, 1 + gap_depth], [-grip_wedge_width, gap_depth], [-grip_wedge_width, 0] ]); } module gap() { translate([0, 0, gap_elevation]) linear_extrude(gap_height) difference() { footprint(); offset(-gap_depth) footprint(); } } module undercut() { translate([53, cartridge_width - 7, -foot_height]) linear_extrude(gap_elevation + foot_height) rotate(45) offset(3) square([16, 16]); } module tape() { translate([0, 0, (cartridge_height - tape_height) / 2]) { roller(); tape_line(); } } module roller() { translate(roller_position) cylinder( h=tape_height, r=10, ); } module tape_line() { translate(tape_line_position) cube([ window_depth - tape_line_position.x, roller_position.y - tape_line_position.y, tape_height ]); } module nfc_coin() { translate([ cartridge_length / 2, cartridge_width / 2, cartridge_height - nfc_coin_depth ]) cylinder( h=nfc_coin_depth + nfc_coin_tolerance, d=nfc_coin_diameter + 2 * nfc_coin_tolerance, ); }; module footprint() { hull() { translate([ cartridge_length - back_vertical_fillet_radius, back_vertical_fillet_radius, ]) { circle(back_vertical_fillet_radius); // translate([0, cartridge_width - back_vertical_fillet_radius]) // circle(back_vertical_fillet_radius); } translate([ cartridge_length - back_vertical_fillet_radius, cartridge_width - back_vertical_fillet_radius, ]) circle(back_vertical_fillet_radius); translate([ tape_guide_depth + side_vertical_fillet_radius * tan(0.5 * atan(tape_guide_slope)), side_vertical_fillet_radius, ]) circle(side_vertical_fillet_radius); translate([ tape_guide_depth + side_vertical_fillet_radius * tan(0.5 * atan(tape_guide_slope)), cartridge_width - side_vertical_fillet_radius, ]) circle(side_vertical_fillet_radius); translate([ front_vertical_fillet_radius, tape_guide_width + front_vertical_fillet_radius * tan(0.5 * atan(1 / tape_guide_slope)), ]) circle(front_vertical_fillet_radius); translate([ front_vertical_fillet_radius, cartridge_width - tape_guide_width - front_vertical_fillet_radius * tan(0.5 * atan(1 / tape_guide_slope)), ]) circle(front_vertical_fillet_radius); } } module tracks() { track_interval = cartridge_width - 2 * track_side_inset; translate([track_front_inset, track_side_inset, cartridge_height]) rotate([0, 90, 0]) { track(); translate([0, track_interval]) track(); } } module track() { track_length = cartridge_length - track_front_inset - track_back_inset; cylinder( h=track_taper_length, r1=0, r2=track_radius, ); translate([0, 0, track_taper_length]) cylinder( h=track_length - 2 * track_taper_length, r=track_radius, ); translate([0, 0, track_length - track_taper_length]) cylinder( h=track_taper_length, r1=track_radius, r2=0, ); } module feet() { foot_interval = cartridge_width - 2 * foot_side_inset - foot_width; translate([foot_front_inset, foot_side_inset, -foot_height]) { foot(); translate([0, foot_interval, 0]) foot(); } } module foot() { foot_length = cartridge_length - foot_front_inset - foot_back_inset; translate([foot_height, 0]) cube([ foot_length - foot_height * 2, foot_width, foot_height, ]); intersection() { cube([ cartridge_length, foot_width, foot_height, ]); union() { translate([foot_height, foot_width, foot_height]) rotate([90, 90, 0]) cylinder(h=foot_width, r=foot_height); translate([foot_length - foot_height, foot_width, foot_height]) rotate([90, 90, 0]) cylinder(h=foot_width, r=foot_height); } } }