[code] undoable place_component() "MoveTool" clone

Continuing the discussion from:

Model.place_component component_definition, boolean cannot be undone?:

Here is an example of a single undoable place component operation.

It is not a tool, rather it is a command.

It achieves the single undo, via 3 linked operations. The first is attached to the following model#place_component() operation, and the last is attached to it’s predecessor.

It can move both compound (groups, images and component instances,) and primitive objects.

move_selection_set.rb (1.9 KB)


# encoding: UTF-8

module Author; end
module Author::SomePlugin

  extend self
  
  unless defined?(@@loaded)
    @@opname ||= "MoveObjects"
  end
  
  class DefSpy < Sketchup::DefinitionObserver
    OUTER = Module::nesting[1]
    def onComponentInstanceAdded(definition, instance)
      definition.remove_observer(self)
      UI.start_timer(1.0,false) {
        OUTER::remove_definition(definition, instance)
      }
    end
  end

  def move_selection_set(sel)
    #
    choice = UI::messagebox("Erase original selection ?",MB_YESNO)
    #
    return if choice == IDCANCEL
    #
    mod  = sel.model
    ents = mod.active_entities
    mod.start_operation(@@opname,true,true)
      #
      cdef = mod.definitions[@@opname]
      if cdef
        cdef.clear! if cdef.entities.size > 0
        grp = cdef.entities.add_group(sel.to_a)
        t = grp.transformation
        grp.explode
        if choice != IDYES
          ents.add_instance(cdef,t)
        end
      else
        grp = ents.add_group(sel.to_a)
        ins = grp.to_component
        cdef = ins.definition
        cdef.name= @@opname
        if choice == IDYES
          ins.erase!
        else
          ins.explode
        end
      end
      #
    mod.commit_operation
    #
    cdef.add_observer(DefSpy::new)
    mod.place_component(cdef)
    #
  end ###

  def remove_definition(definition, instance)
    #
    mod = definition.model
    mod.start_operation(@@opname,true,false,true)
      #
      instance.explode rescue nil
      if mod.definitions.respond_to?(:remove)
        mod.definitions.remove(definition)
      else # pre-2018
        definition.entities.clear! rescue nil
      end
      #
    mod.commit_operation
    #
  end ###


  if !defined?(@@loaded)
    UI.add_context_menu_handler {|popup|
      sel = Sketchup.active_model.selection
      if !sel.empty?
        popup.add_item(@@opname) { move_selection_set(sel) }
      end
    }
    @@loaded = true
  end

end

Tested in SketchUp 2016.

[2021-JAN-19] Updated to use DefinitionList#remove in SketchUp version 2018+.

1 Like

#1.Load model
model = Sketchup.active_model
abc_path = ‘C:\Users\jekye\Desktop\11.skp’
abc_def = model.definitions.load abc_path
model.place_component(abc_def)

#2.How can I explode the load model at the same time?
Sketchup.active_model.selection[0].explode

Please fix your post …

You might need to implement a DefinitionObserver as shown in the example above. After the instance is placed, in the onComponentInstanceAdded callback method you can start a timer that will explode the instance. The tricky part is that you should not do this until after SketchUp is done notifying all observers about the new instance object.

Also within the timer block you can purge the just loaded definition (abc_def) using the Sketchup::DefinitionsList#remove method that was added to the API since this example was posted.

I just learned, I don’t know how to write. Can you help me improve it and write it down? thank you!

If you would like to learn to code Ruby extensions, you can start here …

This topic thread and it’s example is written the way it is by design. It serves it’s purpose, as stated in the topic title.

Feel free to use it as a starting point. Modify it for your own use. (But I will not be modifying this example here in this topic thread.)

However, it uses operations and observers which are advanced API concepts. It will will take quite some time for a new coder to become familiar and confident in event-driven coding to use these concepts.

We ask here in this forum category that coders at least try to solve their own problems, and ask specific coding questions. It is not appropriate to ask others here in this category to do your work for you.


If you do not have the time or inclination to learn Ruby programming, then you can hire an experienced coder to do it for you. Please post such a request in the Commercial category: