如何使用乌龟填充形状区域 [英] How to get the area of shape filled using turtle

查看:91
本文介绍了如何使用乌龟填充形状区域的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Turtle在Python中绘制了一个分形形状,并试图在足够高的迭代后获得该分形的面积.对于那些感兴趣的人来说,这种分形与科赫雪花有关.

我能够使用begin_fill()和end_fill()用黑色填充分形.然后,我使用

请注意,分形是由 turtle 绘制的,而边界框是由基础的 tkinter 绘制的,以验证我们是否正确转换了坐标.

可能的解决方案

找到一种不依赖于 find_overlapping()的方法.我认为接下来要研究的是将画布转换为位图图像并对其进行像素计数,或者首先绘制位图图像.关于SO直接或间接通过Postscript将画布转换为位图有几种讨论.然后,您可以加载该图像并使用Python图像库之一来计数像素.尽管比较复杂,但它应提供一种恒定的时间来计数像素.或者,有一些库可以绘制位图,您可以将其加载到tkinter中以进行视觉验证,但随后可以直接对像素进行计数.祝你好运!

I graphed a fractal shape in Python using turtle, and am trying to get the area of this fractal after a sufficiently high iteration. This fractal is related to the Koch snowflake, for those interested.

I was able to fill in the fractal with black using begin_fill() and end_fill(). I then used this answer to get the color of each pixel in a valid range. If it wasn't equal to white, then I added one to the count. This solution works for a small iteration of the fractal. However, it takes an exorbitant amount of time when trying to go to a higher iteration.

Here is my code for the fractal.

def realSnowflake(length, n, s, show = False):
    #n: after n iterations
    #s: number of sides (in Koch snowflake, it is 3)
    #length: starting side length
    turtle.begin_fill()
    a = 360/s
    for i in range(s):
        snowflake(length, n, s) 
        turtle.right(a)
    turtle.end_fill()

Here is my code for finding the area.

count = 0
canvas = turtle.getcanvas()
for x in range(x1, x2+1): #limits calculated through math
    for y in range(y2, y1+1):
        if get_pixel_color(x, y, canvas) != "white":
            count += 1

I want to be able to find the area of this fractal faster. It takes the most amount of time not in graphing the fractal, but in the double for loop of x and y. I think if there is a way to find the area while turtle is filling, this would be optimal.

解决方案

the complexity of the image drawn shouldn't affect the time it takes to count black pixels

Unfortunately, in this case it does. If we lookup an earlier source of the get_pixel_color() code, we find the telling text, "is slow". But it's worse than that, it actually slows down!

This code is built atop canvas.find_overlapping() which is looking for high level objects that sit over X,Y. In the case of tkinter filling an object for turtle, there is overlap, up to three layers in the code below. This increases as the factal gets more complex. Here's my code to demonstrate this:

from turtle import Screen, Turtle
from math import floor, ceil
from time import time

def koch_curve(turtle, iterations, length):
    if iterations == 0:
        turtle.forward(length)
    else:
        for angle in [60, -120, 60, 0]:
            koch_curve(turtle, iterations - 1, length / 3)
            turtle.left(angle)

def koch_snowflake(turtle, iterations, length):
    turtle.begin_poly()
    turtle.begin_fill()

    for _ in range(3):
        koch_curve(turtle, iterations, length)
        turtle.right(120)

    turtle.end_fill()
    turtle.end_poly()

    return turtle.get_poly()

def bounding_box(points):
    x_coordinates, y_coordinates = zip(*points)
    return [(min(x_coordinates), min(y_coordinates)), (max(x_coordinates), max(y_coordinates))]

def get_pixel_color(x, y):
    ids = canvas.find_overlapping(x, y, x, y)  # This is our bottleneck!

    if ids: # if list is not empty
        index = ids[-1]
        return canvas.itemcget(index, 'fill')

    return 'white' # default color

screen = Screen()
screen.setup(500, 500)
turtle = Turtle(visible=False)
turtle.color('red')

canvas = screen.getcanvas()
width, height = screen.window_width(), screen.window_height()

for iterations in range(1, 7):
    screen.clear()
    turtle.clear()

    screen.tracer(False)

    polygon_start_time = time()
    polygon = koch_snowflake(turtle, iterations, 200)
    polygon_elapsed = round((time() - polygon_start_time) * 1000)  # milliseconds

    screen.tracer(True)

    ((x_min, y_min), (x_max, y_max)) = bounding_box(polygon)
    screen.update()

    # Convert from turtle coordinates to tkinter coordinates
    x1, y1 = floor(x_min), floor(-y_max)
    x2, y2 = ceil(x_max), ceil(-y_min)

    canvas.create_rectangle((x1, y1, x2, y2))

    count = 0

    pixel_count_start_time = time()
    for x in range(x1, x2 + 1):
        for y in range(y1, y2 + 1):
            if get_pixel_color(x, y) == 'red':
                count += 1
    pixel_count_elapsed = round((time() - pixel_count_start_time) * 1000)

    print(iterations, count, polygon_elapsed, pixel_count_elapsed, ((x1, y1), (x2, y2)))

screen.exitonclick()

CONSOLE OUTPUT

> python3 test.py
1 23165 1 493 ((-1, -58), (201, 174))
2 26064 4 1058 ((-1, -58), (201, 174))
3 27358 9 1347 ((-1, -58), (201, 174))
4 28159 29 2262 ((0, -58), (201, 174))
5 28712 104 5925 ((0, -58), (201, 174))
6 28881 449 19759 ((0, -58), (200, 174))
> 

The fields are as follows:

  1. Iterations
  2. Pixels counted
  3. Time to draw image in ms
  4. Time to count pixels in ms
  5. Computed bounding box (in tkinter coordinates)

FINAL ITERATION SCREEN OUTPUT

Note that the fractal is drawn by turtle but the bounding box is drawn by the underlying tkinter to verify that we converted coordinates correctly.

POSSIBLE SOLUTION

Find an approach that doesn't rely on find_overlapping(). I think the next thing to investigate would be either to convert the canvas to a bitmap image, and pixel count it, or draw a bitmap image in the first place. There are several discusions on SO about converting a canvas to a bitmap, either directly or indirectly via Postscript. You could then load in that image and use one of the Python image libraries to count the pixels. Although more complicated, it should provide a constant time way of counting pixels. Alternately, there are libraries to draw bitmaps, which you could load into tkinter for visual verfication, but could then pixel count directly. Good luck!

这篇关于如何使用乌龟填充形状区域的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