I am making a geometry interface in python (currently using tkinter) but I have stumbled upon a major problem: I need a function that is able to return a point, that is at a certain angle with a certain line segment, is a certain length apart from the vertex of the angle. We know the coordinates of the points of the line segment, and also the angle at which we want the point to be. I have attached an image below for a more graphical view of my question.
The problem: I can calculate it using trigonometry, where
x, y = vertex.getCoords()
endx = x + length * cos(radians(angle))
endy = y + length * sin(radians(angle))
p = Point(endx, endy)
The angle I input is in degrees. That calculation is true only when the line segment is parallel to the abscissa. But the sizes of the angles I get back are very strange, to say the least. I want the function to work wherever the first two points are on the tkinter canvas, whatever the angle is. I am very lost as to what I should do to fix it. What I found out: I get as output a point that when connected to the vertex, makes a line that is at the desired angle to the abscissa. So it works when the first arm(leg, shoulder) of the angle is parallel to the abscissa, then the function runs flawlessly (because of cross angles) - the Z formation. As soon as I make it not parallel, it becomes weird. This is because we are taking the y of the vertex, not where the foot of the perpendicular lands(C1 on the attached image). I am pretty good at math, so feel free to post some more technical solutions, I will understand them
EDIT: I just wanted to make a quick recap of my question: how should I construct a point that is at a certain angle from a line segment. I have already made functions that create the angle in respect to the X and Y axes, but I have no idea how i can make it in respect to the line inputted. Some code for the two functions:
def inRespectToXAxis(vertex, angle, length):
x, y = vertex.getCoords()
newx = x + length * cos(radians(angle))
newy = y + length * sin(radians(angle))
p = Point(abs(newx), abs(newy))
return p
def inRespectToYAxis(vertex, length, angle):
x, y = vertex.getCoords()
theta_rad = pi / 2 - radians(angle)
newx = x + length * cos(radians(angle))
newy = y + length * sin(radians(angle))
p = Point(newx, newy)
return p
Seems you want to add line segment angle to get proper result. You can calculate it using segment ends coordinates (x1,y1) and (x2,y2)
lineAngle = math.atan2(y2 - y1, x2 - x1)
Result is in radians, so apply it as
endx = x1 + length * cos(radians(angle) + lineAngle) etc
Related
I face a seemingly simple problem that somehow I didn't manage to solve, despite looking into several trigonometry and geometry intros.
I have a 2D space in which x=0; y=0 is the centre. I would like, given some position x1, y1 (being the coordinates of one end of the segment), and a length and an angle (0 denoting vertical lines), to find the coordinates of the other end of the segment.
In other words, being able to move from one set of parameters (x1; y1; angle; length) to (x1; y1; x2; y2) and vice versa.
Thanks a lot,
For this you want to use sine and cosine. Here is some example code:
from math import cos, sin, radians
a = radians(45)
l = 10
x1, y1 = (10, 15)
x2 += sin(a) * l
y2 += cos(a) * l
Here is an article about how and why this works.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
The community reviewed whether to reopen this question 8 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I got a challenge in a learning group today to draw this shape in python turtle library.
I cannot figure out a way to express the geometrical solution to find the angles to turn and the size of the line I need.
Can you please tell me how to draw the first polygon alone? I already know how to make the pattern.
I am in fifth grade. So please give me a solution that I can understand.
Here is the solution I came up with. It is based on this diagram:
Math background
My solution uses "trigonometry", which is a method for calculating the length of one side of a triangle from the length of another side and the angles of the triangle. This is advanced math which I would expect to be taught maybe in 9th or 10th grade. I do not expect someone in 5th grade to know trigonometry. Also I cannot explain every detail of trigonometry, because I would have to write a lot and I do not think I have the teaching skills to make it clear. I would recommend you to look at for example this video to learn about the method:
https://www.youtube.com/watch?v=5tp74g4N8EY
You could also ask your teacher for more information, or research about it on the internet on your own.
Step 1: Calculating the angles
We can do this without trigonometry.
First, we see there is a "pentagon" (5-sided polygon) in the middle. I want to know the inner angle of a corner in this "pentagon". I call this angle X:
How can we calculate the angle X? We first remember that the sum of the inner angles in a triangle is 180°. We see that we can divide a 5-sides polygon into 5-2 triangles like this:
The sum of the inner angle of each of these 5-2 triangles is 180°. So for the whole 5-sided polygon, the sum of the inner angles is 180° * (5-2). Since all angles have the same size, each angle is 180°*(5-2) / 5 = 108°. So we have X = 108°.
The angle on the other side is the same as X. This allows us the calculate the angle between the two X. I will call this angle Y:
Since a full circle is 360°, we know that 360° = 2*X + 2*Y. Therefore, Y = (360° - 2*X) / 2. We know that X = 108°, so we get Y = 72°.
Next, we see there is a triangle containing the Y angle. I want to know the angle Z at the other corner of the triangle:
The inner angles of a triangle sum up to 180°*(3-2) = 180°. Therefore, we know that 180° = 2*Y + Z, so Z = 180° - 2*Y. We know that Y = 72°, so we get Z = 36°.
We will use the angle Z a lot. You can see that every corner of the green star has angle Z. The blue star is the same as the green star except it is rotated, so all blue corners also have angle Z. The corners of the red star are twice as wide as the corners of the green and blue stars, so the corners of the red star have the angle 2*Z.
Step 2: Calculating the lengths
First, we observe that all outer corners are on a circle. We call the radius of this circle R. We do not have to calculate R. Instead, we can take any value we want for R. We will always get the same shape but in different sizes. We could call R a "parameter" of the shape.
Given some value for R, I want to know the following lengths:
Calculating A:
We start with A. We can see the following triangle:
The long side of the triangle is our radius R. The other side has length A/2 and we do not care about the third side. The angle in the right-most corner is Z/2 (with Z = 36° being the angle we calculated in the previous section). The angle S is a right-angle, so S = 90°. We can calculate the third angle T because we know that the inner angles of a triangle sum up to 180°. Therefore, 180° = S + Z/2 + T. Solving for T, we get T = 180° - S - Z/2 = 180° - 90° - 36°/2 = 72°.
Next, we use trigonometry to calculate A/2. Trigonometry teaches us that A/2 = R * sin(T). Putting in the formula for T, we get A/2 = R * sin(72°). Solving for A, we get A = 2*R*sin(72°).
If you pick some value for R, for example R = 100, you can now calculate A with this formula. You would need a calculator for sin(72°), because it would be extremely difficult to calculate this in your head. Putting sin(72) into my calculator gives me 0.951056516. So for our choice R = 100, we know that A = 2 * R * sin(72°) = 2 * 100 * 0.951056516 = 190.211303259.
Calculating B:
We use the same technique to find a formula for B. We see the following triangle:
So the bottom side is the length of our radius R. The right side is B/2. We do not care about the third side. The right-most angle is three times Z/2. The angle S is a right-angle, so we have S = 90°. We can calculate the remaining angle T with 180° = S + T + 3*Z/2. Solving for T, we get T = 180° - S - 3*Z/2 = 180° - 90° - 3*36°/2 = 36°. Ok so T = Z, we could have also seen this from the picture, but now we have calculated it anyways.
Using trigonometry, we know that B/2 = R * sin(T), so we get the formula B = 2 * R * sin(36°) to calculate B for some choice of R.
Calculating C:
We see the following triangle:
So the bottom side has length A/2 and the top side has length B. We already have formulas for both of these sides. The third side is C, for which we want to find a formula. The right-most angle is Z. The angle S is a right-angle, so S = 90°. The top-most angle is three times Z/2.
Using trigonometry, we get C = sin(Z) * B.
Calculating D:
We see the following triangle:
We already have a formula for C. We want to find a formula for D. The top-most angle is Z/2 (I could not fit the text into the triangle). The bottom-left angle S is a right-angle.
Using trigonometry, we know that D = tan(Z/2) * C. The tan function is similar to the sin from the previous formulas. You can again put it into your calculator to compute the value, so for Z = 36°, I can put tan(36/2) into my calculator and it gives me 0.324919696.
Calculating E:
Ok this is easy, E = 2*D.
Halfway done already!
Calculating F:
This is similar to A and B:
We want to find a formula for F. The top side has length F/2. The bottom side has the length of our radius R. The right-most corner has angle Z. S is a right-angle. We can calculate T = 180° - S - Z = 180° - 90° - Z = 90° - Z.
Using trigonometry, we get F/2 = R * sin(T). Putting in the formula for T gives us F/2 = R*sin(90° - Z). Solving for F gives us F = 2*R*sin(90°-Z).
Calculating G:
We see the following triangle:
The top side has length F, we already know a formula for it. The right side has length G, we want to find a formula for it. We do not care about the bottom side. The left-most corner has angle Z/2. The right-most corner has angle 2*Z. The bottom corner has angle S, which is a right-angle, so S = 90°. It was not immediately obvious to me that the red line and the green line are perfectly perpendicular to each other so that S really is a right-angle, but you can verify this by using the formula for the inner angles of a triangle, which gives you 180° = Z/2 + 2*Z + S. Solving for S gives us S = 180° - Z/2 - 2*Z. Using Z = 36°, we get S = 180° - 36°/2 - 2* 36° = 90°.
Using trigonometry, we get G = F * sin(Z/2).
Calculating H:
We see the following triangle:
The right side has length G, we already have formula for that. The bottom side has length H, we want to find a formula for that. We do not care about the third side. The top corner has angle Z, the bottom-right corner has angle S. We already know that S is a right-angle from the last section.
Using trigonometry, we get H = G * tan(Z).
Calculating I:
This is easy, I is on the same line as A. We can see that A can be divided into A = I + H + E + H + I. We can simplify this to A = 2*I + 2*H + E. Solving for I gives us I = (A - 2*H - E)/2.
Calculating J:
Again this is easy, J is on the same line as F. We can see that F can be divided into F = G + J + G. We can simplify that to F = 2*G + J. Solving for J gives us J = F - 2*G.
Writing the Python program
We now have formulas for all the lines we were interested in! We can now put these into a Python program to draw the picture.
Python gives you helper functions for computing sin and tan. They are contained in the math module. So you would add import math to the top of your program, and then you can use math.sin(...) and math.tan(...) in your program. However, there is one problem: These Python functions do not use degrees to measure angles. Instead they use a different unit called "radians". Fortunately, it is easy to convert between degrees and radians: In degrees a full circle is 360°. In radians, a full circle is 2*pi, where pi is a special constant that is approximately 3.14159265359.... Therefore, we can convert an angle that is measured in degrees into an angle that is measured in radians, by dividing the angle by 360° and then multiplying it by 2*pi. We can write the following helper functions in Python:
import math
def degree_to_radians(angle_in_degrees):
full_circle_in_degrees = 360
full_circle_in_radians = 2 * math.pi
angle_in_radians = angle_in_degrees / full_circle_in_degrees * full_circle_in_radians
return angle_in_radians
def sin_from_degrees(angle_in_degrees):
angle_in_radians = degree_to_radians(angle_in_degrees)
return math.sin(angle_in_radians)
def tan_from_degrees(angle_in_degrees):
angle_in_radians = degree_to_radians(angle_in_degrees)
return math.tan(angle_in_radians)
We can now use our functions sin_from_degrees and tan_from_degrees to compute sin and tan from angles measured in degrees.
Putting it all together:
from turtle import *
import math
# Functions to calculate sin and tan ###########################################
def degree_to_radians(angle_in_degrees):
full_circle_in_degrees = 360
full_circle_in_radians = 2 * math.pi
angle_in_radians = angle_in_degrees / full_circle_in_degrees * full_circle_in_radians
return angle_in_radians
def sin_from_degrees(angle_in_degrees):
angle_in_radians = degree_to_radians(angle_in_degrees)
return math.sin(angle_in_radians)
def tan_from_degrees(angle_in_degrees):
angle_in_radians = degree_to_radians(angle_in_degrees)
return math.tan(angle_in_radians)
# Functions to calculate the angles ############################################
def get_X():
num_corners = 5
return (num_corners-2)*180 / num_corners
def get_Y():
return (360 - 2*get_X()) / 2
def get_Z():
return 180 - 2*get_Y()
# Functions to calculate the lengths ###########################################
def get_A(radius):
Z = get_Z()
return 2 * radius * sin_from_degrees(90 - Z/2)
def get_B(radius):
Z = get_Z()
return 2 * radius * sin_from_degrees(90 - 3*Z/2)
def get_C(radius):
Z = get_Z()
return sin_from_degrees(Z) * get_B(radius)
def get_D(radius):
Z = get_Z()
return tan_from_degrees(Z/2) * get_C(radius)
def get_E(radius):
return 2 * get_D(radius)
def get_F(radius):
Z = get_Z()
return 2 * radius * sin_from_degrees(90 - Z)
def get_G(radius):
Z = get_Z()
return get_F(radius) * sin_from_degrees(Z/2)
def get_H(radius):
Z = get_Z()
return get_G(radius) * tan_from_degrees(Z)
def get_I(radius):
A = get_A(radius)
E = get_E(radius)
H = get_H(radius)
return (A - E - 2*H) / 2
def get_J(radius):
F = get_F(radius)
G = get_G(radius)
return F - 2*G
# Functions to draw the stars ##################################################
def back_to_center():
penup()
goto(0, 0)
setheading(0)
pendown()
def draw_small_star(radius):
penup()
forward(radius)
pendown()
Z = get_Z()
left(180)
right(Z/2)
E = get_E(radius)
H = get_H(radius)
I = get_I(radius)
for i in range(0,5):
penup()
forward(I)
pendown()
forward(H)
penup()
forward(E)
pendown()
forward(H)
penup()
forward(I)
left(180)
right(Z)
back_to_center()
def draw_green_star(radius):
pencolor('green')
draw_small_star(radius)
def draw_blue_star(radius):
pencolor('blue')
Z = get_Z()
left(Z)
draw_small_star(radius)
def draw_red_star(radius):
pencolor('red')
Z = get_Z()
penup()
forward(radius)
pendown()
left(180)
right(Z)
G = get_G(radius)
J = get_J(radius)
for i in range(0,10):
pendown()
forward(G)
penup()
forward(J)
pendown()
forward(G)
left(180)
right(2*Z)
back_to_center()
def draw_shape(radius):
draw_green_star(radius)
draw_blue_star(radius)
draw_red_star(radius)
radius = 400
draw_shape(radius)
done()
Output:
Here's a different solution. It's based on a kite polygon where the upper portion is a pair of 3-4-5 right triangles and the lower portion is a pair of 8-15-17 right triangles:
from turtle import Screen, Turtle
KITES = 10
RADIUS = 100
def kite(t):
t.right(37)
t.forward(100)
t.right(81)
t.forward(170)
t.right(124)
t.forward(170)
t.right(81)
t.forward(100)
t.right(37)
turtle = Turtle()
turtle.penup()
turtle.sety(-RADIUS)
for _ in range(KITES):
turtle.circle(RADIUS, extent=180/KITES)
turtle.pendown()
kite(turtle)
turtle.penup()
turtle.circle(RADIUS, extent=180/KITES)
turtle.hideturtle()
screen = Screen()
screen.exitonclick()
(Yes, I'm obsessed with this puzzle.) I was sufficiently impressed by the brevity of #AnnZen's solution, I decided to see if I could come up with an even shorter one. The only unique structure in this polygon is the side of the kite:
So the problem becomes drawing ten of these in a circular fashion, and then reversing the code to draw them again in the opposite direction:
from turtle import *
for _ in range(2):
for _ in range(10):
fd(105)
lt(90)
fd(76.5)
pu()
bk(153)
rt(54)
pd()
lt(72)
lt, rt = rt, lt
done()
My Short code:
from turtle import *
for f, t in [(0,-72),(71,108),(71,0)]*10+[(29,90),(73,72),(73,90),(29,72)]*10:fd(f),rt(t)
Here's the solution:
import turtle
#turtle.tracer(0)
a = turtle.Turtle()
for _ in range(10):
a.forward(100)
a.right(90)
a.forward(73)
a.right(72)
a.forward(73)
a.backward(73)
a.right(108)
a.forward(73)
a.right(90)
a.penup()
a.forward(100)
a.pendown()
a.forward(100)
a.right(108)
#turtle.update()
Let's look at a yet another approach to drawing this shape. We'll start with the same diagram as #f9c69e9781fa194211448473495534
Using a ruler (1" = 100px) and protractor on the OP's original image, we can approximate this diagram with very simple code:
from turtle import Screen, Turtle
turtle = Turtle()
turtle.hideturtle()
turtle.penup() # center on the screen
turtle.setposition(-170, -125)
turtle.pendown()
for _ in range(10):
turtle.forward(340)
turtle.left(126)
turtle.forward(400)
turtle.left(126)
screen = Screen()
screen.exitonclick()
This is equivalent to drawing with a pencil and not lifting it nor overdrawing (backing up) on any line.
To make the shape we want pop out, we divide the two lines that we draw above into segments that are thinner and thicker. The first line breaks into three segments and the second into five:
To do this, we simply break the forward() calls in our loop into wide and narrow segments:
for _ in range(10):
turtle.width(4)
turtle.forward(105)
turtle.width(1)
turtle.forward(130)
turtle.width(4)
turtle.forward(105)
turtle.left(126)
turtle.width(1)
turtle.forward(76.5)
turtle.width(4)
turtle.forward(76.5)
turtle.width(1)
turtle.forward(94)
turtle.width(4)
turtle.forward(76.5)
turtle.width(1)
turtle.forward(76.5)
turtle.left(126)
Finally, we replace the thickness changes with lifting and lowering the pen:
Which is simply a matter of replacing the width() calls with penup() and pendown():
for _ in range(10):
turtle.pendown()
turtle.forward(105)
turtle.penup()
turtle.forward(130)
turtle.pendown()
turtle.forward(105)
turtle.left(126)
turtle.penup()
turtle.forward(76.5)
turtle.pendown()
turtle.forward(76.5)
turtle.penup()
turtle.forward(94)
turtle.pendown()
turtle.forward(76.5)
turtle.penup()
turtle.forward(76.5)
turtle.left(126)
Ray Casting Algorithm
MandelBulb Ray Casting Algorithm Python Example
So, if I understand correctly, the ray casting algorithm requires that an observer be located external to the 3D fractal at which point vectors are drawn from the observer toward a point on the plane normal to the vector and intersecting the origin.
It would seem to me that this would either severely limit the rendered view of the fractal or require stereoscopic 3D reconstruction of the fractal using multiple observer positions (which seems difficult to me). Additionally, no information can be gathered regarding the internal structure of the fractal.
Other Algorithms
Alternatively, Direct Volume Rendering seems intuitive enough however, computationally expensive and potentially inefficient in and of itself. Indirect Volume Rendering using an algorithm such as marching cubes might also employ a bit of a learning curve it seems.
Somewhere in the pdf of the 2nd link it talks about cut plane views in order to see slices of the fractal.
Question:
Why not use cut planes as a rendering method?
1) Using a modified ray tracing algorithm, say we put the observer at point Q at the origin (0, 0, 0).
2) Let us then emit rays from the origin toward the incident plane spanned by y & z point combinations that is slicing the fractal.
3) Calculate the distance to the fractal surface using the algorithm in the 1st link. If the x component of computed distance is within a certain tolerance, dx of the slicing plane, then the y & z coordinates along with the x value of the slicing plane are stored as the x, y, z coordinates. These coordinates are now representative of the surface at that specific slice in x.
4) Let us say that the slicing plane has one degree of freedom in the x direction. By moving the plane in its degree of freedom, we can receive yet another set of x, y, z coordinates for a given slice.
5) The final result is a calculable surface generated by the point cloud created in the previous steps.
6) Additionally, the degree of freedom of the slicing plane can be altered to create an another point cloud which can then be verified against the previous as a means of post-processing.
Please see the image below as a visual aid (the sphere represents the MandelBulb).
Below is my Python code so far, adapted from the first link. I successfully generate the plane of points and am able to get the directions from the origin to the points on the plane. There must be something fundamentally flawed in the distance estimator function because thats where everything breaks down and I get nans for the total distances
def get_plane_points(x, y_res=500, z_res=500, y_min=-10, y_max=10, z_min=-10, z_max=10):
y = np.linspace(y_min, y_max, y_res)
z = np.linspace(z_min, z_max, z_res)
x, y, z = np.meshgrid(x, y, z)
x, y, z = x.reshape(-1), y.reshape(-1) , z.reshape(-1)
P = np.vstack((x, y, z)).T
return P
def get_directions(P):
v = np.array(P - 0)
v = v/np.linalg.norm(v, axis=1)[:, np.newaxis]
return v
#jit
def DistanceEstimator(positions, plane_loc, iterations, degree):
m = positions.shape[0]
x, y, z = np.zeros(m), np.zeros(m), np.zeros(m)
x0, y0, z0 = positions[:, 0], positions[:, 1], positions[:, 2]
dr = np.zeros(m) + 1
r = np.zeros(m)
theta = np.zeros(m)
phi = np.zeros(m)
zr = np.zeros(m)
for _ in range(iterations):
r = np.sqrt(x * x + y * y + z * z)
dx = .01
x_loc = plane_loc
idx = (x < x_loc + dx) & (x > x_loc - dx)
dr[idx] = np.power(r[idx], degree - 1) * degree * dr[idx] + 1.0
theta[idx] = np.arctan2(np.sqrt(x[idx] * x[idx] + y[idx] * y[idx]), z[idx])
phi[idx] = np.arctan2(y[idx], x[idx])
zr[idx] = r[idx] ** degree
theta[idx] = theta[idx] * degree
phi[idx] = phi[idx] * degree
x[idx] = zr[idx] * np.sin(theta[idx]) * np.cos(phi[idx]) + x0[idx]
y[idx] = zr[idx] * np.sin(theta[idx]) * np.sin(phi[idx]) + y0[idx]
z[idx] = zr[idx] * np.cos(theta[idx]) + z0[idx]
return 0.5 * np.log(r) * r / dr
def trace(directions, plane_location, max_steps=50, iterations=50, degree=8):
total_distance = np.zeros(directions.shape[0])
keep_iterations = np.ones_like(total_distance)
steps = np.zeros_like(total_distance)
for _ in range(max_steps):
positions = total_distance[:, np.newaxis] * directions
distance = DistanceEstimator(positions, plane_location, iterations, degree)
total_distance += distance * keep_iterations
steps += keep_iterations
# return 1 - (steps / max_steps) ** power
return total_distance
def run():
plane_location = 2
plane_points = get_plane_points(x=plane_location)
directions = get_directions(plane_points)
distance = trace(directions, plane_location)
return distance
I am eager to hear thoughts on this and what limitations/issues I may encounter. Thanks in advance for the help!
If I am not mistaken, it is not impossible for this algorithm to work. There is inherent potential for problems with any assumptions made about the internal structure of the MandelBulb and what positions an observer is allowed to occupy. That is, if the observer is known to initially be in a zone of convergence then the ray tracing algorithm with return nothing meaningful since the furthest distance that could be measured is 0. This is due to the fact that the current ray tracing algorithm terminates upon first contact with the surface. It is likely this could be altered, however.
Rather than slicing the fractal with plane P, it might make more sense to prevent the termination of the ray upon first contact and instead, terminate based on a distance thats known to exist past the surface of the MandelBulb.
I have code to expand the polygon, it works by multiplying the xs and ys by a factor then re centering the resultant polyon at the center of the original.
I also have code to find the value for the expansion factor, given a point that the polygon needs to reach:
import numpy as np
import itertools as IT
import copy
from shapely.geometry import LineString, Point
def getPolyCenter(points):
"""
http://stackoverflow.com/a/14115494/190597 (mgamba)
"""
area = area_of_polygon(*zip(*points))
result_x = 0
result_y = 0
N = len(points)
points = IT.cycle(points)
x1, y1 = next(points)
for i in range(N):
x0, y0 = x1, y1
x1, y1 = next(points)
cross = (x0 * y1) - (x1 * y0)
result_x += (x0 + x1) * cross
result_y += (y0 + y1) * cross
result_x /= (area * 6.0)
result_y /= (area * 6.0)
return (result_x, result_y)
def expandPoly(points, factor):
points = np.array(points, dtype=np.float64)
expandedPoly = points*factor
expandedPoly -= getPolyCenter(expandedPoly)
expandedPoly += getPolyCenter(points)
return np.array(expandedPoly, dtype=np.int64)
def distanceLine2Point(points, point):
points = np.array(points, dtype=np.float64)
point = np.array(point, dtype=np.float64)
points = LineString(points)
point = Point(point)
return points.distance(point)
def distancePolygon2Point(points, point):
distances = []
for i in range(len(points)):
if i==len(points)-1:
j = 0
else:
j = i+1
line = [points[i], points[j]]
distances.append(distanceLine2Point(line, point))
minDistance = np.min(distances)
#index = np.where(distances==minDistance)[0][0]
return minDistance
"""
Returns the distance from a point to the nearest line of the polygon,
AND the distance from where the normal to the line (to reach the point)
intersets the line to the center of the polygon.
"""
def distancePolygon2PointAndCenter(points, point):
distances = []
for i in range(len(points)):
if i==len(points)-1:
j = 0
else:
j = i+1
line = [points[i], points[j]]
distances.append(distanceLine2Point(line, point))
minDistance = np.min(distances)
i = np.where(distances==minDistance)[0][0]
if i==len(points)-1:
j = 0
else:
j = i+1
line = copy.deepcopy([points[i], points[j]])
centerDistance = distanceLine2Point(line, getPolyCenter(points))
return minDistance, centerDistance
minDistance, centerDistance = distancePolygon2PointAndCenter(points, point)
expandedPoly = expandPoly(points, 1+minDistance/centerDistance)
This code only works when the point is directly opposing one of the polygons lines.
Modify your method distancePolygon2PointAndCenter to instead of
Returns the distance from a point to the nearest line of the polygon
To return the distance from a point to the segment intersected by a ray from the center to the point. This is the line that will intersect the point once the polygon is fully expanded. To get this segment, take both endpoints of each segment of your polygon, and plug them into the equation for the line parallel & intersecting the ray mentioned earlier. That is y = ((centerY-pointY)/(centerX-pointX)) * (x - centerX) + centerY. You want to want to find endpoints where either one of them intersect the line, or the two are on opposite sides of the line.
Then, the only thing left to do is make sure that we pick the segment intersecting the right "side" of the line. To do this, there are a few options. The fail-safe method would be to use the formula cos(theta) = sqrt((centerX**2 + centerY**2)*(pointX**2 + pointY**2)) / (centerX * pointX + centerY * pointY) however, you could use methods such as comparing x and y values, taking the arctan2(), and such to figure out which segment is on the correct "side" of center. You'll just have lots of edge cases to cover. After all this is said and done, your two (unless its not convex, in which case take the segment farthest from you center) endpoints makeup the segment to expand off of.
Determine what is "polygon center" as central point C of expanding. Perhaps it is centroid (or some point with another properties?).
Make a segment from your point P to C. Find intersection point I between PC and polygon edges. If polygon is concave and there are some intersection points, choose the closest one to P.
Calculate coefficient of expanding:
E = Length(PC) / Length(CI)
Calculate new vertex coordinates. For i-th vertex of polygon:
V'[i].X = C.X + (V[i].X - C.X) * E
V'[i].Y = C.Y + (V[i].Y - C.Y) * E
Decide which point you want to reach, then calculate how much % your polygon needs to expand to reach that point and use the shapely.affinity.scale function. For example, in my case I just needed to make the polygon 5% bigger:
region = shapely.affinity.scale(myPolygon,
xfact=1.05, yfact=1.05 )
I'm trying to find the angle required to move my camera so it's directly in front of an object. If my camera is looking at the object at a 30 degree angle from the left, then my script should return 30 degrees. I'm using cv2.decomposeHomographyMat to find a rotation matrix which works fine. There are 4 solutions returned from this function, so in my script I am outputting 4 angles. Of these angles, there are only two unique angles. My problem is I don't know which of these two angles is correct.
I know the decomposeHomographyMat returns four possible solutions, but shouldn't the angles be the same? I also found the coordinates of my points projected on a 2D plane, but I wasn't sure what to do with this information in regards to finding which angle is correct (here pts3D are the 2D points of the object taken from the camera image with a 0 added for the z column making it 3D pts):
for i in range(len(Ts)):
projectedPts = cv2.projectPoints(pts3D, Rs[i], Ts[i], CAM_MATRIX, DIST_COEFFS)[0][:,0,:]
Here is a snippet from my code. Maybe I am incorrectly determining the angles from the rotation matrix? In my example below, y1 and y2 will be the same angle, and y3 and y4 will be the same angle. Can someone help explain how I determine which angle is the correct angle, and why there are two different angles returned?
def rotationMatrixToEulerAngles(R):
sy = math.sqrt(Rs[0][0] * R[0][0] + R[1][0] * R[1][0])
singular = sy < 1e-6
if not singular :
x = math.atan2(R[2][1] , R[2][2])
y = math.atan2(-R[2][0], sy)
z = math.atan2(R[1][0], R[0][0])
else :
x = math.atan2(-R[1][2], R[1][1])
y = math.atan2(-R[2][0], sy)
z = 0
return np.rad2deg(y)
H, status = cv2.findHomography(corners, REFPOINTS)
output = cv2.warpPerspective(frame, H, (800,800))
# Rs returns 4 matricies, we use the first one
_, Rs, Ts, Ns = cv2.decomposeHomographyMat(H, CAM_MATRIX)
y1 = rotationMatrixToEulerAngles(Rs[0])
y2 = rotationMatrixToEulerAngles(Rs[1])
y3 = rotationMatrixToEulerAngles(Rs[2])
y4 = rotationMatrixToEulerAngles(Rs[3])
Thanks!