module knot(knot, tile_width = 5, tile_height, ribbon_width, gap) {
  rth = tile_height != undef ? tile_height : tile_width/2;
  rrw = ribbon_width != undef ? ribbon_width : tile_width/sqrt(2);
  rgap = gap != undef ? gap : tile_width/(6*sqrt(2));

  x = len(knot[0]);
  y = len(knot);
  for (i=[0:x-1]) {
    for (j=[0:y-1]) {
      translate([(i-x/2)*tile_width*2, (y/2-j)*tile_width*2, 0])
        knot_piece(knot[j][i], tile_width, rth, rrw, rgap);
    }
  }
}

module fine_knot(knot, tile_width = 5, tile_height, ribbon_width, gap) {
  rth = tile_height != undef ? tile_height : tile_width/2;
  rrw = ribbon_width != undef ? ribbon_width : tile_width/sqrt(2);
  rgap = gap != undef ? gap : tile_width/(6*sqrt(2));

  x = len(knot[0]);
  y = len(knot);
  for (i=[0:x-1]) {
    for (j=[0:y-1]) {
      translate([(i-(x-1)/2)*tile_width, ((y-1)/2-j)*tile_width, 0])
        knot_tile(knot[j][i], tile_width, rth, rrw, rgap);
    }
  }
}

module knot_piece(c, tile_width = 5, tile_height, ribbon_width, gap) {
  rth = tile_height != undef ? tile_height : tile_width/2;
  rrw = ribbon_width != undef ? ribbon_width : tile_width/sqrt(2);
  rgap = gap != undef ? gap : tile_width/(6*sqrt(2));

