diff --git a/CHANGELOG.md b/CHANGELOG.md index 1df271d6..9b847d43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,11 @@ # Changelog -## [v2.6.0] - forthcoming +## [v2.6.0] - 12 November 2020 ### Added - additional methods for `offsetpoly()` for open polylines -- experimental image_as_matrix!() -- @play macro +- image_as_matrix!() - reusable buffer ### Changed diff --git a/docs/make.jl b/docs/make.jl index 59812d6b..86046f93 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -21,6 +21,7 @@ makedocs( "Images" => "images.md", "Turtle graphics" => "turtle.md", "Animation" => "animation.md", + "Live graphics" => "livegraphics.md", "More examples" => "moreexamples.md", "Index" => "functionindex.md" ] diff --git a/docs/src/assets/figures/clock.gif b/docs/src/assets/figures/clock.gif new file mode 100644 index 00000000..918d41df Binary files /dev/null and b/docs/src/assets/figures/clock.gif differ diff --git a/docs/src/livegraphics.md b/docs/src/livegraphics.md new file mode 100644 index 00000000..ab846d43 --- /dev/null +++ b/docs/src/livegraphics.md @@ -0,0 +1,93 @@ +# Live graphics + +With the help of an external appication to manage windows, it's possible to use Luxor to create continuously changing graphics in a window. + +This example uses the [MiniFB](https://github.com/aviks/MiniFB.jl) package, which you can add using `] add MiniFB`. + +The file `play.jl` defines a simple macro, `@play`, which continuously evaluates and draws the graphics in a window. For example, this code: + +``` +using Luxor + +include(dirname(pathof(Luxor)) * "/play.jl") + +let θ = 0 + @play 400 400 begin + # + background("black") + sethue("white") + rotate(θ) + hypotrochoid(200, 110, 37, :stroke) + θ += π/120 + sleep(0.01) + # + end +end +``` + +draws a continuously rotating hypotrochoid. + +## Clock + +This code also imports the `@play` macro. + +The call to `sleep()` reduces the CPU time, and allows other processes to run, but the millisecond +animation will be less smooth as a result. + +![clock](assets/figures/clock.gif) + +``` +using Luxor, Colors, Dates, ColorSchemes + +include(dirname(pathof(Luxor)) * "/play.jl") + +function clock(cscheme=ColorSchemes.leonardo) + @play 400 600 begin + + # background + sethue(get(cscheme, .0)) + paint() + + # 24hour sector + fontsize(30) + sethue(get(cscheme, .2)) + h = Dates.hour(now()) + sector(O, 180, 200, π/2, π/2 + rescale(h, 0, 24, 0, 2pi), :fill) + + @layer begin + fontsize(12) + sethue("white") + @. text(["0", "6", "12", "18"], polar(190, [i * π/2 for i in 1:4]), + halign=:center, + valign=:middle) + end + + # minute sector + sethue(get(cscheme, .4)) + m = Dates.minute(now()) + sector(O, 160, 180, 3π/2, 3π/2 + rescale(m, 0, 60, 0, 2pi), :fill) + + # second sector + sethue(get(cscheme, .6)) + s = Dates.second(now()) + sector(O, 140, 160, 3π/2, 3π/2 + rescale(s, 0, 60, 0, 2pi), :fill) + + # millisecond indicator + @layer begin + setopacity(0.5) + sethue(get(cscheme, .8)) + ms = Dates.value(Dates.Millisecond(Dates.now())) + circle(polar(120, 3π/2 + rescale(ms, 0, 1000, 0, 2pi)), 20, :fill) + end + + # central text + fontface("JuliaMono-Black") + sethue(get(cscheme, 1.0)) + text(Dates.format(Dates.now(), "HH:MM:SS"), halign=:center) + + sleep(0.05) + end +end + +clock(ColorSchemes.klimt) +``` diff --git a/src/Luxor.jl b/src/Luxor.jl index 9f9858a8..e83dd51a 100644 --- a/src/Luxor.jl +++ b/src/Luxor.jl @@ -12,8 +12,6 @@ module Luxor using Juno, Cairo, Colors, FileIO, Dates -# , MiniFB - #= from Cairo use: CairoARGBSurface, CairoEPSSurface, CairoMatrix, CairoPDFSurface, CairoPattern, CairoPatternMesh, CairoSurface, CairoSVGSurface, CairoContext, arc, arc_negative, circle, clip, clip_preserve, close_path, convert_cairo_path_data, copy_path, copy_path_flat, curve_to, destroy, fill, @@ -54,8 +52,8 @@ include("Boxmaptile.jl") include("noise.jl") include("deprecations.jl") include("graphlayout.jl") -# include("play.jl") include("Style.jl") +# include("play.jl") # will require MiniFB #include("shapefile.jl") # don't load unless you've loaded Shapefile.jl export Drawing, diff --git a/src/play.jl b/src/play.jl index eb9f59af..84692c35 100644 --- a/src/play.jl +++ b/src/play.jl @@ -1,6 +1,25 @@ +using MiniFB + +function onclick(window, button, mod, isPressed)::Cvoid + if Bool(isPressed) + println("mouse clicked") + end + mousex = mfb_get_mouse_x(window) + mousey = mfb_get_mouse_y(window) + println("x: $mousex y: $mousey") +end + +function active_fn(win::Ptr{Cvoid}, is_active::Bool) + println("Window is now ", is_active ? "" : "in", "active") +end + macro play(w, h, body) quote - window = mfb_open_ex("julia", $w, $h, MiniFB.WF_RESIZABLE) + window = mfb_open_ex("Luxor -> Julia", $w, $h, MiniFB.WF_RESIZABLE) + + mfb_set_active_callback(window, active_fn) + mfb_set_mouse_button_callback(window, onclick) + buffer = zeros(UInt32, $w, $h) while true Drawing($w, $h, :image) @@ -17,7 +36,51 @@ macro play(w, h, body) end end -#= bez path thingy +#= Examples: + +1 clock + +using Luxor, Colors, Dates, ColorSchemes + +include(dirname(pathof(Luxor)) * "/play.jl") + +function clock(cscheme=ColorSchemes.leonardo) + @play 400 600 begin + fontface("JuliaMono-Regular") + + sethue(get(cscheme, .0)) + paint() + fontsize(30) + + sethue(get(cscheme, .2)) + h = Dates.hour(now()) + sector(O, 180, 200, 3π/2, 3π/2 + rescale(h, 0, 24, 0, 2pi), :fill) + + sethue(get(cscheme, .4)) + m = Dates.minute(now()) + sector(O, 160, 180, 3π/2, 3π/2 + rescale(m, 0, 60, 0, 2pi), :fill) + + sethue(get(cscheme, .6)) + s = Dates.second(now()) + sector(O, 140, 160, 3π/2, 3π/2 + rescale(s, 0, 60, 0, 2pi), :fill) + + sethue(get(cscheme, .8)) + ms = Dates.value(Dates.Millisecond(Dates.now())) + sector(O, 137, 140, 3π/2, 3π/2 + rescale(ms, 0, 1000, 0, 2pi), :fill) + + sethue(get(cscheme, 1.0)) + text(Dates.format(Dates.now(), "HH:MM:SS"), halign=:center) + end +end + +clock(ColorSchemes.botticelli) + +=# + + +#= + +2: bezier path thingy using Luxor, Colors, Dates @@ -66,7 +129,9 @@ bez() =# -#= some balls +#= + +3 some balls using Luxor, Colors, Dates @@ -106,35 +171,3 @@ end f() =# - -#= clock - -using Luxor, Colors, Dates - -function clock() - @play 400 600 begin - fontface("JuliaMono-Regular") - # outer - sethue("black") - paint() - sethue("white") - fontsize(30) - text(Dates.format(Dates.now(), "HH:MM:SS"), halign=:center) - sethue("cyan") - h = Dates.hour(now()) - sector(O, 180, 200, 3π/2, 3π/2 + rescale(h, 0, 24, 0, 2pi), :fill) - sethue("magenta") - m = Dates.minute(now()) - sector(O, 160, 180, 3π/2, 3π/2 + rescale(m, 0, 60, 0, 2pi), :fill) - sethue("red") - s = Dates.second(now()) - sector(O, 140, 160, 3π/2, 3π/2 + rescale(s, 0, 60, 0, 2pi), :fill) - sethue("orange") - ms = Dates.value(Dates.Millisecond(Dates.now())) - sector(O, 137, 140, 3π/2, 3π/2 + rescale(ms, 0, 1000, 0, 2pi), :fill) - end -end - -clock() - -=#