Randomly orientated lines drawn off a random point in python - python

I'm trying to create python program that has several vertical lines which act as boundaries where randomly generated points or "dots" (as referred to in the code) which draw a straight line at a random degree. If the straight line intersects with one of the vertical "boundaries" I want to make it change colour. I have a picture of what I am trying to achieve which will probably explain my situation a bit clearer. The code I post below has drawn the "vertical boundaries" and has the points randomly generated within the region, however that is where I am stuck.
What I am aiming to achieve:
Example of program
My current Code:
setup(750,750)
screen_size = 750
max_coord = (screen_size - 30) / 2
### change the number of dots you have via that variable
num_dots = 500
bgcolor('yellow')
dot_size=5
reset() # Create an empty window
pi = Turtle()
hideturtle()
def parallel_lines(number):
pi.pensize(2)
pi.pencolor('black')
width = pi.window_width()
height = pi.window_height()
pi.setheading(90)
pi.penup()
pi.setposition(width/-2, height/-2)
for i in range(1, number +2):
pi.pendown()
pi.forward(height)
pi.penup()
pi.setposition(width/-2+i*(width/(number+1)),height/-2)
parallel_lines(7)
## centre turtle back in the middle of the page
goto(0,0)
### list to hold the dots
x_coords = []
y_coords = []
### Draw the dots via randomint
penup()
color("blue")
for dot_num in range(num_dots):
dot_pos_x = randint (-max_coord, max_coord)
dot_pos_y = randint (-max_coord, max_coord)
goto(dot_pos_x, dot_pos_y)
dot(dot_size)
x_coords.append(dot_pos_x)
y_coords.append(dot_pos_y)
done()
Thank you in advance for anyone that can help.

Here's an implementation of the program the OP describes. If there is a line intersection, it uses Python 3 turtle's undo feature to remove the line and redraw it in the alternate color:
from turtle import Turtle, Screen
from random import randint, randrange
SCREEN_SIZE = 750
PLANK_COUNT = 8
PINHEAD_SIZE = 5
FLOOR_COLOR = "yellow"
DEFAULT_COLOR = "blue"
CROSSING_COLOR = "red"
screen = Screen()
screen.setup(SCREEN_SIZE, SCREEN_SIZE)
screen.bgcolor(FLOOR_COLOR)
# configure numbers to replicate Lazzarini's setup
NUMBER_PINS = 3408
PIN_LENGTH = 78.125
PLANK_WIDTH = screen.window_width() / PLANK_COUNT
def parallel_lines(turtle, width, height):
turtle.penup()
turtle.setheading(90)
turtle.sety(height / -2)
x_coordinates = []
for i in range(PLANK_COUNT + 1):
x = i * PLANK_WIDTH - width / 2
turtle.setx(x)
turtle.pendown()
turtle.forward(height)
turtle.penup()
turtle.left(180)
x_coordinates.append(x)
return x_coordinates
pi = Turtle(visible=False)
pi.speed("fastest")
x_coordinates = parallel_lines(pi, screen.window_width(), screen.window_height())
def crosses(x0, x1, coordinates):
for coordinate in coordinates:
if x0 <= coordinate <= x1 or x1 <= coordinate <= x0:
return True
return False
previous_crossings = crossings = 0
max_coord = screen.window_width() / 2
for pin in range(NUMBER_PINS):
x0, y0 = randint(-max_coord, max_coord), randint(-max_coord, max_coord)
pi.color(DEFAULT_COLOR)
pi.goto(x0, y0)
pi.dot(PINHEAD_SIZE)
pi.setheading(randrange(360))
pi.pendown()
pi.forward(PIN_LENGTH)
if crosses(x0, pi.xcor(), x_coordinates):
pi.undo()
pi.color(CROSSING_COLOR)
pi.dot(PINHEAD_SIZE)
pi.forward(PIN_LENGTH)
crossings += 1
pi.penup()
if previous_crossings != crossings:
estimate = (2 * PIN_LENGTH * pin) / (PLANK_WIDTH * crossings)
print(estimate)
previous_crossings = crossings
screen.exitonclick()
Now for the rest of the story. What the OP didn't mention is that this is a drawing of planks in a floor, and we're dropping pins onto it, keeping track of how many cross lines in the flooring, as a means of estimating the value of PI (π)!
Read about Buffon's needle for details. The gist is the probability of a pin crossing a line is a function of PI so we can turn the equation around, drop actual (or virtual) pins, to estimate PI.
The program outputs the running estimate for PI (π), based on pins dropped so far, to the console:
...
3.121212121212121
3.1215970961887476
3.1370772946859904
3.134418324291742
3.131768953068592
3.1381381381381384
3.1384892086330933
3.1358467983243568
3.1451612903225805
3.1454979129397733
3.1458333333333335
3.1491384432560903
3.1465005931198102
3.1438721136767316
3.144208037825059
3.144542772861357
3.1419316843345113

Related

How do I generate monteCarlopi function in a given circle/square?

Write a function monteCarloPi(n, radius) that takes two arguments, the number of simulations n and the radius of the circle, and returns a float, which is the estimated value for pi. This radius should be the same radius that you used to draw your inscribed circle in the previous problem. Generate a set of random points, and test to see if that value is inside the circle or outside the circle. Use turtle to draw a dot at each location. This can be done using the function turtle.dot(size, color). Find the ratio of the points inside the circle to the number of points in the simulation. The latter number is an estimate for the area of the square. Multiple the ratio by four to get your estimate for pi.
This is what I have and I have no idea why it only draws one dot. Can anybody help me? I am a beginner /:
import turtle as t
import random
def monteCarloPi(n, radius):
'''
Takes two arguments, the number of simulations n and the radius of the circle, and returns a float, which is the estimated value for pi.
'''
t.dot() # Origin (0, 0)
t.pu()
t.goto(0, -radius)
t.pd()
t.circle(radius)
t.pu()
t.goto(-radius ,-radius)
t.pd()
for square in range(4):
t.fd(radius * 2)
t.lt(90)
points_in_circle = 0
points_in_square = 0
x = random.uniform(-radius, radius)
y = random.uniform(-radius, radius)
for dots in range(n):
t.pu()
t.goto(x, y)
t.dot()
origin = x ** 2 + y ** 2
if origin <=1 :
points_in_circle += 1
else:
points_in_square +=1
pi = 4 * (points_in_circle / points_in_square)
return pi
I see a number of potential problems with your code. The last two lines shown aren't indented correctly, they should be after the loop, not in it.
This equation origin = x ** 2 + y ** 2 seems incorrect and might need to instead be origin = (x ** 2 + y ** 2) ** 0.5
These two statements:
x = random.uniform(-radius, radius)
y = random.uniform(-radius, radius)
need to be at the top of the loop, not before the loop, otherwise you're plotting the same point n times, instead of n points. This calculation seems wrong:
pi = 4 * (points_in_circle / points_in_square)
reread the instructions you provided above. You should be dividing by n not points_in_square. This doesn't seem correct:
if origin <=1 :
as we're not working with a unit circle, so I'd expect:
if origin <= radius:
The reworked code based on my above observations:
from turtle import Screen, Turtle
from random import uniform
def monteCarloPi(n, radius):
'''
Takes two arguments, the number of simulations n and the radius of the circle,
and returns a float, which is the estimated value for pi.
'''
turtle.dot() # Origin (0, 0)
turtle.penup()
turtle.sety(-radius)
turtle.pendown()
turtle.circle(radius)
turtle.penup()
turtle.setx(-radius)
turtle.pendown()
for _ in range(4):
turtle.forward(radius * 2)
turtle.left(90)
turtle.penup()
turtle.hideturtle()
points_in_circle = 0
for _ in range(n):
x = uniform(-radius, radius)
y = uniform(-radius, radius)
turtle.goto(x, y)
turtle.dot()
origin = (x ** 2 + y ** 2) ** 0.5
if origin <= radius:
points_in_circle += 1
pi = 4 * points_in_circle / n
return pi
screen = Screen()
turtle = Turtle()
turtle.speed('fastest') # because I have no patience
print(monteCarloPi(100, 100))
screen.exitonclick()

