using PlotlyJS, Parameters #-----------------------------------------------------------------------# @with_kw mutable struct Quiver x::AbstractArray #Array of x-coord for arrow positions y::AbstractArray #Array of y-coord for arrow positions u::AbstractArray #Array of first coord of the vector field v::AbstractArray #Array of second coord of the vector field vector_scale = 0.1 #scale factor in (0, 1] for the vector directions to avoid quiver overlapping arrow_scale = 0.3 angle = π/9 scaleratio = 1.0 #aspect ratio for the 2d plot d =1.0 #a scale factor in (0.9, 1] for the already scaled direction; #d=0.95 makes a sharper arrow when barb line has width>1 (for example, when plotting single arrow) end function tuple_interleave(tu::Union{NTuple{3, Vector}, NTuple{4, Vector}}) #auxilliary function to interleave elements of a NTuple of vectors, N=3 or 4 zipped_data = collect(zip(tu...)) vv_zdata = [collect(elem) for elem in zipped_data] return reduce(vcat, vv_zdata) end function quiverPlot(q::Quiver; color="RoyalBlue") @unpack_Quiver q x = vec(x) y = vec(y) u = vec(u) v = vec(v) (length(x) == length(y) == length(u) == length(v)) && vector_scale > 0 && arrow_scale > 0 || error("the vects x, y, u, v do not have the same length") u = vector_scale * scaleratio *u v = vector_scale * v end_x = x .+ u end_y = y .+ v vect_nans = repeat([NaN], length(x)) barb_x = tuple_interleave((x, x .+ d*u, vect_nans)) barb_y = tuple_interleave((y, y .+ d*v, vect_nans)) barb_length = sqrt.((u/scaleratio) .^2 .+ v .^2) arrow_length = arrow_scale * barb_length barb_angle = atan.(v, u/scaleratio) ang1 = barb_angle .+ angle ang2 = barb_angle .- angle seg1_x = arrow_length .* cos.(ang1) seg1_y = arrow_length .* sin.(ang1) seg2_x = arrow_length .* cos.(ang2) seg2_y = arrow_length .* sin.(ang2) arrowend1_x = end_x .- seg1_x *scaleratio arrowend1_y = end_y .- seg1_y arrowend2_x = end_x .- seg2_x *scaleratio arrowend2_y = end_y .- seg2_y arrow_x = tuple_interleave((arrowend1_x, end_x, arrowend2_x, vect_nans)) arrow_y = tuple_interleave((arrowend1_y, end_y, arrowend2_y, vect_nans)) barb = scatter(x=barb_x, y=barb_y, mode="lines", line_color=color, name="") arrow = scatter(x=arrow_x, y=arrow_y, mode="lines", line_color=color, fill="toself", fillcolor=color, hoverinfo="skip") layout = Layout(yaxis=attr(scaleratio=scaleratio, scaleanchor="x"), showlegend=false) return Plot([barb, arrow], layout) end #--------------------------------------------------------------------------------# q= Quiver(x=[0],y=[0], u=[1], v=[1], vector_scale=1, angle=pi/18, d=0.95) fig1 = quiverPlot(q;) update!(fig1, line_width=4, [1], layout=Layout(width=400, height=400)) display(fig1) q2=Quiver(x=[0], y=[0], u=[1], v=[1], vector_scale=1, angle=pi/24, d=0.95) fig2 = quiverPlot(q2;) update!(fig2, line_width=4, [1], layout=Layout(width=400, height=400)) display(fig2) xl = yl = -π:π/8:π xq = [xi for yi in yl, xi in xl] yq = [yi for yi in yl, xi in xl] uq = sin.(yq) vq = cos.(xq) #norm_vects = sqrt.(uq .^2 + vq .^2) q3 = Quiver(x=xq, y=yq, u=uq, v=vq, vector_scale=0.375, angle=pi/12) fig3 = quiverPlot(q3;) update!(fig3, line_width=0.75, [1], layout=Layout(title_text="Quiver plot with PlotlyJS.jl", title_x=0.5, width=500, height=500, xaxis_constain="domain", yaxis_constain="domain")) display(fig3) xl = yl = range(0, 1, length=16) x = [xi for yi in yl, xi in xl] y = [yi for yi in yl, xi in xl] u = @. sin(π*x) * cos(π*y) v = @. cos(π*x) * sin(π*y) q4 = Quiver(x=x, y=y, u=u, v=v, vector_scale=0.062) fig4 = quiverPlot(q4; color="#8f180b") update!(fig4, line_width=0.75, [1], layout=Layout(width=500, height=500, plot_bgcolor="#faeee0", template=nothing, xaxis_zeroline=false, yaxis_zeroline=false)) display(fig4)