Skip to content

Instantly share code, notes, and snippets.

@amonger
Created April 6, 2025 18:41
Show Gist options
  • Save amonger/b75b08c1ebe06733a7f383a82b71c74c to your computer and use it in GitHub Desktop.
Save amonger/b75b08c1ebe06733a7f383a82b71c74c to your computer and use it in GitHub Desktop.
// select the type and complete the matching tab below (***)
Type = "T";// [T:Text, W:Wi-Fi,P:Phone Call,V:vCard]
/* [*** Text Type ***] */
// string to encode (text, url, mail, ect.)
Text = "hello world"; // 128
/* [*** Wi-Fi Type ***] */
// Wi-Fi network name
SSID = "my ssid"; // 32
// Wi-Fi Authentication Type
Auth = "WPA"; // [nopass: open network - no password, WEP:WEP password protection - obsolete,WPA:WPA password protection]
// Wi-Fi Password
Password = "my password";
// whether network is hidden
Is_Hidden = false;
/* [*** Phone Call Type ****] */
// phone number
Number = "+48111111111"; // 32
/* [*** vCard Type ***] */
// firstname
Firstname = "John"; // 32
// surname - lastname
Surname = "Smith"; // 32
// street address
Address = "My Street, 123"; // 64
// city name
City = "Los Angeles"; // 64
// region (e.g. state or province)
Region = "California"; // 64
// postal code
Postalcode = "90001"; // 32
// full country name
Country = "United States"; // 64
// email address
Email = "[email protected]"; // 64
// website or other URL
Site_URL = "https://www.example.org"; // 64
// phone number
Phone_Number = "+48111111111"; // 32
/* [Dimensions] */
// overall size of the object (mm)
Overall_Size = 50;// [10:0.1:200]
// size of the border (mm)
Border_Size = 1;// [0:0.1:20]
// thickness of the background (mm)
Background_Thickness = 1;// [1:0.1:20]
// thickness of the QR Code (mm)
QR_Code_Thickness = 0.6;// [0.6:0.1:20]
/* [QR Code Options] */
// use round blocks
Round_Blocks = false;
/* [Hole Keyring Options] */
// diameter of the hole (mm, 0 to remove)
Hole_Size = 0; // [0:1:10]
/* [Label Options] */
// size of the label (mm, 0 to remove)
Label_Size = 0; //[0:1:10]
// position of the label (if enabled)
Label_Position = "T";// [T:Top,B:Bottom,TB:Top & Bottom]
// primary string (when top or bottom)
Label_Primary_String = "SCAN ME"; // 32
// secondary string (when both)
Label_Secondary_String = "NOW"; // 32
// thickness of the label (mm)
Label_Thickness = 0.6;// [0.6:0.1:20]
// font name
Font = "Roboto:style=Regular"; // font
/* [Magnet Options] */
// magnet type
Magnet_Type = "N"; // [N:None, C_04_02:D4x2 mm Round Magnet, C_05_01:D5x1 mm Round Magnet, C_05_02:D5x2 mm Round Magnet, C_05_03:D5x3 mm Round Magnet, C_06_02:D6x2 mm Round Magnet, C_06_03:D6x3 mm Round Magnet, C_06_02:D6x4 mm Round Magnet, C_08_02:D8x2 mm Round Magnet, C_08_03:D8x3 mm Round Magnet, C_10_03:D10x3 mm Round Magnet, C_12_03:D12x3 mm Round Magnet, C_15_03:D15x3 mm Round Magnet, C_20_03:D20x3 mm Round Magnet, R_10_05_01:10x5x1 mm Rectangular Magnet, R_10_05_02:10x5x2 mm Rectangular Magnet, R_20_10_02:20x10x2 mm Rectangular Magnet, R_20_10_03:20x10x3 mm Rectangular Magnet, R_25_15_05:25x15x5 mm Rectangular Magnet]
// magnet hole clearance (mm)
Magnet_Clearance = 0.20; // [0:0.05:0.4]
/* [Colors] */
// background color
Color_Background = "#000000"; // color
// QR Code color
Color_QR_Code = "#FFFFFF"; // color
// label color
Color_Labels = "#333333"; // color
/* [Parts Download] */
// parts to download in STL (to be merged in Bambu Studio)
Parts = "A"; //[A:All, B:Background,Q:QR Code,L:Label - if present]
/* [Hidden] */
//
// Automatically generated by generate.py.
// DO NOT EDIT!!!
// Source files are in the src/ directory.
//
//
// ######## ## ####### #######
// ##////// /## ##/////## /##////##
// /## ##### ###### /## ## //## /## /##
// /######### ##///## //////## ######/## /## /#######
// ////////##/## // ####### ##///##/## ##/## /##///##
// /##/## ## ##////## /## /##//## // ## /## //##
// ######## //##### //########//###### //####### ##/## //##
// //////// ///// //////// ////// /////// // // //
//
// Effortlessly generate QR codes directly in OpenSCAD
// https://github.com/xypwn/scadqr
//
// Copyright (c) 2024 Darwin Schuppan and contributors. All rights reserved.
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
// BEGIN src/qr.scad
//
// Public API
//
//@PUBLIC
// Generates a QR code encoding plain text.
// error_correction: options: "L" (~7%), "M" (~15%), "Q" (~25%) or "H" (~30%)
// thickness: thickness or 0 for 2D
// mask_pattern: range: 0-7
// encoding: options: "UTF-8" (Unicode) or "Shift_JIS" (Shift Japanese International Standards)
module qr(message, error_correction="M", width=100, height=100, thickness=1, center=false, mask_pattern=0, encoding="UTF-8", rounded=false)
if (rounded) {
qr_custom(message, error_correction, width, height, thickness, center, mask_pattern, encoding) {
translate([0.5, 0.5])
scale([0.8, 0.8])
circle(d=1, $fn=16);
translate([3.5, 3.5]) union() {
difference() {
circle(d=7, $fn=32);
circle(d=5.2, $fn=32);
}
circle(d=3, $fn=24);
}
translate([2.5, 2.5]) union() {
difference() {
circle(d=4.3, $fn=32);
circle(d=3, $fn=32);
}
circle(d=1, $fn=16);
}
}
} else {
qr_custom(message, error_correction, width, height, thickness, center, mask_pattern, encoding) {
_qr_default_module();
_qr_default_position_pattern();
_qr_default_alignment_pattern();
}
}
// Generates a QR code using custom elements.
// Child elements (2D, origin: [0,0], must extend into positive XY, 1 module = 1mm):
// - `children(0)`: Module (black pixel)
// - `children(1)`: Position pattern
// - `children(2)`: Alignment pattern
// error_correction: options: "L" (~7%), "M" (~15%), "Q" (~25%) or "H" (~30%)
// thickness: thickness or 0 for 2D
// mask_pattern: range: 0-7
// encoding: options: "UTF-8" (Unicode) or "Shift_JIS" (Shift Japanese International Standards)
module qr_custom(message, error_correction="M", width=100, height=100, thickness=1, center=false, mask_pattern=0, encoding="UTF-8") {
ec_lvl =
error_correction == "L" ? _qr_EC_L :
error_correction == "M" ? _qr_EC_M :
error_correction == "Q" ? _qr_EC_Q :
error_correction == "H" ? _qr_EC_H :
undef;
assert(ec_lvl >= _qr_EC_L && ec_lvl <= _qr_EC_H, "error_correction must be \"L\", \"M\", \"Q\" or \"H\"");
enc =
encoding == "Shift_JIS" ? _qr_ENC_SJIS :
encoding == "UTF-8" ? _qr_ENC_UTF8 :
undef;
assert(enc >= _qr_ENC_SJIS && enc <= _qr_ENC_UTF8, "encoding must be \"UTF-8\" or \"Shift_JIS\"");
message_bytes = _qr_str2bytes(message);
ver = _qr_get_version(len(message_bytes), ec_lvl, enc);
size = _qr_version2size(ver);
bits = _qr_encode_message(message_bytes, ec_lvl, mask_pattern, ver, enc);
positions = _qr_data_bit_positions(size);
translate(center ? [-width/2, -height/2, 0] : [0,0,0])
_qr_extrude_or_2d(thickness)
scale([width/size, height/size]) {
// Position patterns
for(i=[[0,6],[size-7,6],[0,size-1]])
translate([i[0], size-1-i[1], 0])
children(1);
// Timing patterns
for(x=[8:size-1-8])
if (x%2 == 0)
_qr_module_1(size, x, 6) children(0);
for(y=[8:size-1-8])
if (y%2 == 0)
_qr_module_1(size, 6, y) children(0);
// Alignment patterns
if (ver >= 2) {
n_pats = _qr_n_alignment_patterns(ver);
pat_step = _qr_alignment_pattern_step(ver);
pat_last = size-1-6;
pat_coords = concat([6], [
for(i=[0:max(0, n_pats-2)]) pat_last-i*pat_step
]);
for(y=pat_coords,x=pat_coords)
if (!(
(x == 6 && y == 6) ||
(x == 6 && y == pat_last) ||
(x == pat_last && y == 6)
))
translate([x-2, size-1-y-2, 0])
children(2);
}
// Version information
if(ver >= 7) {
verinf = _qr_verinf_bits(ver);
for(i=[0:17])
if (verinf[17-i])
_qr_module_1(size, floor(i/3), size-11+i%3) children(0);
for(i=[0:17])
if (verinf[17-i])
_qr_module_1(size, size-11+i%3, floor(i/3)) children(0);
}
// Format info
fmtinf = _qr_fmtinf_bits(ec_lvl, mask_pattern);
for(i=[0:7])
if (fmtinf[14-i])
_qr_module_1(size, 8, i <= 5 ? i : i+1) children(0);;
for(i=[8:14])
if (fmtinf[14-i])
_qr_module_1(size, 15-(i <= 8 ? i : i+1), 8) children(0);;
for(i=[0:7])
if (fmtinf[14-i])
_qr_module_1(size, size-1-i, 8) children(0);;
for(i=[8:14])
if (fmtinf[14-i])
_qr_module_1(size, 8, size-1-6+i-8) children(0);;
_qr_module_1(size, 8, size-1-7) children(0);;
// Modules
for(p=positions) {
x = p[0];
y = p[1];
i = p[2];
val = _qr_apply_mask_pattern(
bits[i],
x, y, mask_pattern
);
if (val)
_qr_module_1(size, x, y) children(0);
}
}
}
// Returns the length of one side of the QR code (in modules/squares).
// error_correction: options: "L" (~7%), "M" (~15%), "Q" (~25%) or "H" (~30%)
// encoding: options: "UTF-8" (Unicode) or "Shift_JIS" (Shift Japanese International Standards)
function qr_size(message, error_correction="M", encoding="UTF-8") =
_qr_version2size(qr_version(message, error_correction, encoding));
// Returns the version of a QR code (1 <= version <= 40; version dictates the size).
// error_correction: options: "L" (~7%), "M" (~15%), "Q" (~25%) or "H" (~30%)
// encoding: options: "UTF-8" (Unicode) or "Shift_JIS" (Shift Japanese International Standards)
function qr_version(message, error_correction="M", encoding="UTF-8") =
let(ec_lvl =
error_correction == "L" ? _qr_EC_L :
error_correction == "M" ? _qr_EC_M :
error_correction == "Q" ? _qr_EC_Q :
error_correction == "H" ? _qr_EC_H :
undef)
assert(ec_lvl >= _qr_EC_L && ec_lvl <= _qr_EC_H, "error_correction must be \"L\", \"M\", \"Q\" or \"H\"")
let(enc =
encoding == "Shift_JIS" ? _qr_ENC_SJIS :
encoding == "UTF-8" ? _qr_ENC_UTF8 :
undef)
assert(enc >= _qr_ENC_SJIS && enc <= _qr_ENC_UTF8, "encoding must be \"UTF-8\" or \"Shift_JIS\"")
_qr_get_version(_qr_str_num_bytes(message), ec_lvl, enc);
// Generates a 'connect to wifi' message which can be input into qr().
// ssid: network name
// psk: network password
// auth: options: "nopass" (open network), "WPA" (WPA password protection), "WEP" (WEP password protection; obsolete)
// hidden: whether network is hidden
function qr_wifi(ssid, psk, auth="WPA", hidden=false) =
(auth != "nopass" && auth != "WPA" && auth != "WEP") ? undef :
str("WIFI:T:", auth, ";S:", ssid, ";P:", auth != "nopass" ? psk : "", ";", hidden ? "H:true" : "", ";");
// Generates a 'make a phone call' message which can be input into qr().
function qr_phone_call(number) =
str("TEL:", number);
// Generates a VCard containing contact info which can be input into qr().
// Only a basic subset of VCard is implemented.
// If applicable, multiple entries must be separated by commas (e.g. middlenames, nameprefixes...).
// lastname: last name
// firstname: first name
// middlenames: additional first names
// nameprefixes: honorific prefixes
// namesuffixes: honorific suffixes
// customfullname: full name, leave blank to automatically generate
// email: email address
// url: website or other URL
// phone: phone number
// address: street address
// ext_address: extended address (e.g. apartment or suite number)
// city: city name
// region: region (e.g. state or province)
// postalcode: postal code
// country: full country name
function qr_vcard(lastname, firstname, middlenames="", nameprefixes="", namesuffixes="", customfullname="", email="", url="", phone="", address="", ext_address="", city="", region="", postalcode="", country="") =
let (fullname = customfullname ? customfullname :
_qr_strjoin(
[ for (s=[nameprefixes, firstname, middlenames, lastname, namesuffixes]) if (s != "") s ],
delim=" "
))
str(
"BEGIN:VCARD\n",
"VERSION:3.0\n",
"N:",lastname,";",firstname,";",middlenames,";",nameprefixes,";",namesuffixes,"\n",
"FN:",fullname,"\n",
email ?
str("EMAIL;type=PREF,INTERNET:",email,"\n") : "",
url ?
str("URL:",url,"\n") : "",
phone ?
str("TEL:",phone,"\n") : "",
(address || ext_address || city || region || postalcode || country) ?
str("ADR;TYPE=HOME:",";",ext_address,";",address,";",city,";",region,";",postalcode,";",country,"\n") : "",
"END:VCARD\n"
);
// Generates a VCalendar event which can be input into qr().
// summary: short event description
// description: event description
// location: location name
// start_datetime: start date time UTC string, can be generated using qr_vevent_datetime()
// end_datetime: end date time UTC string, can be generated using qr_vevent_datetime()
function qr_vevent(summary="", description="", location="", start_datetime, end_datetime) =
str(
"BEGIN:VCALENDAR\n",
"VERSION:2.0\n",
"PRODID:-//hacksw/handcal//NONSGML v1.0//EN\n",
"BEGIN:VEVENT\n",
summary ?
str("SUMMARY:", summary, "\n") : "",
description ?
str("DESCRIPTION:", description, "\n") : "",
location ?
str("LOCATION:", location, "\n") : "",
"DTSTAMP:", start_datetime, "\n",
"DTSTART:", start_datetime, "\n",
"DTEND:", end_datetime, "\n",
"END:VEVENT\n",
"END:VCALENDAR\n"
);
// Generates a UTC datetime string to be input into qr_vevent.
function qr_vevent_datetime(year, month, day, hour, minute, second) =
str(
_qr_padstr(str(year), "0", 4), _qr_padstr(str(month), "0", 2), _qr_padstr(str(day), "0", 2), "T",
_qr_padstr(str(hour), "0", 2), _qr_padstr(str(minute), "0", 2), _qr_padstr(str(second), "0", 2), "Z"
);
//@PRIVATE
//
// Misc helper functions
//
function _qr_padstr(s, ch, pad, acc="") =
len(acc) >= pad-len(s) ?
str(acc, s) :
_qr_padstr(s, ch, pad, str(acc, ch));
//
// QR code helper modules
//
module _qr_default_module() {
square([1, 1]);
}
module _qr_default_position_pattern() union() {
difference() {
square(7);
translate([1, 1])
square(5);
}
translate([2, 2])
square(3);
}
module _qr_default_alignment_pattern() union() {
difference() {
square(5);
translate([1, 1])
square(3);
}
translate([2, 2])
square(1);
}
module _qr_module_1(size, x, y) {
epsilon=0.0001; // ensures adjacent modules fuse together when rendering
translate([x-epsilon, size-1-y-epsilon, 0])
scale([1+2*epsilon, 1+2*epsilon, 1])
children(0);
}
// Applies linear_extrude(thickness) only if thickness > 0
module _qr_extrude_or_2d(thickness) {
if (thickness == 0) {
children(0);
} else {
linear_extrude(thickness)
children(0);
}
}
function _qr_data_bit_positions(size, index=0, pos=undef, acc=[]) =
let(nextpos=_qr_next_module_position(pos, size))
nextpos == undef ? acc :
let(app=concat([nextpos[0], nextpos[1]], index))
_qr_data_bit_positions(size, index+1, nextpos, concat([app], acc));
//
// QR code general functions
//
// Error correction levels
_qr_EC_L = 0; // low (7% recovery)
_qr_EC_M = 1; // medium (15% recovery)
_qr_EC_Q = 2; // quartile (25% recovery)
_qr_EC_H = 3; // high (30% recovery)
// Encodings supported by this library
_qr_ENC_SJIS = 0; // Shift Japanese International Standards (standard QR code encoding)
_qr_ENC_UTF8 = 1; // Unicode
function _qr_version2size(ver) = 17+4*ver;
function _qr_size2version(size) = (size-17)/4;
function _qr_do_get_version(msg_bytelen, ec_lvl, ver, encoding) =
ver > 40 ? undef :
_qr_get_max_msg_bytelen(ver, ec_lvl, encoding) >= msg_bytelen ?
ver :
_qr_do_get_version(msg_bytelen, ec_lvl, ver+1, encoding);
// Picks the right QR code size (called version) for
// the given message length and error correction level
function _qr_get_version(msg_bytelen, ec_lvl, encoding) =
_qr_do_get_version(msg_bytelen, ec_lvl, 1, encoding);
// Applies one of the 7 mask patterns via XOR
function _qr_apply_mask_pattern(val, x, y, pat) =
pat == 0 ?
((y + x) % 2 == 0 ? !val : val) :
pat == 1 ?
(y % 2 == 0 ? !val : val) :
pat == 2 ?
(x % 3 == 0 ? !val : val) :
pat == 3 ?
((y + x) % 3 == 0 ? !val : val) :
pat == 4 ?
((floor(y/2) + floor(x/3)) % 2 == 0 ? !val : val) :
pat == 5 ?
(y*x % 2 + y*x % 3 == 0 ? !val : val) :
pat == 6 ?
((y*x % 2 + y*x % 3) % 2 == 0 ? !val : val) :
pat == 7 ?
((y*x%3 + y+x) % 2 == 0 ? !val : val) :
undef;
//
// QR code message encoding
//
function _qr_get_max_msg_bytelen(ver, ec_lvl, encoding) =
let(maxbytes=_qr_ectab[ver-1][ec_lvl][0])
let(msg_len_bytes=ver <= 9 ? 1 : 2)
let(extra_bytes= // see _qr_data_codewords() for what these do
encoding == _qr_ENC_SJIS ? 1 :
encoding == _qr_ENC_UTF8 ? 2 :
undef)
maxbytes - msg_len_bytes - extra_bytes;
// Performs a gf2^8 finite field multiplication
function _qr_gf256_mul(a, b) =
a == 0 || b == 0 ? 0 :
_qr_gf256_exp[
(_qr_gf256_log[a] + _qr_gf256_log[b]) % 255
];
// Performs gf2^8 polynomial long division of data_cws by gp
function _qr_do_ec_codewords(n, data_cws, gp, res, i) =
i >= len(data_cws) ?
res :
let (lt = _qr_xor_byte(data_cws[i], res[0]))
let (res = [ for(i=[1:len(res)-1]) res[i] ])
let (res = concat(res, [0]))
let (res = [ for(i=[0:n-1])
_qr_xor_byte(res[i], _qr_gf256_mul(gp[i], lt))
])
_qr_do_ec_codewords(n, data_cws, gp, res, i+1);
// Generates n error correction codewords for data_cws
function _qr_ec_codewords(n, data_cws) =
_qr_do_ec_codewords(n, data_cws, _qr_generator_polynomials[n], [ for(i=[0:n]) 0 ], 0);
// Error correction patterns converted to decimal
_qr_ec_pats = [
1,
0,
3,
2
];
// Look up format info with error correction
function _qr_fmtinf_bits(ec_lvl, mask_pat) =
// equivalent to: ec_lvl << 3 | mask_pat
_qr_fmtinf_strs[_qr_ec_pats[ec_lvl] * _qr_pow2[3] + mask_pat];
// Look up version info bits
function _qr_verinf_bits(ver) =
_qr_verinf_strs[ver-1];
// Pads bytes with add additional bytes
// The padding bytes alternate between the
// values 236 and 17
function _qr_pad_bytes(bytes, add) =
[ for(i=[0:len(bytes)+add-1])
i < len(bytes) ?
bytes[i] :
(i-len(bytes)) % 2 == 0 ? 236 : 17
];
// Encode msg as data codewords, including the header
// and padding
// Returns a byte stream
function _qr_data_codewords(msg_bytes, ec_lvl, ver, encoding) =
let(max_msg_bytes=_qr_get_max_msg_bytelen(ver, ec_lvl, encoding))
let(msg_len_bits=_qr_bytes2bits(ver <= 9 ?
[ len(msg_bytes) ] :
[ floor(len(msg_bytes)/_qr_pow2[8]), len(msg_bytes) ]))
let(mode=
encoding == _qr_ENC_SJIS ? [0,1,0,0] :
encoding == _qr_ENC_UTF8 ? [0,1,1,1] :
undef)
let(eci_enc=
encoding == _qr_ENC_SJIS ? [] :
encoding == _qr_ENC_UTF8 ? _qr_bytes2bits([26]) :
undef)
let(eci_mode=
encoding == _qr_ENC_SJIS ? [] :
encoding == _qr_ENC_UTF8 ? [0,1,0,0] :
undef)
let(terminator=
encoding == _qr_ENC_SJIS ? [0,0,0,0] :
encoding == _qr_ENC_UTF8 ? (
// the terminator may be omitted if the
// message fits perfectly into the maximum
// number of bytes
len(msg_bytes) == max_msg_bytes ?
[] : [0,0,0,0,0,0,0,0]
) :
undef)
let(bits=concat(
mode,
eci_enc,
eci_mode,
msg_len_bits,
_qr_bytes2bits(msg_bytes),
terminator
))
let(pad_amt=max_msg_bytes
-len(msg_bytes)
-(len(terminator) == 8 ? 1 : 0))
_qr_pad_bytes(_qr_bits2bytes(bits), pad_amt);
// Splits the data codewords into the appropriate blocks
function _qr_data_blocks(data_cws, ec_lvl, ver) =
let(n_blocks_grp1=_qr_ectab[ver-1][ec_lvl][2])
let(n_blocks_grp2=_qr_ectab[ver-1][ec_lvl][4])
let(grp1_block_size=_qr_ectab[ver-1][ec_lvl][3])
let(grp2_block_size=_qr_ectab[ver-1][ec_lvl][5])
[ for(i=[0:n_blocks_grp1+n_blocks_grp2-1])
let(block_offset=i < n_blocks_grp1 ?
i*grp1_block_size :
n_blocks_grp1*grp1_block_size + (i-n_blocks_grp1)*grp2_block_size)
let(block_size=i < n_blocks_grp1 ? grp1_block_size : grp2_block_size)
[ for(j=[0:block_size-1])
data_cws[block_offset+j]
]];
function _qr_interleave_codewords(blocks) =
[ for(i=[0:max([ for(b=blocks) len(b) ])-1])
for(j=[0:len(blocks)-1])
if(i < len(blocks[j]))
blocks[j][i]
];
function _qr_ec_blocks(data_blocks, ec_lvl, ver) =
let(ec_n=_qr_ectab[ver-1][ec_lvl][1])
[ for(block=data_blocks)
_qr_ec_codewords(ec_n, block) ];
// Get final encoded data including error
// correction as bits
function _qr_encode_message(msg_bytes, ec_lvl, mask_pattern, ver, encoding) =
let(data_blocks=_qr_data_blocks(_qr_data_codewords(msg_bytes, ec_lvl, ver, encoding), ec_lvl, ver))
let(data_cws=_qr_interleave_codewords(data_blocks))
let(ec_blocks=_qr_ec_blocks(data_blocks, ec_lvl, ver))
let(ec_cws=_qr_interleave_codewords(ec_blocks))
concat(
_qr_bytes2bits(data_cws), // data codewords
_qr_bytes2bits(ec_cws) // error correction
);
//
// QR code module placement
//
// Gets the maximum alignment patterns per row /
// column, NOT the overall total
function _qr_n_alignment_patterns(ver) =
ver == 1 ? 0 :
floor(ver/7)+2;
// Distance between alignment patterns
// (excluding the first one which is
// always at x=6)
function _qr_alignment_pattern_step(ver) =
let(size=_qr_version2size(ver))
let(n=_qr_n_alignment_patterns(ver))
2*ceil((size-1-12)/(2*(n-1)));
// x can be either x or y; does not account
// for illegal positions
function _qr_coord_is_in_alignment_pattern(x, size) =
let(ver=_qr_size2version(size))
let(s=_qr_alignment_pattern_step(ver))
ver == 1 ? false :
(x >= 4 && x < 9) ||
(
(x > 6+2) &&
((s+size-1-6+2-x)%s) < 5
);
function _qr_region_is_in_bounds(x, y, size) =
x >= 0 && x < size &&
y >= 0 && y < size;
function _qr_region_is_data(x, y, size) =
_qr_region_is_in_bounds(x, y, size) &&
// position squares and format info
!(
(x < 9 && y < 9) ||
(x < 9 && y > size-9) ||
(y < 9 && x > size-9)
) &&
// version info
!(
size >= _qr_version2size(7) && (
(x < 6 && y > size-12) ||
(y < 6 && x > size-12)
)
) &&
// timing pattern
!(x == 6 || y == 6) &&
// alignment pattern
!(
size > _qr_version2size(1) &&
!(
// illegal position
// for alignment patterns
// (intersecting with
// position pattern)
(x == size-9 && y < 9) ||
(y == size-9 && x < 9)
) &&
(
_qr_coord_is_in_alignment_pattern(x, size) &&
_qr_coord_is_in_alignment_pattern(y, size)
)
);
// Finds the next free module starting
// from x, y while going in the y-direction
// ydir in a right-to-left zig-zag
function _qr_find_next_free_module(x, y, ydir, size, depth=0) =
_qr_region_is_data(x, y, size) ? [x, y] :
_qr_region_is_data(x-1, y, size) ? [x-1, y] :
_qr_find_next_free_module(x, y+ydir, ydir, size, depth+1);
function _qr_next_module_position(prev, size, depth=0) =
prev == undef ? [size-1, size-1] :
let(eff_x=
prev[0] < 6 ? prev[0] :
prev[0]-1)
let(ydir=
eff_x % 4 < 2 ? 1 : -1)
let(right=eff_x % 2 == 1)
let(x=
right ? prev[0]-1 : prev[0]+1)
let(y=
right ? prev[1] : prev[1] + ydir)
!_qr_region_is_in_bounds(x, y, size) ? (
x < 2 ? undef :
let(x=
x == 8 ? x-3 : x-2) // go 1 further left if module would collide with timing pattern
_qr_find_next_free_module(x, y, -ydir, size)
) :
!_qr_region_is_data(x, y, size) ? (
_qr_region_is_data(x-1, y, size) ? [x-1, y] :
_qr_next_module_position([x-1, y], size, depth+1)
) :
[x, y];
// END src/qr.scad
// BEGIN src/bits.scad
//
// Bit operation utils (not specific to QR)
//
_qr_pow2=[1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768];
_qr_char_nums = [[0,0],["\x01",1],["\x02",2],["\x03",3],["\x04",4],["\x05",5],["\x06",6],["\x07",7],["\x08",8],["\x09",9],["\x0a",10],["\x0b",11],["\x0c",12],["\x0d",13],["\x0e",14],["\x0f",15],["\x10",16],["\x11",17],["\x12",18],["\x13",19],["\x14",20],["\x15",21],["\x16",22],["\x17",23],["\x18",24],["\x19",25],["\x1a",26],["\x1b",27],["\x1c",28],["\x1d",29],["\x1e",30],["\x1f",31],["\x20",32],["\x21",33],["\x22",34],["\x23",35],["\x24",36],["\x25",37],["\x26",38],["\x27",39],["\x28",40],["\x29",41],["\x2a",42],["\x2b",43],["\x2c",44],["\x2d",45],["\x2e",46],["\x2f",47],["\x30",48],["\x31",49],["\x32",50],["\x33",51],["\x34",52],["\x35",53],["\x36",54],["\x37",55],["\x38",56],["\x39",57],["\x3a",58],["\x3b",59],["\x3c",60],["\x3d",61],["\x3e",62],["\x3f",63],["\x40",64],["\x41",65],["\x42",66],["\x43",67],["\x44",68],["\x45",69],["\x46",70],["\x47",71],["\x48",72],["\x49",73],["\x4a",74],["\x4b",75],["\x4c",76],["\x4d",77],["\x4e",78],["\x4f",79],["\x50",80],["\x51",81],["\x52",82],["\x53",83],["\x54",84],["\x55",85],["\x56",86],["\x57",87],["\x58",88],["\x59",89],["\x5a",90],["\x5b",91],["\x5c",92],["\x5d",93],["\x5e",94],["\x5f",95],["\x60",96],["\x61",97],["\x62",98],["\x63",99],["\x64",100],["\x65",101],["\x66",102],["\x67",103],["\x68",104],["\x69",105],["\x6a",106],["\x6b",107],["\x6c",108],["\x6d",109],["\x6e",110],["\x6f",111],["\x70",112],["\x71",113],["\x72",114],["\x73",115],["\x74",116],["\x75",117],["\x76",118],["\x77",119],["\x78",120],["\x79",121],["\x7a",122],["\x7b",123],["\x7c",124],["\x7d",125],["\x7e",126],["\x7f",127],["\u0080",128],["\u0081",129],["\u0082",130],["\u0083",131],["\u0084",132],["\u0085",133],["\u0086",134],["\u0087",135],["\u0088",136],["\u0089",137],["\u008a",138],["\u008b",139],["\u008c",140],["\u008d",141],["\u008e",142],["\u008f",143],["\u0090",144],["\u0091",145],["\u0092",146],["\u0093",147],["\u0094",148],["\u0095",149],["\u0096",150],["\u0097",151],["\u0098",152],["\u0099",153],["\u009a",154],["\u009b",155],["\u009c",156],["\u009d",157],["\u009e",158],["\u009f",159],["\u00a0",160],["\u00a1",161],["\u00a2",162],["\u00a3",163],["\u00a4",164],["\u00a5",165],["\u00a6",166],["\u00a7",167],["\u00a8",168],["\u00a9",169],["\u00aa",170],["\u00ab",171],["\u00ac",172],["\u00ad",173],["\u00ae",174],["\u00af",175],["\u00b0",176],["\u00b1",177],["\u00b2",178],["\u00b3",179],["\u00b4",180],["\u00b5",181],["\u00b6",182],["\u00b7",183],["\u00b8",184],["\u00b9",185],["\u00ba",186],["\u00bb",187],["\u00bc",188],["\u00bd",189],["\u00be",190],["\u00bf",191],["\u00c0",192],["\u00c1",193],["\u00c2",194],["\u00c3",195],["\u00c4",196],["\u00c5",197],["\u00c6",198],["\u00c7",199],["\u00c8",200],["\u00c9",201],["\u00ca",202],["\u00cb",203],["\u00cc",204],["\u00cd",205],["\u00ce",206],["\u00cf",207],["\u00d0",208],["\u00d1",209],["\u00d2",210],["\u00d3",211],["\u00d4",212],["\u00d5",213],["\u00d6",214],["\u00d7",215],["\u00d8",216],["\u00d9",217],["\u00da",218],["\u00db",219],["\u00dc",220],["\u00dd",221],["\u00de",222],["\u00df",223],["\u00e0",224],["\u00e1",225],["\u00e2",226],["\u00e3",227],["\u00e4",228],["\u00e5",229],["\u00e6",230],["\u00e7",231],["\u00e8",232],["\u00e9",233],["\u00ea",234],["\u00eb",235],["\u00ec",236],["\u00ed",237],["\u00ee",238],["\u00ef",239],["\u00f0",240],["\u00f1",241],["\u00f2",242],["\u00f3",243],["\u00f4",244],["\u00f5",245],["\u00f6",246],["\u00f7",247],["\u00f8",248],["\u00f9",249],["\u00fa",250],["\u00fb",251],["\u00fc",252],["\u00fd",253],["\u00fe",254],["\u00ff",255]];
function _qr_xor(a, b) = (a || b) && !(a && b);
function _qr_xor_byte(a, b) =
let(ba=_qr_bytes2bits([a]), bb=_qr_bytes2bits([b]))
_qr_bits2byte([ for (i=[0:8-1]) _qr_xor(ba[i], bb[i]) ? 1 : 0 ]);
function _qr_is_bit_set(val, idx) =
floor(val / _qr_pow2[7-idx%8]) % 2 == 1;
function _qr_bits2byte(bits) =
bits[0]*_qr_pow2[7] +
bits[1]*_qr_pow2[6] +
bits[2]*_qr_pow2[5] +
bits[3]*_qr_pow2[4] +
bits[4]*_qr_pow2[3] +
bits[5]*_qr_pow2[2] +
bits[6]*_qr_pow2[1] +
bits[7]*_qr_pow2[0];
// Truncating right bitshift
function _qr_rsh(x, n) =
floor(x/pow(2,n));
function _qr_bittrunc(x, nbits) =
x%pow(2,nbits);
function _qr_do_str2bytes(cps, idx=0, acc=[]) =
idx >= len(cps) ? acc :
cps[idx] <= 127 ?
_qr_do_str2bytes(cps, idx+1, concat(acc, cps[idx])) :
cps[idx] <= 2047 ?
_qr_do_str2bytes(cps, idx+1, concat(
acc,
128+64+_qr_rsh(cps[idx],6),
128+_qr_bittrunc(cps[idx],6)
)) :
cps[idx] <= 65535 ?
_qr_do_str2bytes(cps, idx+1, concat(
acc,
128+64+32+_qr_rsh(cps[idx],12),
128+_qr_bittrunc(_qr_rsh(cps[idx],6),6),
128+_qr_bittrunc(cps[idx],6)
)) :
cps[idx] <= 1114111 ?
_qr_do_str2bytes(cps, idx+1, concat(
acc,
128+64+32+16+_qr_rsh(cps[idx],18),
128+_qr_bittrunc(_qr_rsh(cps[idx],12),6),
128+_qr_bittrunc(_qr_rsh(cps[idx],6),6),
128+_qr_bittrunc(cps[idx],6)
)) :
undef;
// UTF-8 encodes the result of str2codepts
function _qr_str2bytes(s) =
_qr_do_str2bytes(_qr_str2codepts(s));
function _qr_do_str_num_bytes(cps, idx=0, acc=0) =
idx >= len(cps) ? acc :
cps[idx] <= 127 ?
_qr_do_str_num_bytes(cps, idx+1, acc+1) :
cps[idx] <= 2047 ?
_qr_do_str_num_bytes(cps, idx+1, acc+2) :
cps[idx] <= 65535 ?
_qr_do_str_num_bytes(cps, idx+1, acc+3) :
cps[idx] <= 1114111 ?
_qr_do_str_num_bytes(cps, idx+1, acc+3) :
undef;
// Length of string in UTF-8 encoding
function _qr_str_num_bytes(s) =
_qr_do_str_num_bytes(_qr_str2codepts(s));
// ord got added in ver 2019.05 (missing in Thingiverse Customizer)
function _qr_str2codepts(s) =
version_num() >= 20190500 ?
[ for(i=s) ord(i) ] :
[ for(i=search(s, _qr_char_nums, num_returns_per_match=0))
i[0] ];
function _qr_bytes2bits(bytes) = [ for(i=[0:len(bytes)*8-1]) _qr_is_bit_set(bytes[floor(i/8)], i) ? 1 : 0 ];
// Pads not fully filled bytes with 0s
function _qr_bits2bytes(bits) = [ for(i=[0:ceil(len(bits)/8)-1]) _qr_bits2byte([
for(j=[0:8-1])
let(bitidx=8*i + j)
bitidx < len(bits) ? bits[bitidx] : 0
]) ];
function _qr_do_strjoin(strs, delim, idx=0, acc="") =
idx >= len(strs) ? acc :
_qr_do_strjoin(strs, delim, idx+1, str(acc, acc == "" ? "" : delim, strs[idx]));
function _qr_strjoin(strs, delim="") =
_qr_do_strjoin(strs, delim);
// END src/bits.scad
// BEGIN src/data.scad
// Galois field 256 exponentiation table
_qr_gf256_exp = [
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,1
];
// Galois field 256 log table
_qr_gf256_log = [
undef,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
];
// Form is gp[0]*x^0...gp[n]*x^n (gp[i] is this constant at index i)
_qr_generator_polynomials = [
[],
[],
[],
[],
[],
[],
[],
[127, 122, 154, 164, 11, 68, 117],
[255, 11, 81, 54, 239, 173, 200, 24],
[226, 207, 158, 245, 235, 164, 232, 197, 37],
[216, 194, 159, 111, 199, 94, 95, 113, 157, 193],
[172, 130, 163, 50, 123, 219, 162, 248, 144, 116, 160],
[68, 119, 67, 118, 220, 31, 7, 84, 92, 127, 213, 97],
[137, 73, 227, 17, 177, 17, 52, 13, 46, 43, 83, 132, 120],
[14, 54, 114, 70, 174, 151, 43, 158, 195, 127, 166, 210, 234, 163],
[29, 196, 111, 163, 112, 74, 10, 105, 105, 139, 132, 151, 32, 134, 26],
[59, 13, 104, 189, 68, 209, 30, 8, 163, 65, 41, 229, 98, 50, 36, 59],
[119, 66, 83, 120, 119, 22, 197, 83, 249, 41, 143, 134, 85, 53, 125, 99, 79],
[239, 251, 183, 113, 149, 175, 199, 215, 240, 220, 73, 82, 173, 75, 32, 67, 217, 146],
[194, 8, 26, 146, 20, 223, 187, 152, 85, 115, 238, 133, 146, 109, 173, 138, 33, 172, 179],
[152, 185, 240, 5, 111, 99, 6, 220, 112, 150, 69, 36, 187, 22, 228, 198, 121, 121, 165, 174],
[44, 243, 13, 131, 49, 132, 194, 67, 214, 28, 89, 124, 82, 158, 244, 37, 236, 142, 82, 255, 89],
[89, 179, 131, 176, 182, 244, 19, 189, 69, 40, 28, 137, 29, 123, 67, 253, 86, 218, 230, 26, 145, 245],
[179, 68, 154, 163, 140, 136, 190, 152, 25, 85, 19, 3, 196, 27, 113, 198, 18, 130, 2, 120, 93, 41, 71],
[122, 118, 169, 70, 178, 237, 216, 102, 115, 150, 229, 73, 130, 72, 61, 43, 206, 1, 237, 247, 127, 217, 144, 117],
[245, 49, 228, 53, 215, 6, 205, 210, 38, 82, 56, 80, 97, 139, 81, 134, 126, 168, 98, 226, 125, 23, 171, 173, 193],
[246, 51, 183, 4, 136, 98, 199, 152, 77, 56, 206, 24, 145, 40, 209, 117, 233, 42, 135, 68, 70, 144, 146, 77, 43, 94],
[240, 61, 29, 145, 144, 117, 150, 48, 58, 139, 94, 134, 193, 105, 33, 169, 202, 102, 123, 113, 195, 25, 213, 6, 152, 164, 217],
[252, 9, 28, 13, 18, 251, 208, 150, 103, 174, 100, 41, 167, 12, 247, 56, 117, 119, 233, 127, 181, 100, 121, 147, 176, 74, 58, 197],
[228, 193, 196, 48, 170, 86, 80, 217, 54, 143, 79, 32, 88, 255, 87, 24, 15, 251, 85, 82, 201, 58, 112, 191, 153, 108, 132, 143, 170],
[212, 246, 77, 73, 195, 192, 75, 98, 5, 70, 103, 177, 22, 217, 138, 51, 181, 246, 72, 25, 18, 46, 228, 74, 216, 195, 11, 106, 130, 150]
];
_qr_fmtinf_strs = [
[1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
[1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1],
[1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0],
[1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1],
[1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0],
[1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1],
[1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0],
[1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1],
[1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0],
[1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1],
[1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1],
[1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0],
[1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0],
[0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
[0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0],
[0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1],
[0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1],
[0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1],
[0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1],
[0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1],
[0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0],
[0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1],
[0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1]
];
_qr_verinf_strs = [
[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1],
[0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1],
[0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1],
[0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0],
[0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0],
[0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1],
[0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1],
[0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1],
[0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1],
[0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0],
[0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1],
[0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0],
[0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0],
[0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1],
[0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1],
[0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0],
[0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0],
[0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1],
[0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0],
[1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1],
[1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1],
[1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0],
[1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0],
[1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1]
];
// total data codewords / ec codewords per block / num blocks in group 1 / num data codewords in each of grp 1's blocks / num blocks in group 2 / num data codewords in each of grp 2's blocks
_qr_ectab = [
[
[19, 7, 1, 19, 0, 0],
[16, 10, 1, 16, 0, 0],
[13, 13, 1, 13, 0, 0],
[9, 17, 1, 9, 0, 0]
],
[
[34, 10, 1, 34, 0, 0],
[28, 16, 1, 28, 0, 0],
[22, 22, 1, 22, 0, 0],
[16, 28, 1, 16, 0, 0]
],
[
[55, 15, 1, 55, 0, 0],
[44, 26, 1, 44, 0, 0],
[34, 18, 2, 17, 0, 0],
[26, 22, 2, 13, 0, 0]
],
[
[80, 20, 1, 80, 0, 0],
[64, 18, 2, 32, 0, 0],
[48, 26, 2, 24, 0, 0],
[36, 16, 4, 9, 0, 0]
],
[
[108, 26, 1, 108, 0, 0],
[86, 24, 2, 43, 0, 0],
[62, 18, 2, 15, 2, 16],
[46, 22, 2, 11, 2, 12]
],
[
[136, 18, 2, 68, 0, 0],
[108, 16, 4, 27, 0, 0],
[76, 24, 4, 19, 0, 0],
[60, 28, 4, 15, 0, 0]
],
[
[156, 20, 2, 78, 0, 0],
[124, 18, 4, 31, 0, 0],
[88, 18, 2, 14, 4, 15],
[66, 26, 4, 13, 1, 14]
],
[
[194, 24, 2, 97, 0, 0],
[154, 22, 2, 38, 2, 39],
[110, 22, 4, 18, 2, 19],
[86, 26, 4, 14, 2, 15]
],
[
[232, 30, 2, 116, 0, 0],
[182, 22, 3, 36, 2, 37],
[132, 20, 4, 16, 4, 17],
[100, 24, 4, 12, 4, 13]
],
[
[274, 18, 2, 68, 2, 69],
[216, 26, 4, 43, 1, 44],
[154, 24, 6, 19, 2, 20],
[122, 28, 6, 15, 2, 16]
],
[
[324, 20, 4, 81, 0, 0],
[254, 30, 1, 50, 4, 51],
[180, 28, 4, 22, 4, 23],
[140, 24, 3, 12, 8, 13]
],
[
[370, 24, 2, 92, 2, 93],
[290, 22, 6, 36, 2, 37],
[206, 26, 4, 20, 6, 21],
[158, 28, 7, 14, 4, 15]
],
[
[428, 26, 4, 107, 0, 0],
[334, 22, 8, 37, 1, 38],
[244, 24, 8, 20, 4, 21],
[180, 22, 12, 11, 4, 12]
],
[
[461, 30, 3, 115, 1, 116],
[365, 24, 4, 40, 5, 41],
[261, 20, 11, 16, 5, 17],
[197, 24, 11, 12, 5, 13]
],
[
[523, 22, 5, 87, 1, 88],
[415, 24, 5, 41, 5, 42],
[295, 30, 5, 24, 7, 25],
[223, 24, 11, 12, 7, 13]
],
[
[589, 24, 5, 98, 1, 99],
[453, 28, 7, 45, 3, 46],
[325, 24, 15, 19, 2, 20],
[253, 30, 3, 15, 13, 16]
],
[
[647, 28, 1, 107, 5, 108],
[507, 28, 10, 46, 1, 47],
[367, 28, 1, 22, 15, 23],
[283, 28, 2, 14, 17, 15]
],
[
[721, 30, 5, 120, 1, 121],
[563, 26, 9, 43, 4, 44],
[397, 28, 17, 22, 1, 23],
[313, 28, 2, 14, 19, 15]
],
[
[795, 28, 3, 113, 4, 114],
[627, 26, 3, 44, 11, 45],
[445, 26, 17, 21, 4, 22],
[341, 26, 9, 13, 16, 14]
],
[
[861, 28, 3, 107, 5, 108],
[669, 26, 3, 41, 13, 42],
[485, 30, 15, 24, 5, 25],
[385, 28, 15, 15, 10, 16]
],
[
[932, 28, 4, 116, 4, 117],
[714, 26, 17, 42, 0, 0],
[512, 28, 17, 22, 6, 23],
[406, 30, 19, 16, 6, 17]
],
[
[1006, 28, 2, 111, 7, 112],
[782, 28, 17, 46, 0, 0],
[568, 30, 7, 24, 16, 25],
[442, 24, 34, 13, 0, 0]
],
[
[1094, 30, 4, 121, 5, 122],
[860, 28, 4, 47, 14, 48],
[614, 30, 11, 24, 14, 25],
[464, 30, 16, 15, 14, 16]
],
[
[1174, 30, 6, 117, 4, 118],
[914, 28, 6, 45, 14, 46],
[664, 30, 11, 24, 16, 25],
[514, 30, 30, 16, 2, 17]
],
[
[1276, 26, 8, 106, 4, 107],
[1000, 28, 8, 47, 13, 48],
[718, 30, 7, 24, 22, 25],
[538, 30, 22, 15, 13, 16]
],
[
[1370, 28, 10, 114, 2, 115],
[1062, 28, 19, 46, 4, 47],
[754, 28, 28, 22, 6, 23],
[596, 30, 33, 16, 4, 17]
],
[
[1468, 30, 8, 122, 4, 123],
[1128, 28, 22, 45, 3, 46],
[808, 30, 8, 23, 26, 24],
[628, 30, 12, 15, 28, 16]
],
[
[1531, 30, 3, 117, 10, 118],
[1193, 28, 3, 45, 23, 46],
[871, 30, 4, 24, 31, 25],
[661, 30, 11, 15, 31, 16]
],
[
[1631, 30, 7, 116, 7, 117],
[1267, 28, 21, 45, 7, 46],
[911, 30, 1, 23, 37, 24],
[701, 30, 19, 15, 26, 16]
],
[
[1735, 30, 5, 115, 10, 116],
[1373, 28, 19, 47, 10, 48],
[985, 30, 15, 24, 25, 25],
[745, 30, 23, 15, 25, 16]
],
[
[1843, 30, 13, 115, 3, 116],
[1455, 28, 2, 46, 29, 47],
[1033, 30, 42, 24, 1, 25],
[793, 30, 23, 15, 28, 16]
],
[
[1955, 30, 17, 115, 0, 0],
[1541, 28, 10, 46, 23, 47],
[1115, 30, 10, 24, 35, 25],
[845, 30, 19, 15, 35, 16]
],
[
[2071, 30, 17, 115, 1, 116],
[1631, 28, 14, 46, 21, 47],
[1171, 30, 29, 24, 19, 25],
[901, 30, 11, 15, 46, 16]
],
[
[2191, 30, 13, 115, 6, 116],
[1725, 28, 14, 46, 23, 47],
[1231, 30, 44, 24, 7, 25],
[961, 30, 59, 16, 1, 17]
],
[
[2306, 30, 12, 121, 7, 122],
[1812, 28, 12, 47, 26, 48],
[1286, 30, 39, 24, 14, 25],
[986, 30, 22, 15, 41, 16]
],
[
[2434, 30, 6, 121, 14, 122],
[1914, 28, 6, 47, 34, 48],
[1354, 30, 46, 24, 10, 25],
[1054, 30, 2, 15, 64, 16]
],
[
[2566, 30, 17, 122, 4, 123],
[1992, 28, 29, 46, 14, 47],
[1426, 30, 49, 24, 10, 25],
[1096, 30, 24, 15, 46, 16]
],
[
[2702, 30, 4, 122, 18, 123],
[2102, 28, 13, 46, 32, 47],
[1502, 30, 48, 24, 14, 25],
[1142, 30, 42, 15, 32, 16]
],
[
[2812, 30, 20, 117, 4, 118],
[2216, 28, 40, 47, 7, 48],
[1582, 30, 43, 24, 22, 25],
[1222, 30, 10, 15, 67, 16]
],
[
[2956, 30, 19, 118, 6, 119],
[2334, 28, 18, 47, 31, 48],
[1666, 30, 34, 24, 34, 25],
[1276, 30, 20, 15, 61, 16]
]
];
// END src/data.scad
/* private functions */
function strtoint (s, ret=0, i=0) =
i >= len(s)
? ret
: strtoint(s, ret*10 + ord(s[i]) - ord("0"), i+1);
/* modules */
module background_qrcode_obj(x, y, z, size, thickness, magnet_shape, magnet_d1, magnet_d2, magnet_d3) {
color(Color_Background)
translate([x,y,z]) {
difference() {
cube([size,size,thickness]);
if (magnet_shape == "C") {
translate([size/2,size/2,z]) {
cylinder(h=magnet_d2, d=magnet_d1+Magnet_Clearance);
}
} else if (magnet_shape == "R") {
translate([size/2-(magnet_d1+Magnet_Clearance)/2,size/2-(magnet_d2+Magnet_Clearance)/2,z]) {
cube([magnet_d1+Magnet_Clearance,magnet_d2+Magnet_Clearance,magnet_d3]);
}
}
}
}
}
module qrcode_obj(message, x, y, z, size, thickness, rounded) {
color(Color_QR_Code)
translate([x,y,z]) {
qr(
message,
width=size,
height=size,
thickness=thickness,
rounded=rounded
);
}
}
module background_label_obj(x, y, background_thickness, border_size, width, text_size) {
color(Color_Background)
translate([x,y,0]) {
cube([width,text_size+2*border_size,background_thickness]);
}
}
module label_obj(x, y, background_thickness, border_size, text_thickness, text_size, text_string) {
color(Color_Labels)
translate([x,y+border_size,background_thickness]) {
linear_extrude(text_thickness)
text(
text_string,
font = Font,
size = text_size,
halign = "center"
);
}
}
module hole_obj(x, y, z, size, thickness) {
Hole_Diameter_Ext = size*2;
color(Color_Background)
translate([x,y,z]) {
difference() {
union() {
cube([Hole_Diameter_Ext,Hole_Diameter_Ext/2,thickness]);
translate([Hole_Diameter_Ext/2,Hole_Diameter_Ext/2,0]) {
cylinder(h=Background_Thickness+((Magnet_Shape == "C")?Magnet_D2:(Magnet_Shape == "R")?Magnet_D3:0), d= Hole_Diameter_Ext);
}
}
translate([Hole_Diameter_Ext/2,Hole_Diameter_Ext/2,0]) {
cylinder(h=thickness, d= size);
}
}
}
}
/* magnets */
Magnet_Shape = Magnet_Type[0];
Magnet_D1 = (Magnet_Shape != "N")?(strtoint(Magnet_Type[2])*10+strtoint(Magnet_Type[3])):0;
Magnet_D2 = (Magnet_Shape != "N")?(strtoint(Magnet_Type[5])*10+strtoint(Magnet_Type[6])):0;
Magnet_D3 = (Magnet_Shape == "R")?(strtoint(Magnet_Type[8])*10+strtoint(Magnet_Type[9])):0;
/* background QR Code */
if (Parts == "A" || Parts == "B") {
background_qrcode_obj(
x = -Overall_Size/2,
y = -Overall_Size/2,
z = 0,
size = Overall_Size,
thickness = Background_Thickness+((Magnet_Shape == "C")?Magnet_D2:(Magnet_Shape == "R")?Magnet_D3:0),
magnet_shape = Magnet_Shape,
magnet_d1 = Magnet_D1,
magnet_d2 = Magnet_D2,
magnet_d3 = Magnet_D3
);
}
/* QR Code */
if (Parts == "A" || Parts == "Q") {
message = (Type == "W") ? qr_wifi(ssid=SSID,
psk=Password,
auth=Auth,
hidden=Is_Hidden)
: (Type == "P")? qr_phone_call(Number)
: (Type == "V") ? qr_vcard(
lastname=Surname,
firstname=Firstname,
address=Address,
city=City,
region=Region,
postalcode=Postalcode,
country=Country,
email=Email,
url=Site_URL,
phone=Phone_Number)
: Text;
qrcode_obj(
message = message,
x = Border_Size-Overall_Size/2,
y = Border_Size-Overall_Size/2,
z = Background_Thickness+((Magnet_Shape == "C")?Magnet_D2:(Magnet_Shape == "R")?Magnet_D3:0),
size = Overall_Size-2*Border_Size,
thickness = QR_Code_Thickness,
rounded = Round_Blocks
);
}
/* Hole */
if (Parts == "A" || Parts == "B") {
hole_y = ((Label_Position == "T" || Label_Position == "TB") && Label_Size !=0)?Label_Size+2*Border_Size+Overall_Size/2:Overall_Size/2;
hole_obj(
x = -Hole_Size,
y = hole_y,
z = 0,
size = Hole_Size,
thickness = Background_Thickness+((Magnet_Shape == "C")?Magnet_D2:(Magnet_Shape == "R")?Magnet_D3:0)
);
}
/* label */
if (Label_Size != 0) {
label_y_top = Overall_Size/2;
label_y_bottom = -Overall_Size/2-Label_Size-2*Border_Size;
label_y = (Label_Position == "T" || Label_Position == "TB")? label_y_top:label_y_bottom;
if (Parts == "A" || Parts == "B") {
background_label_obj(
x = -Overall_Size/2,
y = label_y,
background_thickness = Background_Thickness+((Magnet_Shape == "C")?Magnet_D2:(Magnet_Shape == "R")?Magnet_D3:0),
border_size = Border_Size,
width = Overall_Size,
text_size = Label_Size
);
}
if (Parts == "A" || Parts == "L") {
label_obj(
x = 0,
y = label_y,
background_thickness = Background_Thickness+((Magnet_Shape == "C")?Magnet_D2:(Magnet_Shape == "R")?Magnet_D3:0),
border_size = Border_Size,
text_thickness = Label_Thickness,
text_size = Label_Size,
text_string = Label_Primary_String
);
}
if(Label_Position == "TB") {
if (Parts == "A" || Parts == "B") {
background_label_obj(
x = -Overall_Size/2,
y = label_y_bottom,
background_thickness = Background_Thickness+((Magnet_Shape == "C")?Magnet_D2:(Magnet_Shape == "R")?Magnet_D3:0),
border_size = Border_Size,
width = Overall_Size,
text_size = Label_Size
);
}
if (Parts == "A" || Parts == "L") {
label_obj(
x = 0,
y = label_y_bottom,
background_thickness = Background_Thickness+((Magnet_Shape == "C")?Magnet_D2:(Magnet_Shape == "R")?Magnet_D3:0),
border_size = Border_Size,
text_thickness = Label_Thickness,
text_size = Label_Size,
text_string = Label_Secondary_String
);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment