Python: Boundary points of a non-convex grid - python

I have the following situation as shown in the Figure below:
I want to find out the grid points that surround the red points. The red points are trajectories of moving agents. So in many situations we have a bunch of points, therefore the solution should be as fast as possible.
The grid is plotted as points.
First step, I managed to reduce the number of grid points as shown below (plotted as x):
This is my code:
step = .5
gridX, gridY = np.meshgrid(np.arange(xmin-step, xmax+step, step), np.arange(ymin-step, ymax+step, step))
mask = False * np.empty_like(gridX, dtype=bool)
threshold = 0.5
for (x,y) in zip(df_traj['X'], df_traj['Y']):
pX = x * np.ones_like(gridX)
pY = y * np.ones_like(gridY)
distX = (pX - gridX)**2
distY = (pY - gridY)**2
dist = np.sqrt(distX + distY)
condition = (dist < threshold)
mask = mask | condition
gX = gridX*mask
gY = gridY*mask
Second step, and this is where I need a little help:
How can I efficiently filter out the inner points of the grid and keep only the "x-points" outside the "red area"?
EDIT
In this special case I have 92450 red points.

I think if you just walk around the edge, since its a evenly spaced grid, it should work. No need for far more complicated non-convex-hull to handle pnts that can be anywhere. This isn't adapted to your code and I cheat with my data structures to make the code easy so youll have to handle that but it think as psuedocode it should work.
pnts = <<lists of points>>
edge_pnts = []
fpnt = pnt_with_min_x_then_min_y
cpnt = fpnt
npnt = None
while npnt != fpnt:
if (cpnt[0] + 1, cpnt[1] ) in pnts: npnt = (cpnt[0] + 1, cpnt[1] )
elif (cpnt[0] + 1, cpnt[1] + 1) in pnts: npnt = (cpnt[0] + 1, cpnt[1] + 1)
elif (cpnt[0], cpnt[1] + 1) in pnts: npnt = (cpnt[0] , cpnt[1] + 1)
elif (cpnt[0] - 1, cpnt[1] + 1) in pnts: npnt = (cpnt[0] - 1, cpnt[1] + 1)
elif (cpnt[0] - 1, cpnt[1] ) in pnts: npnt = (cpnt[0] - 1, cpnt[1] )
elif (cpnt[0] - 1, cpnt[1] - 1) in pnts: npnt = (cpnt[0] - 1, cpnt[1] - 1)
elif (cpnt[0] , cpnt[1] - 1) in pnts: npnt = (cpnt[0] , cpnt[1] - 1)
elif (cpnt[0] + 1, cpnt[1] - 1) in pnts: npnt = (cpnt[0] + 1, cpnt[1] - 1)
else: raise ValueError("Oh no!")
edge_pnts.append(npnt)
cpnt = npnt

For non convex polygons, like your example, convex hull is not a solution. My recommendation is that, given you already have a discrete grid, that you simply attribute the value False to a bool grid cell when a sample occurs inside. Something like this:
import numpy as np
import matplotlib.pyplot as plt
# Generic data production
X, Y = np.random.normal(0, 1, 100000), np.random.normal(0, 1, 100000)
ind = np.where((X > 0) & (Y > 0))
X[ind] = 0
Y[ind] = 0
# Generic grid definition
step = 0.5
xmin, xmax = X.min(), X.max()
ymin, ymax = Y.min(), Y.max()
firstx = xmin-step/2
firsty = ymin-step/2
lastx = xmax+2*step/2
lasty = ymax+2*step/2
gridX, gridY = np.meshgrid(np.arange(firstx, lastx, step), np.arange(firsty, lasty, step))
# This is the actual code that computes inside or outside
bool_grid = np.ones(gridX.shape, dtype="bool")
bool_grid[np.int_(0.5+(Y-firsty)/step), np.int_(0.5+(X-firstx)/step)] = False
# Plot code
plt.scatter(gridX.flatten(), gridY.flatten(), marker="+", color="black", alpha=0.3)
plt.scatter(gridX[bool_grid].flatten(), gridY[bool_grid].flatten(), marker="+", s=90, color="green")
plt.scatter(X, Y, s=10, color="red")
plt.show()
, which results in the following (green crosses are True values):
NOTE: This is very fast but it has some limitations. If your data is not compact you'll have True values inside the shape (so holes are possible). It's possible to process the image to remove the holes however (a flood fill or a moving window based algorithm for example). Another possibility is to play with the resolution of the grid.

