Draw lines inside a square using PIL

con*_*dor 3 python python-imaging-library

I'm trying to recreate this image using Python and PIL.

在此处输入图片说明

This is the code I come upped with:

from PIL import Image, ImageDraw


def draw_lines(draw, points):
    new_points = []
    for idx, point in enumerate(points):
        x, y = point
        if idx != len(points) - 1:
            if idx == 0:
                x = x + 25
            elif idx == 1:
                y = y + 25
            elif idx == 2:
                x = x - 25
            elif idx == 3:
                y = y - 25
        else:
            x = x + 25
        new_points.append((x, y))
    draw.line(new_points, fill="black", width=1)
    return new_points


def main():
    im = Image.new('RGB', (501, 501), color=(255, 255, 255))
    draw = ImageDraw.Draw(im)
    points = [
        (0, 0),
        (500, 0),
        (500, 500),
        (0, 500),
        (0, 0),
    ]
    draw.line(points, fill="black", width=1)
    for i in range(80):
        points = draw_lines(draw, points)
    im.save("out.png")


if __name__ == '__main__':
    main()

Run Code Online (Sandbox Code Playgroud)

and this is the output:

在此处输入图片说明

and also how can I fill those formed triangles with color?

Update:

By modifying the answer here Rotating a square in PIL, I was able to do this.
在此处输入图片说明
Code:

import math
from PIL import Image, ImageDraw


def distance(ax, ay, bx, by):
    return math.sqrt((by - ay) ** 2 + (bx - ax) ** 2)


def rotated_about(ax, ay, bx, by, angle):
    radius = distance(ax, ay, bx, by)
    angle += math.atan2(ay - by, ax - bx)
    return (
        round(bx + radius * math.cos(angle)),
        round(by + radius * math.sin(angle))
    )


image = Image.new('RGB', (510, 510), color=(255, 255, 255))
draw = ImageDraw.Draw(image)


def draw_sqr(pos, sqlen, rota):
    square_center = pos
    square_length = sqlen

    square_vertices = (
        (square_center[0] + square_length / 2, square_center[1] + square_length / 2),
        (square_center[0] + square_length / 2, square_center[1] - square_length / 2),
        (square_center[0] - square_length / 2, square_center[1] - square_length / 2),
        (square_center[0] - square_length / 2, square_center[1] + square_length / 2)
    )

    square_vertices = [rotated_about(x, y, square_center[0], square_center[1], math.radians(rota)) for x, y in
                       square_vertices]
    draw.polygon(square_vertices, outline="black")


def draw_rot_sqr(pos):
    scale = 500
    rot = 0
    n = 1.1575
    for i in range(10):
        draw_sqr(pos, scale, rot)
        rot = rot * n + 10
        scale = scale / n - 10


draw_rot_sqr((255, 255))

image.show()
Run Code Online (Sandbox Code Playgroud)

Now, how can I properly scale and rotate the squares where all points intersect with the sides at any size?

Edit, drawing triangles

Vertices for drawing triangles:

def draw_sqr(pos, p_len, rota):
    x, y = pos
    altitude = p_len * math.sqrt(3) / 2
    apothem = altitude / 3
    x_top = x
    y_top = y - apothem * 2
    x_base_1 = x + p_len / 2
    x_base_2 = x - p_len / 2
    y_base = y + apothem

    vertices = (
        (x_top, y_top),
        (x_base_1, y_base),
        (x_base_2, y_base)
    )

    vertices = [rotated_about(x, y, pos[0], pos[1], rota) for x, y in
                vertices]
    draw.polygon(vertices, outline="black")
Run Code Online (Sandbox Code Playgroud)

Outputs:
在此处输入图片说明

Gab*_*ski 6

这是一个可爱的数学问题。

可爱的方块图

鉴于上图,其中 大号 是起始正方形的边的长度,并且 L线 是新方块的长度,我们必须找到 塞塔 这样一来,当旋转新正方形时,所有角都会接触前一个正方形的边。

L线 可以定义为 计算L线,其中 F是比例因子。例如,如果缩放因子为0.9,则每个新正方形的边将是前一个边的长度的90%。

有了一些基本的三角学, 一种 可以发现是:

计算一个

对于通用多边形,其定义为

计算通用a

在其中 α 是多边形的内角值(正方形为90°,因此它会退回到上一个方程式)。

应该注意的是 F 下界 f一般不平等,给出公式中的平方根。

从几何上讲,这是有道理的。例如,对于一个正方形,新正方形的对角线2倍L线的平方根 应该不小于上一个的边,即 不等式

解决 计算L线,我们发现

f下界

缩放比例超过1时,新的正方形将更大,但是仍然可以使用接触角的原理。

至于公式中的正负号,负号对应于顺时针旋转,正号代表逆时针旋转。

最后, 塞塔 可以用正弦法则计算

计算θ

考虑到这一点,您可以产生以下输出。

观察:该代码仅考虑正方形,即它认为 α 等于90°,​​尽管它可以很容易地泛化(请参阅 一种塞塔 方程式)。

可爱的方块

import math
from PIL import Image, ImageDraw


def calc_a(L, f):
    return L/2.0*(1-(1-2*(1-f**2))**.5)


def calc_theta(L, f, direction='cw'):
    a = calc_a(L, f)
    if direction == 'cw':
        d = 1
    elif direction == 'ccw':
        d = -1
    return d*math.asin(a/(f*L))


def distance(ax, ay, bx, by):
    return math.sqrt((by - ay) ** 2 + (bx - ax) ** 2)


def rotated_about(ax, ay, bx, by, angle):
    radius = distance(ax, ay, bx, by)
    angle += math.atan2(ay - by, ax - bx)
    return (
        round(bx + radius * math.cos(angle)),
        round(by + radius * math.sin(angle))
    )


image = Image.new('RGB', (510, 510), color=(255, 255, 255))
draw = ImageDraw.Draw(image)


def draw_sqr(pos, sqlen, rota):
    square_center = pos
    square_length = sqlen

    square_vertices = (
        (square_center[0] + square_length / 2, square_center[1] + square_length / 2),
        (square_center[0] + square_length / 2, square_center[1] - square_length / 2),
        (square_center[0] - square_length / 2, square_center[1] - square_length / 2),
        (square_center[0] - square_length / 2, square_center[1] + square_length / 2)
    )

    square_vertices = [rotated_about(x, y, square_center[0], square_center[1], rota) for x, y in
                       square_vertices]
    draw.polygon(square_vertices, outline="black")


def draw_rot_sqr(pos):
    side = 500  # starting square side length
    f = 0.9     # should be bigger than 1/sqrt(2), for math reasons
    base_theta = calc_theta(side, f, direction='cw')
    theta = 0   # first square has no rotation
    for i in range(10):
        draw_sqr(pos, side, theta)
        # theta is relative to previous square, so we should accumulate it
        theta += base_theta
        side *= f

draw_rot_sqr((255, 255))

image.show()
Run Code Online (Sandbox Code Playgroud)

使用考虑到这一点的通用实现 α可以与90°不同,可以使用任何多边形来实现。这是将其应用于三角形的示例:

螺旋三角形


奖励模因

输出:1000次迭代,缩放系数为0.98;和一二的平方根 比例因子。

疯狂的正方形 对称正方形