A little help needed on getting started on animation and scenes using Ruby. Yes, I am comfortable
with developing plugins for Sketchup. My question is as follows. I see two sets of APIs for animation. One is the Animation object and using the nextFrame method (su_examples has a animation.rb which manipulates camera). My requirement is moving multiple objects and I see adding scenes/pages and adding frameChangeObservers to pages as the second method. Also, in order to kickoff
an animation thru script I need to do active_model.pages.selected_page= xxx .
From the wisdom of the community, what is recommended ?
Write code inside Animation.nextFrame to move objects without pages/scenes
Write code to add pages for each scene and loop through selected_pages in a loop
Write code to add pages for each scene and add frameChangeObserver and let user kick off animation through the sketchup interface.
A different code flow…
Yes, I am aware of a few commercial plugins like su animate and keyframe animation. But I may need to customise a lot and do not have the luxury of using off the shelf plugins.
I use Sketchup for animation as a hobby and have some comments:
One is the Animation object and using the nextFrame method . . .
I find creating a class to handle nextFrame just creates extra code that makes the program more difficult to understand. A benefit it offers is that when the nextFrame message handler is complete, it returns control to Sketchup and you can interact with the model between each frame.
I see adding scenes/pages and adding frameChangeObservers to pages as the second method
If you need to have real animation during Sketchup’s built-in scene-to-scene camera interpolation, then frame change observers allow that. One of the arguments to the “frameChange” method is a ratio that allows the developer to know how far the scene’s camera interpolation has progressed, allowing a potential “real time” animation.
I find observers unintuitive, and Sketchup doesn’t allow user’s to purge all observers. Although Sketchup can write video files of scene-to-scene animation, it does not run any Ruby code during the “File…Export…Animation” menu option.
A different code flow…
The “nextFrame” and “frameChange” methods are message handlers called by Sketchup, and not by any developer code. I find they create additional unnecessary code and are less intuitive. The vast majority of my animations involve the camera and grouped objects. If a developer uses the “move!” method that groups and component instances support, then the animation isn’t slowed down by logging of edit undo operations, and it isn’t necessary to include edit…undo exception handling. Since the “move!” method by itself doesn’t refresh the view, it is necessary after repositioning models to perform “view.refresh”. If it is necessary to animate loose drawing objects like faces and edges, or vertices, then the “transform_by_vectors” and “transform_entities” methods are required (which can also animate group and components), but they do log edit undo operations. A developer can replace the “view.refresh” with “view.write_image” to write each animation frame, and then merge them together outside of Sketchup. The image writes though take quite a bit of time. The move and refresh statements are in a simpler loop that outputs a frame each time through the loop.
These programs are Ruby scripts, but they are not plugins and I do not develop them in the plugins folder or subfolders.
Bruce, any chance you can post or point to your animations? My interest can be found by searching the forum for Sketchup and SETI. Looking forward to knowing more!
For scene page transition animation speed and delay settings, see the Model Info > Animation panel.
Via Ruby, these are accessed via the Sketchup::Model#options() instance method, which returns a collection object of class OptionsManager, to access objects of class OptionsProvider.
Notice the global transition time is not exposed. We have to set them individually upon each scene page object (or set the first one and hope new scene pick up the value.)
For your own Animation class loop. Ruby has a global sleep() method that might be used for short pauses. But don’t pause very long, or else Windows will set SketchUp’s application to “Not Responding”.
Thanks Dan. I just noticed view.show_frame takes parameter which allows the frame to be redrawn after some time. That should cover it. I am toying with using a mix of Bruce’s suggestion of move! and animation class rather than scenes to reduce the complexity.
The code assumes that the first element in the entities collection is a group or component (on my startup template the Sketchup startup person). This program will return the model near its original position (not exactly because of precision errors), but the error is not noticeable.
If it is too quick, then increase the number of frames to perform the same amount of movement. The program above is very fast on my computer. Try increasing variable “number_of_frames” to 360 or 1000.
Comments:
The amount of time it takes to refresh one frame is related to the number of total drawing elements in the file. A given Sketchup file’s frame rate will not only vary from computer to computer, but the loading on the existing processor from other programs.
As long as the Ruby code is running, the user cannot interact with the rest of Sketchup until the program terminates. When animating for more than a few seconds, Windows does treat the app as “Not Responding” until the animation ends, but the animation continues to run.
due to the slowness of rotating large amounts of geometry, I’ve been trying to create a similar visual result by rotating the camera, targeted on the same entity.bounds.center…
to avoid the jump from camera.target to bounds.center I added an incremental step towards the desired target…
this is some test code for looking at the progression in Ruby Console…
# basic ingediants
model = Sketchup.active_model
view = model.active_view
cam = model.active_view.camera
ents = model.active_entities
sel = model.selection
bbc = sel[0].bounds.center.to_a rescue ents[0].bounds.center.to_a
# step camera target to bounds center
p a = cam.target.to_a
b = bbc # @@pt1.to_a #
count = 10
target = []
# test for the progression
count.times do
c = a.zip(b)
target = []
c.each do |i|
target << ( i[0] + ((i[1] - i[0])/count ))
end
a = target
count -= 1
p target if count.modulo(1) == 0
end
target == bbc
in my plugin it happens over a 360 degree rotation and needs a second spin to show what I really want…
is there a better way?
are there some clever maths that avoid array manipulation?
From what I understand, you just want to interpolate position (keeping camera direction the same). The target needs to be adjusted as well. I haven’t tested this, but Point3D linear combination could be used as follows:
Although you can use #offset or #offset! on Array or Geom::Point3d objects, many coders forget that they (and Geom::Vector3d objects,) can be transformed.
In this scenario it doesn’t matter whether #offset or #tranform is used as they both take a vector object.
Leaving the eye in the old place and sweeping the target between old target and bb center:
# given the basic ingredients:
bbc_vec = cam.target.vector_to(bbc)
if bbc_vec.length > 0
steps = 10
step_len = bbc_vec.length / steps
vec_t = bbc_vec.clone
vec_t.length = step_len
delay = 0.25 # secs
steps.times do |i|
sleep(delay) if i > 0
aim = cam.target.transform(vec_t)
cam.set(
cam.eye,
aim,
cam.eye.vector_to(aim).axes.y
)
view.refresh
end
end
EDIT:
So in my example the cam.eye argument in the cam.set call can also be transformed by the same vec_t, and then the cam.up vector should remain the same (and not need computing from the other arguments.)
Animating the camera avoids logging of edit…undo operations. Instead, you could select all the objects and group them into one entity. They could be rotated with a rotation transform
and the “move!” method “entity.move! rotation * entity.transformation”.
I wonder if you would notice any frame rate difference.
The bug here is that the selection set is not empty, but the items are not highlighted.
Calling sel.add(sel.to_a) does nothing. It does not remove them as the docs says it should, nor does it change the selection because the items are already there. Calling view.refresh or view.invalidate also does nothing.
The only workaround is:
a = sel.to_a
sel.remove(a)
sel.add(a)
Which is totally weird because the docs says the #add(), #remove() and #toggle() are all aliases for each other. And this does not work:
I see this also, (SketchUp 2016, 2015 & 2014) but it happens with any kind of transformation, even a vector translation.
It does not matter of it is a loop or just the nextFrame callback of an Animation object. The move!() method will only do a transform one (or two times at most.)
If I have puts calls in there to print the iterator step, and have started the animation from the Ruby console, I must click in the model window to advance the loop. Which is a bit weird.
The move!() method was designed to work within animations. What is going on !