Open hexaeder opened 10 months ago
Depth shift does do something but it's not what you're looking for:
I would say there are two ways you can go about this. The first is to continue with scatter, but remove the billboarding, i.e. that scatter markers are always drawn on a plane parallel to the screen. You'll need to move your scatter plot to markerspace = :data
and have Vec3 or Quaternion rotations
for this. For example:
fig = Figure()
ax = LScene(fig[1,1])
lines!(ax, [(1,0,0), (0,1,1)])
# utriangle points in +y dir, width in x dir, flat in z dir
rot =
Makie.rotation_between(Vec3f(0, 1, 0), Vec3f(-1, 1, 1)) * # y to line dir
Makie.rotation_between(Vec3f(1, 0, 0), Vec3f(0, 0, 1)) # x to z
scatter!(ax, [(.5,.5,.5)];
marker = :utriangle, markersize=0.5, markerspace = :data,
rotations = rot,
depth_shift = -1f-3 # push scatter forward to draw over line
)
fig
The other option is to go full 3D and use meshscatter
, like we do for arrows. E.g.:
fig = Figure()
ax = LScene(fig[1,1])
ps = Point3f[(1,0,0), (0,1,1)]
rot = Makie.rotation_between(Vec3f(0,0,1), ps[2] - ps[1])
len = norm(ps[2] - ps[1])
width = 0.02
# These may eventually move to GeometryBasics
cone = Makie.merge([
# origin, radius, normal, angular resolution
Makie._circle(Point3f(0, 0, -0.5), 0.5f0, Vec3f(0,0,-1), 16) ,
# start pos, end pos, start radius, end radius, angular resolution
Makie._mantle(Point3f(0, 0, -0.5), Point3f(0, 0, 0.5), 0.5f0, 0f0, 16)
])
tube = Makie.merge([
Makie._circle(Point3f(0), 0.5f0, Vec3f(0,0,-1), 16) ,
Makie._mantle(Point3f(0), Point3f(0, 0, 1), 0.5f0, 0.5f0, 16),
Makie._circle(Point3f(0, 0, 1), 0.5f0, Vec3f(0,0,1), 16)
])
meshscatter!(ax,
[(1,0,0)], markersize = [Vec3f(width, width, len)], rotation = [rot],
marker = tube
)
meshscatter!(ax,
[(.5,.5,.5)], markersize = 0.2, rotation = rot,
marker = cone
)
fig
Thanks for the examples! Unfortunately, both options bring the marker to the data space, which I'd like to avoid. This would create more problems in terms of determining a suitable marker size and so on.
Sure that dept_shift
is not what I am looking for? Shifting the markers all the way to the front might solve my problems... at least in some experimentation, I got it to work with -1f0
. On the other hand, I don't really understand that argument and it seems to have some global hidden state which I can't wrap my head around and which scares me:
fig = Figure()
ax = LScene(fig[1,1])
lines!(ax, [(1,0,0), (0,1,1)])
scatter!(ax, [(.5,.5,.5)]; markersize=100, depth_shift=0.01f0)
# brings to the back
fig = Figure()
ax = LScene(fig[1,1])
lines!(ax, [(1,0,0), (0,1,1)])
scatter!(ax, [(.5,.5,.5)]; markersize=100, depth_shift=-0.01)
# does not work, maybe because it's f64 not f32? But does not reset it either; still in the back
fig = Figure()
ax = LScene(fig[1,1])
lines!(ax, [(1,0,0), (0,1,1)])
scatter!(ax, [(.5,.5,.5)]; markersize=100, depth_shift=-0.01f0)
# Now it's in the front
for depth in [-0.01f0, -0.01, 0.01, 0.01f0]
fig = Figure()
ax = LScene(fig[1,1])
lines!(ax, [(1,0,0), (0,1,1)])
scatter!(ax, [(.5,.5,.5)]; markersize=100, depth_shift=depth)
save("depth$depth.png", fig)
end
# same output 4 times, always in the front, wat? seems to depend on the last state before the loop.
Ok maybe I should write my thoughts down a bit more.
Any ideas on how to achieve the CairoMakie-look?
I don't think you actually want that, because CairoMakie fully ignores depth from one plot object to the next. So if you just draw lots of markers and lines one or the other will be in front:
Scatter last | Lines last |
---|---|
I'm assuming what you actually want is for markers to be in front of the line segment they relate to, but not globally. So if there is another line that's closer to the viewer/camera I'm assuming you'd want that to still plot over the marker. To get that behavior you'd need to find the lowest depth of the line in the area of the marker/circle and put the marker at that depth.
With this example
the depth value of the line should be constant up to float precision, because (-1, 1, 1) is perpendicular to the default camera direction (-1, -1, -1). So you just need to shift it a little to get one in front of the other. (Which is what depth_shift does. I.e. the very last transformation is basically final_z = z + depth_shift
with final_z being responsible for the order of objects.)
If you rotate the camera the depth value you need changes. This is more or less the worst case: Here the depth values are:
Makie.project(ax.scene.camera, :data, :clip, Vec3f(0, 1, 1))[3] # 0.8039375
Makie.project(ax.scene.camera, :data, :clip, Vec3f(0.5))[3] # 0.8953843
Makie.project(ax.scene.camera, :data, :clip, Vec3f(1, 0, 0))[3] # 0.9442493
so you'd need to shift the marker by about -0.1 to get in front of the full line. But then it's also going to be in front of everything else that's behind the end point of this line, like labels, other lines, etc. If everything is the same color that's probably fine but then I think you actually just want to get rid of the artifacts around the lines, i.e. #3408 / https://discourse.julialang.org/t/hide-stroke-in-3d-scatter-using-makie-jl/106383? Because without those you can't tell what's in front: (no shift)
And if you do want to use more than one color, then you're going to have issues with arrow markers being in front of other objects that should be in front of the marker. And you also have to do a bunch of back-and-forth transforming to figure out how much shifting you need. That's probably going to be more expensive than some heuristic for finding an acceptable markersize for meshscatter or scatter.
Shifting the markers all the way to the front might solve my problems... at least in some experimentation, I got it to work with -1f0.
That's going to get you to the CairoMakie case but you may run into issues with markers getting clipped earlier than you want to, because on -1 < depth < 1
should be drawn.
does not work, maybe because it's f64 not f32? But does not reset it either; still in the back
Yea that might be missing a convert_attribute
same output 4 times, always in the front, wat? seems to depend on the last state before the loop.
Weird, maybe there is some screen reuse going on that's messing with typing as well? backlight
was doing something similar when it didn't have a convert_attribute method.
Any ideas on whats happening here? (overdraw)
Setting overdraw = true
makes the plot ignore the depth buffer, both read and write. So it plots over everything that's already there but doesn't prevent later plots from plotting over it. The axis happens to have transparency = true
which delays it until after all the transparency = false
plots, so it draws over the overdraw.
The white outline might be fixed in #4150 atm because I'm currently drawing to transparent backgrounds. The draw order issue with overdraw comes from the order of post processors. #4150 won't fix that, but it's a step towards making post processors more adjustable which is more or less required to fix it.
In 3D plots in
GraphMakie
, we overlay the line-segments based edge plot with scatter plots containing node and edge-arrow markers like this:In
GLMakie
this looks a bit weird because of the dept effect of the flat sprites:scatter!(...; depth_shift=x)
changes nothing andweirdly interacts with the LScene axis
Any ideas on whats happening here and how to achieve the CairoMakie-look?