Python: objects in a list - python

How do I make it so that the circles in this list can be modified or removed later on? Isn't the list different from the actual objects?
def drawAllBubbles(window,numOfBubbles):
bublist=list()
for x in range(numOfBubbles):
p1= random.randrange(1000)
p2= random.randrange(1000)
center= graphics.Point(p1,p2)
bubx = center.getX()
buby = center.getY()
r = random.randint(1, 255)#randomize rgb values
g = random.randint(1, 255)
b = random.randint(1, 255)
circle = graphics.Circle(center, 5)
circle.setFill(color_rgb(r, g, b))
circle.setOutline("black")
circle.draw(window)
bublist.append(circle)
return bublist
window.getMouse()
This part of the script essentially draws
And then returns a list of circles.

The objects are contained in bublist
If you iterate over the list, you can change, remove, or redraw the circles. For example:
for bubble in bublist:
bubble.setOutline("green")
bubble.draw(window)

Related

Delaunay triangulation in Python with opencv and igraph, strange behavior

So I have some code in Python (3.9.13) to obtain a Delaunay triangulation of a set of points in real time and analyze the graph properties. First I use OpenCV (opencv-python 4.6.0.66) Subdiv2D method to obtain the triangulation. Then I convert it in a graph I can analyze with igraph (igraph 0.10.3). But I am not sure why, once every few frames the graph produced by igraph is messed up such as shown in this image:
graph messed up (Left is OpenCV and right is igraph):
Else it is working properly.
good graph (Left is OpenCV and right is igraph):
Here is my demo code:
import time
import numpy
import cv2
import igraph as ig
# Draw a point
def draw_point(img, p, color):
cv2.circle(img, (int(p[0]), int(p[1])), 2, color, 0)
# Get a triangulation
def get_delaunay(subdiv):
return subdiv.getTriangleList()
# Draw delaunay triangles
def draw_delaunay(img, subdiv, delaunay_color):
triangleList = subdiv.getTriangleList()
size = img.shape
r = (0, 0, size[1], size[0])
for t in triangleList:
pt1 = (int(t[0]), int(t[1]))
pt2 = (int(t[2]), int(t[3]))
pt3 = (int(t[4]), int(t[5]))
cv2.line(img, pt1, pt2, delaunay_color, 1, cv2.LINE_AA, 0)
cv2.line(img, pt2, pt3, delaunay_color, 1, cv2.LINE_AA, 0)
cv2.line(img, pt3, pt1, delaunay_color, 1, cv2.LINE_AA, 0)
if __name__ == '__main__':
NUM_PART = 500
SIZE = 1000
REPEAT = 10
for iteration in range(REPEAT):
positions = numpy.random.randint(0, SIZE, size=(NUM_PART, 2))
print("There is {p} positions. And {up} unique position".format(p=len(positions), up=len(numpy.unique(positions, axis=1))))
# Create an instance of Subdiv2D
rect = (0, 0, SIZE, SIZE)
subdiv = cv2.Subdiv2D(rect)
timer = time.time()
# Insert points into subdiv
print("There is {} points in subdiv".format(len(positions)))
for p in positions:
p = p.astype("float32")
subdiv.insert(p)
# get triangulation
trilist = get_delaunay(subdiv)
print("Took {}s".format(round(time.time() - timer, 12)))
print("there is {} triangles in trilist".format(len(trilist)))
# create image
opencv_image = numpy.zeros((SIZE, SIZE, 3))
# Draw delaunay triangles
draw_delaunay(opencv_image, subdiv, (255, 255, 255))
# Draw points
for p in positions:
draw_point(opencv_image, p, (0, 0, 255))
timer = time.time()
n_vertices = NUM_PART
# create graph
g = ig.Graph(n=n_vertices, )
g.vs["name"] = range(NUM_PART)
print("graph name vector of length {l}:\n{v}".format(l=len(g.vs["name"]), v=g.vs["name"]))
# Inversion x positions
positionsx = [SIZE - pos for pos in positions[:, 0]]
g.vs["x"] = positions[:, 0]
print("graph x vector of length {l}:\n{v}".format(l=len(g.vs["x"]), v=g.vs["x"]))
g.vs["y"] = positions[:, 1]
print("graph y vector of length {l}:\n{v}".format(l=len(g.vs["y"]), v=g.vs["y"]))
print("Graph took {}s".format(round(time.time() - timer, 12)))
list_vtx = []
for tri in trilist:
vertex1, _ = subdiv.findNearest((tri[0], tri[1]))
vertex2, _ = subdiv.findNearest((tri[2], tri[3]))
vertex3, _ = subdiv.findNearest((tri[4], tri[5]))
list_vtx.extend([vertex3, vertex2, vertex1])
list_cleared = list(dict.fromkeys(list_vtx))
list_cleared.sort()
print("list cleared of length {len}: {lst}".format(len=len(list_cleared), lst=list_cleared))
for tri in trilist:
vertex1, _ = subdiv.findNearest((tri[0], tri[1]))
vertex2, _ = subdiv.findNearest((tri[2], tri[3]))
vertex3, _ = subdiv.findNearest((tri[4], tri[5]))
#print("vertex 1: {v} of position {p}".format(v=vertex1, p=(tri[0], tri[1])))
#print("vertex 2: {v} of position {p}".format(v=vertex2, p=(tri[2], tri[3])))
#print("vertex 3: {v} of position {p}".format(v=vertex3, p=(tri[4], tri[5])))
# -4 because https://stackoverflow.com/a/52377891/18493005
g.add_edges([
(vertex1 - 4, vertex2 - 4),
(vertex2 - 4, vertex3 - 4),
(vertex3 - 4, vertex1 - 4),
])
# simplify graph
g.simplify()
nodes = g.vs.indices
print(nodes)
print(subdiv)
# create image
igraph_image = numpy.zeros((SIZE, SIZE, 3))
for point in g.vs:
draw_point(igraph_image, (point["x"], point["y"]), (0, 0, 255))
for edge in g.es:
# print(edge.tuple)
# print(g.vs["x"][edge.tuple[0]])
cv2.line(igraph_image, (int(g.vs["x"][edge.tuple[0]]), int(g.vs["y"][edge.tuple[0]])),
(int(g.vs["x"][edge.tuple[1]]), int(g.vs["y"][edge.tuple[1]])), (255, 255, 255), 1, cv2.LINE_AA, 0)
numpy_horizontal = numpy.hstack((opencv_image, igraph_image))
# Show results
cv2.imshow('L: opencv || R: igraph', numpy_horizontal)
cv2.waitKey(0)
I try to have a repeatable result of my graph in igraph. But it is only working 80% of the time which is pretty strange behavior. Any idea of what are my mistakes here?
Edit: it seems to be a variation in the length of the list generated by:
trilist = get_delaunay(subdiv)
list_vtx = []
for tri in trilist:
vertex1, _ = subdiv.findNearest((tri[0], tri[1]))
vertex2, _ = subdiv.findNearest((tri[2], tri[3]))
vertex3, _ = subdiv.findNearest((tri[4], tri[5]))
list_vtx.extend([vertex3, vertex2, vertex1])
list_cleared = list(dict.fromkeys(list_vtx))
list_cleared.sort()
but I am not sure why.
Edit2:
After the modification sugested by Markus. I do not get a messed up graph anymore. But now the graph is missing some edges
x_pos = [0] * NUM_PART # create 0-filled array of x-positions
y_pos = [0] * NUM_PART # create 0-filled array of y-positions
edges = [] # create empty array of edges
# for each triangle add vertex positions and edges
for tri in trilist:
vertex1 = subdiv.findNearest((tri[0], tri[1]))[0] - 4
vertex2 = subdiv.findNearest((tri[2], tri[3]))[0] - 4
vertex3 = subdiv.findNearest((tri[4], tri[5]))[0] - 4
x_pos[vertex1] = tri[0]
y_pos[vertex1] = tri[1]
x_pos[vertex2] = tri[2]
y_pos[vertex2] = tri[3]
x_pos[vertex3] = tri[4]
y_pos[vertex3] = tri[5]
edges.append((vertex1, vertex2))
edges.append((vertex2, vertex3))
edges.append((vertex2, vertex3))
# create graph
g = ig.Graph(NUM_PART, edges)
g.vs["name"] = range(NUM_PART)
g.vs["x"] = x_pos
g.vs["y"] = y_pos
g.simplify()
The following image shows an overlay between 3 type of drawing (White=opencv , Red=Markus suggestion, Green + Red = previous method used)
Overlay of Markus solution in case of no mess up
Overlay of Markus solution in case of mess up
So Markus solution indeed remove the mess up, but also some edges, even in the case that was working previously.
So in fact my test code was working as expected. The issue was not from Subdiv2D or igraph but from the generation of my position.
I made a mistake verifying the uniqueness of my position with
len(numpy.unique(positions, axis=1))
but should have been using
len(numpy.unique(positions, axis=0)).
So when I used subdiv.findNearest()[0] or subdiv.locate()[2] I was in fact finding several points at the same position, and only the first index was thrown back by the function and so the graph was being messed up.
In order to generate unique position I uses the following code and the graph messing disappeared:
rng = numpy.random.default_rng()
xx = rng.choice(range(SIZE), NUM_PART, replace=False).astype("float32")
yy = rng.choice(range(SIZE), NUM_PART, replace=False).astype("float32")
pp= numpy.stack((xx,yy), axis=1)
The fact that numpy.random.randint(0, 1000, size=(500, 2)) was providing a similar position every 10 or so frame is pretty strange to me as the probability of getting two identical positions seems intuitively to be lower than 0.1