Drawing regular polygons inside each other with specific specing using turtle module

I am trying to create a function that draws regular polygons inside each other as in the attached picture. The size of the polygons' side is defined by the following formula:
initial_size = initial_radius*(2*math.sin(360/(2*number_of_angles)))
I have two questions: 1. Why when I assign "initial_size" by the above formula my drawing starts in a different direction rather than when I simply assign initial_size = 100 (disregarding the formula)? 2. Can you please provide me with a hint (or any direction) on a way I can draw regular polygons as on the picture (i.e., starting from different points (moving along x-axis) and drawing each polygon inside the other?
import turtle
import math
turtle.speed(1)
def reg_polygon(number_of_angles, initial_radius):
Q = 180-(180/number_of_angles)/2
turtle.left(Q)
initial_size = initial_radius*(2*math.sin(360/(2*number_of_angles)))
if number_of_angles>=3:
sum_angle = 180*(number_of_angles-2)
angle = sum_angle/number_of_angles
for i in range(number_of_angles):
turtle.forward(initial_size)
turtle.left(180-angle)
elif number_of_angles<3:
print("Minimum number of angels should be >=3")
for i in range(3,6):
reg_polygon(i,100)
turtle.done()
Here is what the following code snippet draws; it is not an absolutely perfect reproduction of the gif you posted (some tweaks for the size progression for 3, 4, and 5-gons must be done to avoid the smaller neighbors to touch at some points - i/e the gif maker somewhat cheated!), but it follows mathematical symmetry and perfection.
The following code has some magic numbers; I may come back to it later, but not at this time. The resources I used to calculate a regular polygon can be found here, and there.
import turtle
import math
def reg_polygon(start_pos, number_of_angles, side):
interior_angle = (180 * (number_of_angles - 2)) / number_of_angles
turtle.setheading(180 - interior_angle//2)
for i in range(number_of_angles):
turtle.forward(side)
turtle.left(180 - interior_angle)
def reset_start_point():
global start_pos, startx, starty, initial_size, number_of_angles, side
startx += 8
starty -= 0
initial_size += 8
number_of_angles += 1
side = 2 * initial_size * math.sin(math.radians(180/number_of_angles))
start_pos = startx, starty
turtle.penup()
turtle.goto((startx, starty))
turtle.pendown()
start_pos = startx, starty = 0, 0
number_of_angles = 2
initial_size = 15 # radius
side = 0
while number_of_angles < 21:
reset_start_point()
reg_polygon(start_pos, number_of_angles, side)
turtle.done()

Generate "scattered" items, that do not overlap

This is a question that i had to solve, but have been banging my head against a wall for the past 5 days. There must be something really simple that i am missing, or misunderstanding, as the logic seems 100% correct in my head, but it just won't work.
I need to draw "scattered" houses, that do not over lap, 100 of them.
I have to use turtle to draw them, so i have X and Y coordinates.
And X and Y list for verification purposes, to see if a house is already in that place.
What i am doing is drawing a "house" (square with a triangle on top) on the randomized coordinates,
I loop while the number of houses is less than 100.
On each loop i randomize the x and y coordinates, from where i start drawing each house with turtle.
I check if the value is already in the X and Y verification lists, and also if my new X and Y are +/- the size of the house (treating it as a square)
import turtle
import time
import random
t = turtle.Turtle()
turtle.screensize(1920,1000)
x_verif = []
y_verif = []
t.speed(0)
collision = None
def square():
for s in range(0, 4):
t.forward(90)
t.left(90)
def triangle():
for s in range(0, 2):
t.left(-60)
t.forward(52)
def house():
square()
t.left(90)
t.forward(90)
triangle()
def scatter():
print("a")
house()
x1 = random.randint(-850, 850)
y1 = random.randint(-380, 380)
count = 0
while count < 100:
print("a2")
i = 0
u = 0
collision = False
for tries in range (0, 2):
print("a3")
x_verif.append(x1)
y_verif.append(y1)
while u < 100:
print("a4")
print(x_verif, y_verif, x1, y1)
while i < len(x_verif):
x1 = random.randint(-850, 850)
y1 = random.randint(-380, 380)
print(x1, y1)
if x1 not in x_verif and (x1 > x_verif[i]+91 or x1 < x_verif[i]-91):
if y1 not in y_verif and (y1 > y_verif[i]+142 or y1 < y_verif[i]-142):
collision = False
else:
collision = True
if collision == False:
t.penup()
t.hideturtle()
t.setx(x1)
t.sety(y1)
t.pendown()
house()
x_verif.append(x1)
y_verif.append(y1)
count += 1
i+= 1
u += 1
scatter()
Sorry for the ugly code, and the simplicity behind it. I would love to use list comprehensions for this, but i do not know where my logic fails at the moment. This is like my 100th try, as of this version it only draws the initial house and i think it loops infinitely somewhere....
My problem lies in looping through the whole lists for every new value. Do i need to loop through them every time, or is there some IF condition that I can use? Edit: It keeps on looping through random values, but none of them are accepted by the two IF statements that i'm using.
On a side note: With my current code they also change direction of drawing every time... No idea why is that happening....
Edit: I am very thankful for all the solutions! I'm struggling with the note at the beginning of the question. It says that it should only take a couple more lines to do the last one, compared to the first one..... Are they joking?
This turned out to be a trickier problem than I assumed from the description. My approach was to treat, and store, a house as two polygons, a square and a triangle. I randomly test draw (pen up) a house and compare all the points in its polygons to see if they are inside existing house polygons, and vice versa. If no overlap, draw the house for real. The solution is not efficient, but it allows tight packing of houses than a simple diameter-based approach.
The point in triangle routine is based on one from GeeksForGeeks.org
I have one slight fudge factor in my code that still needs to be polished out. But overall it seems to achieve the objective:
from turtle import Screen, Turtle
from random import randint
HOUSES = 100
HOUSE_SIZE = 90
WINDOW_WIDTH, WINDOW_HEIGHT = 1920, 1000
# assumes roof is an isosceles triangle
ROOF_SIDE = HOUSE_SIZE * 3**0.5 / 3
ROOF_HEIGHT = ROOF_SIDE // 2
FONT_SIZE = HOUSE_SIZE // 3
FONT = ('Arial', FONT_SIZE, 'normal')
def square(turtle, identity=None):
turtle.begin_poly()
for _ in range(3):
turtle.forward(HOUSE_SIZE)
turtle.right(90)
turtle.end_poly()
turtle.forward(HOUSE_SIZE/2 - FONT_SIZE/2) # visually finish square
if identity: # label each house with a number
turtle.penup()
turtle.right(90)
turtle.forward(HOUSE_SIZE/2)
turtle.write(identity, align='center', font=FONT)
turtle.backward(HOUSE_SIZE/2)
turtle.left(90)
turtle.pendown()
turtle.forward(HOUSE_SIZE/2 + FONT_SIZE/2) # visually finish square
turtle.right(90) # return to original orientation
return turtle.get_poly()
def triangle(turtle):
turtle.begin_poly()
turtle.forward(HOUSE_SIZE)
turtle.left(150)
turtle.forward(ROOF_SIDE)
turtle.end_poly()
turtle.left(60)
turtle.forward(ROOF_SIDE) # visually finish triangle
turtle.right(210) # return to original orientation
return turtle.get_poly()
def house(turtle, identity=None):
return (square(turtle, identity), triangle(turtle))
def area_of_triangle(p1, p2, p3):
x1, y1 = p1
x2, y2 = p2
x3, y3 = p3
return abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2))) // 2
def is_inside_triangle(point, triangle):
p1, p2, p3 = triangle
a = area_of_triangle(p1, p2, p3)
b = area_of_triangle(point, p2, p3)
c = area_of_triangle(p1, point, p3)
d = area_of_triangle(p1, p2, point)
return abs(a - (b + c + d)) < 5 # 5 == fudge factor, sigh
def is_inside_square(point, square):
x, y = point
p1, p2, p3, p4 = square
_, y1 = p1
x2, _ = p2
_, y3 = p3
x4, _ = p4
return x4 <= x <= x2 and y3 <= y <= y1
def scatter(turtle):
houses = []
count = 0
while count < HOUSES:
x = randint(-WINDOW_WIDTH/2, WINDOW_WIDTH/2 - HOUSE_SIZE)
y = randint(HOUSE_SIZE - WINDOW_HEIGHT/2, WINDOW_HEIGHT/2 - ROOF_HEIGHT)
turtle.penup()
turtle.goto(x, y)
proposed_square, proposed_triangle = house(turtle) # test draw invisible house
turtle.pendown()
collision = False
for point in proposed_square + proposed_triangle: # does proposed house collide with houses?
for square, triangle in houses:
if is_inside_square(point, square) or is_inside_triangle(point, triangle):
collision = True
break
if collision:
break
for square, triangle in houses: # do houses collide with proposed house?
for point in square + triangle:
if is_inside_square(point, proposed_square) or is_inside_triangle(point, proposed_triangle):
collision = True
break
if collision:
break
if not collision:
count += 1
houses.append(house(turtle, identity=count)) # actually draw new house
print(count)
screen = Screen()
screen.screensize(WINDOW_WIDTH, WINDOW_HEIGHT)
screen.tracer(False)
turtle = Turtle()
turtle.hideturtle()
scatter(turtle)
screen.tracer(True)
screen.exitonclick()
This problem is simplified somewhat by the consistent orientation of houses. If the houses were randomly oriented, compass-wise, the square overlap calculations would be more complex.
The solution could be made more efficient by doing triangle overlap with triangle, square overlap with triangle, etc. tests instead of simply "point inside". We could also push the collision logic down into the square() and triangle() routines to throw an error as soon as there's a collsion, rather than completing a house and then testing.
Given the size of the screen area, size of a house, the number of houses, and the random scattering, I believe it may be possible for an idividual run of the program to stall trying to place a house where there may not be available space:
Defo no the best way to do it but
import turtle
import time
import random
t = turtle.Turtle()
turtle.screensize(1920,1000)
x_verif = []
y_verif = []
t.speed(0)
collision = None
def square():
for s in range(0, 4):
t.forward(90)
t.left(90)
def triangle():
for s in range(0, 2):
t.left(-60)
t.forward(52)
def house():
square()
t.left(90)
t.forward(90)
triangle()
t.left(30) #returning to 90 degrres
def scatter():
beenAt = [] #this will hold every place that there is a house
for i in range(100):
t.penup()
loop = True
while loop == True:
area = random.randint(-850, 850)
for i in range(91): #looping for the size of the house
if area+i in beenAt: #if the number chosen plus i is in beenAt stop because we cant use that place
break
if i == 90: #if at the finial part of our loop then draw house
t.goto(area, 0)
t.pendown()
for i in range(area, area + 91):
beenAt.append(i) #add to been at list every place we have drawn
house()
loop = False
scatter()

