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

      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