  if (c == " ") {
  } else if (c == "<") {                              // top square corners
    make_knot_piece("   <", tile_width, rth, rrw, rgap);
  } else if (c == ">") {
    make_knot_piece("  > ", tile_width, rth, rrw, rgap);
  } else if (c == ",") {                              // top rounded corners
    make_knot_piece("   ,", tile_width, rth, rrw, rgap);
  } else if (c == ".") {
    make_knot_piece("  . ", tile_width, rth, rrw, rgap);

  } else if (c == "~") {                              // normal top piece
    make_knot_piece("  rq", tile_width, rth, rrw, rgap);
  } else if (c == "=") {                              // straight top piece
    make_knot_piece("  --", tile_width, rth, rrw, rgap);

  } else if (c == "!") {                             // normal left piece
    make_knot_piece(" A E", tile_width, rth, rrw, rgap);
  } else if (c == "|") {                             // straight left piece
    make_knot_piece(" | |", tile_width, rth, rrw, rgap);

  } else if (c == "&") {                             // normal internal piece
    make_knot_piece("tYGh", tile_width, rth, rrw, rgap);

  } else if (c == ";") {                             // normal right piece
    make_knot_piece("F W ", tile_width, rth, rrw, rgap);
  } else if (c == ":") {                             // straight right piece
    make_knot_piece("! ! ", tile_width, rth, rrw, rgap);

  } else if (c == "-") {                             // normal bottom piece
    make_knot_piece("sd  ", tile_width, rth, rrw, rgap);
  } else if (c == "_") {                             // straight bottom piece
    make_knot_piece("__  ", tile_width, rth, rrw, rgap);

  } else if (c == "[") {                             // bottom square corners
    make_knot_piece(" [  ", tile_width, rth, rrw, rgap);
  } else if (c == "]") {
    make_knot_piece("]   ", tile_width, rth, rrw, rgap);

  } else if (c == "{") {                             // bottom rounded corners
    make_knot_piece(" {  ", tile_width, rth, rrw, rgap);
  } else if (c == "}") {
    make_knot_piece("}   ", tile_width, rth, rrw, rgap);

  } else if (c == "n") {                        // top rounded internal piece
    make_knot_piece("ewGh", tile_width, rth, rrw, rgap);
  } else if (c == "u") {                        // bottom rounded internal piece
    make_knot_piece("tYaf", tile_width, rth, rrw, rgap);
  } else if (c == "(") {                        // left rounded internal piece
    make_knot_piece("QYDh", tile_width, rth, rrw, rgap);
  } else if (c == ")") {                        // right rounded internal piece
    make_knot_piece("tRGS", tile_width, rth, rrw, rgap);

  } else if (c == "X") {                        // crossing internal pieces
    make_knot_piece("QRDS", tile_width, rth, rrw, rgap);
  } else if (c == "x") {
    make_knot_piece("ewaf", tile_width, rth, rrw, rgap);

  } else if (c == "O") {                        // round internal loop piece
    make_knot_piece(",.{}", tile_width, rth, rrw, rgap);
  } else if (c == "#") {                        // square internal loop piece
    make_knot_piece("<>[]", tile_width, rth, rrw, rgap);


  } else if (c == "Q") {                        // two crossing internal pieces
                                                // (square if upper case)
    make_knot_piece("<wDh", tile_width, rth, rrw, rgap);
  } else if (c == "q") {
    make_knot_piece(",wDh", tile_width, rth, rrw, rgap);
  } else if (c == "p") {
    make_knot_piece("e.GS", tile_width, rth, rrw, rgap);
  } else if (c == "P") {
    make_knot_piece("e>GS", tile_width, rth, rrw, rgap);
  } else if (c == "D") {
    make_knot_piece("QY[f", tile_width, rth, rrw, rgap);
  } else if (c == "d") {
    make_knot_piece("QY{f", tile_width, rth, rrw, rgap);
  } else if (c == "B") {
    make_knot_piece("tRa]", tile_width, rth, rrw, rgap);
  } else if (c == "b") {
    make_knot_piece("tRa}", tile_width, rth, rrw, rgap);


  } else if (c == "i") {                        // loops open at one end
                                                // (square if upper case)
    make_knot_piece(",.DS", tile_width, rth, rrw, rgap);
  } else if (c == "I") {
    make_knot_piece("<>DS", tile_width, rth, rrw, rgap);
  } else if (c == "j") {
    make_knot_piece(",w{f", tile_width, rth, rrw, rgap);
  } else if (c == "J") {
    make_knot_piece("<w[f", tile_width, rth, rrw, rgap);
  } else if (c == "k") {
    make_knot_piece("QR{}", tile_width, rth, rrw, rgap);
  } else if (c == "K") {
    make_knot_piece("QR[]", tile_width, rth, rrw, rgap);
  } else if (c == "l") {
    make_knot_piece("e.a}", tile_width, rth, rrw, rgap);
  } else if (c == "L") {
    make_knot_piece("e>a]", tile_width, rth, rrw, rgap);


  } else if (c == "t") {                        // loops open at one end
                                                // (mix of round and square)
    make_knot_piece(",>DS", tile_width, rth, rrw, rgap);
  } else if (c == "T") {
    make_knot_piece("<.DS", tile_width, rth, rrw, rgap);
  } else if (c == "f") {
    make_knot_piece(",w[f", tile_width, rth, rrw, rgap);
  } else if (c == "F") {
    make_knot_piece("<w{f", tile_width, rth, rrw, rgap);
  } else if (c == "g") {
    make_knot_piece("QR{]", tile_width, rth, rrw, rgap);
  } else if (c == "G") {
    make_knot_piece("QR[}", tile_width, rth, rrw, rgap);
  } else if (c == "h") {
    make_knot_piece("e.a]", tile_width, rth, rrw, rgap);
  } else if (c == "H") {
    make_knot_piece("e>a}", tile_width, rth, rrw, rgap);

  } else if (c == "w") {                        // concave corners
    make_knot_piece(" Arh", tile_width, rth, rrw, rgap);
  } else if (c == "a") {
    make_knot_piece("sY E", tile_width, rth, rrw, rgap);
  } else if (c == "e") {
    make_knot_piece("F Gq", tile_width, rth, rrw, rgap);
  } else if (c == "s") {
    make_knot_piece("tdW ", tile_width, rth, rrw, rgap);

  } else {
    translate([tile_width,-tile_width,0])
      invalid_tile(tile_width, rth, rrw, rgap);
  }
}

