# Macro for plotting simple curves

Do any of the plotting packages have a macro for plotting simple curves? I’m looking for something like this:

``````# plot the curve from x=-10 to x=10
@curve(x^3 - 6x^2 + 4x + 12, -10, 10)
``````

Basically, I’m looking for something as quick and easy to use as going to Wolfram Alpha and typing this in:

``````plot x^3 - 6x^2 + 4x + 12
``````

Probably not, because we can do this:

``````plot([x^3 - 6x^2 + 4x + 12 for x in -10:0.1:10])
``````

which doesn’t have many more characters than the macro example. (But you know that already )

1 Like

Unfortunately that doesn’t plot the right thing. When you only provide one vector, `y`, with length `N`, the `plot` function plots that `y` vector versus `x=1:N`. So the output of

``````plot([x^3 - 6x^2 + 4x + 12 for x in -10:0.1:10]; legend=false)
``````

is this:

True, perhaps this is most character-economic version?

`````` x=-10:0.1:10; plot(x,@. x^3 - 6x^2 + 4x + 12)
``````
2 Likes

Not bad! Though for some reason I find `10:0.1:10` hard to type.

1 Like

Fwiw I use Desmos for this kind of thing and like it.

1 Like

This is an exercise in my macro tutorials

An example solution with no error handling might be:

``````import Plots

to_string(e) = String(Symbol(e))

macro curve(e, from, to)
quote
Plots.plot(
range(\$(esc(from)), \$(esc(to)), length = 512),
\$(
Expr(
:->,
esc(:x),
esc(e),
)
),
ylabel = \$(to_string(e)),
)
end
end

@curve(x^3 - 6x^2 + 4x + 12, -10, 10)
``````

A more exciting variant is to analyze the expression so that an expression in only `x` is a 2D plot and an expression in `x` and `y` is a 3D plot.

5 Likes

With `Plots`:

``````julia> plot(f::F, rang) where {F<:Function} = scatter([x for x in rang], [f(x) for x in rang])
plot (generic function with 1 method)

julia> plot(x -> -x^2 + x - 1.0, -1:0.1:1)
``````

Edit: Sorry, forgot to subtype.

5 Likes

Now what would be really great is if Makie or Plots had a `@curve` macro and some logic that determines a good scale for the axes like Wolfram Alpha does. Then I could just type this:

``````@curve(x^3 - 6x^2 + 4x + 12)
``````

EDIT:

I think I could even get away with typing it like this:

``````@curve x^3 - 6x^2 + 4x + 12
``````

EDIT #2:

Though the parsing might be too fragile without the parentheses. Note the difference between these two expressions:

``````julia> Meta.show_sexpr(:(@curve x^3 - 6x^2 + 4x + 12))
(:macrocall, Symbol("@curve"), :(#= REPL[30]:1 =#), (:call, :+, (:call, :-, (:call, :^, :x, 3), (:call, :*, 6, (:call, :^, :x, 2))), (:call, :*, 4, :x), 12))

julia> Meta.show_sexpr(:(@curve x^3 -6x^2 + 4x + 12))
(:macrocall, Symbol("@curve"), :(#= REPL[31]:1 =#), (:call, :^, :x, 3), (:call, :+, (:call, :*, -6, (:call, :^, :x, 2)), (:call, :*, 4, :x), 12))
``````

There are some really cool things logic wise that you could do to find scales for plotting. One potentially interesting option would be to use autodiff and root-finding to make sure that all zeros and turning points were included in the plot.

3 Likes

Why not just

``````using Plots
plot(x -> x^3 - 6x^2 + 4x + 12, xlim=(-10,10))
``````
12 Likes

Hey, that’s pretty good! I’m really not very proficient with plotting in Julia, so I either forgot about that plot method or I never knew it existed…

1 Like

This seems to work too:

``````plot([x -> x^3 - 6x^2 + 4x + 12],-10,10)
``````
3 Likes

Ah yes! Don’t even need the square brackets actually:

``````plot(x -> x^3 - 6x^2 + 4x + 12, -10, 10)
``````

@CameronBieganek This is really close to what you wanted, just change `@curves` into `plot`, and add the `x ->` to make it explicit that it is a function of `x`.

6 Likes

Take that Wolfram!

2 Likes

If you omit the bounds, Plots will choose some (I think just -5 to 5):

``````plot(x -> x^3 - 2x^2 + 3x - 1)
``````
2 Likes

``````plot(x -> x^3 - 2x^2 + 3x - 1)
``````

and then refine the window size as needed with

``````plot(x -> x^3 - 2x^2 + 3x - 1, -10, 10)
``````
1 Like

The two gnuplot packages `Gnuplot.jl` and `Gaston.jl` also support direct plotting of functions.

``````x = 1.:20
@gp x x.^2 "with lines title 'Parabola'"https://github.com/mbaz/Gaston.jl
save(term="pngcairo size 480,360", output="examples/ex1.png")
save("parabola.gp")  # => save a script file with both data and command to re-create the plot.
``````
``````using Gaston, SpecialFunctions
x = y = 0:0.075:10
surf(x, y, (x,y) -> besselj0(y)*x^2, with = "pm3d",
Axes(view = (45, 45),
pm3d = "lighting primary 0.5 specular 0.4",
key = :off)
)
``````

2 Likes

Correct – and the syntax `plot(f::Function, xmin, xmax; samples=100)` will be available in the next release of `Gaston.jl`, with automatic calculation of number of samples (if I can get it to work). See https://github.com/mbaz/Gaston.jl/issues/137

3 Likes