Search code examples
pythonmathpygamelinebezier

Generate lines instead of bezier if distance between 2 points is lower than specified


I wanted to generate a line if the distance between 2 points was lower than the amount of pixels I specified (which is the circle size) except it doesn't do what I want and instead creates weird lines that don't connect enter image description here

as you can see, it's doesn't work as wished, (not saying intended as it's possible I did something wrong with my code causing this issue) here is my code rn:

import pygame
from pygame import gfxdraw
from random import randint
from win32api import GetSystemMetrics
from time import time
import math

class circles(pygame.sprite.Sprite):
    def __init__(self,surface,x,y,cs):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("./assets/circle.png").convert_alpha()
        size = (round(2.25*(109-(9*cs))),round(2.25*(109-(9*cs))))
        self.image = pygame.transform.scale(self.image, size)
        self.draw = surface.blit(self.image,(x-((size[0])/2),y-((size[1])/2)))

width = GetSystemMetrics(0)
height = GetSystemMetrics(1)

def generate_original_points_coordinates():
    points.append([randint(0,width), randint(0,height)])

def generate_points_coordinates(count, spacing, circle_size_px):
    for i in range(0,count):
        generating = True
        while generating:
            x = points[0][0]+randint(-spacing,spacing)
            y = points[0][1]+randint(-spacing,spacing)
            if y < height-circle_size_px and y > circle_size_px and x < width-circle_size_px and x > circle_size_px: 
                generating = False
        points.append([x, y])

def generate_original_and_p(count, spacing, circle_size_px):
    generate_original_points_coordinates()
    generate_points_coordinates(count, spacing, circle_size_px)
    return points

def draw_circles(surface,x,y,r,rgb_code):
    gfxdraw.filled_circle(surface,x,y,r,rgb_code)


running = True
while running:
    start = time()
    points = []
    gen_points = generate_original_and_p(10,800,234)
    size = [width, height]
    screen = pygame.display.set_mode(size,0,32)
    screen.fill((255,255,255))
    circles_list = [circles(screen, gen_points[i][0], gen_points[i][1], 4) for i in range(0,len(gen_points))]

    # Draw bezier curves from points in a list

    for i in range(2,len(gen_points)):
        if math.sqrt(math.pow(gen_points[i-1][0]-gen_points[i-2][0],2)+math.pow(gen_points[i-1][1]-gen_points[i-2][1],2)) > 234:
            gfxdraw.line(screen,gen_points[i-2][0], gen_points[i-2][1], gen_points[i-1][1], gen_points[i-1][1],(0,0,255))
    
    [gfxdraw.bezier(screen, [gen_points[i-2], gen_points[i-1], gen_points[i]],4500,(255,0,0)) for i in range(2, len(gen_points))]

    # Place points on such curves and lines

    
    pygame.display.flip()
    print(gen_points)
    end = time()
    print(end-start)
    i = input("quit?: ")
    pygame.display.quit()
    if i == "quit":
        running = False

I also tried math.hypo but that gave me the same result so I'm short on ideas


Solution

  • there was several issues with my implementation and i now have reworked it all, it should be working for now here is an example case i wanted and produced

    case if 2 non-control points distance < 234:

    case if 2 non-control points distance < 234

    else:

    enter image description here

    as you can see it now work with this piece of code i could probably shorten and optimize somehow

    import pygame
    from pygame import gfxdraw
    from random import randint
    from win32api import GetSystemMetrics
    from time import time
    import math
    
    class circles(pygame.sprite.Sprite):
        def __init__(self,surface,x,y,cs):
            pygame.sprite.Sprite.__init__(self)
            self.image = pygame.image.load("./assets/circle.png").convert_alpha()
            size = (round(2.25*(109-(9*cs))),round(2.25*(109-(9*cs))))
            self.image = pygame.transform.scale(self.image, size)
            self.draw = surface.blit(self.image,(x-((size[0])/2),y-((size[1])/2)))
    
    width = GetSystemMetrics(0)
    height = GetSystemMetrics(1)
    
    def generate_original_points_coordinates(circle_size_px):
        generating = True
        while generating:
            x = randint(0,width)
            y = randint(0,height)
            if y < height-circle_size_px and y > circle_size_px and x < width-circle_size_px and x > circle_size_px:
                points.append([x, y])
                generating = False
    
    def generate_points_coordinates(count, spacing, circle_size_px):
        for i in range(0,count-1):
            generating = True
            while generating:
                x = points[-1][0]+randint(-spacing,spacing)
                y = points[-1][1]+randint(-spacing,spacing)
                if y < height-circle_size_px and y > circle_size_px and x < width-circle_size_px and x > circle_size_px: 
                    generating = False
                    points.append([x, y])
            
    def generate_original_and_p(count, spacing, circle_size_px):
        generate_original_points_coordinates(circle_size_px)
        generate_points_coordinates(count, spacing, circle_size_px)
        return points
    
    def draw_circles(surface,x,y,r,rgb_code):
        gfxdraw.filled_circle(surface,x,y,r,rgb_code)
    from time import sleep
    
    prompt = True
    while prompt:
        circle_count = input("Count: ")
        if circle_count.isdigit():
            circle_count = int(circle_count)
            prompt = False
    
    running = True
    while running:
        pygame.font.init()
        font = pygame.font.SysFont('freesansbold.ttf', 32)
        text = font.render('NotBezier', True, (0,0,255))
        start = time()
        points = []
        gen_points = generate_original_and_p(circle_count,800,234)
        size = [width, height]
        screen = pygame.display.set_mode(size,0,32)
        screen.fill((33,33,33))
        #circles_list = [circles(screen, gen_points[i][0], gen_points[i][1], 4) for i in range(0,len(gen_points))]
        circles_list = []
        # Draw bezier curves from points in a list
        skip = 0
        for i in range(0,len(gen_points)-2):
            dist_s = math.sqrt(math.pow(gen_points[i+2][0]-gen_points[i][0],2)+math.pow(gen_points[i+2][1]-gen_points[i][1],2))
            if skip:
                skip = 0
                continue
            if dist_s < 234/2:
                print(dist_s)
                gfxdraw.line(screen,gen_points[i][0], gen_points[i][1], gen_points[i+2][0], gen_points[i+2][1],(0,0,255))
                skip=1
                print("line")
            else:
                gfxdraw.bezier(screen, [gen_points[i], gen_points[i+1], gen_points[i+2]],4500,(255,0,0))
                skip = 1
            if i+2 == len(gen_points)-1:
                circles_list.append(circles(screen, gen_points[i+2][0], gen_points[i+2][1], 4))
                gfxdraw.filled_circle(screen, gen_points[i+2][0], gen_points[i+2][1],5,(0,255,0))
                screen.blit(font.render(str(i+2), True, (0,255,0)), (gen_points[i+2][0],gen_points[i+2][1]+10))
                
            circles_list.append(circles(screen, gen_points[i][0], gen_points[i][1], 4))
            gfxdraw.filled_circle(screen, gen_points[i][0], gen_points[i][1],5,(0,255,0))
            screen.blit(font.render(str(i), True, (0,255,0)), (gen_points[i][0],gen_points[i][1]+10))
    
        pygame.display.flip()
    
        # Place points on such curves and lines
    
        print(gen_points)
        end = time()
        print(end-start)
        i = input("quit?: ")
        pygame.display.quit()
        if i == "quit":
            running = False
    

    skipping control points was the solution i was looking for and this was the result