# Need Help to Create Animation for Tangent Line with Luxor

Hi all,

I want to understand more of tangent line / gradient line then read the wikipedia and saw the animation here for the tangent line:

I create the plot (static image):

``````using Plots,  LaTeXStrings
gr()

f(x) =  -x^2 + 2x + 2
f'

# gradient_line(f, x₀) is a tangent line with an intercept at point x₀
# in other words it will pass x₀
# and a slope of f'(x₀) - essentially your usual y = a + bx
# where a is the value of the actual function at the point x₀ through
# which we want the line to pass,
# b is the derivative of the function at that point,
# and the "x" in y=a+bx is replaced by x-x₀
# because our "origin" is the point at which we're taking the tangent.

gradient_line(f, x₀) = (x -> f(x₀) + f'(x₀)*(x-x₀))

default(markerstrokecolor = "white", linewidth = 2);

#Plot f
plot(f, -3:0.1:5, label = "f(x) = -x² + 2x + 2", xlabel = "x", ylabel = "f(x)");

# Plot tangent lines
scatter!([-1], [f(-1)], label = "", markersize = 5);
plot!(gradient_line(f, -1), -3:0.1:0.5, label = "f'(-1)", color = 2);

scatter!([1/2], [f(1/2)], label = "", markersize = 5, color = 3);
plot!(gradient_line(f, 1/2), -1:0.1:2, label = "f'(1/2)", color = 3)

scatter!([2], [f(2)], label = "", markersize = 5, color = 4);
plot!(gradient_line(f, 2), 0.2:0.1:3, label = "f'(2)", color = 4)

scatter!([3], [f(3)], label = "", markersize = 5, color = 5);
plot!(gradient_line(f, 3), 0.8:0.1:4, label = "f'(3)", color = 5)
``````

Now, I want to create the moving tangent lines animation, but there is an error, which I have no idea why it said missing comma or ), it works at the above codes…

``````using Plots,  LaTeXStrings
using Luxor
gr()

let
f(x) = x*(sin(x^(2))+1
f'
gradient_line(f, x₀) = (x -> f(x₀) + f'(x₀)*(x-x₀))
default(markerstrokecolor = "white", linewidth = 2);
w, h = 600, 400
i = 1

plot(f, -3:0.1:5, label = L"f(x) = x \ * sin(x^{2}) + 1",
xlabel = "x", ylabel = "f(x)");

function frame(scene, i)

background("white"); sethue("green")

# translate near bottom right corner
# scatter([i], [f(i)], label = "", markersize = 5);
plot(gradient_line(f, i), -3:0.1:0.5, label = "", color = 2);
circle_marker_pos = getworldposition(Point([i], f[i]))

for k = 1:0.1:3
setfont("Georgia Bold", 73)
sethue("black")
text(string("x = \$k"),
Point(O.x-83, O.y-108),
halign=:center)
end

i += 0.1

end
demo = Movie(600, 400, "needforspeedtangent")
animate(demo, [Scene(demo, frame, 1:360)], creategif=true)
end
``````

LoadError: syntax: missing comma or ) in argument list
Stacktrace:
** [1] top-level scope**
** @ ~/LasthrimProjection/plot.jl:7**
** [2] include(fname::String)**
** @ Base.MainInclude ./client.jl:451**
** [3] top-level scope**
** @ REPL[1]:1**
in expression starting at /home/browni/LasthrimProjection/plot.jl:7

There is a missing comma or ) here.

1 Like

`Plots.plot()` and `frame()` won’t draw onto the same canvas. I’ve never tried to use Plots and Luxor at the same time - I don’t think it’s possible, but I’m happy if I’m wrong…

I forgot this, but remember again, thus I can’t plot the graph easily then.

Here’s a quick version in Luxor.jl of `f(x) = sin(2x) * cos(x/2)`, using Zygote and code from @nilshg’s StackOverflow answer.

Julia code
``````using Luxor
using Zygote

## mathspace to drawingspace
K = 400/2 / 2π
Pt(x, y) = Point(K * x, -K * y)

