// LEGO-like parametric tire...
// (c) Vitaliy Filippov 2014, license: CC-BY-SA 4.0

$fn=60;

// 81.6x50:

// rim offset is (SPOKE_THICKNESS+(R_I-SPACING-RIM_THICKNESS-CUT_BASE)/2),
// so offset=4mm if SPOKE_THICKNESS=12.85

R=40.8; // outer radius
R_I=26.5; // innermost radius
BEVEL=1;
BEVEL_OUTER=1.4;
THICKNESS=1.6;
BORDER_THICKNESS=1.2;
PROT_DEPTH=0.5;
PROT_L_WIDTH=2;
PROT_A_WIDTH=2;
FIX_WIDTH=2;
B=2.5; // width of edge for the rim
B_W=1.6; // thickness of edge for the rim
B_P=4; // position of edge for the rim
W=50; // tire width
N_L=4; // number of lateral protectors
N_A=10; // number of angular protectors
SPOKE_THICKNESS=12.85; // thickness of solid spoke part
RIM_THICKNESS=2;
N_SPOKE=9; // number of spokes
CUT_BASE=8;
CUT_ANGLE=12;
CUT_I_R=1.5;
CUT_O_R=2;
SPACING=0.6;
HOLE_RADIUS=2.4; // my Wanhao Duplicator 4 makes no errors with Slic3r, 2.4mm is ideal lego hole

N_A=0;

// 6-spoke variant
N_SPOKE=6;
CUT_ANGLE=21;
CUT_I_R=2.5;
CUT_O_R=2;

// small
R=22;
BEVEL=0.6;
BEVEL_OUTER=0.8;
THICKNESS=1.2;
BORDER_THICKNESS=0.8;
PROT_DEPTH=0.4;
SPOKE_THICKNESS=8;
FIX_WIDTH=1.2;
PROT_A_WIDTH=1;
PROT_L_WIDTH=1;
B_P=3;
B_W=1;
B=1.6;
R_I=15;
CUT_O_R=1;
CUT_BASE=5;
N_L=2;
N_A=0;
W=16;

difference() {
  union() {
    rim();
    difference() {
      tire();
      // first layer usually becomes slightly bigger...
      difference() {
        cylinder(r=R, h=0.3);
        translate([0, 0, -0.1]) cylinder(r=R-BEVEL_OUTER-0.2, h=1);
      }
    }
    /*
    // "Manual support material" needed to print tire
    difference() {
      hull() {
        cylinder(r=R_I+B-0.2, h=B_P-0.2-(BEVEL-0.2*(2-sqrt(2))));
        cylinder(r=R_I+B-0.2-(BEVEL-0.2*(2-sqrt(2))), h=B_P-0.2);
      }
      translate([0, 0, -0.1]) cylinder(r=R_I+B-0.2-2, h=B_P);
    }
    translate([0, 0, B_P+B_W+0.3])
    difference() {
      cylinder(r=R_I+B+THICKNESS, h=W-2*(B_P+B_W+0.3));
      cylinder(r=R_I, h=W);
    }*/
  }
  // to cut and look inside :)
  translate([0, 0, -0.5]) cube(size=[R+10, R+10, W+1]);
}

