Skip to content

Commit

Permalink
Merge pull request #257 from rdeits/rd/drop-cassette
Browse files Browse the repository at this point in the history
Drop Cassette.jl by rewriting animation handlers
  • Loading branch information
rdeits authored Nov 8, 2024
2 parents f9a66eb + 69def60 commit ee42997
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 66 deletions.
4 changes: 1 addition & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
name = "MeshCat"
uuid = "283c5d60-a78f-5afe-a0af-af636b173e11"
authors = ["Robin Deits <[email protected]>"]
version = "0.16.3"
version = "1.0.0"

[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
Cassette = "7057c7e9-c182-5462-911a-8362d720325c"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
CoordinateTransformations = "150eb455-5306-5404-9cee-2592286d6298"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
Expand All @@ -26,7 +25,6 @@ Tar = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[compat]
Cassette = "0.2.5, 0.3"
Colors = "0.9, 0.10, 0.11, 0.12, 0.13"
CoordinateTransformations = "0.5, 0.6"
DocStringExtensions = "0.5, 0.6, 0.7, 0.8, 0.9"
Expand Down
8 changes: 4 additions & 4 deletions notebooks/animation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"metadata": {},
"outputs": [],
"source": [
"anim = Animation()\n",
"anim = Animation(vis)\n",
"\n",
"atframe(anim, 0) do\n",
" # within the context of atframe, calls to \n",
Expand Down Expand Up @@ -153,7 +153,7 @@
"metadata": {},
"outputs": [],
"source": [
"anim = Animation()\n",
"anim = Animation(vis)\n",
"\n",
"atframe(anim, 0) do\n",
" settransform!(vis[\"/Cameras/default\"], Translation(0, 0, 0))\n",
Expand All @@ -179,7 +179,7 @@
"metadata": {},
"outputs": [],
"source": [
"anim = Animation()\n",
"anim = Animation(vis)\n",
"\n",
"atframe(anim, 0) do\n",
" setprop!(vis[\"/Cameras/default/rotated/<object>\"], \"zoom\", 1.0)\n",
Expand Down Expand Up @@ -226,7 +226,7 @@
"metadata": {},
"outputs": [],
"source": [
"anim = Animation()\n",
"anim = Animation(vis)\n",
"atframe(anim, 0) do\n",
" setvisible!(vis[:sphere], false)\n",
" settransform!(vis[:box1], Translation(0.0, 0, 0))\n",
Expand Down
30 changes: 29 additions & 1 deletion src/MeshCat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ using Sockets: listen, @ip_str, IPAddr, IPv4, IPv6
using Base64: base64encode
using MsgPack: MsgPack, pack
using Pkg.Artifacts: @artifact_str
import Cassette
import FFMPEG
import HTTP
import Logging
Expand Down Expand Up @@ -102,6 +101,35 @@ abstract type AbstractMaterial end
include("util.jl")
include("trees.jl")
using .SceneTrees

struct AnimationContext
animation
frame::Int
end

"""
Low-level type which manages the actual meshcat server. See [`Visualizer`](@ref)
for the public-facing interface.
"""
mutable struct CoreVisualizer
tree::SceneNode
connections::Set{HTTP.WebSockets.WebSocket}
host::IPAddr
port::Int
server::HTTP.Server
animation_contexts::Vector{AnimationContext}

function CoreVisualizer(host::IPAddr = ip"127.0.0.1", default_port=8700)
connections = Set([])
tree = SceneNode()
port = find_open_port(host, default_port, 500)
core = new(tree, connections, host, port)
core.server = start_server(core)
core.animation_contexts = AnimationContext[]
return core
end
end

include("mesh_files.jl")
include("geometry.jl")
include("objects.jl")
Expand Down
21 changes: 12 additions & 9 deletions src/animations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,19 @@ function Base.merge!(a::AnimationClip, others::AnimationClip...)
end

struct Animation
clips::Dict{Path, AnimationClip}
visualizer::CoreVisualizer
default_framerate::Int
end
clips::Dict{Path, AnimationClip}

"""
Create a new animation. See [`atframe`](@ref) to adjust object poses or properties
in that animation.
"""
Create a new animation. See [`atframe`](@ref) to adjust object poses or properties
in that animation.
$(TYPEDSIGNATURES)
"""
Animation(visualizer::CoreVisualizer; fps=30) = new(visualizer, fps, Dict())
end

$(TYPEDSIGNATURES)
"""
Animation(fps::Int=30) = Animation(Dict{Path, AnimationClip}(), fps)

"""
Merge multiple animations, storing the result in `a`.
Expand All @@ -65,6 +67,7 @@ $(TYPEDSIGNATURES)
"""
function Base.merge!(a::Animation, others::Animation...)
for other in others
@assert a.visualizer === other.visualizer
@assert a.default_framerate == other.default_framerate
merge!(merge!, a.clips, other.clips) # merge clips recursively
end
Expand All @@ -80,7 +83,7 @@ The animations may involve the same properties or different properties
(animations of the same property on the same path will have their events
interleaved). All animations must have the same framerate.
"""
Base.merge(a::Animation, others::Animation...) = merge!(Animation(a.default_framerate), a, others...)
Base.merge(a::Animation, others::Animation...) = merge!(Animation(a.visualizer; fps=a.default_framerate), a, others...)

"""
Convert the `.tar` file of still images produced by the meshcat "record" feature
Expand Down
31 changes: 7 additions & 24 deletions src/atframe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,6 @@ end

js_position(t::Transformation) = convert(Vector, t(SVector(0., 0, 0)))

Cassette.@context AnimationCtx

function Cassette.overdub(ctx::AnimationCtx, ::typeof(settransform!), vis::Visualizer, tform::Transformation)
animation, frame = ctx.metadata
clip = getclip!(animation, vis.path)
_setprop!(clip, frame, "scale", "vector3", js_scaling(tform))
_setprop!(clip, frame, "position", "vector3", js_position(tform))
_setprop!(clip, frame, "quaternion", "quaternion", js_quaternion(tform))
end

function Cassette.overdub(ctx::AnimationCtx, ::typeof(setprop!), vis::Visualizer, prop::AbstractString, value)
animation, frame = ctx.metadata
clip = getclip!(animation, vis.path)
_setprop!(clip, frame, prop, get_property_type(prop), value)
end

function Cassette.overdub(ctx::AnimationCtx, ::typeof(setprop!), vis::Visualizer, prop::AbstractString, jstype::AbstractString, value)
animation, frame = ctx.metadata
clip = getclip!(animation, vis.path)
_setprop!(clip, frame, prop, jstype, value)
end

"""
Call the given function `f`, but intercept any `settransform!` or `setprop!` calls
and apply them to the given animation at the given frame instead.
Expand All @@ -81,7 +59,7 @@ Usage:
vis = Visualizer()
setobject!(vis[:cube], Rect(Vec(0.0, 0.0, 0.0), Vec(0.5, 0.5, 0.5)))
anim = Animation()
anim = Animation(vis)
# At frame 0, set the cube's position to be the origin
atframe(anim, 0) do
Expand All @@ -97,6 +75,11 @@ setanimation!(vis, anim)
```
"""
function atframe(f, animation::Animation, frame::Integer)
Cassette.overdub(AnimationCtx(metadata=(animation, frame)), f)
push!(animation.visualizer.animation_contexts, AnimationContext(animation, frame))
try
f()
finally
pop!(animation.visualizer.animation_contexts)
end
return animation
end
57 changes: 34 additions & 23 deletions src/visualizer.jl
Original file line number Diff line number Diff line change
@@ -1,24 +1,3 @@
"""
Low-level type which manages the actual meshcat server. See [`Visualizer`](@ref)
for the public-facing interface.
"""
mutable struct CoreVisualizer
tree::SceneNode
connections::Set{HTTP.WebSockets.WebSocket}
host::IPAddr
port::Int
server::HTTP.Server

function CoreVisualizer(host::IPAddr = ip"127.0.0.1", default_port=8700)
connections = Set([])
tree = SceneNode()
port = find_open_port(host, default_port, 500)
core = new(tree, connections, host, port)
core.server = start_server(core)
return core
end
end

function find_open_port(host, default_port, max_retries)
for port in default_port:(default_port + max_retries)
server = try
Expand Down Expand Up @@ -217,7 +196,15 @@ of its parents, so setting the transform of `vis[:group1]` affects the poses of
the objects at `vis[:group1][:box1]` and `vis[:group1][:box2]`.
"""
function settransform!(vis::Visualizer, tform::Transformation)
send(vis.core, SetTransform(tform, vis.path))
if !isempty(vis.core.animation_contexts)
ctx = last(vis.core.animation_contexts)
clip = getclip!(ctx.animation, vis.path)
_setprop!(clip, ctx.frame, "scale", "vector3", js_scaling(tform))
_setprop!(clip, ctx.frame, "position", "vector3", js_position(tform))
_setprop!(clip, ctx.frame, "quaternion", "quaternion", js_quaternion(tform))
else
send(vis.core, SetTransform(tform, vis.path))
end
vis
end

Expand All @@ -240,7 +227,29 @@ $(TYPEDSIGNATURES)
with the Base.setproperty! function introduced in Julia v0.7)
"""
function setprop!(vis::Visualizer, property::AbstractString, value)
send(vis.core, SetProperty(vis.path, property, value))
if !isempty(vis.core.animation_contexts)
ctx = last(vis.core.animation_contexts)
clip = getclip!(ctx.animation, vis.path)
_setprop!(clip, ctx.frame, property, get_property_type(property), value)
else
send(vis.core, SetProperty(vis.path, property, value))
end
vis
end

"""
Variation of `setprop!` which accepts an explicit type for the underlying JS property. This property type is only used within an animation context.
$(TYPEDSIGNATURES)
"""
function setprop!(vis::Visualizer, property::AbstractString, jstype::AbstractString, value)
if !isempty(vis.core.animation_contexts)
ctx = last(vis.core.animation_contexts)
clip = getclip!(ctx.animation, vis.path)
_setprop!(clip, ctx.frame, property, jstype, value)
else
send(vis.core, SetProperty(vis.path, property, value))
end
vis
end

Expand Down Expand Up @@ -275,3 +284,5 @@ For example, if you have `vis::Visualizer` with path `/meshcat/foo`, you can do
"""
Base.getindex(vis::Visualizer, path...) =
Visualizer(vis.core, joinpath(vis.path, path...))

Animation(vis::Visualizer, args...; kw...) = Animation(vis.core, args...; kw...)
4 changes: 2 additions & 2 deletions test/visualizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -236,15 +236,15 @@ end
end

@testset "Animation" begin
anim1 = Animation()
anim1 = Animation(vis)
atframe(anim1, 0) do
settransform!(vis[:shapes][:box], Translation(0., 0, 0))
end
atframe(anim1, 30) do
settransform!(vis[:shapes][:box], Translation(2., 0, 0) LinearMap(RotZ/2)))
end
setanimation!(vis, anim1)
anim2 = Animation()
anim2 = Animation(vis)
atframe(anim2, 0) do
setprop!(vis["/Cameras/default/rotated/<object>"], "zoom", 1)
end
Expand Down

2 comments on commit ee42997

@rdeits
Copy link
Owner Author

@rdeits rdeits commented on ee42997 Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register()

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/118942

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v1.0.0 -m "<description of version>" ee429975297207bf7a131de0a3ea4ef5fede5061
git push origin v1.0.0

Please sign in to comment.