在Python中创建出版品质的几何图形

时间:2016-02-04 19:35:23

标签: python geometry

我是一名数学家。最近,我成为了一本着名杂志的谜题和问题专栏的编辑。偶尔,我需要创建一个伴随问题或解决方案的数字。这些图主要涉及2D(偶尔为3D)欧几里德几何(线,多边形,圆,加上偶尔的椭圆或其他圆锥截面)。目标是使用Computer Modern(" TeX")文本标签获得高质量(按下就绪)的数字。我希望找到(或者可能帮助写!)一个相对高级的Python库,它知道"欧几里德几何在自然操作的意义上(例如,绘制垂直线到达给定点的垂直线,平分给定角度,或反映线L上的图形A以获得新的图形A')已在库中定义。当然,在定义元素之后创建数字的能力是一个至关重要的目标(例如,封装的Postscript)。

我知道这个问题的多个次优解决方案(有些部分),但我不知道任何既简单又灵活的解决方案。让我解释一下:

  • Asymptote(类似于/基于Metapost)允许创建极其复杂的高质量数字,但几乎不了解几何结构(它是一种相当低级的语言),因此任何重要的构造都需要相当长的脚本。
  • 包含TikZ
  • tkz-euclide是高级的,灵活的,并且还可以生成高质量的数据,但是它的语法非常繁重,我只是为了比较简单而感到愚蠢。 (有些程序实际上输出到TikZ ---见下文。)
  • 动态几何程序,我最熟悉的Geogebra,通常具有图形导出功能(EPS,TikZ等),但是应该以交互方式使用。有时,人们需要的是基于硬规格的数字(例如,精确的边长) - 在脚本中定义对象最终会更灵活(如果相应地不太方便)。
  • 两个程序EukleidesGCLC最接近我正在寻找的程序:它们生成数字(EPS格式; GCLC也导出到TikZ)。 Eukleides拥有所有选项中最漂亮,最简单的语法(请参阅examples),但它恰好用C语言编写(源代码可用,虽然我对许可证不确定),而是有限/不可定制,不再维护。 GCLC仍然保留,但它是封闭源,它的语法明显比Eukleides更糟,并且还有一些其他不自然的怪癖。此外,它不适用于Mac OS(我的笔记本电脑是Mac)。

Python有:

  • Matplotlib,它产生极高质量的数字(特别是函数或数值数据),但似乎不知道几何结构,
  • Sympy有一个几何模块, 了解几何对象和构造,所有这些都可以通过愉快的Python语法访问,但似乎没有图形导出(甚至显示?)功能

最后,一个问题:是否有一个库,类似于" Sympy / geometry"的数字,它使用Python语法描述几何对象和结构,允许生成高质量的数字(主要用于打印) ,说E​​PS)?

如果不存在具有此类功能的库,我会考虑帮助编写一个(也许是Sympy的扩展名?)。我会很感激指点。

1 个答案:

答案 0 :(得分:1)

有一种方法可以使用matplotlob生成矢量图像,并通过库io输出到具有this approach的矢量图像(SVG)。

我亲自尝试将该方法的代码作为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")

以前,您需要在最上面的行上点安装所需的库,然后它成功地保存了带图的SVG文件(您可以读取该文件并在直方图中放大,并且不会得到像素,因为图像用数学函数生成的。)

(显然在我们这个时代)它使用python 3。

然后您可以将SVG图像导入TeX文档中以进行发布呈现。

我希望它会有所帮助。

问候, 哈维尔。

相关问题