module rim() {
  V_SPACING=0.2;
  color([0.5, 0.5, 1]) {
    difference() {
      union() {
        cylinder(r=R_I-SPACING, h=W);
        // outer fixing edges
        cylinder(r=R_I+B-SPACING, h=FIX_WIDTH+(B_P-FIX_WIDTH-V_SPACING));
        translate([0, 0, W-(B_P-FIX_WIDTH-V_SPACING)-FIX_WIDTH])
          cylinder(r=R_I+B-SPACING, h=FIX_WIDTH+(B_P-FIX_WIDTH-V_SPACING));
      }
      // make spokes by cutting space between them
      for (i = [1 : N_SPOKE])
        rotate([0, 0, i*360/N_SPOKE]) rim_cut();
      // cut spokes toroidally inside the rim
      translate([0, 0, W-SPOKE_THICKNESS-(R_I-SPACING-RIM_THICKNESS-CUT_BASE)/2])
        rotate_extrude(convexity = 10)
        translate([(R_I-SPACING-RIM_THICKNESS-CUT_BASE)/2+CUT_BASE, 0, 0])
        circle(r = (R_I-SPACING-RIM_THICKNESS-CUT_BASE)/2);
      // remove the rest of spokes
      cylinder(r=R_I-SPACING-RIM_THICKNESS, h=W-SPOKE_THICKNESS-(R_I-SPACING-RIM_THICKNESS-CUT_BASE)/2);
      // add axle hole at the center
      translate([0, 0, -0.5]) linear_extrude(height=W+1) axle();
      // add center cut around axle hole
      translate([0, 0, W-(B_P-FIX_WIDTH-SPACING)-4]) cylinder(r=CUT_BASE-2, h=5);
      // make spokes convex using another toroidal cut
      difference() {
        translate([0, 0, W-(B_P-FIX_WIDTH-V_SPACING)]) cylinder(r=1+R_I+B-0.5/*R_I-2*/, h=1+(B_P-FIX_WIDTH-V_SPACING));
        translate([0, 0, W-(B_P-FIX_WIDTH-V_SPACING)])
          scale([1, 1, (B_P-FIX_WIDTH-V_SPACING)/((R_I-SPACING-RIM_THICKNESS-CUT_BASE+2)/2)])
          rotate_extrude(convexity = 10)
          translate([(R_I-SPACING-CUT_BASE)/2+CUT_BASE-RIM_THICKNESS, 0, 0]) circle(r = (R_I-SPACING-CUT_BASE)/2);
      }
      // remove the rest symmetrically
      translate([0, 0, -1]) cylinder(r=1+R_I+B-0.5, h=1+(B_P-FIX_WIDTH-V_SPACING));
    }
  }
}

module rim_cut() {
  in_angle = atan((R_I-SPACING-RIM_THICKNESS)*sin(CUT_ANGLE)/((R_I-SPACING-RIM_THICKNESS)*cos(CUT_ANGLE)-CUT_BASE-CUT_I_R));
  translate([0, 0, -0.5]) linear_extrude(height=W+1) {
    hull() {
      translate([CUT_BASE+CUT_I_R, 0]) circle(r=CUT_I_R);
      rotate([0, 0, CUT_ANGLE]) translate([R_I-SPACING-RIM_THICKNESS-CUT_O_R, 0, 0]) circle(r=CUT_O_R);
      rotate([0, 0, -CUT_ANGLE]) translate([R_I-SPACING-RIM_THICKNESS-CUT_O_R, 0, 0]) circle(r=CUT_O_R);
    }
    difference() {
      circle(r=R_I-SPACING-RIM_THICKNESS);
      translate([CUT_BASE+CUT_I_R, 0]) rotate([0, 0, in_angle]) translate([-50, 0]) square(size=[100, 100]);
      translate([CUT_BASE+CUT_I_R, 0]) rotate([0, 0, -in_angle]) translate([-50, -100]) square(size=[100, 100]);
    }
  }
}

