"""ServiceSim のレポート(報告動画・パネル付き動画・詰まりの地図)の色と字。決まりは STYLE.md(2026-11-08 エイイチ了承)。 3つのスクリプトはここから色と字を読む。スクリプトの中で色や字を決めない。変えるときはエイイチに確かめてから。 字:日本語は Noto Sans(Noto Sans CJK JP)、英数字は Inter(サイト pixelstomotors.com と同じ組み合わせ)。 """ import os HERE = os.path.dirname(os.path.abspath(__file__)) # ---- 下地(サイトの Base.astro と同じ)---- PAPER = "#f7f7f7" # 背景 SHEET = "#ffffff" # 白い面 INK = "#242424" # 字・Unity の線・「次にやること」の札・字幕帯 MUTED = "#555555" # 補足の字 FAINT = "#888888" # 目盛り・卓の枠など、目立たせない線と字 RULE = "#e6e6e6" # 罫線・グリッド BAND = "#ececec" # 範囲の帯(パネルの「今見ている所」など) MAP_WALL = "#c8c8c8" # 地図の通れない所(狭い道だけの所は RULE、通れる所は SHEET) # ---- 立体の色(Unity の録画と同じ。最優先。この3つの意味にしか使わない)---- GUEST = "#e8c520" # お客さん=黄 STAFF = "#2a55c8" # スタッフ=青 ROBOT = "#ffffff" # ロボット=白(白い面に置くときは INK の縁を付ける) ROBOT_SWATCH = "#f2f2f2" # 色の説明の丸だけ(白だと背景に溶けるため) # ---- 役割の色 ---- # 青=その画面の主役(一番見てほしいもの)。何が主役かは図ごとに決め、凡例か線の横の名前で必ず書く。 # ただしスタッフが出る画面(録画・パネル)では青はスタッフ。その画面では主役に青を使わない。1つの画面で青に2つの意味を持たせない MAIN = "#2f6fdf" # 主役の線・点・枠 MAIN_SOFT = "#e8f0ff" # 主役の面(地図の待機点など) # ---- 意味の色(意味を固定)---- BAD = "#d85a30" # 悪い・詰まり・待ち・止まり・助けが要る GOOD = "#1d9e75" # 良くなった・合格 QUIET = "#9a9a9a" # 比べる相手・基準(サイトの試算の点線など)・記録だけ・取りやめ # 報告動画の札:意味の名前 → 色。ここにない名前は使わない TAG = {"good": GOOD, "bad": BAD, "quiet": QUIET, "next": INK} # グラフの線:主役(ふつうは Unity)は MAIN の実線、比べる相手(サイトの試算)は QUIET の点線 SERIES = {"unity": MAIN, "main": MAIN, "site": QUIET, "bad": BAD} # 古い spec の名前の読み替え(acc は青だった。良し悪しのない札・次にやることなので next にする) TAG_ALIAS = {"acc": "next"} SERIES_ALIAS = {"acc": "unity", "good": "unity"} # ---- 字 ---- INTER = os.path.join(HERE, "fonts", "Inter-Regular.ttf") INTER_B = os.path.join(HERE, "fonts", "Inter-Bold.ttf") _NOTO_R = ["/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", os.path.expanduser("~/Library/Fonts/NotoSansCJKjp-Regular.otf"), os.path.expanduser("~/Library/Fonts/NotoSansCJK-Regular.ttc"), "/Library/Fonts/NotoSansCJKjp-Regular.otf"] _NOTO_B = ["/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc", os.path.expanduser("~/Library/Fonts/NotoSansCJKjp-Bold.otf"), os.path.expanduser("~/Library/Fonts/NotoSansCJK-Bold.ttc"), "/Library/Fonts/NotoSansCJKjp-Bold.otf"] def _first(paths, what): for p in paths: if os.path.exists(p): return p raise SystemExit("字が見つからない(%s)。Noto Sans CJK JP を入れてから作る: %s" % (what, paths)) NOTO = _first(_NOTO_R, "Noto Sans CJK JP Regular") NOTO_B = _first(_NOTO_B, "Noto Sans CJK JP Bold") for _p in (INTER, INTER_B): if not os.path.exists(_p): raise SystemExit("字が見つからない: %s" % _p) CSS_FONT = "'Inter','Noto Sans CJK JP','Noto Sans JP',sans-serif" # Inter は英数字と半角の記号だけ(unicode-range)。①・→ などは Noto で描き、Pillow の is_latin と同じ分け方にする _RANGE = "unicode-range:U+0000-00D6,U+00D8-024F" CSS_FACE = ("@font-face{font-family:'Inter';font-weight:400;src:url('file://%s');%s}" "@font-face{font-family:'Inter';font-weight:700;src:url('file://%s');%s}") % (INTER, _RANGE, INTER_B, _RANGE) def is_latin(ch): """英数字と半角の記号は Inter。それ以外(かな・漢字・全角の記号・①など)は Noto""" return ord(ch) < 0x250 and ch != "×" # ---- Pillow:1つの文字列を Inter と Noto に分けて描く ---- _cache = {} def _font(size, bold, latin): from PIL import ImageFont k = (size, bold, latin) if k not in _cache: path = (INTER_B if bold else INTER) if latin else (NOTO_B if bold else NOTO) _cache[k] = ImageFont.truetype(path, size, index=0) return _cache[k] def _runs(s): out = [] for ch in s: lat = is_latin(ch) if out and out[-1][0] == lat: out[-1][1] += ch else: out.append([lat, ch]) return out def text_width(s, size, bold=False): return sum(_font(size, bold, lat).getlength(t) for lat, t in _runs(s)) def text(d, xy, s, size, fill, bold=False, anchor="la"): """d.text の代わり。anchor は横 l/m/r と縦 a/m/s(Pillow と同じ書き方)。高さは Noto の字で決める""" x, y = xy w = text_width(s, size, bold) h, v = anchor[0], anchor[1] x = x - (w / 2 if h == "m" else w if h == "r" else 0) asc, desc = _font(size, bold, False).getmetrics() base = y + (asc if v == "a" else (asc - desc) / 2 if v == "m" else 0) for lat, t in _runs(s): f = _font(size, bold, lat) d.text((x, base), t, font=f, fill=fill, anchor="ls") x += f.getlength(t) # ---- matplotlib ---- def mpl_setup(): import matplotlib from matplotlib import font_manager as fm for p in (INTER, INTER_B, NOTO, NOTO_B): fm.fontManager.addfont(p) jp = fm.FontProperties(fname=NOTO).get_name() matplotlib.rcParams["font.family"] = ["Inter", jp] # 英数字は Inter、無い字は Noto(matplotlib 3.6 以降) matplotlib.rcParams["text.color"] = INK matplotlib.rcParams["axes.labelcolor"] = INK matplotlib.rcParams["axes.edgecolor"] = FAINT matplotlib.rcParams["xtick.color"] = MUTED matplotlib.rcParams["ytick.color"] = MUTED matplotlib.rcParams["figure.facecolor"] = PAPER matplotlib.rcParams["savefig.facecolor"] = PAPER