I am trying to draw a rectangle which would have an edge next to each of its sides (it should represent a building with a fence) for one building it goes quite well, but when I am trying to add a new building it messes itself up completely.
I have this code to create buildings:
class Building():
def __init__(self, Box_2_World,shape, position, sensor= None):
self.Box_2_World = Box_2_World
self.shape = shape
self.position = position
if sensor == None:
sensor = False
self.sensor = sensor
self.footprint = self.Box_2_World.CreateStaticBody(position = position,
angle = 0.0,
fixtures = b2FixtureDef(
shape = b2PolygonShape(box=(self.shape)),
density = 1000,
friction = 1000))
self.Lower_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0],self.footprint.position[1] -1.75*self.shape[1]))
self.Lower_Fence.CreateEdgeChain([(self.Lower_Fence.position[0]-4.25*self.shape[0],self.Lower_Fence.position[1]),
(self.Lower_Fence.position[0]-2.25*self.shape[0],self.Lower_Fence.position[1]),
])
self.Right_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0]-1*self.shape[0],self.footprint.position[1]))
self.Right_Fence.CreateEdgeChain([(self.Right_Fence.position[0],self.Right_Fence.position[1] - 1.25*self.shape[1]),
(self.Right_Fence.position[0],self.Right_Fence.position[1]-3.25*self.shape[1]),
])
self.Upper_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0],self.footprint.position[1] -0.45* self.shape[1]))
self.Upper_Fence.CreateEdgeChain([(self.Upper_Fence.position[0] - 4.25* self.shape[0],self.Upper_Fence.position[1]),
(self.Upper_Fence.position[0]- 3.25* self.shape[0]+ self.shape[0],self.Upper_Fence.position[1]),
])
self.Left_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0]-2.25*self.shape[0],self.footprint.position[1]))
self.Left_Fence.CreateEdgeChain([(self.Left_Fence.position[0],self.Left_Fence.position[1] - 1.25*self.shape[1]),
(self.Left_Fence.position[0],self.Left_Fence.position[1]-3*self.shape[1]),
])
Skyscrapers = []
Rectangles = [(pos_X-5, pos_Y-5),(pos_X+15, pos_Y -5),(pos_X - 5,pos_Y + 15),(pos_X+15, pos_Y + 15)]
for i in range(4):
Skyscrapers.append(Building(Box_2_World,shape = (5,5), position = Rectangles[i]))
and these functions to draw it using PyGame:
SCREEN_OFFSETX, SCREEN_OFFSETY = SCREEN_WIDTH/16, SCREEN_HEIGHT
def fix_vertices(vertices):
return [(int(SCREEN_OFFSETX + v[0]), int(SCREEN_OFFSETY - v[1])) for v in vertices]
def _draw_polygon(polygon, screen, body, fixture):
transform = body.transform
vertices = fix_vertices([transform * v * PPM for v in polygon.vertices])
pygame.draw.polygon(
screen, [c / 2.0 for c in colors[body.type]], vertices, 0)
pygame.draw.polygon(screen, colors[body.type], vertices, 1)
polygonShape.draw = _draw_polygon
def _draw_edge(edge, screen, body, fixture):
vertices = fix_vertices(
[body.transform * edge.vertex1 * PPM, body.transform * edge.vertex2 * PPM])
pygame.draw.line(screen, colors[body.type], vertices[0], vertices[1])
edgeShape.draw = _draw_edge
And the output is this: Blue rectangles represent buildings, blue lines are fences, first building fits quite nice, but the others are for some reason out of desired positions
Also, if you find out a way how to create the fences using for loop, it would be great (that's the reason why I put for-loop tag into this question)
Any help appreciated
The coordinates which are passed to CreateEdgeChain have to be relative to the body.
The position of the body is set when the object is constructed e.g:
self.Lower_Fence = self.Box_2_World.CreateStaticBody(
position=(self.footprint.position[0], self.footprint.position[1] -1.75*self.shape[1]))
And the edges have to be relative to this position rather than an absolute position.e.g:
self.Lower_Fence.CreateEdgeChain([(-4.25*self.shape[0], 0), (-2.25*self.shape[0], 0)])
To create the fences in a for-loop, you've to define a list of the edges. With the list of the edges you can create a list of fences:
fence_edges = [
[(-4.25, -1.75), (-2.25, -1.75)],
[(-1.00, -1.25), (-1.00, -3.25)],
[(-4.25, -0.45), (-2.25, -0.45)],
[(-2.25, -1.25), (-2.25, -3.25)]
]
self.Fences = []
for edge in fence_edges:
p1, p2 = edge
fence = self.Box_2_World.CreateStaticBody(position=self.footprint.position[:])
fence.CreateEdgeChain(
[(p1[0] * self.shape[0], p1[1] * self.shape[1]),
(p2[0] * self.shape[0], p2[1] * self.shape[1])])
self.Fences.append(fence)
self.Lower_Fence, self.Right_Fence, self.Upper_Fence, self.Left_Fence = self.Fences
Related
I'm trying to straiting out a ring using Manim Community. I want to acheive an animation exactly like this straiting a ring. I followed the code source of this video but the out put is not exactly the same (I used Manim Community and manimgl). Here is the code
class CircleScene(Scene):
def get_ring(self, radius=1, dR=0.1, color = GREEN):
ring = Circle(radius = radius + dR)
inner_ring = Circle(radius = radius)
inner_ring.rotate(np.pi,RIGHT)
ring.append_vectorized_mobject(inner_ring)
ring.set_stroke(width = 0)
ring.set_fill(color,1)
ring.R = radius
ring.dR = dR
return ring
def get_unwrapped(self, ring, to_edge = LEFT, **kwargs):
R = ring.R
R_plus_dr = ring.R + ring.dR
n_anchors = ring.get_num_curves()
result = VMobject()
points=[
interpolate(np.pi*R_plus_dr*LEFT, np.pi*R_plus_dr*RIGHT, a)
for a in np.linspace(0, 1, n_anchors // 2)
]+[
interpolate(np.pi*R*RIGHT+ring.dR*UP, np.pi*R*LEFT+ring.dR*UP, a)
for a in np.linspace(0, 1, n_anchors//2)
]
result.set_points_as_corners(points)
result.set_stroke(color=ring.get_fill_color(), width= ring.get_stroke_width()),
result.set_fill( color=ring.get_fill_color() , opacity=ring.get_fill_opacity()),
return result
class Example(CircleScene):
def construct(self,**kwargs):
circle=self.get_ring(1.5,0.1).rotate(PI/2);
line=self.get_unwrapped(circle).to_edge(DOWN)
self.play(Transform(circle,line))
I know that we can strait a circle to a line using the following code
circle=Circle(1).rotate(PI/2);
line=Line((-PI,-2,0),(PI,-2,0))
self.play(Transform(circle,line))
I am trying to read all the touching pixels with the same color in a image.
For that I use reccursive functions. When I check one pixel, I look on the right, left, top and bottom if the pixel close to it is the same color. If it is I add it to an array otherwise I don't.
The code is as follow:
vimport tkinter as tk
from PIL import Image
import sys
sys.setrecursionlimit(200000)
## WINDOWS
# to launch in debug mode
imgToDraw = Image.open('assets-test\\smile-face.png')
# to launch normaly
# imgToDraw = Image.open('..\\assets-test\\smile-face.png')
## LINUX
# imgToDraw = Image.open('../assets-test/smile-face.png')
imgPixels = imgToDraw.load()
imgWidth = imgToDraw.size[0]
imgHeight = imgToDraw.size[1]
# an element is a part of the image, it's a bunch of pixels with approximately the same color
# and each pixel touch at least one other pixel of the same element
elements = [];
isPixelChecked = [[ False for y in range( imgWidth ) ] for x in range( imgHeight )]
# min tolerable difference between two colors to consider them the same
# the higher the value is the more colors will be considered the same
COLOR_TOLERANCE = 10
reccursionCount = 0
class Element:
def __init__(self, color):
self.pixels = [];
self.color = color;
def addPixel(self, pixel):
self.pixels.append(pixel);
class Pixel:
def __init__(self, x, y, color):
self.x = x # x position of the pixel
self.y = y # y position of the pixel
self.color = color # color is a tuple (r,g,b)
def cutImageInElements():
global element
completeElement(element.pixels)
def completeElement(elemPixels):
global reccursionCount
global isPixelChecked
reccursionCount += 1
nbPixels = len(elemPixels);
xIndex = elemPixels[nbPixels - 1].x
yIndex = elemPixels[nbPixels - 1].y
xRightIdx = elemPixels[nbPixels - 1].x + 1
xLeftIdx = elemPixels[nbPixels - 1].x - 1
yBottomIdx = elemPixels[nbPixels - 1].y + 1
yTopIdx = elemPixels[nbPixels - 1].y - 1
isPixelChecked[xIndex][yIndex] = True
if((xRightIdx < imgWidth) and isPixelChecked[xRightIdx][yIndex] == False):
if(isColorAlmostSame(imgPixels[elemPixels[0].x, elemPixels[0].y], imgPixels[xRightIdx, yIndex])):
pixelAppended = Pixel(xRightIdx, yIndex, imgPixels[xRightIdx, yIndex])
elemPixels.append(pixelAppended)
completeElement(elemPixels)
if((xLeftIdx >= 0) and isPixelChecked[xLeftIdx][yIndex] == False):
if(isColorAlmostSame(imgPixels[elemPixels[0].x, elemPixels[0].y], imgPixels[xLeftIdx, yIndex])):
pixelAppended = Pixel(xLeftIdx, yIndex, imgPixels[xLeftIdx, yIndex])
elemPixels.append(pixelAppended)
completeElement(elemPixels)
if((yBottomIdx < imgHeight) and isPixelChecked[xIndex][yBottomIdx] == False):
if(isColorAlmostSame(imgPixels[elemPixels[0].x, elemPixels[0].y], imgPixels[xIndex, yBottomIdx])):
pixelAppended = Pixel(xIndex, yBottomIdx, imgPixels[xIndex, yBottomIdx])
elemPixels.append(pixelAppended)
completeElement(elemPixels)
if((yTopIdx >= 0) and isPixelChecked[xIndex][yTopIdx] == False):
if(isColorAlmostSame(imgPixels[elemPixels[0].x, elemPixels[0].y], imgPixels[xIndex, yTopIdx])):
pixelAppended = Pixel(xIndex, yTopIdx, imgPixels[xIndex, yTopIdx])
elemPixels.append(pixelAppended)
completeElement(elemPixels)
def isColorAlmostSame(pixel1, pixel2):
redDiff = abs(pixel1[0] - pixel2[0])
greenDiff = abs(pixel1[1] - pixel2[1])
blueDiff = abs(pixel1[2] - pixel2[2])
if(redDiff < COLOR_TOLERANCE and greenDiff < COLOR_TOLERANCE and blueDiff < COLOR_TOLERANCE):
return True
else:
return False
def printPixelsArr(pixelsArr):
for x in range(0, len(pixelsArr)):
print(pixelsArr[x].x, pixelsArr[x].y, pixelsArr[x].color)
if __name__ == '__main__':
pixel = Pixel(0, 0, imgPixels[0, 0]);
element = Element(pixel.color);
element.addPixel(pixel);
cutImageInElements();
print("NbReccursive call: ", reccursionCount)
This code works for small images of size 100x100 but crashes with an image of 400x400 with the error "terminated by signal SIGSEGV (Address boundary error)" when I launch the program on wsl2. When I run the program on cmd or powershell it just crashes but with no error code/msg.
I cannot understand why it would work with some size of images and not others. I can only think that the memory runs out or something but in the task manager the program uses almost no memory.
Not sure why that's failing, but that much recursion in Python isn't a great idea. I'd suggest reading about tail recursion that other languages use to make some recursive algorithms consume constant stack space. Note that your algorithm is not tail recursive, so this optimisation wouldn't help even if Python supported it.
I hacked together the following flood fill implementation. It uses Numpy so that it's only 10x slower than Pillow's ImageDraw.floodfill.
import numpy as np
def floodfill(im, row, col, threshold):
similar = np.mean(np.abs(im - im[row, col]), 2) < threshold
mask = np.zeros_like(similar)
mask[row, col] = 1
m2 = mask.copy()
while True:
m2[:,:] = mask
m2[1:,:] |= mask[:-1]
m2[:-1,:] |= mask[1:]
m2[:,1:] |= mask[:,:-1]
m2[:,:-1] |= mask[:,1:]
m2 &= similar
if np.all(m2 == mask):
return mask
mask[:,:] = m2
As an example of using this, you could do;
import requests
from io import BytesIO
res = requests.get("https://picsum.photos/300")
res.raise_for_status()
src = Image.open(BytesIO(res.content))
mask = floodfill(np.array(src, int), 10, 10, 40)
where the random image I got and the output mask are:
I have been, with pygame and pyopengl (oh and obviously Python), been trying to make a little tile-based 2D game, but I'm having trouble with the rendering using VBOs and glMultiDrawArray().
The program runs without errors, but I don't see any geometry drawn, so it's just a blank screen.
I've tried using glTranslate to see if maybe the geometry is being drawn, but I can't see it, as well as changing between using GluPerspective() and glOrthro2D(). No luck. I've pored over the code to see where it isn't working, but I have no clue what could be wrong. I'm still struggling to understand OpenGL and VBOs.
Here are the relevant bits of my code:
The Chunk class. Every chunk has its own VBO for vertices and textures (textures are currently unused)
class Chunk():
def __init__(self, position):
self.Position = position
self.VertexVBOId = _get_chunk_id()
self.VertexVBO = glGenBuffers(self.VertexVBOId)
self.TextureVBOId = _get_chunk_id()
self.TextureVBO = glGenBuffers(self.TextureVBOId)
Chunks[str(position)] = self
self.__updateVertexArray()
# glBindBuffer (GL_ARRAY_BUFFER, self.VertexVBO)
#self.__updateVertexArray()
def __getvertices(self):
vertices = []
for x in range(self.Position.x, self.Position.x + 16):
for y in range(self.Position.y, self.Position.y + 16):
pos = Vector2(x, y)
tile = GetTile(pos)
if tile != "air":
vertices.append(x+1)
vertices.append(y)
vertices.append(x+1)
vertices.append(y+1)
vertices.append(x)
vertices.append(y+1)
vertices.append(x)
vertices.append(y)
return vertices
def __updateVertexArray(self): #This will be called when a change is made the the chunk, as well as once initially
print("UPDATING VERTEX ARRAY")
vertices = self.__getvertices()
glBindBuffer (GL_ARRAY_BUFFER, self.VertexVBOId)
glBufferData (GL_ARRAY_BUFFER, len(vertices)*4, (c_float*len(vertices))(*vertices), GL_DYNAMIC_DRAW)
And here is the rendering loop:
def main():
print("Started")
pygame.init()
global displaySize
global SCREENSIZE
global PIXELS_PER_TILE
pygame.display.set_mode(displaySize, DOUBLEBUF|OPENGL)
#gluOrtho2D(-SCREENSIZE[0]/2, SCREENSIZE[0]/2, -SCREENSIZE[1]/2, SCREENSIZE[1]/2)
gluPerspective(180, 2, 0.1, 100)
... some other stuff ...
while True:
#Drawing
glClearColor(0.7, 0.7, 1, 0)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
cameraTranslateX = (camera.Position.x % 1) * PIXELS_PER_TILE
cameraTranslateY = (camera.Position.y % 1) * PIXELS_PER_TILE
#Figure out which chunks to render
botLeft = camera.Position - Vector2(SCREENSIZE[0]/2, SCREENSIZE[1]/2) + Vector2(cameraTranslateX, cameraTranslateY)
topRight = camera.Position + Vector2(SCREENSIZE[0]/2, SCREENSIZE[1]/2) + Vector2(cameraTranslateX, cameraTranslateY)
FirstChunkPos = (botLeft/16).floor()
TotalChunksX = (topRight/16).ceil().x - FirstChunkPos.x
TotalChunksY = (topRight/16).ceil().y - FirstChunkPos.y
for x in range(TotalChunksX):
for y in range(TotalChunksY):
pos = Vector2(x + FirstChunkPos.x, y + FirstChunkPos.y)
chunk = Chunks.get(str(pos))
if not chunk:
chunk = Chunk(pos)
VertexVBO = chunk.VertexVBOId
glBindBuffer (GL_ARRAY_BUFFER, VertexVBO)
glVertexPointer (2, GL_FLOAT, 0, None)
TextureVBO = chunk.TextureVBOId
glMultiDrawArrays (GL_POLYGON, vertexArrayThingy1, vertexArrayThingy2, 255)
# glUnmapBuffer(GL_ARRAY_BUFFER,VertexVBO)
pygame.display.flip ()
The 2nd and 3rd arguments of glMultiDrawArrays are of type const GLint* and const GLsizei*. This function cannot draw from different buffers. There is no glDraw* command that can use multiple vertex buffers for drawing. All vertecx attributes must be in one and the same buffer. glMultiDrawArrays should draw different ranges from the buffer. Suppose you want to draw the following 3 attribute ranges [3:6], [18:27], [30:36]:
first = [3, 18, 30]
count = [3, 9, 6]
glMultiDrawArrays(GL_TRIANGLES, first, count, 3)
If you want to draw multiple lists of indices you have to use glMultiDrawElements and you have to create an array of pointers to arrays of indices:
import ctypes
ia1 = (GLuint * 6)(0, 1, 2, 0, 2, 3)
ia2 = (GLuint * 6)(12, 13, 14, 12, 14, 15)
counts = [6, 6]
indexPtr = (GLvoidp * 2)(ctypes.addressof(ia1), ctypes.addressof(ia2))
glMultiDrawElements(GL_TRIANGLES, counts, GL_UNSIGNED_INT, indexPtr, 2)
I am trying to draw a rectangle which would have an edge next to each of its sides (it should represent a building with a fence) for one building it goes quite well, but when I am trying to add a new building it messes itself up completely.
I have this code to create buildings:
class Building():
def __init__(self, Box_2_World,shape, position, sensor= None):
self.Box_2_World = Box_2_World
self.shape = shape
self.position = position
if sensor == None:
sensor = False
self.sensor = sensor
self.footprint = self.Box_2_World.CreateStaticBody(position = position,
angle = 0.0,
fixtures = b2FixtureDef(
shape = b2PolygonShape(box=(self.shape)),
density = 1000,
friction = 1000))
self.Lower_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0],self.footprint.position[1] -1.75*self.shape[1]))
self.Lower_Fence.CreateEdgeChain([(self.Lower_Fence.position[0]-4.25*self.shape[0],self.Lower_Fence.position[1]),
(self.Lower_Fence.position[0]-2.25*self.shape[0],self.Lower_Fence.position[1]),
])
self.Right_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0]-1*self.shape[0],self.footprint.position[1]))
self.Right_Fence.CreateEdgeChain([(self.Right_Fence.position[0],self.Right_Fence.position[1] - 1.25*self.shape[1]),
(self.Right_Fence.position[0],self.Right_Fence.position[1]-3.25*self.shape[1]),
])
self.Upper_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0],self.footprint.position[1] -0.45* self.shape[1]))
self.Upper_Fence.CreateEdgeChain([(self.Upper_Fence.position[0] - 4.25* self.shape[0],self.Upper_Fence.position[1]),
(self.Upper_Fence.position[0]- 3.25* self.shape[0]+ self.shape[0],self.Upper_Fence.position[1]),
])
self.Left_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0]-2.25*self.shape[0],self.footprint.position[1]))
self.Left_Fence.CreateEdgeChain([(self.Left_Fence.position[0],self.Left_Fence.position[1] - 1.25*self.shape[1]),
(self.Left_Fence.position[0],self.Left_Fence.position[1]-3*self.shape[1]),
])
Skyscrapers = []
Rectangles = [(pos_X-5, pos_Y-5),(pos_X+15, pos_Y -5),(pos_X - 5,pos_Y + 15),(pos_X+15, pos_Y + 15)]
for i in range(4):
Skyscrapers.append(Building(Box_2_World,shape = (5,5), position = Rectangles[i]))
and these functions to draw it using PyGame:
SCREEN_OFFSETX, SCREEN_OFFSETY = SCREEN_WIDTH/16, SCREEN_HEIGHT
def fix_vertices(vertices):
return [(int(SCREEN_OFFSETX + v[0]), int(SCREEN_OFFSETY - v[1])) for v in vertices]
def _draw_polygon(polygon, screen, body, fixture):
transform = body.transform
vertices = fix_vertices([transform * v * PPM for v in polygon.vertices])
pygame.draw.polygon(
screen, [c / 2.0 for c in colors[body.type]], vertices, 0)
pygame.draw.polygon(screen, colors[body.type], vertices, 1)
polygonShape.draw = _draw_polygon
def _draw_edge(edge, screen, body, fixture):
vertices = fix_vertices(
[body.transform * edge.vertex1 * PPM, body.transform * edge.vertex2 * PPM])
pygame.draw.line(screen, colors[body.type], vertices[0], vertices[1])
edgeShape.draw = _draw_edge
And the output is this: Blue rectangles represent buildings, blue lines are fences, first building fits quite nice, but the others are for some reason out of desired positions
Also, if you find out a way how to create the fences using for loop, it would be great (that's the reason why I put for-loop tag into this question)
Any help appreciated
The coordinates which are passed to CreateEdgeChain have to be relative to the body.
The position of the body is set when the object is constructed e.g:
self.Lower_Fence = self.Box_2_World.CreateStaticBody(
position=(self.footprint.position[0], self.footprint.position[1] -1.75*self.shape[1]))
And the edges have to be relative to this position rather than an absolute position.e.g:
self.Lower_Fence.CreateEdgeChain([(-4.25*self.shape[0], 0), (-2.25*self.shape[0], 0)])
To create the fences in a for-loop, you've to define a list of the edges. With the list of the edges you can create a list of fences:
fence_edges = [
[(-4.25, -1.75), (-2.25, -1.75)],
[(-1.00, -1.25), (-1.00, -3.25)],
[(-4.25, -0.45), (-2.25, -0.45)],
[(-2.25, -1.25), (-2.25, -3.25)]
]
self.Fences = []
for edge in fence_edges:
p1, p2 = edge
fence = self.Box_2_World.CreateStaticBody(position=self.footprint.position[:])
fence.CreateEdgeChain(
[(p1[0] * self.shape[0], p1[1] * self.shape[1]),
(p2[0] * self.shape[0], p2[1] * self.shape[1])])
self.Fences.append(fence)
self.Lower_Fence, self.Right_Fence, self.Upper_Fence, self.Left_Fence = self.Fences
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I want to draw a triangle like this:
I have tried different ways of solving it, but I have not done it correctly. How to add median lines in the triangle? Could someone please help and explain this to me?
from turtle import *
import random
def allTriMedian (w=300):
speed (0)
vertices = []
point = turtle.Point(x,y)
for i in range (3):
x = random.randint(0,300)
y = random.randint(0,300)
vertices.append(trutle.Point(x,y))
point = turtle.Point(x,y)
triangle = turtle.Polygon(vertices)
a = triangle.side()
b = triangle.side()
c = triangle.side()
m1 = tirangle.median
m2 = triangle.median
m3 = triangle.median
I tried to put the equation directly
def Median (a, b, c):
m1 = sqrt((((2b^2)+(2c^2)-(a^2))))
m2 = sqrt((((2a^2)+(2c^2)-(b^2))))
m3 = sqrt((((2a^2)+(2b^2)-(c^2))))
triangle.setFill("yellow")
triangle.draw(allTriMedian)
Or I thought to find a midpoint and draw a line segment to connect the vertices and midpoints.
def getMid(p1,p2):
return ( (p1[0]+p2[0]) / 2, (p1[1] + p2[1]))
mid1 = Line((point(p1[0]+p2[0]) / 2),point(x))
mid2 = Line((point(p2[1]+p3[1]) / 2),point(y))
I hate doing math. Let's see if we can solve this by throwing turtles at the problem. Lots of turtles.
We'll randomly generate the verticies of the triangle. Taking pairs of verticies in turn, we'll start a turtle at each heading toward the other. When the turtles collide (at the midpoint), we'll eliminate one turtle and send the other toward the vertex not in the pair. Once we've done this three times (with six turtles), we should have the drawing in question. Well, mostly (no fill in my solution):
from turtle import Turtle, Screen
from random import seed, randint
WIDTH, HEIGHT = 640, 480
def meet_in_the_middle(turtle_1, turtle_2):
position_2 = turtle_2.position()
while True:
turtle_1.setheading(turtle_1.towards(turtle_2))
turtle_1.forward(1)
position_1 = turtle_1.position()
if int(position_1[0]) == int(position_2[0]) and int(position_1[1]) == int(position_2[1]):
break
turtle_2.setheading(turtle_2.towards(turtle_1))
turtle_2.forward(1)
position_2 = turtle_2.position()
if int(position_2[0]) == int(position_1[0]) and int(position_2[1]) == int(position_1[1]):
break
seed()
screen = Screen()
screen.setup(WIDTH * 1.25, HEIGHT * 1.25)
vertices = []
for _ in range(3):
x = randint(-WIDTH//2, WIDTH//2)
y = randint(-HEIGHT//2, HEIGHT//2)
vertices.append((x, y))
A, B, C = vertices
turtle_AtoB = Turtle(shape='turtle')
turtle_AtoB.penup()
turtle_AtoB.goto(A)
turtle_AtoB.pendown()
turtle_BtoA = Turtle(shape='turtle')
turtle_BtoA.penup()
turtle_BtoA.goto(B)
turtle_BtoA.pendown()
meet_in_the_middle(turtle_AtoB, turtle_BtoA)
turtle_BtoA.hideturtle()
turtle_AtoB.setheading(turtle_AtoB.towards(C))
turtle_AtoB.goto(C)
turtle_AtoB.hideturtle()
turtle_BtoC = Turtle(shape='turtle')
turtle_BtoC.penup()
turtle_BtoC.goto(B)
turtle_BtoC.pendown()
turtle_CtoB = Turtle(shape='turtle')
turtle_CtoB.penup()
turtle_CtoB.goto(C)
turtle_CtoB.pendown()
meet_in_the_middle(turtle_BtoC, turtle_CtoB)
turtle_CtoB.hideturtle()
turtle_BtoC.setheading(turtle_BtoC.towards(A))
turtle_BtoC.goto(A)
turtle_BtoC.hideturtle()
turtle_CtoA = Turtle(shape='turtle')
turtle_CtoA.penup()
turtle_CtoA.goto(C)
turtle_CtoA.pendown()
turtle_AtoC = Turtle(shape='turtle')
turtle_AtoC.penup()
turtle_AtoC.goto(A)
turtle_AtoC.pendown()
meet_in_the_middle(turtle_CtoA, turtle_AtoC)
turtle_AtoC.hideturtle()
turtle_CtoA.setheading(turtle_CtoA.towards(B))
turtle_CtoA.goto(B)
turtle_CtoA.hideturtle()
screen.exitonclick()
Turtles at work:
Finished drawing:
thanks to cdlane, I took his code and put some functionality into functions to make it a Little clearer (at least for me)
# -*- coding: cp1252 -*-
import turtle
from turtle import Turtle, Screen
from random import seed, randint
WIDTH, HEIGHT = 640, 480
def create_screen(width, height):
screen = Screen()
screen.setup(width * 1.25, height * 1.25)
return screen
def create_points(count,width = WIDTH, height = HEIGHT):
vertices = []
for _ in range(count):
x = randint(-width//2, width//2)
y = randint(-height//2, height//2)
vertices.append((x, y))
return vertices
def create_turtle_at_position(position):
turtle = Turtle(shape='turtle')
turtle.hideturtle()
turtle.penup()
turtle.goto(position)
turtle.showturtle()
turtle.pendown()
return turtle
def meet_in_the_middle(turtle_1, turtle_2):
position_2 = turtle_2.position()
while True:
turtle_1.setheading(turtle_1.towards(turtle_2))
turtle_1.forward(1)
position_1 = turtle_1.position()
if int(position_1[0]) == int(position_2[0]) and int(position_1[1]) == int(position_2[1]):
break
turtle_2.setheading(turtle_2.towards(turtle_1))
turtle_2.forward(1)
position_2 = turtle_2.position()
if int(position_2[0]) == int(position_1[0]) and int(position_2[1]) == int(position_1[1]):
break
turtle_1.hideturtle()
turtle_2.hideturtle()
return create_turtle_at_position(position_2)
def draw_median(P1st, P2nd, POpposite):
turtle_AtoB = create_turtle_at_position(P1st)
turtle_BtoA = create_turtle_at_position(P2nd)
turtle_AandBmiddle = meet_in_the_middle(turtle_AtoB, turtle_BtoA)
turtle_AandBmiddle.setheading(turtle_AandBmiddle.towards(POpposite))
turtle_AandBmiddle.goto(POpposite)
return turtle_AandBmiddle
seed()
sc = create_screen(WIDTH, HEIGHT)
for _ in range(5):
sc = create_screen(WIDTH, HEIGHT)
A, B, C = create_points(3)
draw_median(A,B,C)
draw_median(B,C,A)
draw_median(C,A,B)
sc.exitonclick()
mathematical it is the easiest way to calculate this by vector. Let me say you have a triangle ABC and want to draw a line from A to the middle of BC so your vector starts at A and ends on A + AB + 1/2 BC or A + AC + 1/2 CB (vectorial)
(ax) + (bx - ax) + 0.5 (cx - bx)
(ay) (by - ay) (cy - by)
that results in the coordinates for the opposite Point of
x = 0.5(cx + bx)
y = 0.5(cy + by)