module tire() {
b_i = BEVEL;
bo_i = max(BEVEL, BEVEL_OUTER-THICKNESS*(2-sqrt(2)));
difference() {
  hull() {
    translate([0, 0, BEVEL_OUTER]) cylinder(r=R, h=W-BEVEL_OUTER*2);
    cylinder(r=R-BEVEL_OUTER, h=W);
  }
  // center hole through all piece
  translate([0, 0, -0.5]) cylinder(r=R_I, h=W+1);
  // top hole
  hull() {
    translate([0, 0, W-B_P+BEVEL]) cylinder(r=R_I+B, h=B_P+1);
    translate([0, 0, W-B_P]) cylinder(r=R_I+B-BEVEL, h=B_P+1);
  }
  hull() {
    translate([0, 0, W-BEVEL]) cylinder(r=R_I+B, h=BEVEL);
    translate([0, 0, W]) cylinder(r=R_I+B+BEVEL, h=1);
  }
  // bottom hole
  hull() {
    translate([0, 0, -1]) cylinder(r=R_I+B-BEVEL, h=B_P+1);
    translate([0, 0, -1]) cylinder(r=R_I+B, h=B_P+1-BEVEL);
  }
  hull() {
    translate([0, 0, 0]) cylinder(r=R_I+B, h=BEVEL);
    translate([0, 0, -1]) cylinder(r=R_I+B+BEVEL, h=1);
  }
  // inside cut
  translate([0, 0, B_P+B_W]) cylinder(r=R-THICKNESS, h=W-(B_P+B_W)*2);
  difference() {
    hull() {
      translate([0, 0, BORDER_THICKNESS]) cylinder(r=R-THICKNESS-bo_i, h=W-BORDER_THICKNESS*2);
      translate([0, 0, BORDER_THICKNESS+bo_i]) cylinder(r=R-THICKNESS, h=W-BORDER_THICKNESS*2-bo_i*2);
    }
    cylinder(r=R_I+B+THICKNESS, h=W);
    translate([0, 0, W-BORDER_THICKNESS-b_i])
      linear_extrude(height=b_i, scale=(R_I+B+THICKNESS+b_i)/(R_I+B+THICKNESS))
      circle(r=R_I+B+THICKNESS);
    translate([0, 0, BORDER_THICKNESS])
      linear_extrude(height=b_i, scale=(R_I+B+THICKNESS)/(R_I+B+THICKNESS+b_i))
      circle(r=R_I+B+THICKNESS+b_i);
  }
  // lateral protector
  if (N_L > 0) for (i = [1 : N_L])
    translate([0, 0, -PROT_L_WIDTH/2+i*W/(N_L+1)])
      difference() {
        cylinder(r=R+1, h=PROT_L_WIDTH);
        translate([0, 0, -0.01]) linear_extrude(height=PROT_L_WIDTH/2+0.01, scale=(R-PROT_DEPTH)/(R+0.01)) circle(r=R+0.01);
        translate([0, 0, PROT_L_WIDTH]) rotate([180, 0, 0]) translate([0, 0, -0.01]) linear_extrude(height=PROT_L_WIDTH/2+0.01, scale=(R-PROT_DEPTH)/(R+0.01)) circle(r=R+0.01);
        translate([0, 0, -0.1]) cylinder(r=R-PROT_DEPTH, h=PROT_L_WIDTH+0.2);
      }
  // angular protector
  if (N_A > 0) for (i = [1 : N_A])
    rotate([0, 0, 360/N_A*i]) protector_angular();
}
}

module protector_angular() {
  difference() {
    union() {
      translate([(W/2)/2, 0, W/2]) rotate([0, -45, 0])
        cube(size=[PROT_A_WIDTH, R+4, sqrt((W/2)*(W/2)*2)]);
      translate([-(W/2)/2, 0, 0]) rotate([0, 45, 0])
        cube(size=[PROT_A_WIDTH, R+4, PROT_A_WIDTH+sqrt((W/2)*(W/2)*2)]);
    }
    translate([0, 0, -0.5]) cylinder(r=R-PROT_DEPTH, h=W+1);
  }
}

module axle() {
  axle_gap = 1.95;
  union() {
    translate([-HOLE_RADIUS, -axle_gap/2, 0])
      roundedRect(HOLE_RADIUS * 2, axle_gap, .2);
    translate([-axle_gap/2, -HOLE_RADIUS, 0])
      roundedRect(axle_gap, HOLE_RADIUS * 2, .2);
  }
}

module roundedRect(x, y, radius) {
  hull() {
    translate([radius, radius, 0]) circle(r=radius);
    translate([x - radius, radius, 0]) circle(r=radius);
    translate([x - radius, y - radius, 0]) circle(r=radius);
    translate([radius, y - radius, 0]) circle(r=radius);
  }
}