How to get the points of a bezier curve/parabola using 3 unique points on a graph

I'm trying to make a parabolic/bezier curve between two main points (using a third point as the control point) and cannot work out how to do it.
from turtle import *
pointA = (0.00,50.00)
pointB = (0.00,350.00)
pointC = (-300.00,50.00)
pu()
goto(pointB)
pd()
dot()
goto(pointC)
dot()
ht()
this creates a line between the 2 points that are my main points,
i also want to make a curve using pointA so i can have multiple lines,
i have already ruled out the possibility of parabolic curves because that equation doesn't fit on a parabola unless i rotate the plane but thats a whole other kettle of fish i'm not ready for.
i would love some help as i am decently stuck,
thank you
Edit:
i have tried a fair few things none of them got close, i ended up resorting to just running with a midpoint translated a few pixels. E.G.
for j in range(3):
pu()
goto(pointB)
pd()
dot()
midpoint = ((pointB[0]+pointC[0])/2, (pointB[1]+pointC[1])/2)
goto(midpoint[0]+(20*j), midpoint[1])
goto(pointC)
dot()
this is a more realistic use of what im using it for, except i want to change that solid line into a variable line as dependednt on the position of the two points it will be on the same line, thus making it look like 1 singular line.
Based on Wikipedia's explanation of quadratic Bézier curves we should be able to simply do:
from turtle import Screen, Turtle, Vec2D
p0 = Vec2D(0, 50)
p1 = Vec2D(-300, 50)
p2 = Vec2D(0, 350)
b = lambda t: p1 + (1 - t)**2 * (p0 - p1) + t**2 * (p2 - p1)
turtle = Turtle()
turtle.penup()
for position in [p2, p1, p0]:
turtle.goto(position)
turtle.dot()
turtle.pendown()
t = 0
while t <= 1:
position = b(t)
turtle.setheading(turtle.towards(position))
turtle.goto(position)
t += 0.1
screen = Screen()
screen.exitonclick()

