ざっと構造レベルで見た限り、かなりきれいにまとまってます。
「JSON ⇔ dataclass モデル ⇔ matplotlib (scitex.plt)」の三層がちゃんと分離されていて、将来の拡張もしやすそうです。

20251121_105809-vis

1. 全体の構成について

scitex.vis パッケージ構造

model/（Figure/Axes/Plot/Guide/Annotation + Style）

backend/（parser, render, export）

io/（project ディレクトリ構造との橋渡し）

utils/（validate + journal テンプレート）

__init__.py で

FigureModel, AxesModel, PlotModel, GuideModel, AnnotationModel

build_figure_from_json, export_figure 系

get_template, list_templates, NATURE_SINGLE_COLUMN_MM など
が top-level に綺麗にエクスポートされていて、
import scitex as stx; stx.vis.xxx で使える API としては十分わかりやすいです。

設計としては「論文図用の JSON DSL」として、もう “モジュールとして完成” しているレベルだと思います。

2. backend 周りの確認ポイント
2-1. validate_figure_json
from ..utils import validate_json_structure, validate_axes_layout

validate_json_structure(fig_json)
fig_model = parse_figure_json(fig_json)
fig_model.validate()
validate_axes_layout(...)


粗い JSON チェック → dataclass 化 → モデルの validate → レイアウト整合性
というパイプラインになっていて、エントリポイントとしてとても良いです。

io.load_figure_json(validate=True) からもこれを呼び出していて、
「ファイル読み込み＝即 validation」になっているのも、SciTeX 全体の哲学と合っていると思います。

2-2. render_figure / mm 制御

width_mm, height_mm をインチに変換して stx.plt.subplots(...) に渡し、

その上で nrows, ncols, dpi などを統一的に指定。

ここはすでに「mm ベースの figure サイズ」は担保済みなので、
後から left_mm などを stx.plt.subplots() の引数にマップするだけで、
“完全 mm 制御” にアップグレードできます（TODO コメントも書いてあって良いです）。

3. Axes / Plot / Guide / Annotation + Style

backend.render から見える前提は：

AxesModel に style: AxesStyle

PlotModel に style: PlotStyle

GuideModel に style: GuideStyle

AnnotationModel に style: TextStyle

があり、style.to_dict() で matplotlib に渡す kwargs を生成、という設計になっています。

style = axes_model.style
...
if style.grid:
    ax.grid(alpha=style.grid_alpha, ...)
...
if style.legend:
    ax.legend(loc=style.legend_loc, ...)

style_dict = plot_model.style.to_dict()
...
kwargs = {k: v for k, v in style_dict.items() if k in relevant_keys}

kwargs = guide_model.style.to_dict()
...
kwargs = annotation_model.style.to_dict()

3-1. モデル側で確認したいこと

コード全体が長く、該当ファイルが途中でトランケートされているので推測も入りますが、
最低限、次が守られているかだけ一度ローカルで確認しておくと安心です：

各 Model に style フィールドが定義されていること

AxesModel(style: AxesStyle = AxesStyle()) のような形

PlotModel, GuideModel, AnnotationModel も同様

.from_dict() が style を正しく復元していること

例：

@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "PlotModel":
    style_data = data.pop("style", {})
    obj = cls(**data)
    obj.style = PlotStyle.from_dict(style_data)  # など
    return obj


こうしておかないと、JSON → Model の round-trip で style がただの dict のままになってしまいます。

validate() が style に依存するフィールドにアクセスしていないこと

たとえば旧実装では Annotation で self.ha, self.va を直接見ていましたが、
これを style.ha, style.va に移しているなら、validate() も必ず style 経由に統一する必要があります。

4. plot_type の網羅性（小さめの仕様ギャップ）

PlotModel.validate() 側では、かなり多くの plot_type を許容しているはずです（line, scatter, bar, barh, hist, errorbar, fill_between, heatmap, imshow, contour, contourf, box, violin, step, stem など）。
一方で render_plot() の本体は、現状だと line / scatter / errorbar / bar / barh / hist / fill_between / heatmap / imshow / contour / contourf までしか実装されていません。

20251121_105809-vis

結果として：

plot_type="box" や "violin" を JSON では許容しているのに、

実際に render_plot すると Unsupported plot type になります。

提案

どちらかに寄せる：

(A) 先に実装する plot_type だけ validate() で許可する

MVP の段階では line / scatter / bar / barh / hist / errorbar / heatmap / imshow / contour / contourf / fill_between に限定

(B) box, violin, step, stem も最低限の実装を書く

例えば：

box: ax.boxplot(...)

violin: ax.violinplot(...)

step: ax.step(...)

stem: ax.stem(...)

MVP を急ぐなら (A) で一旦締めてしまって、
Sigma UI 側のメニューからも「未実装タイプ」は隠すのが UX 的にも安全だと思います。

5. I/O & プロジェクト構造

io.load_figure_json_from_project / save_figure_json_to_project は

project_dir/scitex/vis/figs/{figure_id}.json


という構造で統一されていて、以前話していた project-dir/scitex/vis/figs にきちんと揃っています。

20251121_105809-vis

これは /vis/sigma/ フロントエンドとかなり綺麗につながる形になっていて、

UI 側：Figure ツリーの編集 → JSON

Django API：JSON を scitex.vis.io.save_figure_json_to_project(...) で保存

解析/論文側：scitex.vis.backend.export_figure(...) or build_figure_from_json(...) で再利用

というフローが素直に組めます。

6. テストとしてやっておくといいこと

ローカルで、次の 2〜3 個だけ回しておけば、かなり安心できると思います：

最小構成 + line plot

import scitex as stx
import numpy as np

fig_json = stx.vis.utils.get_template("nature_single", height_mm=100)
fig_json["axes"] = [{
    "row": 0,
    "col": 0,
    "xlabel": "X",
    "ylabel": "Y",
    "title": "Demo",
    "plots": [{
        "plot_type": "line",
        "data": {
            "x": list(range(10)),
            "y": [i*i for i in range(10)],
        },
        # style はデフォルトに任せる or 最小限指定
    }],
    "annotations": [],
    "guides": [],
    # style は AxesStyle のデフォルト
}]

stx.vis.backend.validate_figure_json(fig_json)
fig, axes = stx.vis.backend.build_figure_from_json(fig_json)
stx.io.save(fig, "test.png", dpi=300)


project round-trip

stx.vis.io.save_figure_json_to_project("/tmp/proj", "fig-001", fig_json)
loaded = stx.vis.io.load_figure_json_from_project("/tmp/proj", "fig-001")
stx.vis.backend.validate_figure_json(loaded)


存在しない plot_type でちゃんとエラーになるか

plot_type="unknown" にして render_plot 実行 → ValueError になるか確認

7. 結論

モジュール構造・API 形状・mm ベース設計・project 連携まで含めて、
「論文図用のバックエンド」としてかなり完成度が高いです。

いま見えている主なタスクは：

style dataclass と Model の整合性（style フィールドの from_dict/to_dict/validate）

plot_type の実装範囲と validate() の整合性

mm-based spacing を stx.plt.subplots にどう渡すか（これは TODO でも問題なし）

ここまで出来ていれば、/vis/sigma/ からは

JSON を組み立てて

save_figure_json_to_project で保存し

裏で export_figure を叩いて PNG を生成

という「世界一まともな論文図パイプライン」の中核は、もう出来ていると言って良いと思います。
