This question already has answers here:
Find coordinates to draw arrow head (isoscele triangle) at the end of a line
(2 answers)
Closed 17 days ago.
I have drawn a line (x1, y1), (x2, y2) and would now like to draw 2 more lines that would form an arrowhead at point (x2, y2). How do I determine the 2 points to which I need to draw the arrowheads from point (x2, y2)? I know that I need to find 2 points on a circle with center at point (x2, y2) that are equidistant from line (x1, y1), (x2, y2) but I do not know how to find this. Geometry is not my forte.
Your line is vector from (x1,y1) to (x2,y2), its components are
D = (dx, dy) = (x2-x1, y2-y1)
Length of vector (perhaps you have Hypot function in math library):
Norm = Sqrt(dx * dx + dy * dy)
Normalized (unit length) vector:
uD = (udx, udy) = (dx/Norm, dy/Norm)
(again - math library might contain a function for normalized vector to replace two last formulas)
Unit perpendicular vector:
uP = (upx, upy) = (-udy, udx)
Now make two points at distance L along line from line end, and with perpendicular distance W from the line:
ax = x2 - udx * L + W * upx
ay = y2 - udy * L + W * upy
bx = x2 - udx * L - W * upx
by = y2 - udy * L - W * upy
At last draw lines A-P2 and B-P2
P.S. If you want to define arrow angle, tg(alpha) = W/L, also you can look at calculations here
Related
Given the region bounded by the curves y=x^2, y=(x-2)^2 and the axis.
I want to plot the 3-D solid rotated about the x-axis.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Define the function to rotate
def f(x):
return x**2
def g(x):
return (x-2)**2
# Define the range of x values to plot
x = np.linspace(0, 1, 100)
x2=np.linspace(1, 2, 100)
# Define the range of angles to rotate over
theta = np.linspace(0, 2*np.pi, 100)
# Create a meshgrid of x and theta values
X, Theta = np.meshgrid(x, theta)
X2, Theta = np.meshgrid(x2, theta)
# Calculate the corresponding cylindrical coordinates
R = X
Y = R*np.sin(Theta)
Z = R*np.cos(Theta)*f(X)
R2 = X2
Y2 = R2*np.sin(Theta)
Z2 = R2*np.cos(Theta)*g(X2)
# Create the 3D plot
fig = plt.figure(figsize = (11,8))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z)
ax.plot_surface(X2, Y2, Z2)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
Output:
As you can see, it works fine for the first curve y = x^2 (blue) but it's not rendering correctly for y=(x-2)^2 (orange). Why is it doing that?
The code and output attached above.
I used a trick to make the plotting process easier.
Instead of rotating around the x-axis, it is much easier rotating around the z-axis using spherical coordinates. matplotlib has intuitive example of utilizing spherical coordinates to draw a ball. Hence, we can swap the axis (e.g. treat the x-axis in the 2D plot as the z-axis in the 3D plot), compute the required spherical coordinates from the given two functions, and then convert back to Cartesian for plotting.
Since we swap the coordinates, eventually we have to rotate the plot and manually assign the axis label.
import matplotlib.pyplot as plt
import numpy as np
from typing import Tuple, Callable
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(projection='3d')
# Define the function to rotate
def f(x):
return x**2
def g(x):
return (x-2)**2
def get_p(phi: np.ndarray, f: Callable, x0: float = 0) -> np.ndarray:
"""Get the distance p
Let the origin be O and a line starting from O with its angle relative to
x-axis being phi intersect with the curve y = f(x) at point Q, the distance
p is the length of the line segment OQ.
:param phi: the angle relative to x-axis
:type phi: np.ndarray
:param f: the curve to be rotated around its x-axis
:type f: Callable
:param x0: starting estimate of x-coord of intersection Q. Use this to
control which intersection is desired. default to 0
:type x0: optional, float
:return: an array of distance, corresponding to each given phi
:rtype: np.ndarray
"""
ks = np.tan(phi)
x = []
for k in ks:
func = lambda x : f(x) - k * x
# we only look for one root
x.append(scipy.optimize.fsolve(func, x0)[0])
x = np.array(x)
y = x * ks
return np.sqrt(x**2 + y**2)
def get_xyz(
theta: np.ndarray, phi: np.ndarray, p: np.ndarray,
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
"""Produce the Cartesian coordinates from the given spherical coordinates.
For reference, see: https://mathinsight.org/spherical_coordinates#:~:text=In%20summary%2C%20the%20formulas%20for,%CE%B8z%3D%CF%81cos%CF%95.
:param theta: in the 3D coordinate, given its origin O, a point P and its
projection Q on the XY plane, theta is the angle between line segment
OQ and positive x-axis.
:type theta: np.ndarray
:param phi: using the same setup as described above, phi is the angle
between line segment OP and positive z-axis
:type phi: np.ndarray
:param p: using the same setup as described above, p is the length of line
segment OP.
:type p: np.ndarray
:return: the Cartesian coordinates converted from the spherical coordinates
in the form of (x, y, z)
:rtype: Tuple[np.ndarray, np.ndarray, np.ndarray]
"""
return (
np.outer(np.cos(theta), np.sin(phi) * p),
np.outer(np.sin(theta), np.sin(phi) * p),
np.outer(np.ones(np.size(theta)), np.cos(phi) * p),
)
# Make data
theta = np.linspace(0, 2 * np.pi, 100)
phi_intercept = np.pi / 4 # the angle relative to x-axis when the two curves meet
# Plot y = x^2 half
phi2 = np.linspace(0, phi_intercept, 50)
p2 = get_p(phi2, f, x0=1)
ax.plot_surface(*get_xyz(theta, phi2, p2))
# Plot y = (x - 2)^2 half
phi1 = np.linspace(0, phi_intercept, 50)
p1 = get_p(phi1, g, x0=1)
ax.plot_surface(*get_xyz(theta, phi1, p1))
# Set plot properties
ax.set_box_aspect([1,1,1])
# x axis in the 2D plot becomes z here
ax.set_zlim(0, 2)
ax.set_zlabel('X')
# y axis in the 2D plot is still y here
ax.set_ylim(-1, 1)
ax.set_ylabel('Y')
# the new z axis after rotation becomes x here
ax.set_xlim(-1, 1)
ax.set_xlabel('Z')
# rotate the plot
ax.view_init(10, 0, -90)
plt.savefig('demo.png', dpi=100)
I've got a code with bokeh. There is two math functions where there is an area zone between these two functions in the interval [0, 2]. How can I fill this area zone with a color? I can't use polygon because it is not a polygon.
Here's the code:
import numpy as np
from bokeh.plotting import *
N = 300
x0 = np.linspace(-1, 4, N)
x1 = np.linspace(0, 4, N)
y0 = 0.5 * (x0 ** 2)
y1 = np.sqrt(2 * x1)
y2 = -y1
# output to static HTML file
output_file('plotting_areas.html')
TOOLS = 'pan, wheel_zoom, box_zoom, reset,save, box_select, lasso_select'
p = figure(tools=TOOLS, width=350, height=350,
title=None, x_range=(-1, 5), y_range=(-5, 5))
p.line(x0, y0)
p.line(x1, y1)
p.line(x1, y2)
show(p)
And here is an image for more details.
Thanks
There is nothing built in to Bokeh that will do, e.g. a flood fill, which is really what would be needed. Your best bet is to compute a polygonal approximation to the area yourself.
Otherwise you could (in principle) create a custom extension to perform a flood-fill in JavaScript, but I'm not sure how much effort that would take.
Ok, I've found the solution with bokeh and it is very simple and possible. The key is making two vectors (arrays) with the images of every two math functions between the OX interval. For each vector make a polygon with patch bokeh instruction without border line.
Here is the code:
import numpy as np
from bokeh.plotting import *
N = 300
x0 = np.linspace(-1, 4, N)
x1 = np.linspace(0, 4, N)
y0 = 0.5 * (x0 ** 2)
y1 = np.sqrt(2 * x1)
y2 = -y1
def f1(x):
return 0.5 * (x**2)
def f2(x):
return np.sqrt(2 * x)
z = np.zeros(N)
w = np.zeros(N)
x = np.linspace(0, 2, N)
for i in np.arange(len(x)):
z[i] = f1(x[i])
w[i] = f2(x[i])
# output to static HTML file
output_file('plotting_areas.html')
TOOLS = 'pan, wheel_zoom, box_zoom, reset,save, box_select, lasso_select'
p = figure(tools=TOOLS, width=350, height=350,
title=None, x_range=(-1, 5), y_range=(-5, 5))
p.line(x0, y0)
p.line(x1, y1)
p.line(x1, y2)
p.patch(x, z, color='red')
p.patch(x, w, color='red')
show(p)
And here is an image with the optimal solution:
Thanks
There is VArea now which should do the trick. Perhaps you might want to restict the plotting range to f1 > f2.
How can I get from a plot in Python an exact value on y - axis? I have two arrays vertical_data and gradient(temperature_data) and I plotted them as:
plt.plot(gradient(temperature_data),vertical_data)
plt.show()
Plot shown here:
I need the zero value but it is not exactly zero, it's a float.
I did not find a good answer to the question of how to find the roots or zeros of a numpy array, so here is a solution, using simple linear interpolation.
import numpy as np
N = 750
x = .4+np.sort(np.random.rand(N))*3.5
y = (x-4)*np.cos(x*9.)*np.cos(x*6+0.05)+0.1
def find_roots(x,y):
s = np.abs(np.diff(np.sign(y))).astype(bool)
return x[:-1][s] + np.diff(x)[s]/(np.abs(y[1:][s]/y[:-1][s])+1)
z = find_roots(x,y)
import matplotlib.pyplot as plt
plt.plot(x,y)
plt.plot(z, np.zeros(len(z)), marker="o", ls="", ms=4)
plt.show()
Of course you can invert the roles of x and y to get
plt.plot(y,x)
plt.plot(np.zeros(len(z)),z, marker="o", ls="", ms=4)
Because people where asking how to get the intercepts at non-zero values y0, note that one may simply find the zeros of y-y0 then.
y0 = 1.4
z = find_roots(x,y-y0)
# ...
plt.plot(z, np.zeros(len(z))+y0)
People were also asking how to get the intersection between two curves. In that case it's again about finding the roots of the difference between the two, e.g.
x = .4 + np.sort(np.random.rand(N)) * 3.5
y1 = (x - 4) * np.cos(x * 9.) * np.cos(x * 6 + 0.05) + 0.1
y2 = (x - 2) * np.cos(x * 8.) * np.cos(x * 5 + 0.03) + 0.3
z = find_roots(x,y2-y1)
plt.plot(x,y1)
plt.plot(x,y2, color="C2")
plt.plot(z, np.interp(z, x, y1), marker="o", ls="", ms=4, color="C1")
I have a game represented like this :
I would like to calculate the coordinates of the point x4,y4.
What I know is :
y4 = y3, x4 is on the line x1,y1,x2,y2 and the line is 45° (degree)
I tried x4 = y4 - y1 + x1 but it doesn't work very well..
Any ideas ?
m= (y2-y1)/(x2-x1) = (y4-y1)/(x4-x1)=1 as slope is 45 degree.
so x4=x1+y4-y1;
substitute y4=y3;
then X4 = x1+y3-y1;
This video should help you to get the right formula.
Should be:
m = (y2 - y1) / (x2 - x1)
b = y2 - m * x2
x4 = (y3 - b) / m
Since the line is at a 45° angle, dx=dy between two points on the line. Thus:
x4 = x1+(y1-y4)
I'm intersecting a line in 2D and I calculate the X,Y coordinates of the intersection point. What I need is the Z of the intersection point given the X,Y,Z of the line points, and the X,Y of the intersection. From what I understand of equations it should be a one-liner but I don't know enough math to get there.
Your question is rather vague, but I will try to answer.
So take following equation:
Let's note it as Fx(X) = Fy(Y) = Fz(Z) and take a part of it:
Fx(X) = Fz(Z)
Then you said you know x, y and z for two points, put it to x1, x2, z1, z2 accordingly. Then put x of intersection to x. Now you have a linear equation with one variable z. Here it is:
z = (x - x1) / (x2 - x1) * (z2 - z1) + z1