# -*- coding: utf-8 -*- # 動画「メーカーのロボットは、どこまで変えられるか」— 考察回「壁打ちの型」の3本目(もとは LOG406)の場面 # 書き写した元: 現場で扱えるか/scenes_kabe.py(型の決まりは video_rules.md 10節) # 紙の文字は真ん中にそろえる/線を使わず面で分ける(事実=墨・エイイチの見立て=濃い灰・ハコブくん=薄い灰) # 朱は判子とリアクション(却下・保留・ボツ・即答・エイイチの推し)だけ/字幕は動かさない # 脱線(画面いっぱいの絵)は4回: 自分で改造(マンガ)・ロボ検(マンガ)・扉、全開(写真風)・課金で解放(写真風)。どれも Gemini で作った絵 # 天丼: ハコブくんが「改造申請書」を出すたびに、判子が落ちる。冒頭「却下」・やり取り2「却下」・最後だけ「保留」 import os from kabe_kit import * import scene_kit as _sk _tl = Timeline() tl = _tl N = len(_tl.idx) A = lambda i, off=0.0: tl.start(i) + off # 動画の頭からの秒 S = tl.speech # その文を読み終わるまでの長さ LOGO = _sk.LOGO BRAND = f'' SANS = "font-family:'Noto Sans CJK JP',sans-serif;" BG = "src/bg/" GEM = "絵は生成AI(Gemini)で作成" LAW = "法律の話は、調べた範囲の理解です。専門家の判断ではありません" BLOCKS = [] def block(name, sents, fn, video=None): b0 = _tl.start(sents[0]) T = lambda i, off=0.0: tl.start(i) - b0 + off E = lambda i: tl.end(i) - b0 b = dict(name=name, sents=list(sents)) if video: b["video"] = video else: b["html"] = page(fn(T, E) + BRAND) BLOCKS.append(b) def inkl(text, t, step=0.05): """「/」で行を分けて書く(1〜2文字だけ次の行に落ちないように)""" out, tt = [], t for k, part in enumerate(text.split("/")): out.append(ink(part, tt, step)); tt += len(part) * step return "
".join(out) def line(text, t, size=30, weight=700, step=0.05, style=""): return f'
{ink(text, t, step)}
' def qline(text, t, size=46, step=0.06, style=""): return f'
{ink(text, t, step)}
' def src(text, t=0.0): return f'
{text}
' def bigline(text, t, size=110, style=""): return f'
{land(text, t)}
' def hanko(text, t, style, size=72, t_hide=None): """朱の判子(却下・保留)。四角い枠で、斜めに落ちる。t_hide で消す""" an = f"animation:slam .35s cubic-bezier(.5,0,.75,0) {t:.3f}s forwards" + (f", hide .2s linear {t_hide:.3f}s forwards" if t_hide else "") + ";" return (f'
{text}
') FORM_CSS = """""" def form(no, items, t_in, t_items): """改造申請書(天丼)。items は文字のリスト、t_items はそれぞれが出る秒""" its = "".join(f'
✓{txt}
' for txt, t in zip(items, t_items)) return (FORM_CSS + f'
' f'

改造申請書

No.{no} 申請者:ハコブくん
{its}
') # ================= 0 つかみ(改造申請書1枚目 →「却下」) ================= def hook(T, E): return (paper(tag("考察ノート") + '
' + form(1, ["速さの上限を外す", "荷物を倍積む", "夜も走る"], T(0, 0.3), [T(1, 0.2), T(1, 0.6), T(1, 1.0)]) + card("mi", inkl("それ、事故が起きたら、誰の責任?", T(2, 0.3), 0.06), T(2, 0.1), "エイイチ", w=700, anim="wipe") + '
', 0.0) + hanko("却下", T(3, 0.25), "left:1110px;top:150px", size=80) + shake_js([(T(3, 0.6), 0.3)])) block("hook", [0, 1, 2, 3], hook) def hook_law(T, E): return (paper(tag("改造した人は、どうなるか") + '
' + card("fact", inkl("メーカーが想定していない改変で、危険を増やした人は、/メーカーと同じ義務を負う", T(4, 0.6), 0.04), T(4, 0.3), "EU", w=900) + card("fact", inkl("仕事として加工して人に渡した業者は、/作った側と同じ責任を負う側に入る", T(4, 2.8), 0.04), T(4, 2.5), "日本", w=900) + '
' + card("hb", "車なら、買えば好きに改造できる", T(6, 0.3), "ハコブくん", w=470, fade=T(7, 1.0), style="font-size:30px") + card("mi", inkl("車も、保安基準に/合わない改造はだめ", T(7, 0.3), 0.06), T(7, 0.1), "エイイチ", w=400, anim="wipe", style="font-size:30px") + '
' + f'
{ink("ロボットは、買っても、", T(8, 2.6), 0.06)}' f'{land("思ったほど自由じゃない", T(8, 3.8), "font-size:48px;font-weight:900")}
' + '
' + src(LAW, T(4, 0.3)), 0.0)) block("hook_law", [4, 5, 6, 7, 8], hook_law) def hook_q(T, E): return paper(tag("考察回 壁打ち") + '

' f'メーカーの
' f'ロボットは、
' f'{land("どこまで", T(9, 1.8), "font-size:108px;font-weight:900")}
' f'変えられるか。

', 0.0) block("hook_q", [9], hook_q) # ================= 1 自己紹介(DAY124 の映像とテロップ) ================= O_TITLE = '
フィジカルAI解説
案内役:ハコブくん(AI音声)
' O_EIICHI = '
エイイチ
デザイナー/ロボティクスの研究・開発
' O_OUTRO = '
相談は概要欄の連絡先から
高評価・チャンネル登録/コメントはエイイチが全部読んでいます
' OVERLAYS = {"title": _sk.overlay(O_TITLE), "title_eiichi": _sk.overlay(O_EIICHI), "outro": _sk.overlay(O_OUTRO)} _t_title = tl.end(11) - tl.start(10) block("title", [10, 11], None, video=("src/day124_crop.mp4", 4.5, "png/ovl/title.png")) block("title_eiichi", [12], None, video=("src/day124_crop.mp4", 4.5 + _t_title, "png/ovl/title_eiichi.png")) def title_kabe(T, E): return paper(tag("考察回") + '
' + f'
{land("壁打ち", T(13, 0.5))}
' + line("エイイチの考えに、ハコブくんが付き合う", T(13, 1.3), size=34, weight=500) + '
', 0.0) block("title_kabe", [13], title_kabe) # ================= 2 持ち込み:5段階の線(脱線①「自分で改造。」) ================= def bring(T, E): return (paper(tag("持ち込み") + '
' + card("mi", inkl("搬送や配膳、掃除のロボットの動きを良くしたいとき、/メーカーの製品を、ソフトもハードも触ったら?", T(15, 0.4), 0.04), T(15, 0.15), "エイイチの疑問", w=920, anim="wipe") + line("実物の改造ではなく、Claude と一緒に、決まり・契約・実例を調べた話", T(16, 1.6), size=28, weight=500, style="color:#5a564f") + card("hb", "店で見て、まず直したいのは?", T(17, 0.2), "ハコブくん", w=520, style="font-size:30px") + card("mi", inkl("速さより、人とすれ違うときと、/迷う動きを、まず直したい", T(18, 0.4), 0.05), T(18, 0.15), "エイイチ", w=760, anim="wipe") + '
' + src(LAW, T(16, 0.3)), 0.0) + stamp("覚えた", T(19, 2.4), "left:1250px;top:430px;font-size:48px")) block("bring", [14, 15, 16, 17, 18, 19], bring) STEP_CSS = """""" def steps(T, E): fly = (T(22, S(22) - 0.2), E(22) + 0.05) t_cut = T(23, 0.3) labels = ["設定", "公式の外部接続", "外付け", "ソフトの書き換え", "機体の改造"] # 階段の面: 下から上へ。左下から右上に積む。4段目から右へずれて切れ目ができる html = "" for k, lab in enumerate(labels): x, y, w, h = 60 + k * 150, 600 - k * 96, 300, 84 col = ("#22201c", "#f5f3ee") if k < 3 else ("#dcd6c9", "#22201c") gx = "70px" if k >= 3 else "0px" appear = T(20, 1.2 + 0.25 * k) if k < 1 else T(21, [0.0, 1.0, 3.3, 4.8, 6.6][k]) html += (f'
{k + 1}{lab}
') zones = (f'
メーカーの想定の内側
' f'
外側
') return (dassen(BG + "自分で改造.jpg", fly[0] + 0.05, fly[1] - 0.05, words="自分で|!改造。", word_style="top:70px;font-size:130px", src_note=GEM) + STEP_CSS + paper(tag("触り方の5段階") + html + zones + f'
' + card("hb", "5段目まで、一気に", T(22, 0.3), "ハコブくん", w=380, botsu=E(22) + 0.15, fade=T(23, 0.2), style="font-size:30px") + '
' + f'
' + f'
法律・保証・保守も、
ほぼこの切れ目で分かれる
', 0.0, flights=[fly]) + shake_js([(t_cut, 0.3)])) block("steps", [20, 21, 22, 23, 24], steps) def steps2(T, E): return paper(tag("切れ目は、思ったより手前") + '
' + card("mi", inkl("思ったより、手前で切れてる", T(25, 0.3), 0.06), T(25, 0.1), "エイイチ", w=600, anim="wipe") + f'
{ink("動きや操作性を良くしたい用事の多くは、", T(26, 1.2), 0.05)}
' f'{land("3段目まで", T(26, 4.2), "font-size:96px;font-weight:900")}{ink("で届く", T(26, 4.8), 0.08)}
' + line("Claude の分析(シミュレーションの店でも、詰まりの多くは店の形と回し方の直しで減った)", T(26, 6.0), size=24, weight=500, style="color:#5a564f") + '
', 0.0) block("steps2", [25, 26], steps2) # ================= 3 やり取り1:車とサブスク(脱線②「ロボ検、開幕。」) ================= def car(T, E): return paper(tag("やり取り 1 / 車と比べると") + '
' + card("mi", inkl("車やバイクは、自分でもいじってきた。/ロボットも個人で持つようになったら、/改造したい人は必ず多い。僕もしたい", T(28, 0.4), 0.045), T(28, 0.15), "エイイチ", w=880, anim="wipe") + bigline("改造したい側の人", T(29, 0.4), size=80) + '
', 0.0) block("car", [27, 28, 29], car) def car2(T, E): col = lambda kind, head, sub, t: (f'
' f'{head}
{sub}
') return paper(tag("買う? 借りる?") + '
' + '
' + col("hb", "車は、買う", "買えば、いろいろ変えられそう", T(30, 0.3)) + col("fact", "ロボットは、サブスク", "用意された以上は、認められない?", T(30, 1.6)) + '
' + card("mi", inkl("ハードはともかく、ソフトは/アップデートが多くて、追いつかなそう", T(31, 0.4), 0.05), T(31, 0.15), "エイイチ", w=780, anim="wipe") + card("fact", "サブスク:機体はメーカーか販売会社の物。契約で縛られる", T(32, 0.3), None, w=900, style="font-size:30px") + bigline("では、買ったら?", T(33, 0.2), size=64) + '
', 0.0) block("car2", [30, 31, 32, 33], car2) def soft(T, E): return (paper(tag("買っても残る縛り") + '
' + f'
{ink("ソフトは、", T(34, 0.2), 0.08)}{land("買っていない", T(34, 0.9), "font-size:90px;font-weight:900")}
' + line("使う権利を、借りているだけ", T(34, 2.4), size=30, weight=500, style="color:#5a564f") + card("fact", inkl("中古で売られた車の運転を助ける機能を、/メーカーが遠くから外した(2020年の報道)", T(35, 0.4), 0.04), T(35, 0.15), "車では", w=880) + '
' + card("mi", inkl("え、買ったのに?", T(36, 0.2), 0.07), T(36, 0.05), "エイイチ", w=380, anim="wipe") + card("hb", "騒ぎになって、あとで戻された", T(37, 0.3), None, w=480, style="font-size:28px") + '
' + f'
{land("体は自分の物、機能は借り物", T(37, 3.0))}
' + '
' + src("出どころ:Electrek(2020-02-07)。騒ぎのあと、機能は戻された", T(35, 0.3)), 0.0)) block("soft", [34, 35, 36, 37], soft) def rule(T, E): fly = (T(39, S(39) - 0.3), E(39) + 0.1) col = lambda kind, head, sub, t: (f'
' f'{head}
{sub}
') return (dassen(BG + "ロボ検.jpg", fly[0] + 0.05, fly[1] - 0.05, words="ロボ検、|!開幕。", word_style="top:40px;font-size:130px", src_note=GEM) + paper(tag("もう1つの違い") + '
' + qline("ここまで変えたら、この検査", T(38, 1.6), size=44) + '
' + col("fact", "車 ある", "受け皿の制度がある", T(38, 4.0)) + col("hb", "屋内のロボット ない", "決まった手続きがない", T(38, 6.0)) + '
' + '
' + card("hb", "ロボ検を作る", T(39, 0.4), "ハコブくん", w=360, botsu=T(41, 0.4), fade=T(41, 1.0), style="font-size:30px") + card("mi", inkl("誰が検査するの、それ", T(40, 0.2), 0.06), T(40, 0.05), "エイイチ", w=460, anim="wipe") + '
' + card("fact", inkl("改造した人が、安全を自分で説明する", T(42, 0.6), 0.05), T(42, 0.3), "手続きがない分", w=760) + '
', 0.0, flights=[fly])) block("rule", [38, 39, 40, 41, 42], rule) def value(T, E): chip = lambda txt, t: (f'
{txt}
') return (paper(tag("いちばん大事な所") + '
' + f'
{ink("配膳ロボットの値打ちは、", T(44, 0.2), 0.06)}{land("ソフトの側", T(44, 1.4), "font-size:84px;font-weight:900")}
' + f'
{chip("地図", T(44, 2.6))}{chip("よけ方", T(44, 3.3))}{chip("遠くからの見守り", T(44, 4.0))}
' + card("mi", inkl("アップデートに追いつかない", T(43, 0.3), 0.06), T(43, 0.1), "エイイチの言葉が、本題だった", w=620, anim="wipe") + card("hb", "すれ違い方・迷い方も、ここ", T(45, 0.4), "最初に直したいと言っていた所", w=620, style="font-size:34px;font-weight:700") + '
', 0.0)) block("value", [43, 44, 45], value) # ================= 4 やり取り2:口を開けると、穴も開く(申請書2枚目 →「却下」・脱線③「扉、全開。」) ================= def port(T, E): fly = (T(48, S(48) - 0.9), T(49, 0.3)) return (dassen(BG + "扉全開.jpg", fly[0] + 0.05, fly[1] - 0.05, words="扉、|!全開。", word_style="top:60px;font-size:140px", src_note=GEM) + paper(tag("やり取り 2 / 外からつなげる口") + '
' + f'
' + form(2, ["外からつなげる口を、全部開ける", "誰とでも仲良くする"], T(46, 0.2), [T(46, 1.0), T(46, 3.4)]) + '
' + f'
' + f'
{ink("口を増やすほど、", T(47, 0.1), 0.07)}{land("穴も増える", T(47, 1.0), "font-size:84px;font-weight:900")}
' + card("fact", inkl("2025年、ある配膳ロボットの会社の外部接続は、鍵は求めるのに、/その機体の持ち主かを確かめていなかった", T(48, 0.4), 0.04), T(48, 0.15), "報告されていること", w=900) + '
' + card("mi", inkl("よその店のロボットまで、/動かせたってこと?", T(49, 0.4), 0.06), T(49, 0.2), "エイイチ", w=480, anim="wipe") + card("hb", "運ぶ物によっては、笑えない", T(50, 0.4), None, w=430, style="font-size:30px") + '
' + card("fact", inkl("開けるのは、メーカーが用意した窓口だけ。/それでも、更新で作り直しになることがある", T(51, 0.4), 0.045), T(51, 0.15), None, w=860) + '
' + src("出どころ:見つけた人の報告・The Register(2025-08-29)。手口は扱わない", T(48, 0.3)), 0.0, flights=[fly]) + hanko("却下", E(46) - 0.9, "left:1130px;top:180px", size=80, t_hide=T(47, 0.0)) + shake_js([(E(46) - 0.55, 0.3)])) block("port", [46, 47, 48, 49, 50, 51], port) # ================= 5 山場:満足できない人が増えたら ================= def peak(T, E): names = ["有料の性能解放", "非公認の解放業者", "アプリの市場", "公認の調整業者", "修理する権利", "汎用の土台+自前のソフト", "サポート切れ機の再生", "追加学習"] tile = lambda k, txt, t: (f'
{txt}
') grid = '
' + "".join(tile(k, n, T(54, 2.6 + 0.35 * k)) for k, n in enumerate(names)) + '
' return (paper(tag("満足できない人が増えたら") + '
' + card("mi", inkl("ロボットが一般的になったら、/標準の性能だけじゃ、満足できない人も出てくる", T(53, 0.4), 0.045), T(53, 0.15), "見立て ・ エイイチ", w=860, anim="wipe") + f'
{ink("考えられる形は、", T(54, 1.0), 0.06)}{land("8つ", T(54, 2.0), "font-size:72px;font-weight:900")}
' + grid + '
' + card("mi", inkl("細かいのは、記事に書いといて", T(55, 0.3), 0.06), T(55, 0.1), "エイイチ", w=520, anim="wipe") + card("hb", 'もう書いてあります
pixelstomotors.com / LOG406
', T(56, 0.2), None, w=400, style="font-size:30px") + '
', 0.0)) block("peak", [52, 53, 54, 55, 56], peak) def unlock(T, E): fly = (T(58, S(58) + 0.05), T(59, 0.3)) return (dassen(BG + "課金で解放.jpg", fly[0] + 0.05, fly[1] - 0.05, words="課金で|!解放。", word_style="top:60px;font-size:140px", src_note=GEM) + paper(tag("車では、もう起きている") + '
' + line("Model Y(2025年) 部品は替えずに、ソフトの更新だけで速くなる", T(58, 0.6), size=30, weight=700) + f'
約$0
' + f'
{land("同じ体のまま、お金で速くなる", T(59, 0.4))}
' + '
' + card("mi", inkl("ロボットでもやりそう", T(60, 0.2), 0.06), T(60, 0.05), "エイイチ", w=420, anim="wipe") + card("hb", "払いますか?", T(61, 0.1), "ハコブくん", w=300, style="font-size:32px") + card("mi", f'{ink("払います", T(62, 0.1), 0.06)}', T(62, 0.0), "エイイチ", w=300, anim="wipe") + '
' + src("出どころ:EVANNEX(2025-06-02)。米国での価格", T(58, 0.6)), 0.0, flights=[fly]) + stamp("即答", T(63, 0.05), "left:1330px;top:300px;font-size:60px") + shake_js([(T(63, 0.4), 0.3)]) + counter_js("c2k", T(58, 2.4), T(58, 4.2), 2000, dec=0)) block("unlock", [57, 58, 59, 60, 61, 62, 63], unlock) def learn(T, E): return paper(tag("8つのうち、長い目で見た主役") + '
' + card("hb", inkl("動きを学習で作るロボットが広がれば、/長い目で見た主役は、最後の1つ", T(64, 0.5), 0.045), T(64, 0.2), "Claude の見立て", w=880) + bigline("追加学習", T(65, 1.6), size=140) + line("自分の店のお手本を足して、動きを学ばせ直す", T(65, 0.4), size=32, weight=500) + f'
{ink("改造は、ネジから、", T(66, 0.4), 0.08)}{land("お手本へ。", T(66, 1.8), "font-size:70px")}
' + '
', 0.0) block("learn", [64, 65, 66], learn) def learn_peak(T, E): return ('
' '
' '
' '

