WGLMakie
|
该页面正在翻译中。 |
WGLMakie是基于web的后端,现在主要在Julia中实现。 WGLMakie使用https://github.com/SimonDanisch/Bonito.jl[鲣鱼]生成html和JavaScript以显示图。 在JavaScript方面,我们使用https://threejs.org/[ThreeJS]和https://en.wikipedia.org/wiki/WebGL[WebGL]渲染图。 将更多的实现迁移到JavaScript是目前的目标,它将为我们提供更好的JavaScript API,以及更多的交互,而无需运行Julia服务器。
|
警告WGLMakie可以被认为是实验性的,因为JavaScript API还不稳定,笔记本集成还不完美,但所有的情节类型都应该工作,因此所有的食谱,但有一些警告 |
浏览器支持
伊朱利亚
-
Bonito现在使用IJulia连接,因此即使在复杂的代理设置中也可以使用,而无需任何额外的设置
-
不支持重新加载页面,如果重新加载,则需要重新执行所有单元格并确保
页()被首先执行。
JupyterHub/Jupyterlab/活页夹
-
WGLMakie主要应该使用websocket连接。 鲣鱼试图https://github.com/SimonDanisch/Bonito.jl/blob/master/src/server-defaults.jl[推断代理设置]需要连接到julia进程。 在本地jupyterlab实例上,这应该没问题。 在托管实例上,可能需要https://jupyter-server-proxy.readthedocs.io/en/latest/arbitrary-ports-hosts.html#with-jupyterhub[脧锚脧赂`jupyter-服务器-代理`]安装,然后执行类似
页面(;listen_port=9091,proxy_url="<jhub-instance>.com/user/<username>/proxy/9091"). 另请参阅: 第2464期 第2405期
冥王星
-
仍然使用Bonito的Websocket连接,因此需要为远程服务器进行额外的设置。
-
不支持重新加载页面,如果重新加载,则需要重新执行所有单元格并确保
页()被首先执行。 -
静态html导出尚未完全工作
朱利亚胡布
-
浏览器中的VSCode应该开箱即用。
-
朱利亚胡布的冥王星仍然有一个https://github.com/SimonDanisch/Bonito.jl/issues/140[问题]与WebSocket连接。 所以,你会看到一个情节,但互动不起作用。
浏览器支持
有些浏览器可能只有Webgl1.0,或者需要额外的步骤来启用WebGL,但一般来说,所有现代浏览器都在https://www.lambdatest.com/web-technologies/webgl2[移动和桌面应支持WebGL2.0]。 Safari用户可能需要https://discussions.apple.com/thread/8655829[启用]WebGL,虽然。 如果您最终停留在WebGL1.0上,则主要缺少的功能将是 卷数 & 轮廓(体积).
激活和屏幕配置
通过调用激活后端 WGLMakie.启动!() 具有以下选项:
#
<无翻译>*WGLMakie.启动!*-Function
WGLMakie.activate!(; screen_config...)
将WGLMakie设置为当前活动的后端,并允许快速设置 屏幕_config. 请注意, 屏幕_config 也可以通过永久设置 Makie。set_theme!(WGLMakie=(screen_config。..,)).
可以通过的参数 屏幕_config:
-
帧速率=30:将帧速率(每秒帧数)设置为更高的数字以获得更流畅的动画,或设置为更低的数字以使用更少的资源。 -
resize_to=无:调整画布到父元素的大小resize_to=:父,或身体,如果resize_to=:身体. 默认值什么都没有,将调整什么。 元组也是允许的,只有宽度/高度的值是相同的。
输出
您可以在Pluto,IJulia,Webpages和Documenter中使用Bonito和WGLMakie创建交互式应用程序和仪表板,在实时网页上提供它们,或将它们导出为静态HTML。
本教程将贯穿不同的模式和期望的限制。
页面
页() 可以用来重置多页输出所需的鲣鱼状态,就像它的情况一样 文件中心 或各种笔记本(Ijulia/冥王星/等)。 以前,有必要始终插入并显示 页面 打电话给笔记本,但现在打电话给 页() 是可选的,不需要显示。 它所做的是纯粹重置新的多页输出的状态,这通常是针对 文件中心,它在一个Julia会话中创建多个页面,或者您可以使用它来重置笔记本中的状态,例如在页面重新加载后。 页面(可导出=true,离线=true) 可用于强制内联所有数据和js依赖关系,以便所有内容都可以在单个HTML对象中加载,而无需运行Julia进程。 默认值应该已经被这样选择,例如文档中心,所以这应该主要用于例如。 冥王星 离线导出(目前不完全支持,但应该很快)。
以下是如何在Franklin中使用this的示例:
using WGLMakie
using Bonito, Markdown
Page() # for Franklin, you still need to configure
WGLMakie.activate!()
Makie.inline!(true) # Make sure to inline plots into Documenter output!
scatter(1:4, color=1:4)
正如你所看到的,输出是完全静态的,因为我们没有一个正在运行的Julia服务器,就像Pluto一样。 为了使情节具有交互性,我们需要在JS中编写WGLMakie的更多部分,这是一项持续的努力。 正如你所看到的,交互性已经继续为3D工作:
N = 60
function xy_data(x, y)
r = sqrt(x^2 + y^2)
r == 0.0 ? 1f0 : (sin(r)/r)
end
l = range(-10, stop = 10, length = N)
z = Float32[xy_data(x, y) for x in l, y in l]
surface(
-1..1, -1..1, z,
colormap = :Spectral
)
有几种方法可以在静态导出中保持与绘图交互。
记录状态图
Bonito允许为满足以下接口的所有小部件记录statemap:
# must be true to be found inside the DOM
is_widget(x) = true
# Updating the widget isn't dependent on any other state (only thing supported right now)
is_independant(x) = true
# The values a widget can iterate
function value_range end
# updating the widget with a certain value (usually an observable)
function update_value!(x, value) end
目前,只有滑块重载接口:
using Observables
App() do session::Session
n = 10
index_slider = Slider(1:n)
volume = rand(n, n, n)
slice = map(index_slider) do idx
return volume[:, :, idx]
end
fig = Figure()
ax, cplot = contour(fig[1, 1], volume)
rectplot = linesegments!(ax, Rect(-1, -1, 12, 12), linewidth=2, color=:red)
on(index_slider) do idx
translate!(rectplot, 0,0,idx)
end
heatmap(fig[1, 2], slice)
slider = DOM.div("z-index: ", index_slider, index_slider.value)
return Bonito.record_states(session, DOM.div(slider, fig))
end
直接执行Javascript
Bonito可以轻松构建整个HTML和JS应用程序。 例如,您可以直接注册在更改时运行的JavaScript函数。
using Bonito
App() do session::Session
s1 = Slider(1:100)
slider_val = DOM.p(s1[]) # initialize with current value
# call the `on_update` function whenever s1.value changes in JS:
onjs(session, s1.value, js"""function on_update(new_value) {
//interpolating of DOM nodes and other Julia values work mostly as expected:
const p_element = $(slider_val)
p_element.innerText = new_value
}
""")
return DOM.div("slider 1: ", s1, slider_val)
end
也可以将图插值到JS中,并通过JS更新这些图。 问题是,还没有一个惊人的界面。 返回的对象直接是一个三个对象,所有的plot属性都转换为Javascript类型。 好消息是,所有属性都应该在 三烯。材料。制服,或 三烯。几何。属性. 接下来,我们应该在WGLMakie中创建一个API,这使得它像在Julia中一样简单: 情节。属性=值. 但是,虽然这还没有到位,但记录返回的对象可以很容易地弄清楚该怎么做-顺便说一句,JS控制台+日志记录是惊人的,并且可以很容易地在记录对象后玩
using Bonito: on_document_load
using WGLMakie
App() do session::Session
s1 = Slider(1:100)
slider_val = DOM.p(s1[]) # initialize with current value
fig, ax, splot = scatter(1:4)
# With on_document_load one can run JS after everything got loaded.
# This is an alternative to `evaljs`, which we can't use here,
# since it gets run asap, which means the plots won't be found yet.
on_document_load(session, js"""
// you get a promise for an array of plots, when interpolating into JS:
$(splot).then(plots=>{
// just one plot for atomics like scatter, but for recipes it can be multiple plots
const scatter_plot = plots[0]
// open the console with ctr+shift+i, to inspect the values
// tip - you can right click on the log and store the actual variable as a global, and directly interact with it to change the plot.
console.log(scatter_plot)
console.log(scatter_plot.material.uniforms)
console.log(scatter_plot.geometry.attributes)
})
""")
# with the above, we can find out that the positions are stored in `offset`
# (*sigh*, this is because threejs special cases `position` attributes so it can't be used)
# Now, lets go and change them when using the slider :)
onjs(session, s1.value, js"""function on_update(new_value) {
$(splot).then(plots=>{
const scatter_plot = plots[0]
// change first point x + y value
scatter_plot.geometry.attributes.pos.array[0] = (new_value/100) * 4
scatter_plot.geometry.attributes.pos.array[1] = (new_value/100) * 4
// this always needs to be set of geometry attributes after an update
scatter_plot.geometry.attributes.pos.needsUpdate = true
})
}
""")
# and for got measures, add a slider to change the color:
color_slider = Slider(LinRange(0, 1, 100))
onjs(session, color_slider.value, js"""function on_update(hue) {
$(splot).then(plots=>{
const scatter_plot = plots[0]
const color = new THREE.Color()
color.setHSL(hue, 1.0, 0.5)
scatter_plot.material.uniforms.color.value.x = color.r
scatter_plot.material.uniforms.color.value.y = color.g
scatter_plot.material.uniforms.color.value.z = color.b
})
}""")
markersize = Slider(1:100)
onjs(session, markersize.value, js"""function on_update(size) {
$(splot).then(plots=>{
const scatter_plot = plots[0]
scatter_plot.material.uniforms.markersize.value.x = size
scatter_plot.material.uniforms.markersize.value.y = size
})
}""")
return DOM.div(s1, color_slider, markersize, fig)
end
这总结了静态页面中与WGLMakie交互的当前状态。
离线工具提示
麦琪数据探测仪 与WGLMakie工作得很好,但它需要一个正在运行的Julia进程来显示和更新工具提示。
还有一种方法可以直接在Javascript中显示工具提示,它需要插入到HTML dom中。 这意味着,我们实际上需要使用 鲣鱼。应用程序 返回 多姆 对象:
App() do session
f, ax, pl = scatter(1:4, markersize=100, color=Float32[0.3, 0.4, 0.5, 0.6])
custom_info = ["a", "b", "c", "d"]
on_click_callback = js"""(plot, index) => {
// the plot object is currently just the raw THREEJS mesh
console.log(plot)
// Which can be used to extract e.g. position or color:
const {pos, color} = plot.geometry.attributes
console.log(pos)
console.log(color)
const x = pos.array[index*2] // everything is a flat array in JS
const y = pos.array[index*2+1]
const c = Math.round(color.array[index] * 10) / 10 // rounding to a digit in JS
const custom = $(custom_info)[index]
// return either a string, or an HTMLNode:
return "Point: <" + x + ", " + y + ">, value: " + c + " custom: " + custom
}
"""
# ToolTip(figurelike, js_callback; plots=plots_you_want_to_hover)
tooltip = WGLMakie.ToolTip(f, on_click_callback; plots=pl)
return DOM.div(f, tooltip)
end
冥王星/伊朱利亚
请注意,只要Julia会话正在运行,Makie的正常交互性就会在例如Pluto中保留WGLMakie。 这给我们带来了设置冥王星/伊朱利亚会议! 在本地,WGLMakie应该只是为Pluto/IJulia开箱即用,但如果您从另一台PC访问笔记本电脑,则必须设置如下内容:
begin
using Bonito
some_forwarded_port = 8080
Page(listen_url="0.0.0.0", listen_port=some_forwarded_port)
end
或者还指定代理URL,如果您有更复杂的代理设置。 有关更高级的设置,请参阅 ?页面 文件及 鲣鱼。configure_server!. 在 headless文档,您还可以阅读有关设置Bonito服务器和端口转发的更多信息。
造型设计
Bonito允许加载任意css,并且 多姆。xxx 包装所有现有的HTML标签。 所以任何CSS文件都可以使用,例如,甚至像https://tailwindcss.com/[顺风]带 资产:
TailwindCSS = Bonito.Asset("/path/to/tailwind.min.css")
鲣鱼还提供 款式 类型,它允许定义整个样式表并将它们分配给任何DOM对象。 这就是鲣鱼如何创建样式化的组件:
Rows(args...) = DOM.div(args..., style=Styles(
"display" => "grid",
"grid-template-rows" => "fr",
"grid-template-columns" => "repeat($(length(args)), fr)",
))
这个样式对象在一个会话中只会被插入一次DOM,后续的使用只会给div一个相同的类。
请注意,鲣鱼已经定义了类似上述的东西 行:
using Colors
using Bonito
App() do session::Session
hue_slider = Slider(0:360)
color_swatch = DOM.div(class="h-6 w-6 p-2 m-2 rounded shadow")
onjs(session, hue_slider.value, js"""function (hue){
$(color_swatch).style.backgroundColor = "hsl(" + hue + ",60%,50%)"
}""")
return Row(hue_slider, color_swatch)
end
Bonito还提供了一个styleable卡组件:
using Markdown
App() do session::Session
# We can now use this wherever we want:
fig = Figure(size=(300, 300))
contour(fig[1,1], rand(4,4))
card = Card(Grid(
Centered(DOM.h1("Hello"); style=Styles("grid-column" => "1 / 3")),
StylableSlider(1:100; style=Styles("grid-column" => "1 / 3")),
DOM.img(src="https://julialang.org/assets/infra/logo.svg"),
fig; columns="1fr 1fr", justify_items="stretch"
))
# Markdown creates a DOM as well, and you can interpolate
# arbitrary jsrender'able elements in there:
return DOM.div(card)
end
希望随着时间的推移,会有很多像上面这样的风格化元素的帮助库,用Bonito+WGLMakie制作华丽的仪表板。
出口
Documenter只是将plots+页面呈现为html,因此如果您想将WGLMakie/Bonito对象内联到您自己的页面中,可以使用如下所示:
using WGLMakie, Bonito, FileIO
WGLMakie.activate!()
open("index.html", "w") do io
println(io, """
<html>
<head>
</head>
<body>
""")
Page(exportable=true, offline=true)
# Then, you can just inline plots or whatever you want :)
# Of course it would make more sense to put this into a single app
app = App() do
C(x;kw...) = Card(x; height="fit-content", width="fit-content", kw...)
figure = (; size=(300, 300))
f1 = scatter(1:4; figure)
f2 = mesh(load(assetpath("brain.stl")); figure)
C(DOM.div(
Bonito.StylableSlider(1:100),
Row(C(f1), C(f2))
); padding="30px", margin="15px")
end
show(io, MIME"text/html"(), app)
# or anything else from Bonito, or that can be displayed as html:
println(io, """
</body>
</html>
""")
end