Given only the coordinates of the vertices of an acute triangle, how can I efficiently and quickly find the coordinates of the point at which the altitude from a particular vertex meets the opposite base?
A solution using only math, numpy, or scipy would be incredibly helpful.
Needed point is orthogonal projection of vertex point (say vertex C) onto the line containing opposite side (say AB).
To find projection point, get vectors for AB and AC
AB = (B - A) //in coordinates ab.x = b.x-a.x, ab.y = b.y-a.y
AC = (C - A)
and find parameter using scalar product of AB and AC
t =(AB * AC) / (AB * AB)
t =((b.x-a.x)*(c.x-a.x) + (b.y-a.y)*(c.y-a.y)) / ((b.x-a.x)*(b.x-a.x) + (b.y-a.y)*(b.y-a.y))
Projection point coordinates
P = A + AB * t
p.x = a.x + (b.x-a.x) * t
p.y = a.y + (b.y-a.y) * t
That's all
def orthoProjection(ax, ay, bx, by, cx, cy):
abx = bx - ax
aby = by - ay
acx = cx - ax
acy = cy - ay
t = (abx * acx + aby * acy) / (abx * abx + aby * aby)
px = ax + t * abx
py = ay + t * aby
return px, py
print(orthoProjection(0, 0, 4, 4, -1, 5))
>>(2.0, 2.0)
Consider the triangle with vertices at points A, B and C, and you wish to find where the altitude extending from vertex C intersects the line AB.
So first, you can determine the equation for line AB. You have points A and B (Ax, Ay; and Bx, By). Given that you can calculate the slope_AB as (By-Ay)/(Bx-Ax).
Now the format of a line is Y = MX+B where M is the slope just calculated, and B is the Y intercept, so: Y_intercept_AB = Ay - slope_AB * Ax. So the equation for AB is Y = slope_AB*X + Y_intercept_AB.
OK, so now, the slope of the altitude from C to where it intersects line AB (let's call that point D, and the altitude line CD) is the negative reciprocal of the slope of AB; so slope_CD = -(1/slope_AB).
So now, given that you have one point (C) on line CD and its slope, you can get the equation for CD the same way as you did for AB. First, find its Y-intercept: Y_intercept_CD = Cy - slope_CD * Cx
So the equation for CD is Y = slope_CD * X + Y_intercept_CD.
So now you have equations for line AB and line CD:
Y = slope_AB * X + Y_intercept_AB
Y = slope_CD * X + Y_intercept_CD
And your problem is simplified to finding where those lines intersect, which is point D.
From the above equations, since both right-hand sides are equal to Y we can set them equal to each other:
slope_AB * X + Y_intercept_AB = slope_CD * X + Y_intercept_CD
and now it's just a matter of solving for X.
slope_AB * X - slope_CD*X = Y_intercept_CD - Y_intercept_AB
(slope_AB - slope_CD)*X = Y_intercept_CD - Y_intercept_AB
X = (Y_intercept_CD - Y_intercept_AB)/(slope_AB - slope_CD)
That will give you the X-value for D (Dx). For the Y-value, use either line equation. Let's use the one for AB:
Dy = slope_AB * Dx + Y_intercept_AB
Putting it all together, assume a triangle of A=(-4, 2), B=(0, 6), C=(6, -4):
#Points A, B,C:
Ax = -4; Ay = 2
Bx = 0; By = 6
Cx = 6; Cy = -4
#Line AB:
slope_AB = (By - Ay)/(Bx - Ax)
Y_intercept_AB = Ay - slope_AB*Ax
print("AB: slope: %s, intercept: %s" % (slope_AB, Y_intercept_AB))
#Line CD:
slope_CD = -(1/slope_AB)
Y_intercept_CD = Cy - slope_CD*Cx
print("CD: slope: %s, intercept: %s" % (slope_CD, Y_intercept_CD))
#Find the intersection of the two lines AB & CD:
Dx = (Y_intercept_CD - Y_intercept_AB)/(slope_AB - slope_CD)
Dy = slope_AB*Dx + Y_intercept_AB
print("Intersection at (%s, %s)" % (Dx, Dy))
Prints:
AB: slope: 1.0, intercept: 6.0
CD: slope: -1.0, intercept: 2.0
Intersection at (-2.0, 4.0)
One more thing: this will divide-by-zero and fail where points A & B have the same X-value (because it divides by Ax-Bx, which would be zero); but it's a start.
Related
I am working on a python script and I am sure there is an easier way to approach this problem then my solution so far.
Give two coordinates, say (0,0) and (40,40) and a set distance to travel, say 5, how would I find a new coordinate pair for the point that is 5 units from (0,0) heading along the line that connects (0,0) and (40,40)?
I am doing the following given the distance_to_travel and points p0, p1:
ang = math.atan2(p1.y - p0.y, p1.x - p0.x)
xx = node0.x + (distance_to_travel * math.cos(ang))
yy = node0.y + (distance_to_travel * math.sin(ang))
Following the method outlined in my comment:
# find the vector a-b
ab.x, ab.y = p1.x - p0.x, p1.y - p0.y
# find the length of this vector
len_ab = (ab.x * ab.x + ab.y * ab.y) ** 0.5
# scale to length 5
ab5.x, ab5.y = ab.x *5 / len_ab, ab.y *5 / len_ab
# add to a (== p0)
fin.x, fin.y = p0.x + ab5.x, p0.y + ab5.y
You should find the slope of the line between a and b |ab|.
Then you can use 2D spherical (polar) coordinate system to get r mount of distance with a given slope (The slope you found earlier).
Now you can convert from spherical to Cartesian to find x, y coordinates of new point. Then you add this values to values of point a:
import math
def slope(p1, p2):
return math.atan2(
(p2[1] - p1[1]), (p2[0] - p1[0])
)
def spherical_to_cartesian(r, theta):
x = r * math.cos(theta)
y = r * math.sin(theta)
return x, y
def add_points(p1, p2):
return p1[0] + p2[0], p1[1] + p2[1]
if __name__ == '__main__':
a = [0, 0]
b = [40, 40]
slp = slope(a, b)
r = 5
new_p = spherical_to_cartesian(r, slp)
res = add_points(a, new_p)
print(res)
I'm trying to replicate a function used in a study, but don't really have the mathematical background to fully appreciate how this ought to be done. The measure takes three points from a tongue contour and uses these three points to calculate the radius of a circle that would pass through them. I have looked here and found something that does this in python. I've tried to modify the code so it would work in R with my own data. (Posted at the bottom)
The problem is, based on the study I am reading, I then need to calculate the concavity of the circumference of the circle and find the inverse of the radius of the circle passing through the three points. I'm googling and googling but honestly this means nothing to me. The only thing I have found is that I seem to need to calculate the first and second derivatives of the tongue surface curve. I'm really hoping somebody might be able to help explore how I would do this in R. To be brutally honest, I am not overly interested in understanding the mathematics here, just how to actually implement it.
Edit: I thought below was the formula that I need to replicate. As MBo points out, this isn't the case.
I'll repeat something from another study that used a very, very similar method in case that helps.
'Any three points (A, B, C) can be conceived as lying on the circumference of a circle. The circle will have a radius, the inverse of which represents the curvature of the circle passing through those three points.' The set of three points 'yields a curvature numeber which is the inverse of the radius of the circle passing through them. Three points which lie along a straight line have a curvature of zero, since their concavity is zero and this becomes the numerator of of the curvature equation'. It's this that I need to do, but don't know where to begin operationalising it in R.
The code below is the python code I'm attempting to replicate for my purposes in R to obtain the radius from three points. I have no idea how to proceed after that.
def define_circle(p1, p2, p3):
"""
Returns the center and radius of the circle passing the given 3 points.
In case the 3 points form a line, returns (None, infinity).
"""
temp = p2[0] * p2[0] + p2[1] * p2[1]
bc = (p1[0] * p1[0] + p1[1] * p1[1] - temp) / 2
cd = (temp - p3[0] * p3[0] - p3[1] * p3[1]) / 2
det = (p1[0] - p2[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p2[1])
if abs(det) < 1.0e-6:
return (None, np.inf)
# Center of circle
cx = (bc*(p2[1] - p3[1]) - cd*(p1[1] - p2[1])) / det
cy = ((p1[0] - p2[0]) * cd - (p2[0] - p3[0]) * bc) / det
radius = np.sqrt((cx - p1[0])**2 + (cy - p1[1])**2)
return ((cx, cy), radius)
Here's my R attempt.
I haven't written the function yet, but I will be looking at three points along a curve, A, B and C. The function will extract x and y values for each of these three points (called x_value_a, y_value_a etc.). Once this is done. I will run the code that follows. It's after this that I am properly stumped.
temp = x_value_b ^ 2 + y_value_b ^ 2
bc = (x_value_a ^ 2 + y_value_a ^ 2 - temp) / 2
cd = (temp - x_value_c ^ 2 - y_value_c ^ 2) / 2
det = (x_value_a - x_value_b) * (y_value_b - y_value_c) - (x_value_b - x_value_c) * (y_value_a - y_value_b)
cx = (bc * (y_value_b - y_value_c) - cd * (y_value_a - y_value_b)) / det
cy = ((x_value_a - x_value_b) * cd - (x_value_b - x_value_c) * bc) / det
radius = sqrt((cx - x_value_a)^2 + (cy - y_value_a)^2)
Any help would be greatly appreciated. I'm sorry for my mathematical ignorance.
If you only want the Python script translated into R, that's pretty straightforward (I don't quite understand why you split it up in the R code you added).
define_circle = function(p1, p2, p3) {
# Returns the center and radius of the circle passing the given 3 points.
# In case the 3 points form a line, returns warning.
temp = p2[1] * p2[1] + p2[2] * p2[2]
bc = (p1[1] * p1[1] + p1[2] * p1[2] - temp) / 2
cd = (temp - p3[1] * p3[1] - p3[2] * p3[2]) / 2
det = (p1[1] - p2[1]) * (p2[2] - p3[2]) - (p2[1] - p3[1]) * (p1[2] - p2[2])
if (abs(det) < 1.0e-6) {
return(c("Three points form a line"))
} else {
# Center of circle
cx = (bc*(p2[2] - p3[2]) - cd*(p1[2] - p2[2])) / det
cy = ((p1[1] - p2[1]) * cd - (p2[1] - p3[1]) * bc) / det
radius = sqrt((cx - p1[1])**2 + (cy - p1[2])**2)
return(list("center" = c(cx, cy), "radius" = radius))
}
}
Note that p1-3 represents a vector containing an x- and y-coordinate. I have to trust the original Python code here but a quick check using desmos.com seems to indicate it works:
> define_circle(c(0,1), c(2,2), c(0.5,5))
$center
[1] 0.25 3.00
$radius
[1] 2.015564
Example circle plot
By leaving the function intact, you can calculate the inverse radius for any set of points you want. I agree that the inverse radius simply means 1/radius.
Here's a geometric approach. Suppose I have three random points in a data frame:
set.seed(1)
df <- setNames(as.data.frame(matrix(rnorm(6), nrow = 3)), c("x", "y"))
df
#> x y
#> 1 -0.6264538 1.5952808
#> 2 0.1836433 0.3295078
#> 3 -0.8356286 -0.8204684
plot(df$x, df$y, xlim = c(-3, 2), ylim = c(-2, 2))
Now, I can draw lines between these points and find the mid-point arithmetically:
lines(df$x, df$y)
mid_df <- data.frame(x = diff(df$x)/2 + df$x[-3],
y = diff(df$y)/2 + df$y[-3],
slope = -diff(df$x)/diff(df$y))
mid_df$intercept <- mid_df$y - mid_df$slope * mid_df$x
points(mid_df$x, mid_df$y)
If I draw lines perpendicular to these lines through the midpoint, then the resulting point should be equidistant from my three starting points:
abline(a = mid_df$intercept[1], b = mid_df$slope[1], col = "red", lty = 2)
abline(a = mid_df$intercept[2], b = mid_df$slope[2], col = "red", lty = 2)
center_x <- (mid_df$intercept[2] - mid_df$intercept[1]) /
(mid_df$slope[1] - mid_df$slope[2])
center_y <- mid_df$slope[1] * center_x + mid_df$intercept[1]
points(center_x, center_y)
As is indeed the case:
distances <- sqrt((center_x - df$x)^2 + (center_y - df$y)^2)
distances
#> [1] 1.136489 1.136489 1.136489
So, the radius of the circle is given by distances[1], and its center is at center_x, center_y. The curvature that is your end result is given by 1/distances[1]
To prove this, let's draw the circle this describes:
xvals <- seq(center_x - distances[1], center_x + distances[1], length.out = 100)
yvals <- center_y + sqrt(distances[1]^2 - (xvals - center_x)^2)
yvals <- c(yvals, center_y - sqrt(distances[1]^2 - (xvals - center_x)^2))
xvals <- c(xvals, rev(xvals))
lines(xvals, yvals)
My favorite resolution:
subtract the coordinates of one point from the two others;
now your circle is through the origin and has the simplified equation
2 Xc X + 2 Yc Y = X² + Y²
you have a standard and easy system of two equations in two unknowns.
X1 Xc + Y1 Yc = (X1² + Y1²) / 2 = Z1
X2 Xc + Y2 Yc = (X2² + Y2²) / 2 = Z2
when you have computed Xc and Yc, the radius is √Xc²+Yc².
Using complex numbers:
We map the points Z1, Z2 to -1 and 1 by the transformation Z = (2Z - Z1 - Z2) / (Z2 - Z1). Now the center of the circle is on the imaginary axis, let iH. We express that the center is equidistant to 1 and to the third point (2 Z3 - Z0 - Z1) / (Z1 - Z0) = X + iY,
H² + 1 = X² + (Y - H)²
or
H = (X² + Y² - 1) / 2Y
and
R = √H²+1.
I am trying to find the intersect between a straight line and a quadratic curve, however the result I am getting appears to be imaginary although I don't see how this can be the case as I can see them intersect on real axes:
Import numpy
#quadratic coefficients
a,b,c = (-3.09363812e-04, 1.52138019e+03, -1.87044961e+09)
# y = ax^2 + bx + c
#line coefficients
m,d = (1.06446434e-03, -2.61660911e+03)
#y = mx + d
intersect = (-(b-m)+((b-m)**2 - 4*a*(c-d))**0.5)/(2*a)
print(intersect)
The output of this is 2458883.4674943495-107.95731226786134j
I am trying to find the intersect between the yellow curve over the blue points and the black dotted line
The graphed curves you presented vs. your equations are not the same, and your equations do not intersect.
I rewrote some example code for you. numpy isn't needed, and an exact solution is possible.
import math
import collections
def calculateIntersection(p, l):
b = p.B - l.slope
c = p.C - l.yInt
discriminant = b**2 - (4 * p.A * c)
if discriminant > 0.0:
# 2 points of intersection
x1 = (-b + math.sqrt(discriminant)) / (2.0 * p.A)
x2 = (-b - math.sqrt(discriminant)) / (2.0 * p.A)
return discriminant, [(x1, l.slope * x1 + l.yInt), (x2, l.slope * x2 + l.yInt)]
elif discriminant == 0.0:
# 1 point of intersection
x1 = -b / (2.0 * p.A)
return discriminant, [(x1, slope * x1 + l.yInt)]
else:
# no points of intersection
return discriminant, []
Line = collections.namedtuple('Line', 'slope yInt')
Poly = collections.namedtuple('Poly', 'A B C')
p = Poly(A=-3.09363812e-04, B=1.52138019e+03, C=-1.87044961e+09)
print(p)
l = Line(slope=1.06446434e-03, yInt=-2.61660911e+03)
print(l)
(discriminant, points) = calculateIntersection(p, l)
if (len(points) > 0):
print("Intersection: {}".format(points))
else:
print("No intersection: {}".format(discriminant))
Hey trying to learn how to code and I cant figure this exercise out.
Specifically getting the precise y axis intercept points.
The formula given works for getting the x axis points but I cant figure out how to get the y axis points.
Exercise :
Input : Radius of circle and the y - intercept of the line.
Output : Circle drawn with a horizontal line across the window with the given y intercept. Mark two points of the intersection.
Print the x values of the points of intersection *Formula : x = ± √r^2 - y^2
Code::
from graphics import *
from math import *
def main():
# enter radius and the y intercept of the line
radius = eval(input("Put in radius:: "))
yinter = eval(input("Put in y intersec:: "))
#Draw window + circle + line
win = GraphWin()
win.setCoords(-10.0, -10.0, 10.0, 10.0)
circle = Circle(Point(0.0,0.0), radius)
mcircle = Circle(Point(0.0,0.0), 0.5)
circle.draw(win)
mcircle.draw(win)
line = Line(Point(-10, 0), Point(10, yinter))
line.draw(win)
#Calculate x axis points of intersept
xroot1 = sqrt(radius * radius - yinter * yinter)
xroot2 = -abs(xroot1)
print("Xroot 1 : ", xroot1)
print("Xroot 2 : ", xroot2)
x = 0
yroot1 = sqrt(radius * radius - x * x)
yroot2 = -abs(yroot1)
print("Yroot 1 : ", yroot1)
print("Yroot 2 : ", yroot2)
#mark two points of intersept in red
sc1 = Circle(Point(xroot1, yroot1), 0.3)
sc1.setFill('red')
sc2 = Circle(Point(xroot2, yroot2), 0.3)
sc2.setFill('red')
sc1.draw(win)
sc2.draw(win)
main()
Answer - With Radius of 8 and Y intersect point of 2
Yroot1 = 7.75
Yroot2 = -7.75
Xroot1 = 8.0
Xroot2 = -8.0
I just came up with a subroutine to find intersection points while solving another Zelle-graphics related SO question. There may be ways to simplify the math but I'm going the long way around:
from graphics import *
def intersection(center, radius, p1, p2):
""" find the two points where a secant intersects a circle """
dx, dy = p2.x - p1.x, p2.y - p1.y
a = dx**2 + dy**2
b = 2 * (dx * (p1.x - center.x) + dy * (p1.y - center.y))
c = (p1.x - center.x)**2 + (p1.y - center.y)**2 - radius**2
discriminant = b**2 - 4 * a * c
assert (discriminant > 0), 'Not a secant!'
t1 = (-b + discriminant**0.5) / (2 * a)
t2 = (-b - discriminant**0.5) / (2 * a)
return Point(dx * t1 + p1.x, dy * t1 + p1.y), Point(dx * t2 + p1.x, dy * t2 + p1.y)
def main(win):
center = Point(0.0, 0.0)
# Enter radius
radius = float(input("Put in radius: "))
# Draw circle and center dot
Circle(center, radius).draw(win)
Circle(center, 0.3).draw(win)
# Enter the y intercept of the line
yinter = float(input("Put in y intercept: "))
# Draw line
p1, p2 = Point(-10.0, 0.0), Point(10.0, yinter)
Line(p1, p2).draw(win)
# Mark two points of intercept in red
for i, root in enumerate(intersection(center, radius, p1, p2), start=1):
print("Root {}:".format(i), root)
dot = Circle(root, 0.3)
dot.setFill('red')
dot.draw(win)
win = GraphWin()
win.setCoords(-10.0, -10.0, 10.0, 10.0)
main(win)
win.getMouse()
OUTPUT
NOTE
You can get values input that do not produce a secant, e.g. a radius of 2 and an intercept of 8. Your code doesn't account for this -- the above will simply throw an assertion error if it occurs. But you can upgrade that to an error you can catch and fix.
For the y coordinates you can use a similar formula:
y = ± sqrt(r^2 - x^2)
and the do everything the same with marking the roots.
You should write the code like this:
x = sqrt(r ** 2 - y ** 2)
line = Line(Point(-10, 0), Point(10, yinter))
line.draw(win)
Line(Point(-10,0) is wrong, it should read:
Line(Point(-10,yinter).
Also set Xroot1 and Xroot2 = 0 or -x=0, x=0
def intersection(center, radius, p1, p2):
dx, dy = p2[0] - p1[0], p2[1] - p1[1]
a = dx**2 + dy**2
b = 2 * (dx * (p1[0]- center[0]) + dy * (p1[1] - center[1]))
c = (p1[0] - center[0])**2 + (p1[1] - center[1])**2 - radius**2
K = b**2 - 4 * a * c
return True if K>0 else False
if __name__ == "__main__":
p1 = 10,50
p2 = 100,50
center= 50,150
radius = 600
print(intersection(center, radius, p1, p2))
Given 3 points in space (3D): A = (x1, y1, z1), B = (x2, y2, z2) C = (x3, y3, z3); then how to find the center and radius of the circle (arc) that passes through these three points, i.e. find circle equation? Using Python and Numpy here is my initial code
import numpy as np
A = np.array([x1, y1, z1])
B = np.array([x2, y2, z2])
C = np.array([x3, y3, z3])
#Find vectors connecting the three points and the length of each vector
AB = B - A
BC = C - B
AC = C - A
# Triangle Lengths
a = np.linalg.norm(AB)
b = np.linalg.norm(BC)
c = np.linalg.norm(AC)
From the Circumradius definition, the radius can be found using:
R = (a * b * c) / np.sqrt(2.0 * a**2 * b**2 +
2.0 * b**2 * c**2 +
2.0 * c**2 * a**2 -
a**4 - b**4 - c**4)
However, I am having problems finding the Cartesian coordinates of the center. One possible solution is to use the "Barycentric Coordinates" of the triangle points to find the trilinear coordinates of the circumcenter (Circumcenter).
First (using this source) we find the circumcenter barcyntric coordinates:
#barcyntric coordinates of center
b1 = a**2 * (b**2 + c**2 - a**2)
b2 = b**2 * (c**2 + a**2 - b**2)
b3 = c**2 * (a**2 + b**2 - c**2)
Then the Cartesian coordinates of the center (P) would be:
Px = (b1 * A[0]) + (b2 * B[0]) + (b3 * C[0])
Py = (b1 * A[1]) + (b2 * B[1]) + (b3 * C[1])
Pz = (b1 * A[2]) + (b2 * B[2]) + (b3 * C[2])
However, the barcyntric coordinates values above do not seem to be correct. When solved with an example of known values, the radius is correct, but the coordinates of the center are not.
Example: For these three points:
A = np.array([2.0, 1.5, 0.0])
B = np.array([6.0, 4.5, 0.0])
C = np.array([11.75, 6.25, 0.0])
The radius and center coordinates are:
R = 15.899002930062595
P = [13.4207317073, -9.56097560967, 0]
Any ideas on how to find the center coordinates?
There are two issues with your code.
The first is in the naming convention. For all the formulas you are using to hold, the side of length a has to be the one opposite the point A, and similarly for b and B and c and C. You can solve that by computing them as:
a = np.linalg.norm(C - B)
b = np.linalg.norm(C - A)
c = np.linalg.norm(B - A)
The second has to do with the note in your source for the barycentric coordinates of the circumcenter: not necessarily homogeneous. That is, they need not be normalized in any way, and the formula you are using to compute the Cartesian coordinates from the barycentric ones is only valid when they add up to one.
Fortunately, you only need to divide the resulting Cartesian coordinates by b1 + b2 + b3 to get the result you are after. Streamlining a little bit your code for efficiency, I get the results you expect:
>>> A = np.array([2.0, 1.5, 0.0])
>>> B = np.array([6.0, 4.5, 0.0])
>>> C = np.array([11.75, 6.25, 0.0])
>>> a = np.linalg.norm(C - B)
>>> b = np.linalg.norm(C - A)
>>> c = np.linalg.norm(B - A)
>>> s = (a + b + c) / 2
>>> R = a*b*c / 4 / np.sqrt(s * (s - a) * (s - b) * (s - c))
>>> b1 = a*a * (b*b + c*c - a*a)
>>> b2 = b*b * (a*a + c*c - b*b)
>>> b3 = c*c * (a*a + b*b - c*c)
>>> P = np.column_stack((A, B, C)).dot(np.hstack((b1, b2, b3)))
>>> P /= b1 + b2 + b3
>>> R
15.899002930062531
>>> P
array([ 13.42073171, -9.56097561, 0. ])
As an extension to the original problem: Now suppose we have an arc of known length (e.g. 1 unit) extending from point A as defined above (away from point B). How to find the 3D coordinates of the end point (N)? The new point N lies on the same circle passing through points A, B & C.
This can be solved by first finding the angle between the two vectors PA & PN:
# L = Arc Length
theta = L / R
Now all we need to do is rotate the vector PA (Radius) by this angle theta in the correct direction. In order to do that, we need the 3D rotation matrix. For that we use the Euler–Rodrigues formula:
def rotation_matrix_3d(axis, theta):
axis = axis / np.linalg.norm(axis)
a = np.cos(theta / 2.0)
b, c, d = axis * np.sin(theta / 2.0)
rot = np.array([[a*a+b*b-c*c-d*d, 2*(b*c-a*d), 2*(b*d+a*c)],
[ 2*(b*c+a*d), a*a+c*c-b*b-d*d, 2*(c*d-a*b)],
[ 2*(b*d-a*c), 2*(c*d+a*b), a*a+d*d-b*b-c*c]])
return rot
Next, we need to find the rotation axis to rotate PA around. This can be achieved by finding the axis normal to the plane of the circle passing through A, B & C. The coordinates of point N can then be found from the center of the circle.
PA = P - A
PB = P - B
xx = np.cross(PB, PA)
r3d = rotation_matrix_3d(xx, theta)
PN = np.dot(r3d, PA)
N = P - PN
as an example, we want to find coordinates of point N that are 3 degrees away from point A, the coordinates would be
N = (1.43676498, 0.8871264, 0.)
Which is correct! (manually verified using CAD program)