use t = "1000 followers"; head_size = 30; qr_thickness = 1.5; peg_radius = 2.5; spacing = 0.5; min_error_correction_level = "medium"; // [low, medium, quartile, high] mask = 3; // [0:7] encoding = "byte"; // [num, alphanum, byte] qr_coder(); module qr_coder() { color("black") rotate([0, 180, 0]) translate([0, 0, -qr_thickness - head_size]) linear_extrude(qr_thickness) mirror([1, 0, 0]) qrcode(t, head_size * 0.9, encoding, min_error_correction_level, mask, center = true); cube_character(head_size); } module cube_character(head_size) { $fn = 48; module head() { half_head_size = head_size / 2; difference() { linear_extrude(head_size) square(head_size, center = true); translate([0, -half_head_size, half_head_size]) rotate([-90, 0, 0]) connector_peg(peg_radius, spacing = spacing, void = true); } } module body() { body_size = head_size * 0.75; half_body_size = body_size / 2; module peg_void() { translate([half_body_size + 1, head_size * 0.2, half_body_size]) rotate([0, -90, 0]) connector_peg(peg_radius, spacing = spacing, void = true); } difference() { linear_extrude(body_size) union() { square(body_size, center = true); // feet translate([0, -head_size * 0.4, 0]) difference() { square(head_size * 0.65, center = true); square([head_size * 0.05, head_size * 0.65], center = true); } } // holes translate([0, half_body_size + 1, half_body_size]) rotate([90, 0, 0]) connector_peg(peg_radius, spacing = spacing, void = true); peg_void(); mirror([1, 0, 0]) peg_void(); } // hands hand_size = [head_size * 0.275, head_size * 0.625]; hand_offsetx = body_size + 2; module hand() { translate([half_body_size / 2, 0, 0]) rotate([0, -90, 0]) translate([hand_size[0] / 2, 0, 0]) { linear_extrude(half_body_size) square(hand_size, center = true); translate([head_size * 0.1375, head_size * 0.1375, half_body_size / 2]) rotate([0, 90, 0]) connector_peg(peg_radius, spacing = spacing); } } translate([-body_size, 0]) hand(); translate([body_size, 0]) hand(); } head(); translate([0, -head_size, 0]) body(); translate([0, head_size * 0.75]) connector_peg(peg_radius, spacing = spacing, ends = true); } // refactored from https://www.thingiverse.com/thing:258542 module qrcode(t, size, encoding = "byte", min_ec = "medium", mask = 3, center = false) { module px(x, y) { translate([x,y]) square([1.01, 1.01]); } module draw_encoded_string(encoded_string, version, mask) { for(bit=[0:len(encoded_string)-1]) { draw(encoded_string, version, bit, position(bit,version), mask); } } module draw(t, version, bit, xy, mask) { _draw(t, version, bit, floor(xy/1000), xy%1000, mask); } module _draw(t, version, bit, x, y, mask) { if(xor(t[bit]=="1",mask(x,y,mask))) { px(x, y); } } module draw_alignment_patterns(version) { if(version > 1) { if(version < 7) { offset = 8+4*version; alignment_pattern(offset, offset); } else if(version < 14) { for(i=[0:2]) { for(j=[0:2]) { if((i!=0 && j!=0) || i==1 || j==1) { alignment_pattern(2*i*(version+1)+4, 2*j*(version+1)+4); } } } } } } module position_pattern() { difference() { square([7.01,7.01]); translate(v=[1,1]) square([5.01,5.01]); } translate(v=[2,2]) square([3.01,3.01]); } module alignment_pattern(x, y) { translate(v=[x, y]) { difference() { square([5.01,5.01]); translate(v=[1,1]) square([3.01,3.01]); } px(2, 2); } } module format_pattern(sequence, version) { for(i=[0:5]) { if(sequence[14-i]=="1") { px(i, 8); px(8, 16+4*version-i); } } for(i=[6:7]) { if(sequence[14-i]=="1") { px(i+1, 8); px(8, 16+4*version-i); } } for(i=[8]) { if(sequence[14-i]=="1") { px(8, 15-i); px(2+4*version+i, 8); } } for(i=[9:14]) { if(sequence[14-i]=="1") { px(8, 14-i); px(2+4*version+i, 8); } } } module timing_pattern(version) { for(i=[0:2*version]) { px(2*i+8, 6); px(6, 2*i+8); } } function substring(t,start,end,output="") = start==end ? output : str(t[start],substring(t,start+1,end,output)); 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)); function encode_error(s, ec, v) = encode_error_data(error_data(s, ec, v), ec, v); version_table = [ [[17,[[19,7]],1],[14,[[16,10]],1],[11,[[13,13]],1],[7,[[9,17]],1],0], [[32,[[34,10]],1],[26,[[28,16]],1],[20,[[22,22]],1],[14,[[16,28]],1],7], [[53,[[55,15]],1],[42,[[44,26]],1],[32,[[17,18],[17,18]],1],[24,[[13,22],[13,22]],2],7], [[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], [[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], [[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] ]; function encode_error_data(t, ec, version, pos=0, byte=0, block=0) = 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))) : ""; function _next_epos(v,ec,p,y,b) = bbyte ? 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))) : ""; function _next_dpos(v,ec,p,y,b) = b2*version_table[v-1][ec][0]+2+floor((v+20)/30) ? str(t,pad_string) : len(pad_string)%4 == 0 ? pad_data(t, ec, v, str(pad_string,"EC")) : pad_data(t, ec, v, str(pad_string,"11")); 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); function _bit(char, bit) = char>=pow(2,7-bit) ? 1 : 0; function hex_bits(chars) = bits(hex_to_int(chars[0])*16+hex_to_int(chars[1])); function hex_to_int(char) = search(char,"0123456789ABCDEF")[0]; function int(char) = (search(char," !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ \t\n \r")[0]+32) % 256; function ec_to_int(ec) = ec=="low" ? 0 : ec=="medium" ? 1 : ec=="quartile" ? 2 : 3; 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]; function version(t, encoding, ec, v=1) = len(t)<=version_table[v-1][ec][0] ? v : version(t, encoding, ec, v+1); function ec(s,ec,v) = ec<4 && len(s)<=version_table[v-1][ec][0] ? ec(s,ec+1,v) : ec-1; function xor(a, b) = (a||b)&& !(a&&b); 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); function mask(x, y, m) = m==0 ? mask0(x,y) : m==1 ? mask1(x,y) : m==2 ? mask2(x,y) : m==3 ? mask3(x,y) : m==4 ? mask4(x,y) : m==5 ? mask5(x,y) : m==6 ? mask6(x,y) : mask7(x,y); function mask0(x,y) = (x+y)%2 == 0; function mask1(x,y) = x%2 == 0; function mask2(x,y) = y%3 == 0; function mask3(x,y) = (x+y)%3 == 0; function mask4(x,y) = (floor(x/2)+floor(y/3))%2 == 0; function mask5(x,y) = (x*y)%2 + (x*y)%3 == 0; function mask6(x,y) = ((x*y)%2 + (x*y)%3)%2 == 0; function mask7(x,y) = ((x+y)%2 + (x*y)%3)%2 == 0; function position(bit, version) = _position(bit,4*version+16,4*version+16,version,-1); function reserved(x,y,v) = x==6 || y==6 || (x<9 && y<9) || (x<9 && y>4*v+8) || (x>4*v+8 && y<9) || (v>6 && x>4*v+5 && y<6) || (v>6 && y>4*v+5 && x<6) || (v>1 && x>=4*v+8 && x<=4*v+12 && y>=4*v+8 && y<=4*v+12) || (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))); function _position(b,x,y,v,d) = b==0 && !reserved(x,y,v) ? 1000*x+y : reserved(x,y,v) ? _next_pos(b,x,y,v,d) : _next_pos(b-1,x,y,v,d); 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) : (d<0 && x == 0) || (d>0 && x == 4*v+16) ? _position(b,x,y-1,v,-d) : _position(b,x+d,y+1,v,d); 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)); function int_to_hex(i) = str("0123456789ABCDEF"[floor(i/16)],"0123456789ABCDEF"[i%16]); version = version(t, encoding, ec_to_int(min_ec)); dist_between_position_pattern = 10 + 4 * version; ec=ec(t,ec_to_int(min_ec),version); 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); qr_size = dist_between_position_pattern + 7; half_qr_size = qr_size / 2; s_factor = is_undef(size) ? 1 : size / qr_size; scaled_size = s_factor * qr_size; half_scaled_size = scaled_size / 2; // QR Code translate(center ? [-half_scaled_size, -half_scaled_size] : [0, 0]) scale(s_factor) { position_pattern(); translate(v = [dist_between_position_pattern, 0]) position_pattern(); translate(v=[0, dist_between_position_pattern]) position_pattern(); px(9+4*version, 8); format_pattern(format_sequence(ec,mask), version); timing_pattern(version); draw_alignment_patterns(version); draw_encoded_string(encoded_string, version, mask); } }