module knot_tile(c, tile_width = 5, tile_height, ribbon_width, gap) {
  rth = tile_height != undef ? tile_height : tile_width/2;
  rrw = ribbon_width != undef ? ribbon_width : tile_width/sqrt(2);
  rgap = gap != undef ? gap : tile_width/(6*sqrt(2));
 
  if (c == " ") {                                                // Blank

  } else if (c == ",") {                                         // Corners
    rotate([0,0,90]) round_corner(tile_width, rth, rrw, rgap);
  } else if (c == "<") {
    rotate([0,0,90]) square_corner(tile_width, rth, rrw, rgap);
  } else if (c == ".") {
    round_corner(tile_width, rth, rrw, rgap);
  } else if (c == ">") {
    square_corner(tile_width, rth, rrw, rgap);
  } else if (c == "{") {
    rotate([0,0,180]) round_corner(tile_width, rth, rrw, rgap);
  } else if (c == "[") {
    rotate([0,0,180]) square_corner(tile_width, rth, rrw, rgap);
  } else if (c == "}") {
    rotate([0,0,-90]) round_corner(tile_width, rth, rrw, rgap);
  } else if (c == "]") {
    rotate([0,0,-90]) square_corner(tile_width, rth, rrw, rgap);

  } else if (c == "q") {                                         // Curves
    curve(tile_width, rth, rrw, rgap);
  } else if (c == "Q") {
    scale([1,-1,1]) rotate([0,0,90]) curve(tile_width, rth, rrw, rgap);
  } else if (c == "w") {
    scale([-1,1,1]) curve(tile_width, rth, rrw, rgap);
  } else if (c == "W") {
    rotate([0,0,-90]) curve(tile_width, rth, rrw, rgap);
  } else if (c == "s") {
    rotate([0,0,180]) curve(tile_width, rth, rrw, rgap);
  } else if (c == "S") {
    scale([-1,1,1]) rotate([0,0,90]) curve(tile_width, rth, rrw, rgap);
  } else if (c == "a") {
    scale([1,-1,1]) curve(tile_width, rth, rrw, rgap);
  } else if (c == "A") {
    rotate([0,0,90]) curve(tile_width, rth, rrw, rgap);

  } else if (c == "e") {                                         // Cross Curves
    cross_curve(tile_width, rth, rrw, rgap);
  } else if (c == "E") {
    scale([1,-1,1]) rotate([0,0,90]) cross_curve(tile_width, rth, rrw, rgap);
  } else if (c == "r") {
    scale([-1,1,1]) cross_curve(tile_width, rth, rrw, rgap);
  } else if (c == "R") {
    rotate([0,0,-90]) cross_curve(tile_width, rth, rrw, rgap);
  } else if (c == "f") {
    rotate([0,0,180]) cross_curve(tile_width, rth, rrw, rgap);
  } else if (c == "F") {
    scale([-1,1,1]) rotate([0,0,90]) cross_curve(tile_width, rth, rrw, rgap);
  } else if (c == "d") {
    scale([1,-1,1]) cross_curve(tile_width, rth, rrw, rgap);
  } else if (c == "D") {
    rotate([0,0,90]) cross_curve(tile_width, rth, rrw, rgap);

  } else if (c == "t") {                                         // Cross
    cross(tile_width, rth, rrw, rgap);
  } else if (c == "T") {
    scale([1,-1,1]) rotate([0,0,90]) cross(tile_width, rth, rrw, rgap);
  } else if (c == "y") {
    scale([-1,1,1]) cross(tile_width, rth, rrw, rgap);
  } else if (c == "Y") {
    rotate([0,0,-90]) cross(tile_width, rth, rrw, rgap);
  } else if (c == "h") {
    rotate([0,0,180]) cross(tile_width, rth, rrw, rgap);
  } else if (c == "H") {
    scale([-1,1,1]) rotate([0,0,90]) cross(tile_width, rth, rrw, rgap);
  } else if (c == "g") {
    scale([1,-1,1]) cross(tile_width, rth, rrw, rgap);
  } else if (c == "G") {
    rotate([0,0,90]) cross(tile_width, rth, rrw, rgap);

  } else if (c == "-") {                                         // Straight edge
    rotate([0,0,90]) straight(tile_width, rth, rrw, rgap);
  } else if (c == "_") {
    rotate([0,0,-90]) straight(tile_width, rth, rrw, rgap);
  } else if (c == "|") {
    rotate([0,0,180]) straight(tile_width, rth, rrw, rgap);
  } else if (c == "!") {
    straight(tile_width, rth, rrw, rgap);
  } else {

    invalid_tile(tile_width, rth, rrw, rgap);
  }
}

