// 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;

// 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;

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();
}

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, 0])
    linear_extrude(gap_elevation)
    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() {
    one_side_vertical_fillets();
    translate([0, cartridge_width]) mirror([0, 1]) one_side_vertical_fillets();
  }
}

module one_side_vertical_fillets() {
  translate([
    cartridge_length - back_vertical_fillet_radius,
    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([
    front_vertical_fillet_radius,
    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,
  );
}
