# How to Use Python inside Julia with py""" """ to Plot an angle between 2 vectors/lines?

Hi all,

this is basically still using Julia (calling Python), but I want to know whether this code works:

``````using PyCall

py"""
import matplotlib.pyplot as plt
from matplotlib.patches import Arc
from matplotlib.lines import Line2D
import math

sim_score =[0.993832,0.543218,0.234745 ,0.873513,0.234565,0.789212]

def get_angle_text(angle_plot):
angle = angle_plot.get_label()[:-1]  # Excluding the degree symbol
angle = "%0.2f" % float(angle) + u"\u00b0"  # Display angle upto 2 decimal places

# Set the label position
x_width = angle_plot.width / 2

return [x_width, y_width, angle]

def get_angle_plot(line1, offset=1, color=None, origin=[0, 0], len_x_axis=1, len_y_axis=1):

l1xy = line1.get_xydata()

# Angle between line1 and x-axis
slope = (l1xy - l1xy) / float(l1xy - l1xy)
angle = abs(math.degrees(math.atan(slope)))  # Taking only the positive angle

if color is None:
color = line1.get_color()  # Uses the color of line 1 if color parameter is not passed.

return Arc(origin, len_x_axis * offset, len_y_axis * offset, 0, 0, angle, color=color,
label=str(angle) + u"\u00b0")

sim_score = [0.993832, 0.543218, 0.234745, 0.873513]

fig = plt.figure()
offset = 0
for i in sim_score:
offset += 2  # you play with this value to draw arc spaced from the previous
a = math.acos(i)
b = a * 180 / math.pi
point = (0, 0)
x, y = point
length = 10
# find the end point
endy = length * math.sin(math.radians(b))
endx = length * math.cos(math.radians(b))

line = Line2D([x, endx], [y, endy], linewidth=1, linestyle="-", color="blue")
angle_plot = get_angle_plot(line, offset)
angle_text = get_angle_text(angle_plot)
ax.text(*angle_text)  # To display the angle value

ax.set_ylim([0, 10])
ax.set_xlim([0, 10])
plt.show()
"""
``````

I retrieve errors of:
/home/browni/.julia/packages/PyCall/7a7w0/src/pyeval.jl:57: UserWarning: Matplotlib is currently using agg, which is a non-GUI backend, so cannot show the figure.
** depbuf = IOBuffer()**

I have been reading from these two sources of StackOverFlow:

https://stackoverflow.com/questions/25227100/best-way-to-plot-an-angle-between-two-lines-in-matplotlib

https://stackoverflow.com/questions/55531920/how-to-show-an-arc-with-an-angle-of-a-line-wrt-x-axis-in-python

I have been searching and there is no Julia code to plot angle directly between two intersecting vectors/lines, correct me if I am wrong. I tried before when create a unit circle but it needs manual adjustment and not easy at all to be applied here.

The following magic function should list your available backends in an ipython shell:

``````%matplotlib -l
# or %matplotlib --list
``````

which you can then switch to with:

``````plt.switch_backend("<backend>")
``````

I have been searching and there is no Julia code to plot angle directly between two intersecting vectors/lines, correct me if I am wrong.

How about with `arc` in Makie? For example, something like:

``````using CairoMakie

# Label angle next to plotted arc
function angle_text!(ax, radius, angle)
half_angle = angle/2
text="\$(round(half_angle; digits=2))"
)
end

# Plot setup
fig = Figure()
ax = Axis(fig[1, 1]; limits=(0, 1, 0, 1))
colsize!(fig.layout, 1, Aspect(1, 1.0)) # Square aspect ratio
resize_to_layout!(fig) # Clip the extra margins around the figure

# Arc settings
center = (0, 0)
radii = (0.25, 0.5, 0.75)
start_angle = 0
stop_angles = (π/6, π/4, π/3)

# Line settings
intercept = 0

# Make the plots
ablines!(intercept, tan(stop_angle))
arc!(ax, center, radius, start_angle, stop_angle)
end

# Display
fig
``````

2 Likes

@icweaver Thanks!
better stick with CairoMakie then.

How about create a line with an arrow, is it possible with CairoMakie + Makie?

I want to disable the y axis too so only showing the line between the angles.