module knot_piece_boundary(tile_width = 5, tile_height) {
 // helper for alphabet only
 for (p = [ [tile_width*0.5, -tile_width*0.5, 0],
            [tile_width*1.5, -tile_width*0.5, 0],
            [tile_width*0.5, -tile_width*1.5, 0],
            [tile_width*1.5, -tile_width*1.5, 0] ]) {
    translate(p) knot_tile_boundary(tile_width, tile_height);
  }
}

module make_knot_piece(p, tile_width, tile_height, ribbon_width, gap) {
  rth = tile_height != undef ? tile_height : tile_width/2;
  rrw = ribbon_width != undef ? ribbon_width : tile_width/sqrt(2);
  rgap = gap != undef ? gap : tile_width/(6*sqrt(2));

  top_left     = [tile_width*0.5, -tile_width*0.5, 0];
  top_right    = [tile_width*1.5, -tile_width*0.5, 0];
  bottom_left  = [tile_width*0.5, -tile_width*1.5, 0];
  bottom_right = [tile_width*1.5, -tile_width*1.5, 0];

  translate(top_left)     knot_tile(p[0], tile_width, rth, rrw, rgap);
  translate(top_right)    knot_tile(p[1], tile_width, rth, rrw, rgap);
  translate(bottom_left)  knot_tile(p[2], tile_width, rth, rrw, rgap);
  translate(bottom_right) knot_tile(p[3], tile_width, rth, rrw, rgap);
}

module knot_tile_boundary(tile_width, tile_height) {
  translate([0, 0, 1.001*tile_height/2])
    cube([tile_width*1.001, tile_width*1.001, tile_height*1.001],
         center = true);
}

module cross(tile_width, tile_height, ribbon_width, gap) {
  gap2=gap*sqrt(2);

  intersection() {
    knot_tile_boundary(tile_width, tile_height);
    difference() {
      union() {
        rotate([0,0,-45])
          cube([ribbon_width, tile_width*2, tile_height*2], center = true);
        translate([-tile_width/2, -tile_width/2, tile_height]) rotate([0,0,45])
          cube([ribbon_width, ribbon_width, tile_height], center = true);
      }
      translate([-tile_width/4+gap2/4, -tile_width/4+gap2/4, 0])
        rotate([0,0,-45])
          cube([tile_width*2, gap, tile_height*4], center = true);
    }
  }
}

module curve(tile_width = 5, tile_height, ribbon_width) {

  cra=66;
  cr=.5*tile_width/cos(cra);

  intersection() {
    knot_tile_boundary(tile_width, tile_height);
    union() {
      intersection() {
        translate([0,-tile_width/2*cos(cra),0])
        rotate([0,0,cra]) translate([cr*1.5,0,0]) cube(cr*3, center = true);
        translate([ tile_width/2,
                    (tile_width-ribbon_width)/2-(cr-ribbon_width/2), 0])
          rotate_extrude() {
            translate([cr-ribbon_width/2, 0, 0])
              square([ribbon_width, tile_height*2], center = true);
          }
      }
      difference() {
        rotate([0,0,-45])
          cube([ribbon_width,tile_width*2, tile_height*2], center = true);
        translate([0,-tile_width/2*cos(cra),0])
        rotate([0,0,cra]) translate([cr*1.5,0,0]) cube(cr*3, center = true);
      }
    }
  }
}

module cross_curve(tile_width = 5, tile_height, ribbon_width, gap) {
  gap2=gap*sqrt(2);

