sketchucation logo sketchucation
    • Login
    ℹ️ Licensed Extensions | FredoBatch, ElevationProfile, FredoSketch, LayOps, MatSim and Pic2Shape will require license from Sept 1st More Info

    [Code] AnimateSelection Example v1.0.1

    Scheduled Pinned Locked Moved Developers' Forum
    12 Posts 4 Posters 1.4k Views 4 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Dan RathbunD Offline
      Dan Rathbun
      last edited by Dan Rathbun

      AnimateSelection

      Here is a cleaned up Code Example based on topic: Help with Animation


      ➑ ****goto: ver 1.0.1 by Todd Burch


      Contributors: Martin Rinehart, Chris Fullmer, Dan Rathbun, Todd Burch

      #  AnimateSelection Example
      #
      #  Contributors;
      #  Martin Rinehart, Chris Fullmer, Dan Rathbun
      #
      #  Version ; 1.0.0
      
      require('sketchup.rb')
      
      module Author  # <-- replace with the name of YOUR toplevel namespace module
      
        module AnimateSelection
        
          VERSION ||= '1.0.0'
        
          @@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 )
              @objs.each {|ent|
                next unless ent.valid? # skip any deleted entity
                if ent.is_a?(Sketchup;;Group) ||
                    ent.is_a?(Sketchup;;ComponentInstance)
                  ent.transform!( @t )
                else
                  ent.parent.entities.transform_entities(@t,ent)
                end
              }
              @elapsed += (Time.now.to_i - @start)
              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
      

      I'm not here much anymore.

      1 Reply Last reply Reply Quote 0
      • renderizaR Offline
        renderiza
        last edited by

        Thank you

        I will study this code very carefully so I can learn how to make changes if needed to accomplish what I want.

        [url=https://www.sketchupcode.com/:z3kqsidd]My Extensions ...[/url:z3kqsidd]

        1 Reply Last reply Reply Quote 0
        • Dan RathbunD Offline
          Dan Rathbun
          last edited by

          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.)

          I'm not here much anymore.

          1 Reply Last reply Reply Quote 0
          • T Offline
            todd burch
            last edited by

            #  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
            
            
            1 Reply Last reply Reply Quote 0
            • Dan RathbunD Offline
              Dan Rathbun
              last edited by

              Good Job Todd. πŸ‘

              Add your name to the contributors.

              I'm not here much anymore.

              1 Reply Last reply Reply Quote 0
              • thomthomT Offline
                thomthom
                last edited by

                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)

                ❓

                Thomas Thomassen β€” SketchUp Monkey & Coding addict
                List of my plugins and link to the CookieWare fund

                1 Reply Last reply Reply Quote 0
                • T Offline
                  todd burch
                  last edited by

                  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)
                  
                  1 Reply Last reply Reply Quote 0
                  • Dan RathbunD Offline
                    Dan Rathbun
                    last edited by

                    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?

                    I'm not here much anymore.

                    1 Reply Last reply Reply Quote 0
                    • T Offline
                      todd burch
                      last edited by

                      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.

                      Before After

                      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.

                      1 Reply Last reply Reply Quote 0
                      • Dan RathbunD Offline
                        Dan Rathbun
                        last edited by

                        @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() and resume() 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.

                        I'm not here much anymore.

                        1 Reply Last reply Reply Quote 0
                        • Dan RathbunD Offline
                          Dan Rathbun
                          last edited by

                          @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 uses transform!(); if unchecked the code uses move!()


                          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'm not here much anymore.

                          1 Reply Last reply Reply Quote 0
                          • T Offline
                            todd burch
                            last edited by

                            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

                            1 Reply Last reply Reply Quote 0
                            • 1 / 1
                            • First post
                              Last post
                            Buy SketchPlus
                            Buy SUbD
                            Buy WrapR
                            Buy eBook
                            Buy Modelur
                            Buy Vertex Tools
                            Buy SketchCuisine
                            Buy FormFonts

                            Advertisement