見出し画像

リリース: Python製作図ライブラリ Drawlib

Drawlib というPythonでイラストを描画するための機能豊富なライブラリを作ってリリースしました。Pythonを知っていれば新しいことを覚えなくてもすぐに使えますし、IDE(VSCodeなど)がコード補完やヘルプ表示で書くことを支援してくれます。GitHub などでドキュメントなどのイラストをコードとして管理するのに便利なはずです。Illustration as Code !!

Drawlib では以下の画像のようにコードからイラストを作成します。

コードからイラストを生成 (中にある円の絵だけでなく上のイラスト自体もdrawlibで書いています)

ここでは誰が見ても分かるように円を描画する circle 関数において、どこにどれぐらいの大きさかを書き、その見た目のスタイルを ShapeStyle クラスで定義しています。読めばなんとなく何をするか想像できますね。上の例のように circle に ShapeStyle を定義するのは HTML に CSS をベタ書きしているようなものです。

多くのかたがご存知のように HTMLであれば、CSSのベタ書きは推奨されません。HTMLファイルがCSSファイルを参照し、タグに事前定義スタイルを適用させるのが普通です。

Drawlib もこれと全く同じです。Pythonのコードとしてスタイル定義を書いたファイルを作り、それをイラストを書くコードが参照して、名前でスタイルを適用するのが本来のdrawlibの利用方法です。そのようにすると、先のサンプルコードのスタイル定義は名前指定だけになります。コードも短くなりますし、スタイル定義をいじるだけでそれを参照しているすべての図が更新されます。すばやく書いて、変更を楽にして、見ために一貫性をもたせるのに便利です。たくさんのイラストを作成する場合は特におすすめです。

公式ドキュメントと GitHub リポジトリ

英語ですが Drawlib の公式ドキュメントは以下となります。ベータリリースを卒業した段階で日本語のドキュメントも書くかもしれませんが、今の段階ではブラウザなどの機械翻訳で日本語にして読んでください。

GitHub リポジトリは以下となります。黙って開発して、プライベートアルファ版からパブリックベータ版に進んで公開した直後です。記事を書いた時点では全然見られていないですね(笑)。気に入ってもらえればスターなどで応援してくれるとうれしいです。使って仲間を増やしてくれるともっとうれしいです。

ちなみに、開発のトリガーは某出版社からの「Pythonの上級者向け書籍を書いてくれませんか」という依頼です。OKしたものの「うーん、またイラストにパワポ使うの嫌だな。俺 Google で働いているから宗教的にも微妙なのよね。買うの高いし。。。よっしゃライブラリ作るか」という現実逃避がスタート地点ですね。出版社さま、執筆前に余計な時間をかけてすみません。。。

これも余談ですが、ライブラリ開発にAIをがっつり活用してみました。3~4ヶ月ぐらいかけて、働きながら業務時間外におよそコード5万行(4万行ぐらいはマクロとAIで生成しましたが。。。)、ユニットテスト350個、英語ドキュメントを本一冊ぶんぐらい書いた気がします。コンセプトのブラッシュアップやソフトウェアのアーキテクチャ設計などの重要な難しい箇所は私がやりましたが、それほど難しくない局所的(関数レベルやテスト)な実装や docstring 含めたドキュメント系にはだいぶAIを使っています。特に私の幼稚な英語をそれっぽい英語に添削してくれたのは非常に助かりました。いろいろチャレンジしましたが「今の段階では開発支援系のAIは素人のためではなくベテランのためのものだな」という感想になりました。

以上で要約と感想は終わりです。以下から記事のメインコンテンツが始まります!!

Python 作図ライブラリ Drawlib の紹介

Illustration as Code の実現。シンプルなコードをそのままイラストへ。

マークダウンでドキュメントを書いていますか。便利ですよね、テキスト形式でドキュメントの構造を書けるのでバージョン管理もしやすいです。そこに書かれているままにドキュメントを作れるのは便利なので、IT領域では開発でもインフラでも広く利用されているはずです。IT以外でもnoteやブログなどのページ作成に利用しているかたも多いかもしれません。

本やドキュメントを書くのにマークダウンを使い、バージョン管理しながらどんどんとアップデートしていきます。一人でやる場合もあれば複数人での共同作業の場合もあるでしょう。チームでやるなら、共同作業しながら「誰がなにを変更したか」をすぐに把握したいところです。私が本を書く場合はGitHubのプライベートリポジトリで分担作業をしています。私が本を書き、編集者が直し、ドキュメントをビルドして体裁を整える。チームとして動けています。仕事でドキュメントを書くことも多いですが、マークダウン系の場合にやることはたいして変わりません。

ただ、そこで使う作画イラストはどうでしょうか。たとえば以下のようなものです。

書籍で使われたイラスト

これは少し前に書いた私の本(DockerとKubernetes)からの引用ですが、当時はパワーポイントやGoogleスライドなどを使って作図していました。それのスクリーンショットを撮って使うかたちです。上記のイラストはそれをベースにプロのかたに再作成してもらったものです。

テキスト形式のマークダウンとは異なりパワーポイントに書いた図は人間の手作業での作図そのものです。Git や GitHubなどでの管理と相性が非常に悪いです。作図も属人化しますし、本のような規模が大きいものだとスタイルがちぐはぐになったりして手直しに苦労することも多いです。

Drawlibはそういった状況を改善するために開発したツールです。非常に短いPythonのコードで作図をし、それがそのままイメージデータ(PNGなど)になります。以下に利用イメージを記載します。

コードがそのままイラストになる (このイラストもdrawlibで書いている)

上記のイラストはDrawlibの利用イメージです。円をどこにどれぐらいの大きさで、どういう見た目(本来はCSSみたいに事前定義したものを適用するが、ここではベタ書き)で書くかを定義し、それがそのままイラストになっています。まさに「Illustration as Code(コードでイラストを定義する)」ですね。これがやりたくて、自分で手を動かして「あれも必要、これも必要」と欲張り、その結果としてそこそこ多機能なライブラリを開発することになりました。

以下からは本家ドキュメントの概要とインストール、クイックスタートを日本語訳したものを紹介します。機械翻訳をちょっとだけ手直ししたものとなりますので、ぶっきらぼうな日本語はご容赦ください。

Drawlibの概要

豊富な描画アイテム

Drawlibは、様々なスタイルの描画アイテムを提供します。以下は、主要な描画アイテムを様々なスタイルで描いたサンプル画像です。

アイコン、イメージ、線、図形、テキストに対応。スタイル適用も可能
  • アイコン (Icon): 5つのスタイル (thin, light, regular, bold, fill) で 1500 種類以上のパターンを用意.

  • 画像 (Image): 色やモザイク加工などの効果を簡単に適用できる.

  • 線 (Line): 直線や曲線などさまざまなパターンに対応.

  • 形状 (Shape): 約 20 種類のパターン.

  • テキスト (Text): さまざまなサイズ、フォント、ウェイト (light, regular, bold) をサポートし、主要な地域言語に対応.

Drawlibは、Microsoft PowerPointの機能を参考に開発されました。可能な限り多くの一般的な機能を実装することを目指しています。Drawlibは、基本的な描画機能に加え、コードハイライト機能など、スマートアートとして実装された高度 な機能も備えています。

コンセプト: スタイルをコンテンツに適用

円のパラメータには、座標、サイズ、色などが含まれます。多くの描画ツールでは、これらのパラメータを一律に扱います。しかし、Drawlibでは、パラメータを以下の 2 つに分割します。

  • コンテンツ: 座標、サイズ、角度など、描画アイテムの種類を定義する情報です。(HTMLに相当)

  • スタイル: 色、線の太さ、フォントなどの要素です。(CSSに相当)

HTML/CSS に慣れ親しんでいる方にとって、コンテンツは HTML に、スタイルは CSS に相当します。CSS でスタイルを定義し、HTML で参照することを推奨するように、Drawlib ではスタイルを別途定義し、イラストレーションコードで参照することを奨励します。
以下に概念図を記載します。

HTMLにCSSを適用するように、コンテンツにスタイルを適用

確かに、芸術的な観点からは、コンテンツとスタイルの融合が不可欠です。しかし、芸術的な外観を必要としないイラストの場合、コンテンツはスタイルよりも重要です。線の太さや色は、線が引かれている場所よりも重要ではありません。

コンテンツとスタイルを分離することで、最初に重要なコンテンツに焦点を当てることができます。コンテンツが作成されると、コンテンツ外のスタイルを変更することで外観を変更できます。さらに、コンテンツが分離されている場合は、1 つのスタイルを複数のコンテンツ アイテムに適用できます。イラストの一貫性を維持するために、多くのアイテムに同じスタイルを使用することが重要です。

整理された API

Drawlib は、イラストレーション作成を 簡素化することを目指しています。アイコン、画像、線、形状、テキストなどの描画のための 幅広い API を提供し、様々なスタイルにも対応しています。

描画を開始するために細部をすべて覚える必要はありません。ライブラリのデザインを理解することで、効率的なコードを書くことができます。IDE は、クラス、関数、およびそのオプションにすばやくアクセスできるようにすることで支援してくれます。

Drawlib は以下の API を中心として構成されております。

  • 基本クラスと関数: save() や config() など、キャンバス操作の基本的なメソッドが含まれます.

  • 描画関数: circle() や line() などが挙げられます.

  • スタイルクラス: ShapeStyle や LineStyle などのクラスは、要素の視覚的な外観を定義します.

  • テーマとスタイルアクセサモジュール (dtheme): このモジュールは、テーマの管理と、ドローイング間でのスタイルへのアクセスを容易にします.

  • 高度なクラスと関数: これらのコンポーネントは、前述の API を内部的に利用して、拡張機能を提供します.

以下にライブラリアーキテクチャ図を記載します。

一貫された API 設計

上記のイメージは、Drawlib のコアコンポーネントを 5 つのセクションに分類し、各 API を表しています。Drawlib は一見複雑に見えますが、関数引数とスタイルクラスの一貫性が保たれています。

ライブラリの基本概念を理解すれば、関数と引数の結果を直感的に予測できるようになります。

Drawlib: ドキュメントや書籍に適したライブラリ

現代のソフトウェア開発において、バージョン管理はコードだけでなくドキュメントにも広がりつつあります。すべては Git を通してシームレスに管理されます。

私も技術文書やリファレンスマニュアルを作成する際は、VSCode と Markdown を使用していますが、以前はイラストには PowerPoint に頼っていました。しかしながら、このアプローチでは、バージョン管理されているドキュメントのイメージとの互換性がありません。

そこで登場するのが Drawlib です。 Drawlib を使えば、テキストのドキュメントだけでなく、イラストレーションコードも Git を通して管理することができるようになります。これにより、スクリプトや CI/CD パイプラインを使用したビルドタスクの自動化が容易になります。

Drawlib を使ったドキュメントビルドの流れ。自動化しやすい

