Python Turtle: Is it possible to use layers in fill command - python

I have recently been developing a software which will be used for creating fractal images. But I realized for it to fill the shapes it will need to be done in layers otherwise it will overwrite sections. Here is my current code:
import turtle
def CreatePolygon (turt, Side, Size):
if Size <= 1:
return
else:
#This will create a polygon of a certain size.
#And iterate smaller polygons inside each polygon thus creating a fractal.
for i in range (0, Side):
turt.forward(Size)
turt.left(360/Side)
CreatePolygon(turt, Side, Size/(Side-1))
Size = 250
t = turtle.Turtle()
t.hideturtle()
t.speed(0)
#Calling The Function
CreatePolygon (t, 5, Size)
My main intention is for the polygons to be filled by different colours which I understand how to do. The issue lies in the filled polygon being overwritten once the larger polygon it is inside gets filled. I'm not sure how to fix this issue as the requirements are:
Smaller Item Gets Filled First (Inside Bigger Item).
Bigger Item Gets Filled In Second While Not Filling In Where The Smaller Item Filled In.

We don't have layers in Python turtle but we can still achieve the effect you want with a little bit of duplication and rearrangement of code:
from turtle import Screen, Turtle
COLORS = ['red', 'green', 'blue', 'magenta', 'yellow', 'cyan']
def CreatePolygon(turt, sides, size, color=0):
if size <= 1:
return
# This will create a polygon of a certain size.
turt.fillcolor(COLORS[color])
turt.begin_fill()
for _ in range(sides):
turt.forward(size)
turt.left(360 / sides)
turt.end_fill()
# And iterate smaller polygons inside each polygon thus creating a fractal.
for _ in range(sides):
turt.forward(size)
turt.left(360 / sides)
CreatePolygon(turt, sides, size / (sides - 1), color + 1)
screen = Screen()
turtle = Turtle(visible=False)
# Calling The Function
screen.tracer(False)
CreatePolygon(turtle, 5, 250)
screen.tracer(True)
screen.exitonclick()
We have to draw the larger polygon first, fill it, and then recursively draw the smaller polygons.

Related

Turtle drawing a hexagon and hexagon grid

current code
#import the turtle modules
import turtle
#Start a work Screen
ws=turtle.Screen()
#Define a Turtle Instance
geekyTurtle=turtle.Turtle()
#executing loop 6 times for 6 sides
for i in range(6):
#Move forward by 90 units
geekyTurtle.forward(90)
#Turn left the turtle by 300 degrees
geekyTurtle.left(300)
My goal is to make a hexagon grid pattern and I am failing to do it properly. My first issue is if you run the code you get a hexagon but the top is flat, I can't get it to get the pointy corners to get on top. Second I tried to make the grid and it failed and I am not sure why I am unable to copy the same hexagon and clone it next to the other. I will or should have a file of the image that I am going for below.
The output I am getting:
The output I am trying to get:
Before going into loop, turn 30 degrees.
geekyTurtle.right(30)
In order to have its clone beside, just put the turtle to the new place and draw the shape again:
for i in range(6):
geekyTurtle.forward(90)
geekyTurtle.left(300)
geekyTurtle.up()
geekyTurtle.goto(90 * 3 ** .5, 0)
geekyTurtle.down()
for i in range(6):
geekyTurtle.forward(90)
geekyTurtle.left(300)
Put it in a loop to have it for more than two times
You can use the idea of .up() and .goto(x, y) and .down() to draw grids.
It seems like this is a problem that recursion could simplify in a fractal-like way. Each side of the initial hexagon is itself a hexagon, and so forth, filling the available space:
from turtle import Screen, Turtle
SIDE = 75 # pixels
def hexagon(side, depth):
if depth > 0:
for _ in range(6):
turtle.forward(side)
turtle.right(60)
hexagon(side, depth - 1)
turtle.left(120)
screen = Screen()
screen.tracer(False) # because I have no patience
turtle = Turtle()
turtle.penup()
turtle.width(2)
turtle.sety(-SIDE) # center hexagons on window
turtle.pendown()
turtle.left(30) # optional, orient hexagons
hexagon(SIDE, depth=6) # depth depends on coverage area
turtle.hideturtle()
screen.tracer(True)
screen.exitonclick()

Simulation using python and graphics.py image

