From 8d743b74caf2270c50b32a1a2938087624f3a501 Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Sun, 9 Feb 2020 22:27:17 -0500 Subject: [PATCH 1/8] Remove WebIO dependency and build directly on Mux/HTTP --- .gitignore | 2 +- Project.toml | 10 +- notebooks/animation.ipynb | 13 +-- notebooks/demo.ipynb | 12 +-- src/MeshCat.jl | 9 +- src/commands.jl | 19 ---- src/ijulia.jl | 22 +++++ src/lowering.jl | 25 ----- src/servers.jl | 66 +++++-------- src/visualizer.jl | 196 +++++++++++++++++++++----------------- 10 files changed, 170 insertions(+), 204 deletions(-) create mode 100644 src/ijulia.jl diff --git a/.gitignore b/.gitignore index 5d454ed..9abf692 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .ipynb_checkpoints deps/build.log -Manifest.toml +/Manifest.toml diff --git a/Project.toml b/Project.toml index 382df73..6f7aba4 100644 --- a/Project.toml +++ b/Project.toml @@ -4,18 +4,16 @@ authors = ["Robin Deits "] version = "0.9.1" [deps] -AssetRegistry = "bf4720bc-e11a-5d0c-854e-bdca1663c893" Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" BinDeps = "9e28174c-4ba2-5203-b857-d8d62c4213ee" -Blink = "ad839575-38b3-5650-b840-f874b8c74a25" Cassette = "7057c7e9-c182-5462-911a-8362d720325c" Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" CoordinateTransformations = "150eb455-5306-5404-9cee-2592286d6298" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" FFMPEG = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" GeometryTypes = "4d00f742-c7ba-57c2-abde-4428a4b178cb" -JSExpr = "97c1335a-c9c5-57fe-bc5d-ec35cebe8660" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" MsgPack = "99f44e22-a591-53d1-9472-aa23ef4bd671" Mux = "a975b10e-0019-58db-a62f-e48ff68538c9" Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" @@ -24,26 +22,22 @@ Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc" Sockets = "6462fe0b-24de-5631-8697-dd941f90decc" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -WebIO = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" +WebSockets = "104b5d7c-a370-577a-8038-80a2059c5097" [compat] -AssetRegistry = "0.1" BinDeps = "1" -Blink = "0.12" Cassette = "0.2.5" Colors = "0.9, 0.10, 0.11" CoordinateTransformations = "0.5" DocStringExtensions = "0.5, 0.6, 0.7, 0.8" FFMPEG = "0.2" GeometryTypes = "0.6, 0.7" -JSExpr = "0.3, 0.5" MsgPack = "1" Mux = "0.7" Parameters = "0.10, 0.11, 0.12" Requires = "0.5, 1" Rotations = "0.8, 0.9, 0.10, 0.11, 0.13" StaticArrays = "0.10, 0.11, 0.12" -WebIO = "0.8.11" julia = "0.7, 1" [extras] diff --git a/notebooks/animation.ipynb b/notebooks/animation.ipynb index 1ca691b..4a14ffc 100644 --- a/notebooks/animation.ipynb +++ b/notebooks/animation.ipynb @@ -219,13 +219,6 @@ "source": [ "# MeshCat.convert_frames_to_video(\"/home/rdeits/Downloads/meshcat_1528401494656.tar\", overwrite=true)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -234,15 +227,15 @@ "lastKernelId": null }, "kernelspec": { - "display_name": "Julia 1.1.0", + "display_name": "Julia 1.3.1", "language": "julia", - "name": "julia-1.1" + "name": "julia-1.3" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", - "version": "1.1.0" + "version": "1.3.1" } }, "nbformat": 4, diff --git a/notebooks/demo.ipynb b/notebooks/demo.ipynb index e3f30bc..a13dbfb 100644 --- a/notebooks/demo.ipynb +++ b/notebooks/demo.ipynb @@ -436,7 +436,7 @@ "outputs": [], "source": [ "using GeometryTypes: Point3f0\n", - "points = rand(Point3f0, 100000)\n", + "points = rand(Point3f0, 1000000)\n", "setobject!(vis[:pointcloud], PointCloud(points))" ] }, @@ -531,19 +531,19 @@ ], "metadata": { "@webio": { - "lastCommId": "057909cff550485386c7979bb5f0930e", - "lastKernelId": "81aa3ac9-8239-4690-9c92-5615ab42195e" + "lastCommId": null, + "lastKernelId": null }, "kernelspec": { - "display_name": "Julia 1.1.0", + "display_name": "Julia 1.3.1", "language": "julia", - "name": "julia-1.1" + "name": "julia-1.3" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", - "version": "1.1.0" + "version": "1.3.1" } }, "nbformat": 4, diff --git a/src/MeshCat.jl b/src/MeshCat.jl index 81a568d..f942057 100644 --- a/src/MeshCat.jl +++ b/src/MeshCat.jl @@ -2,9 +2,7 @@ __precompile__() module MeshCat -using WebIO import Mux -import AssetRegistry import Cassette import FFMPEG using GeometryTypes, CoordinateTransformations @@ -14,7 +12,6 @@ using StaticArrays: StaticVector, SVector, SDiagonal, SMatrix using GeometryTypes: raw using Parameters: @with_kw using DocStringExtensions: SIGNATURES -using JSExpr: @js, @new, @var using Requires: @require using Base.Filesystem: rm using BinDeps: download_cmd, unpack_cmd @@ -87,6 +84,7 @@ include("msgpack.jl") include("visualizer.jl") include("atframe.jl") include("arrow_visualizer.jl") +include("ijulia.jl") include("servers.jl") const VIEWER_ROOT = joinpath(@__DIR__, "..", "assets", "meshcat", "dist") @@ -111,8 +109,6 @@ function develop_meshcat_assets(skip_confirmation=false) rm(joinpath(meshcat_dir, "..", "meshcat.stamp")) end -const ASSET_KEYS = String[] - function __init__() main_js = abspath(joinpath(VIEWER_ROOT, "main.min.js")) if !isfile(main_js) @@ -120,14 +116,13 @@ function __init__() main.min.js not found at $main_js. Please build MeshCat using `import Pkg; Pkg.build("MeshCat")`""") end - push!(ASSET_KEYS, AssetRegistry.register(main_js)) @require Blink="ad839575-38b3-5650-b840-f874b8c74a25" begin function Base.open(core::CoreVisualizer, w::Blink.AtomShell.Window) # Ensure the window is ready Blink.js(w, "ok") # Set its contents - Blink.body!(w, core.scope) + Blink.loadurl(w, url(core)) w end end diff --git a/src/commands.jl b/src/commands.jl index b150961..7edb391 100644 --- a/src/commands.jl +++ b/src/commands.jl @@ -20,25 +20,6 @@ struct SetProperty{T} <: AbstractCommand value::T end -abstract type AbstractControl end - -struct Button <: AbstractControl - observer::Observable - name::String -end - -struct NumericControl{T} <: AbstractControl - observer::Observable - name::String - value::T - min::T - max::T -end - -struct SetControl <: AbstractCommand - control::AbstractControl -end - struct SetAnimation{A <: Animation} <: AbstractCommand animation::A play::Bool diff --git a/src/ijulia.jl b/src/ijulia.jl new file mode 100644 index 0000000..14304b3 --- /dev/null +++ b/src/ijulia.jl @@ -0,0 +1,22 @@ +struct IJuliaCell + core::CoreVisualizer +end + +""" +Render a MeshCat visualizer inline inside a Jupyter notebook cell. + +The visualizer should show up automatically in your Jupyter cell output +as long as this command is the *last* command in the input cell. +""" +IJuliaCell(v::Visualizer) = IJuliaCell(v.core) + +url(c::IJuliaCell) = url(c.core) + +function Base.show(io::IO, ::MIME"text/html", frame::IJuliaCell) + wait_for_server(frame.core) + print(io, """ +
+ +
+""") +end diff --git a/src/lowering.jl b/src/lowering.jl index ff394be..bcff4c7 100644 --- a/src/lowering.jl +++ b/src/lowering.jl @@ -293,31 +293,6 @@ function lower(cmd::SetProperty) ) end -function lower(b::Button) - Dict{String, Any}( - "name" => b.name, - "callback" => string(@js((value) -> $(b.observer)[] = [$(b.name), Date.now()])), - ) -end - -function lower(n::NumericControl) - Dict{String, Any}( - "name" => n.name, - "callback" => string(@js((val) -> $(n.observer)[] = [$(n.name), val])), - "value" => n.value, - "min" => n.min, - "max" => n.max - ) -end - -function lower(cmd::SetControl) - d = Dict{String, Any}( - "type" => "set_control", - ) - merge!(d, lower(cmd.control)) - d -end - function lower(track::AnimationTrack) Dict{String, Any}( "name" => string(".", track.name), diff --git a/src/servers.jl b/src/servers.jl index ab85318..e5c40c4 100644 --- a/src/servers.jl +++ b/src/servers.jl @@ -1,28 +1,31 @@ +using Sockets: connect -""" - open(vis::Visualizer; host::IPAddr = ip"127.0.0.1", start_browser = true, - default_port = 8700, max_retries = 500) - -Start a server for the visualizer so that it can be accessed from a browser -using the given host URL. This method will try to search for an open port, -starting at `default_port` and then trying `max_retries` additional port -numbers. If `start_browser` is true, then a new browser window will be opened. - - open(vis::Visualizer, port::Integer; host::IPAddr = ip"127.0.0.1", start_browser = true) - -Start a server for the visualizer so that it can be accessed from a browser -using the given host URL and port. If `start_browser` is true, then a new -browser window will be opened. - -""" Base.open(vis::Visualizer, args...; kw...) = open(vis.core, args...; kw...) -function Base.open(core::CoreVisualizer, port::Integer; - host::IPAddr = ip"127.0.0.1", start_browser = true) - @async WebIO.webio_serve(Mux.page("/", req -> core.scope), host, port) - url = "http://$host:$port" - @info("Serving MeshCat visualizer at $url") - start_browser && open_url(url) +function wait_for_server(core::CoreVisualizer, timeout=100) + interval = 0.25 + socket = nothing + for i in range(0, timeout, step=interval) + try + socket = connect(core.host, core.port) + sleep(interval) + break + catch e + if e isa Base.IOError + sleep(interval) + end + end + end + if socket === nothing + error("Could not establish a connection to the visualizer.") + else + close(socket) + end +end + +function Base.open(core::CoreVisualizer) + wait_for_server(core) + open_url(url(core)) end function open_url(url) @@ -40,22 +43,3 @@ function open_url(url) println(url) end end - -function Base.open(core::CoreVisualizer; - host::IPAddr=ip"127.0.0.1", default_port=8700, max_retries=500, kw...) - for port in default_port:(default_port + max_retries) - server = try - listen(host, port) - catch e - if e isa Base.IOError - continue - end - end - close(server) - # It is *possible* that a race condition could occur here, in which - # some other process grabs the given port in between the close() above - # and the open() below. But it's unlikely and would not be terribly - # damaging (the user would just have to call open() again). - return open(core, port; host=host, kw...) - end -end diff --git a/src/visualizer.jl b/src/visualizer.jl index 886ce36..e77bfee 100644 --- a/src/visualizer.jl +++ b/src/visualizer.jl @@ -1,76 +1,116 @@ -mutable struct CoreVisualizer - scope::WebIO.Scope - tree::SceneNode - command_channel::Observable{Vector{UInt8}} - request_channel::Observable{String} - controls_channel::Observable{Vector{Any}} - controls::Dict{String, Tuple{Observable, AbstractControl}} - - function CoreVisualizer() - scope = WebIO.Scope(imports=ASSET_KEYS, outbox=Channel{Any}(Inf)) - command_channel = Observable(scope, "meshcat-command", UInt8[]) - request_channel = Observable(scope, "meshcat-request", "") - controls_channel = Observable(scope, "meshcat-controls", []) - - onimport(scope, @js function(mc) - @var element = this.dom.children[0] - this.viewer = @new mc.Viewer(element) - $request_channel[] = String(Date.now()) - window.document.body.style.margin = "0" - window.meshcat_viewer = this.viewer - end) - - onjs(command_channel, @js function(val) - this.viewer.handle_command_message(Dict(:data => val)) - end) - scope = scope(dom"div.meshcat-viewer"( - style=Dict( - :width => "100vw", - :height => "100vh", - :position => "absolute", - :left => 0, - :right => 0, - :margin => 0, - ) - )) - scope.dom.props[:style][:overflow] = "hidden" +using MsgPack +using Mux +using Logging +import Mux.WebSockets +struct CoreVisualizer + tree::SceneNode + command_channel::Channel{Vector{UInt8}} + new_connections::Channel{Bool} + queues::Dict{Any, Any} + host::IPAddr + port::Int + + function CoreVisualizer(host::IPAddr = ip"127.0.0.1", default_port=8700) + cmd = Channel{Vector{UInt8}}(typemax(Int)) + new_connections = Channel{Bool}(typemax(Int)) + queues = Dict() tree = SceneNode() - controls = Dict{String, Observable}() - vis = new(scope, tree, command_channel, request_channel, controls_channel, controls) - on(request_channel) do x - send_scene(vis) + port = find_open_port(host, default_port, 500) + core = new(tree, cmd, new_connections, queues, + host, port) + @async while true + if isempty(queues) + wait(new_connections) + end + while isready(new_connections) + take!(new_connections) + end + message = take!(cmd) + for queue in values(queues) + put!(queue, message) + end end + start_server(core) + return core + end +end - on(controls_channel) do msg - name::String, value = msg - if haskey(vis.controls, name) - @async vis.controls[name] = value - # Base.invokelatest(setindex!, vis.controls[name], value) +function find_open_port(host, default_port, max_retries) + for port in default_port:(default_port + max_retries) + server = try + listen(host, port) + catch e + if e isa Base.IOError + continue end end - vis + close(server) + # It is *possible* that a race condition could occur here, in which + # some other process grabs the given port in between the close() above + # and the open() below. But it's unlikely and would not be terribly + # damaging (the user would just have to call open() again). + return port end end -WebIO.render(core::CoreVisualizer) = core.scope +function start_server(core::CoreVisualizer) + asset_files = Set(["index.html", "main.min.js", "main.js"]) -function WebIO.iframe(core::CoreVisualizer; height="100%", width="100%", minHeight="400px") - ifr = WebIO.iframe(core.scope) - onimport(ifr, @js function() - this.dom.style.height = "100%" - window.foo = this - this.dom.children[0].children[0].style.flexGrow = "1" - end) - style = get!(Dict, ifr.dom.props, :style) - style[:height] = height - style[:minHeight] = minHeight - style[:width] = width - style[:display] = "flex" - style[:flexDirection] = "column" - ifr + function read_asset(file) + if file in asset_files + return open(s -> read(s, String), joinpath(VIEWER_ROOT, file)) + else + return "Not found" + end + end + default = "index.html" + @app h = ( + Mux.defaults, + page("/index.html", req -> read_asset("index.html")), + page("/main.js", req -> read_asset("main.js")), + page("/main.min.js", req -> read_asset("main.min.js")), + page("/", req -> read_asset(default)), + Mux.notfound()); + @app w = ( + Mux.wdefaults, + route("/", req -> add_connection!(core, req)), + Mux.wclose, + Mux.notfound()); + @async begin + # Suppress noisy unhelpful log messages from HTTP.jl, e.g. + # https://github.com/JuliaWeb/HTTP.jl/issues/392 + Logging.with_logger(Logging.NullLogger()) do + WebSockets.serve( + WebSockets.ServerWS( + Mux.http_handler(h), + Mux.ws_handler(w), + ), core.port); + end + end + @info "MeshCat server started. You can open the visualizer by visiting the following URL in your browser:\n$(url(core))" end +function url(core::CoreVisualizer) + "http://localhost:$(core.port[])" +end + +function add_connection!(core::CoreVisualizer, req) + connection = req[:socket] + queue = Channel{Vector{UInt8}}(0) + core.queues[connection] = queue + put!(core.new_connections, true) + send_scene(core) + while true + message = take!(queue) + if isopen(connection) + WebSockets.writeguarded(connection, message) + else + break + end + end + delete!(core.queues, connection) +end function update_tree!(core::CoreVisualizer, cmd::SetObject, data) core.tree[cmd.path].object = data @@ -88,35 +128,31 @@ function update_tree!(core::CoreVisualizer, cmd::Delete, data) end end -update_tree!(core::CoreVisualizer, cmd::SetControl, data) = nothing update_tree!(core::CoreVisualizer, cmd::SetAnimation, data) = nothing update_tree!(core::CoreVisualizer, cmd::SetProperty, data) = nothing function send_scene(core::CoreVisualizer) foreach(core.tree) do node if node.object !== nothing - core.command_channel[] = node.object + put!(core.command_channel, node.object) end if node.transform !== nothing - core.command_channel[] = node.transform + put!(core.command_channel, node.transform) end end - for (name, (obs, control)) in core.controls - send(core, SetControl(control)) - end end function send(c::CoreVisualizer, cmd::AbstractCommand) data = pack(lower(cmd)) update_tree!(c, cmd, data) - c.command_channel[] = data + put!(c.command_channel, data) nothing end function Base.wait(c::CoreVisualizer) - pool = c.scope.pool - WebIO.ensure_connection(pool) - nothing + while isempty(c.queues) + sleep(0.5) + end end """ @@ -146,9 +182,9 @@ execution until the browser window has opened. """ Base.wait(v::Visualizer) = wait(v.core) -IJuliaCell(vis::Visualizer; kw...) = iframe(vis.core; kw...) +# IJuliaCell(vis::Visualizer; kw...) = iframe(vis.core; kw...) -Base.show(io::IO, v::Visualizer) = print(io, "MeshCat Visualizer with path $(v.path)") +Base.show(io::IO, v::Visualizer) = print(io, "MeshCat Visualizer with path $(v.path) at $(url(v.core))") """ $(SIGNATURES) @@ -202,20 +238,6 @@ function setprop!(vis::Visualizer, property::AbstractString, value) vis end -function setcontrol!(vis::Visualizer, name::AbstractString, obs::Observable) - control = Button(vis.core.controls_channel, name) - vis.core.controls[name] = (obs, control) - send(vis.core, SetControl(control)) - vis -end - -function setcontrol!(vis::Visualizer, name::AbstractString, obs::Observable, value, min=zero(value), max=one(value)) - control = NumericControl(vis.core.controls_channel, name, value, min, max) - vis.core.controls[name] = (obs, control) - send(vis.core, SetControl(control)) - vis -end - function setanimation!(vis::Visualizer, anim::Animation; play::Bool=true, repetitions::Integer=1) cmd = SetAnimation(anim, play, repetitions) send(vis.core, cmd) From f1949ec964dc4d7437dd85fc441f7cae6552fc73 Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Sun, 9 Feb 2020 22:48:02 -0500 Subject: [PATCH 2/8] make writing synchronous and get rid of all the queues --- notebooks/Manifest.toml | 309 ++++++++++++++++++++++++++++++++++++++++ src/visualizer.jl | 62 +++----- test/runtests.jl | 2 - 3 files changed, 330 insertions(+), 43 deletions(-) create mode 100644 notebooks/Manifest.toml diff --git a/notebooks/Manifest.toml b/notebooks/Manifest.toml new file mode 100644 index 0000000..4c64297 --- /dev/null +++ b/notebooks/Manifest.toml @@ -0,0 +1,309 @@ +# This file is machine-generated - editing it directly is not advised + +[[AssetRegistry]] +deps = ["Distributed", "JSON", "Pidfile", "SHA", "Test"] +git-tree-sha1 = "b25e88db7944f98789130d7b503276bc34bc098e" +uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893" +version = "0.1.0" + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[BinDeps]] +deps = ["Libdl", "Pkg", "SHA", "URIParser", "Unicode"] +git-tree-sha1 = "66158ad56b4bf6cc8413b37d0b7bc52402682764" +uuid = "9e28174c-4ba2-5203-b857-d8d62c4213ee" +version = "1.0.0" + +[[BinaryProvider]] +deps = ["Libdl", "SHA"] +git-tree-sha1 = "5b08ed6036d9d3f0ee6369410b830f8873d4024c" +uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232" +version = "0.5.8" + +[[Cassette]] +git-tree-sha1 = "da85d135b6048d3e78603e277cf9a4609f7e0673" +uuid = "7057c7e9-c182-5462-911a-8362d720325c" +version = "0.2.6" + +[[ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "7b62b728a5f3dd6ee3b23910303ccf27e82fad5e" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.8.1" + +[[Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "InteractiveUtils", "Printf", "Reexport"] +git-tree-sha1 = "c9c1845d6bf22e34738bee65c357a69f416ed5d1" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.9.6" + +[[Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "ed2c4abadf84c53d9e58510b5fc48912c2336fbb" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "2.2.0" + +[[CoordinateTransformations]] +deps = ["Compat", "Rotations", "StaticArrays"] +git-tree-sha1 = "47f05d0b7f4999609f92e657147df000818c1f24" +uuid = "150eb455-5306-5404-9cee-2592286d6298" +version = "0.5.0" + +[[DataStructures]] +deps = ["InteractiveUtils", "OrderedCollections"] +git-tree-sha1 = "b7720de347734f4716d1815b00ce5664ed6bbfd4" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.17.9" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[DocStringExtensions]] +deps = ["LibGit2", "Markdown", "Pkg", "Test"] +git-tree-sha1 = "88bb0edb352b16608036faadcc071adda068582a" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.8.1" + +[[FFMPEG]] +deps = ["BinaryProvider", "Libdl"] +git-tree-sha1 = "9143266ba77d3313a4cf61d8333a1970e8c5d8b6" +uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" +version = "0.2.4" + +[[FileIO]] +deps = ["Pkg"] +git-tree-sha1 = "2c84c57aced468fa21763c66d3bef33adcd09ec7" +uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +version = "1.2.2" + +[[FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" + +[[FixedPointNumbers]] +git-tree-sha1 = "d14a6fa5890ea3a7e5dcab6811114f132fec2b4b" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.6.1" + +[[GeometryTypes]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "a96baa00f5ac755689c82c29bc3395e161337c69" +uuid = "4d00f742-c7ba-57c2-abde-4428a4b178cb" +version = "0.7.7" + +[[HTTP]] +deps = ["Base64", "Dates", "IniFile", "MbedTLS", "Sockets"] +git-tree-sha1 = "5c49dab19938b119fe204fd7d7e8e174f4e9c68b" +uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" +version = "0.8.8" + +[[Hiccup]] +deps = ["MacroTools", "Test"] +git-tree-sha1 = "6187bb2d5fcbb2007c39e7ac53308b0d371124bd" +uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7" +version = "0.2.2" + +[[IniFile]] +deps = ["Test"] +git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8" +uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f" +version = "0.5.0" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "b34d7cef7b337321e97d22242c3c2b91f476748e" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.0" + +[[Lazy]] +deps = ["MacroTools"] +git-tree-sha1 = "0bd934e15f5df97414aa81abf74ba8a2d5042964" +uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" +version = "0.15.0" + +[[LibGit2]] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[MacroTools]] +deps = ["DataStructures", "Markdown", "Random"] +git-tree-sha1 = "07ee65e03e28ca88bc9a338a3726ae0c3efaa94b" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.4" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[MbedTLS]] +deps = ["BinaryProvider", "Dates", "Libdl", "Random", "Sockets"] +git-tree-sha1 = "85f5947b53c8cfd53ccfa3f4abae31faa22c2181" +uuid = "739be429-bea8-5141-9913-cc70e7f3736d" +version = "0.7.0" + +[[MeshCat]] +deps = ["Base64", "BinDeps", "Cassette", "Colors", "CoordinateTransformations", "DocStringExtensions", "FFMPEG", "GeometryTypes", "LinearAlgebra", "MsgPack", "Mux", "Parameters", "Requires", "Rotations", "Sockets", "StaticArrays", "UUIDs", "WebSockets"] +path = ".." +uuid = "283c5d60-a78f-5afe-a0af-af636b173e11" +version = "0.9.1" + +[[MeshIO]] +deps = ["ColorTypes", "FileIO", "GeometryTypes", "Printf", "Test"] +git-tree-sha1 = "3e01e12c4c626578a9d47fac0d15c9fe8dad5139" +uuid = "7269a6da-0436-5bbc-96c2-40638cbb6118" +version = "0.3.1" + +[[Meshing]] +deps = ["GeometryTypes", "LinearAlgebra", "Profile", "Statistics", "Test"] +git-tree-sha1 = "4a61ec51d3f1548f779ae46ff43638797a96de92" +uuid = "e6723b4c-ebff-59f1-b4b7-d97aa5274f73" +version = "0.4.1" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[MsgPack]] +deps = ["Serialization"] +git-tree-sha1 = "a8cbf066b54d793b9a48c5daa5d586cf2b5bd43d" +uuid = "99f44e22-a591-53d1-9472-aa23ef4bd671" +version = "1.1.0" + +[[Mux]] +deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "Lazy", "Pkg", "Sockets", "Test", "WebSockets"] +git-tree-sha1 = "5b41f03d63400c290bab4e1a49fb9ac36de1084a" +uuid = "a975b10e-0019-58db-a62f-e48ff68538c9" +version = "0.7.0" + +[[OrderedCollections]] +deps = ["Random", "Serialization", "Test"] +git-tree-sha1 = "c4c13474d23c60d20a67b217f1d7f22a40edf8f1" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.1.0" + +[[Parameters]] +deps = ["OrderedCollections"] +git-tree-sha1 = "b62b2558efb1eef1fa44e4be5ff58a515c287e38" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.0" + +[[Parsers]] +deps = ["Dates", "Test"] +git-tree-sha1 = "d112c19ccca00924d5d3a38b11ae2b4b268dda39" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "0.3.11" + +[[Pidfile]] +deps = ["FileWatching", "Test"] +git-tree-sha1 = "1ffd82728498b5071cde851bbb7abd780d4445f3" +uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307" +version = "1.1.0" + +[[Pkg]] +deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Test", "UUIDs"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[Profile]] +deps = ["Printf"] +uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[Reexport]] +deps = ["Pkg"] +git-tree-sha1 = "7b1d07f411bc8ddb7977ec7f377b97b158514fe0" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "0.2.0" + +[[Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "d37400976e98018ee840e0ca4f9d20baa231dc6b" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.0.1" + +[[Rotations]] +deps = ["LinearAlgebra", "StaticArrays", "Statistics"] +git-tree-sha1 = "d5f83867093db7319a9366d55f29280ecae9bcda" +uuid = "6038ab10-8711-5258-84ad-4b1120ba62dc" +version = "0.13.0" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[StaticArrays]] +deps = ["LinearAlgebra", "Random", "Statistics"] +git-tree-sha1 = "5a3bcb6233adabde68ebc97be66e95dcb787424c" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "0.12.1" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[Test]] +deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[URIParser]] +deps = ["Test", "Unicode"] +git-tree-sha1 = "6ddf8244220dfda2f17539fa8c9de20d6c575b69" +uuid = "30578b45-9adc-5946-b283-645ec420af67" +version = "0.4.0" + +[[UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[WebSockets]] +deps = ["Base64", "Dates", "Distributed", "HTTP", "Logging", "Random", "Sockets", "Test"] +git-tree-sha1 = "13f763d38c7a05688938808b49cb29b18b60c8c8" +uuid = "104b5d7c-a370-577a-8038-80a2059c5097" +version = "1.5.2" diff --git a/src/visualizer.jl b/src/visualizer.jl index e77bfee..53decb5 100644 --- a/src/visualizer.jl +++ b/src/visualizer.jl @@ -5,32 +5,15 @@ import Mux.WebSockets struct CoreVisualizer tree::SceneNode - command_channel::Channel{Vector{UInt8}} - new_connections::Channel{Bool} - queues::Dict{Any, Any} + connections::Set{Any} host::IPAddr port::Int function CoreVisualizer(host::IPAddr = ip"127.0.0.1", default_port=8700) - cmd = Channel{Vector{UInt8}}(typemax(Int)) - new_connections = Channel{Bool}(typemax(Int)) - queues = Dict() + connections = Set([]) tree = SceneNode() port = find_open_port(host, default_port, 500) - core = new(tree, cmd, new_connections, queues, - host, port) - @async while true - if isempty(queues) - wait(new_connections) - end - while isready(new_connections) - take!(new_connections) - end - message = take!(cmd) - for queue in values(queues) - put!(queue, message) - end - end + core = new(tree, connections, host, port) start_server(core) return core end @@ -97,19 +80,9 @@ end function add_connection!(core::CoreVisualizer, req) connection = req[:socket] - queue = Channel{Vector{UInt8}}(0) - core.queues[connection] = queue - put!(core.new_connections, true) - send_scene(core) - while true - message = take!(queue) - if isopen(connection) - WebSockets.writeguarded(connection, message) - else - break - end - end - delete!(core.queues, connection) + push!(core.connections, connection) + send_scene(core, connection) + wait() end function update_tree!(core::CoreVisualizer, cmd::SetObject, data) @@ -131,26 +104,33 @@ end update_tree!(core::CoreVisualizer, cmd::SetAnimation, data) = nothing update_tree!(core::CoreVisualizer, cmd::SetProperty, data) = nothing -function send_scene(core::CoreVisualizer) +function send_scene(core::CoreVisualizer, connection) foreach(core.tree) do node if node.object !== nothing - put!(core.command_channel, node.object) + WebSockets.writeguarded(connection, node.object) end if node.transform !== nothing - put!(core.command_channel, node.transform) + WebSockets.writeguarded(connection, node.transform) end end end -function send(c::CoreVisualizer, cmd::AbstractCommand) +function Base.write(core::CoreVisualizer, data) + for connection in core.connections + WebSockets.writeguarded(connection, data) + end +end + +function send(core::CoreVisualizer, cmd::AbstractCommand) data = pack(lower(cmd)) - update_tree!(c, cmd, data) - put!(c.command_channel, data) + update_tree!(core, cmd, data) + write(core, data) + nothing end -function Base.wait(c::CoreVisualizer) - while isempty(c.queues) +function Base.wait(core::CoreVisualizer) + while isempty(core.connections) sleep(0.5) end end diff --git a/test/runtests.jl b/test/runtests.jl index 1ca3741..6681378 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,8 +17,6 @@ include("util.jl") include("wait.jl") end -sleep(10) - module ModuleTest # Test for any https://github.com/JuliaLang/julia/issues/21653 # related issues when MeshCat is included in another module From 92f939f01a2e1b118739dc0121ca9ad0403425e7 Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Sun, 9 Feb 2020 22:53:34 -0500 Subject: [PATCH 3/8] make send() synchronous and drop all the various queues --- src/visualizer.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/visualizer.jl b/src/visualizer.jl index 53decb5..a12bbae 100644 --- a/src/visualizer.jl +++ b/src/visualizer.jl @@ -117,7 +117,11 @@ end function Base.write(core::CoreVisualizer, data) for connection in core.connections - WebSockets.writeguarded(connection, data) + if isopen(connection) + WebSockets.writeguarded(connection, data) + else + delete!(core.connections, connection) + end end end From 3c2d45eede96f2af7789e1fdb745d894ce915e36 Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 10 Feb 2020 21:21:30 -0500 Subject: [PATCH 4/8] clean up and implement basic WebIO rendering --- .gitignore | 3 +- notebooks/Manifest.toml | 309 ---------------------------------------- src/MeshCat.jl | 34 +---- src/assets.jl | 19 +++ src/ijulia.jl | 33 ++++- src/integrations.jl | 17 +++ 6 files changed, 67 insertions(+), 348 deletions(-) delete mode 100644 notebooks/Manifest.toml create mode 100644 src/assets.jl create mode 100644 src/integrations.jl diff --git a/.gitignore b/.gitignore index 9abf692..9555aca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .ipynb_checkpoints deps/build.log -/Manifest.toml - +Manifest.toml diff --git a/notebooks/Manifest.toml b/notebooks/Manifest.toml deleted file mode 100644 index 4c64297..0000000 --- a/notebooks/Manifest.toml +++ /dev/null @@ -1,309 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -[[AssetRegistry]] -deps = ["Distributed", "JSON", "Pidfile", "SHA", "Test"] -git-tree-sha1 = "b25e88db7944f98789130d7b503276bc34bc098e" -uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893" -version = "0.1.0" - -[[Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[BinDeps]] -deps = ["Libdl", "Pkg", "SHA", "URIParser", "Unicode"] -git-tree-sha1 = "66158ad56b4bf6cc8413b37d0b7bc52402682764" -uuid = "9e28174c-4ba2-5203-b857-d8d62c4213ee" -version = "1.0.0" - -[[BinaryProvider]] -deps = ["Libdl", "SHA"] -git-tree-sha1 = "5b08ed6036d9d3f0ee6369410b830f8873d4024c" -uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232" -version = "0.5.8" - -[[Cassette]] -git-tree-sha1 = "da85d135b6048d3e78603e277cf9a4609f7e0673" -uuid = "7057c7e9-c182-5462-911a-8362d720325c" -version = "0.2.6" - -[[ColorTypes]] -deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "7b62b728a5f3dd6ee3b23910303ccf27e82fad5e" -uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.8.1" - -[[Colors]] -deps = ["ColorTypes", "FixedPointNumbers", "InteractiveUtils", "Printf", "Reexport"] -git-tree-sha1 = "c9c1845d6bf22e34738bee65c357a69f416ed5d1" -uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.9.6" - -[[Compat]] -deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "ed2c4abadf84c53d9e58510b5fc48912c2336fbb" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "2.2.0" - -[[CoordinateTransformations]] -deps = ["Compat", "Rotations", "StaticArrays"] -git-tree-sha1 = "47f05d0b7f4999609f92e657147df000818c1f24" -uuid = "150eb455-5306-5404-9cee-2592286d6298" -version = "0.5.0" - -[[DataStructures]] -deps = ["InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "b7720de347734f4716d1815b00ce5664ed6bbfd4" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.17.9" - -[[Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" - -[[DelimitedFiles]] -deps = ["Mmap"] -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" - -[[Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" - -[[DocStringExtensions]] -deps = ["LibGit2", "Markdown", "Pkg", "Test"] -git-tree-sha1 = "88bb0edb352b16608036faadcc071adda068582a" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.8.1" - -[[FFMPEG]] -deps = ["BinaryProvider", "Libdl"] -git-tree-sha1 = "9143266ba77d3313a4cf61d8333a1970e8c5d8b6" -uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" -version = "0.2.4" - -[[FileIO]] -deps = ["Pkg"] -git-tree-sha1 = "2c84c57aced468fa21763c66d3bef33adcd09ec7" -uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.2.2" - -[[FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" - -[[FixedPointNumbers]] -git-tree-sha1 = "d14a6fa5890ea3a7e5dcab6811114f132fec2b4b" -uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.6.1" - -[[GeometryTypes]] -deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "a96baa00f5ac755689c82c29bc3395e161337c69" -uuid = "4d00f742-c7ba-57c2-abde-4428a4b178cb" -version = "0.7.7" - -[[HTTP]] -deps = ["Base64", "Dates", "IniFile", "MbedTLS", "Sockets"] -git-tree-sha1 = "5c49dab19938b119fe204fd7d7e8e174f4e9c68b" -uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "0.8.8" - -[[Hiccup]] -deps = ["MacroTools", "Test"] -git-tree-sha1 = "6187bb2d5fcbb2007c39e7ac53308b0d371124bd" -uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7" -version = "0.2.2" - -[[IniFile]] -deps = ["Test"] -git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8" -uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f" -version = "0.5.0" - -[[InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "b34d7cef7b337321e97d22242c3c2b91f476748e" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.0" - -[[Lazy]] -deps = ["MacroTools"] -git-tree-sha1 = "0bd934e15f5df97414aa81abf74ba8a2d5042964" -uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" -version = "0.15.0" - -[[LibGit2]] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" - -[[Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[LinearAlgebra]] -deps = ["Libdl"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" - -[[MacroTools]] -deps = ["DataStructures", "Markdown", "Random"] -git-tree-sha1 = "07ee65e03e28ca88bc9a338a3726ae0c3efaa94b" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.4" - -[[Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[MbedTLS]] -deps = ["BinaryProvider", "Dates", "Libdl", "Random", "Sockets"] -git-tree-sha1 = "85f5947b53c8cfd53ccfa3f4abae31faa22c2181" -uuid = "739be429-bea8-5141-9913-cc70e7f3736d" -version = "0.7.0" - -[[MeshCat]] -deps = ["Base64", "BinDeps", "Cassette", "Colors", "CoordinateTransformations", "DocStringExtensions", "FFMPEG", "GeometryTypes", "LinearAlgebra", "MsgPack", "Mux", "Parameters", "Requires", "Rotations", "Sockets", "StaticArrays", "UUIDs", "WebSockets"] -path = ".." -uuid = "283c5d60-a78f-5afe-a0af-af636b173e11" -version = "0.9.1" - -[[MeshIO]] -deps = ["ColorTypes", "FileIO", "GeometryTypes", "Printf", "Test"] -git-tree-sha1 = "3e01e12c4c626578a9d47fac0d15c9fe8dad5139" -uuid = "7269a6da-0436-5bbc-96c2-40638cbb6118" -version = "0.3.1" - -[[Meshing]] -deps = ["GeometryTypes", "LinearAlgebra", "Profile", "Statistics", "Test"] -git-tree-sha1 = "4a61ec51d3f1548f779ae46ff43638797a96de92" -uuid = "e6723b4c-ebff-59f1-b4b7-d97aa5274f73" -version = "0.4.1" - -[[Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" - -[[MsgPack]] -deps = ["Serialization"] -git-tree-sha1 = "a8cbf066b54d793b9a48c5daa5d586cf2b5bd43d" -uuid = "99f44e22-a591-53d1-9472-aa23ef4bd671" -version = "1.1.0" - -[[Mux]] -deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "Lazy", "Pkg", "Sockets", "Test", "WebSockets"] -git-tree-sha1 = "5b41f03d63400c290bab4e1a49fb9ac36de1084a" -uuid = "a975b10e-0019-58db-a62f-e48ff68538c9" -version = "0.7.0" - -[[OrderedCollections]] -deps = ["Random", "Serialization", "Test"] -git-tree-sha1 = "c4c13474d23c60d20a67b217f1d7f22a40edf8f1" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.1.0" - -[[Parameters]] -deps = ["OrderedCollections"] -git-tree-sha1 = "b62b2558efb1eef1fa44e4be5ff58a515c287e38" -uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" -version = "0.12.0" - -[[Parsers]] -deps = ["Dates", "Test"] -git-tree-sha1 = "d112c19ccca00924d5d3a38b11ae2b4b268dda39" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "0.3.11" - -[[Pidfile]] -deps = ["FileWatching", "Test"] -git-tree-sha1 = "1ffd82728498b5071cde851bbb7abd780d4445f3" -uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307" -version = "1.1.0" - -[[Pkg]] -deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Test", "UUIDs"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" - -[[Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[Profile]] -deps = ["Printf"] -uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" - -[[REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - -[[Random]] -deps = ["Serialization"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[Reexport]] -deps = ["Pkg"] -git-tree-sha1 = "7b1d07f411bc8ddb7977ec7f377b97b158514fe0" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "0.2.0" - -[[Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "d37400976e98018ee840e0ca4f9d20baa231dc6b" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.0.1" - -[[Rotations]] -deps = ["LinearAlgebra", "StaticArrays", "Statistics"] -git-tree-sha1 = "d5f83867093db7319a9366d55f29280ecae9bcda" -uuid = "6038ab10-8711-5258-84ad-4b1120ba62dc" -version = "0.13.0" - -[[SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" - -[[Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" - -[[Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" - -[[SparseArrays]] -deps = ["LinearAlgebra", "Random"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - -[[StaticArrays]] -deps = ["LinearAlgebra", "Random", "Statistics"] -git-tree-sha1 = "5a3bcb6233adabde68ebc97be66e95dcb787424c" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "0.12.1" - -[[Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[Test]] -deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[URIParser]] -deps = ["Test", "Unicode"] -git-tree-sha1 = "6ddf8244220dfda2f17539fa8c9de20d6c575b69" -uuid = "30578b45-9adc-5946-b283-645ec420af67" -version = "0.4.0" - -[[UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" - -[[WebSockets]] -deps = ["Base64", "Dates", "Distributed", "HTTP", "Logging", "Random", "Sockets", "Test"] -git-tree-sha1 = "13f763d38c7a05688938808b49cb29b18b60c8c8" -uuid = "104b5d7c-a370-577a-8038-80a2059c5097" -version = "1.5.2" diff --git a/src/MeshCat.jl b/src/MeshCat.jl index f942057..d324f82 100644 --- a/src/MeshCat.jl +++ b/src/MeshCat.jl @@ -86,29 +86,11 @@ include("atframe.jl") include("arrow_visualizer.jl") include("ijulia.jl") include("servers.jl") +include("assets.jl") +include("integrations.jl") const VIEWER_ROOT = joinpath(@__DIR__, "..", "assets", "meshcat", "dist") -function develop_meshcat_assets(skip_confirmation=false) - meshcat_dir = abspath(joinpath(@__DIR__, "..", "assets", "meshcat")) - if !skip_confirmation - println("CAUTION: This will delete all downloaded meshcat assets and replace them with a git clone.") - println("The following path will be overwritten:") - println(meshcat_dir) - println("To undo this operation, you will need to manually remove that directory and then run `Pkg.build(\"MeshCat\")`") - print("Proceed? (y/n) ") - choice = chomp(readline()) - if isempty(choice) || lowercase(choice[1]) != 'y' - println("Canceled.") - return - end - end - println("Removing $meshcat_dir") - rm(meshcat_dir, force=true, recursive=true) - run(`git clone https://github.com/rdeits/meshcat $meshcat_dir`) - rm(joinpath(meshcat_dir, "..", "meshcat.stamp")) -end - function __init__() main_js = abspath(joinpath(VIEWER_ROOT, "main.min.js")) if !isfile(main_js) @@ -116,16 +98,8 @@ function __init__() main.min.js not found at $main_js. Please build MeshCat using `import Pkg; Pkg.build("MeshCat")`""") end - - @require Blink="ad839575-38b3-5650-b840-f874b8c74a25" begin - function Base.open(core::CoreVisualizer, w::Blink.AtomShell.Window) - # Ensure the window is ready - Blink.js(w, "ok") - # Set its contents - Blink.loadurl(w, url(core)) - w - end - end + setup_integrations() end + end diff --git a/src/assets.jl b/src/assets.jl new file mode 100644 index 0000000..d441ea4 --- /dev/null +++ b/src/assets.jl @@ -0,0 +1,19 @@ +function develop_meshcat_assets(skip_confirmation=false) + meshcat_dir = abspath(joinpath(@__DIR__, "..", "assets", "meshcat")) + if !skip_confirmation + println("CAUTION: This will delete all downloaded meshcat assets and replace them with a git clone.") + println("The following path will be overwritten:") + println(meshcat_dir) + println("To undo this operation, you will need to manually remove that directory and then run `Pkg.build(\"MeshCat\")`") + print("Proceed? (y/n) ") + choice = chomp(readline()) + if isempty(choice) || lowercase(choice[1]) != 'y' + println("Canceled.") + return + end + end + println("Removing $meshcat_dir") + rm(meshcat_dir, force=true, recursive=true) + run(`git clone https://github.com/rdeits/meshcat $meshcat_dir`) + rm(joinpath(meshcat_dir, "..", "meshcat.stamp")) +end diff --git a/src/ijulia.jl b/src/ijulia.jl index 14304b3..d3868b3 100644 --- a/src/ijulia.jl +++ b/src/ijulia.jl @@ -1,18 +1,28 @@ -struct IJuliaCell +struct DisplayedVisualizer core::CoreVisualizer end + +DisplayedVisualizer(vis::Visualizer) = DisplayedVisualizer(vis.core) + +url(c::DisplayedVisualizer) = url(c.core) + """ -Render a MeshCat visualizer inline inside a Jupyter notebook cell. +Render a MeshCat visualizer inline in Jupyter or Juno. + +If this is the last command in a Jupyter notebook cell, then the +visualizer should show up automatically in the corresponding output +cell. -The visualizer should show up automatically in your Jupyter cell output -as long as this command is the *last* command in the input cell. +If this is run from the Juno console, then the visualizer should show +up in the Juno plot pane. """ -IJuliaCell(v::Visualizer) = IJuliaCell(v.core) +render(vis::Visualizer) = render(vis.core) +render(core::CoreVisualizer) = DisplayedVisualizer(core) -url(c::IJuliaCell) = url(c.core) +@deprecate IJuliaCell(v::Visualizer) render(v) -function Base.show(io::IO, ::MIME"text/html", frame::IJuliaCell) +function Base.show(io::IO, ::MIME"text/html", frame::DisplayedVisualizer) wait_for_server(frame.core) print(io, """
@@ -20,3 +30,12 @@ function Base.show(io::IO, ::MIME"text/html", frame::IJuliaCell)
""") end + +function Base.show(io::IO, ::MIME"application/prs.juno.plotpane+html", d::DisplayedVisualizer) + wait_for_server(d.core) + print(io, """ +
+ +
+ """) +end diff --git a/src/integrations.jl b/src/integrations.jl new file mode 100644 index 0000000..042e5d9 --- /dev/null +++ b/src/integrations.jl @@ -0,0 +1,17 @@ +function setup_integrations() + @require Blink="ad839575-38b3-5650-b840-f874b8c74a25" begin + function Base.open(core::CoreVisualizer, w::Blink.AtomShell.Window) + # Ensure the window is ready + Blink.js(w, "ok") + # Set its contents + Blink.loadurl(w, url(core)) + w + end + end + + @require WebIO="0f1e0344-ec1d-5b48-a673-e5cf874b6c29" begin + WebIO.render(vis::Visualizer) = WebIO.render(vis.core) + + WebIO.render(core::CoreVisualizer) = WebIO.render(MeshCat.render(core)) + end +end From 6ce1ce1662e7417137115c130b89ed4d18609b26 Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 10 Feb 2020 21:46:23 -0500 Subject: [PATCH 5/8] clean up imports and use `render()` in the demo notebooks --- notebooks/animation.ipynb | 2 +- notebooks/demo.ipynb | 9 +++++++-- src/MeshCat.jl | 15 ++++++++------- src/visualizer.jl | 19 +++++++------------ 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/notebooks/animation.ipynb b/notebooks/animation.ipynb index 4a14ffc..12438cf 100644 --- a/notebooks/animation.ipynb +++ b/notebooks/animation.ipynb @@ -55,7 +55,7 @@ "# open(vis)\n", "\n", "## To open the visualizer inside this jupyter notebook, do: \n", - "# IJuliaCell(vis)\n", + "# render(vis)\n", "\n", "## To open this visualizer in a standalone window, do:\n", "# using Blink\n", diff --git a/notebooks/demo.ipynb b/notebooks/demo.ipynb index a13dbfb..365911a 100644 --- a/notebooks/demo.ipynb +++ b/notebooks/demo.ipynb @@ -56,7 +56,12 @@ "metadata": {}, "outputs": [], "source": [ - "IJuliaCell(vis)" + "# The `render(vis)` function will try to render a MeshCat\n", + "# visualizer inline. In Jupyter, it will cause the visualizer\n", + "# to be displayed in the output of the current Jupyter cell. \n", + "# In Juno, it will cause the visualizer to be displayed in\n", + "# the plot pane.\n", + "render(vis)" ] }, { @@ -92,7 +97,7 @@ "### Embed the visualizer inside this notebook\n", "\n", "```julia\n", - "IJuliaCell(vis)\n", + "render(vis)\n", "```" ] }, diff --git a/src/MeshCat.jl b/src/MeshCat.jl index d324f82..473e2b1 100644 --- a/src/MeshCat.jl +++ b/src/MeshCat.jl @@ -1,10 +1,5 @@ -__precompile__() - module MeshCat -import Mux -import Cassette -import FFMPEG using GeometryTypes, CoordinateTransformations using Rotations: rotation_between, Rotation, Quat using Colors: Color, Colorant, RGB, RGBA, alpha, hex @@ -20,12 +15,18 @@ using LinearAlgebra: UniformScaling, Diagonal, norm using Sockets: listen, @ip_str, IPAddr, IPv4, IPv6 using Base64: base64encode using MsgPack: MsgPack, pack +import Mux +import Logging +import Mux.WebSockets +import Cassette +import FFMPEG + import Base: delete! export Visualizer, - ViewerWindow, - IJuliaCell + IJuliaCell, + render export setobject!, settransform!, diff --git a/src/visualizer.jl b/src/visualizer.jl index a12bbae..6d59258 100644 --- a/src/visualizer.jl +++ b/src/visualizer.jl @@ -1,8 +1,3 @@ -using MsgPack -using Mux -using Logging -import Mux.WebSockets - struct CoreVisualizer tree::SceneNode connections::Set{Any} @@ -48,16 +43,16 @@ function start_server(core::CoreVisualizer) end end default = "index.html" - @app h = ( + Mux.@app h = ( Mux.defaults, - page("/index.html", req -> read_asset("index.html")), - page("/main.js", req -> read_asset("main.js")), - page("/main.min.js", req -> read_asset("main.min.js")), - page("/", req -> read_asset(default)), + Mux.page("/index.html", req -> read_asset("index.html")), + Mux.page("/main.js", req -> read_asset("main.js")), + Mux.page("/main.min.js", req -> read_asset("main.min.js")), + Mux.page("/", req -> read_asset(default)), Mux.notfound()); - @app w = ( + Mux.@app w = ( Mux.wdefaults, - route("/", req -> add_connection!(core, req)), + Mux.route("/", req -> add_connection!(core, req)), Mux.wclose, Mux.notfound()); @async begin From 6e5b778da2ead2a62fed54e5130b7d9b5eca67b8 Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 10 Feb 2020 22:07:47 -0500 Subject: [PATCH 6/8] bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 6f7aba4..407088e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MeshCat" uuid = "283c5d60-a78f-5afe-a0af-af636b173e11" authors = ["Robin Deits "] -version = "0.9.1" +version = "0.10.0" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" From 26fe994ff0e5c6ac7815ce37ad823c291fa640c5 Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 10 Feb 2020 22:41:40 -0500 Subject: [PATCH 7/8] support the VSCode plot pane too --- notebooks/demo.ipynb | 2 +- src/MeshCat.jl | 2 +- src/{ijulia.jl => render.jl} | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) rename src/{ijulia.jl => render.jl} (69%) diff --git a/notebooks/demo.ipynb b/notebooks/demo.ipynb index 365911a..faac762 100644 --- a/notebooks/demo.ipynb +++ b/notebooks/demo.ipynb @@ -59,7 +59,7 @@ "# The `render(vis)` function will try to render a MeshCat\n", "# visualizer inline. In Jupyter, it will cause the visualizer\n", "# to be displayed in the output of the current Jupyter cell. \n", - "# In Juno, it will cause the visualizer to be displayed in\n", + "# In Juno/VSCode, it will cause the visualizer to be displayed in\n", "# the plot pane.\n", "render(vis)" ] diff --git a/src/MeshCat.jl b/src/MeshCat.jl index 473e2b1..127d47e 100644 --- a/src/MeshCat.jl +++ b/src/MeshCat.jl @@ -85,7 +85,7 @@ include("msgpack.jl") include("visualizer.jl") include("atframe.jl") include("arrow_visualizer.jl") -include("ijulia.jl") +include("render.jl") include("servers.jl") include("assets.jl") include("integrations.jl") diff --git a/src/ijulia.jl b/src/render.jl similarity index 69% rename from src/ijulia.jl rename to src/render.jl index d3868b3..2a11590 100644 --- a/src/ijulia.jl +++ b/src/render.jl @@ -8,21 +8,25 @@ DisplayedVisualizer(vis::Visualizer) = DisplayedVisualizer(vis.core) url(c::DisplayedVisualizer) = url(c.core) """ -Render a MeshCat visualizer inline in Jupyter or Juno. +Render a MeshCat visualizer inline in Jupyter, Juno, or VSCode. If this is the last command in a Jupyter notebook cell, then the visualizer should show up automatically in the corresponding output cell. If this is run from the Juno console, then the visualizer should show -up in the Juno plot pane. +up in the Juno plot pane. Likewise if this is run from VSCode with +the julia-vscode extension, then the visualizer should show up in the +Julia Plots pane. """ render(vis::Visualizer) = render(vis.core) render(core::CoreVisualizer) = DisplayedVisualizer(core) @deprecate IJuliaCell(v::Visualizer) render(v) -function Base.show(io::IO, ::MIME"text/html", frame::DisplayedVisualizer) +function Base.show(io::IO, + ::Union{MIME"text/html", MIME"juliavscode/html"}, + frame::DisplayedVisualizer) wait_for_server(frame.core) print(io, """
@@ -31,7 +35,9 @@ function Base.show(io::IO, ::MIME"text/html", frame::DisplayedVisualizer) """) end -function Base.show(io::IO, ::MIME"application/prs.juno.plotpane+html", d::DisplayedVisualizer) +function Base.show(io::IO, + ::MIME"application/prs.juno.plotpane+html", + d::DisplayedVisualizer) wait_for_server(d.core) print(io, """
From d5d75bf444caf48f8ef531b50382c8b34eeb8dde Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 10 Feb 2020 23:06:53 -0500 Subject: [PATCH 8/8] mention VSCode in readme [ci-skip] --- Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.md b/Readme.md index 864d2cc..33a9cb0 100644 --- a/Readme.md +++ b/Readme.md @@ -12,6 +12,7 @@ The MeshCat viewer runs entirely in the browser, with no external dependencies. * Inside a Jupyter Notebook with [IJulia.jl](https://github.com/JuliaLang/IJulia.jl) * In a standalone window with [Blink.jl](https://github.com/JunoLab/Blink.jl) * Inside the [Juno IDE](http://junolab.org/) +* Inside the VSCode editor with the [julia-vscode](https://www.julia-vscode.org/) extension. As much as possible, MeshCat.jl tries to use existing implementations of its fundamental types. In particular, we use: