Plane fitting through points in 3D using python - python-3.x

I have points in 3D space.
X Y Z
0 0.61853 0.52390 0.26304
1 0.61843 0.52415 0.26297
2 0.62292 0.52552 0.26108
3 0.62681 0.51726 0.25622
4 0.62772 0.51610 0.25903
I have defined a plane through the points which should vertically divide these points, but it is not dividing them vertically or horizontally. The plane and the points are way apart while I'm plotting them.
def plane_equation(x1, y1, z1, x2, y2, z2, x3, y3, z3):
a1 = x2 - x1
b1 = y2 - y1
c1 = z2 - z1
a2 = x3 - x1
b2 = y3 - y1
c2 = z3 - z1
a = b1 * c2 - b2 * c1
b = a2 * c1 - a1 * c2
c = a1 * b2 - b1 * a2
d = (- a * x1 - b * y1 - c * z1)
return a, b, c, d
# Finding the equation of the plane
a, b, c, d = plane_equation(x0, y0, z0, x1, y1, z1, x2, y2, z2)
print("equation of plane is ", a, "x +", b, "y +", c, "z +", d, "= 0.")
x = np.arange(0, 1, 0.1)
y = np.arange(0, 1, 0.1)
X,Y = np.meshgrid(x,y)
Z = a*X + b*Y + d
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.scatter(df.x, df.y, df.z, color = 'c', marker = 'o', alpha = 0.5)
surf = ax.plot_surface(X, Y, Z)
equation of plane is -0.0002496952000000007 x + 0.00036812320000000016 y + 0.0007697304000000002 z + -0.00024088567529317268 = 0.
I need plane to pass through these points and should be in vertical direction. The blue plane should pass through cyan points and plane should be in vertical direction.

There are several things that might go wrong here:
too small values
Your normal is not normalized and its coordinates are very small magnitude (0.000???) so its possible that your plot is handling all the values as zero (as the plot on your image is of the plane Z=0 which has nothing to do with values you provided).
From your feedback in chat This assumption of mine was right so to solve this problem just normalize your normal:
n(nx,ny,nz) /= sqrt(nx*nx + ny*ny +nz*nz)
And compute the d with this new values:
d = nx*x0 + ny*y0 + nz*z0
where (x0,y0,z0) is any from the selected points.
Wrongly selected points
The 3 selected points should be not too close to each other and not on a single line. If they do the computed normal is invalid. Also if you select points containing big noise the accuracy is lowered by it...
To improve this select 3 points randomly compute normal. Compute n such normals and average them together. The higher n the better accuracy.
Fit
To improve accuracy even more you can try to fit the normal and d. simply by using normal from #1 or #2 and fit its coordinates and d in near range to minimize the avg or max distance of all points to plane. However this is O(n.log^4(m)) where n is the number of used points and m relate to the fitted range of each parameter, but provides the best accuracy you can get.
You can use binary search or Approximation search or what ever optimizer your environment have at disposal

Related

How to use VBA to incorporate functions to avoid occupation of extra cells?