f(x) = sin(2x) * cos(x/2)

gradient_line(f, x₀) = (x -> f(x₀) + f'(x₀) * (x - x₀))

function drawcurve(w, h)
@layer begin
setopacity(0.25)
rule(O)
rule(O, π/2)
end
move(Pt(-2π, 0))
for x in -2π:π/24:2π
line(Pt(x, f(x)))
end
strokepath()
end

w, h = scene.movie.width, scene.movie.height
background("black")
sethue("gold")
drawcurve(w, h)
x = rescale(framenumber, 1, scene.framerange.stop, -2π, 2π)
pt = Pt(x, f(x))
circle(pt, 5, :fill)
δ = 0.1
point_on_curve = Pt(x, y)

y1 = gradient_line(f, x)(x - δ)
y2 = gradient_line(f, x)(x + δ)

sl = slope(Pt(x - δ, y1), Pt(x + δ, y2))
sethue("white")
line(point_on_curve - polar(100, sl),
point_on_curve + polar(100, sl), :stroke)
end

demo = Movie(400, 300, "slope")
animate(demo, [Scene(demo, frame, 1:100)], framerate=10, creategif=true)
``````

This isn’t to say that this is the best or only way to write it - it’s probably easier in Plots or Makie or Javis, although I’m not familiar enough with those packages to know for sure.

Although most Julia packages usually work well together, I’d recommend using only one of the “graphical output” packages in a session. The least of the problems will be having multiple definitions for common functions; accessing the output ‘channels’ of other modules could require some advanced knowledge of their internals.

6 Likes

That’s really cool! I like how Luxor takes care of keeping the length of the tangent line constant for us. This is what comes to mind for trying something similar in Makie (just manually applying `Luxor.slope`)

Julia code
``````using CairoMakie, Zygote

f(x) = sin(2x) * cos(x/2)
gradient_line(f, x₀) = (x -> f(x₀) + f'(x₀) * (x - x₀))

with_theme(theme_dark()) do
fig = Figure()
ax = Axis(fig[1, 1]; limits=(-2π, 2π, -3, 3))

# Static plot setup
x = -2π:π/24:2π
lines!(ax, x, f)
r_line = 1

# Animated point setup
x_point = Observable(x[begin])
y_point = @lift f(\$x_point)

# Animated line setup
x_line = @lift begin
m_line = f'(\$x_point)
x_lim = r_line * cos(mod2pi(atan(m_line)))
range(\$x_point - x_lim, \$x_point + x_lim, 100)
end

# Plot line and point
lines!(ax, x_line, y_line; color=:goldenrod)
scatter!(ax, x_point, y_point; color=:goldenrod)

# Animate
record(fig, "test.gif", x; framerate=15) do xi
x_point[] = xi
end
end
``````
2 Likes

I was trying to make a code works for the old conventional Plots, because I think Luxor is way of my league now… thanks again @cormullion

But I got errors trying Makie’ code of yours (using IJulia):

`Makie.convert_arguments` for the plot type Scatter{Tuple{Float64, Float64}} and its conversion trait Makie.PointBased() was unsuccessful.

The signature that could not be converted was:
::Float64, ::Float64

Makie needs to convert all plot input arguments to types that can be consumed by the backends (typically Arrays with Float32 elements).
You can define a method for `Makie.convert_arguments` (a type recipe) for these types or their supertypes to make this set of arguments convertible (See http://makie.juliaplots.org/stable/recipes.html).

Alternatively, you can define `Makie.convert_single_argument` for single arguments which have types that are unknown to Makie but which can be converted to known types and fed back to the conversion pipeline.

Could it be that you are using an older version of Makie? `scatter` should be able to handle `Tuple{Float64, Float64}` just fine I believe, e.g., `scatter((1., 2.))`. This is my current environment:

``````  [13f3f980] CairoMakie v0.8.13
[e88e6eb3] Zygote v0.6.49
``````

on Makie v0.17.13