Subtract one multiple-instanced box from an outer box

edited August 2013 in Ruby Scripting

Hi,

When I run the following script that I wrote, any boxes on layer2 are subtracted from any overlapping boxes on layer1. However the boxes have to all be in the same cell.

module BoxBoxSubtract

  app = RBA::Application.instance
  mw = app.main_window

  lv = mw.current_view
  if lv == nil
    raise "No view selected"
  end

  lv.transaction("Box box subtract")
  begin

    # put code here
    outerboxes=[] #layer 401
    innerboxes=[] #layer 400
    result_cell=[]
    result_layer=[]

    lv.each_object_selected do |obj|

      shape = obj.shape
      layout = lv.cellview(obj.cv_index).layout

      lnum = 1 # GDS Layer 1
      dtnum = 0
      outerbox_layer = layout.layer_indices.find do |li|
        info = layout.get_info(li)
        info.layer == lnum && info.datatype == dtnum
      end
      lnum = 2 # GDS Layer 2
      dtnum = 0
      innerbox_layer = layout.layer_indices.find do |li|
        info = layout.get_info(li)
        info.layer == lnum && info.datatype == dtnum
      end
      lnum = 3 # GDS Layer 3
      dtnum = 0
      result_layer = layout.layer_indices.find do |li|
        info = layout.get_info(li)
        info.layer == lnum && info.datatype == dtnum
      end

      if shape.is_box? && obj.layer == outerbox_layer
        # add it to the array
        outerboxes << obj
        # save a reference to the cell it's on, as this is the one we'll write our result on
        result_cell = layout.cell(obj.cell_index)
      elsif shape.is_box? && obj.layer == innerbox_layer
        # add it to the array
        innerboxes << obj
      end
    end

    test1 = []
    test2 = []
    outerboxes.each do |b|
      test1 << RBA::Polygon::new(b.shape.box)
    end
    innerboxes.each do |b|
      test2 << RBA::Polygon::new(b.shape.box)
    end

    ep = RBA::EdgeProcessor::new
    outerMinusInner = ep.boolean_p2p( test1 ,  test2 , RBA::EdgeProcessor::ModeANotB, false, false)
    outerMinusInner.each do |p|
      result_cell.shapes(result_layer).insert(p)
    end

    p 'Done'

    ensure 
    lv.commit
  end
end

Now I need to extend that to operate over multiple cells.

Imagine in the most general case I have three cells: "top", "child1", "child2".

On "child1" there is a large box.

On "child2" there is a small box.

On "top", child1 and child2 are instanced, overlapping each other.

When I instance multiple instances of child2 (multiple small boxes on top of one big box), I want the result to be the big boxes minus ALL the small boxes.

