GR: fix axis flip / mirror in 3D plots (#3584)

* fix axis flip in 3D plots

* add mwe as example - fix needs_3d_axes

* fix major / minor grids when mirroring

Co-authored-by: t-bltg <t-bltg@users.noreply.github.com>
This commit is contained in:
t-bltg 2021-06-30 22:50:09 +02:00 committed by GitHub
parent 3f9105054c
commit bba971f7ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 98 additions and 21 deletions

View File

@ -853,15 +853,17 @@ function axis_drawing_info_3d(sp, letter)
)
end
if grid
fa0_, fa1_ = reverse_if((fa0, fa1), ax[:mirror])
ga0_, ga1_ = reverse_if((ga0, ga1), ax[:mirror])
push!(
segments,
sort_3d_axes(tick, ga0, fa0, letter),
sort_3d_axes(tick, ga1, fa0, letter),
sort_3d_axes(tick, ga0_, fa0_, letter),
sort_3d_axes(tick, ga1_, fa0_, letter),
)
push!(
segments,
sort_3d_axes(tick, ga1, fa0, letter),
sort_3d_axes(tick, ga1, fa1, letter),
sort_3d_axes(tick, ga1_, fa0_, letter),
sort_3d_axes(tick, ga1_, fa1_, letter),
)
end
end

View File

@ -1287,12 +1287,22 @@ function gr_set_window(sp, viewport_plotarea)
gr_set_viewport_polar(viewport_plotarea)
else
xmin, xmax, ymin, ymax = gr_xy_axislims(sp)
needs_3d = needs_any_3d_axes(sp)
if needs_3d
zmin, zmax = gr_z_axislims(sp)
zok = zmax > zmin
else
zok = true
end
scaleop = 0
if xmax > xmin && ymax > ymin
sp[:xaxis][:scale] == :log10 && (scaleop |= GR.OPTION_X_LOG)
sp[:yaxis][:scale] == :log10 && (scaleop |= GR.OPTION_Y_LOG)
sp[:xaxis][:flip] && (scaleop |= GR.OPTION_FLIP_X)
sp[:yaxis][:flip] && (scaleop |= GR.OPTION_FLIP_Y)
if xmax > xmin && ymax > ymin && zok
sp[:xaxis][:scale] == :log10 && (scaleop |= GR.OPTION_X_LOG)
sp[:yaxis][:scale] == :log10 && (scaleop |= GR.OPTION_Y_LOG)
needs_3d && sp[:zaxis][:scale] == :log10 && (scaleop |= GR.OPTION_Z_LOG)
sp[:xaxis][:flip] && (scaleop |= GR.OPTION_FLIP_X)
sp[:yaxis][:flip] && (scaleop |= GR.OPTION_FLIP_Y)
needs_3d && sp[:zaxis][:flip] && (scaleop |= GR.OPTION_FLIP_Z)
# NOTE: setwindow sets the "data coordinate" limits of the current "viewport"
GR.setwindow(xmin, xmax, ymin, ymax)
GR.setscale(scaleop)
@ -1315,7 +1325,7 @@ function gr_draw_axes(sp, viewport_plotarea)
if RecipesPipeline.is3d(sp)
# set space
xmin, xmax, ymin, ymax = gr_xy_axislims(sp)
zmin, zmax = axis_limits(sp, :z)
zmin, zmax = gr_z_axislims(sp)
GR.setspace(zmin, zmax, round.(Int, sp[:camera])...)
# fill the plot area
@ -1327,7 +1337,7 @@ function gr_draw_axes(sp, viewport_plotarea)
GR.fillarea(x_bg, y_bg)
for letter in (:x, :y, :z)
gr_draw_axis_3d(sp, letter)
gr_draw_axis_3d(sp, letter, viewport_plotarea)
end
elseif ispolar(sp)
r = gr_set_viewport_polar(viewport_plotarea)
@ -1357,7 +1367,7 @@ function gr_draw_axis(sp, letter, viewport_plotarea)
gr_label_axis(sp, letter, viewport_plotarea)
end
function gr_draw_axis_3d(sp, letter)
function gr_draw_axis_3d(sp, letter, viewport_plotarea)
ax = axis_drawing_info_3d(sp, letter)
axis = sp[Symbol(letter, :axis)]
@ -1369,8 +1379,10 @@ function gr_draw_axis_3d(sp, letter)
gr_draw_ticks(sp, axis, ax.tick_segments, gr_polyline3d)
# labels
GR.setscale(0)
gr_label_ticks_3d(sp, letter, ax.ticks)
gr_label_axis_3d(sp, letter)
gr_set_window(sp, viewport_plotarea)
end
function gr_draw_grid(sp, axis, segments, func = gr_polyline)
@ -1445,13 +1457,14 @@ function gr_label_ticks(sp, letter, ticks)
oamin, oamax = axis_limits(sp, oletter)
gr_set_tickfont(sp, letter)
out_factor = ifelse(axis[:tick_direction] === :out, 1.5, 1)
x_offset = isy ? (axis[:mirror] ? 1 : -1) * 1.5e-2 * out_factor : 0
y_offset = isy ? 0 : (axis[:mirror] ? 1 : -1) * 8e-3 * out_factor
x_offset = isy ? -1.5e-2 * out_factor : 0
y_offset = isy ? 0 : -8e-3 * out_factor
ov = sp[:framestyle] == :origin ? 0 : xor(oaxis[:flip], axis[:mirror]) ? oamax : oamin
sgn = axis[:mirror] ? -1 : 1
for (cv, dv) in zip(ticks...)
x, y = GR.wctondc(reverse_if((cv, ov), isy)...)
gr_text(x + x_offset, y + y_offset, dv)
gr_text(x + sgn * x_offset, y + sgn * y_offset, dv)
end
end
@ -1483,8 +1496,8 @@ function gr_label_ticks_3d(sp, letter, ticks)
xax, yax, zax = getindex.(Ref(sp), asyms)
gr_set_tickfont(sp, letter)
nt = sp[:framestyle] == :origin ? 0 : xor(ax[:mirror], nax[:flip]) ? n1 : n0
ft = sp[:framestyle] == :origin ? 0 : xor(ax[:mirror], fax[:flip]) ? famax : famin
nt = sp[:framestyle] == :origin ? 0 : ax[:mirror] ? n1 : n0
ft = sp[:framestyle] == :origin ? 0 : ax[:mirror] ? famax : famin
xoffset = if letter === :x
(sp[:yaxis][:mirror] ? 1 : -1) * 1e-2 * (sp[:xaxis][:tick_direction] == :out ? 1.5 : 1)
@ -1501,7 +1514,10 @@ function gr_label_ticks_3d(sp, letter, ticks)
0
end
for (cv, dv) in zip(ticks...)
cvs, dvs = ticks
ax[:flip] && reverse!(cvs)
for (cv, dv) in zip((cvs, dvs)...)
xi, yi = gr_w3tondc(sort_3d_axes(cv, nt, ft, letter)...)
gr_text(xi + xoffset, yi + yoffset, dv)
end
@ -1570,8 +1586,8 @@ function gr_label_axis_3d(sp, letter)
# color = ax[:guidefontcolor],
)
ag = (amin + amax) / 2
ng = xor(ax[:mirror], nax[:flip]) ? n1 : n0
fg = xor(ax[:mirror], fax[:flip]) ? famax : famin
ng = ax[:mirror] ? n1 : n0
fg = ax[:mirror] ? famax : famin
x, y = gr_w3tondc(sort_3d_axes(ag, ng, fg, letter)...)
if letter in (:x, :y)
h = gr_axis_height(sp, ax)
@ -1582,7 +1598,8 @@ function gr_label_axis_3d(sp, letter)
y_offset = 0
end
letter === :z && GR.setcharup(-1, 0)
gr_text(x + x_offset, y + y_offset, ax[:guide])
sgn = ax[:mirror] ? -1 : 1
gr_text(x + sgn * x_offset, y + sgn * y_offset, ax[:guide])
GR.restorestate()
end
end

