Skip to content

Commit

Permalink
Add Axis3 axis reversal attributes (#3138)
Browse files Browse the repository at this point in the history
* Add `Axis3` axis reversal attributes

* add NEWS entry

* add attribute examples for docs
  • Loading branch information
jkrumbiegel committed Aug 9, 2023
1 parent f3f34d0 commit cf62375
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 47 deletions.
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

## master

- 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)
- Fix automatic normal generation for meshes with shading and no normals [#3041](https://github.com/MakieOrg/Makie.jl/pull/3041).
- Fixed automatic normal generation for meshes with shading and no normals [#3041](https://github.com/MakieOrg/Makie.jl/pull/3041).

## v0.19.7

Expand Down
12 changes: 11 additions & 1 deletion ReferenceTests/src/tests/figures_and_makielayout.jl
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,14 @@ end
)

f
end
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
188 changes: 143 additions & 45 deletions src/makielayout/blocks/axis3d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function initialize_block!(ax::Axis3)
end

matrices = lift(calculate_matrices, scene, finallimits, scene.px_area, ax.elevation, ax.azimuth,
ax.perspectiveness, ax.aspect, ax.viewmode)
ax.perspectiveness, ax.aspect, ax.viewmode, ax.xreversed, ax.yreversed, ax.zreversed)

on(scene, matrices) do (view, proj, eyepos)
cam = camera(scene)
Expand Down Expand Up @@ -67,18 +67,18 @@ function initialize_block!(ax::Axis3)
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)
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)
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)
add_gridlines_and_frames!(blockscene, scene, 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)
add_ticks_and_ticklabels!(blockscene, scene, ax, 1, finallimits, ticknode_1, mi1, mi2, mi3, ax.azimuth, ax.xreversed, ax.yreversed, ax.zreversed)
yticks, yticklabels, ylabel =
add_ticks_and_ticklabels!(blockscene, scene, ax, 2, finallimits, ticknode_2, mi2, mi1, mi3, ax.azimuth)
add_ticks_and_ticklabels!(blockscene, scene, ax, 2, finallimits, ticknode_2, mi2, mi1, mi3, ax.azimuth, ax.xreversed, ax.yreversed, ax.zreversed)
zticks, zticklabels, zlabel =
add_ticks_and_ticklabels!(blockscene, scene, ax, 3, finallimits, ticknode_3, mi3, mi1, mi2, ax.azimuth)
add_ticks_and_ticklabels!(blockscene, scene, ax, 3, finallimits, ticknode_3, mi3, mi1, mi2, ax.azimuth, ax.xreversed, ax.yreversed, ax.zreversed)

titlepos = lift(scene, scene.px_area, ax.titlegap, ax.titlealign) do a, titlegap, align

Expand Down Expand Up @@ -174,9 +174,25 @@ end
can_be_current_axis(ax3::Axis3) = true

function calculate_matrices(limits, px_area, elev, azim, perspectiveness, aspect,
viewmode)
viewmode, xreversed, yreversed, zreversed)

ori = limits.origin
ws = widths(limits)

limits = Rect3f(
(
ori[1] + (xreversed ? ws[1] : zero(ws[1])),
ori[2] + (yreversed ? ws[2] : zero(ws[2])),
ori[3] + (zreversed ? ws[3] : zero(ws[3])),
),
(
ws[1] * (xreversed ? -1 : 1),
ws[2] * (yreversed ? -1 : 1),
ws[3] * (zreversed ? -1 : 1),
)
)

ws = widths(limits)

t = Makie.translationmatrix(-Float64.(limits.origin))
s = if aspect === :equal
Expand Down Expand Up @@ -383,7 +399,7 @@ function dim2(dim)
end
end

function add_gridlines_and_frames!(topscene, scene, ax, dim::Int, limits, ticknode, miv, min1, min2)
function add_gridlines_and_frames!(topscene, scene, 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))
Expand All @@ -392,11 +408,14 @@ function add_gridlines_and_frames!(topscene, scene, ax, dim::Int, limits, tickno
d1 = dim1(dim)
d2 = dim2(dim)


tickvalues = @lift($ticknode[1])

endpoints = lift(limits, tickvalues, min1, min2) do lims, ticks, min1, min2
f1 = min1 ? minimum(lims)[d1] : maximum(lims)[d1]
f2 = min2 ? minimum(lims)[d2] : maximum(lims)[d2]
endpoints = lift(limits, tickvalues, min1, min2, xreversed, yreversed, zreversed) do lims, ticks, min1, min2, xrev, yrev, zrev
rev1 = (xrev, yrev, zrev)[d1]
rev2 = (xrev, yrev, zrev)[d2]
f1 = min1 rev1 ? minimum(lims)[d1] : maximum(lims)[d1]
f2 = min2 rev2 ? minimum(lims)[d2] : maximum(lims)[d2]
# from tickvalues and f1 and min2:max2
mi = minimum(lims)
ma = maximum(lims)
Expand All @@ -409,9 +428,11 @@ function add_gridlines_and_frames!(topscene, scene, ax, dim::Int, limits, tickno
xautolimits = false, yautolimits = false, zautolimits = false, transparency = true,
visible = attr(:gridvisible), inspectable = false)

endpoints2 = lift(limits, tickvalues, min1, min2) do lims, ticks, min1, min2
f1 = min1 ? minimum(lims)[d1] : maximum(lims)[d1]
f2 = min2 ? minimum(lims)[d2] : maximum(lims)[d2]
endpoints2 = lift(limits, tickvalues, min1, min2, xreversed, yreversed, zreversed) do lims, ticks, min1, min2, xrev, yrev, zrev
rev1 = (xrev, yrev, zrev)[d1]
rev2 = (xrev, yrev, zrev)[d2]
f1 = min1 rev1 ? minimum(lims)[d1] : maximum(lims)[d1]
f2 = min2 rev2 ? minimum(lims)[d2] : maximum(lims)[d2]
# from tickvalues and f1 and min2:max2
mi = minimum(lims)
ma = maximum(lims)
Expand All @@ -425,10 +446,16 @@ function add_gridlines_and_frames!(topscene, scene, ax, dim::Int, limits, tickno
visible = attr(:gridvisible), inspectable = false)


framepoints = lift(limits, scene.camera.projectionview, scene.px_area, min1, min2
) do lims, _, pxa, mi1, mi2
framepoints = lift(limits, scene.camera.projectionview, scene.px_area, min1, min2, xreversed, yreversed, zreversed
) do lims, _, pxa, mi1, mi2, xrev, yrev, zrev
o = pxa.origin

rev1 = (xrev, yrev, zrev)[d1]
rev2 = (xrev, yrev, zrev)[d2]

mi1 = mi1 rev1
mi2 = mi2 rev2

f(mi) = mi ? minimum : maximum
p1 = dpoint(minimum(lims)[dim], f(!mi1)(lims)[d1], f(mi2)(lims)[d2])
p2 = dpoint(maximum(lims)[dim], f(!mi1)(lims)[d1], f(mi2)(lims)[d2])
Expand Down Expand Up @@ -463,7 +490,7 @@ function to_topscene_z_2d(p3d, scene)
Point3f(p2d..., -10000)
end

function add_ticks_and_ticklabels!(topscene, scene, ax, dim::Int, limits, ticknode, miv, min1, min2, azimuth)
function add_ticks_and_ticklabels!(topscene, scene, ax, dim::Int, limits, ticknode, miv, min1, min2, azimuth, xreversed, yreversed, zreversed)

dimsym(sym) = Symbol(string((:x, :y, :z)[dim]) * string(sym))
attr(sym) = getproperty(ax, dimsym(sym))
Expand All @@ -480,13 +507,17 @@ function add_ticks_and_ticklabels!(topscene, scene, ax, dim::Int, limits, tickno
ticksize = attr(:ticksize)

tick_segments = lift(topscene, limits, tickvalues, miv, min1, min2,
scene.camera.projectionview, scene.px_area, ticksize) do lims, ticks, miv, min1, min2,
pview, pxa, tsize
f1 = !min1 ? minimum(lims)[d1] : maximum(lims)[d1]
f2 = min2 ? minimum(lims)[d2] : maximum(lims)[d2]
scene.camera.projectionview, scene.px_area, ticksize, xreversed, yreversed, zreversed) do lims, ticks, miv, min1, min2,
pview, pxa, tsize, xrev, yrev, zrev

f1_oppo = min1 ? minimum(lims)[d1] : maximum(lims)[d1]
f2_oppo = !min2 ? minimum(lims)[d2] : maximum(lims)[d2]
rev1 = (xrev, yrev, zrev)[d1]
rev2 = (xrev, yrev, zrev)[d2]

f1 = !(min1 rev1) ? minimum(lims)[d1] : maximum(lims)[d1]
f2 = (min2 rev2) ? minimum(lims)[d2] : maximum(lims)[d2]

f1_oppo = (min1 rev1) ? minimum(lims)[d1] : maximum(lims)[d1]
f2_oppo = !(min2 rev2) ? minimum(lims)[d2] : maximum(lims)[d2]

diff_f1 = f1 - f1_oppo
diff_f2 = f2 - f2_oppo
Expand Down Expand Up @@ -568,13 +599,20 @@ function add_ticks_and_ticklabels!(topscene, scene, ax, dim::Int, limits, tickno

onany(topscene,
scene.px_area, scene.camera.projectionview, limits, miv, min1, min2,
attr(:labeloffset), attr(:labelrotation), attr(:labelalign)
) do pxa, pv, lims, miv, min1, min2, labeloffset, lrotation, lalign
attr(:labeloffset), attr(:labelrotation), attr(:labelalign), xreversed, yreversed, zreversed
) do pxa, pv, lims, miv, min1, min2, labeloffset, lrotation, lalign, xrev, yrev, zrev

o = pxa.origin

f1 = !min1 ? minimum(lims)[d1] : maximum(lims)[d1]
f2 = min2 ? minimum(lims)[d2] : maximum(lims)[d2]
rev1 = (xrev, yrev, zrev)[d1]
rev2 = (xrev, yrev, zrev)[d2]
revdim = (xrev, yrev, zrev)[dim]

minr1 = min1 rev1
minr2 = min2 rev2

f1 = !minr1 ? minimum(lims)[d1] : maximum(lims)[d1]
f2 = minr2 ? minimum(lims)[d2] : maximum(lims)[d2]

# get end points of axis
p1 = dpoint(minimum(lims)[dim], f1, f2)
Expand All @@ -591,9 +629,9 @@ function add_ticks_and_ticklabels!(topscene, scene, ax, dim::Int, limits, tickno
diff = pp2 - pp1

diffsign = if dim == 1 || dim == 3
!(min1 min2) ? 1 : -1
!(min1 min2 revdim) ? 1 : -1
else
(min1 min2) ? 1 : -1
(min1 min2 revdim) ? 1 : -1
end

a = pi/2
Expand Down Expand Up @@ -848,11 +886,11 @@ function Makie.xlims!(ax::Axis3, xlims::Tuple{Union{Real, Nothing}, Union{Real,
error("Invalid xlims length of $(length(xlims)), must be 2.")
elseif xlims[1] == xlims[2]
error("Can't set x limits to the same value $(xlims[1]).")
# elseif all(x -> x isa Real, xlims) && xlims[1] > xlims[2]
# xlims = reverse(xlims)
# ax.xreversed[] = true
# else
# ax.xreversed[] = false
elseif all(x -> x isa Real, xlims) && xlims[1] > xlims[2]
xlims = reverse(xlims)
ax.xreversed[] = true
else
ax.xreversed[] = false
end

ax.limits.val = (xlims, ax.limits[][2], ax.limits[][3])
Expand All @@ -865,11 +903,11 @@ function Makie.ylims!(ax::Axis3, ylims::Tuple{Union{Real, Nothing}, Union{Real,
error("Invalid ylims length of $(length(ylims)), must be 2.")
elseif ylims[1] == ylims[2]
error("Can't set y limits to the same value $(ylims[1]).")
# elseif all(x -> x isa Real, ylims) && ylims[1] > ylims[2]
# ylims = reverse(ylims)
# ax.yreversed[] = true
# else
# ax.yreversed[] = false
elseif all(x -> x isa Real, ylims) && ylims[1] > ylims[2]
ylims = reverse(ylims)
ax.yreversed[] = true
else
ax.yreversed[] = false
end

ax.limits.val = (ax.limits[][1], ylims, ax.limits[][3])
Expand All @@ -882,11 +920,11 @@ function Makie.zlims!(ax::Axis3, zlims)
error("Invalid zlims length of $(length(zlims)), must be 2.")
elseif zlims[1] == zlims[2]
error("Can't set y limits to the same value $(zlims[1]).")
# elseif all(x -> x isa Real, zlims) && zlims[1] > zlims[2]
# zlims = reverse(zlims)
# ax.zreversed[] = true
# else
# ax.zreversed[] = false
elseif all(x -> x isa Real, zlims) && zlims[1] > zlims[2]
zlims = reverse(zlims)
ax.zreversed[] = true
else
ax.zreversed[] = false
end

ax.limits.val = (ax.limits[][1], ax.limits[][2], zlims)
Expand Down Expand Up @@ -1035,6 +1073,66 @@ function attribute_examples(::Type{Axis3})
title = "elevation = \$(elevation)π", viewmode = :fit)
end
fig
"""
),
],
:xreversed => [
Example(
name = "`xreversed` on and off",
code = """
using FileIO
fig = Figure()
brain = load(assetpath("brain.stl"))
ax1 = Axis3(fig[1, 1], title = "xreversed = false")
ax2 = Axis3(fig[2, 1], title = "xreversed = true", xreversed = true)
for ax in [ax1, ax2]
mesh!(ax, brain, color = getindex.(brain.position, 1))
end
fig
"""
),
],
:yreversed => [
Example(
name = "`yreversed` on and off",
code = """
using FileIO
fig = Figure()
brain = load(assetpath("brain.stl"))
ax1 = Axis3(fig[1, 1], title = "yreversed = false")
ax2 = Axis3(fig[2, 1], title = "yreversed = true", yreversed = true)
for ax in [ax1, ax2]
mesh!(ax, brain, color = getindex.(brain.position, 2))
end
fig
"""
),
],
:zreversed => [
Example(
name = "`zreversed` on and off",
code = """
using FileIO
fig = Figure()
brain = load(assetpath("brain.stl"))
ax1 = Axis3(fig[1, 1], title = "zreversed = false")
ax2 = Axis3(fig[2, 1], title = "zreversed = true", zreversed = true)
for ax in [ax1, ax2]
mesh!(ax, brain, color = getindex.(brain.position, 3))
end
fig
"""
),
Expand Down
6 changes: 6 additions & 0 deletions src/makielayout/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1597,6 +1597,12 @@ end
yautolimitmargin = (0.05, 0.05)
"The relative margins added to the autolimits in z direction."
zautolimitmargin = (0.05, 0.05)
"Controls if the x axis goes rightwards (false) or leftwards (true) in default camera orientation."
xreversed::Bool = false
"Controls if the y axis goes leftwards (false) or rightwards (true) in default camera orientation."
yreversed::Bool = false
"Controls if the z axis goes upwards (false) or downwards (true) in default camera orientation."
zreversed::Bool = false
end
end

Expand Down

0 comments on commit cf62375

Please sign in to comment.