diff --git a/CHANGELOG.md b/CHANGELOG.md index b62f2c7bead..d1424ad94aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Added `transform_marker` attribute to meshscatter and changed the default behavior to not transform marker/mesh vertices [#4606](https://github.com/MakieOrg/Makie.jl/pull/4606) - Fixed some issues with meshscatter not correctly transforming with transform functions and float32 rescaling [#4606](https://github.com/MakieOrg/Makie.jl/pull/4606) - Fixed `poly` pipeline for 3D and/or Float64 polygons that begin from an empty vector [#4615](https://github.com/MakieOrg/Makie.jl/pull/4615). +- Added option to display the front spines in Axis3 to close the outline box [#2349](https://github.com/MakieOrg/Makie.jl/pull/4305) - Fixed gaps in corners of `poly(Rect2(...))` stroke [#4664](https://github.com/MakieOrg/Makie.jl/pull/4664) - Fixed an issue where `reinterpret`ed arrays of line points were not handled correctly in CairoMakie [#4668](https://github.com/MakieOrg/Makie.jl/pull/4668). - Fixed various issues with `markerspace = :data`, `transform_marker = true` and `rotation` for scatter in CairoMakie (incorrect marker transformations, ignored transformations, Cairo state corruption) [#4663](https://github.com/MakieOrg/Makie.jl/pull/4663) diff --git a/ReferenceTests/src/tests/figures_and_makielayout.jl b/ReferenceTests/src/tests/figures_and_makielayout.jl index 3cad48dd186..9173fc56dfc 100644 --- a/ReferenceTests/src/tests/figures_and_makielayout.jl +++ b/ReferenceTests/src/tests/figures_and_makielayout.jl @@ -306,6 +306,30 @@ end f end +@reference_test "Axis3 fullbox" begin + f = Figure(size = (400, 400)) + a = Axis3(f[1, 1], front_spines = true, xspinewidth = 5, yspinewidth = 5, zspinewidth = 5) + mesh!(a, Sphere(Point3f(-0.2, 0.2, 0), 1f0), color = :darkgray, transparency = false) + mesh!(a, Sphere(Point3f(0.2, -0.2, 0), 1f0), color = :darkgray, transparency = true) + + for ((x, y), viskey, colkey) in zip([(1,2), (2,1), (2,2)], [:x, :y, :z], [:y, :z, :x]) + kwargs = Dict( + Symbol(viskey, :spinesvisible) => false, + Symbol(colkey, :spinecolor_1) => :red, + Symbol(colkey, :spinecolor_2) => :green, + Symbol(colkey, :spinecolor_3) => :blue, + Symbol(colkey, :spinecolor_4) => :orange, + ) + a = Axis3( + f[x, y], title = "$viskey hidden, $colkey colored", front_spines = true, + xspinewidth = 5, yspinewidth = 5, zspinewidth = 5; kwargs...) + + mesh!(a, Sphere(Point3f(-0.2, 0.2, 0), 1f0), color = :darkgray, transparency = false) + mesh!(a, Sphere(Point3f(0.2, -0.2, 0), 1f0), color = :darkgray, transparency = true) + end + f +end + @reference_test "Axis3 viewmodes, xreversed, aspect, perspectiveness" begin fig = Figure(size = (800, 1200)) diff --git a/src/makielayout/blocks/axis3d.jl b/src/makielayout/blocks/axis3d.jl index a9d3306af0c..77f399973aa 100644 --- a/src/makielayout/blocks/axis3d.jl +++ b/src/makielayout/blocks/axis3d.jl @@ -95,12 +95,20 @@ function initialize_block!(ax::Axis3) add_panel!(scene, ax, 2, 3, 1, finallimits, mi1) add_panel!(scene, ax, 1, 3, 2, finallimits, mi2) - xgridline1, xgridline2, xframelines = - add_gridlines_and_frames!(blockscene, scene, ax, 1, finallimits, ticknode_1, mi1, mi2, mi3, ax.xreversed, ax.yreversed, ax.zreversed) - ygridline1, ygridline2, yframelines = - add_gridlines_and_frames!(blockscene, scene, ax, 2, finallimits, ticknode_2, mi2, mi1, mi3, ax.xreversed, ax.yreversed, ax.zreversed) - zgridline1, zgridline2, zframelines = - add_gridlines_and_frames!(blockscene, scene, ax, 3, finallimits, ticknode_3, mi3, mi1, mi2, ax.xreversed, ax.yreversed, ax.zreversed) + # This exists as a bandaid for WGLMakie. See add_gridlines_and_frames!() + overlay = Scene( + blockscene, scenearea, clear = false, backgroundcolor = :transparent, + camera = scene.camera, transformation = scene.transformation) + + xgridline1, xgridline2, xframelines = add_gridlines_and_frames!( + blockscene, scene, overlay, ax, 1, finallimits, ticknode_1, mi1, mi2, mi3, + ax.xreversed, ax.yreversed, ax.zreversed) + ygridline1, ygridline2, yframelines = add_gridlines_and_frames!( + blockscene, scene, overlay, ax, 2, finallimits, ticknode_2, mi2, mi1, mi3, + ax.xreversed, ax.yreversed, ax.zreversed) + zgridline1, zgridline2, zframelines = add_gridlines_and_frames!( + blockscene, scene, overlay, ax, 3, finallimits, ticknode_3, mi3, mi1, mi2, + ax.xreversed, ax.yreversed, ax.zreversed) xticks, xticklabels, xlabel = add_ticks_and_ticklabels!(blockscene, scene, ax, 1, finallimits, ticknode_1, mi1, mi2, mi3, ax.azimuth, ax.xreversed, ax.yreversed, ax.zreversed) @@ -393,7 +401,7 @@ function dim2(dim) end end -function add_gridlines_and_frames!(topscene, scene, ax, dim::Int, limits, ticknode, miv, min1, min2, xreversed, yreversed, zreversed) +function add_gridlines_and_frames!(topscene, scene, overlay, ax, dim::Int, limits, ticknode, miv, min1, min2, xreversed, yreversed, zreversed) dimsym(sym) = Symbol(string((:x, :y, :z)[dim]) * string(sym)) attr(sym) = getproperty(ax, dimsym(sym)) @@ -456,17 +464,62 @@ function add_gridlines_and_frames!(topscene, scene, ax, dim::Int, limits, tickno p4 = dpoint(maximum(lims)[dim], f(mi1)(lims)[d1], f(mi2)(lims)[d2]) p5 = dpoint(minimum(lims)[dim], f(mi1)(lims)[d1], f(!mi2)(lims)[d2]) p6 = dpoint(maximum(lims)[dim], f(mi1)(lims)[d1], f(!mi2)(lims)[d2]) - # p7 = dpoint(minimum(lims)[dim], f(!mi1)(lims)[d1], f(!mi2)(lims)[d2]) - # p8 = dpoint(maximum(lims)[dim], f(!mi1)(lims)[d1], f(!mi2)(lims)[d2]) return [p1, p2, p3, p4, p5, p6] end + framepoints_front_spines = lift(limits, min1, min2, xreversed, yreversed, zreversed + ) do lims, mi1, mi2, xrev, yrev, zrev + + rev1 = (xrev, yrev, zrev)[d1] + rev2 = (xrev, yrev, zrev)[d2] + + mi1 = mi1 ⊻ rev1 + mi2 = mi2 ⊻ rev2 + + f(mi) = mi ? minimum : maximum + + p7 = dpoint(minimum(lims)[dim], f(!mi1)(lims)[d1], f(!mi2)(lims)[d2]) + p8 = dpoint(maximum(lims)[dim], f(!mi1)(lims)[d1], f(!mi2)(lims)[d2]) + + return [p7, p8] + end colors = Observable{Any}() map!(vcat, colors, attr(:spinecolor_1), attr(:spinecolor_2), attr(:spinecolor_3)) + framelines = linesegments!(scene, framepoints, color = colors, linewidth = attr(:spinewidth), transparency = true, visible = attr(:spinesvisible), inspectable = false, xautolimits = false, yautolimits = false, zautolimits = false, clip_planes = Plane3f[]) + front_framelines = linesegments!(overlay, framepoints_front_spines, color = attr(:spinecolor_4), + linewidth = attr(:spinewidth), visible = map((a,b) -> a && b, ax.front_spines, attr(:spinesvisible)), + transparency = true, inspectable = false, + xautolimits = false, yautolimits = false, zautolimits = false, clip_planes = Plane3f[]) + + #= On transparency and render order + We have transparency = true here mostly for render order and depth testing + reasons. + In GLMakie: + - transparency = true gets rendered after transparency = false. This fixes + artifacts of the line AA, which mixes with the current background. (I.e. + if lines render first they will mix with the scene background color rather + than plots) + - transparency = true turns off depth writes which means the frame lines don't + see the grid lines and draw over them. This fixes grid lines poking through + frame lines, and also fixes mixing issues where frame lines meet. This + could also be fixed by explicit order with overdraw = true + - Note that transparency = true causes frame lines to never be 100% opaque + in GLMakie + In WGLMakie: + - transparency = true also turns off depth writes, see above + - transparency = true does not affect render order. Since it does turn off + depth writes other things will draw over the front frame lines. To fix this + we add an overlay scene which renders after the main scene, i.e. after + grid lines, back frame lines and user plots. + In CairoMakie: + - transparency does not matter, only plot order does. The overlay scene + forces does the same as in WGLMakie + =# + return gridline1, gridline2, framelines end diff --git a/src/makielayout/types.jl b/src/makielayout/types.jl index a538eb4806c..969b12fb4ae 100644 --- a/src/makielayout/types.jl +++ b/src/makielayout/types.jl @@ -1798,7 +1798,15 @@ end "The color of y spine 3 opposite of the ticks" yspinecolor_3 = :black "The color of z spine 3 opposite of the ticks" - zspinecolor_3 = :black + zspinecolor_3 = :black + "Controls if the 4. Spines are created to close the outline box" + front_spines = false + "The color of x spine 4" + xspinecolor_4 = :black + "The color of y spine 4" + yspinecolor_4 = :black + "The color of z spine 4" + zspinecolor_4 = :black "The x spine width" xspinewidth = 1 "The y spine width" diff --git a/src/themes/theme_black.jl b/src/themes/theme_black.jl index 3ee6e7fb23d..22efc783dd9 100644 --- a/src/themes/theme_black.jl +++ b/src/themes/theme_black.jl @@ -31,6 +31,9 @@ function theme_black() xspinecolor_3 = :white, yspinecolor_3 = :white, zspinecolor_3 = :white, + xspinecolor_4 = :white, + yspinecolor_4 = :white, + zspinecolor_4 = :white, xticklabelpad = 3, yticklabelpad = 3, zticklabelpad = 6, diff --git a/src/themes/theme_minimal.jl b/src/themes/theme_minimal.jl index 4c3ac1af7e6..51f774139f0 100644 --- a/src/themes/theme_minimal.jl +++ b/src/themes/theme_minimal.jl @@ -39,10 +39,13 @@ function theme_minimal() zticklabelpad = 6, xspinecolor_2 = :transparent, xspinecolor_3 = :transparent, + xspinecolor_4 = :transparent, yspinecolor_2 = :transparent, yspinecolor_3 = :transparent, + yspinecolor_4 = :transparent, zspinecolor_2 = :transparent, zspinecolor_3 = :transparent, + zspinecolor_4 = :transparent, ), Colorbar = ( ticksvisible = false,