如何以编程方式创建重复的图像模式?

时间:2014-02-28 16:48:25

标签: python loops matplotlib

我想使用Python创建以下图像模式。 enter image description here 为清楚起见:这些是两个独立的图像序列(一个在顶行,一个在底行)。 它们是相互关联的,因为它们是堆积四面体的投影区域。 在3D环境中,它看起来如下:

enter image description here 请注意,这些3D对象不会缩放,以使总对象尺寸保持不变。上面显示的投影区域就是这种情况 四级结构(未示出)顶部将有另外10个单元 C级别的单元格总数n为:

C = (n^3 + 3*n^2 + 2*n)/6 

现在我正在手工创建模式(制作3D对象,渲染投影区域,重复)但这对于更多细分来说非常繁琐且不可行。

我设法使用以下代码创建一个多边形,但是我无法弄清楚如何循环它以使总边长保持不变,但多边形以上面可视化的方式细分。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon

el = 1
dfv = 1/np.sqrt(3)*el
dfe = 1/(2*np.sqrt(3))*el
vertices1 = [(0,0),(0.5*el,-dfe),(0,dfv),(-0.5*el,-dfe)]
vertices2 = [(0.5*el,-dfe),(0,dfv),(-0.5*el,-dfe)]

fig = plt.figure()
ax1 = fig.add_subplot(121)
ax1.add_patch(Polygon(vertices1, closed=True, fill=True))
ax1.set_xlim([-1, 1])
ax1.set_ylim([-1, 1])
ax1.set_aspect('equal')

ax2 = fig.add_subplot(122)
ax2.add_patch(Polygon(vertices2, closed=True, fill=True))
ax2.set_xlim([-1, 1])
ax2.set_ylim([-1, 1])
ax2.set_aspect('equal')
plt.show()

enter image description here
我使用matplotlib和包含的Polygon补丁,但我不确定这是否是最佳方法。 多边形或颜色的方向也不重要。

3 个答案:

答案 0 :(得分:1)

助手类:

class Custom_Polygon(object):
    """docstring for Polygon"""
    def __init__(self, verts):
        self.verticies = np.array(verts)
        self.dims = self.verticies.shape[1]

    def scale(self, scaleFactor):
        scaleMatrix = np.zeros((self.dims, self.dims))
        np.fill_diagonal(scaleMatrix, scaleFactor)
        self.verticies = np.dot(self.verticies, scaleMatrix)

    def scale_with_orgin(self, scaleFactor, origin):
        origin = origin.copy()
        self.translate([-i for i in origin])
        self.scale(scaleFactor)
        self.translate([i for i in origin])

    def translate(self, shiftBy):
        self.verticies += shiftBy

    def get_width(self):
        x_min = self.verticies[:,0].min()
        x_max = self.verticies[:,0].max()
        return abs(x_max - x_min)


    def get_height(self):
        y_min = self.verticies[:,1].min()
        y_max = self.verticies[:,1].max()
        return abs(y_max - y_min)

制作一个辅助类,用于缩放和平移多边形以制作图案。并编写了绘制第一个模式的算法。绘制第二个模式应该不难做出类似的算法。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon

def makeFirstPattern(ax, d, verticies, scale=True):
    Pn = Custom_Polygon(verticies)
    direction = 1 
    divisions = d
    if scale: Pn.scale_with_orgin(2.0/(divisions+1), Pn.verticies[-1])
    for i in range(divisions, 0, -2):
        for _ in range(i):
            ax.add_patch(Polygon(Pn.verticies, closed=True, fill=True, ec='none'))
            Pn.translate([direction * Pn.get_width()/2, 0])
        direction *= -1
        Pn.translate([direction * Pn.get_width(), Pn.get_height()])

el = 1
dfv = 1/np.sqrt(3)*el
dfe = 1/(2*np.sqrt(3))*el
vertices1 = [(0,0),(0.5*el,-dfe),(0,dfv),(-0.5*el,-dfe)]
vertices2 = [(0.5*el,-dfe),(0,dfv),(-0.5*el,-dfe)]


fig = plt.figure()
ax1 = fig.add_subplot(111)

makeFirstPattern(ax1, 7, vertices2)

ax1.set_xlim([-1, 1])
ax1.set_ylim([-1, 1])
ax1.set_aspect('equal')
plt.show()

enter image description here

