diff --git a/.github/workflows/Docs.yml b/.github/workflows/Docs.yml index 713a076214e..5362c068677 100644 --- a/.github/workflows/Docs.yml +++ b/.github/workflows/Docs.yml @@ -1,16 +1,14 @@ name: Docs build and deploy on: - push: + pull_request: branches: - - main - master - - breaking-release - tags: '*' - pull_request: + - sd/beta-20 + push: + tags: + - '*' branches: - - main - master - - breaking-release concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/cairomakie.yaml b/.github/workflows/cairomakie.yaml index 8f314850197..4f1c4d90892 100644 --- a/.github/workflows/cairomakie.yaml +++ b/.github/workflows/cairomakie.yaml @@ -6,15 +6,10 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 push: - paths-ignore: - - 'docs/**' - - '*.md' - branches: - - master - - breaking-release - tags: '*' + tags: + - '*' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9bd234ebb1..70e6bbf0fdd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,15 +6,12 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 push: - paths-ignore: - - 'docs/**' - - '*.md' + tags: + - '*' branches: - master - - breaking-release - tags: '*' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -42,10 +39,10 @@ jobs: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - uses: julia-actions/cache@v1 - - name: Install Julia dependencies + - name: Develop and test Makie shell: julia --project=monorepo {0} run: | - using Pkg; + using Pkg # dev mono repo versions pkg"dev . ./MakieCore" Pkg.test("Makie"; coverage=true) diff --git a/.github/workflows/compilation-benchmark.yaml b/.github/workflows/compilation-benchmark.yaml index fabccef141b..a3255a6030b 100644 --- a/.github/workflows/compilation-benchmark.yaml +++ b/.github/workflows/compilation-benchmark.yaml @@ -6,7 +6,7 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 jobs: benchmark: name: ${{ matrix.package }} diff --git a/.github/workflows/glmakie.yaml b/.github/workflows/glmakie.yaml index 0d8b24bfe79..6723f735052 100644 --- a/.github/workflows/glmakie.yaml +++ b/.github/workflows/glmakie.yaml @@ -6,15 +6,12 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 push: - paths-ignore: - - 'docs/**' - - '*.md' + tags: + - '*' branches: - master - - breaking-release - tags: '*' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -44,7 +41,7 @@ jobs: 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 + - 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: | @@ -57,22 +54,25 @@ jobs: 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: Comment if there are missing refimages - if: matrix.version == '1' && steps.referencetests.outputs.n_missing_refimages != '0' - uses: mshick/add-pr-comment@v2 - with: - message: | - ## Missing reference images - Found ${{steps.referencetests.outputs.n_missing_refimages}} new images without existing references. - Upload new reference images before merging this PR. - repo-token: ${{ secrets.GITHUB_TOKEN }} - allow-repeats: true - 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 diff --git a/.github/workflows/refimages_status.yaml b/.github/workflows/refimages_status.yaml new file mode 100644 index 00000000000..e3a1ee59005 --- /dev/null +++ b/.github/workflows/refimages_status.yaml @@ -0,0 +1,65 @@ +name: Reference Image Status + +on: + workflow_run: + workflows: [GLMakie CI] + types: + - completed + +jobs: + download: + permissions: + statuses: write + runs-on: ubuntu-latest + steps: + - name: "Download artifact" + uses: actions/github-script@v6 + with: + debug: true + script: | + console.log(context.payload.workflow_run); + let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { + return artifact.name == "n_missing_refimages" + })[0]; + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + let fs = require('fs'); + fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/n_missing_refimages.zip`, Buffer.from(download.data)); + + - name: 'Unzip artifact' + run: unzip n_missing_refimages.zip + + - name: Add reference images status + uses: actions/github-script@v6 + with: + debug: true + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + let fs = require('fs'); + let content = fs.readFileSync('./n_missing_refimages', { encoding: 'utf8' }); + let lines = content.split('\n'); + let n_missing = Number(lines[0]); + let commit_sha = lines[1]; + + await github.request('POST /repos/{owner}/{repo}/statuses/{sha}', { + owner: context.repo.owner, + repo: context.repo.repo, + sha: commit_sha, + state: n_missing === 0 ? 'success' : 'failure', + target_url: null, + description: `${n_missing == 0 ? 'No' : n_missing} missing reference image${n_missing == 1 ? '' : 's'} must be uploaded`, + context: 'Reference Tests', + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }) + \ No newline at end of file diff --git a/.github/workflows/rprmakie.yaml b/.github/workflows/rprmakie.yaml index d6e301ec30b..6b5e7a89e7c 100644 --- a/.github/workflows/rprmakie.yaml +++ b/.github/workflows/rprmakie.yaml @@ -6,15 +6,12 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 push: - paths-ignore: - - 'docs/**' - - '*.md' + tags: + - '*' branches: - master - - breaking-release - tags: '*' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/wglmakie.yaml b/.github/workflows/wglmakie.yaml index 242e8b2056c..2942af72af5 100644 --- a/.github/workflows/wglmakie.yaml +++ b/.github/workflows/wglmakie.yaml @@ -6,16 +6,12 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 push: - paths-ignore: - - 'docs/**' - - '*.md' + tags: + - '*' branches: - master - - breaking-release - tags: '*' - concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true diff --git a/CairoMakie/Project.toml b/CairoMakie/Project.toml index 7cda71a68ec..7c6bf59841e 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.10.6" +version = "0.10.9" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" @@ -23,7 +23,7 @@ FFTW = "1" FileIO = "1.1" FreeType = "3, 4.0" GeometryBasics = "0.4.1" -Makie = "=0.19.6" +Makie = "=0.19.9" PrecompileTools = "1.0" julia = "1.3" diff --git a/CairoMakie/src/infrastructure.jl b/CairoMakie/src/infrastructure.jl index 179750974d3..6a036a84727 100644 --- a/CairoMakie/src/infrastructure.jl +++ b/CairoMakie/src/infrastructure.jl @@ -11,7 +11,7 @@ function cairo_draw(screen::Screen, scene::Scene) Cairo.save(screen.context) draw_background(screen, scene) - allplots = get_all_plots(scene) + allplots = Makie.collect_atomic_plots(scene; is_atomic_plot = is_cairomakie_atomic_plot) zvals = Makie.zvalue2d.(allplots) permute!(allplots, sortperm(zvals)) @@ -23,10 +23,12 @@ function cairo_draw(screen::Screen, scene::Scene) Cairo.save(screen.context) for p in allplots - to_value(get(p, :visible, true)) || continue + check_parent_plots(p) do plot + to_value(get(plot, :visible, true)) + end || continue # only prepare for scene when it changes # this should reduce the number of unnecessary clipping masks etc. - pparent = p.parent::Scene + pparent = Makie.parent_scene(p) pparent.visible[] || continue if pparent != last_scene Cairo.restore(screen.context) @@ -36,8 +38,8 @@ function cairo_draw(screen::Screen, scene::Scene) end Cairo.save(screen.context) - # When a plot is too large to save with a reasonable file size on a vector backend, - # the user can choose to rasterize it when plotting to vector backends, by using the + # When a plot is too large to save with a reasonable file size on a vector backend, + # the user can choose to rasterize it when plotting to vector backends, by using the # `rasterize` keyword argument. This can be set to a Bool or an Int which describes # the density of rasterization (in terms of a direct scaling factor.) # TODO: In future, this can also be set to a Tuple{Module, Int} which describes @@ -54,12 +56,33 @@ function cairo_draw(screen::Screen, scene::Scene) return end -function get_all_plots(scene, plots = AbstractPlot[]) - append!(plots, scene.plots) - for c in scene.children - get_all_plots(c, plots) +""" + is_cairomakie_atomic_plot(plot::Combined)::Bool + +Returns whether the plot is considered atomic for the CairoMakie backend. +This is overridden for `Poly`, `Band`, and `Tricontourf` so we can apply +CairoMakie can treat them as atomic plots and render them directly. + +Plots with children are by default recursed into. This can be overridden +by defining specific dispatches for `is_cairomakie_atomic_plot` for a given plot type. +""" +is_cairomakie_atomic_plot(plot::Combined) = isempty(plot.plots) || to_value(get(plot, :rasterize, false)) != false + +""" + check_parent_plots(f, plot::Combined)::Bool +Returns whether the plot's parent tree satisfies the predicate `f`. +`f` must return a `Bool` and take a plot as its only argument. +""" +function check_parent_plots(f, plot::Combined) + if f(plot) + check_parent_plots(f, parent(plot)) + else + return false end - plots +end + +function check_parent_plots(f, scene::Scene) + return true end function prepare_for_scene(screen::Screen, scene::Scene) diff --git a/CairoMakie/src/overrides.jl b/CairoMakie/src/overrides.jl index d7301ddc8d9..25da6a9c9c6 100644 --- a/CairoMakie/src/overrides.jl +++ b/CairoMakie/src/overrides.jl @@ -12,6 +12,10 @@ function draw_plot(scene::Scene, screen::Screen, poly::Poly) draw_poly(scene, screen, poly, to_value.(poly.input_args)...) end +# Override `is_cairomakie_atomic_plot` to allow `poly` to remain a unit, +# instead of auto-decomposing in lines and mesh. +is_cairomakie_atomic_plot(plot::Poly) = true + """ Fallback method for args without special treatment. """ @@ -33,12 +37,13 @@ end function draw_poly(scene::Scene, screen::Screen, poly, points::Vector{<:Point2}) color = to_cairo_color(poly.color[], poly) strokecolor = to_cairo_color(poly.strokecolor[], poly) - draw_poly(scene, screen, poly, points, color, poly.model[], strokecolor, poly.strokewidth[]) + strokestyle = Makie.convert_attribute(poly.linestyle[], key"linestyle"()) + draw_poly(scene, screen, poly, points, color, poly.model[], strokecolor, strokestyle, poly.strokewidth[]) end # when color is a Makie.AbstractPattern, we don't need to go to Mesh function draw_poly(scene::Scene, screen::Screen, poly, points::Vector{<:Point2}, color::Union{Colorant, Cairo.CairoPattern}, - model, strokecolor, strokewidth) + model, strokecolor, strokestyle, strokewidth) space = to_value(get(poly, :space, :data)) points = project_position.(Ref(scene), space, points, Ref(model)) Cairo.move_to(screen.context, points[1]...) @@ -52,15 +57,18 @@ function draw_poly(scene::Scene, screen::Screen, poly, points::Vector{<:Point2}, Cairo.fill_preserve(screen.context) Cairo.set_source_rgba(screen.context, rgbatuple(to_color(strokecolor))...) Cairo.set_line_width(screen.context, strokewidth) + isnothing(strokestyle) || Cairo.set_dash(screen.context, diff(Float64.(strokestyle)) .* strokewidth) Cairo.stroke(screen.context) end function draw_poly(scene::Scene, screen::Screen, poly, points_list::Vector{<:Vector{<:Point2}}) color = to_cairo_color(poly.color[], poly) strokecolor = to_cairo_color(poly.strokecolor[], poly) + strokestyle = Makie.convert_attribute(poly.linestyle[], key"linestyle"()) + broadcast_foreach(points_list, color, - strokecolor, poly.strokewidth[], Ref(poly.model[])) do points, color, strokecolor, strokewidth, model - draw_poly(scene, screen, poly, points, color, model, strokecolor, strokewidth) + strokecolor, strokestyle, poly.strokewidth[], Ref(poly.model[])) do points, color, strokecolor, strokestyle, strokewidth, model + draw_poly(scene, screen, poly, points, color, model, strokecolor, strokestyle, strokewidth) end end @@ -72,12 +80,21 @@ function draw_poly(scene::Scene, screen::Screen, poly, rects::Vector{<:Rect2}) projected_rects = project_rect.(Ref(scene), space, rects, Ref(model)) color = to_cairo_color(poly.color[], poly) - strokecolor = to_cairo_color(poly.strokecolor[], poly) + linestyle = Makie.convert_attribute(poly.linestyle[], key"linestyle"()) + if isnothing(linestyle) + linestyle_diffed = nothing + elseif linestyle isa AbstractVector{Float64} + linestyle_diffed = diff(Float64.(linestyle)) + else + error("Wrong type for linestyle: $(poly.linestyle[]).") + end + strokecolor = to_cairo_color(poly.strokecolor[], poly) broadcast_foreach(projected_rects, color, strokecolor, poly.strokewidth[]) do r, c, sc, sw Cairo.rectangle(screen.context, origin(r)..., widths(r)...) set_source(screen.context, c) Cairo.fill_preserve(screen.context) + isnothing(linestyle_diffed) || Cairo.set_dash(screen.context, linestyle_diffed .* sw) set_source(screen.context, sc) Cairo.set_line_width(screen.context, sw) Cairo.stroke(screen.context) @@ -85,6 +102,7 @@ function draw_poly(scene::Scene, screen::Screen, poly, rects::Vector{<:Rect2}) end function polypath(ctx, polygon) + isempty(polygon) && return nothing ext = decompose(Point2f, polygon.exterior) Cairo.set_fill_type(ctx, Cairo.CAIRO_FILL_RULE_EVEN_ODD) Cairo.move_to(ctx, ext[1]...) @@ -111,12 +129,13 @@ draw_poly(scene::Scene, screen::Screen, poly, circle::Circle) = draw_poly(scene, function draw_poly(scene::Scene, screen::Screen, poly, polygons::AbstractArray{<:Polygon}) model = poly.model[] space = to_value(get(poly, :space, :data)) - projected_polys = project_polygon.(Ref(scene), space, polygons, Ref(model)) + projected_polys = project_polygon.(Ref(poly), space, polygons, Ref(model)) color = to_cairo_color(poly.color[], poly) strokecolor = to_cairo_color(poly.strokecolor[], poly) + strokestyle = Makie.convert_attribute(poly.linestyle[], key"linestyle"()) - broadcast_foreach(projected_polys, color, strokecolor, poly.strokewidth[]) do po, c, sc, sw + broadcast_foreach(projected_polys, color, strokecolor, strokestyle, poly.strokewidth[]) do po, c, sc, ss, sw polypath(screen.context, po) set_source(screen.context, c) Cairo.fill_preserve(screen.context) @@ -134,13 +153,14 @@ function draw_poly(scene::Scene, screen::Screen, poly, polygons::AbstractArray{< color = to_cairo_color(poly.color[], poly) strokecolor = to_cairo_color(poly.strokecolor[], poly) - - broadcast_foreach(projected_polys, color, strokecolor, poly.strokewidth[]) do mpo, c, sc, sw + strokestyle = Makie.convert_attribute(poly.linestyle[], key"linestyle"()) + broadcast_foreach(projected_polys, color, strokecolor, strokestyle, poly.strokewidth[]) do mpo, c, sc, ss, sw for po in mpo.polygons polypath(screen.context, po) set_source(screen.context, c) Cairo.fill_preserve(screen.context) set_source(screen.context, sc) + isnothing(ss) || Cairo.set_dash(screen.context, diff(Float64.(ss)) .* sw) Cairo.set_line_width(screen.context, sw) Cairo.stroke(screen.context) end @@ -182,6 +202,12 @@ function draw_plot(scene::Scene, screen::Screen, nothing end +# Override `is_cairomakie_atomic_plot` to allow this dispatch of `band` to remain a unit, +# instead of auto-decomposing in lines and mesh. +function is_cairomakie_atomic_plot(plot::Band{<:Tuple{<:AbstractVector{<:Point2},<:AbstractVector{<:Point2}}}) + return true +end + ################################################################################# # Tricontourf # # Tricontourf creates many disjoint polygons that are adjacent and form contour # @@ -214,3 +240,9 @@ function draw_plot(scene::Scene, screen::Screen, tric::Tricontourf) return end + +# Override `is_cairomakie_atomic_plot` to allow `Tricontourf` to remain a unit, +# instead of auto-decomposing in lines and mesh. +function is_cairomakie_atomic_plot(plot::Tricontourf) + return true +end diff --git a/CairoMakie/src/primitives.jl b/CairoMakie/src/primitives.jl index 1309a95e7ca..f636b129b8c 100644 --- a/CairoMakie/src/primitives.jl +++ b/CairoMakie/src/primitives.jl @@ -3,8 +3,7 @@ ################################################################################ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Union{Lines, LineSegments})) - fields = @get_attribute(primitive, (color, linewidth, linestyle)) - linestyle = Makie.convert_attribute(linestyle, Makie.key"linestyle"()) + @get_attribute(primitive, (color, linewidth, linestyle)) ctx = screen.context model = primitive[:model][] positions = primitive[1][] @@ -30,7 +29,7 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Unio space = to_value(get(primitive, :space, :data)) projected_positions = project_position.(Ref(scene), (Makie.transform_func(primitive),), Ref(space), positions, Ref(model)) - color = to_cairo_color(color, primitive) + color = to_color(primitive.calculated_colors[]) # color is now a color or an array of colors # if it's an array of colors, each segment must be stroked separately @@ -58,8 +57,10 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Unio # stroke each segment separately, this means disjointed segments with probably # wonky dash patterns if segments are short - # we can hide the gaps by setting the line cap to round - Cairo.set_line_cap(ctx, Cairo.CAIRO_LINE_CAP_ROUND) + # Butted segments look the best for varying colors, at least when connection angles are small. + # While round style has nicer sharp joins, it looks bad with alpha colors (double paint) and + # also messes with dash patterns (they are too long because of the caps) + Cairo.set_line_cap(ctx, Cairo.CAIRO_LINE_CAP_BUTT) draw_multi( primitive, ctx, projected_positions, @@ -158,30 +159,22 @@ function draw_multi(primitive, ctx, positions, color, linewidths::AbstractArray, draw_multi(primitive, ctx, positions, [color for l in linewidths], linewidths, dash) end -function draw_multi(primitive::Union{Lines, LineSegments}, ctx, positions, colors::AbstractArray, linewidths::AbstractArray, dash) - if primitive isa LineSegments - @assert iseven(length(positions)) - end +function draw_multi(primitive::LineSegments, ctx, positions, colors::AbstractArray, linewidths::AbstractArray, dash) + @assert iseven(length(positions)) @assert length(positions) == length(colors) @assert length(linewidths) == length(colors) - iterator = if primitive isa Lines - 1:length(positions)-1 - elseif primitive isa LineSegments - 1:2:length(positions) - end - - for i in iterator + for i in 1:2:length(positions) if isnan(positions[i+1]) || isnan(positions[i]) continue end - Cairo.move_to(ctx, positions[i]...) - - Cairo.line_to(ctx, positions[i+1]...) if linewidths[i] != linewidths[i+1] error("Cairo doesn't support two different line widths ($(linewidths[i]) and $(linewidths[i+1])) at the endpoints of a line.") end + Cairo.move_to(ctx, positions[i]...) + Cairo.line_to(ctx, positions[i+1]...) Cairo.set_line_width(ctx, linewidths[i]) + !isnothing(dash) && Cairo.set_dash(ctx, dash .* linewidths[i]) c1 = colors[i] c2 = colors[i+1] @@ -199,8 +192,99 @@ function draw_multi(primitive::Union{Lines, LineSegments}, ctx, positions, color Cairo.destroy(pat) end end - # force clearing of path in case of skipped NaN - Cairo.new_path(ctx) +end + +function draw_multi(primitive::Lines, ctx, positions, colors::AbstractArray, linewidths::AbstractArray, dash) + @assert length(positions) == length(colors) + @assert length(linewidths) == length(colors) + + prev_color = colors[begin] + prev_linewidth = linewidths[begin] + prev_position = positions[begin] + prev_nan = isnan(prev_position) + prev_continued = false + + if !prev_nan + # first is not nan, move_to + Cairo.move_to(ctx, positions[begin]...) + else + # first is nan, do nothing + end + + for i in eachindex(positions)[2:end] + this_position = positions[i] + this_color = colors[i] + this_nan = isnan(this_position) + this_linewidth = linewidths[i] + if this_nan + # this is nan + if prev_continued + # and this is prev_continued, so set source and stroke to finish previous line + Cairo.set_line_width(ctx, this_linewidth) + !isnothing(dash) && Cairo.set_dash(ctx, dash .* this_linewidth) + Cairo.set_source_rgba(ctx, red(prev_color), green(prev_color), blue(prev_color), alpha(prev_color)) + Cairo.stroke(ctx) + else + # but this is not prev_continued, so do nothing + end + end + if prev_nan + # previous was nan + if !this_nan + # but this is not nan, so move to this position + Cairo.move_to(ctx, this_position...) + else + # and this is also nan, do nothing + end + else + if this_color == prev_color + # this color is like the previous + if !this_nan + # and this is not nan, so line_to and set prev_continued + this_linewidth != prev_linewidth && error("Encountered two different linewidth values $prev_linewidth and $this_linewidth in `lines` at index $(i-1). Different linewidths in one line are only permitted in CairoMakie when separated by a NaN point.") + Cairo.line_to(ctx, this_position...) + prev_continued = true + + if i == lastindex(positions) + # this is the last element so stroke this + Cairo.set_line_width(ctx, this_linewidth) + !isnothing(dash) && Cairo.set_dash(ctx, dash .* this_linewidth) + Cairo.set_source_rgba(ctx, red(this_color), green(this_color), blue(this_color), alpha(this_color)) + Cairo.stroke(ctx) + end + else + # but this is nan, so do nothing + end + else + prev_continued = false + if !this_nan + this_linewidth != prev_linewidth && error("Encountered two different linewidth values $prev_linewidth and $this_linewidth in `lines` at index $(i-1). Different linewidths in one line are only permitted in CairoMakie when separated by a NaN point.") + # this is not nan + # and this color is different than the previous, so move_to prev and line_to this + # create gradient pattern and stroke + Cairo.move_to(ctx, prev_position...) + Cairo.line_to(ctx, this_position...) + !isnothing(dash) && Cairo.set_dash(ctx, dash .* this_linewidth) + Cairo.set_line_width(ctx, this_linewidth) + + pat = Cairo.pattern_create_linear(prev_position..., this_position...) + Cairo.pattern_add_color_stop_rgba(pat, 0, red(prev_color), green(prev_color), blue(prev_color), alpha(prev_color)) + Cairo.pattern_add_color_stop_rgba(pat, 1, red(this_color), green(this_color), blue(this_color), alpha(this_color)) + Cairo.set_source(ctx, pat) + Cairo.stroke(ctx) + Cairo.destroy(pat) + + Cairo.move_to(ctx, this_position...) + else + # this is nan, do nothing + end + end + end + prev_nan = this_nan + prev_color = this_color + prev_linewidth = linewidths[i] + prev_position = this_position + end end ################################################################################ @@ -208,35 +292,27 @@ end ################################################################################ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Scatter)) - fields = @get_attribute(primitive, (color, markersize, strokecolor, strokewidth, marker, marker_offset, rotations)) - @get_attribute(primitive, (transform_marker,)) + @get_attribute(primitive, (markersize, strokecolor, strokewidth, marker, marker_offset, rotations, transform_marker)) ctx = screen.context - model = primitive[:model][] + model = primitive.model[] positions = primitive[1][] isempty(positions) && return size_model = transform_marker ? model : Mat4f(I) font = to_font(to_value(get(primitive, :font, Makie.defaultfont()))) - colors = to_cairo_color(color, primitive) - - markerspace = to_value(get(primitive, :markerspace, :pixel)) - space = to_value(get(primitive, :space, :data)) + colors = to_color(primitive.calculated_colors[]) + markerspace = primitive.markerspace[] + space = primitive.space[] transfunc = Makie.transform_func(primitive) - marker_conv = _marker_convert(marker) - - draw_atomic_scatter(scene, ctx, transfunc, colors, markersize, strokecolor, strokewidth, marker_conv, marker_offset, rotations, model, positions, size_model, font, markerspace, space) + return draw_atomic_scatter(scene, ctx, transfunc, colors, markersize, strokecolor, strokewidth, marker, + marker_offset, rotations, model, positions, size_model, font, markerspace, + space) end -# an array of markers is converted to string by itself, which is inconvenient for the iteration logic -_marker_convert(markers::AbstractArray) = map(m -> convert_attribute(m, key"marker"(), key"scatter"()), markers) -_marker_convert(marker) = convert_attribute(marker, key"marker"(), key"scatter"()) -# image arrays need to be converted as a whole -_marker_convert(marker::AbstractMatrix{<:Colorant}) = [ convert_attribute(marker, key"marker"(), key"scatter"()) ] - function draw_atomic_scatter(scene, ctx, transfunc, colors, markersize, strokecolor, strokewidth, marker, marker_offset, rotations, model, positions, size_model, font, markerspace, space) broadcast_foreach(positions, colors, markersize, strokecolor, strokewidth, marker, marker_offset, remove_billboard(rotations)) do point, col, @@ -251,15 +327,14 @@ function draw_atomic_scatter(scene, ctx, transfunc, colors, markersize, strokeco Cairo.set_source_rgba(ctx, rgbatuple(col)...) Cairo.save(ctx) - marker_converted = Makie.to_spritemarker(m) # Setting a markersize of 0.0 somehow seems to break Cairos global state? # At least it stops drawing any marker afterwards # TODO, maybe there's something wrong somewhere else? if !(norm(scale) ≈ 0.0) - if marker_converted isa Char - draw_marker(ctx, marker_converted, best_font(m, font), pos, scale, strokecolor, strokewidth, offset, rotation) + if m isa Char + draw_marker(ctx, m, best_font(m, font), pos, scale, strokecolor, strokewidth, offset, rotation) else - draw_marker(ctx, marker_converted, pos, scale, strokecolor, strokewidth, offset, rotation) + draw_marker(ctx, m, pos, scale, strokecolor, strokewidth, offset, rotation) end end Cairo.restore(ctx) @@ -392,7 +467,7 @@ function draw_marker(ctx, marker::Matrix{T}, pos, scale, # convert marker to Cairo compatible image data marker = permutedims(marker, (2,1)) - marker_surf = to_cairo_image(marker, ()) + marker_surf = to_cairo_image(marker) w, h = size(marker) @@ -606,6 +681,17 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Unio t = Makie.transform_func(primitive) identity_transform = (t === identity || t isa Tuple && all(x-> x === identity, t)) && (abs(model[1, 2]) < 1e-15) regular_grid = xs isa AbstractRange && ys isa AbstractRange + xy_aligned = let + # Only allow scaling and translation + pv = scene.camera.projectionview[] + M = Mat4f( + pv[1, 1], 0.0, 0.0, 0.0, + 0.0, pv[2, 2], 0.0, 0.0, + 0.0, 0.0, pv[3, 3], 0.0, + pv[1, 4], pv[2, 4], pv[3, 4], 1.0 + ) + pv ≈ M + end if interpolate if !regular_grid @@ -624,11 +710,12 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Unio xymax = project_position(scene, space, Point2f(last.(imsize)), model) w, h = xymax .- xy - can_use_fast_path = !(is_vector && !interpolate) && regular_grid && identity_transform + can_use_fast_path = !(is_vector && !interpolate) && regular_grid && identity_transform && + (interpolate || xy_aligned) use_fast_path = can_use_fast_path && !disable_fast_path if use_fast_path - s = to_cairo_image(image, primitive) + s = to_cairo_image(to_color(primitive.calculated_colors[])) weird_cairo_limit = (2^15) - 23 if s.width > weird_cairo_limit || s.height > weird_cairo_limit @@ -651,7 +738,7 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Unio # this already takes care of flipping the image to correct cairo orientation space = to_value(get(primitive, :space, :data)) xys = project_position.(scene, (Makie.transform_func(primitive),), space, [Point2f(x, y) for x in xs, y in ys], (model,)) - colors = to_rgba_image(image, primitive) + colors = to_color(primitive.calculated_colors[]) # Note: xs and ys should have size ni+1, nj+1 ni, nj = size(image) @@ -716,23 +803,12 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Maki end function draw_mesh2D(scene, screen, @nospecialize(plot), @nospecialize(mesh)) - @get_attribute(plot, (color,)) - color = to_color(hasproperty(mesh, :color) ? mesh.color : color) vs = decompose(Point2f, mesh)::Vector{Point2f} fs = decompose(GLTriangleFace, mesh)::Vector{GLTriangleFace} uv = decompose_uv(mesh)::Union{Nothing, Vector{Vec2f}} model = plot.model[]::Mat4f - colormap = haskey(plot, :colormap) ? to_colormap(plot.colormap[]) : nothing - colorrange = convert_attribute(to_value(get(plot, :colorrange, nothing)), key"colorrange"())::Union{Nothing, Vec2f} - - lowclip = get_color_attr(plot, :lowclip) - highclip = get_color_attr(plot, :highclip) - nan_color = get_color_attr(plot, :nan_color) - - cols = per_face_colors( - color, colormap, colorrange, nothing, fs, nothing, uv, - lowclip, highclip, nan_color) - + color = hasproperty(mesh, :color) ? to_color(mesh.color) : plot.calculated_colors[] + cols = per_face_colors(color, nothing, fs, nothing, uv) space = to_value(get(plot, :space, :data))::Symbol transform_func = Makie.transform_func(plot) return draw_mesh2D(scene, screen, cols, space, transform_func, vs, fs, model) @@ -776,30 +852,18 @@ nan2zero(x) = !isnan(x) * x function draw_mesh3D(scene, screen, attributes, mesh; pos = Vec4f(0), scale = 1f0) - # Priorize colors of the mesh if present - @get_attribute(attributes, (color,)) + @get_attribute(attributes, (shading, diffuse, specular, shininess, faceculling)) - colormap = haskey(attributes, :colormap) ? to_colormap(attributes.colormap[]) : nothing - colorrange = convert_attribute(to_value(get(attributes, :colorrange, nothing)), key"colorrange"())::Union{Nothing, Vec2f} matcap = to_value(get(attributes, :matcap, nothing)) - - color = hasproperty(mesh, :color) ? mesh.color : color meshpoints = decompose(Point3f, mesh)::Vector{Point3f} meshfaces = decompose(GLTriangleFace, mesh)::Vector{GLTriangleFace} meshnormals = decompose_normals(mesh)::Vector{Vec3f} meshuvs = texturecoordinates(mesh)::Union{Nothing, Vector{Vec2f}} - lowclip = get_color_attr(attributes, :lowclip) - highclip = get_color_attr(attributes, :highclip) - nan_color = get_color_attr(attributes, :nan_color) - - per_face_col = per_face_colors( - color, colormap, colorrange, matcap, meshfaces, meshnormals, meshuvs, - lowclip, highclip, nan_color - ) + # Priorize colors of the mesh if present + color = hasproperty(mesh, :color) ? mesh.color : to_value(attributes.calculated_colors) - @get_attribute(attributes, (shading, diffuse, - specular, shininess, faceculling)) + per_face_col = per_face_colors(color, matcap, meshfaces, meshnormals, meshuvs) model = attributes.model[]::Mat4f space = to_value(get(attributes, :space, :data))::Symbol @@ -973,13 +1037,7 @@ end function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Makie.MeshScatter)) - @get_attribute(primitive, (color, model, marker, markersize, rotations)) - - if color isa AbstractArray{<: Number} - color = numbers_to_colors(color, primitive) - end - - m = convert_attribute(marker, key"marker"(), key"meshscatter"()) + @get_attribute(primitive, (model, marker, markersize, rotations)) pos = primitive[1][] # For correct z-ordering we need to be in view/camera or screen space model = copy(model) @@ -991,9 +1049,10 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Maki cam_pos[3] / cam_pos[4] end, rev=false) + color = to_color(primitive.calculated_colors[]) submesh = Attributes( model=model, - color=color, + calculated_colors = color, shading=primitive.shading, diffuse=primitive.diffuse, specular=primitive.specular, shininess=primitive.shininess, faceculling=get(primitive, :faceculling, -10), @@ -1006,11 +1065,10 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Maki submesh[:model] = model * R end scales = primitive[:markersize][] - for i in zorder p = pos[i] if color isa AbstractVector - submesh[:color] = color[i] + submesh[:calculated_colors] = color[i] end if rotations isa Vector R = Makie.rotationmatrix4(to_rotation(rotations[i])) @@ -1019,7 +1077,7 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Maki scale = markersize isa Vector ? markersize[i] : markersize draw_mesh3D( - scene, screen, submesh, m, pos = p, + scene, screen, submesh, marker, pos = p, scale = scale isa Real ? Vec3f(scale) : to_ndim(Vec3f, scale, 1f0) ) end diff --git a/CairoMakie/src/screen.jl b/CairoMakie/src/screen.jl index ed8cd516697..16de9cca42f 100644 --- a/CairoMakie/src/screen.jl +++ b/CairoMakie/src/screen.jl @@ -311,3 +311,6 @@ end is_vector_backend(ctx::Cairo.CairoContext) = is_vector_backend(ctx.surface) is_vector_backend(surf::Cairo.CairoSurface) = is_vector_backend(get_render_type(surf)) is_vector_backend(rt::RenderType) = rt in (PDF, EPS, SVG) + +# no need for resizing screen, since we need to redisplay anyways! +Base.resize!(screen::Screen, w, h) = nothing diff --git a/CairoMakie/src/utils.jl b/CairoMakie/src/utils.jl index f3a2ad4cca0..32dd81c49ac 100644 --- a/CairoMakie/src/utils.jl +++ b/CairoMakie/src/utils.jl @@ -2,13 +2,13 @@ # Projection utilities # ################################################################################ -function project_position(scene, transform_func::T, space, point, model, yflip::Bool = true) where T +function project_position(scene::Scene, transform_func::T, space, point, model::Mat4, yflip::Bool = true) where T # use transform func point = Makie.apply_transform(transform_func, point, space) _project_position(scene, space, point, model, yflip) end -function _project_position(scene, space, point, model, yflip) +function _project_position(scene::Scene, space, point, model, yflip::Bool) res = scene.camera.resolution[] p4d = to_ndim(Vec4f, to_ndim(Vec3f, point, 0f0), 1f0) clip = Makie.space_to_clip(scene.camera, space) * model * p4d @@ -24,8 +24,9 @@ function _project_position(scene, space, point, model, yflip) return p_0_to_1 .* res end -function project_position(scene, space, point, model, yflip::Bool = true) - project_position(scene, scene.transformation.transform_func[], space, point, model, yflip) +function project_position(scenelike, space, point, model, yflip::Bool = true) + scene = Makie.get_scene(scenelike) + project_position(scene, Makie.transform_func(scenelike), space, point, model, yflip) end function project_scale(scene::Scene, space, s::Number, model = Mat4f(I)) @@ -46,23 +47,23 @@ function project_scale(scene::Scene, space, s, model = Mat4f(I)) end end -function project_rect(scene, space, rect::Rect, model) - mini = project_position(scene, space, minimum(rect), model) - maxi = project_position(scene, space, maximum(rect), model) +function project_rect(scenelike, space, rect::Rect, model) + mini = project_position(scenelike, space, minimum(rect), model) + maxi = project_position(scenelike, space, maximum(rect), model) return Rect(mini, maxi .- mini) end -function project_polygon(scene, space, poly::P, model) where P <: Polygon +function project_polygon(scenelike, space, poly::P, model) where P <: Polygon ext = decompose(Point2f, poly.exterior) interiors = decompose.(Point2f, poly.interiors) Polygon( - Point2f.(project_position.(Ref(scene), space, ext, Ref(model))), - [Point2f.(project_position.(Ref(scene), space, interior, Ref(model))) for interior in interiors], + Point2f.(project_position.(Ref(scenelike), space, ext, Ref(model))), + [Point2f.(project_position.(Ref(scenelike), space, interior, Ref(model))) for interior in interiors], ) end -function project_multipolygon(scene, space, multipoly::MP, model) where MP <: MultiPolygon - return MultiPolygon(project_polygon.(Ref(scene), Ref(space), multipoly.polygons, Ref(model))) +function project_multipolygon(scenelike, space, multipoly::MP, model) where MP <: MultiPolygon + return MultiPolygon(project_polygon.(Ref(scenelike), Ref(space), multipoly.polygons, Ref(model))) end scale_matrix(x, y) = Cairo.CairoMatrix(x, 0.0, 0.0, y, 0.0, 0.0) @@ -122,7 +123,8 @@ to_uint32_color(c) = reinterpret(UInt32, convert(ARGB32, premultiplied_rgba(c))) ######################################## function to_cairo_color(colors::Union{AbstractVector{<: Number},Number}, plot_object) - return numbers_to_colors(colors, plot_object) + cmap = Makie.assemble_colors(colors, Observable(colors), plot_object) + return to_color(to_value(cmap)) end function to_cairo_color(color::Makie.AbstractPattern, plot_object) @@ -147,40 +149,10 @@ end # Image/heatmap -> ARGBSurface # ######################################## -function to_cairo_image(img::AbstractMatrix{<: AbstractFloat}, attributes) - to_cairo_image(to_rgba_image(img, attributes), attributes) -end - -function to_rgba_image(img::AbstractMatrix{<: AbstractFloat}, attributes) - Makie.@get_attribute attributes (colormap, colorrange, nan_color, lowclip, highclip) - - nan_color = Makie.to_color(nan_color) - lowclip = isnothing(lowclip) ? lowclip : Makie.to_color(lowclip) - highclip = isnothing(highclip) ? highclip : Makie.to_color(highclip) - - [get_rgba_pixel(pixel, colormap, colorrange, nan_color, lowclip, highclip) for pixel in img] -end - -to_rgba_image(img::AbstractMatrix{<: Colorant}, attributes) = RGBAf.(img) - -function get_rgba_pixel(pixel, colormap, colorrange, nan_color, lowclip, highclip) - vmin, vmax = colorrange - if isnan(pixel) - RGBAf(nan_color) - elseif pixel < vmin && !isnothing(lowclip) - RGBAf(lowclip) - elseif pixel > vmax && !isnothing(highclip) - RGBAf(highclip) - else - RGBAf(Makie.interpolated_getindex(colormap, pixel, colorrange)) - end -end -function to_cairo_image(img::AbstractMatrix{<: Colorant}, attributes) - to_cairo_image(to_uint32_color.(img), attributes) -end +to_cairo_image(img::AbstractMatrix{<: Colorant}) = to_cairo_image(to_uint32_color.(img)) -function to_cairo_image(img::Matrix{UInt32}, attributes) +function to_cairo_image(img::Matrix{UInt32}) # we need to convert from column-major to row-major storage, # therefore we permute x and y return Cairo.CairoARGBSurface(permutedims(img)) @@ -223,11 +195,9 @@ function get_color_attr(attributes, attribute)::Union{Nothing, RGBAf} return color_or_nothing(to_value(get(attributes, attribute, nothing))) end -function per_face_colors( - color, colormap, colorrange, matcap, faces, normals, uv, - lowclip=nothing, highclip=nothing, nan_color=nothing - ) - if matcap !== nothing +function per_face_colors(_color, matcap, faces, normals, uv) + color = to_color(_color) + if !isnothing(matcap) wsize = reverse(size(matcap)) wh = wsize .- 1 cvec = map(normals) do n @@ -238,37 +208,21 @@ function per_face_colors( return FaceIterator(cvec, faces) elseif color isa Colorant return FaceIterator{:Const}(color, faces) - elseif color isa AbstractArray - if color isa AbstractVector{<: Colorant} - return FaceIterator(color, faces) - elseif color isa AbstractArray{<: Number} - low, high = extrema(colorrange) - cvec = map(color[:]) do c - if isnan(c) && nan_color !== nothing - return nan_color - elseif c < low && lowclip !== nothing - return lowclip - elseif c > high && highclip !== nothing - return highclip - else - Makie.interpolated_getindex(colormap, c, colorrange) - end - end - return FaceIterator(cvec, faces) - elseif color isa Makie.AbstractPattern - # let next level extend and fill with CairoPattern - return color - elseif color isa AbstractMatrix{<: Colorant} && uv !== nothing - wsize = reverse(size(color)) - wh = wsize .- 1 - cvec = map(uv) do uv - x, y = clamp.(round.(Int, Tuple(uv) .* wh) .+ 1, 1, wh) - return color[end - (y - 1), x] - end - # TODO This is wrong and doesn't actually interpolate - # Inside the triangle sampling the color image - return FaceIterator(cvec, faces) + elseif color isa AbstractVector{<: Colorant} + return FaceIterator(color, faces) + elseif color isa Makie.AbstractPattern + # let next level extend and fill with CairoPattern + return color + elseif color isa AbstractMatrix{<: Colorant} && !isnothing(uv) + wsize = reverse(size(color)) + wh = wsize .- 1 + cvec = map(uv) do uv + x, y = clamp.(round.(Int, Tuple(uv) .* wh) .+ 1, 1, wh) + return color[end - (y - 1), x] end + # TODO This is wrong and doesn't actually interpolate + # Inside the triangle sampling the color image + return FaceIterator(cvec, faces) end error("Unsupported Color type: $(typeof(color))") end diff --git a/CairoMakie/test/Project.toml b/CairoMakie/test/Project.toml new file mode 100644 index 00000000000..da4b952feee --- /dev/null +++ b/CairoMakie/test/Project.toml @@ -0,0 +1,4 @@ +[deps] +Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" +ReferenceTests = "d37af2e0-5618-4e00-9939-d430db56ee94" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/CairoMakie/test/runtests.jl b/CairoMakie/test/runtests.jl index d7ae59760ff..84330b132e7 100644 --- a/CairoMakie/test/runtests.jl +++ b/CairoMakie/test/runtests.jl @@ -1,10 +1,8 @@ using Test using CairoMakie -using Pkg using Makie.FileIO +using ReferenceTests -path = normpath(joinpath(dirname(pathof(Makie)), "..", "ReferenceTests")) -Pkg.develop(PackageSpec(path = path)) # Before changing Pkg environment, try the test in #864 @testset "Runs without error" begin fig = Figure() diff --git a/GLMakie/Project.toml b/GLMakie/Project.toml index ad8027ba3a2..e649a787568 100644 --- a/GLMakie/Project.toml +++ b/GLMakie/Project.toml @@ -1,6 +1,6 @@ name = "GLMakie" uuid = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" -version = "0.8.6" +version = "0.8.9" [deps] ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" @@ -29,11 +29,11 @@ FixedPointNumbers = "0.7, 0.8" FreeTypeAbstraction = "0.10" GLFW = "3" GeometryBasics = "0.4.1" -Makie = "=0.19.6" +Makie = "=0.19.9" MeshIO = "0.4" ModernGL = "1" Observables = "0.5.1" -ShaderAbstractions = "0.3" +ShaderAbstractions = "0.4" PrecompileTools = "1.0" StaticArrays = "0.12, 1.0" julia = "1" diff --git a/GLMakie/assets/shader/heatmap.frag b/GLMakie/assets/shader/heatmap.frag index 62c6a391944..29171539322 100644 --- a/GLMakie/assets/shader/heatmap.frag +++ b/GLMakie/assets/shader/heatmap.frag @@ -8,13 +8,9 @@ in vec2 o_uv; flat in uvec2 o_objectid; {{intensity_type}} intensity; -uniform sampler1D color_map; +{{color_map_type}} color_map; {{color_norm_type}} color_norm; -uniform float stroke_width; -uniform vec4 stroke_color; -uniform float levels; - uniform vec4 highclip; uniform vec4 lowclip; uniform vec4 nan_color; @@ -38,7 +34,7 @@ vec4 get_color_from_cmap(float value, sampler1D color_map, vec2 colorrange) { return texture(color_map, i01); } -vec4 get_color(sampler2D intensity, vec2 uv, Nothing color_norm, sampler1D color_map){ +vec4 get_color(sampler2D intensity, vec2 uv, Nothing color_norm, Nothing color_map){ return getindex(intensity, uv); } diff --git a/GLMakie/assets/shader/line_segment.vert b/GLMakie/assets/shader/line_segment.vert index 7e42313e132..d93ee008ca3 100644 --- a/GLMakie/assets/shader/line_segment.vert +++ b/GLMakie/assets/shader/line_segment.vert @@ -10,7 +10,7 @@ in float lastlen; {{thickness_type}} thickness; {{color_type}} color; -{{color_map_type}} color_map; +{{color_map_type}} color_map; {{color_norm_type}} color_norm; uniform mat4 projectionview, model; diff --git a/GLMakie/assets/shader/lines.geom b/GLMakie/assets/shader/lines.geom index 0162ca8ec5c..2670ad655ae 100644 --- a/GLMakie/assets/shader/lines.geom +++ b/GLMakie/assets/shader/lines.geom @@ -49,16 +49,21 @@ vec3 screen_space(vec4 vertex) // Manual uv calculation // - position in screen space (double resolution as generally used) // - uv with uv.u normalized (0..1), uv.v unnormalized (0..pattern_length) -void emit_vertex(vec3 position, vec2 uv, int index) +void emit_vertex(vec3 position, vec2 uv, int index, float thickness) { f_uv = uv; f_color = g_color[index]; gl_Position = vec4((position.xy / resolution), position.z, 1.0); f_id = g_id[index]; // linewidth scaling may shrink the effective linewidth - f_thickness = abs(uv.y) - AA_THICKNESS; + f_thickness = thickness; EmitVertex(); } +// default for miter joins +void emit_vertex(vec3 position, vec2 uv, int index) +{ + emit_vertex(position, uv, index, g_thickness[index]); +} // For center point void emit_vertex(vec3 position, vec2 uv) @@ -72,15 +77,20 @@ void emit_vertex(vec3 position, vec2 uv) } // Debug -void emit_vertex(vec3 position, vec2 uv, int index, vec4 color) +void emit_vertex(vec3 position, vec2 uv, int index, vec4 color, float thickness) { f_uv = uv; f_color = color; gl_Position = vec4((position.xy / resolution), position.z, 1.0); f_id = g_id[index]; - f_thickness = abs(uv.y) - AA_THICKNESS; + f_thickness = thickness; EmitVertex(); } +// default for miter joins +void emit_vertex(vec3 position, vec2 uv , int index, vec4 color) +{ + emit_vertex(position, uv , index, color, g_thickness[index]); +} void emit_vertex(vec3 position, vec2 uv, vec4 color) { f_uv = uv; @@ -98,8 +108,11 @@ void emit_vertex(vec3 position, vec2 offset, vec2 line_dir, vec2 uv, int index) emit_vertex( position + vec3(offset, 0), vec2(uv.x + px2uv * dot(line_dir, offset), uv.y), - index + index, + abs(uv.y) - AA_THICKNESS ); + // `abs(uv.y) - AA_THICKNESS` corrects for enlarged AA padding between + // segments of different linewidth, see #2953 } void emit_vertex(vec3 position, vec2 offset, vec2 line_dir, vec2 uv) diff --git a/GLMakie/assets/shader/util.vert b/GLMakie/assets/shader/util.vert index dfd42427ccb..5d3863deff8 100644 --- a/GLMakie/assets/shader/util.vert +++ b/GLMakie/assets/shader/util.vert @@ -111,6 +111,34 @@ mat4 translate_scale(vec3 xyz, vec3 scale){ vec4(xyz, 1)); } + +uniform vec4 highclip; +uniform vec4 lowclip; +uniform vec4 nan_color; + +vec4 get_color_from_cmap(float value, sampler1D color_map, vec2 colorrange) { +float cmin = colorrange.x; +float cmax = colorrange.y; +if (value <= cmax && value >= cmin) { + // in value range, continue! +} else if (value < cmin) { +return lowclip; +} else if (value > cmax) { +return highclip; +} else { + // isnan CAN be broken (of course) -.- + // so if outside value range and not smaller/bigger min/max we assume NaN +return nan_color; +} +float i01 = clamp((value - cmin) / (cmax - cmin), 0.0, 1.0); + // 1/0 corresponds to the corner of the colormap, so to properly interpolate + // between the colors, we need to scale it, so that the ends are at 1 - (stepsize/2) and 0+(stepsize/2). +float stepsize = 1.0 / float(textureSize(color_map, 0)); +i01 = (1.0 - stepsize) * i01 + 0.5 * stepsize; +return texture(color_map, i01); +} + + //Mapping 1D index to 1D, 2D and 3D arrays int ind2sub(int dim, int linearindex){return linearindex;} ivec2 ind2sub(ivec2 dim, int linearindex){ @@ -197,13 +225,14 @@ vec4 _color(samplerBuffer color, Nothing intensity, Nothing color_map, Nothing c return texelFetch(color, index); } vec4 _color(Nothing color, sampler1D intensity, sampler1D color_map, vec2 color_norm, int index, int len){ - return color_lookup(texture(intensity, float(index)/float(len-1)).x, color_map, color_norm); + float value = texture(intensity, float(index) / float(len - 1)).x; + return get_color_from_cmap(value, color_map, color_norm); } vec4 _color(Nothing color, samplerBuffer intensity, sampler1D color_map, vec2 color_norm, int index, int len){ return vec4(texelFetch(intensity, index).x, 0.0, 0.0, 0.0); } vec4 _color(Nothing color, float intensity, sampler1D color_map, vec2 color_norm, int index, int len){ - return color_lookup(intensity, color_map, color_norm); + return get_color_from_cmap(intensity, color_map, color_norm); } out vec3 o_view_pos; @@ -353,29 +382,3 @@ vec3 getnormal(Nothing pos, sampler1D xs, sampler1D ys, sampler2D zs, vec2 uv){ return normal_from_points(s0, s1, s2, s3, s4, uv, off1, off2, off3, off4); } - -uniform vec4 highclip; -uniform vec4 lowclip; -uniform vec4 nan_color; - -vec4 get_color_from_cmap(float value, sampler1D color_map, vec2 colorrange) { - float cmin = colorrange.x; - float cmax = colorrange.y; - if (value <= cmax && value >= cmin) { - // in value range, continue! - } else if (value < cmin) { - return lowclip; - } else if (value > cmax) { - return highclip; - } else { - // isnan CAN be broken (of course) -.- - // so if outside value range and not smaller/bigger min/max we assume NaN - return nan_color; - } - float i01 = clamp((value - cmin) / (cmax - cmin), 0.0, 1.0); - // 1/0 corresponds to the corner of the colormap, so to properly interpolate - // between the colors, we need to scale it, so that the ends are at 1 - (stepsize/2) and 0+(stepsize/2). - float stepsize = 1.0 / float(textureSize(color_map, 0)); - i01 = (1.0 - stepsize) * i01 + 0.5 * stepsize; - return texture(color_map, i01); -} diff --git a/GLMakie/src/GLAbstraction/GLRender.jl b/GLMakie/src/GLAbstraction/GLRender.jl index 72581554e46..e48d3a11c3a 100644 --- a/GLMakie/src/GLAbstraction/GLRender.jl +++ b/GLMakie/src/GLAbstraction/GLRender.jl @@ -56,7 +56,7 @@ a lot of objects. """ function render(renderobject::RenderObject, vertexarray=renderobject.vertexarray) renderobject.requires_update = false - + if renderobject.visible renderobject.prerenderfunction() program = vertexarray.program diff --git a/GLMakie/src/GLAbstraction/GLTexture.jl b/GLMakie/src/GLAbstraction/GLTexture.jl index 24ab5ccda96..9f6cbd529d8 100644 --- a/GLMakie/src/GLAbstraction/GLTexture.jl +++ b/GLMakie/src/GLAbstraction/GLTexture.jl @@ -137,6 +137,9 @@ Colors from Colors.jl should mostly work as well Texture(image::Array{T, NDim}; kw_args...) where {T <: GLArrayEltypes, NDim} = Texture(pointer(image), size(image); kw_args...)::Texture{T, NDim} +Texture(image::AbstractArray{T, NDim}; kw_args...) where {T <: GLArrayEltypes, NDim} = + Texture(collect(image); kw_args...) + function Texture(s::ShaderAbstractions.Sampler{T, N}; kwargs...) where {T, N} tex = Texture( pointer(s.data), size(s.data), @@ -145,7 +148,8 @@ function Texture(s::ShaderAbstractions.Sampler{T, N}; kwargs...) where {T, N} anisotropic = s.anisotropic; kwargs... ) obsfunc = ShaderAbstractions.connect!(s, tex) - push!(tex.observers, obsfunc) + obsfunc2 = on(x -> tex.requires_update[] = true, s.updates.update) + push!(tex.observers, obsfunc, obsfunc2) return tex end @@ -348,14 +352,19 @@ function similar(t::TextureBuffer{T}, newdims::NTuple{1, Int}) where T buff = similar(t.buffer, newdims...) return TextureBuffer(buff) end + function similar(t::Texture{T, NDim}, newdims::NTuple{NDim, Int}) where {T, NDim} - Texture( - Ptr{T}(C_NULL), - newdims, t.texturetype, + id = glGenTextures() + glBindTexture(t.texturetype, id) + glTexImage(t.texturetype, 0, t.internalformat, newdims..., 0, t.format, t.pixeltype, C_NULL) + return Texture{T, NDim}( + id, + t.texturetype, t.pixeltype, t.internalformat, t.format, - t.parameters + t.parameters, + newdims ) end # Resize Texture @@ -395,7 +404,6 @@ texsubimage(t::Texture{T, 3}, newvalue::Array{T, 3}, xrange::UnitRange, yrange:: t.format, t.pixeltype, newvalue ) - Base.iterate(t::TextureBuffer{T}) where {T} = iterate(t.buffer) function Base.iterate(t::TextureBuffer{T}, state::Tuple{Ptr{T}, Int}) where T v_idx = iterate(t.buffer, state) diff --git a/GLMakie/src/drawing_primitives.jl b/GLMakie/src/drawing_primitives.jl index e67c1d47887..d203c283773 100644 --- a/GLMakie/src/drawing_primitives.jl +++ b/GLMakie/src/drawing_primitives.jl @@ -3,14 +3,6 @@ using Makie: attribute_per_char, FastPixel, el32convert, Pixel using Makie: convert_arguments Makie.el32convert(x::GLAbstraction.Texture) = x -Makie.convert_attribute(s::ShaderAbstractions.Sampler{RGBAf}, k::key"color") = s -function Makie.convert_attribute(s::ShaderAbstractions.Sampler{T, N}, k::key"color") where {T, N} - ShaderAbstractions.Sampler( - el32convert(s.data), minfilter = s.minfilter, magfilter = s.magfilter, - x_repeat = s.repeat[1], y_repeat = s.repeat[min(2, N)], z_repeat = s.repeat[min(3, N)], - anisotropic = s.anisotropic, color_swizzel = s.color_swizzel - ) -end gpuvec(x) = GPUVector(GLBuffer(x)) @@ -78,6 +70,33 @@ function connect_camera!(plot, gl_attributes, cam, space = gl_attributes[:space] return nothing end +function handle_intensities!(attributes, plot) + color = plot.calculated_colors + if color[] isa Makie.ColorMapping + attributes[:intensity] = color[].color_scaled + interp = color[].color_mapping_type[] === Makie.continuous ? :linear : :nearest + attributes[:color_map] = Texture(color[].colormap; minfilter=interp) + attributes[:color_norm] = color[].colorrange_scaled + attributes[:nan_color] = color[].nan_color + attributes[:highclip] = Makie.highclip(color[]) + attributes[:lowclip] = Makie.lowclip(color[]) + attributes[:color] = nothing + else + attributes[:color] = color + delete!(attributes, :intensity) + delete!(attributes, :color_map) + delete!(attributes, :color_norm) + delete!(attributes, :colorscale) + end + return +end + +function get_space(x) + is_fast_pixel = to_value(get(x, :marker, nothing)) isa FastPixel + is_fast_pixel && return x.space + return haskey(x, :markerspace) ? x.markerspace : x.space +end + function cached_robj!(robj_func, screen, scene, x::AbstractPlot) # poll inside functions to make wait on compile less prominent pollevents(screen) @@ -86,7 +105,9 @@ function cached_robj!(robj_func, screen, scene, x::AbstractPlot) !in(k, ( :transformation, :tickranges, :ticklabels, :raw, :SSAO, :lightposition, :material, - :inspector_label, :inspector_hover, :inspector_clear, :inspectable + :inspector_label, :inspector_hover, :inspector_clear, :inspectable, + :colorrange, :colormap, :colorscale, :highclip, :lowclip, :nan_color, + :calculated_colors )) end @@ -108,14 +129,18 @@ function cached_robj!(robj_func, screen, scene, x::AbstractPlot) end gl_attributes[:track_updates] = screen.config.render_on_demand + handle_intensities!(gl_attributes, x) + connect_camera!(x, gl_attributes, scene.camera, get_space(x)) + robj = robj_func(gl_attributes) - !haskey(gl_attributes, :ssao) && (robj[:ssao] = Observable(false)) + get!(gl_attributes, :ssao, Observable(false)) screen.cache2plot[robj.id] = x + robj end push!(screen, scene, robj) - robj + return robj end Makie.Base.insert!(screen::GLMakie.Screen, scene::Scene, x::Makie.PlotList) = nothing @@ -185,16 +210,6 @@ end pixel2world(scene, msize::AbstractVector) = pixel2world.(scene, msize) -function handle_intensities!(attributes) - if haskey(attributes, :color) && attributes[:color][] isa AbstractVector{<: Number} - c = pop!(attributes, :color) - attributes[:intensity] = lift(x-> convert(Vector{Float32}, x), c) - else - delete!(attributes, :intensity) - delete!(attributes, :color_map) - delete!(attributes, :color_norm) - end -end function draw_atomic(screen::Screen, scene::Scene, @nospecialize(x::Union{Scatter, MeshScatter})) return cached_robj!(screen, scene, x) do gl_attributes @@ -202,19 +217,18 @@ function draw_atomic(screen::Screen, scene::Scene, @nospecialize(x::Union{Scatte gl_attributes[:shading] = to_value(get(gl_attributes, :shading, true)) marker = lift_convert(:marker, pop!(gl_attributes, :marker), x) - space = get(gl_attributes, :space, :data) # needs to happen before connect_camera! call + space = x.space positions = handle_view(x[1], gl_attributes) positions = apply_transform(transform_func_obs(x), positions, space) if x isa Scatter - mspace = get(gl_attributes, :markerspace, :pixel) + mspace = x.markerspace cam = scene.camera gl_attributes[:preprojection] = map(space, mspace, cam.projectionview, cam.resolution) do space, mspace, _, _ return Makie.clip_to_space(cam, mspace) * Makie.space_to_clip(cam, space) end # fast pixel does its own setup if !(marker[] isa FastPixel) - connect_camera!(x, gl_attributes, cam, mspace) gl_attributes[:billboard] = map(rot-> isa(rot, Billboard), x.rotations) atlas = gl_texture_atlas() isnothing(gl_attributes[:distancefield][]) && delete!(gl_attributes, :distancefield) @@ -236,23 +250,17 @@ function draw_atomic(screen::Screen, scene::Scene, @nospecialize(x::Union{Scatte gl_attributes[:scale] = scale gl_attributes[:quad_offset] = quad_offset end - else - connect_camera!(x, gl_attributes, scene.camera) end if marker[] isa FastPixel - # connect camera - connect_camera!(x, gl_attributes, cam, get(gl_attributes, :space, :data)) + if haskey(gl_attributes, :intensity) + gl_attributes[:color] = pop!(gl_attributes, :intensity) + end filter!(gl_attributes) do (k, v,) k in (:color_map, :color, :color_norm, :scale, :model, :projectionview, :visible) end - if !(gl_attributes[:color][] isa AbstractVector{<: Number}) - delete!(gl_attributes, :color_norm) - delete!(gl_attributes, :color_map) - end return draw_pixel_scatter(screen, positions, gl_attributes) else - handle_intensities!(gl_attributes) if x isa MeshScatter if haskey(gl_attributes, :color) && to_value(gl_attributes[:color]) isa AbstractMatrix{<: Colorant} gl_attributes[:image] = gl_attributes[:color] @@ -268,18 +276,16 @@ end _mean(xs) = sum(xs) / length(xs) # skip Statistics import - function draw_atomic(screen::Screen, scene::Scene, @nospecialize(x::Lines)) return cached_robj!(screen, scene, x) do gl_attributes linestyle = pop!(gl_attributes, :linestyle) data = Dict{Symbol, Any}(gl_attributes) positions = handle_view(x[1], data) - space = get!(gl_attributes, :space, :data) # needs to happen before connect_camera! call - connect_camera!(x, data, scene.camera) transform_func = transform_func_obs(x) ls = to_value(linestyle) + space = x.space if isnothing(ls) data[:pattern] = ls data[:fast] = true @@ -302,8 +308,6 @@ function draw_atomic(screen::Screen, scene::Scene, @nospecialize(x::Lines)) output end end - - handle_intensities!(data) return draw_lines(screen, positions, data) end end @@ -321,18 +325,11 @@ function draw_atomic(screen::Screen, scene::Scene, @nospecialize(x::LineSegments data[:pattern] = ls .* _mean(to_value(linewidth)) data[:fast] = false end - space = get(gl_attributes, :space, :data) # needs to happen before connect_camera! call positions = handle_view(x.converted[1], data) - positions = apply_transform(transform_func_obs(x), positions, space) - if haskey(data, :color) && data[:color][] isa AbstractVector{<: Number} - c = pop!(data, :color) - data[:color] = el32convert(c) - else - delete!(data, :color_map) - delete!(data, :color_norm) + positions = apply_transform(transform_func_obs(x), positions, x.space) + if haskey(data, :intensity) + data[:color] = pop!(data, :intensity) end - connect_camera!(x, data, scene.camera) - return draw_linesegments(screen, positions, data) end end @@ -344,8 +341,8 @@ function draw_atomic(screen::Screen, scene::Scene, transfunc = Makie.transform_func_obs(x) pos = gl_attributes[:position] - space = get(gl_attributes, :space, Observable(:data)) # needs to happen before connect_camera! call - markerspace = gl_attributes[:markerspace] + space = x.space + markerspace = x.markerspace offset = pop!(gl_attributes, :offset, Vec2f(0)) atlas = gl_texture_atlas() @@ -406,7 +403,6 @@ function draw_atomic(screen::Screen, scene::Scene, gl_attributes[:preprojection] = map(space, markerspace, cam.projectionview, cam.resolution) do s, ms, pv, res Makie.clip_to_space(cam, ms) * Makie.space_to_clip(cam, s) end - connect_camera!(x, gl_attributes, cam, markerspace) return draw_scatter(screen, (DISTANCEFIELD, positions), gl_attributes) end @@ -418,12 +414,12 @@ xy_convert(x::AbstractArray{Float32}, n) = copy(x) xy_convert(x::AbstractArray, n) = el32convert(x) xy_convert(x, n) = Float32[LinRange(extrema(x)..., n + 1);] -function draw_atomic(screen::Screen, scene::Scene, x::Heatmap) - return cached_robj!(screen, scene, x) do gl_attributes - t = Makie.transform_func_obs(x) - mat = x[3] - space = get(gl_attributes, :space, :data) # needs to happen before connect_camera! call - xypos = map(t, x[1], x[2], space) do t, x, y, space +function draw_atomic(screen::Screen, scene::Scene, heatmap::Heatmap) + return cached_robj!(screen, scene, heatmap) do gl_attributes + t = Makie.transform_func_obs(heatmap) + mat = heatmap[3] + space = heatmap.space # needs to happen before connect_camera! call + xypos = lift(t, heatmap[1], heatmap[2], space) do t, x, y, space x1d = xy_convert(x, size(mat[], 1)) y1d = xy_convert(y, size(mat[], 2)) # Only if transform doesn't do anything, we can stay linear in 1/2D @@ -451,51 +447,48 @@ function draw_atomic(screen::Screen, scene::Scene, x::Heatmap) end interp = to_value(pop!(gl_attributes, :interpolate)) interp = interp ? :linear : :nearest - if !(to_value(mat) isa ShaderAbstractions.Sampler) - tex = Texture(el32convert(mat), minfilter = interp) + intensity = haskey(gl_attributes, :intensity) ? pop!(gl_attributes, :intensity) : pop!(gl_attributes, :color) + if intensity isa ShaderAbstractions.Sampler + gl_attributes[:intensity] = to_value(intensity) else - tex = to_value(mat) + gl_attributes[:intensity] = Texture(el32convert(intensity); minfilter=interp) end - pop!(gl_attributes, :color) - gl_attributes[:stroke_width] = pop!(gl_attributes, :thickness) - connect_camera!(x, gl_attributes, scene.camera) - return draw_heatmap(screen, tex, gl_attributes) + return draw_heatmap(screen, gl_attributes) end end function draw_atomic(screen::Screen, scene::Scene, x::Image) return cached_robj!(screen, scene, x) do gl_attributes - mesh = const_lift(x[1], x[2]) do x, y + position = lift(x, x[1], x[2]) do x, y r = to_range(x, y) x, y = minimum(r[1]), minimum(r[2]) xmax, ymax = maximum(r[1]), maximum(r[2]) rect = Rect2f(x, y, xmax - x, ymax - y) - points = decompose(Point2f, rect) - faces = decompose(GLTriangleFace, rect) - uv = map(decompose_uv(rect)) do uv - return 1f0 .- Vec2f(uv[2], uv[1]) - end - return GeometryBasics.Mesh(meta(points; uv=uv), faces) + return decompose(Point2f, rect) + end + gl_attributes[:vertices] = apply_transform(transform_func_obs(x), position, x.space) + rect = Rect2f(0, 0, 1, 1) + gl_attributes[:faces] = decompose(GLTriangleFace, rect) + gl_attributes[:texturecoordinates] = map(decompose_uv(rect)) do uv + return 1.0f0 .- Vec2f(uv[2], uv[1]) end - gl_attributes[:color] = x[3] gl_attributes[:shading] = false - space = get(gl_attributes, :space, :data) # needs to happen before connect_camera! call - connect_camera!(x, gl_attributes, scene.camera) - return mesh_inner(screen, mesh, transform_func_obs(x), gl_attributes, space) + _interp = to_value(pop!(gl_attributes, :interpolate, true)) + interp = _interp ? :linear : :nearest + if haskey(gl_attributes, :intensity) + gl_attributes[:image] = Texture(pop!(gl_attributes, :intensity); minfilter=interp) + else + gl_attributes[:image] = Texture(pop!(gl_attributes, :color); minfilter=interp) + end + return draw_mesh(screen, gl_attributes) end end -function update_positions(mesh::GeometryBasics.Mesh, positions) - points = coordinates(mesh) - attr = GeometryBasics.attributes(points) - delete!(attr, :position) # position == metafree(points) - return GeometryBasics.Mesh(meta(positions; attr...), faces(mesh)) -end - function mesh_inner(screen::Screen, mesh, transfunc, gl_attributes, space=:data) # signals not supported for shading yet - gl_attributes[:shading] = to_value(pop!(gl_attributes, :shading)) + shading = to_value(pop!(gl_attributes, :shading)) + gl_attributes[:shading] = shading color = pop!(gl_attributes, :color) interp = to_value(pop!(gl_attributes, :interpolate, true)) interp = interp ? :linear : :nearest @@ -517,22 +510,36 @@ function mesh_inner(screen::Screen, mesh, transfunc, gl_attributes, space=:data) elseif to_value(color) isa AbstractVector{<: Union{Number, Colorant}} gl_attributes[:vertex_color] = lift(el32convert, color) else - error("Unsupported color type: $(typeof(to_value(color)))") + # error("Unsupported color type: $(typeof(to_value(color)))") end - mesh = map(mesh, transfunc, space) do mesh, func, space - if !Makie.is_identity_transform(func) - return update_positions(mesh, apply_transform.((func,), mesh.position, space)) + + if haskey(gl_attributes, :intensity) + intensity = pop!(gl_attributes, :intensity) + if intensity[] isa Matrix + gl_attributes[:image] = Texture(intensity, minfilter = interp) + else + gl_attributes[:vertex_color] = intensity end - return mesh + gl_attributes[:color] = nothing + end + + gl_attributes[:vertices] = lift(transfunc, mesh, space) do t, mesh, space + apply_transform(t, metafree(coordinates(mesh)), space) + end + gl_attributes[:faces] = lift(x-> decompose(GLTriangleFace, x), mesh) + if hasproperty(to_value(mesh), :uv) + gl_attributes[:texturecoordinates] = lift(decompose_uv, mesh) + end + if hasproperty(to_value(mesh), :normals) && shading + gl_attributes[:normals] = lift(decompose_normals, mesh) end - return draw_mesh(screen, mesh, gl_attributes) + return draw_mesh(screen, gl_attributes) end function draw_atomic(screen::Screen, scene::Scene, meshplot::Mesh) return cached_robj!(screen, scene, meshplot) do gl_attributes t = transform_func_obs(meshplot) - space = get(gl_attributes, :space, :data) # needs to happen before connect_camera! call - connect_camera!(meshplot, gl_attributes, scene.camera) + space = meshplot.space # needs to happen before connect_camera! call return mesh_inner(screen, meshplot[1], t, gl_attributes, space) end end @@ -543,8 +550,8 @@ function draw_atomic(screen::Screen, scene::Scene, x::Surface) img = nothing # signals not supported for shading yet # We automatically insert x[3] into the color channel, so if it's equal we don't need to do anything - if isa(to_value(color), AbstractMatrix{<: Number}) && to_value(color) !== to_value(x[3]) - img = el32convert(color) + if haskey(gl_attributes, :intensity) + img = pop!(gl_attributes, :intensity) elseif to_value(color) isa Makie.AbstractPattern pattern_img = lift(x -> el32convert(Makie.to_image(x)), color) img = ShaderAbstractions.Sampler(pattern_img, x_repeat=:repeat, minfilter=:nearest) @@ -559,11 +566,10 @@ function draw_atomic(screen::Screen, scene::Scene, x::Surface) gl_attributes[:color_norm] = nothing end - space = get(gl_attributes, :space, :data) # needs to happen before connect_camera! call + space = x.space gl_attributes[:image] = img gl_attributes[:shading] = to_value(get(gl_attributes, :shading, true)) - connect_camera!(x, gl_attributes, scene.camera) @assert to_value(x[3]) isa AbstractMatrix types = map(v -> typeof(to_value(v)), x[1:2]) @@ -623,7 +629,11 @@ function draw_atomic(screen::Screen, scene::Scene, vol::Volume) ) return convert(Mat4f, m) * m2 end - connect_camera!(vol, gl_attributes, scene.camera) - return draw_volume(screen, vol[4], gl_attributes) + if haskey(gl_attributes, :intensity) + intensity = pop!(gl_attributes, :intensity) + return draw_volume(screen, intensity, gl_attributes) + else + return draw_volume(screen, vol[4], gl_attributes) + end end end diff --git a/GLMakie/src/glshaders/image_like.jl b/GLMakie/src/glshaders/image_like.jl index ec21243a53f..97c6fa990e8 100644 --- a/GLMakie/src/glshaders/image_like.jl +++ b/GLMakie/src/glshaders/image_like.jl @@ -31,19 +31,13 @@ end """ A matrix of Intensities will result in a contourf kind of plot """ -function draw_heatmap(screen, main, data::Dict) +function draw_heatmap(screen, data::Dict) primitive = triangle_mesh(Rect2(0f0,0f0,1f0,1f0)) to_opengl_mesh!(data, primitive) @gen_defaults! data begin - intensity = main => Texture - nan_color = RGBAf(1, 0, 0, 1) - highclip = RGBAf(0, 0, 0, 0) - lowclip = RGBAf(0, 0, 0, 0) + intensity = nothing => Texture color_map = nothing => Texture color_norm = nothing - stroke_width::Float32 = 0.0f0 - levels::Float32 = 0f0 - stroke_color = RGBA{Float32}(0,0,0,0) transparency = false shader = GLVisualizeShader( screen, diff --git a/GLMakie/src/glshaders/lines.jl b/GLMakie/src/glshaders/lines.jl index 1fd0b61912d..86a25d916cf 100644 --- a/GLMakie/src/glshaders/lines.jl +++ b/GLMakie/src/glshaders/lines.jl @@ -72,9 +72,9 @@ function draw_lines(screen, position::Union{VectorTypes{T}, MatTypes{T}}, data:: total_length::Int32 = const_lift(x-> Int32(length(x)), position) vertex = p_vec => GLBuffer intensity = nothing + color = nothing => GLBuffer color_map = nothing => Texture color_norm = nothing - color = (color_map == nothing ? default(RGBA, s) : nothing) => GLBuffer thickness = 2f0 => GLBuffer pattern = nothing pattern_sections = pattern => Texture @@ -132,7 +132,7 @@ function draw_linesegments(screen, positions::VectorTypes{T}, data::Dict) where fast = false indices = const_lift(length, positions) => to_index_buffer # TODO update boundingbox - transparency = false + transparency = false shader = GLVisualizeShader( screen, "fragment_output.frag", "util.vert", "line_segment.vert", "line_segment.geom", "lines.frag", @@ -153,6 +153,7 @@ function draw_linesegments(screen, positions::VectorTypes{T}, data::Dict) where data[:pattern] = tex data[:pattern_length] = Float32((last(pattern) - first(pattern))) end - return assemble_shader(data) + robj = assemble_shader(data) + return robj end @specialize diff --git a/GLMakie/src/glshaders/mesh.jl b/GLMakie/src/glshaders/mesh.jl index 26981344155..678d8f7b25f 100644 --- a/GLMakie/src/glshaders/mesh.jl +++ b/GLMakie/src/glshaders/mesh.jl @@ -34,18 +34,20 @@ function to_opengl_mesh!(result, mesh_obs::TOrSignal{<: GeometryBasics.Mesh}) return result end -function draw_mesh(screen, @nospecialize(mesh), data::Dict) - to_opengl_mesh!(data, mesh) +function draw_mesh(screen, data::Dict) @gen_defaults! data begin + vertices = nothing => GLBuffer + faces = nothing => indexbuffer + normals = nothing => GLBuffer shading = true backlight = 0f0 vertex_color = nothing => GLBuffer - texturecoordinates = Vec2f(0) image = nothing => Texture matcap = nothing => Texture color_map = nothing => Texture color_norm = nothing fetch_pixel = false + texturecoordinates = Vec2f(0) => GLBuffer uv_scale = Vec2f(1) transparency = false interpolate_in_fragment_shader = true diff --git a/GLMakie/src/glshaders/particles.jl b/GLMakie/src/glshaders/particles.jl index 9bc4d3b4fae..f44bec57305 100644 --- a/GLMakie/src/glshaders/particles.jl +++ b/GLMakie/src/glshaders/particles.jl @@ -102,7 +102,7 @@ function draw_pixel_scatter(screen, position::VectorTypes, data::Dict) @gen_defaults! data begin vertex = position => GLBuffer color_map = nothing => Texture - color = (color_map === nothing ? default(RGBA{Float32}, s) : nothing) => GLBuffer + color = nothing => GLBuffer color_norm = nothing scale = 2f0 transparency = false @@ -172,7 +172,7 @@ function draw_scatter(screen, (marker, position), data) rot = get!(data, :rotation, Vec4f(0, 0, 0, 1)) rot = vec2quaternion(rot) delete!(data, :rotation) - + @gen_defaults! data begin shape = Cint(0) position = position => GLBuffer @@ -191,7 +191,6 @@ function draw_scatter(screen, (marker, position), data) return shape end end - @gen_defaults! data begin quad_offset = Vec2f(0) => GLBuffer intensity = nothing => GLBuffer @@ -228,6 +227,7 @@ function draw_scatter(screen, (marker, position), data) # different length compared to position. Intensities will be interpolated in that case data[:intensity] = intensity_convert(intensity, position) data[:len] = const_lift(length, position) + return assemble_shader(data) end diff --git a/GLMakie/src/screen.jl b/GLMakie/src/screen.jl index 61021022490..24432b071c6 100644 --- a/GLMakie/src/screen.jl +++ b/GLMakie/src/screen.jl @@ -106,7 +106,7 @@ mutable struct ScreenConfig end end -const LAST_INLINE = Ref{Union{Makie.Automatic, Bool}}(Makie.automatic) +const LAST_INLINE = Ref{Union{Makie.Automatic, Bool}}(false) """ GLMakie.activate!(; screen_config...) @@ -342,7 +342,6 @@ function apply_config!(screen::Screen, config::ScreenConfig; start_renderloop::B replace_processor!(config.fxaa ? fxaa_postprocessor : empty_postprocessor, 3) # Set the config screen.config = config - if start_renderloop start_renderloop!(screen) else @@ -518,7 +517,7 @@ end function Base.delete!(screen::Screen, scene::Scene, plot::AbstractPlot) if !isempty(plot.plots) # this plot consists of children, so we flatten it and delete the children instead - for cplot in Makie.flatten_plots(plot) + for cplot in Makie.collect_atomic_plots(plot) delete!(screen, scene, cplot) end else @@ -688,7 +687,13 @@ function Makie.colorbuffer(screen::Screen, format::Makie.ImageStorageFormat = Ma # GLFW.PollEvents() # keep current buffer size to allows larger-than-window renders render_frame(screen, resize_buffers=false) # let it render - glFinish() # block until opengl is done rendering + if screen.config.visible + GLFW.SwapBuffers(to_native(screen)) + else + # SwapBuffers blocks as well, but if we don't call that + # We need to call glFinish to wait for all OpenGL changes to finish + glFinish() + end if size(ctex) != size(screen.framecache) screen.framecache = Matrix{RGB{N0f8}}(undef, size(ctex)) end @@ -919,7 +924,7 @@ function renderloop(screen) end function plot2robjs(screen::Screen, plot) - plots = Makie.flatten_plots(plot) + plots = Makie.collect_atomic_plots(plot) return map(x-> screen.cache[objectid(x)], plots) end diff --git a/GLMakie/test/Project.toml b/GLMakie/test/Project.toml index b87115ddd72..e8cafac0840 100644 --- a/GLMakie/test/Project.toml +++ b/GLMakie/test/Project.toml @@ -1,8 +1,10 @@ [deps] FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +ReferenceTests = "d37af2e0-5618-4e00-9939-d430db56ee94" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/GLMakie/test/runtests.jl b/GLMakie/test/runtests.jl index 8df6f3b28cf..b235938df7d 100644 --- a/GLMakie/test/runtests.jl +++ b/GLMakie/test/runtests.jl @@ -1,20 +1,16 @@ +using Makie using GLMakie, Test using FileIO using GeometryBasics using GeometryBasics: origin -using Makie -using ImageMagick -using Pkg using Random +using ReferenceTests if !GLMakie.ModernGL.enable_opengl_debugging # can't error, since we can't enable debugging for users @warn("TESTING WITHOUT OPENGL DEBUGGING") end -reference_tests_dir = normpath(joinpath(dirname(pathof(Makie)), "..", "ReferenceTests")) -Pkg.develop(PackageSpec(path = reference_tests_dir)) -using ReferenceTests GLMakie.activate!(framerate=1.0) diff --git a/GLMakie/test/unit_tests.jl b/GLMakie/test/unit_tests.jl index c8d71e1ff5f..e1252cc934f 100644 --- a/GLMakie/test/unit_tests.jl +++ b/GLMakie/test/unit_tests.jl @@ -229,7 +229,7 @@ end images = map(Makie.colorbuffer, screens) @test all(x-> x ≈ first(images), images) - @test Base.summarysize(screens) / 10^6 > 300 + @test Base.summarysize(screens) / 10^6 > 280 foreach(close, screens) for screen in screens diff --git a/MakieCore/Project.toml b/MakieCore/Project.toml index 729ca0b07e5..2e60870acac 100644 --- a/MakieCore/Project.toml +++ b/MakieCore/Project.toml @@ -1,7 +1,7 @@ authors = ["Simon Danisch"] name = "MakieCore" uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" -version = "0.6.3" +version = "0.6.6" [deps] Observables = "510215fc-4207-5dde-b226-833fc4488ee2" diff --git a/MakieCore/src/attributes.jl b/MakieCore/src/attributes.jl index 84eddc36f94..2b30965c7fe 100644 --- a/MakieCore/src/attributes.jl +++ b/MakieCore/src/attributes.jl @@ -31,6 +31,7 @@ Attributes(pairs::AbstractVector) = Attributes(Dict{Symbol, Observable}(node_pai Attributes(pairs::Iterators.Pairs) = Attributes(collect(pairs)) Attributes(nt::NamedTuple) = Attributes(; nt...) attributes(x::Attributes) = getfield(x, :attributes) +attributes(x::AbstractPlot) = getfield(x, :attributes) Base.keys(x::Attributes) = keys(x.attributes) Base.values(x::Attributes) = values(x.attributes) function Base.iterate(x::Attributes, state...) @@ -69,18 +70,16 @@ end Base.merge(target::Attributes, args::Attributes...) = merge!(copy(target), args...) -@generated hasfield(x::T, ::Val{key}) where {T, key} = :($(key in fieldnames(T))) - -@inline function Base.getproperty(x::Union{Attributes, AbstractPlot}, key::Symbol) - if hasfield(x, Val(key)) +function Base.getproperty(x::Union{Attributes, AbstractPlot}, key::Symbol) + if hasfield(typeof(x), key) getfield(x, key) else getindex(x, key) end end -@inline function Base.setproperty!(x::Union{Attributes, AbstractPlot}, key::Symbol, value) - if hasfield(x, Val(key)) +function Base.setproperty!(x::Union{Attributes, AbstractPlot}, key::Symbol, value) + if hasfield(typeof(x), key) setfield!(x, key, value) else setindex!(x, value, key) @@ -186,7 +185,7 @@ function Base.getindex(x::AbstractPlot, key::Symbol) if idx === nothing return x.attributes[key] else - x.converted[idx] + return x.converted[idx] end end @@ -237,7 +236,12 @@ function get_attribute(dict, key, default=nothing) if haskey(dict, key) value = to_value(dict[key]) value isa Automatic && return default - return convert_attribute(to_value(dict[key]), Key{key}()) + plot_k = plotkey(dict) + if isnothing(plot_k) + return convert_attribute(value, Key{key}()) + else + return convert_attribute(value, Key{key}(), Key{plot_k}()) + end else return default end diff --git a/MakieCore/src/basic_plots.jl b/MakieCore/src/basic_plots.jl index 48e5753976a..99381416a82 100644 --- a/MakieCore/src/basic_plots.jl +++ b/MakieCore/src/basic_plots.jl @@ -1,3 +1,109 @@ +default_theme(scene) = generic_plot_attributes!(Attributes()) + + +""" +### Generic attributes + +- `visible::Bool = true` sets whether the plot will be rendered or not. +- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. +- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. +- `fxaa::Bool = true` adjusts whether the plot is rendered with fxaa (anti-aliasing). +- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. +- `depth_shift::Float32 = 0f0` adjusts the depth value of a plot after all other transformations, i.e. in clip space, where `0 <= depth <= 1`. This only applies to GLMakie and WGLMakie and can be used to adjust render order (like a tunable overdraw). +- `model::Makie.Mat4f` sets a model matrix for the plot. This replaces adjustments made with `translate!`, `rotate!` and `scale!`. +- `space::Symbol = :data` sets the transformation space for box encompassing the volume plot. See `Makie.spaces()` for possible inputs. +""" +function generic_plot_attributes!(attr) + attr[:transformation] = automatic + attr[:model] = automatic + attr[:visible] = true + attr[:transparency] = false + attr[:overdraw] = false + attr[:ssao] = false + attr[:inspectable] = true + attr[:depth_shift] = 0.0f0 + attr[:space] = :data + return attr +end + +function generic_plot_attributes(attr) + return ( + transformation = attr[:transformation], + model = attr[:model], + visible = attr[:visible], + transparency = attr[:transparency], + overdraw = attr[:overdraw], + ssao = attr[:ssao], + inspectable = attr[:inspectable], + depth_shift = attr[:depth_shift], + space = attr[:space] + ) +end + +""" +### Color attributes + +- `colormap::Union{Symbol, Vector{<:Colorant}} = :viridis` sets the colormap that is sampled for numeric `color`s. + `PlotUtils.cgrad(...)`, `Makie.Reverse(any_colormap)` can be used as well, or any symbol from ColorBrewer or PlotUtils. + To see all available color gradients, you can call `Makie.available_gradients()`. +- `colorscale::Function = identity` color transform function. Can be any function, but only works well together with `Colorbar` for `identity`, `log`, `log2`, `log10`, `sqrt`, `logit`, `Makie.pseudolog10` and `Makie.Symlog10`. +- `colorrange::Tuple{<:Real, <:Real}` sets the values representing the start and end points of `colormap`. +- `nan_color::Union{Symbol, <:Colorant} = RGBAf(0,0,0,0)` sets a replacement color for `color = NaN`. +- `lowclip::Union{Nothing, Symbol, <:Colorant} = nothing` sets a color for any value below the colorrange. +- `highclip::Union{Nothing, Symbol, <:Colorant} = nothing` sets a color for any value above the colorrange. +- `alpha = 1.0` sets the alpha value of the colormap or color attribute. Multiple alphas like in `plot(alpha=0.2, color=(:red, 0.5)`, will get multiplied. +""" +function colormap_attributes!(attr, colormap) + attr[:colormap] = colormap + attr[:colorscale] = identity + attr[:colorrange] = automatic + attr[:lowclip] = automatic + attr[:highclip] = automatic + attr[:nan_color] = :transparent + attr[:alpha] = 1.0 + return attr +end + +function colormap_attributes(attr) + return ( + colormap = attr[:colormap], + colorscale = attr[:colorscale], + colorrange = attr[:colorrange], + lowclip = attr[:lowclip], + highclip = attr[:highclip], + nan_color = attr[:nan_color], + alpha = attr[:alpha] + ) +end + +""" +### 3D shading attributes + +- `shading = true` enables lighting. +- `diffuse::Vec3f = Vec3f(0.4)` sets how strongly the red, green and blue channel react to diffuse (scattered) light. +- `specular::Vec3f = Vec3f(0.2)` sets how strongly the object reflects light in the red, green and blue channels. +- `shininess::Real = 32.0` sets how sharp the reflection is. +- `ssao::Bool = false` adjusts whether the plot is rendered with ssao (screen space ambient occlusion). Note that this only makes sense in 3D plots and is only applicable with `fxaa = true`. +""" +function shading_attributes!(attr) + attr[:shading] = true + attr[:diffuse] = 0.4 + attr[:specular] = 0.2 + attr[:shininess] = 32.0f0 + attr[:backlight] = 0f0 + attr[:ssao] = false +end + +function shading_attributes(attr) + return ( + shading = attr[:shading], + diffuse = attr[:diffuse], + specular = attr[:specular], + shininess = attr[:shininess], + backlight = attr[:backlight], + ssao = attr[:ssao] + ) +end """ `calculated_attributes!(trait::Type{<: AbstractPlot}, plot)` @@ -21,38 +127,19 @@ Plots an image on range `x, y` (defaults to dimensions). ### Specific to `Image` -- `lowclip::Union{Nothing, Symbol, <:Colorant} = nothing` sets a color for any value below the colorrange. -- `highclip::Union{Nothing, Symbol, <:Colorant} = nothing` sets a color for any value above the colorrange. - `interpolate::Bool = true` sets whether colors should be interpolated. -### Generic +$(Base.Docs.doc(colormap_attributes!)) -- `visible::Bool = true` sets whether the plot will be rendered or not. -- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. -- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. -- `fxaa::Bool = false` adjusts whether the plot is rendered with fxaa (anti-aliasing). -- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. -- `depth_shift::Float32 = 0f0` adjusts the depth value of a plot after all other transformations, i.e. in clip space, where `0 <= depth <= 1`. This only applies to GLMakie and WGLMakie and can be used to adjust render order (like a tunable overdraw). -- `model::Makie.Mat4f` sets a model matrix for the plot. This replaces adjustments made with `translate!`, `rotate!` and `scale!`. -- `color` is set by the plot. -- `colormap::Union{Symbol, Vector{<:Colorant}} = [:black, :white` sets the colormap that is sampled for numeric `color`s. -- `colorrange::Tuple{<:Real, <:Real}` sets the values representing the start and end points of `colormap`. -- `nan_color::Union{Symbol, <:Colorant} = RGBAf(0,0,0,0)` sets a replacement color for `color = NaN`. -- `space::Symbol = :data` sets the transformation space for the position of the image. See `Makie.spaces()` for possible inputs. +$(Base.Docs.doc(MakieCore.generic_plot_attributes!)) """ @recipe(Image, x, y, image) do scene - Attributes(; - default_theme(scene)..., - colormap = [:black, :white], - colorrange = automatic, - lowclip = automatic, - highclip = automatic, - nan_color = :transparent, + attr = Attributes(; interpolate = true, fxaa = false, - inspectable = theme(scene, :inspectable), - space = :data ) + generic_plot_attributes!(attr) + return colormap_attributes!(attr, [:black, :white]) end """ @@ -65,40 +152,22 @@ Plots a heatmap as an image on `x, y` (defaults to interpretation as dimensions) ### Specific to `Heatmap` -- `lowclip::Union{Nothing, Symbol, <:Colorant} = nothing` sets a color for any value below the colorrange. -- `highclip::Union{Nothing, Symbol, <:Colorant} = nothing` sets a color for any value above the colorrange. - `interpolate::Bool = false` sets whether colors should be interpolated. -### Generic +$(Base.Docs.doc(colormap_attributes!)) -- `visible::Bool = true` sets whether the plot will be rendered or not. -- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. -- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. -- `fxaa::Bool = true` adjusts whether the plot is rendered with fxaa (anti-aliasing). -- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. -- `depth_shift::Float32 = 0f0` adjusts the depth value of a plot after all other transformations, i.e. in clip space, where `0 <= depth <= 1`. This only applies to GLMakie and WGLMakie and can be used to adjust render order (like a tunable overdraw). -- `model::Makie.Mat4f` sets a model matrix for the plot. This replaces adjustments made with `translate!`, `rotate!` and `scale!`. -- `color` is set by the plot. -- `colormap::Union{Symbol, Vector{<:Colorant}} = :viridis` sets the colormap that is sampled for numeric `color`s. -- `colorrange::Tuple{<:Real, <:Real}` sets the values representing the start and end points of `colormap`. -- `nan_color::Union{Symbol, <:Colorant} = RGBAf(0,0,0,0)` sets a replacement color for `color = NaN`. -- `space::Symbol = :data` sets the transformation space for the position of the heatmap. See `Makie.spaces()` for possible inputs. +$(Base.Docs.doc(MakieCore.generic_plot_attributes!)) """ @recipe(Heatmap, x, y, values) do scene - Attributes(; - default_theme(scene)..., - colormap = theme(scene, :colormap), - colorrange = automatic, - lowclip = automatic, - highclip = automatic, - nan_color = :transparent, - linewidth = 0.0, + attr = Attributes(; + interpolate = false, - levels = 1, + + linewidth = 0.0, fxaa = true, - inspectable = theme(scene, :inspectable), - space = :data ) + generic_plot_attributes!(attr) + return colormap_attributes!(attr, theme(scene, :colormap)) end """ @@ -122,47 +191,29 @@ Available algorithms are: - `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. -### Generic +$(Base.Docs.doc(shading_attributes!)) -- `visible::Bool = true` sets whether the plot will be rendered or not. -- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. -- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. -- `fxaa::Bool = true` adjusts whether the plot is rendered with fxaa (anti-aliasing). -- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. -- `depth_shift::Float32 = 0f0` adjusts the depth value of a plot after all other transformations, i.e. in clip space, where `0 <= depth <= 1`. This only applies to GLMakie and WGLMakie and can be used to adjust render order (like a tunable overdraw). -- `model::Makie.Mat4f` sets a model matrix for the plot. This replaces adjustments made with `translate!`, `rotate!` and `scale!`. -- `colormap::Union{Symbol, Vector{<:Colorant}} = :viridis` sets the colormap that is sampled for numeric `color`s. -- `colorrange::Tuple{<:Real, <:Real}` sets the values representing the start and end points of `colormap`. -- `nan_color::Union{Symbol, <:Colorant} = RGBAf(0,0,0,0)` sets a replacement color for `color = NaN`. -- `space::Symbol = :data` sets the transformation space for box encompassing the volume plot. See `Makie.spaces()` for possible inputs. -### Generic 3D +$(Base.Docs.doc(colormap_attributes!)) -- `shading = true` enables lighting. -- `diffuse::Vec3f = Vec3f(0.4)` sets how strongly the red, green and blue channel react to diffuse (scattered) light. -- `specular::Vec3f = Vec3f(0.2)` sets how strongly the object reflects light in the red, green and blue channels. -- `shininess::Real = 32.0` sets how sharp the reflection is. -- `ssao::Bool = false` adjusts whether the plot is rendered with ssao (screen space ambient occlusion). Note that this only makes sense in 3D plots and is only applicable with `fxaa = true`. +$(Base.Docs.doc(MakieCore.generic_plot_attributes!)) """ @recipe(Volume, x, y, z, volume) do scene - Attributes(; - default_theme(scene)..., + attr = Attributes(; + algorithm = :mip, isovalue = 0.5, isorange = 0.05, - color = nothing, - colormap = theme(scene, :colormap), - colorrange = (0, 1), + fxaa = true, - inspectable = theme(scene, :inspectable), - space = :data, - diffuse=0.4, - specular=0.2, - shininess=32.0f0 ) + generic_plot_attributes!(attr) + shading_attributes!(attr) + return colormap_attributes!(attr, theme(scene, :colormap)) end """ surface(x, y, z) + surface(z) Plots a surface, where `(x, y)` define a grid whose heights are the entries in `z`. `x` and `y` may be `Vectors` which define a regular grid, **or** `Matrices` which define an irregular grid. @@ -173,51 +224,25 @@ Plots a surface, where `(x, y)` define a grid whose heights are the entries in ### Specific to `Surface` -- `lowclip::Union{Nothing, Symbol, <:Colorant} = nothing` sets a color for any value below the colorrange. -- `highclip::Union{Nothing, Symbol, <:Colorant} = nothing` sets a color for any value above the colorrange. - `invert_normals::Bool = false` inverts the normals generated for the surface. This can be useful to illuminate the other side of the surface. +- `color = nothing`, can be set to an `Matrix{<: Union{Number, Colorant}}` to color surface independent of the `z` component. If `color=nothing`, it defaults to `color=z`. -### Generic +$(Base.Docs.doc(shading_attributes!)) -- `visible::Bool = true` sets whether the plot will be rendered or not. -- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. -- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. -- `fxaa::Bool = true` adjusts whether the plot is rendered with fxaa (anti-aliasing). -- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. -- `depth_shift::Float32 = 0f0` adjusts the depth value of a plot after all other transformations, i.e. in clip space, where `0 <= depth <= 1`. This only applies to GLMakie and WGLMakie and can be used to adjust render order (like a tunable overdraw). -- `model::Makie.Mat4f` sets a model matrix for the plot. This replaces adjustments made with `translate!`, `rotate!` and `scale!`. -- `colormap::Union{Symbol, Vector{<:Colorant}} = :viridis` sets the colormap that is sampled for numeric `color`s. -- `colorrange::Tuple{<:Real, <:Real}` sets the values representing the start and end points of `colormap`. -- `nan_color::Union{Symbol, <:Colorant} = RGBAf(0,0,0,0)` sets a replacement color for `color = NaN`. -- `space::Symbol = :data` sets the transformation space for vertices generated by surface. See `Makie.spaces()` for possible inputs. +$(Base.Docs.doc(colormap_attributes!)) -### Generic 3D - -- `shading = true` enables lighting. -- `diffuse::Vec3f = Vec3f(0.4)` sets how strongly the red, green and blue channel react to diffuse (scattered) light. -- `specular::Vec3f = Vec3f(0.2)` sets how strongly the object reflects light in the red, green and blue channels. -- `shininess::Real = 32.0` sets how sharp the reflection is. -- `ssao::Bool = false` adjusts whether the plot is rendered with ssao (screen space ambient occlusion). Note that this only makes sense in 3D plots and is only applicable with `fxaa = true`. +$(Base.Docs.doc(MakieCore.generic_plot_attributes!)) """ @recipe(Surface, x, y, z) do scene - Attributes(; - default_theme(scene)..., - backlight = 0f0, + attr = Attributes(; color = nothing, - colormap = theme(scene, :colormap), - colorrange = automatic, - lowclip = automatic, - highclip = automatic, - nan_color = :transparent, - shading = true, - fxaa = true, invert_normals = false, - inspectable = theme(scene, :inspectable), - space = :data, - diffuse = 0.4, - specular = 0.2, - shininess = 32f0, + + fxaa = true, ) + shading_attributes!(attr) + generic_plot_attributes!(attr) + return colormap_attributes!(attr, theme(scene, :colormap)) end """ @@ -231,40 +256,30 @@ Creates a connected line plot for each element in `(x, y, z)`, `(x, y)` or `posi ## Attributes -### Specific +### Specific to `Lines` +- `color=theme(scene, :linecolor)` sets the color of the line. If no color is set, multiple calls to `line!` will cycle through the axis color palette. + Otherwise, one can set one color per line point by passing a `Vector{<:Colorant}`, or one colorant for the whole line. If color is a vector of numbers, the colormap args are used to map the numbers to colors. - `cycle::Vector{Symbol} = [:color]` sets which attributes to cycle when creating multiple plots. -- `linestyle::Union{Nothing, Symbol, Vector} = nothing` sets the pattern of the line (e.g. `:solid`, `:dot`, `:dashdot`) +- `linestyle::Union{Nothing, Symbol, Linestyle} = nothing` sets the pattern of the line e.g. `:solid`, `:dot`, `:dashdot`. For custom patterns look at `Linestyle(Number[...])`. - `linewidth::Union{Real, Vector} = 1.5` sets the width of the line in pixel units. -### Generic +$(Base.Docs.doc(colormap_attributes!)) -- `visible::Bool = true` sets whether the plot will be rendered or not. -- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. -- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. -- `fxaa::Bool = false` adjusts whether the plot is rendered with fxaa (anti-aliasing). Note that line plots already use a different form of anti-aliasing. -- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. -- `depth_shift::Float32 = 0f0` adjusts the depth value of a plot after all other transformations, i.e. in clip space, where `0 <= depth <= 1`. This only applies to GLMakie and WGLMakie and can be used to adjust render order (like a tunable overdraw). -- `model::Makie.Mat4f` sets a model matrix for the plot. This replaces adjustments made with `translate!`, `rotate!` and `scale!`. -- `color` sets the color of the plot. It can be given as a named color `Symbol` or a `Colors.Colorant`. Transparency can be included either directly as an alpha value in the `Colorant` or as an additional float in a tuple `(color, alpha)`. The color can also be set for each point in the line by passing a `Vector` of colors or be used to index the `colormap` by passing a `Real` number or `Vector{<: Real}`. -- `colormap::Union{Symbol, Vector{<:Colorant}} = :viridis` sets the colormap that is sampled for numeric `color`s. -- `colorrange::Tuple{<:Real, <:Real}` sets the values representing the start and end points of `colormap`. -- `nan_color::Union{Symbol, <:Colorant} = RGBAf(0,0,0,0)` sets a replacement color for `color = NaN`. -- `space::Symbol = :data` sets the transformation space for line position. See `Makie.spaces()` for possible inputs. +$(Base.Docs.doc(MakieCore.generic_plot_attributes!)) """ @recipe(Lines, positions) do scene - Attributes(; - default_theme(scene)..., - linewidth = theme(scene, :linewidth), + attr = Attributes(; + color = theme(scene, :linecolor), - colormap = theme(scene, :colormap), - colorrange = automatic, + linewidth = theme(scene, :linewidth), + linestyle = nothing, fxaa = false, cycle = [:color], - inspectable = theme(scene, :inspectable), - space = :data ) + generic_plot_attributes!(attr, ) + return colormap_attributes!(attr, theme(scene, :colormap)) end """ @@ -279,24 +294,15 @@ Plots a line for each pair of points in `(x, y, z)`, `(x, y)`, or `positions`. ### Specific to `LineSegments` +- `color=theme(scene, :linecolor)` sets the color of the linesegments. If no color is set, multiple calls to `linesegments!` will cycle through the axis color palette. + Otherwise, one can set one color per line point or one color per linesegment by passing a `Vector{<:Colorant}`, or one colorant for the whole line. If color is a vector of numbers, the colormap args are used to map the numbers to colors. - `cycle::Vector{Symbol} = [:color]` sets which attributes to cycle when creating multiple plots. - `linestyle::Union{Nothing, Symbol, Vector} = nothing` sets the pattern of the line (e.g. `:solid`, `:dot`, `:dashdot`) - `linewidth::Union{Real, Vector} = 1.5` sets the width of the line in pixel units. -### Generic +$(Base.Docs.doc(colormap_attributes!)) -- `visible::Bool = true` sets whether the plot will be rendered or not. -- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. -- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. -- `fxaa::Bool = false` adjusts whether the plot is rendered with fxaa (anti-aliasing). Note that line plots already use a different form of anti-aliasing. -- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. -- `depth_shift::Float32 = 0f0` adjusts the depth value of a plot after all other transformations, i.e. in clip space, where `0 <= depth <= 1`. This only applies to GLMakie and WGLMakie and can be used to adjust render order (like a tunable overdraw). -- `model::Makie.Mat4f` sets a model matrix for the plot. This replaces adjustments made with `translate!`, `rotate!` and `scale!`. -- `color` sets the color of the plot. It can be given as a named color `Symbol` or a `Colors.Colorant`. Transparency can be included either directly as an alpha value in the `Colorant` or as an additional float in a tuple `(color, alpha)`. The color can also be set for each point in the line by passing a `Vector` or be used to index the `colormap` by passing a `Real` number or `Vector{<: Real}`. -- `colormap::Union{Symbol, Vector{<:Colorant}} = :viridis` sets the colormap that is sampled for numeric `color`s. -- `colorrange::Tuple{<:Real, <:Real}` sets the values representing the start and end points of `colormap`. -- `nan_color::Union{Symbol, <:Colorant} = RGBAf(0,0,0,0)` sets a replacement color for `color = NaN`. -- `space::Symbol = :data` sets the transformation space for line position. See `Makie.spaces()` for possible inputs. +$(Base.Docs.doc(MakieCore.generic_plot_attributes!)) """ @recipe(LineSegments, positions) do scene default_theme(scene, Lines) @@ -313,52 +319,30 @@ Plots a 3D or 2D mesh. Supported `mesh_object`s include `Mesh` types from [Geome ## Attributes -### Generic +### Specific to `Mesh` -- `visible::Bool = true` sets whether the plot will be rendered or not. -- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. -- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. -- `fxaa::Bool = true` adjusts whether the plot is rendered with fxaa (anti-aliasing). -- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. -- `depth_shift::Float32 = 0f0` adjusts the depth value of a plot after all other transformations, i.e. in clip space, where `0 <= depth <= 1`. This only applies to GLMakie and WGLMakie and can be used to adjust render order (like a tunable overdraw). -- `model::Makie.Mat4f` sets a model matrix for the plot. This replaces adjustments made with `translate!`, `rotate!` and `scale!`. -- `color` sets the color of the plot. It can be given as a named color `Symbol` or a `Colors.Colorant`. Transparency can be included either directly as an alpha value in the `Colorant` or as an additional float in a tuple `(color, alpha)`. A `Vector` of any of these can be passed to define the color per vertex. (It may be helpful to check `GeometryBasics.coordinates(my_mesh)` for this.) A `Vector{<: Real}` can also be passed to sample a colormap for each vertex. And finally, if the mesh includes uv coordinates you can pass a `Matrix` of colors to be used as a texture. -- `colormap::Union{Symbol, Vector{<:Colorant}} = :viridis` sets the colormap that is sampled for numeric `color`s. -- `colorrange::Tuple{<:Real, <:Real}` sets the values representing the start and end points of `colormap`. -- `nan_color::Union{Symbol, <:Colorant} = RGBAf(0,0,0,0)` sets a replacement color for `color = NaN`. -- `lowclip::Union{Automatic, Symbol, <:Colorant} = automatic` sets a color for any value below the colorrange. -- `highclip::Union{Automatic, Symbol, <:Colorant} = automatic` sets a color for any value above the colorrange. -- `space::Symbol = :data` sets the transformation space for vertex positions. See `Makie.spaces()` for possible inputs. -- `interpolate::Bool = true` wether color=Matrix gets interpolated or not +- `color=theme(scene, :patchcolor)` sets the color of the mesh. Can be a `Vector{<:Colorant}` for per vertex colors or a single `Colorant`. + A `Matrix{<:Colorant}` can be used to color the mesh with a texture, which requires the mesh to contain texture coordinates. + Vector or Matrices of numbers can be used as well, which will use the colormap arguments to map the numbers to colors. +- `interpolate::Bool = false` sets whether colors should be interpolated. -### Generic 3D +$(Base.Docs.doc(shading_attributes!)) -- `shading = true` enables lighting. -- `diffuse::Vec3f = Vec3f(0.4)` sets how strongly the red, green and blue channel react to diffuse (scattered) light. -- `specular::Vec3f = Vec3f(0.2)` sets how strongly the object reflects light in the red, green and blue channels. -- `shininess::Real = 32.0` sets how sharp the reflection is. -- `ssao::Bool = false` adjusts whether the plot is rendered with ssao (screen space ambient occlusion). Note that this only makes sense in 3D plots and is only applicable with `fxaa = true`. +$(Base.Docs.doc(colormap_attributes!)) + +$(Base.Docs.doc(MakieCore.generic_plot_attributes!)) """ @recipe(Mesh, mesh) do scene - Attributes(; - default_theme(scene)..., + attr = Attributes(; color = :black, - backlight = 0f0, - colormap = theme(scene, :colormap), - colorrange = automatic, - lowclip = automatic, - highclip = automatic, - nan_color = :transparent, interpolate = true, - shading = true, + fxaa = true, - inspectable = theme(scene, :inspectable), cycle = [:color => :patchcolor], - space = :data, - diffuse = 0.4, - specular = 0.2, - shininess = 32f0, ) + shading_attributes!(attr) + generic_plot_attributes!(attr) + return colormap_attributes!(attr, theme(scene, :colormap)) end """ @@ -372,6 +356,8 @@ Plots a marker for each element in `(x, y, z)`, `(x, y)`, or `positions`. ### Specific to `Scatter` +- `color=theme(scene, :markercolor)` sets the color of the marker. If no color is set, multiple calls to `scatter!` will cycle through the axis color palette. + Otherwise, one can set one color per point by passing a `Vector{<:Colorant}`, or one colorant for the whole scatterplot. If color is a vector of numbers, the colormap args are used to map the numbers to colors. - `cycle::Vector{Symbol} = [:color]` sets which attributes to cycle when creating multiple plots. - `marker::Union{Symbol, Char, Matrix{<:Colorant}, BezierPath, Polygon}` sets the scatter marker. - `markersize::Union{<:Real, Vec2f} = 9` sets the size of the marker. @@ -383,32 +369,13 @@ Plots a marker for each element in `(x, y, z)`, `(x, y)`, or `positions`. - `rotations::Union{Real, Billboard, Quaternion} = Billboard(0f0)` sets the rotation of the marker. A `Billboard` rotation is always around the depth axis. - `transform_marker::Bool = false` controls whether the model matrix (without translation) applies to the marker itself, rather than just the positions. (If this is true, `scale!` and `rotate!` will affect the marker.) -### Generic +$(Base.Docs.doc(colormap_attributes!)) -- `visible::Bool = true` sets whether the plot will be rendered or not. -- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. -- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. -- `fxaa::Bool = false` adjusts whether the plot is rendered with fxaa (anti-aliasing). Note that scatter plots already include a different form of anti-aliasing when plotting non-image markers. -- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. -- `depth_shift::Float32 = 0f0` adjusts the depth value of a plot after all other transformations, i.e. in clip space, where `0 <= depth <= 1`. This only applies to GLMakie and WGLMakie and can be used to adjust render order (like a tunable overdraw). -- `model::Makie.Mat4f` sets a model matrix for the plot. This replaces adjustments made with `translate!`, `rotate!` and `scale!`. -- `color` sets the color of the plot. It can be given as a named color `Symbol` or a `Colors.Colorant`. Transparency can be included either directly as an alpha value in the `Colorant` or as an additional float in a tuple `(color, alpha)`. The color can also be set for each scattered marker by passing a `Vector` of colors or be used to index the `colormap` by passing a `Real` number or `Vector{<: Real}`. -- `colormap::Union{Symbol, Vector{<:Colorant}} = :viridis` sets the colormap that is sampled for numeric `color`s. -- `colorrange::Tuple{<:Real, <:Real}` sets the values representing the start and end points of `colormap`. -- `nan_color::Union{Symbol, <:Colorant} = RGBAf(0,0,0,0)` sets a replacement color for `color = NaN`. -- `lowclip::Union{Automatic, Symbol, <:Colorant} = automatic` sets a color for any value below the colorrange. -- `highclip::Union{Automatic, Symbol, <:Colorant} = automatic` sets a color for any value above the colorrange. -- `space::Symbol = :data` sets the transformation space for positions of markers. See `Makie.spaces()` for possible inputs. +$(Base.Docs.doc(MakieCore.generic_plot_attributes!)) """ @recipe(Scatter, positions) do scene - Attributes(; - default_theme(scene)..., + attr = Attributes(; color = theme(scene, :markercolor), - colormap = theme(scene, :colormap), - colorrange = automatic, - lowclip = automatic, - highclip = automatic, - nan_color = :transparent, marker = theme(scene, :marker), markersize = theme(scene, :markersize), @@ -420,15 +387,17 @@ Plots a marker for each element in `(x, y, z)`, `(x, y)`, or `positions`. rotations = Billboard(), marker_offset = automatic, + transform_marker = false, # Applies the plots transformation to marker distancefield = nothing, uv_offset_width = (0.0, 0.0, 0.0, 0.0), - space = :data, markerspace = :pixel, + fxaa = false, cycle = [:color], - inspectable = theme(scene, :inspectable) ) + generic_plot_attributes!(attr) + return colormap_attributes!(attr, theme(scene, :colormap)) end """ @@ -443,59 +412,34 @@ Plots a mesh for each element in `(x, y, z)`, `(x, y)`, or `positions` (similar ### Specific to `MeshScatter` +- `color = theme(scene, :markercolor)` sets the color of the marker. If no color is set, multiple calls to `meshscatter!` will cycle through the axis color palette. + Otherwise, one can set one color per point by passing a `Vector{<:Colorant}`, or one colorant for the whole meshscatterplot. If color is a vector of numbers, the colormap args are used to map the numbers to colors. - `cycle::Vector{Symbol} = [:color]` sets which attributes to cycle when creating multiple plots. - `marker::Union{Symbol, GeometryBasics.GeometryPrimitive, GeometryBasics.Mesh}` sets the scattered mesh. - `markersize::Union{<:Real, Vec3f} = 0.1` sets the scale of the mesh. This can be given as a Vector to apply to each scattered mesh individually. - `rotations::Union{Real, Vec3f, Quaternion} = 0` sets the rotation of the mesh. A numeric rotation is around the z-axis, a `Vec3f` causes the mesh to rotate such that the the z-axis is now that vector, and a quaternion describes a general rotation. This can be given as a Vector to apply to each scattered mesh individually. -### Generic +$(Base.Docs.doc(shading_attributes!)) -- `visible::Bool = true` sets whether the plot will be rendered or not. -- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. -- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. -- `fxaa::Bool = true` adjusts whether the plot is rendered with fxaa (anti-aliasing). -- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. -- `depth_shift::Float32 = 0f0` adjusts the depth value of a plot after all other transformations, i.e. in clip space, where `0 <= depth <= 1`. This only applies to GLMakie and WGLMakie and can be used to adjust render order (like a tunable overdraw). -- `model::Makie.Mat4f` sets a model matrix for the plot. This replaces adjustments made with `translate!`, `rotate!` and `scale!`. -- `color` sets the color of the plot. It can be given as a named color `Symbol` or a `Colors.Colorant`. Transparency can be included either directly as an alpha value in the `Colorant` or as an additional float in a tuple `(color, alpha)`. The color can also be set for each scattered mesh by passing a `Vector` of colors or be used to index the `colormap` by passing a `Real` number or `Vector{<: Real}`. -- `colormap::Union{Symbol, Vector{<:Colorant}} = :viridis` sets the colormap that is sampled for numeric `color`s. -- `colorrange::Tuple{<:Real, <:Real}` sets the values representing the start and end points of `colormap`. -- `nan_color::Union{Symbol, <:Colorant} = RGBAf(0,0,0,0)` sets a replacement color for `color = NaN`. -- `lowclip::Union{Automatic, Symbol, <:Colorant} = automatic` sets a color for any value below the colorrange. -- `highclip::Union{Automatic, Symbol, <:Colorant} = automatic` sets a color for any value above the colorrange. -- `space::Symbol = :data` sets the transformation space for the positions of meshes. See `Makie.spaces()` for possible inputs. +$(Base.Docs.doc(colormap_attributes!)) -### Generic 3D - -- `shading = true` enables lighting. -- `diffuse::Vec3f = Vec3f(0.4)` sets how strongly the red, green and blue channel react to diffuse (scattered) light. -- `specular::Vec3f = Vec3f(0.2)` sets how strongly the object reflects light in the red, green and blue channels. -- `shininess::Real = 32.0` sets how sharp the reflection is. -- `ssao::Bool = false` adjusts whether the plot is rendered with ssao (screen space ambient occlusion). Note that this only makes sense in 3D plots and is only applicable with `fxaa = true`. +$(Base.Docs.doc(MakieCore.generic_plot_attributes!)) """ @recipe(MeshScatter, positions) do scene - Attributes(; - default_theme(scene)..., - color = :black, - colormap = theme(scene, :colormap), - colorrange = automatic, - lowclip = automatic, - highclip = automatic, - nan_color = :transparent, + attr = Attributes(; + color = theme(scene, :markercolor), marker = :Sphere, markersize = 0.1, rotations = 0.0, - backlight = 0f0, space = :data, - shading = true, + fxaa = true, - inspectable = theme(scene, :inspectable), cycle = [:color], - diffuse = 0.4, - specular = 0.2, - shininess = 32f0, ) + shading_attributes!(attr) + generic_plot_attributes!(attr) + return colormap_attributes!(attr, theme(scene, :colormap)) end """ @@ -510,6 +454,7 @@ Plots one or multiple texts passed via the `text` keyword. ### Specific to `Text` +- `color=theme(scene, :textcolor)` sets the color of the text. One can set one color per glyph by passing a `Vector{<:Colorant}`, or one colorant for the whole text. If color is a vector of numbers, the colormap args are used to map the numbers to colors. - `text` specifies one piece of text or a vector of texts to show, where the number has to match the number of positions given. Makie supports `String` which is used for all normal text and `LaTeXString` which layouts mathematical expressions using `MathTeXEngine.jl`. - `align::Tuple{Union{Symbol, Real}, Union{Symbol, Real}} = (:left, :bottom)` sets the alignment of the string w.r.t. `position`. Uses `:left, :center, :right, :top, :bottom, :baseline` or fractions. - `font::Union{String, Vector{String}} = :regular` sets the font for the string or each character. @@ -523,27 +468,17 @@ Plots one or multiple texts passed via the `text` keyword. - `glowcolor::Union{Symbol, <:Colorant} = (:black, 0)` sets the color of the glow effect. - `word_wrap_with::Real = -1` specifies a linewidth limit for text. If a word overflows this limit, a newline is inserted before it. Negative numbers disable word wrapping. -### Generic attributes - -- `visible::Bool = true` sets whether the plot will be rendered or not. -- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. -- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. -- `fxaa::Bool = false` adjusts whether the plot is rendered with fxaa (anti-aliasing). Note that text plots already include a different form of anti-aliasing. -- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. -- `depth_shift::Float32 = 0f0` adjusts the depth value of a plot after all other transformations, i.e. in clip space, where `0 <= depth <= 1`. This only applies to GLMakie and WGLMakie and can be used to adjust render order (like a tunable overdraw). -- `model::Makie.Mat4f` sets a model matrix for the plot. This replaces adjustments made with `translate!`, `rotate!` and `scale!`. -- `color` sets the color of the plot. It can be given as a named color `Symbol` or a `Colors.Colorant`. Transparency can be included either directly as an alpha value in the `Colorant` or as an additional float in a tuple `(color, alpha)`. The color can also be set for each character by passing a `Vector` of colors. -- `space::Symbol = :data` sets the transformation space for text positions. See `Makie.spaces()` for possible inputs. +$(Base.Docs.doc(colormap_attributes!)) +$(Base.Docs.doc(MakieCore.generic_plot_attributes!)) """ @recipe(Text, positions) do scene - Attributes(; - default_theme(scene)..., + attr = Attributes(; color = theme(scene, :textcolor), - colormap = theme(scene, :colormap), - colorrange = automatic, + font = theme(scene, :font), fonts = theme(scene, :fonts), + strokecolor = (:black, 0.0), strokewidth = 0, align = (:left, :bottom), @@ -552,12 +487,13 @@ Plots one or multiple texts passed via the `text` keyword. position = (0.0, 0.0), justification = automatic, lineheight = 1.0, - space = :data, markerspace = :pixel, + offset = (0.0, 0.0), word_wrap_width = -1, - inspectable = theme(scene, :inspectable) ) + generic_plot_attributes!(attr) + return colormap_attributes!(attr, theme(scene, :colormap)) end """ @@ -580,85 +516,77 @@ Plots polygons, which are defined by ## Attributes ### Specific to `Poly` - -- `lowclip::Union{Automatic, Symbol, <:Colorant} = automatic` sets a color for any value below the colorrange. -- `highclip::Union{Automatic, Symbol, <:Colorant} = automatic` sets a color for any value above the colorrange. +- `color=theme(scene, :patchcolor)` sets the color of the poly. Can be a `Vector{<:Colorant}` for per vertex colors or a single `Colorant`. + A `Matrix{<:Colorant}` can be used to color the mesh with a texture, which requires the mesh to contain texture coordinates. + Vector or Matrices of numbers can be used as well, which will use the colormap arguments to map the numbers to colors. + One can also use `Makie.LinePattern`, to cover the poly with a regular stroke pattern. - `strokecolor::Union{Symbol, <:Colorant} = :black` sets the color of the outline around a marker. +- `strokecolormap`::Union{Symbol, Vector{<:Colorant}} = :viridis` sets the colormap that is sampled for numeric `color`s. - `strokewidth::Real = 0` sets the width of the outline around a marker. - `linestyle::Union{Nothing, Symbol, Vector} = nothing` sets the pattern of the line (e.g. `:solid`, `:dot`, `:dashdot`) -### Generic +$(Base.Docs.doc(colormap_attributes!)) -- `visible::Bool = true` sets whether the plot will be rendered or not. -- `overdraw::Bool = false` sets whether the plot will draw over other plots. This specifically means ignoring depth checks in GL backends. -- `transparency::Bool = false` adjusts how the plot deals with transparency. In GLMakie `transparency = true` results in using Order Independent Transparency. -- `fxaa::Bool = true` adjusts whether the plot is rendered with fxaa (anti-aliasing). -- `inspectable::Bool = true` sets whether this plot should be seen by `DataInspector`. -- `color` is set by the plot. -- `colormap::Union{Symbol, Vector{<:Colorant}} = [:black, :white` sets the colormap that is sampled for numeric `color`s. -- `colorrange::Tuple{<:Real, <:Real}` sets the values representing the start and end points of `colormap`. -- `nan_color::Union{Symbol, <:Colorant} = RGBAf(0,0,0,0)` sets a replacement color for `color = NaN`. -- `lowclip::Union{Automatic, Symbol, <:Colorant} = automatic` sets a color for any value below the colorrange. -- `highclip::Union{Automatic, Symbol, <:Colorant} = automatic` sets a color for any value above the colorrange. -- `space::Symbol = :data` sets the transformation space for the position of the image. See `Makie.spaces()` for possible inputs. -- `cycle::Vector{Symbol} = [:color => :patchcolor]` sets which attributes to cycle when creating multiple plots. -- `shading = false` enables lighting. +$(Base.Docs.doc(MakieCore.generic_plot_attributes!)) """ @recipe(Poly) do scene - Attributes(; + attr = Attributes(; color = theme(scene, :patchcolor), - visible = theme(scene, :visible), + strokecolor = theme(scene, :patchstrokecolor), - colormap = theme(scene, :colormap), - colorrange = automatic, - lowclip = automatic, - highclip = automatic, - nan_color = :transparent, + strokecolormap = theme(scene, :colormap), strokewidth = theme(scene, :patchstrokewidth), + linestyle = nothing, + shading = false, fxaa = true, - linestyle = nothing, - overdraw = false, - transparency = false, + cycle = [:color => :patchcolor], - inspectable = theme(scene, :inspectable), - space = :data ) + generic_plot_attributes!(attr) + return colormap_attributes!(attr, theme(scene, :colormap)) end @recipe(Wireframe) do scene - # default_theme(scene, LineSegments) - Attributes(; - default_theme(scene, LineSegments)..., + attr = Attributes(; depth_shift = -1f-5, ) + return merge!(attr, default_theme(scene, LineSegments)) end @recipe(Arrows, points, directions) do scene - attr = merge!( - default_theme(scene), - Attributes( - arrowhead = automatic, - arrowtail = automatic, - color = :black, - linecolor = automatic, - arrowsize = automatic, - linestyle = nothing, - align = :origin, - normalize = false, - lengthscale = 1f0, - colormap = theme(scene, :colormap), - quality = 32, - inspectable = theme(scene, :inspectable), - markerspace = :pixel, - diffuse=0.4, - specular=0.2, - shininess=32.0f0 - ) + attr = Attributes( + color = :black, + + arrowsize = automatic, + arrowhead = automatic, + arrowtail = automatic, + + linecolor = automatic, + linestyle = nothing, + align = :origin, + + normalize = false, + lengthscale = 1f0, + + colorscale = identity, + + quality = 32, + inspectable = theme(scene, :inspectable), + markerspace = :pixel, + + diffuse=0.4, + specular=0.2, + shininess=32.0f0, + ssao = false ) + + generic_plot_attributes!(attr) + colormap_attributes!(attr, theme(scene, :colormap)) + attr[:fxaa] = automatic attr[:linewidth] = automatic # connect arrow + linecolor by default get!(attr, :arrowcolor, attr[:linecolor]) - attr + return attr end diff --git a/MakieCore/src/recipes.jl b/MakieCore/src/recipes.jl index 741ab37a169..e0cc89b5cb9 100644 --- a/MakieCore/src/recipes.jl +++ b/MakieCore/src/recipes.jl @@ -21,6 +21,7 @@ func2type(f::Function) = Combined{f} plotkey(::Type{<: AbstractPlot{Typ}}) where Typ = Symbol(lowercase(func2string(Typ))) plotkey(::T) where T <: AbstractPlot = plotkey(T) plotkey(::Nothing) = :scatter +plotkey(any) = nothing """ default_plot_signatures(funcname, funcname!, PlotType) @@ -55,8 +56,8 @@ end # Since we can use Combined like a scene in some circumstances, we define this alias theme(x::SceneLike, args...) = theme(x.parent, args...) theme(x::AbstractScene) = x.theme -theme(x::AbstractScene, key) = deepcopy(x.theme[key]) -theme(x::AbstractPlot, key) = deepcopy(x.attributes[key]) +theme(x::AbstractScene, key; default=nothing) = deepcopy(get(x.theme, key, default)) +theme(x::AbstractPlot, key; default=nothing) = deepcopy(get(x.attributes, key, default)) Attributes(x::AbstractPlot) = x.attributes diff --git a/MakieRecipes/src/bezier.jl b/MakieRecipes/src/bezier.jl index bf222bd1b02..c3c0afd2324 100644 --- a/MakieRecipes/src/bezier.jl +++ b/MakieRecipes/src/bezier.jl @@ -12,15 +12,16 @@ end conversion_trait(::Type{<: Bezier}) = PointBased() function calculated_attributes!(::Type{<: Bezier}, plot) - color_and_colormap!(plot) pos = plot[1][] # extend one color per linesegment to be one (the same) color per vertex # taken from @edljk in PR #77 - if haskey(plot, :color) && isa(plot[:color][], AbstractVector) && iseven(length(pos)) && (length(pos) ÷ 2) == length(plot[:color][]) - plot[:color] = lift(plot[:color]) do cols + if haskey(plot, :color) && isa(plot.color[], AbstractVector) && iseven(length(pos)) && (length(pos) ÷ 2) == length(plot.color[]) + plot[:color] = lift(plot.color) do cols map(i-> cols[(i + 1) ÷ 2], 1:(length(cols) * 2)) end end + color_and_colormap!(plot) + return end # used in the pipeline too (for poly) diff --git a/NEWS.md b/NEWS.md index 3053736b4c9..5064e00f31c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,14 +2,52 @@ ## master +## v0.19.9 + +- Allow arbitrary reversible scale functions through `ReversibleScale`. +- Deprecated `linestyle=vector_of_gaps` in favor of `linestyle=Linestyle(vector_of_gaps)` [3135](https://github.com/MakieOrg/Makie.jl/pull/3135), [3193](https://github.com/MakieOrg/Makie.jl/pull/3193). +- Fixed some errors around dynamic changes of `ax.xscale` or `ax.yscale` [#3084](https://github.com/MakieOrg/Makie.jl/pull/3084) +- Improved Barplot Label Alignment [#3160](https://github.com/MakieOrg/Makie.jl/issues/3160). +- Fixed regression in determining axis limits [#3179](https://github.com/MakieOrg/Makie.jl/pull/3179) +- Added a theme `theme_latexfonts` that uses the latex font family as default fonts [#3147](https://github.com/MakieOrg/Makie.jl/pull/3147). +- Upgrades `StableHashTraits` from 0.3 to 1.0 + +## v0.19.8 + +- Improved CairoMakie rendering of `lines` with repeating colors in an array [#3141](https://github.com/MakieOrg/Makie.jl/pull/3141). +- Added `strokecolormap` to poly. [#3145](https://github.com/MakieOrg/Makie.jl/pull/3145) +- Added `xreversed`, `yreversed` and `zreversed` attributes to `Axis3` [#3138](https://github.com/MakieOrg/Makie.jl/pull/3138). +- Fixed incorrect placement of contourlabels with transform functions [#3083](https://github.com/MakieOrg/Makie.jl/pull/3083) +- Fixed automatic normal generation for meshes with shading and no normals [#3041](https://github.com/MakieOrg/Makie.jl/pull/3041). +- Added the `triplot` and `voronoiplot` recipes from DelaunayTriangulation.jl [#3102](https://github.com/MakieOrg/Makie.jl/pull/3102), [#3159](https://github.com/MakieOrg/Makie.jl/pull/3159). + +## v0.19.7 + +- Allow arbitrary functions to color `streamplot` lines by passing a `Function` to `color`. This must accept `Point` of the appropriate dimension and return a `Point`, `Vec`, or other arraylike object [#2002](https://github.com/MakieOrg/Makie.jl/pull/2002). +- `arrows` can now take input of the form `x::AbstractVector, y::AbstractVector, [z::AbstractVector,] f::Function`, where `f` must return a `VecTypes` of the appropriate dimension [#2597](https://github.com/MakieOrg/Makie.jl/pull/2597). +- Exported colorbuffer, and added `colorbuffer(axis::Axis; include_decorations=false, colorbuffer_kws...)`, to get an image of an axis with or without decorations [#3078](https://github.com/MakieOrg/Makie.jl/pull/3078). +- Fixed an issue where the `linestyle` of some polys was not applied to the stroke in CairoMakie. [#2604](https://github.com/MakieOrg/Makie.jl/pull/2604) +- Add `colorscale = identity` to any plotting function using a colormap. This works with any scaling function like `log10`, `sqrt` etc. Consequently, `scale` for `hexbin` is replaced with `colorscale` [#2900](https://github.com/MakieOrg/Makie.jl/pull/2900). +- Add `alpha=1.0` argument to all basic plots, which supports independently adding an alpha component to colormaps and colors. Multiple alphas like in `plot(alpha=0.2, color=RGBAf(1, 0, 0, 0.5))`, will get multiplied [#2900](https://github.com/MakieOrg/Makie.jl/pull/2900). +- `hexbin` now supports any per-observation weights which StatsBase respects - `<: StatsBase.AbstractWeights`, `Vector{Real}`, or `nothing` (the default). [#2804](https://github.com/MakieOrg/Makie.jl/pulls/2804) +- Added a new Axis type, `PolarAxis`, which is an axis with a polar projection. Input is in `(r, theta)` coordinates and is transformed to `(x, y)` coordinates using the standard polar-to-cartesian transformation. + Generally, its attributes are very similar to the usual `Axis` attributes, but `x` is replaced by `r` and `y` by `θ`. + It also inherits from the theme of `Axis` in this manner, so should work seamlessly with Makie themes [#2990](https://github.com/MakieOrg/Makie.jl/pull/2990). +- `inherit` now has a new signature `inherit(scene, attrs::NTuple{N, Symbol}, default_value)`, allowing recipe authors to access nested attributes when trying to inherit from the parent Scene. + For example, one could inherit from `scene.Axis.yticks` by `inherit(scene, (:Axis, :yticks), $default_value)` [#2990](https://github.com/MakieOrg/Makie.jl/pull/2990). +- Fixed incorrect rendering of 3D heatmaps [#2959](https://github.com/MakieOrg/Makie.jl/pull/2959) +- Deprecated `flatten_plots` in favor of `collect_atomic_plots`. Using the new `collect_atomic_plots` fixed a bug in CairoMakie where the z-level of plots within recipes was not respected. [#2793](https://github.com/MakieOrg/Makie.jl/pull/2793) +- Fixed incorrect line depth in GLMakie [#2843](https://github.com/MakieOrg/Makie.jl/pull/2843) +- Fixed incorrect line alpha in dense lines in GLMakie [#2843](https://github.com/MakieOrg/Makie.jl/pull/2843) - Fixed DataInspector interaction with transformations [#3002](https://github.com/MakieOrg/Makie.jl/pull/3002) +- Added option `WGLMakie.activate!(resize_to_body=true)`, to make plots resize to the VSCode plotpane. Resizes to the HTML body element, so may work outside VSCode [#3044](https://github.com/MakieOrg/Makie.jl/pull/3044), [#3042](https://github.com/MakieOrg/Makie.jl/pull/3042). +- Fixed DataInspector interaction with transformations [#3002](https://github.com/MakieOrg/Makie.jl/pull/3002). - Fix incomplete stroke with some Bezier markers in CairoMakie and blurry strokes in GLMakie [#2961](https://github.com/MakieOrg/Makie.jl/pull/2961) - Added the ability to use custom triangulations from DelaunayTriangulation.jl [#2896](https://github.com/MakieOrg/Makie.jl/pull/2896). - Adjusted scaling of scatter/text stroke, glow and anti-aliasing width under non-uniform 2D scaling (Vec2f markersize/fontsize) in GLMakie [#2950](https://github.com/MakieOrg/Makie.jl/pull/2950). - Scaled `errorbar` whiskers and `bracket` correctly with transformations [#3012](https://github.com/MakieOrg/Makie.jl/pull/3012). - Updated `bracket` when the screen is resized or transformations change [#3012](https://github.com/MakieOrg/Makie.jl/pull/3012). - ## v0.19.6 - Fixed broken AA for lines with strongly varying linewidth [#2953](https://github.com/MakieOrg/Makie.jl/pull/2953). @@ -37,7 +75,6 @@ - Fixed an issue with GLMakie lines becoming discontinuous [#2828](https://github.com/MakieOrg/Makie.jl/pull/2828). ## v0.19.3 - - Added the `stephist` plotting function [#2408](https://github.com/JuliaPlots/Makie.jl/pull/2408). - Added the `brackets` plotting function [#2356](https://github.com/MakieOrg/Makie.jl/pull/2356). - Fixed an issue where `poly` plots with `Vector{<: MultiPolygon}` inputs with per-polygon color were mistakenly rendered as meshes using CairoMakie [#2590](https://github.com/MakieOrg/Makie.jl/pulls/2478). @@ -160,7 +197,7 @@ role as `datalimits` in `violin` [#2137](https://github.com/MakieOrg/Makie.jl/pu ## v0.17.7 -- Improved `Menu` performance, now it should me much harder to reach the boundary of 255 scenes in GLMakie. `Menu` also takes a `default` keyword argument now and can be scrolled if there is too little space available. +- Improved `Menu` performance, now it should be much harder to reach the boundary of 255 scenes in GLMakie. `Menu` also takes a `default` keyword argument now and can be scrolled if there is too little space available. ## v0.17.6 diff --git a/Project.toml b/Project.toml index 0153527c67d..97bb90c24da 100644 --- a/Project.toml +++ b/Project.toml @@ -1,11 +1,12 @@ name = "Makie" uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" authors = ["Simon Danisch", "Julius Krumbiegel"] -version = "0.19.6" +version = "0.19.9" [deps] Animations = "27a7e980-b3e6-11e9-2bcd-0b925532e340" Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +CRC32c = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" ColorBrewer = "a2cac450-b92f-5266-8821-25eda20663c8" ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4" ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" @@ -15,7 +16,7 @@ DelaunayTriangulation = "927a84f5-c5f4-47a5-9785-b46e178433df" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -FFMPEG = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" +FFMPEG_jll = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" Formatting = "59287772-0a20-5a39-b81b-1366585eb4c0" @@ -46,6 +47,7 @@ REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RelocatableFolders = "05181044-ff0b-4ac5-8273-598c1e38db00" Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46" +ShaderAbstractions = "65257c39-d410-5151-9873-9b3e5be5013e" Showoff = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" SignedDistanceFields = "73760f76-fbc4-59ce-8f25-708e95d2df96" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -64,10 +66,9 @@ ColorSchemes = "3.5" ColorTypes = "0.8, 0.9, 0.10, 0.11" Colors = "0.9, 0.10, 0.11, 0.12" Contour = "0.5, 0.6" -DelaunayTriangulation = "0.6.2, 0.7" +DelaunayTriangulation = "0.8.7" Distributions = "0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25" DocStringExtensions = "0.8, 0.9" -FFMPEG = "0.2, 0.3, 0.4" FileIO = "1.6" FixedPointNumbers = "0.6, 0.7, 0.8" Formatting = "0.4" @@ -81,7 +82,7 @@ Isoband = "0.1" KernelDensity = "0.5, 0.6" LaTeXStrings = "1.2" MacroTools = "0.5" -MakieCore = "=0.6.3" +MakieCore = "=0.6.6" Match = "1.1" MathTeXEngine = "0.5" Observables = "0.5.3" @@ -92,9 +93,10 @@ PolygonOps = "0.1.1" PrecompileTools = "1.0" RelocatableFolders = "0.1, 0.2, 0.3, 1.0" Setfield = "1" +ShaderAbstractions = "0.4" Showoff = "0.3, 1.0.2" SignedDistanceFields = "0.4" -StableHashTraits = "0.3" +StableHashTraits = "1" StatsBase = "0.31, 0.32, 0.33, 0.34" StatsFuns = "0.9, 1.0" StructArrays = "0.3, 0.4, 0.5, 0.6" diff --git a/README.md b/README.md index a002066acc1..49c5a3ccaa1 100644 --- a/README.md +++ b/README.md @@ -3,32 +3,61 @@ Makie.jl logo + src="/assets/makie_logo_canvas.svg" width="350"> -From the japanese word [_Maki-e_](https://en.wikipedia.org/wiki/Maki-e), which is a technique to sprinkle lacquer with gold and silver powder. -Data is the gold and silver of our age, so let's spread it out beautifully on the screen! +
+ +[![][docs-stable-img]][docs-stable-url] +[![][docs-master-img]][docs-master-url] -[Check out the documentation here!](http://docs.makie.org/stable/) +[![Build Status](https://github.com/MakieOrg/Makie.jl/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/MakieOrg/Makie.jl/actions/workflows/ci.yml?query=branch%3Amaster) +[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/MakieOrg/Makie.jl/blob/main/LICENSE) +[![Downloads](https://shields.io/endpoint?url=https://pkgs.genieframework.com/api/v1/badge/Makie&label=Downloads)](https://pkgs.genieframework.com?packages=Makie) -[![][docs-stable-img]][docs-stable-url] [![][docs-master-img]][docs-master-url] [![](https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Follow%20%40MakiePlots)](https://twitter.com/MakiePlots) +[![JOSS][joss-img]][joss-url] +[![Citation Badge](https://api.juleskreuer.eu/citation-badge.php?doi=10.21105/joss.03349)](https://juleskreuer.eu/projekte/citation-badge) + +[![](https://img.shields.io/badge/Twitter-@MakiePlots-1DA1F2?&logo=twitter&logoColor=white)](https://twitter.com/MakiePlots) +[![chat][discord-img]][discord-url] + +
+Makie is an interactive data visualization and plotting ecosystem for the [Julia programming language](https://julialang.org/), available on Windows, Linux and Mac. +The backend packages **GLMakie**, **WGLMakie**, **CairoMakie** and **RPRMakie** add different functionalities: +You can use Makie to interactively explore your data and create simple GUIs +in native windows or web browsers, export high-quality vector graphics or even raytrace with physically accurate lighting. + +The name Makie (we pronounce it Mah-kee) is derived from the japanese word [_Maki-e_](https://en.wikipedia.org/wiki/Maki-e), which is a technique to sprinkle lacquer with gold and silver powder. +Data is the gold and silver of our age, so let's spread it out beautifully on the screen! + +To learn more, we invite you to visit the documentation at [docs.makie.org](http://docs.makie.org/stable/). [gitlab-img]: https://gitlab.com/JuliaGPU/Makie.jl/badges/master/pipeline.svg [gitlab-url]: https://gitlab.com/JuliaGPU/Makie.jl/pipelines -[docs-stable-img]: https://img.shields.io/badge/docs-stable-lightgrey.svg +[docs-stable-img]: https://img.shields.io/badge/Docs-Stable-lightgrey.svg [docs-stable-url]: http://docs.makie.org/stable/ -[docs-master-img]: https://img.shields.io/badge/docs-master-blue.svg +[docs-master-img]: https://img.shields.io/badge/Docs-Dev-blue.svg [docs-master-url]: http://docs.makie.org/dev/ +[joss-url]: https://doi.org/10.21105/joss.03349 +[joss-img]: http://joss.theoj.org/papers/10.21105/joss.03349/status.svg + +[discord-url]: https://discord.com/invite/2FBjYAT3cY +[discord-img]: https://img.shields.io/discord/996787732149981214.svg?logo=discord&colorB=7289DA&label=Discord + +## Citing Makie -# Citing Makie +If you use Makie for a scientific publication, please acknowledge and support our work by citing [our JOSS paper](https://joss.theoj.org/papers/10.21105/joss.03349) the following way: -If you use Makie for a scientific publication, please cite [our JOSS paper](https://joss.theoj.org/papers/10.21105/joss.03349) the following way: +``` +Danisch & Krumbiegel, (2021). Makie.jl: Flexible high-performance data visualization for Julia. +Journal of Open Source Software, 6(65), 3349, https://doi.org/10.21105/joss.03349 +``` -> Danisch & Krumbiegel, (2021). Makie.jl: Flexible high-performance data visualization for Julia. Journal of Open Source Software, 6(65), 3349, https://doi.org/10.21105/joss.03349 -BibTeX entry: +
+ BibTeX entry: ```bib @article{DanischKrumbiegel2021, @@ -44,20 +73,27 @@ BibTeX entry: journal = {Journal of Open Source Software} } ``` +
or [Download the BibTeX file](./assets/DanischKrumbiegel2021.bibtex). -# Installation +## Community Channels + +We are on [Discord](https://discord.com/invite/2FBjYAT3cY) and [Discourse](https://discourse.julialang.org/c/17?tags=Makie)! Community channels are a great way for you to ask questions and get help. Please join us! -Please consider using the backends directly. As explained in the documentation, they re-export all of Makie's functionality. -So, instead of installing Makie, just install e.g. GLMakie directly: +## Installation + +Choose one or more backend packages: **GLMakie** (interactive OpenGL in native OS windows), **WGLMakie** (interactive WebGL in browsers, IDEs, notebooks), **CairoMakie** (static 2D vector graphics and images) and **RPRMakie** (raytracing). +Each backend re-exports all of Makie.jl so you don't have to install or load it explicitly. + +Install: ```julia julia>] pkg> add GLMakie ``` -You may check the installed version with: +Check the installed version: ```julia ]st GLMakie @@ -71,6 +107,9 @@ using GLMakie ## Developing Makie +
+ 🔥 Click for more 🔥 + Makie and its backends all live in the Makie monorepo. This makes it easier to change code across all packages. Therefore, dev'ing Makie almost works as with other Julia packages, just, that one needs to also dev the sub packages: @@ -85,75 +124,88 @@ To run the tests, you also should add: ]dev dev/Makie/ReferenceTests ``` For more info about ReferenceTests, check out its [README](./ReferenceUpdater/README.md) +
-# Quick start +## Examples The following examples are supposed to be self-explanatory. For further information [check out the documentation!](http://docs.makie.org/stable/) ### A simple parabola ```julia -x = 1:10 -fig = lines(x, x.^2; label = "Parabola") -axislegend() -save("./assets/parabola.png", fig, resolution = (600, 400)) +x = 1:0.1:10 +fig = lines(x, x.^2; label = "Parabola", + axis = (; xlabel = "x", ylabel = "y", title ="Title"), + figure = (; resolution = (800,600), fontsize = 22)) +axislegend(; position = :lt) +save("./assets/parabola.png", fig) fig ``` - + ### A more complex plot with unicode characters and LaTeX strings: [Similar to the one on this link]() +
+ Show Code + ```julia x = -2pi:0.1:2pi approx = fill(0.0, length(x)) -set_theme!(palette = (; patchcolor = cgrad(:Egypt, alpha=0.65))) -fig, axis, lineplot = lines(x, sin.(x); label = L"sin(x)", linewidth = 3, color = :black, - axis = (; title = "Polynomial approximation of sin(x)", - xgridstyle = :dash, ygridstyle = :dash, - xticksize = 10, yticksize = 10, xtickalign = 1, ytickalign = 1, - xticks = (-π:π/2:π, ["π", "-π/2", "0", "π/2", "π"]) - )) -translate!(lineplot, 0, 0, 2) # move line to foreground -band!(x, sin.(x), approx .+= x; label = L"n = 0") -band!(x, sin.(x), approx .+= -x .^ 3 / 6; label = L"n = 1") -band!(x, sin.(x), approx .+= x .^ 5 / 120; label = L"n = 2") -band!(x, sin.(x), approx .+= -x .^ 7 / 5040; label = L"n = 3") -limits!(-3.8, 3.8, -1.5, 1.5) -axislegend(; position = :ct, bgcolor = (:white, 0.75), framecolor = :orange) -save("./assets/approxsin.png", fig, resolution = (600, 400)) -fig +cmap = [:gold, :deepskyblue3, :orangered, "#e82051"] +with_theme(palette = (; patchcolor = cgrad(cmap, alpha=0.45))) do + fig, axis, lineplot = lines(x, sin.(x); label = L"sin(x)", linewidth = 3, color = :black, + axis = (; title = "Polynomial approximation of sin(x)", + xgridstyle = :dash, ygridstyle = :dash, + xticksize = 10, yticksize = 10, xtickalign = 1, ytickalign = 1, + xticks = (-π:π/2:π, ["π", "-π/2", "0", "π/2", "π"]) + )) + translate!(lineplot, 0, 0, 2) # move line to foreground + band!(x, sin.(x), approx .+= x; label = L"n = 0") + band!(x, sin.(x), approx .+= -x .^ 3 / 6; label = L"n = 1") + band!(x, sin.(x), approx .+= x .^ 5 / 120; label = L"n = 2") + band!(x, sin.(x), approx .+= -x .^ 7 / 5040; label = L"n = 3") + limits!(-3.8, 3.8, -1.5, 1.5) + axislegend(; position = :ct, bgcolor = (:white, 0.75), framecolor = :orange) + save("./assets/approxsin.png", fig, resolution = (800, 600)) + fig +end ``` +
- + ### Simple layout: Heatmap, contour and 3D surface plot +
+ Show Code + ```julia x = y = -5:0.5:5 z = x .^ 2 .+ y' .^ 2 -set_theme!(colormap = :Hiroshige) -fig = Figure() -ax3d = Axis3(fig[1, 1]; aspect = (1, 1, 1), - perspectiveness = 0.5, azimuth = 2.19, elevation = 0.57) -ax2d = Axis(fig[1, 2]; aspect = 1) -pltobj = surface!(ax3d, x, y, z; transparency = true) -heatmap!(ax2d, x, y, z; colormap = (:Hiroshige, 0.5)) -contour!(ax2d, x, y, z; linewidth = 2, levels = 12, color = :black) -contour3d!(ax3d, x, y, z; linewidth = 4, levels = 12, - transparency = true) -Colorbar(fig[1, 3], pltobj) -colsize!(fig.layout, 1, Aspect(1, 1.0)) -colsize!(fig.layout, 2, Aspect(1, 1.0)) -resize_to_layout!(fig) -save("./assets/simpleLayout.png", fig) -fig +cmap = :plasma +with_theme(colormap = cmap) do + fig = Figure(fontsize = 22) + ax3d = Axis3(fig[1, 1]; aspect = (1, 1, 1), + perspectiveness = 0.5, azimuth = 2.19, elevation = 0.57) + ax2d = Axis(fig[1, 2]; aspect = 1, xlabel = "x", ylabel="y") + pltobj = surface!(ax3d, x, y, z; transparency = true) + heatmap!(ax2d, x, y, z; colormap = (cmap, 0.65)) + contour!(ax2d, x, y, z; linewidth = 2, levels = 12, color = :black) + contour3d!(ax3d, x, y, z; linewidth = 4, levels = 12, + transparency = true) + Colorbar(fig[1, 3], pltobj; label="z", labelrotation=pi) + colsize!(fig.layout, 1, Aspect(1, 1.0)) + colsize!(fig.layout, 2, Aspect(1, 1.0)) + resize_to_layout!(fig) + save("./assets/simpleLayout.png", fig) + fig +end ``` +
- - -⚠️WARNING⚠️. Don't forget to reset to the default Makie settings by doing `set_theme!()`. + Interactive example by [AlexisRenchon](https://github.com/AlexisRenchon): diff --git a/RPRMakie/Project.toml b/RPRMakie/Project.toml index e0aea02aeb2..c651e55ab83 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.5.6" +version = "0.5.9" [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" @@ -17,7 +17,7 @@ julia = "1.3" Colors = "0.9, 0.10, 0.11, 0.12" FileIO = "1.6" GeometryBasics = "0.4.1" -Makie = "=0.19.6" +Makie = "=0.19.9" RadeonProRender = "0.3.0" [extras] diff --git a/RPRMakie/examples/datashader-rpr.jl b/RPRMakie/examples/datashader-rpr.jl new file mode 100644 index 00000000000..dce5f7c845e --- /dev/null +++ b/RPRMakie/examples/datashader-rpr.jl @@ -0,0 +1,52 @@ +using DelimitedFiles, GLMakie +GLMakie.activate!() # hide +# For saving/showing/inlining into documentation we need to disable async calculation. +Makie.set_theme!(DataShader=(; async_latest=false)) +airports = Point2f.(eachrow(readdlm(assetpath("airportlocations.csv")))) +(xmin, ymin), (xmax, ymax) = extrema(xx) + +xx = Rect2f(points) +all(x-> x in xx, points) + + +canvas = Canvas(Rect2f(points)) +aggregate!(canvas, points); + +m = collect(Makie.get_aggregation(canvas)) + +(xmin, ymin), (xmax, ymax) = map(x-> x./widths(canvas.bounds), extrema(canvas.bounds)) + +xw, yw = 1 ./ size(m) +maxi = maximum(mscaled) +GLMakie.activate!() +radiance = 50 +lights = [EnvironmentLight(0.5, load(RPR.assetpath("studio026.exr"))), + PointLight(Vec3f(0, 0, 2), RGBf(radiance, radiance, radiance))] +mscaled = m ./ widths(canvas.bounds)[1] +recmesh = GeometryBasics.normal_mesh(Rect3f(Vec3f(-0.5), Vec3f(1))) +RPRMakie.activate!(plugin=RPR.Northstar, iterations=1, resource=RPR.RPR_CREATION_FLAGS_ENABLE_GPU1) +f, ax, pl = meshscatter( + xmin .. xmax, ymin .. ymax, mscaled; + axis=(; type=LScene, show_axis=false, scenekw=(; lights=lights)), + marker=recmesh, + color=mscaled, + colorrange=Vec2f(0.000001, maxi), + lowclip=(:blue, 0.1), + colormap=[:white, :red], + material=(; type=:Microfacet, color=:gray, roughness=0.2, ior=1.390), + markersize=Vec3f.(xw, yw, vec(mscaled)) +) +ax.scene |> display +display(f; backend=GLMakie) +using RPRMakie, FileIO + +RPRMakie.activate!(plugin=RPR.Tahoe, iterations=1, resource=RPR.RPR_CREATION_FLAGS_ENABLE_GPU1) +RPRMakie.replace_scene_rpr!(ax.scene) + + +l = lights[2] + +l.position[] = Vec3f(xmin + xmax/2, ymin + ymax / 2, widths(canvas.bounds)[1]) +l.radiance[] = RGBf(500, 500, 500) + +pl.colorrange[] = Vec2f(0.000001, maxi) diff --git a/RPRMakie/src/meshes.jl b/RPRMakie/src/meshes.jl index c94f3611a97..2c3c8af71d3 100644 --- a/RPRMakie/src/meshes.jl +++ b/RPRMakie/src/meshes.jl @@ -81,7 +81,6 @@ function to_rpr_object(context, matsys, scene, plot::Makie.MeshScatter) cmap = to_colormap(plot.colormap[]) crange = plot.colorrange[] color_from_num = Makie.interpolated_getindex.((cmap,), color, (crange,)) - object_id = RPR.InputLookupMaterial(matsys) object_id.value = RPR.RPR_MATERIAL_NODE_LOOKUP_OBJECT_ID @@ -89,6 +88,18 @@ function to_rpr_object(context, matsys, scene, plot::Makie.MeshScatter) tex = RPR.Texture(matsys, collect(color_from_num'); uv = uv) + material.color = tex + elseif color isa AbstractMatrix{<:Number} + cmap = to_colormap(plot.colormap[]) + crange = plot.colorrange[] + color_from_num = Makie.interpolated_getindex.((cmap,), color, (crange,)) + object_id = RPR.InputLookupMaterial(matsys) + object_id.value = RPR.RPR_MATERIAL_NODE_LOOKUP_OBJECT_ID + + uv = object_id * Vec3f(0, 1/n_instances, 0) + + tex = RPR.Texture(matsys, color_from_num; uv=uv) + material.color = tex elseif color isa Colorant material.color = color diff --git a/ReferenceTests/src/ReferenceTests.jl b/ReferenceTests/src/ReferenceTests.jl index fe844401fa1..28d62aff621 100644 --- a/ReferenceTests/src/ReferenceTests.jl +++ b/ReferenceTests/src/ReferenceTests.jl @@ -11,7 +11,6 @@ using FileIO using MacroTools using Makie using Makie: Record, Stepper, Axis -using Makie.FFMPEG using Printf using Tar using Downloads diff --git a/ReferenceTests/src/tests/attributes.jl b/ReferenceTests/src/tests/attributes.jl index e81f432d05f..845a1d3e7e0 100644 --- a/ReferenceTests/src/tests/attributes.jl +++ b/ReferenceTests/src/tests/attributes.jl @@ -5,7 +5,7 @@ end @reference_test "isorange, isovalue" begin r = range(-1, stop=1, length=100) matr = [(x.^2 + y.^2 + z.^2) for x = r, y = r, z = r] - volume(matr .* (matr .> 1.4), algorithm=:iso, isorange=0.05, isovalue=1.7) + volume(matr .* (matr .> 1.4), algorithm=:iso, isorange=0.05, isovalue=1.7, colorrange=(0, 1)) end @reference_test "levels" begin diff --git a/ReferenceTests/src/tests/examples2d.jl b/ReferenceTests/src/tests/examples2d.jl index 836977ecbde..9692f7ec4d0 100644 --- a/ReferenceTests/src/tests/examples2d.jl +++ b/ReferenceTests/src/tests/examples2d.jl @@ -47,7 +47,7 @@ end @reference_test "Arrows on hemisphere" begin s = Sphere(Point3f(0), 0.9f0) - fig, ax, meshplot = mesh(s, transparency=true, alpha=0.05) + fig, ax, meshplot = mesh(s) pos = decompose(Point3f, s) dirs = decompose_normals(s) arrows!(ax, pos, dirs, arrowcolor=:red, arrowsize=0.1, linecolor=:red) @@ -197,7 +197,7 @@ end @reference_test "Streamplot animation" begin v(x::Point2{T}, t) where T = Point2{T}(one(T) * x[2] * t, 4 * x[1]) - sf = Observable(Base.Fix2(v, 0e0)) + sf = Observable(Base.Fix2(v, 0.0)) title_str = Observable("t = 0.00") sp = streamplot(sf, -2..2, -2..2; linewidth=2, colormap=:magma, axis=(;title=title_str)) @@ -589,6 +589,43 @@ end fig end +@reference_test "colorscale (heatmap)" begin + x = 10.0.^(1:0.1:4) + y = 1.0:0.1:5.0 + fig, ax, hm = heatmap(x, y, (x, y) -> x; axis = (; xscale = log10), colorscale = log10) + Colorbar(fig[1, 2], hm) + fig +end + +@reference_test "colorscale (lines)" begin + xs = 0:0.01:10 + ys = 2 .* (1 .+ sin.(xs)) + fig = Figure() + lines(fig[1, 1], xs, ys; linewidth=50, color=ys, colorscale=identity) + lines(fig[2, 1], xs, ys; linewidth=50, color=ys, colorscale=sqrt) + fig +end + +@reference_test "colorscale (scatter)" begin + xs = range(0, 10; length = 30) + ys = 0.5 .* sin.(xs) + color = (1:30) .^ 2 + markersize = 100 + fig = Figure() + scatter(fig[1, 1], xs, ys; markersize, color, colorscale = identity) + scatter(fig[2, 1], xs, ys; markersize, color, colorscale = log10) + fig +end + +@reference_test "colorscale (hexbin)" begin + x = RNG.randn(10_000) + y = RNG.randn(10_000) + fig = Figure() + hexbin(fig[1, 1], x, y; bins = 40, colorscale = identity) + hexbin(fig[1, 2], x, y; bins = 40, colorscale = log10) + fig +end + @reference_test "multi rect with poly" begin # use thick strokewidth, so it will make tests fail if something is missing poly([Rect2f(0, 0, 1, 1)], color=:green, strokewidth=100, strokecolor=:black) @@ -692,10 +729,10 @@ end y = [sin.(angles); 2 .* sin.(angles .+ pi/n)] z = (x .- 0.5).^2 + (y .- 0.5).^2 .+ 0.5.* RNG.randn.() - inner = [n:-1:1; n] # clockwise inner + inner = [n:-1:1; n] # clockwise inner outer = [(n+1):(2n); n+1] # counter-clockwise outer boundary_nodes = [[outer], [inner]] - tri = DelaunayTriangulation.triangulate([x'; y'], boundary_nodes = boundary_nodes) + tri = triangulate([x'; y'], boundary_nodes = boundary_nodes) f, ax, _ = tricontourf(tri, z) scatter!(x, y, color = z, strokewidth = 1, strokecolor = :black) f @@ -707,16 +744,16 @@ end [(25.0, 0.0), (25.0, 5.0), (25.0, 10.0), (25.0, 15.0), (25.0, 20.0), (25.0, 25.0)], [(25.0, 25.0), (20.0, 25.0), (15.0, 25.0), (10.0, 25.0), (5.0, 25.0), (0.0, 25.0)], [(0.0, 25.0), (0.0, 20.0), (0.0, 15.0), (0.0, 10.0), (0.0, 5.0), (0.0, 0.0)] - ] + ] curve_2 = [ [(4.0, 6.0), (4.0, 14.0), (4.0, 20.0), (18.0, 20.0), (20.0, 20.0)], [(20.0, 20.0), (20.0, 16.0), (20.0, 12.0), (20.0, 8.0), (20.0, 4.0)], [(20.0, 4.0), (16.0, 4.0), (12.0, 4.0), (8.0, 4.0), (4.0, 4.0), (4.0, 6.0)] - ] + ] curve_3 = [ [(12.906, 10.912), (16.0, 12.0), (16.16, 14.46), (16.29, 17.06), (13.13, 16.86), (8.92, 16.4), (8.8, 10.9), (12.906, 10.912)] - ] + ] curves = [curve_1, curve_2, curve_3] points = [ (3.0, 23.0), (9.0, 24.0), (9.2, 22.0), (14.8, 22.8), (16.0, 22.0), @@ -770,6 +807,16 @@ end fig end +@reference_test "contour labels with transform_func" begin + f = Figure(resolution = (400, 400)) + a = Axis(f[1, 1], xscale = log10) + xs = 10 .^ range(0, 3, length=101) + ys = range(1, 4, length=101) + zs = [sqrt(x*x + y*y) for x in -50:50, y in -50:50] + contour!(a, xs, ys, zs, labels = true, labelsize = 20) + f +end + @reference_test "contour labels 3D" begin fig = Figure() Axis3(fig[1, 1]) @@ -897,26 +944,26 @@ end f = Figure() hexbin(f[1, 1], x, y, bins = 40, axis = (aspect = DataAspect(), title = "scale = identity")) - hexbin(f[1, 2], x, y, bins = 40, scale=log10, + hexbin(f[1, 2], x, y, bins = 40, colorscale=log10, axis = (aspect = DataAspect(), title = "scale = log10")) f end # Scatter needs working highclip/lowclip first -# @reference_test "hexbin colorrange highclip lowclip" begin -# x = RNG.randn(100000) -# y = RNG.randn(100000) - -# hexbin(x, y, -# bins = 40, -# axis = (aspect = DataAspect(),), -# colorrange = (10, 300), -# highclip = :red, -# lowclip = :pink, -# strokewidth = 1, -# strokecolor = :gray30 -# ) -# end +@reference_test "hexbin colorrange highclip lowclip" begin + x = RNG.randn(100000) + y = RNG.randn(100000) + + f, ax, pl = hexbin(x, y, + bins = 40, + axis = (aspect = DataAspect(),), + colorrange = (10, 300), + highclip = :red, + lowclip = :pink, + strokewidth = 1, + strokecolor = :gray30 + ) +end @reference_test "Latex labels after the fact" begin f = Figure(fontsize = 50) @@ -985,6 +1032,34 @@ end f end +@reference_test "Log scale histogram (barplot)" begin + f = Figure() + hist( + f[1, 1], + RNG.randn(10^6); + axis=(; yscale=log2) + ) + hist( + f[1, 2], + RNG.randn(10^6); + axis=(; xscale=log2), + direction = :x + ) + # make a gap in histogram as edge case + hist( + f[2, 1], + filter!(x-> x<0 || x > 1.5, RNG.randn(10^6)); + axis=(; yscale=log10) + ) + hist( + f[2, 2], + filter!(x-> x<0 || x > 1.5, RNG.randn(10^6)); + axis=(; xscale=log10), + direction = :x + ) + f +end + @reference_test "Stephist" begin stephist(RNG.rand(10000)) current_figure() @@ -1019,3 +1094,258 @@ end end f end + +@reference_test "Z-translation within a recipe" begin + # This is testing whether backends respect the + # z-level of plots within recipes in 2d. + # Ideally, the output of this test + # would be a blue line with red scatter markers. + # However, if a backend does not correctly pick up on translations, + # then this will be drawn in the drawing order, and blue + # will completely obscure red. + + # It seems like we can't define recipes in `@reference_test` yet, + # so we'll have to fake a recipe's structure. + + fig = Figure(resolution = (600, 600)) + # Create a recipe plot + ax, plot_top = heatmap(fig[1, 1], randn(10, 10)) + # Plot some recipes at the level below the contour + scatterlineplot_1 = scatterlines!(plot_top, 1:10, 1:10; linewidth = 20, markersize = 20, color = :red) + scatterlineplot_2 = scatterlines!(plot_top, 1:10, 1:10; linewidth = 20, markersize = 30, color = :blue) + # Translate the lowest level plots (scatters) + translate!(scatterlineplot_1.plots[2], 0, 0, 1) + translate!(scatterlineplot_2.plots[2], 0, 0, -1) + # Display + fig +end + +@reference_test "Plotting empty polygons" begin + p = Makie.Polygon(Point2f[]) + q = Makie.Polygon(Point2f[(-1.0, 0.0), (1.0, 0.0), (0.0, 1.0)]) + fig, ax, sc = poly([p, q]) + poly!(Axis(fig[1,2]), p, color = :black) + poly!(Axis(fig[2,1]), [p, q], color = [:red, :blue]) + poly!(Axis(fig[2,2]), [p, q], color = :red) + poly!(Axis(fig[3,1]), Makie.MultiPolygon([p]), color = :green) + poly!(Axis(fig[3,2]), Makie.MultiPolygon([p, q]), color = [:black, :red]) + fig +end + +@reference_test "lines (some with NaNs) with array colors" begin + f = Figure() + ax = Axis(f[1, 1]) + hidedecorations!(ax) + hidespines!(ax) + lines!(ax, 1:10, 1:10, color = fill(RGBAf(1, 0, 0, 0.5), 10), linewidth = 5) + lines!(ax, 1:10, 2:11, color = [fill(RGBAf(1, 0, 0, 0.5), 5); fill(RGBAf(0, 0, 1, 0.5), 5)], linewidth = 5) + lines!(ax, 1:10, [3, 4, NaN, 6, 7, NaN, 9, 10, 11, NaN], color = [fill(RGBAf(1, 0, 0, 0.5), 5); fill(RGBAf(0, 0, 1, 0.5), 5)], linewidth = 5) + lines!(ax, 1:10, 4:13, color = repeat([RGBAf(1, 0, 0, 0.5), RGBAf(0, 0, 1, 0.5)], 5), linewidth = 5) + lines!(ax, 1:10, fill(NaN, 10), color = repeat([RGBAf(1, 0, 0, 0.5), RGBAf(0, 0, 1, 0.5)], 5), linewidth = 5) + lines!(ax, 1:10, [6, 7, 8, NaN, 10, 11, 12, 13, 14, 15], color = [:red, :blue, fill(:red, 8)...], linewidth = 5) + lines!(ax, 1:3, [7, 8, 9], color = [:red, :red, :blue], linewidth = 5) + lines!(ax, 1:3, [8, 9, NaN], color = [:red, :red, :blue], linewidth = 5) + lines!(ax, 1:3, [NaN, 10, 11], color = [:red, :red, :blue], linewidth = 5) + lines!(ax, 1:5, [10, 11, NaN, 13, 14], color = [:red, :red, :blue, :blue, :blue], linewidth = [5, 5, 5, 10, 10]) + lines!(ax, 1:10, 11:20, color = [fill(RGBAf(1, 0, 0, 0.5), 5); fill(RGBAf(0, 0, 1, 0.5), 5)], linewidth = 5, linestyle = :dot) + lines!(ax, 1:10, 12:21, color = fill(RGBAf(1, 0, 0, 0.5), 10), linewidth = 5, linestyle = :dot) + f +end + +@reference_test "contour with single alpha color" begin + x = range(-π, π; length=50) + z = @. sin(x) * cos(x') + fig, ax = contour(x, x, z, color=RGBAf(1,0,0,0.4), linewidth=6) +end + +@reference_test "Triplot with points, ghost edges, and convex hull" begin + pts = RNG.rand(2, 50) + tri = triangulate(pts; rng = RNG.STABLE_RNG) + fig, ax, sc = triplot(tri, + triangle_color = :lightgray, strokewidth = 4, + show_points=true, markersize = 20, markercolor = :orange, + show_ghost_edges=true, ghost_edge_linewidth = 4, + show_convex_hull=true, convex_hull_linewidth = 4 + + ) + fig +end + +@reference_test "Triplot of a constrained triangulation with holes and a custom bounding box" begin + curve_1 = [[ + (0.0, 0.0), (4.0, 0.0), (8.0, 0.0), (12.0, 0.0), (12.0, 4.0), + (12.0, 8.0), (14.0, 10.0), (16.0, 12.0), (16.0, 16.0), + (14.0, 18.0), (12.0, 20.0), (12.0, 24.0), (12.0, 28.0), + (8.0, 28.0), (4.0, 28.0), (0.0, 28.0), (-2.0, 26.0), (0.0, 22.0), + (0.0, 18.0), (0.0, 10.0), (0.0, 8.0), (0.0, 4.0), (-4.0, 4.0), + (-4.0, 0.0), (0.0, 0.0), + ]] + curve_2 = [[ + (4.0, 26.0), (8.0, 26.0), (10.0, 26.0), (10.0, 24.0), + (10.0, 22.0), (10.0, 20.0), (8.0, 20.0), (6.0, 20.0), + (4.0, 20.0), (4.0, 22.0), (4.0, 24.0), (4.0, 26.0) + ]] + curve_3 = [[(4.0, 16.0), (12.0, 16.0), (12.0, 14.0), (4.0, 14.0), (4.0, 16.0)]] + curve_4 = [[(4.0, 8.0), (10.0, 8.0), (8.0, 6.0), (6.0, 6.0), (4.0, 8.0)]] + curves = [curve_1, curve_2, curve_3, curve_4] + points = [ + (2.0, 26.0), (2.0, 24.0), (6.0, 24.0), (6.0, 22.0), (8.0, 24.0), (8.0, 22.0), + (2.0, 22.0), (0.0, 26.0), (10.0, 18.0), (8.0, 18.0), (4.0, 18.0), (2.0, 16.0), + (2.0, 12.0), (6.0, 12.0), (2.0, 8.0), (2.0, 4.0), (4.0, 2.0), + (-2.0, 2.0), (4.0, 6.0), (10.0, 2.0), (10.0, 6.0), (8.0, 10.0), (4.0, 10.0), + (10.0, 12.0), (12.0, 12.0), (14.0, 26.0), (16.0, 24.0), (18.0, 28.0), + (16.0, 20.0), (18.0, 12.0), (16.0, 8.0), (14.0, 4.0), (14.0, -2.0), + (6.0, -2.0), (2.0, -4.0), (-4.0, -2.0), (-2.0, 8.0), (-2.0, 16.0), + (-4.0, 22.0), (-4.0, 26.0), (-2.0, 28.0), (6.0, 15.0), (7.0, 15.0), + (8.0, 15.0), (9.0, 15.0), (10.0, 15.0), (6.2, 7.8), + (5.6, 7.8), (5.6, 7.6), (5.6, 7.4), (6.2, 7.4), (6.0, 7.6), + (7.0, 7.8), (7.0, 7.4)] + boundary_nodes, points = convert_boundary_points_to_indices(curves; existing_points=points) + tri = triangulate(points; boundary_nodes=boundary_nodes, rng = RNG.STABLE_RNG) + refine!(tri, max_area = 1e-3get_total_area(tri), rng = RNG.STABLE_RNG) + fig, ax, sc = triplot(tri, + show_points=true, + show_constrained_edges=true, + constrained_edge_linewidth=2, + strokewidth=0.2, + markersize=15, + point_color=:blue, + show_ghost_edges=true, # not as good because the outer boundary is not convex, but just testing + marker='x', + bounding_box = (-5,20,-5,35)) # also testing the conversion to Float64 for bbox here + fig +end + +@reference_test "Triplot with nonlinear transformation" begin + f = Figure() + ax = PolarAxis(f[1, 1]) + points = Point2f[(r, phi) for r in 1:10 for phi in range(0, 2pi, length=36)[1:35]] + tr = triplot!(ax, points) + f +end + +@reference_test "Triplot after adding points and make sure the representative_point_list is correctly updated" begin + points = [(0.0,0.0),(0.95,0.0),(1.0,1.4),(0.0,1.0)] # not 1 so that we have a unique triangulation + tri = Observable(triangulate(points; delete_ghosts = false)) + fig, ax, sc = triplot(tri, show_points = true, markersize = 14, show_ghost_edges = true, recompute_centers = true) + for p in [(0.3, 0.5), (-1.5, 2.3), (0.2, 0.2), (0.2, 0.5)] + add_point!(tri[], p) + end + convex_hull!(tri[]) + notify(tri) + ax = Axis(fig[1, 2]) + triplot!(ax, tri[], show_points = true, markersize = 14, show_ghost_edges = true, recompute_centers = true) + fig +end + +@reference_test "Triplot Showing ghost edges for a triangulation with disjoint boundaries" begin + θ = LinRange(0, 2π, 20) |> collect + θ[end] = 0 # need to make sure that 2π gives the exact same coordinates as 0 + xy = Vector{Vector{Vector{NTuple{2,Float64}}}}() + cx = 0.0 + for i in 1:2 + ## Make the exterior circle + push!(xy, [[(cx + cos(θ), sin(θ)) for θ in θ]]) + ## Now the interior circle - clockwise + push!(xy, [[(cx + 0.5cos(θ), 0.5sin(θ)) for θ in reverse(θ)]]) + cx += 3.0 + end + boundary_nodes, points = convert_boundary_points_to_indices(xy) + tri = triangulate(points; boundary_nodes=boundary_nodes, check_arguments=false) + fig, ax, sc = triplot(tri, show_ghost_edges=true) + fig +end + +@reference_test "Voronoiplot for a centroidal tessellation with an automatic colormap" begin + points = [(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)] + tri = triangulate(points; boundary_nodes = [1,2,3,4,1], rng = RNG.STABLE_RNG) + refine!(tri; max_area=1e-2, min_angle = 29.871, rng = RNG.STABLE_RNG) + vorn = voronoi(tri) + smooth_vorn = centroidal_smooth(vorn; maxiters = 250, rng = RNG.STABLE_RNG) + cmap = cgrad(:matter) + fig, ax, sc = voronoiplot(smooth_vorn, markersize=10, strokewidth = 4, markercolor = :red) + fig +end + +@reference_test "Voronoiplot for a tessellation with a custom bounding box" begin + pts = 25RNG.randn(2, 50) + tri = triangulate(pts; rng = RNG.STABLE_RNG) + vorn = voronoi(tri, false) + fig, ax, sc = voronoiplot(vorn, + show_generators=true, + colormap=:RdBu, + strokecolor=:white, + strokewidth=4, + markersize=25, + marker = 'x', + markercolor=:green, + unbounded_edge_extension_factor=5.0) + xlims!(ax, -120, 120) + ylims!(ax, -120, 120) + fig +end + +@reference_test "Voronoiplots with clipped tessellation and unbounded polygons" begin + pts = 25RNG.randn(2, 10) + tri = triangulate(pts; rng = RNG.STABLE_RNG) + vorn = voronoi(tri, true) + fig, ax, sc = voronoiplot(vorn, color = (:blue,0.2), markersize = 20, strokewidth = 4) + + # used to be bugged + points = [(0.0, 1.0), (-1.0, 2.0), (-2.0, -1.0)] + tri = triangulate(points) + vorn = voronoi(tri) + voronoiplot(fig[1,2], vorn, show_generators = true, strokewidth = 4, + color = [:red, :blue, :green], markercolor = :white, markersize = 20) + + fig +end + +@reference_test "Voronoiplot with a nonlinear transform" begin + f = Figure() + ax = PolarAxis(f[1, 1]) + points = Point2f[(r, phi) for r in 1:10 for phi in range(0, 2pi, length=36)[1:35]] + polygon_color = [r for r in 1:10 for phi in range(0, 2pi, length=36)[1:35]] + polygon_color_2 = [phi for r in 1:10 for phi in range(0, 2pi, length=36)[1:35]] + tr = voronoiplot!(ax, points, smooth = false, show_generators = false, color = polygon_color) + Makie.rlims!(ax, 12) # to make rect clip visible if circular clip doesn't happen + ax = PolarAxis(f[1, 2]) + tr = voronoiplot!(ax, points, smooth = true, show_generators = false, color = polygon_color_2) + Makie.rlims!(ax, 12) + f +end + +@reference_test "Voronoiplot with some custom bounding boxes may not contain all data sites" begin + points = [(-3.0, 7.0), (1.0, 6.0), (-1.0, 3.0), (-2.0, 4.0), (3.0, -2.0), (5.0, 5.0), (-4.0, -3.0), (3.0, 8.0)] + tri = triangulate(points) + vorn = voronoi(tri) + color = [:red, :blue, :green, :yellow, :cyan, :magenta, :black, :brown] # the polygon colors should not change even if some are not included (because they're outside of the box) + fig = Figure() + ax1 = Axis(fig[1, 1], title = "Default") + voronoiplot!(ax1, vorn, show_generators = true, markersize=14, strokewidth = 4, color = color) + ax2 = Axis(fig[1, 2], title = "Some excluded") + voronoiplot!(ax2, vorn, show_generators = true, markersize=14, strokewidth = 4, color = color, clip = BBox(0.0, 5.0, -15.0, 15.0)) + ax3 = Axis(fig[2, 1], title = "Bigger range") + voronoiplot!(ax3, vorn, show_generators = true, markersize=14, strokewidth = 4, color = color, clip = (-15.0, 15.0, -15.0, 15.0)) + ax4 = Axis(fig[2, 2], title = "Only one polygon") + voronoiplot!(ax4, vorn, show_generators = true, markersize=14, strokewidth = 4, color = color, clip = (10.0, 12.0, 2.0, 5.0)) + for ax in fig.content + xlims!(ax4, -15, 15) + ylims!(ax4, -15, 15) + end + fig +end + +@reference_test "Voronoiplot after adding points" begin + points = Observable([(0.0,0.0), (1.0,0.0), (1.0,1.0), (0.0,1.0)]) + fig, ax, sc = voronoiplot(points, show_generators=true, markersize=36) # make sure any regressions with missing generators are identified, so use 36 + push!(points[], (2.0, 2.0), (0.5, 0.5), (0.25, 0.25), (0.25, 0.75), (0.75, 0.25), (0.75, 0.75)) + notify(points) + ax2 = Axis(fig[1, 2]) + voronoiplot!(ax2, voronoi(triangulate(points[])), show_generators=true, markersize=36) + xlims!(ax,-0.5,2.5) + ylims!(ax,-0.5,2.5) + xlims!(ax2,-0.5,2.5) + ylims!(ax2,-0.5,2.5) # need to make sure all generators are shown, and the bounding box is automatically updated + fig +end diff --git a/ReferenceTests/src/tests/examples3d.jl b/ReferenceTests/src/tests/examples3d.jl index 0e43c64c6a4..e32e9980322 100644 --- a/ReferenceTests/src/tests/examples3d.jl +++ b/ReferenceTests/src/tests/examples3d.jl @@ -241,6 +241,30 @@ end fig end +@reference_test "colorscale (surface)" begin + x = y = range(-1, 1; length = 20) + f(x, y) = exp(-(x^2 + y^2)^2) + fig = Figure() + surface(fig[1, 1], x, y, f; colorscale = identity) + surface(fig[1, 2], x, y, f; colorscale = log10) + fig +end + +@reference_test "colorscale (poly)" begin + X = [0.0 1 1 2; 1 1 2 2; 0 0 1 1] + Y = [1.0 1 1 1; 1 0 1 0; 0 0 0 0] + Z = [1.0 1 1 1; 1 0 1 0; 0 0 0 0] + C = [0.5 1.0 1.0 0.5; 1.0 0.5 0.5 0.1667; 0.333 0.333 0.5 0.5] .^ 3 + + vertices = connect(reshape([X[:] Y[:] Z[:]]', :), Point3f) + indices = connect(1:length(X), TriangleFace) + + fig = Figure() + poly!(Axis3(fig[1, 1]), vertices, indices; color=C[:], colorscale=identity) + poly!(Axis3(fig[1, 2]), vertices, indices; color=C[:], colorscale=log10) + fig +end + @reference_test "FEM mesh 3D" begin cat = loadasset("cat.obj") vertices = decompose(Point3f, cat) @@ -499,7 +523,8 @@ end fig, ax, pl = volume( r, r, r, # coordinates to plot on ρ, # charge density (functions as colorant) - algorithm=:mip # maximum-intensity-projection + algorithm=:mip, # maximum-intensity-projection + colorrange=(0, 1), ) ax.scene[OldAxis].names.textcolor = :gray # let axis labels be seen on dark background fig.scene.backgroundcolor[] = to_color(:black) @@ -583,3 +608,8 @@ end end fig end + +# TODO: get 3D images working in CairoMakie and test them here too +@reference_test "Heatmap 3D" begin + heatmap(-2..2, -1..1, RNG.rand(100, 100); axis = (; type = LScene)) +end diff --git a/ReferenceTests/src/tests/figures_and_makielayout.jl b/ReferenceTests/src/tests/figures_and_makielayout.jl index f433729cef2..1cb7c313c74 100644 --- a/ReferenceTests/src/tests/figures_and_makielayout.jl +++ b/ReferenceTests/src/tests/figures_and_makielayout.jl @@ -136,3 +136,109 @@ end surface!(ax, xs, ys, zs) fig end + +@reference_test "PolarAxis surface" begin + f = Figure() + ax = PolarAxis(f[1, 1]) + zs = [r*cos(phi) for r in range(1, 2, length=100), phi in range(0, 4pi, length=100)] + p = surface!(ax, 0..10, 0..2pi, zs, shading = false, colormap = :coolwarm, colorrange=(-2, 2)) + Colorbar(f[1, 2], p) + f +end + +# may fail in WGLMakie due to missing dashes +@reference_test "PolarAxis scatterlines spine" begin + f = Figure(resolution = (800, 400)) + ax1 = PolarAxis(f[1, 1], title = "No spine", spinevisible = false) + scatterlines!(ax1, range(0, 1, length=100), range(0, 10pi, length=100), color = 1:100) + + ax2 = PolarAxis(f[1, 2], title = "Modified spine") + ax2.spinecolor[] = :red + ax2.spinestyle[] = :dash + ax2.spinewidth[] = 5 + scatterlines!(ax2, range(0, 1, length=100), range(0, 10pi, length=100), color = 1:100) + + f +end + +# may fail in CairoMakie due to different text stroke handling +# and in WGLMakie due to missing stroke +@reference_test "PolarAxis decorations" begin + f = Figure(resolution = (400, 400), backgroundcolor = :black) + ax = PolarAxis( + f[1, 1], + backgroundcolor = :black, + rminorgridvisible = true, rminorgridcolor = :red, + rminorgridwidth = 1.0, rminorgridstyle = :dash, + thetaminorgridvisible = true, thetaminorgridcolor = :blue, + thetaminorgridwidth = 1.0, thetaminorgridstyle = :dash, + rgridwidth = 2, rgridcolor = :red, + thetagridwidth = 2, thetagridcolor = :blue, + rticklabelsize = 18, rticklabelcolor = :red, + rticklabelstrokewidth = 1, rticklabelstrokecolor = :white, + thetaticklabelsize = 18, thetaticklabelcolor = :blue, + thetaticklabelstrokewidth = 1, thetaticklabelstrokecolor = :white, + ) + f +end + +@reference_test "Axis3 axis reversal" begin + f = Figure(resolution = (1000, 1000)) + revstr(dir, rev) = rev ? "$dir rev" : "" + for (i, (x, y, z)) in enumerate(Iterators.product(fill((false, true), 3)...)) + Axis3(f[fldmod1(i, 3)...], title = "$(revstr("x", x)) $(revstr("y", y)) $(revstr("z", z))", xreversed = x, yreversed = y, zreversed = z) + surface!(0:0.5:10, 0:0.5:10, (x, y) -> (sin(x) + 0.5x) * (cos(y) + 0.5y)) + end + f +end + +@reference_test "Colorbar for recipes" begin + fig, ax, pl = barplot(1:3; color=1:3, colormap=Makie.Categorical(:viridis), figure=(;resolution=(800, 800))) + Colorbar(fig[1, 2], pl; size=100) + x = LinRange(-1, 1, 20) + y = LinRange(-1, 1, 20) + z = LinRange(-1, 1, 20) + values = [sin(x[i]) * cos(y[j]) * sin(z[k]) for i in 1:20, j in 1:20, k in 1:20] + + # TO not make this fail in CairoMakie, we dont actually plot the volume + _f, ax, cp = contour(x, y, z, values; levels=10, colormap=:viridis) + Colorbar(fig[2, :], cp; size=300) + + # horizontal colorbars + Colorbar(fig[1, 3][2, 1]; limits=(0, 10), colormap=:viridis, + vertical=false) + Colorbar(fig[1, 3][3, 1]; limits=(0, 5), size=25, + colormap=cgrad(:Spectral, 5; categorical=true), vertical=false) + Colorbar(fig[1, 3][4, 1]; limits=(-1, 1), colormap=:heat, vertical=false, flipaxis=false, + highclip=:cyan, lowclip=:red) + xs = LinRange(0, 20, 50) + ys = LinRange(0, 15, 50) + zs = [cos(x) * sin(y) for x in xs, y in ys] + ax, hm = contourf(fig[2, 3][1, 1], xs, ys, zs; + colormap=:Spectral, levels=[-1, -0.5, -0.25, 0, 0.25, 0.5, 1]) + Colorbar(fig[2, 3][1, 2], hm; ticks=-1:0.25:1) + + ax, hm = contourf(fig[3, :][1, 1], xs, ys, zs; + colormap=:Spectral, colorscale=sqrt, levels=[ 0, 0.25, 0.5, 1]) + Colorbar(fig[3, :][1, 2], hm; width=200) + + fig +end + +@reference_test "datashader" begin + airports = Point2f.(eachrow(readdlm(assetpath("airportlocations.csv")))) + # Dont use the full dataset, since WGLMakie seems to time out if it's too big + fewer = airports[RNG.rand(1:length(airports), 1000)] + fig, ax, ds = datashader(fewer; async=false) + Colorbar(fig[1, 2], ds; width=100) + hidedecorations!(ax) + hidespines!(ax) + + normaldist = RNG.randn(Point2f, 100000) + ds1 = normaldist .+ (Point2f(-1, 0),) + ds2 = normaldist .+ (Point2f(1, 0),) + ax, pl = datashader(fig[2, :], Dict("a" => ds1, "b" => ds2); async=false) + hidedecorations!(ax) + axislegend(ax) + fig +end diff --git a/ReferenceTests/src/tests/primitives.jl b/ReferenceTests/src/tests/primitives.jl index 4327bf8ab7e..dbc411ac2d0 100644 --- a/ReferenceTests/src/tests/primitives.jl +++ b/ReferenceTests/src/tests/primitives.jl @@ -5,7 +5,7 @@ points = Point2f[(1, 1), (1, 2), (2, 3), (2, 1)] linestyles = [ :solid, :dash, :dot, :dashdot, :dashdotdot, - [1, 2, 3], [1, 2, 4, 5] + Linestyle([1, 2, 3]), Linestyle([1, 2, 4, 5]) ] for linewidth in 1:10 for (i, linestyle) in enumerate(linestyles) @@ -150,7 +150,7 @@ end rotations = [ 2pi/3 * (i-1) for i = 1:length(pixel_types) ] s = Scene(resolution = (100+100*length(pixel_types), 400), camera = campixel!) filename = Makie.assetpath("icon_transparent.png") - marker_image = FileIO.load(filename) + marker_image = load(filename) for (i, (rot, pxtype)) in enumerate(zip(rotations, pixel_types)) marker = convert.(pxtype, marker_image) p = Point2f((i-1) * 100 + 100, 200) @@ -248,6 +248,17 @@ end for (i, marker) in enumerate(markers) scatter!(Point2f.(1:5, i), marker = marker, markersize = range(10, 30, length = 5), color = :black) scatter!(Point2f.(1:5, i), markersize = 4, color = :white) + + # # Debug - show bbox outline + # if !(marker isa Char) + # scene = Makie.get_scene(ax) + # bb = Makie.bbox(Makie.DEFAULT_MARKER_MAP[marker]) + # w, h = widths(bb) + # ox, oy = origin(bb) + # xy = map(pv -> Makie.project(pv, Vec2f(widths(pixelarea(scene)[])), Point2f(5, i)), scene.camera.projectionview) + # bb = map(xy -> Rect2f(xy .+ 30 * Vec2f(ox, oy), 30 * Vec2f(w, h)), xy) + # lines!(bb, linewidth = 1, color = :orange, space = :pixel, linestyle = :dash) + # end end f @@ -259,13 +270,13 @@ end # Same as above markers = [ - :rect, :circle, :cross, :x, :utriangle, :rtriangle, :dtriangle, :ltriangle, :pentagon, + :rect, :circle, :cross, :x, :utriangle, :rtriangle, :dtriangle, :ltriangle, :pentagon, :hexagon, :octagon, :star4, :star5, :star6, :star8, :vline, :hline, 'x', 'X' ] for (i, marker) in enumerate(markers) scatter!( - Point2f.(1:5, i), marker = marker, + Point2f.(1:5, i), marker = marker, markersize = range(10, 30, length = 5), color = :orange, strokewidth = 2, strokecolor = :black ) @@ -434,3 +445,15 @@ end scene end + +@reference_test "barplot with TeX-ed labels" begin + fig = Figure(resolution = (800, 800)) + lab1 = L"\int f(x) dx" + lab2 = lab1 + # lab2 = L"\frac{a}{b} - \sqrt{b}" # this will not work until #2667 is fixed + + barplot(fig[1,1], [1, 2], [0.5, 0.2], bar_labels = [lab1, lab2], flip_labels_at = 0.3, direction=:x) + barplot(fig[1,2], [1, 2], [0.5, 0.2], bar_labels = [lab1, lab2], flip_labels_at = 0.3) + + fig +end diff --git a/ReferenceTests/src/tests/short_tests.jl b/ReferenceTests/src/tests/short_tests.jl index 1518f5482d7..c939fc60626 100644 --- a/ReferenceTests/src/tests/short_tests.jl +++ b/ReferenceTests/src/tests/short_tests.jl @@ -54,8 +54,8 @@ end @reference_test "volume translated" begin r = range(-3pi, stop=3pi, length=100) - fig, ax, vplot = Makie.volume(r, r, r, (x, y, z) -> cos(x) + sin(y) + cos(z), algorithm=:iso, isorange=0.1f0, axis = (;show_axis=false)) - v2 = volume!(ax, r, r, r, (x, y, z) -> cos(x) + sin(y) + cos(z), algorithm=:mip, + fig, ax, vplot = Makie.volume(r, r, r, (x, y, z) -> cos(x) + sin(y) + cos(z), colorrange=(0, 1), algorithm=:iso, isorange=0.1f0, axis = (;show_axis=false)) + v2 = volume!(ax, r, r, r, (x, y, z) -> cos(x) + sin(y) + cos(z), algorithm=:mip, colorrange=(0, 1), transformation=(translation=Vec3f(6pi, 0, 0),)) fig end @@ -257,6 +257,31 @@ end f end +@reference_test "colorbuffer for axis" begin + fig = Figure() + ax1 = Axis(fig[1, 1]) + ax2 = Axis(fig[1, 2]) + ax3 = Axis(fig[2, 2]) + ax4 = Axis(fig[2, 1]) + scatter!(ax1, 1:10, 1:10; markersize=50, color=1:10) + scatter!(ax2, 1:10, 1:10; markersize=50, color=:red) + heatmap!(ax3, -8:0.1:8, 8:0.1:8, (x, y) -> sin(x) + cos(y)) + meshscatter!(ax4, 1:10, 1:10; markersize=1, color=:red) + img1 = colorbuffer(ax1; include_decorations=true) + img2 = colorbuffer(ax2; include_decorations=false) + img3 = colorbuffer(ax3; include_decorations=true) + img4 = colorbuffer(ax4; include_decorations=false) + f, ax5, pl = image(rotr90(img1); axis=(; aspect=DataAspect())) + ax6, pl = image(f[1, 2], rotr90(img2); axis=(; aspect=DataAspect())) + ax7, pl = image(f[2, 2], rotr90(img3); axis=(; aspect=DataAspect())) + ax8, pl = image(f[2, 1], rotr90(img4); axis=(; aspect=DataAspect())) + hidedecorations!(ax5) + hidedecorations!(ax6) + hidedecorations!(ax7) + hidedecorations!(ax8) + f +end + # Needs a way to disable autolimits on show # @reference_test "interactions after close" begin diff --git a/ReferenceTests/src/tests/updating.jl b/ReferenceTests/src/tests/updating.jl index 7b5118e17aa..27f21af75cd 100644 --- a/ReferenceTests/src/tests/updating.jl +++ b/ReferenceTests/src/tests/updating.jl @@ -64,7 +64,7 @@ function load_frames(video, dir) mkdir(framedir) Makie.extract_frames(video, framedir) return map(readdir(framedir; join=true)) do path - return convert(Matrix{RGB{N0f8}}, FileIO.load(path)) + return convert(Matrix{RGB{N0f8}}, load(path)) end end @@ -127,3 +127,45 @@ end sleep(1.0) f end + +@reference_test "interactive colorscale - mesh" begin + brain = load(assetpath("brain.stl")) + color = [abs(tri[1][2]) for tri in brain for i in 1:3] + f, ax, m = mesh(brain; color, colorscale=identity) + mesh(f[1, 2], brain; color, colorscale=log10) + st = Stepper(f) + Makie.step!(st) + m.colorscale = log10 + Makie.step!(st) +end + +@reference_test "interactive colorscale - heatmap" begin + data = exp.(abs.(RNG.randn(20, 20))) + f, ax, hm = heatmap(data, colorscale=log10, axis=(; title="log10")) + Colorbar(f[1, 2], hm) + ax2, hm2 = heatmap(f[1, 3], data, colorscale=log10, axis=(; title="log10")) + st = Stepper(f) + Makie.step!(st) + + hm2.colorscale = identity + ax2.title = "identity" + Makie.step!(st) + + hm.colorscale = identity + ax.title = "identity" + Makie.step!(st) +end + +@reference_test "interactive colorscale - hexbin" begin + x = RNG.randn(1_000) + y = RNG.randn(1_000) + f = Figure() + hexbin(f[1, 1], x, y; axis=(aspect=DataAspect(), title="identity")) + ax, hb = hexbin(f[1, 2], x, y; colorscale=log, axis=(aspect=DataAspect(), title="log")) + Colorbar(f[1, end+1], hb) + st = Stepper(f) + Makie.step!(st) + hb.colorscale = identity + ax.title = "identity" + Makie.step!(st) +end diff --git a/WGLMakie/Project.toml b/WGLMakie/Project.toml index 09053cbf7bb..6d8d7d6d6f5 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.8.10" +version = "0.8.13" [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" @@ -26,13 +26,13 @@ FreeTypeAbstraction = "0.10" GeometryBasics = "0.4.1" Hyperscript = "0.0.3, 0.0.4" JSServe = "2.2" -Makie = "=0.19.6" +Makie = "=0.19.9" Observables = "0.5.1" RelocatableFolders = "0.1, 0.2, 0.3, 1.0" -ShaderAbstractions = "0.3" +ShaderAbstractions = "0.4" PrecompileTools = "1.0" StaticArrays = "0.12, 1.0" -PNGFiles = "0.3" +PNGFiles = "0.3, 0.4" julia = "1.3" [extras] diff --git a/WGLMakie/src/Camera.js b/WGLMakie/src/Camera.js index ae0c66109f5..25223aca629 100644 --- a/WGLMakie/src/Camera.js +++ b/WGLMakie/src/Camera.js @@ -55,21 +55,23 @@ export function attach_3d_camera(canvas, makie_camera, cam3d, scene) { camera.up = new THREE.Vector3(...cam3d.upvector); camera.position.set(...cam3d.eyeposition); camera.lookAt(center); - function update() { - camera.updateProjectionMatrix(); - camera.updateWorldMatrix(); const view = camera.matrixWorldInverse; const projection = camera.projectionMatrix; - const [width, height] = makie_camera.resolution.value; + const [width, height] = cam3d.resolution.value; const [x, y, z] = camera.position; + camera.aspect = width / height; + camera.updateProjectionMatrix(); + camera.updateWorldMatrix(); + makie_camera.update_matrices( view.elements, projection.elements, [width, height], [x, y, z] - ); + ); } + cam3d.resolution.on(update); function addMouseHandler(domObject, drag, zoomIn, zoomOut) { let startDragX = null; diff --git a/WGLMakie/src/Serialization.js b/WGLMakie/src/Serialization.js index 041b96bf6be..a9c7f4b995b 100644 --- a/WGLMakie/src/Serialization.js +++ b/WGLMakie/src/Serialization.js @@ -266,13 +266,13 @@ function create_texture(data) { } function re_create_texture(old_texture, buffer, size) { + let tex; if (size.length == 3) { - const tex = new THREE.DataTexture3D(buffer, size[0], size[1], size[2]); + tex = new THREE.DataTexture3D(buffer, size[0], size[1], size[2]); tex.format = old_texture.format; tex.type = old_texture.type; - return tex; } else { - return new THREE.DataTexture( + tex = new THREE.DataTexture( buffer, size[0], size[1] ? size[1] : 1, @@ -280,6 +280,17 @@ function re_create_texture(old_texture, buffer, size) { old_texture.type ); } + tex.minFilter = old_texture.minFilter + tex.magFilter = old_texture.magFilter + tex.anisotropy = old_texture.anisotropy + tex.wrapS = old_texture.wrapS + if (size.length > 1) { + tex.wrapT = old_texture.wrapT + } + if (size.length > 2) { + tex.wrapR = old_texture.wrapR + } + return tex } function BufferAttribute(buffer) { const jsbuff = new THREE.BufferAttribute(buffer.flat, buffer.type_length); diff --git a/WGLMakie/src/display.jl b/WGLMakie/src/display.jl index e95155f02a5..424e284e60d 100644 --- a/WGLMakie/src/display.jl +++ b/WGLMakie/src/display.jl @@ -46,6 +46,7 @@ const WEB_MIMES = ( """ struct ScreenConfig framerate::Float64 # =30.0 + resize_to_body::Bool # false end """ @@ -73,6 +74,9 @@ mutable struct Screen <: Makie.MakieScreen end end +# Resizing the scene is enough for WGLMakie +Base.resize!(::WGLMakie.Screen, w, h) = nothing + function Base.isopen(screen::Screen) three = get_three(screen) return !isnothing(three) && isopen(three.session) @@ -205,7 +209,7 @@ function session2image(session::Session, scene::Scene) to_data = js"""function (){ return $(scene).then(scene => { const {renderer} = scene.screen - WGLMakie.render_scene(scene) + WGL.render_scene(scene) const img = renderer.domElement.toDataURL() return img }) @@ -342,7 +346,7 @@ const DISABLE_JS_FINALZING = Base.RefValue(false) const DELETE_QUEUE = LockfreeQueue{Tuple{Screen,String,Vector{String}}}(delete_plot!) function Base.delete!(screen::Screen, scene::Scene, plot::Combined) - atomics = Makie.flatten_plots(plot) # delete all atomics + atomics = Makie.collect_atomic_plots(plot) # delete all atomics # only queue atomics to actually delete on js if !DISABLE_JS_FINALZING[] push!(DELETE_QUEUE, (screen, js_uuid(scene), js_uuid.(atomics))) diff --git a/WGLMakie/src/events.jl b/WGLMakie/src/events.jl index d1d98e9d510..6db9ccfa831 100644 --- a/WGLMakie/src/events.jl +++ b/WGLMakie/src/events.jl @@ -102,6 +102,9 @@ function connect_scene_events!(scene::Scene, comm::Observable) e.keyboardbutton[] = KeyEvent(code_to_keyboard(keyup), Keyboard.release) end end + @handle msg.resize begin + resize!(scene, tuple(resize...)) + end catch err @warn "Error in window event callback" exception=(err, Base.catch_backtrace()) end diff --git a/WGLMakie/src/imagelike.jl b/WGLMakie/src/imagelike.jl index bf20aafaeba..1fc9426b94e 100644 --- a/WGLMakie/src/imagelike.jl +++ b/WGLMakie/src/imagelike.jl @@ -9,43 +9,85 @@ nothing_or_color(c::Nothing) = RGBAf(0, 0, 0, 1) lift_or(f, x) = f(x) lift_or(f, x::Observable) = lift(f, x) -function draw_mesh(mscene::Scene, mesh, plot; uniforms...) - uniforms = Dict(uniforms) - filter!(kv -> !(kv[2] isa Function), uniforms) +function create_shader(mscene::Scene, plot::Surface) + # TODO OWN OPTIMIZED SHADER ... Or at least optimize this a bit more ... + px, py, pz = plot[1], plot[2], plot[3] + grid(x, y, z, trans, space) = Makie.matrix_grid(p-> apply_transform(trans, p, space), x, y, z) - colormap = if haskey(plot, :colormap) - cmap = lift(el32convert ∘ to_colormap, plot.colormap) - uniforms[:colormap] = Sampler(cmap) - end + positions = Buffer(lift(grid, px, py, pz, transform_func_obs(plot), get(plot, :space, :data))) + rect = lift(z -> Tesselation(Rect2(0f0, 0f0, 1f0, 1f0), size(z)), pz) + faces = Buffer(lift(r -> decompose(GLTriangleFace, r), rect)) + uv = Buffer(lift(decompose_uv, rect)) + normals = Buffer(lift(surface_normals, px, py, pz)) + per_vertex = Dict(:positions => positions, :faces => faces, :uv => uv, :normals => normals) - colorrange = if haskey(plot, :colorrange) - uniforms[:colorrange] = lift(Vec2f, plot.colorrange) - end + uniforms = Dict(:uniform_color => color, :color => false) + return draw_mesh(mscene, per_vertex, plot, uniforms) +end - get!(uniforms, :colormap, false) - get!(uniforms, :colorrange, false) - get!(uniforms, :color, false) - get!(uniforms, :pattern, false) - get!(uniforms, :model, plot.model) - get!(uniforms, :depth_shift, 0f0) - get!(uniforms, :lightposition, Vec3f(1)) - get!(uniforms, :ambient, Vec3f(1)) - get!(uniforms, :interpolate_in_fragment_shader, true) - uniforms[:normalmatrix] = map(mscene.camera.view, plot.model) do v, m - i = Vec(1, 2, 3) - return transpose(inv(v[i, i] * m[i, i])) - end +function create_shader(mscene::Scene, plot::Union{Heatmap, Image}) + minfilter = to_value(get(plot, :interpolate, false)) ? :linear : :nearest + mesh = limits_to_uvmesh(plot) + + uniforms = Dict( + :normals => Vec3f(0), + :shading => false, + :diffuse => Vec3f(0), + :specular => Vec3f(0), + :shininess => 0.0f0, + :backlight => 0.0f0, + ) + + return draw_mesh(mscene, mesh, plot, uniforms) +end - for key in (:diffuse, :specular, :shininess, :backlight) - uniforms[key] = lift_or(x -> convert_attribute(x, Key{key}()), uniforms[key]) +function create_shader(mscene::Scene, plot::Volume) + x, y, z, vol = plot[1], plot[2], plot[3], plot[4] + box = GeometryBasics.mesh(Rect3f(Vec3f(0), Vec3f(1))) + cam = cameracontrols(mscene) + model2 = lift(plot.model, x, y, z) do m, xyz... + mi = minimum.(xyz) + maxi = maximum.(xyz) + w = maxi .- mi + m2 = Mat4f(w[1], 0, 0, 0, 0, w[2], 0, 0, 0, 0, w[3], 0, mi[1], mi[2], mi[3], 1) + return convert(Mat4f, m) * m2 end - # id + picking gets filled in JS, needs to be here to emit the correct shader uniforms - uniforms[:picking] = false - uniforms[:object_id] = UInt32(0) - return Program(WebGL(), lasset("mesh.vert"), lasset("mesh.frag"), mesh, uniforms) + modelinv = lift(inv, model2) + algorithm = lift(x -> Cuint(convert_attribute(x, key"algorithm"())), plot.algorithm) + + diffuse = lift(x -> convert_attribute(x, Key{:diffuse}()), plot.diffuse) + specular = lift(x -> convert_attribute(x, Key{:specular}()), plot.specular) + shininess = lift(x -> convert_attribute(x, Key{:shininess}()), plot.shininess) + + + + uniforms = Dict{Symbol, Any}( + :modelinv => modelinv, + :isovalue => lift(Float32, plot.isovalue), + :isorange => lift(Float32, plot.isorange), + :absorption => lift(Float32, get(plot, :absorption, Observable(1.0f0))), + :algorithm => algorithm, + :diffuse => diffuse, + :specular => specular, + :shininess => shininess, + :model => model2, + :depth_shift => get(plot, :depth_shift, Observable(0.0f0)), + # these get filled in later by serialization, but we need them + # as dummy values here, so that the correct uniforms are emitted + :lightposition => Vec3f(1), + :eyeposition => Vec3f(1), + :ambient => Vec3f(1), + :picking => false, + :object_id => UInt32(0) + ) + + handle_color!(plot, uniforms, nothing, :volumedata; permute_tex=false) + return Program(WebGL(), lasset("volume.vert"), lasset("volume.frag"), box, uniforms) end + + xy_convert(x::AbstractArray{Float32}, n) = copy(x) xy_convert(x::AbstractArray, n) = el32convert(x) xy_convert(x, n) = Float32[LinRange(extrema(x)..., n + 1);] @@ -68,6 +110,7 @@ function fast_faces(nvertices) end return faces end + # TODO, speed up GeometryBasics function fast_uv(nvertices) xrange, yrange = LinRange.((0, 1), (1, 0), nvertices) @@ -76,7 +119,7 @@ end function limits_to_uvmesh(plot) px, py, pz = plot[1], plot[2], plot[3] - px = map((x, z)-> xy_convert(x, size(z, 1)), px, pz; ignore_equal_values=true) + px = map((x, z) -> xy_convert(x, size(z, 1)), px, pz; ignore_equal_values=true) py = map((y, z) -> xy_convert(y, size(z, 2)), py, pz; ignore_equal_values=true) # Special path for ranges of length 2 which # can be displayed as a rectangle @@ -90,125 +133,18 @@ function limits_to_uvmesh(plot) ymin, ymax = extrema(y) return Rect2(xmin, ymin, xmax - xmin, ymax - ymin) end - positions = Buffer(lift(rect-> decompose(Point2f, rect), rect)) + positions = Buffer(lift(rect -> decompose(Point2f, rect), rect)) faces = Buffer(lift(rect -> decompose(GLTriangleFace, rect), rect)) uv = Buffer(lift(decompose_uv, rect)) else - grid(x, y, trans, space) = Makie.matrix_grid(p-> apply_transform(trans, p, space), x, y, zeros(length(x), length(y))) + function grid(x, y, trans, space) + return Makie.matrix_grid(p -> apply_transform(trans, p, space), x, y, zeros(length(x), length(y))) + end resolution = lift((x, y) -> (length(x), length(y)), px, py; ignore_equal_values=true) positions = Buffer(lift(grid, px, py, t, get(plot, :space, :data))) faces = Buffer(lift(fast_faces, resolution)) uv = Buffer(lift(fast_uv, resolution)) end vertices = GeometryBasics.meta(positions; uv=uv) - return GeometryBasics.Mesh(vertices, faces) -end - -function get_color(plot, key::Symbol)::Observable{RGBAf} - if haskey(plot, key) - return lift(to_color, plot[key]) - else - return Observable(RGBAf(0, 0, 0, 0)) - end -end - -function create_shader(mscene::Scene, plot::Surface) - # TODO OWN OPTIMIZED SHADER ... Or at least optimize this a bit more ... - px, py, pz = plot[1], plot[2], plot[3] - grid(x, y, z, trans, space) = Makie.matrix_grid(p-> apply_transform(trans, p, space), x, y, z) - positions = Buffer(lift(grid, px, py, pz, transform_func_obs(plot), get(plot, :space, :data))) - rect = lift(z -> Tesselation(Rect2(0f0, 0f0, 1f0, 1f0), size(z)), pz) - faces = Buffer(lift(r -> decompose(GLTriangleFace, r), rect)) - uv = Buffer(lift(decompose_uv, rect)) - plot_attributes = copy(plot.attributes) - pcolor = if haskey(plot, :color) && plot.color[] isa AbstractArray - if plot.color[] isa AbstractMatrix{<:Colorant} - delete!(plot_attributes, :colormap) - delete!(plot_attributes, :colorrange) - end - plot.color - else - pz - end - minfilter = to_value(get(plot, :interpolate, true)) ? :linear : :nearest - color = Sampler(lift(x -> el32convert(to_color(permutedims(x))), pcolor), minfilter=minfilter) - normals = Buffer(lift(surface_normals, px, py, pz)) - vertices = GeometryBasics.meta(positions; uv=uv, normals=normals) - mesh = GeometryBasics.Mesh(vertices, faces) - return draw_mesh(mscene, mesh, plot_attributes; uniform_color=color, color=false, - shading=plot.shading, diffuse=plot.diffuse, - specular=plot.specular, shininess=plot.shininess, - depth_shift=get(plot, :depth_shift, Observable(0f0)), - backlight=plot.backlight, - highclip=get_color(plot, :highclip), - lowclip=get_color(plot, :lowclip), - nan_color=get_color(plot, :nan_color)) -end - -function create_shader(mscene::Scene, plot::Union{Heatmap, Image}) - image = plot[3] - color = Sampler(map(x -> el32convert(x'), image); - minfilter=to_value(get(plot, :interpolate, false)) ? :linear : :nearest) - mesh = limits_to_uvmesh(plot) - plot_attributes = copy(plot.attributes) - if eltype(color) <: Colorant - delete!(plot_attributes, :colormap) - delete!(plot_attributes, :colorrange) - end - - return draw_mesh(mscene, mesh, plot_attributes; - uniform_color=color, color=false, - normals=Vec3f(0), shading=false, - diffuse=Vec3f(0), specular=Vec3f(0), - shininess=0f0, - colorrange=haskey(plot, :colorrange) ? plot.colorrange : false, - highclip=get_color(plot, :highclip), - lowclip=get_color(plot, :lowclip), - nan_color=get_color(plot, :nan_color), - backlight=0f0, - depth_shift = get(plot, :depth_shift, Observable(0f0))) -end - -function create_shader(mscene::Scene, plot::Volume) - x, y, z, vol = plot[1], plot[2], plot[3], plot[4] - box = GeometryBasics.mesh(Rect3f(Vec3f(0), Vec3f(1))) - cam = cameracontrols(mscene) - model2 = lift(plot.model, x, y, z) do m, xyz... - mi = minimum.(xyz) - maxi = maximum.(xyz) - w = maxi .- mi - m2 = Mat4f(w[1], 0, 0, 0, 0, w[2], 0, 0, 0, 0, w[3], 0, mi[1], mi[2], mi[3], 1) - return convert(Mat4f, m) * m2 - end - - modelinv = lift(inv, model2) - algorithm = lift(x -> Cuint(convert_attribute(x, key"algorithm"())), plot.algorithm) - - diffuse = lift(x -> convert_attribute(x, Key{:diffuse}()), plot.diffuse) - specular = lift(x -> convert_attribute(x, Key{:specular}()), plot.specular) - shininess = lift(x -> convert_attribute(x, Key{:shininess}()), plot.shininess) - - uniforms = Dict{Symbol, Any}( - :volumedata => Sampler(lift(Makie.el32convert, vol)), - :modelinv => modelinv, - :colormap => Sampler(lift(to_colormap, plot.colormap)), - :colorrange => lift(Vec2f, plot.colorrange), - :isovalue => lift(Float32, plot.isovalue), - :isorange => lift(Float32, plot.isorange), - :absorption => lift(Float32, get(plot, :absorption, Observable(1.0f0))), - :algorithm => algorithm, - :diffuse => diffuse, - :specular => specular, - :shininess => shininess, - :model => model2, - :depth_shift => get(plot, :depth_shift, Observable(0.0f0)), - # these get filled in later by serialization, but we need them - # as dummy values here, so that the correct uniforms are emitted - :lightposition => Vec3f(1), - :eyeposition => Vec3f(1), - :ambient => Vec3f(1), - :picking => false, - :object_id => UInt32(0) - ) - return Program(WebGL(), lasset("volume.vert"), lasset("volume.frag"), box, uniforms) + return Dict(:positions => positions, :faces => faces, :uv => uv) end diff --git a/WGLMakie/src/lines.jl b/WGLMakie/src/lines.jl index 12827230958..bfdcfc6f158 100644 --- a/WGLMakie/src/lines.jl +++ b/WGLMakie/src/lines.jl @@ -13,7 +13,7 @@ end function create_shader(scene::Scene, plot::Union{Lines,LineSegments}) # Potentially per instance attributes - positions = lift(plot[1], transform_func_obs(plot), get(plot, :space, :data)) do points, trans, space + positions = lift(plot[1], transform_func_obs(plot), plot.space) do points, trans, space points = apply_transform(trans, topoint(points), space) if plot isa LineSegments return points @@ -23,8 +23,10 @@ function create_shader(scene::Scene, plot::Union{Lines,LineSegments}) end trans end + startr = lift(p -> 1:2:(length(p) - 1), positions) endr = lift(p -> 2:2:length(p), positions) + p_start_end = lift(positions) do positions return (positions[startr[]], positions[endr[]]) end @@ -32,23 +34,29 @@ function create_shader(scene::Scene, plot::Union{Lines,LineSegments}) per_instance = Dict{Symbol,Any}(:segment_start => Buffer(lift(first, p_start_end)), :segment_end => Buffer(lift(last, p_start_end))) uniforms = Dict{Symbol,Any}() - for k in (:linewidth, :color) - attribute = lift(plot[k]) do x - x = convert_attribute(x, Key{k}(), key"lines"()) - if plot isa LineSegments - return x - else - # Repeat every second point to connect the lines! - return isscalar(x) ? x : reinterpret(eltype(x), TupleView{2, 1}(x)) - end + + linewidth = converted_attribute(plot, :linewidth) + cmap = plot.calculated_colors[] + + color = cmap isa Makie.ColorMapping ? cmap.color_scaled : plot.calculated_colors + + for (k, attribute) in [:linewidth => linewidth, :color => color] + attribute = lift(attribute) do x + plot isa LineSegments && return x + # Repeat every second point to connect the lines! + return isscalar(x) ? x : reinterpret(eltype(x), TupleView{2, 1}(x)) end if isscalar(attribute) uniforms[k] = attribute uniforms[Symbol("$(k)_start")] = attribute uniforms[Symbol("$(k)_end")] = attribute else - if attribute[] isa AbstractVector{<:Number} && haskey(plot, :colorrange) - attribute = lift(array2color, attribute, plot.colormap, plot.colorrange) + if attribute[] isa AbstractVector{<:Number} && k == :color + @assert cmap isa Makie.ColorMapping + attribute = lift(Makie.numbers_to_colors, attribute, cmap.colormap, identity, + cmap.colorrange_scaled, cmap.lowclip, + cmap.highclip, + cmap.nan_color) end per_instance[Symbol("$(k)_start")] = Buffer(lift(x -> x[startr[]], attribute)) per_instance[Symbol("$(k)_end")] = Buffer(lift(x -> x[endr[]], attribute)) diff --git a/WGLMakie/src/meshes.jl b/WGLMakie/src/meshes.jl index 832272dbcb6..11922e71647 100644 --- a/WGLMakie/src/meshes.jl +++ b/WGLMakie/src/meshes.jl @@ -11,20 +11,87 @@ facebuffer(x) = faces(x) facebuffer(x::AbstractArray{<:GLTriangleFace}) = x facebuffer(x::Observable) = Buffer(lift(facebuffer, x)) - -function array2color(colors, cmap, crange) - cmap = RGBAf.(Colors.color.(to_colormap(cmap)), 1.0) - return Makie.interpolated_getindex.((cmap,), colors, (crange,)) +function converted_attribute(plot::AbstractPlot, key::Symbol) + return lift(plot[key]) do value + return convert_attribute(value, Key{key}(), Key{plotkey(plot)}()) + end end -function array2color(colors::AbstractArray{<:Colorant}, cmap, crange) - return RGBAf.(colors) +function handle_color!(plot, uniforms, buffers, uniform_color_name = :uniform_color; permute_tex=true) + color = plot.calculated_colors + minfilter = to_value(get(plot, :interpolate, true)) ? :linear : :nearest + + convert_text(x) = permute_tex ? lift(permutedims, x) : x + + if color[] isa Colorant + uniforms[uniform_color_name] = color + elseif color[] isa AbstractVector + buffers[:color] = Buffer(color) + elseif color[] isa Makie.AbstractPattern + uniforms[:pattern] = true + uniforms[uniform_color_name] = Sampler(convert_text(color); minfilter=minfilter) + elseif color[] isa AbstractMatrix + uniforms[uniform_color_name] = Sampler(convert_text(color); minfilter=minfilter) + elseif color[] isa Makie.ColorMapping + if color[].color_scaled[] isa AbstractVector + buffers[:color] = Buffer(color[].color_scaled) + else + color_scaled = convert_text(color[].color_scaled) + uniforms[uniform_color_name] = Sampler(color_scaled; minfilter=minfilter) + end + uniforms[:colormap] = Sampler(color[].colormap) + uniforms[:colorrange] = color[].colorrange_scaled + uniforms[:highclip] = Makie.highclip(color[]) + uniforms[:lowclip] = Makie.lowclip(color[]) + uniforms[:nan_color] = color[].nan_color + end + get!(uniforms, :color, false) + get!(uniforms, uniform_color_name, false) + get!(uniforms, :colormap, false) + get!(uniforms, :colorrange, false) + get!(uniforms, :highclip, RGBAf(0, 0, 0, 0)) + get!(uniforms, :lowclip, RGBAf(0, 0, 0, 0)) + get!(uniforms, :nan_color, RGBAf(0, 0, 0, 0)) + return end -function converted_attribute(plot::AbstractPlot, key::Symbol) - return lift(plot[key]) do value - return convert_attribute(value, Key{key}(), Key{plotkey(plot)}()) +function draw_mesh(mscene::Scene, per_vertex, plot, uniforms; permute_tex=true) + filter!(kv -> !(kv[2] isa Function), uniforms) + handle_color!(plot, uniforms, per_vertex; permute_tex=permute_tex) + + get!(uniforms, :pattern, false) + get!(uniforms, :model, plot.model) + get!(uniforms, :lightposition, Vec3f(1)) + get!(uniforms, :ambient, Vec3f(1)) + + uniforms[:interpolate_in_fragment_shader] = get(plot, :interpolate_in_fragment_shader, true) + + get!(uniforms, :shading, get(plot, :shading, false)) + + uniforms[:normalmatrix] = map(mscene.camera.view, plot.model) do v, m + i = Vec(1, 2, 3) + return transpose(inv(v[i, i] * m[i, i])) end + + + for key in (:diffuse, :specular, :shininess, :backlight, :depth_shift) + if !haskey(uniforms, key) + uniforms[key] = lift_or(x -> convert_attribute(x, Key{key}()), plot[key]) + end + end + if haskey(uniforms, :color) && haskey(per_vertex, :color) + to_value(uniforms[:color]) isa Bool && delete!(uniforms, :color) + to_value(per_vertex[:color]) isa Bool && delete!(per_vertex, :color) + end + + # id + picking gets filled in JS, needs to be here to emit the correct shader uniforms + uniforms[:picking] = false + uniforms[:object_id] = UInt32(0) + pos = pop!(per_vertex, :positions) + faces = pop!(per_vertex, :faces) + mesh = GeometryBasics.Mesh(meta(pos; per_vertex...), faces) + + return Program(WebGL(), lasset("mesh.vert"), lasset("mesh.frag"), mesh, uniforms) end function create_shader(scene::Scene, plot::Makie.Mesh) @@ -47,86 +114,10 @@ function create_shader(scene::Scene, plot::Makie.Mesh) end end - if haskey(data, :attributes) && data[:attributes] isa AbstractVector - attr = get_attribute(mesh_signal, :attributes) - attr_id = get_attribute(mesh_signal, :attribute_id) - color = lift((c, id) -> c[Int.(id) .+ 1], attr_id) - attributes[:color] = Buffer(color) - uniforms[:uniform_color] = false - else - color_signal = converted_attribute(plot, :color) - color = color_signal[] - mesh_color = color_signal[] - uniforms[:uniform_color] = Observable(false) # this is the default - - if color isa Colorant && haskey(data, :color) - color_signal = get_attribute(mesh_signal, :color) - color = color_signal[] - end - - if color isa AbstractArray - if color isa AbstractVector - attributes[:color] = Buffer(color_signal) # per vertex colors - else - uniforms[:uniform_color] = Sampler(color_signal) # Texture - uniforms[:color] = false - if color isa Makie.AbstractPattern - uniforms[:pattern] = true - # add texture coordinates - uv = Buffer(lift(decompose_uv, mesh_signal)) - delete!(uniforms, :uv) - attributes[:uv] = uv - end - end - if eltype(color_signal[]) <: Number - uniforms[:colorrange] = converted_attribute(plot, :colorrange) - uniforms[:colormap] = Sampler(converted_attribute(plot, :colormap)) - end - elseif color isa Colorant && !haskey(attributes, :color) - uniforms[:uniform_color] = color_signal - else - error("Unsupported color type: $(typeof(color))") - end - end - if !haskey(attributes, :color) - get!(uniforms, :color, false) # make sure we have a color attribute, if not in instance attributes - end - - uniforms[:shading] = plot.shading - - for key in (:diffuse, :specular, :shininess, :backlight) - uniforms[key] = lift(x-> convert_attribute(x, Key{key}()), plot[key]) - end - faces = facebuffer(mesh_signal) positions = vertexbuffer(mesh_signal, plot) - instance = GeometryBasics.Mesh(GeometryBasics.meta(positions; attributes...), faces) - - get!(uniforms, :colorrange, true) - get!(uniforms, :colormap, true) - get!(uniforms, :pattern, false) - get!(uniforms, :model, plot.model) - get!(uniforms, :lightposition, Vec3f(1)) - get!(uniforms, :ambient, Vec3f(1)) - - for key in (:nan_color, :highclip, :lowclip) - if haskey(plot, key) - uniforms[key] = converted_attribute(plot, key) - else - uniforms[key] = RGBAf(0, 0, 0, 0) - end - end - - uniforms[:depth_shift] = get(plot, :depth_shift, Observable(0f0)) - - uniforms[:normalmatrix] = map(scene.camera.view, plot.model) do v, m - i = Vec(1, 2, 3) - return transpose(inv(v[i, i] * m[i, i])) - end - - # id + picking gets filled in JS, needs to be here to emit the correct shader uniforms - uniforms[:picking] = false - uniforms[:object_id] = UInt32(0) + attributes[:faces] = faces + attributes[:positions] = positions - return Program(WebGL(), lasset("mesh.vert"), lasset("mesh.frag"), instance, uniforms) + return draw_mesh(scene, attributes, plot, uniforms; permute_tex=false) end diff --git a/WGLMakie/src/particles.jl b/WGLMakie/src/particles.jl index 702a16d3df3..381d8076853 100644 --- a/WGLMakie/src/particles.jl +++ b/WGLMakie/src/particles.jl @@ -1,17 +1,11 @@ -function handle_color!(uniform_dict, instance_dict) - color, udict = if haskey(uniform_dict, :color) - to_value(uniform_dict[:color]), uniform_dict - elseif haskey(instance_dict, :color) - to_value(instance_dict[:color]), instance_dict - else - nothing, uniform_dict +function handle_color_getter!(uniform_dict, per_instance) + if haskey(uniform_dict, :color) && haskey(per_instance, :color) + to_value(uniform_dict[:color]) isa Bool && delete!(uniform_dict, :color) + to_value(per_instance[:color]) isa Bool && delete!(per_instance, :color) end - if color isa Colorant || - color isa AbstractVector{<:Colorant} || - color === nothing - delete!(uniform_dict, :colormap) - elseif color isa AbstractArray{<:Real} + color = haskey(uniform_dict, :color) ? to_value(uniform_dict[:color]) : to_value(per_instance[:color]) + if color isa AbstractArray{<:Real} uniform_dict[:color_getter] = """ vec4 get_color(){ vec2 norm = get_colorrange(); @@ -38,6 +32,7 @@ function handle_color!(uniform_dict, instance_dict) } """ end + return end const IGNORE_KEYS = Set([ @@ -49,12 +44,12 @@ const IGNORE_KEYS = Set([ function create_shader(scene::Scene, plot::MeshScatter) # Potentially per instance attributes - per_instance_keys = (:rotations, :markersize, :color, :intensity) + per_instance_keys = (:rotations, :markersize, :intensity) per_instance = filter(plot.attributes.attributes) do (k, v) return k in per_instance_keys && !(isscalar(v[])) end - space = get(plot, :space, :data) - per_instance[:offset] = apply_transform(transform_func_obs(plot), plot[1], space) + + per_instance[:offset] = apply_transform(transform_func_obs(plot), plot[1], plot.space) for (k, v) in per_instance per_instance[k] = Buffer(lift_convert(k, v, plot)) @@ -65,12 +60,15 @@ function create_shader(scene::Scene, plot::MeshScatter) end uniform_dict = Dict{Symbol,Any}() + color_keys = Set([:color, :colormap, :highclip, :lowclip, :nan_color, :colorrange, :colorscale, :calculated_colors]) for (k, v) in uniforms k in IGNORE_KEYS && continue + k in color_keys && continue uniform_dict[k] = lift_convert(k, v, plot) end - handle_color!(uniform_dict, per_instance) + handle_color!(plot, uniform_dict, per_instance, :color) + handle_color_getter!(uniform_dict, per_instance) instance = convert_attribute(plot.marker[], key"marker"(), key"meshscatter"()) if !hasproperty(instance, :uv) @@ -81,13 +79,6 @@ function create_shader(scene::Scene, plot::MeshScatter) uniform_dict[:backlight] = plot.backlight get!(uniform_dict, :ambient, Vec3f(1)) - for key in (:nan_color, :highclip, :lowclip) - if haskey(plot, key) - uniforms[key] = converted_attribute(plot, key) - else - uniforms[key] = RGBAf(0, 0, 0, 0) - end - end # id + picking gets filled in JS, needs to be here to emit the correct shader uniforms uniform_dict[:picking] = false @@ -124,6 +115,8 @@ function serialize_three(fta::NoDataTextureAtlas) end + + function scatter_shader(scene::Scene, attributes, plot) # Potentially per instance attributes per_instance_keys = (:pos, :rotations, :markersize, :color, :intensity, @@ -162,10 +155,15 @@ function scatter_shader(scene::Scene, attributes, plot) return !haskey(per_instance, k) end + color_keys = Set([:color, :colormap, :highclip, :lowclip, :nan_color, :colorrange, :colorscale, + :calculated_colors]) + for (k, v) in uniforms k in IGNORE_KEYS && continue + k in color_keys && continue uniform_dict[k] = lift_convert(k, v, plot) end + if !isnothing(marker) get!(uniform_dict, :shape_type) do return Makie.marker_to_sdf_shape(marker) @@ -181,7 +179,13 @@ function scatter_shader(scene::Scene, attributes, plot) uniform_dict[:distancefield] = Observable(false) end - handle_color!(uniform_dict, per_instance) + handle_color!(plot, uniform_dict, per_instance, :color) + handle_color_getter!(uniform_dict, per_instance) + + if haskey(uniform_dict, :color) && haskey(per_instance, :color) + to_value(uniform_dict[:color]) isa Bool && delete!(uniform_dict, :color) + to_value(per_instance[:color]) isa Bool && delete!(per_instance, :color) + end instance = uv_mesh(Rect2(-0.5f0, -0.5f0, 1f0, 1f0)) # Don't send obs, since it's overwritten in JS to be updated by the camera @@ -278,11 +282,12 @@ function create_shader(scene::Scene, plot::Makie.Text{<:Tuple{<:Union{<:Makie.Gl end cam = scene.camera + plot_attributes = copy(plot.attributes) + plot_attributes.attributes[:calculated_colors] = uniform_color uniforms = Dict( :model => plot.model, :shape_type => Observable(Cint(3)), - :color => uniform_color, :rotations => uniform_rotation, :pos => positions, :marker_offset => char_offset, @@ -295,5 +300,5 @@ function create_shader(scene::Scene, plot::Makie.Text{<:Tuple{<:Union{<:Makie.Gl :depth_shift => get(plot, :depth_shift, Observable(0f0)) ) - return scatter_shader(scene, uniforms, plot) + return scatter_shader(scene, uniforms, plot_attributes) end diff --git a/WGLMakie/src/picking.jl b/WGLMakie/src/picking.jl index 72df2389730..4c5589ffe20 100644 --- a/WGLMakie/src/picking.jl +++ b/WGLMakie/src/picking.jl @@ -17,7 +17,7 @@ function pick_native(screen::Screen, rect::Rect2i) if isempty(matrix) return empty else - all_children = Makie.flatten_plots(scene) + all_children = Makie.collect_atomic_plots(scene) lookup = Dict(Pair.(js_uuid.(all_children), all_children)) return map(matrix) do (uuid, index) !haskey(lookup, uuid) && return (nothing, 0) @@ -27,7 +27,7 @@ function pick_native(screen::Screen, rect::Rect2i) end function plot_lookup(scene::Scene) - all_plots = Makie.flatten_plots(scene) + all_plots = Makie.collect_atomic_plots(scene) return Dict(Pair.(js_uuid.(all_plots), all_plots)) end @@ -93,7 +93,7 @@ App() do session } \"\"\" - tooltip = WGLMakie.ToolTip(f, on_click_callback; plots=pl) + tooltip = WGL.ToolTip(f, on_click_callback; plots=pl) return DOM.div(f, tooltip) end ``` @@ -107,7 +107,7 @@ struct ToolTip if isnothing(plots) plots = scene.plots end - all_plots = WGLMakie.js_uuid.(filter!(x-> x.inspectable[], Makie.flatten_plots(plots))) + all_plots = js_uuid.(filter!(x-> x.inspectable[], Makie.collect_atomic_plots(plots))) new(scene, callback, all_plots) end end diff --git a/WGLMakie/src/serialization.jl b/WGLMakie/src/serialization.jl index 044d31d8192..f8357625fa1 100644 --- a/WGLMakie/src/serialization.jl +++ b/WGLMakie/src/serialization.jl @@ -2,6 +2,21 @@ using ShaderAbstractions: InstancedProgram, Program using Makie: Key, plotkey using Colors: N0f8 +function lift_convert(key, value, ::Attributes) + convert(value) = wgl_convert(value, Key{key}()) + if value isa Observable + val = lift(convert, value) + else + val = convert(value) + end + if key === :colormap && val[] isa AbstractArray + return ShaderAbstractions.Sampler(val) + else + return val + end +end + + function lift_convert(key, value, plot) convert(value) = wgl_convert(value, Key{key}(), Key{plotkey(plot)}()) if value isa Observable @@ -152,8 +167,10 @@ function ShaderAbstractions.convert_uniform(::ShaderAbstractions.AbstractContext return convert(Quaternion, t) end -function wgl_convert(value, key1, key2) - val = Makie.convert_attribute(value, key1, key2) + + +function wgl_convert(value, key1, key2...) + val = Makie.convert_attribute(value, key1, key2...) return if val isa AbstractArray{<:Float64} return Makie.el32convert(val) else @@ -161,7 +178,7 @@ function wgl_convert(value, key1, key2) end end -function wgl_convert(value::AbstractMatrix, ::key"colormap", key2) +function wgl_convert(value::AbstractMatrix, ::key"colormap", key2...) return ShaderAbstractions.Sampler(value) end @@ -180,7 +197,7 @@ function register_geometry_updates(update_buffer::Observable, named_buffers) if buffer isa Buffer on(ShaderAbstractions.updater(buffer).update) do (f, args) # update to replace the whole buffer! - if f === (setindex!) && args[1] isa AbstractArray && args[2] isa Colon + if f === ShaderAbstractions.update! new_array = args[1] flat = flatten_buffer(new_array) update_buffer[] = [name, serialize_three(flat), length(new_array)] @@ -205,7 +222,7 @@ function uniform_updater(uniforms::Dict) for (name, value) in uniforms if value isa Sampler on(ShaderAbstractions.updater(value).update) do (f, args) - if f == setindex! && args[2] isa Colon + if f === ShaderAbstractions.update! updater[] = [name, [Int32[size(value.data)...], serialize_three(args[1])]] end return @@ -233,7 +250,7 @@ reinterpret_faces(faces::AbstractVector) = collect(reinterpret(UInt32, decompose function reinterpret_faces(faces::Buffer) result = Observable(reinterpret_faces(ShaderAbstractions.data(faces))) on(ShaderAbstractions.updater(faces).update) do (f, args) - if f === (setindex!) && args[1] isa AbstractArray && args[2] isa Colon + if f === ShaderAbstractions.update! result[] = reinterpret_faces(args[1]) end end @@ -264,7 +281,9 @@ function serialize_scene(scene::Scene) cam3d_state = if cam_controls isa Camera3D fields = (:lookat, :upvector, :eyeposition, :fov, :near, :far) - Dict((f => serialize_three(getfield(cam_controls, f)[]) for f in fields)) + dict = Dict((f => serialize_three(getfield(cam_controls, f)[]) for f in fields)) + dict[:resolution] = lift(res -> Int32[res...], scene.camera.resolution) + dict else nothing end diff --git a/WGLMakie/src/three_plot.jl b/WGLMakie/src/three_plot.jl index c7707e8acb7..af035faf3a1 100644 --- a/WGLMakie/src/three_plot.jl +++ b/WGLMakie/src/three_plot.jl @@ -6,7 +6,7 @@ js_uuid(object) = string(objectid(object)) function all_plots_scenes(scene::Scene; scene_uuids=String[], plot_uuids=String[]) push!(scene_uuids, js_uuid(scene)) for plot in scene.plots - append!(plot_uuids, (js_uuid(p) for p in Makie.flatten_plots(plot))) + append!(plot_uuids, (js_uuid(p) for p in Makie.collect_atomic_plots(plot))) end for child in scene.children all_plots_scenes(child, plot_uuids=plot_uuids, scene_uuids=scene_uuids) @@ -15,7 +15,7 @@ function all_plots_scenes(scene::Scene; scene_uuids=String[], plot_uuids=String[ end function JSServe.print_js_code(io::IO, plot::AbstractPlot, context::JSServe.JSSourceContext) - uuids = js_uuid.(Makie.flatten_plots(plot)) + uuids = js_uuid.(Makie.collect_atomic_plots(plot)) # This is a bit more complicated then it has to be, since evaljs / on_document_load # isn't guaranteed to run after plot initialization in an App... So, if we don't find any plots, # we have to check again after inserting new plots @@ -45,18 +45,22 @@ function three_display(session::Session, scene::Scene; screen_config...) window_open = scene.events.window_open width, height = size(scene) canvas_width = lift(x -> [round.(Int, widths(x))...], pixelarea(scene)) - canvas = DOM.um("canvas"; tabindex="0") - wrapper = DOM.div(canvas) + canvas = DOM.m("canvas"; tabindex="0", style="display: block") + wrapper = DOM.div(canvas; style="width: 100%; height: 100%") comm = Observable(Dict{String,Any}()) done_init = Observable(false) # Keep texture atlas in parent session, so we don't need to send it over and over again ta = JSServe.Retain(TEXTURE_ATLAS) evaljs(session, js""" $(WGL).then(WGL => { - // well.... not nice, but can't deal with the `Promise` in all the other functions - window.WGLMakie = WGL - WGL.create_scene($wrapper, $canvas, $canvas_width, $scene_serialized, $comm, $width, $height, $(config.framerate), $(ta)) - $(done_init).notify(true) + try { + WGL.create_scene($wrapper, $canvas, $canvas_width, $scene_serialized, $comm, $width, $height, $(ta), $(config.framerate), $(config.resize_to_body)) + $(done_init).notify(true) + } catch (e) { + JSServe.Connection.send_error("error initializing scene", e) + $(done_init).notify(false) + return + } }) """) on(session, done_init) do val diff --git a/WGLMakie/src/wglmakie.bundled.js b/WGLMakie/src/wglmakie.bundled.js index a5ed1aafc6f..ce41247902c 100644 --- a/WGLMakie/src/wglmakie.bundled.js +++ b/WGLMakie/src/wglmakie.bundled.js @@ -885,8 +885,8 @@ var gt = class { } let y = a * p; if (l = l * v + d * y, c = c * v + f * y, h = h * v + m * y, u = u * v + x * y, v === 1 - a) { - let b1 = 1 / Math.sqrt(l * l + c * c + h * h + u * u); - l *= b1, c *= b1, h *= b1, u *= b1; + let b = 1 / Math.sqrt(l * l + c * c + h * h + u * u); + l *= b, c *= b, h *= b, u *= b; } } e[t] = l, e[t + 1] = c, e[t + 2] = h, e[t + 3] = u; @@ -965,14 +965,14 @@ var gt = class { let f = .5 / Math.sqrt(d + 1); this._w = .25 / f, this._x = (h - l) * f, this._y = (r - c) * f, this._z = (o - i) * f; } else if (n > a && n > u) { - let f1 = 2 * Math.sqrt(1 + n - a - u); - this._w = (h - l) / f1, this._x = .25 * f1, this._y = (i + o) / f1, this._z = (r + c) / f1; + let f = 2 * Math.sqrt(1 + n - a - u); + this._w = (h - l) / f, this._x = .25 * f, this._y = (i + o) / f, this._z = (r + c) / f; } else if (a > u) { - let f2 = 2 * Math.sqrt(1 + a - n - u); - this._w = (r - c) / f2, this._x = (i + o) / f2, this._y = .25 * f2, this._z = (l + h) / f2; + let f = 2 * Math.sqrt(1 + a - n - u); + this._w = (r - c) / f, this._x = (i + o) / f, this._y = .25 * f, this._z = (l + h) / f; } else { - let f3 = 2 * Math.sqrt(1 + u - n - a); - this._w = (o - i) / f3, this._x = (r + c) / f3, this._y = (l + h) / f3, this._z = .25 * f3; + let f = 2 * Math.sqrt(1 + u - n - a); + this._w = (o - i) / f, this._x = (r + c) / f, this._y = (l + h) / f, this._z = .25 * f; } return this._onChangeCallback(), this; } @@ -1730,20 +1730,20 @@ var ef = new Lt, vl = new M, Xr = new M, So = new M, An = class { let d = o * h, f = o * u, m = a * h, x = a * u; t[0] = l * h, t[4] = -l * u, t[8] = c, t[1] = f + m * c, t[5] = d - x * c, t[9] = -a * l, t[2] = x - d * c, t[6] = m + f * c, t[10] = o * l; } else if (e.order === "YXZ") { - let d1 = l * h, f1 = l * u, m1 = c * h, x1 = c * u; - t[0] = d1 + x1 * a, t[4] = m1 * a - f1, t[8] = o * c, t[1] = o * u, t[5] = o * h, t[9] = -a, t[2] = f1 * a - m1, t[6] = x1 + d1 * a, t[10] = o * l; + let d = l * h, f = l * u, m = c * h, x = c * u; + t[0] = d + x * a, t[4] = m * a - f, t[8] = o * c, t[1] = o * u, t[5] = o * h, t[9] = -a, t[2] = f * a - m, t[6] = x + d * a, t[10] = o * l; } else if (e.order === "ZXY") { - let d2 = l * h, f2 = l * u, m2 = c * h, x2 = c * u; - t[0] = d2 - x2 * a, t[4] = -o * u, t[8] = m2 + f2 * a, t[1] = f2 + m2 * a, t[5] = o * h, t[9] = x2 - d2 * a, t[2] = -o * c, t[6] = a, t[10] = o * l; + let d = l * h, f = l * u, m = c * h, x = c * u; + t[0] = d - x * a, t[4] = -o * u, t[8] = m + f * a, t[1] = f + m * a, t[5] = o * h, t[9] = x - d * a, t[2] = -o * c, t[6] = a, t[10] = o * l; } else if (e.order === "ZYX") { - let d3 = o * h, f3 = o * u, m3 = a * h, x3 = a * u; - t[0] = l * h, t[4] = m3 * c - f3, t[8] = d3 * c + x3, t[1] = l * u, t[5] = x3 * c + d3, t[9] = f3 * c - m3, t[2] = -c, t[6] = a * l, t[10] = o * l; + let d = o * h, f = o * u, m = a * h, x = a * u; + t[0] = l * h, t[4] = m * c - f, t[8] = d * c + x, t[1] = l * u, t[5] = x * c + d, t[9] = f * c - m, t[2] = -c, t[6] = a * l, t[10] = o * l; } else if (e.order === "YZX") { - let d4 = o * l, f4 = o * c, m4 = a * l, x4 = a * c; - t[0] = l * h, t[4] = x4 - d4 * u, t[8] = m4 * u + f4, t[1] = u, t[5] = o * h, t[9] = -a * h, t[2] = -c * h, t[6] = f4 * u + m4, t[10] = d4 - x4 * u; + let d = o * l, f = o * c, m = a * l, x = a * c; + t[0] = l * h, t[4] = x - d * u, t[8] = m * u + f, t[1] = u, t[5] = o * h, t[9] = -a * h, t[2] = -c * h, t[6] = f * u + m, t[10] = d - x * u; } else if (e.order === "XZY") { - let d5 = o * l, f5 = o * c, m5 = a * l, x5 = a * c; - t[0] = l * h, t[4] = -u, t[8] = c * h, t[1] = d5 * u + x5, t[5] = o * h, t[9] = f5 * u - m5, t[2] = m5 * u - f5, t[6] = a * h, t[10] = x5 * u + d5; + let d = o * l, f = o * c, m = a * l, x = a * c; + t[0] = l * h, t[4] = -u, t[8] = c * h, t[1] = d * u + x, t[5] = o * h, t[9] = f * u - m, t[2] = m * u - f, t[6] = a * h, t[10] = x * u + d; } return t[3] = 0, t[7] = 0, t[11] = 0, t[12] = 0, t[13] = 0, t[14] = 0, t[15] = 1, this; } @@ -2205,24 +2205,24 @@ var Js = class { } } if (this.isSkinnedMesh && (i.bindMode = this.bindMode, i.bindMatrix = this.bindMatrix.toArray(), this.skeleton !== void 0 && (r(e.skeletons, this.skeleton), i.skeleton = this.skeleton.uuid)), this.material !== void 0) if (Array.isArray(this.material)) { - let a1 = []; - for(let l1 = 0, c1 = this.material.length; l1 < c1; l1++)a1.push(r(e.materials, this.material[l1])); - i.material = a1; + let a = []; + for(let l = 0, c = this.material.length; l < c; l++)a.push(r(e.materials, this.material[l])); + i.material = a; } else i.material = r(e.materials, this.material); if (this.children.length > 0) { i.children = []; - for(let a2 = 0; a2 < this.children.length; a2++)i.children.push(this.children[a2].toJSON(e).object); + for(let a = 0; a < this.children.length; a++)i.children.push(this.children[a].toJSON(e).object); } if (this.animations.length > 0) { i.animations = []; - for(let a3 = 0; a3 < this.animations.length; a3++){ - let l2 = this.animations[a3]; - i.animations.push(r(e.animations, l2)); + for(let a = 0; a < this.animations.length; a++){ + let l = this.animations[a]; + i.animations.push(r(e.animations, l)); } } if (t) { - let a4 = o(e.geometries), l3 = o(e.materials), c2 = o(e.textures), h1 = o(e.images), u1 = o(e.shapes), d = o(e.skeletons), f = o(e.animations); - a4.length > 0 && (n.geometries = a4), l3.length > 0 && (n.materials = l3), c2.length > 0 && (n.textures = c2), h1.length > 0 && (n.images = h1), u1.length > 0 && (n.shapes = u1), d.length > 0 && (n.skeletons = d), f.length > 0 && (n.animations = f); + let a = o(e.geometries), l = o(e.materials), c = o(e.textures), h = o(e.images), u = o(e.shapes), d = o(e.skeletons), f = o(e.animations); + a.length > 0 && (n.geometries = a), l.length > 0 && (n.materials = l), c.length > 0 && (n.textures = c), h.length > 0 && (n.images = h), u.length > 0 && (n.shapes = u), d.length > 0 && (n.skeletons = d), f.length > 0 && (n.animations = f); } return n.object = i, n; function o(a) { @@ -2640,9 +2640,9 @@ var ae = class { break; } } else if (n = /^\#([A-Fa-f\d]+)$/.exec(e)) { - let i1 = n[1], r1 = i1.length; - if (r1 === 3) return this.r = parseInt(i1.charAt(0) + i1.charAt(0), 16) / 255, this.g = parseInt(i1.charAt(1) + i1.charAt(1), 16) / 255, this.b = parseInt(i1.charAt(2) + i1.charAt(2), 16) / 255, this; - if (r1 === 6) return this.r = parseInt(i1.charAt(0) + i1.charAt(1), 16) / 255, this.g = parseInt(i1.charAt(2) + i1.charAt(3), 16) / 255, this.b = parseInt(i1.charAt(4) + i1.charAt(5), 16) / 255, this; + let i = n[1], r = i.length; + if (r === 3) return this.r = parseInt(i.charAt(0) + i.charAt(0), 16) / 255, this.g = parseInt(i.charAt(1) + i.charAt(1), 16) / 255, this.b = parseInt(i.charAt(2) + i.charAt(2), 16) / 255, this; + if (r === 6) return this.r = parseInt(i.charAt(0) + i.charAt(1), 16) / 255, this.g = parseInt(i.charAt(2) + i.charAt(3), 16) / 255, this.b = parseInt(i.charAt(4) + i.charAt(5), 16) / 255, this; } return e && e.length > 0 ? this.setColorName(e) : this; } @@ -2820,7 +2820,7 @@ var Je = new M, Qr = new X, Ue = class { } applyMatrix3(e) { if (this.itemSize === 2) for(let t = 0, n = this.count; t < n; t++)Qr.fromBufferAttribute(this, t), Qr.applyMatrix3(e), this.setXY(t, Qr.x, Qr.y); - else if (this.itemSize === 3) for(let t1 = 0, n1 = this.count; t1 < n1; t1++)Je.fromBufferAttribute(this, t1), Je.applyMatrix3(e), this.setXYZ(t1, Je.x, Je.y, Je.z); + else if (this.itemSize === 3) for(let t = 0, n = this.count; t < n; t++)Je.fromBufferAttribute(this, t), Je.applyMatrix3(e), this.setXYZ(t, Je.x, Je.y, Je.z); return this; } applyMatrix4(e) { @@ -3044,10 +3044,10 @@ var de = class extends Ue { } Tt.getCenter(n); let i = 0; - for(let r1 = 0, o1 = e.count; r1 < o1; r1++)ht.fromBufferAttribute(e, r1), i = Math.max(i, n.distanceToSquared(ht)); - if (t) for(let r2 = 0, o2 = t.length; r2 < o2; r2++){ - let a1 = t[r2], l = this.morphTargetsRelative; - for(let c = 0, h = a1.count; c < h; c++)ht.fromBufferAttribute(a1, c), l && (ci.fromBufferAttribute(e, c), ht.add(ci)), i = Math.max(i, n.distanceToSquared(ht)); + for(let r = 0, o = e.count; r < o; r++)ht.fromBufferAttribute(e, r), i = Math.max(i, n.distanceToSquared(ht)); + if (t) for(let r = 0, o = t.length; r < o; r++){ + let a = t[r], l = this.morphTargetsRelative; + for(let c = 0, h = a.count; c < h; c++)ht.fromBufferAttribute(a, c), l && (ci.fromBufferAttribute(e, c), ht.add(ci)), i = Math.max(i, n.distanceToSquared(ht)); } this.boundingSphere.radius = Math.sqrt(i), isNaN(this.boundingSphere.radius) && console.error('THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this); } @@ -3075,8 +3075,8 @@ var de = class extends Ue { count: n.length } ]); - for(let B1 = 0, P = y.length; B1 < P; ++B1){ - let w = y[B1], E = w.start, D = w.count; + for(let B = 0, P = y.length; B < P; ++B){ + let w = y[B], E = w.start, D = w.count; for(let U = E, F = E + D; U < F; U += 3)_(n[U + 0], n[U + 1], n[U + 2]); } let b = new M, A = new M, L = new M, I = new M; @@ -3087,9 +3087,9 @@ var de = class extends Ue { let E = A.dot(h[B]) < 0 ? -1 : 1; l[B * 4] = b.x, l[B * 4 + 1] = b.y, l[B * 4 + 2] = b.z, l[B * 4 + 3] = E; } - for(let B2 = 0, P1 = y.length; B2 < P1; ++B2){ - let w1 = y[B2], E1 = w1.start, D1 = w1.count; - for(let U1 = E1, F1 = E1 + D1; U1 < F1; U1 += 3)k(n[U1 + 0]), k(n[U1 + 1]), k(n[U1 + 2]); + for(let B = 0, P = y.length; B < P; ++B){ + let w = y[B], E = w.start, D = w.count; + for(let U = E, F = E + D; U < F; U += 3)k(n[U + 0]), k(n[U + 1]), k(n[U + 2]); } } computeVertexNormals() { @@ -3099,11 +3099,11 @@ var de = class extends Ue { if (n === void 0) n = new Ue(new Float32Array(t.count * 3), 3), this.setAttribute("normal", n); else for(let d = 0, f = n.count; d < f; d++)n.setXYZ(d, 0, 0, 0); let i = new M, r = new M, o = new M, a = new M, l = new M, c = new M, h = new M, u = new M; - if (e) for(let d1 = 0, f1 = e.count; d1 < f1; d1 += 3){ - let m = e.getX(d1 + 0), x = e.getX(d1 + 1), v = e.getX(d1 + 2); + if (e) for(let d = 0, f = e.count; d < f; d += 3){ + let m = e.getX(d + 0), x = e.getX(d + 1), v = e.getX(d + 2); i.fromBufferAttribute(t, m), r.fromBufferAttribute(t, x), o.fromBufferAttribute(t, v), h.subVectors(o, r), u.subVectors(i, r), h.cross(u), a.fromBufferAttribute(n, m), l.fromBufferAttribute(n, x), c.fromBufferAttribute(n, v), a.add(h), l.add(h), c.add(h), n.setXYZ(m, a.x, a.y, a.z), n.setXYZ(x, l.x, l.y, l.z), n.setXYZ(v, c.x, c.y, c.z); } - else for(let d2 = 0, f2 = t.count; d2 < f2; d2 += 3)i.fromBufferAttribute(t, d2 + 0), r.fromBufferAttribute(t, d2 + 1), o.fromBufferAttribute(t, d2 + 2), h.subVectors(o, r), u.subVectors(i, r), h.cross(u), n.setXYZ(d2 + 0, h.x, h.y, h.z), n.setXYZ(d2 + 1, h.x, h.y, h.z), n.setXYZ(d2 + 2, h.x, h.y, h.z); + else for(let d = 0, f = t.count; d < f; d += 3)i.fromBufferAttribute(t, d + 0), r.fromBufferAttribute(t, d + 1), o.fromBufferAttribute(t, d + 2), h.subVectors(o, r), u.subVectors(i, r), h.cross(u), n.setXYZ(d + 0, h.x, h.y, h.z), n.setXYZ(d + 1, h.x, h.y, h.z), n.setXYZ(d + 2, h.x, h.y, h.z); this.normalizeNormals(), n.needsUpdate = !0; } } @@ -3141,19 +3141,19 @@ var de = class extends Ue { t.setAttribute(a, c); } let r = this.morphAttributes; - for(let a1 in r){ - let l1 = [], c1 = r[a1]; - for(let h = 0, u = c1.length; h < u; h++){ - let d = c1[h], f = e(d, n); - l1.push(f); + for(let a in r){ + let l = [], c = r[a]; + for(let h = 0, u = c.length; h < u; h++){ + let d = c[h], f = e(d, n); + l.push(f); } - t.morphAttributes[a1] = l1; + t.morphAttributes[a] = l; } t.morphTargetsRelative = this.morphTargetsRelative; let o = this.groups; - for(let a2 = 0, l2 = o.length; a2 < l2; a2++){ - let c2 = o[a2]; - t.addGroup(c2.start, c2.count, c2.materialIndex); + for(let a = 0, l = o.length; a < l; a++){ + let c = o[a]; + t.addGroup(c.start, c.count, c.materialIndex); } return t; } @@ -3179,18 +3179,18 @@ var de = class extends Ue { array: Array.prototype.slice.call(t.array) }); let n = this.attributes; - for(let l1 in n){ - let c1 = n[l1]; - e.data.attributes[l1] = c1.toJSON(e.data); + for(let l in n){ + let c = n[l]; + e.data.attributes[l] = c.toJSON(e.data); } let i = {}, r = !1; - for(let l2 in this.morphAttributes){ - let c2 = this.morphAttributes[l2], h = []; - for(let u = 0, d = c2.length; u < d; u++){ - let f = c2[u]; + for(let l in this.morphAttributes){ + let c = this.morphAttributes[l], h = []; + for(let u = 0, d = c.length; u < d; u++){ + let f = c[u]; h.push(f.toJSON(e.data)); } - h.length > 0 && (i[l2] = h, r = !0); + h.length > 0 && (i[l] = h, r = !0); } r && (e.data.morphAttributes = i, e.data.morphTargetsRelative = this.morphTargetsRelative); let o = this.groups; @@ -3216,16 +3216,16 @@ var de = class extends Ue { this.setAttribute(c, h.clone(t)); } let r = e.morphAttributes; - for(let c1 in r){ - let h1 = [], u = r[c1]; - for(let d = 0, f = u.length; d < f; d++)h1.push(u[d].clone(t)); - this.morphAttributes[c1] = h1; + for(let c in r){ + let h = [], u = r[c]; + for(let d = 0, f = u.length; d < f; d++)h.push(u[d].clone(t)); + this.morphAttributes[c] = h; } this.morphTargetsRelative = e.morphTargetsRelative; let o = e.groups; - for(let c2 = 0, h2 = o.length; c2 < h2; c2++){ - let u1 = o[c2]; - this.addGroup(u1.start, u1.count, u1.materialIndex); + for(let c = 0, h = o.length; c < h; c++){ + let u = o[c]; + this.addGroup(u.start, u.count, u.materialIndex); } let a = e.boundingBox; a !== null && (this.boundingBox = a.clone()); @@ -3262,8 +3262,8 @@ var Cl = new pe, hi = new Cn, Bo = new An, mn = new M, gn = new M, xn = new M, z } } } else { - let t1 = e.morphTargets; - t1 !== void 0 && t1.length > 0 && console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); + let t = e.morphTargets; + t !== void 0 && t.length > 0 && console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } } raycast(e, t) { @@ -3280,24 +3280,24 @@ var Cl = new pe, hi = new Cn, Bo = new An, mn = new M, gn = new M, xn = new M, z } } else { - let x1 = Math.max(0, m.start), v1 = Math.min(a.count, m.start + m.count); - for(let g1 = x1, p1 = v1; g1 < p1; g1 += 3){ - let _1 = a.getX(g1), y1 = a.getX(g1 + 1), b1 = a.getX(g1 + 2); - o = os(this, i, e, hi, l, c, h, u, d, _1, y1, b1), o && (o.faceIndex = Math.floor(g1 / 3), t.push(o)); + let x = Math.max(0, m.start), v = Math.min(a.count, m.start + m.count); + for(let g = x, p = v; g < p; g += 3){ + let _ = a.getX(g), y = a.getX(g + 1), b = a.getX(g + 2); + o = os(this, i, e, hi, l, c, h, u, d, _, y, b), o && (o.faceIndex = Math.floor(g / 3), t.push(o)); } } - else if (l !== void 0) if (Array.isArray(i)) for(let x2 = 0, v2 = f.length; x2 < v2; x2++){ - let g2 = f[x2], p2 = i[g2.materialIndex], _2 = Math.max(g2.start, m.start), y2 = Math.min(l.count, Math.min(g2.start + g2.count, m.start + m.count)); - for(let b2 = _2, A1 = y2; b2 < A1; b2 += 3){ - let L1 = b2, I1 = b2 + 1, k1 = b2 + 2; - o = os(this, p2, e, hi, l, c, h, u, d, L1, I1, k1), o && (o.faceIndex = Math.floor(b2 / 3), o.face.materialIndex = g2.materialIndex, t.push(o)); + else if (l !== void 0) if (Array.isArray(i)) for(let x = 0, v = f.length; x < v; x++){ + let g = f[x], p = i[g.materialIndex], _ = Math.max(g.start, m.start), y = Math.min(l.count, Math.min(g.start + g.count, m.start + m.count)); + for(let b = _, A = y; b < A; b += 3){ + let L = b, I = b + 1, k = b + 2; + o = os(this, p, e, hi, l, c, h, u, d, L, I, k), o && (o.faceIndex = Math.floor(b / 3), o.face.materialIndex = g.materialIndex, t.push(o)); } } else { - let x3 = Math.max(0, m.start), v3 = Math.min(l.count, m.start + m.count); - for(let g3 = x3, p3 = v3; g3 < p3; g3 += 3){ - let _3 = g3, y3 = g3 + 1, b3 = g3 + 2; - o = os(this, i, e, hi, l, c, h, u, d, _3, y3, b3), o && (o.faceIndex = Math.floor(g3 / 3), t.push(o)); + let x = Math.max(0, m.start), v = Math.min(l.count, m.start + m.count); + for(let g = x, p = v; g < p; g += 3){ + let _ = g, y = g + 1, b = g + 2; + o = os(this, i, e, hi, l, c, h, u, d, _, y, b), o && (o.faceIndex = Math.floor(g / 3), t.push(o)); } } } else n.isGeometry && console.error("THREE.Mesh.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); @@ -3330,14 +3330,14 @@ function os(s, e, t, n, i, r, o, a, l, c, h, u) { let f = hf(s, e, t, n, mn, gn, xn, Ho); if (f) { a && (ns.fromBufferAttribute(a, c), is.fromBufferAttribute(a, h), rs.fromBufferAttribute(a, u), f.uv = nt.getUV(Ho, mn, gn, xn, ns, is, rs, new X)), l && (ns.fromBufferAttribute(l, c), is.fromBufferAttribute(l, h), rs.fromBufferAttribute(l, u), f.uv2 = nt.getUV(Ho, mn, gn, xn, ns, is, rs, new X)); - let m1 = { + let m = { a: c, b: h, c: u, normal: new M, materialIndex: 0 }; - nt.getNormal(mn, gn, xn, m1.normal), f.face = m1; + nt.getNormal(mn, gn, xn, m.normal), f.face = m; } return f; } @@ -3365,9 +3365,9 @@ var wn = class extends _e { ce[x] = le * p, ce[v] = W * _, ce[g] = D, c.push(ce.x, ce.y, ce.z), ce[x] = 0, ce[v] = 0, ce[g] = A > 0 ? 1 : -1, h.push(ce.x, ce.y, ce.z), u.push(he / L), u.push(1 - V / I), O += 1; } } - for(let V1 = 0; V1 < I; V1++)for(let W1 = 0; W1 < L; W1++){ - let he1 = d + W1 + U * V1, le1 = d + W1 + U * (V1 + 1), fe = d + (W1 + 1) + U * (V1 + 1), Be = d + (W1 + 1) + U * V1; - l.push(he1, le1, Be), l.push(le1, fe, Be), ne += 6; + for(let V = 0; V < I; V++)for(let W = 0; W < L; W++){ + let he = d + W + U * V, le = d + W + U * (V + 1), fe = d + (W + 1) + U * (V + 1), Be = d + (W + 1) + U * V; + l.push(he, le, Be), l.push(le, fe, Be), ne += 6; } a.addGroup(f, ne, k), f += ne, d += O; } @@ -3461,7 +3461,7 @@ var uf = { } Object.keys(this.defines).length > 0 && (t.defines = this.defines), t.vertexShader = this.vertexShader, t.fragmentShader = this.fragmentShader; let n = {}; - for(let i1 in this.extensions)this.extensions[i1] === !0 && (n[i1] = !0); + for(let i in this.extensions)this.extensions[i] === !0 && (n[i] = !0); return Object.keys(n).length > 0 && (t.extensions = n), t; } }; @@ -3862,9 +3862,9 @@ var Pi = class extends _e { m.push(y, -p, 0), x.push(0, 0, 1), v.push(_ / a), v.push(1 - g / l); } } - for(let g1 = 0; g1 < l; g1++)for(let p1 = 0; p1 < a; p1++){ - let _1 = p1 + c * g1, y1 = p1 + c * (g1 + 1), b = p1 + 1 + c * (g1 + 1), A = p1 + 1 + c * g1; - f.push(_1, y1, A), f.push(y1, b, A); + for(let g = 0; g < l; g++)for(let p = 0; p < a; p++){ + let _ = p + c * g, y = p + c * (g + 1), b = p + 1 + c * (g + 1), A = p + 1 + c * g; + f.push(_, y, A), f.push(y, b, A); } this.setIndex(f), this.setAttribute("position", new de(m, 3)), this.setAttribute("normal", new de(x, 3)), this.setAttribute("uv", new de(v, 2)); } @@ -7432,8 +7432,8 @@ function Wm(s, e, t, n) { let ce = m(F, U, D); c !== ce && (c = ce, d(c.object)), ne = v(F, O), ne && g(F, O); } else { - let ce1 = D.wireframe === !0; - (c.geometry !== F.id || c.program !== U.id || c.wireframe !== ce1) && (c.geometry = F.id, c.program = U.id, c.wireframe = ce1, ne = !0); + let ce = D.wireframe === !0; + (c.geometry !== F.id || c.program !== U.id || c.wireframe !== ce) && (c.geometry = F.id, c.program = U.id, c.wireframe = ce, ne = !0); } E.isInstancedMesh === !0 && (ne = !0), O !== null && t.update(O, 34963), ne && (L(E, D, U, F), O !== null && s.bindBuffer(34963, t.get(O).buffer)); } @@ -7521,31 +7521,31 @@ function Wm(s, e, t, n) { if (ge && ge.isInstancedInterleavedBuffer) { for(let G = 0; G < W.locationSize; G++)y(W.location + G, ge.meshPerAttribute); E.isInstancedMesh !== !0 && F._maxInstanceCount === void 0 && (F._maxInstanceCount = ge.meshPerAttribute * ge.count); - } else for(let G1 = 0; G1 < W.locationSize; G1++)_(W.location + G1); + } else for(let G = 0; G < W.locationSize; G++)_(W.location + G); s.bindBuffer(34962, Y); - for(let G2 = 0; G2 < W.locationSize; G2++)A(W.location + G2, fe / W.locationSize, Ce, le, xe * ye, (Oe + fe / W.locationSize * G2) * ye); + for(let G = 0; G < W.locationSize; G++)A(W.location + G, fe / W.locationSize, Ce, le, xe * ye, (Oe + fe / W.locationSize * G) * ye); } else { if (he.isInstancedBufferAttribute) { - for(let ge1 = 0; ge1 < W.locationSize; ge1++)y(W.location + ge1, he.meshPerAttribute); + for(let ge = 0; ge < W.locationSize; ge++)y(W.location + ge, he.meshPerAttribute); E.isInstancedMesh !== !0 && F._maxInstanceCount === void 0 && (F._maxInstanceCount = he.meshPerAttribute * he.count); - } else for(let ge2 = 0; ge2 < W.locationSize; ge2++)_(W.location + ge2); + } else for(let ge = 0; ge < W.locationSize; ge++)_(W.location + ge); s.bindBuffer(34962, Y); - for(let ge3 = 0; ge3 < W.locationSize; ge3++)A(W.location + ge3, fe / W.locationSize, Ce, le, fe * ye, fe / W.locationSize * ge3 * ye); + for(let ge = 0; ge < W.locationSize; ge++)A(W.location + ge, fe / W.locationSize, Ce, le, fe * ye, fe / W.locationSize * ge * ye); } } else if (ce !== void 0) { - let le1 = ce[V]; - if (le1 !== void 0) switch(le1.length){ + let le = ce[V]; + if (le !== void 0) switch(le.length){ case 2: - s.vertexAttrib2fv(W.location, le1); + s.vertexAttrib2fv(W.location, le); break; case 3: - s.vertexAttrib3fv(W.location, le1); + s.vertexAttrib3fv(W.location, le); break; case 4: - s.vertexAttrib4fv(W.location, le1); + s.vertexAttrib4fv(W.location, le); break; default: - s.vertexAttrib1fv(W.location, le1); + s.vertexAttrib1fv(W.location, le); } } } @@ -7712,9 +7712,9 @@ function Ym(s) { let l = e.get(o).texture; return t(l, o.mapping); } else { - let l1 = o.image; - if (l1 && l1.height > 0) { - let c = s.getRenderTarget(), h = new js(l1.height / 2); + let l = o.image; + if (l && l.height > 0) { + let c = s.getRenderTarget(), h = new js(l.height / 2); return h.fromEquirectangularTexture(s, o), e.set(o, h), s.setRenderTarget(c), o.addEventListener("dispose", i), t(h.texture, o.mapping); } else return null; } @@ -7910,7 +7910,7 @@ var Ei = 4, Mn = 8, Vt = Math.pow(2, Mn), sh = [ let L = A / x, I = Math.exp(-L * L / 2); g.push(I), A == 0 ? p += I : A < v && (p += 2 * I); } - for(let A1 = 0; A1 < g.length; A1++)g[A1] = g[A1] / p; + for(let A = 0; A < g.length; A++)g[A] = g[A] / p; d.envMap.value = e.texture, d.samples.value = v, d.weights.value = g, d.latitudinal.value = o === "latitudinal", a && (d.poleAxis.value = a), d.dTheta.value = m, d.mipInt.value = Mn - n; let _ = Ll[i], y = 3 * Math.max(0, Vt - 2 * _), b = (i === 0 ? 0 : 2 * Vt) + 2 * _ * (i > Mn - Ei ? i - Mn + Ei : 0); cs(t, y, b, 3 * _, 2 * _), l.setRenderTarget(t), l.render(u, Go); @@ -8356,8 +8356,8 @@ function Km(s, e, t, n) { let d = u.attributes; for(let m in d)e.update(d[m], 34962); let f = u.morphAttributes; - for(let m1 in f){ - let x = f[m1]; + for(let m in f){ + let x = f[m]; for(let v = 0, g = x.length; v < g; v++)e.update(x[v], 34962); } } @@ -8371,11 +8371,11 @@ function Km(s, e, t, n) { d.push(b, A, A, L, L, b); } } else { - let p1 = m.array; + let p = m.array; x = m.version; - for(let _1 = 0, y1 = p1.length / 3 - 1; _1 < y1; _1 += 3){ - let b1 = _1 + 0, A1 = _1 + 1, L1 = _1 + 2; - d.push(b1, A1, A1, L1, L1, b1); + for(let _ = 0, y = p.length / 3 - 1; _ < y; _ += 3){ + let b = _ + 0, A = _ + 1, L = _ + 2; + d.push(b, A, A, L, L, b); } } let v = new (Yc(d) > 65535 ? Zs : Ys)(d, 1); @@ -8520,33 +8520,33 @@ function rg(s, e, t) { }, r.set(h, x); } let v = 0; - for(let p1 = 0; p1 < f.length; p1++)v += f[p1]; + for(let p = 0; p < f.length; p++)v += f[p]; let g = h.morphTargetsRelative ? 1 : 1 - v; d.getUniforms().setValue(s, "morphTargetBaseInfluence", g), d.getUniforms().setValue(s, "morphTargetInfluences", f), d.getUniforms().setValue(s, "morphTargetsTexture", x.texture, t), d.getUniforms().setValue(s, "morphTargetsTextureSize", x.size); } else { - let m1 = f === void 0 ? 0 : f.length, x1 = n[h.id]; - if (x1 === void 0 || x1.length !== m1) { - x1 = []; - for(let y1 = 0; y1 < m1; y1++)x1[y1] = [ - y1, + let m = f === void 0 ? 0 : f.length, x = n[h.id]; + if (x === void 0 || x.length !== m) { + x = []; + for(let y = 0; y < m; y++)x[y] = [ + y, 0 ]; - n[h.id] = x1; + n[h.id] = x; } - for(let y2 = 0; y2 < m1; y2++){ - let b1 = x1[y2]; - b1[0] = y2, b1[1] = f[y2]; + for(let y = 0; y < m; y++){ + let b = x[y]; + b[0] = y, b[1] = f[y]; } - x1.sort(ig); - for(let y3 = 0; y3 < 8; y3++)y3 < m1 && x1[y3][1] ? (a[y3][0] = x1[y3][0], a[y3][1] = x1[y3][1]) : (a[y3][0] = Number.MAX_SAFE_INTEGER, a[y3][1] = 0); + x.sort(ig); + for(let y = 0; y < 8; y++)y < m && x[y][1] ? (a[y][0] = x[y][0], a[y][1] = x[y][1]) : (a[y][0] = Number.MAX_SAFE_INTEGER, a[y][1] = 0); a.sort(ng); - let v1 = h.morphAttributes.position, g1 = h.morphAttributes.normal, p2 = 0; - for(let y4 = 0; y4 < 8; y4++){ - let b2 = a[y4], A1 = b2[0], L1 = b2[1]; - A1 !== Number.MAX_SAFE_INTEGER && L1 ? (v1 && h.getAttribute("morphTarget" + y4) !== v1[A1] && h.setAttribute("morphTarget" + y4, v1[A1]), g1 && h.getAttribute("morphNormal" + y4) !== g1[A1] && h.setAttribute("morphNormal" + y4, g1[A1]), i[y4] = L1, p2 += L1) : (v1 && h.hasAttribute("morphTarget" + y4) === !0 && h.deleteAttribute("morphTarget" + y4), g1 && h.hasAttribute("morphNormal" + y4) === !0 && h.deleteAttribute("morphNormal" + y4), i[y4] = 0); + let v = h.morphAttributes.position, g = h.morphAttributes.normal, p = 0; + for(let y = 0; y < 8; y++){ + let b = a[y], A = b[0], L = b[1]; + A !== Number.MAX_SAFE_INTEGER && L ? (v && h.getAttribute("morphTarget" + y) !== v[A] && h.setAttribute("morphTarget" + y, v[A]), g && h.getAttribute("morphNormal" + y) !== g[A] && h.setAttribute("morphNormal" + y, g[A]), i[y] = L, p += L) : (v && h.hasAttribute("morphTarget" + y) === !0 && h.deleteAttribute("morphTarget" + y), g && h.hasAttribute("morphNormal" + y) === !0 && h.deleteAttribute("morphNormal" + y), i[y] = 0); } - let _1 = h.morphTargetsRelative ? 1 : 1 - p2; - d.getUniforms().setValue(s, "morphTargetBaseInfluence", _1), d.getUniforms().setValue(s, "morphTargetInfluences", i); + let _ = h.morphTargetsRelative ? 1 : 1 - p; + d.getUniforms().setValue(s, "morphTargetBaseInfluence", _), d.getUniforms().setValue(s, "morphTargetInfluences", i); } } return { @@ -9863,37 +9863,37 @@ function _x(s, e) { let x = 0, v = 0, g = 0, p = 0, _ = 0, y = 0, b = 0, A = 0; h.sort(vx); let L = u !== !0 ? Math.PI : 1; - for(let k1 = 0, B = h.length; k1 < B; k1++){ - let P = h[k1], w = P.color, E = P.intensity, D = P.distance, U = P.shadow && P.shadow.map ? P.shadow.map.texture : null; + for(let k = 0, B = h.length; k < B; k++){ + let P = h[k], w = P.color, E = P.intensity, D = P.distance, U = P.shadow && P.shadow.map ? P.shadow.map.texture : null; if (P.isAmbientLight) d += w.r * E * L, f += w.g * E * L, m += w.b * E * L; else if (P.isLightProbe) for(let F = 0; F < 9; F++)i.probe[F].addScaledVector(P.sh.coefficients[F], E); else if (P.isDirectionalLight) { - let F1 = t.get(P); - if (F1.color.copy(P.color).multiplyScalar(P.intensity * L), P.castShadow) { + let F = t.get(P); + if (F.color.copy(P.color).multiplyScalar(P.intensity * L), P.castShadow) { let O = P.shadow, ne = n.get(P); ne.shadowBias = O.bias, ne.shadowNormalBias = O.normalBias, ne.shadowRadius = O.radius, ne.shadowMapSize = O.mapSize, i.directionalShadow[x] = ne, i.directionalShadowMap[x] = U, i.directionalShadowMatrix[x] = P.shadow.matrix, y++; } - i.directional[x] = F1, x++; + i.directional[x] = F, x++; } else if (P.isSpotLight) { - let F2 = t.get(P); - if (F2.position.setFromMatrixPosition(P.matrixWorld), F2.color.copy(w).multiplyScalar(E * L), F2.distance = D, F2.coneCos = Math.cos(P.angle), F2.penumbraCos = Math.cos(P.angle * (1 - P.penumbra)), F2.decay = P.decay, P.castShadow) { - let O1 = P.shadow, ne1 = n.get(P); - ne1.shadowBias = O1.bias, ne1.shadowNormalBias = O1.normalBias, ne1.shadowRadius = O1.radius, ne1.shadowMapSize = O1.mapSize, i.spotShadow[g] = ne1, i.spotShadowMap[g] = U, i.spotShadowMatrix[g] = P.shadow.matrix, A++; + let F = t.get(P); + if (F.position.setFromMatrixPosition(P.matrixWorld), F.color.copy(w).multiplyScalar(E * L), F.distance = D, F.coneCos = Math.cos(P.angle), F.penumbraCos = Math.cos(P.angle * (1 - P.penumbra)), F.decay = P.decay, P.castShadow) { + let O = P.shadow, ne = n.get(P); + ne.shadowBias = O.bias, ne.shadowNormalBias = O.normalBias, ne.shadowRadius = O.radius, ne.shadowMapSize = O.mapSize, i.spotShadow[g] = ne, i.spotShadowMap[g] = U, i.spotShadowMatrix[g] = P.shadow.matrix, A++; } - i.spot[g] = F2, g++; + i.spot[g] = F, g++; } else if (P.isRectAreaLight) { - let F3 = t.get(P); - F3.color.copy(w).multiplyScalar(E), F3.halfWidth.set(P.width * .5, 0, 0), F3.halfHeight.set(0, P.height * .5, 0), i.rectArea[p] = F3, p++; + let F = t.get(P); + F.color.copy(w).multiplyScalar(E), F.halfWidth.set(P.width * .5, 0, 0), F.halfHeight.set(0, P.height * .5, 0), i.rectArea[p] = F, p++; } else if (P.isPointLight) { - let F4 = t.get(P); - if (F4.color.copy(P.color).multiplyScalar(P.intensity * L), F4.distance = P.distance, F4.decay = P.decay, P.castShadow) { - let O2 = P.shadow, ne2 = n.get(P); - ne2.shadowBias = O2.bias, ne2.shadowNormalBias = O2.normalBias, ne2.shadowRadius = O2.radius, ne2.shadowMapSize = O2.mapSize, ne2.shadowCameraNear = O2.camera.near, ne2.shadowCameraFar = O2.camera.far, i.pointShadow[v] = ne2, i.pointShadowMap[v] = U, i.pointShadowMatrix[v] = P.shadow.matrix, b++; + let F = t.get(P); + if (F.color.copy(P.color).multiplyScalar(P.intensity * L), F.distance = P.distance, F.decay = P.decay, P.castShadow) { + let O = P.shadow, ne = n.get(P); + ne.shadowBias = O.bias, ne.shadowNormalBias = O.normalBias, ne.shadowRadius = O.radius, ne.shadowMapSize = O.mapSize, ne.shadowCameraNear = O.camera.near, ne.shadowCameraFar = O.camera.far, i.pointShadow[v] = ne, i.pointShadowMap[v] = U, i.pointShadowMatrix[v] = P.shadow.matrix, b++; } - i.point[v] = F4, v++; + i.point[v] = F, v++; } else if (P.isHemisphereLight) { - let F5 = t.get(P); - F5.skyColor.copy(P.color).multiplyScalar(E * L), F5.groundColor.copy(P.groundColor).multiplyScalar(E * L), i.hemi[_] = F5, _++; + let F = t.get(P); + F.skyColor.copy(P.color).multiplyScalar(E * L), F.groundColor.copy(P.groundColor).multiplyScalar(E * L), i.hemi[_] = F, _++; } } p > 0 && (e.isWebGL2 || s.has("OES_texture_float_linear") === !0 ? (i.rectAreaLTC1 = ie.LTC_FLOAT_1, i.rectAreaLTC2 = ie.LTC_FLOAT_2) : s.has("OES_texture_half_float_linear") === !0 ? (i.rectAreaLTC1 = ie.LTC_HALF_1, i.rectAreaLTC2 = ie.LTC_HALF_2) : console.error("THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.")), i.ambient[0] = d, i.ambient[1] = f, i.ambient[2] = m; @@ -9908,17 +9908,17 @@ function _x(s, e) { let b = i.directional[d]; b.direction.setFromMatrixPosition(y.matrixWorld), r.setFromMatrixPosition(y.target.matrixWorld), b.direction.sub(r), b.direction.transformDirection(g), d++; } else if (y.isSpotLight) { - let b1 = i.spot[m]; - b1.position.setFromMatrixPosition(y.matrixWorld), b1.position.applyMatrix4(g), b1.direction.setFromMatrixPosition(y.matrixWorld), r.setFromMatrixPosition(y.target.matrixWorld), b1.direction.sub(r), b1.direction.transformDirection(g), m++; + let b = i.spot[m]; + b.position.setFromMatrixPosition(y.matrixWorld), b.position.applyMatrix4(g), b.direction.setFromMatrixPosition(y.matrixWorld), r.setFromMatrixPosition(y.target.matrixWorld), b.direction.sub(r), b.direction.transformDirection(g), m++; } else if (y.isRectAreaLight) { - let b2 = i.rectArea[x]; - b2.position.setFromMatrixPosition(y.matrixWorld), b2.position.applyMatrix4(g), a.identity(), o.copy(y.matrixWorld), o.premultiply(g), a.extractRotation(o), b2.halfWidth.set(y.width * .5, 0, 0), b2.halfHeight.set(0, y.height * .5, 0), b2.halfWidth.applyMatrix4(a), b2.halfHeight.applyMatrix4(a), x++; + let b = i.rectArea[x]; + b.position.setFromMatrixPosition(y.matrixWorld), b.position.applyMatrix4(g), a.identity(), o.copy(y.matrixWorld), o.premultiply(g), a.extractRotation(o), b.halfWidth.set(y.width * .5, 0, 0), b.halfHeight.set(0, y.height * .5, 0), b.halfWidth.applyMatrix4(a), b.halfHeight.applyMatrix4(a), x++; } else if (y.isPointLight) { - let b3 = i.point[f]; - b3.position.setFromMatrixPosition(y.matrixWorld), b3.position.applyMatrix4(g), f++; + let b = i.point[f]; + b.position.setFromMatrixPosition(y.matrixWorld), b.position.applyMatrix4(g), f++; } else if (y.isHemisphereLight) { - let b4 = i.hemi[v]; - b4.direction.setFromMatrixPosition(y.matrixWorld), b4.direction.transformDirection(g), b4.direction.normalize(), v++; + let b = i.hemi[v]; + b.direction.setFromMatrixPosition(y.matrixWorld), b.direction.transformDirection(g), b.direction.normalize(), v++; } } } @@ -10084,18 +10084,18 @@ function yh(s, e, t) { D.map = new At(i.x, i.y, O), D.map.texture.name = E.name + ".shadowMap", D.mapPass = new At(i.x, i.y, O), D.camera.updateProjectionMatrix(); } if (D.map === null) { - let O1 = { + let O = { minFilter: rt, magFilter: rt, format: ct }; - D.map = new At(i.x, i.y, O1), D.map.texture.name = E.name + ".shadowMap", D.camera.updateProjectionMatrix(); + D.map = new At(i.x, i.y, O), D.map.texture.name = E.name + ".shadowMap", D.camera.updateProjectionMatrix(); } s.setRenderTarget(D.map), s.clear(); let F = D.getViewportCount(); - for(let O2 = 0; O2 < F; O2++){ - let ne = D.getViewport(O2); - o.set(r.x * ne.x, r.y * ne.y, r.x * ne.z, r.y * ne.w), B.viewport(o), D.updateMatrices(E, O2), n = D.getFrustum(), _(b, A, D.camera, E, this.type); + for(let O = 0; O < F; O++){ + let ne = D.getViewport(O); + o.set(r.x * ne.x, r.y * ne.y, r.x * ne.z, r.y * ne.w), B.viewport(o), D.updateMatrices(E, O), n = D.getFrustum(), _(b, A, D.camera, E, this.type); } !D.isPointLightShadow && this.type === ir && g(D, A), D.needsUpdate = !1; } @@ -10130,12 +10130,12 @@ function yh(s, e, t) { } } } else if (w.visible) { - let E1 = p(y, P, w, L, A.near, A.far, I); - s.renderBufferDirect(A, null, P, E1, y, null); + let E = p(y, P, w, L, A.near, A.far, I); + s.renderBufferDirect(A, null, P, E, y, null); } } let B = y.children; - for(let P1 = 0, w1 = B.length; P1 < w1; P1++)_(B[P1], b, A, L, I); + for(let P = 0, w = B.length; P < w; P++)_(B[P], b, A, L, I); } } function Sx(s, e, t) { @@ -10527,9 +10527,9 @@ function Tx(s, e, t, n, i, r, o) { if (!!C) { if ($.__webglTexture !== void 0 && (s.deleteTexture($.__webglTexture), o.memory.textures--), C.depthTexture && C.depthTexture.dispose(), C.isWebGLCubeRenderTarget) for(let re = 0; re < 6; re++)s.deleteFramebuffer(J.__webglFramebuffer[re]), J.__webglDepthbuffer && s.deleteRenderbuffer(J.__webglDepthbuffer[re]); else s.deleteFramebuffer(J.__webglFramebuffer), J.__webglDepthbuffer && s.deleteRenderbuffer(J.__webglDepthbuffer), J.__webglMultisampledFramebuffer && s.deleteFramebuffer(J.__webglMultisampledFramebuffer), J.__webglColorRenderbuffer && s.deleteRenderbuffer(J.__webglColorRenderbuffer), J.__webglDepthRenderbuffer && s.deleteRenderbuffer(J.__webglDepthRenderbuffer); - if (C.isWebGLMultipleRenderTargets) for(let re1 = 0, Z = T.length; re1 < Z; re1++){ - let Me = n.get(T[re1]); - Me.__webglTexture && (s.deleteTexture(Me.__webglTexture), o.memory.textures--), n.remove(T[re1]); + if (C.isWebGLMultipleRenderTargets) for(let re = 0, Z = T.length; re < Z; re++){ + let Me = n.get(T[re]); + Me.__webglTexture && (s.deleteTexture(Me.__webglTexture), o.memory.textures--), n.remove(T[re]); } n.remove(T), n.remove(C); } @@ -10615,13 +10615,13 @@ function Tx(s, e, t, n, i, r, o) { } else Ee ? (me && t.texStorage2D(3553, Re, R, Z.width, Z.height), t.texSubImage2D(3553, 0, 0, 0, Z.width, Z.height, ve, te, Z.data)) : t.texImage2D(3553, 0, R, Z.width, Z.height, 0, ve, te, Z.data); else if (T.isCompressedTexture) { Ee && me && t.texStorage2D(3553, Re, R, Q[0].width, Q[0].height); - for(let oe1 = 0, Le1 = Q.length; oe1 < Le1; oe1++)ee = Q[oe1], T.format !== ct && T.format !== Gn ? ve !== null ? Ee ? t.compressedTexSubImage2D(3553, oe1, 0, 0, ee.width, ee.height, ve, ee.data) : t.compressedTexImage2D(3553, oe1, R, ee.width, ee.height, 0, ee.data) : console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()") : Ee ? t.texSubImage2D(3553, oe1, 0, 0, ee.width, ee.height, ve, te, ee.data) : t.texImage2D(3553, oe1, R, ee.width, ee.height, 0, ve, te, ee.data); + for(let oe = 0, Le = Q.length; oe < Le; oe++)ee = Q[oe], T.format !== ct && T.format !== Gn ? ve !== null ? Ee ? t.compressedTexSubImage2D(3553, oe, 0, 0, ee.width, ee.height, ve, ee.data) : t.compressedTexImage2D(3553, oe, R, ee.width, ee.height, 0, ee.data) : console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()") : Ee ? t.texSubImage2D(3553, oe, 0, 0, ee.width, ee.height, ve, te, ee.data) : t.texImage2D(3553, oe, R, ee.width, ee.height, 0, ve, te, ee.data); } else if (T.isDataTexture2DArray) Ee ? (me && t.texStorage3D(35866, Re, R, Z.width, Z.height, Z.depth), t.texSubImage3D(35866, 0, 0, 0, 0, Z.width, Z.height, Z.depth, ve, te, Z.data)) : t.texImage3D(35866, 0, R, Z.width, Z.height, Z.depth, 0, ve, te, Z.data); else if (T.isDataTexture3D) Ee ? (me && t.texStorage3D(32879, Re, R, Z.width, Z.height, Z.depth), t.texSubImage3D(32879, 0, 0, 0, 0, Z.width, Z.height, Z.depth, ve, te, Z.data)) : t.texImage3D(32879, 0, R, Z.width, Z.height, Z.depth, 0, ve, te, Z.data); else if (T.isFramebufferTexture) Ee && me ? t.texStorage2D(3553, Re, R, Z.width, Z.height) : t.texImage2D(3553, 0, R, Z.width, Z.height, 0, ve, te, null); else if (Q.length > 0 && Me) { Ee && me && t.texStorage2D(3553, Re, R, Q[0].width, Q[0].height); - for(let oe2 = 0, Le2 = Q.length; oe2 < Le2; oe2++)ee = Q[oe2], Ee ? t.texSubImage2D(3553, oe2, 0, 0, ve, te, ee) : t.texImage2D(3553, oe2, R, ve, te, ee); + for(let oe = 0, Le = Q.length; oe < Le; oe++)ee = Q[oe], Ee ? t.texSubImage2D(3553, oe, 0, 0, ve, te, ee) : t.texImage2D(3553, oe, R, ve, te, ee); T.generateMipmaps = !1; } else Ee ? (me && t.texStorage2D(3553, Re, R, Z.width, Z.height), t.texSubImage2D(3553, 0, 0, 0, ve, te, Z)) : t.texImage2D(3553, 0, R, ve, te, Z); b(T, Me) && A($), C.__version = T.version, T.onUpdate && T.onUpdate(T); @@ -10636,26 +10636,26 @@ function Tx(s, e, t, n, i, r, o) { let Re; if ($) { Q && Ee && t.texStorage2D(34067, me, ee, Me.width, Me.height); - for(let oe1 = 0; oe1 < 6; oe1++){ - Re = Z[oe1].mipmaps; + for(let oe = 0; oe < 6; oe++){ + Re = Z[oe].mipmaps; for(let Le = 0; Le < Re.length; Le++){ let Xe = Re[Le]; - T.format !== ct && T.format !== Gn ? te !== null ? Q ? t.compressedTexSubImage2D(34069 + oe1, Le, 0, 0, Xe.width, Xe.height, te, Xe.data) : t.compressedTexImage2D(34069 + oe1, Le, ee, Xe.width, Xe.height, 0, Xe.data) : console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()") : Q ? t.texSubImage2D(34069 + oe1, Le, 0, 0, Xe.width, Xe.height, te, R, Xe.data) : t.texImage2D(34069 + oe1, Le, ee, Xe.width, Xe.height, 0, te, R, Xe.data); + T.format !== ct && T.format !== Gn ? te !== null ? Q ? t.compressedTexSubImage2D(34069 + oe, Le, 0, 0, Xe.width, Xe.height, te, Xe.data) : t.compressedTexImage2D(34069 + oe, Le, ee, Xe.width, Xe.height, 0, Xe.data) : console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()") : Q ? t.texSubImage2D(34069 + oe, Le, 0, 0, Xe.width, Xe.height, te, R, Xe.data) : t.texImage2D(34069 + oe, Le, ee, Xe.width, Xe.height, 0, te, R, Xe.data); } } } else { Re = T.mipmaps, Q && Ee && (Re.length > 0 && me++, t.texStorage2D(34067, me, ee, Z[0].width, Z[0].height)); - for(let oe2 = 0; oe2 < 6; oe2++)if (re) { - Q ? t.texSubImage2D(34069 + oe2, 0, 0, 0, Z[oe2].width, Z[oe2].height, te, R, Z[oe2].data) : t.texImage2D(34069 + oe2, 0, ee, Z[oe2].width, Z[oe2].height, 0, te, R, Z[oe2].data); - for(let Le1 = 0; Le1 < Re.length; Le1++){ - let We = Re[Le1].image[oe2].image; - Q ? t.texSubImage2D(34069 + oe2, Le1 + 1, 0, 0, We.width, We.height, te, R, We.data) : t.texImage2D(34069 + oe2, Le1 + 1, ee, We.width, We.height, 0, te, R, We.data); + for(let oe = 0; oe < 6; oe++)if (re) { + Q ? t.texSubImage2D(34069 + oe, 0, 0, 0, Z[oe].width, Z[oe].height, te, R, Z[oe].data) : t.texImage2D(34069 + oe, 0, ee, Z[oe].width, Z[oe].height, 0, te, R, Z[oe].data); + for(let Le = 0; Le < Re.length; Le++){ + let We = Re[Le].image[oe].image; + Q ? t.texSubImage2D(34069 + oe, Le + 1, 0, 0, We.width, We.height, te, R, We.data) : t.texImage2D(34069 + oe, Le + 1, ee, We.width, We.height, 0, te, R, We.data); } } else { - Q ? t.texSubImage2D(34069 + oe2, 0, 0, 0, te, R, Z[oe2]) : t.texImage2D(34069 + oe2, 0, ee, te, R, Z[oe2]); - for(let Le2 = 0; Le2 < Re.length; Le2++){ - let Xe1 = Re[Le2]; - Q ? t.texSubImage2D(34069 + oe2, Le2 + 1, 0, 0, te, R, Xe1.image[oe2]) : t.texImage2D(34069 + oe2, Le2 + 1, ee, te, R, Xe1.image[oe2]); + Q ? t.texSubImage2D(34069 + oe, 0, 0, 0, te, R, Z[oe]) : t.texImage2D(34069 + oe, 0, ee, te, R, Z[oe]); + for(let Le = 0; Le < Re.length; Le++){ + let Xe = Re[Le]; + Q ? t.texSubImage2D(34069 + oe, Le + 1, 0, 0, te, R, Xe.image[oe]) : t.texImage2D(34069 + oe, Le + 1, ee, te, R, Xe.image[oe]); } } } @@ -10676,10 +10676,10 @@ function Tx(s, e, t, n, i, r, o) { } else s.renderbufferStorage(36161, $, T.width, T.height); s.framebufferRenderbuffer(36160, 36096, 36161, C); } else if (T.depthBuffer && T.stencilBuffer) { - let $1 = ue(T); - J && T.useRenderbuffer ? s.renderbufferStorageMultisample(36161, $1, 35056, T.width, T.height) : T.useRenderToTexture ? f.renderbufferStorageMultisampleEXT(36161, $1, 35056, T.width, T.height) : s.renderbufferStorage(36161, 34041, T.width, T.height), s.framebufferRenderbuffer(36160, 33306, 36161, C); + let $ = ue(T); + J && T.useRenderbuffer ? s.renderbufferStorageMultisample(36161, $, 35056, T.width, T.height) : T.useRenderToTexture ? f.renderbufferStorageMultisampleEXT(36161, $, 35056, T.width, T.height) : s.renderbufferStorage(36161, 34041, T.width, T.height), s.framebufferRenderbuffer(36160, 33306, 36161, C); } else { - let $2 = T.isWebGLMultipleRenderTargets === !0 ? T.texture[0] : T.texture, re1 = r.convert($2.format), Z1 = r.convert($2.type), Me = L($2.internalFormat, re1, Z1, $2.encoding), ve = ue(T); + let $ = T.isWebGLMultipleRenderTargets === !0 ? T.texture[0] : T.texture, re = r.convert($.format), Z = r.convert($.type), Me = L($.internalFormat, re, Z, $.encoding), ve = ue(T); J && T.useRenderbuffer ? s.renderbufferStorageMultisample(36161, ve, Me, T.width, T.height) : T.useRenderToTexture ? f.renderbufferStorageMultisampleEXT(36161, ve, Me, T.width, T.height) : s.renderbufferStorage(36161, Me, T.width, T.height); } s.bindRenderbuffer(36161, null); @@ -10716,31 +10716,31 @@ function Tx(s, e, t, n, i, r, o) { J.__webglFramebuffer = []; for(let te = 0; te < 6; te++)J.__webglFramebuffer[te] = s.createFramebuffer(); } else if (J.__webglFramebuffer = s.createFramebuffer(), Z) if (i.drawBuffers) { - let te1 = C.texture; - for(let R = 0, ee = te1.length; R < ee; R++){ - let Q = n.get(te1[R]); + let te = C.texture; + for(let R = 0, ee = te.length; R < ee; R++){ + let Q = n.get(te[R]); Q.__webglTexture === void 0 && (Q.__webglTexture = s.createTexture(), o.memory.textures++); } } else console.warn("THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension."); else if (C.useRenderbuffer) if (a) { J.__webglMultisampledFramebuffer = s.createFramebuffer(), J.__webglColorRenderbuffer = s.createRenderbuffer(), s.bindRenderbuffer(36161, J.__webglColorRenderbuffer); - let te2 = r.convert(T.format), R1 = r.convert(T.type), ee1 = L(T.internalFormat, te2, R1, T.encoding), Q1 = ue(C); - s.renderbufferStorageMultisample(36161, Q1, ee1, C.width, C.height), t.bindFramebuffer(36160, J.__webglMultisampledFramebuffer), s.framebufferRenderbuffer(36160, 36064, 36161, J.__webglColorRenderbuffer), s.bindRenderbuffer(36161, null), C.depthBuffer && (J.__webglDepthRenderbuffer = s.createRenderbuffer(), ye(J.__webglDepthRenderbuffer, C, !0)), t.bindFramebuffer(36160, null); + let te = r.convert(T.format), R = r.convert(T.type), ee = L(T.internalFormat, te, R, T.encoding), Q = ue(C); + s.renderbufferStorageMultisample(36161, Q, ee, C.width, C.height), t.bindFramebuffer(36160, J.__webglMultisampledFramebuffer), s.framebufferRenderbuffer(36160, 36064, 36161, J.__webglColorRenderbuffer), s.bindRenderbuffer(36161, null), C.depthBuffer && (J.__webglDepthRenderbuffer = s.createRenderbuffer(), ye(J.__webglDepthRenderbuffer, C, !0)), t.bindFramebuffer(36160, null); } else console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2."); if (re) { t.bindTexture(34067, $.__webglTexture), le(34067, T, ve); - for(let te3 = 0; te3 < 6; te3++)Ce(J.__webglFramebuffer[te3], C, T, 36064, 34069 + te3); + for(let te = 0; te < 6; te++)Ce(J.__webglFramebuffer[te], C, T, 36064, 34069 + te); b(T, ve) && A(34067), t.unbindTexture(); } else if (Z) { - let te4 = C.texture; - for(let R2 = 0, ee2 = te4.length; R2 < ee2; R2++){ - let Q2 = te4[R2], Ee = n.get(Q2); - t.bindTexture(3553, Ee.__webglTexture), le(3553, Q2, ve), Ce(J.__webglFramebuffer, C, Q2, 36064 + R2, 3553), b(Q2, ve) && A(3553); + let te = C.texture; + for(let R = 0, ee = te.length; R < ee; R++){ + let Q = te[R], Ee = n.get(Q); + t.bindTexture(3553, Ee.__webglTexture), le(3553, Q, ve), Ce(J.__webglFramebuffer, C, Q, 36064 + R, 3553), b(Q, ve) && A(3553); } t.unbindTexture(); } else { - let te5 = 3553; - Me && (a ? te5 = T.isDataTexture3D ? 32879 : 35866 : console.warn("THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2.")), t.bindTexture(te5, $.__webglTexture), le(te5, T, ve), Ce(J.__webglFramebuffer, C, T, 36064, te5), b(T, ve) && A(te5), t.unbindTexture(); + let te = 3553; + Me && (a ? te = T.isDataTexture3D ? 32879 : 35866 : console.warn("THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2.")), t.bindTexture(te, $.__webglTexture), le(te, T, ve), Ce(J.__webglFramebuffer, C, T, 36064, te), b(T, ve) && A(te), t.unbindTexture(); } C.depthBuffer && xe(C); } @@ -10987,8 +10987,8 @@ var vh = class extends En { }); } else { f = x.antialias; - let W1 = null, he = null, le = null; - x.depth && (le = x.stencil ? 35056 : 33190, W1 = x.stencil ? Li : Vn, he = x.stencil ? Ti : cr); + let W = null, he = null, le = null; + x.depth && (le = x.stencil ? 35056 : 33190, W = x.stencil ? Li : Vn, he = x.stencil ? Ti : cr); let fe = { colorFormat: x.alpha || f ? 32856 : 32849, depthFormat: le, @@ -11001,7 +11001,7 @@ var vh = class extends En { }), f ? g = new Xs(u.textureWidth, u.textureHeight, { format: ct, type: rn, - depthTexture: new ks(u.textureWidth, u.textureHeight, he, void 0, void 0, void 0, void 0, void 0, void 0, W1), + depthTexture: new ks(u.textureWidth, u.textureHeight, he, void 0, void 0, void 0, void 0, void 0, void 0, W), stencilBuffer: x.stencil, ignoreDepth: u.ignoreDepthValues, useRenderToTexture: l, @@ -11009,7 +11009,7 @@ var vh = class extends En { }) : g = new At(u.textureWidth, u.textureHeight, { format: x.alpha ? ct : Gn, type: rn, - depthTexture: new ks(u.textureWidth, u.textureHeight, he, void 0, void 0, void 0, void 0, void 0, void 0, W1), + depthTexture: new ks(u.textureWidth, u.textureHeight, he, void 0, void 0, void 0, void 0, void 0, void 0, W), stencilBuffer: x.stencil, ignoreDepth: u.ignoreDepthValues, encoding: e.outputEncoding @@ -11023,18 +11023,18 @@ var vh = class extends En { function w(V) { let W = i.inputSources; for(let he = 0; he < p.length; he++)_.set(W[he], p[he]); - for(let he1 = 0; he1 < V.removed.length; he1++){ - let le = V.removed[he1], fe = _.get(le); + for(let he = 0; he < V.removed.length; he++){ + let le = V.removed[he], fe = _.get(le); fe && (fe.dispatchEvent({ type: "disconnected", data: le }), _.delete(le)); } - for(let he2 = 0; he2 < V.added.length; he2++){ - let le1 = V.added[he2], fe1 = _.get(le1); - fe1 && fe1.dispatchEvent({ + for(let he = 0; he < V.added.length; he++){ + let le = V.added[he], fe = _.get(le); + fe && fe.dispatchEvent({ type: "connected", - data: le1 + data: le }); } } @@ -11060,7 +11060,7 @@ var vh = class extends En { for(let fe = 0; fe < he.length; fe++)F(he[fe], W); L.matrixWorld.decompose(L.position, L.quaternion, L.scale), V.position.copy(L.position), V.quaternion.copy(L.quaternion), V.scale.copy(L.scale), V.matrix.copy(L.matrix), V.matrixWorld.copy(L.matrixWorld); let le = V.children; - for(let fe1 = 0, Be = le.length; fe1 < Be; fe1++)le[fe1].updateMatrixWorld(!0); + for(let fe = 0, Be = le.length; fe < Be; fe++)le[fe].updateMatrixWorld(!0); he.length === 2 ? U(L, y, b) : L.projectionMatrix.copy(y.projectionMatrix); }, this.getCamera = function() { return L; @@ -11089,9 +11089,9 @@ var vh = class extends En { } } let he = i.inputSources; - for(let le1 = 0; le1 < p.length; le1++){ - let fe1 = p[le1], Be1 = he[le1]; - fe1.update(Be1, W, o); + for(let le = 0; le < p.length; le++){ + let fe = p[le], Be = he[le]; + fe.update(Be, W, o); } O && O(V, W), m = null; } @@ -11218,8 +11218,8 @@ function qe(s = {}) { precision: 1 }; }); - } catch (S1) { - throw console.error("THREE.WebGLRenderer: " + S1.message), S1; + } catch (S) { + throw console.error("THREE.WebGLRenderer: " + S.message), S; } let ye, ge, xe, Oe, G, j, K, ue, se, Se, Te, Pe, Ye, C, T, J, $, re, Z, Me, ve, te, R; function ee() { @@ -11333,8 +11333,8 @@ function qe(s = {}) { } else q.isPoints ? je.setMode(0) : q.isSprite && je.setMode(4); if (q.isInstancedMesh) je.renderInstances(kt, Gt, q.count); else if (H.isInstancedBufferGeometry) { - let Zt1 = Math.min(H.instanceCount, H._maxInstanceCount); - je.renderInstances(kt, Gt, Zt1); + let Zt = Math.min(H.instanceCount, H._maxInstanceCount); + je.renderInstances(kt, Gt, Zt); } else je.render(kt, Gt); } }, this.compile = function(S, N) { @@ -11393,18 +11393,18 @@ function qe(s = {}) { } } else if ((S.isMesh || S.isLine || S.isPoints) && (S.isSkinnedMesh && S.skeleton.frame !== Oe.render.frame && (S.skeleton.update(), S.skeleton.frame = Oe.render.frame), !S.frustumCulled || ne.intersectsObject(S))) { z && le.setFromMatrixPosition(S.matrixWorld).applyMatrix4(he); - let Ae1 = Te.update(S), Ie1 = S.material; - if (Array.isArray(Ie1)) { - let we = Ae1.groups; + let Ae = Te.update(S), Ie = S.material; + if (Array.isArray(Ie)) { + let we = Ae.groups; for(let He = 0, De = we.length; He < De; He++){ - let ze = we[He], je = Ie1[ze.materialIndex]; - je && je.visible && u.push(S, Ae1, je, H, le.z, ze); + let ze = we[He], je = Ie[ze.materialIndex]; + je && je.visible && u.push(S, Ae, je, H, le.z, ze); } - } else Ie1.visible && u.push(S, Ae1, Ie1, H, le.z, null); + } else Ie.visible && u.push(S, Ae, Ie, H, le.z, null); } } let be = S.children; - for(let Ae2 = 0, Ie2 = be.length; Ae2 < Ie2; Ae2++)Qa(be[Ae2], N, H, z); + for(let Ae = 0, Ie = be.length; Ae < Ie; Ae++)Qa(be[Ae], N, H, z); } function Ka(S, N, H, z) { let q = S.opaque, be = S.transmissive, Ae = S.transparent; @@ -11469,15 +11469,15 @@ function qe(s = {}) { let Gr = !1, Gt = !1, Zt = !1, xt = kt.getUniforms(), Xi = Ge.uniforms; if (xe.useProgram(kt.program) && (Gr = !0, Gt = !0, Zt = !0), z.id !== y && (y = z.id, Gt = !0), Gr || b !== S) { if (xt.setValue(Y, "projectionMatrix", S.projectionMatrix), ge.logarithmicDepthBuffer && xt.setValue(Y, "logDepthBufFC", 2 / (Math.log(S.far + 1) / Math.LN2)), b !== S && (b = S, Gt = !0, Zt = !0), z.isShaderMaterial || z.isMeshPhongMaterial || z.isMeshToonMaterial || z.isMeshStandardMaterial || z.envMap) { - let Pt1 = xt.map.cameraPosition; - Pt1 !== void 0 && Pt1.setValue(Y, le.setFromMatrixPosition(S.matrixWorld)); + let Pt = xt.map.cameraPosition; + Pt !== void 0 && Pt.setValue(Y, le.setFromMatrixPosition(S.matrixWorld)); } (z.isMeshPhongMaterial || z.isMeshToonMaterial || z.isMeshLambertMaterial || z.isMeshBasicMaterial || z.isMeshStandardMaterial || z.isShaderMaterial) && xt.setValue(Y, "isOrthographic", S.isOrthographicCamera === !0), (z.isMeshPhongMaterial || z.isMeshToonMaterial || z.isMeshLambertMaterial || z.isMeshBasicMaterial || z.isMeshStandardMaterial || z.isShaderMaterial || z.isShadowMaterial || q.isSkinnedMesh) && xt.setValue(Y, "viewMatrix", S.matrixWorldInverse); } if (q.isSkinnedMesh) { xt.setOptional(Y, q, "bindMatrix"), xt.setOptional(Y, q, "bindMatrixInverse"); - let Pt2 = q.skeleton; - Pt2 && (ge.floatVertexTextures ? (Pt2.boneTexture === null && Pt2.computeBoneTexture(), xt.setValue(Y, "boneTexture", Pt2.boneTexture, j), xt.setValue(Y, "boneTextureSize", Pt2.boneTextureSize)) : xt.setOptional(Y, Pt2, "boneMatrices")); + let Pt = q.skeleton; + Pt && (ge.floatVertexTextures ? (Pt.boneTexture === null && Pt.computeBoneTexture(), xt.setValue(Y, "boneTexture", Pt.boneTexture, j), xt.setValue(Y, "boneTextureSize", Pt.boneTextureSize)) : xt.setOptional(Y, Pt, "boneMatrices")); } return !!H && (H.morphAttributes.position !== void 0 || H.morphAttributes.normal !== void 0) && Z.update(q, H, z, kt), (Gt || Ge.receiveShadow !== q.receiveShadow) && (Ge.receiveShadow = q.receiveShadow, xt.setValue(Y, "receiveShadow", q.receiveShadow)), Gt && (xt.setValue(Y, "toneMappingExposure", x.toneMappingExposure), Ge.needsLights && cu(Xi, Zt), be && z.fog && Ye.refreshFogUniforms(Xi, be), Ye.refreshMaterialUniforms(Xi, z, P, B, W), bn.upload(Y, Ge.uniformsList, Xi, j)), z.isShaderMaterial && z.uniformsNeedUpdate === !0 && (bn.upload(Y, Ge.uniformsList, Xi, j), z.uniformsNeedUpdate = !1), z.isSpriteMaterial && xt.setValue(Y, "center", q.center), xt.setValue(Y, "modelViewMatrix", q.modelViewMatrix), xt.setValue(Y, "normalMatrix", q.normalMatrix), xt.setValue(Y, "modelMatrix", q.matrixWorld), kt; } @@ -11509,29 +11509,29 @@ function qe(s = {}) { } let q = null, be = !1, Ae = !1; if (S) { - let we1 = S.texture; - (we1.isDataTexture3D || we1.isDataTexture2DArray) && (Ae = !0); + let we = S.texture; + (we.isDataTexture3D || we.isDataTexture2DArray) && (Ae = !0); let He = G.get(S).__webglFramebuffer; S.isWebGLCubeRenderTarget ? (q = He[N], be = !0) : S.useRenderbuffer ? q = G.get(S).__webglMultisampledFramebuffer : q = He, A.copy(S.viewport), L.copy(S.scissor), I = S.scissorTest; } else A.copy(D).multiplyScalar(P).floor(), L.copy(U).multiplyScalar(P).floor(), I = F; if (xe.bindFramebuffer(36160, q) && ge.drawBuffers && z) { - let we2 = !1; + let we = !1; if (S) if (S.isWebGLMultipleRenderTargets) { - let He1 = S.texture; - if (O.length !== He1.length || O[0] !== 36064) { - for(let De = 0, ze = He1.length; De < ze; De++)O[De] = 36064 + De; - O.length = He1.length, we2 = !0; + let He = S.texture; + if (O.length !== He.length || O[0] !== 36064) { + for(let De = 0, ze = He.length; De < ze; De++)O[De] = 36064 + De; + O.length = He.length, we = !0; } - } else (O.length !== 1 || O[0] !== 36064) && (O[0] = 36064, O.length = 1, we2 = !0); - else (O.length !== 1 || O[0] !== 1029) && (O[0] = 1029, O.length = 1, we2 = !0); - we2 && (ge.isWebGL2 ? Y.drawBuffers(O) : ye.get("WEBGL_draw_buffers").drawBuffersWEBGL(O)); + } else (O.length !== 1 || O[0] !== 36064) && (O[0] = 36064, O.length = 1, we = !0); + else (O.length !== 1 || O[0] !== 1029) && (O[0] = 1029, O.length = 1, we = !0); + we && (ge.isWebGL2 ? Y.drawBuffers(O) : ye.get("WEBGL_draw_buffers").drawBuffersWEBGL(O)); } if (xe.viewport(A), xe.scissor(L), xe.setScissorTest(I), be) { - let we3 = G.get(S.texture); - Y.framebufferTexture2D(36160, 36064, 34069 + N, we3.__webglTexture, H); + let we = G.get(S.texture); + Y.framebufferTexture2D(36160, 36064, 34069 + N, we.__webglTexture, H); } else if (Ae) { - let we4 = G.get(S.texture), He2 = N || 0; - Y.framebufferTextureLayer(36160, 36064, we4.__webglTexture, H || 0, He2); + let we = G.get(S.texture), He = N || 0; + Y.framebufferTextureLayer(36160, 36064, we.__webglTexture, H || 0, He); } y = -1; }, this.readRenderTargetPixels = function(S, N, H, z, q, be, Ae) { @@ -11555,8 +11555,8 @@ function qe(s = {}) { } Y.checkFramebufferStatus(36160) === 36053 ? N >= 0 && N <= S.width - z && H >= 0 && H <= S.height - q && Y.readPixels(N, H, z, q, te.convert(He), te.convert(De), be) : console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."); } finally{ - let we1 = _ !== null ? G.get(_).__webglFramebuffer : null; - xe.bindFramebuffer(36160, we1); + let we = _ !== null ? G.get(_).__webglFramebuffer : null; + xe.bindFramebuffer(36160, we); } } }, this.copyFramebufferToTexture = function(S, N, H = 0) { @@ -12009,9 +12009,9 @@ var rc = new pe, Px = new pe, ao = class { let n = this.bones[e]; n && n.matrixWorld.copy(this.boneInverses[e]).invert(); } - for(let e1 = 0, t1 = this.bones.length; e1 < t1; e1++){ - let n1 = this.bones[e1]; - n1 && (n1.parent && n1.parent.isBone ? (n1.matrix.copy(n1.parent.matrixWorld).invert(), n1.matrix.multiply(n1.matrixWorld)) : n1.matrix.copy(n1.matrixWorld), n1.matrix.decompose(n1.position, n1.quaternion, n1.scale)); + for(let e = 0, t = this.bones.length; e < t; e++){ + let n = this.bones[e]; + n && (n.parent && n.parent.isBone ? (n.matrix.copy(n.parent.matrixWorld).invert(), n.matrix.multiply(n.matrixWorld)) : n.matrix.copy(n.matrixWorld), n.matrix.decompose(n.position, n.quaternion, n.scale)); } } update() { @@ -12178,15 +12178,15 @@ var ac = new M, lc = new M, cc = new pe, Xo = new Cn, ms = new An, on = class ex }); } } else { - let g1 = Math.max(0, o.start), p1 = Math.min(v.count, o.start + o.count); - for(let _1 = g1, y1 = p1 - 1; _1 < y1; _1 += f){ - if (c.fromBufferAttribute(v, _1), h.fromBufferAttribute(v, _1 + 1), Xo.distanceSqToSegment(c, h, d, u) > l) continue; + let g = Math.max(0, o.start), p = Math.min(v.count, o.start + o.count); + for(let _ = g, y = p - 1; _ < y; _ += f){ + if (c.fromBufferAttribute(v, _), h.fromBufferAttribute(v, _ + 1), Xo.distanceSqToSegment(c, h, d, u) > l) continue; d.applyMatrix4(this.matrixWorld); - let A1 = e.ray.origin.distanceTo(d); - A1 < e.near || A1 > e.far || t.push({ - distance: A1, + let A = e.ray.origin.distanceTo(d); + A < e.near || A > e.far || t.push({ + distance: A, point: u.clone().applyMatrix4(this.matrixWorld), - index: _1, + index: _, face: null, faceIndex: null, object: this @@ -12210,8 +12210,8 @@ var ac = new M, lc = new M, cc = new pe, Xo = new Cn, ms = new An, on = class ex } } } else { - let t1 = e.morphTargets; - t1 !== void 0 && t1.length > 0 && console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); + let t = e.morphTargets; + t !== void 0 && t.length > 0 && console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } }; @@ -12272,8 +12272,8 @@ var dc = new pe, sa = new Cn, gs = new An, xs = new M, zr = class extends Ne { xs.fromBufferAttribute(u, v), fc(xs, v, l, i, e, t, this); } } else { - let d1 = Math.max(0, o.start), f1 = Math.min(u.count, o.start + o.count); - for(let m1 = d1, x1 = f1; m1 < x1; m1++)xs.fromBufferAttribute(u, m1), fc(xs, m1, l, i, e, t, this); + let d = Math.max(0, o.start), f = Math.min(u.count, o.start + o.count); + for(let m = d, x = f; m < x; m++)xs.fromBufferAttribute(u, m), fc(xs, m, l, i, e, t, this); } } else console.error("THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."); } @@ -12292,8 +12292,8 @@ var dc = new pe, sa = new Cn, gs = new An, xs = new M, zr = class extends Ne { } } } else { - let t1 = e.morphTargets; - t1 !== void 0 && t1.length > 0 && console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); + let t = e.morphTargets; + t !== void 0 && t.length > 0 && console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead."); } } }; @@ -12376,7 +12376,7 @@ var fr = class extends _e { let f = n + u / t * i; c.x = e * Math.cos(f), c.y = e * Math.sin(f), o.push(c.x, c.y, c.z), a.push(0, 0, 1), h.x = (o[d] / e + 1) / 2, h.y = (o[d + 1] / e + 1) / 2, l.push(h.x, h.y); } - for(let u1 = 1; u1 <= t; u1++)r.push(u1, u1 + 1, 0); + for(let u = 1; u <= t; u++)r.push(u, u + 1, 0); this.setIndex(r), this.setAttribute("position", new de(o, 3)), this.setAttribute("normal", new de(a, 3)), this.setAttribute("uv", new de(l, 2)); } static fromJSON(e) { @@ -12409,9 +12409,9 @@ var fr = class extends _e { } x.push(k); } - for(let I1 = 0; I1 < i; I1++)for(let k1 = 0; k1 < r; k1++){ - let B1 = x[k1][I1], P1 = x[k1 + 1][I1], w1 = x[k1 + 1][I1 + 1], E1 = x[k1][I1 + 1]; - h.push(B1, P1, E1), h.push(P1, w1, E1), A += 6; + for(let I = 0; I < i; I++)for(let k = 0; k < r; k++){ + let B = x[k][I], P = x[k + 1][I], w = x[k + 1][I + 1], E = x[k][I + 1]; + h.push(B, P, E), h.push(P, w, E), A += 6; } c.addGroup(g, A, 0), g += A; } @@ -12419,13 +12419,13 @@ var fr = class extends _e { let b = m, A = new X, L = new M, I = 0, k = y === !0 ? e : t, B = y === !0 ? 1 : -1; for(let w = 1; w <= i; w++)u.push(0, v * B, 0), d.push(0, B, 0), f.push(.5, .5), m++; let P = m; - for(let w1 = 0; w1 <= i; w1++){ - let D = w1 / i * l + a, U = Math.cos(D), F = Math.sin(D); + for(let w = 0; w <= i; w++){ + let D = w / i * l + a, U = Math.cos(D), F = Math.sin(D); L.x = k * F, L.y = v * B, L.z = k * U, u.push(L.x, L.y, L.z), d.push(0, B, 0), A.x = U * .5 + .5, A.y = F * .5 * B + .5, f.push(A.x, A.y), m++; } - for(let w2 = 0; w2 < i; w2++){ - let E = b + w2, D1 = P + w2; - y === !0 ? h.push(D1, D1 + 1, E) : h.push(D1 + 1, D1, E), I += 3; + for(let w = 0; w < i; w++){ + let E = b + w, D = P + w; + y === !0 ? h.push(D, D + 1, E) : h.push(D + 1, D, E), I += 3; } c.addGroup(g, I, y === !0 ? 1 : 2), g += I; } @@ -12471,9 +12471,9 @@ var fr = class extends _e { let k = p.clone().lerp(y, I / A), B = _.clone().lerp(y, I / A), P = A - I; for(let w = 0; w <= P; w++)w === 0 && I === A ? L[I][w] = k : L[I][w] = k.clone().lerp(B, w / P); } - for(let I1 = 0; I1 < A; I1++)for(let k1 = 0; k1 < 2 * (A - I1) - 1; k1++){ - let B1 = Math.floor(k1 / 2); - k1 % 2 === 0 ? (d(L[I1][B1 + 1]), d(L[I1 + 1][B1]), d(L[I1][B1])) : (d(L[I1][B1 + 1]), d(L[I1 + 1][B1 + 1]), d(L[I1 + 1][B1])); + for(let I = 0; I < A; I++)for(let k = 0; k < 2 * (A - I) - 1; k++){ + let B = Math.floor(k / 2); + k % 2 === 0 ? (d(L[I][B + 1]), d(L[I + 1][B]), d(L[I][B])) : (d(L[I][B + 1]), d(L[I + 1][B + 1]), d(L[I + 1][B])); } } function c(p) { @@ -12733,9 +12733,9 @@ var fr = class extends _e { }); } } - for(let m1 in d)if (d[m1]) { - let { index0: x1 , index1: v1 } = d[m1]; - ys.fromBufferAttribute(a, x1), vs.fromBufferAttribute(a, v1), f.push(ys.x, ys.y, ys.z), f.push(vs.x, vs.y, vs.z); + for(let m in d)if (d[m]) { + let { index0: x , index1: v } = d[m]; + ys.fromBufferAttribute(a, x), vs.fromBufferAttribute(a, v), f.push(ys.x, ys.y, ys.z), f.push(vs.x, vs.y, vs.z); } this.setAttribute("position", new de(f, 3)); } @@ -12809,18 +12809,18 @@ var fr = class extends _e { r[0] = new M, o[0] = new M; let c = Number.MAX_VALUE, h = Math.abs(i[0].x), u = Math.abs(i[0].y), d = Math.abs(i[0].z); h <= c && (c = h, n.set(1, 0, 0)), u <= c && (c = u, n.set(0, 1, 0)), d <= c && n.set(0, 0, 1), a.crossVectors(i[0], n).normalize(), r[0].crossVectors(i[0], a), o[0].crossVectors(i[0], r[0]); - for(let f1 = 1; f1 <= e; f1++){ - if (r[f1] = r[f1 - 1].clone(), o[f1] = o[f1 - 1].clone(), a.crossVectors(i[f1 - 1], i[f1]), a.length() > Number.EPSILON) { + for(let f = 1; f <= e; f++){ + if (r[f] = r[f - 1].clone(), o[f] = o[f - 1].clone(), a.crossVectors(i[f - 1], i[f]), a.length() > Number.EPSILON) { a.normalize(); - let m1 = Math.acos(mt(i[f1 - 1].dot(i[f1]), -1, 1)); - r[f1].applyMatrix4(l.makeRotationAxis(a, m1)); + let m = Math.acos(mt(i[f - 1].dot(i[f]), -1, 1)); + r[f].applyMatrix4(l.makeRotationAxis(a, m)); } - o[f1].crossVectors(i[f1], r[f1]); + o[f].crossVectors(i[f], r[f]); } if (t === !0) { - let f2 = Math.acos(mt(r[0].dot(r[e]), -1, 1)); - f2 /= e, i[0].dot(a.crossVectors(r[0], r[e])) > 0 && (f2 = -f2); - for(let m2 = 1; m2 <= e; m2++)r[m2].applyMatrix4(l.makeRotationAxis(i[m2], f2 * m2)), o[m2].crossVectors(i[m2], r[m2]); + let f = Math.acos(mt(r[0].dot(r[e]), -1, 1)); + f /= e, i[0].dot(a.crossVectors(r[0], r[e])) > 0 && (f = -f); + for(let m = 1; m <= e; m++)r[m].applyMatrix4(l.makeRotationAxis(i[m], f * m)), o[m].crossVectors(i[m], r[m]); } return { tangents: i, @@ -13580,7 +13580,7 @@ var Jt = class { t.forEach(gc); for(let l = 0; l < t.length; l++)i.push(o), o += t[l].length, xc(n, t[l]); let a = Ox.triangulate(n, i); - for(let l1 = 0; l1 < a.length; l1 += 3)r.push(a.slice(l1, l1 + 3)); + for(let l = 0; l < a.length; l += 3)r.push(a.slice(l, l + 3)); return r; } }; @@ -13625,9 +13625,9 @@ var ln = class extends _e { } } let E = Jt.triangulateShape(B, P), D = B; - for(let G1 = 0, j1 = P.length; G1 < j1; G1++){ - let K1 = P[G1]; - B = B.concat(K1); + for(let G = 0, j = P.length; G < j; G++){ + let K = P[G]; + B = B.concat(K); } function U(G, j, K) { return j || console.error("THREE.ExtrudeGeometry: vec does not exist"), j.clone().multiplyScalar(K).add(G); @@ -13642,56 +13642,56 @@ var ln = class extends _e { if (ee <= 2) return new X(ue, se); Se = Math.sqrt(ee / 2); } else { - let $1 = !1; - Te > Number.EPSILON ? Ye > Number.EPSILON && ($1 = !0) : Te < -Number.EPSILON ? Ye < -Number.EPSILON && ($1 = !0) : Math.sign(Pe) === Math.sign(C) && ($1 = !0), $1 ? (ue = -Pe, se = Te, Se = Math.sqrt(T)) : (ue = Te, se = Pe, Se = Math.sqrt(T / 2)); + let $ = !1; + Te > Number.EPSILON ? Ye > Number.EPSILON && ($ = !0) : Te < -Number.EPSILON ? Ye < -Number.EPSILON && ($ = !0) : Math.sign(Pe) === Math.sign(C) && ($ = !0), $ ? (ue = -Pe, se = Te, Se = Math.sqrt(T)) : (ue = Te, se = Pe, Se = Math.sqrt(T / 2)); } return new X(ue / Se, se / Se); } let ce = []; - for(let G2 = 0, j2 = D.length, K2 = j2 - 1, ue = G2 + 1; G2 < j2; G2++, K2++, ue++)K2 === j2 && (K2 = 0), ue === j2 && (ue = 0), ce[G2] = ne(D[G2], D[K2], D[ue]); + for(let G = 0, j = D.length, K = j - 1, ue = G + 1; G < j; G++, K++, ue++)K === j && (K = 0), ue === j && (ue = 0), ce[G] = ne(D[G], D[K], D[ue]); let V = [], W, he = ce.concat(); - for(let G3 = 0, j3 = P.length; G3 < j3; G3++){ - let K3 = P[G3]; + for(let G = 0, j = P.length; G < j; G++){ + let K = P[G]; W = []; - for(let ue1 = 0, se = K3.length, Se = se - 1, Te = ue1 + 1; ue1 < se; ue1++, Se++, Te++)Se === se && (Se = 0), Te === se && (Te = 0), W[ue1] = ne(K3[ue1], K3[Se], K3[Te]); + for(let ue = 0, se = K.length, Se = se - 1, Te = ue + 1; ue < se; ue++, Se++, Te++)Se === se && (Se = 0), Te === se && (Te = 0), W[ue] = ne(K[ue], K[Se], K[Te]); V.push(W), he = he.concat(W); } - for(let G4 = 0; G4 < v; G4++){ - let j4 = G4 / v, K4 = f * Math.cos(j4 * Math.PI / 2), ue2 = m * Math.sin(j4 * Math.PI / 2) + x; - for(let se1 = 0, Se1 = D.length; se1 < Se1; se1++){ - let Te1 = U(D[se1], ce[se1], ue2); - Ce(Te1.x, Te1.y, -K4); + for(let G = 0; G < v; G++){ + let j = G / v, K = f * Math.cos(j * Math.PI / 2), ue = m * Math.sin(j * Math.PI / 2) + x; + for(let se = 0, Se = D.length; se < Se; se++){ + let Te = U(D[se], ce[se], ue); + Ce(Te.x, Te.y, -K); } - for(let se2 = 0, Se2 = P.length; se2 < Se2; se2++){ - let Te2 = P[se2]; - W = V[se2]; - for(let Pe = 0, Ye = Te2.length; Pe < Ye; Pe++){ - let C = U(Te2[Pe], W[Pe], ue2); - Ce(C.x, C.y, -K4); + for(let se = 0, Se = P.length; se < Se; se++){ + let Te = P[se]; + W = V[se]; + for(let Pe = 0, Ye = Te.length; Pe < Ye; Pe++){ + let C = U(Te[Pe], W[Pe], ue); + Ce(C.x, C.y, -K); } } } let le = m + x; - for(let G5 = 0; G5 < F; G5++){ - let j5 = d ? U(B[G5], he[G5], le) : B[G5]; - y ? (L.copy(b.normals[0]).multiplyScalar(j5.x), A.copy(b.binormals[0]).multiplyScalar(j5.y), I.copy(_[0]).add(L).add(A), Ce(I.x, I.y, I.z)) : Ce(j5.x, j5.y, 0); + for(let G = 0; G < F; G++){ + let j = d ? U(B[G], he[G], le) : B[G]; + y ? (L.copy(b.normals[0]).multiplyScalar(j.x), A.copy(b.binormals[0]).multiplyScalar(j.y), I.copy(_[0]).add(L).add(A), Ce(I.x, I.y, I.z)) : Ce(j.x, j.y, 0); } - for(let G6 = 1; G6 <= h; G6++)for(let j6 = 0; j6 < F; j6++){ - let K5 = d ? U(B[j6], he[j6], le) : B[j6]; - y ? (L.copy(b.normals[G6]).multiplyScalar(K5.x), A.copy(b.binormals[G6]).multiplyScalar(K5.y), I.copy(_[G6]).add(L).add(A), Ce(I.x, I.y, I.z)) : Ce(K5.x, K5.y, u / h * G6); + for(let G = 1; G <= h; G++)for(let j = 0; j < F; j++){ + let K = d ? U(B[j], he[j], le) : B[j]; + y ? (L.copy(b.normals[G]).multiplyScalar(K.x), A.copy(b.binormals[G]).multiplyScalar(K.y), I.copy(_[G]).add(L).add(A), Ce(I.x, I.y, I.z)) : Ce(K.x, K.y, u / h * G); } - for(let G7 = v - 1; G7 >= 0; G7--){ - let j7 = G7 / v, K6 = f * Math.cos(j7 * Math.PI / 2), ue3 = m * Math.sin(j7 * Math.PI / 2) + x; - for(let se3 = 0, Se3 = D.length; se3 < Se3; se3++){ - let Te3 = U(D[se3], ce[se3], ue3); - Ce(Te3.x, Te3.y, u + K6); + for(let G = v - 1; G >= 0; G--){ + let j = G / v, K = f * Math.cos(j * Math.PI / 2), ue = m * Math.sin(j * Math.PI / 2) + x; + for(let se = 0, Se = D.length; se < Se; se++){ + let Te = U(D[se], ce[se], ue); + Ce(Te.x, Te.y, u + K); } - for(let se4 = 0, Se4 = P.length; se4 < Se4; se4++){ - let Te4 = P[se4]; - W = V[se4]; - for(let Pe1 = 0, Ye1 = Te4.length; Pe1 < Ye1; Pe1++){ - let C1 = U(Te4[Pe1], W[Pe1], ue3); - y ? Ce(C1.x, C1.y + _[h - 1].y, _[h - 1].x + K6) : Ce(C1.x, C1.y, u + K6); + for(let se = 0, Se = P.length; se < Se; se++){ + let Te = P[se]; + W = V[se]; + for(let Pe = 0, Ye = Te.length; Pe < Ye; Pe++){ + let C = U(Te[Pe], W[Pe], ue); + y ? Ce(C.x, C.y + _[h - 1].y, _[h - 1].x + K) : Ce(C.x, C.y, u + K); } } } @@ -13705,18 +13705,18 @@ var ln = class extends _e { ye(se[2] + K, se[1] + K, se[0] + K); } j = h + v * 2, K = F * j; - for(let ue1 = 0; ue1 < O; ue1++){ - let se1 = E[ue1]; - ye(se1[0] + K, se1[1] + K, se1[2] + K); + for(let ue = 0; ue < O; ue++){ + let se = E[ue]; + ye(se[0] + K, se[1] + K, se[2] + K); } } else { - for(let j1 = 0; j1 < O; j1++){ - let K1 = E[j1]; - ye(K1[2], K1[1], K1[0]); + for(let j = 0; j < O; j++){ + let K = E[j]; + ye(K[2], K[1], K[0]); } - for(let j2 = 0; j2 < O; j2++){ - let K2 = E[j2]; - ye(K2[0] + F * h, K2[1] + F * h, K2[2] + F * h); + for(let j = 0; j < O; j++){ + let K = E[j]; + ye(K[0] + F * h, K[1] + F * h, K[2] + F * h); } } n.addGroup(G, i.length / 3 - G, 0); @@ -13941,17 +13941,17 @@ var _r = class extends an { default: v = e[p + 1].x - e[p].x, g = e[p + 1].y - e[p].y, f.x = g * 1, f.y = -v, f.z = g * 0, m.copy(f), f.x += x.x, f.y += x.y, f.z += x.z, f.normalize(), l.push(f.x, f.y, f.z), x.copy(m); } - for(let p1 = 0; p1 <= t; p1++){ - let _ = n + p1 * h * i, y = Math.sin(_), b = Math.cos(_); + for(let p = 0; p <= t; p++){ + let _ = n + p * h * i, y = Math.sin(_), b = Math.cos(_); for(let A = 0; A <= e.length - 1; A++){ - u.x = e[A].x * y, u.y = e[A].y, u.z = e[A].x * b, o.push(u.x, u.y, u.z), d.x = p1 / t, d.y = A / (e.length - 1), a.push(d.x, d.y); + u.x = e[A].x * y, u.y = e[A].y, u.z = e[A].x * b, o.push(u.x, u.y, u.z), d.x = p / t, d.y = A / (e.length - 1), a.push(d.x, d.y); let L = l[3 * A + 0] * y, I = l[3 * A + 1], k = l[3 * A + 0] * b; c.push(L, I, k); } } - for(let p2 = 0; p2 < t; p2++)for(let _1 = 0; _1 < e.length - 1; _1++){ - let y1 = _1 + p2 * e.length, b1 = y1, A1 = y1 + e.length, L1 = y1 + e.length + 1, I1 = y1 + 1; - r.push(b1, A1, I1), r.push(A1, L1, I1); + for(let p = 0; p < t; p++)for(let _ = 0; _ < e.length - 1; _++){ + let y = _ + p * e.length, b = y, A = y + e.length, L = y + e.length + 1, I = y + 1; + r.push(b, A, I), r.push(A, L, I); } this.setIndex(r), this.setAttribute("position", new de(o, 3)), this.setAttribute("uv", new de(a, 2)), this.setAttribute("normal", new de(c, 3)); } @@ -14033,10 +14033,10 @@ var _r = class extends an { } u += d; } - for(let x1 = 0; x1 < i; x1++){ - let v1 = x1 * (n + 1); - for(let g1 = 0; g1 < n; g1++){ - let p = g1 + v1, _ = p, y = p + n + 1, b = p + n + 2, A = p + 1; + for(let x = 0; x < i; x++){ + let v = x * (n + 1); + for(let g = 0; g < n; g++){ + let p = g + v, _ = p, y = p + n + 1, b = p + n + 2, A = p + 1; a.push(_, y, A), a.push(y, b, A); } } @@ -14068,16 +14068,16 @@ var _r = class extends an { Jt.isClockWise(p) === !0 && (m[v] = p.reverse()); } let x = Jt.triangulateShape(f, m); - for(let v1 = 0, g1 = m.length; v1 < g1; v1++){ - let p1 = m[v1]; - f = f.concat(p1); + for(let v = 0, g = m.length; v < g; v++){ + let p = m[v]; + f = f.concat(p); } - for(let v2 = 0, g2 = f.length; v2 < g2; v2++){ - let p2 = f[v2]; - i.push(p2.x, p2.y, 0), r.push(0, 0, 1), o.push(p2.x, p2.y); + for(let v = 0, g = f.length; v < g; v++){ + let p = f[v]; + i.push(p.x, p.y, 0), r.push(0, 0, 1), o.push(p.x, p.y); } - for(let v3 = 0, g3 = x.length; v3 < g3; v3++){ - let p3 = x[v3], _ = p3[0] + u, y = p3[1] + u, b = p3[2] + u; + for(let v = 0, g = x.length; v < g; v++){ + let p = x[v], _ = p[0] + u, y = p[1] + u, b = p[2] + u; n.push(_, y, b), l += 3; } } @@ -14125,9 +14125,9 @@ var Fi = class extends _e { } h.push(p); } - for(let g1 = 0; g1 < n; g1++)for(let p1 = 0; p1 < t; p1++){ - let _1 = h[g1][p1 + 1], y1 = h[g1][p1], b1 = h[g1 + 1][p1], A1 = h[g1 + 1][p1 + 1]; - (g1 !== 0 || o > 0) && f.push(_1, y1, A1), (g1 !== n - 1 || l < Math.PI) && f.push(y1, b1, A1); + for(let g = 0; g < n; g++)for(let p = 0; p < t; p++){ + let _ = h[g][p + 1], y = h[g][p], b = h[g + 1][p], A = h[g + 1][p + 1]; + (g !== 0 || o > 0) && f.push(_, y, A), (g !== n - 1 || l < Math.PI) && f.push(y, b, A); } this.setIndex(f), this.setAttribute("position", new de(m, 3)), this.setAttribute("normal", new de(x, 3)), this.setAttribute("uv", new de(v, 2)); } @@ -14187,9 +14187,9 @@ var Fi = class extends _e { let x = m / i * r, v = f / n * Math.PI * 2; u.x = (e + t * Math.cos(v)) * Math.cos(x), u.y = (e + t * Math.cos(v)) * Math.sin(x), u.z = t * Math.sin(v), a.push(u.x, u.y, u.z), h.x = e * Math.cos(x), h.y = e * Math.sin(x), d.subVectors(u, h).normalize(), l.push(d.x, d.y, d.z), c.push(m / i), c.push(f / n); } - for(let f1 = 1; f1 <= n; f1++)for(let m1 = 1; m1 <= i; m1++){ - let x1 = (i + 1) * f1 + m1 - 1, v1 = (i + 1) * (f1 - 1) + m1 - 1, g = (i + 1) * (f1 - 1) + m1, p = (i + 1) * f1 + m1; - o.push(x1, v1, p), o.push(v1, g, p); + for(let f = 1; f <= n; f++)for(let m = 1; m <= i; m++){ + let x = (i + 1) * f + m - 1, v = (i + 1) * (f - 1) + m - 1, g = (i + 1) * (f - 1) + m, p = (i + 1) * f + m; + o.push(x, v, p), o.push(v, g, p); } this.setIndex(o), this.setAttribute("position", new de(a, 3)), this.setAttribute("normal", new de(l, 3)), this.setAttribute("uv", new de(c, 2)); } @@ -14216,9 +14216,9 @@ var Fi = class extends _e { u.x = f.x + (L * g.x + I * x.x), u.y = f.y + (L * g.y + I * x.y), u.z = f.z + (L * g.z + I * x.z), l.push(u.x, u.y, u.z), d.subVectors(u, f).normalize(), c.push(d.x, d.y, d.z), h.push(_ / n), h.push(b / i); } } - for(let _1 = 1; _1 <= n; _1++)for(let y1 = 1; y1 <= i; y1++){ - let b1 = (i + 1) * (_1 - 1) + (y1 - 1), A1 = (i + 1) * _1 + (y1 - 1), L1 = (i + 1) * _1 + y1, I1 = (i + 1) * (_1 - 1) + y1; - a.push(b1, A1, I1), a.push(A1, L1, I1); + for(let _ = 1; _ <= n; _++)for(let y = 1; y <= i; y++){ + let b = (i + 1) * (_ - 1) + (y - 1), A = (i + 1) * _ + (y - 1), L = (i + 1) * _ + y, I = (i + 1) * (_ - 1) + y; + a.push(b, A, I), a.push(A, L, I); } this.setIndex(a), this.setAttribute("position", new de(l, 3)), this.setAttribute("normal", new de(c, 3)), this.setAttribute("uv", new de(h, 2)); function p(_, y, b, A, L) { @@ -14296,10 +14296,10 @@ var Fi = class extends _e { } } } else { - let o1 = e.attributes.position; - for(let a1 = 0, l1 = o1.count / 3; a1 < l1; a1++)for(let c1 = 0; c1 < 3; c1++){ - let h1 = 3 * a1 + c1, u1 = 3 * a1 + (c1 + 1) % 3; - i.fromBufferAttribute(o1, h1), r.fromBufferAttribute(o1, u1), yc(i, r, n) === !0 && (t.push(i.x, i.y, i.z), t.push(r.x, r.y, r.z)); + let o = e.attributes.position; + for(let a = 0, l = o.count / 3; a < l; a++)for(let c = 0; c < 3; c++){ + let h = 3 * a + c, u = 3 * a + (c + 1) % 3; + i.fromBufferAttribute(o, h), r.fromBufferAttribute(o, u), yc(i, r, n) === !0 && (t.push(i.x, i.y, i.z), t.push(r.x, r.y, r.z)); } } this.setAttribute("position", new de(t, 3)); @@ -14556,8 +14556,8 @@ var sy = Object.freeze({ } r.tracks = o; let a = 1 / 0; - for(let l1 = 0; l1 < r.tracks.length; ++l1)a > r.tracks[l1].times[0] && (a = r.tracks[l1].times[0]); - for(let l2 = 0; l2 < r.tracks.length; ++l2)r.tracks[l2].shift(-1 * a); + for(let l = 0; l < r.tracks.length; ++l)a > r.tracks[l].times[0] && (a = r.tracks[l].times[0]); + for(let l = 0; l < r.tracks.length; ++l)r.tracks[l].shift(-1 * a); return r.resetDuration(), r; }, makeClipAdditive: function(s, e = 0, t = s, n = 30) { @@ -14579,20 +14579,20 @@ var sy = Object.freeze({ let g = h, p = u - h; x = Ze.arraySlice(a.values, g, p); } else if (r >= a.times[m]) { - let g1 = m * u + h, p1 = g1 + u - h; - x = Ze.arraySlice(a.values, g1, p1); + let g = m * u + h, p = g + u - h; + x = Ze.arraySlice(a.values, g, p); } else { - let g2 = a.createInterpolant(), p2 = h, _ = u - h; - g2.evaluate(r), x = Ze.arraySlice(g2.resultBuffer, p2, _); + let g = a.createInterpolant(), p = h, _ = u - h; + g.evaluate(r), x = Ze.arraySlice(g.resultBuffer, p, _); } l === "quaternion" && new gt().fromArray(x).normalize().conjugate().toArray(x); let v = c.times.length; - for(let g3 = 0; g3 < v; ++g3){ - let p3 = g3 * f + d; - if (l === "quaternion") gt.multiplyQuaternionsFlat(c.values, p3, x, 0, c.values, p3); + for(let g = 0; g < v; ++g){ + let p = g * f + d; + if (l === "quaternion") gt.multiplyQuaternionsFlat(c.values, p, x, 0, c.values, p); else { - let _1 = f - d * 2; - for(let y = 0; y < _1; ++y)c.values[p3 + y] -= x[y]; + let _ = f - d * 2; + for(let y = 0; y < _; ++y)c.values[p + y] -= x[y]; } } } @@ -14621,8 +14621,8 @@ var sy = Object.freeze({ break n; } if (!(e >= r)) { - let a1 = t[1]; - e < a1 && (n = 2, r = a1); + let a = t[1]; + e < a && (n = 2, r = a); for(let l = n - 2;;){ if (r === void 0) return this._cachedIndex = 0, this.beforeStart_(0, e, i); if (n === l) break; @@ -14634,8 +14634,8 @@ var sy = Object.freeze({ break e; } for(; n < o;){ - let a2 = n + o >>> 1; - e < t[a2] ? o = a2 : n = a2 + 1; + let a = n + o >>> 1; + e < t[a] ? o = a : n = a + 1; } if (i = t[n], r = t[n - 1], r === void 0) return this._cachedIndex = 0, this.beforeStart_(0, e, i); if (i === void 0) return n = t.length, this._cachedIndex = n, this.afterEnd_(n - 1, r, e); @@ -14819,10 +14819,10 @@ var Ph = class extends cn { } o = l; } - if (i !== void 0 && Ze.isTypedArray(i)) for(let a1 = 0, l1 = i.length; a1 !== l1; ++a1){ - let c = i[a1]; + if (i !== void 0 && Ze.isTypedArray(i)) for(let a = 0, l = i.length; a !== l; ++a){ + let c = i[a]; if (isNaN(c)) { - console.error("THREE.KeyframeTrack: Value is not a valid number.", this, a1, c), e = !1; + console.error("THREE.KeyframeTrack: Value is not a valid number.", this, a, c), e = !1; break; } } @@ -14846,15 +14846,15 @@ var Ph = class extends cn { if (l) { if (a !== o) { e[o] = e[a]; - let u1 = a * n, d1 = o * n; - for(let f1 = 0; f1 !== n; ++f1)t[d1 + f1] = t[u1 + f1]; + let u = a * n, d = o * n; + for(let f = 0; f !== n; ++f)t[d + f] = t[u + f]; } ++o; } } if (r > 0) { e[o] = e[r]; - for(let a1 = r * n, l1 = o * n, c1 = 0; c1 !== n; ++c1)t[l1 + c1] = t[a1 + c1]; + for(let a = r * n, l = o * n, c = 0; c !== n; ++c)t[l + c] = t[a + c]; ++o; } return o !== e.length ? (this.times = Ze.arraySlice(e, 0, o), this.values = Ze.arraySlice(t, 0, o * n)) : (this.times = e, this.values = t), this; @@ -14944,7 +14944,7 @@ var Lr = class { let i = e; n = i.geometry && i.geometry.animations || i.animations; } - for(let i1 = 0; i1 < n.length; i1++)if (n[i1].name === t) return n[i1]; + for(let i = 0; i < n.length; i++)if (n[i].name === t) return n[i]; return null; } static CreateClipsFromMorphTargetSequences(e, t, n) { @@ -14957,7 +14957,7 @@ var Lr = class { } } let o = []; - for(let a1 in i)o.push(this.CreateFromMorphTargetSequence(a1, i[a1], t, n)); + for(let a in i)o.push(this.CreateFromMorphTargetSequence(a, i[a], t, n)); return o; } static parseAnimation(e, t) { @@ -14973,18 +14973,18 @@ var Lr = class { if (!(!d || d.length === 0)) if (d[0].morphTargets) { let f = {}, m; for(m = 0; m < d.length; m++)if (d[m].morphTargets) for(let x = 0; x < d[m].morphTargets.length; x++)f[d[m].morphTargets[x]] = -1; - for(let x1 in f){ + for(let x in f){ let v = [], g = []; for(let p = 0; p !== d[m].morphTargets.length; ++p){ let _ = d[m]; - v.push(_.time), g.push(_.morphTarget === x1 ? 1 : 0); + v.push(_.time), g.push(_.morphTarget === x ? 1 : 0); } - i.push(new Ar(".morphTargetInfluence[" + x1 + "]", v, g)); + i.push(new Ar(".morphTargetInfluence[" + x + "]", v, g)); } l = f.length * (o || 1); } else { - let f1 = ".bones[" + t[u].name + "]"; - n(Cr, f1 + ".position", d, "pos", i), n(Wi, f1 + ".quaternion", d, "rot", i), n(Cr, f1 + ".scale", d, "scl", i); + let f = ".bones[" + t[u].name + "]"; + n(Cr, f + ".position", d, "pos", i), n(Wi, f + ".quaternion", d, "rot", i), n(Cr, f + ".scale", d, "scl", i); } } return i.length === 0 ? null : new this(r, l, i, a); @@ -15652,13 +15652,13 @@ var zh = class extends bt { i.uniforms[r].value = o.value; } } - if (e.defines !== void 0 && (i.defines = e.defines), e.vertexShader !== void 0 && (i.vertexShader = e.vertexShader), e.fragmentShader !== void 0 && (i.fragmentShader = e.fragmentShader), e.extensions !== void 0) for(let r1 in e.extensions)i.extensions[r1] = e.extensions[r1]; + if (e.defines !== void 0 && (i.defines = e.defines), e.vertexShader !== void 0 && (i.vertexShader = e.vertexShader), e.fragmentShader !== void 0 && (i.fragmentShader = e.fragmentShader), e.extensions !== void 0) for(let r in e.extensions)i.extensions[r] = e.extensions[r]; if (e.shading !== void 0 && (i.flatShading = e.shading === 1), e.size !== void 0 && (i.size = e.size), e.sizeAttenuation !== void 0 && (i.sizeAttenuation = e.sizeAttenuation), e.map !== void 0 && (i.map = n(e.map)), e.matcap !== void 0 && (i.matcap = n(e.matcap)), e.alphaMap !== void 0 && (i.alphaMap = n(e.alphaMap)), e.bumpMap !== void 0 && (i.bumpMap = n(e.bumpMap)), e.bumpScale !== void 0 && (i.bumpScale = e.bumpScale), e.normalMap !== void 0 && (i.normalMap = n(e.normalMap)), e.normalMapType !== void 0 && (i.normalMapType = e.normalMapType), e.normalScale !== void 0) { - let r2 = e.normalScale; - Array.isArray(r2) === !1 && (r2 = [ - r2, - r2 - ]), i.normalScale = new X().fromArray(r2); + let r = e.normalScale; + Array.isArray(r) === !1 && (r = [ + r, + r + ]), i.normalScale = new X().fromArray(r); } return e.displacementMap !== void 0 && (i.displacementMap = n(e.displacementMap)), e.displacementScale !== void 0 && (i.displacementScale = e.displacementScale), e.displacementBias !== void 0 && (i.displacementBias = e.displacementBias), e.roughnessMap !== void 0 && (i.roughnessMap = n(e.roughnessMap)), e.metalnessMap !== void 0 && (i.metalnessMap = n(e.metalnessMap)), e.emissiveMap !== void 0 && (i.emissiveMap = n(e.emissiveMap)), e.emissiveIntensity !== void 0 && (i.emissiveIntensity = e.emissiveIntensity), e.specularMap !== void 0 && (i.specularMap = n(e.specularMap)), e.specularIntensityMap !== void 0 && (i.specularIntensityMap = n(e.specularIntensityMap)), e.specularColorMap !== void 0 && (i.specularColorMap = n(e.specularColorMap)), e.envMap !== void 0 && (i.envMap = n(e.envMap)), e.envMapIntensity !== void 0 && (i.envMapIntensity = e.envMapIntensity), e.reflectivity !== void 0 && (i.reflectivity = e.reflectivity), e.refractionRatio !== void 0 && (i.refractionRatio = e.refractionRatio), e.lightMap !== void 0 && (i.lightMap = n(e.lightMap)), e.lightMapIntensity !== void 0 && (i.lightMapIntensity = e.lightMapIntensity), e.aoMap !== void 0 && (i.aoMap = n(e.aoMap)), e.aoMapIntensity !== void 0 && (i.aoMapIntensity = e.aoMapIntensity), e.gradientMap !== void 0 && (i.gradientMap = n(e.gradientMap)), e.clearcoatMap !== void 0 && (i.clearcoatMap = n(e.clearcoatMap)), e.clearcoatRoughnessMap !== void 0 && (i.clearcoatRoughnessMap = n(e.clearcoatRoughnessMap)), e.clearcoatNormalMap !== void 0 && (i.clearcoatNormalMap = n(e.clearcoatNormalMap)), e.clearcoatNormalScale !== void 0 && (i.clearcoatNormalScale = new X().fromArray(e.clearcoatNormalScale)), e.transmissionMap !== void 0 && (i.transmissionMap = n(e.transmissionMap)), e.thicknessMap !== void 0 && (i.thicknessMap = n(e.thicknessMap)), e.sheenColorMap !== void 0 && (i.sheenColorMap = n(e.sheenColorMap)), e.sheenRoughnessMap !== void 0 && (i.sheenRoughnessMap = n(e.sheenRoughnessMap)), i; } @@ -15732,43 +15732,43 @@ var Uh = class extends bt { o.setIndex(new Ue(f, 1)); } let l = e.data.attributes; - for(let f1 in l){ - let m = l[f1], x; + for(let f in l){ + let m = l[f], x; if (m.isInterleavedBufferAttribute) { let v = i(e.data, m.data); x = new Sn(v, m.itemSize, m.offset, m.normalized); } else { - let v1 = wi(m.type, m.array), g = m.isInstancedBufferAttribute ? Xn : Ue; - x = new g(v1, m.itemSize, m.normalized); + let v = wi(m.type, m.array), g = m.isInstancedBufferAttribute ? Xn : Ue; + x = new g(v, m.itemSize, m.normalized); } - m.name !== void 0 && (x.name = m.name), m.usage !== void 0 && x.setUsage(m.usage), m.updateRange !== void 0 && (x.updateRange.offset = m.updateRange.offset, x.updateRange.count = m.updateRange.count), o.setAttribute(f1, x); + m.name !== void 0 && (x.name = m.name), m.usage !== void 0 && x.setUsage(m.usage), m.updateRange !== void 0 && (x.updateRange.offset = m.updateRange.offset, x.updateRange.count = m.updateRange.count), o.setAttribute(f, x); } let c = e.data.morphAttributes; - if (c) for(let f2 in c){ - let m1 = c[f2], x1 = []; - for(let v2 = 0, g1 = m1.length; v2 < g1; v2++){ - let p = m1[v2], _; + if (c) for(let f in c){ + let m = c[f], x = []; + for(let v = 0, g = m.length; v < g; v++){ + let p = m[v], _; if (p.isInterleavedBufferAttribute) { let y = i(e.data, p.data); _ = new Sn(y, p.itemSize, p.offset, p.normalized); } else { - let y1 = wi(p.type, p.array); - _ = new Ue(y1, p.itemSize, p.normalized); + let y = wi(p.type, p.array); + _ = new Ue(y, p.itemSize, p.normalized); } - p.name !== void 0 && (_.name = p.name), x1.push(_); + p.name !== void 0 && (_.name = p.name), x.push(_); } - o.morphAttributes[f2] = x1; + o.morphAttributes[f] = x; } e.data.morphTargetsRelative && (o.morphTargetsRelative = !0); let u = e.data.groups || e.data.drawcalls || e.data.offsets; - if (u !== void 0) for(let f3 = 0, m2 = u.length; f3 !== m2; ++f3){ - let x2 = u[f3]; - o.addGroup(x2.start, x2.count, x2.materialIndex); + if (u !== void 0) for(let f = 0, m = u.length; f !== m; ++f){ + let x = u[f]; + o.addGroup(x.start, x.count, x.materialIndex); } let d = e.data.boundingSphere; if (d !== void 0) { - let f4 = new M; - d.center !== void 0 && f4.fromArray(d.center), o.boundingSphere = new An(f4, d.radius); + let f = new M; + d.center !== void 0 && f.fromArray(d.center), o.boundingSphere = new An(f, d.radius); } return e.name && (o.name = e.name), e.userData && (o.userData = e.userData), o; } @@ -15921,8 +15921,8 @@ var Uh = class extends bt { v !== null && (v instanceof HTMLImageElement ? i[u.uuid].push(v) : i[u.uuid].push(new qn(v.data, v.width, v.height))); } } else { - let f1 = a(u.url); - f1 !== null && (i[u.uuid] = f1); + let f = a(u.url); + f !== null && (i[u.uuid] = f); } } } @@ -15951,8 +15951,8 @@ var Uh = class extends bt { f !== null && (f instanceof HTMLImageElement ? n[l.uuid].push(f) : n[l.uuid].push(new qn(f.data, f.width, f.height))); } } else { - let h1 = await r(l.url); - h1 !== null && (n[l.uuid] = h1); + let h = await r(l.url); + h !== null && (n[l.uuid] = h); } } } @@ -16064,22 +16064,22 @@ var Uh = class extends bt { o = new Ne; } if (o.uuid = e.uuid, e.name !== void 0 && (o.name = e.name), e.matrix !== void 0 ? (o.matrix.fromArray(e.matrix), e.matrixAutoUpdate !== void 0 && (o.matrixAutoUpdate = e.matrixAutoUpdate), o.matrixAutoUpdate && o.matrix.decompose(o.position, o.quaternion, o.scale)) : (e.position !== void 0 && o.position.fromArray(e.position), e.rotation !== void 0 && o.rotation.fromArray(e.rotation), e.quaternion !== void 0 && o.quaternion.fromArray(e.quaternion), e.scale !== void 0 && o.scale.fromArray(e.scale)), e.castShadow !== void 0 && (o.castShadow = e.castShadow), e.receiveShadow !== void 0 && (o.receiveShadow = e.receiveShadow), e.shadow && (e.shadow.bias !== void 0 && (o.shadow.bias = e.shadow.bias), e.shadow.normalBias !== void 0 && (o.shadow.normalBias = e.shadow.normalBias), e.shadow.radius !== void 0 && (o.shadow.radius = e.shadow.radius), e.shadow.mapSize !== void 0 && o.shadow.mapSize.fromArray(e.shadow.mapSize), e.shadow.camera !== void 0 && (o.shadow.camera = this.parseObject(e.shadow.camera))), e.visible !== void 0 && (o.visible = e.visible), e.frustumCulled !== void 0 && (o.frustumCulled = e.frustumCulled), e.renderOrder !== void 0 && (o.renderOrder = e.renderOrder), e.userData !== void 0 && (o.userData = e.userData), e.layers !== void 0 && (o.layers.mask = e.layers), e.children !== void 0) { - let d1 = e.children; - for(let f1 = 0; f1 < d1.length; f1++)o.add(this.parseObject(d1[f1], t, n, i, r)); + let d = e.children; + for(let f = 0; f < d.length; f++)o.add(this.parseObject(d[f], t, n, i, r)); } if (e.animations !== void 0) { - let d2 = e.animations; - for(let f2 = 0; f2 < d2.length; f2++){ - let m1 = d2[f2]; - o.animations.push(r[m1]); + let d = e.animations; + for(let f = 0; f < d.length; f++){ + let m = d[f]; + o.animations.push(r[m]); } } if (e.type === "LOD") { e.autoUpdate !== void 0 && (o.autoUpdate = e.autoUpdate); - let d3 = e.levels; - for(let f3 = 0; f3 < d3.length; f3++){ - let m2 = d3[f3], x = o.getObjectByProperty("uuid", m2.object); - x !== void 0 && o.addLevel(x, m2.distance); + let d = e.levels; + for(let f = 0; f < d.length; f++){ + let m = d[f], x = o.getObjectByProperty("uuid", m.object); + x !== void 0 && o.addLevel(x, m.distance); } } return o; @@ -16164,8 +16164,8 @@ var Ss, Hh = { Hh.getContext().decodeAudioData(l, function(h) { t(h); }); - } catch (l1) { - i ? i(l1) : console.error(l1), r.manager.itemError(e); + } catch (l) { + i ? i(l) : console.error(l), r.manager.itemError(e); } }, n, i); } @@ -16461,8 +16461,8 @@ var Nn = new M, Lc = new gt, py = new M, Bn = new M, my = class extends Ne { o = t; } else { o += t; - let a1 = t / o; - this._mixBufferRegion(n, r, 0, a1, i); + let a = t / o; + this._mixBufferRegion(n, r, 0, a, i); } this.cumulativeWeight = o; } @@ -16477,7 +16477,7 @@ var Nn = new M, Lc = new gt, py = new M, Bn = new M, my = class extends Ne { this._mixBufferRegion(n, i, l, 1 - r, t); } o > 0 && this._mixBufferRegionAdditive(n, i, this._addIndex * t, 1, t); - for(let l1 = t, c = t + t; l1 !== c; ++l1)if (n[l1] !== n[l1 + t]) { + for(let l = t, c = t + t; l !== c; ++l)if (n[l] !== n[l + t]) { a.setValue(n, i); break; } @@ -16586,15 +16586,15 @@ var Nn = new M, Lc = new gt, py = new M, Bn = new M, my = class extends Ne { if (n !== void 0) return n; } if (e.children) { - let n1 = function(r) { + let n = function(r) { for(let o = 0; o < r.length; o++){ let a = r[o]; if (a.name === t || a.uuid === t) return a; - let l = n1(a.children); + let l = n(a.children); if (l) return l; } return null; - }, i = n1(e.children); + }, i = n(e.children); if (i) return i; } return null; @@ -16709,8 +16709,8 @@ var Nn = new M, Lc = new gt, py = new M, Bn = new M, my = class extends Ne { } let o = e[i]; if (o === void 0) { - let c1 = t.nodeName; - console.error("THREE.PropertyBinding: Trying to update property for track: " + c1 + "." + i + " but it wasn't found.", e); + let c = t.nodeName; + console.error("THREE.PropertyBinding: Trying to update property for track: " + c + "." + i + " but it wasn't found.", e); return; } let a = this.Versioning.None; @@ -16812,11 +16812,11 @@ var Yh = class { for(let x = 0, v = o; x !== v; ++x)r[x].push(new ke(d, n[x], i[x])); } else if (m < c) { a = e[m]; - let x1 = --c, v1 = e[x1]; - t[v1.uuid] = m, e[m] = v1, t[f] = x1, e[x1] = d; + let x = --c, v = e[x]; + t[v.uuid] = m, e[m] = v, t[f] = x, e[x] = d; for(let g = 0, p = o; g !== p; ++g){ - let _ = r[g], y = _[x1], b = _[m]; - _[m] = y, b === void 0 && (b = new ke(d, n[g], i[g])), _[x1] = b; + let _ = r[g], y = _[x], b = _[m]; + _[m] = y, b === void 0 && (b = new ke(d, n[g], i[g])), _[x] = b; } } else e[m] !== a && console.error("THREE.AnimationObjectGroup: Different objects with the same UUID detected. Clean the caches or recreate your infrastructure when reloading scenes."); } @@ -16849,11 +16849,11 @@ var Yh = class { p[u] = _, p[d] = y, p.pop(); } } else { - let d1 = --o, f1 = e[d1]; - d1 > 0 && (t[f1.uuid] = u), e[u] = f1, e.pop(); - for(let m1 = 0, x1 = i; m1 !== x1; ++m1){ - let v1 = n[m1]; - v1[u] = v1[d1], v1.pop(); + let d = --o, f = e[d]; + d > 0 && (t[f.uuid] = u), e[u] = f, e.pop(); + for(let m = 0, x = i; m !== x; ++m){ + let v = n[m]; + v[u] = v[d], v.pop(); } } } @@ -16987,14 +16987,14 @@ var Zh = class { t *= this._updateTimeScale(e); let o = this._updateTime(t), a = this._updateWeight(e); if (a > 0) { - let l1 = this._interpolants, c = this._propertyBindings; + let l = this._interpolants, c = this._propertyBindings; switch(this.blendMode){ case qc: - for(let h = 0, u = l1.length; h !== u; ++h)l1[h].evaluate(o), c[h].accumulateAdditive(a); + for(let h = 0, u = l.length; h !== u; ++h)l[h].evaluate(o), c[h].accumulateAdditive(a); break; case ua: default: - for(let h1 = 0, u1 = l1.length; h1 !== u1; ++h1)l1[h1].evaluate(o), c[h1].accumulate(i, a); + for(let h = 0, u = l.length; h !== u; ++h)l[h].evaluate(o), c[h].accumulate(i, a); } } } @@ -17102,9 +17102,9 @@ var Zh = class { this._bindAction(e, r && r.knownActions[0]), this._addInactiveAction(e, i, n); } let t = e._propertyBindings; - for(let n1 = 0, i1 = t.length; n1 !== i1; ++n1){ - let r1 = t[n1]; - r1.useCount++ === 0 && (this._lendBinding(r1), r1.saveOriginalState()); + for(let n = 0, i = t.length; n !== i; ++n){ + let r = t[n]; + r.useCount++ === 0 && (this._lendBinding(r), r.saveOriginalState()); } this._lendAction(e); } @@ -17239,7 +17239,7 @@ var Zh = class { let t = this._actions, n = this._nActiveActions, i = this.time += e, r = Math.sign(e), o = this._accuIndex ^= 1; for(let c = 0; c !== n; ++c)t[c]._update(i, e, r, o); let a = this._bindings, l = this._nActiveBindings; - for(let c1 = 0; c1 !== l; ++c1)a[c1].apply(o); + for(let c = 0; c !== l; ++c)a[c].apply(o); return this; } setTime(e) { @@ -17270,9 +17270,9 @@ var Zh = class { l !== void 0 && (this._deactivateAction(l), this._removeInactiveAction(l)); } let i = this._bindingsByRootAndName, r = i[t]; - if (r !== void 0) for(let o1 in r){ - let a1 = r[o1]; - a1.restoreOriginalState(), this._removeInactiveBinding(a1); + if (r !== void 0) for(let o in r){ + let a = r[o]; + a.restoreOriginalState(), this._removeInactiveBinding(a); } } uncacheAction(e, t) { @@ -17686,11 +17686,11 @@ var Ry = class extends st { let x = u & 1 ? r : o; l.push(x.r, x.g, x.b), l.push(x.r, x.g, x.b); } - for(let u1 = 0; u1 <= n; u1++){ - let d1 = u1 & 1 ? r : o, f1 = e - e / n * u1; - for(let m1 = 0; m1 < i; m1++){ - let x1 = m1 / i * (Math.PI * 2), v = Math.sin(x1) * f1, g = Math.cos(x1) * f1; - a.push(v, 0, g), l.push(d1.r, d1.g, d1.b), x1 = (m1 + 1) / i * (Math.PI * 2), v = Math.sin(x1) * f1, g = Math.cos(x1) * f1, a.push(v, 0, g), l.push(d1.r, d1.g, d1.b); + for(let u = 0; u <= n; u++){ + let d = u & 1 ? r : o, f = e - e / n * u; + for(let m = 0; m < i; m++){ + let x = m / i * (Math.PI * 2), v = Math.sin(x) * f, g = Math.cos(x) * f; + a.push(v, 0, g), l.push(d.r, d.g, d.b), x = (m + 1) / i * (Math.PI * 2), v = Math.sin(x) * f, g = Math.cos(x) * f, a.push(v, 0, g), l.push(d.r, d.g, d.b); } } let c = new _e; @@ -18118,26 +18118,26 @@ var Ls = new Lt, iu = class extends wt { }); if (!f[0]) return n(o); if (f.length > 1) { - let p1 = !1, _1 = []; + let p = !1, _ = []; for(let y = 0, b = f.length; y < b; y++)d[y] = []; - for(let y1 = 0, b1 = f.length; y1 < b1; y1++){ - let A = m[y1]; + for(let y = 0, b = f.length; y < b; y++){ + let A = m[y]; for(let L = 0; L < A.length; L++){ let I = A[L], k = !0; - for(let B = 0; B < f.length; B++)i(I.p, f[B].p) && (y1 !== B && _1.push({ - froms: y1, + for(let B = 0; B < f.length; B++)i(I.p, f[B].p) && (y !== B && _.push({ + froms: y, tos: B, hole: L - }), k ? (k = !1, d[B].push(I)) : p1 = !0); - k && d[y1].push(I); + }), k ? (k = !1, d[B].push(I)) : p = !0); + k && d[y].push(I); } } - _1.length > 0 && (p1 || (m = d)); + _.length > 0 && (p || (m = d)); } let g; - for(let p2 = 0, _2 = f.length; p2 < _2; p2++){ - c = f[p2].s, h.push(c), g = m[p2]; - for(let y2 = 0, b2 = g.length; y2 < b2; y2++)c.holes.push(g[y2].h); + for(let p = 0, _ = f.length; p < _; p++){ + c = f[p].s, h.push(c), g = m[p]; + for(let y = 0, b = g.length; y < b; y++)c.holes.push(g[y].h); } return h; } @@ -19555,12 +19555,13 @@ function attach_3d_camera(canvas, makie_camera, cam3d, scene) { camera.position.set(...cam3d.eyeposition); camera.lookAt(center); function update() { - camera.updateProjectionMatrix(); - camera.updateWorldMatrix(); const view = camera.matrixWorldInverse; const projection = camera.projectionMatrix; - const [width, height] = makie_camera.resolution.value; + const [width, height] = cam3d.resolution.value; const [x, y, z] = camera.position; + camera.aspect = width / height; + camera.updateProjectionMatrix(); + camera.updateWorldMatrix(); makie_camera.update_matrices(view.elements, projection.elements, [ width, height @@ -19570,6 +19571,7 @@ function attach_3d_camera(canvas, makie_camera, cam3d, scene) { z ]); } + cam3d.resolution.on(update); function addMouseHandler(domObject, drag, zoomIn, zoomOut) { let startDragX = null; let startDragY = null; @@ -20002,14 +20004,25 @@ function create_texture(data) { } } function re_create_texture(old_texture, buffer, size) { + let tex; if (size.length == 3) { - const tex = new mod.DataTexture3D(buffer, size[0], size[1], size[2]); + tex = new mod.DataTexture3D(buffer, size[0], size[1], size[2]); tex.format = old_texture.format; tex.type = old_texture.type; - return tex; } else { - return new mod.DataTexture(buffer, size[0], size[1] ? size[1] : 1, old_texture.format, old_texture.type); + tex = new mod.DataTexture(buffer, size[0], size[1] ? size[1] : 1, old_texture.format, old_texture.type); + } + tex.minFilter = old_texture.minFilter; + tex.magFilter = old_texture.magFilter; + tex.anisotropy = old_texture.anisotropy; + tex.wrapS = old_texture.wrapS; + if (size.length > 1) { + tex.wrapT = old_texture.wrapT; + } + if (size.length > 2) { + tex.wrapR = old_texture.wrapR; } + return tex; } function BufferAttribute(buffer) { const jsbuff = new mod.BufferAttribute(buffer.flat, buffer.type_length); @@ -20279,15 +20292,23 @@ function start_renderloop(three_scene) { } function throttle_function(func, delay) { let prev = 0; - return (...args)=>{ + let future_id = undefined; + function inner_throttle(...args) { const now = new Date().getTime(); + if (future_id !== undefined) { + clearTimeout(future_id); + future_id = undefined; + } if (now - prev > delay) { prev = now; return func(...args); + } else { + future_id = setTimeout(()=>inner_throttle(...args), now - prev + 1); } - }; + } + return inner_throttle; } -function threejs_module(canvas, comm, width, height) { +function threejs_module(canvas, comm, width, height, resize_to_body) { let context = canvas.getContext("webgl2", { preserveDrawingBuffer: true }); @@ -20371,10 +20392,28 @@ function threejs_module(canvas, comm, width, height) { } canvas.addEventListener("contextmenu", (e)=>e.preventDefault()); canvas.addEventListener("focusout", contextmenu); + function resize_callback() { + const bodyStyle = window.getComputedStyle(document.body); + const width_padding = parseInt(bodyStyle.paddingLeft, 10) + parseInt(bodyStyle.paddingRight, 10) + parseInt(bodyStyle.marginLeft, 10) + parseInt(bodyStyle.marginRight, 10); + const height_padding = parseInt(bodyStyle.paddingTop, 10) + parseInt(bodyStyle.paddingBottom, 10) + parseInt(bodyStyle.marginTop, 10) + parseInt(bodyStyle.marginBottom, 10); + const width = (window.innerWidth - width_padding) * pixelRatio1; + const height = (window.innerHeight - height_padding) * pixelRatio1; + comm.notify({ + resize: [ + width, + height + ] + }); + } + if (resize_to_body) { + const resize_callback_throttled = throttle_function(resize_callback, 100); + window.addEventListener("resize", (event)=>resize_callback_throttled()); + resize_callback_throttled(); + } return renderer; } -function create_scene(wrapper, canvas, canvas_width, scenes, comm, width, height, fps, texture_atlas_obs) { - const renderer = threejs_module(canvas, comm, width, height); +function create_scene(wrapper, canvas, canvas_width, scenes, comm, width, height, texture_atlas_obs, fps, resize_to_body) { + const renderer = threejs_module(canvas, comm, width, height, resize_to_body); TEXTURE_ATLAS[0] = texture_atlas_obs; if (renderer) { const camera = new mod.PerspectiveCamera(45, 1, 0, 100); @@ -20575,8 +20614,8 @@ function register_popup(popup, scene, plots_to_pick, callback) { } popup.style.left = event.pageX + "px"; popup.style.top = event.pageY + "px"; - const [x, y] = WGLMakie.event2scene_pixel(scene, event); - const [_, picks] = WGLMakie.pick_native(scene, x, y, 1, 1); + const [x, y] = event2scene_pixel(scene, event); + const [_, picks] = pick_native(scene, x, y, 1, 1); if (picks.length == 1) { const [plot, index] = picks[0]; if (plots_to_pick.has(plot.plot_uuid)) { @@ -20612,7 +20651,8 @@ window.WGL = { create_scene, event2scene_pixel, on_next_insert, - register_popup + register_popup, + render_scene }; export { deserialize_scene as deserialize_scene, threejs_module as threejs_module, start_renderloop as start_renderloop, delete_plots as delete_plots, insert_plot as insert_plot, find_plots as find_plots, delete_scene as delete_scene, find_scene as find_scene, scene_cache as scene_cache, plot_cache as plot_cache, delete_scenes as delete_scenes, create_scene as create_scene, event2scene_pixel as event2scene_pixel, on_next_insert as on_next_insert }; export { render_scene as render_scene }; diff --git a/WGLMakie/src/wglmakie.js b/WGLMakie/src/wglmakie.js index e01bb47ad0e..d87a13face0 100644 --- a/WGLMakie/src/wglmakie.js +++ b/WGLMakie/src/wglmakie.js @@ -80,9 +80,19 @@ function start_renderloop(three_scene) { function throttle_function(func, delay) { // Previously called time of the function let prev = 0; - return (...args) => { + // ID of queued future update + let future_id = undefined; + function inner_throttle(...args) { // Current called time of the function const now = new Date().getTime(); + + // If we had a queued run, clear it now, we're + // either going to execute now, or queue a new run. + if (future_id !== undefined) { + clearTimeout(future_id); + future_id = undefined; + } + // If difference is greater than delay call // the function again. if (now - prev > delay) { @@ -91,11 +101,18 @@ function throttle_function(func, delay) { // returning the function with the // array of arguments return func(...args); + } else { + // Otherwise, we want to queue this function call + // to occur at some later later time, so that it + // does not get lost; we'll schedule it so that it + // fires just a bit after our choke ends. + future_id = setTimeout(() => inner_throttle(...args), now - prev + 1); } }; + return inner_throttle; } -function threejs_module(canvas, comm, width, height) { +function threejs_module(canvas, comm, width, height, resize_to_body) { let context = canvas.getContext("webgl2", { preserveDrawingBuffer: true, }); @@ -199,6 +216,32 @@ function threejs_module(canvas, comm, width, height) { canvas.addEventListener("contextmenu", (e) => e.preventDefault()); canvas.addEventListener("focusout", contextmenu); + function resize_callback() { + const bodyStyle = window.getComputedStyle(document.body); + // Subtract padding that is added by VSCode + const width_padding = + parseInt(bodyStyle.paddingLeft, 10) + + parseInt(bodyStyle.paddingRight, 10) + + parseInt(bodyStyle.marginLeft, 10) + + parseInt(bodyStyle.marginRight, 10); + const height_padding = + parseInt(bodyStyle.paddingTop, 10) + + parseInt(bodyStyle.paddingBottom, 10) + + parseInt(bodyStyle.marginTop, 10) + + parseInt(bodyStyle.marginBottom, 10); + const width = (window.innerWidth - width_padding) * pixelRatio; + const height = (window.innerHeight - height_padding) * pixelRatio; + + // Send the resize event to Julia + comm.notify({ resize: [width, height] }); + } + if (resize_to_body) { + const resize_callback_throttled = throttle_function(resize_callback, 100); + window.addEventListener("resize", (event) => resize_callback_throttled()); + // Fire the resize event once at the start to auto-size our window + resize_callback_throttled(); + } + return renderer; } @@ -210,10 +253,17 @@ function create_scene( comm, width, height, + texture_atlas_obs, fps, - texture_atlas_obs + resize_to_body ) { - const renderer = threejs_module(canvas, comm, width, height); + const renderer = threejs_module( + canvas, + comm, + width, + height, + resize_to_body + ); TEXTURE_ATLAS[0] = texture_atlas_obs; if (renderer) { @@ -440,8 +490,8 @@ export function register_popup(popup, scene, plots_to_pick, callback) { } popup.style.left = event.pageX + "px"; popup.style.top = event.pageY + "px"; - const [x, y] = WGLMakie.event2scene_pixel(scene, event); - const [_, picks] = WGLMakie.pick_native(scene, x, y, 1, 1); + const [x, y] = event2scene_pixel(scene, event); + const [_, picks] = pick_native(scene, x, y, 1, 1); if (picks.length == 1) { const [plot, index] = picks[0]; if (plots_to_pick.has(plot.plot_uuid)) { @@ -463,6 +513,8 @@ export function register_popup(popup, scene, plots_to_pick, callback) { }); } + + window.WGL = { deserialize_scene, threejs_module, @@ -479,6 +531,7 @@ window.WGL = { event2scene_pixel, on_next_insert, register_popup, + render_scene, }; export { @@ -495,5 +548,5 @@ export { delete_scenes, create_scene, event2scene_pixel, - on_next_insert + on_next_insert, }; diff --git a/WGLMakie/test/Project.toml b/WGLMakie/test/Project.toml index 0edb0064c73..066e2ecdc44 100644 --- a/WGLMakie/test/Project.toml +++ b/WGLMakie/test/Project.toml @@ -4,7 +4,5 @@ FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" JSServe = "824d6782-a2ef-11e9-3a09-e5662e0c26f9" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +ReferenceTests = "d37af2e0-5618-4e00-9939-d430db56ee94" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[compat] -Electron = "4.1.1" diff --git a/WGLMakie/test/runtests.jl b/WGLMakie/test/runtests.jl index 20b317aecaa..3d13c3cb842 100644 --- a/WGLMakie/test/runtests.jl +++ b/WGLMakie/test/runtests.jl @@ -1,11 +1,7 @@ using FileIO using WGLMakie, Makie, Test -using Pkg using WGLMakie.JSServe import Electron - -path = normpath(joinpath(dirname(pathof(Makie)), "..", "ReferenceTests")) -Pkg.develop(PackageSpec(path = path)) using ReferenceTests @testset "mimes" begin diff --git a/assets/approxsin.png b/assets/approxsin.png index e3e81eabbb6..bd1564e8918 100644 Binary files a/assets/approxsin.png and b/assets/approxsin.png differ diff --git a/assets/parabola.png b/assets/parabola.png index 93c0187a75e..b1290f28ddd 100644 Binary files a/assets/parabola.png and b/assets/parabola.png differ diff --git a/assets/simpleLayout.png b/assets/simpleLayout.png index 28c21578b90..dcc3520aaa9 100644 Binary files a/assets/simpleLayout.png and b/assets/simpleLayout.png differ diff --git a/docs/Project.toml b/docs/Project.toml index d05280df457..0398c2f82ac 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -27,3 +27,6 @@ Observables = "510215fc-4207-5dde-b226-833fc4488ee2" RPRMakie = "22d9f318-5e34-4b44-b769-6e3734a732a6" RadeonProRender = "27029320-176d-4a42-b57d-56729d2ad457" WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008" + +[compat] +Documenter = "<1" \ No newline at end of file diff --git a/docs/_assets/datashader-14million.mp4 b/docs/_assets/datashader-14million.mp4 new file mode 100644 index 00000000000..2a2d52ff025 Binary files /dev/null and b/docs/_assets/datashader-14million.mp4 differ diff --git a/docs/_assets/datashader_2-7_billion.mp4 b/docs/_assets/datashader_2-7_billion.mp4 new file mode 100644 index 00000000000..467ce254592 Binary files /dev/null and b/docs/_assets/datashader_2-7_billion.mp4 differ diff --git a/docs/_assets/nyc-per-vendor.png b/docs/_assets/nyc-per-vendor.png new file mode 100644 index 00000000000..30d7a272c31 Binary files /dev/null and b/docs/_assets/nyc-per-vendor.png differ diff --git a/docs/_css/makie.css b/docs/_css/makie.css index 2d2d27eff86..eff07ce7b1c 100644 --- a/docs/_css/makie.css +++ b/docs/_css/makie.css @@ -778,6 +778,10 @@ button.copy-code { align-items: center; } +button.copy-code .far { + font-family: "Font Awesome 5 Free"; +} + .copied-text { font-size: 0.6em; margin-right: 0.2em; diff --git a/docs/_layout/body_layout.html b/docs/_layout/body_layout.html index c14099ed39a..920bd59ff55 100644 --- a/docs/_layout/body_layout.html +++ b/docs/_layout/body_layout.html @@ -13,9 +13,7 @@ --> {{if searchpage}} {{else}} -
- -
+
{{end}}

Navigation

diff --git a/docs/_layout/foot.html b/docs/_layout/foot.html index f9e38282cfd..f3393ade90b 100644 --- a/docs/_layout/foot.html +++ b/docs/_layout/foot.html @@ -25,43 +25,21 @@ - - - - {{if searchpage}} + + - {{else}} - - - {{end}} - - - - - + + + {{ if hasmath }} {{ insert foot_katex.html }} @@ -70,4 +48,4 @@ {{ insert foot_highlight.html }} {{ end }} - \ No newline at end of file + diff --git a/docs/_layout/masthead.html b/docs/_layout/masthead.html index 0ce914f5a50..eaaa7ed71d1 100644 --- a/docs/_layout/masthead.html +++ b/docs/_layout/masthead.html @@ -10,9 +10,9 @@
  • -
  • Examples
  • +
  • Reference
  • Tutorials
  • -
  • Documentation
  • +
  • Explanations
  • API
  • News
  • diff --git a/docs/deploydocs.jl b/docs/buildutils/deploydocs.jl similarity index 100% rename from docs/deploydocs.jl rename to docs/buildutils/deploydocs.jl diff --git a/docs/buildutils/redirect_generation.jl b/docs/buildutils/redirect_generation.jl new file mode 100644 index 00000000000..bf5cfbaf8e9 --- /dev/null +++ b/docs/buildutils/redirect_generation.jl @@ -0,0 +1,62 @@ +function generate_redirects(rules; dry_run = true) + htmlpaths = cd("__site") do + collect(Iterators.flatmap(walkdir(".")) do (root, dirs, files) + (chop(joinpath(root, file), head = 1, tail = 0) for file in files if endswith(file, ".html")) + end) + end + redirects_dict = Dict{String,String}() + for rule in rules + for path in htmlpaths + redirect_file = replace(path, rule) + if redirect_file != path + redirects_dict[redirect_file] = path + end + end + end + redirect_files = keys(redirects_dict) + overwrites = intersect(redirect_files, htmlpaths) + if !isempty(overwrites) + strs = ["\"$r\" (redirects to \"$(redirects_dict[r])\")" for r in overwrites] + error("The following redirect files would overwrite existing files:\n$(join(strs, "\n"))") + end + + for (redirect_file, existing_file) in redirects_dict + write_redirection_html(redirect_file, existing_file; dry_run) + end + + return +end + +function write_redirection_html(redirect_file, existing_file; dry_run) + rel = relpath(existing_file, dirname(redirect_file)) + @assert startswith(redirect_file, "/") + @assert startswith(existing_file, "/") + + @info "Adding redirect from $redirect_file to $existing_file" + if !dry_run + cd("__site") do + filepath = "." * redirect_file + mkpath(splitdir(filepath)[1]) + open(filepath, "w") do io + print(io, """ + + + + + + + + Page Redirection + + + If you are not redirected automatically, follow this link. + + + """) + end + end + end + return +end diff --git a/docs/buildutils/relative_links.jl b/docs/buildutils/relative_links.jl new file mode 100644 index 00000000000..dd42d4a4565 --- /dev/null +++ b/docs/buildutils/relative_links.jl @@ -0,0 +1,80 @@ +""" +Converts the string `s` which might be an absolute path, +to a relative path, relative to the current location `here` +""" +function make_relative(s, here) + if !startswith(s, "/") + return s + end + + there = s[2:end] + + if here == "" + return there + end + + hereparts = split(here, "/") + thereparts = split(there, "/") + + # tutorials/layout-tutorial tutorials/ + closest_common = 0 + for i in 1:min(length(thereparts), length(hereparts)) + if hereparts[i] == thereparts[i] + closest_common = i + else + break + end + end + n_up = length(hereparts) - closest_common + therepart = join(thereparts[(closest_common + 1):end], "/") + if n_up == 0 + therepart + else + up_part = join((".." for i in 1:n_up), "/") + if therepart == "" + up_part + else + up_part * "/" * therepart + end + end +end + +""" +Replaces all absolute links in all html files in the __site folder with +relative links. +""" +function make_links_relative() + cd("__site") do + for (root, _, files) in walkdir(".") + path = join(splitpath(root)[2:end], "/") + + html_files = filter(endswith(".html"), files) + for file in html_files + s = read(joinpath(root, file), String) + s = replace(s, '\0' => "\\0") + + html = parsehtml(s) + + for e in PreOrderDFS(html.root) + if (e isa HTMLElement{:script} || e isa HTMLElement{:img} || e isa HTMLElement{:video}) && + haskey(e.attributes, "src") + link = e.attributes["src"] + e.attributes["src"] = make_relative(link, path) + + elseif (e isa HTMLElement{:link} || e isa HTMLElement{:a}) && haskey(e.attributes, "href") + link = e.attributes["href"] + e.attributes["href"] = make_relative(link, path) + + elseif e isa HTMLElement{:form} && haskey(e.attributes, "action") + link = e.attributes["action"] + e.attributes["action"] = make_relative(link, path) + end + end + + open(joinpath(root, file), "w") do f + return print(f, html) + end + end + end + end +end \ No newline at end of file diff --git a/docs/buildutils/stork.jl b/docs/buildutils/stork.jl new file mode 100644 index 00000000000..e7bc2caadea --- /dev/null +++ b/docs/buildutils/stork.jl @@ -0,0 +1,62 @@ +function populate_stork_config(subfolder) + sites = [] + tempdir = mktempdir() + + _get(el, type) = el.children[findfirst(x -> x isa type, el.children)] + + cd("__site/") do + for (root, dirs, files) in walkdir(".") + if any(x -> startswith(root, x), ["libs", "css", "assets"]) + continue + end + f = filter(endswith(".html"), files) + isempty(f) && continue + + for file in f + s = read(joinpath(root, file), String) + s = replace(s, '\0' => "\\0") + + html = parsehtml(s) + head = _get(html.root, HTMLElement{:head}) + title = _get(head, HTMLElement{:title}) + titletext = _get(title, HTMLText).text + + for e in PreOrderDFS(html.root) + if e isa HTMLElement + filter!(child -> !(child isa HTMLElement{:script}), e.children) + end + end + + randfilepath = joinpath(tempdir, Random.randstring(20) * ".html") + open(joinpath(tempdir, randfilepath), "w") do io + print(io, html) + end + + push!(sites, ( + title = titletext, + path = randfilepath, + url = normpath(joinpath(root, file)), # remove "./" prefix + )) + end + end + end + + for file in ["config_box", "config_page"] + cp("__site/libs/stork/$(file).toml", "__site/libs/stork/$(file)_filled.toml", force = true) + + toml = TOML.parsefile("__site/libs/stork/$(file).toml") + open("__site/libs/stork/$(file)_filled.toml", "w") do io + toml["input"]["files"] = map(Dict ∘ pairs, sites) + toml["input"]["url_prefix"] = isempty(subfolder) ? "/" : "/" * subfolder * "/" # then url without / prefix + TOML.print(io, toml, sorted = true) + end + end + return +end + +function run_stork() + cd("__site/libs/stork") do + run(`$stork build --input config_box_filled.toml --output index_box.st`) + run(`$stork build --input config_page_filled.toml --output index_page.st`) + end +end diff --git a/docs/documentation.md b/docs/documentation.md deleted file mode 100644 index 8076d2b016d..00000000000 --- a/docs/documentation.md +++ /dev/null @@ -1,5 +0,0 @@ -@def order = 3 - -# Documentation - -{{list_folder documentation}} \ No newline at end of file diff --git a/docs/examples.md b/docs/examples.md deleted file mode 100644 index 93c1cb7fba4..00000000000 --- a/docs/examples.md +++ /dev/null @@ -1,5 +0,0 @@ -@def order = 1 - -# Examples - -{{list_folder examples}} \ No newline at end of file diff --git a/docs/examples/blocks.md b/docs/examples/blocks.md deleted file mode 100644 index d092976a970..00000000000 --- a/docs/examples/blocks.md +++ /dev/null @@ -1,9 +0,0 @@ -# Blocks & Widgets - -All examples in this section are presented as static CairoMakie vector graphics for clarity of visuals. -Keep in mind that CairoMakie is not interactive. -Use GLMakie for interactive widgets, as WGLMakie currently doesn't have picking implemented. - -## Example gallery - -{{list_folder_with_images blocks}} \ No newline at end of file diff --git a/docs/examples/plotting_functions.md b/docs/examples/plotting_functions.md deleted file mode 100644 index 4d27cb489b3..00000000000 --- a/docs/examples/plotting_functions.md +++ /dev/null @@ -1,3 +0,0 @@ -# Plotting functions - -{{list_folder_with_images plotting_functions}} \ No newline at end of file diff --git a/docs/explanations.md b/docs/explanations.md new file mode 100644 index 00000000000..3542f41c3d7 --- /dev/null +++ b/docs/explanations.md @@ -0,0 +1,5 @@ +@def order = 3 + +# Explanations + +{{list_folder explanations}} \ No newline at end of file diff --git a/docs/documentation/animation.md b/docs/explanations/animation.md similarity index 98% rename from docs/documentation/animation.md rename to docs/explanations/animation.md index 4b251dd1281..9c0faf351d1 100644 --- a/docs/documentation/animation.md +++ b/docs/explanations/animation.md @@ -50,7 +50,7 @@ record(change_function, fig, "color_animation.mp4", hue_iterator; framerate = fr ## File formats -Video files are created with [`FFMPEG.jl`](https://github.com/JuliaIO/FFMPEG.jl). +Video files are created with [`FFMPEG_jll.jl`](https://github.com/JuliaBinaryWrappers/FFMPEG_jll.jl). You can choose from the following file formats: - `.mkv` (the default, doesn't need to convert) diff --git a/docs/documentation/backends.md b/docs/explanations/backends.md similarity index 100% rename from docs/documentation/backends.md rename to docs/explanations/backends.md diff --git a/docs/documentation/backends/cairomakie.md b/docs/explanations/backends/cairomakie.md similarity index 100% rename from docs/documentation/backends/cairomakie.md rename to docs/explanations/backends/cairomakie.md diff --git a/docs/documentation/backends/glmakie.md b/docs/explanations/backends/glmakie.md similarity index 100% rename from docs/documentation/backends/glmakie.md rename to docs/explanations/backends/glmakie.md diff --git a/docs/documentation/backends/rprmakie.md b/docs/explanations/backends/rprmakie.md similarity index 99% rename from docs/documentation/backends/rprmakie.md rename to docs/explanations/backends/rprmakie.md index 31fcd286d43..bbd4f31903c 100644 --- a/docs/documentation/backends/rprmakie.md +++ b/docs/explanations/backends/rprmakie.md @@ -314,7 +314,7 @@ end ~~~ ~~~ -