Drawlib をワークフローに導入することは 直感的で、マークダウンドキュメントの管理と大きな違いはありません。マークダウンや類似のフォーマットを使用してドキュメントを作成しているなら、Drawlib を簡単に採用できます。

ちなみに、Drawlib で書くようなドキュメントのイラストのコードはあまりクリティカルでなくクオリティが低くても構わないです。そのため、新卒のようななにも分かっていない新人プログラマに Python に習熟させるために書いてもらい、それをどう改善すべきかレビューしてスキル向上させるという使いかたもありなんじゃないかなと思います。コードでイラストを書くのは半分遊びみたいなものなので、結構気軽にコードを書けるよい題材なんじゃないかと思います。大事なサービスにゴミコードを書かれるよりずっといいです(笑)。

Pythonのコード補完やヘルプなどが効く

以下は私の VSCode 上での drawlib のサンプルコードのスクリーンショットです。ここではShapeStyle にマウスフォーカスがあたっていますが、ヘルプとしてどのような引数をとるかだけではなく下部にドキュメントが表示されています(スクロールダウンするともっと細かい情報がでてきます)。

VSCode 上でのヘルプと補完

イラストを書くために特別に用意された環境を用意する必要はなく、使い慣れた開発環境の Python にパッケージを1つインストールするだけで全てが整います。雑にこういう関数があったと覚えていれば、あとはヘルプと補完を参照しながら必要なパラメーターを設定していけばよいだけです。イラストを書くためにガッツリと新しいことを覚えるのは極力避けたいですよね。

インストール

Drawlib は、以下のコマンドでインストールできます。システム上で Python 3 と pip 3 を利用している場合は置き換えてください。

$ pip install drawlib

Drawlib のインストールが成功したかどうかは、以下のコマンドで確認できます。

$ python -m drawlib --version
software=0.1.24
api=0.1.24

$ drawlib --version
software=0.1.24
api=0.1.24

Drawlib パッケージのインストールには、drawlib コマンドも含まれます。このコマンドは、大量の画像を生成する際に便利です。詳細についてはドキュメントを参照ください。

Windows限定トピックですが、もし以下のようなエラーが出たら matplotlib のインストールに失敗していますので、追加でpipコマンドにて msvc-runtime をいれてください。

ImportError: DLL load failed while importing _cext: The specified module could not be found

pip install msvc-runtime

本家ドキュメントには仮想環境(venv)への導入や、 IDE の設定あたりのトピックも書いてあります。

Quick Start

Drawlibでの作画手順の概要

Drawlib を使用した描画は、おおまかに以下の手順で行います。

  1. Drawlib ライブラリのインポート: まず、Python 環境に Drawlib ライブラリをインポートします。

  2. (オプション) スタイルコードとユーティリティのインポート: 必要に応じて、カスタムスタイル定義やユーティリティ関数がある場合はインポートします

  3. (オプション) キャンバスの設定: キャンバスのサイズと解像度を指定して設定します

  4. 要素の描画: Drawlib の API を使って、アイコン、画像、線、形状、テキストなどをキャンバスに描画します

  5. キャンバスの保存: 描画が完了したら、キャンバスを画像ファイルとして保存します

詳細な説明は、以降のドキュメントで解説しますが、各ステップを簡単な例と共に概観してみましょう。

from drawlib.apis import *

config(width=100, height=100)

line((10, 10), (90, 90))
circle((25, 75), radius=20)
image((75, 25), width=30, image="python.png")
text((75, 5), "Hello drawlib!")

save()

このコードを python コマンドで実行すると以下のような画像が生成されます。

コードから生成された画像

Drawlib の基本的な使い方を理解しましたね。
では、これから Drawlib の機能を 1 つずつ詳しく見ていきましょう。

Drawlibライブラリのインポート

Drawlib は純粋な Python ライブラリであり、インストール後、他のライブラリと同様にインポートして使用できます。多くのライブラリは API を複数のパッケージやモジュールに分散させていますが、 Drawlib は公開 API をすべて 「drawlib.apis」 パッケージ内にまとめているので、以下のようにワイルドカード * を使ってすべての API をまとめてインポートできます。

from drawlib.apis import *

一般的に、Python のコーディング規約である PEP 8 では、コードのわかりやすさと保守性を保つためにワイルドカードインポートを推奨していません。ただし、Drawlibは簡潔なスクリプトであることがほとんどですので、ワイルドカードインポートは簡潔でコードの見通しが良くなります。このインポート方法では、インストールされている Drawlib で利用可能な最新の API にすべてアクセスできます。逆にいうと、これ以外のインポートはされないことを前提にライブラリ開発をしています。

Drawlib の古いバージョンで使用されていた API を新しいライブラリで使いたい場合は、以下のようにバージョンを指定してインポートすることが可能です。

from drawlib.v0_2.apis import *

ここでは v0_2 はバージョン 0.2.* を意味します。

キャンバスのサイズと DPI の設定

Drawlib ライブラリをインポートしたら、描画を開始できます。しかし、キャンバスサイズなどのパラメーターを定義するために、config() 関数を使用してキャンバスを設定することをお勧めします。

たとえば、次のようなコードは、キャンバスの幅を 100 ユニット、高さを 100 ユニットに設定します。

config(width=100, height=100)

これらの単位は、ピクセル値ではなく、キャンバス内での座標を表します。幅と高さの両方を 100 に設定すると、X 軸と Y 軸の両方の座標範囲は 0 から 100 になります。幅と高さを両方とも 10 に設定した場合、x = 20 を指定すると範囲外になります。Drawlib はこの場合エラーを発生させませんが、アイテムが期待通りにレンダリングされない可能性があります。デフォルトでは、幅と高さの両方が 100 ユニットに設定されています。