答案 1 :(得分:0)

对于第一个模式,您可以使用此代码:

from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon

nrows = 5
pat = np.array([[0,0],[0.5,np.sqrt(0.75)],[-0.5,np.sqrt(0.75)]])

fig = plt.figure()
ax = fig.add_subplot(111)

for base in range(nrows):
    npat = 2*base + 1
    for col in np.linspace(-base/2, base/2, npat):
        pp = Polygon(pat + np.array([col, base*v]), fc='k', ec='none')
        ax.add_patch(pp)

ax.autoscale_view(True,True,True)
ax.set_aspect('equal')

plt.show()

enter image description here

答案 2 :(得分:0)

我没有使用matplotlib,而是使用 SVG 提供解决方案,您的脚本只打印出相应的SVG命令。

注意:创建的SVG标头缺少相同的定义,这就是某些程序无法处理生成的图像的原因。 Inkscape虽然可以毫无问题地打开它并再次保存。

在SVG中定义多边形

描述的解决方案基于您显示的“箭头”示例,即较低的一个 我将箭头编码为 SVG路径,有四个点p0,p1,p2和p3,其中p0是上尖端,p1是右下角,p3是左下角,p2是尖端下面。每个点都有一个x和y坐标(p0x,p0y ......)。

注意:SVG坐标与数学坐标不同,从左到右(x)和自上而下(y)增加,因此原点是左上角。

路径存储为字符串,其中的点为varibale。最后一个字符串是使用python的str.format()方法创建的。

代码

# width of the complete picture
width=600
# height of the complete picture
height=600
# desired recursion depth (>=1)
n=5
# "shape factor" of the arrow (=(p1y-p2y)/(p2y-p0y))
# a=0 would result in a triangle
a=0.3
def create_arrows(n, depth=1, width=600, height=600, refx=None, refy=None):
    global a
    if refx==None or refy==None:
        # the first polygon instances defines it's reference point
        # following instances are given a reference point by the caller
        refx = (width - width/n)/2
        refy = 0
    if depth==1:
        # the first polygon instance defines the size of all instancens
        width=width/n
        height=height/n
    # the SVG definition of the polygon
    polyg = r'<path d="M{p0x} {p0y} L{p1x} {p1y} L{p2x} {p2y} L{p3x} {p3y} Z" />'
    points = {"p0x":refx+width/2, "p0y":refy, "p1x":refx+width, "p1y":refy+height, "p2x":refx+width/2, "p2y":refy+(1-a)*height, "p3x":refx, "p3y":refy+height}
    # create the requested polygon
    polygons = [polyg.format(**points)]
    # if maximum recursion depth not reached call method recursively
    if depth<n:
        polygons.extend(myfunction(n, depth+1, width=width, height=height, refy=refy+(1-a)*height, refx=refx)) # down shifted
        polygons.extend(myfunction(n, depth+1, width=width, height=height, refy=refy+height, refx=refx-width/2)) # bottom left
        polygons.extend(myfunction(n, depth+1, width=width, height=height, refy=refy+height, refx=refx+width/2)) #bottom right
    return(polygons)
print('<svg height="{height}" width="{width}">'.format(width=width, height=height))
# converting the list to a set is necessary since some polygons can be created by multiple recursion paths
print('\n'.join(set(create_arrows(4,width=width,height=height))))
print('</svg>')

解释

箭头以递归方式创建,即您从顶部箭头开始。箭头的尺寸为width/nheight/n,其中n是所需的递归级别(n> = 1)。您展示的示例包括n=1n=2n=3。以下解释的递归模式是根据经验推导出来的,并不直接基于您的3D示例。

n = 1 :在级别n=1中,您在创建顶部箭头后完成了。

n = 2 :在级别n=2中,此TOP箭头分别创建另外三个,一个位于右下方,每个位于左下方。这三个箭头的尖端(p0)分别位于原始箭头的p2,p3和p1处。你已经完成了。

n = 3 :对级别n=2中创建的每个箭头重复上述步骤,依此类推。

下面请查看级别n=6的示例。 对于三角形,你的上层例子,这个想法很容易适应。您只需更改多边形路径和递归模式。 顺便说一句。使用给定脚本a=0创建trianlges的旋转版本。所以如果你很懒,只需使用它并在inkscape中转换生成的SVG。

imgage created using above script at level n=6