From 119c4e9d425169bd430e7c925a8985b2b65eae24 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Wed, 30 Oct 2024 05:57:35 +0100 Subject: [PATCH] improve spring animations --- docs/src/examples/three_springs.md | 6 +++--- ext/Render.jl | 24 ++++++++++++++---------- src/PlanarMechanics/components.jl | 4 ++++ src/forces.jl | 7 +++++-- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/docs/src/examples/three_springs.md b/docs/src/examples/three_springs.md index 31909eff..da08e52a 100644 --- a/docs/src/examples/three_springs.md +++ b/docs/src/examples/three_springs.md @@ -24,9 +24,9 @@ systems = @named begin bar1 = FixedTranslation(r = [0.3, 0, 0]) bar2 = FixedTranslation(r = [0, 0, 0.3]) spring1 = Multibody.Spring(c = 20, m = 0, s_unstretched = 0.1, fixed_rotation_at_frame_b=true, - r_rel_0 = [-0.2, -0.2, 0.2], radius=0.05, num_windings=10) - spring2 = Multibody.Spring(c = 40, m = 0, s_unstretched = 0.1, color=[0,0,0.3,1]) - spring3 = Multibody.Spring(c = 20, m = 0, s_unstretched = 0.1, radius=0.05, num_windings=10) + r_rel_0 = [-0.2, -0.2, 0.2], radius=0.05, num_windings=10, end_ratio=0.1) + spring2 = Multibody.Spring(c = 40, m = 0, s_unstretched = 0.1, color=[0,0,0.3,1], end_ratio=0.1) + spring3 = Multibody.Spring(c = 20, m = 0, s_unstretched = 0.1, radius=0.05, num_windings=10, end_ratio=0.1) end eqs = [connect(world.frame_b, bar1.frame_a) connect(world.frame_b, bar2.frame_a) diff --git a/ext/Render.jl b/ext/Render.jl index 53395abf..6cd726eb 100644 --- a/ext/Render.jl +++ b/ext/Render.jl @@ -661,7 +661,7 @@ function render!(scene, ::typeof(BodyShape), sys, sol, t) Makie.transform!(m, translation=r1, rotation=Q) end else - error("Shape $shape not supported") + error("""Shape $shape not supported, currently only supports "cylinder" and "box".""") end else T = get_frame_fun(sol, sys.frame_a) @@ -834,12 +834,13 @@ function render!(scene, ::Union{typeof(Spring), typeof(SpringDamperParallel)}, s n_wind = sol(sol.t[1], idxs=sys.num_windings) radius = sol(sol.t[1], idxs=sys.radius) |> Float32 N = round(Int, sol(sol.t[1], idxs=sys.N)) + end_ratio = sol(sol.t[1], idxs=sys.end_ratio) |> Float32 thing = @lift begin r1 = Point3f(r_0a($t)) r2 = Point3f(r_0b($t)) - spring_mesh(r1,r2; n_wind, radius, N) + spring_mesh(r1,r2; n_wind, radius, N, end_ratio) end - plot!(scene, thing; color) + plot!(scene, thing; color, transparency=true) true end @@ -894,7 +895,7 @@ function render!(scene, ::Function, sys, sol, t, args...) # Fallback for systems false end -function spring_mesh(p1, p2; n_wind=6, radius=0.1f0, N=200) +function spring_mesh(p1, p2; n_wind=6, radius=0.1f0, N=200, end_ratio = 0.1) phis = range(0, n_wind*2π, length=N) d = p2 - p1 z = range(0, norm(d), length=N) # Correct length @@ -902,8 +903,10 @@ function spring_mesh(p1, p2; n_wind=6, radius=0.1f0, N=200) R = rot_from_line(dn) points = map(enumerate(phis)) do (i,phi) - x = radius*cos(phi) - y = radius*sin(phi) + n_ramp = N * end_ratio + ri = i <= n_ramp ? i/n_ramp : i > N-n_ramp ? (N-i)/n_ramp : 1 + x = ri*radius*cos(phi) + y = ri*radius*sin(phi) pᵢ = Point3f(x, y, z[i]) R * pᵢ + p1 @@ -1041,12 +1044,13 @@ function render!(scene, ::Union{typeof(P.Spring), typeof(P.SpringDamper)}, sys, n_wind = sol(sol.t[1], idxs=sys.num_windings) radius = sol(sol.t[1], idxs=sys.radius) |> Float32 N = sol(sol.t[1], idxs=sys.N) |> Int + end_ratio = sol(sol.t[1], idxs=sys.end_ratio) |> Float32 thing = @lift begin r1 = Point3f(r_0a($t)..., 0) r2 = Point3f(r_0b($t)..., 0) - spring_mesh(r1,r2; n_wind, radius, N) + spring_mesh(r1,r2; n_wind, radius, N, end_ratio, end_ratio) end - plot!(scene, thing; color) + plot!(scene, thing; color, transparency=true) true end @@ -1108,8 +1112,8 @@ function render!(scene, ::typeof(Multibody.BoxVisualizer), sys, sol, t) height = Float32(sol(sol.t[1], idxs=sys.height)) length = Float32(sol(sol.t[1], idxs=sys.length)) - length_dir = sol(sol.t[1], idxs=collect(sys.render_length_dir)) - width_dir = sol(sol.t[1], idxs=collect(sys.render_width_dir)) + length_dir = sol(sol.t[1], idxs=collect(sys.length_direction)) + width_dir = sol(sol.t[1], idxs=collect(sys.width_direction)) height_dir = normalize(cross(normalize(length_dir), normalize(width_dir))) width_dir = normalize(cross(height_dir, length_dir)) diff --git a/src/PlanarMechanics/components.jl b/src/PlanarMechanics/components.jl index c91ca242..ec640673 100644 --- a/src/PlanarMechanics/components.jl +++ b/src/PlanarMechanics/components.jl @@ -215,6 +215,7 @@ Linear 2D translational spring - `render = true` Render the spring in animations - `radius = 0.1` Radius of spring when rendered - `N = 200` Number of points in mesh when rendered +- `end_ratio = 0.0`: Ratio of the length of the spring [0, 0.5] that is rendered with decreasing radius at the ends. Set this to 0 to have uniform radius along the entire spring. # Connectors: - `frame_a` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque @@ -239,6 +240,7 @@ Linear 2D translational spring render = true, [description = "Render the spring in animations"] radius = 0.1, [description = "Radius of spring when rendered"] N = 200, [description = "Number of points in mesh when rendered"] + end_ratio = 0.0 end @variables begin @@ -352,6 +354,7 @@ Linear 2D translational spring damper model - `render = true` Render the spring in animations - `radius = 0.1` Radius of spring when rendered - `N = 200` Number of points in mesh when rendered +- `end_ratio = 0.0`: Ratio of the length of the spring that is rendered with decreasing radius at the ends. # Connectors: - `frame_a` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque @@ -379,6 +382,7 @@ Linear 2D translational spring damper model render = true, [description = "Render the spring in animations"] radius = 0.1, [description = "Radius of spring when rendered"] N = 200, [description = "Number of points in mesh when rendered"] + end_ratio = 0.0 end @variables begin diff --git a/src/forces.jl b/src/forces.jl index ac30aacf..1c746329 100644 --- a/src/forces.jl +++ b/src/forces.jl @@ -388,10 +388,11 @@ additional equations to handle the mass are removed. - `color = [0,0,1,1]`: Color of the spring when rendered - `radius = 0.1`: Radius of spring when rendered - `N = 200`: Number of points in mesh when rendered. Rendering time can be reduced somewhat by reducing this number. +- `end_ratio = 0.0`: Ratio of the length of the spring [0, 0.5] that is rendered with decreasing radius at the ends. Set this to 0 to have uniform radius along the entire spring. See also [`SpringDamperParallel`](@ref) """ -@component function Spring(; c, name, m = 0, lengthfraction = 0.5, s_unstretched = 0, num_windings=6, color=[0,0,1,1], radius=0.1, N=200, kwargs...) +@component function Spring(; c, name, m = 0, lengthfraction = 0.5, s_unstretched = 0, num_windings=6, color=[0,0,1,1], radius=0.1, N=200, end_ratio = 0.0, kwargs...) @named ptf = PartialTwoFrames() @unpack frame_a, frame_b = ptf pars = @parameters begin @@ -401,6 +402,7 @@ See also [`SpringDamperParallel`](@ref) bounds = (0, Inf), ] num_windings = num_windings, [description = "Number of windings of the coil when rendered"] + end_ratio = 0.0 color[1:4] = color radius = radius, [description = "Radius of spring when rendered"] N = N, [description = "Number of points in mesh when rendered"] @@ -501,7 +503,7 @@ f = c (s - s_{unstretched}) + d \\cdot D(s) where `c`, `s_unstretched` and `d` are parameters, `s` is the distance between the origin of `frame_a` and the origin of `frame_b` and `D(s)` is the time derivative of `s`. """ @component function SpringDamperParallel(; name, c, d, s_unstretched=0, - color = [0, 0, 1, 1], radius = 0.1, N = 200, num_windings = 6, kwargs...) + color = [0, 0, 1, 1], radius = 0.1, N = 200, num_windings = 6, end_ratio = 0.0, kwargs...) @named plf = PartialLineForce(; kwargs...) @unpack s, f = plf @@ -516,6 +518,7 @@ where `c`, `s_unstretched` and `d` are parameters, `s` is the distance between t radius = radius, [description = "Radius of spring when rendered"] N = N, [description = "Number of points in mesh when rendered"] num_windings = num_windings, [description = "Number of windings of the coil when rendered"] + end_ratio = end_ratio end # pars = collect_all(pars)