qr_coder.scad
1 use <part/connector_peg.scad> 2 3 t = "1000 followers"; 4 head_size = 30; 5 qr_thickness = 1.5; 6 7 peg_radius = 2.5; 8 spacing = 0.5; 9 10 min_error_correction_level = "medium"; // [low, medium, quartile, high] 11 mask = 3; // [0:7] 12 encoding = "byte"; // [num, alphanum, byte] 13 14 qr_coder(); 15 16 module qr_coder() { 17 color("black") 18 rotate([0, 180, 0]) 19 translate([0, 0, -qr_thickness - head_size]) 20 linear_extrude(qr_thickness) 21 mirror([1, 0, 0]) 22 qrcode(t, head_size * 0.9, encoding, min_error_correction_level, mask, center = true); 23 24 cube_character(head_size); 25 } 26 27 module cube_character(head_size) { 28 $fn = 48; 29 30 module head() { 31 half_head_size = head_size / 2; 32 difference() { 33 linear_extrude(head_size) 34 square(head_size, center = true); 35 36 translate([0, -half_head_size, half_head_size]) 37 rotate([-90, 0, 0]) 38 connector_peg(peg_radius, spacing = spacing, void = true); 39 } 40 } 41 42 module body() { 43 body_size = head_size * 0.75; 44 half_body_size = body_size / 2; 45 46 module peg_void() { 47 translate([half_body_size + 1, head_size * 0.2, half_body_size]) 48 rotate([0, -90, 0]) 49 connector_peg(peg_radius, spacing = spacing, void = true); 50 } 51 52 difference() { 53 linear_extrude(body_size) 54 union() { 55 square(body_size, center = true); 56 // feet 57 translate([0, -head_size * 0.4, 0]) 58 difference() { 59 square(head_size * 0.65, center = true); 60 square([head_size * 0.05, head_size * 0.65], center = true); 61 } 62 } 63 64 // holes 65 translate([0, half_body_size + 1, half_body_size]) 66 rotate([90, 0, 0]) 67 connector_peg(peg_radius, spacing = spacing, void = true); 68 69 peg_void(); 70 mirror([1, 0, 0]) peg_void(); 71 } 72 73 // hands 74 hand_size = [head_size * 0.275, head_size * 0.625]; 75 hand_offsetx = body_size + 2; 76 77 module hand() { 78 translate([half_body_size / 2, 0, 0]) 79 rotate([0, -90, 0]) 80 translate([hand_size[0] / 2, 0, 0]) { 81 linear_extrude(half_body_size) 82 square(hand_size, center = true); 83 84 translate([head_size * 0.1375, head_size * 0.1375, half_body_size / 2]) 85 rotate([0, 90, 0]) 86 connector_peg(peg_radius, spacing = spacing); 87 } 88 } 89 90 translate([-body_size, 0]) hand(); 91 translate([body_size, 0]) hand(); 92 } 93 94 head(); 95 96 translate([0, -head_size, 0]) 97 body(); 98 99 translate([0, head_size * 0.75]) 100 connector_peg(peg_radius, spacing = spacing, ends = true); 101 } 102 103 // refactored from https://www.thingiverse.com/thing:258542 104 module qrcode(t, size, encoding = "byte", min_ec = "medium", mask = 3, center = false) { 105 module px(x, y) { 106 translate([x,y]) square([1.01, 1.01]); 107 } 108 109 module draw_encoded_string(encoded_string, version, mask) { 110 for(bit=[0:len(encoded_string)-1]) { 111 draw(encoded_string, version, bit, position(bit,version), mask); 112 } 113 } 114 115 module draw(t, version, bit, xy, mask) { 116 _draw(t, version, bit, floor(xy/1000), xy%1000, mask); 117 } 118 119 module _draw(t, version, bit, x, y, mask) { 120 if(xor(t[bit]=="1",mask(x,y,mask))) { 121 px(x, y); 122 } 123 } 124 125 module draw_alignment_patterns(version) { 126 if(version > 1) { 127 if(version < 7) { 128 offset = 8+4*version; 129 alignment_pattern(offset, offset); 130 } else if(version < 14) { 131 for(i=[0:2]) { 132 for(j=[0:2]) { 133 if((i!=0 && j!=0) || i==1 || j==1) { 134 alignment_pattern(2*i*(version+1)+4, 2*j*(version+1)+4); 135 } 136 } 137 } 138 } 139 } 140 } 141 142 module position_pattern() { 143 difference() { 144 square([7.01,7.01]); 145 translate(v=[1,1]) 146 square([5.01,5.01]); 147 } 148 translate(v=[2,2]) 149 square([3.01,3.01]); 150 } 151 152 module alignment_pattern(x, y) { 153 translate(v=[x, y]) { 154 difference() { 155 square([5.01,5.01]); 156 translate(v=[1,1]) 157 square([3.01,3.01]); 158 } 159 px(2, 2); 160 } 161 } 162 163 module format_pattern(sequence, version) { 164 for(i=[0:5]) { 165 if(sequence[14-i]=="1") { 166 px(i, 8); 167 px(8, 16+4*version-i); 168 } 169 } 170 for(i=[6:7]) { 171 if(sequence[14-i]=="1") { 172 px(i+1, 8); 173 px(8, 16+4*version-i); 174 } 175 } 176 for(i=[8]) { 177 if(sequence[14-i]=="1") { 178 px(8, 15-i); 179 px(2+4*version+i, 8); 180 } 181 } 182 for(i=[9:14]) { 183 if(sequence[14-i]=="1") { 184 px(8, 14-i); 185 px(2+4*version+i, 8); 186 } 187 } 188 } 189 190 module timing_pattern(version) { 191 for(i=[0:2*version]) { 192 px(2*i+8, 6); 193 px(6, 2*i+8); 194 } 195 } 196 197 function substring(t,start,end,output="") = start==end ? output : str(t[start],substring(t,start+1,end,output)); 198 199 function encode(t, ec, version) = str(encode_data(pad_data(t, ec, version), ec, version), encode_error(pad_data(t, ec, version), ec, version), padding_bits(ec, version)); 200 201 function encode_error(s, ec, v) = encode_error_data(error_data(s, ec, v), ec, v); 202 203 version_table = [ 204 [[17,[[19,7]],1],[14,[[16,10]],1],[11,[[13,13]],1],[7,[[9,17]],1],0], 205 [[32,[[34,10]],1],[26,[[28,16]],1],[20,[[22,22]],1],[14,[[16,28]],1],7], 206 [[53,[[55,15]],1],[42,[[44,26]],1],[32,[[17,18],[17,18]],1],[24,[[13,22],[13,22]],2],7], 207 [[78,[[80,20]],1],[62,[[32,18],[32,18]],2],[46,[[24,26],[24,26]],2],[34,[[9,16],[9,16],[9,16],[9,16]],4],7], 208 [[106,[[108,26]],1],[84,[[43,24],[43,24]],2],[60,[[15,18],[15,18],[16,18],[16,18]],4],[44,[[11,22],[11,22],[12,22],[12,22]],4],7], 209 [[134,[[68,18],[68,18]],2],[106,[[27,16],[27,16],[27,16],[27,16]],4],[74,[[19,24],[19,24],[19,24],[19,24]],4],[58,[[15,28],[15,28],[15,28],[15,28]],4],7] 210 ]; 211 function encode_error_data(t, ec, version, pos=0, byte=0, block=0) = 212 version_table[version-1][ec][1][block][1]>byte ? str(bits(hex_to_int(t[pos])*16+hex_to_int(t[pos+1])), encode_error_data(t, ec, version, _next_epos(version, ec, pos, byte, block), _next_ebyte(version, ec, pos, byte, block), _next_eblock(version, ec, pos, byte, block))) : ""; 213 function _next_epos(v,ec,p,y,b) = b<len(version_table[v-1][ec][1])-1 ? p+2*version_table[v-1][ec][1][b][1] : 214 y<version_table[v-1][ec][1][0][1]-1 ? 2*(y+1) : 215 y<version_table[v-1][ec][1][b][1]-1 ? 2*(version_table[v-1][ec][2]*version_table[v-1][ec][1][0][1]+y+1) : 2*(y+1); 216 function _next_ebyte(v,ec,p,y,b) = b<len(version_table[v-1][ec][1])-1 ? y : y+1; 217 function _next_eblock(v,ec,p,y,b) = b<len(version_table[v-1][ec][1])-1 ? b+1 : 218 y<version_table[v-1][ec][1][0][1]-1 ? 0 : 219 y<version_table[v-1][ec][1][b][1]-1 ? version_table[v-1][ec][2] : 0; 220 221 function error_data(s, ec, v, b=0) = b<len(version_table[v-1][ec][1]) ? str(error_block(substring(s,0,2*version_table[v-1][ec][1][b][0]), ec, v, b),error_data(substring(s,2*version_table[v-1][ec][1][b][1],len(s)), ec, v, b+1)) : ""; 222 223 error_formula = ["","","001901","","","0071A4A6770A","0074008605B00F","0057E59295EE6615","00AFEED0F9D7FCC41C","","00FB432E3D7646405E202D","","","004A98B06456646A6882DACE8C4E","00C7F99B30BE7CDA89D857CF3B165B","0008B73D5BCA25333A3AED8C7C056369","0078686B6D66A14C035BBF93A9C2E178","002B8BCE4E2BEF7BCED69318639627F3A388","00D7EA9E5EB86176AA4FBB9894FCB305626099","","00113C4F323DA31ABBCAB4DDE153EF9CA4D4D4BCBE","","00D2ABF7F25DE60E6DDD35C84A08AC6250DB86A069A5E7","","00E5798730D375FB7E9FB4A998C0E2E4DA6F0075E85760E315","","00AD7D9E0267B6761191C96F1CA535A115F58E0D6630E39991DA46","","00A8DFC868E0EA6CB46EBEC393CD1BE8C9152BF5572AC3D477F225097B","","0029AD9198D81FB3B632306E56EF60DE7D2AADE2C1E0829C25FBD8EE28C0B4","","0A066ABEF9A70443D18A8A20F27B591B78B9509C2645AB3C1CDE5034FEB9DCF1","","006F4D925E1A156C13695E71C1568CA37D3A9EE5EFDA673846723DB781A70D623E8133","","00C8B76210AC1FF6EA3C987300A79871F8EE6B123FDA2557D269B1784A79C475FB71E91E2F083B744FA1FC6280CD80A1F739A338EB6A351ABBAEE268AA07AF23B57258292FA37D864814E835230F","","","","","","00FA67DDE6191289E700033AF2DDBF6E54E608BC6A60930F838B2265DF2765D5C7EDFEC97BABA2C2753260","","00BE073D7947F64537A8BC59F3BF19487B09910EF701EE2C4E8F3EE07E767244A334C2D993CCA92582716649B5","","00705E5870FDE0CA73BB6359053671812C3A1087D8A9D3240104603CF14968EA08F9F577AE34199DE02BCADF13520F","","00E419C482D3923C18FB5A2766F03DB23F2E7B7312DD6F87A0B6CD6BCE5F9678B85B15F79C8CEEBF0B5EE35432A327226C","","00E87D9DA1A409762ED163CBC12303D16FC3F2CBE12E0D20A07ED182A0F2D7F24B4D2ABD2071417C45E472EBAF7CAAD7E885CD","","00743256BA32DCFB59C02E567F7C13B8E997D7160E3B9125F2CB86FE59BE5E3B417C7164E9EB79164C566127F2C8DC6521EFFE7433","","00B71AC957D2DD71152E412D32EEB8F9E1663AD1DA6DA51A5FB8C034F523FEEEAFAC4F7B197A2B786CD75080C9EB08993B651FC64C1F9C","","006A786B9DA4D87074025BF8A324C9CAE50690FE9B87D0AAD10C8B7F8EB6F9B1AEBE1C0A55EFB8657C98CE6017A33D1BC4F7979ACACF143D0A","","0052741AF7421B3E6BFCB6C8B9EB37FBF2D2909AEDB08DC0F898F9CE55FD8E41A57D17181E7AF0D60681DA1D917F86CEF5751D293F9F8EE97D947B","","006B8C1A0C098DF3C5E2C5DB2DD365DB781CB57F0664F702CDC63973DB656DA0522526EE31A0D179560B7C1EB55419C2574166BEDC461BD110590721F0","","0041CA716247DFF876D65E007A251702E43A790769874EF376464CDF594832466FC211D47EB523DD75EB0BE595937BD5287306C8641AF6B6DA7FD724BA6E6A","","002D33AF09079E9F3144775C7BB1CCBBFEC84E8D95771A7F35A05DC7D41D18919CD096DAD104D85B2FB8922F8CC3C37DF2EE3F636C8CE6F21FCC0BB2F3D99CD5E7","","000576DEB48888A2332E750DD751118BF7C5AB5FAD4189B2446F5F652948D6A9C55F072C9A4D6FEC28798F3F5750FDF07ED94D22E86A32A8524C92436AAB19845D2D69","","00F79FDF21E05D4D465AA020FE2B965465BECD85343CCAA5DCCB975D540F54FDADA059E334C7615FE734B1297D89F1A6E17602362052D7AFC62BEEEB1B65B87F030508A3EE"]; 224 function error_block(s, ec, v, b) = encode_eb(pad_eb(s,version_table[v-1][ec][1][b][1]),error_formula[version_table[v-1][ec][1][b][1]]); 225 226 function pad_eb(s,count) = count<=0 ? s : pad_eb(str(s,"00"),count-1); 227 228 function encode_eb(s,f) = len(s)<=len(f)-2 ? s : encode_eb(substring(_encode_eb(s,f,log_a(hex_to_int(s[0])*16+hex_to_int(s[1]))),2,len(s)),f); 229 230 function _encode_eb(s,f,s0,p=0) = p==len(f) ? substring(s,p,len(s)) : 231 str(int_to_hex(bitwise_xor(pow_a(((hex_to_int(f[p])*16+hex_to_int(f[p+1]))+s0)%255),hex_to_int(s[p])*16+hex_to_int(s[p+1]))),_encode_eb(s,f,s0,p+2)); 232 233 coefficients = [1,2,4,8,16,32,64,128,29,58,116,232,205,135,19,38,76,152,45,90,180,117,234,201,143,3,6,12,24,48,96,192,157,39,78,156,37,74,148,53,106,212,181,119,238,193,159,35,70,140,5,10,20,40,80,160,93,186,105,210,185,111,222,161,95,190,97,194,153,47,94,188,101,202,137,15,30,60,120,240,253,231,211,187,107,214,177,127,254,225,223,163,91,182,113,226,217,175,67,134,17,34,68,136,13,26,52,104,208,189,103,206,129,31,62,124,248,237,199,147,59,118,236,197,151,51,102,204,133,23,46,92,184,109,218,169,79,158,33,66,132,21,42,84,168,77,154,41,82,164,85,170,73,146,57,114,228,213,183,115,230,209,191,99,198,145,63,126,252,229,215,179,123,246,241,255,227,219,171,75,150,49,98,196,149,55,110,220,165,87,174,65,130,25,50,100,200,141,7,14,28,56,112,224,221,167,83,166,81,162,89,178,121,242,249,239,195,155,43,86,172,69,138,9,18,36,72,144,61,122,244,245,247,243,251,235,203,139,11,22,44,88,176,125,250,233,207,131,27,54,108,216,173,71,142]; 234 function pow_a(x) = coefficients[x]; 235 236 inv_coef = [255,0,1,25,2,50,26,198,3,223,51,238,27,104,199,75,4,100,224,14,52,141,239,129,28,193,105,248,200,8,76,113,5,138,101,47,225,36,15,33,53,147,142,218,240,18,130,69,29,181,194,125,106,39,249,185,201,154,9,120,77,228,114,166,6,191,139,98,102,221,48,253,226,152,37,179,16,145,34,136,54,208,148,206,143,150,219,189,241,210,19,92,131,56,70,64,30,66,182,163,195,72,126,110,107,58,40,84,250,133,186,61,202,94,155,159,10,21,121,43,78,212,229,172,115,243,167,87,7,112,192,247,140,128,99,13,103,74,222,237,49,197,254,24,227,165,153,119,38,184,180,124,17,68,146,217,35,32,137,46,55,63,209,91,149,188,207,205,144,135,151,178,220,252,190,97,242,86,211,171,20,42,93,158,132,60,57,83,71,109,65,162,31,45,67,216,183,123,164,118,196,23,73,236,127,12,111,246,108,161,59,82,41,157,85,170,251,96,134,177,187,204,62,90,203,89,95,176,156,169,160,81,11,245,22,235,122,117,44,215,79,174,213,233,230,231,173,232,116,214,244,234,168,80,88,175]; 237 function log_a(x) = inv_coef[x]; 238 239 function encode_data(t, ec, version, pos=0, byte=0, block=0) = 240 version_table[version-1][ec][1][block][0]>byte ? str(bits(hex_to_int(t[pos])*16+hex_to_int(t[pos+1])), encode_data(t, ec, version, _next_dpos(version, ec, pos, byte, block), _next_dbyte(version, ec, pos, byte, block), _next_dblock(version, ec, pos, byte, block))) : ""; 241 function _next_dpos(v,ec,p,y,b) = b<len(version_table[v-1][ec][1])-1 ? p+2*version_table[v-1][ec][1][b][0] : 242 y<version_table[v-1][ec][1][0][0]-1 ? 2*(y+1) : 243 y<version_table[v-1][ec][1][b][0]-1 ? 2*(version_table[v-1][ec][2]*version_table[v-1][ec][1][0][0]+y+1) : 2*(y+1); 244 function _next_dbyte(v,ec,p,y,b) = b<len(version_table[v-1][ec][1])-1 ? y : y+1; 245 function _next_dblock(v,ec,p,y,b) = b<len(version_table[v-1][ec][1])-1 ? b+1 : 246 y<version_table[v-1][ec][1][0][0]-1 ? 0 : 247 y<version_table[v-1][ec][1][b][0]-1 ? version_table[v-1][ec][2] : 0; 248 249 function padding_bits(ec, v, b=0) = b<version_table[v-1][4] ? str("0",padding_bits(ec,v,b+1)) : ""; 250 251 function pad_data(t, ec, v, pad_string="") = len(t)+len(pad_string)>2*version_table[v-1][ec][0]+2+floor((v+20)/30) ? str(t,pad_string) : 252 len(pad_string)%4 == 0 ? pad_data(t, ec, v, str(pad_string,"EC")) : 253 pad_data(t, ec, v, str(pad_string,"11")); 254 255 function bits(char, bit=0) = bit<7 ? str(_bit(char,bit),bits(char - (_bit(char,bit) * pow(2,7-bit)),bit+1)) : _bit(char,bit); 256 function _bit(char, bit) = char>=pow(2,7-bit) ? 1 : 0; 257 258 function hex_bits(chars) = bits(hex_to_int(chars[0])*16+hex_to_int(chars[1])); 259 function hex_to_int(char) = search(char,"0123456789ABCDEF")[0]; 260 261 function int(char) = (search(char," !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ \t\n\r")[0]+32) % 256; 262 function ec_to_int(ec) = ec=="low" ? 0 : 263 ec=="medium" ? 1 : 264 ec=="quartile" ? 2 : 3; 265 266 function format_sequence(ec,mask) = ["101010000010010","101000100100101","101111001111100","101101101001011","100010111111001","100000011001110","100111110010111","100101010100000","111011111000100","111001011110011","111110110101010","111100010011101","110011000101111","110001100011000","110110001000001","110100101110110","001011010001001","001001110111110","001110011100111","001100111010000","000011101100010","000001001010101","000110100001100","000100000111011","011010101011111","011000001101000","011111100110001","011101000000110","010010010110100","010000110000011","010111011011010","010101111101101"][bitwise_xor(ec,1)*8+mask]; 267 268 function version(t, encoding, ec, v=1) = len(t)<=version_table[v-1][ec][0] ? v : version(t, encoding, ec, v+1); 269 270 function ec(s,ec,v) = ec<4 && len(s)<=version_table[v-1][ec][0] ? ec(s,ec+1,v) : ec-1; 271 272 function xor(a, b) = (a||b)&& !(a&&b); 273 274 function bitwise_xor(a, b) = (a==0 && b==0) ? 0 : 2*bitwise_xor(floor(a/2), floor(b/2))+(xor(a%2==1,b%2==1)?1:0); 275 276 function mask(x, y, m) = m==0 ? mask0(x,y) : 277 m==1 ? mask1(x,y) : 278 m==2 ? mask2(x,y) : 279 m==3 ? mask3(x,y) : 280 m==4 ? mask4(x,y) : 281 m==5 ? mask5(x,y) : 282 m==6 ? mask6(x,y) : 283 mask7(x,y); 284 function mask0(x,y) = (x+y)%2 == 0; 285 function mask1(x,y) = x%2 == 0; 286 function mask2(x,y) = y%3 == 0; 287 function mask3(x,y) = (x+y)%3 == 0; 288 function mask4(x,y) = (floor(x/2)+floor(y/3))%2 == 0; 289 function mask5(x,y) = (x*y)%2 + (x*y)%3 == 0; 290 function mask6(x,y) = ((x*y)%2 + (x*y)%3)%2 == 0; 291 function mask7(x,y) = ((x+y)%2 + (x*y)%3)%2 == 0; 292 293 function position(bit, version) = _position(bit,4*version+16,4*version+16,version,-1); 294 function reserved(x,y,v) = x==6 || y==6 || 295 (x<9 && y<9) || (x<9 && y>4*v+8) || (x>4*v+8 && y<9) || 296 (v>6 && x>4*v+5 && y<6) || (v>6 && y>4*v+5 && x<6) || 297 (v>1 && x>=4*v+8 && x<=4*v+12 && y>=4*v+8 && y<=4*v+12) || 298 (v>=7 && v<=13 && ((x>=2*v+6 && x<=2*v+10 && y%(2*v+2)>=4 && y%(2*v+2)<=8) || (y>=2*v+6 && y<=2*v+10 && x%(2*v+2)>=4 && x%(2*v+2)<=8))); 299 function _position(b,x,y,v,d) = b==0 && !reserved(x,y,v) ? 1000*x+y : 300 reserved(x,y,v) ? _next_pos(b,x,y,v,d) : _next_pos(b-1,x,y,v,d); 301 function _next_pos(b,x,y,v,d) = (y == 6) || ((y>6 && y%2 == 0) || (y<6 && y%2 == 1)) ? _position(b,x,y-1,v,d) : 302 (d<0 && x == 0) || (d>0 && x == 4*v+16) ? _position(b,x,y-1,v,-d) : 303 _position(b,x+d,y+1,v,d); 304 305 function str_to_hex(t,char=0) = char==len(t) ? "" : str("0123456789ABCDEF"[floor(int(t[char])/16)],"0123456789ABCDEF"[int(t[char])%16],str_to_hex(t,char+1)); 306 307 function int_to_hex(i) = str("0123456789ABCDEF"[floor(i/16)],"0123456789ABCDEF"[i%16]); 308 309 version = version(t, encoding, ec_to_int(min_ec)); 310 dist_between_position_pattern = 10 + 4 * version; 311 312 ec=ec(t,ec_to_int(min_ec),version); 313 314 encoded_string = version<10 ? encode(str("4",int_to_hex(len(t)),str_to_hex(t),"0"), ec, version) : encode(str("4",int_to_hex(floor(len(t)/256)),int_to_hex(len(t)%256),str_to_hex(t),"0"), ec, version); 315 316 qr_size = dist_between_position_pattern + 7; 317 half_qr_size = qr_size / 2; 318 319 s_factor = is_undef(size) ? 1 : size / qr_size; 320 scaled_size = s_factor * qr_size; 321 half_scaled_size = scaled_size / 2; 322 323 // QR Code 324 translate(center ? [-half_scaled_size, -half_scaled_size] : [0, 0]) scale(s_factor) { 325 position_pattern(); 326 327 translate(v = [dist_between_position_pattern, 0]) 328 position_pattern(); 329 330 translate(v=[0, dist_between_position_pattern]) 331 position_pattern(); 332 333 px(9+4*version, 8); 334 335 format_pattern(format_sequence(ec,mask), version); 336 timing_pattern(version); 337 draw_alignment_patterns(version); 338 draw_encoded_string(encoded_string, version, mask); 339 } 340 }