  intersection() {
    knot_tile_boundary(tile_width, tile_height);
    difference() {
      curve(tile_width, tile_height, ribbon_width);
      translate([-tile_width/4+gap2/4, -tile_width/4+gap2/4, 0])
        rotate([0,0,-45])
          cube([tile_width*2, gap, tile_height*3], center = true);
    }
  }
}

module round_corner(tile_width = 5, tile_height, ribbon_width) {
  intersection() {
    knot_tile_boundary(tile_width, tile_height);
    translate([-tile_width/2, -tile_width/2, 0]) rotate_extrude()
      translate([tile_width-ribbon_width/2, 0, 0])
        square([ribbon_width, tile_height*2], center = true);
  }
}

module square_corner(tile_width = 5, tile_height, ribbon_width) {
  intersection() {
    knot_tile_boundary(tile_width, tile_height);
    union() {
      translate([(tile_width-ribbon_width)/2, 0, 0])
        cube([ribbon_width,tile_width*2, tile_height*2], center = true);
      translate([0, (tile_width-ribbon_width)/2, 0])
        cube([tile_width*2,ribbon_width, tile_height*2], center = true);
    }
  }
}

module straight(tile_width = 5, tile_height, ribbon_width) {
  intersection() {
    knot_tile_boundary(tile_width, tile_height);
    translate([(tile_width-ribbon_width)/2, 0, 0])
      cube([ribbon_width,tile_width*2, tile_height*2], center = true);
  }
}

module invalid_tile(tile_width = 5, tile_height, ribbon_width) {
  #cube([tile_width, tile_width, tile_height], center = true);
}


pwidth = 6;
l = pwidth*20;
w = 5;
h = 20;

module whole(){

module base(l,w,h) {
    difference() {   
    cube([l,w*2,h],w);
        union() {
            hull() {
            translate([l/2,w*2,h])
                rotate([0,90,0])
                    cylinder(l+h, w, w, true);
            translate([l/2,w*2,h-w/2])
                rotate([0,90,0])
                    cylinder(l+h, w, w, true);
            }
            hull() {
            translate([l/2,w*2,0])
                rotate([0,90,0])
                    cylinder(l+h, w, w, true);
            translate([l/2,w*2,w/2])
                rotate([0,90,0])
                    cylinder(l+h, w, w, true);
            }
        }
    }
}

rotate([90,0,0]) {
translate([0,-w/2,0])base(l,w,h); //lattice side
rotate([0,0,270])
    translate([-l,0,0])
        base(l,w,h); //base of wall side

difference() {
    translate([0,l/4,h])
        rotate([0,90,0])
        linear_extrude(w)
            circle(w*2);
    translate([w,l/4,h+3])
        rotate([0,90,0])
            cylinder(15,2,2,true);
}


difference() {
    translate([0,l/4,0])
        rotate([0,90,0])
        linear_extrude(w)
            circle(w*2);
    translate([w,l/4,-3])
        rotate([0,90,0])
            cylinder(15,2,2,true);
}

difference() {
translate([35,35,7.5])
    rotate([0,0,-45])
    knot(["<~=~~=~~=~>", 
          "!&n&&n&&n&;", 
          "!&u&&u&&u&;", 
          "[-_--_--_-]"], tile_width = pwidth, tile_height = w, gap = 0);
    translate([0, -w*5, 0])
        cube([l,w*6,h]);
    rotate([0,0,270])
        translate([-l,-w*5,0])
            cube([l,w*6,h]);
}

   
translate([pwidth*10,0,h/2])   
    rotate([90,0,0]) {
        translate([0,0,3/4*w/2])
            knot(["<~=~~=~~=~>", 
                  "!&n&&n&&n&;", 
                  "!&u&&u&&u&;", 
                  "[-_--_--_-]"], 
                  tile_width = pwidth, tile_height = 1/4*(w/2), gap = 2);
        knot(["<~=~~=~~=~>", 
              "!&n&&n&&n&;", 
              "!&u&&u&&u&;", 
              "[-_--_--_-]"], 
              tile_width = pwidth, tile_height = 3/4*(w/2), gap = 0);
    }
}
    
      }

difference(){
whole();
rotate([0,90,0])translate([-110,-10,-5])cylinder(r=2, h=20);
}