diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5dba0ab..1d634d1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -18,7 +18,6 @@ jobs: fail-fast: false matrix: version: - - '1.6' - '1' os: - ubuntu-latest diff --git a/.gitignore b/.gitignore index ba39cc5..3199bb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ Manifest.toml +test*.jl \ No newline at end of file diff --git a/Project.toml b/Project.toml index a44f952..b407341 100644 --- a/Project.toml +++ b/Project.toml @@ -5,16 +5,19 @@ version = "0.3.9" [deps] AbstractPlutoDingetjes = "6e696c72-6542-2067-7265-42206c756150" -Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" +BaseDirs = "18cc8868-cbac-4acf-b575-c8ff214dc66f" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" HypertextLiteral = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" -PackageExtensionCompat = "65ce6f38-6b18-4e1d-a461-8949797d7930" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" -PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" +SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" +TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" [weakdeps] PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c" @@ -24,11 +27,10 @@ PlotlyKaleidoExt = "PlotlyKaleido" [compat] AbstractPlutoDingetjes = "1" +BaseDirs = "1" Colors = "0.12" -HypertextLiteral = "0.9" +HypertextLiteral = "0.9.4" LaTeXStrings = "1" -PackageExtensionCompat = "1" PlotlyBase = "0.8" -PlutoUI = "0.7" Reexport = "1" -julia = "1.6" +julia = "1.9" diff --git a/notebooks/tests.jl b/notebooks/tests.jl new file mode 100644 index 0000000..d5d0f7d --- /dev/null +++ b/notebooks/tests.jl @@ -0,0 +1,860 @@ +### A Pluto.jl notebook ### +# v0.19.27 + +#> custom_attrs = ["hide-enabled"] + +using Markdown +using InteractiveUtils + +# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error). +macro bind(def, element) + quote + local iv = try Base.loaded_modules[Base.PkgId(Base.UUID("6e696c72-6542-2067-7265-42206c756150"), "AbstractPlutoDingetjes")].Bonds.initial_value catch; b -> missing; end + local el = $(esc(element)) + global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el) + el + end +end + +# ╔═╡ 72c073fd-5f1b-4af0-901b-aaa901f0f273 +begin + using PlutoDevMacros + using PlutoUI + using PlutoExtras + using Dates + using Colors +end + +# ╔═╡ 70dc8fa0-cc32-4ebe-af0d-62b5bb3a82ed +@fromparent begin + using ^ + using >.HypertextLiteral +end + +# ╔═╡ 7bd46437-8af0-4a15-87e9-1508869e1600 +ExtendedTableOfContents() + +# ╔═╡ 0820a893-26a3-49e9-bbc9-13db0a6fccee +PlutoPlotly._fromparent_dict_ + +# ╔═╡ acba5003-a456-4c1a-a53f-71a3bec30251 +md""" +# Tests +""" + +# ╔═╡ 6d49055d-0347-4bce-a2c5-f1568596e2e6 +md""" +## Image +""" + +# ╔═╡ c56df6f6-865e-4af8-b8c0-429b021af1eb +md""" +This is testing the expected behavior of image traces as per [Issue #16](https://github.com/JuliaPluto/PlutoPlotly.jl/issues/16) +""" + +# ╔═╡ 7ba8b496-f7c4-4fbc-a168-dc3d7af92d0c +image_issue = image(;z = rand(3,5,3)*255) + +# ╔═╡ f6ed3a0a-e548-4b99-9cf7-085991a5c99e +plot(image_issue) + +# ╔═╡ 0f9f50f8-95c8-4cb5-96f2-e4ce177ca2dd +Plot(image_issue) + +# ╔═╡ 359d22e8-b13d-420b-b409-b18136c3ff3b +md""" +## Colors.jl colors +""" + +# ╔═╡ e54cc4c4-2a93-4d44-90ed-5944edbf4b0f +let + red = scatter(;y=rand(10), line_color = RGB(1,0,0)) + green_transp = scatter(;y=rand(10), line_color = RGBA(0,1,0, .3)) + plot([red, green_transp]) +end + +# ╔═╡ 3e5b09a9-6d18-4d2f-a37b-ac260ea36646 +md""" +## Respect provided height/width +""" + +# ╔═╡ 070820c5-082f-4428-8d5e-1fdd1ce29eba +let + @htl """ +
+ $(plot(rand(10), Layout( + width = 400, + #height= 400 + ))) +
+ """ +end + +# ╔═╡ 0c30855c-6542-4b1a-9427-3a8427e75210 +md""" +## Slider + UIRevision +""" + +# ╔═╡ de0cb780-ff4e-4236-89c4-4c3163337cfc +@bind clk Clock() + +# ╔═╡ dd23fe10-a8d5-461a-85a8-e03468cdcd97 +@bind N Slider(50:50:250) +# N =let +# clk +# rand(50:100) +# end + +# ╔═╡ 8bf75ceb-e4ae-4c6c-8ab0-a81350f19bc7 +pp = Plot(scatter3d(x = rand(N), y = rand(N), z = rand(N), mode="markers"), Layout( + uirevision = 1, + scene = attr( + xaxis_range = [-1,2], + yaxis_range = [-1,2], + zaxis_range = [-1,2], + aspectmode = "cube", + ), + height = 550 + # autosize = true, +)); + +# ╔═╡ ccf62e33-8fcf-45d9-83ed-c7de80800b76 +let + p = PlutoPlot(pp) + add_plotly_listener!(p, "plotly_relayout", htl_js(""" + (e) => { + + console.log(e) + //console.log(PLOT._fullLayout._preGUI) + + + var eye = e['scene.camera']?.eye; + + if (eye) { + console.log('update: ', eye); + } else { + console.log(e) + } + console.log('div: ',PLOT._fullLayout.scene.camera.eye) + console.log('plot_obj: ',plot_obj.layout.scene?.camera?.eye) + +} + """)) + PlutoPlotly._show(p) +end + +# ╔═╡ 1460ece1-7828-4e93-ac37-e979b874b492 +md""" +## @bind click +""" + +# ╔═╡ 18c80ea2-0df4-40ea-bd87-f8fee463161e +@bind asdasd let + p = PlutoPlot(Plot(scatter(y = rand(10), name = "test", showlegend=true))) + add_plotly_listener!(p,"plotly_click", " + (e) => { + + console.log(e) + let dt = e.points[0] + PLOT.value = [dt.x, dt.y] + PLOT.dispatchEvent(new CustomEvent('input')) +} + ") + p +end + +# ╔═╡ ce29fa1f-0c52-4d38-acbd-0a96cb3b9ce6 +asdasd + +# ╔═╡ c3e29c94-941d-4a52-a358-c4ffbfc8cab8 +md""" +## @bind filtering +""" + +# ╔═╡ b0473b9a-2db5-4d03-8344-b8eaf8428d6c +points = [(rand(),rand()) for _ in 1:10000] + +# ╔═╡ 73945da3-af45-41fb-9c5d-6fbba6362256 +@bind limits let + p = Plot( + scatter(x = first.(points), y = last.(points), mode = "markers") + )|> PlutoPlot + add_plotly_listener!(p, "plotly_relayout", " + e => { + //console.log(e) + let layout = PLOT.layout + let asd = {xaxis: layout.xaxis.range, yaxis: layout.yaxis.range} + PLOT.value = asd + PLOT.dispatchEvent(new CustomEvent('input')) + } + ") +end + +# ╔═╡ ea9faecf-ecd7-483b-99ad-ede08ba05383 +visible_points = let + if ismissing(limits) + points + else + xrange = limits["xaxis"] + yrange = limits["yaxis"] + func(x,y) = x >= xrange[1] && x <= xrange[2] && y >= yrange[1] && y <= yrange[2] + filter(x -> func(x...), points) + end +end + +# ╔═╡ 684ef6d7-c1ae-4af3-b1bd-f54bc29d7b53 +length(visible_points) + +# ╔═╡ f8f7b530-1ded-4ce0-a7d9-a8c92afb95c7 +md""" +## Multiple Listeners +""" + +# ╔═╡ c3b1a198-ef19-4a54-9c32-d9ea32a63812 +let + p = PlutoPlot(Plot(rand(10), Layout(uirevision = 1))) + add_plotly_listener!(p, "plotly_relayout", htl_js(""" +function(e) { + + console.log('listener 1') + +} + """)) + add_plotly_listener!(p, "plotly_relayout", htl_js(""" +function(e) { + + console.log('listener 2') + +} + """)) + @htl "$p" +end + +# ╔═╡ e9fc2030-c2f0-48e9-a807-424039e796b2 +let + p = PlutoPlot(Plot(rand(10), Layout(uirevision = 1))) + add_plotly_listener!(p, "plotly_relayout", htl_js(""" +function(e) { + + console.log('listener 1') + +} + """)) + add_plotly_listener!(p, "plotly_relayout", htl_js(""" +function(e) { + + console.log('listener 2') + +} + """)) + p.plotly_listeners +end + +# ╔═╡ de101f40-27db-43ea-91ed-238502ceaaf7 +md""" +## JS Listener +""" + +# ╔═╡ 6c709fa0-7a53-4554-ab2a-d8181267ec93 +lololol = 1 + +# ╔═╡ 671296b9-6743-48d6-9c4d-1beac2b505b5 +let + lololol + p = PlutoPlot(Plot(rand(10), Layout(uirevision = 1))) + add_js_listener!(p, "mousedown", htl_js(""" +function(e) { + + console.log('MOUSEDOWN!') + +} + """)) +end + +# ╔═╡ 6128ff76-3f1f-4144-bb3d-f44678210013 +md""" +## flexbox +""" + +# ╔═╡ a5823eb2-3aaa-4791-bdc8-196eac2ccf2e +@htl """ +
+$(Plot(rand(10)) |> PlutoPlot) + +$(Plot(rand(10)) |> PlutoPlot) +
+""" + +# ╔═╡ b45cc21d-bfff-4375-a524-95108661a2ef +md""" +## flexbox + bind +""" + +# ╔═╡ 4ea48316-62d9-4b24-bb1d-c9fd1db044dc +@htl """ +
+$(@bind asd plot(rand(10))) + +$(plot(rand(10))) +
+ +""" + +# ╔═╡ e92a0cf8-f869-4737-a465-db17318498a2 +asd + +# ╔═╡ 62126774-e246-473b-9d0b-92e967cd36ac +md""" +## flexbox + PlutoUI.ExperimentalLayout.hbox +""" + +# ╔═╡ cfa78790-aa4c-4c7b-8a9f-198987338516 +# Without bonds, the plots resize automatically inside hbox +PlutoUI.ExperimentalLayout.hbox([plot(rand(10)), plot(rand(10))]) + +# ╔═╡ 340262a2-c823-4e19-8f19-9a05f4504bb5 +# When you wrap plots in bonds, the subplots do not automatically resize to the cell width +PlutoUI.ExperimentalLayout.hbox([(@bind asdasd2_val plot(rand(10))), plot(rand(10))]) + +# ╔═╡ 21682322-13a7-4575-8846-19e61fb8667d +# We need to specify flex-grow to allow resizing +html""" + +""" + +# ╔═╡ a7ef3dfc-d6ad-41c2-bccb-a794e26e80bb +PlutoUI.ExperimentalLayout.hbox([(@bind asdasd1_val plot(rand(10))), plot(rand(10))]; class="hbox_test") + +# ╔═╡ aaf0fe61-d5e6-4d93-8a22-7f97f1249b35 +md""" +## flexbox + uirevision +""" + +# ╔═╡ 6e12592d-01fe-455a-a19c-7544258b9791 +voila = 10 + +# ╔═╡ 36c4a5b1-03f2-4f5f-b9af-822a8f7c8cdf +let + voila + @htl """ +
+
+$(Plot(rand(10), Layout(uirevision = 1)) |> PlutoPlot) + +$(Plot(rand(10)) |> PlutoPlot) +
+
+$(Plot(rand(10)) |> PlutoPlot) + +$(Plot(rand(10)) |> PlutoPlot) +
+
+""" +end + +# ╔═╡ 38a81414-0bcd-4d71-af1d-fe154d2ae09a +md""" +## custom class +""" + +# ╔═╡ 2dd5534f-ce46-4770-b0f3-6e16005b3a90 +cl = ["test_css_class", "lol"] + +# ╔═╡ f69c6955-800c-461e-b464-cab4989913f6 +let + p = PlutoPlot(Plot(rand(10))) + for cn ∈ cl + add_class!(p, cn) + end + p +end + +# ╔═╡ bfe5f717-4702-4316-808a-726fefef9e7e +html""" + +""" + +# ╔═╡ cb3f5ee4-5504-4337-8a8d-d45784f54c85 +md""" +## Sphere +""" + +# ╔═╡ e0271a15-08b5-470f-a2d2-6f064cd3a2b2 +function sphere(radius, origin = [0,0,0]; N = 50) +u = range(0, 2π; length = N) +v = range(0, π, length = N) +x = [radius * cos(u) * sin(v) + origin[1] for u ∈ u, v ∈ v] +y = [radius * sin(u) * sin(v) + origin[2] for u ∈ u, v ∈ v] +z = [radius * cos(v) + origin[3] for u ∈ u, v ∈ v] + surface(;x,y,z) +end + +# ╔═╡ cb1f840f-8d99-4076-9554-7d8ba56e9865 +@bind M Slider(0:90) + +# ╔═╡ 22245242-80a6-4a5b-815e-39b469002f84 +let + s = sphere(1) + r = 2 + i = 0 + # M = 90 + x = [r*cosd(M)] + y = [r*sind(M)] + z = [0] + sat = scatter3d(;x,y,z, mode = "markers") + data = [s,sat] + plot(data, Layout( + uirevision = 1, + scene = attr( + xaxis_range = [-3,3], + yaxis_range = [-3,3], + zaxis_range = [-3,3], + aspectmode = "cube", + ), + )) +end + +# ╔═╡ ebdc0ebc-da58-49c8-a992-5924045c2cac +md""" +## Plot Dates +""" + +# ╔═╡ 6d74e3fa-1806-40b0-9995-c1555519603d +let + x = Date.(2000:2020) + y = rand(length(x)) + plot(scatter(;x,y,name = "values", showlegend = true), Layout( + template = "none" + )) +end + +# ╔═╡ 7054b9ce-cd00-42cf-b81c-bc27b29f4714 +md""" +## Heatmap/Contour tests +""" + +# ╔═╡ 5b5293bf-81b3-4e80-995a-f15f91971bd4 +md""" +This is testing the expected behavior of heatmap and contour plots as per [https://github.com/JuliaPlots/PlotlyJS.jl/issues/355](https://github.com/JuliaPlots/PlotlyJS.jl/issues/355) +""" + +# ╔═╡ 3612826c-0af0-4fef-aafe-112a2948f669 +let + plasma = [[0.0, "#0c0786"], + [0.1, "#40039c"], + [0.2, "#6a00a7"], + [0.3, "#8f0da3"], + [0.4, "#b02a8f"], + [0.5, "#cb4777"], + [0.6, "#e06461"], + [0.7, "#f2844b"], + [0.8, "#fca635"], + [0.9, "#fcce25"], + [1.0, "#eff821"]] + plot(heatmap(z=[1 24 30; 28 18 40; 36 54 7], colorscale=plasma), Layout( + template = "none", + yaxis = attr( + scaleanchor = "x", + scaleratio = 1, + ) + )) +end + +# ╔═╡ ce90eb60-98ed-4901-9218-ed8466bb03c7 +let + a, b = -1., 1. + c, d= 0., 1. + nx, ny= 200, 100 + hx = (b-a)/(nx-1) + x = a:hx:b + hy = (d-c)/(ny-1) + y=c:hy:d + X = x' .* ones(length(y)) + Y = ones(length(x))'.*y + Z= sin.(pi*pi*(X.*X+Y.*Y)); + # print(size(Z)) + plot(contour(x=x, y=y, z=Z), Layout(width=650, height=350, template = "none")) +end + +# ╔═╡ 00000000-0000-0000-0000-000000000001 +PLUTO_PROJECT_TOML_CONTENTS = """ +[deps] +Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +PlutoDevMacros = "a0499f29-c39b-4c5c-807c-88074221b949" +PlutoExtras = "ed5d0301-4775-4676-b788-cf71e66ff8ed" +PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8" + +[compat] +Colors = "~0.12.10" +PlutoDevMacros = "~0.5.7" +PlutoExtras = "~0.7.8" +PlutoUI = "~0.7.37" +""" + +# ╔═╡ 00000000-0000-0000-0000-000000000002 +PLUTO_MANIFEST_TOML_CONTENTS = """ +# This file is machine-generated - editing it directly is not advised + +[[AbstractPlutoDingetjes]] +deps = ["Pkg"] +git-tree-sha1 = "91bd53c39b9cbfb5ef4b015e8b582d344532bd0a" +uuid = "6e696c72-6542-2067-7265-42206c756150" +version = "1.2.0" + +[[ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.1" + +[[Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.11.4" + +[[Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] +git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.12.10" + +[[CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.0.5+0" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" + +[[FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" + +[[FixedPointNumbers]] +deps = ["Statistics"] +git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.4" + +[[Hyperscript]] +deps = ["Test"] +git-tree-sha1 = "8d511d5b81240fc8e6802386302675bdf47737b9" +uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91" +version = "0.0.4" + +[[HypertextLiteral]] +deps = ["Tricks"] +git-tree-sha1 = "c47c5fa4c5308f27ccaac35504858d8914e102f9" +uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2" +version = "0.9.4" + +[[IOCapture]] +deps = ["Logging", "Random"] +git-tree-sha1 = "d75853a0bdbfb1ac815478bacd89cd27b550ace6" +uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" +version = "0.2.3" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.4" + +[[LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.3" + +[[LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "7.84.0+0" + +[[LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.10.2+0" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[LinearAlgebra]] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[MIMEs]] +git-tree-sha1 = "65f28ad4b594aebe22157d6fac869786a255b7eb" +uuid = "6c6e2e6c-3030-632d-7369-2d6c69616d65" +version = "0.1.4" + +[[MacroTools]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "9ee1618cbf5240e6d4e0371d6f24065083f60c48" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.11" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.2+0" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2022.10.11" + +[[NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" + +[[OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.21+4" + +[[OrderedCollections]] +git-tree-sha1 = "2e73fe17cac3c62ad1aebe70d44c963c3cfdc3e3" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.6.2" + +[[Parsers]] +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "716e24b21538abc91f6205fd1d8363f39b442851" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.7.2" + +[[Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.9.2" + +[[PlutoDevMacros]] +deps = ["HypertextLiteral", "InteractiveUtils", "MacroTools", "Markdown", "Pkg", "Random", "TOML"] +git-tree-sha1 = "51e747644116b5806936ad355f1e32e124ec04b1" +uuid = "a0499f29-c39b-4c5c-807c-88074221b949" +version = "0.5.7" + +[[PlutoExtras]] +deps = ["AbstractPlutoDingetjes", "HypertextLiteral", "InteractiveUtils", "Markdown", "OrderedCollections", "PlutoDevMacros", "PlutoUI", "REPL"] +git-tree-sha1 = "2ce56cb64b4c346406a855105850e7ba68d2197c" +uuid = "ed5d0301-4775-4676-b788-cf71e66ff8ed" +version = "0.7.8" + +[[PlutoUI]] +deps = ["AbstractPlutoDingetjes", "Base64", "ColorTypes", "Dates", "FixedPointNumbers", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "MIMEs", "Markdown", "Random", "Reexport", "URIs", "UUIDs"] +git-tree-sha1 = "e47cd150dbe0443c3a3651bc5b9cbd5576ab75b7" +uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8" +version = "0.7.52" + +[[PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.2.0" + +[[Preferences]] +deps = ["TOML"] +git-tree-sha1 = "7eb1686b4f04b82f96ed7a4ea5890a4f0c7a09f1" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.4.0" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["SHA", "Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[Reexport]] +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.2.2" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SparseArrays]] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.9.0" + +[[SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "Pkg", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "5.10.1+6" + +[[TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" + +[[Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" + +[[Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[Tricks]] +git-tree-sha1 = "aadb748be58b492045b4f56166b5188aa63ce549" +uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" +version = "0.1.7" + +[[URIs]] +git-tree-sha1 = "b7a5e99f24892b6824a954199a45e9ffcc1c70f0" +uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" +version = "1.5.0" + +[[UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.13+0" + +[[libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.8.0+0" + +[[nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.48.0+0" + +[[p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+0" +""" + +# ╔═╡ Cell order: +# ╠═7bd46437-8af0-4a15-87e9-1508869e1600 +# ╠═72c073fd-5f1b-4af0-901b-aaa901f0f273 +# ╠═70dc8fa0-cc32-4ebe-af0d-62b5bb3a82ed +# ╠═0820a893-26a3-49e9-bbc9-13db0a6fccee +# ╟─acba5003-a456-4c1a-a53f-71a3bec30251 +# ╟─6d49055d-0347-4bce-a2c5-f1568596e2e6 +# ╟─c56df6f6-865e-4af8-b8c0-429b021af1eb +# ╠═7ba8b496-f7c4-4fbc-a168-dc3d7af92d0c +# ╠═f6ed3a0a-e548-4b99-9cf7-085991a5c99e +# ╠═0f9f50f8-95c8-4cb5-96f2-e4ce177ca2dd +# ╟─359d22e8-b13d-420b-b409-b18136c3ff3b +# ╠═e54cc4c4-2a93-4d44-90ed-5944edbf4b0f +# ╟─3e5b09a9-6d18-4d2f-a37b-ac260ea36646 +# ╠═070820c5-082f-4428-8d5e-1fdd1ce29eba +# ╟─0c30855c-6542-4b1a-9427-3a8427e75210 +# ╠═8bf75ceb-e4ae-4c6c-8ab0-a81350f19bc7 +# ╠═de0cb780-ff4e-4236-89c4-4c3163337cfc +# ╠═dd23fe10-a8d5-461a-85a8-e03468cdcd97 +# ╠═ccf62e33-8fcf-45d9-83ed-c7de80800b76 +# ╠═1460ece1-7828-4e93-ac37-e979b874b492 +# ╠═18c80ea2-0df4-40ea-bd87-f8fee463161e +# ╠═ce29fa1f-0c52-4d38-acbd-0a96cb3b9ce6 +# ╟─c3e29c94-941d-4a52-a358-c4ffbfc8cab8 +# ╠═b0473b9a-2db5-4d03-8344-b8eaf8428d6c +# ╠═73945da3-af45-41fb-9c5d-6fbba6362256 +# ╠═684ef6d7-c1ae-4af3-b1bd-f54bc29d7b53 +# ╠═ea9faecf-ecd7-483b-99ad-ede08ba05383 +# ╠═f8f7b530-1ded-4ce0-a7d9-a8c92afb95c7 +# ╠═c3b1a198-ef19-4a54-9c32-d9ea32a63812 +# ╠═e9fc2030-c2f0-48e9-a807-424039e796b2 +# ╟─de101f40-27db-43ea-91ed-238502ceaaf7 +# ╠═6c709fa0-7a53-4554-ab2a-d8181267ec93 +# ╠═671296b9-6743-48d6-9c4d-1beac2b505b5 +# ╟─6128ff76-3f1f-4144-bb3d-f44678210013 +# ╠═a5823eb2-3aaa-4791-bdc8-196eac2ccf2e +# ╟─b45cc21d-bfff-4375-a524-95108661a2ef +# ╠═4ea48316-62d9-4b24-bb1d-c9fd1db044dc +# ╠═e92a0cf8-f869-4737-a465-db17318498a2 +# ╟─62126774-e246-473b-9d0b-92e967cd36ac +# ╠═cfa78790-aa4c-4c7b-8a9f-198987338516 +# ╠═340262a2-c823-4e19-8f19-9a05f4504bb5 +# ╠═21682322-13a7-4575-8846-19e61fb8667d +# ╠═a7ef3dfc-d6ad-41c2-bccb-a794e26e80bb +# ╟─aaf0fe61-d5e6-4d93-8a22-7f97f1249b35 +# ╠═6e12592d-01fe-455a-a19c-7544258b9791 +# ╠═36c4a5b1-03f2-4f5f-b9af-822a8f7c8cdf +# ╟─38a81414-0bcd-4d71-af1d-fe154d2ae09a +# ╠═2dd5534f-ce46-4770-b0f3-6e16005b3a90 +# ╠═f69c6955-800c-461e-b464-cab4989913f6 +# ╠═bfe5f717-4702-4316-808a-726fefef9e7e +# ╟─cb3f5ee4-5504-4337-8a8d-d45784f54c85 +# ╠═e0271a15-08b5-470f-a2d2-6f064cd3a2b2 +# ╠═cb1f840f-8d99-4076-9554-7d8ba56e9865 +# ╠═22245242-80a6-4a5b-815e-39b469002f84 +# ╟─ebdc0ebc-da58-49c8-a992-5924045c2cac +# ╠═6d74e3fa-1806-40b0-9995-c1555519603d +# ╟─7054b9ce-cd00-42cf-b81c-bc27b29f4714 +# ╟─5b5293bf-81b3-4e80-995a-f15f91971bd4 +# ╠═3612826c-0af0-4fef-aafe-112a2948f669 +# ╠═ce90eb60-98ed-4901-9218-ed8466bb03c7 +# ╟─00000000-0000-0000-0000-000000000001 +# ╟─00000000-0000-0000-0000-000000000002 diff --git a/notebooks/wrapper.jl b/notebooks/wrapper.jl deleted file mode 100644 index ebb8d14..0000000 --- a/notebooks/wrapper.jl +++ /dev/null @@ -1,1628 +0,0 @@ -### A Pluto.jl notebook ### -# v0.19.26 - -using Markdown -using InteractiveUtils - -# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error). -macro bind(def, element) - quote - local iv = try Base.loaded_modules[Base.PkgId(Base.UUID("6e696c72-6542-2067-7265-42206c756150"), "AbstractPlutoDingetjes")].Bonds.initial_value catch; b -> missing; end - local el = $(esc(element)) - global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el) - el - end -end - -# ╔═╡ 7b8a4360-aaa4-11ec-2efc-b1778f6d3a8c -begin - using PlotlyBase - using HypertextLiteral - using Colors -end - -# ╔═╡ 810fb486-10b5-460f-a25a-1a7c9d84e256 -using LaTeXStrings - -# ╔═╡ 676d318f-b4a4-4949-a5db-1c3a5fd9fa68 -using AbstractPlutoDingetjes - -# ╔═╡ fc52e423-1370-4ca9-95dc-090815278a4a -using PlutoUI - -# ╔═╡ a9e7c04a-2d60-4727-9e24-a6dd9a0ee73c -using Dates - -# ╔═╡ 7bd46437-8af0-4a15-87e9-1508869e1600 -TableOfContents() - -# ╔═╡ 4a18fa5d-7c73-468b-bed2-3acff51e3981 -publish_to_js = if is_inside_pluto() - PlutoRunner.publish_to_js -else - @warn "You are trying to show a PlutoPlot outside of Pluto, this is not the intended behavior and you should use either PlotlyBase or PlotlyJS directly. - NOTE: If you receive this warning during pre-compilation or sysimage creation, you can ignore this warning." - x -> x -end - -# ╔═╡ 413ffc5c-c53f-48b0-922d-4c7f8718507c -current_cell_id()::Base.UUID = if is_inside_pluto() - Main.PlutoRunner.currently_running_cell_id[] -else - Base.UUID(zero(UInt128)) -end - -# ╔═╡ 0ae3f943-4f9b-4cfb-aa76-3bcdc7dc9963 -""" - htl_js(x) -Simple convenience constructor for `HypertextLiteral.JavaScript` objects, renamed and re-exported from HypertextLiteral for convenience in case HypertextLiteral is not explicitly loaded alongisde PlutoPlotly. - -See also: [`add_plotly_listeners!`](@ref) -""" -htl_js(x) = HypertextLiteral.JavaScript(x) - -# ╔═╡ 90fd960f-65b3-4d8c-b8a8-42d3be8c770f -function Base.show(io::IO, mime::MIME"text/html", s::HypertextLiteral.JavaScript) -if is_inside_pluto() - show(io, mime, Markdown.MD(Markdown.Code("js",s.content))) -else - show(io, MIME"text/plain",s) -end -end - -# ╔═╡ e9d43bc6-390e-43c3-becb-d1584202da41 -# This is only used to simplify debugging (by setting this to false) the plotly internal functions using the developer console -const LOAD_MINIFIED = Ref(true) - -# ╔═╡ 16f4b455-086b-4a8b-8767-26fb00a77aad -md""" -# Utility Functions -""" - -# ╔═╡ 03bf1bc5-37a9-4b02-bff7-f8b42500c4fc -const JS = HypertextLiteral.JavaScript - -# ╔═╡ c3dcd4d9-5e57-4189-a7f8-524afd6db1e6 -md""" -## Hyperscript.content hack! -""" - -# ╔═╡ 6a4a5cc2-dca5-4f5d-a7e2-9b1f2fbaa406 -md""" -## ScriptContents struct -""" - -# ╔═╡ 441b20b3-ef9a-4d8a-a6b0-6b6be151a3dd -""" - ScriptContents -Wrapper around a vector of `HypertextLiteral.JavaScript` elements. It has a custom print implementation of `HypertextLiteral.print_script` in order to allow serialization of its various elements inside a script tag. - -It is used inside the PlutoPlot to allow modularity and ease customization of the script contents that is used to generate the plotlyjs plot in Javascript. -""" -struct ScriptContents - vec::Vector{JS} -end - -# ╔═╡ 271fd3a7-8347-407d-92d0-2d49758cb3f1 -function HypertextLiteral.print_script(io::IO, value::ScriptContents) - for el ∈ value.vec - print(io, el.content, '\n') - end -end - -# ╔═╡ 907d51fd-9aaf-43d0-a83b-879cae330a0b -md""" -## Plotly Version -""" - -# ╔═╡ 10da78b9-9a67-4cd8-9453-c01ea4baabeb -const PLOTLY_VERSION = Ref("2.16.1") - -# ╔═╡ ea88edae-c1a1-4cd3-95da-fd6d5cf337ff -function change_plotly_version(ver::String) - PLOTLY_VERSION[] = ver -end - -# ╔═╡ 5e5cf476-609c-4669-bf5d-c3fc3b75b2fe -get_plotly_version() = PLOTLY_VERSION[] - -# ╔═╡ 0c84069c-8362-43e7-8f0f-10f445ddc7fd -check_plotly_version() = @htl """ - -""" - -# ╔═╡ 83f2fe95-a6c2-42ec-ac86-72a4b4ec95c3 -md""" -## Mathjax hack -""" - -# ╔═╡ 4b1688b2-677e-41be-9446-e395925a7311 -const FORCE_MATHJAX_LOCAL = Ref(false) - -# ╔═╡ 49cf85b1-bf09-49de-8468-4e240dc621fa -""" - force_pluto_mathjax_local::Bool - force_pluto_mathjax_local(flag::Bool)::Bool - -Returns `true` if the `PlutoPlot` `show` method forces svgs produced by MathJax to be locally cached and `false` otherwise. - -The flag can be set at package level by providing the intended boolean value as argument to the function - -Local svg caching is used to make mathjax in recent plolty versions (>2.10) work as expected. The default `global` caching in Pluto creates problems with the math display. -""" -force_pluto_mathjax_local() = FORCE_MATHJAX_LOCAL[] - -# ╔═╡ 997f1421-b2f7-40c2-bc5b-f8a21cb4b04a -force_pluto_mathjax_local(flag::Bool) = FORCE_MATHJAX_LOCAL[] = flag - -# ╔═╡ e6b52b32-def4-4d71-80ca-e43530b1e704 -md""" -## Preprocess data -""" - -# ╔═╡ 77fe2c5d-f3dd-4779-92a4-e0ceadb639a9 -md""" -This function is basically `_json_lower` from PlotlyBase, but we do it directly on the PlutoPlot to avoid the modifying the behavior of `_json_lower` for `Plot` objects (which is required to modify how matrices are passed to `publish_to_js`) -""" - -# ╔═╡ 2380a265-700d-4fed-a52e-f6fa1ce41391 -# Defaults to JSON.lower for generic non-overloaded types -_preprocess(x) = PlotlyBase.JSON.lower(x) - -# ╔═╡ 65638528-ed6b-4e67-9cda-70390db81f64 -_preprocess(x::TimeType) = sprint(print, x) - -# ╔═╡ bc727ded-8675-420d-806e-0b49357118e5 -begin - _preprocess(x::Union{Bool,String,Number,Nothing,Missing}) = x - _preprocess(x::Symbol) = string(x) - _preprocess(x::Union{Tuple,AbstractArray}) = _preprocess.(x) - _preprocess(A::AbstractArray{<:Number, N}) where N = if N == 1 - collect(A) - else - [_preprocess(collect(s)) for s ∈ eachslice(A; dims = ndims(A))] - end - _preprocess(d::Dict) = Dict{Any,Any}(k => _preprocess(v) for (k, v) in pairs(d)) - _preprocess(a::PlotlyBase.HasFields) = Dict{Any,Any}(k => _preprocess(v) for (k, v) in pairs(a.fields)) - _preprocess(c::PlotlyBase.Cycler) = c.vals - function _preprocess(c::PlotlyBase.ColorScheme)::Vector{Tuple{Float64,String}} - N = length(c.colors) - map(ic -> ((ic[1] - 1) / (N - 1), _preprocess(ic[2])), enumerate(c.colors)) - end -end - -# ╔═╡ f9c0a331-1f1c-4648-9c24-5e9e16d6be18 -_preprocess(t::PlotlyBase.Template) = Dict( - :data => _preprocess(t.data), - :layout => _preprocess(t.layout) -) - -# ╔═╡ b0d77b4f-da8f-4a0b-a244-043b2e3bdfae -function _preprocess(pc::PlotlyBase.PlotConfig) - out = Dict{Symbol,Any}() - for fn in fieldnames(PlotlyBase.PlotConfig) - field = getfield(pc, fn) - if !isnothing(field) - out[fn] = field - end - end - out -end - -# ╔═╡ b8e1b177-6686-4b58-8c4c-991d9c148520 -# Escape latexstrings -_preprocess(s::LaTeXString) = s.s - -# ╔═╡ d1a08479-2814-4d1c-8af9-510aaa4c9089 -begin - # Process colors - _preprocess(c::Color) = @views begin - s = hex(c, :rrggbb) - r = parse(Int, s[1:2]; base = 16) - g = parse(Int, s[3:4]; base = 16) - b = parse(Int, s[5:6]; base = 16) - return "rgb($r,$g,$b)" - end - _preprocess(c::TransparentColor) = @views begin - s = hex(c, :rrggbbaa) - r = parse(Int, s[1:2]; base = 16) - g = parse(Int, s[3:4]; base = 16) - b = parse(Int, s[5:6]; base = 16) - a = parse(Int, s[7:8]; base = 16) - return "rgba($r,$g,$b,$(a/255))" - end -end - -# ╔═╡ a060a009-aee3-46f2-b70f-1811a27d06fa -md""" -## prepend\_cell\_selector -""" - -# ╔═╡ f15f831f-6418-4656-80e5-47057662552d -""" - prepend_cell_selector(selector="") - prepend_cell_selector(selectors) - -Prepends a CSS selector (represented by the argument `selector`) with a selector of the current pluto-cell (of the form `pluto-cell[id='cell_id']`, where `cell_id` is the currently running cell). - -It can be used to ease creating style sheets (using `@htl` from HypertextLiteral.jl) with selector that only apply to the cell where they are executed. - -When called with a vector of selectors as input, prepends each selector and joins them together using `,` as separator. - -`prepend_cell_selector("div") = pluto-cell[id='\$cell_id'] div` - -`prepend_cell_selector(["div", "span"]) = pluto-cell[id='\$cell_id'] div, pluto-cell[id='\$cell_id'] span` - -As example, one can create a plot and force its width to 400px in CSS by using the following snippet: -```julia -@htl \"\"\" -\$(plot(rand(10))) - -\"\"\" -``` -""" -prepend_cell_selector(str::AbstractString="")::String = "pluto-cell[id='$(current_cell_id())'] $str" |> strip - -# ╔═╡ 3f6c98c6-879f-48f3-9885-52e0cc99295a -prepend_cell_selector(selectors) = join(map(prepend_cell_selector, selectors), ",\n") - -# ╔═╡ 4e296bdd-cbd4-4d43-a769-0b4a80d7dec9 -md""" -## Unique Counter -""" - -# ╔═╡ 628c6e1f-03eb-43a2-8092-a2f61cf6bcbd -function unique_io_counter(io::IO, identifier = "script_id") - !get(io, :is_pluto, false) && return -1 # We simply return -1 if not inside pluto - # By default pluto inserts a dict inside the IOContext under key :extra_items. See https://github.com/fonsp/Pluto.jl/blob/10747db7ed512c6b3a9881c5cdb2a4daadea766d/src/runner/PlutoRunner.jl#L786 - dict = io.dict[:extra_items] - # The dict has key of type Tuple{ObjectID, Int64}, so we a custom key with our custom identifier where we will store the counter - key = (objectid(identifier), 0) - counter = get(dict, key, 0) + 1 - # Update the counter on the dict that is shared within this IOContext - dict[key] = counter -end - -# ╔═╡ ebcc9c42-9928-4a20-a307-02ee6ef726d0 -# Using the unique_io_counter inside the show3 method allows to have unique counters for plots within a same cell. -# This does not ensure that the same plot object is always given the same unique script id if the plots are added to the cells with `if...end` blocks. -function plotly_script_id(io::IO) - counter = unique_io_counter(io, "plotly-plot") - return "plot_$counter" -end - -# ╔═╡ fa975cb6-4ec1-419a-bcd6-527c0762a533 -md""" -# Plot Wrapper -""" - -# ╔═╡ 8b57581f-65b3-4edf-abe3-9dfa4ed82ed5 -md""" -We define a wrapper around the PlotlyBase.Plot object -""" - -# ╔═╡ f5491a94-5ea8-4459-b6ee-5d37f2ba6188 -md""" -## Default script contents -""" - -# ╔═╡ 92f5a728-6c57-47b7-9929-e2e19f91da2f -const _default_script_contents = htl_js.([ - """ - // Flag to check if this cell was manually ran or reactively ran - const firstRun = this ? false : true - const PLOT = this ?? document.createElement("div"); - const parent = currentScript.parentElement - const isPlutoWrapper = parent.classList.contains('raw-html-wrapper') - """, - """ - if (firstRun) { - // It seem plot divs would not autosize themself inside flexbox containers without this - parent.appendChild(PLOT) - } - """, - """ - // If width is not specified, set it to 100% - PLOT.style.width = plot_obj.layout.width ? "" : "100%" - - // For the height we have to also put a fixed value in case the plot is put on a non-fixed-size container (like the default wrapper) - PLOT.style.height = plot_obj.layout.height ? "" : - (isPlutoWrapper || parent.clientHeight == 0) ? "400px" : "100%" - """, - """ - - - PLOT.classList.forEach(cn => { - if (cn !== 'js-plotly-plot' && !custom_classlist.includes(cn)) { - PLOT.classList.toggle(cn, false) - } - }) - for (const className of custom_classlist) { - PLOT.classList.toggle(className, true) - } - """, - """ - - // Create the resizeObserver to make the plot even more responsive! :magic: - const resizeObserver = new ResizeObserver(entries => { - PLOT.style.height = plot_obj.layout.height ? "" : - (isPlutoWrapper || parent.clientHeight == 0) ? "400px" : "100%" - /* - The addition of the invalid argument `plutoresize` seems to fix the problem with calling `relayout` simply with `{autosize: true}` as update breaking mouse relayout events tracking. - See https://github.com/plotly/plotly.js/issues/6156 for details - */ - Plotly.relayout(PLOT, {..._.pick(PLOT.layout, ['width','height']), autosize: true, plutoresize: true}) - }) - - resizeObserver.observe(PLOT) - """, - """ - - Plotly.react(PLOT, plot_obj).then(() => { - // Assign the Plotly event listeners - for (const [key, listener_vec] of Object.entries(plotly_listeners)) { - for (const listener of listener_vec) { - PLOT.on(key, listener) - } - } - // Assign the JS event listeners - for (const [key, listener_vec] of Object.entries(js_listeners)) { - for (const listener of listener_vec) { - PLOT.addEventListener(key, listener) - } - } - } - ) - """, - """ - - invalidation.then(() => { - // Remove all plotly listeners - PLOT.removeAllListeners() - // Remove all JS listeners - for (const [key, listener_vec] of Object.entries(js_listeners)) { - for (const listener of listener_vec) { - PLOT.removeEventListener(key, listener) - } - } - // Remove the resizeObserver - resizeObserver.disconnect() - }) - """, -]) - -# ╔═╡ 656e982b-d805-4a75-b3e6-53a4444e5374 -md""" -## Struct definition -""" - -# ╔═╡ 0f088a21-7d5f-43f7-b99f-688338b61dc6 -begin -""" - PlutoPlot(p::Plot; kwargs...) - -A wrapper around `PlotlyBase.Plot` to provide optimized visualization within Pluto notebooks exploiting `@htl` from HypertextLiteral. - -# Fields -- `Plot::PlotlyBase.Plot` -- `plotly_listeners::Dict{String, Vector{HypertextLitera.JavaScript}}` -- `js_listeners::Dict{String, Vector{HypertextLitera.JavaScript}}` -- `classList::Vector{String}` -- `script_contents::ScriptContents` - -Once the wrapper has been created, the underlying `Plot` object can be accessed from the `Plot` field of the `PlutoPlot` object. - -Custom listeners to [plotly events](https://plotly.com/javascript/plotlyjs-events/) are saved inside the `plotly_listeners` field and can be added to the `PlutoPlot` as *javascript* functions using the [`add_plotly_listener!`](@ref) function. - -Custom listeners to normal javascript events can instead be added to the `PlutoPlot` as *javascript* functions using the [`add_js_listener!`](@ref) function. - -Multiple listeners can be associated to each event, and they are executed in the order they are added. - -A list of custom CSS classes can be added to the PlutoPlot by using the [`add_class!`](@ref) and [`remove_class!`](@ref) functions. - -Finally, the contents of the script tag generating the plot are stored in the field `script_contents` which is of type [`ScriptContents`](@ref). The elements of `script_contents` are written serially inside the javascript script tag. The displayed plot can be customized by modifying the elements of this field. - -# Examples -```julia -p = PlutoPlot(Plot(rand(10))) -add_plotly_listener!(p, "plotly_click", "e => console.log(e)") -add_class!(p, "custom_class") -``` - -See also: [`ScriptContents`](@ref), [`add_js_listener!`](@ref), [`add_plotly_listener!`](@ref) -""" -Base.@kwdef struct PlutoPlot - Plot::PlotlyBase.Plot - plotly_listeners::Dict{String, Vector{JS}} = Dict{String, Vector{JS}}() - js_listeners::Dict{String, Vector{JS}} = Dict{String, Vector{JS}}() - classList::Vector{String} = String[] - script_contents::ScriptContents = ScriptContents(deepcopy(_default_script_contents)) -end -PlutoPlot(p::PlotlyBase.Plot; kwargs...) = PlutoPlot(;kwargs..., Plot = p) -end - -# ╔═╡ 214cae09-fb98-4ca8-8475-62563e31f665 -# This is needed till next version of HypertextLiteral is out (current is 0.9.3). See https://github.com/JuliaPluto/HypertextLiteral.jl/issues/28 for details -HypertextLiteral.content(p::PlutoPlot) = HypertextLiteral.Render(p) - -# ╔═╡ 1686debe-6d74-4ec5-bf25-12346c8045c2 -function push_script!(p::PlutoPlot, items::Vararg{String,N}) where N - @nospecialize - push_script!(p, htl_js.(items)...) -end - -# ╔═╡ 64ce91b4-aaa3-45ec-b4d6-f24457167667 -function _preprocess(pp::PlutoPlot) - p = pp.Plot - out = Dict( - :data => _preprocess(p.data), - :layout => _preprocess(p.layout), - :frames => _preprocess(p.frames), - :config => _preprocess(p.config) - ) - - if templates.default !== "none" && PlotlyBase._isempty(get(out[:layout], :template, Dict())) - out[:layout][:template] = _preprocess(templates[templates.default]) - end - out -end - -# ╔═╡ 4ebd0ae4-9f4f-42b2-980e-a25550d01b6b -md""" -## Add plotly listeners -""" - -# ╔═╡ de2a547b-3ccd-4f56-96c0-81a7d9b2d272 -""" - add_plotly_listener!(p::PlutoPlot, event_name::String, listener::HypertextLiteral.JavaScript) - add_plotly_listener!(p::PlutoPlot, event_name::String, listener::String) - -Add a custom *javascript* `listener` (to be provided as `String` or directly as `HypertextLiteral.JavaScript`) to the `PlutoPlot` object `p`, and associated to the [plotly event](https://plotly.com/javascript/plotlyjs-events/) specified by `event_name`. - -The listeners are added to the HTML plot div after rendering. The div where the plot is inserted can be accessed using the variable named `PLOT` inside the listener code. - -# Differences with `add_js_listener!` -This function adds a listener using the plotly internal events via the `on` function. These events differ from the standard javascript ones and provide data specific to the plot. - -See also: [`add_js_listener!`](@ref), [`htl_js`](@ref) - -# Examples: -```julia -p = PlutoPlot(Plot(rand(10), Layout(uirevision = 1))) -add_plotly_listener!(p, "plotly_relayout", htl_js(\"\"\" -function(e) { - -console.log(PLOT) // logs the plot div inside the developer console - -} -\"\"\" -``` -""" -function add_plotly_listener!(p::PlutoPlot, event_name::String, listener::JS) - ldict = p.plotly_listeners - listeners_array = get!(ldict, event_name, JS[]) - push!(listeners_array, listener) - return p -end; - -# ╔═╡ 35e643ab-e3ea-427b-85f2-685b6b6103b8 -add_plotly_listener!(p::PlutoPlot, event_name, listener::String) = add_plotly_listener!(p, event_name, htl_js(listener)) - -# ╔═╡ 3cc48426-1c39-4afe-bf3e-7f4aa2197fca -md""" -## Add JS listeners -""" - -# ╔═╡ 67137f84-a284-4615-b7a8-729f0a412939 -""" - add_js_listener!(p::PlutoPlot, event_name::String, listener::HypertextLiteral.JavaScript) - add_js_listener!(p::PlutoPlot, event_name::String, listener::String) - -Add a custom *javascript* `listener` (to be provided as `String` or directly as `HypertextLiteral.JavaScript`) to the `PlutoPlot` object `p`, and associated to the javascript event specified by `event_name`. - -The listeners are added to the HTML plot div after rendering. The div where the plot is inserted can be accessed using the variable named `PLOT` inside the listener code. - -# Differences with `add_plotly_listener!` -This function adds standard javascript events via the `addEventListener` function. These events differ from the plotly specific events. - -See also: [`add_plotly_listener!`](@ref), [`htl_js`](@ref) - -# Examples: -```julia -p = PlutoPlot(Plot(rand(10), Layout(uirevision = 1))) -add_js_listener!(p, "mousedown", htl_js(\"\"\" -function(e) { - -console.log(PLOT) // logs the plot div inside the developer console when pressing down the mouse - -} -\"\"\" -``` -""" -function add_js_listener!(p::PlutoPlot, event_name::String, listener::JS) - ldict = p.js_listeners - listeners_array = get!(ldict, event_name, JS[]) - push!(listeners_array, listener) - return p -end; - -# ╔═╡ 83906aab-d4ac-4c2b-b7a4-718edb0c2a18 -add_js_listener!(p::PlutoPlot, event_name, listener::String) = add_js_listener!(p, event_name, htl_js(listener)) - -# ╔═╡ 0215aea2-eb79-449e-8dee-a32ca3c5d5f9 -md""" -## add_class! -""" - -# ╔═╡ 4c6a1004-52ca-40c1-915a-081c0a3c5fbf -""" - add_class!(p::PlutoPlot, className::String) - -Add a CSS class with name `className` to the list of custom classes that are added to the PLOT div when displayed inside Pluto. This can be used to give custom CSS styles to certain plots. - -See also: [`remove_class!`](@ref) -""" -function add_class!(p::PlutoPlot, className::String) - cl = p.classList - if className ∉ cl - push!(cl, className) - end - return p -end - -# ╔═╡ 87af8c6f-3d6d-44c6-87bb-588e01829339 -md""" -## remove_class! -""" - -# ╔═╡ 50077612-a858-48a0-a187-a9de1489f34f -""" - remove_class!(p::PlutoPlot, className::String) - -Remove a CSS class with name `className` (if present) from the list of custom classes that are added to the PLOT div when displayed inside Pluto. This can be used to give custom CSS styles to certain plots. - -See also: [`add_class!`](@ref) -""" -function remove_class!(p::PlutoPlot, className::String) - cl = p.classList - idx = findfirst(x -> x === className, cl) - if idx !== nothing - deleteat!(cl, idx) - end - return p -end - -# ╔═╡ 49fe75d5-844d-46d5-a251-7023706a7f92 -let - p = PlutoPlot(Plot()) - add_class!(p, "lol") - remove_class!(p, "lola") - p.classList -end - -# ╔═╡ 73290c37-481a-4f7d-a92d-038766702890 -md""" -# plot Method -""" - -# ╔═╡ c3fcc72a-389c-456a-aba5-cbfb4a798c9e -function plot(args...;kwargs...) - @nospecialize - PlutoPlot(Plot(args...;kwargs...)) -end - -# ╔═╡ 18e74f8f-39b6-4c8f-a06f-214d4e9dc6fb -plot(scatter(x = 1:10, y = rand(10)), Layout(title = "TITLE", template = "none")) - -# ╔═╡ 8a047414-cd5d-4491-a143-eb30578928ce -md""" -# Show Method -""" - -# ╔═╡ f9d1e69f-7a07-486d-b43a-334c1c77790a -function _show(pp::PlutoPlot, script_id = "pluto-plotly-div") -ver = htl_js(PLOTLY_VERSION[]) -suffix = htl_js(LOAD_MINIFIED[] ? "min.js" : "js") -@htl """ - -""" -end - -# ╔═╡ d42d4694-e05d-4e0e-a198-79a3a5cb688a -function Base.show(io::IO, mime::MIME"text/html", plt::PlutoPlot) - show(io, mime, _show(plt, plotly_script_id(io))) - # show(io, mime, _show(plt)) -end - -# ╔═╡ acba5003-a456-4c1a-a53f-71a3bec30251 -md""" -# Tests -""" - -# ╔═╡ 6d49055d-0347-4bce-a2c5-f1568596e2e6 -md""" -## Image -""" - -# ╔═╡ c56df6f6-865e-4af8-b8c0-429b021af1eb -md""" -This is testing the expected behavior of image traces as per [Issue #16](https://github.com/JuliaPluto/PlutoPlotly.jl/issues/16) -""" - -# ╔═╡ 464964d1-ada4-4ec3-8df1-eaeb8ea28e4a -# We check that the new definition works as the old one for matrices -let - a = rand(5,6) - t1 = _preprocess(a) == [collect(r) for r in eachcol(a)] -end - -# ╔═╡ 7ba8b496-f7c4-4fbc-a168-dc3d7af92d0c -image_issue = image(;z = rand(3,5,3)*255) - -# ╔═╡ f6ed3a0a-e548-4b99-9cf7-085991a5c99e -plot(image_issue) - -# ╔═╡ 0f9f50f8-95c8-4cb5-96f2-e4ce177ca2dd -Plot(image_issue) - -# ╔═╡ 359d22e8-b13d-420b-b409-b18136c3ff3b -md""" -## Colors.jl colors -""" - -# ╔═╡ e54cc4c4-2a93-4d44-90ed-5944edbf4b0f -let - red = scatter(;y=rand(10), line_color = RGB(1,0,0)) - green_transp = scatter(;y=rand(10), line_color = RGBA(0,1,0, .3)) - plot([red, green_transp]) -end - -# ╔═╡ 3e5b09a9-6d18-4d2f-a37b-ac260ea36646 -md""" -## Respect provided height/width -""" - -# ╔═╡ 070820c5-082f-4428-8d5e-1fdd1ce29eba -let - @htl """ -
- $(plot(rand(10), Layout( - width = 400, - #height= 400 - ))) -
- """ -end - -# ╔═╡ 0c30855c-6542-4b1a-9427-3a8427e75210 -md""" -## Slider + UIRevision -""" - -# ╔═╡ de0cb780-ff4e-4236-89c4-4c3163337cfc -@bind clk Clock() - -# ╔═╡ dd23fe10-a8d5-461a-85a8-e03468cdcd97 -@bind N Slider(50:50:250) -# N =let -# clk -# rand(50:100) -# end - -# ╔═╡ 6da3c910-a350-4a7e-b481-88942c97686b -""" - push_script!(p::PlutoPlot, items...) -Add script contents contained in collection `items` at the end of the plot show method script. -The `item` must either be a collection of `String` or `HypertextLiteral.JavaScript` elements -""" -function push_script!(p::PlutoPlot, items::Vararg{JS,N}) where N - @nospecialize - push!(p.script_contents.vec, items...) - return p -end - -# ╔═╡ fdc22972-b8aa-4202-bb4e-bfff92574814 -let - p = plot(rand(4)) - push_script!(p, "console.log('PUSHED')") -end - -# ╔═╡ f6a63433-553c-4857-b767-33465eb22934 -let - p = plot(rand(10,4)) - push_script!(p,htl_js("console.log('MAGIC PUSH SCRIPT')")) - p -end - -# ╔═╡ 8bf75ceb-e4ae-4c6c-8ab0-a81350f19bc7 -pp = Plot(scatter3d(x = rand(N), y = rand(N), z = rand(N), mode="markers"), Layout( - uirevision = 1, - scene = attr( - xaxis_range = [-1,2], - yaxis_range = [-1,2], - zaxis_range = [-1,2], - aspectmode = "cube", - ), - height = 550 - # autosize = true, -)); - -# ╔═╡ ccf62e33-8fcf-45d9-83ed-c7de80800b76 -let - p = PlutoPlot(pp) - add_plotly_listener!(p, "plotly_relayout", htl_js(""" - (e) => { - - console.log(e) - //console.log(PLOT._fullLayout._preGUI) - - - var eye = e['scene.camera']?.eye; - - if (eye) { - console.log('update: ', eye); - } else { - console.log(e) - } - console.log('div: ',PLOT._fullLayout.scene.camera.eye) - console.log('plot_obj: ',plot_obj.layout.scene?.camera?.eye) - -} - """)) - _show(p) -end - -# ╔═╡ 1460ece1-7828-4e93-ac37-e979b874b492 -md""" -## @bind click -""" - -# ╔═╡ 18c80ea2-0df4-40ea-bd87-f8fee463161e -@bind asdasd let - p = PlutoPlot(Plot(scatter(y = rand(10), name = "test", showlegend=true))) - add_plotly_listener!(p,"plotly_click", " - (e) => { - - console.log(e) - let dt = e.points[0] - PLOT.value = [dt.x, dt.y] - PLOT.dispatchEvent(new CustomEvent('input')) -} - ") - p -end - -# ╔═╡ ce29fa1f-0c52-4d38-acbd-0a96cb3b9ce6 -asdasd - -# ╔═╡ c3e29c94-941d-4a52-a358-c4ffbfc8cab8 -md""" -## @bind filtering -""" - -# ╔═╡ b0473b9a-2db5-4d03-8344-b8eaf8428d6c -points = [(rand(),rand()) for _ in 1:10000] - -# ╔═╡ 73945da3-af45-41fb-9c5d-6fbba6362256 -@bind limits let - p = Plot( - scatter(x = first.(points), y = last.(points), mode = "markers") - )|> PlutoPlot - add_plotly_listener!(p, "plotly_relayout", " - e => { - //console.log(e) - let layout = PLOT.layout - let asd = {xaxis: layout.xaxis.range, yaxis: layout.yaxis.range} - PLOT.value = asd - PLOT.dispatchEvent(new CustomEvent('input')) - } - ") -end - -# ╔═╡ ea9faecf-ecd7-483b-99ad-ede08ba05383 -visible_points = let - if ismissing(limits) - points - else - xrange = limits["xaxis"] - yrange = limits["yaxis"] - func(x,y) = x >= xrange[1] && x <= xrange[2] && y >= yrange[1] && y <= yrange[2] - filter(x -> func(x...), points) - end -end - -# ╔═╡ 684ef6d7-c1ae-4af3-b1bd-f54bc29d7b53 -length(visible_points) - -# ╔═╡ f8f7b530-1ded-4ce0-a7d9-a8c92afb95c7 -md""" -## Multiple Listeners -""" - -# ╔═╡ c3b1a198-ef19-4a54-9c32-d9ea32a63812 -let - p = PlutoPlot(Plot(rand(10), Layout(uirevision = 1))) - add_plotly_listener!(p, "plotly_relayout", htl_js(""" -function(e) { - - console.log('listener 1') - -} - """)) - add_plotly_listener!(p, "plotly_relayout", htl_js(""" -function(e) { - - console.log('listener 2') - -} - """)) - @htl "$p" -end - -# ╔═╡ e9fc2030-c2f0-48e9-a807-424039e796b2 -let - p = PlutoPlot(Plot(rand(10), Layout(uirevision = 1))) - add_plotly_listener!(p, "plotly_relayout", htl_js(""" -function(e) { - - console.log('listener 1') - -} - """)) - add_plotly_listener!(p, "plotly_relayout", htl_js(""" -function(e) { - - console.log('listener 2') - -} - """)) - p.plotly_listeners -end - -# ╔═╡ de101f40-27db-43ea-91ed-238502ceaaf7 -md""" -## JS Listener -""" - -# ╔═╡ 6c709fa0-7a53-4554-ab2a-d8181267ec93 -lololol = 1 - -# ╔═╡ 671296b9-6743-48d6-9c4d-1beac2b505b5 -let - lololol - p = PlutoPlot(Plot(rand(10), Layout(uirevision = 1))) - add_js_listener!(p, "mousedown", htl_js(""" -function(e) { - - console.log('MOUSEDOWN!') - -} - """)) -end - -# ╔═╡ 6128ff76-3f1f-4144-bb3d-f44678210013 -md""" -## flexbox -""" - -# ╔═╡ a5823eb2-3aaa-4791-bdc8-196eac2ccf2e -@htl """ -
-$(Plot(rand(10)) |> PlutoPlot) - -$(Plot(rand(10)) |> PlutoPlot) -
-""" - -# ╔═╡ b45cc21d-bfff-4375-a524-95108661a2ef -md""" -## flexbox + bind -""" - -# ╔═╡ 4ea48316-62d9-4b24-bb1d-c9fd1db044dc -@htl """ -
-$(@bind asd plot(rand(10))) - -$(plot(rand(10))) -
- -""" - -# ╔═╡ 62126774-e246-473b-9d0b-92e967cd36ac -md""" -## flexbox + PlutoUI.ExperimentalLayout.hbox -""" - -# ╔═╡ cfa78790-aa4c-4c7b-8a9f-198987338516 -# Without bonds, the plots resize automatically inside hbox -PlutoUI.ExperimentalLayout.hbox([plot(rand(10)), plot(rand(10))]) - -# ╔═╡ 340262a2-c823-4e19-8f19-9a05f4504bb5 -# When you wrap plots in bonds, the subplots do not automatically resize to the cell width -PlutoUI.ExperimentalLayout.hbox([(@bind asdasd2_val plot(rand(10))), plot(rand(10))]) - -# ╔═╡ 21682322-13a7-4575-8846-19e61fb8667d -# We need to specify flex-grow to allow resizing -html""" - -""" - -# ╔═╡ a7ef3dfc-d6ad-41c2-bccb-a794e26e80bb -PlutoUI.ExperimentalLayout.hbox([(@bind asdasd1_val plot(rand(10))), plot(rand(10))]; class="hbox_test") - -# ╔═╡ aaf0fe61-d5e6-4d93-8a22-7f97f1249b35 -md""" -## flexbox + uirevision -""" - -# ╔═╡ 6e12592d-01fe-455a-a19c-7544258b9791 -voila = 1 - -# ╔═╡ 36c4a5b1-03f2-4f5f-b9af-822a8f7c8cdf -let - voila - @htl """ -
-
-$(Plot(rand(10), Layout(uirevision = 1)) |> PlutoPlot) - -$(Plot(rand(10)) |> PlutoPlot) -
-
-$(Plot(rand(10)) |> PlutoPlot) - -$(Plot(rand(10)) |> PlutoPlot) -
-
-""" -end - -# ╔═╡ 38a81414-0bcd-4d71-af1d-fe154d2ae09a -md""" -## custom class -""" - -# ╔═╡ 2dd5534f-ce46-4770-b0f3-6e16005b3a90 -cl = ["test_css_class", "lol"] - -# ╔═╡ f69c6955-800c-461e-b464-cab4989913f6 -let - p = PlutoPlot(Plot(rand(10))) - for cn ∈ cl - add_class!(p, cn) - end - p -end - -# ╔═╡ bfe5f717-4702-4316-808a-726fefef9e7e -html""" - -""" - -# ╔═╡ cb3f5ee4-5504-4337-8a8d-d45784f54c85 -md""" -## Sphere -""" - -# ╔═╡ e0271a15-08b5-470f-a2d2-6f064cd3a2b2 -function sphere(radius, origin = [0,0,0]; N = 50) -u = range(0, 2π; length = N) -v = range(0, π, length = N) -x = [radius * cos(u) * sin(v) + origin[1] for u ∈ u, v ∈ v] -y = [radius * sin(u) * sin(v) + origin[2] for u ∈ u, v ∈ v] -z = [radius * cos(v) + origin[3] for u ∈ u, v ∈ v] - surface(;x,y,z) -end - -# ╔═╡ cb1f840f-8d99-4076-9554-7d8ba56e9865 -@bind M Slider(0:90) - -# ╔═╡ 22245242-80a6-4a5b-815e-39b469002f84 -let - s = sphere(1) - r = 2 - i = 0 - # M = 90 - x = [r*cosd(M)] - y = [r*sind(M)] - z = [0] - sat = scatter3d(;x,y,z, mode = "markers") - data = [s,sat] - plot(data, Layout( - uirevision = 1, - scene = attr( - xaxis_range = [-3,3], - yaxis_range = [-3,3], - zaxis_range = [-3,3], - aspectmode = "cube", - ), - )) -end - -# ╔═╡ ebdc0ebc-da58-49c8-a992-5924045c2cac -md""" -## Plot Dates -""" - -# ╔═╡ 6d74e3fa-1806-40b0-9995-c1555519603d -let - x = Date.(2000:2020) - y = rand(length(x)) - plot(scatter(;x,y,name = "values", showlegend = true), Layout( - template = "none" - )) -end - -# ╔═╡ 7054b9ce-cd00-42cf-b81c-bc27b29f4714 -md""" -## Heatmap/Contour tests -""" - -# ╔═╡ 5b5293bf-81b3-4e80-995a-f15f91971bd4 -md""" -This is testing the expected behavior of heatmap and contour plots as per [https://github.com/JuliaPlots/PlotlyJS.jl/issues/355](https://github.com/JuliaPlots/PlotlyJS.jl/issues/355) -""" - -# ╔═╡ 3612826c-0af0-4fef-aafe-112a2948f669 -let - plasma = [[0.0, "#0c0786"], - [0.1, "#40039c"], - [0.2, "#6a00a7"], - [0.3, "#8f0da3"], - [0.4, "#b02a8f"], - [0.5, "#cb4777"], - [0.6, "#e06461"], - [0.7, "#f2844b"], - [0.8, "#fca635"], - [0.9, "#fcce25"], - [1.0, "#eff821"]] - plot(heatmap(z=[1 24 30; 28 18 40; 36 54 7], colorscale=plasma), Layout( - template = "none", - yaxis = attr( - scaleanchor = "x", - scaleratio = 1, - ) - )) -end - -# ╔═╡ ce90eb60-98ed-4901-9218-ed8466bb03c7 -let - a, b = -1., 1. - c, d= 0., 1. - nx, ny= 200, 100 - hx = (b-a)/(nx-1) - x = a:hx:b - hy = (d-c)/(ny-1) - y=c:hy:d - X = x' .* ones(length(y)) - Y = ones(length(x))'.*y - Z= sin.(pi*pi*(X.*X+Y.*Y)); - # print(size(Z)) - plot(contour(x=x, y=y, z=Z), Layout(width=650, height=350, template = "none")) -end - -# ╔═╡ 2fa13939-eba2-4d25-b461-56be79fc1db6 -md""" -# Re-execute errored cells -""" - -# ╔═╡ 5a324fba-1033-4dcf-b10c-1fa4f231355c -md""" -Since we have functions defined at the bottom of the notebook, when first opening the notebook some of the tests above will error and would need to be re-executed after all the function definitions are loaded. - -To do this, we put at the bottom of the notebook a javascript function that re-executes all the errored cells -""" - -# ╔═╡ 9f2c0123-7e1a-43b7-861a-d059bb28f776 -# # Not really needed anymore after putting the tests at the bottom - -# @htl """ -# -# """ - -# ╔═╡ 00000000-0000-0000-0000-000000000001 -PLUTO_PROJECT_TOML_CONTENTS = """ -[deps] -AbstractPlutoDingetjes = "6e696c72-6542-2067-7265-42206c756150" -Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" -Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" -HypertextLiteral = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2" -LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" -PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8" - -[compat] -AbstractPlutoDingetjes = "~1.1.4" -Colors = "~0.12.8" -HypertextLiteral = "~0.9.3" -LaTeXStrings = "~1.3.0" -PlotlyBase = "~0.8.18" -PlutoUI = "~0.7.37" -""" - -# ╔═╡ 00000000-0000-0000-0000-000000000002 -PLUTO_MANIFEST_TOML_CONTENTS = """ -# This file is machine-generated - editing it directly is not advised - -[[AbstractPlutoDingetjes]] -deps = ["Pkg"] -git-tree-sha1 = "8eaf9f1b4921132a4cff3f36a1d9ba923b14a481" -uuid = "6e696c72-6542-2067-7265-42206c756150" -version = "1.1.4" - -[[ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.1" - -[[Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" - -[[Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[ColorSchemes]] -deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random"] -git-tree-sha1 = "12fc73e5e0af68ad3137b886e3f7c1eacfca2640" -uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.17.1" - -[[ColorTypes]] -deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597" -uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.11.0" - -[[Colors]] -deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40" -uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.12.8" - -[[CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.0.2+0" - -[[Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" - -[[DelimitedFiles]] -deps = ["Mmap"] -git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" -version = "1.9.1" - -[[DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.8.6" - -[[Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -version = "1.6.0" - -[[FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" - -[[FixedPointNumbers]] -deps = ["Statistics"] -git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" -uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.4" - -[[Hyperscript]] -deps = ["Test"] -git-tree-sha1 = "8d511d5b81240fc8e6802386302675bdf47737b9" -uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91" -version = "0.0.4" - -[[HypertextLiteral]] -git-tree-sha1 = "2b078b5a615c6c0396c77810d92ee8c6f470d238" -uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2" -version = "0.9.3" - -[[IOCapture]] -deps = ["Logging", "Random"] -git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a" -uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.2" - -[[InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.3" - -[[LaTeXStrings]] -git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996" -uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.3.0" - -[[LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.3" - -[[LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "7.84.0+0" - -[[LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" - -[[LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.10.2+0" - -[[Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[LinearAlgebra]] -deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" - -[[Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.2+0" - -[[Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" - -[[MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2022.10.11" - -[[NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.2.0" - -[[OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.21+4" - -[[OrderedCollections]] -git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.4.1" - -[[Parameters]] -deps = ["OrderedCollections", "UnPack"] -git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" -uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" -version = "0.12.3" - -[[Parsers]] -deps = ["Dates"] -git-tree-sha1 = "621f4f3b4977325b9128d5fae7a8b4829a0c2222" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.2.4" - -[[Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.9.0" - -[[PlotlyBase]] -deps = ["ColorSchemes", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"] -git-tree-sha1 = "180d744848ba316a3d0fdf4dbd34b77c7242963a" -uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5" -version = "0.8.18" - -[[PlutoUI]] -deps = ["AbstractPlutoDingetjes", "Base64", "ColorTypes", "Dates", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "UUIDs"] -git-tree-sha1 = "670e559e5c8e191ded66fa9ea89c97f10376bb4c" -uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8" -version = "0.7.38" - -[[Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - -[[Random]] -deps = ["SHA", "Serialization"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" - -[[SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" -version = "0.7.0" - -[[Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" - -[[SparseArrays]] -deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - -[[Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.9.0" - -[[SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "Pkg", "libblastrampoline_jll"] -uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "5.10.1+6" - -[[TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" - -[[Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" - -[[Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[UnPack]] -git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" -uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" -version = "1.0.2" - -[[Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" - -[[Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.13+0" - -[[libblastrampoline_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.8.0+0" - -[[nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.48.0+0" - -[[p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.4.0+0" -""" - -# ╔═╡ Cell order: -# ╠═7b8a4360-aaa4-11ec-2efc-b1778f6d3a8c -# ╠═810fb486-10b5-460f-a25a-1a7c9d84e256 -# ╠═676d318f-b4a4-4949-a5db-1c3a5fd9fa68 -# ╠═fc52e423-1370-4ca9-95dc-090815278a4a -# ╠═a9e7c04a-2d60-4727-9e24-a6dd9a0ee73c -# ╠═7bd46437-8af0-4a15-87e9-1508869e1600 -# ╠═4a18fa5d-7c73-468b-bed2-3acff51e3981 -# ╠═413ffc5c-c53f-48b0-922d-4c7f8718507c -# ╠═0ae3f943-4f9b-4cfb-aa76-3bcdc7dc9963 -# ╠═90fd960f-65b3-4d8c-b8a8-42d3be8c770f -# ╠═e9d43bc6-390e-43c3-becb-d1584202da41 -# ╟─16f4b455-086b-4a8b-8767-26fb00a77aad -# ╠═03bf1bc5-37a9-4b02-bff7-f8b42500c4fc -# ╟─c3dcd4d9-5e57-4189-a7f8-524afd6db1e6 -# ╠═214cae09-fb98-4ca8-8475-62563e31f665 -# ╟─6a4a5cc2-dca5-4f5d-a7e2-9b1f2fbaa406 -# ╠═441b20b3-ef9a-4d8a-a6b0-6b6be151a3dd -# ╠═271fd3a7-8347-407d-92d0-2d49758cb3f1 -# ╠═6da3c910-a350-4a7e-b481-88942c97686b -# ╠═1686debe-6d74-4ec5-bf25-12346c8045c2 -# ╠═fdc22972-b8aa-4202-bb4e-bfff92574814 -# ╟─907d51fd-9aaf-43d0-a83b-879cae330a0b -# ╠═10da78b9-9a67-4cd8-9453-c01ea4baabeb -# ╠═ea88edae-c1a1-4cd3-95da-fd6d5cf337ff -# ╠═5e5cf476-609c-4669-bf5d-c3fc3b75b2fe -# ╠═0c84069c-8362-43e7-8f0f-10f445ddc7fd -# ╟─83f2fe95-a6c2-42ec-ac86-72a4b4ec95c3 -# ╠═4b1688b2-677e-41be-9446-e395925a7311 -# ╠═49cf85b1-bf09-49de-8468-4e240dc621fa -# ╠═997f1421-b2f7-40c2-bc5b-f8a21cb4b04a -# ╟─e6b52b32-def4-4d71-80ca-e43530b1e704 -# ╟─77fe2c5d-f3dd-4779-92a4-e0ceadb639a9 -# ╠═2380a265-700d-4fed-a52e-f6fa1ce41391 -# ╠═65638528-ed6b-4e67-9cda-70390db81f64 -# ╠═bc727ded-8675-420d-806e-0b49357118e5 -# ╠═f9c0a331-1f1c-4648-9c24-5e9e16d6be18 -# ╠═b0d77b4f-da8f-4a0b-a244-043b2e3bdfae -# ╠═64ce91b4-aaa3-45ec-b4d6-f24457167667 -# ╠═b8e1b177-6686-4b58-8c4c-991d9c148520 -# ╠═d1a08479-2814-4d1c-8af9-510aaa4c9089 -# ╟─a060a009-aee3-46f2-b70f-1811a27d06fa -# ╠═f15f831f-6418-4656-80e5-47057662552d -# ╠═3f6c98c6-879f-48f3-9885-52e0cc99295a -# ╟─4e296bdd-cbd4-4d43-a769-0b4a80d7dec9 -# ╠═628c6e1f-03eb-43a2-8092-a2f61cf6bcbd -# ╠═ebcc9c42-9928-4a20-a307-02ee6ef726d0 -# ╟─fa975cb6-4ec1-419a-bcd6-527c0762a533 -# ╟─8b57581f-65b3-4edf-abe3-9dfa4ed82ed5 -# ╠═f5491a94-5ea8-4459-b6ee-5d37f2ba6188 -# ╠═92f5a728-6c57-47b7-9929-e2e19f91da2f -# ╟─656e982b-d805-4a75-b3e6-53a4444e5374 -# ╠═0f088a21-7d5f-43f7-b99f-688338b61dc6 -# ╟─4ebd0ae4-9f4f-42b2-980e-a25550d01b6b -# ╠═de2a547b-3ccd-4f56-96c0-81a7d9b2d272 -# ╠═35e643ab-e3ea-427b-85f2-685b6b6103b8 -# ╠═3cc48426-1c39-4afe-bf3e-7f4aa2197fca -# ╠═67137f84-a284-4615-b7a8-729f0a412939 -# ╠═83906aab-d4ac-4c2b-b7a4-718edb0c2a18 -# ╟─0215aea2-eb79-449e-8dee-a32ca3c5d5f9 -# ╠═4c6a1004-52ca-40c1-915a-081c0a3c5fbf -# ╟─87af8c6f-3d6d-44c6-87bb-588e01829339 -# ╠═50077612-a858-48a0-a187-a9de1489f34f -# ╠═49fe75d5-844d-46d5-a251-7023706a7f92 -# ╠═73290c37-481a-4f7d-a92d-038766702890 -# ╠═c3fcc72a-389c-456a-aba5-cbfb4a798c9e -# ╠═f6a63433-553c-4857-b767-33465eb22934 -# ╠═18e74f8f-39b6-4c8f-a06f-214d4e9dc6fb -# ╟─8a047414-cd5d-4491-a143-eb30578928ce -# ╠═f9d1e69f-7a07-486d-b43a-334c1c77790a -# ╠═d42d4694-e05d-4e0e-a198-79a3a5cb688a -# ╟─acba5003-a456-4c1a-a53f-71a3bec30251 -# ╟─6d49055d-0347-4bce-a2c5-f1568596e2e6 -# ╟─c56df6f6-865e-4af8-b8c0-429b021af1eb -# ╠═464964d1-ada4-4ec3-8df1-eaeb8ea28e4a -# ╠═7ba8b496-f7c4-4fbc-a168-dc3d7af92d0c -# ╠═f6ed3a0a-e548-4b99-9cf7-085991a5c99e -# ╠═0f9f50f8-95c8-4cb5-96f2-e4ce177ca2dd -# ╟─359d22e8-b13d-420b-b409-b18136c3ff3b -# ╠═e54cc4c4-2a93-4d44-90ed-5944edbf4b0f -# ╠═3e5b09a9-6d18-4d2f-a37b-ac260ea36646 -# ╠═070820c5-082f-4428-8d5e-1fdd1ce29eba -# ╟─0c30855c-6542-4b1a-9427-3a8427e75210 -# ╠═8bf75ceb-e4ae-4c6c-8ab0-a81350f19bc7 -# ╠═de0cb780-ff4e-4236-89c4-4c3163337cfc -# ╠═dd23fe10-a8d5-461a-85a8-e03468cdcd97 -# ╠═ccf62e33-8fcf-45d9-83ed-c7de80800b76 -# ╠═1460ece1-7828-4e93-ac37-e979b874b492 -# ╠═18c80ea2-0df4-40ea-bd87-f8fee463161e -# ╠═ce29fa1f-0c52-4d38-acbd-0a96cb3b9ce6 -# ╟─c3e29c94-941d-4a52-a358-c4ffbfc8cab8 -# ╠═b0473b9a-2db5-4d03-8344-b8eaf8428d6c -# ╠═73945da3-af45-41fb-9c5d-6fbba6362256 -# ╠═684ef6d7-c1ae-4af3-b1bd-f54bc29d7b53 -# ╠═ea9faecf-ecd7-483b-99ad-ede08ba05383 -# ╠═f8f7b530-1ded-4ce0-a7d9-a8c92afb95c7 -# ╠═c3b1a198-ef19-4a54-9c32-d9ea32a63812 -# ╠═e9fc2030-c2f0-48e9-a807-424039e796b2 -# ╟─de101f40-27db-43ea-91ed-238502ceaaf7 -# ╠═6c709fa0-7a53-4554-ab2a-d8181267ec93 -# ╠═671296b9-6743-48d6-9c4d-1beac2b505b5 -# ╟─6128ff76-3f1f-4144-bb3d-f44678210013 -# ╠═a5823eb2-3aaa-4791-bdc8-196eac2ccf2e -# ╟─b45cc21d-bfff-4375-a524-95108661a2ef -# ╠═4ea48316-62d9-4b24-bb1d-c9fd1db044dc -# ╟─62126774-e246-473b-9d0b-92e967cd36ac -# ╠═cfa78790-aa4c-4c7b-8a9f-198987338516 -# ╠═340262a2-c823-4e19-8f19-9a05f4504bb5 -# ╠═21682322-13a7-4575-8846-19e61fb8667d -# ╠═a7ef3dfc-d6ad-41c2-bccb-a794e26e80bb -# ╟─aaf0fe61-d5e6-4d93-8a22-7f97f1249b35 -# ╠═6e12592d-01fe-455a-a19c-7544258b9791 -# ╠═36c4a5b1-03f2-4f5f-b9af-822a8f7c8cdf -# ╟─38a81414-0bcd-4d71-af1d-fe154d2ae09a -# ╠═2dd5534f-ce46-4770-b0f3-6e16005b3a90 -# ╠═f69c6955-800c-461e-b464-cab4989913f6 -# ╠═bfe5f717-4702-4316-808a-726fefef9e7e -# ╟─cb3f5ee4-5504-4337-8a8d-d45784f54c85 -# ╠═e0271a15-08b5-470f-a2d2-6f064cd3a2b2 -# ╠═cb1f840f-8d99-4076-9554-7d8ba56e9865 -# ╠═22245242-80a6-4a5b-815e-39b469002f84 -# ╟─ebdc0ebc-da58-49c8-a992-5924045c2cac -# ╠═6d74e3fa-1806-40b0-9995-c1555519603d -# ╟─7054b9ce-cd00-42cf-b81c-bc27b29f4714 -# ╟─5b5293bf-81b3-4e80-995a-f15f91971bd4 -# ╠═3612826c-0af0-4fef-aafe-112a2948f669 -# ╠═ce90eb60-98ed-4901-9218-ed8466bb03c7 -# ╟─2fa13939-eba2-4d25-b461-56be79fc1db6 -# ╟─5a324fba-1033-4dcf-b10c-1fa4f231355c -# ╠═9f2c0123-7e1a-43b7-861a-d059bb28f776 -# ╟─00000000-0000-0000-0000-000000000001 -# ╟─00000000-0000-0000-0000-000000000002 diff --git a/src/PlutoPlotly.jl b/src/PlutoPlotly.jl index d9c9852..4cb26a1 100644 --- a/src/PlutoPlotly.jl +++ b/src/PlutoPlotly.jl @@ -3,17 +3,34 @@ module PlutoPlotly using Reexport @reexport using PlotlyBase -using PackageExtensionCompat +using HypertextLiteral +using AbstractPlutoDingetjes +using Dates +using BaseDirs +using TOML +using Colors +using LaTeXStrings +using Markdown +using Downloads: download -export PlutoPlot, get_plotly_version, change_plotly_version, check_plotly_version, force_pluto_mathjax_local, htl_js, add_plotly_listener!, add_class!, remove_class!, add_js_listener! +export PlutoPlot, get_plotly_version, change_plotly_version, +check_plotly_version, force_pluto_mathjax_local, htl_js, add_plotly_listener!, +add_class!, remove_class!, add_js_listener! export plot, push_script!, prepend_cell_selector -include("../notebooks/wrapper.jl") + +include("local_plotly_library.jl") + +include("main_struct.jl") +include("basics.jl") +include("mathjax.jl") +include("preprocess.jl") +include("js_helpers.jl") +include("show.jl") function __init__() # if !is_inside_pluto() # @warn "You loaded this package outside of Pluto, this is not the intended behavior and you should use either PlotlyBase or PlotlyJS directly.\nNOTE: If you receive this warning during pre-compilation or sysimage creation, you can ignore this warning." # end - @require_extensions end end \ No newline at end of file diff --git a/src/basics.jl b/src/basics.jl new file mode 100644 index 0000000..0890797 --- /dev/null +++ b/src/basics.jl @@ -0,0 +1,117 @@ +publish_to_js = if is_inside_pluto() + if isdefined(Main.PlutoRunner, :core_published_to_js) + Main.PlutoRunner.PublishedToJavascript + else + Main.PlutoRunner.publish_to_js + end +else + x -> x +end + +current_cell_id()::Base.UUID = if is_inside_pluto() + Main.PlutoRunner.currently_running_cell_id[] +else + Base.UUID(zero(UInt128)) +end + +function Base.show(io::IO, mime::MIME"text/html", s::JS) + if is_inside_pluto() + show(io, mime, Markdown.MD(Markdown.Code("js",s.content))) + else + show(io, MIME"text/plain",s) + end +end + + +## Plotly Version ## +function change_plotly_version(ver::String) + maybe_add_plotly_local(ver) + PLOTLY_VERSION[] = ver +end + +get_plotly_version() = PLOTLY_VERSION[] + +check_plotly_version() = @htl """ + +""" + +## Prepend Cell Selector ## +""" + prepend_cell_selector(selector="") + prepend_cell_selector(selectors) + +Prepends a CSS selector (represented by the argument `selector`) with a selector +of the current pluto-cell (of the form `pluto-cell[id='cell_id']`, where +`cell_id` is the currently running cell). + +It can be used to ease creating style sheets (using `@htl` from +HypertextLiteral.jl) with selector that only apply to the cell where they are +executed. + +When called with a vector of selectors as input, prepends each selector and +joins them together using `,` as separator. + +`prepend_cell_selector("div") = pluto-cell[id='\$cell_id'] div` + +`prepend_cell_selector(["div", "span"]) = pluto-cell[id='\$cell_id'] div, pluto-cell[id='\$cell_id'] span` + +As example, one can create a plot and force its width to 400px in CSS by using the following snippet: +```julia +@htl \"\"\" +\$(plot(rand(10))) + +\"\"\" +``` +""" +prepend_cell_selector(str::AbstractString="")::String = "pluto-cell[id='$(current_cell_id())'] $str" |> strip +prepend_cell_selector(selectors) = join(map(prepend_cell_selector, selectors), ",\n") + +const IO_DICT = Ref{Tuple{<:IO, Dict{UInt, Int}}}((IOBuffer(), Dict{UInt, Int}())) +function get_IO_DICT(io::IO) + old_io = first(IO_DICT[]) + dict = if old_io === io + last(IO_DICT[]) + else + d = Dict{UInt, Int}() + IO_DICT[] = (io, d) + d + end + return dict +end + +## Unique Counter ## +function unique_io_counter(io::IO, identifier = "script_id") + !get(io, :is_pluto, false) && return -1 # We simply return -1 if not inside pluto + # We extract (or create if not existing) a dictionary that will keep track of instances of the same script name + dict = get_IO_DICT(io) + # We use the objectid as the key + key = objectid(identifier) + counter = get(dict, key, 0) + 1 + # Update the counter on the dict that is shared within this IOContext + dict[key] = counter +end + +# Using the unique_io_counter inside the show3 method allows to have unique counters for plots within a same cell. +# This does not ensure that the same plot object is always given the same unique script id if the plots are added to the cells with `if...end` blocks. +function plotly_script_id(io::IO) + counter = unique_io_counter(io, "plotly-plot") + return "plot_$counter" +end \ No newline at end of file diff --git a/src/js_helpers.jl b/src/js_helpers.jl new file mode 100644 index 0000000..5cb497d --- /dev/null +++ b/src/js_helpers.jl @@ -0,0 +1,113 @@ +## add listeners ## +""" + add_js_listener!(p::PlutoPlot, event_name::String, listener::HypertextLiteral.JavaScript) + add_js_listener!(p::PlutoPlot, event_name::String, listener::String) + +Add a custom *javascript* `listener` (to be provided as `String` or directly as `HypertextLiteral.JavaScript`) to the `PlutoPlot` object `p`, and associated to the javascript event specified by `event_name`. + +The listeners are added to the HTML plot div after rendering. The div where the plot is inserted can be accessed using the variable named `PLOT` inside the listener code. + +# Differences with `add_plotly_listener!` +This function adds standard javascript events via the `addEventListener` function. These events differ from the plotly specific events. + +See also: [`add_plotly_listener!`](@ref), [`htl_js`](@ref) + +# Examples: +```julia +p = PlutoPlot(Plot(rand(10), Layout(uirevision = 1))) +add_js_listener!(p, "mousedown", htl_js(\"\"\" +function(e) { + +console.log(PLOT) // logs the plot div inside the developer console when pressing down the mouse + +} +\"\"\" +``` +""" +function add_js_listener!(p::PlutoPlot, event_name::String, listener::JS) + ldict = p.js_listeners + listeners_array = get!(ldict, event_name, JS[]) + push!(listeners_array, listener) + return p +end +add_js_listener!(p::PlutoPlot, event_name, listener::String) = add_js_listener!(p, event_name, htl_js(listener)) + +## add class ## +""" + add_class!(p::PlutoPlot, className::String) + +Add a CSS class with name `className` to the list of custom classes that are added to the PLOT div when displayed inside Pluto. This can be used to give custom CSS styles to certain plots. + +See also: [`remove_class!`](@ref) +""" +function add_class!(p::PlutoPlot, className::String) + cl = p.classList + if className ∉ cl + push!(cl, className) + end + return p +end + +## remove class ## + +""" + remove_class!(p::PlutoPlot, className::String) + +Remove a CSS class with name `className` (if present) from the list of custom classes that are added to the PLOT div when displayed inside Pluto. This can be used to give custom CSS styles to certain plots. + +See also: [`add_class!`](@ref) +""" +function remove_class!(p::PlutoPlot, className::String) + cl = p.classList + idx = findfirst(x -> x === className, cl) + if idx !== nothing + deleteat!(cl, idx) + end + return p +end + +## Push Script ## +""" + push_script!(p::PlutoPlot, items...) +Add script contents contained in collection `items` at the end of the plot show method script. +The `item` must either be a collection of `String` or `HypertextLiteral.JavaScript` elements +""" +function push_script!(p::PlutoPlot, items::Vararg{JS,N}) where N + @nospecialize + push!(p.script_contents.vec, items...) + return p +end + +## plotly listener ## +""" + add_plotly_listener!(p::PlutoPlot, event_name::String, listener::HypertextLiteral.JavaScript) + add_plotly_listener!(p::PlutoPlot, event_name::String, listener::String) + +Add a custom *javascript* `listener` (to be provided as `String` or directly as `HypertextLiteral.JavaScript`) to the `PlutoPlot` object `p`, and associated to the [plotly event](https://plotly.com/javascript/plotlyjs-events/) specified by `event_name`. + +The listeners are added to the HTML plot div after rendering. The div where the plot is inserted can be accessed using the variable named `PLOT` inside the listener code. + +# Differences with `add_js_listener!` +This function adds a listener using the plotly internal events via the `on` function. These events differ from the standard javascript ones and provide data specific to the plot. + +See also: [`add_js_listener!`](@ref), [`htl_js`](@ref) + +# Examples: +```julia +p = PlutoPlot(Plot(rand(10), Layout(uirevision = 1))) +add_plotly_listener!(p, "plotly_relayout", htl_js(\"\"\" +function(e) { + +console.log(PLOT) // logs the plot div inside the developer console + +} +\"\"\" +``` +""" +function add_plotly_listener!(p::PlutoPlot, event_name::String, listener::JS) + ldict = p.plotly_listeners + listeners_array = get!(ldict, event_name, JS[]) + push!(listeners_array, listener) + return p +end +add_plotly_listener!(p::PlutoPlot, event_name, listener::String) = add_plotly_listener!(p, event_name, htl_js(listener)) \ No newline at end of file diff --git a/src/local_plotly_library.jl b/src/local_plotly_library.jl new file mode 100644 index 0000000..f0ee444 --- /dev/null +++ b/src/local_plotly_library.jl @@ -0,0 +1,89 @@ +const DATA_FOLDER = BaseDirs.User.data("plutoplotly/") +isdir(DATA_FOLDER) || mkdir(DATA_FOLDER) +const VERSIONS_PATH = joinpath(DATA_FOLDER, "plotly_versions") +const VERSIONS_DICT = Ref( + try + TOML.tryparsefile(VERSIONS_PATH) + catch + Dict{String, Any}() + end + ) + +function pluto_server_folder() + is_inside_pluto() || return nothing + ml = methods(Main.PlutoRunner.embed_display) + m = first(ml) + plutorunner_path = string(m.file) + pluto_path = normpath(plutorunner_path, "../../..") +end +function maybe_put_plotly_in_pluto(v) + name = get_local_name(v) + pluto_path = pluto_server_folder() + pluto_path !== nothing || return false + maybe_add_plotly_local(v) + # We check whether the plotly library has been already loaded in this Pluto location, and we copy it otherwise + for subdir in ("frontend-dist", "frontend") + dist_path = joinpath(pluto_path, subdir) + isdir(dist_path) || (subdir === "frontend" ? error("Something went wrong") : continue) + file_path = joinpath(dist_path, "plotlyjs", "$name.min.js") + if !isfile(file_path) + isdir(joinpath(dist_path, "plotlyjs")) || mkdir(joinpath(dist_path, "plotlyjs")) + cp(get_local_path(v), file_path) + end + end + return true +end + +function update_versions_file() + open(VERSIONS_PATH, "w") do io + TOML.print(io, VERSIONS_DICT[]) + end +end +function get_esm_url(v) + v = string(v) + d = VERSIONS_DICT[] + url = if haskey(d, v) + d[v] + else + line = last(eachline(download("https://esm.sh/plotly.js-dist-min@$(v)"))) + parsed_url = "https://esm.sh$(match(r"\".*\"", line).match[2:end-1])" + d[v] = parsed_url + update_versions_file() + parsed_url + end + +end + +get_plotly_cdn_url(v) = "https://cdn.plot.ly/plotly-$(VersionNumber(v)).min.js" +get_local_pluto_src(v) = let + maybe_put_plotly_in_pluto(v) || error("Something wrong") + "./plotlyjs/$(get_local_name(v)).min.js" +end + +get_local_path(v) = joinpath(DATA_FOLDER, "$(get_local_name(v)).min.js") +get_local_name(v) = "plotlyjs-$(VersionNumber(v))" + +function maybe_add_plotly_local(v) + ver = VersionNumber(v) + # Check if the artifact already exists + path = get_local_path(v) + if !isfile(path) + # We download bundle and save locally + @info "Downloading a local version of plotly@$v" + base_url = get_esm_url(v) + bundle_url = replace(base_url, r".(\w+)$" => s".bundle.\1") + download(bundle_url, path) + end + nothing +end + +function get_plotly_src(v, force = "local") + if lowercase(string(force)) === "esm" + get_esm_url(v) + elseif lowercase(string(force)) === "cdn" + get_plotly_cdn_url(v) + elseif lowercase(string(force)) === "local" + get_local_pluto_src(v) + end +end + \ No newline at end of file diff --git a/src/main_struct.jl b/src/main_struct.jl new file mode 100644 index 0000000..99ff714 --- /dev/null +++ b/src/main_struct.jl @@ -0,0 +1,169 @@ +const PLOTLY_VERSION = Ref("2.25.2") +const JS = HypertextLiteral.JavaScript + +""" + ScriptContents +Wrapper around a vector of `HypertextLiteral.JavaScript` elements. It has a custom print implementation of `HypertextLiteral.print_script` in order to allow serialization of its various elements inside a script tag. + +It is used inside the PlutoPlot to allow modularity and ease customization of the script contents that is used to generate the plotlyjs plot in Javascript. +""" +struct ScriptContents + vec::Vector{JS} +end + +function HypertextLiteral.print_script(io::IO, value::ScriptContents) + for el ∈ value.vec + print(io, el.content, '\n') + end +end + +""" + htl_js(x) +Simple convenience constructor for `HypertextLiteral.JavaScript` objects, renamed and re-exported from HypertextLiteral for convenience in case HypertextLiteral is not explicitly loaded alongisde PlutoPlotly. + +See also: [`add_plotly_listeners!`](@ref) +""" +htl_js(x) = HypertextLiteral.JavaScript(x) + +const _default_script_contents = htl_js.([ + """ + // Flag to check if this cell was manually ran or reactively ran + const firstRun = this ? false : true + const PLOT = this ?? document.createElement("div"); + const parent = currentScript.parentElement + const isPlutoWrapper = parent.classList.contains('raw-html-wrapper') + """, + """ + if (firstRun) { + // It seem plot divs would not autosize themself inside flexbox containers without this + parent.appendChild(PLOT) + } + """, + """ + // If width is not specified, set it to 100% + PLOT.style.width = plot_obj.layout.width ? "" : "100%" + + // For the height we have to also put a fixed value in case the plot is put on a non-fixed-size container (like the default wrapper) + PLOT.style.height = plot_obj.layout.height ? "" : + (isPlutoWrapper || parent.clientHeight == 0) ? "400px" : "100%" + """, + """ + + + PLOT.classList.forEach(cn => { + if (cn !== 'js-plotly-plot' && !custom_classlist.includes(cn)) { + PLOT.classList.toggle(cn, false) + } + }) + for (const className of custom_classlist) { + PLOT.classList.toggle(className, true) + } + """, + """ + + // Create the resizeObserver to make the plot even more responsive! :magic: + const resizeObserver = new ResizeObserver(entries => { + PLOT.style.height = plot_obj.layout.height ? "" : + (isPlutoWrapper || parent.clientHeight == 0) ? "400px" : "100%" + /* + The addition of the invalid argument `plutoresize` seems to fix the problem with calling `relayout` simply with `{autosize: true}` as update breaking mouse relayout events tracking. + See https://github.com/plotly/plotly.js/issues/6156 for details + */ + Plotly.relayout(PLOT, {..._.pick(PLOT.layout, ['width','height']), autosize: true, plutoresize: true}) + }) + + resizeObserver.observe(PLOT) + """, + """ + + Plotly.react(PLOT, plot_obj).then(() => { + // Assign the Plotly event listeners + for (const [key, listener_vec] of Object.entries(plotly_listeners)) { + for (const listener of listener_vec) { + PLOT.on(key, listener) + } + } + // Assign the JS event listeners + for (const [key, listener_vec] of Object.entries(js_listeners)) { + for (const listener of listener_vec) { + PLOT.addEventListener(key, listener) + } + } + } + ) + """, + """ + + invalidation.then(() => { + // Remove all plotly listeners + PLOT.removeAllListeners() + // Remove all JS listeners + for (const [key, listener_vec] of Object.entries(js_listeners)) { + for (const listener of listener_vec) { + PLOT.removeEventListener(key, listener) + } + } + // Remove the resizeObserver + resizeObserver.disconnect() + }) + """, +]) + +""" + PlutoPlot(p::Plot; kwargs...) + +A wrapper around `PlotlyBase.Plot` to provide optimized visualization within +Pluto notebooks exploiting `@htl` from HypertextLiteral. + +# Fields +- `Plot::PlotlyBase.Plot` +- `plotly_listeners::Dict{String, Vector{HypertextLitera.JavaScript}}` +- `js_listeners::Dict{String, Vector{HypertextLitera.JavaScript}}` +- `classList::Vector{String}` +- `script_contents::ScriptContents` + +Once the wrapper has been created, the underlying `Plot` object can be accessed +from the `Plot` field of the `PlutoPlot` object. + +Custom listeners to [plotly +events](https://plotly.com/javascript/plotlyjs-events/) are saved inside the +`plotly_listeners` field and can be added to the `PlutoPlot` as *javascript* +functions using the [`add_plotly_listener!`](@ref) function. + +Custom listeners to normal javascript events can instead be added to the +`PlutoPlot` as *javascript* functions using the [`add_js_listener!`](@ref) +function. + +Multiple listeners can be associated to each event, and they are executed in the +order they are added. + +A list of custom CSS classes can be added to the PlutoPlot by using the +[`add_class!`](@ref) and [`remove_class!`](@ref) functions. + +Finally, the contents of the script tag generating the plot are stored in the +field `script_contents` which is of type [`ScriptContents`](@ref). The elements +of `script_contents` are written serially inside the javascript script tag. The +displayed plot can be customized by modifying the elements of this field. + +# Examples +```julia +p = PlutoPlot(Plot(rand(10))) +add_plotly_listener!(p, "plotly_click", "e => console.log(e)") +add_class!(p, "custom_class") +``` + +See also: [`ScriptContents`](@ref), [`add_js_listener!`](@ref), [`add_plotly_listener!`](@ref) +""" +Base.@kwdef struct PlutoPlot + Plot::PlotlyBase.Plot + plotly_listeners::Dict{String, Vector{JS}} = Dict{String, Vector{JS}}() + js_listeners::Dict{String, Vector{JS}} = Dict{String, Vector{JS}}() + classList::Vector{String} = String[] + script_contents::ScriptContents = ScriptContents(deepcopy(_default_script_contents)) +end +PlutoPlot(p::PlotlyBase.Plot; kwargs...) = PlutoPlot(;kwargs..., Plot = p) + +function plot(args...;kwargs...) + @nospecialize + PlutoPlot(Plot(args...;kwargs...)) +end \ No newline at end of file diff --git a/src/mathjax.jl b/src/mathjax.jl new file mode 100644 index 0000000..7998ab9 --- /dev/null +++ b/src/mathjax.jl @@ -0,0 +1,20 @@ +# This hack is necessary to force loading mathjax + +const FORCE_MATHJAX_LOCAL = Ref(false) + +""" + force_pluto_mathjax_local::Bool + force_pluto_mathjax_local(flag::Bool)::Bool + +Returns `true` if the `PlutoPlot` `show` method forces svgs produced by MathJax +to be locally cached and `false` otherwise. + +The flag can be set at package level by providing the intended boolean value as +argument to the function + +Local svg caching is used to make mathjax in recent plolty versions (>2.10) work +as expected. The default `global` caching in Pluto creates problems with the +math display. +""" +force_pluto_mathjax_local() = FORCE_MATHJAX_LOCAL[] +force_pluto_mathjax_local(flag::Bool) = FORCE_MATHJAX_LOCAL[] = flag \ No newline at end of file diff --git a/src/preprocess.jl b/src/preprocess.jl new file mode 100644 index 0000000..cd91563 --- /dev/null +++ b/src/preprocess.jl @@ -0,0 +1,77 @@ +#= +This function is basically `_json_lower` from PlotlyBase, but we do it directly +on the PlutoPlot to avoid the modifying the behavior of `_json_lower` for `Plot` +objects (which is required to modify how matrices are passed to `publish_to_js`) +=# + +# Main _preprocess for the PlutoPlot object +function _preprocess(pp::PlutoPlot) + p = pp.Plot + out = Dict( + :data => _preprocess(p.data), + :layout => _preprocess(p.layout), + :frames => _preprocess(p.frames), + :config => _preprocess(p.config) + ) + + if templates.default !== "none" && PlotlyBase._isempty(get(out[:layout], :template, Dict())) + out[:layout][:template] = _preprocess(templates[templates.default]) + end + out +end + +# Defaults to JSON.lower for generic non-overloaded types +_preprocess(x) = PlotlyBase.JSON.lower(x) # Default +_preprocess(x::TimeType) = sprint(print, x) # For handling datetimes + +_preprocess(x::Union{Bool,String,Number,Nothing,Missing}) = x +_preprocess(x::Symbol) = string(x) +_preprocess(x::Union{Tuple,AbstractArray}) = _preprocess.(x) +_preprocess(A::AbstractArray{<:Number, N}) where N = if N == 1 + collect(A) +else + [_preprocess(collect(s)) for s ∈ eachslice(A; dims = ndims(A))] +end +_preprocess(d::Dict) = Dict{Any,Any}(k => _preprocess(v) for (k, v) in pairs(d)) +_preprocess(a::PlotlyBase.HasFields) = Dict{Any,Any}(k => _preprocess(v) for (k, v) in pairs(a.fields)) +_preprocess(c::PlotlyBase.Cycler) = c.vals +function _preprocess(c::PlotlyBase.ColorScheme)::Vector{Tuple{Float64,String}} + N = length(c.colors) + map(ic -> ((ic[1] - 1) / (N - 1), _preprocess(ic[2])), enumerate(c.colors)) +end + +_preprocess(t::PlotlyBase.Template) = Dict( + :data => _preprocess(t.data), + :layout => _preprocess(t.layout) +) + +function _preprocess(pc::PlotlyBase.PlotConfig) + out = Dict{Symbol,Any}() + for fn in fieldnames(PlotlyBase.PlotConfig) + field = getfield(pc, fn) + if !isnothing(field) + out[fn] = field + end + end + out +end + +# Files that will be later moved to an extension. At the moment it's pointless because PlotlyBase uses those internally anyway. +_preprocess(s::LaTeXString) = s.s + +# Colors, they can be put inside an extension +_preprocess(c::Color) = @views begin + s = hex(c, :rrggbb) + r = parse(Int, s[1:2]; base = 16) + g = parse(Int, s[3:4]; base = 16) + b = parse(Int, s[5:6]; base = 16) + return "rgb($r,$g,$b)" +end +_preprocess(c::TransparentColor) = @views begin + s = hex(c, :rrggbbaa) + r = parse(Int, s[1:2]; base = 16) + g = parse(Int, s[3:4]; base = 16) + b = parse(Int, s[5:6]; base = 16) + a = parse(Int, s[7:8]; base = 16) + return "rgba($r,$g,$b,$(a/255))" +end \ No newline at end of file diff --git a/src/show.jl b/src/show.jl new file mode 100644 index 0000000..6fb5f95 --- /dev/null +++ b/src/show.jl @@ -0,0 +1,42 @@ +function _show(pp::PlutoPlot; script_id = "pluto-plotly-div", ver = PLOTLY_VERSION[]) +@htl """ + +""" +end + +# ╔═╡ d42d4694-e05d-4e0e-a198-79a3a5cb688a +function Base.show(io::IO, mime::MIME"text/html", plt::PlutoPlot) + show(io, mime, _show(plt; script_id = plotly_script_id(io))) + # show(io, mime, _show(plt)) +end \ No newline at end of file diff --git a/test/plotlykaleido.jl b/test/plotlykaleido.jl index 1233469..7549134 100644 --- a/test/plotlykaleido.jl +++ b/test/plotlykaleido.jl @@ -2,6 +2,8 @@ using PlutoPlotly using PlotlyKaleido using Test +PlotlyKaleido.start() + mktempdir() do dir cd() do p = plot(rand(10,4))