[Code] AnimateSelection Example v1.0.1
-
Thank you
I will study this code very carefully so I can learn how to make changes if needed to accomplish what I want.
-
Notice how:
-
No
$
global variables are used: -
Instead
@@
module variables are used. -
All code is executing (defined and evaluated,) within the Author's namespace.
-
(With the exception of the
require('sketchup.rb')
statement at the top. It also could have been put within the Author's namespace, but serves to provide an exit if the file is loaded into standard Ruby outside SketchUp.)
-
-
# AnimateSelection Example # # Contributors; # Martin Rinehart, Chris Fullmer, Dan Rathbun # # Version ; 1.0.0 # # Bug Fixes / Enhancements; # Version 1.0.1 October 23, 2012 Todd Burch # 1. @elapsed not being calculated properly # 2. Group entire selection to perform just 1 transformation per # frame, and surround all that within a commit scope. # require('sketchup.rb') module Author # <-- replace with the name of YOUR toplevel namespace module module AnimateSelection VERSION ||= '1.0.1' @@menu_loaded ||= false @@delay ||= 0.5 @@duration ||= 15 @@vlength ||= 1.to_l class Animate def initialize( objs, trans, delay = 0.5, duration = 15 ) @delay = delay.to_f @t = trans @objs = objs @start = Time.now.to_i @duration = duration @elapsed = 0 end def nextFrame( view ) model = view.model # new code V1.0.1 model.start_operation("Animation Move") # new code V1.0.1 group = model.active_entities.add_group(@objs) ; # new code V1.0.1 group.transform!( @t ) # new code V1.0.1 group.explode # new code V1.0.1 model.commit_operation # new code V1.0.1 @elapsed = Time.now.to_i - @start # new code V1.0.1 if @elapsed < @duration view.show_frame( @delay ) else return false # stops the animation end end def pause() @start = 0 end def resume() @start = Time.now.to_i end def stop() puts("Animation Complete (#{@elapsed} secs)") end end # class Animate class << self # Proxy class of AnimateSelection module def animate(objs,vec,view=Sketchup.active_model.active_view) # t = Geom;;Transformation.new(vec) # view.animation = Animate.new(objs,t,@@delay,@@duration) # end def delay_input(title='Enter Delay') # prompt = 'Frame Delay (secs); ' default = @@delay num = UI.inputbox( [prompt], [default], title ) # if num @@delay = set_delay(num[0]) end # end def set_delay(num=0.5) @@delay = num.to_f rescue 0.5 end def duration_input(title='Enter Duration') # prompt = 'Animate Duration (secs); ' default = @@duration num = UI.inputbox( [prompt], [default], title ) # if num @@duration = set_duration(num[0]) end # end def set_duration(num=15) @@duration = num.to_i rescue 15 end def vector_input(title='Enter Vector Length') # prompt = 'Vector length; ' default = @@vlength # Length class instance vec = UI.inputbox( [prompt], [default], title ) # if vec @@vlength = vec[0] if @@vlength != vec[0] return vec[0] else return false end # end end # proxy class unless @@menu_loaded # Build Popup Context Menu; UI.add_context_menu_handler {|popup| sel = Sketchup.active_model.selection unless sel.empty? # show the submenu; objs = sel.to_a view = sel.model.active_view animenu = popup.add_submenu('Animate Selection') # animenu.add_item('Vector Animate X') { vecX = vector_input('Vector X Length') vec = Geom;;Vector3d.new(vecX,0,0) animate(objs,vec,view) } animenu.add_item('Vector Animate Y') { vecY = vector_input('Vector Y Length') vec = Geom;;Vector3d.new(0,vecY,0) animate(objs,vec,view) } animenu.add_item('Vector Animate Z') { vecZ = vector_input('Vector Z Length') vec = Geom;;Vector3d.new(0,0,vecZ) animate(objs,vec,view) } animenu.add_separator animenu.add_item('Set Duration ...') { duration_input() } animenu.add_item('Set Frame Delay ...') { delay_input() } # end } # Mark file as loaded once; @@menu_loaded = true file_loaded("AnimateSelection;#{File.basename(__FILE__)}") end end # module AnimateSelection end # Author's toplevel namespace module
-
Good Job Todd.
Add your name to the contributors.
-
Why make a group to do a bulk transformation? Why not just apply the transformation to all entities with .transform_entities ? Should be even less overhead with out group and explode. Explode can even mess about with geometry as it triggers merge and intersection.
ent.parent.entities.transform_entities(@t,@objs)
-
I made a group because as the model was being twisted and torqued out of shape due to moving one entity at a time, SU was also leaving some bits un-transformed (obviously a bug) and that caused the model to be incorrect at the end of the animation.
Your solutions works fine too, removing
group = model.active_entities.add_group(@objs) ; group.transform!( @t ) group.explode
and replacing it with
@objs[0].parent.entities.transform_entities(@t,@objs)
-
OK I see that
@elapsed
is still not correct.The intention is that when the animation is paused, the "clock stops running", and starts again when it resumes.
What was the error you were seeing Todd?
-
Ha! No such thing as not working correct when there are no statements or comments about what it should be doing! BTW, there is no mechanism to pause this, so how was that supposed to work? V2?
As I said, the model is moved one entity at a time. First, an edge, then a face, then a group, etc...
Here are two pictures that show 2 bugs in the original code. There is a face and then 2 connected lines outside the face (7 entities total). You can see the starting position (Before) and the ending position (After) when the animation is complete.
bug1: The angle of the two connected lines is now turned inside out.
bug2: Because of the user-chosen duration and frame delay, the square, at the end of the animation, is now a skewed polygon, since the animation ended before the square could be transformed into square again (since it is being moved a piece at a time).
EDIT: FYI: for my example, the square is 30" each side, the delay is 1 second and the duration is 15 seconds.
-
@unknownuser said:
Ha! No such thing as not working correct when there are no statements or comments about what it should be doing! BTW, there is no mechanism to pause this, so how was that supposed to work? V2?
Yep.. ya' got me there. I did not read the API Dictionary well, and assumed the stop and start buttons work for custom Animations (which the docs say does not yet work. But implies perhaps they will in the future.)
So the
pause()
andresume()
methods either need to come out, or... create our own animation control toolbar.One thing I did not have time to do was "steal" code from the "linetool.rb" and add a "Draw Move Vector" option.
-
@unknownuser said:
As I said, the model is moved one entity at a time. First, an edge, then a face, then a group, etc...
Ok I get it now. I did not notice because the OP in the original thread that instigated this was moving groups. And I tested it moving a grouped manifold cube.
The OP (I think,) never had the else clause that moved individual entities. I believe that it was I that added that in as a "bonus" (which it now turns out to be a headache.)
Undo ops: It was discussed in the other thread, that using
move!()
might be better so that the undo stack is not cluttered.I did not make that change because of course it only works on groups and instances, not primitives. (And I don't remember whether I thought of grouping primitives like you did or not. Simply, it was aimed at the request of the Op in the companion thread, which has a link to this one, in order to show how to use the animation class in the manner he wanted. I had to get it posted that day, and then I moved on to other projects.)
So.. again good debugging job. I also agree this example needs to be made more general. But bear in mind it's really a teaching example, and not meant to be a plugin, really.
How about a "Undoable" menu option (checked/unchecked) ?
If checked an undo operation is created (but I would propose it wrap the entire animation, if possible,) and the code usestransform!()
; if unchecked the code usesmove!()
Just thinking out loud... Examples should clear and as simple as possible. It we get too fancy the answer to the original question becomes obscured. The question was:
"How do I use the API's Animation class to move a group in a certain direction."Ordinarily the coder will hard code the duration, delay and the vector will come from some other part of his code. I added in the popup menu, and input boxes for delay and duration, only for development purposes. Ie, the coder would play with the settings, until they liked what happened then use those values to assign to the instance variables.
Here, I am thinking I may have confused things a bit by adding in these UI features. (It makes the example look like a plugin, although it is not intended to be. I thought it would be nice to be able to fire the animation via the menu, rather than force the "learner" to type a method name at the Ruby Console.)
-
I understood it was an example, but the logic of the code told me it accepted primitives, and primitives didn't work like they should have, nor did the duration work as expected.
As an example, for its intent, it's fine and I would not bother (other than adding comments for the learner) with any additional enhancements.
Todd
Advertisement