This spreadsheet aims to calculate distances between atoms, and we want to improve the functions so as to avoid the occupation of extra columns. (See image postscripted. Atom coordinates are given in the Column A to D, and the atom pair whose distance should be calculated is given in Column F to G.)
Currently in the first step, coordinates of specified atoms are picked up in columns I to O. e.g. Cell I4 is filled with the function:
=VLOOKUP($F4,$A$4:$E$1023,2,FALSE)
and then in the next step, the distance could be resolved in Column Q with Euclidean distance formula on the coordinates picked up. e.g. Cell Q4 is:
=SQRT(POWER((I4-M4),2)+POWER((J4-N4),2)+POWER((K4-O4),2))
According to the distance calculating algorithm, once the two atoms are specified, the distance is then determined. Thus, is it possible to write a function with VBA to gracefully incorporate these functions and take away these pilot processes from columns I to O? (Because these columns will be used otherwise in the future; and the code readability would be terrible if we put, for example, the six VLOOKUP functions directly into the final SQRT function.)
I'm new to VBA. Any help would be appreciated. Thanks!
The original data in this spreadsheet is as below: (From the third line)
Atom_No X_coordinate Y_coordinate Z_coordinate Atom_No1 Atom_No2 X1 Y1 Z1 X2 Y2 Z2 Distance
1 2.35739851 13.17160225 4.022993565 4 2 3.827347994 9.501971245 8.374602318 4.403610706 11.14351559 6.991936684 2.222276039
2 4.403610706 11.14351559 6.991936684 3 2 0.721047342 12.58075523 2.64032793 4.403610706 11.14351559 6.991936684 5.879067059
3 0.721047342 12.58075523 2.64032793 1 4 2.35739851 13.17160225 4.022993565 3.827347994 9.501971245 8.374602318 5.879068118
4 3.827347994 9.501971245 8.374602318 2 1 4.403610706 11.14351559 6.991936684 2.35739851 13.17160225 4.022993565 4.13699687
… … … … 3 1 0.721047342 12.58075523 2.64032793 2.35739851 13.17160225 4.022993565 2.22227577
Finally, these two modules will work and get the correct result when CoordinateTableRange and Atom_No1-2 pair is given like this table. You could load these two modules, and write in column R (e.g. R4 cell) with
=AtomsDistance(F4,G4)
, then you'll find the distance is the same as you got in col Q.
Function CoordinateVLookUp(Atom_No As Integer, CoordinateTableRange As Range, Column As Integer, isFuzzy As Boolean) As Double
'To find the atom coordinates according to columns, with CoordinateTableRange selected and isFuzzy specified.
Dim myResult As Variant
myResult = Application.WorksheetFunction.VLookup(Atom_No, CoordinateTableRange, Column, isFuzzy)
If IsError(myResult) Then
MsgBox ("No result found.")
Else
CoordinateVLookUp = myResult
End If
End Function
Function AtomsDistance(Atom_No1 As Integer, Atom_No2 As Integer) As Double
'To call CoordinateVLookUp function above to acquire the x, y, z coordinates of both atoms, and then calculate the distance through Euclidean distance formula.
Dim x1 As Double
Dim y1 As Double
Dim z1 As Double
Dim x2 As Double
Dim y2 As Double
Dim z2 As Double
Dim CoordinateTableRange As Range
Set CoordinateTableRange = Range("A4:E1023") 'set should be added
x1 = CoordinateVLookUp(Atom_No1, CoordinateTableRange, 2, False)
y1 = CoordinateVLookUp(Atom_No1, CoordinateTableRange, 3, False)
z1 = CoordinateVLookUp(Atom_No1, CoordinateTableRange, 4, False)
x2 = CoordinateVLookUp(Atom_No2, CoordinateTableRange, 2, False)
y2 = CoordinateVLookUp(Atom_No2, CoordinateTableRange, 3, False)
z2 = CoordinateVLookUp(Atom_No2, CoordinateTableRange, 4, False)
AtomsDistance = Math.Sqr((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2))
End Function

How to check if a point C(x3,y3) is falling between the straight line formed by two points A(x1,y1) & B(x2,y2)

I have two points A(x1,y1) & B(x2,y2) and I need to check if point c(x3,y3) falls on the straight line formed by point A & B.
A------C--------------------B then yes C is between A & B
A---------------------------B
C
in second case C isn't between A & B.
Using complex numbers, we define a similarity transformation that maps A to 0 and B to 1:
W = (Z - Za) / (Zb - Za)
Then Wc = (Zc - Za) / (Zb - Za) is a complex number that should have a zero or tiny imaginary value, and a real value between 0 and 1.
Make two vectors
cax = x1 - x3
cay = y1 - y3
cbx = x2 - x3
cby = y2 - y3
and check that angle between them is Pi:
Calculate dot and cross product of these vectors
dot = cax * cbx + cay * cby
cross = cax * cby - cay * cbx
Point C lies on segment AB if cross is zero and dot is negative
dot < 0
abs(cross) < eps
where eps is small value like 1e-6 to compensate floating point calculation errors.
if you are sure that all three point are aligned, you can use the dot product between vectors AC and AB, and the dot product between vectors BA and BC.
Reminder: Vector AB = (xB - xA, yB - yA)
Reminder: dot(AB, AC) = xABxAC + yAByAC
if dot(AB, AC) > 0, then it means C is in direction of B from A
if dot(BA, BC) > 0, then it means C is in direction of A from B
if both conditions above are satisfied, it means C is between A and B

Exporting Data in X,Y,Z format

I have a number of Excel files containing coordinate data that is not normalized.
A single row describes multiple points.
Invariably, one column gives the X value. As an illustration some files have say 3 columns representing Z values, and one column representing Y.
X Y1 Y2 Y3 Z
Other files have Multiple Y and Z columns in which point sets may share Y or Z columns.
There may be as many as 40 columns in a table and there are usually about 300 rows.
I need to export the data in CSV format with X, Y, Z values. Is there some relatively simple way to generate a sheet containing X, Y, Z coordinates from such data so that it can be exported?
So that
X Y1 Y2 Y3 Z
becomes
X, Y1, Z ; multiple rows
x, Y2, Z ; multiple rows
X, Y3, Z ; multiple rows
Such that the x and z values are repeated one for each set of Y values.
There is one other format that I have to deal convert. One column contains the X, values, one Row contains the y values, and the z values are in the row and column with the X and Y value.
Complicating all this is that there may be omitted or missing values in some of the cells.
This appears to be the same problem in python
Convert datafiles 'X' 'Y' 'Z' 'data' format
In response to ANDY:
A sheet may be laid out as columns:
X Y1 Y2 Z1 Y3 Y4 Y5 Z2
The coordinate sets are:
X, Y1, Z1
X, Y2, Z1
X, Y3, Z2
X, Y4, Z2

