I'm currently trying to replicate this image:
https://imgur.com/a/IZIPGkg
I'm trying to make that gradient in the background but I have zero clue how to do it and there's basically nothing on the internet.
Edit: I have the RGB colors for both ends if that helps. The top is rgb(154,0,254) and the bottom is rgb(221,122,80).
Crude but resonably quick and effective:
from turtle import Screen, Turtle
COLOR = (0.60156, 0, 0.99218) # (154, 0, 254)
TARGET = (0.86328, 0.47656, 0.31250) # (221, 122, 80)
screen = Screen()
screen.tracer(False)
WIDTH, HEIGHT = screen.window_width(), screen.window_height()
deltas = [(hue - COLOR[index]) / HEIGHT for index, hue in enumerate(TARGET)]
turtle = Turtle()
turtle.color(COLOR)
turtle.penup()
turtle.goto(-WIDTH/2, HEIGHT/2)
turtle.pendown()
direction = 1
for distance, y in enumerate(range(HEIGHT//2, -HEIGHT//2, -1)):
turtle.forward(WIDTH * direction)
turtle.color([COLOR[i] + delta * distance for i, delta in enumerate(deltas)])
turtle.sety(y)
direction *= -1
screen.tracer(True)
screen.exitonclick()
Related
Here's my attempt:
from turtle import *
speed(100)
pensize(4)
color("black", "yellow")
begin_fill()
for i in range(4):
fd(200)
lt(90)
end_fill()
up()
fd(100)
lt(90)
fd(100)
down()
colors = ["red", "blue" , "green" , "violet" ]
for i in range(4):
color("black",colors[i])
begin_fill()
circle(50)
rt(90)
end_fill()
ht()
Here's the expected result:
What am I doing wrong here?
An alternate approach, using stamping instead of drawing:
from turtle import Screen, Turtle
SIZE = 50
CURSOR_SIZE = 20
COLORS = ['red', 'blue', 'lime', 'yellow']
screen = Screen()
turtle = Turtle()
turtle.speed('fastest')
turtle.shape('circle')
turtle.shapesize(SIZE*2 / CURSOR_SIZE, outline=4)
turtle.penup()
for color in COLORS:
turtle.fillcolor(color)
turtle.stamp()
turtle.backward(SIZE)
turtle.left(90)
turtle.hideturtle()
screen.exitonclick()
You're pretty close. You can see that there's a 45-degree rotation relative to your drawing, and the colors are a bit off.
My strategy is this: draw a square around the origin with each corner counterclockwise a colored circle.
import turtle
size = 50
colors = (1, 0, 0), (0, 0, 1), (0, 1, 0), (1, 1, 0)
t = turtle.Turtle()
t.speed("fastest")
t.pensize(4)
t.penup()
t.goto(size / 2, size / 2)
for color in colors:
t.color("black", color)
t.pendown()
t.begin_fill()
t.circle(size)
t.end_fill()
t.penup()
t.left(90)
t.forward(size)
t.hideturtle()
turtle.exitonclick()
I prefer a turtle instance rather than the namespace-polluting from turtle import * which can lead to subtle bugs.
I recommend using the long version of the commands for clarity.
Also, speed(100) isn't any faster than the maximum of 0 or "fastest", according to the docs.
I'm being really bugged by a task:
User inputs radius r and then turtle draws the circle then proceeds to draw another circle with the same center but 10 px smaller until the radius is 0
First let us approximate a circle as a regular polygon with 36 sides/segments.
To draw this shape given a radius r we need to know;
The length of each segment
The angle to turn between each segment
To calculate the length, we first need the circumference, which is 2πr (we will approximate pi as 3.1415), giving us
circumference = 2 * 3.1415 * radius
Next we divide this by the number of segments we are approximating, giving
circumference = 2 * 3.1415 * radius
seg_lenght = circumferece/36
Now we need the angle difference between the segments, or the external angle. This is simply 360/n for a regular n-gon(polygon with n sides), so we do 360/36 = 10
We can now define a function to generate the segment length and draw the circle:
def circle_around_point(radius):
circumference = 2 * 3.1415 * radius
seg_length = circumference/36
penup()
fd(radius) #Move from the centre to the circumference
right(90) #Face ready to start drawing the circle
pendown()
for i in range(36): #Draw each segment
fd(seg_length)
right(10)
penup()
right(90) #Face towards the centre of the circle
fd(radius) #Go back to the centre of the circle
right(180) #Restore original rotation
pendown()
Now for the concentric circles:
def concentric_circles(radius):
while radius > 0:
circle_around_point(radius)
radius -= 10
It's not clear why #IbraheemRodrigues felt the need to recode turtle's circle() function based on your problem description, but we can simplify his solution by not reinventing the wheel:
def circle_around_point(turtle, radius):
is_down = turtle.isdown()
if is_down:
turtle.penup()
turtle.forward(radius) # move from the center to the circumference
turtle.left(90) # face ready to start drawing the circle
turtle.pendown()
turtle.circle(radius)
turtle.penup()
turtle.right(90) # face awary from the center of the circle
turtle.backward(radius) # go back to the center of the circle
if is_down:
turtle.pendown() # restore original pen state
def concentric_circles(turtle, radius):
for r in range(radius, 0, -10):
circle_around_point(turtle, r)
The key to circle() is that the current position is on the edge of the circle so you need to shift your position by the radius to make a specific point the center of the circle.
However, to solve this problem, I might switch from drawing to stamping and do it this way to speed it up and simplify the code:
import turtle
STAMP_SIZE = 20
radius = int(input("Please input a radius: "))
turtle.shape('circle')
turtle.fillcolor('white')
for r in range(radius, 0, -10):
turtle.shapesize(r * 2 / STAMP_SIZE)
turtle.stamp()
turtle.mainloop()
However, this draws crude circles as it's blowing up a small one:
To fix that, I might compromise between the two solutions above and do:
import turtle
radius = int(input("Please input a radius: "))
turtle.penup()
turtle.forward(radius)
turtle.left(90)
turtle.pendown()
turtle.begin_poly()
turtle.circle(radius)
turtle.penup()
turtle.end_poly()
turtle.addshape('round', turtle.get_poly()) # 'circle' is already taken
turtle.right(90)
turtle.backward(radius)
turtle.shape('round')
turtle.fillcolor('white')
for r in range(radius - 10, 0, -10):
turtle.shapesize(r / radius)
turtle.stamp()
turtle.mainloop()
This improves circle quality by shrinking a large one instead of enlarging a small one:
Where quality of the circle can be controlled using the steps= argument to the call to circle().
But, if I really wanted to minimize code while keeping quality high and speed fast, I might do:
import turtle
radius = int(input("Please input a radius: "))
for diameter in range(radius * 2, 0, -20):
turtle.dot(diameter, 'black')
turtle.dot(diameter - 2, 'white')
turtle.hideturtle()
turtle.mainloop()
The dot() method draws from the center instead of the edge, uses diameters instead of radii, draws only filled circles, and seems our best solution to this particular exercise:
import turtle
#### ##### #### Below class draws concentric circles.
class Circle:
def __init__(self, pen, cx, cy, radius):
self.pen = pen
self.cx = cx
self.cy = cy
self.radius = radius
def drawCircle(self):
self.pen.up()
self.pen.setposition( self.cx, self.cy - self.radius )
self.pen.down()
self.pen.circle(self.radius)
def drawConCircle(self, minRadius = 10, delta = 10):
if( self.radius > minRadius ) :
self.drawCircle()
self.radius -= delta # reduce radius of next circle
self.drawConCircle()
#### End class circle #######
win = turtle.Screen()
win.bgcolor("white")
s = Circle( turtle.Turtle(), 0, 0, 200 )
s.drawConCircle()
win.exitonclick()
I'm looking for best way to automatically find starting position for new turtle drawing so that it would be centered in graphics window regardless of its size and shape.
So far I've developed a function that checks with each drawn element turtle position to find extreme values for left, right, top and bottom and that way I find picture size and can use it to adjust starting position before releasing my code. This is example of simple shape drawing with my picture size detection added:
from turtle import *
Lt=0
Rt=0
Top=0
Bottom=0
def chkPosition():
global Lt
global Rt
global Top
global Bottom
pos = position()
if(Lt>pos[0]):
Lt = pos[0]
if(Rt<pos[0]):
Rt= pos[0]
if(Top<pos[1]):
Top = pos[1]
if(Bottom>pos[1]):
Bottom = pos[1]
def drawShape(len,angles):
for i in range(angles):
chkPosition()
forward(len)
left(360/angles)
drawShape(80,12)
print(Lt,Rt,Top,Bottom)
print(Rt-Lt,Top-Bottom)
This method does work however it seems very clumsy to me so I would like to ask more experiences turtle programmers is there a better way to find starting position for turtle drawings to make them centered?
Regards
There is no universal method to center every shape (before you draw it and find all your max, min points).
For your shape ("almost" circle) you can calculate start point using geometry.
alpha + alpha + 360/repeat = 180
so
alpha = (180 - 360/repeat)/2
but I need 180-alpha to move right (and later to move left)
beta = 180 - aplha = 180 - (180 - 360/repeat)/2
Now width
cos(alpha) = (lengt/2) / width
so
width = (lengt/2) / cos(alpha)
Because Python use radians in cos() so I need
width = (length/2) / math.cos(math.radians(alpha))
Now I have beta and width so I can move start point and shape will be centered.
from turtle import *
import math
# --- functions ---
def draw_shape(length, repeat):
angle = 360/repeat
# move start point
alpha = (180-angle)/2
beta = 180 - alpha
width = (length/2) / math.cos(math.radians(alpha))
#color('red')
penup()
right(beta)
forward(width)
left(beta)
pendown()
#color('black')
# draw "almost" circle
for i in range(repeat):
forward(length)
left(angle)
# --- main ---
draw_shape(80, 12)
penup()
goto(0,0)
pendown()
draw_shape(50, 36)
penup()
goto(0,0)
pendown()
draw_shape(70, 5)
penup()
goto(0,0)
pendown()
exitonclick()
I left red width on image.
I admire #furas' explanation and code, but I avoid math. To illustrate that there's always another way to go about a problem here's a math-free solution that produces the same concentric polygons:
from turtle import Turtle, Screen
def draw_shape(turtle, radius, sides):
# move start point
turtle.penup()
turtle.sety(-radius)
turtle.pendown()
# draw "almost" circle
turtle.circle(radius, steps=sides)
turtle = Turtle()
shapes = [(155, 12), (275, 36), (50, 5)]
for shape in shapes:
draw_shape(turtle, *shape)
turtle.penup()
turtle.home()
turtle.pendown()
screen = Screen()
screen.exitonclick()
How can I draw a rectangle where: the smallest is 5 in height and each consecutive rectangle adds the first rectangles height, i.e. 5, 10, 15, …. The width of each rectangle is 200. The color of the lines are blue and the fill color, from top to bottom is grey0, grey14, grey28, ….
How can I do this and make sure the picture fits onto the screen?
(Each new rectangle goes below the previous one)
This is what I have so far, but I don't know how I can fill it:
import turtle
def rectangle(t, l, w):
for i in range(2):
t.right(90)
t.forward(l)
t.right(90)
t.forward(w)
me = turtle.Turtle()
me.color('blue')
me.pensize(2)
me.penup()
l = 2.5
w = 250
x = 50
y = 150
for i in range(9):
rectangle(me, l, w)
l = l*2
w = w
x = x
y = y
me.setposition(x,y)
me.pendown()
def filled_rectangle(t, l, w):
t.begin_fill()
for i in range(2):
t.right(90)
t.forward(l)
t.right(90)
t.forward(w)
t.end_fill()
Filling the rectangles is simple as #JoranBeasley has addressed. However, your specification of "smallest is 5" and "make sure the picture fits onto the screen" are in conflict. We need to fit the rectangles to the screen and take whatever starting size we get. Since each rectangle is twice the height of the next, then the starting rectangle is the available height divided by 2 (since we're doubling) raised to the power of the number of grey shades you want to represent:
from turtle import Turtle, Screen
def rectangle(t, l, w):
t.begin_fill()
for _ in range(2):
t.right(90)
t.forward(l)
t.right(90)
t.forward(w)
t.end_fill()
screen = Screen()
me = Turtle(visible=False)
me.penup()
GREYS = [ # adjust to taste
('grey0' , '#000000'),
('grey14', '#242424'),
('grey28', '#474747'),
('grey42', '#6B6B6B'),
('grey56', '#8F8F8F'),
('grey70', '#B3B3B3'),
('grey84', '#D6D6D6'),
('grey98', '#FAFAFA'),
]
WIDTH = 2 ** (len(GREYS) + 1) # depends on font and keep below screen.window_width()
x = WIDTH / 2 # rectangle() draws right to left -- move x right to center drawing
canvas_height = screen.window_height() * 0.90 # use most of the space available
length = canvas_height / 2 ** len(GREYS) # determine starting length to fill canvas
y = canvas_height / 2 # begin at the top of canvas
fontsize = 1
for name, color in GREYS:
me.fillcolor(color)
me.setposition(x, y)
me.pendown()
rectangle(me, length, WIDTH)
me.penup()
if 4 <= fontsize <= length:
font = ("Arial", fontsize, "bold")
me.setposition(0, y - length / 2 - fontsize / 2)
me.write(name, align="center", font=font)
fontsize *= 2
y -= length
length *= 2
screen.exitonclick()
The width is more arbitrary than the height but I made it a function of the fontsize and the doubling so I could write the shade names in the rectangles:
I reverted the outline color to black instead of blue so there'd be pure black nearby to compare the grey shades against.
I need to draw a square given a center point using the turtle module.
def drawCentSq(t,center,side):
xPt=center[0]
yPt=center[1]
xPt-=int(side/side)
yPt+=int(side/side)
t.up()
t.goto(xPt,yPt)
t.down()
for i in range(4):
t.forward(side)
t.right(90)
def main():
import turtle
mad=turtle.Turtle()
wn=mad.getscreen()
print(drawCentSq(mad,(0,0),50))
main()
I'm having a hard time making my turtle go to the right starting point.
You need:
xPt-=int(side/2.0)
yPt+=int(side/2.0)
As it was you were just += and -= 1.
I need to draw a square given a center point using the turtle module.
As #seth notes, you can do this by fixing the center calculation in your code:
from turtle import Turtle, Screen
def drawCentSq(turtle, center, side):
""" A square is a series of perpendicular sides """
xPt, yPt = center
xPt -= side / 2
yPt += side / 2
turtle.up()
turtle.goto(xPt, yPt)
turtle.down()
for _ in range(4):
turtle.forward(side)
turtle.right(90)
yertle = Turtle()
drawCentSq(yertle, (0, 0), 50)
screen = Screen()
screen.exitonclick()
But let's step back and consider how else we can draw a square at a given point of a given size. Here's a completely different solution:
def drawCentSq(turtle, center, side):
""" A square is a circle drawn at a rough approximation """
xPt, yPt = center
xPt -= side / 2
yPt -= side / 2
turtle.up()
turtle.goto(xPt, yPt)
turtle.right(45)
turtle.down()
turtle.circle(2**0.5 * side / 2, steps=4)
turtle.left(45) # return cursor to original orientation
And here's yet another:
STAMP_UNIT = 20
def drawCentSq(turtle, center, side):
""" A square can be stamped directly from a square cursor """
mock = turtle.clone() # clone turtle to avoid cleaning up changes
mock.hideturtle()
mock.shape("square")
mock.fillcolor("white")
mock.shapesize(side / STAMP_UNIT)
mock.up()
mock.goto(center)
return mock.stamp()
Note that this solution returns a stamp ID that you can pass to yertle's clearstamp() method to remove the square from the screen if/when you wish.