I am trying to create a simulator. (referring to John Zelle's graphics.py)
Basically, my object will make use of graphics.py to display the object as a circle. Then, using the .move method in the class in graphics.py, the object will move in the x direction and y direction. If the object is currently drawn, the circle is adjusted to the new position.
Moving just one object can easily be done with the following codes:
win = GraphWin("My Circle", 100, 100)
c = Circle(Point(50,50), 10)
c.draw(win)
for i in range(40):
c.move(30, 0) #speed=30
time.sleep(1)
win.close()
However, I want the program to display multiple circles at once that moves at different speed. I've created a Circle object class which takes speed as an input, and a list with 3 Circle objects in it
circle = []
circle1 = Car(40)
circle2= Car(50)
circle3 = Car(60)
In summary, my question is, how do make use of this list such that I am able to display and move multiple circles in one window at once using the methods available in graphics.py?
That all depends on how you create your Car class, but nothing stops you from using the same code to move multiple circles in the same refresh cycle, e.g.:
win = GraphWin("My Circle", 1024, 400)
speeds = [40, 50, 60] # we'll create a circle for each 'speed'
circles = [] # hold our circles
for speed in speeds:
c = Circle(Point(50, speed), 10) # use speed as y position, too
c.draw(win) # add it to the window
circles.append((c, speed)) # add it to our circle list as (circle, speed) pair
for i in range(40): # main animation loop
for circle in circles: # loop through the circles list
circle[0].move(circle[1], 0) # move the circle on the x axis by the defined speed
time.sleep(1) # wait a second...
win.close()
Of course, if you're already going to use classes, you might as well implement move() in it so your Car instances can remember their speed and then just apply it when you call move() on them in a loop.

Python Turtle, how to get colors to change by themselves?

creating an image of a cube for minecraft.
Trying to get the colors inside the box to change by themselves. Is there some kind of integer algorithm I can use to achieve this besides using random? Because right now, it creates random colors, but I want the little boxes to change colors by themselves. Any Ideas?
import turtle
import random
minecraft = turtle.Turtle()
minecraft.ht()
minecraft.speed(9999999999999) #I guess there is a max speed??? wanted it to make the mini cubes a lot faster.
#centers the box
minecraft.up()
minecraft.goto(-50,50)
minecraft.down()
#end of center box
for i in range(4): #Creates the box
minecraft.forward(100)
minecraft.right(90)
for i in range(1000): #Repeats all this code over and over
for i in range(10): #makes the 10 cubes going down, then it comes back up and repeates making cubes until it gets to the last cube.
for i in range(10): #initiate the random colors
red = random.random()
blue = random.random()
yellow = random.random()
minecraft.color(red, blue, yellow)
for i in range(1): #the little boxes
minecraft.begin_fill()
minecraft.forward(10)
minecraft.right(90)
minecraft.forward(10)
minecraft.right(90)
minecraft.forward(10)
minecraft.right(90)
minecraft.forward(10)
minecraft.right(90)
minecraft.end_fill()
minecraft.right(90) #little boxes changing directions
minecraft.forward(10)
minecraft.right(-90)
minecraft.forward(10) #little boxes changing directions...again
minecraft.right(-90)
minecraft.forward(100)
minecraft.right(90)
minecraft.right(180) #and again...
minecraft.forward(100)
minecraft.right(180)
Based on your description of the problem, this is what I believe you're asking for -- a grid of randomly colored boxes that appear to individually change color at random:
from turtle import Turtle, Screen
import random
BOX_SIZE = 100
SQUARE_SIZE = 10
DELAY = 100 # milliseconds
minecraft = Turtle(shape="square", visible=False)
minecraft.shapesize(SQUARE_SIZE / 20)
minecraft.speed("fastest")
# Center the box
minecraft.up()
minecraft.goto(-BOX_SIZE//2, BOX_SIZE//2)
minecraft.down()
# Create the box
for _ in range(4):
minecraft.forward(BOX_SIZE)
minecraft.right(90)
minecraft.up()
# Move turtle inside box
minecraft.forward(SQUARE_SIZE//2)
minecraft.right(90)
minecraft.forward(SQUARE_SIZE//2)
minecraft.left(90)
squares = []
for i in range(BOX_SIZE // SQUARE_SIZE):
# makes the 10 cubes going across, then it backs up and
# repeats making cubes until it gets to the last cube.
for j in range(BOX_SIZE // SQUARE_SIZE):
# initiate the random colors
red = random.random()
blue = random.random()
yellow = random.random()
minecraft.color(red, blue, yellow)
squares.append((minecraft.position(), minecraft.stamp()))
minecraft.forward(SQUARE_SIZE)
minecraft.backward(SQUARE_SIZE)
minecraft.sety(minecraft.ycor() - SQUARE_SIZE)
minecraft.right(180) # little boxes changing directions
def change():
random_choice = random.choice(squares)
squares.remove(random_choice)
position, stamp = random_choice
minecraft.goto(position)
red = random.random()
blue = random.random()
yellow = random.random()
minecraft.color(red, blue, yellow)
minecraft.clearstamp(stamp)
squares.append((minecraft.position(), minecraft.stamp()))
screen.ontimer(change, DELAY)
screen = Screen()
screen.ontimer(change, DELAY)
screen.exitonclick()
The key to this, like many turtle problems, is to stamp, not draw. Stamped images can do a number of things that drawn images can't -- in this case they can be individually removed and replace by other stamps.
Also, never let your turtle code run forever, nor any anywere close to it -- use an ontimer() event instead so that other turtle events (like properly closing the window) can trigger correctly.
random.random() create a random number falls between 0 and 1. So when you need a random number generate between 0 and MAX, just simply multiply with it, in your case, like this way:
int(random.random()*256)
BTW, I checked the turtle docs again. turtle.color(*args) expects two color args, "Return or set pencolor and fillcolor". That means you need to pass it turtle.color((40, 80, 120), (160, 200, 240)) this way.

Is it possible to change turtle's pen stroke?

I need to draw a bar graph using Python's turtle graphics and I figured it would be easier to simply make the pen a thick square so I could draw the bars like that and not have to worry about making dozens of rectangles and filling them in.
When I set the turtle shape using turtle.shape('square') though, it only changes the appearance of the pen but has no effect on the actual drawing:
Is there a way to make turtle actually draw a rectangular stroke, whether that be through built-in methods or through modifying the turtle file?
I DON'T want rounded edges, like this:
To answer the question asked in the title: No, it is not possible to change the pen stroke directly (see cdlane's answer for a possible way to do it by modifying the hardcoded values from tkinter).
I did find a workaround for the use case presented in the question body, however.
A custom pen shape (in this case, representing the exact shape and size of the bar) can be registered like this:
screen.register_shape("bar", ((width / 2, 0), (-width / 2, 0), (-width / 2, height), (width / 2, height)))`
We can then simply loop through each bar, update the pen shape with the new values, and use turtle.stamp to stamp the completed bars onto the graph, no drawing required.
It looks like changing the shape of the pen stroke itself isn't possible. turtle.shape('square') only changes the shape of the turtle, not the pen stroke. I suggest lowering the pen size, and creating a function to draw a rectangle. You could use this do draw the bars.
I've two solutions to this problem that I've used in various programs.
The first is a variation on your stamp solution. Rather than use screen.register_shape() to register a custom polygon for each line, use a square turtle and for each line turtle.turtlesize() it into the rectangle you want to stamp:
from turtle import Turtle, Screen
STAMP_SIZE = 20 # size of the square turtle shape
WIDTH, LENGTH = 25, 125
yertle = Turtle(shape="square")
yertle.penup()
yertle.turtlesize(WIDTH / STAMP_SIZE, LENGTH / STAMP_SIZE)
yertle.goto(100 + LENGTH//2, 100) # stamps are centered, so adjust X
yertle.stamp()
screen = Screen()
screen.exitonclick()
My other solution, when I need to draw instead of stamp, is to reach into turtle's tkinter underpinning and modify turtle's hardcoded line end shape itself:
from turtle import Turtle, Screen
import tkinter as _
_.ROUND = _.BUTT
WIDTH, LENGTH = 25, 125
yertle = Turtle()
yertle.width(WIDTH)
yertle.penup()
yertle.goto(100, 100)
yertle.pendown()
yertle.forward(LENGTH)
screen = Screen()
screen.exitonclick()
Use multiple stamps like so:
import turtle
turtle.shape("square")
for count in range(x):
turtle.stamp()
turtle.forward(1)

PyGame Platformer with Interactive Platforms "Drawn" In

I'm looking for the easiest way to implement this. I'm trying to implement platforms (with full collision detection) that you can draw in via mouse. Right now I have a line drawing function that actually draws small circles, but they're so close together that they more or less look like a line. Would the best solution be to create little pygame.Rect objects at each circle? That's going to be a lot of rect objects. It's not an image so pixel perfect doesn't seem like an option?
def drawGradientLine(screen, index, start, end, width, color_mode):
#color values change based on index
cvar1 = max(0, min(255, 9 * index-256))
cvar2 = max(0, min(255, 9 * index))
#green(0,255,0), blue(0,0,255), red(255,0,0), yellow(255,255,0)
if color_mode == 'green':
color = (cvar1, cvar2, cvar1)
elif color_mode == 'blue':
color = (cvar1, cvar1, cvar2)
elif color_mode == 'red':
color = (cvar2, cvar1, cvar1)
elif color_mode == 'yellow':
color = (cvar2, cvar2, cvar1)
dx = end[0] - start[0]
dy = end[1] - start[1]
dist = max(abs(dx), abs(dy))
for i in xrange(dist):
x = int(start[0]+float(i)/dist*dx)
y = int(start[1]+float(i)/dist*dy)
pygame.draw.circle(screen, color, (x, y), width)
That's my drawing function. And here's my loop that I have put in my main game event loop.
i = 0
while (i < len(pointList)-1):
drawGradientLine(screen, i, pointList[i], pointList[i + 1], r, mode)
i += 1
Thanks for any help, collision detection is giving me a huge headache right now (still can't get it right for my tiles either..).
Any reason you want to stick with circles?
Rectangles will make the line/rectangle a lot more smooth and will make collision detecting a lot easier unless you want to look into pixel perfect collision.
You also don't seem to save your drawn objects anywhere (like in a list or spritegroup), so how are you going to check for collision?
Here's a leveleditor I did for game awhile back, it's not perfect, but it works:
https://gist.github.com/marcusmoller/bae9ea310999db8d8d95
How it works:
The whole game level is divided up into 10x10px grid for easier drawing
The leveleditor check if the mouse is being clicked and then saves that mouse position
The player now moves the mouse to another position and releases the mouse button, the leveleditor now saves that new position.
You now have two different coordinates and can easily make a rectangle out of them.
Instead of creating a whole bunch of rect objects to test collision against, I'm going to recommend creating something called a mask of the drawn-in collideable object, and test for collision against that. Basically, a mask is a map of which pixels are being used and which are not in an image. You can almost think of it as a shadow or silhouette of a surface.
When you call pygame.draw.circle, you are already passing in a surface. Right now you are drawing directly to the screen, which might not be as useful for what I'm suggesting. I would recommend creating a rect which covers the entire area of the line being drawn, and then creating a surface of that size, and then draw the line to this surface. My code will assume you already know the bounds of the line's points.
line_rect = pygame.Rect(leftmost, topmost, rightmost - leftmost, bottommost - topmost)
line_surf = pygame.Surface((line_rect.width, line_rect.height))
In your drawGradientLine function, you'll have to translate the point coordinates to the object space of the line_surf.
while (i < len(pointList)-1):
drawGradientLine(line_surf, (line_rect.x, line_rect.y), i, pointList[i], pointList[i+1], r, mode)
i += 1
def drawGradientLine(surf, offset, index, start, end, width, color_mode):
# the code leading up to where you draw the circle...
for i in xrange(dist):
x = int(start[0]+float(i)/dist*dx) - offset[0]
y = int(start[1]+float(i)/dist*dy) - offset[1]
pygame.draw.circle(surf, color, (x, y), width)
Now you'll have a surface with the drawn object blitted to it. Note that you might have to add some padding to the surface when you create it if the width of the lines you are drawing is greater than 1.
Now that you have the surface, you will want to create the mask of it.
surf_mask = pygame.mask.from_surface(line_surf)
Hopefully this isn't getting too complicated for you! Now you can either check each "active" point in the mask for collision within a rect from your player (or whatever other objects you want to collide withe drawn-in platforms), or you can create a mask from the surface of such a player object and use the pygame.Mask.overlap_area function to check for pixel-perfect collision.
# player_surf is a surface object I am imagining exists
# player_rect is a rect object I am imagining exists
overlap_count = surf_mask.overlap_area(player_surf, (line_rect.x - player_rect.x, line_rect.y - player_rect.y))
overlap_count should be a count of the number of pixels that are overlapping between the masks. If this is greater than zero, then you know there has been a collision.
Here is the documentation for pygame.Mask.overlap_area: http://www.pygame.org/docs/ref/mask.html#pygame.mask.Mask.overlap_area

Categories