config(width=200, height=100) を使用してキャンバスを設定すると、各アイテムの座標系を維持したまま、より幅広のキャンバスが生成されます。以下に出力画像をご覧ください。

幅200, 高さ100で横長に

図のどこになにを配置するかを考えるには、パッと1/2, 1/3, 1/4 などの数字を計算できると便利です。そういう点で「50や100」は「1920や1080」よりもおすすめです。面倒くさいので私はもっぱら 100, 50 か 100,100 を使っています。

高解像度の画像を作成するには、DPI (Dots Per Inch) を調整する必要があります。

config(dpi=200)

Drawlib はキャンバスの幅を常に 10 インチに維持します。そのため、前述の例からの座標ベースの幅の変更は出力に影響しません

提示された例では、「10 インチ x 200 DPI」のキャンバスサイズで、画像の幅は 2000 ピクセルになります。DPI を 400 に増やすと、画像の幅は 2 倍の 4000 ピクセルになります。ただし、高解像度の画像を生成すると、より多くの時間とディスク容量が消費されます。上限値は設定されていませんが、DPI 1000 は過剰な場合があります。

高解像度のイラストなどをなかに取り込む場合は、DPIを増やすことをおすすめします。

キャンバスグリッドの設定

Drawlib の config() 関数は、グリッド機能など、いくつかの高度なオプションを提供します。グリッド機能は、キャンバス上のアイテムの座標を把握しながら、すばやく配置するのに特に便利です。

config(width=100, height=100, grid=True)

grid=True を有効にすると、通常の画像生成にプラスして、グリッド付きのがオズも生成されるようになります。したがって、グリッドのない画像を取得するために grid=True オプションを削除する必要はありません。グリッド画像のみが必要な場合は、代わりに grid_only=True を使用できます。デフォルトでは、グリッドとグリッド_オンリーはどちらも False に設定されています。

これらの調整の効果は、次のファイルで示されています。

グリッドなしの出力 (グリッドありとともに別に出力される)

And

グリッドありの出力

コードファイル image_config2.py は、2 つのファイル image_config2.png と image_config2_grid.png を生成します。

  • グリッドなしの画像ファイルは通常のファイル名です。

  • グリッド付きの画像ファイルは、末尾に「_grid」が付きます。

座標系と配置

Drawlib は、描画機能をアイコン、イメージ、ライン、シェイプ、テキストの 5 つの主要カテゴリに分類しています。これらのカテゴリを詳しく調べる前に、すべての描画オブジェクトが依存している Drawlib の座標系を理解することが不可欠です

Drawlib の描画オブジェクトはそれぞれ、xy 座標を使用して配置されます。
これらの xy 座標の配置は、水平方向 (halign) と垂直方向 (valign) の配置設定に依存します。一般的な配置オプションとしては以下が挙げられます。

水平方向の配置: halign

  • left

  • center

  • right

垂直方向の配置: valign

  • bottom

  • center

  • top

例を通して配置オプションを確認します。

from drawlib.apis import *

config(width=100, height=50, grid_only=True)

circle(
    xy=(25, 25),
    radius=10,
    style=ShapeStyle(halign="center", valign="center"),
)
circle(
    xy=(25, 25),
    radius=1,
    style=ShapeStyle(fcolor=Colors.Red, lcolor=Colors.Red),
)
text((25, 10), "Align center,center", style=TextStyle(color=Colors.Red))

circle(
    xy=(75, 25),
    radius=10,
    style=ShapeStyle(halign="left", valign="bottom"),
)
circle(
    xy=(75, 25),
    radius=1,
    style=ShapeStyle(fcolor=Colors.Red, lcolor=Colors.Red),
)
text((75, 10), "Align left,bottom", style=TextStyle(color=Colors.Red))

save()イメージ内の左側にある円は、指定どおり xy 座標が "center, center" に配置されています。一方、右側にある円は xy 座標が "left, bottom" に配置されています。
水平、垂直アラインの設定

Drawlib は、矩形などのシェイプの配置をデフォルトで "center, center" に設定します。これは、他の多くの "left, bottom" をデフォルトとする描画システムとは異なります。Drawlib が "center, center" を選択しているのは、さまざまなサイズのアイテムを縦横両方向に揃えるプロセスを簡素化するためです

デフォルト設定ではありますが、"center, center" よりも "left, bottom" の配置が望ましい場合もあります。そのような場合は、目的の配置設定を持つカスタムスタイルオブジェクトを定義し、特定のアイテムに選択的に適用することをお勧めします。Drawlib では、スタイルAをスタイルBで簡単に一部を上書きできる merge() 機能を持っているのでスタイルの部分アップデートは簡単です。詳細はドキュメントをご参照ください。

アイコンを描く

アイコンの描画は、後述する画像イメージを描くことと似ています。しかし、多くのツールではアイコンには png/jpeg 画像を利用することが多いのに対し、Drawlib のアイコンは「フォントアイコン(Font Icon)」です。フォントアイコンに慣れていない場合は、最初に Font Awesome をチェックしてどのようなものか把握することをお勧めします。

Drawlib は、アイコンを描くための 2 つの方法を提供します。

  • icon() 関数を使用する

  • icon_phosphor モジュールとその機能を使用する

独自のフォントアイコンファイルを使用してアイコンを描画したい場合は、icon() 関数を使用できます。たとえば、Font Awesome (デフォルトではサポートされていません) を使用する場合は、この関数にファイルと描画パラメーターを指定する必要があります。