You just need to pick a point you know is on the hull (let's take the leftmost point among the topmost points), and assume you "got to it" from above (as we know there is no points above it).
now while the next point is not in your list:
Try going CCW from the direction you came from.
The code looks like that:
matrix = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
# Find the leftmost topmost point
first_point = None
for i in range(len(matrix)):
if first_point:
break
for j in range(len(matrix[0])):
if matrix[i][j]:
first_point = [i, j]
break
next_point = first_point
prev_direction = 'up'
next_direction_dict = {'up': 'left', 'left': 'down', 'down': 'right', 'right': 'up'}
opposite_direction = {'up': 'down', 'left': 'right', 'down': 'up', 'right': 'left'}
hull_points = []
def go_direction(point, direction):
# Find the point to a given direction of a given point
i = point[0]
j = point[1]
if direction == 'right':
j += 1
elif direction == 'up':
i -= 1
elif direction == 'left':
j -= 1
elif direction == 'down':
i += 1
else:
raise ValueError
return [i, j]
def find_next_point(matrix, point, prev_direction):
next_direction = next_direction_dict[prev_direction]
next_point = go_direction(point, next_direction)
prev_direction = next_direction
while not matrix[next_point[0]][next_point[1]]:
next_direction = next_direction_dict[prev_direction]
next_point = go_direction(point, next_direction)
prev_direction = next_direction
from_direction = opposite_direction[prev_direction]
return next_point, from_direction
next_point, prev_direction = find_next_point(matrix, next_point, prev_direction)
while next_point != first_point:
if next_point not in hull_points:
hull_points.append(next_point)
next_point, prev_direction = find_next_point(matrix, next_point, prev_direction)
Edit:
Now also handles single point 'tentacles' by iterating until returning to the first point

Heres another though that came to my mind --
A flood fill of the space:
pnts = <<lists of points>>
seen = set()
edges = []
stack = (0,0)
while stack:
ele = stack.pop()
if ele in pnts:
edges.append(ele)
else:
seen.add(ele)
if (ele[0] + 1, ele[1]) not in seen:
stack.append(ele[0] + 1, ele[1])
if (ele[0] - 1, ele[1]) not in seen:
stack.append(ele[0] - 1, ele[1])
if (ele[0], ele[1] + 1) not in seen:
stack.append(ele[0], ele[1] + 1)
if (ele[0], ele[1] - 1) not in seen:
stack.append(ele[0], ele[1] - 1)
Then you need to sort the points which shouldn't be too hard.

Related

Python Matrix check for diagonals and horizontals correctly to win a game

I'm building a game like Connect4 the user can connect any number. I am having a problem checking the horizontal and diagonals lines:
def WinningSequenceCheck(board,piece,row_count):
#Horizontal Check
piecePlayer1=0
piecePlayer2=0
win_sequence=board[0]["TamanhoSequĂȘncia"]
width=board[0]["CumpGrelha"]
height=row_count
for c in range(height+1):
for r in range(width):
if board[0]["Tabuleiro"][c][r]==1:
piecePlayer1+=1
if piecePlayer1==win_sequence:
return True
if board[0]["Tabuleiro"][c][r]==2:
pecasPlayer2+=1
if pecasPlayer2==win_sequence:
return True
#For diagonals i did this:
diag1=0
diag2=0
a=0
b=height
d=0
for c in range(width):
for l in range(height+1):
if board[0]["Tabuleiro"][b-l][l+a]==1:
diag1+=1
if diag1==win_sequence:
return True
if board[0]["Tabuleiro"][b-l][l+a]==2:
diag2+=1
if diag2==win_sequence:
return True
if (b-l<height):
b=height
if l<=l-1:
a=a
elif(a+l<width-1):
a+=1
elif(a+l<=width-1):
return
else:
break
The problem with diagonals is that it goes from left to right but it's not checking properly if there is a line of a winning sequence of 1s or 2s
If it finds it should return true because im incrementing each time he finds 1 or 2
Im i doing the horizontal check properly?
Is there a better way to check in diagonals ?
The expected result from the horizontal is that if it finds the number 1 it adds to the variable piecePlayer1 (for the player 1)
If the piecePlayer1 is equal to Victory sequence ( example victory sequence is 5) the game returns true because in one of the lines he found 5 one's .
Winning Sequence=5
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0] ---> Win
The same is for the diagonal check:
Winning Sequence =4
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0] Diagonal WIN
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
#Stunna, here is simple sample code to check the diagonal values, you can try it and modify to suit your main module:
def winner(board, length):
"""Returns the 'mark' of the winning player"""
W = range(len(board)) # width
H = range(len(board[0])) # height
directions = [(0, 1), (1, 0), (1, 1), (1, -1)]
for dx, dy in directions:
edges = []
if dx > 0:
edges += [(0, y) for y in H]
if dy > 0: # scanning down
edges += [(x, 0) for x in W]
if dy < 0: # scanning up
edges += [(x, H[-1]) for x in W]
for ex, ey in edges:
row = 0; mark = None
x, y = ex, ey
while x in W and y in H:
if board[x][y] == mark:
row += 1
else:
mark = board[x][y]
row = 1
if mark is not None and row >= length:
return mark
x, y = x + dx, y + dy
return None
print(winner([
['X', 'O', 'O', 'O'],
['O', 'X', 'O', 'X'],
['O', 'O', 'X', 'O'],
['O', 'X', 'X', 'X'] ], 4)) # X