I also try to draw more than 1 plots / 2 rows of plotting:

``````using CairoMakie
# Label angle next to plotted arc
function angle_text!(ax, radius, angle)
half_angle = angle/2
text="\$(round(half_angle; digits=2))"
)
end

fig = Figure()
ax = Axis(fig[1, 1]; limits=(0, 1, 0, 1))
bx = Axis(fig[2, 1]; limits=(0, 1, 0, 1))
colsize!(fig.layout, 1, Aspect(1, 1.0))

# Arc settings
center = (0, 0)
radii = (0.25, 0.5, 0.75)
start_angle = 0
stop_angles = (π/6, π/4, π/3)

radiib = (0.25, 0.5)
start_angleb = 0
stop_anglesb = (π/6, π/4)

# Line settings

intercept = 0

ablines!(intercept, tan(stop_angle))
arc!(ax, center, radius, start_angle, stop_angle)
end

ablines!(intercept, tan(stop_angleb))
arc!(bx, center, radiusb, start_angleb, stop_angleb)
end

resize_to_layout!(fig)

fig
``````

It turns out I can’t make the third line disappear at the plot on the second row, I want to show the angle in degree instead of in sin term.

Basically, this is what I want to create (the coding part is quite difficult since I am not an expert in Makie):

(If you all wondering, I am summarizing Linear Algebra with Julia) I try to just focus on one line, I get the command to calculate the inverse of the angle through `rad2deg()`.

Here is the code:

``````using CairoMakie
# Label angle next to plotted arc
function angle_text!(ax, radius, angle)
half_angle = angle/2
theta =rad2deg(angle) # Compute from radians to degrees.
text="\$theta"
)
end

fig = Figure()
ax = Axis(fig[1, 1]; limits=(0, 1, 0, 1))
colsize!(fig.layout, 1, Aspect(1, 1.0))

# Arc settings
center = (0, 0)
start_angle = 0
stop_angles = (π/4) # define the angle of the line toward x-axis

# Line settings

intercept = 0

ablines!(intercept, tan(stop_angle))
arc!(ax, center, radius, start_angle, stop_angle)
end

resize_to_layout!(fig)

fig

``````

This is as close as I can get to your diagram (using Luxor.jl) with a quick bit of code:

Code
``````using Luxor

function vector(pt1, pt2)
arrow(pt1, pt2,
arrowheadfunction=(s, e, sl) -> begin
@layer begin
translate(s)
rotate(sl)
@layer begin
sethue("white")
ngon(O, 10, 3, 0, :fillpreserve)
end
strokepath()
end
end,
linewidth=3)
circle(pt1, 5, :fill)
end

function draw_vector_angle(r, θ)
p₁ = Point(0, 0)
p₂ = polar(r, 0)
p₃ = Point(r * cos(θ), r * sin(θ))
sethue("darkcyan")
vector(p₁, p₂)
vector(p₁, p₃)
sethue("red")

arrow(O, r / 4, 0, θ,
clockwise=false,
decoration=0.5,
decorate=() -> begin
sethue("green")
fontsize(12)
rotate(-getrotation())
text("θ = " * string(round(rad2deg(anglethreepoints(p₂, p₁, p₃)), digits=1)) * "°")
end)
sethue("black")
fontsize(20)
fontface("NewCM10-Bold")
label("v", :s, between(p₁, p₂, 0.7), offset=10)
label("u", :nw, between(p₁, p₃, 0.6), offset=10)
end

@drawsvg begin
background("grey90")
tiles = Tiler(600, 600, 2, 2, margin=70)
r = 100
angles = deg2rad.([-30, -100, 180, -210])
for (pos, n) in tiles
@layer begin
translate(pos)
draw_vector_angle(r, angles[n])
end
end
end

``````

Some extra code would be needed to handle the last case correctly. And extra tweaking work would be required, depending on your intended use.

2 Likes

Cool @cormullion ,

I guess from CairoMakie we go to Luxor now. Well, it depends on your use case - Makie is much more powerful, but you may not need all of its power, in which case you could perhaps get away with something simpler such as Luxor.jl.

Probably the best tool for making diagrams like this is Tikz, but I think it’s quite a challenge to master it.