I have been at it for a long time but can't finish what I'm trying to do. I am trying to find a way to flip coordinates based on a list of objects and a list of their coordinates.
objects = [1, 2, 3, 4, 5]
grid_placement = [(0,0), (32, 0), (64, 0), (0, -32), (0, -64)]
I want to get the flipped axis coordinates of where they are
So the ideal flipped Y output would essentially be:
placement = [(0, -64), (32, -64), (64, -64), (0, -32), (0, 0)]
And the ideal flipped X output would be:
placement = [(64, 0), (32, -64), (0,0), (64, -32), (64, -64)]
So essentially if something is in the top right, it would be on the top left after flipping on X. Their index positions would remain the same in the list, but with the altered coordinates.
The code I have cobbled together works just fine for the Y axis, but I cannot get the X axis working. My brain is a little fried from messing with this for a few hours, any outside advice would be highly appreciated.
Here is my code (messy)
gridSize = 32
if axis == "x":
columns = defaultdict(dict)
for gridOffset in self.offsetList:
row = gridOffset[1] // gridSize
col = gridOffset[0] // gridSize
columns[col][gridOffset] = row
new_order = []
order = list(reversed(list(columns.keys())))
for col in order:
for offset in self.offsetList:
if offset in columns[col]:
new_order.append((col * gridSize, columns[col][offset] * gridSize))
elif axis == "y":
rows = defaultdict(dict)
for gridOffset in self.offsetList:
row = gridOffset[1] // gridSize
col = gridOffset[0] // gridSize
rows[row][gridOffset] = col
new_order = []
order = list(reversed(list(rows.keys())))
for offset in self.offsetList:
for row in order:
if offset in rows[row]:
new_order.append((rows[row][offset] * gridSize, row * -gridSize))
self.offsetList = new_order
Your examples suggest that you want to swap object coordinates rather than "flip" them. To actually flip on a Cartesian plane you would need a pivot point. If that point is (0,0) flipping would amount to inverting the sign of the X or Y coordinate you want to flip.
On the other hand if you want to swap coordinates between objects, use zip:
# vertical swap
grid_placement = [(F[0],B[1]) for F,B in zip(grid_placement, grid_placement[::-1])
# horizontal swap
grid_placement = [(F[1],B[0]) for F,B in zip(grid_placement, grid_placement[::-1])
Related
I need to draw n planes in 3D space. The quads are planes created from two points, and with an algorithm I get 4 vertex for drawing the quad. The problem I have is that the order of the vertices affect to the result, obviously. Here it is what I mean:
But when the plane is horizontal instead of vertical:
I can imagine two possible solutions. Using triangles and combining them or ordering the vertex properly. I dont know how to do the second idea. I have tried using triangles but I get the same problem.
# self.planos = [('A', (500, 500, 10), (-500, 500, 10), (-500, -500, 10), (500, -500, 10))] for horizontal
# self.planos = [('A', (-500, 10, 500), (500, 10, 500), (-500, 10, -500), (500, 10, -500))] for vertical
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glDepthMask(GL_FALSE)
glBegin(GL_QUADS)
glColor(0.5, 0.5, 0.1, 0.5)
for i in range(len(self.planos)):
glVertex(self.planos[i][1][0], self.planos[i][1][2], self.planos[i][1][1])
glVertex(self.planos[i][2][0], self.planos[i][2][2], self.planos[i][2][1])
glVertex(self.planos[i][3][0], self.planos[i][3][2], self.planos[i][3][1])
glVertex(self.planos[i][4][0], self.planos[i][4][2], self.planos[i][4][1])
glEnd()
glDepthMask(GL_TRUE)
glDisable(GL_BLEND)
Intersection code for getting four vertex to draw planes:
In init method:
#Vertices of the cube
self.v = (Point3D(500, 500, 500), Point3D(-500, 500, 500), Point3D(-500, -500, 500),
Point3D(500, -500, 500), Point3D(500, 500, -500), Point3D(-500, 500, -500),
Point3D(-500, -500, -500), Point3D(500, -500, -500))
# Edges of the cube
self.a = (Segment3D(self.v[0], self.v[1]), Segment3D(self.v[1], self.v[2]),
Segment3D(self.v[2], self.v[3]), Segment3D(self.v[3], self.v[0]),
Segment3D(self.v[0], self.v[4]), Segment3D(self.v[1], self.v[5]),
Segment3D(self.v[2], self.v[6]), Segment3D(self.v[3], self.v[7]),
Segment3D(self.v[4], self.v[5]), Segment3D(self.v[5], self.v[6]),
Segment3D(self.v[6], self.v[7]), Segment3D(self.v[7], self.v[4]))
# Algorithm for getting 4 points
def plano_limites(self, point1, point2, point3):
plano = Plane(Point3D(point1), Point3D(point2), Point3D(point3))
good = []
for i in range(12):
a = intersection(plano, self.a[i]) # Sympy intersection
if a:
good.append(a[0])
return good
First of all, be aware that your intersection can result in more or less than four vertices. But since the region will always be convex, you can simply draw it with a triangle fan.
To sort your vertices, you need the normal n of the plane and the centroid c of all the vertices v_i:
c = 1/(number of vertices) * (v_1 + v_2 + v3 + ...)
Then, we need a coordinate system whose z-axis is the normal. For this, we can simply define an arbitrary other direction vector d and define x = normalize(cross(d, normal)), y = cross(normal, x). For cases where d conincides with normal, we need an alternative d.
We can then calculate a representative angle of any vertex in this coordinate system:
angle_i = atan2(dot(x, v_i - c), dot(y, v_i - c))
Sort by this angle and you are done.
Thanks to Nico's answer I was able to learn how to do this for myself but I thought it would be useful to do a full write up as it still took me further learning to understand what was going on here.
We essentiality need to find an x- and y-axis where the z-axis is aligned with the normal, we do this by taking the cross product of an arbitrary vector other. If the other vector coincides with the normal we will need to use a different vector. Two vectors coincide if their dot product is equal to 1.
By using the other vector of (0, 1, 0) (or (0, 0, -1) in the case they coincide) in a right-hand coordinate system we will produce easier to understand x- and y-axis vectors. This does not matter for the sake of the algorithm but I found this was the most crucial part initially missing in my own understanding.
By using the other vector of (0, 1, 0) when the normal of the plane is (0, 0, 1) the x-axis will be (1, 0, 0) and the y-axis will be (0, 1, 0).
By using the other vector of (0, 0, -1) when the normal of the plane is (0, 1, 0) the x-axis will be (1, 0, 0) and the y-axis will be (-1, 0, 0).
This can easily be modeled by using your right hand and turning your finger that is the normal to point up to positive z-axis (towards yourself).
def order_on_plane(vertices, normal):
# Find the centroid of the vertices
centroid = (0, 0, 0)
for v in vertices:
centroid = add(centroid, v)
centroid = scale(centroid, 1 / len(vertices))
# Determine the 'other' vector, used to create the axis vectors
other = (0, 1, 0)
# If the other vector coincides with the normal vector, we need to use a different other
if math.fabs(math.fabs(dot(other, normal)) - 1.0) < 0.0001:
other = (0, 0, -1)
# Create the axis vectors
x_axis = normalize(cross(other, normal))
y_axis = normalize(cross(normal, x_axis))
# Sort by angle as a vector from the centroid
angles = []
for v in vertices:
vector = sub(v, centroid)
x_pos = dot(vector, x_axis)
y_pos = dot(vector, y_axis)
# y_pos is passed in first for east counter clockwise convention
angle = math.atan2(y_pos, x_pos)
angles.append(angle)
# Sort vertices by angle
vertices = sorted(zip(angles, vertices), key=lambda x: x[0])
vertices = list(map(lambda x: x[1], vertices))
return vertices
I have a sorted list dataPts that is sorted based on the angle each point makes with the minimum Y value minY in dataPts, such as [(0, 0), (10, 10), (20, 20) ... ] (0, 0) being minY.
Then I create a new list angles which is a list of all those angles, for instance [0, 45, 45, ...].
You will notice that angles contains duplicate values, for instance 45, 45,. What I want to do is locate the points in dataPts that share the same angle. I then want to delete those points, EXCEPT the one that is the furthest distance from minY using a function that returns a value.
For example, (10, 10) and (20, 20) both have corresponding values in angles, which is 45. How can I pick out the value with greater distance to minY which is (20, 20) and delete (10, 10)?
you could create a dict using the angles as keys, where the values are all of the elements with a given angle, then choose the max based on your distance function.
i.e. something like:
d = defaultdict(lambda: [])
for angle, pt in zip(angles, dataPts):
d[angle].append(pt)
result = [max(pt, key=my_dist_func) for angle, pt in d.items()]
Given the ymin and distance function you're describing, I think this works:
from collections import defaultdict
dataPts = [(0, 0), (10, 10), (20, 20) ]
angles = [0,45,45]
ymin = min((p[1] for p in dataPts))
d = defaultdict(lambda: [])
for angle, pt in zip(angles, dataPts):
d[angle].append(pt)
result = [max(pt, key=lambda p: p[1]-ymin) for angle, pt in d.items()]
Try this
angles1 = [(0, 0), (10, 10), (20, 20)]
angles = [0, 45, 45]
dumy = {}
duplicates = []
for i,items in enumerate(angles):
if (items not in dumy):
dumy[items] = ""
else:
duplicates.append(i)
if((angles[i-1] == items) and i-1 not in duplicates):
duplicates.append(i-1)
for i in (duplicates):
del angles1[i]
Suppose if you want to remove the only duplicates, try the following code
for i,items in enumerate(angles):
if (items not in dumy):
dumy[items] = ""
else:
duplicates.append(i)
del angles1[i]
if((angles[i-1] == items) and i-1 not in duplicates):
del angles1[i-1]
Suppose that the variables x and theta can take the possible values [0, 1, 2] and [0, 1, 2, 3], respectively.
Let's say that in one realization, x = 1 and theta = 3. The natural way to represent this is by a tuple (1,3). However, I'd like to instead label the state (1,3) by a single index. A 'brute-force' method of doing this is to form the Cartesian product of all the possible ordered pairs (x,theta) and look it up:
import numpy as np
import itertools
N_x = 3
N_theta = 4
np.random.seed(seed = 1)
x = np.random.choice(range(N_x))
theta = np.random.choice(range(N_theta))
def get_box(x, N_x, theta, N_theta):
states = list(itertools.product(range(N_x),range(N_theta)))
inds = [i for i in range(len(states)) if states[i]==(x,theta)]
return inds[0]
print (x, theta)
box = get_box(x, N_x, theta, N_theta)
print box
This gives (x, theta) = (1,3) and box = 7, which makes sense if we look it up in the states list:
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3)]
However, this 'brute-force' approach seems inefficient, as it should be possible to determine the index beforehand without looking it up. Is there any general way to do this? (The number of states N_x and N_theta may vary in the actual application, and there might be more variables in the Cartesian product).
If you always store your states lexicographically and the possible values for x and theta are always the complete range from 0 to some maximum as your examples suggests, you can use the formula
index = x * N_theta + theta
where (x, theta) is one of your tuples.
This generalizes in the following way to higher dimensional tuples: If N is a list or tuple representing the ranges of the variables (so N[0] is the number of possible values for the first variable, etc.) and p is a tuple, you get the index into a lexicographically sorted list of all possible tuples using the following snippet:
index = 0
skip = 1
for dimension in reversed(range(len(N))):
index += skip * p[dimension]
skip *= N[dimension]
This might not be the most Pythonic way to do it but it shows what is going on: You think of your tuples as a hypercube where you can only go along one dimension, but if you reach the edge, your coordinate in the "next" dimension increases and your traveling coordinate resets. The reader is advised to draw some pictures. ;)
I think it depends on the data you have. If they are sparse, the best solution is a dictionary. And works for any tuple's dimension.
import itertools
import random
n = 100
m = 100
l1 = [i for i in range(n)]
l2 = [i for i in range(m)]
a = {}
prod = [element for element in itertools.product(l1, l2)]
for i in prod:
a[i] = random.randint(1, 100)
A very good source about the performance is in this discution.
For the sake of completeness I'll include my implementation of Julian Kniephoff's solution, get_box3, with a slightly adapted version of the original implementation, get_box2:
# 'Brute-force' method
def get_box2(p, N):
states = list(itertools.product(*[range(n) for n in N]))
return states.index(p)
# 'Analytic' method
def get_box3(p, N):
index = 0
skip = 1
for dimension in reversed(range(len(N))):
index += skip * p[dimension]
skip *= N[dimension]
return index
p = (1,3,2) # Tuple characterizing the total state of the system
N = [3,4,3] # List of the number of possible values for each state variable
print "Brute-force method yields %s" % get_box2(p, N)
print "Analytical method yields %s" % get_box3(p, N)
Both the 'brute-force' and 'analytic' method yield the same result:
Brute-force method yields 23
Analytical method yields 23
but I expect the 'analytic' method to be faster. I've changed the representation to p and N as suggested by Julian.
I am dealing with polygons composed of square tiles on a 2D grid. A polygon is simply stored as a list of tuples, with each tuple representing the coordinates of a tile. The polygons are always contiguous and have no holes.
What I want to be able to do is determine which of the tiles represent vertices along the border of the polygon, such that later I could trace between each one to produce the polygon's border, or determine the distance between two consecutive vertices to find the length of a side, etc.
Here is an example of a polygon (a 5x4 rectangle with a 3x2 rectangle subtracted from the top left, producing a backward 'L'):
polygon_tiles = [(3, 0), (4, 0), (3, 1), (4, 1), (0, 2), (1, 2), (2, 2), (3, 2),
(4, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3)]
Ideally the algorithm I am seeking would produce a result that looked like this:
polygon_verts = [(3, 0), (4, 0), (4, 3), (0, 3), (0, 2), (3, 2)]
with the vertices listed in order tracing around the border clockwise.
Just fiddling around with some test cases, this problem seems to be much more complicated than I would have thought, especially in weird circumstances like when a polygon has a 1-tile-wide extrusion (in this case one of the tiles might have to be stored as a vertex twice??).
I'm working in Python, but any insight is appreciated, even if it's in pseudocode.
Assuming your shape has no internal holes.
Find the topmost row. Pick the leftmost tile of this row. This guarantees we begin on a corner.
From this tile, attempt to go straight right If you can't, go straight downright, straight down, etc until you have picked a direction. This guarnatees we can trace a clockwise perimeter of the polygon
Continue to take steps in your chosen direction. After each step:
If the next step would be onto a tile, rotate counterclockwise and look again.
If the next step would be onto an empty space, rotate clockwise and look again.
Stop rotating once you have moved onto empty space and back onto a tile again.
If we rotated from the initial direction, we must be standing on a vertex. Mark it as such.
Mark every other tile you traverse as being part of the edge.
Keep walking the edge until you arrive at your initial tile. You may walk over tiles more than once in the case of 1 tile extrusions.
If this algorithm doesn't make sense in your head, try getting out some paper and following it by hand :)
This problem is a convex hull variation, for which e.g. the gift wrapping algorithm could be applied. The constraints of discrete coordinates and line directions lead to simplifications. Here is some python code that gives the desired answer (Patashu's answer is in the same spirit):
#!/usr/bin/python
import math
def neighbors(coord):
for dir in (1,0):
for delta in (-1,1):
yield (coord[0]+dir*delta, coord[1]+(1-dir)*delta)
def get_angle(dir1, dir2):
angle = math.acos(dir1[0] * dir2[0] + dir1[1] * dir2[1])
cross = dir1[1] * dir2[0] - dir1[0] * dir2[1]
if cross > 0:
angle = -angle
return angle
def trace(p):
if len(p) <= 1:
return p
# start at top left-most point
pt0 = min(p, key = lambda t: (t[1],t[0]))
dir = (0,-1)
pt = pt0
outline = [pt0]
while True:
pt_next = None
angle_next = 10 # dummy value to be replaced
dir_next = None
# find leftmost neighbor
for n in neighbors(pt):
if n in p:
dir2 = (n[0]-pt[0], n[1]-pt[1])
angle = get_angle(dir, dir2)
if angle < angle_next:
pt_next = n
angle_next = angle
dir_next = dir2
if angle_next != 0:
outline.append(pt_next)
else:
# previous point was unnecessary
outline[-1]=pt_next
if pt_next == pt0:
return outline[:-1]
pt = pt_next
dir = dir_next
polygon_tiles = [(3, 0), (4, 0), (3, 1), (4, 1), (0, 2), (1, 2), (2, 2), (3, 2),
(4, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3)]
outline = trace(polygon_tiles)
print(outline)
I would just calculate the slopes of the lines between the vertices
# Do sort stuff
vertices = []
for position, polygon in enumerate(polygon_tiles):
# look for IndexErrors
try:
polygon_tiles[position+1]
except IndexError:
break
try:
polygon_tiles[position+2]
except IndexError:
# Bad practice
position = position - 1
# calculate the slope of the line between of vertex 1 and vertex 2
s1 = (polygon_tiles[position+1][1] - polygon[1]) / (polygon_tiles[position+1][0] - polygon[0])
# calculate the slope of vertex 2 and vertex 3
s2 = (polygon_tiles[position+2][1] - polygon_tiles[position+1][1]) / (polygon_tiles[position+2][0] - polygon_tiles[position+1][0])
# if the slopes differ then you have a vertex
if d1 != d2:
vertices.append(polygon_tiles[position+1])
I have a python list of points (x/y coordinates):
[(200, 245), (344, 248), (125, 34), ...]
It represents a contour on a 2d plane. I would like to use some numpy/scipy algorithms for smoothing, interpolation etc. They normally require numpy array as input. For example scipy.ndimage.interpolation.zoom.
What is the simplest way to get the right numpy array from my list of points?
EDIT: I added the word "image" to my question, hope it is clear now, I am really sorry, if it was somehow misleading. Example of what I meant (points to binary image array).
Input:
[(0, 0), (2, 0), (2, 1)]
Output:
[[0, 0, 1],
[1, 0, 1]]
Rounding the accepted answer here is the working sample:
import numpy as np
coordinates = [(0, 0), (2, 0), (2, 1)]
x, y = [i[0] for i in coordinates], [i[1] for i in coordinates]
max_x, max_y = max(x), max(y)
image = np.zeros((max_y + 1, max_x + 1))
for i in range(len(coordinates)):
image[max_y - y[i], x[i]] = 1
Ah, better now, so you do have all the points you want to fill... then its very simple:
image = np.zeros((max_x, max_y))
image[coordinates] = 1
You could create an array first, but its not necessary.
numpy.array(your_list)
numpy has very extensive documentation that you should try reading. You can find it online or by typing help(obj_you_want_help_with) (eg. help(numpy)) on the REPL.
Building on what Jon Clements and Dunes said, after doing
new_array = numpy.array([(200, 245), (344, 248), (125, 34), ...])
you will get a two-dimensional array where the first column contains the x coordinates and the second column contains the y coordinates. The array can be further split into separate x and y arrays like this:
x_coords = new_array[:,0]
y_coords = new_array[:,1]