I have a problem understanding the A* Algorithm (Python)

I'm trying to look into the A* Algorithm but I'm kind of having a hard time understanding a specific part. So the A* Algorithm Python Code with the example is this:
class Node():
"""A node class for A* Pathfinding"""
def __init__(self, parent=None, position=None):
self.parent = parent
self.position = position
self.g = 0
self.h = 0
self.f = 0
def __eq__(self, other):
return self.position == other.position
def astar(maze, start, end):
"""Returns a list of tuples as a path from the given start to the given end in the given maze"""
# Create start and end node
start_node = Node(None, start)
start_node.g = start_node.h = start_node.f = 0
end_node = Node(None, end)
end_node.g = end_node.h = end_node.f = 0
# Initialize both open and closed list
open_list = []
closed_list = []
# Add the start node
open_list.append(start_node)
# Loop until you find the end
while len(open_list) > 0:
# Get the current node
current_node = open_list[0]
current_index = 0
for index, item in enumerate(open_list):
if item.f < current_node.f:
current_node = item
current_index = index
# Pop current off open list, add to closed list
open_list.pop(current_index)
closed_list.append(current_node)
# Found the goal
if current_node == end_node:
path = []
current = current_node
while current is not None:
path.append(current.position)
current = current.parent
return path[::-1] # Return reversed path
# Generate children
children = []
for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]: # Adjacent squares
# Get node position
node_position = (current_node.position[0] + new_position[0], current_node.position[1] + new_position[1])
# Make sure within range
if node_position[0] > (len(maze) - 1) or node_position[0] < 0 or node_position[1] > (len(maze[len(maze)-1]) -1) or node_position[1] < 0:
continue
# Make sure walkable terrain
if maze[node_position[0]][node_position[1]] != 0:
continue
# Create new node
new_node = Node(current_node, node_position)
# Append
children.append(new_node)
# Loop through children
for child in children:
# Child is on the closed list
for closed_child in closed_list:
if child == closed_child:
continue
# Create the f, g, and h values
child.g = current_node.g + 1
child.h = ((child.position[0] - end_node.position[0]) ** 2) + ((child.position[1] - end_node.position[1]) ** 2)
child.f = child.g + child.h
# Child is already in the open list
for open_node in open_list:
if child == open_node and child.g > open_node.g:
continue
# Add the child to the open list
open_list.append(child)
def main():
maze = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0]]
start = (4, 3)
end = (4, 5)
path = astar(maze, start, end)
print(path)
if __name__ == '__main__':
main()
In the
for index, item in enumerate(open_list):
if item.f < current_node.f:
current_node = item
current_index = index
I don't get how the current_node can be defined as the item in the maze I've given above. In the example I've given above, the start = (4,3) and end = (4,5), giving the only possible shortest distance would be as something like the following:
maze = [[0, 0, 0, 0, *, 0, 0, 0, 0, 0],
[0, 0, 0, *, 1, *, 0, 0, 0, 0],
[0, 0, 0, *, 1, *, 0, 0, 0, 0],
[0, 0, 0, *, 1, *, 0, 0, 0, 0],
[0, 0, 0, s, 1, e, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0]]
with the s being the start_node and e being the end_node.
However, in the code of the A* Algorithm, the current_node becomes the item only if the item.f is smaller than the current_node.f. In the example I've given here, I can't see that the first * would have an f value smaller than the f value of the start_node - I mean, in the code, we already have described the start_node.f = 0 haven't we? And we defined the first current_node as the start_node... so no item in the open_list would have an item.f value smaller than zero..
How is this possible?? Or am I missing something here??
I think the clue is that you have to take into account the two lines above this for loop as well:
# Get the current node
current_node = open_list[0]
current_index = 0
for index, item in enumerate(open_list):
if item.f < current_node.f:
current_node = item
current_index = index
What happens:
In the first iteration of your while loop:
There is only one item in the open_list, being the start_node where indeed f=0
So after the above code block, this start node becomes the current_node
Right after the above loop the start_node is removed from the open_list: open_list.pop(current_index)
The open_list is then populated by the valid neighbouring locations (by walking its children)
In the second iteration of your while loop:
The above code block looks for the item in the open_list with the lowest f value
because of the first line current_node = open_list[0], you will be sure that the new current_node is always one from the open_list.
as the start_node has been removed from the open_list, it will for sure be replaced here