' f'追加学習の形に、
' f'{land("魅力", T(67, 2.4), "font-weight:900;font-size:96px")}がある。

' f'
エイイチ
' '
' + stamp("エイイチの推し", T(67, S(67) + 0.1), "left:1080px;top:600px;font-size:44px") + shake_js([(T(67, S(67) + 0.45), 0.3)])) block("learn_peak", [67, 68], learn_peak) # ================= 6 やり取り3:半分開いたロボット ================= def open_(T, E): box = lambda head, sub, kind, t: (f'
' f'{head}
{sub}
') return (paper(tag("やり取り 3 / 半分開いたロボット") + '
' + card("mi", inkl("オープン型のロボットも出てきそう。/市場が大きくなったら、差別化で。競争が激しくなるかも", T(69, 0.4), 0.045), T(69, 0.15), "見立て ・ エイイチ", w=880, anim="wipe") + bigline("もう、出ていた", T(70, 0.2), size=56) + line("Unitree(中国のヒューマノイド・四足ロボットのメーカー)", T(71, 0.6), size=26, weight=500, style="color:#5a564f") + line("関節ごとの細かい動きまで、開けている", T(71, 3.0), size=32, weight=700) + line("でも、人型ロボット G1 の、機体の中の計算機は", T(72, 0.6), size=28, weight=500) + '
' + box("計算機 1", "メーカーのサービス専用。開発には使えない", "fact", T(72, 3.6)) + box("計算機 2", "開発に使える", "hb", T(72, 4.6)) + '
' + '
' + src("出どころ:Unitree G1 開発の手引き", T(71, 0.6)), 0.0)) block("open", [69, 70, 71, 72], open_) def open2(T, E): seg = lambda txt, bg, fg, t: (f'
{txt}
') return paper(tag("Claude の読み") + '
' + '
' + seg("安全は、閉じる", "#22201c", "#f5f3ee", T(73, 1.6)) + seg("動きは、開く", "#ece9e2", "#22201c", T(73, 4.0)) + '
' + line("転ばない・止まる、はメーカー。何をどう動かすか、を開ける", T(73, 5.2), size=28, weight=500, style="color:#5a564f") + card("hb", "スマホの iPhone と Android の関係に近い", T(74, 0.4), "Claude の見立て", w=720, style="font-size:32px") + '
', 0.0) block("open2", [73, 74], open2) def maker(T, E): return (paper(tag("もしメーカーなら") + '
' + card("hb", "エイイチがメーカーなら、どこまで開けますか?", T(75, 0.1), "ハコブくん", w=760, style="font-size:32px") + card("mi", f'{inkl("全部知りたいから、/できる限り分解する", T(76, 0.3), 0.06)}', T(76, 0.1), "エイイチ", w=760, anim="wipe") + f'
{ink("それは、", T(77, 0.6), 0.08)}{land("開けたい側", T(77, 2.0), "font-size:80px;font-weight:900")}{ink("の答え", T(77, 2.6), 0.08)}
' + '
', 0.0)) block("maker", [75, 76, 77], maker) def profit(T, E): return (paper(tag("値打ちは、どこへ") + '
' + card("mi", f'{inkl("利益が残るのは、/作る側より、入れて回す側", T(78, 0.3), 0.06)}', T(78, 0.1), "見立て ・ エイイチ", w=820, anim="wipe") + card("hb", inkl("機体は値下がりし、値打ちは、/学習させた動きと、現場に入れて回す所へ", T(79, 0.6), 0.045), T(79, 0.3), "Claude の見立て(かもしれない)", w=860) + '
', 0.0) + stamp("この見立ては、エイイチから", T(78, S(78) + 0.1), "left:1010px;top:150px") + shake_js([(T(78, S(78) + 0.45), 0.3)])) block("profit", [78, 79], profit) # ================= 7 まとめ(散らばった面が3行の表に収まる) ================= SUM_CSS = """@keyframes gather { from { opacity:0; transform: translate(var(--dx),var(--dy)) rotate(var(--r)); } to { opacity:1; transform: none; } } .trow3 { display:grid; grid-template-columns: 200px 1fr 260px; align-items:center; width:940px; padding:14px 26px; font-family:'Noto Sans CJK JP',sans-serif; font-size:28px; font-weight:500; text-align:left; gap:14px; } .trow3 b { font-weight:900; font-size:30px; } .trow3 i { font-style:normal; font-weight:700; }""" def summ(T, E): row = lambda a, b, c, bg, fg, dx, dy, r, t: (f'
{a}{b}{c}
') head = (f'
' '改造の形中心責任を負うのは
') b3 = lambda txt, t: card("hb", txt, t, None, w=290, botsu=t + 0.25, style="font-size:24px;padding:10px 12px;white-space:nowrap") return ('' + paper(tag("ここまでを、1枚に") + '
' + head + row("これまで", "機体・ソフトの書き換え", "改造した人", "#22201c", "#f5f3ee", 420, 420, 8, T(81, 0.4)) + row("公認の範囲", "設定・外部接続・認められた業者", "メーカーと業者で分ける", "#dcd6c9", "#22201c", 300, -60, -6, T(82, 0.4)) + row("これから", "現場のお手本で追加学習", "まだ決まっていない", "#ece9e2", "#22201c", -420, 180, 10, T(83, 0.4)) + f'

