I have a random cloud generator code that puts an extra line that I cant get rid of?

Viewed 88

I'm trying to draw a cloud background but the code I have found and used adds a line at the end that I don't need, anyone know how to fix this?

This code works perfectly otherwise I've made a few modifications but none have fixed the problem and threw errors so they are removed from this bit of code.

Here's where I got the original code from: https://pythonturtle.academy/tutorial-drawing-clouds-with-python-turtle/

Here's the code:

import math
import random

turtle.speed(0)
turtle.hideturtle()
turtle.up()
turtle.fillcolor('white')
turtle.pensize(2)

n = 500*size # number of points on each ellipse
# X,Y is the center of ellipse, a is radius on x-axis, b is radius on y-axis
# ts is the starting angle of the ellipse, te is the ending angle of the ellipse
# P is the list of coordinates of the points on the ellipse
def ellipse(X,Y,a,b,ts,te,P):
    t = ts
    for i in range(n):
        x = a*math.cos(t)
        y = b*math.sin(t)
        P.append((x+X,y+Y))
        t += (te-ts)/(n-1)

# computes Euclidean distance between p1 and p2
def dist(p1,p2):
    return ((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)**0.5

# draws an arc from p1 to p2 with extent value ext
def draw_arc(p1,p2,ext):
    turtle.up()
    turtle.goto(p1)
    turtle.seth(turtle.towards(p2))
    a = turtle.heading() 
    b = 360-ext 
    c = (180-b)/2
    d = a-c
    e = d-90
    r = dist(p1,p2)/2/math.sin(math.radians(b/2)) # r is the radius of the arc
    turtle.seth(e) # e is initial heading of the circle
    turtle.down()
    turtle.circle(r,ext,100)
    return (turtle.xcor(),turtle.ycor()) # returns the landing position of the circle
                                         # this position should be extremely close to p2 but may not be exactly the same
                                         # return this for continuous drawing to the next point


def cloud(P):
    step = n//10 # draw about 10 arcs on top and bottom part of cloud
    a = 0 # a is index of first point
    b = a + random.randint(step//2,step*2) # b is index of second point
    p1 = P[a] # p1 is the position of the first point
    p2 = P[b] # p2 is the position of the second point
    turtle.fillcolor('white')
    turtle.begin_fill()
    p3 = draw_arc(p1,p2,random.uniform(70,180)) # draws the arc with random extention
    while b < len(P)-1:
        p1 = p3 # start from the end of the last arc 
        if b < len(P)/2: # first half is top, more ragged
            ext = random.uniform(70,180)
            b += random.randint(step//2,step*2)
        else: # second half is bottom, more smooth
            ext = random.uniform(30,70)
            b += random.randint(step,step*2)
        b = min(b,len(P)-1) # make sure to not skip past the last point
        p2 = P[b] # second point
        p3 = draw_arc(p1,p2,ext) # draws an arc and return the end position
    turtle.end_fill()
      

P = [] # starting from empty list
ellipse(x,y,15*size,10*size,0,math.pi,P) # taller top half
ellipse(x,y,15*size,2.5*size,math.pi,math.pi*2,P) # shorter bottom half
cloud(P)
1 Answers

This code works perfectly

The code doesn't work perfectly as posted: turtle module isn't imported; size isn't defined; the window closes immediately after drawing. Adding back missing bits, I don't get the problematic extra line you describe. (Though most likely a penup() / pendown() issue.)

I've rewritten the code below to run. And I've simplified it by using object-oriented turtle instead of the functional turtle API, made use of turtle vectors and switched the mix of radians and degrees to all radians instead:

from math import sin, cos, pi
from random import randint, uniform
from turtle import Screen, Turtle, Vec2D

N = 50  # number of points on each ellipse

# point is the center of ellipse, a is radius on x-axis, b is radius on y-axis
# ts is the starting angle of the ellipse, te is the ending angle of the ellipse
# P is the list of coordinates of the points on the ellipse
def ellipse(origin, a, b, ts, te):
    t = ts
    positions = []
    increment = (te - ts) / (N - 1)

    for _ in range(N):
        position = Vec2D(a * cos(t), b * sin(t))
        positions.append(position + origin)
        t += increment

    return positions

# draws an arc from p1 to p2 with extent
def draw_arc(p1, p2, extent):
    turtle.penup()
    turtle.goto(p1)

    a = turtle.towards(p2)
    b = pi*2 - extent
    c = (pi - b) / 2
    d = a - c
    heading = d - pi/2
    radius = turtle.distance(p2) / 2 / sin(b / 2)

    turtle.setheading(heading)
    turtle.pendown()
    turtle.circle(radius, extent, 100)

    # return the landing position of the circle
    # this position should be extremely close to p2 but may not be exactly the same
    # return this for continuous drawing to the next point
    return turtle.position()

def cloud(P):
    step = N//10  # draw about 10 arcs on top and bottom part of cloud
    a = 0  # a is index of first point
    b = a + randint(step//2, step*2)  # b is index of second point
    p1 = P[a]  # p1 is the position of the first point
    p2 = P[b]  # p2 is the position of the second point
    p3 = draw_arc(p1, p2, uniform(1.25, pi)) # draws the arc with random extent

    turtle.fillcolor('white')
    turtle.begin_fill()

    while b < len(P) - 1:
        p1 = p3 # start from the end of the last arc

        if b < len(P) / 2: # first half is top, more ragged
            ext = uniform(1.25, pi)
            b += randint(step//2, step*2)
        else: # second half is bottom, more smooth
            ext = uniform(0.05, 1.25)
            b += randint(step, step*2)

        b = min(b, len(P) - 1) # make sure to not skip past the last point
        p2 = P[b] # second point
        p3 = draw_arc(p1, p2, ext) # draws an arc and return the end position

    turtle.end_fill()

screen = Screen()

turtle = Turtle()
turtle.hideturtle()
turtle.speed('fastest')
turtle.radians()
turtle.pensize(2)

origin = Vec2D(0, 200)
size = 10
points = [] # starting from empty list

points += ellipse(origin, size*15, size*10, 0, pi) # taller top half
points += ellipse(origin, size*15, size*2.5, pi, pi*2) # shorter bottom half

cloud(points)

screen.exitonclick()

enter image description here

Related