Computing Adjusted Rand Index

I am trying to compute the ARI between two sets of clusters, using this code:
#computes ARI for this type of clustering
def ARI(table,n):
index = 0
sum_a = 0
sum_b = 0
for i in range(len(table)-1):
for j in range(len(table)-1):
sum_a += choose(table[i][len(table)-1],2)
sum_b += choose(table[len(table)-1][j],2)
index += choose(table[i][j],2)
expected_index = (sum_a*sum_b)
expected_index = expected_index/choose(n,2)
max_index = (sum_a+sum_b)
max_index = max_index/2
return (index - expected_index)/(max_index-expected_index)
#choose to compute rand
def choose(n,r):
f = math.factorial
if (n-r)>=0:
return f(n) // f(r) // f(n-r)
else:
return 0
assuming I have created the contingency table correctly, I still get values outside the range of (-1,1).
For instance:
Contingency table:
[1, 0, 0, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 0, 0, 1]
[0, 0, 0, 1, 0, 0, 0, 1]
[0, 1, 0, 0, 0, 0, 0, 1]
[0, 0, 0, 0, 0, 1, 1, 2]
[1, 0, 1, 0, 1, 0, 0, 3]
[0, 0, 0, 0, 0, 0, 1, 1]
[3, 1, 1, 1, 1, 1, 2, 0]
yields an ARI of -1.6470588235294115 when I run my code.
Is there a bug in this code?
Also Here is how I am computing the contingency matrix:
table = [[0 for _ in range(len(subjects)+1)]for _ in range(len(subjects)+1)]
#comparing all clusters
for i in range(len(clusters)):
index_count = 0
for subject, orgininsts in orig_clusters.items():
madeinsts = clusters[i].instances
intersect_count = 0
#comparing all instances between the 2 clusters
for orginst in orgininsts:
for madeinst in makeinsts:
if orginst == madeinst:
intersect_count += 1
table[index_count][i] = intersect_count
index_count += 1
for i in range(len(table)-1):
a = 0
b = 0
for j in range(len(table)-1):
a += table[i][j]
b += table[j][i]
table[i][len(table)-1] = a
table[len(table)-1][i] = b
clusters is a list of cluster objects that have attribute instances, which is a list of instances contained in that cluster. orig_clusters is a dictonary with keys representing cluster labels, and values are a list of instances contained in that cluster. Is there a bug in this code?
You make some mistakes calculating the ARI in your code -- you calculate a and b too often because you loop over your table twice instead of just once.
Also, you pass n as a parameter, but apparently it is set to 10 (that is how I get your result). It would be easier to just pass the table and then calculate n from there. I fixed your code a bit:
def ARI(table):
index = 0
sum_a = 0
sum_b = 0
n = sum([sum(subrow) for subrow in table]) #all items summed
for i in range(len(table)):
b_row = 0#this is to hold the col sums
for j in range(len(table)):
index += choose(table[i][j], 2)
b_row += table[j][i]
#outside of j-loop b.c. we want to use a=rowsums, b=colsums
sum_a += choose(sum(table[i]), 2)
sum_b += choose(b_row, 2)
expected_index = (sum_a*sum_b)
expected_index = expected_index/choose(n,2)
max_index = (sum_a+sum_b)
max_index = max_index/2
return (index - expected_index)/(max_index-expected_index)
or if you pass on the table with row- and column sums:
def ARI(table):
index = 0
sum_a = 0
sum_b = 0
n = sum(table[len(table)-1]) + sum([table[i][len(table)-1] for i in range(len(table)-1)])
for i in range(len(table)-1):
sum_a += choose(table[i][len(table)-1],2)
sum_b += choose(table[len(table)-1][i],2)
for j in range(len(table)-1):
index += choose(table[i][j],2)
expected_index = (sum_a*sum_b)
expected_index = expected_index/choose(n,2)
max_index = (sum_a+sum_b)
max_index = max_index/2
return (index - expected_index)/(max_index-expected_index)
then
def choose(n,r):
f = math.factorial
if (n-r)>=0:
return f(n) // f(r) // f(n-r)
else:
return 0
table = [[1, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 1, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 1, 1, 2],
[1, 0, 1, 0, 1, 0, 0, 3],
[0, 0, 0, 0, 0, 0, 1, 1],
[3, 1, 1, 1, 1, 1, 2, 0]]
ARI(table)
ARI(table)
Out[56]: -0.0604008667388949
The correct result!

