Pythonで出版品質の幾何学図形を作成する 質問する

Pythonで出版品質の幾何学図形を作成する 質問する

私は数学者です。最近、有名な雑誌のパズルと問題コラムの編集者になりました。時々、問題や解答に添える図を作成する必要があります。これらの図は、主に 2D (場合によっては 3D) ユークリッド幾何学 (線、多角形、円、およびときどき楕円やその他の円錐曲線) に関するものです。目標は、Computer Modern (「TeX」) テキスト ラベルが付いた、非常に高品質 (印刷可能な) の図を作成することです。私の希望は、自然な操作 (たとえば、特定の点を通る特定の点に垂直な線を引く、特定の角度を二等分する、または図形 A を直線 L に反射させて新しい図形 A' を取得するなど) がライブラリに既に定義されているという意味でユークリッド幾何学を「理解する」比較的高レベルの Python ライブラリを見つけること (または作成を手伝うこと) です。もちろん、要素が定義された後で図形を作成できることは重要な目標です (たとえば、Encapsulated Postscript として)。

この問題に対する最適ではない解決策は複数(一部は部分的)知っていますが、シンプルで柔軟性のある解決策は知りません。説明しましょう。

  • 漸近線(類似/に基づくメタポスト) は、非常に複雑で高品質な図形を作成できますが、幾何学的構成についてはほとんど何も認識しません (かなり低レベルの言語です)。そのため、重要な構成には非常に長いスクリプトが必要になります。
  • ティックZパッケージ付きtkz-ユークリッドは高レベルで柔軟性があり、高品質の図も生成しますが、構文が非常に複雑なので、比較すると Python のシンプルさに涙するばかりです。(一部のプログラムは実際に TikZ にエクスポートします。下記を参照してください。)
  • 私が最もよく知っているダイナミックジオメトリプログラムジオジェブラには、多くの場合、図をエクスポートする機能 (EPS、TikZ など) がありますが、対話型で使用することを目的としています。場合によっては、厳密な仕様 (正確な辺の長さなど) に基づいた図が必要になることがあります。スクリプトでオブジェクトを定義すると、最終的にはより柔軟になります (ただし、それに応じて利便性は低下します)。
  • 2つのプログラム、ユークレイデスそしてGCLCは、私が探しているものに最も近いものです。図を生成します(EPS形式、GCLCはTikZにもエクスポートします)。Eukleidesは、すべてのオプションの中で最も美しく、最もシンプルな構文を持っています() ですが、C で書かれており (ソースは入手可能ですが、ライセンスについてはよくわかりません)、かなり制限がありカスタマイズできず、メンテナンスもされていません。GCLC はまだメンテナンスされていますが、クローズドソースであり、構文は Eukleides よりも大幅に劣っており、他にも不自然な癖があります。また、Mac OS では利用できません (私のラップトップは Mac です)。

Python には次の機能があります。

  • マトプロットライブラリは、非常に高品質の図(特に関数や数値データ)を生成しますが、幾何学的構成については知らないようです。
  • シンピージオメトリモジュールがあり、する幾何学的なオブジェクトと構造について知っており、すべて魅力的な Python 構文でアクセスできますが、図をエクスポートする機能 (または表示する機能さえも) がないようです。

最後に質問です。Python 構文を使用して幾何学的オブジェクトと構造を記述し、高品質の図 (主に印刷用、たとえば EPS) を生成できる、「Figures for Sympy/geometry」のようなライブラリはありますか?

そのような機能を備えたライブラリが存在しない場合は、ライブラリの作成を手伝うことを検討します (Sympy の拡張機能など)。ご指摘いただければ幸いです。

ベストアンサー1

matplotlobベクター画像を生成するには、ライブラリを使用してioベクター画像(SVG)に出力する方法があります。このアプローチ

私は個人的に、その Web ページにあるアプローチのコード (ベクトル ヒストグラムを生成する) を Python ファイルとして実行してみましたが、うまくいきました。

コード:

    import numpy as np
    import matplotlib.pyplot as plt
    import xml.etree.ElementTree as ET
    from io import BytesIO
    import json
    plt.rcParams['svg.fonttype'] = 'none'
    # Apparently, this `register_namespace` method is necessary to avoid garbling
    # the XML namespace with ns0.
    ET.register_namespace("", "http://www.w3.org/2000/svg")
    # Fixing random state for reproducibility
    np.random.seed(19680801)
    # --- Create histogram, legend and title ---
    plt.figure()
    r = np.random.randn(100)
    r1 = r + 1
    labels = ['Rabbits', 'Frogs']
    H = plt.hist([r, r1], label=labels)
    containers = H[-1]
    leg = plt.legend(frameon=False)
    plt.title("From a web browser, click on the legend\n"
            "marker to toggle the corresponding histogram.")
    # --- Add ids to the svg objects we'll modify
    hist_patches = {}
    for ic, c in enumerate(containers):
        hist_patches['hist_%d' % ic] = []
        for il, element in enumerate(c):
            element.set_gid('hist_%d_patch_%d' % (ic, il))
            hist_patches['hist_%d' % ic].append('hist_%d_patch_%d' % (ic, il))
    # Set ids for the legend patches
    for i, t in enumerate(leg.get_patches()):
        t.set_gid('leg_patch_%d' % i)
    # Set ids for the text patches
    for i, t in enumerate(leg.get_texts()):
        t.set_gid('leg_text_%d' % i)
    # Save SVG in a fake file object.
    f = BytesIO()
    plt.savefig(f, format="svg")
    # Create XML tree from the SVG file.
    tree, xmlid = ET.XMLID(f.getvalue())
    # --- Add interactivity ---
    # Add attributes to the patch objects.
    for i, t in enumerate(leg.get_patches()):
        el = xmlid['leg_patch_%d' % i]
        el.set('cursor', 'pointer')
        el.set('onclick', "toggle_hist(this)")
    # Add attributes to the text objects.
    for i, t in enumerate(leg.get_texts()):
        el = xmlid['leg_text_%d' % i]
        el.set('cursor', 'pointer')
        el.set('onclick', "toggle_hist(this)")
    # Create script defining the function `toggle_hist`.
    # We create a global variable `container` that stores the patches id
    # belonging to each histogram. Then a function "toggle_element" sets the
    # visibility attribute of all patches of each histogram and the opacity
    # of the marker itself.
    script = """
    <script type="text/ecmascript">
    <![CDATA[
    var container = %s
    function toggle(oid, attribute, values) {
        /* Toggle the style attribute of an object between two values.
        Parameters
        ----------
        oid : str
        Object identifier.
        attribute : str
        Name of style attribute.
        values : [on state, off state]
        The two values that are switched between.
        */
        var obj = document.getElementById(oid);
        var a = obj.style[attribute];
        a = (a == values[0] || a == "") ? values[1] : values[0];
        obj.style[attribute] = a;
        }
    function toggle_hist(obj) {
        var num = obj.id.slice(-1);
        toggle('leg_patch_' + num, 'opacity', [1, 0.3]);
        toggle('leg_text_' + num, 'opacity', [1, 0.5]);

        var names = container['hist_'+num]

        for (var i=0; i < names.length; i++) {
            toggle(names[i], 'opacity', [1, 0])
        };
        }
    ]]>
    </script>
    """ % json.dumps(hist_patches)
    # Add a transition effect
    css = tree.getchildren()[0][0]
    css.text = css.text + "g {-webkit-transition:opacity 0.4s ease-out;" + \
        "-moz-transition:opacity 0.4s ease-out;}"
    # Insert the script and save to file.
    tree.insert(0, ET.XML(script))
    ET.ElementTree(tree).write("svg_histogram.svg")

以前は、必要なライブラリを最上位の行に pip でインストールする必要があり、プロット付きの SVG ファイルを正常に保存しました (ファイルを読み取り、ヒストグラムでズームすると、画像は数学関数で生成されるため、ピクセルは表示されません)。

それは(明らかに私たちの時代では)Python 3 を使用します。

その後、出版物のレンダリングのために、TeX ドキュメント内に SVG イメージをインポートできます。

役に立つと幸いです。

こんにちは、ハビエル。

おすすめ記事