View File

@ -1163,6 +1163,56 @@ const _examples = PlotExample[
),
],
),
PlotExample( # 55
"3D axis flip / mirror",
"",
[
:(
begin
meshgrid(x, y) = (ones(eltype(y), length(y)) * x', y * ones(eltype(x), length(x))')
scalefontsizes(.5)
x, y = meshgrid(-6:0.5:10, -8:0.5:8)
r = sqrt.(x .^ 2 + y .^ 2) .+ eps()
z = sin.(r) ./ r
args = x[1, :], y[:, 1], z[:]
kwargs = Dict(
:xlabel => "x", :ylabel => "y", :zlabel => "z",
:grid => true, :minorgrid => true, :dpi => 200
)
plots = [wireframe(args..., title = "wire"; kwargs...)]
for ax (:x, :y, :z)
push!(plots, wireframe(
args...,
title = "wire-flip-$ax",
xflip = ax == :x,
yflip = ax == :y,
zflip = ax == :z;
kwargs...,
))
end
for ax (:x, :y, :z)
push!(plots, wireframe(
args...,
title = "wire-mirror-$ax",
xmirror = ax == :x,
ymirror = ax == :y,
zmirror = ax == :z;
kwargs...,
))
end
plot(plots..., layout=(@layout [_ ° _; ° ° °; ° ° °]), margin=2Plots.mm)
scalefontsizes()
end
),
],
),
]
# Some constants for PlotDocs and PlotReferenceImages

View File

@ -338,6 +338,14 @@ function _override_seriestype_check(plotattributes::AKW, st::Symbol)
st
end
function needs_any_3d_axes(sp::Subplot)
any(
RecipesPipeline.needs_3d_axes(
_override_seriestype_check(s.plotattributes, s.plotattributes[:seriestype])
) for s in series_list(sp)
)
end
function _expand_subplot_extrema(sp::Subplot, plotattributes::AKW, st::Symbol)
# adjust extrema and discrete info
if st == :image