I need to figure out how to control the self._newline(), in turtle.py. I found out about this during my python Mandelbrot set program, when it started doing weird things; see Why is turtle lightening pixels? for more details. However, when I tried to make an extremely similar program that graphed the tangent of complex numbers, the same thing did not happen...but the program slowed down considerably over time.
Basically, I am asking 3 questions:
What is the difference between these programs that causes this discrepancy? (intellectual inquiry)
How do I activate/stop self._newline()? (Necessary, main question)
How do I keep self._newline() from causing color deviations (DSM suggested that I insert self._pencolor() references into turtle.py, but I have no idea how to do this)? (Not necessary, but desired)
Even if you do not answer the middle question, your input will still be greatly appreciated!
Complex tangent code:
import turtle
import math
import cmath
turtle.speed(0)
def bengant(size, onelen):
turtle.left(90)
for x in range(-size*onelen, size*onelen+1):
turtle.up()
turtle.goto(x, -size*onelen-1)
turtle.down()
for y in range(-size*onelen, size*onelen+1):
c = complex(x*1.0/onelen,y*1.0/onelen)
k = cmath.tan(c)
turtle.pencolor(0,math.atan(k.real)/math.pi+1/2,math.atan(k.imag)/math.pi+1/2)
turtle.forward(1)
bengant(2,100)
x = raw_input("Press Enter to Exit")
How do I activate/stop self._newline()? (Necessary, main question)
Use penup/pendown to respectively stop/activate self.__newline
References
PyBook Chapter 13: Turtle Graphics (pdf)
What is the difference between these programs that causes this
discrepancy?
The problem happens with long monochromatic lines which don't occur often enough in your bengant() program. If I make it more monochromatic (i.e. pass 0 as third color triple instead of math.atan(k.imag) / math.pi + 1/2) it makes an appearance:
Instrumenting Python's turtle library confirms you're hitting the optimization clause at these points.
How do I activate/stop self._newline()?
You don't. The problem isn't that this optimization exists, the problem is there's something wrong in its implementation. But as you can see in your latest bengant() program, it disappears when more complexity is involved. Perhaps a bug report to the right people with the right example.
How do I keep self._newline() from causing color deviations?
As far as your benoit() code goes, you can effectively eliminate it using a line width of 1.5 instead of the default 1. It doesn't seem to affect the image quality too much:
That's 1.0 on the left, 1.5 on the right. However, your lines every 42 pixels will disappear. Another approach would be to add some random noise (small fractional additions) to your color values that don't affect it visually for humans but keep the troublesome optimization from triggering.
Here's my rework of your benoit() code with this fix and some speed optimizations:
import turtle
def benoit(onelen):
turtle.tracer(False)
turtle.left(90)
for x in range(-2 * onelen, onelen):
turtle.up()
turtle.goto(x, int(-1.5 * onelen) - 1)
turtle.down()
for y in range(int(-1.5 * onelen) - 1, int(1.5 * onelen) - 1):
z = complex(0, 0)
c = complex(x * 1.0 / onelen, y * 1.0 / onelen)
g = 0
for k in range(20):
z = z * z + c
if abs(z) > 2:
g = 0.2 + 0.8 * (20 - k) / 20
break
turtle.pencolor(0, g, 0)
turtle.forward(1)
turtle.update()
turtle.tracer(True)
turtle.setup(1000, 750)
turtle.hideturtle()
turtle.setundobuffer(None)
turtle.pensize(1.5) # work around for "42" glitch
benoit(250)
turtle.exitonclick()
Here's my rework of your bengant() code along similar lines:
import math
import cmath
import turtle
def bengant(size, onelen):
turtle.tracer(False)
turtle.left(90)
size_onelen = size * onelen
for x in range(-size_onelen, size_onelen + 1):
turtle.up()
turtle.goto(x, -size_onelen - 1)
turtle.down()
for y in range(-size_onelen, size_onelen + 1):
c = complex(x * 1.0 / onelen, y * 1.0 / onelen)
k = cmath.tan(c)
turtle.pencolor(0, math.atan(k.real) / math.pi + 1/2, math.atan(k.imag) / math.pi + 1/2)
turtle.forward(1)
turtle.update()
turtle.tracer(True)
turtle.hideturtle()
bengant(2, 100)
turtle.exitonclick()
Related
I'm trying to make a Hilbert Curve that takes as little time as possible to complete. Here is the code so far (adapted from Hilbert curve using turtle graphics and recursion)
from turtle import *
from win32api import GetSystemMetrics
def hilbert_curve(amt, facing, n, start_at_corner=True) -> None:
if start_at_corner:
ht()
up()
goto(x=(- (GetSystemMetrics(0) - 30) / 2), y=(- (GetSystemMetrics(1) / 2 - 50)))
down()
if n < 1:
return
try: # Only here because I find error messages annoying
left(facing * 90)
hilbert_curve(amt, - facing, n - 1, False)
fd(amt)
right(facing * 90)
hilbert_curve(amt, facing, n - 1, False)
fd(amt)
hilbert_curve(amt, facing, n - 1, False)
right(facing * 90)
fd(amt)
hilbert_curve(amt, - facing, n - 1, False)
left(facing * 90)
except Terminator:
from sys import exit
exit()
screen = getscreen()
speed(0)
hilbert_curve(5, 1, 15)
screen.mainloop()
The issue with this is that the turtle makes a lot of unnecessary turns - at the start and at all connections - I understand why this happens but I don't know how to fix it.
If there are any other things I can change in above code to make the turtle faster, suggestions are welcome!
Short of rethinking the program's entire approach, here's a quick fix that provides noticeable speed up. We'll use tracer() and update() to precisely control the graphics. We don't want to hide any of the drawing (which is possible with tracer()) but rather only draw when there is a line to draw, treating all the turtle's turns as internal logic calculations, displaying only the final heading:
from turtle import Screen, Turtle
def hilbert_curve(distance, facing, n):
if n < 1:
return
turtle.left(facing * 90)
hilbert_curve(distance, -facing, n - 1)
turtle.forward(distance)
screen.update()
turtle.right(facing * 90)
hilbert_curve(distance, facing, n - 1)
turtle.forward(distance)
screen.update()
hilbert_curve(distance, facing, n - 1)
turtle.right(facing * 90)
turtle.forward(distance)
screen.update()
hilbert_curve(distance, -facing, n - 1)
turtle.left(facing * 90)
screen = Screen()
screen.tracer(False)
turtle = Turtle()
turtle.hideturtle()
turtle.penup()
turtle.goto(10 - screen.window_width()/2, 20 - screen.window_height()/2)
turtle.pendown()
hilbert_curve(5, 1, 15)
screen.tracer(True)
screen.mainloop()
If, on the other hand, you don't care about watching the drawing and simply want to fill the plane as quickly as possible with a Hilbert curve, then remove the screen.update() calls from the above code and change this line:
screen.tracer(False)
to instead be:
screen.tracer(1000)
to fill the window with the fractal in a few seconds.
I'd like to draw a series of nested triangles using recursion.
My faulty code below:
def recursiveTri(x, y, shrink):
tt.penup()
tt.setx(x)
tt.sety(y)
if x > -10:
return
for element in range(3):
tt.pendown()
tt.forward(x)
tt.right(120)
recursiveTri(x + shrink, y - shrink, shrink)
def main():
recursiveTri(-300,300,30)
main()
The current code produces the following:
Here is what I mean by nested shapes, except that I would like to draw triangles instead of squares:
And now, just for fun, the "better living through stamping" solution:
import turtle
CURSOR_SIZE = 20
def recursiveTri(side, shrink):
if side > 10:
turtle.shapesize(side / CURSOR_SIZE)
turtle.stamp()
recursiveTri(side - shrink, shrink)
turtle.hideturtle()
turtle.shape('triangle')
turtle.fillcolor('white')
recursiveTri(300, 30)
turtle.dot()
turtle.exitonclick()
This is the default orientation, you can turn it anyway you want before calling recursiveTri(). Stamping is an alternative to drawing that works best with simple geometric patterms like this.
The problem is with
tt.forward(x)
Remember that your x is always negative, and the length of a side is not x, but rather -2 * x, if you want to be symmetrical about zero. Since your triangles are nested, you can also compute the initial y from x, given that it is 1/3 of the way up the main bisector. -sqrt(3) / 3 * x therefore puts the center of the circles circumscribed around and inscribed in your triangle at 0, 0.
In fact, it is probably easier to just fix the length of a side, and compute x and y from that:
import turtle as tt
from math import sqrt
def recursiveTri(side, shrink):
if side < 10: return
tt.penup()
tt.goto(-side / 2, sqrt(3) / 6 * side)
tt.pendown()
for _ in range(3):
tt.forward(side)
tt.right(120)
recursiveTri(side - shrink, shrink)
tt.penup()
tt.home()
tt.dot()
recursiveTri(300, 30)
In this case, shrink is the total amount removed from each side. If you want it to be the amount you step forward, change the recursive call to recursiveTri(side - 2 * shrink, shrink).
The result (without multiplying shrink by 2) is
I'm new to this, but I'm trying to create a program that goes on a Random Walk in turtle and I have no idea what I'm doing.This is the program that I need to create Requirements, and this is what I have so far Code. I'm starting to get a little lost, and am wondering if I 'm headed towards the right direction or if i need to scrap it and start all over again
Thanks,
import turtle
import random
import math
def start():
myS = turtle.Screen()
myS.title("Random Walk")
border = 50
myWin.setworldcoordinates(-border, -border, border, border)
def go(heading, step_size):
turtle.setheading(heading)
turtle.forward(step_size)
def random_walk(step_size, steps):
angle = random.random() * 2 * math.pi
x = 0
y = 0
x = x + math.cos(angle)
y = y + math.sin(angle)
coord = (x, y)
for _ in range(steps):
go(random.choice(coord), step_size)
if __name__ == '__main__':
turtle.hideturtle()
turtle.speed('fastest')
random_walk(15, 1000)
Your random_walk function does way to many things; I think it grew over your head a little. What it should do for every step:
Calculate a random angle between 0 and 2*math.pi
Calculate x and y with the given formulas
Call turtle.goto(x,y) to go to these coordinates
Also there was a little typo in start.
import turtle
import random
import math
def start():
myS = turtle.Screen()
myS.title("Random Walk")
border = 50
myS.setworldcoordinates(-border, -border, border, border)
def go(heading, step_size):
turtle.setheading(heading)
turtle.forward(step_size)
def random_walk(steps):
x = 0
y = 0
for i in range(steps):
angle = random.random()*2*math.pi
x = x + math.cos(angle)
y = y + math.sin(angle)
turtle.goto(x,y)
if __name__=="__main__":
start()
random_walk(100)
Because this looks like a homework assignment I don't want to just fix your errors but instead give you pointers to the right solution. I will also hide hints behind spoilers...
if you run random_walk() with e.g. parameters random_walk(1, 1) your will notice that the direction of the walk will not appear random. Use some form of debugging to find out why that is
your go() functions expects an angle, look at what random.choice() produces
if you fix the issue number 1. you will see that the turtle will not draw a random line at all again
looks like the angle does not change every step. find a solution that updates the angle before each step!
units of angles: find out what unit setheading() expects: hint
your requirements mention the output of straight line distance and distance travelled. This somehow sounds like you need some form of tally variable (one or several). hint: if you read the turtle documentation carefully you might find a funtion that maybe makes this task easier
read the documentation for turtle.position().
I'm using Python Turtles to draw a circle using forward() and right().
I have a for loop counting from 0 to 359, and each time it triggers, it moves the turtle forward 1 and right 1.
But the problem is I need specific diameters. I am nearly 100% sure I'll need to use trig, but I've tried to no avail.
I can't figure out the math how to do it. We're supposed to use forward() and right(), NOT circle().
Thanks!
Here is a working example:
import turtle
import math
def circle(radius):
turtle.up()
# go to (0, radius)
turtle.goto(0,radius)
turtle.down()
turtle.color("black")
# number of times the y axis has been crossed
times_crossed_y = 0
x_sign = 1.0
while times_crossed_y <= 1:
# move by 1/360 circumference
turtle.forward(2*math.pi*radius/360.0)
# rotate by one degree (there will be
# approx. 360 such rotations)
turtle.right(1.0)
# we use the copysign function to get the sign
# of turtle's x coordinate
x_sign_new = math.copysign(1, turtle.xcor())
if(x_sign_new != x_sign):
times_crossed_y += 1
x_sign = x_sign_new
return
circle(100)
print('finished')
turtle.done()
Well, a complete circle is 360°, and you are planning on turning 360 times, so each turn should be:
right( 360 ° / 360 ), or
right(1)
The distance traveled will be one circumference, or π * diameter, so your forward might be:
forward( diameter * π / 360 )
I haven't tested this yet -- give it a try and see how it works.
This is one of the exercises in "Think Python," in chapter 4. It really is a horrible exercise to have this early in the book, especially with the "hint" given. I'm using forward and left here, but you can switch left with right.
You should have the polygon function:
def polygon(t, length, n):
for i in range(n):
bob.fd(length)
bob.lt(360 / n)
Then you create a circle function:
def circle(t):
polygon(t, 1, 360)
That will draw a circle, no radius needed. The turtle goes forward 1, then left 1 (360 / 360), 360 times.
Then, if you want to make the circle bigger, you calculate the circumference of the circle. The hint says:
Hint: figure out the circumference of the circle and make sure that
length * n = circumference.
Ok, so the formula for circumference = 2 * pi * radius. And the hint says length * n = circumference. n = 360 (number of sides/degrees). We have circumference, so we need to solve for length.
So:
def circle(t, r):
circumference = 2 * 3.14 * r
length = circumference / 360
polygon(t, length, 360)
Now, call the function with whatever radius you want:
circle(bob, 200)
I was looking at the wikipedia page for the Koch Snowflake (here) and was bothered by the all the examples all being in the logo/turtle style. So i set out to make my own that returned a list or coordinates.
My implementation is in python and i basically ripped off the python turtle implementation but replaced the turtle specific stuff with basic trig. It resulted in some ugly code. My challenge for you is to either improve my code or come up with a more elligant solution of your own. It can be in python, or your favorite language.
My Code:
from math import sin, cos, radians
def grow(steps, length = 200, startPos = (0,0)):
angle = 0
try:
jump = float(length) / (3 ** steps)
except:
jump = length
set="F"
for i in xrange(steps): set=set.replace("F", "FLFRFLF")
coords = [startPos]
for move in set:
if move is "F":
coords.append(
(coords[-1][0] + jump * cos(angle),
coords[-1][1] + jump * sin(angle)))
if move is "L":
angle += radians(60)
if move is "R":
angle -= radians(120)
return coords
EDIT: due to lazy copying, i forgot the import
I don't see it as particularly ugly and I'd only refactor it incrementally, e.g. as a first step (I've removed the try/except because I don't know what you're trying to ward against... if it needs to get back in it should be a bit more explicit, IMHO):
import math
angles = [math.radians(60*x) for x in range(6)]
sines = [math.sin(x) for x in angles]
cosin = [math.cos(x) for x in angles]
def L(angle, coords, jump):
return (angle + 1) % 6
def R(angle, coords, jump):
return (angle + 4) % 6
def F(angle, coords, jump):
coords.append(
(coords[-1][0] + jump * cosin[angle],
coords[-1][1] + jump * sines[angle]))
return angle
decode = dict(L=L, R=R, F=F)
def grow(steps, length=200, startPos=(0,0)):
pathcodes="F"
for i in xrange(steps):
pathcodes = pathcodes.replace("F", "FLFRFLF")
jump = float(length) / (3 ** steps)
coords = [startPos]
angle = 0
for move in pathcodes:
angle = decode[move](angle, coords, jump)
return coords
If a second step was warranted I'd probably roll this functionality up into a class, but I'm not sure that would make things substantially better (or, better at all, in fact;-).
I liked your question so much that I posted an answer to it as a new question, so that other people could improve it:
https://stackoverflow.com/questions/7420248
I used no Logo/Turtle stuff, neither trigonometry.
Congrats for being the first one bringing this problem to StackOverflow!
Mathematica is superior when it comes to math stuff:
points = {{0.0, 1.0}};
koch[pts_] := Join[
pts/3,
(RotationMatrix[60 Degree].#/3 + {1/3, 0}) & /# pts,
(RotationMatrix[-60 Degree].#/3 + {1/2, 1/Sqrt[12]}) & /# pts,
(#/3 + {2/3, 0}) & /# pts
];
Graphics[Line[Nest[koch, points, 5]], PlotRange -> {{0, 1}, {0, 0.3}}] //Print
Something to consider, if not for your implementation then for testing your implementation, is that Python turtle can record what it's doing and give you back the coordinates. You use begin_poly() and end_poly() around the code you want to record and then use get_poly() afterwards to get the points.
In this example, I'll draw the snowflake based on code from this site then register those coordinates back as a new turtle shape that I'll randomly (and quickly) stamp about the screen:
import turtle
from random import random, randrange
def koch_curve(turtle, steps, length):
if steps == 0:
turtle.forward(length)
else:
for angle in [60, -120, 60, 0]:
koch_curve(turtle, steps - 1, length / 3)
turtle.left(angle)
def koch_snowflake(turtle, steps, length):
turtle.begin_poly()
for _ in range(3):
koch_curve(turtle, steps, length)
turtle.right(120)
turtle.end_poly()
return turtle.get_poly()
turtle.speed("fastest")
turtle.register_shape("snowflake", koch_snowflake(turtle.getturtle(), 3, 100))
turtle.reset()
turtle.penup()
turtle.shape("snowflake")
width, height = turtle.window_width() / 2, turtle.window_height() / 2
for _ in range(24):
turtle.color((random(), random(), random()), (random(), random(), random()))
turtle.goto(randrange(-width, width), randrange(-height, height))
turtle.stamp()
turtle.done()
You can have the pen up and turtle hidden during polygon generation if you don't want this step to be seen by the user.