I am facing with the sorting airfoil coordinates. In particular given a set of coordinates, which are not sorted, I have to sorted them starting from the trailing edge upper surface. Here I report the code that I have developed but as you can see, the starting point do not match with what I suppose, moreover exist several oscillations as you can see in the reported figure (and a detail, in blue the starting point after the sort).
Can someone suggest me what I miss? How can I do?
Thanks you in advance.
def sort_airfoil(points):
x0 = np.mean(-points[:,1])
y0 = np.mean(points[:,2])
r = np.sqrt((-points[:,1]-x0)**2 + (points[:,2]-y0)**2)
tempx=-points[:,1]
xmax=np.max(tempx)
ind_max=np.where(tempx==xmax)
ymax=np.max(points[ind_max,2])
ind_max_t=np.where((tempx>0.95*xmax) & (tempx<xmax))
ymax_t=points[ind_max_t,2]
ymin=np.min(ymax_t)
indx_temp=np.where(points[:,2]==ymin)
xmin=np.max(tempx[indx_temp])
xmed=(xmin+xmax)/2
ymed=(ymin+ymax)/2
print(x0,y0)
print(xmin,ymin)
print((xmin+xmax)/2, (ymin+ymax)/2)
angle0=np.arctan2((ymed-y0),(xmed-x0))
print("angle", angle0)
angles = np.where((points[:,2]-y0) > 0, np.arccos((-points[:,1]-x0)/r), 2*np.pi-np.arccos((-points[:,1]-x0)/r))
angles=angles-angle0
for i in range(len(angles)):
if angles[i]<0:
angles[i]=angles[i]+2*np.pi
elif angles[i]>2*np.pi:
angles[i]=angles[i]-2*np.pi
mask = np.argsort(angles)
x_sorted = points[mask,1]
y_sorted = points[mask,2]
points_new=np.zeros([len(points), 3])
points_new[:,0]=points[:,0]
points_new[:,1]=x_sorted
points_new[:,2]=y_sorted
return points_new
The issue comes from the algorithm itself: it only work when the points form a convex polygon. However, the shape is concave.
More specifically, the first sorted points (and the last ones) form a zigzag-shaped lines because there is two sets of points (green arrows) interleaving with growing angles (red arrow) from the median point (red line).
Note the points are horizontally flipped on the gathered point from the question. Thus the points are sorted clockwise.
One simple solution is to split horizontally the shape in many set of point (eg. 10 set) so that each set form a convex shape. Then, the parts can be merged to form the final shape. The merge consists in finding the points at the "edge" of each locally-sorted set of points (parts) and reorder the partially sorted array of points consequently.
More specifically, the points of each part are split in 2 sub-sets: the upper ones and the lower ones. You can find them easily by selecting the 2 left-most points of a right part with the right-most points of a left part. The 2 top-most points needs to be connected each other and the same for the 2 bottom-most points. Thus, the sequence of the two upper sets of points needs to be reordered so they are contiguous and the same for the lower part.
Here is an example:
Note that if you are unsure about how to split the points in many parts so that each one form a convex-shaped sets of points, then you can: split the shape in n parts, check if the set of points form a convex shape by computing a convex hull (eg. using a Graham scan) and split evenly the parts that are concave (recursively). This is quite expensive, but more robust.
Related
I have a bunch of points (x, y and z) in a 3d space and want to extract some points out of them. I copied a simplified example with two arrays which are linked together:
all_points=[[np.array([[6.8,1.,0.1], [6.8,3.,0.1], [6.8,6.,0.1],\
[4.8,1.,2.], [4.8,3.,2.], [4.8,6.,2.],\
[3.8,1.,3.], [3.8,3.,3.], [3.8,6.,3.],\
[2.8,1.,4.1], [2.8,3.,4.1], [2.8,6.,4.1]]),\
np.array([[5.,1.,2.], [5.,3.,2.], [5.,6.,2.],\
[4.,1.,3.], [4.,3.,3.], [4.,6.,3.],\
[6.,1.,3.], [6.,3.,3.], [6.,6.,3.],\
[7.,1.,4.], [7.,3.,4.], [7.,6.,4.],\
[3.,1.,4.], [3.,3.,4.], [3.,6.,4.]])]]
Firstly, I want to check whether the array is normal or not. If I sort a normal array based on z values, the x value of srted array will be increasing or decreasing. First array (blue dots in upladed fig) clearly show a normal set. For normal arrays I just do a simple task and export four points showing corners of them (shown by yellow and green arrows in my fig). These points are found based on the minimum and maximum of x, y and z. Following code gives me four corners of normals:
four_corners=[]
for points in all_points:
for sub_points in points:
sorted_sub=np.sort(sub_points.view('i8,i8,i8'), order=['f2', 'f1'], axis=0).view('float')
le_st=sorted_sub[np.where(sorted_sub[:,2] == sorted_sub[0,2])]
le_st=len(le_st)
le_en=sorted_sub[np.where(sorted_sub[:,2] == sorted_sub[-1,2])]
le_en=len(le_en)
cor=np.array([sorted_sub[0,:], sorted_sub[int((le_st-1)),:], sorted_sub[-1,:], sorted_sub[-le_en,:]])
four_corners.append(cor)
In abnormal sets (black squares in my fig) usually some points are very close to a normal set (a limit can be defined) and then they go away. I want to extract four points but by creating two planes. First plane is created using three of the four corners points found for the normal points. Second surface is created using each three points of the abnormal points that are not close to the normal points (highlighted by a red line in my fig). Then, I want to find intersection line of two surfaces and find the x and z in the minimum and maximum of y (1 and 6) of the intersection. y value of all my corners points (normal or abnormal) is the minimum or maximum value. Other two points are created by substituting the y and z values of the two corners points coming from the normal plane that have higher z values (highlted by yellow arrows) into the equation of the plane of abnormal set. I only know how to create surfaces based on this solution. In reality I may have several normal and abnormal sets that all are linked to the normal. In advance, I do appreciate any help and contribution for doing what I want in python.
I have some points in the 3d space (x, y and z). These point sets are stored as arrays in lists. I copied a simplified example having two point sets:
all_points=[[np.array([[6.8,1.,0.1], [6.8,3.,0.1], [6.8,6.,0.1],\
[5.8,1.,1.1], [5.8,3.,1.1], [5.8,6.,1.1],\
[4.8,1.,2.], [4.8,3.,2.], [4.8,6.,2.],\
[3.8,1.,3.], [3.8,3.,3.], [3.8,6.,3.],\
[2.8,1.,4.1], [2.8,3.,4.1], [2.8,6.,4.1]]),\
np.array([[5.,1.,2.], [5.,3.,2.], [5.,6.,2.],[6.,1.,1.2],\
[4.,1.,3.], [4.,3.,3.], [4.,6.,3.],[5.5,3.,1.5],\
[6.,1.,3.], [6.,3.,3.], [6.,6.,3.],\
[7.,1.,4.], [7.,3.,4.], [7.,6.,4.],\
[3.,1.,4.], [3.,3.,4.], [3.,6.,4.]])]]
My point sets are normal or abnormal. They are normal if when I sort them based on their z, the x value will be only increasing or decreasing. Blue dots in my fig cleary show the normal type. But black squares show an abnormal point set. These two sets are linked because some points of the abnormal set are close to the normal one. Minimum and maximum of y value in both normal and abnormal sets is fixed (1 and 6 in my example). In normal set, I simply want four corners of them (shown by green arrows in my fig). This code gives me four corners:
four_corners=[]
for points in all_points:
for sub_points in points:
sorted_sub=np.sort(sub_points.view('i8,i8,i8'), order=['f2', 'f1'], axis=0).view('float')
le_st=sorted_sub[np.where(sorted_sub[:,2] == sorted_sub[0,2])]
le_st=len(le_st)
le_en=sorted_sub[np.where(sorted_sub[:,2] == sorted_sub[-1,2])]
le_en=len(le_en)
cor=np.array([sorted_sub[0,:], sorted_sub[int((le_st-1)),:], sorted_sub[-1,:], sorted_sub[-le_en,:]])
four_corners.append(cor)
Abnormal point sets can be devided into two groups: a group that is close to normal point sets and another one is far from them. A threshold can separate them. I tried the following code to seperate them (I should transfer my normal and abnormal arrays automatically here, but I have written them manually):
from scipy.spatial import distance
import numpy_indexed as npi
threshold=0.5
close_points=abnormal[np.where(np.min(distance.cdist(abnormal, normal),axis=0)<threshold)[0],:]
far_points= npi.difference(abnormal, close_points)
After separation, I want two points from far_points and two point from close_points. In far_points I want two point that have the highest z values and have min of y (1) and max of y (6). These two points are shown by yellow arrows in my fig and are:
[[7.,1.,4.], [7.,6.,4.]]
In close_points I want the points that their y value is again min and max (1 and 6). I name them y_min and y_max subgroups and from each subgroup, I want the point that the least z value. In my data they are and are shown by red arrows:
[[6.,1.,1.2],[5.,6.,2.]]
Finally, I want to find two point of the normal point sets that are closest to theese two point of close_points of the abnormal group. They are:
[[5.8,1.,1.1], [4.8,6.,2.]]
So, I want a method to firstly distiguish which array is normal and which is abnormal. Then find four simple corners of my normal sets and explained four explained points of abnormal sets. the method should be also able to ditinguish which normal set is connected to which abnormal ones. I may have one normal sets and two or three linked abnormal sets or maybe two normals and one abnormal which is connected to a normal set. I do appreciate any help for doing what I want in python.
I've posted this in another forum as well due to the mathematical nature of the issue:
forum post
I have an .ifc file in which the raw data exported describes a wall in the xy plane by a set of coordinates and their corresponding indexes according to the link explanation:
Explanation
I have a txt where the data is divided into the coordinates in xyz space, then indexes and some other data.
I was hoping that someone can help me understand how to link the indexes to their corresponding coordinates. There are 164 coordinate pairs and 324 index pairs so it doesn't make sense to me that each index relates to only 1 coordinate pair.
The goal is to establish a relationship between indexes and coordinates such that this type of data can output the wall thickness, which is in this case '10'. I was thinking that (according to the link above) by taking the first triangle described, it should describe the edge of the wall in 3D and therefore give us one of its sides as the shortest segment in the wall which is the thickness.
I received an answer in the mentioned forum post, that I should
"...expanding out each coordinate in terms of X's, Y's, and Z's [instead of (X,Y,Z) triples) and then use every index triple to get the actual coordinate for the individual coordinate instead of one triple.
So for example you have X[], Y[] and Z[] and you have an index (a,b,c) then you find X[a], Y[b], and Z[c] not Point(a,b,c)... "
I didn't quite understand this explanation, and would appreciate any help or further explanation in order to achieve my goal.
Thank you
Let's start with the cordinates (IfcCartesianPointList3D): each one is a triplet, resulting in a Point with (x,y,z) coordinates.
Then the IfcTriangulatedFaceSet uses indices to construct triangles. It has 2 indexing modes: direct and indirect via PnIndex. The indexing mode is determined by the existence of an array for PnIndex (attribute number 5). Take note that I call these variants direct and indirect - they aren't mentioned that way in the IFC documentation.
Direct indexing
PnIndex is not set. Lets look at an (simple and constructed) example:
#100=IFCCARTESIANPOINTLIST(((0,0,0),(1,0,0),(1,1,0),(0,1,0)));
#101=IFCTRIANGULATEDFACESET(
/*reference to the points*/ #100,
/*no normals*/ $,
/*no indication if closed or open*/ $,
/*coordinate indices*/ ((1,2,3),(1,3,4)),
/*no PnIndex*/ ());
This describes a square lying in the x-y-plane. Each entry in attribute CoordIndex is a triplet giving a one-based index into a point in the IfcCartesianPointList. This means there are two triangles constructed from the following points:
(0,0,0) (1,0,0) (1,1,0)
(0,0,0) (1,1,0) (0,1,0)
Indirect indexing
Lets build further on the previous example:
#100=IFCCARTESIANPOINTLIST(((0,0,0),(1,0,0),(1,1,0),(0,1,0)));
#101=IFCTRIANGULATEDFACESET(
/*reference to the points*/ #100,
/*no normals*/ $,
/*no indication if closed or open*/ $,
/*coordinate indices*/ ((1,2,3),(1,3,4)),
/*PnIndex*/ (2,3,4,1));
This time there is PnIndex set. It adds a level of indirection to access the points. Triplets from CoordIndex point into PnIndex (1-based). The value found in PnIndex is then used to access the IfcCartesianPointList.
So for the first triangle we have: (1,2,3) in CoordIndex. These point to 2, 3 and 4 in PnIndex. These result in the following points from the point list: (1,0,0) (1,1,0) (0,1,0)
Repeating the procudure for the second triangle (1,3,4) we get values 2, 4, 1 from PnIndex and the following points: (1,0,0) (0,1,0) (0,0,0)
It is again a square, but this time with a different triangulation.
Now if you want to know your wall thickness you will need to calculate the extents from the resulting geometry. If your wall is aligned with the coordinate system axes this is easy (get the difference between the smallest and largest X, Y and Z). If it is not, you might need to transform the points or look further into 3D-extent calculations (my knowledge ends there).
In a triangulation it's roughly num of triangles = 2 * num of vertices.
A wall (e.g. a rectangle) may be described by two triangles that share an edge and the two vertices of this edge.
Instead of describing the whole model triangle by triangle, each with its three vertices, or edge by edge, it's cheaper, avoids repeating vertex data, to set an index for each vertex and set a triangle by the three indices of its vertices. This is usually called "indexed rendering".
I'm working on a problem where I have a large set (>4 million) of data points located in a three-dimensional space, each with a scalar function value. This is represented by four arrays: XD, YD, ZD, and FD. The tuple (XD[i], YD[i], ZD[i]) refers to the location of data point i, which has a value of FD[i].
I'd like to superimpose a rectilinear grid of, say, 100x100x100 points in the same space as my data. This grid is set up as follows.
[XGrid, YGrid, ZGrid] = np.mgrid[Xmin:Xmax:Xstep, Ymin:Ymax:Ystep, Zmin:Zmax:Zstep]
XG = XGrid[:,0,0]
YG = YGrid[0,:,0]
ZG = ZGrid[0,0,:]
XGrid is a 3D array of the x-value at each point in the grid. XG is a 1D array of the x-values going from Xmin to Xmax, separated by a distance of XStep.
I'd like to use an interpolation algorithm I have to find the value of the function at each grid point based on the data surrounding it. In this algorithm I require 20 data points closest (or at least close) to my grid point of interest. That is, for grid point (XG[i], YG[j], ZG[k]) I want to find the 20 closest data points.
The only way I can think of is to have one for loop that goes through each data point and a subsequent embedded for loop going through all (so many!) data points, calculating the Euclidean distance, and picking out the 20 closest ones.
for i in range(0,XG.shape):
for j in range(0,YG.shape):
for k in range(0,ZG.shape):
Distance = np.zeros([XD.shape])
for a in range(0,XD.shape):
Distance[a] = (XD[a] - XG[i])**2 + (YD[a] - YG[j])**2 + (ZD[a] - ZG[k])**2
B = np.zeros([20], int)
for a in range(0,20):
indx = np.argmin(Distance)
B[a] = indx
Distance[indx] = float(inf)
This would give me an array, B, of the indices of the data points closest to the grid point. I feel like this would take too long to go through each data point at each grid point.
I'm looking for any suggestions, such as how I might be able to organize the data points before calculating distances, which could cut down on computation time.
Have a look at a seemingly simmilar but 2D problem and see if you cannot improve with ideas from there.
From the top of my head, I'm thinking that you can sort the points according to their coordinates (three separate arrays). When you need the closest points to the [X, Y, Z] grid point you'll quickly locate points in those three arrays and start from there.
Also, you don't really need the euclidian distance, since you are only interested in relative distance, which can also be described as:
abs(deltaX) + abs(deltaY) + abs(deltaZ)
And save on the expensive power and square roots...
No need to iterate over your data points for each grid location: Your grid locations are inherently ordered, so just iterate over your data points once, and assign each data point to the eight grid locations that surround it. When you're done, some grid locations may have too few data points. Check the data points of adjacent grid locations. If you have plenty of data points to go around (it depends on how your data is distributed), you can already select the 20 closest neighbors during the initial pass.
Addendum: You may want to reconsider other parts of your algorithm as well. Your algorithm is a kind of piecewise-linear interpolation, and there are plenty of relatively simple improvements. Instead of dividing your space into evenly spaced cubes, consider allocating a number of center points and dynamically repositioning them until the average distance of data points from the nearest center point is minimized, like this:
Allocate each data point to its closest center point.
Reposition each center point to the coordinates that would minimize the average distance from "its" points (to the "centroid" of the data subset).
Some data points now have a different closest center point. Repeat steps 1. and 2. until you converge (or near enough).
This is what I am currently doing:
Creating 4 axis that are perpendicular to 4 edges of 2 rectangles. Since they are rectangles I do not need to generate an axis (normal) per edge.
I then loop over my 4 axes.
So for each axis:
I get the projection of every corner of a rectangle on to the axis.
There are 2 lists (arrays) containing those projections. One for each rectangle.
I then get the dot product of each projection and the axis. This returns a scalar value
that can be used to to determine the min and max.
Now the 2 lists contain scalars and not vectors. I sort the lists so I can easily select the min and max values. If the min of box B >= the max of box A OR the max of box B <= the min of box A then there is no collision on that axis and no collision between the objects.
At this point the function finishes and the loop breaks.
If those conditions are never met for all the axis then we have a collision
I hope this was the correct way of doing it.
The python code itself can be found here http://pastebin.com/vNFP3mAb
Also:
http://www.gamedev.net/page/reference/index.html/_/reference/programming/game-programming/collision-detection/2d-rotated-rectangle-collision-r2604
The problem i was having is that the code above does not work. It always detects a a collision even where there is not a collision. What i typed out is exactly what the code is doing. If I am missing any steps or just not understanding how SAT works please let me know.
In general it is necessary to carry out the steps outlined in the Question to determine if the rectangles "collide" (intersect), noting as the OP does that we can break (with a conclusion of non-intersection) as soon as a separating axis is found.
There are a couple of simple ways to "optimize" in the sense of providing chances for earlier exits. The practical value of these depends on the distribution of rectangles being checked, but both are easily incorporated in the existing framework.
(1) Bounding Circle Check
One quick way to prove non-intersection is by showing the bounding circles of the two rectangles do not intersect. The bounding circle of a rectangle shares its center, the midpoint of either diagonal, and has diameter equal to the length of either diagonal. If the distance between the two centers exceeds the sum of the two circles' radii, then the circles do not intersect. Thus the rectangles also cannot intersect. If the purpose was to find an axis of separation, we haven't accomplished that yet. However if we only want to know if the rectangles "collide", this allows an early exit.
(2) Vertex of one rectangle inside the other
The projection of a vertex of one rectangle on axes parallel to the other rectangle's edges provides enough information to detect when that vertex is inside the other rectangle. This check is especially easy when the latter rectangle has been translated and unrotated to the origin (with edges parallel to the ordinary axes). If it happens that a vertex of one rectangle is inside the other, the rectangles obviously intersect. Of course this is a sufficient condition for intersection, not a necessary one. But it allows for an early exit with a conclusion of intersection (and of course without finding an axis of separation because none will exist).
I see two things wrong. First, the projection should simply be the dot product of a vertex with the axis. What you're doing is way too complicated. Second, the way you get your axis is incorrect. You write:
Axis1 = [ -(A_TR[0] - A_TL[0]),
A_TR[1] - A_TL[1] ]
Where it should read:
Axis1 = [ -(A_TR[1] - A_TL[1]),
A_TR[0] - A_TL[0] ]
The difference is coordinates does give you a vector, but to get the perpendicular you need to exchange the x and y values and negate one of them.
Hope that helps.
EDIT Found another bug
In this code:
if not ( B_Scalars[0] <= A_Scalars[3] or B_Scalars[3] >= A_Scalars[0] ):
#no overlap so no collision
return 0
That should read:
if not ( B_Scalars[3] <= A_Scalars[0] or A_Scalars[3] <= B_Scalars[0] ):
Sort gives you a list increasing in value. So [1,2,3,4] and [10,11,12,13] do not overlap because the minimum of the later is greater than the maximum of the former. The second comparison is for when the input sets are swapped.