diff --git a/.gitignore b/.gitignore index 763513e..6009346 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .ipynb_checkpoints +deps/build.log + diff --git a/.travis.yml b/.travis.yml index a68155f..5a5286c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,9 @@ matrix: - os: linux julia: 0.7 env: TESTCMD="xvfb-run julia" + - os: linux + julia: 1.0 + env: TESTCMD="xvfb-run julia" - os: linux julia: nightly env: TESTCMD="xvfb-run julia" @@ -17,17 +20,17 @@ matrix: - os: osx julia: 0.7 env: TESTCMD="julia" + - os: osx + julia: 1.0 + env: TESTCMD="julia" - os: osx julia: nightly env: TESTCMD="julia" allow_failures: - julia: nightly - - julia: 0.7 notifications: email: false -git: - depth: 99999999 branches: only: @@ -43,13 +46,11 @@ addons: before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install ffmpeg; fi - script: # - julia -e 'Pkg.clone(pwd()); Pkg.build("MeshCat"); Pkg.test("MeshCat"; coverage=true)' - - $TESTCMD -e 'Pkg.clone(pwd()); Pkg.build("MeshCat"); Pkg.test("MeshCat"; coverage=true)' + - $TESTCMD -e 'if VERSION < v"0.7-"; Pkg.clone(pwd()); else; import Pkg; Pkg.add(Pkg.PackageSpec(path=pwd())); end; Pkg.build("MeshCat"); Pkg.test("MeshCat"; coverage=true)' - julia --code-coverage=user -e 'using MeshCat; MeshCat.develop_meshcat_assets(true)' - - julia -e 'Pkg.build("MeshCat")' + - julia -e 'if VERSION >= v"0.7-"; import Pkg; end; Pkg.build("MeshCat")' after_success: - # push coverage results to Coveralls # push coverage results to Codecov - - julia -e 'cd(Pkg.dir("MeshCat")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' + - julia -e 'if VERSION >= v"0.7-"; import Pkg; end; Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' diff --git a/REQUIRE b/REQUIRE index e7e96e3..ae617c1 100644 --- a/REQUIRE +++ b/REQUIRE @@ -16,8 +16,8 @@ Requires # Communication MsgPack 0.1.1 -WebIO 0.2.0 -Mux 0.2.3 -JSExpr 0.1.0 +WebIO # TODO: require v0.3.0 when we drop Julia v0.6 +Mux 0.2.3 # TODO: require v0.5.2 when we drop Julia v0.6 +JSExpr # TODO: require v0.3.0 when we drop Julia v0.6 AssetRegistry 0.0.1 JSON diff --git a/appveyor.yml b/appveyor.yml index 1b633ad..0c44ddd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,20 +1,24 @@ environment: matrix: - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe" - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe" - # - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - # - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" + - julia_version: 0.6 + - julia_version: 0.7 + - julia_version: 1 + - julia_version: nightly -# uncomment the following lines to allow failures on nightly julia +platform: + - x86 # 32-bit + - x64 # 64-bit + +# Uncomment the following lines to allow failures on nightly julia # (tests will run but not make your overall status red) matrix: - allow_failures: - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" + allow_failures: + - julia_version: nightly branches: only: - master + - /release-.*/ notifications: - provider: Email @@ -23,24 +27,18 @@ notifications: on_build_status_changed: false install: - - ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" -# If there's a newer build queued for the same PR, cancel this one - - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` - https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` - Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` - throw "There are newer queued builds for this pull request, failing early." } -# Download most recent Julia Windows binary - - ps: (new-object net.webclient).DownloadFile( - $env:JULIA_URL, - "C:\projects\julia-binary.exe") -# Run installer silently, output to C:\projects\julia - - C:\projects\julia-binary.exe /S /D=C:\projects\julia + - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1")) build_script: -# Need to convert from shallow to complete for Pkg.clone to work - - IF EXIST .git\shallow (git fetch --unshallow) - - C:\projects\julia\bin\julia -e "versioninfo(); - Pkg.clone(pwd(), \"MeshCat\"); Pkg.build(\"MeshCat\")" + - echo "%JL_BUILD_SCRIPT%" + - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%" test_script: - - C:\projects\julia\bin\julia -e "Pkg.test(\"MeshCat\")" + - echo "%JL_TEST_SCRIPT%" + - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%" + +# # Uncomment to support code coverage upload. Should only be enabled for packages +# # which would have coverage gaps without running on Windows +# on_success: +# - echo "%JL_CODECOV_SCRIPT%" +# - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%" \ No newline at end of file diff --git a/demo.ipynb b/demo.ipynb index 80f506a..083c931 100644 --- a/demo.ipynb +++ b/demo.ipynb @@ -17,6 +17,7 @@ "source": [ "# Activate the MeshCat package, and import some other \n", "# useful functions\n", + "using Compat\n", "using MeshCat\n", "using CoordinateTransformations\n", "import GeometryTypes: HyperRectangle, Vec, Point, HomogenousMesh, SignedDistanceField\n", @@ -335,7 +336,11 @@ "# just simple boxes. Let's load a 3D mesh and visualize it:\n", "using MeshIO\n", "using FileIO\n", - "cat_mesh = load(joinpath(Pkg.dir(\"GeometryTypes\"), \"test\", \"data\", \"cat.obj\"))\n", + "if VERSION < v\"0.7-\"\n", + " cat_mesh = load(joinpath(Pkg.dir(\"GeometryTypes\"), \"test\", \"data\", \"cat.obj\"))\n", + "else\n", + " cat_mesh = load(joinpath(dirname(pathof(GeometryTypes)), \"..\", \"test\", \"data\", \"cat.obj\"))\n", + "end\n", "setobject!(vis, cat_mesh)\n", "settransform!(vis, LinearMap(AngleAxis(pi/2, 1, 0, 0)))" ] @@ -399,7 +404,7 @@ "outputs": [], "source": [ "delete!(vis)\n", - "pointcloud = PointCloud([[x, 0 + 0.01 * randn(), 0.5] for x in linspace(-1, 1, 1000)])\n", + "pointcloud = PointCloud([[x, 0 + 0.01 * randn(), 0.5] for x in Compat.range(-1, stop=1, length=1000)])\n", "setobject!(vis[:pointcloud], pointcloud)" ] }, @@ -472,7 +477,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Julia 0.6.3", + "display_name": "Julia 0.6.4", "language": "julia", "name": "julia-0.6" }, @@ -480,7 +485,7 @@ "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", - "version": "0.6.3" + "version": "0.6.4" } }, "nbformat": 4, diff --git a/deps/build.jl b/deps/build.jl index 705f93c..bda179d 100644 --- a/deps/build.jl +++ b/deps/build.jl @@ -20,7 +20,7 @@ function update_meshcat() return end if isdir(meshcat_dir) && isfile(stamp_file) - stamped_sha = strip(open(readstring, stamp_file)) + stamped_sha = strip(open(f -> read(f, String), stamp_file)) if stamped_sha == meshcat_sha return else @@ -36,7 +36,7 @@ function update_meshcat() download_path = joinpath(download_dir, "meshcat.zip") run(download_cmd(meshcat_url, download_path)) run(unpack_cmd(download_path, download_dir, ".zip", nothing)) - mv(joinpath(download_dir, "meshcat-$meshcat_sha"), meshcat_dir; remove_destination=true) + Compat.mv(joinpath(download_dir, "meshcat-$meshcat_sha"), meshcat_dir; force=true) end open(stamp_file, "w") do file print(file, meshcat_sha) diff --git a/src/MeshCat.jl b/src/MeshCat.jl index 8cb8fe5..5c16500 100644 --- a/src/MeshCat.jl +++ b/src/MeshCat.jl @@ -2,22 +2,25 @@ __precompile__() module MeshCat -using Compat using WebIO +import Mux import AssetRegistry using GeometryTypes, CoordinateTransformations using Rotations: rotation_between, Rotation -using Colors: Color, Colorant, RGB, RGBA, alpha +using Colors: Color, Colorant, RGB, RGBA, alpha, hex using StaticArrays: StaticVector, SVector, SDiagonal using GeometryTypes: raw using Parameters: @with_kw -using Compat.UUIDs: UUID, uuid1 using DocStringExtensions: SIGNATURES using JSExpr: @js, @new, @var using Requires: @require using Base.Filesystem: rm using BinDeps: download_cmd, unpack_cmd -using Compat.LinearAlgebra: UniformScaling +using Compat +using Compat.UUIDs: UUID, uuid1 +using Compat.LinearAlgebra: UniformScaling, norm +using Compat.Sockets: listen +using Compat.Base64: base64encode import Base: delete!, length import MsgPack: pack, Ext @@ -73,7 +76,6 @@ include("servers.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 @@ -98,6 +100,16 @@ const ASSET_KEYS = String[] function __init__() push!(ASSET_KEYS, AssetRegistry.register(abspath(joinpath(VIEWER_ROOT, "main.min.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) + w + end + end end end diff --git a/src/lowering.jl b/src/lowering.jl index 342e454..a5f644c 100644 --- a/src/lowering.jl +++ b/src/lowering.jl @@ -115,8 +115,8 @@ end js_array_type(::Type{Float32}) = "Float32Array" js_array_type(::Type{UInt32}) = "Uint32Array" -struct PackedVector{V <: AbstractVector} # TODO: should require contiguous layout - data::V +struct PackedVector{T} + data::Vector{T} end function lower(points::Vector{P}) where {P <: Union{StaticVector, Colorant}} @@ -125,7 +125,8 @@ function lower(points::Vector{P}) where {P <: Union{StaticVector, Colorant}} Dict{String, Any}( "itemSize" => N, "type" => js_array_type(T), - "array" => PackedVector(reinterpret(T, points, (N * length(points),))), + "array" => PackedVector{T}( + reshape(reinterpret(T, points), (N * length(points),))), ) end diff --git a/src/msgpack.jl b/src/msgpack.jl index 608fef4..bd112e6 100644 --- a/src/msgpack.jl +++ b/src/msgpack.jl @@ -1,7 +1,10 @@ -extcode(v::PackedVector) = extcode(eltype(v.data)) +extcode(v::PackedVector{T}) where {T} = extcode(T) extcode(::Type{UInt8}) = 0x12 extcode(::Type{Int32}) = 0x15 extcode(::Type{UInt32}) = 0x16 extcode(::Type{Float32}) = 0x17 -pack(io::IO, v::PackedVector) = pack(io, Ext(extcode(v), reinterpret(UInt8, v.data, (sizeof(v.data),)))) +pack(io::IO, v::PackedVector) = pack(io, Ext(extcode(v), + convert(Vector{UInt8}, + reshape(reinterpret(UInt8, v.data), + (sizeof(v.data),))))) diff --git a/src/servers.jl b/src/servers.jl index 7a686be..a36c0d3 100644 --- a/src/servers.jl +++ b/src/servers.jl @@ -1,116 +1,48 @@ -module Servers - module MuxProvider - using Compat - using Mux - using WebIO - using JSON - - # This module was copied over from - # https://github.com/JuliaGizmos/WebIO.jl/blob/8e25ed2f9cf3158652907735bd44420205a105d9/src/providers/mux.jl - # to attempt to avoid inference issues described in: - # https://github.com/JuliaLang/julia/issues/21653 - function webio_serve(app, port=8000) - http = Mux.App(Mux.mux( - Mux.defaults, - app, - Mux.notfound() - )) - - websock = Mux.App(Mux.mux( - Mux.wdefaults, - route("/webio-socket", create_socket), - Mux.wclose, - Mux.notfound(), - )) - - serve(http, websock, port) - end - - struct WebSockConnection <: AbstractConnection - sock - end - - function create_socket(req) - sock = req[:socket] - conn = WebSockConnection(sock) - - t = @async while isopen(sock) - data = read(sock) +@static if VERSION < v"0.7-" + const IOError = Base.UVError +else + using Base: IOError +end - msg = JSON.parse(String(data)) - WebIO.dispatch(conn, msg) - end +Base.open(vis::Visualizer, args...; kw...) = open(vis.core, args...; kw...) - wait(t) - end +function Base.open(core::CoreVisualizer, port::Integer) + @async WebIO.webio_serve(Mux.page("/", req -> core.scope), port) + url = "http://127.0.0.1:$port" + Compat.@info("Serving MeshCat visualizer at $url") + open_url(url) +end - function Base.send(p::WebSockConnection, data) - write(p.sock, sprint(io->JSON.print(io,data))) +function open_url(url) + try + if Compat.Sys.iswindows() + run(`start $url`) + elseif Compat.Sys.isapple() + run(`open $url`) + elseif Compat.Sys.islinux() + run(`xdg-open $url`) end - - Base.isopen(p::WebSockConnection) = isopen(p.sock) - end - - using .MuxProvider: webio_serve - using MeshCat: CoreVisualizer, Visualizer - using Mux: page - using Compat - - Base.open(vis::Visualizer, args...; kw...) = open(vis.core, args...; kw...) - - function Base.open(core::CoreVisualizer, port::Integer) - webio_serve(page("/", req -> core.scope), port) - url = "http://127.0.0.1:$port" - Compat.@info("Serving MeshCat visualizer at $url") - open_url(url) + catch e + println("Could not open browser automatically: $e") + println("Please open the following URL in your browser:") + println(url) end +end - function open_url(url) - try - if is_windows() - run(`start $url`) - elseif is_apple() - run(`open $url`) - elseif is_linux() - run(`xdg-open $url`) - end +function Base.open(core::CoreVisualizer; default_port=8700, max_retries=500) + for port in default_port:(default_port + max_retries) + server = try + listen(port) catch e - println("Could not open browser automatically: $e") - println("Please open the following URL in your browser:") - println(url) - end - end - - function Base.open(core::CoreVisualizer; default_port=8700, max_retries=500) - for port in default_port + (0:max_retries) - server = try - listen(port) - catch e - if e isa Base.UVError - continue - end + if e isa IOError + continue 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) - end - end -end - -module BlinkInterface - using Requires - using MeshCat: CoreVisualizer - - @require Blink 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) - w 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) end end diff --git a/src/trees.jl b/src/trees.jl index 92f8f50..928d7dd 100644 --- a/src/trees.jl +++ b/src/trees.jl @@ -12,11 +12,19 @@ Base.size(p::Path) = size(p.entries) Base.IndexStyle(::Type{Path}) = IndexLinear() Base.convert(::Type{Path}, x::AbstractVector{<:AbstractString}) = Path(x) -Base.joinpath(p::Path, s...) = foldl(joinpath, p, s) +if VERSION < v"0.7-" + Base.joinpath(p::Path, s...) = foldl(joinpath, p, s) +else + Base.joinpath(p::Path, s...) = foldl(joinpath, s, init=p) +end Base.joinpath(p::Path, s::Symbol) = joinpath(p, String(s)) Base.joinpath(p::Path, s::AbstractString) = _joinpath(p, split(s, '/')) -_joinpath(p::Path, s::AbstractVector{<:AbstractString}) = foldl(_joinpath, p, s) +if VERSION < v"0.7-" + _joinpath(p::Path, s::AbstractVector{<:AbstractString}) = foldl(_joinpath, p, s) +else + _joinpath(p::Path, s::AbstractVector{<:AbstractString}) = foldl(_joinpath, s, init=p) +end _joinpath(p::Path, s::AbstractString) = isempty(s) ? Path(String[]) : Path(vcat(p.entries, s)) Base.show(io::IO, p::Path) = print(io, string('/', join(p.entries, '/'))) diff --git a/test/runtests.jl b/test/runtests.jl index be3f514..ab26384 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,3 +1,4 @@ +using Compat using Compat.Test using Compat.LinearAlgebra: UniformScaling using MeshCat @@ -11,18 +12,6 @@ using MeshIO, FileIO include("paths.jl") include("visualizer.jl") include("notebook.jl") - - @testset "deprecations" begin - # Deprecated in v0.0.2 - @testset "cylinder" begin - l = 1.0 - r = 2.0 - c = HyperCylinder(l, r) - @test c.origin == Point(0., 0, 0) - @test c.extremity == Point(0., 0, l) - @test radius(c) == r - end - end end module ModuleTest diff --git a/test/visualizer.jl b/test/visualizer.jl index 5986568..eb8bc8a 100644 --- a/test/visualizer.jl +++ b/test/visualizer.jl @@ -10,7 +10,7 @@ if !haskey(ENV, "CI") open(vis) end -if !(is_windows() && haskey(ENV, "CI")) +if !(Compat.Sys.iswindows() && haskey(ENV, "CI")) # this gets stuck on windows CI, but I don't know why open(vis, window) wait(vis) @@ -33,6 +33,13 @@ function GeometryTypes.decompose(::Type{P}, c::CustomGeometry) where {P <: Point convert(Vector{P}, [Point(0., 0, 0), Point(0., 1, 0), Point(0., 0, 1)]) end +@static if VERSION < v"0.7-" + const cat_mesh_path = joinpath(Pkg.dir("GeometryTypes"), "test", "data", "cat.obj") +else + import GeometryTypes + const cat_mesh_path = joinpath(dirname(pathof(GeometryTypes)), "..", "test", "data", "cat.obj") +end + @testset "self-contained visualizer" begin @testset "shapes" begin v = vis[:shapes] @@ -81,7 +88,7 @@ end @testset "meshes" begin v = vis[:meshes] @testset "cat" begin - mesh = load(joinpath(Pkg.dir("GeometryTypes"), "test", "data", "cat.obj")) + mesh = load(cat_mesh_path) setobject!(v[:cat], mesh) settransform!(v[:cat], Translation(0, -1, 0) ∘ LinearMap(RotZ(π)) ∘ LinearMap(RotX(π/2))) end @@ -113,7 +120,7 @@ end @testset "lines" begin v = vis[:circular_line] settransform!(v, Translation(-1, -1, 0)) - θ = linspace(0, 2π, 100) + θ = Compat.range(0, stop=2π, length=100) geometry = PointCloud(Point.(0.5 .* sin.(θ), 0, 0.5 .* cos.(θ))) setobject!(v, LineSegments(geometry, LineBasicMaterial())) end @@ -145,7 +152,7 @@ end sleep(1) -if !(is_windows() && haskey(ENV, "CI")) +if !(Compat.Sys.iswindows() && haskey(ENV, "CI")) # this also fails on appveyor, and again I have no way to debug it notinstalled && AtomShell.uninstall() end