diff --git a/.github/workflows/Docs.yml b/.github/workflows/Docs.yml index 8ce4a27aad5..43f5c944a31 100644 --- a/.github/workflows/Docs.yml +++ b/.github/workflows/Docs.yml @@ -24,7 +24,7 @@ jobs: persist-credentials: false # NOTE: Python is necessary for the pre-rendering (minification) step - name: Install python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.8' - name: Install binary dependencies @@ -45,7 +45,7 @@ jobs: DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --color=yes makedocs.jl - name: Upload site as artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Docs build path: ./docs/__site diff --git a/.github/workflows/cairomakie.yaml b/.github/workflows/cairomakie.yaml deleted file mode 100644 index 05646257e03..00000000000 --- a/.github/workflows/cairomakie.yaml +++ /dev/null @@ -1,62 +0,0 @@ -name: CairoMakie CI -on: - pull_request: - paths-ignore: - - 'docs/**' - - '*.md' - branches: - - master - - sd/beta-20 - push: - tags: - - '*' - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - test: - name: CairoMakie Julia ${{ matrix.version }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - version: - - '1.6' - - '1' # automatically expands to the latest stable 1.x release of Julia - os: - - ubuntu-20.04 - arch: - - x64 - steps: - - name: Checkout - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 - with: - version: ${{ matrix.version }} - arch: ${{ matrix.arch }} - - uses: julia-actions/cache@v1 - - name: Install Julia dependencies - shell: julia --project=monorepo {0} - run: | - using Pkg; - # dev mono repo versions - pkg"dev . ./MakieCore ./CairoMakie ./ReferenceTests" - - name: Run the tests - continue-on-error: true - run: > - julia --color=yes --project=monorepo -e 'using Pkg; Pkg.test("CairoMakie", coverage=true)' - && echo "TESTS_SUCCESSFUL=true" >> $GITHUB_ENV - - name: Upload test Artifacts - uses: actions/upload-artifact@v3 - with: - name: ReferenceImages_${{ matrix.os }}_${{ matrix.arch }}_${{ matrix.version }} - path: ./CairoMakie/test/recorded_reference_images/ - - name: Fail after artifacts if tests failed - if: ${{ env.TESTS_SUCCESSFUL != 'true' }} - run: exit 1 - - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v3 - with: - file: lcov.info diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 578c220fdf8..c462b0c8a0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Makie CI +name: Makie.jl on: pull_request: paths-ignore: @@ -18,7 +18,7 @@ concurrency: cancel-in-progress: true jobs: - test: + makie: name: Julia ${{ matrix.version }} runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/glmakie.yaml b/.github/workflows/glmakie.yaml deleted file mode 100644 index 647212fad29..00000000000 --- a/.github/workflows/glmakie.yaml +++ /dev/null @@ -1,82 +0,0 @@ -name: GLMakie CI -on: - pull_request: - paths-ignore: - - 'docs/**' - - '*.md' - branches: - - master - - sd/beta-20 - push: - tags: - - '*' - branches: - - master - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - test: - name: GLMakie Julia ${{ matrix.version }} - env: - MODERNGL_DEBUGGING: "true" # turn on errors when running OpenGL tests - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - version: - - '1.6' - - '1' # automatically expands to the latest stable 1.x release of Julia - os: - - ubuntu-20.04 - arch: - - x64 - steps: - - name: Checkout - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 - with: - version: ${{ matrix.version }} - arch: ${{ matrix.arch }} - - uses: julia-actions/cache@v1 - - run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev xsettingsd x11-xserver-utils - - name: Install Julia dependencies - shell: julia --project=monorepo {0} - run: | - using Pkg; - # dev mono repo versions - pkg"dev . ./MakieCore ./GLMakie ./ReferenceTests" - - name: Run the tests - id: referencetests - continue-on-error: true - run: > - DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --color=yes --project=monorepo -e 'using Pkg; Pkg.test("GLMakie", coverage=true)' - && echo "TESTS_SUCCESSFUL=true" >> $GITHUB_ENV - - name: Upload test Artifacts - uses: actions/upload-artifact@v3 - with: - name: ReferenceImages_${{ matrix.os }}_${{ matrix.arch }}_${{ matrix.version }} - path: | - ./GLMakie/test/recorded_reference_images/ - - name: Save number of missing refimages to file - env: - N_MISSING: ${{ steps.referencetests.outputs.n_missing_refimages }} - COMMIT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - run: | - mkdir -p ./n_missing - echo $N_MISSING > ./n_missing/n_missing_refimages - echo $COMMIT_SHA >> ./n_missing/n_missing_refimages - - name: Upload artifact with number of missing refimages - uses: actions/upload-artifact@v3 - with: - name: n_missing_refimages - path: n_missing/ - - name: Fail after artifacts if tests failed - if: ${{ env.TESTS_SUCCESSFUL != 'true' }} - run: exit 1 - - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v3 - with: - file: lcov.info diff --git a/.github/workflows/reference_tests.yml b/.github/workflows/reference_tests.yml new file mode 100644 index 00000000000..47a28f69985 --- /dev/null +++ b/.github/workflows/reference_tests.yml @@ -0,0 +1,177 @@ +name: Backends +on: + pull_request: + paths-ignore: + - 'docs/**' + - '*.md' + branches: + - master + push: + tags: + - '*' + branches: + - master + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + cairomakie: + name: CairoMakie Julia ${{ matrix.version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.6' + - '1' # automatically expands to the latest stable 1.x release of Julia + os: + - ubuntu-20.04 + arch: + - x64 + steps: + - name: Checkout + uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v1 + - name: Install Julia dependencies + shell: julia --project=monorepo {0} + run: | + using Pkg; + # dev mono repo versions + pkg"dev . ./MakieCore ./CairoMakie ./ReferenceTests" + - name: Run the tests + continue-on-error: true + run: > + julia --color=yes --project=monorepo -e 'using Pkg; Pkg.test("CairoMakie", coverage=true)' + && echo "TESTS_SUCCESSFUL=true" >> $GITHUB_ENV + - name: Upload test Artifacts + if: matrix.version == '1' + uses: actions/upload-artifact@v4 + with: + name: ReferenceImages_CairoMakie + path: ./CairoMakie/test/recorded_reference_images/ + - name: Fail after artifacts if tests failed + if: ${{ env.TESTS_SUCCESSFUL != 'true' }} + run: exit 1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v3 + with: + file: lcov.info + + glmakie: + name: GLMakie Julia ${{ matrix.version }} + env: + MODERNGL_DEBUGGING: "true" # turn on errors when running OpenGL tests + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.6' + - '1' # automatically expands to the latest stable 1.x release of Julia + os: + - ubuntu-20.04 + arch: + - x64 + steps: + - name: Checkout + uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v1 + - run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev xsettingsd x11-xserver-utils + - name: Install Julia dependencies + shell: julia --project=monorepo {0} + run: | + using Pkg; + # dev mono repo versions + pkg"dev . ./MakieCore ./GLMakie ./ReferenceTests" + - name: Run the tests + id: referencetests + continue-on-error: true + run: > + DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --color=yes --project=monorepo -e 'using Pkg; Pkg.test("GLMakie", coverage=true)' + && echo "TESTS_SUCCESSFUL=true" >> $GITHUB_ENV + - name: Upload test Artifacts + if: matrix.version == '1' + uses: actions/upload-artifact@v4 + with: + name: ReferenceImages_GLMakie + path: | + ./GLMakie/test/recorded_reference_images/ + - name: Save number of missing refimages to file + if: matrix.version == '1' + env: + N_MISSING: ${{ steps.referencetests.outputs.n_missing_refimages }} + COMMIT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + run: | + mkdir -p ./n_missing + echo $N_MISSING > ./n_missing/n_missing_refimages + echo $COMMIT_SHA >> ./n_missing/n_missing_refimages + - name: Upload artifact with number of missing refimages + if: matrix.version == '1' + uses: actions/upload-artifact@v4 + with: + name: n_missing_refimages + path: n_missing/ + - name: Fail after artifacts if tests failed + if: ${{ env.TESTS_SUCCESSFUL != 'true' }} + run: exit 1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v3 + with: + file: lcov.info + + wglmakie: + name: WGLMakie Julia ${{ matrix.version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.6' + - '1' # automatically expands to the latest stable 1.x release of Julia + os: + - ubuntu-20.04 + arch: + - x64 + steps: + - name: Checkout + uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v1 + - run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev + - name: Install Julia dependencies + shell: julia --project=monorepo {0} + run: | + using Pkg; + # dev mono repo versions + pkg"dev . ./MakieCore ./WGLMakie ./ReferenceTests" + - name: Run the tests + continue-on-error: true + run: > + DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --color=yes --project=monorepo -e 'using Pkg; Pkg.test("WGLMakie", coverage=true)' + && echo "TESTS_SUCCESSFUL=true" >> $GITHUB_ENV + - name: Upload test Artifacts + if: matrix.version == '1' + uses: actions/upload-artifact@v4 + with: + name: ReferenceImages_WGLMakie + path: ./WGLMakie/test/recorded_reference_images/ + - name: Fail after artifacts if tests failed + if: ${{ env.TESTS_SUCCESSFUL != 'true' }} + run: exit 1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v3 + with: + file: lcov.info diff --git a/.github/workflows/refimages_status.yaml b/.github/workflows/refimages_status.yaml index 28de7475326..90aadcf56ec 100644 --- a/.github/workflows/refimages_status.yaml +++ b/.github/workflows/refimages_status.yaml @@ -2,7 +2,7 @@ name: Reference Image Status on: workflow_run: - workflows: [GLMakie CI] + workflows: [Backends] types: - completed diff --git a/.github/workflows/relocatability.yml b/.github/workflows/relocatability.yml new file mode 100644 index 00000000000..5612a9e08e8 --- /dev/null +++ b/.github/workflows/relocatability.yml @@ -0,0 +1,45 @@ +name: Relocatability +on: + pull_request: + paths-ignore: + - 'docs/**' + - '*.md' + branches: + - master + push: + tags: + - '*' + branches: + - master + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + glmakie: + name: GLMakie relocatability + env: + MODERNGL_DEBUGGING: "true" # turn on errors when running OpenGL tests + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1' # automatically expands to the latest stable 1.x release of Julia + os: + - ubuntu-20.04 + arch: + - x64 + steps: + - name: Checkout + uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v1 + - run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev xsettingsd x11-xserver-utils + - name: Relocatability test + run: > + DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia ./relocatability.jl diff --git a/.github/workflows/rprmakie.yaml b/.github/workflows/rprmakie.yaml index a5ba8020fc6..631c1ede9bb 100644 --- a/.github/workflows/rprmakie.yaml +++ b/.github/workflows/rprmakie.yaml @@ -50,7 +50,7 @@ jobs: julia --color=yes --project=monorepo -e 'using Pkg; Pkg.test("RPRMakie", coverage=true)' && echo "TESTS_SUCCESSFUL=true" >> $GITHUB_ENV - name: Upload test Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ReferenceImages_${{ matrix.os }}_${{ matrix.arch }}_${{ matrix.version }} path: ./RPRMakie/test/recorded diff --git a/.github/workflows/wglmakie.yaml b/.github/workflows/wglmakie.yaml deleted file mode 100644 index 6265a58419c..00000000000 --- a/.github/workflows/wglmakie.yaml +++ /dev/null @@ -1,65 +0,0 @@ -name: WGLMakie CI -on: - pull_request: - paths-ignore: - - 'docs/**' - - '*.md' - branches: - - master - - sd/beta-20 - push: - tags: - - '*' - branches: - - master - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - test: - name: WGLMakie Julia ${{ matrix.version }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - version: - - '1.6' - - '1' # automatically expands to the latest stable 1.x release of Julia - os: - - ubuntu-20.04 - arch: - - x64 - steps: - - name: Checkout - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 - with: - version: ${{ matrix.version }} - arch: ${{ matrix.arch }} - - uses: julia-actions/cache@v1 - - run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev - - name: Install Julia dependencies - shell: julia --project=monorepo {0} - run: | - using Pkg; - # dev mono repo versions - pkg"dev . ./MakieCore ./WGLMakie ./ReferenceTests" - - name: Run the tests - continue-on-error: true - run: > - DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --color=yes --project=monorepo -e 'using Pkg; Pkg.test("WGLMakie", coverage=true)' - && echo "TESTS_SUCCESSFUL=true" >> $GITHUB_ENV - - name: Upload test Artifacts - uses: actions/upload-artifact@v3 - with: - name: ReferenceImages_${{ matrix.os }}_${{ matrix.arch }}_${{ matrix.version }} - path: ./WGLMakie/test/recorded_reference_images/ - - name: Fail after artifacts if tests failed - if: ${{ env.TESTS_SUCCESSFUL != 'true' }} - run: exit 1 - - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v3 - with: - file: lcov.info diff --git a/CairoMakie/Project.toml b/CairoMakie/Project.toml index 65d5a532dc8..84723911ef5 100644 --- a/CairoMakie/Project.toml +++ b/CairoMakie/Project.toml @@ -1,7 +1,7 @@ name = "CairoMakie" uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" author = ["Simon Danisch "] -version = "0.11.3" +version = "0.11.4" [deps] CRC32c = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" @@ -24,7 +24,7 @@ FileIO = "1.1" FreeType = "3, 4.0" GeometryBasics = "0.4.1" LinearAlgebra = "1.0, 1.6" -Makie = "=0.20.2" +Makie = "=0.20.3" PrecompileTools = "1.0" julia = "1.3" diff --git a/GLMakie/Project.toml b/GLMakie/Project.toml index aa45ea734c7..a2b334de66d 100644 --- a/GLMakie/Project.toml +++ b/GLMakie/Project.toml @@ -1,6 +1,6 @@ name = "GLMakie" uuid = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" -version = "0.9.3" +version = "0.9.4" [deps] ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" @@ -30,7 +30,7 @@ FreeTypeAbstraction = "0.10" GLFW = "3.3" GeometryBasics = "0.4.1" LinearAlgebra = "1.0, 1.6" -Makie = "=0.20.2" +Makie = "=0.20.3" Markdown = "1.0, 1.6" MeshIO = "0.4" ModernGL = "1" diff --git a/GLMakie/assets/shader/distance_shape.frag b/GLMakie/assets/shader/distance_shape.frag index 6fdeda71b3f..899225239a3 100644 --- a/GLMakie/assets/shader/distance_shape.frag +++ b/GLMakie/assets/shader/distance_shape.frag @@ -27,6 +27,7 @@ uniform float glow_width; uniform int shape; // shape is a uniform for now. Making them a in && using them for control flow is expected to kill performance uniform float px_per_unit; uniform bool transparent_picking; +uniform bool fxaa; flat in float f_viewport_from_u_scale; flat in float f_distancefield_scale; @@ -123,6 +124,14 @@ void stroke(vec4 strokecolor, float signed_distance, float width, inout vec4 col } } +void stroke_fxaa(vec4 strokecolor, float signed_distance, float width, inout vec4 color){ + if (width != 0.0){ + float t = step(min(width, 0.0), signed_distance) - step(max(width, 0.0), signed_distance); + vec4 bg_color = mix(color, vec4(strokecolor.rgb, 0), float(signed_distance < 0.5 * width)); + color = mix(bg_color, strokecolor, t); + } +} + void glow(vec4 glowcolor, float signed_distance, float inside, inout vec4 color){ if (glow_width > 0.0){ float s_stroke_width = px_per_unit * stroke_width; @@ -179,16 +188,30 @@ void main(){ float s_stroke_width = px_per_unit * stroke_width; float inside_start = max(-s_stroke_width, 0.0); - float inside = aastep(inside_start, signed_distance); - - // For the initial coloring we can use the base pixel color and modulate - // its alpha value to create the shape set by the signed distance field. (i.e. inside) vec4 final_color = fill(f_color, image, tex_uv); - final_color.a = final_color.a * inside; - // Stroke and glow need to also modulate colors (rgb) to smoothly transition - // from one to another. - stroke(f_stroke_color, signed_distance, -s_stroke_width, final_color); + if (!fxaa){ // anti-aliasing via sdf + // For the initial coloring we can use the base pixel color and modulate + // its alpha value to create the shape set by the signed distance field. (i.e. inside) + float inside = aastep(inside_start, signed_distance); + final_color.a = final_color.a * inside; + + // Stroke and glow need to also modulate colors (rgb) to smoothly transition + // from one to another. + stroke(f_stroke_color, signed_distance, -s_stroke_width, final_color); + + } else { // AA via FXAA + // Here we don't smooth edges (i.e. use step rather than smoothstep) and + // let fxaa figure out smoothing/anti-aliasing later. This fixes the + // halo artifact when rendering at different depths for solid colors + float inside = step(inside_start, signed_distance); + final_color.a = final_color.a * inside; + + stroke_fxaa(f_stroke_color, signed_distance, -s_stroke_width, final_color); + } + + // glow is always semi transparent so switching between step and smoothstep + // is mostly useless here glow(f_glow_color, signed_distance, aastep(-s_stroke_width, signed_distance), final_color); diff --git a/GLMakie/assets/shader/lines.frag b/GLMakie/assets/shader/lines.frag index 644e7a6c9d3..0de6518c2da 100644 --- a/GLMakie/assets/shader/lines.frag +++ b/GLMakie/assets/shader/lines.frag @@ -14,6 +14,7 @@ flat in vec2 f_uv_minmax; {{pattern_type}} pattern; uniform float pattern_length; +uniform bool fxaa; // Half width of antialiasing smoothstep #define ANTIALIAS_RADIUS 0.8 @@ -60,15 +61,22 @@ void main(){ vec4 color = vec4(f_color.rgb, 0.0); vec2 xy = get_sd(pattern, f_uv); - float alpha = aastep(0.0, xy.x); - float alpha2 = aastep(-f_thickness, f_thickness, xy.y); - float alpha3 = aastep_scaled(f_uv_minmax.x, f_uv_minmax.y, f_uv.x); + float alpha, alpha2, alpha3; + if (!fxaa) { + alpha = aastep(0.0, xy.x); + alpha2 = aastep(-f_thickness, f_thickness, xy.y); + alpha3 = aastep_scaled(f_uv_minmax.x, f_uv_minmax.y, f_uv.x); + } else { + alpha = step(0.0, xy.x); + alpha2 = step(-f_thickness, xy.y) - step(f_thickness, xy.y); + alpha3 = step(f_uv_minmax.x, f_uv.x) - step(f_uv_minmax.y, f_uv.x); + } color = vec4(f_color.rgb, f_color.a * alpha * alpha2 * alpha3); // Debug: Show uv values in line direction (repeating) // color = vec4(mod(f_uv.x, 1.0), 0, 0, 1); - + // Debug: Show uv values in line direction with pattern // color.r = 0.5; // color.g = mod(f_uv.x, 1.0); @@ -79,6 +87,5 @@ void main(){ // color.r = 1 - color.a; // color.a = 0.5 + 0.5 * color.a; - write2framebuffer(color, f_id); } diff --git a/GLMakie/src/GLAbstraction/GLAbstraction.jl b/GLMakie/src/GLAbstraction/GLAbstraction.jl index 67657c15191..e8c36de91c7 100644 --- a/GLMakie/src/GLAbstraction/GLAbstraction.jl +++ b/GLMakie/src/GLAbstraction/GLAbstraction.jl @@ -6,6 +6,7 @@ using Makie using FixedPointNumbers using ColorTypes using ..GLMakie.GLFW +using ..GLMakie: ShaderSource using Printf using LinearAlgebra using Observables diff --git a/GLMakie/src/GLAbstraction/GLShader.jl b/GLMakie/src/GLAbstraction/GLShader.jl index bddc6e61302..0313256dea7 100644 --- a/GLMakie/src/GLAbstraction/GLShader.jl +++ b/GLMakie/src/GLAbstraction/GLShader.jl @@ -103,73 +103,57 @@ function ShaderCache(context) ) end - abstract type AbstractLazyShader end + struct LazyShader <: AbstractLazyShader shader_cache::ShaderCache - paths::Tuple + paths::Vector{ShaderSource} kw_args::Dict{Symbol, Any} - function LazyShader(cache::ShaderCache, paths...; kw_args...) + function LazyShader(cache::ShaderCache, paths::ShaderSource...; kw_args...) args = Dict{Symbol, Any}(kw_args) get!(args, :view, Dict{String, String}()) - new(cache, paths, args) + new(cache, [paths...], args) end end gl_convert(shader::GLProgram, data) = shader - -# TODO remove this silly constructor -function compile_shader(source::Vector{UInt8}, typ, name) - shaderid = createshader(typ) - glShaderSource(shaderid, source) +function compile_shader(source::ShaderSource, template_src::String) + name = source.name + shaderid = createshader(source.typ) + glShaderSource(shaderid, template_src) glCompileShader(shaderid) if !GLAbstraction.iscompiled(shaderid) GLAbstraction.print_with_lines(String(source)) @warn("shader $(name) didn't compile. \n$(GLAbstraction.getinfolog(shaderid))") end - return Shader(name, source, typ, shaderid) + return Shader(name, Vector{UInt8}(template_src), source.typ, shaderid) end -function compile_shader(path, source_str::AbstractString) - typ = shadertype(splitext(path)[2]) - source = Vector{UInt8}(source_str) - name = Symbol(path) - return compile_shader(source, typ, name) -end -function get_shader!(cache::ShaderCache, path, template_replacement) +function get_shader!(cache::ShaderCache, src::ShaderSource, template_replacement) # this should always be in here, since we already have the template keys - shader_dict = cache.shader_cache[path] + shader_dict = cache.shader_cache[src.name] return get!(shader_dict, template_replacement) do - template_source = read(path, String) - source = mustache_replace(template_replacement, template_source) + templated_source = mustache_replace(template_replacement, src.source) ShaderAbstractions.switch_context!(cache.context) - return compile_shader(path, source) + return compile_shader(src, templated_source) end::Shader end -function get_template!(cache::ShaderCache, path, view, attributes) - return get!(cache.template_cache, path) do - _, ext = splitext(path) - - typ = shadertype(ext) - template_source = read(path, String) - source, replacements = template2source( - template_source, view, attributes - ) - s = compile_shader(path, source) +function get_template!(cache::ShaderCache, src::ShaderSource, view, attributes) + return get!(cache.template_cache, src.name) do + templated_source, replacements = template2source(src.source, view, attributes) + shader = compile_shader(src, templated_source) template_keys = collect(keys(replacements)) template_replacements = collect(values(replacements)) # can't yet be in here, since we didn't even have template keys - cache.shader_cache[path] = Dict(template_replacements => s) - + cache.shader_cache[src.name] = Dict(template_replacements => shader) return template_keys end end - -function compile_program(shaders, fragdatalocation) +function compile_program(shaders::Vector{Shader}, fragdatalocation) # Remove old shaders program = createprogram() #attach new ones @@ -210,55 +194,33 @@ gl_convert(lazyshader::AbstractLazyShader, data) = error("gl_convert shader") function gl_convert(lazyshader::LazyShader, data) gl_convert(lazyshader.shader_cache, lazyshader, data) end + function gl_convert(cache::ShaderCache, lazyshader::AbstractLazyShader, data) kw_dict = lazyshader.kw_args paths = lazyshader.paths - if all(x-> isa(x, Shader), paths) - fragdatalocation = get(kw_dict, :fragdatalocation, Tuple{Int, String}[]) - ShaderAbstractions.switch_context!(cache.context) - return compile_program([paths...], fragdatalocation) - end + v = get_view(kw_dict) fragdatalocation = get(kw_dict, :fragdatalocation, Tuple{Int, String}[]) - # Tuple(Source, ShaderType) - if all(paths) do x - isa(x, Tuple) && length(x) == 2 && - isa(first(x), AbstractString) && - isa(last(x), GLenum) - end - # we don't cache view & templates for shader strings! - shaders = map(paths) do source_typ - source, typ = source_typ - src, _ = template2source(source, v, data) - ShaderAbstractions.switch_context!(cache.context) - compile_shader(Vector{UInt8}(src), typ, :from_string) - end - ShaderAbstractions.switch_context!(cache.context) - return compile_program([shaders...], fragdatalocation) - end - if !all(x -> isa(x, AbstractString), paths) - error("Please supply only paths or tuples of (source, typ) for Lazy Shader - Found: $paths" - ) - end template_keys = Vector{Vector{String}}(undef, length(paths)) replacements = Vector{Vector{String}}(undef, length(paths)) - for (i, path) in enumerate(paths) - template = get_template!(cache, path, v, data) + + for (i, shader_source) in enumerate(paths) + template = get_template!(cache, shader_source, v, data) template_keys[i] = template replacements[i] = String[mustache2replacement(t, v, data) for t in template] end + return get!(cache.program_cache, (paths, replacements)) do # when we're here, this means there were uncached shaders, meaning we definitely have # to compile a new program shaders = Vector{Shader}(undef, length(paths)) - for (i, path) in enumerate(paths) + for (i, shader_source) in enumerate(paths) tr = Dict(zip(template_keys[i], replacements[i])) - shaders[i] = get_shader!(cache, path, tr) + shaders[i] = get_shader!(cache, shader_source, tr) end ShaderAbstractions.switch_context!(cache.context) - compile_program(shaders, fragdatalocation) + return compile_program(shaders, fragdatalocation) end end @@ -346,7 +308,6 @@ function mustache2replacement(mustache_key, view, attributes) end # Takes a shader template and renders the template and returns shader source -template2source(source::Vector{UInt8}, view, attributes::Dict{Symbol, Any}) = template2source(String(source), attributes, view) function template2source(source::AbstractString, view, attributes::Dict{Symbol, Any}) replacements = Dict{String, String}() source = mustache_replace(source) do mustache_key diff --git a/GLMakie/src/GLAbstraction/GLTypes.jl b/GLMakie/src/GLAbstraction/GLTypes.jl index 19d7123e4fa..206a8dda42a 100644 --- a/GLMakie/src/GLAbstraction/GLTypes.jl +++ b/GLMakie/src/GLAbstraction/GLTypes.jl @@ -28,7 +28,7 @@ struct Shader id::GLuint context::GLContext function Shader(name, source, typ, id) - new(name, source, typ, id, current_context()) + new(Symbol(name), source, typ, id, current_context()) end end diff --git a/GLMakie/src/GLMakie.jl b/GLMakie/src/GLMakie.jl index 5af488594da..47361006a81 100644 --- a/GLMakie/src/GLMakie.jl +++ b/GLMakie/src/GLMakie.jl @@ -41,16 +41,29 @@ end import ShaderAbstractions: Sampler, Buffer export Sampler, Buffer +struct ShaderSource + typ::GLenum + source::String + name::String +end + +function ShaderSource(path) + typ = GLAbstraction.shadertype(splitext(path)[2]) + source = read(path, String) + name = String(path) + return ShaderSource(typ, source, name) +end + const GL_ASSET_DIR = RelocatableFolders.@path joinpath(@__DIR__, "..", "assets") const SHADER_DIR = RelocatableFolders.@path joinpath(GL_ASSET_DIR, "shader") -const LOADED_SHADERS = Dict{String, String}() +const LOADED_SHADERS = Dict{String, ShaderSource}() function loadshader(name) # Turns out, joinpath is so slow, that it actually makes sense # To memoize it :-O # when creating 1000 plots with the PlotSpec API, timing drop from 1.5s to 1s just from this change: return get!(LOADED_SHADERS, name) do - return joinpath(SHADER_DIR, name) + return ShaderSource(joinpath(SHADER_DIR, name)) end end diff --git a/GLMakie/src/drawing_primitives.jl b/GLMakie/src/drawing_primitives.jl index 9fa55f52b8e..9a66f835321 100644 --- a/GLMakie/src/drawing_primitives.jl +++ b/GLMakie/src/drawing_primitives.jl @@ -571,6 +571,8 @@ function draw_atomic(screen::Screen, scene::Scene, gl_attributes[:uv_offset_width] = uv_offset_width gl_attributes[:distancefield] = get_texture!(atlas) gl_attributes[:visible] = plot.visible + gl_attributes[:fxaa] = get(plot, :fxaa, Observable(false)) + gl_attributes[:depthsorting] = get(plot, :depthsorting, false) cam = scene.camera # gl_attributes[:preprojection] = Observable(Mat4f(I)) gl_attributes[:preprojection] = lift(plot, space, markerspace, cam.projectionview, cam.resolution) do s, ms, pv, res @@ -802,11 +804,14 @@ function draw_atomic(screen::Screen, scene::Scene, plot::Volume) ) return convert(Mat4f, m) * m2 end + interp = to_value(pop!(gl_attributes, :interpolate)) + interp = interp ? :linear : :nearest + Tex(x) = Texture(x; minfilter=interp) if haskey(gl_attributes, :intensity) intensity = pop!(gl_attributes, :intensity) - return draw_volume(screen, intensity, gl_attributes) + return draw_volume(screen, Tex(intensity), gl_attributes) else - return draw_volume(screen, plot[4], gl_attributes) + return draw_volume(screen, Tex(plot[4]), gl_attributes) end end end diff --git a/GLMakie/src/glshaders/particles.jl b/GLMakie/src/glshaders/particles.jl index 90e3aab1575..b0066970c23 100644 --- a/GLMakie/src/glshaders/particles.jl +++ b/GLMakie/src/glshaders/particles.jl @@ -176,6 +176,20 @@ function draw_scatter(screen, (marker, position), data) rot = vec2quaternion(rot) delete!(data, :rotation) + if to_value(pop!(data, :depthsorting, false)) + data[:indices] = map( + data[:projectionview], data[:preprojection], data[:model], + position + ) do pv, pp, m, pos + T = pv * pp * m + depth_vals = map(pos) do p + p4d = T * to_ndim(Point4f, to_ndim(Point3f, p, 0f0), 1f0) + p4d[3] / p4d[4] + end + UInt32.(sortperm(depth_vals, rev = true) .- 1) + end |> indexbuffer + end + @gen_defaults! data begin shape = Cint(0) position = position => GLBuffer @@ -194,6 +208,7 @@ function draw_scatter(screen, (marker, position), data) return shape end end + @gen_defaults! data begin quad_offset = Vec2f(0) => GLBuffer intensity = nothing => GLBuffer @@ -226,6 +241,7 @@ function draw_scatter(screen, (marker, position), data) scale_primitive = true gl_primitive = GL_POINTS end + # Exception for intensity, to make it possible to handle intensity with a # different length compared to position. Intensities will be interpolated in that case data[:intensity] = intensity_convert(intensity, position) diff --git a/GLMakie/test/glmakie_refimages.jl b/GLMakie/test/glmakie_refimages.jl index 4c08416ffda..7aadbc3fa4b 100644 --- a/GLMakie/test/glmakie_refimages.jl +++ b/GLMakie/test/glmakie_refimages.jl @@ -162,5 +162,32 @@ end p = mesh!(scene, Rect3f(Point3f(-10, -10, 0.01), Vec3f(20, 20, 0.02)), color = :white) update_cam!(scene, Vec3f(0, 0, 7), Vec3f(0, 0, 0), Vec3f(0, 1, 0)) + scene +end + +@reference_test "Signed Distance Field - FXAA interaction" begin + scene = Scene(size = (300, 200), camera = campixel!) + + # scatter/text shader + xs = 20:20:280 + ys = fill(170, length(xs)) + zs = range(3, 1, length=length(xs)) + scatter!(scene, xs, ys, zs, color = :blue, markersize = 40, fxaa = false) + ys = fill(130, length(xs)) + scatter!(scene, xs, ys, zs, color = :blue, markersize = 40, fxaa = true) + ys = fill(90, length(xs)) + scatter!(scene, xs, ys, zs, color = :blue, markersize = 40, depthsorting = true) + + # lines/linesegments shader + xs = 20:10:270 + ys = [50 + shift for _ in 1:13 for shift in (-10, 10)] + zs = range(3, 1, length=length(xs)) + lines!(scene, xs, ys, zs, color = :blue, linewidth = 4, fxaa = false) + ys = [20 + shift for _ in 1:13 for shift in (-10, 10)] + lines!(scene, xs, ys, zs, color = :blue, linewidth = 4, fxaa = true) + + # create some harder contrasts + mesh!(scene, Rect2f(0, 0, 300, 200), color = :red) + scene end \ No newline at end of file diff --git a/GLMakie/test/unit_tests.jl b/GLMakie/test/unit_tests.jl index 9ae5d6ca161..409247bc075 100644 --- a/GLMakie/test/unit_tests.jl +++ b/GLMakie/test/unit_tests.jl @@ -6,6 +6,46 @@ function project_sp(scene, point) return point_px .+ offset end +@testset "shader cache" begin + GLMakie.closeall() + screen = display(Figure()) + cache = screen.shader_cache + # Postprocessing shaders + @test length(cache.shader_cache) == 5 + @test length(cache.template_cache) == 5 + @test length(cache.program_cache) == 4 + + # Shaders for scatter + linesegments + poly etc (axis) + display(screen, scatter(1:4)) + @test length(cache.shader_cache) == 16 + @test length(cache.template_cache) == 16 + @test length(cache.program_cache) == 10 + + # No new shaders should be added: + display(screen, scatter(1:4)) + @test length(cache.shader_cache) == 16 + @test length(cache.template_cache) == 16 + @test length(cache.program_cache) == 10 + + # Same for linesegments + display(screen, linesegments(1:4)) + @test length(cache.shader_cache) == 16 + @test length(cache.template_cache) == 16 + @test length(cache.program_cache) == 10 + + # Lines hasn't been compiled so one new program should be added + display(screen, lines(1:4)) + @test length(cache.shader_cache) == 18 + @test length(cache.template_cache) == 18 + @test length(cache.program_cache) == 11 + + # For second time no new shaders should be added + display(screen, lines(1:4)) + @test length(cache.shader_cache) == 18 + @test length(cache.template_cache) == 18 + @test length(cache.program_cache) == 11 +end + @testset "unit tests" begin GLMakie.closeall() @testset "Window handling" begin @@ -250,7 +290,7 @@ end @test screen.root_scene === nothing @test screen.rendertask === nothing - @test (Base.summarysize(screen) / 10^6) < 1.22 + @test (Base.summarysize(screen) / 10^6) < 1.4 end # All should go to pool after close @test all(x-> x in GLMakie.SCREEN_REUSE_POOL, screens) diff --git a/MakieCore/Project.toml b/MakieCore/Project.toml index d2ef60aa7e1..e510242ce42 100644 --- a/MakieCore/Project.toml +++ b/MakieCore/Project.toml @@ -1,7 +1,7 @@ name = "MakieCore" uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" authors = ["Simon Danisch"] -version = "0.7.1" +version = "0.7.2" [deps] Observables = "510215fc-4207-5dde-b226-833fc4488ee2" diff --git a/MakieCore/src/basic_plots.jl b/MakieCore/src/basic_plots.jl index f0905cac58e..cc09c0d620b 100644 --- a/MakieCore/src/basic_plots.jl +++ b/MakieCore/src/basic_plots.jl @@ -192,6 +192,7 @@ Available algorithms are: - `algorithm::Union{Symbol, RaymarchAlgorithm} = :mip` sets the volume algorithm that is used. - `isorange::Real = 0.05` sets the range of values picked up by the IsoValue algorithm. - `isovalue = 0.5` sets the target value for the IsoValue algorithm. +- `interpolate::Bool = true` sets whether the volume data should be sampled with interpolation. $(Base.Docs.doc(shading_attributes!)) @@ -205,7 +206,7 @@ $(Base.Docs.doc(MakieCore.generic_plot_attributes!)) algorithm = :mip, isovalue = 0.5, isorange = 0.05, - + interpolate = true, fxaa = true, ) generic_plot_attributes!(attr) diff --git a/NEWS.md b/NEWS.md index 5150664366c..a96ea390971 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,7 +2,15 @@ ## master +- Changes for Bonito rename and WGLMakie docs improvements [#3477](https://github.com/MakieOrg/Makie.jl/pull/3477). + +## 0.20.3 + +- Add `depthsorting` as a hidden attribute for scatter plots in GLMakie as an alternative fix for outline artifacts. [#3432](https://github.com/MakieOrg/Makie.jl/pull/3432) +- Disable SDF based anti-aliasing in scatter, text and lines plots when `fxaa = true` in GLMakie. This allows removing outline artifacts at the cost of quality. [#3408](https://github.com/MakieOrg/Makie.jl/pull/3408) +- DataInspector Fixes: Fixed depth order, positional labels being in transformed space and `:inspector_clear` not getting called when moving from one plot to another. [#3454](https://github.com/MakieOrg/Makie.jl/pull/3454) - Fixed bug in GLMakie where the update from a (i, j) sized GPU buffer to a (j, i) sized buffer would fail [#3456](https://github.com/MakieOrg/Makie.jl/pull/3456). +- Add `interpolate=true` to `volume(...)`, allowing to disable interpolation [#3485](https://github.com/MakieOrg/Makie.jl/pull/3485). ## 0.20.2 diff --git a/Project.toml b/Project.toml index 3d9792b229e..ea619de351d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Makie" uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" authors = ["Simon Danisch", "Julius Krumbiegel"] -version = "0.20.2" +version = "0.20.3" [deps] Animations = "27a7e980-b3e6-11e9-2bcd-0b925532e340" @@ -89,7 +89,7 @@ KernelDensity = "0.5, 0.6" LaTeXStrings = "1.2" LinearAlgebra = "1.0, 1.6" MacroTools = "0.5" -MakieCore = "=0.7.1" +MakieCore = "=0.7.2" Markdown = "1.0, 1.6" MathTeXEngine = "0.5" Observables = "0.5.5" diff --git a/RPRMakie/Project.toml b/RPRMakie/Project.toml index 42d94c21f05..c795416be98 100644 --- a/RPRMakie/Project.toml +++ b/RPRMakie/Project.toml @@ -1,7 +1,7 @@ name = "RPRMakie" uuid = "22d9f318-5e34-4b44-b769-6e3734a732a6" authors = ["Simon Danisch"] -version = "0.6.2" +version = "0.6.3" [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" @@ -16,7 +16,7 @@ RadeonProRender = "27029320-176d-4a42-b57d-56729d2ad457" Colors = "0.9, 0.10, 0.11, 0.12" FileIO = "1.6" GeometryBasics = "0.4.1" -Makie = "=0.20.2" +Makie = "=0.20.3" RadeonProRender = "0.3.0" julia = "1.3" LinearAlgebra = "1.0, 1.6" diff --git a/RPRMakie/src/scene.jl b/RPRMakie/src/scene.jl index 1e85771e6e2..65ff446771e 100644 --- a/RPRMakie/src/scene.jl +++ b/RPRMakie/src/scene.jl @@ -5,7 +5,6 @@ function update_rpr_camera!(oldvals, camera, cam_controls, cam) c = cam_controls l, u, p, fov = c.lookat[], c.upvector[], c.eyeposition[], c.fov[] far, near, res = c.far[], c.near[], cam.resolution[] - fov = 45f0 # The current camera ignores fov updates new_vals = (; l, u, p, fov, far, near, res) new_vals == oldvals && return oldvals wd = norm(l - p) @@ -14,8 +13,8 @@ function update_rpr_camera!(oldvals, camera, cam_controls, cam) lookat!(camera, p, l, u) RPR.rprCameraSetFarPlane(camera, far) RPR.rprCameraSetNearPlane(camera, near) - h = norm(res) - RPR.rprCameraSetFocalLength(camera, (30*h)/fov) + focal_length = res[2] / (2 * tand(fov / 2)) # fov is vertical + RPR.rprCameraSetFocalLength(camera, focal_length) # RPR_CAMERA_FSTOP # RPR_CAMERA_MODE return new_vals diff --git a/ReferenceUpdater/src/local_server.jl b/ReferenceUpdater/src/local_server.jl index 546b2ecb2ed..39f9833e423 100644 --- a/ReferenceUpdater/src/local_server.jl +++ b/ReferenceUpdater/src/local_server.jl @@ -102,7 +102,7 @@ function serve_update_page(; commit = nothing, pr = nothing) id = checkrun["id"] right_combination = any(["GLMakie", "CairoMakie", "WGLMakie"]) do package # We need to match the name quite specifically, since we need to keep this synchronized to the CI script anyways. - startswith(name, "$package Julia 1.6") + name == "$package Julia 1" end if right_combination if name in unique_artifacts @@ -133,6 +133,7 @@ function serve_update_page(; commit = nothing, pr = nothing) error("Cancelled") end check = checkruns[choice] + chosen_backend = match(r"(\w+) Julia 1", check["name"])[1] job = JSON3.read(authget("https://api.github.com/repos/MakieOrg/Makie.jl/actions/jobs/$(check["id"])").body) run = JSON3.read(authget(job["run_url"]).body) @@ -140,7 +141,7 @@ function serve_update_page(; commit = nothing, pr = nothing) artifacts = JSON3.read(authget(run["artifacts_url"]).body)["artifacts"] for a in artifacts - if endswith(a["name"], "1.6") + if a["name"] == "ReferenceImages_$chosen_backend" @info "Choosing artifact $(a["name"])" download_url = a["archive_download_url"] if !haskey(URL_CACHE, download_url) @@ -194,6 +195,7 @@ function unzip(file, exdir = "") if (endswith(f.name,"/") || endswith(f.name,"\\")) mkdir(fullFilePath) else + mkpath(dirname(fullFilePath)) write(fullFilePath, read(f)) end end diff --git a/WGLMakie/Project.toml b/WGLMakie/Project.toml index 6a1604efed7..6366436f34b 100644 --- a/WGLMakie/Project.toml +++ b/WGLMakie/Project.toml @@ -1,7 +1,7 @@ name = "WGLMakie" uuid = "276b4fcb-3e11-5398-bf8b-a0c2d153d008" authors = ["SimonDanisch "] -version = "0.9.2" +version = "0.9.3" [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" @@ -27,7 +27,7 @@ GeometryBasics = "0.4.1" Hyperscript = "0.0.3, 0.0.4, 0.0.5" Bonito = "3.0.0" LinearAlgebra = "1.0, 1.6" -Makie = "=0.20.2" +Makie = "=0.20.3" Observables = "0.5.1" PNGFiles = "0.3, 0.4" PrecompileTools = "1.0" diff --git a/docs/explanations/recipes.md b/docs/explanations/recipes.md index ba703870f61..7dc955148e3 100644 --- a/docs/explanations/recipes.md +++ b/docs/explanations/recipes.md @@ -136,7 +136,7 @@ As the second part of defining `MyPlot`, you should implement the actual plotting of the `MyPlot` object by specializing `plot!`: ```julia -function plot!(myplot::MyPlot) +function Makie.plot!(myplot::MyPlot) # normal plotting code, building on any previously defined recipes # or atomic plotting operations, and adding to the combined `myplot`: lines!(myplot, rand(10), color = myplot.plot_color) diff --git a/docs/how-to/draw-boxes-around-subfigures.md b/docs/how-to/draw-boxes-around-subfigures.md new file mode 100644 index 00000000000..c1b6c8c94f1 --- /dev/null +++ b/docs/how-to/draw-boxes-around-subfigures.md @@ -0,0 +1,34 @@ +# How to draw boxes around subfigures + +If you want to show that several elements in a `Figure` belong together, you can do this by placing them all in a container. +The trick is to use a nested `GridLayout` for each group of objects and place a `Box` at the same position as this `GridLayout`. +Then the `alignmode = Outside(some_padding)` ensures that objects with protrusions sticking out, like `Axis`, are fully contained within the enclosing boxes. + +\begin{examplefigure}{} +```julia +using CairoMakie +CairoMakie.activate!() # hide + +f = Figure() + +g1 = GridLayout(f[1, 1], alignmode = Outside(15)) +g2 = GridLayout(f[1, 2], alignmode = Outside(15)) +box1 = Box(f[1, 1], cornerradius = 10, color = (:tomato, 0.5), strokecolor = :transparent) +box2 = Box(f[1, 2], cornerradius = 10, color = (:teal, 0.5), strokecolor = :transparent) + +# move the boxes back so the Axis background polys are in front of them +Makie.translate!(box1.blockscene, 0, 0, -100) +Makie.translate!(box2.blockscene, 0, 0, -100) + +Axis(g1[1, 1], backgroundcolor = :white) +Axis(g1[2, 1], backgroundcolor = :white) + +Axis(g2[1, 1], backgroundcolor = :white) +Axis(g2[1, 2], backgroundcolor = :white) +Axis(g2[2, 1:2], backgroundcolor = :white) + +Label(f[0, :], "Two boxes indicate groups of axes that belong together") + +f +``` +\end{examplefigure} diff --git a/docs/reference/plots/lines.md b/docs/reference/plots/lines.md index 28145387529..83c3e4f82e8 100644 --- a/docs/reference/plots/lines.md +++ b/docs/reference/plots/lines.md @@ -51,3 +51,31 @@ end f ``` \end{examplefigure} + +### Dealing with outline artifacts in GLMakie + +In GLMakie 3D line plots can generate outline artifacts depending on the order line segments are rendered in. +Currently there are a few ways to mitigate this problem, but they all come at a cost: +- `fxaa = true` will disable the native anti-aliasing of line segments and use fxaa instead. This results in less detailed lines. +- `transparency = true` will disable depth testing to a degree, resulting in all lines being rendered without artifacts. However with this lines will always have some level of transparency. +- `overdraw = true` will disable depth testing entirely (read and write) for the plot, removing artifacts. This will however change the z-order of line segments and allow plots rendered later to show up on top of the lines plot. + +\begin{examplefigure}{} +```julia +using GLMakie +GLMakie.activate!() # hide + +ps = rand(Point3f, 500) +cs = rand(500) +f = Figure(size = (600, 650)) +Label(f[1, 1], "base", tellwidth = false) +lines(f[2, 1], ps, color = cs, markersize = 20, fxaa = false) +Label(f[1, 2], "fxaa = true", tellwidth = false) +lines(f[2, 2], ps, color = cs, markersize = 20, fxaa = true) +Label(f[3, 1], "transparency = true", tellwidth = false) +lines(f[4, 1], ps, color = cs, markersize = 20, transparency = true) +Label(f[3, 2], "overdraw = true", tellwidth = false) +lines(f[4, 2], ps, color = cs, markersize = 20, overdraw = true) +f +``` +\end{examplefigure} \ No newline at end of file diff --git a/docs/reference/plots/linesegments.md b/docs/reference/plots/linesegments.md index 783f4bde81d..2f336f9e2d4 100644 --- a/docs/reference/plots/linesegments.md +++ b/docs/reference/plots/linesegments.md @@ -23,3 +23,31 @@ linesegments!(xs, ys .- 2, linewidth = 5, color = LinRange(1, 5, length(xs))) f ``` \end{examplefigure} + +### Dealing with outline artifacts in GLMakie + +In GLMakie 3D line plots can generate outline artifacts depending on the order line segments are rendered in. +Currently there are a few ways to mitigate this problem, but they all come at a cost: +- `fxaa = true` will disable the native anti-aliasing of line segments and use fxaa instead. This results in less detailed lines. +- `transparency = true` will disable depth testing to a degree, resulting in all lines being rendered without artifacts. However with this lines will always have some level of transparency. +- `overdraw = true` will disable depth testing entirely (read and write) for the plot, removing artifacts. This will however change the z-order of line segments and allow plots rendered later to show up on top of the linesegments plot. + +\begin{examplefigure}{} +```julia +using GLMakie +GLMakie.activate!() # hide + +ps = rand(Point3f, 500) +cs = rand(500) +f = Figure(size = (600, 650)) +Label(f[1, 1], "base", tellwidth = false) +linesegments(f[2, 1], ps, color = cs, markersize = 20, fxaa = false) +Label(f[1, 2], "fxaa = true", tellwidth = false) +linesegments(f[2, 2], ps, color = cs, markersize = 20, fxaa = true) +Label(f[3, 1], "transparency = true", tellwidth = false) +linesegments(f[4, 1], ps, color = cs, markersize = 20, transparency = true) +Label(f[3, 2], "overdraw = true", tellwidth = false) +linesegments(f[4, 2], ps, color = cs, markersize = 20, overdraw = true) +f +``` +\end{examplefigure} \ No newline at end of file diff --git a/docs/reference/plots/scatter.md b/docs/reference/plots/scatter.md index a507cf49a7b..b91750eb1cb 100644 --- a/docs/reference/plots/scatter.md +++ b/docs/reference/plots/scatter.md @@ -370,3 +370,39 @@ scatter(a[1:50:end, :], marker = '✈', markersize = 20, color = :black) ``` \end{examplefigure} + +### Dealing with outline artifacts in GLMakie + +In GLMakie 3D scatter plots can generate outline artifacts depending on the order markers are rendered in. +Currently there are a few ways to mitigate this problem, but they all come at a cost: +- `fxaa = true` will disable the native anti-aliasing of scatter markers and use fxaa instead. This results in less detailed markers, especially for thin markers like characters. +- `transparency = true` will disable depth testing to a degree, resulting in all markers being rendered without artifacts. However with this markers always have some level of transparency +- `overdraw = true` will disable depth testing entirely (read and write) for the plot, removing artifacts. This will however change the z-order of markers and allow plots rendered later to show up on top of the scatter plot +- `depthsorting = true` will sort markers by depth before rendering to fix the issue. This only works within a plot call, so when other plots are involved the issue may reappear. + +\begin{examplefigure}{} +```julia +using GLMakie +GLMakie.activate!() # hide + +ps = rand(Point3f, 500) +cs = rand(500) +f = Figure(size = (900, 650)) +Label(f[1, 1], "base", tellwidth = false) +scatter(f[2, 1], ps, color = cs, markersize = 20, fxaa = false) +Label(f[1, 2], "fxaa = true", tellwidth = false) +scatter(f[2, 2], ps, color = cs, markersize = 20, fxaa = true) + +Label(f[3, 1], "transparency = true", tellwidth = false) +scatter(f[4, 1], ps, color = cs, markersize = 20, transparency = true) +Label(f[3, 2], "overdraw = true", tellwidth = false) +scatter(f[4, 2], ps, color = cs, markersize = 20, overdraw = true) + +Label(f[1, 3], "depthsorting = true", tellwidth = false) +scatter(f[2, 3], ps, color = cs, markersize = 20, depthsorting = true) +Label(f[3, 3], "depthsorting = true", tellwidth = false) +scatter(f[4, 3], ps, color = cs, markersize = 20, depthsorting = true) +mesh!(Rect3f(Point3f(0), Vec3f(0.9, 0.9, 0.9)), color = :orange) +f +``` +\end{examplefigure} \ No newline at end of file diff --git a/metrics/ttfp/run-benchmark.jl b/metrics/ttfp/run-benchmark.jl index 14bcbd5fc3b..f1b76472ac3 100644 --- a/metrics/ttfp/run-benchmark.jl +++ b/metrics/ttfp/run-benchmark.jl @@ -214,7 +214,7 @@ ENV["JULIA_PKG_PRECOMPILE_AUTO"] = 0 project1 = make_project_folder("current-pr") Pkg.activate(project1) if Package == "WGLMakie" - Pkg.add([(; name="Electron"),]) + Pkg.add([(; name="Electron")]) end pkgs = NamedTuple[(; path="./MakieCore"), (; path="."), (; path="./$Package")] # cd("dev/Makie") diff --git a/relocatability.jl b/relocatability.jl new file mode 100644 index 00000000000..835aec2fc3a --- /dev/null +++ b/relocatability.jl @@ -0,0 +1,50 @@ + +module_src = """ +module MakieApp + +using GLMakie + +function julia_main()::Cint + screen = display(scatter(1:4)) + # wait(screen) commented out to test if this blocks anything, but didn't change anything + return 0 # if things finished successfully +end + +end # module MakieApp +""" + +using Pkg, Test + +makie_dir = pwd() +tmpdir = mktempdir() +# create a temporary project +cd(tmpdir) +Pkg.generate("MakieApp") +Pkg.activate("MakieApp") + +paths = [makie_dir, joinpath(makie_dir, "MakieCore"), joinpath(makie_dir, "GLMakie")] + +Pkg.develop(map(x-> (;path=x), paths)) + +open("MakieApp/src/MakieApp.jl", "w") do io + print(io, module_src) +end + +Pkg.activate(".") +Pkg.add("PackageCompiler") + +using PackageCompiler + +create_app(joinpath(pwd(), "MakieApp"), "executable"; force=true, incremental=true, include_transitive_dependencies=false) +exe = joinpath(pwd(), "executable", "bin", "MakieApp") +@test success(`$(exe)`) +julia_pkg_dir = joinpath(Base.DEPOT_PATH[1], "packages") +@test isdir(julia_pkg_dir) +mvd_julia_pkg_dir = julia_pkg_dir * ".old" +# Move package dir so that we can test relocatability (hardcoded paths to package dir being invalid now) +try + mv(julia_pkg_dir, mvd_julia_pkg_dir) + @test success(`$(exe)`) +catch e + mv(mvd_julia_pkg_dir, julia_pkg_dir) +end diff --git a/src/basic_recipes/tooltip.jl b/src/basic_recipes/tooltip.jl index 74c75241062..b94b557a74b 100644 --- a/src/basic_recipes/tooltip.jl +++ b/src/basic_recipes/tooltip.jl @@ -49,6 +49,7 @@ Creates a tooltip pointing at `position` displaying the given `string` transparency = false, visible = true, inspectable = false, + space = :data, # Text textpadding = (4, 4, 4, 4), # LRBT @@ -81,15 +82,21 @@ end function plot!(p::Tooltip{<:Tuple{<:VecTypes}}) # TODO align - scene = parent_scene(p) - px_pos = map(scene.camera.projectionview, scene.camera.resolution, p[1]) do _, _, p - project(scene, p) + px_pos = map( + p, p[1], scene.camera.projectionview, p.model, transform_func(p), + p.space, scene.viewport) do pos, _, model, tf, space, viewport + + # Adjusted from error_and_rangebars + spvm = clip_to_space(scene.camera, :pixel) * space_to_clip(scene.camera, space) * model + transformed = apply_transform(tf, pos, space) + p4d = spvm * to_ndim(Point4f, to_ndim(Point3f, transformed, 0), 1) + return Point3f(p4d) / p4d[4] end # Text - textpadding = map(p.textpadding) do pad + textpadding = map(p, p.textpadding) do pad if pad isa Real return (pad, pad, pad, pad) elseif length(pad) == 4 @@ -100,7 +107,7 @@ function plot!(p::Tooltip{<:Tuple{<:VecTypes}}) end end - text_offset = map(p.offset, textpadding, p.triangle_size, p.placement, p.align) do o, pad, ts, placement, align + text_offset = map(p, p.offset, textpadding, p.triangle_size, p.placement, p.align) do o, pad, ts, placement, align l, r, b, t = pad if placement === :left @@ -117,7 +124,7 @@ function plot!(p::Tooltip{<:Tuple{<:VecTypes}}) end end - text_align = map(p.placement, p.align) do placement, align + text_align = map(p, p.placement, p.align) do placement, align if placement === :left return (1.0, align) elseif placement === :right @@ -139,17 +146,17 @@ function plot!(p::Tooltip{<:Tuple{<:VecTypes}}) strokewidth = p.strokewidth, strokecolor = p.strokecolor, transparency = p.transparency, visible = p.visible, overdraw = p.overdraw, depth_shift = p.depth_shift, - inspectable = p.inspectable, space = :pixel + inspectable = p.inspectable, space = :pixel, transformation = Transformation() ) translate!(tp, 0, 0, 1) # TODO react to glyphcollection instead bbox = map( - px_pos, p.text, text_align, text_offset, textpadding, p.align + p, px_pos, p.text, text_align, text_offset, textpadding, p.align ) do p, s, _, o, pad, align - bb = Rect2f(boundingbox(tp)) + o + bb = boundingbox(tp) + to_ndim(Vec3f, o, 0) l, r, b, t = pad - return Rect2f(origin(bb) .- (l, b), widths(bb) .+ (l+r, b+t)) + return Rect3f(origin(bb) .- (l, b, 0), widths(bb) .+ (l+r, b+t, 0)) end # Text background mesh @@ -159,7 +166,7 @@ function plot!(p::Tooltip{<:Tuple{<:VecTypes}}) color = p.backgroundcolor, fxaa = false, transparency = p.transparency, visible = p.visible, overdraw = p.overdraw, depth_shift = p.depth_shift, - inspectable = p.inspectable + inspectable = p.inspectable, transformation = Transformation() ) # Triangle mesh @@ -174,27 +181,27 @@ function plot!(p::Tooltip{<:Tuple{<:VecTypes}}) color = p.backgroundcolor, transparency = p.transparency, visible = p.visible, overdraw = p.overdraw, depth_shift = p.depth_shift, - inspectable = p.inspectable + inspectable = p.inspectable, transformation = Transformation() ) - onany(bbox, p.triangle_size, p.placement, p.align) do bb, s, placement, align + onany(p, bbox, p.triangle_size, p.placement, p.align) do bb, s, placement, align o = origin(bb); w = widths(bb) scale!(mp, s, s, s) if placement === :left - translate!(mp, Vec3f(o[1] + w[1], o[2] + align * w[2], 0)) + translate!(mp, Vec3f(o[1] + w[1], o[2] + align * w[2], o[3])) rotate!(mp, qrotation(Vec3f(0,0,1), 0.5pi)) elseif placement === :right translate!(mp, Vec3f(o[1], o[2] + align * w[2], 0)) rotate!(mp, qrotation(Vec3f(0,0,1), -0.5pi)) elseif placement in (:below, :down, :bottom) - translate!(mp, Vec3f(o[1] + align * w[1], o[2] + w[2], 0)) + translate!(mp, Vec3f(o[1] + align * w[1], o[2] + w[2], o[3])) rotate!(mp, Quaternionf(0,0,1,0)) # pi elseif placement in (:above, :up, :top) - translate!(mp, Vec3f(o[1] + align * w[1], o[2], 0)) + translate!(mp, Vec3f(o[1] + align * w[1], o[2], o[3])) rotate!(mp, Quaternionf(0,0,0,1)) # 0 else @error "Tooltip placement $placement invalid. Assuming :above" - translate!(mp, Vec3f(o[1] + align * w[1], o[2], 0)) + translate!(mp, Vec3f(o[1] + align * w[1], o[2], o[3])) rotate!(mp, Quaternionf(0,0,0,1)) end return @@ -202,8 +209,8 @@ function plot!(p::Tooltip{<:Tuple{<:VecTypes}}) # Outline - outline = map(bbox, p.triangle_size, p.placement, p.align) do bb, s, placement, align - l, b = origin(bb); w, h = widths(bb) + outline = map(p, bbox, p.triangle_size, p.placement, p.align) do bb, s, placement, align + l, b, z = origin(bb); w, h, _ = widths(bb) r, t = (l, b) .+ (w, h) # We start/end at half width/height here to avoid corners like this: @@ -255,7 +262,7 @@ function plot!(p::Tooltip{<:Tuple{<:VecTypes}}) ] end - return shift + return to_ndim.(Vec3f, shift, z) end lines!( @@ -264,7 +271,7 @@ function plot!(p::Tooltip{<:Tuple{<:VecTypes}}) linewidth = p.outline_linewidth, linestyle = p.outline_linestyle, transparency = p.transparency, visible = p.visible, overdraw = p.overdraw, depth_shift = p.depth_shift, - inspectable = p.inspectable + inspectable = p.inspectable, transformation = Transformation() ) notify(p[1]) diff --git a/src/ffmpeg-util.jl b/src/ffmpeg-util.jl index a3f5eed6fc1..137524bd342 100644 --- a/src/ffmpeg-util.jl +++ b/src/ffmpeg-util.jl @@ -17,13 +17,13 @@ higher numbers giving lower quality and smaller file sizes (higher compression). The minimum value is `0` (lossless encoding). - For `mp4`, `51` is the maximum. Note that `compression = 0` only works with `mp4` if - `profile = high444`. + `profile = "high444"`. - For `webm`, `63` is the maximum. - `compression` has no effect on `mkv` and `gif` outputs. - `profile = "high422"`: A ffmpeg compatible profile. Currently only applies to `mp4`. If you have issues playing a video, try `profile = "high"` or `profile = "main"`. - `pixel_format = "yuv420p"`: A ffmpeg compatible pixel format (`-pix_fmt`). Currently only -applies to `mp4`. Defaults to `yuv444p` for `profile = high444`. +applies to `mp4`. Defaults to `yuv444p` for `profile = "high444"`. - `loop = 0`: Number of times the video is repeated, for a `gif`. Defaults to `0`, which means infinite looping. A value of `-1` turns off looping, and a value of `n > 0` and above means `n` repetitions (i.e. the video is played `n+1` times). @@ -214,13 +214,13 @@ $(Base.doc(VideoStreamOptions)) """ function VideoStream(fig::FigureLike; format="mp4", framerate=24, compression=nothing, profile=nothing, pixel_format=nothing, loop=nothing, - loglevel="quiet", visible=false, connect=false, backend=current_backend(), + loglevel="quiet", visible=false, update=true, backend=current_backend(), screen_config...) dir = mktempdir() path = joinpath(dir, "$(gensym(:video)).$(format)") scene = get_scene(fig) - update_state_before_display!(fig) + update && update_state_before_display!(fig) config = Dict{Symbol,Any}(screen_config) get!(config, :visible, visible) get!(config, :start_renderloop, false) diff --git a/src/interaction/inspector.jl b/src/interaction/inspector.jl index 228ea7c6e20..f2a25b52cc2 100644 --- a/src/interaction/inspector.jl +++ b/src/interaction/inspector.jl @@ -371,7 +371,12 @@ end # clears temporary plots (i.e. bboxes) and update selection function clear_temporary_plots!(inspector::DataInspector, plot) - inspector.selection = plot + if inspector.selection !== plot + if haskey(inspector.selection, :inspector_clear) + inspector.selection[:inspector_clear][](inspector, inspector.selection) + end + inspector.selection = plot + end for i in length(inspector.obsfuncs):-1:3 off(pop!(inspector.obsfuncs)) @@ -422,8 +427,8 @@ function show_data(inspector::DataInspector, plot::Scatter, idx) tt = inspector.plot scene = parent_scene(plot) - pos = position_on_plot(plot, idx) - proj_pos = shift_project(scene, pos) + pos = position_on_plot(plot, idx, apply_transform = false) + proj_pos = shift_project(scene, apply_transform_and_model(plot, pos)) update_tooltip_alignment!(inspector, proj_pos) if haskey(plot, :inspector_label) @@ -489,8 +494,8 @@ function show_data(inspector::DataInspector, plot::MeshScatter, idx) a.indicator_visible[] = true end - pos = position_on_plot(plot, idx) - proj_pos = shift_project(scene, pos) + pos = position_on_plot(plot, idx, apply_transform = false) + proj_pos = shift_project(scene, apply_transform_and_model(plot, pos)) update_tooltip_alignment!(inspector, proj_pos) if haskey(plot, :inspector_label) @@ -510,8 +515,8 @@ function show_data(inspector::DataInspector, plot::Union{Lines, LineSegments}, i scene = parent_scene(plot) # cast ray from cursor into screen, find closest point to line - pos = position_on_plot(plot, idx) - proj_pos = shift_project(scene, pos) + pos = position_on_plot(plot, idx, apply_transform = false) + proj_pos = shift_project(scene, apply_transform_and_model(plot, pos)) update_tooltip_alignment!(inspector, proj_pos) tt.offset[] = ifelse( @@ -596,7 +601,7 @@ function show_data(inspector::DataInspector, plot::Surface, idx) proj_pos = Point2f(mouseposition_px(inspector.root)) update_tooltip_alignment!(inspector, proj_pos) - pos = position_on_plot(plot, idx) + pos = position_on_plot(plot, idx, apply_transform = false) if !isnan(pos) tt[1][] = proj_pos @@ -810,8 +815,8 @@ function show_data(inspector::DataInspector, plot::BarPlot, idx) tt = inspector.plot scene = parent_scene(plot) - pos = apply_transform_and_model(plot, plot[1][][idx]) - proj_pos = shift_project(scene, to_ndim(Point3f, pos, 0)) + pos = plot[1][][idx] + proj_pos = shift_project(scene, apply_transform_and_model(plot, to_ndim(Point3f, pos, 0))) update_tooltip_alignment!(inspector, proj_pos) if a.enable_indicators[] @@ -852,7 +857,7 @@ end function show_data(inspector::DataInspector, plot::Arrows, idx, source) a = inspector.attributes tt = inspector.plot - pos = apply_transform_and_model(plot, plot[1][][idx]) + pos = plot[1][][idx] mpos = Point2f(mouseposition_px(inspector.root)) update_tooltip_alignment!(inspector, mpos) diff --git a/src/makielayout/blocks/axis.jl b/src/makielayout/blocks/axis.jl index 169ac3b7f89..8248577fbb8 100644 --- a/src/makielayout/blocks/axis.jl +++ b/src/makielayout/blocks/axis.jl @@ -898,14 +898,21 @@ function update_linked_limits!(block_limit_linking, xaxislinks, yaxislinks, tlim end """ + autolimits!() autolimits!(la::Axis) Reset manually specified limits of `la` to an automatically determined rectangle, that depends on the data limits of all plot objects in the axis, as well as the autolimit margins for x and y axis. +The argument `la` defaults to `current_axis()`. """ function autolimits!(ax::Axis) ax.limits[] = (nothing, nothing) return end +function autolimits!() + curr_ax = current_axis() + isnothing(curr_ax) && throw(ArgumentError("Attempted to call `autolimits!` on `current_axis()`, but `current_axis()` returned nothing.")) + autolimits!(curr_ax) +end function autolimits(ax::Axis, dim::Integer) # try getting x limits for the axis and then union them with linked axes