Skip to content

Commit

Permalink
Merge pull request #101 from JuliaPlots/sd/the-new-makie
Browse files Browse the repository at this point in the history
use Makie instead of AbstractPlotting
  • Loading branch information
SimonDanisch authored May 19, 2021
2 parents c916485 + 8d16cb0 commit fc8a503
Show file tree
Hide file tree
Showing 18 changed files with 115 additions and 935 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ jobs:
fail-fast: false
matrix:
version:
- '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia.
- 'nightly'
- '1.5'
- '1.6'
os:
- ubuntu-latest
arch:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
/docs/site/
/debug/
/test/recorded
/test/Manifest.toml
8 changes: 4 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
authors = ["SimonDanisch <sdanisch@gmail.com>"]
name = "WGLMakie"
uuid = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"
authors = ["SimonDanisch <sdanisch@gmail.com>"]
version = "0.3.5"

[deps]
AbstractPlotting = "537997a7-5e4e-5d89-9595-2241ea00577e"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
FreeTypeAbstraction = "663a7486-cb36-511b-a19d-713bb74d65c9"
Expand All @@ -13,20 +12,21 @@ Hyperscript = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
JSServe = "824d6782-a2ef-11e9-3a09-e5662e0c26f9"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
ShaderAbstractions = "65257c39-d410-5151-9873-9b3e5be5013e"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[compat]
AbstractPlotting = "0.18"
Colors = "0.11, 0.12"
FileIO = "1.1"
FreeTypeAbstraction = "0.8, 0.9"
GeometryBasics = "0.3"
Hyperscript = "0.0.3, 0.0.4"
ImageMagick = "1.1"
JSServe = "1.2"
Observables = "0.3, 0.4"
Makie = "0.13"
Observables = "0.4"
ShaderAbstractions = "0.2.1"
StaticArrays = "0.12, 1.0"
julia = "1.3"
Expand Down
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@

![CI](https://github.com/JuliaPlots/WGLMakie.jl/workflows/CI/badge.svg)
[![Codecov](https://codecov.io/gh/JuliaPlots/WGLMakie.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaPlots/WGLMakie.jl)

WGLMakie is a WebGL backend for the [Makie.jl](https://www.github.com/JuliaPlots/Makie.jl) plotting package, implemented using Three.js.


Backend specific docs, for creating interactive and static html pages:

[![](https://img.shields.io/badge/docs-stable-blue.svg)](http://juliaplots.org/WGLMakie.jl/stable/)
Expand All @@ -14,7 +12,7 @@ Backend specific docs, for creating interactive and static html pages:

```julia
using Pkg
pkg"add WGLMakie AbstractPlotting"
pkg"add WGLMakie Makie"
```

## Teardown (if you want to uninstall)
Expand All @@ -24,19 +22,20 @@ using Pkg
pkg"rm WGLMakie"
```


# Usage

Now, it should just work like Makie:

```julia
using AbstractPlotting, WGLMakie
using Makie, WGLMakie

scatter(rand(4))
```

In the REPL, this will open a browser tab, that will refresh on a new display.
In VSCode, this should open in the plotpane.
You can also embed plots in a JSServe webpage:

```julia
function dom_handler(session, request)
return DOM.div(
Expand Down
11 changes: 10 additions & 1 deletion assets/mesh.frag
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,16 @@ float _normalize(float val, float from, float to){return (val-from) / (to - from
vec4 get_color(sampler2D color, vec2 uv, vec2 colorrange, sampler2D colormap){
float value = texture(color, uv).x;
float normed = _normalize(value, colorrange.x, colorrange.y);
return texture(colormap, vec2(normed, 0.0));
vec4 c = texture(colormap, vec2(normed, 0.0));

if (isnan(value)) {
c = get_nan_color();
} else if (value < colorrange.x) {
c = get_lowclip();
} else if (value > colorrange.y) {
c = get_highclip();
}
return c;
}

vec4 get_color(sampler2D color, vec2 uv, bool colorrange, sampler2D colormap){
Expand Down
3 changes: 3 additions & 0 deletions assets/mesh.vert
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ void main(){
// get_* gets the global inputs (uniform, sampler, position array)
// those functions will get inserted by the shader creation pipeline
vec3 vertex_position = tovec3(get_position());
if (isnan(vertex_position.z)) {
vertex_position.z = 0.0;
}
vec4 position_world = model * vec4(vertex_position, 1);

// normal in world space
Expand Down
30 changes: 16 additions & 14 deletions src/WGLMakie.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module WGLMakie
using Hyperscript
using JSServe
using Observables
using AbstractPlotting
using Makie
using Colors
using ShaderAbstractions
using LinearAlgebra
Expand All @@ -19,14 +19,15 @@ using JSServe.DOM
using ShaderAbstractions: VertexArray, Buffer, Sampler, AbstractSampler
using ShaderAbstractions: InstancedProgram

import AbstractPlotting.FileIO
using AbstractPlotting: get_texture_atlas, glyph_uv_width!, SceneSpace, Pixel
using AbstractPlotting: attribute_per_char, glyph_uv_width!, layout_text
using AbstractPlotting: MouseButtonEvent, KeyEvent
using AbstractPlotting: apply_transform, transform_func_obs
import Makie.FileIO
using Makie: get_texture_atlas, glyph_uv_width!, SceneSpace, Pixel
using Makie: attribute_per_char, glyph_uv_width!, layout_text
using Makie: MouseButtonEvent, KeyEvent
using Makie: apply_transform, transform_func_obs
using Makie: inline!

struct WebGL <: ShaderAbstractions.AbstractContext end
struct WGLBackend <: AbstractPlotting.AbstractBackend end
struct WGLBackend <: Makie.AbstractBackend end
#["https://unpkg.com/three@0.123.0/build/three.min.js"
const THREE = Dependency(:THREE,
["https://cdn.jsdelivr.net/gh/mrdoob/three.js/build/three.js"])
Expand All @@ -45,9 +46,9 @@ include("display.jl")

function activate!()
b = WGLBackend()
AbstractPlotting.register_backend!(b)
AbstractPlotting.current_backend[] = b
AbstractPlotting.set_glyph_resolution!(AbstractPlotting.Low)
Makie.register_backend!(b)
Makie.current_backend[] = b
Makie.set_glyph_resolution!(Makie.Low)
return
end

Expand All @@ -57,19 +58,20 @@ function __init__()
# Activate WGLMakie as backend!
activate!()
browser_display = JSServe.BrowserDisplay() in Base.Multimedia.displays
AbstractPlotting.inline!(!browser_display)
Makie.inline!(!browser_display)
# We need to update the texture atlas whenever it changes!
# We do this in three_plot!
AbstractPlotting.font_render_callback!() do sd, uv
Makie.font_render_callback!() do sd, uv
TEXTURE_ATLAS_CHANGED[] = true
end
end

for name in names(AbstractPlotting)
for name in names(Makie)
if name !== :Button && name !== :Slider
@eval import AbstractPlotting: $(name)
@eval import Makie: $(name)
@eval export $(name)
end
end
export inline!

end # module
26 changes: 13 additions & 13 deletions src/display.jl
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@

function JSServe.jsrender(session::Session, scene::Scene)
AbstractPlotting.update!(scene)
Makie.update!(scene)
three, canvas = WGLMakie.three_display(session, scene)
return canvas
end

function JSServe.jsrender(session::Session, scene::AbstractPlotting.FigureLike)
return JSServe.jsrender(session, AbstractPlotting.get_scene(scene))
function JSServe.jsrender(session::Session, scene::Makie.FigureLike)
return JSServe.jsrender(session, Makie.get_scene(scene))
end

const WEB_MIMES = (MIME"text/html", MIME"application/vnd.webio.application+html",
MIME"application/prs.juno.plotpane+html", MIME"juliavscode/html")

for M in WEB_MIMES
@eval begin
function AbstractPlotting.backend_show(::WGLBackend, io::IO, m::$M, scene::Scene)
function Makie.backend_show(::WGLBackend, io::IO, m::$M, scene::Scene)
three = nothing
inline_display = App() do session::Session
three, canvas = three_display(session, scene)
Expand Down Expand Up @@ -47,31 +47,31 @@ function scene2image(scene::Scene)
if done == :timed_out
error("JS Session not ready after 30s waiting, possibly errored while displaying")
end
return AbstractPlotting.colorbuffer(three)
return Makie.colorbuffer(three)
end

function AbstractPlotting.backend_show(::WGLBackend, io::IO, m::MIME"image/png",
function Makie.backend_show(::WGLBackend, io::IO, m::MIME"image/png",
scene::Scene)
img = scene2image(scene)
return FileIO.save(FileIO.Stream(FileIO.format"PNG", io), img)
end

function AbstractPlotting.backend_show(::WGLBackend, io::IO, m::MIME"image/jpeg",
function Makie.backend_show(::WGLBackend, io::IO, m::MIME"image/jpeg",
scene::Scene)
img = scene2image(scene)
return FileIO.save(FileIO.Stream(FileIO.format"JPEG", io), img)
end

function AbstractPlotting.backend_showable(::WGLBackend, ::T, scene::Scene) where {T<:MIME}
function Makie.backend_showable(::WGLBackend, ::T, scene::Scene) where {T<:MIME}
return T in WEB_MIMES
end

struct WebDisplay <: AbstractPlotting.AbstractScreen
struct WebDisplay <: Makie.AbstractScreen
three::Base.RefValue{Any}
display::Any
end

function AbstractPlotting.backend_display(::WGLBackend, scene::Scene)
function Makie.backend_display(::WGLBackend, scene::Scene)
# Reference to three object which gets set once we serve this to a browser
three_ref = Base.RefValue{Any}(nothing)
app = App() do s, request
Expand All @@ -90,13 +90,13 @@ end
function session2image(sessionlike)
s = JSServe.session(sessionlike)
to_data = js"document.querySelector('canvas').toDataURL()"
picture_base64 = JSServe.evaljs_value(s, to_data)
picture_base64 = JSServe.evaljs_value(s, to_data; time_out=100)
picture_base64 = replace(picture_base64, "data:image/png;base64," => "")
bytes = JSServe.Base64.base64decode(picture_base64)
return ImageMagick.load_(bytes)
end

function AbstractPlotting.colorbuffer(screen::ThreeDisplay)
function Makie.colorbuffer(screen::ThreeDisplay)
return session2image(screen)
end

Expand All @@ -121,7 +121,7 @@ function get_three(screen::WebDisplay; timeout = 30)
return nothing
end

function AbstractPlotting.colorbuffer(screen::WebDisplay)
function Makie.colorbuffer(screen::WebDisplay)
return session2image(get_three(screen))
end

Expand Down
2 changes: 1 addition & 1 deletion src/events.jl
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,6 @@ function connect_scene_events!(scene::Scene, comm::Observable)
return
end

function AbstractPlotting.pick(scene::Scene, THREE::ThreeDisplay, xy::Vec{2,Float64})
function Makie.pick(scene::Scene, THREE::ThreeDisplay, xy::Vec{2,Float64})
return @warn "Picking not supported yet by WGLMakie"
end
38 changes: 26 additions & 12 deletions src/imagelike.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using AbstractPlotting: el32convert, surface_normals, get_dim
using Makie: el32convert, surface_normals, get_dim

# Somehow we started using Nothing for some colors in Makie,
# but the convert leaves them at nothing -.-
# TODO clean this up in Makie
nothing_or_color(c) = to_color(c)
nothing_or_color(c::Nothing) = RGBAf0(0, 0, 0, 1)

function draw_mesh(mscene::Scene, mesh, plot; uniforms...)
uniforms = Dict(uniforms)
Expand All @@ -17,6 +22,7 @@ function draw_mesh(mscene::Scene, mesh, plot; uniforms...)
get!(uniforms, :colorrange, false)
get!(uniforms, :color, false)
get!(uniforms, :model, plot.model)

uniforms[:normalmatrix] = map(mscene.camera.view, plot.model) do v, m
i = SOneTo(3)
return transpose(inv(v[i, i] * m[i, i]))
Expand All @@ -25,16 +31,18 @@ function draw_mesh(mscene::Scene, mesh, plot; uniforms...)
end

function limits_to_uvmesh(plot)
px, py = plot[1], plot[2]
rectangle = lift(px, py) do x, y
xmin, xmax = extrema(x)
ymin, ymax = extrema(y)
return Rect2D(xmin, ymin, xmax - xmin, ymax - ymin)
px, py, pz = plot[1], plot[2], plot[3]
function grid(x, y, z, trans)
g = map(CartesianIndices(z)) do i
return Point3f0(get_dim(x, i, 1, size(z)), get_dim(y, i, 2, size(z)), 0.0)
end
return apply_transform(trans, vec(g))
end

positions = Buffer(lift(x -> decompose(Point2f0, x), rectangle))
faces = Buffer(lift(x -> decompose(GLTriangleFace, x), rectangle))
uv = Buffer(lift(decompose_uv, rectangle))
positions = Buffer(lift(grid, px, py, pz, transform_func_obs(plot)))
rect = lift(z -> Tesselation(Rect2D(0f0, 0f0, 1f0, 1f0), size(z)), pz)
faces = Buffer(lift(r -> decompose(GLTriangleFace, r), rect))
uv = Buffer(lift(decompose_uv, rect))

vertices = GeometryBasics.meta(positions; uv=uv)

Expand Down Expand Up @@ -67,7 +75,10 @@ function create_shader(mscene::Scene, plot::Surface)
return draw_mesh(mscene, mesh, plot; uniform_color=color, color=Vec4f0(0),
shading=plot.shading, ambient=plot.ambient, diffuse=plot.diffuse,
specular=plot.specular, shininess=plot.shininess,
lightposition=Vec3f0(1))
lightposition=Vec3f0(1),
highclip=lift(nothing_or_color, plot.highclip),
lowclip=lift(nothing_or_color, plot.lowclip),
nan_color=lift(nothing_or_color, plot.nan_color))
end

function create_shader(mscene::Scene, plot::Union{Heatmap,Image})
Expand All @@ -80,7 +91,10 @@ function create_shader(mscene::Scene, plot::Union{Heatmap,Image})
normals=Vec3f0(0), shading=false, ambient=plot.ambient,
diffuse=plot.diffuse, specular=plot.specular,
colorrange=haskey(plot, :colorrange) ? plot.colorrange : false,
shininess=plot.shininess, lightposition=Vec3f0(1))
shininess=plot.shininess, lightposition=Vec3f0(1),
highclip=lift(nothing_or_color, plot.highclip),
lowclip=lift(nothing_or_color, plot.lowclip),
nan_color=lift(nothing_or_color, plot.nan_color))
end

function create_shader(mscene::Scene, plot::Volume)
Expand All @@ -99,7 +113,7 @@ function create_shader(mscene::Scene, plot::Volume)
algorithm = lift(x -> Cuint(convert_attribute(x, key"algorithm"())), plot.algorithm)

return Program(WebGL(), lasset("volume.vert"), lasset("volume.frag"), box,
volumedata=Sampler(lift(AbstractPlotting.el32convert, vol)),
volumedata=Sampler(lift(Makie.el32convert, vol)),
modelinv=modelinv, colormap=Sampler(lift(to_colormap, plot.colormap)),
colorrange=lift(Vec2f0, plot.colorrange),
isovalue=lift(Float32, plot.isovalue),
Expand Down
8 changes: 6 additions & 2 deletions src/meshes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ end

function array2color(colors, cmap, crange)
cmap = RGBAf0.(Colors.color.(to_colormap(cmap)), 1.0)
return AbstractPlotting.interpolated_getindex.((cmap,), colors, (crange,))
return Makie.interpolated_getindex.((cmap,), colors, (crange,))
end

function array2color(colors::AbstractArray{<:Colorant}, cmap, crange)
Expand All @@ -28,7 +28,7 @@ function converted_attribute(plot::AbstractPlot, key::Symbol)
end
end

function create_shader(scene::Scene, plot::AbstractPlotting.Mesh)
function create_shader(scene::Scene, plot::Makie.Mesh)
# Potentially per instance attributes
mesh_signal = plot[1]
mattributes = GeometryBasics.attributes
Expand Down Expand Up @@ -104,6 +104,10 @@ function create_shader(scene::Scene, plot::AbstractPlotting.Mesh)
get!(uniforms, :model, plot.model)
get!(uniforms, :lightposition, Vec3f0(1))

get!(uniforms, :nan_color, RGBAf0(0, 0, 0, 0))
get!(uniforms, :highclip, RGBAf0(0, 0, 0, 0))
get!(uniforms, :lowclip, RGBAf0(0, 0, 0, 0))

uniforms[:normalmatrix] = map(scene.camera.view, plot.model) do v, m
i = SOneTo(3)
return transpose(inv(v[i, i] * m[i, i]))
Expand Down
Loading

0 comments on commit fc8a503

Please sign in to comment.