I'm coding in python. I've created a spiral with my current for loop below. I want to create tiny spiral off-shoots around every 10 degrees. The problem is I don't know how to do that. I'm trying to do something like this:
My question is how would I create the off-shoots? Is this even possible or not?
Any suggestions are greatly appreciated.
import turtle
import math
me = turtle.Turtle()
def miniSpiral():
for i in range(0,360):
x,y = me.position()
me.left(1)
if x%10==0:
x2,y2 = me.forward(((5*i)+5)*math.pi/360)
else:
x2,y2= me.forward(5*math.pi/360)
me.goto((x+x2),(y+y2))
for x2 in range(0,720):
me.left(1)
if x2%10==0:
me.forward(((10*x2)+10)*math.pi/360)
#miniSpiral()
me.forward(10*math.pi/360)
In general, the easiest way to draw a fractal programmatically is to use recursion. Start with the code to draw one "segment" of your fractal. In the image you linked to, that would be one 90-degree piece of the spiral (since that's the distance between the branchings).
Once you have code that can draw one segment, you add a recursive call to it. Give it some parameter (e.g. the initial size), and have the recursive call reduce the value passed on to the next call. Add a base case where a call with that parameter set too small gets skipped (e.g. if size < 1: return) so that the recursion doesn't go on forever.
Now you can add branching. Instead of just one recursive call, make two. You'll need to add some extra logic to move the position of the turtle in between the calls (so the second one starts at roughly the same spot as the first), but that shouldn't be too hard. To make the two branches distinct, vary their initial position or angle, or give them different parameters. In your example image, the "extra" branches all start going the oposite direction from the "main" branch, and they start smaller.
Here's a pseudo-code implementation of the spiral you want (I'm not adding actual turtle code because you seem to be using a different turtle module than the one I have from the standard library):
def spiral(size):
if size < 1: return # base case
draw_segment(size) # this draws a 90 degree piece of the spiral
position = getposition() # save state so we can back up after the first recursive call
angle = getangle()
spiral(size - 1) # main branch of the recursion
penup() # restore state (mostly)
setposition(position)
pendown()
setangle(angle + 180) # we want to start facing the other direction for the second branch
spiral(size - 2) # extra branch of the recursion
You can play around with the details (like how you modify the size for the recursive calls) to suit your tastes or the fractal design you're looking for. For instance, you could multiply the size by some factor (e.g. size * 0.75) rather than subtracting a fixed amount.
Each mini spiral is just a smaller version of the original spiral, as such you can carefully call the original function to make each smaller one.
import turtle
import math
def spiral(steps, left, forward, level):
for step in range(50, steps):
me.left(left)
me.forward(step / forward)
if step % 200 == 0 and level > 0:
x, y = me.position()
heading = me.heading()
spiral((steps * 2) / 3, -left * 1.2, forward * 1.2, level - 1)
me.up()
me.setpos(x, y)
me.setheading(heading)
me.down()
#turtle.tracer(5, 200) # to speed things up
me = turtle.Turtle()
spiral(800, 0.6, 200.0, 4)
Each time spiral() is called, the arguments are modified slightly and the level is reduced by 1. Also the left argument is made negative which has the effect of changing the direction for each sub spiral. Each sub spiral is called after 200 steps. Each sub spiral has 2/3rds of the original steps and so on.... lots of numbers to play with to see how they effect the outcome. When the sub spiral finishes drawing, it jumps back to the point where it started drawing it an continues with the original spiral.
Calling it with a level of 0 would give you a single simple spiral for example.
This would give you the following type of output:
Related
I'm working in turtle graphics to recreate this pattern:
This is probably a very basic question, but is there a simpler way for me to create that rotated square within a square shape? As it is, I just use one turtle to make a normal square, then slowly move a second turtle into position to draw the rotated part. Ex:
import turtle
alex = turtle.Turtle()
tess = turtle.Turtle()
for i in range(4):
alex.fd(50)
alex.lt(90)
tess.pu()
tess.fd(25)
tess.rt(90)
tess.fd(10)
tess.rt(225)
tess.pd()
for i in range(4):
tess.fd(50)
tess.lt(90)
Which to me is clunky at best, and doesn't work if I change the side-lengths (which I intend to do).
Thanks very much in advance!
This is a great time to start using functions! Using functions you can create a reusable chunk of code which can repeat a certain task - for example, drawing a square or a square-in-square shape.
Lets take your code and add a square function which draws a square of a certain size. To do this, we'll tell the function which turtle to use, as well as the size of the square:
def square(this_turtle, side_length):
for i in range(4):
this_turtle.fd(side_length)
this_turtle.lt(90)
Now let's use the new method in your code:
square(alex, 50)
tess.pu()
tess.fd(25)
tess.rt(90)
tess.fd(10)
tess.rt(225)
tess.pd()
square(tess, 50)
From here you can then think about how you could make a star function which makes a "square-in-square" shape of any given size. Good luck!
Here's a more detailed explaination on how you can use functions: http://openbookproject.net/thinkcs/python/english3e/functions.html (I suspect this is the tutorial you're already following!)
I'm going to suggest a contrary approach to yours and the other answers which are too focused on drawing squares which will take too much work to complete. Since this is a repeating pattern, I think stamping is the way to go, just like repeated patterns in real life. Specifically:
from turtle import Turtle, Screen
BASE_UNIT = 20
def tessellate(turtle, start, stop, step):
for x in range(start, stop + 1, step):
for y in range(start, stop + 1, step):
turtle.goto(x * BASE_UNIT, y * BASE_UNIT)
turtle.stamp()
turtle.left(45)
turtle.stamp()
alex = Turtle(shape="square")
alex.shapesize(8)
alex.color("red")
alex.penup()
tessellate(alex, -12, 12, 12)
tess = Turtle(shape="square")
tess.shapesize(4)
tess.color("gold")
tess.penup()
tessellate(tess, -6, 6, 12)
screen = Screen()
screen.exitonclick()
OUTPUT
Turtle stamps naturally rotate and scale which turtle drawings do not!
One thing you'll notice is that my pattern isn't quite the same. In the original the two red (or yellow) squares that make up a star are not the same size! They are slightly different to make the pattern work -- I leave it as an exercise for the OP to correct this.
Learn how to write a function; this is an excellent place to start. Write a function to draw a square of a given size, assuming that the turtle is presently at the starting point and facing the proper direction. Then put your square-drawing loop inside the function:
def draw_square(tortuga, size):
for i in range(4):
tortuga.fd(size)
tortuga.lt(90)
This will remove the drawing details from your main code.
The next thing you do is to write more general code to make Tess follow Alex to the proper spot -- or to make Alex move after finishing the first square, doing the second one himself.
I'm currently working with the turtle library of python.
I'm working on my midterm project for my coding class and my project is to draw cos, sin, and tangent curves using turtle as well as their inverse functions.
My problem is that when I'm coding inverse sin, the graph shows up way too small and is impossible to be seen by the user. I was wondering if there was a zoom function or a way to stretch the graph to make it bigger?
Here is my code for arcsin:
def drawarcsincurve(amplitude, period, horShift, verShift):
turtle.speed(0)
startPoint = -1
turtle.goto(startPoint, math.asin(startPoint))
turtle.pendown()
for angles in range(-1,1):
y = math.asin(angles)
turtle.goto(angles,y)
Your main problem here, I think, is the range over which your are iterating your angles variable. The line
for angles in range(-1,1):
will execute the loop only twice, with angle == 1 and angle == 0 - i.e. it is equivalent to using
for angles in [-1,0]:
Type range(-1,1) in a Python interpreter window to see what I mean.
You might be getting confused over names as well. You call your loop variable angles, but it's actually representing a ratio (the sine value whose inverse you are calculating).
What you probably want really is something that iterates over the range -1 to 1 in fairly small steps. Lets choose 0.01 as our step (that's arbitrary)
I've altered your code directly rather than doing my own implementation.
I've put in a scale factor (plot_scale) which is equivalent to the zoom that I think you want in your original question.
I've left your original function arguments in, although I don't use them. I thought you might want to play with them later.
def drawarcsincurve(amplitude, period, horShift, verShift):
plot_scale = 100 # Arbitrary value - up to you - similar to "zoom"
turtle.speed(1)
turtle.penup()
startPoint = -1
turtle.goto(plot_scale*startPoint, plot_scale*math.asin(startPoint))
turtle.pendown()
for angles in range(-100,100):
sinval = 1.0 * angles / 100 # this will run -1 to 1 in 0.01 steps
y = math.asin(sinval)
turtle.goto(plot_scale*sinval,plot_scale*y)
This outputs:
So Panda3D has this option to update the starting position of a Lerp interval by using the parameter "bakeInStart", if we set it equal to 0. However, I can't find a solution to overcome this problem, which is updating the end HPR (yaw, pitch, roll) value each time there is a new iteration of the Lerp interval loop. I've managed to work around this to get the desired effect, but a whole screen of code had to be written (because of multiple intervals written in sequence). I've tried this so far to minimize code:
self.rotateTargets(0)
def rotateTargets(self, angle):
# Create Lerp intervals for each target orbit node to rotate all of the targets
# in a parallel sequence with easing
self.angle = angle
self.rotateTargets = Sequence(
Parallel(
LerpHprInterval(self.orbitNodes["orbit0"], 4, Point3(0, 0, self.angle+90), blendType = "easeInOut", bakeInStart = 0),
name = "rotateTargets0"), Func(self.targetReparent(self.orbitNodes["orbit0"].getR()))
self.rotateTargets.loop()
def targetReparent(self, newAngle):
# Define new angle
self.newAngle = newAngle
self.angle = self.newAngle
An assertion error appears in the console, but it is related to the imported functions, and not my code.
I am trying to estimate the value of pi using a monte carlo simulation. I need to use two unit circles that are a user input distance from the origin. I understand how this problem works with a single circle, I just don't understand how I am meant to use two circles. Here is what I have got so far (this is the modified code I used for a previous problem the used one circle with radius 2.
import random
import math
import sys
def main():
numDarts=int(sys.argv[1])
distance=float(sys.argv[2])
print(montePi(numDarts,distance))
def montePi(numDarts,distance):
if distance>=1:
return(0)
inCircle=0
for I in range(numDarts):
x=(2*(random.random()))-2
y=random.random()
d=math.sqrt(x**2+y**2)
if d<=2 and d>=-2:
inCircle=inCircle+1
pi=inCircle/numDarts*4
return pi
main()
I need to change this code to work with 2 unit circles, but I do not understand how to use trigonometry to do this, or am I overthinking the problem? Either way help will be appreciated as I continue trying to figure this out.
What I do know is that I need to change the X coordinate, as well as the equation that determines "d" (d=math.sqrt(x*2+y*2)), im just not sure how.
These are my instructions-
Write a program called mcintersection.py that uses the Monte Carlo method to
estimate the area of this shape (and prints the result). Your program should take
two command-line parameters: distance and numDarts. The distance parameter
specifies how far away the circles are from the origin on the x-axis. So if distance
is 0, then both circles are centered on the origin, and completely overlap. If
distance is 0.5 then one circle is centered at (-0.5, 0) and the other at (0.5, 0). If
distance is 1 or greater, then the circles do not overlap at all! In that last case, your
program can simply output 0. The numDarts parameter should specify the number
of random points to pick in the Monte Carlo process.
In this case, the rectangle should be 2 units tall (with the top at y = 1 and the
bottom at y = -1). You could also safely make the rectangle 2 units wide, but this
will generally be much bigger than necessary. Instead, you should figure out
exactly how wide the shape is, based on the distance parameter. That way you can
use as skinny a rectangle as possible.
If I understand the problem correctly, you have two unit circles centered at (distance, 0) and (-distance, 0) (that is, one is slightly to the right of the origin and one is slightly to the left). You're trying to determine if a given point, (x, y) is within both circles.
The simplest approach might be to simply compute the distance between the point and the center of each of the circles. You've already done this in your previous code, just repeat the computation twice, once with the offset distance inverted, then use and to see if your point is in both circles.
But a more elegant solution would be to notice how your two circles intersect each other exactly on the y-axis. To the right of the axis, the left circle is completely contained within the right one. To the left of the y-axis, the right circle is entirely within the left circle. And since the shape is symmetrical, the two halves are of exactly equal size.
This means you can limit your darts to only hitting on one side of the axis, and then get away with just a single distance test:
def circle_intersection_area(num_darts, distance):
if distance >= 1:
return 0
in_circle = 0
width = 1-distance # this is enough to cover half of the target
for i in range(num_darts):
x = random.random()*width # random value from 0 to 1-distance
y = random.random()*2 - 1 # random value from -1 to 1
d = math.sqrt((x+distance)**2 + y**2) # distance from (-distance, 0)
if d <= 1:
in_circle += 1
sample_area = width * 2
target_area = sample_area * (in_circle / num_darts)
return target_area * 2 # double, since we were only testing half the target
I have created a Python file to generate a Mandelbrot set image. The original maths code was not mine, so I do not understand it - I only heavily modified it to make it about 250x faster (Threads rule!).
Anyway, I was wondering how I could modify the maths part of the code to make it render one specific bit. Here is the maths part:
for y in xrange(size[1]):
coords = (uleft[0] + (x/size[0]) * (xwidth),uleft[1] - (y/size[1]) * (ywidth))
z = complex(coords[0],coords[1])
o = complex(0,0)
dotcolor = 0 # default, convergent
for trials in xrange(n):
if abs(o) <= 2.0:
o = o**2 + z
else:
dotcolor = trials
break # diverged
im.putpixel((x,y),dotcolor)
And the size definitions:
size1 = 500
size2 = 500
n=64
box=((-2,1.25),(0.5,-1.25))
plus = size[1]+size[0]
uleft = box[0]
lright = box[1]
xwidth = lright[0] - uleft[0]
ywidth = uleft[1] - lright[1]
what do I need to modify to make it render a certain section of the set?
The line:
box=((-2,1.25),(0.5,-1.25))
is the bit that defines the area of coordinate space that is being rendered, so you just need to change this line. First coordinate pair is the top-left of the area, the second is the bottom right.
To get a new coordinate from the image should be quite straightforward. You've got two coordinate systems, your "image" system 100x100 pixels in size, origin at (0,0). And your "complex" plane coordinate system defined by "box". For X:
X_complex=X_complex_origin+(X_image/X_image_width)*X_complex_width
The key in understanding how to do this is to understand what the coords = line is doing:
coords = (uleft[0] + (x/size[0]) * (xwidth),uleft[1] - (y/size[1]) * (ywidth))
Effectively, the x and y values you are looping through which correspond to the coordinates of the on-screen pixel are being translated to the corresponding point on the complex plane being looked at. This means that (0,0) screen coordinate will translate to the upper left region being looked at (-2,1.25), and (1,0) will be the same, but moved 1/500 of the distance (assuming a 500 pixel width window) between the -2 and 0.5 x-coordinate.
That's exactly what that line is doing - I'll expand just the X-coordinate bit with more illustrative variable names to indicate this:
mandel_x = mandel_start_x + (screen_x / screen_width) * mandel_width
(The mandel_ variables refer to the coordinates on the complex plane, the screen_ variables refer to the on-screen coordinates of the pixel being plotted.)
If you want then to take a region of the screen to zoom into, you want to do exactly the same: take the screen coordinates of the upper-left and lower-right region, translate them to the complex-plane coordinates, and make those the new uleft and lright variables. ie to zoom in on the box delimited by on-screen coordinates (x1,y1)..(x2,y2), use:
new_uleft = (uleft[0] + (x1/size[0]) * (xwidth), uleft[1] - (y1/size[1]) * (ywidth))
new_lright = (uleft[0] + (x2/size[0]) * (xwidth), uleft[1] - (y2/size[1]) * (ywidth))
(Obviously you'll need to recalculate the size, xwidth, ywidth and other dependent variables based on the new coordinates)
In case you're curious, the maths behind the mandelbrot set isn't that complicated (just complex).
All it is doing is taking a particular coordinate, treating it as a complex number, and then repeatedly squaring it and adding the original number to it.
For some numbers, doing this will cause the result diverge, constantly growing towards infinity as you repeat the process. For others, it will always stay below a certain level (eg. obviously (0.0, 0.0) never gets any bigger under this process. The mandelbrot set (the black region) is those coordinates which don't diverge. Its been shown that if any number gets above the square root of 5, it will diverge - your code is just using 2.0 as its approximation to sqrt(5) (~2.236), but this won't make much noticeable difference.
Usually the regions that diverge get plotted with the number of iterations of the process that it takes for them to exceed this value (the trials variable in your code) which is what produces the coloured regions.