Replicating this design in Python

I've been trying to replicate this design using Python.
I am using the Graphics module, obtained here. I can't use any other Graphic Module at the moment.
This code here allows me to draw 5 circles in a line repeated by a loop.
def fdShape():
win = GraphWin(199,199)
centre = Point(20,100)
for y in range(5):
for x in range(5):
centre = Point(x * 40 + 20, y * 40 + 60)
circle = Circle(centre, 20)
circle.setOutline("red")
circle.draw(win)
One problem that I've found with this code is that it leaves a blank line at the top of the window and place the last circle line at the bottom, beyond the borders of the window. Thats the first problem.
The second is using the code to display semi circles displayed in red. As you can see in the image at the top of this page. I'm unsure of how to replicate this picture using Python.
Thank you!
This looks pretty close:
from graphic import *
def main():
repeat = 5
diameter = 40
radius = diameter // 2
offset = radius // 2
win = GraphWin("Graphic Design", diameter*repeat + offset, diameter*repeat)
win.setBackground('white')
for i in range(repeat):
for j in range(repeat):
draw_symbol(win, i % 2,
Point(i*diameter + offset, j*diameter), radius, 'red')
win.getMouse()
win.close()
def draw_symbol(win, kind, lower_left, radius, colour):
centre = Point(lower_left.x+radius, lower_left.y+radius)
circle = Circle(centre, radius)
circle.setOutline('')
circle.setFill(colour)
circle.draw(win)
if kind == 0:
rectangle = Rectangle(lower_left,
Point(lower_left.x+radius, lower_left.y+radius*2))
else:
rectangle = Rectangle(lower_left,
Point(lower_left.x+radius*2, lower_left.y+radius))
rectangle.setOutline('white')
rectangle.setFill('white')
rectangle.draw(win)
circle = Circle(centre, radius)
circle.setWidth(1)
circle.setOutline(colour)
circle.setFill('')
circle.draw(win)
main()
Two issues I see.
GraphWin should be initialized to 200x200, not 199x199.
This line:
centre = Point(x * 40 + 20, y * 40 + 60)
Should most likely be:
centre = Point(x * 40 + 20, y * 40 + 20)

Categories