// Umbra: a shadow machine. Six flat fins stand on a base at different // depths and angles. Lit by a small LED through a 1 mm pinhole, their shadows // line up on a wall 900 mm away into one dragon standing on a rock. Each fin // outline is the dragon's shadow projected back onto that fin's plane. /* [Output] */ // All parts, one group, or two pieces to test the slot fit output = "all"; // [all:All parts, fins:The six fins, structure:Base stone and light holder, fit:Fit test] // Scored numbers and alignment marks score_detail = true; /* [Material] */ // Sheet thickness $thickness = 3; // [2.8:0.1:3.2] // Added to every slot, for a glued slip fit clearance = 0.15; // [0.10:0.05:0.30] // Beam width $kerf = 0.15; // [0:0.01:0.25] /* [Hidden] */ $material = "plywood"; $sheet = [600, 400]; $gap = 6; $fn = 96; t = $thickness; sw = t + clearance; // The wall is L mm in front of the pinhole, which is sz mm above the base L = 900; sz = 25; // Each fin stands depth mm from the pinhole, turned by its yaw in degrees depths = [245, 275, 185, 155, 125, 215]; yaws = [9, 15, -3, -9, -15, 3]; // A point [x, z] of the picture on the wall, projected onto fin i: the fin is // a vertical plane through [0, depth] turned by its yaw. function warp(p, i) = let (a = yaws[i], d = depths[i], f = d / (L - p[0] * tan(a))) [f * p[0] / cos(a), sz + f * (p[1] - sz)]; // The dragon on the wall, in six fragments, one per fin T0 = [[-202.5366, 337.6753], [-227, 388], [-177.4432, 362.1121], [-169.1793, 369.0774], [-154.699, 361.5131], [-149.2495, 357.4991], [-127.6525, 340.5957], [-106.6053, 322.4979], [-86.2426, 303.3208], [-66.6998, 283.1791], [-48.1136, 262.1864], [-30.6214, 240.4557], [-14.3616, 218.0987], [-1.1879, 198], [28, 198], [28, 141], [23.9744, 141.9472], [18, 129.6], [18, 97.2632], [28, 82], [28, 57.8], [27, 57], [18, 62.7857], [18, 35], [-18, 35], [-18, 59.5], [-28, 57], [-28, 88.8889], [-18.123, 92.6202], [-23, 104], [-28, 110.6094], [-28, 172.7213], [-42.4058, 188.2796], [-42.2033, 189.1382], [-44.3083, 190.3343], [-70.2036, 218.3012], [-69.9296, 230.7208], [-70.5118, 235.8344], [-71.5792, 240.8117], [-75.1955, 250.4647], [-80.893, 259.8491], [-89.115, 270.1244], [-92.811, 273.5871], [-97.5446, 276.3256], [-101.5494, 276.9722], [-106.1357, 275.9018], [-109.9794, 273.855], [-128.295, 259.1001], [-130.5146, 260.9069], [-131.2948, 283.0353], [-132.7841, 289.986], [-135.5295, 296.3848], [-139.6256, 302.4154], [-145.2288, 308.1677], [-152.4931, 313.6416], [-158.7674, 316.9745], [-162.5038, 317.9852], [-166.1084, 318.2274], [-169.8032, 317.7325], [-173.7782, 316.4109], [-180.4189, 312.5686], [-195.3017, 299.9189], [-209.5861, 329.304]]; T1 = [[-18, 175.3864], [-49, 193], [-54.23, 198.15], [-59.52, 202.4], [-64.87, 205.75], [-70.28, 208.2], [-78.5075, 210.1875], [-86.87, 210.15], [-95.3675, 208.0875], [-104, 204], [-103.4048, 208.5952], [-103.8097, 215.955], [-104.9446, 221.173], [-106.7716, 226.6401], [-109.2907, 232.3564], [-114.3668, 241.3979], [-121, 251], [-125.7751, 256.6886], [-130.6886, 261.0484], [-135.7405, 264.0796], [-140.9308, 265.782], [-146.2595, 266.1557], [-148.9758, 265.8443], [-154.5121, 264.2249], [-160.1869, 261.2768], [-166, 257], [-166.2344, 263.0469], [-167.4375, 268.6875], [-169.6094, 273.9219], [-172.75, 278.75], [-176.8594, 283.1719], [-181.9375, 287.1875], [-187.9844, 290.7969], [-195, 294], [-202.2769, 295.2583], [-209.9256, 295.2149], [-217.9463, 293.8698], [-223.5, 292.25], [-232.1405, 288.7355], [-241.1529, 283.9194], [-250.5372, 277.8017], [-257, 273], [-193, 349], [-123, 408], [-66, 449], [-40, 328], [-33.9104, 312.3875], [-27.5473, 293.4314], [-22.7288, 276.5478], [-18.4971, 259.0774], [-15.4187, 244.0706], [-12.2626, 225.5247], [-10.0806, 209.6215], [-7.7273, 188], [18, 188], [18, 35], [-18, 35]]; T2 = [[-0.0622, 204.5378], [6.7844, 208.3344], [13.1111, 213.1111], [18.9178, 218.8678], [24.2044, 225.6044], [28.9711, 233.3211], [34.5178, 245.1344], [39.14, 258.69], [44.543, 279.5977], [48.4219, 288.1406], [51.75, 293.25], [57.8555, 300.0352], [62.668, 303.9727], [71, 309], [60, 342], [84, 330], [88, 350], [102, 330], [120, 332], [130, 324.5], [106.6667, 300], [122.7692, 300], [112, 290], [140, 290], [133, 277], [110, 275], [104.4379, 270.7396], [98.8284, 267.8047], [96.0059, 266.8343], [90.3254, 265.8876], [87.4675, 265.9112], [81.716, 266.9527], [76.978, 268.8269], [73, 271], [70.9438, 254.2101], [67.1095, 235.429], [61.784, 218.1391], [56.2071, 204.8698], [51, 195], [18, 154], [18, 35], [-18, 35], [-18, 188], [-8.0882, 188], [-10, 201]]; T3 = [[-18, 67.4286], [-61, 49], [-70.43, 46.6214], [-79.7431, 44.788], [-88.9394, 43.4997], [-98.0189, 42.7566], [-106.9816, 42.5587], [-118.7501, 43.1428], [-127.4402, 44.2169], [-136.0135, 45.8361], [-144.47, 48.0005], [-152.8096, 50.7101], [-161.0324, 53.9648], [-171.8145, 59.1525], [-185, 67], [-192.7158, 72.7788], [-199.7253, 78.9429], [-206.0285, 85.4923], [-211.6254, 92.4269], [-214.9643, 97.264], [-219.3841, 104.8407], [-223.0975, 112.8026], [-225.1807, 118.3246], [-227.717, 126.9287], [-229.0155, 132.8787], [-235, 182], [-223, 163], [-192, 182], [-195.6747, 172.564], [-197.5017, 166.3772], [-199.308, 157.2526], [-199.9931, 148.3149], [-199.827, 142.4602], [-198.6436, 133.8339], [-197.0631, 128.0754], [-193.6763, 119.7627], [-190.9383, 114.6049], [-186.1111, 107.4444], [-180.4198, 100.9753], [-176.1454, 97.0466], [-169.0137, 91.7298], [-161.0178, 87.1043], [-152.1578, 83.1701], [-145.7709, 80.9314], [-126.037, 75.7407], [-116.5093, 73.9769], [-107.1481, 72.7963], [-91.9167, 72.125], [-83, 72.5], [-74.25, 73.4583], [-62.8426, 75.6435], [-51.7315, 78.8657], [-43.5926, 81.963], [-33, 87], [12, 104], [2.8235, 91], [18, 91], [18, 35], [-18, 35]]; T4 = [[-17.4286, 91], [-23, 104], [-30.5022, 114.3111], [-35.1256, 122.2778], [-38.8889, 130.4444], [-41.7922, 138.8111], [-43.8356, 147.3778], [-45.2222, 159.1111], [-45.2589, 168.1444], [-43.97, 180.5], [-42, 190], [-36.3253, 203.0657], [-31.4152, 211.4844], [-27.8304, 216.4394], [-23.9965, 220.8685], [-19.9135, 224.7716], [-15.5813, 228.1488], [-7.9689, 232.4356], [-2.12, 234.32], [0.6978, 234.7689], [6.12, 234.68], [8.7244, 234.1422], [13.72, 232.08], [18.4311, 228.7022], [22.8578, 224.0089], [28.5488, 215.8166], [32.3964, 208.9112], [35.0459, 201.4734], [36.497, 193.503], [36.7988, 187.8935], [36.568, 182.0473], [35.2234, 172.8343], [31.5666, 159.7219], [25.7796, 145.6627], [9, 111], [28, 82], [57, 78], [68, 65], [51, 67], [53, 56], [37, 65], [27, 57], [18, 62.7857], [18, 35], [-18, 35], [-18, 59.5], [-36, 55], [-51, 64], [-66, 61], [-61, 75], [-45, 79], [-18, 76.6176], [-18, 91]]; T5 = [[35.2181, 175.3922], [50, 169], [75, 185], [89, 204], [96, 200], [91, 180], [111, 191], [114, 184], [101, 167], [120, 171], [120, 163], [90, 151], [76, 153], [45, 137], [18, 143.3529], [18, 35], [-18, 35], [-18, 188], [-8.0882, 188], [-10, 201], [-0.0622, 204.5378], [8.9511, 209.8178], [17.04, 216.84], [24.2044, 225.6044], [30.4444, 236.1111], [35.76, 248.36], [39.14, 258.69], [43.5469, 276.5156], [45.6875, 282.5625], [50.0117, 290.7539], [53.6367, 295.6289], [57.8197, 300], [106.6667, 300], [130, 324.5], [132, 323], [161, 319], [151, 303], [126, 303], [112, 290], [140, 290], [133, 277], [110, 275], [104.4379, 270.7396], [98.8284, 267.8047], [96.0059, 266.8343], [90.3254, 265.8876], [87.4675, 265.9112], [81.716, 266.9527], [76.978, 268.8269], [73, 271], [71.4379, 257.4852], [69.213, 244.6331], [66.3254, 232.4438], [62.7751, 220.9172], [57.4053, 207.4408], [51, 195]]; targets = [T0, T1, T2, T3, T4, T5]; rock = [[-94, 25], [-84, 43], [-49, 49], [-34, 66], [-12, 60], [6, 69], [32, 52], [68, 43], [88, 25]]; // The foot of a fin: two tabs into the base module foot() { polygon([[-14, 0], [14, 0], [14, 10], [3, 25], [3, 32], [-3, 32], [-3, 25], [-14, 10]]); for (u = [-8, 8]) translate([u - 3, -t]) rect([6, t + 0.3]); } module fin(i) { union() { polygon([for (p = targets[i]) warp(p, i)]); foot(); } if (score_detail) score() { translate([0, 5]) text(str(i + 1), size = 3, halign = "center", valign = "center"); line([-13, 0], [13, 0]); // Points the same way as the arrow at this fin's slots in the base polyline([[-5, 10], [5, 10], [2, 12]]); } } // The rock under the dragon, projected onto a plane 300 mm from the light module stone() { union() { polygon([for (p = rock) [p[0] * 300 / L, sz + (p[1] - sz) * 300 / L]]); polygon([[-94 * 300 / L, 0], [88 * 300 / L, 0], [88 * 300 / L, 25], [-94 * 300 / L, 25]]); for (u = [-20, 20]) translate([u - 4, -t]) rect([8, t + 0.3]); } if (score_detail) score() { translate([0, 8]) text("STONE", size = 3, halign = "center"); polyline([[-24, 28], [-10, 32], [0, 29], [11, 32], [22, 27]]); } } // Holds the pinhole: tape thin card over the 10 mm hole and prick a 1 mm // hole in its centre, with a battery LED close behind it module light_holder() { difference() { union() { translate([-25, 0]) rect([50, 45], r = 3); for (u = [-15, 15]) translate([u - 4, -t]) rect([8, t + 3.3]); } translate([0, 25]) circle(d = 10); } if (score_detail) score() { translate([0, 39]) text("SOURCE", size = 3, halign = "center"); translate([0, 10]) text("1 mm PINHOLE", size = 2.4, halign = "center"); line([-9, 25], [-6, 25]); line([6, 25], [9, 25]); line([0, 31], [0, 34]); line([0, 16], [0, 19]); line([-10, 0], [10, 0]); } } module base() { difference() { polygon([[-100, -25], [-90, -35], [90, -35], [100, -25], [100, 305], [90, 315], [-90, 315], [-100, 305]]); for (i = [0:5]) translate([0, depths[i]]) rotate(yaws[i]) for (u = [-8, 8]) translate([u, 0]) rect([6 + clearance, sw], center = true); translate([0, 300]) for (u = [-20, 20]) translate([u, 0]) rect([8 + clearance, sw], center = true); // The holder sits just behind y = 0, so its front face is at the origin translate([0, -t / 2]) for (u = [-15, 15]) translate([u, 0]) rect([8 + clearance, sw], center = true); } if (score_detail) score() { line([-4, 0], [4, 0]); line([0, -4], [0, 4]); translate([8, -2]) text("O", size = 3); line([0, 12], [0, 115]); polyline([[-3, 109], [0, 115], [3, 109]]); translate([0, 49]) text("U M B R A", size = 5, halign = "center"); translate([0, 61]) text("A DRAGON FROM FRAGMENTS", size = 2.6, halign = "center"); translate([0, 75]) text("WALL Y=900 / PINHOLE Z=25", size = 2.5, halign = "center"); translate([0, 87]) text("BATTERY LED ONLY", size = 3, halign = "center"); for (i = [0:5]) translate([0, depths[i]]) rotate(yaws[i]) { translate([21, 0]) text(str(i + 1), size = 3, halign = "center", valign = "center"); polyline([[14, 0], [18, 0], [16, 2]]); } translate([42, 300]) text("STONE", size = 2.8, halign = "center", valign = "center"); translate([35, -1.5]) text("S", size = 3, halign = "center"); line([-82, 0], [-82, 300]); for (y = [0:25:300]) { line([-82, y], [-78, y]); if (y % 50 == 0) translate([-85, y]) text(str(y), size = 2.5, halign = "right", valign = "center"); } translate([64, 150]) rotate(90) text("+Y TO WALL / 900 mm FROM O", size = 3, halign = "center"); } } module fit_test() { part("Fit test, slots") { difference() { rect([68, 32], r = 2); for (x = [18, 50]) translate([x, 16]) rect([6 + clearance, sw], center = true); } if (score_detail) score() translate([34, 6]) text("GENTLE SLIP FIT", size = 3, halign = "center"); } part("Fit test, tab") { union() { rect([24, 28]); translate([9, -t]) rect([6, t + 0.2]); } if (score_detail) score() { translate([12, 14]) text("6 mm TAB", size = 3, halign = "center"); line([0, 0], [24, 0]); } } } // Assembled: every piece stands in its slots in the base, the fins turned // to their yaws, the stone at the back and the holder at the origin function fin_at(i) = [-t / 2 * sin(yaws[i]), depths[i] + t / 2 * cos(yaws[i]), 0]; if (output == "all" || output == "fins") step("Glue the fins into their numbered slots, arrows matching") for (i = [0:5]) part(str("Fin ", i + 1), at = fin_at(i), rotate = [90, 0, yaws[i]]) fin(i); if (output == "all" || output == "structure") { part("Base", at = [0, 0, -t]) base(); step("Glue the stone and the light holder into their slots") { part("Stone", at = [0, 300 + t / 2, 0], rotate = [90, 0, 0]) stone(); part("Light holder", at = [0, 0, 0], rotate = [90, 0, 0]) light_holder(); } } if (output == "fit") fit_test(); // Setting up: glue the fins, stone and holder into their numbered slots, the // arrows on the fins matching the arrows on the base. In a dark room, stand // the base square to a flat wall with the pinhole 900 mm from it, and light // it with a cool battery LED close behind the pinhole. Never a flame or laser.