Square divided by a ray. What is the area of a part?

I have a square, for simplicity assume bottom left corner is on origin and width of the square is 1.
A ray divides the square into two parts. I have the coordinates of intersection points. I want to obtain the area that lies right of the vector from p1 to p2:
Right now I have 16 if statements checking every combination of 2 points and calculating the area accordingly. It looks awful. Is there a more clever way of doing this?
Call the points A and B instead of p1 and p2. I'll assume x increases to the right and y increases upward, as per convention.
The point A must have a coordinate (x or y) that is 0 or 1. Rotate the square (really just the two points) to make it x=0.
The point B might be at x=-1, in which case the area is 1-(Ay+By)/2.
Or B might be at y=0, area = 1+(AyBx)/2
Or B might be at y=1, area = (Ay-1)Bx/2
This solution assumes that p1 and p2 form a right-triangle as depicted in the shaded area:
Area to the right of the vector = (w * w) - (0.5 * p1 * p2)
where w is the width of the square, and 0 <= p1 <= w, and 0 <= p2 <= w.
For example if w = 1, p1 = 0.5, and p2 = 0.75
then Area = (1 * 1) - (0.5 * 0.5 * 0.75) = 0.8125

Intersecting Point for Circle Morphs

I am trying to find intersecting Points for Circle with help of this link.
The following note describes how to find the intersection point(s) between two circles on a plane, the following notation is used. The aim is to find the two points P3 = (x3, y3) if they exist.
First calculate the distance d between the center of the circles. d = ||P1 - P0||.
If d > r0 + r1 then there are no solutions, the circles are separate.
If d < |r0 - r1| then there are no solutions because one circle is contained within the other.
If d = 0 and r0 = r1 then the circles are coincident and there are an infinite number of solutions.
Considering the two triangles P0P2P3 and P1P2P3 we can write
a2 + h2 = r02 and b2 + h2 = r12
Using d = a + b we can solve for a,
a = (r02 - r12 + d2 ) / (2 d)
It can be readily shown that this reduces to r0 when the two circles touch at one point, ie: d = r0 + r1
Solve for h by substituting a into the first equation, h2 = r02 - a2
So
P2 = P0 + a ( P1 - P0 ) / d
And finally, P3 = (x3,y3) in terms of P0 = (x0,y0), P1 = (x1,y1) and P2 = (x2,y2), is
x3 = x2 +- h ( y1 - y0 ) / d
y3 = y2 -+ h ( x1 - x0 ) / d
http://paulbourke.net/geometry/2circle/
b:=CircleMorph new.
b center: 60#60.
b openInWorld.
b1:=CircleMorph new.
b center: 100#100.
b1 openInWorld.
d:= b1 center - b center. // distance between 2 circles
r1:= (((b center x abs)squared +(b center y abs)squared)sqrt).
r2:= (((b1 center x abs)squared +(b1 center y abs)squared)sqrt).
r3:= r1+ r2.
(d) > (r3) ifTrue:[Transcript show:'Circles are seprate';cr]
When i compare distance with sum of radius of 2 circles is get distance less than radius of both circles which i know is not true,when circles are seprate Am i calculating radius correctly or not idk there is some problem with this help.
One possible solution is this:
| b b1 d r1 r2 r3 |
b := CircleMorph new.
b center: 60#60.
b bounds: (Rectangle origin: 100#40 corner: 40#100).
b openInWorld.
b1 := CircleMorph new.
b center: 100#100.
b bounds: (Rectangle origin: 100#40 corner: 40#100).
b1 openInWorld.
r1 := b bounds width / 2.
r2 := b1 bounds width / 2.
r3 := r1+ r2.
(d < r3)
ifTrue: [| a h mid |
a := (r1 squared - r2 squared + d squared) / (2 * d).
h := (r1 squared - a squared) sqrt.
mid := (b1 center x - b center x) # (b1 center y - b center y).
{(mid x + (h * (b1 center y - b center y))) # (mid y - (h * (b1 center x - b center x))).
(mid x - (h * (b1 center y - b center y))) # (mid y + (h * (b1 center x - b center x)))}]
ifFalse: ['separate']
You'll want to check my arithmetic. (Update: I hadn't used the point between the two circle centres to calculate the points.)

Resources