OpenSCAD でドット絵を描く #
MakerChip 作る時のQR コード、SVG 読み込むんじゃなくて直接書けばいいじゃん、と思ったので、まずはドットを打つ部分を。
URL 文字列→QR コード→配列生成がキモなんで、そこを用意するまでは手打ちドッターとして遊ぶ用。
追記: Gemini に「qrencode で QRコードを ASCII 出力して、それを整形するシェルスクリプト」を所望したらすぐ作ってくれたよ。動くので細かいチェックはしてない

OpenSCAD #
$fs = 0.1;
module make_dots_(pos=[]) {
x = 1; // ドットの幅
t = 1; // 厚さ
rows = len(pos); // 行数
cols = len(pos[0]); // 列数
translate([-x*cols/2,x*rows/2-1,0]) { // センターに揃える
for ( r = [ 0 : len(pos) -1 ] ) { // 行ループ
for ( c = [ 0 : len(pos[r]) -1 ] ) { // 列ループ
if ( pos[r][c] != 0 ) { // 0 じゃない時ドット
translate([x*c, -x*r, 0])
cube([x,x,t]);
}
}
}
}
}
pos = [
[0,0,0,1,1,0,0,0],
[0,0,1,1,1,1,0,0],
[0,1,1,1,1,1,1,0],
[1,1,0,1,1,0,1,1],
[1,1,1,1,1,1,1,1],
[0,0,1,0,0,1,0,0],
[0,1,0,1,1,0,1,0],
[1,0,1,0,0,1,0,1],
];
make_dots_(pos);
qrencode #
#!/bin/sh
# set -e: コマンドがエラーで終了したら、スクリプトを即座に終了する
# set -u: 未定義の変数を使用しようとしたらエラーで終了する
set -eu
# --- 依存コマンドのチェック ---
# スクリプトが必要とする 'qrencode' と 'awk' が存在するかを確認する
if ! command -v qrencode >/dev/null 2>&1; then
echo "エラー: 'qrencode' コマンドが見つかりません。" >&2
echo "先にインストールしてください (例: 'sudo apt-get install qrencode' or 'brew install qrencode')。" >&2
exit 1
fi
if ! command -v awk >/dev/null 2>&1; then
echo "エラー: 'awk' コマンドが見つかりません。" >&2
echo "awkは多くのUnix系システムで標準コマンドです。環境を確認してください。" >&2
exit 1
fi
# --- 引数のチェック ---
# スクリプトに渡された引数が1つでない場合は、使い方を表示して終了する
if [ "$#" -ne 1 ]; then
# basename "$0" はスクリプトファイル名を取得する
echo "使い方: $(basename "$0") \"QRコードにしたい文字列\"" >&2
echo "例: $(basename "$0") \"Hello, OpenSCAD!\" > qr_data.scad" >&2
exit 1
fi
# --- メイン処理 ---
# 最初の引数を変数に格納する
TEXT_TO_ENCODE="$1"
# qrencodeでマージンなし("-m 0")のASCIIアートを生成し、パイプでawkに渡して整形する
# qrencode の "--" は、引数がハイフンで始まる場合でもそれをオプションと誤認させないための記述
qrencode -t ASCII -m 0 -o - -- "$TEXT_TO_ENCODE" | awk '
BEGIN {
# 配列の開始部分を出力
print "qr_matrix = [";
# 最初の行かどうかを判定するためのフラグ
first_line = 1;
}
{
# 2行目以降は、前の行の終わりにカンマと改行を出力
if (!first_line) {
printf ",\n";
}
first_line = 0;
# 各行の処理: 行の開始ブラケットを出力
printf " [";
# 1文字ずつループして処理
for (i = 1; i <= length($0); i++) {
char = substr($0, i, 1);
# "#" なら 1、スペースなら 0 に変換
val = (char == "#") ? 1 : 0;
# 数値と、行末でなければカンマを出力
printf "%d%s", val, (i == length($0) ? "" : ",");
}
# 行の終了ブラケットを出力
printf "]";
}
END {
# 配列の終了部分を出力
print "\n];";
}
'
実行結果 #
$ qrcode-txt.sh "https://d3.haro.jp/"
qr_matrix = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1],
[1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1],
[1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1],
[1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1],
[1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0],
[0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0],
[0,0,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,1,1],
[0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,1,1],
[1,1,1,1,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1],
[1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0],
[1,1,0,0,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1,1,1],
[1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1],
[1,1,0,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1],
[1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,0,0,1,1,0,0],
[1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,1,1],
[1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1],
[1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1],
[1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];