Any ideas how to access those cells? I tried duplicating all small boxes onto result_layer first, then doing the boolean subtraction. (Basically, replace

# add it to the array
innerboxes << obj

with

# add it to the array
new_shape = obj.shape.dup
result_cell.shapes(innerbox_layer).insert(new_shape)
innerboxes << new_shape

HOWEVER, this only seems to duplicate one box over to result_cell, not all instanced boxes in their relative positions; and besides it is strange -- the duplicated cell does not show up at the same xy location as the original instanced cell! I guess I need to find the xy location of the instanced cells, then duplicate onto result_cell and place them to exactly fall on top of their original instances. But have searched and cannot seem to find a way to get the xy location of the instance!

Hope that is clear. Any ideas? Thanks!

Comments

  • edited November -1

    Hi David,

    again, please be patient. I'll need some days to comment on this. Acually I am on vacation and my laptop's battery is running low ... :-)

    Matthias

  • edited September 2013

    Hi David,

    it's basically quite simple - you have to apply the relevant transformations. It's easy if you choose to output the results to the current cell, because then the transformation is readily available through ObjectInstPath#trans as a CplxTrans object. In order to stay within integer coordinates we have to create a ICplxTrans object thereof:

    result_cell = lv.active_cellview.cell
    result_layout = lv.active_cellview.layout
    
    test1 = []
    test2 = []
    outerboxes.each do |b|
      # transform into the current cell's space (use a ICplxTrans object to avoid conversion to floating-point coordinates)
      test1 << b.shape.polygon.transformed_cplx(RBA::ICplxTrans::from_trans(b.trans))
    end
    innerboxes.each do |b|
      # see above.
      test2 << b.shape.polygon.transformed_cplx(RBA::ICplxTrans::from_trans(b.trans))
    end
    
    ep = RBA::EdgeProcessor::new
    outerMinusInner = ep.boolean_p2p(test1, test2, RBA::EdgeProcessor::ModeANotB, false, false)
    outerMinusInner.each do |p|
      result_cell.shapes(result_layer).insert(p)
    end
    

    (BTW: I'd suggest to take the layer search out of the each_object_selected loop. Plus I have used Shape#polygon which is more generic than using Shape#box and creating a polygon from that box).

    The above code does not account for the case of multiple loaded layouts. Basically the two kind of shapes can reside in different layouts then. The corresponding extension is possible but adds a lot of special case handling and additional transformations because of potentially different database units to that code.

    Matthias

  • edited November -1

    Fantastic, thanks! I didn't understand about this transformation meant before. The code is working and I paste final code below for future reference of anyone reading this. It may not be the most efficient or best code but it works..

    module BoxBoxSubtract
    
      app = RBA::Application.instance
      mw = app.main_window
    
      lv = mw.current_view
      if lv == nil
        raise "No view selected"
      end
    
      lv.transaction("Box box subtract")
      begin
    
        layout = RBA::Application::instance.main_window.current_view.active_cellview.layout
    
        outerboxes=[] #layer 401
        innerboxes=[] #layer 400
        result_cell = lv.active_cellview.cell
        result_layout = lv.active_cellview.layout
        result_layer=[]
    
        lnum = 1 # GDS Layer 1
        dtnum = 0
        outerbox_layer = layout.layer_indices.find do |li|
          info = layout.get_info(li)
          info.layer == lnum && info.datatype == dtnum
        end
        lnum = 2 # GDS Layer 2
        dtnum = 0
        innerbox_layer = layout.layer_indices.find do |li|
          info = layout.get_info(li)
          info.layer == lnum && info.datatype == dtnum
        end
        lnum = 3 # GDS Layer 3
        dtnum = 0
        result_layer = layout.layer_indices.find do |li|
          info = layout.get_info(li)
          info.layer == lnum && info.datatype == dtnum
        end
    
        lv.each_object_selected do |obj|
    
          shape = obj.shape
    
          if shape.is_box? && obj.layer == outerbox_layer
            outerboxes << obj
          elsif shape.is_box? && obj.layer == innerbox_layer
            innerboxes << obj
          end
        end
    
        outer = []
        inner = []
    
        outerboxes.each do |b|
          # transform into the current cell's space (use a ICplxTrans object to avoid conversion to floating-point coordinates)
          outer << RBA::Polygon::new(b.shape.box).transformed_cplx(RBA::ICplxTrans::from_trans(b.trans)) #test1 << RBA::Polygon::new(b.shape.box)
        end
        innerboxes.each do |b|
          # see above.
          inner << RBA::Polygon::new(b.shape.box).transformed_cplx(RBA::ICplxTrans::from_trans(b.trans)) #b.shape.polygon.transformed_cplx(RBA::ICplxTrans::from_trans(b.trans)) #test2 << b.polygon
        end
    
        ep = RBA::EdgeProcessor::new
        outerMinusInner = ep.boolean_p2p( outer ,  inner , RBA::EdgeProcessor::ModeANotB, false, false)
        outerMinusInner.each do |p|
          result_cell.shapes(result_layer).insert(p)
        end
    
        p 'Done'
    
        ensure 
        lv.commit
      end
    end
    
  • hello David, I am not familiar to script, but I suffered similar problem. if I have child1 and child2 are polygon, i dont need transformed_cplx. Is it correct?

  • I don't think it makes sense to do this operation box by box. You could do it but then first you have to transform all the shapes up to a common top cell which is more work. Instead do a Boolean "not" operation on the shapes of two layers. In Python something like:

            sp = kl.ShapeProcessor()
            sp.boolean(lyo, top_cell, layer1, lyo, top_cell, layer2,
                       top_cell.shapes(layer3), kl.EdgeProcessor.ModeNot, True, False, False)
    

    The resulting shapes are in layer3, as polygons. See the docs for kl.ShapeProcessor().

  • I'd like to point out that the original post is almost ten years old ;)

    Today there is the DRC functionality for which this problem boils down to a layer generation operation. If you use deep mode, hierarchy is preserved as far as possible. So the following two-liner may do the job:

    # subtracts boxes from layer 2 from layer 1 and writes the result to layer 100
    deep
    (input(1, 0) - input(2, 0)).output(100, 0)
    

    DRC is very powerful - you can do implement more layer generation logic there.

    Matthias

Sign In or Register to comment.