Data detector for number 1 and 0

I have a data set containing with only 0 and 1. I want to have a detector to find where 1 starts and where 1 ends, and then return something related to their index to a different list each. So I've written some codes as below:
n= [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
def detector (data):
x = 0
start = []
end = []
for index, i in enumerate(data):
if x == 0 and i == 1:
start.append((index+1))
x == 1
elif x == 1 and i==0:
end.append((index))
x == 0
return start, end
print (detector(n))
However when I run the code above, it returned like below, which is not my desired output.
([1, 2, 3, 4, 22, 23, 24, 25, 26, 27, 28, 34, 35, 36, 37, 38], [])
My desired output is as below:
([1, 22, 34], [4,28,38])
As you can see above, the start_time should be[1,22,34] and end_time should be [4,28,38].
If anyone knows how to solve the issue, pls let me know. Appreciated!!
One issue is certainly, that you dont change flag.
== is a comparison operator and does not assign a new value to flag
using enumerate to get positions of 1s and zip to find when sequence of consecutive 1s starts/ends
ones_positions = [position
for position, value in enumerate(n)
if value == 1]
ones_starts = [ones_positions[0]] + [
next_position
for position, next_position in zip(ones_positions,
ones_positions[1:])
if next_position - position > 1]
ones_ends = [position
for position, next_position in zip(ones_positions,
ones_positions[1:])
if next_position - position > 1] + [ones_positions[-1]]
gives us
>>>ones_starts
[0, 21, 33]
>>>ones_ends
[3, 27, 37]
we can specify enumerate's start parameter if you want your indices to start from 1 (when they are naturally start from 0)
ones_positions = [position
for position, value in enumerate(n, start=1)
if value == 1]
after that
>>>ones_starts
[1, 22, 34]
>>>ones_ends
[4, 28, 38]
Finally we can write it as function:
def detector(data, target_value=1):
positions = [position
for position, value in enumerate(data, start=1)
if value == target_value]
start_times = [positions[0]] + [
next_position
for position, next_position in zip(positions,
positions[1:])
if next_position - position > 1]
end_times = [position
for position, next_position in zip(positions,
positions[1:])
if next_position - position > 1] + [positions[-1]]
return start_times, end_times
and test
n = [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
print(detector(n))
gives us
([1, 22, 34], [4, 28, 38])
n = [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
prev_num = 0
starts = []
ends = []
result = (starts, ends)
for idx, num in enumerate(n):
if prev_num == 0 and num == 1:
starts.append(idx + 1)
elif prev_num == 1 and num == 0:
ends.append(idx + 1)
elif num == 1 and idx == (len(n) - 1):
ends.append(idx + 1)
prev_num = num
print(result)
Which prints:
[[1, 22, 34], [5, 29, 38]]
Since #DanielChristiany pointed you where your mistake was. I will present you my solution which is faster than any of presented(at least that works correctly):
edges = (index for index, i in enumerate(n[1:], 1) if i != n[index-1])
if n[0] == 1:
edges = (1, *edges)
if n[-1] == 1:
some = (*edges, len(n))
print(edges[::2], edges[1::2])
Basically it firstly searches edges where element changes from 0 to 1 or from 1 to 0. Then checks if first and last elements are 1 and then print result.
This solution also uses less memory since it uses generators.
You could also try using groupby:
import itertools
L = [[y[0] for y in it]
for x,it in
itertools.groupby(enumerate(n),lambda x: x[1])
][::2]
res = [x[0] for x in L],[x[-1] for x in L]
You could probably arrive at an even more correct solution without using indexes.
Thanks to vishes_shell for the correction

rotating the tires in a car (open gl)

Based on the answer i got for the same question earlier, i changed my code, as per the homework i had to use glmultmatrix. But this is not working. Here is the code, what i am doing is that i translate the center of tire to the center of car, rotate the tire, and then translate back. But it is not placing back the tire where it should be:
if (name == 'Front Driver tire' ) & (self.fFlag == "true"):
self.getCenterTireRim()
bodyFace = self.mini.group(name)
glPushMatrix()
x = self.carCenterX - self.xtFront
y = self.carCenterY - self.ytFront
z = self.carCenterZ - self.ztFront
A = self.matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1)
glMultMatrixd(cast(A, POINTER(c_double)))
#print self.carCenterX, self.carCenterY, self.carCenterZ
#print self.xtFront, self.ytFront, self.ztFront
B = self.matrix(1,0,0,0,0, math.cos(math.radians(self.angle1 + 45)), math.sin(math.radians(self.angle1 + 45)), 0, 0, -math.sin(math.radians(self.angle1 + 45)), math.cos(math.radians(self.angle1 + 45)), 0, 0,0,0, 1)
glMultMatrixd(cast(B, POINTER(c_double)))
for face in bodyFace:
if len(face) == 3:
glBegin(GL_TRIANGLES)
elif len(face) == 4:
glBegin(GL_QUADS)
else:
glBegin(GL_POLYGON)
for i in face:
glNormal3f(*self.mini.normal(i))
glVertex3f(*self.mini.vertex(i))
glEnd()
C = self.matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x - self.carCenterX , y - self.carCenterY, z - self.carCenterZ, 1)
glMultMatrixd(cast(C, POINTER(c_double)))
glPopMatrix()
elif (name == 'Front Driver tire rim') & (self.fFlag == "true"):
bodyFace = self.mini.group(name)
glPushMatrix()
self.getCenterTireRim()
bodyFace = self.mini.group(name)
A1 = self.matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, self.carCenterX - self.xrFront, self.carCenterY - self.yrFront, self.carCenterZ - self.zrFront, 1)
glMultMatrixd(cast(A1, POINTER(c_double)))
#print self.carCenterX, self.carCenterY, self.carCenterZ
#print self.xrFront, self.yrFront, self.zrFront
B1 = self.matrix(1,0,0,0,0, math.cos(math.radians(self.angle1 + 45)), math.sin(math.radians(self.angle1 + 45)), 0, 0, -math.sin(math.radians(self.angle1 + 45)), math.cos(math.radians(self.angle1 + 45)), 0, 0, 0, 0, 1)
glMultMatrixd(cast(B1, POINTER(c_double)))
for face in bodyFace:
if len(face) == 3:
glBegin(GL_TRIANGLES)
elif len(face) == 4:
glBegin(GL_QUADS)
else:
glBegin(GL_POLYGON)
for i in face:
glNormal3f(*self.mini.normal(i))
glVertex3f(*self.mini.vertex(i))
glEnd()
C1 = self.matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, self.xrFront - self.carCenterX , self.yrFront - self.carCenterY, self.zrFront - self.carCenterZ, 1)
glMultMatrixd(cast(C1, POINTER(c_double)))
glPopMatrix()
I'm pretty sure that it is because this line
C = self.matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x - self.carCenterX , y - self.carCenterY, z - self.carCenterZ, 1)
should be
C = self.matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -x , -y, -z, 1)
I'm not sure why you were using x - self.carCenterX and so forth. You translated one way (x,y,z) so just go back the opposite direction, -(x,y,z) = (-x,-y,-z)
I hope that helps.

Categories