icon_phosphor モジュールは、埋め込みフォントアイコンと、それらをドローイングするための関数を提供します。Drawlib では誰でもアクセスできるということから、イラストレーションをコードとして容易にする利便性が高い Phosphor (https://phosphoricons.com) を活用しています。

icon_phosphor を使用した例を次に示します。

from drawlib.apis import *

config(width=100, height=60, grid=True)

icon_phosphor.airplane((25, 30), width=20)
icon_phosphor.coffee(
    xy=(75, 30),
    width=20,
    angle=45,
    style=IconStyle(color=Colors.Red, style="fill"),
)

save()

このコードを実行すると、次の出力画像が生成されます。

アイコンの出力

示すように、関数の名前が描画するアイコンを決定し、IconStyle を調整して色、スタイル、およびその他の属性を変更できます。icon() 関数の詳細な使用方法については、アイコンのドキュメントを参照してください。

画像イメージを描く

image() 関数は、指定された xy 座標と幅で、提供された画像をキャンバス上に描画します。高さは、元の画像のアスペクト比を維持するために、幅に基づいて自動的に計算されます。アスペクト比を調整する必要がある場合は、後で説明する Dimage クラスを使用できます。

次に、image() 関数を使用した例を示します。

from drawlib.apis import *

config(width=100, height=50, grid=True)

image(xy=(25, 25), width=20, image="python.png")
image(
    xy=(75, 25),
    width=20,
    angle=45,
    image="python.png",
    style=ImageStyle(lwidth=1),
)

save()

Python コマンドを使用してこのコードを実行すると、画像が生成されます。

画像イメージの出力

ご覧のように、角度を指定したり、`ImageStyle` を使用して配置と境界線を管理したりすることができます。

画像自体を修正したい場合は、Dimage クラスを使用することを検討してください。このクラスは、画像操作のためのさまざまなメソッドを提供します。

from drawlib.apis import *

config(width=100, height=50, grid=True)

image(xy=(25, 25), width=20, image="python.png")
dimg = Dimage("python.png").mirror().sepia()
image(xy=(75, 25), width=20, image=dimg)

save()

Dimage クラスは、文字列のようなオブジェクトです。
効果を適用するためのメソッドは、画像自体を変更するのではなく、新しい画像オブジェクトを作成します。したがって、メソッドチェーン(メソッドにメソッドをつなげることを繰り返す)を使用して、ミラーリング (水平反転) やセピア (色の変更) などの操作を連続して適用できます。

Dimageを使った画像への効果適用(左右反転とセピア化)

image() 関数と Dimage クラスはどちらも、人気の Pillow ライブラリからの画像を受け取ることができます。高度な画像処理を実行したい場合は、Pillow を使用してから、image() 関数と Dimage クラスを使用して処理済み画像を扱うことをお勧めします

オリジナル画像の解像度が高い場合は、きれいな出力を得るために DPI を増やすことをおすすめします。

線を描く

Drawlib は、線を描くための line() 関数を備えていますが、他にもさまざまな線描画オプションを提供しています。

  • line

  • line_curved

  • line_bezier1

  • line_bezier2

  • lines

  • lines_curved

  • lines_bezier

「line」で始まる関数は、点 xy1 から点 xy2 まで線を引くように設計されており、「lines」で始まる関数は、複数の点を通過する線のために設計されています。

いくつかの線の種類を見てみましょう。

from drawlib.apis import *

config(width=100, height=50, grid=True)

line((20, 7), (80, 7))
line_curved((20, 20), (80, 20), bend=0.2)
line_curved((20, 30), (80, 30), bend=-0.2)
lines([(20, 40), (30, 45), (70, 45), (80, 40)])

save()

`line_curved()` 関数は、xy1 から xy2 まで線を引きますが、bend パラメータを使用すると、曲線を作成できます。
bend の値が 0.2 の場合は、直線より 1.2 倍長い曲線を意味し、-0.2 の場合は反対方向にカーブを作成します。

直線、曲線の描画

ベジェ曲線関数はもう少し複雑です。詳細については、線のドキュメントを参照してください。ただし、複雑な曲線を制御するのに非常に役立ちます。

線のスタイルという観点からは、以下の 2 つのカテゴリがあります。

  • 矢印

  • 見栄えのスタイル: 色、太さ、線のスタイル (実線、破線など) など

矢印は論理的な意味を持つため、HTML の等価物として、関数引数 arrowhead で指定します。矢印の方向を変えるためにスタイル変更しないといけないのも大変ですし。一方で、見栄えのスタイルはあまり論理的な意味を持たないため、 スタイルクラス LineStyle として指定します

スタイル設定の例を次に示します。

from drawlib.apis import *

config(width=100, height=50, grid=True)

line((20, 7), (80, 7))
line(
    (20, 16),
    (80, 16),
    style=LineStyle(style="dashed", width=5, color=Colors.Red),
)
line((20, 25), (80, 25), arrowhead="->")
line((20, 34), (80, 34), arrowhead="<->")
line((20, 43), (80, 43), arrowhead="<-", style=LineStyle(ahscale=50, style="dashdot", ahfill=True))

save()

このコードを実行すると、次のような画像が生成されます。

線へのスタイル適用

ご覧のように、LineStyle クラスを使用して線の幅、色、スタイルなどを設定できます。矢印のスタイルは、関数の引数で直接指定しています。本来はスタイルの領域なのでしょうが、線の太さだけは利便性のために関数の引数でも定義できるようになっています。

図形を描く

Drawlib のキーワードである「Shape」には、円、矩形などさまざまな図形が含まれます。
既に circle() 関数に慣れ親しんでいますが、Drawlib では、他にもさまざまな図形を描くための関数が導入されています。

  • arrow()

  • arc()

  • bubblespeech()

  • chevron()

  • circle()

  • donuts()

  • ellipse()

  • fan()

  • parallelogram()

  • polygon()

  • rectangle()

  • regularpolygon()

  • rhombus()

  • shape()

  • star()

  • trapezoid()

  • triangle()

  • wedge()

これらの関数のほとんどは、円型と矩形型の 2 つのカテゴリのいずれかに分類されます。円タイプの図形は、xy 座標と半径などのパラメーターで定義され、矩形タイプの図形は、xy 座標、幅、高さなどのパラメーターで定義されます。例外は、arrow(), polygon(), shape() です。

このクイックスタートガイドでは詳細については説明しませんが、shape() 関数は、特にカスタムシェイプオブジェクトを作成するのに便利な機能です。これを使用すると、アイテムを指定された xy 座標に配置したり、角度を調整したりするなどのタスクが自動的に処理されます

それでは、円形の「star()」関数と矩形の「rectangle()」関数の 2 つの例を見てみましょう。

from drawlib.apis import *

config(width=100, height=50, grid=True)

star((25, 25), num_vertex=5, radius_ext=20, radius_int=7.5)
rectangle((75, 25), width=30, height=20, r=3, angle=45)

save()

このコードを実行すると、次の画像が生成されます。

星と四角(角を丸めてある)の出力

円タイプの図形は半径で定義され、矩形タイプの図形は幅と高さで定義されます。
デフォルトでは、xy 座標は図形の中心を示します。arrow() と polygon() を除くすべての関数は、角度パラメーターを受け取ることができます

図形は、2 種類のスタイルを使用してスタイル設定することもできます。

  • ShapeStyle: 線の幅、色、塗りつぶし色などの基本的な図形のスタイル設定に使用します

  • ShapeTextStyle: 図形の中のテキストのスタイル設定に使用します

それでは、スタイル設定の例を見てみましょう。

from drawlib.apis import *

config(width=100, height=50, grid=True)

rectangle(
    (25, 25),
    width=30,
    height=20,
    angle=45,
    text="Hello!",
    style=ShapeStyle(lstyle="dashed", lwidth=5, lcolor=Colors.Red, fcolor=Colors.Transparent),
)
rectangle(
    (75, 25),
    width=30,
    height=20,
    angle=45,
    text="Hello!",
    textstyle=ShapeTextStyle(color=Colors.White, size=20, xy_shift=(-10, 0), angle=0),
)

save()

このコードを実行すると、次の出力が生成されます。

図形へのスタイル適用

左の例では、ShapeStyle を設定して矩形にスタイルを追加します。
「l」 で始まるパラメーターは、「line」 の省略形で、線のスタイル設定を表します。たとえば、lwidth は線の幅, lcolor は線の色の設定です。fcolor は塗りつぶし色を設定します。

図形の線のオプションは、LineStyle のオプションとほぼ同じです。シェイプラインが必要ない場合は、lwidth=0 を設定し、塗りつぶし色が必要ない場合は、fcolor=Colors.Transparent を設定すれば中が透明になります。

テキストの角度は、左の図のようにデフォルトで図形の角度に追従することに注意してください。右の例では、矩形の中央のテキストに対して ShapeTextStyle を設定しています。color、size、fontなどのパラメーターは、TextStyle と同様です。ただし、ShapeTextStyle には xy_shift や angle などの追加オプションが含まれています。

xy_shift を指定すると、テキストの中心ポイントを移動できます。xy 座標はグローバル座標ではなく、図形に対する相対座標であり、図形の角度を考慮に入れています。したがって、(-10, 0) を指定すると、中心点は左だけでなく、図形が 45 度傾いているため下にも移動します。

テキストの描画

text() 関数はキャンバス上にテキストを描画するために使用されます。この関数には、xy 座標、テキストメッセージ、およびオプションの角度を指定する必要があります。他のすべてのテキストパラメーターは「TextStyle」内で定義されますが、例外としてフォントサイズだけは TextStyle に加えて text() 関数からも直接設定できるようにしています。

以前説明した ShapeTextStyle とは異なり、TextStyle には xy_shift や angle などの機能はありません。これらは text() の引数として直接設定できるためです。ただし、TextStyle には ShapeTextStyle にはないオプション、テキストの背景オプションが含まれています

いくつかのコード例を見てみましょう。

from drawlib.apis import *

config(width=100, height=50)

text((50, 7), "Hello drawlib. こんにちは。")
text(
    (50, 16),
    "Hello drawlib. こんにちは。",
    angle=10,
    style=TextStyle(font=FontRoboto.ROBOTO_REGULAR),
)
text(
    (50, 25),
    "Hello drawlib.",
    style=TextStyle(font=FontFile("avenger/regular.ttf")),
)
text(
    (50, 34),
    "Hello drawlib. こんにちは。",
    style=TextStyle(color=Colors.Red, size=24),
)
text(
    (50, 43),
    "Hello drawlib. こんにちは。",
    style=TextStyle(color=Colors.White, bgfcolor=Colors.Black),
)
save()

このコードを実行すると、次の画像が生成されます。

テキスト出力とスタイルの適用

この例では、いくつかの TextStyle パラメーターを設定しました。テスト用に日本語テキストを使用しました。ご覧のように、Roboto フォントは日本語を正しくレンダリングできません。非アルファベット文字を使用する場合は注意してください。デフォルトのフォントはアルファベットに加えて、日本語、中国語、韓国語をサポートしています。

Drawlib は、Font-Something クラスで分類されたさまざまな組み込みフォントをサポートしています。たとえば、日本語フォントは `FontJapanese` 内で定義されています。日本語だけでなくアラビア語やタイ語といった他のローカル言語もサポートされています。

フォントは、最初に使用されたときにインターネットからダウンロードされ、その後はマシンの drawlib ライブラリ内にローカルにキャッシュされます。以前に使用されたフォントはダウンロードされません。インターネットが使えない環境で新しいフォントを使用して text() を呼び出すと、ダウンロードエラーが発生する可能性がありますので注意してください。

下から3 番目の珍しいフォントの例では、ローカルに準備したフォントファイルを使用しました。使用したいフォントが Drawlib にない場合は、FontFile クラスを使用してスタイルのフォントパラメーターに渡すことができます。4 番目と 5 番目の例では、色、サイズ、背景などのテキストパラメーターを設定しました。

これらの設定は特に複雑なものではありませんが、キャンバスの幅と高さが変更されてもフォントサイズは一定のままになることに注意してください。キャンバスのサイズを 2 倍にしても、フォントのテキスト サイズが半分になるわけではなく、元のサイズと同じままです。

公式テーマを使用する

Drawlib では、ShapeStyle や TextStyleなどのスタイルクラスオブジェクトを使用して、描画アイテムのスタイルを定義できます。ただし、アイテムごとにスタイルを指定するのは煩雑になりやすく、一貫性が失われるおそれがあります。これを解決するために、Drawlib はテーマとスタイル機能を提供しており、テーマを選択してそのスタイルを名前で簡単に適用することができます

テーマを使用したスタイリングの例。style 引数はテキスト値を取ることに注意してください。

from drawlib.apis import *

config(width=100, height=50)
x1 = 12
x2 = 34
x3 = 62
x4 = 88
line_y = 40
line_length = 7
circle_y = 25
text_y = 10

# blue style
line((x1 - line_length, line_y), (x1 + line_length, line_y), style="blue")
circle((x1, circle_y), radius=8, style="blue")
text((x1, text_y), text="blue", style="blue")

# blue solid style
line((x2 - line_length, line_y), (x2 + line_length, line_y), style="blue_solid")
circle((x2, circle_y), radius=8, style="blue_solid")
text((x2, text_y), text='style="blue_solid"', style="blue")

# green dashed style
line((x3 - line_length, line_y), (x3 + line_length, line_y), style="green_dashed_bold")
circle((x3, circle_y), radius=8, style="green_dashed_bold")
text((x3, text_y), text='style="green_dashed_bold"', style="green_bold")

# red flat style
line((x4 - line_length, line_y), (x4 + line_length, line_y), style="red")
circle((x4, circle_y), radius=8, style="red_flat")
text((x4, text_y), text='style="red_flat"', style="red")

save()

スタイルには、次の構文があります: <color>_<type>_<weight>

<color> 部分は色の名前で、<type> は線の種類で、<weight> は線の太さを表します。<color> および <type> と <weight> がデフォルトの場合は、スタイル名に表示されません。

このコードを実行すると、次の画像が生成されます

コンテントへの事前定義スタイルの適用

Drawlib はいくつかの公式テーマを提供しています。

  • default : 赤青緑黒白が使える

  • essentials : たくさんの色が使える。上級者は今のところ、これがおすすめ

  • monochrome : モノクロ用。白黒印刷の本などによい

先に説明した色とスタイルのルールは上記の3つのテーマにすべて共通しています。

使用可能なスタイルクラスごとに使用可能なスタイル名を確認するには、dtheme.print_style_table() を使用します。多くのスタイル名は少数のスタイルクラスのみをサポートすることに注意してください。なぜなら、「`flat` (境界線なし塗りつぶし)」スタイルは線とテキストに適さないからです。

以下は、テーマで使用可能なスタイルを印刷する方法です。

from drawlib.apis import *

dtheme.apply_official_theme("default")
dtheme.print_style_table()


# +----------------+---+-------+------+------+-------+-------------+------------+--------+--------------+-------------+
# | class \ name   |   | light | bold | flat | solid | solid_light | solid_bold | dashed | dashed_light | dashed_bold |
# +----------------+---+-------+------+------+-------+-------------+------------+--------+--------------+-------------+
# | IconStyle      | x | x     | x    | x    |       |             |            |        |              |             |
# | ImageStyle     | x | x     | x    | x    | x     | x           | x          | x      | x            | x           |
# | LineStyle      | x | x     | x    |      | x     | x           | x          | x      | x            | x           |
# | ShapeStyle     | x | x     | x    | x    | x     | x           | x          | x      | x            | x           |
# | ShapeTextStyle | x | x     | x    |      |       |             |            |        |              |             |
# | TextStyle      | x | x     | x    |      |       |             |            |        |              |             |
# +----------------+---+-------+------+------+-------+-------------+------------+--------+--------------+-------------+

# +----------------+-----+-----------+----------+----------+-----------+-----------------+----------------+------------+------------------+-----------------+
# | class \ name   | red | red_light | red_bold | red_flat | red_solid | red_solid_light | red_solid_bold | red_dashed | red_dashed_light | red_dashed_bold |
# +----------------+-----+-----------+----------+----------+-----------+-----------------+----------------+------------+------------------+-----------------+
# | IconStyle      | x   | x         | x        | x        |           |                 |                |            |                  |                 |
# | ImageStyle     | x   | x         | x        | x        | x         | x               | x              | x          | x                | x               |
# | LineStyle      | x   | x         | x        |          | x         | x               | x              | x          | x                | x               |
# | ShapeStyle     | x   | x         | x        | x        | x         | x               | x              | x          | x                | x               |
# | ShapeTextStyle | x   | x         | x        |          |           |                 |                |            |                  |                 |
# | TextStyle      | x   | x         | x        |          |           |                 |                |            |                  |                 |
# +----------------+-----+-----------+----------+----------+-----------+-----------------+----------------+------------+------------------+-----------------+

# +----------------+-------+-------------+------------+------------+-------------+-------------------+------------------+--------------+--------------------+-------------------+
# | class \ name   | green | green_light | green_bold | green_flat | green_solid | green_solid_light | green_solid_bold | green_dashed | green_dashed_light | green_dashed_bold |
# +----------------+-------+-------------+------------+------------+-------------+-------------------+------------------+--------------+--------------------+-------------------+
# | IconStyle      | x     | x           | x          | x          |             |                   |                  |              |                    |                   |
# | ImageStyle     | x     | x           | x          | x          | x           | x                 | x                | x            | x                  | x                 |
# | LineStyle      | x     | x           | x          |            | x           | x                 | x                | x            | x                  | x                 |
# | ShapeStyle     | x     | x           | x          | x          | x           | x                 | x                | x            | x                  | x                 |
# | ShapeTextStyle | x     | x           | x          |            |             |                   |                  |              |                    |                   |
# | TextStyle      | x     | x           | x          |            |             |                   |                  |              |                    |                   |
# +----------------+-------+-------------+------------+------------+-------------+-------------------+------------------+--------------+--------------------+-------------------+

# +----------------+------+------------+-----------+-----------+------------+------------------+-----------------+-------------+-------------------+------------------+
# | class \ name   | blue | blue_light | blue_bold | blue_flat | blue_solid | blue_solid_light | blue_solid_bold | blue_dashed | blue_dashed_light | blue_dashed_bold |
# +----------------+------+------------+-----------+-----------+------------+------------------+-----------------+-------------+-------------------+------------------+
# | IconStyle      | x    | x          | x         | x         |            |                  |                 |             |                   |                  |
# | ImageStyle     | x    | x          | x         | x         | x          | x                | x               | x           | x                 | x                |
# | LineStyle      | x    | x          | x         |           | x          | x                | x               | x           | x                 | x                |
# | ShapeStyle     | x    | x          | x         | x         | x          | x                | x               | x           | x                 | x                |
# | ShapeTextStyle | x    | x          | x         |           |            |                  |                 |             |                   |                  |
# | TextStyle      | x    | x          | x         |           |            |                  |                 |             |                   |                  |
# +----------------+------+------------+-----------+-----------+------------+------------------+-----------------+-------------+-------------------+------------------+

# +----------------+-------+-------------+------------+------------+-------------+-------------------+------------------+--------------+--------------------+-------------------+
# | class \ name   | black | black_light | black_bold | black_flat | black_solid | black_solid_light | black_solid_bold | black_dashed | black_dashed_light | black_dashed_bold |
# +----------------+-------+-------------+------------+------------+-------------+-------------------+------------------+--------------+--------------------+-------------------+
# | IconStyle      | x     | x           | x          | x          |             |                   |                  |              |                    |                   |
# | ImageStyle     | x     | x           | x          | x          | x           | x                 | x                | x            | x                  | x                 |
# | LineStyle      | x     | x           | x          |            | x           | x                 | x                | x            | x                  | x                 |
# | ShapeStyle     | x     | x           | x          | x          | x           | x                 | x                | x            | x                  | x                 |
# | ShapeTextStyle | x     | x           | x          |            |             |                   |                  |              |                    |                   |
# | TextStyle      | x     | x           | x          |            |             |                   |                  |              |                    |                   |
# +----------------+-------+-------------+------------+------------+-------------+-------------------+------------------+--------------+--------------------+-------------------+

# +----------------+-------+-------------+------------+------------+-------------+-------------------+------------------+--------------+--------------------+-------------------+
# | class \ name   | white | white_light | white_bold | white_flat | white_solid | white_solid_light | white_solid_bold | white_dashed | white_dashed_light | white_dashed_bold |
# +----------------+-------+-------------+------------+------------+-------------+-------------------+------------------+--------------+--------------------+-------------------+
# | IconStyle      | x     | x           | x          | x          |             |                   |                  |              |                    |                   |
# | ImageStyle     | x     | x           | x          | x          | x           | x                 | x                | x            | x                  | x                 |
# | LineStyle      | x     | x           | x          |            | x           | x                 | x                | x            | x                  | x                 |
# | ShapeStyle     | x     | x           | x          | x          | x           | x                 | x                | x            | x                  | x                 |
# | ShapeTextStyle | x     | x           | x          |            |             |                   |                  |              |                    |                   |
# | TextStyle      | x     | x           | x          |            |             |                   |                  |              |                    |                   |
# +----------------+-------+-------------+------------+------------+-------------+-------------------+------------------+--------------+--------------------+-------------------+

事前定義スタイルで通常は問題ないかと思いますが、必要あれば自分で既存スタイルをカスタマイズしたり、スクラッチで作成することも当然できます。話が長くなるのでドキュメントを参照ください。

長くなりましたが、この記事での drawlib の紹介はおわりです。公式ドキュメントには細かい解説があるので、気になるかたはのぞいてみてください。

ではでは。

この記事が気に入ったらサポートをしてみませんか?