使用PIL在正方形内画线

时间:2019-11-26 23:27:12

标签: python python-imaging-library

我正在尝试使用Python和PIL重新创建此图像。

enter image description here

这是我提供的代码:

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()

这是输出:

enter image description here

还有如何用颜色填充那些形成的三角形?

更新

通过在Rotating a square in PIL处修改答案,我能够做到这一点。
enter image description here
代码:

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()

现在,我如何正确缩放和旋转所有点以任意大小相交的正方形?

编辑,绘制三角形

绘制三角形的顶点:

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")

输出:
enter image description here

1 个答案:

答案 0 :(得分:6)

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

Cute squares diagram

鉴于上图,其中L是起始正方形的边的长度,而L line是新正方形的长度,我们必须找到theta使得,当它旋转新方块时,所有角都碰到前一个方块的侧面。

L line可以定义为Calculating L line,其中f是缩放因子。例如,如果缩放因子为0.9,则每个新正方形的边将是前一个边的边长的90%。

使用一些基本的三角函数,可以发现a为:

Calculating a

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

Calculating generic a

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

应注意,给定公式中的平方根,f的下界为f generic inequality

从几何上讲,这是有道理的。例如,对于一个正方形,新正方形Square root of 2 times L line的对角线应不小于前一个正方形的对角线,即转为L' L inequality

使用Calculating L line进行计算,我们发现

f lower bound

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

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

最后,theta可以使用正弦规则计算

Calculate theta

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

Obs .:该代码仅考虑正方形,也就是说,尽管可以很容易地将其归纳为alpha等于90°(请参阅atheta等式)。

Cute squares

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()

使用认为alpha可以不同于90°的通用实现,可以对任何多边形进行此操作。这是将其应用于三角形的示例:

Spiral triangles


奖励模因

输出:1000次迭代,缩放系数为0.98;和square root of one over two比例因子。

Crazy squares Symmetric squares