Editing: hierarchical propagation

Macro file: flatten.lym

This application provides two new toolbar entries bound to keys F7 and F8. The first function brings up all selected shapes and instances to the current cell level and removes them from their original cell. This makes sense only if the selection contains objects from subcells (hence not in "top level only" selection mode). The second function brings up such objects one level in hierarchy. Both functions just bring up objects along the selection path, not into all instances of the selected cell. They are very similar to the "Move up in hierarchy" function in the "Edit/Selection" menu.

The new functions can only be used in "Edit" mode and require version 0.16 or later.

This code demonstrates in particular: <ul> <li>How to use the selection set of objects</li> <li>How to modify geometrical objects (transform, erase, copy)</li> <li>How to implement undo/redo support, which is pretty simple using the LayoutView's "transaction" and "commit" methods</li> </ul>

Source code

module Examples
 
  # HINT: this simple implementation does not account 
  # for variant building - the shapes and instances are simply taken
  # out from the original shapes and therefore might disappear somewhere else.
  
  @f7_handler = RBA::Action.new
  @f7_handler.title = "Flatten"
  @f7_handler.shortcut = "F7"
  @f7_handler.on_triggered do 
  
    app = RBA::Application.instance
    mw = app.main_window
  
    lv = mw.current_view
    if lv == nil
      raise "Flatten: No view selected"
    end
  
    # start transaction for "undo"
    lv.transaction("Flatten")
  
    begin
  
      # because objects might be referenced multiple times (thru different hierarchy paths)
      # we must first copy and then delete them
  
      # copy & transform them
      lv.each_object_selected do |sel|
  
        cv = lv.cellview(sel.cv_index) 
        target = cv.cell 
  
        # only if not flat already ..
        if target.cell_index != sel.cell_index
  
          source = cv.layout.cell(sel.cell_index)
  
          if sel.is_cell_inst? 
  
            # copy and transform
            new_inst = target.insert(sel.inst)
            target.transform(new_inst, sel.source_trans)
  
          else
  
            # copy and transform
            target_shapes = target.shapes(sel.layer)
            new_shape = target_shapes.insert(sel.shape)
            target_shapes.transform(new_shape, sel.source_trans)
            
          end
  
        end
  
      end
  
      # delete the objects
      # HINT: since it is possible that a certain object is used multiple times, we need to test
      # each reference, if it is still valid (i.e. the object has not been deleted yet).
      lv.each_object_selected do |sel|
  
        cv = lv.cellview(sel.cv_index) 
        target = cv.cell 
  
        # only if not flat already ..
        if target.cell_index != sel.cell_index
  
          source = cv.layout.cell(sel.cell_index)
  
          if sel.is_cell_inst? 
            if source.is_valid?(sel.inst)
              source.erase(sel.inst)
            end
          else
            if source.shapes(sel.layer).is_valid?(sel.shape)
              source.shapes(sel.layer).erase(sel.shape)
            end
          end
  
        end
  
      end
  
    ensure
  
      # always execute that code:
  
      # commit transaction
      lv.commit
  
      # clear selection and cancel all other edit operations, so 
      # nothing refers to shapes that might have been deleted.
      lv.cancel
  
    end
  
  end
  
  # HINT: this simple implementation does not account 
  # for variant building - the shapes and instances are simply taken
  # out from the original shapes and therefore might disappear somewhere else.
  
  @f8_handler = RBA::Action.new
  @f8_handler.title = "Propagate"
  @f8_handler.shortcut = "F8"
  @f8_handler.on_triggered do 
  
    app = RBA::Application.instance
    mw = app.main_window
  
    lv = mw.current_view
    if lv == nil
      raise "Propagate: No view selected"
    end
  
    # start transaction for "undo"
    lv.transaction("Propagate")
  
    begin
  
      # because objects might be referenced multiple times (thru different hierarchy paths)
      # we must first copy and then delete them
  
      # copy & transform them
      lv.each_object_selected do |sel|
  
        cv = lv.cellview(sel.cv_index) 
  
        # only if not flat already ..
        if cv.cell_index != sel.cell_index
  
          source = cv.layout.cell(sel.cell_index)
  
          if sel.is_cell_inst? 
  
            # copy and transform
            if sel.path_length <= 2
              target = cv.cell 
            else
              target = cv.layout.cell(sel.path_nth(sel.path_length - 3).cell_inst.cell_index)
            end
            new_inst = target.insert(sel.inst)
            target.transform(new_inst, sel.path_nth(sel.path_length - 2).specific_cplx_trans)
  
          else
  
            # copy and transform
            if sel.path_length <= 1
              target = cv.cell 
            else
              target = cv.layout.cell(sel.path_nth(sel.path_length - 2).cell_inst.cell_index)
            end
            target_shapes = target.shapes(sel.layer)
            new_shape = target_shapes.insert(sel.shape)
            target_shapes.transform(new_shape, sel.path_nth(sel.path_length - 1).specific_cplx_trans)
            
          end
  
        end
  
      end
  
      # delete the objects
      # HINT: since it is possible that a certain object is used multiple times, we need to test
      # each reference, if it is still valid (i.e. the object has not been deleted yet).
      lv.each_object_selected do |sel|
  
        cv = lv.cellview(sel.cv_index) 
        target = cv.cell 
  
        # only if not flat already ..
        if target.cell_index != sel.cell_index
  
          source = cv.layout.cell(sel.cell_index)
  
          if sel.is_cell_inst? 
            if source.is_valid?(sel.inst)
              source.erase(sel.inst)
            end
          else
            if source.shapes(sel.layer).is_valid?(sel.shape)
              source.shapes(sel.layer).erase(sel.shape)
            end
          end
  
        end
  
      end
  
    ensure
  
      # always execute that code:
  
      # commit transaction
      lv.commit
  
      # clear selection and cancel all other edit operations, so 
      # nothing refers to shapes that might have been deleted.
      lv.cancel
  
    end
  
  end
  
  app = RBA::Application.instance
  mw = app.main_window
  
  menu = mw.menu
  menu.insert_separator("@toolbar.end", "name")
  menu.insert_item("@toolbar.end", "rba_flatten", @f7_handler)
  menu.insert_item("@toolbar.end", "rba_propagate", @f8_handler)

end