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
    y_width = math.sin(math.radians(angle_plot.theta2))

    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[1][1] - l1xy[0][1]) / float(l1xy[1][0] - l1xy[0][0])
    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()
ax = fig.add_subplot(111)
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.add_line(line )
    ax.add_patch(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!(ax, radius*cos(half_angle), radius*sin(half_angle);
        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
for (radius, stop_angle) ∈ zip(radii, stop_angles)
    ablines!(intercept, tan(stop_angle))
    arc!(ax, center, radius, start_angle, stop_angle)
    angle_text!(ax, radius, 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!(ax, radius*cos(half_angle), radius*sin(half_angle);
        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

for (radius, stop_angle) ∈ zip(radii, stop_angles)
    ablines!(intercept, tan(stop_angle))
    arc!(ax, center, radius, start_angle, stop_angle)
    angle_text!(ax, radius, stop_angle)
end

for (radiusb, stop_angleb) ∈ zip(radiib, stop_anglesb)
    ablines!(intercept, tan(stop_angleb))
    arc!(bx, center, radiusb, start_angleb, stop_angleb)
    angle_text!(bx, radiusb, 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)

Capture d’écran_2022-08-11_13-23-59

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!(ax, radius*cos(half_angle), radius*sin(half_angle);
        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)
radii = (0.25)
start_angle = 0
stop_angles = (π/4) # define the angle of the line toward x-axis


# Line settings

intercept = 0

for (radius, stop_angle) ∈ zip(radii, stop_angles)
    ablines!(intercept, tan(stop_angle))
    arc!(ax, center, radius, start_angle, stop_angle)
    angle_text!(ax, radius, 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.

:slight_smile: 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.