' f'{ink("改造の行き先は、メーカーが開けた範囲で、", T(84, 2.6), 0.05)}
{land("自分の現場に合う動きを、学ばせること。", T(84, 5.0), "font-size:46px")}

' + f'
{b3("5段目まで一気に", T(85, 1.6))}{b3("ロボ検", T(85, 2.0))}{b3("口を全部開ける", T(85, 2.4))}
' + '
', 0.0)) block("sum", [80, 81, 82, 83, 84, 85], summ) # ================= 8 残った問い ================= def rest(T, E): return paper(tag("残った問い") + '
' + f'
{ink("あとから学ばせた動きを、", T(87, 1.8), 0.06)}
' f'{land("誰が確かめるのか", T(87, 3.4), "font-size:64px;font-weight:900")}
' + line("決まりは動き始めているが、ここはまだ、はっきりしない", T(87, 0.6), size=26, weight=500, style="color:#5a564f") + card("mi", inkl("エンジニアと、店の社員やスタッフが、一緒に確かめるのがいちばん。/ただ実際は、コストの面で難しそう", T(89, 0.4), 0.045), T(89, 0.15), "エイイチ", w=900, anim="wipe") + f'
{ink("良い形と、払える形が、", T(90, 0.2), 0.06)}{land("離れている", T(90, 1.6), "font-size:60px;font-weight:900")}
' + card("hb", '考察ノートの全文 pixelstomotors.com / LOG406', T(91, 0.3), "サイトの記事", w=700, style="font-size:28px;padding:10px 30px") + '
', 0.0) block("rest", [86, 87, 88, 89, 90, 91], rest) def ask(T, E): return paper(tag("あなたの店では") + '
' + qline("あなたの店のロボット、", T(92, 0.2), size=56) + qline("どこまで触っていいか、", T(92, 1.4), size=56) + qline("契約書に書いてありますか。", T(92, 2.8), size=56) + '
', 0.0) block("ask", [92], ask) def end(T, E): return (paper(tag("最後に") + '
' + form(3, ["病院の廊下で、人とすれ違う動きを、追加学習する"], T(93, 0.6), [T(93, 2.2)]) + card("mi", inkl("……それは、ありかもね", T(94, 0.3), 0.07), T(94, 0.1), "エイイチ", w=480, anim="wipe") + '
', 0.0) + hanko("保留", T(94, S(94) + 0.1), "left:1120px;top:170px", size=80) + shake_js([(T(94, S(94) + 0.45), 0.3)])) block("end", [93, 94, 95], end) # ================= 9 アウトロ ================= block("outro", [96, 97, 98], None, video=("src/day124_crop.mp4", 18.0, "png/ovl/outro.png")) assert [i for b in BLOCKS for i in b["sents"]] == list(range(N)), "場面の文の番号が抜けている・重なっている" # ================= ハコブくん ================= # 表情は narration.py の表情が基本。ここは文の途中で替える所: (文, 文の頭から何秒, 何秒間, 表情) HK_FACE = [ (2, 0.0, 2.5, "doubt"), (3, 0.0, 1.5, "trouble"), # 却下 (22, S(22), 1.2, "surprise"), # 一気に → 脱線 (23, 0.0, 1.0, "surprise"), (29, 0.0, 2.0, "surprise"), (36, 0.0, 1.5, "surprise"), (41, 0.0, 2.2, "dot"), # ボツ (46, S(46), 1.4, "trouble"), # 却下(2枚目) (55, 0.0, 2.0, "trouble"), (62, 0.0, 1.0, "surprise"), (67, 0.0, 6.0, "think"), (76, 0.0, 3.0, "surprise"), (94, S(94), 1.6, "trouble"), # 保留…… ] HK_FACE = [(A(i, a), A(i, a) + d, f) for i, a, d, f in HK_FACE] # 体ごとの動き: jump=驚いて全身で飛び出す / fall=前のめりにコケる(up で起き上がる) / reflex=のけぞる HK_MOVES = [ dict(kind="reflex", t0=A(3, 0.2), dur=0.9), # 却下でのけぞる dict(kind="fall", t0=A(41, 0.2), t1=A(42, 0.2), dur=0.5), # ロボ検ボツでコケる dict(kind="jump", t0=A(68, 0.0), dur=1.9), # 「……それです」で飛び出す ] HK_ALPHA = [(A(67, 0.0), tl.end(68) - 2.0, 0.3), # 暗い場面では薄く(飛び出す前まで) (A(22, S(22) - 0.15), A(23, -0.05), 0.0), # 自分で改造の絵にはハコブくんが描いてあるので、重ねない (A(39, S(39) - 0.25), A(40, 0.05), 0.0)] # ロボ検の絵も同じ # ================= エイイチ ================= # (文, 文の頭から何秒, 姿) 1腕組み 2うなずく 3指さす 4首をかしげる 5ひらめく 6のけぞって驚く 7半目でツッコむ 8吹き出して笑う EI_POSE = [ (0, 0.0, 1), (1, 0.6, 6), (2, 0.0, 7), (3, 0.0, 1), (4, 0.0, 2), (6, 0.0, 4), (7, 0.0, 7), (8, 0.0, 2), (9, 0.0, 1), (12, 0.0, 2), (13, 0.0, 1), (14, 0.0, 1), (15, 0.0, 3), (16, 0.0, 2), (17, 0.0, 4), (18, 0.0, 3), (19, 0.0, 2), (20, 0.0, 1), (22, 0.3, 6), (23, 0.0, 1), (24, 0.0, 2), (25, 0.0, 3), (26, 0.0, 2), (27, 0.0, 1), (28, 0.0, 3), (29, 0.0, 8), (30, 0.0, 4), (31, 0.0, 3), (32, 0.0, 2), (33, 0.0, 4), (34, 0.0, 1), (35, 0.0, 4), (36, 0.0, 6), (37, 0.0, 1), (37, 3.0, 2), (38, 0.0, 1), (39, 0.4, 6), (40, 0.0, 7), (41, 0.3, 8), (42, 0.0, 2), (43, 0.0, 5), (44, 0.0, 2), (45, 0.0, 3), (46, 0.0, 4), (46, 2.5, 7), (47, 0.0, 1), (48, 0.0, 4), (49, 0.0, 6), (50, 0.0, 1), (51, 0.0, 2), (52, 0.0, 1), (53, 0.0, 3), (54, 0.0, 4), (55, 0.0, 7), (56, 0.0, 8), (57, 0.0, 1), (58, 0.0, 4), (59, 0.0, 6), (60, 0.0, 3), (61, 0.0, 1), (62, 0.0, 3), (63, 0.0, 8), (64, 0.0, 1), (65, 0.0, 2), (66, 0.0, 2), (67, 0.0, 5), (68, 0.0, 3), (69, 0.0, 3), (70, 0.0, 6), (71, 0.0, 1), (72, 0.0, 4), (73, 0.0, 2), (74, 0.0, 2), (75, 0.0, 4), (76, 0.0, 3), (77, 0.6, 8), (78, 0.0, 5), (79, 0.0, 2), (80, 0.0, 1), (81, 0.0, 2), (84, 0.0, 2), (85, 0.8, 8), (86, 0.0, 1), (87, 0.0, 4), (88, 0.0, 1), (89, 0.0, 3), (90, 0.0, 2), (91, 0.0, 2), (92, 0.0, 3), (93, 0.0, 4), (94, 0.0, 2), (95, 0.0, 3), (96, 0.0, 2), ] EI_POSE = sorted((A(i, a), p) for i, a, p in EI_POSE) # 置き方の替え: (開始, 終了, 置き方)。hide=出さない / big=暗い場面で大きく光らせる EI_PLACE = [ (A(10), tl.end(11), "hide"), (A(67), tl.end(67), "big"), ] # ================= 効果音 ================= # (文, 文の頭から何秒, 音, ナレーションより何dB) SE = [ (0, 0.3, "se_pop", -12), (1, 0.2, "se_pop", -16), (1, 0.6, "se_pop", -16), (1, 1.0, "se_pop", -16), (3, 0.25, "se_pop", -6), # 却下 (9, 1.8, "se_pop", -12), (22, S(22) - 0.2, "se_whoosh", -10), (23, -0.05, "se_whoosh", -14), (22, 99, "se_pop", -12), (23, 0.3, "se_pop", -10), # 切れ目 (33, 0.2, "se_pop", -12), (39, S(39) - 0.3, "se_whoosh", -10), (40, 0.0, "se_whoosh", -14), (41, 0.2, "se_whoosh", -12), (41, 0.45, "se_pop", -6), (41, 0.4, "se_pop", -12), (46, 0.2, "se_pop", -12), (46, S(46) - 0.0, "se_pop", -6), # 申請書2枚目・却下 (48, S(48) - 0.9, "se_whoosh", -10), (49, 0.3, "se_whoosh", -14), (54, 2.0, "se_pop", -12), (58, S(58) + 0.05, "se_whoosh", -10), (59, 0.3, "se_whoosh", -14), (63, 0.05, "se_pop", -8), (65, 1.6, "se_pop", -10), (67, S(67) + 0.1, "se_pop", -8), (68, 0.0, "se_rise", -10), (70, 0.2, "se_pop", -12), (78, S(78) + 0.1, "se_pop", -8), (85, 1.85, "se_pop", -14), (85, 2.25, "se_pop", -14), (85, 2.65, "se_pop", -14), (93, 0.6, "se_pop", -12), (94, S(94) + 0.1, "se_pop", -6), # 保留 ] SE = [s for s in SE if s[1] != 99] # 一覧の画像に撮る時刻(場面の頭からの秒)。書かない場面は最後の0.4秒前 def _rel(b, i, off=0.0): return tl.start(i) - _tl.start(b["sents"][0]) + off SHEET = {} for _b in BLOCKS: _end = _tl.end(_b["sents"][-1]) - _tl.start(_b["sents"][0]) - 0.4 SHEET[_b["name"]] = [_end] _B = {b["name"]: b for b in BLOCKS} SHEET["hook"] = [_rel(_B["hook"], 2, 1.5), SHEET["hook"][0]] SHEET["steps"] = [_rel(_B["steps"], 21, 7.5), _rel(_B["steps"], 22, S(22) + 0.6), SHEET["steps"][0]] SHEET["rule"] = [_rel(_B["rule"], 39, S(39) + 0.4), _rel(_B["rule"], 41, 0.6), SHEET["rule"][0]] SHEET["port"] = [_rel(_B["port"], 46, S(46) + 0.2), _rel(_B["port"], 48, S(48) - 0.2), SHEET["port"][0]] SHEET["unlock"] = [_rel(_B["unlock"], 58, S(58) + 0.6), SHEET["unlock"][0]] SHEET["learn_peak"] = [_rel(_B["learn_peak"], 67, S(67) + 0.5), _rel(_B["learn_peak"], 68, 0.8)] SHEET["end"] = [SHEET["end"][0]] SHEET["title"] = [2.0]