abaqus python selecting outer surface of sphere

I want to select only outer surface of sphere. It has a lot of cells.
def Outer_Surface_Temperature():
Outer_Surface_out = []
Outer_Surface_in = []
Outer_Surface = []
for name in myAssembly.allInstances.keys():
Outer_Surface_out.append(myAssembly.instances[name].faces.getByBoundingSphere((0, 0, 0), 5.100))
Outer_Surface_in.append(myAssembly.instances[name].faces.getByBoundingSphere((0, 0, 0), 4.800))
Outer_Surface = [x for x in Outer_Surface_out if x not in Outer_Surface_in]
break
Outer_Surface_set = myAssembly.Set(name='Outer_Surface', faces=Outer_Surface)
myModel.TemperatureBC(name='Outer_Surface_Temperature', createStepName='Step-1', region=Outer_Surface_set, fixed=OFF, distributionType=UNIFORM, fieldName='', magnitude=1.0, amplitude='Amp-1')
return
it results only 1 face of my outer surface, i need all outer surface of my sphere. what is wrong in my code? thank you.

Create a mesh with boundary markers in Python, Fenics

My aim is to create a mesh in Python and setting markers on some 1-dimensional subset.
Up until now, I have always creates a set, for example a rectangle in gmsh, then put for example a circle in it. Then gmsh puts a mesh on my structure and I can mark the boundary of the rectangle and of the circle as facets (as xdmf files). I can then let Python read my mesh and boundary facets and using it, f.e. to solve PDEs.
What I want to do now is the following: I still want to have my rectangle with a mesh, but instead of defining the facet markes in gmsh, I want to define them using the image of a function.
More precicely: Instead of creating a circle in gmsh, I want to consider, for example, the function
Then I want to use as my facet and mark it in my mesh.
Is there a way to do this? I feel kind of lost here.
I am not sure if I understood everything in your question correctly, but here is some code that might help you. One thing I do not know is how to add element edges to physical groups in gmsh. But maybe you can figure that out.
So here is the code:
import gmsh
import numpy as np
def mapping(point):
x = point[0]
y = point[1]
z = point[2]
result = [2*x,3*y,z]
return result
def inverseMapping(point):
x = point[0]
y = point[1]
z = point[2]
result = [(1/2)*x,(1/3)*y,z]
return result
def getNodes():
nodeTags, nodeCoord, _ = gmsh.model.mesh.getNodes()
nodeCoord = np.reshape(nodeCoord,(len(nodeTags),3))
return nodeTags, nodeCoord
def getEdgeNodeCoordinates():
edgeTags, edgeNodes = gmsh.model.mesh.getAllEdges()
edgeNodes = np.reshape(edgeNodes,(len(edgeTags),2))
nodeTags, nodeCoord = getNodes()
coord = []
for i in range(0,len(edgeTags)):
tagNode1 = edgeNodes[i][0]
tagNode2 = edgeNodes[i][1]
nodeIndex1 = list(nodeTags).index(tagNode1)
nodeIndex2 = list(nodeTags).index(tagNode2)
nodeCoord1 = nodeCoord[nodeIndex1]
nodeCoord2 = nodeCoord[nodeIndex2]
coord.append([nodeCoord1,nodeCoord2])
return edgeTags, edgeNodes, nodeTags, coord
def getInverseNodeCoordinates(edgeNodeCoordinates):
coord = []
for edgeNodes in edgeNodeCoordinates:
nodeCoord1 = edgeNodes[0]
nodeCoord2 = edgeNodes[1]
newCoord1 = inverseMapping(nodeCoord1)
newCoord2 = inverseMapping(nodeCoord2)
coord.append([newCoord1, newCoord2])
return coord
def checkIntersection(edgeTags, edgeNodeCoordinates, inverseCoordinates):
intersectingEdgeTags = []
intersectingEdgeNodeCoord = []
# 1 = inside, 0 = outside
for i in range(0,len(inverseCoordinates)):
pair = inverseCoordinates[i]
coord1 = pair[0]
coord2 = pair[1]
e1 = 1 if np.linalg.norm(coord1) <= 1 else 0
e2 = 1 if np.linalg.norm(coord2) <= 1 else 0
s = e1 + e2 # s = 0 --> both nodes outside of manifold
# s = 1 --> one node inside and one node outside of manifold
# s = 2 --> both nodes inside of manifold
if s == 1:
intersectingEdgeTags.append(edgeTags[i])
intersectingEdgeNodeCoord.append(edgeNodeCoordinates[i])
return intersectingEdgeTags, intersectingEdgeNodeCoord
def visualizeEdges(intersectingEdgeNodeCoord):
for pair in intersectingEdgeNodeCoord:
p1 = pair[0]
p2 = pair[1]
t1 = gmsh.model.occ.addPoint(p1[0],p1[1],p1[2])
t2 = gmsh.model.occ.addPoint(p2[0],p2[1],p2[2])
line = gmsh.model.occ.addLine(t1, t2)
gmsh.model.occ.synchronize()
gmsh.model.setColor([(1,line)], 255, 0, 0)
gmsh.model.occ.synchronize()
gmsh.initialize()
# Create a rectangle which will be meshed later.
tag_vol_1 = gmsh.model.occ.addRectangle(-3, -4, 0, 6, 8)
# Sample the S1 manifold with n_points
S1_sampling_points = []
n_points = 100
maxAngle = 2*np.pi
angle = maxAngle/n_points
z = 0
for i in range(0,n_points):
x = np.cos(i*angle)
y = np.sin(i*angle)
S1_sampling_points.append([x,y,z])
# Map the sampled S1 points to 2*x, 3*y, z.
# This is only for "visualization" via a spline.
mappedPoints = []
mappedPointTags = []
for point in S1_sampling_points:
mappedPoint = mapping(point)
tag = gmsh.model.occ.addPoint(mappedPoint[0], mappedPoint[1], mappedPoint[2])
mappedPointTags.append(tag)
# Here the spline fitting is performed
# You will see it visualized when gmsh opens.
tagMappedS1 = gmsh.model.occ.addSpline(mappedPointTags + [mappedPointTags[0]]) # make the spline periodic by setting the last point equal to the first one
gmsh.model.occ.synchronize()
# Mesh the rectangle and tell gmsh to create edges which we can access.
gmsh.model.mesh.generate(2)
gmsh.model.mesh.createEdges() # You need to call this before using gmsh.model.mesh.getAllEdges()
# Get all these self-explanatory things
edgeTags, edgeNodes, nodeTags, edgeNodeCoordinates = getEdgeNodeCoordinates()
# Calculate the inverse-mapped coordinates of all nodes.
# With this we can just check if the transformed nodes are inside a circle of radius 1 or outside.
# If for every egde one node is inside, and the other node is outside the circle, then the edge is
# intersected by the mapped manifold f(S1) --> (2*x, 3*y, z). We then save the tag of such an edge.
inverseCoordinates = getInverseNodeCoordinates(edgeNodeCoordinates)
intersectingEdgeTags, intersectingEdgeNodeCoord = checkIntersection(edgeTags, edgeNodeCoordinates, inverseCoordinates)
# Here all intersecting edges are created within gmsh so you can see it.
# This is for visualization only. A lot of nodes with the same coordinates are created here twice.
visualizeEdges(intersectingEdgeNodeCoord)
gmsh.fltk.run()
gmsh.finalize()
And the result in gmsh looks like this:

How to sublist in a list the same value pixels of an image (Four Pixel Connectivity)

I'm looking to output a list of lists in python, in which each list contains the pixel coordinates of each shape of a single colour.
I.e. looking at this image:
a 128x128 png
Which I have converted into a 2D grid of RGB values.
I am looking to create a list of lists that each contain the pixel coordinates of the purple "shapes" (squares, rectangles, etc). The criteria for a "shape" is simply at least two same colour pixels adjacent to each other (only up, down, left, right).
In my attempt:
# returns a list of instances of a tile which are all adjacent to each other
# i.e. finds a window's individual tiles and returns them as a list
def coagulateTiles(self, tileType, startX, startY, existingComponents = [], foundTiles = None):
# if the pixel is not the right tile OR if it has already been part of another search
if not self.getTileType(startX, startY) == tileType or not (startX, startY) in foundTiles:
return existingComponents
else:
existingComponents.append((startX, startY))
# a dictionary of the adjacent tiles {"north" = (x, y), etc...}
adjacentCoordDict = self.getAdjacentCoords(startX, startY)
for adjacent in adjacentCoordDict:
if adjacent == tileType:
x, y = adjacentCoordDict[adjacent][0], adjacentCoordDict[adjacent][1]
existingComponents.append(self.coagulateTiles(tileType, x, y, existingComponents))
return existingComponents
def tileSearch(self, tile):
items = []
foundTiles = []
for x in range(self.getSizeX()):
for y in range(self.getSizeY()):
if self.getTileType(x, y) == tile:
if tile in self.getAdjacentTiles(x, y).values():
foundTiles.append((x, y))
listOfTileComponents = self.coagulateTiles(tile, x, y, foundTiles = foundTiles)
items.append(listOfTileComponents)
return items
I end up finding all those pixels, but the outputted list puts all the pixels together into a single list rather than separating the "shapes" into individual sublists.
It returns something like [[(1, 1), (2, 1), (3, 1), (1, 2), (1, 3)]] rather than [[(1, 1), (2, 1)], [(3, 1), (1, 2), (1, 3)]]
My gut feeling is that of course there is a simple logical error in my attempt, but I can't seem to find where I can specify the sublist creation for it to work.
Any ideas?
Here's the code needed to convert the image above into the grid I'm working with:
import numpy as np
from PIL import Image
# (x=0,y=0) of the grid is in the top left corner
class Grid():
def __init__(self, sizeX, sizeY):
self.sizeX = sizeX
self.sizeY = sizeY
global rgbMap
rgbMap = {
"wall":( 0, 0, 0),
"background": (255,255,255),
"closet": (192,192,224),
"bathroom": (192,255,255), # /washroom
"dining room": (224,255,192), # livingroom/kitchen/dining room
"bedroom": (255,224,128),
"hall": (255,160, 96),
"balcony": (255,224,224),
"opening": (255, 60,128) # door & window
}
global tileMap
tileMap = {v: k for k, v in rgbMap.items()}
# create a 2d numpy array of the given size
self.grid = self.createGrid(sizeX, sizeY)
def createGrid(self, sizeX, sizeY):
return np.empty((sizeX, sizeY), dtype=tuple)
# function to input a colour into the 2d grid
def populate(self, locationX, locationY, rgbTuple):
self.grid[locationX,locationY] = rgbTuple
def get(self):
return self.grid
def getSizeX(self):
return self.sizeX
def getSizeY(self):
return self.sizeY
class MapGenerator:
def __init__(self, finalSize = 128):
# resize the image to be axa size where a = 128
self.workingGridSize = 512
self.finalSize = finalSize
def createFromSaveFile(self):
self.createGrid(fromMemory=True)
print("populating the grid with data")
self.populateGrid(self.finalSize, self.grid, self.floorplan)
self.floorplan.close()
def createGrid(self, fromMemory = False):
if fromMemory == True:
self.floorplan = Image.open('saved.png')
self.grid = Grid(self.finalSize, self.finalSize)
else:
self.floorplan = Image.open('result.png')
self.grid = Grid(self.workingGridSize, self.workingGridSize)
def populateGrid(self, gridsize, givenGrid, image):
for x in range(0, gridsize):
for y in range(0, gridsize):
# print(map.getpixel((x,y)))
givenGrid.populate(x, y, image.getpixel((x,y)))
if __name__ == "__main__":
newMap = MapGenerator()
# newMap.create()
newMap.createFromSaveFile()
openings = newMap.grid.tileSearch("opening")
print(openings)
i've come up with a DFS recursive solution that works:
def tileSearch(self, tile):
"""returns a 2d list containing all the pixels
of each shape of a searched tile type as a sublist
a shape is defined as at least two pixels
of the same colour touching each other with Four-Pixel Connectivity
(up, down, left, right)
Args:
tile (string): the tile to search as a string (see rgbMap dict for tile list)
Returns:
list: a 2D list containing each shape as a sublist of its pixels
"""
searchGrid = copy.deepcopy(self)
resultList = list()
for x in range(searchGrid.getSizeX()):
for y in range(searchGrid.getSizeY()):
if searchGrid.getTileType(x, y) == tile:
adjacentTiles = list(searchGrid.getAdjacentTiles(x, y).values())
if tile in adjacentTiles:
shapeList = list()
searchGrid.coagulateShape(tile, x, y, shapeList)
resultList.append(shapeList)
return resultList
# returns a list of instances of a tile which are all adjacent to each other
# i.e. finds a window's individual tiles and returns them as a list
def coagulateShape(self, tile, x, y, shapeList = []):
""" Performs a recursive Depth First Search
returns a list of coordinates of a tile which are all adjacent to each other
through Four Pixel connectivity (up, down, left, right)
i.e. finds a shape's individual tiles and returns them as a list
Args:
tile (string): the tile to search for (see rgbMap dict for tile list)
x (int): the x coordinate of the starting pixel
y (int): the y coordinate of the starting pixel
shapeList (list): the list that the shape's coordinates will be appended to recursively
"""
# base case
if not self.getTileType(x, y) == tile:
return
# recursive case
self.populate(x, y, (0, 0, 0, 0))
shapeList.append((x, y))
adjacentPixels = list(self.getAdjacentCoords(x, y).values())
for pixel in adjacentPixels:
self.coagulateShape(tile, pixel[0], pixel[1], shapeList)

Draw triangle's in pygame given only side lengths

I have three line lengths and I need to plot a triangle on the screen with them.
Say I have:
len1 = 30
len2 = 50
len3 = 70
(these are randomly generated)
I can draw the first line at the bottom like this
pygame.draw.line(screen, red, (500,500), (500+len1,500), 10)
The other two lines will start at (500,500) and (500+len1,500) respectivly and will have the same endpoint but I can't figure out the math to get that location
Converted the formula in Jody Muelaner's answer here to python:
def thirdpoint(a, b, c):
result = []
y=((a**2)+(b**2)-(c**2))/(a*2)
x = math.sqrt((b**2)-(y**2))
result.append(x)
result.append(y)
return result

Categories

Resources