Rounding wire corners

edited June 2013 in General
Hi,

I have some wires with orthogonal corners. I would like to round the corners. In other words, imagine a windy road through the mountains rather than a Manhattan-type xy road.

Is there some way to do this via scripting? I found the Edit>Selection>RoundCorners option, but of course this is for polygons.

Thanks,
David

Comments

  • edited November -1
    Sorry, I am new to ruby, but I wanted to also post what I have so far - for what it's worth. Basically I prompt for a radius value and then it iterates through all the paths. For each path I will iterate through the inner vertices (inner, meaning not the endpoints), do some math to convert vertex into a set of radially-changing points and replace the old (Manhattan) path with the new (curvy) one.

    However I am having trouble on the following things:
    1. I have a path called "shape" below. How do I return the number of vertices?
    2. How do I return a specific vertex (e.g. the third vertex)?
    3. How do I loop through only certain vertices? I want to loop through vertex 1 to N-1, where the vertex indices go from 0 to N. I know of each_point method, but this goes through each point. If you scroll down to the comment with the ### you will see the point where I'm unsure how to proceed.

    =================

    class MenuAction < RBA::Action
    def initialize( title, shortcut, &action )
    self.title = title
    self.shortcut = shortcut
    @action = action
    end
    def triggered
    @action.call( self )
    end
    private
    @action
    end

    $compute_total_area = MenuAction.new( "Convert wires", "" ) do

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

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

    radius = 15.0

    radius = RBA::InputDialog.get_double("Radius", "What radius?",15,5)
    #RBA::MessageBox.info("Result", "Radius is #{radius.value} um or meters", RBA::MessageBox.b_ok)

    lv.each_object_selected do |obj|

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

    if shape.is_path?
    RBA::MessageBox.info("Found path", "Found a path", RBA::MessageBox.b_ok)
    # Get the number of vertices
    #N = ???
    # Loop through the vertices
    #for i in 1..(N-1)
    # prev_vertex = ??? #This should point to the (i-1)th vertex
    # curr_vertex = ??? #This should point to the ith vertex
    # next_vertex = ??? #This should point to the (i+1)th vertex
    # #Here is where I put code that calculates the new multi-segment path
    #
    # #Here is where I put code to replace the old path with the new path
    #
    #end

    end

    end

    #RBA::MessageBox.info("Finished", "Finished converting wires", RBA::MessageBox.b_ok)

    end

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

    menu = mw.menu
    menu.insert_separator("tools_menu.end", "sep_calc_area")
    menu.insert_item("tools_menu.end", "compute_total_area", $compute_total_area)
  • edited June 2013

    Hi David,

    that is actually a good start.

    Now let's look how to proceed after you have checked that the shape is a path:

    # get all vertices
    pts = []
    shape.each_point { |p| pts.push(p) }
    
    # now you can modify the points. "pts" is a Ruby array of 
    # RBA::Point objects and you can use pts[n] to fetch the nth point for 
    # example ...
    
    # replace by the new path using the old width:
    shape.path = RBA::Path::new(pts, shape.path_width)
    

    If you're lucky, the selection remains valid after that operation. That is not necessarily the case, specifically if you change the shape type, so one has to be careful. "lv.cancel" at the end makes sure the selected shape is not used any longer and any pending operations are cancelled.

    You can also add undo support by wrapping your modification code into a block like this:

    lv.transaction("Description of my operation")
    begin
    
      # .. your code goes here ...
    
    ensure 
      lv.commit
    end
    

    Regards,

    Matthias

    BTW: The forum system allows entering code by indenting the lines (Markdown syntax). So far nobody seems to have noticed :-)

  • edited November -1
    Thanks, I'll give it a try.

    I noticed Markdown syntax radio button but unfortunately:

    1. It wasn't clear what kind of markdown/markup I should use. e.g. should I use something similar to wiki markup? Should I indent with spaces? Do I need e.g. <code></code> tags (;ltcode;gt;lt/code;gt)? I now see the page you linked to but it would be nice if there were a short description under the Markdown syntax radio button...

    2. After I submitted, I wanted to undo or edit the post, try the Markdown syntax radio button, and re-post, thus fixing the formatting. However there was no option to edit my post :)

    Just mentioning this because it is probably the same sequence that others who didn't notice Markdown radio button might be going through... So this might help some of the Markdown woes.

    David
  • edited November -1
    Oh never mind, there is an "edit" button! However it's at the top and I didn't see it because I looked at the bottom of my post. My fault...
  • edited July 2013

    I succeeded in doing this - at least a rough version (e.g. no error checking on manhattan geometry input etc). I pasted the code below in case future users are interested.

    However a few questions:

    1. How do I assign a shortcut key? I see in Macro Development you can click on Edit Properties and there is a place to insert a shortcut key, but I have tried "F5", "$", "C" etc all with no luck - I press the key in KLayout and nothing happens...?

    2. I'd like to actually leave the old wire and duplicate it, and act only on the duplicate. And also change the duplicate wire to a different GDS layer number. So in the end you have your original wire and then another duplicate wire on a different layer. I've tried various things but can't figure this out perhaps due to not understanding ruby syntax well enough -- it should be simple though..?

    3. Once I have those two wires I'd like to convert both to polygon or simplepolygon using the commands here http://www.klayout.de/doc/code/class_Path.html. But I can't figure out how to write those commands to work. Should be simple I just can't figure out how to write them. I've tried newpoly = shape.path.polygon and some other things but it's not drawing anything.

    4. Lastly, having two polygons, I hope to subtract one from the other and put the result on yet another layer. Also not having luck there. Could you pls provide a simple code snippit?

    Thanks,
    David

    ===

    class MenuAction < RBA::Action
      def initialize( title, shortcut, &action ) 
        self.title = title
        self.shortcut = shortcut
        @action = action
      end
      def triggered 
        @action.call( self ) 
      end
    private
      @action
    end
    
    $curve_90deg_bends_strip = MenuAction.new( "Curve 90deg bends - strip", "" ) do 
    
      app = RBA::Application.instance
      mw = app.main_window
    
      lv = mw.current_view
      if lv == nil
        raise "No view selected"
      end
    
      lv.transaction("Curve 90deg bends - strip")
      begin
    
        #r = 15.0 # default radius of curvature
        #r = RBA::InputDialog.get_double("Radius", "What radius?",radius,5)
        #r = radius/(obj.trans.mag * layout.dbu) # check this. I think this is 15 micron radius when r=15 with internal units taken into account
    
        # For now just use a hard-coded value
        r = 30 * 1000 #/(obj.trans.mag * layout.dbu)
    
        lv.each_object_selected do |obj|
    
          shape = obj.shape
          layout = lv.cellview(obj.cv_index).layout
    
          if shape.is_path?
            pts=[]
            shape.each_point { |p| pts.push(p) }
    
            i = 0
    
            newPathPoints=[]
            newPathPoints[0]=pts[0] # write the first vertex
    
            pts.each_cons(3) { |prevCurrNextPt| # Get a vector that includes the prev pt, the curr pt, and the next pt. Need all three to calculate the curve
              pr=prevCurrNextPt[0]
              cu=prevCurrNextPt[1]
              ne=prevCurrNextPt[2]
    
              pi=Math::PI
              if pr.x<cu.x && pr.y==cu.y && cu.x==ne.x && cu.y<ne.y
                thetaStart = 3*pi/2
                thetaEnd = 2*pi;
                dx = -r
                dy = r
              elsif pr.x<cu.x &&pr.y==cu.y && cu.x==ne.x && cu.y>ne.y
                thetaStart = pi/2
                thetaEnd = 0
                dx = -r
                dy = -r
              elsif pr.x==cu.x && pr.y<cu.y && cu.x<ne.x && cu.y==ne.y
                thetaStart = pi
                thetaEnd = pi/2
                dx = r
                dy = -r
              elsif pr.x==cu.x && pr.y>cu.y && cu.x<ne.x && cu.y==ne.y
                thetaStart = pi
                thetaEnd = 3*pi/2
                dx = r
                dy = r
              elsif pr.x==cu.x && pr.y>cu.y && cu.x>ne.x && cu.y==ne.y
                thetaStart = 2*pi
                thetaEnd = 3*pi/2
                dx = -r
                dy = r
              elsif pr.x == cu.x && pr.y<cu.y && cu.x>ne.x &&cu.y==ne.y
                thetaStart = 0
                thetaEnd = pi/2
                dx = -r
                dy = -r
              elsif pr.x>cu.x && pr.y==cu.y && cu.x==ne.x && cu.y>ne.y
                thetaStart = pi/2
                thetaEnd = pi
                dx = r
                dy = -r
              elsif pr.x>cu.x && pr.y==cu.y && cu.x==ne.x && cu.y<ne.y
                thetaStart = 3*pi/2
                thetaEnd = pi
                dx = r
                dy = r
              else
                RBA::MessageBox.info("Error","This shouldn't happen",RBA::MessageBox.b_ok)
              end
    
              #make a linearly spaced array from thetaStart to thetaEnd with curveNumPts number of points
              curveNumPts = 10
              dTheta = (thetaEnd-thetaStart)/(curveNumPts-1)
              theta = []
              for j in 0..curveNumPts-1
                theta[j] = j*dTheta + thetaStart
              end
              c        
              xPtsCurve = []
              yPtsCurve = []
              theta.each { |angle| # Loop through each new curvy point for a given vertex
                xPtsCurve = (cu.x + dx + r*Math::cos(angle))
                yPtsCurve = (cu.y + dy + r*Math::sin(angle))
    
                newPathPoints[i+1] = RBA::Point.new(xPtsCurve,yPtsCurve) 
                i+=1
              }
            }
    
            newPathPoints[i]=pts[pts.length-1] #write the last vertex
    
          end
    
          shape.path = RBA::Path::new(newPathPoints, shape.path_width)
    
        end
    
        p 'Done'
    
        ensure 
        lv.commit
      end
    
    end
    
    
    
    app = RBA::Application.instance
    mw = app.main_window
    
    menu = mw.menu
    menu.insert_separator("tools_menu.end", "sep_calc_area")
    menu.insert_item("tools_menu.end", "curve_90deg_bends_strip ", $curve_90deg_bends_strip)
    
  • edited November -1

    Hi David,

    I don't have time right now to look into your code, but I hope I can do so soon.

    Regarding the key assignment: it's working on my installation but under the following conditions:

    • The key must be pressed in the layout window (not inside the macro editor)
    • The key must not be assigned to any other function. For example "Shift+Z" is working on my installation.

    Regards,

    Matthias

  • edited July 2013

    Hi David,

    here are some answers to your question:

    For 2.) you'll have insert a shape rather than changing it. So

    # instead of
    shape.path = new_path
    
    # do:
    shape.shapes.insert(new_path)
    

    If I understand your request correctly for 3.) you want to replace one path by two polygons - one with the rounded corners and one as the original. That would be this code (shape is the original shape):

    orig_path = shape.path
    new_path = ... new path
    
    # replace original by polygon
    shape.polygon = orig_path.polygon
    
    # insert a new path
    shape.shapes.insert(new_path.polygon)
    

    And finally for 4.):

    orig_path = shape.path
    new_path = ... new path
    
    # the output cell (that is where the original shape is) and output layer index
    # (needs to be created in the layout)
    out_cell = layout.cell(obj.cell_index)
    out_layer = ...
    
    ep = RBA::EdgeProcessor::new 
    orig_minus_new = ep.boolean_p2p([ orig_path.polygon ], [ new_path.polygon ], 
                        RBA::EdgeProcessor::ModeANotB, false, false)
    orig_minus_new.each do |p|
      cell.shapes(out_layer).insert(p)
    end
    

    Hope that answers your question.

    Matthias

  • edited July 2013

    Thanks Matthias,

    Regarding the shortcut key - it still appears to not be working. Here are the steps I do:

    1. Open Macro Editor
    2. Open the script I want, inside [Local].
    3. Now I am viewing that script in the panel on the right. 
    4. Click the button that says "Edit properties of macro"
    5. Under Keyboard shortcut, type "Shift+Z" without quotation marks
    6. Press OK
    7. Press F5 to run current script.
    

    At this point I click on the layout editor so it is in focus, I press Shift+Z. The script does not run, but instead the Macro Editor comes back in focus - strange. Happens every time. At this point I close the macro editor to see if that makes a difference. I press Shift+Z again. Nothing happens. However the script itself works as evidenced by the fact I can run it from the Tools menu.

    What am I doing wrong?

    [Regarding the other items, I'll try these and get back to you.]

    Thanks!

  • edited November -1

    Hi David,

    I am still confident that it's working, because I still can reproduce the functionality both on Linux and Windows.

    Just a question: does the keyboard shortcut show up in the menu entry in the Tools menu? In my case I have used the following settings in the properties:

    • Description: "My Macro"
    • Show in menu: checked
    • Keyboard shortcut: Shift+Z

    The macro is a very simple one:

    RBA::MessageBox::warning("warn", "ok", 0)
    

    All other fields in the properties dialog are empty.

    Then the "Tools" menu shows my macro as "My Macro Shift+Z" under "Macros" (0.22.7 on Ubunto 12.04LTS). Seeing the shortcut there is a good indication that the shortcut is registered and will work.

    If I press Shift+Z in the layout area (with a layout loaded), the message box pops up.

    If that is still not working on your installation, I can just guess. Maybe there is something wrong with other settings - you could try moving away the configuration file ("~/.klayout/klayoutrc" on Linux or "%HOME%/KLayout/klayoutrc" on Windows) and see whether it's working then. Maybe there is some custom shortcut interfering with the macro-specific shortcut.

    Regards,

    Matthias

  • edited November -1

    I figured out why I was having strange shortcut key issues.

    If I simply remove this wrapper from my entire code:

    class MenuAction < RBA::Action
      def initialize( title, shortcut, &action ) 
        self.title = title
        self.shortcut = shortcut
        @action = action
      end
      def triggered 
        @action.call( self ) 
      end
    private
      @action
    end
    
    $curve_90deg_bends_rib_bool = MenuAction.new( "Curve 90deg bends - rib bool", "" ) do 
    
      # Code goes here...
    
    end
    
    app = RBA::Application.instance
    mw = app.main_window
    
    menu = mw.menu
    menu.insert_separator("tools_menu.end", "sep_calc_area")
    menu.insert_item("tools_menu.end", "curve_90deg_bends_rib_bool ", $curve_90deg_bends_rib_bool)
    

    and replace it with this wrapper:

    module BareBones
      # Code goes here...
    end
    

    then it all works as you say.

    To be honest I don't understand the function of all of this extra code, I was just building from a known example (the "calculate total area" example on this website, I believe). But hey it works now just as you describe. Thanks!

  • edited August 2013

    However now another question :)

    This works fine (total code pasted below) in the following case:

    • Make a new layout, new top cell called "top", new layer called layer 1, new layer called layer 6.

    • Draw a wire e.g. 1um wide, on the "top" cell, in some arbitrary Manhattan geometry with various sharp 90deg bends.

    • Select the wire.

    • Run this macro from the menu or press Shift-Z. The right-angled wire is now a bendy wire. (Actually, two parallel bendy wires which is what I wanted - but that is a small detail.)

    However this DOESN'T work fine if you do the same as above, but the wire is not on the top cell. If the wire is instead on a child cell that is instanced in the top cell, you can still select the wire the same (assuming you are looking at the top cell you can still select elements of the child cell). You can still run the code. But then KLayout crashes and closes without error message.

    Any ideas how to fix it so it loops through all wires selected, regardless of whether the wire is in the top cell or in a child (or grandchild) cell?

    Thanks! Code pasted below.

    module Curve90DegBendsRibBool
    
      app = RBA::Application.instance
      mw = app.main_window
    
      lv = mw.current_view
      if lv == nil
        raise "No view selected"
      end
    
      lv.transaction("Curve 90deg bends - rib bool")
      begin
    
        rad = 10.0 # default radius of curvature in microns
        rad = RBA::InputDialog.get_int("Radius", "What radius (um)?",rad)
        r = Float(rad.to_i)*1000
    
        lv.each_object_selected do |obj|
    
          shape = obj.shape
          layout = lv.cellview(obj.cv_index).layout
    
          # Get needed parameters
          curveNumPts = 200
          widthInner = 0.37 / (obj.trans.mag * layout.dbu)
          widthOuter = 23 / (obj.trans.mag * layout.dbu)
          lnum = 6 # GDS Layer 6
          dtnum = 0
          out_layer = layout.layer_indices.find do |li|
            info = layout.get_info(li)
            info.layer == lnum && info.datatype == dtnum
          end
    
          if shape.is_path?
            pts=[]
            shape.each_point { |p| pts.push(p) }
    
            i = 0
    
            newPathPoints=[]
            newPathPoints[0]=pts[0] # write the first vertex
    
            pts.each_cons(3) { |prevCurrNextPt| # Get a vector that includes the prev pt, the curr pt, and the next pt. Need all three to calculate the curve
              pr=prevCurrNextPt[0]
              cu=prevCurrNextPt[1]
              ne=prevCurrNextPt[2]
    
              pi=Math::PI
              if pr.x<cu.x && pr.y==cu.y && cu.x==ne.x && cu.y<ne.y
                thetaStart = 3*pi/2
                thetaEnd = 2*pi;
                dx = -r
                dy = r
              elsif pr.x<cu.x &&pr.y==cu.y && cu.x==ne.x && cu.y>ne.y
                thetaStart = pi/2
                thetaEnd = 0
                dx = -r
                dy = -r
              elsif pr.x==cu.x && pr.y<cu.y && cu.x<ne.x && cu.y==ne.y
                thetaStart = pi
                thetaEnd = pi/2
                dx = r
                dy = -r
              elsif pr.x==cu.x && pr.y>cu.y && cu.x<ne.x && cu.y==ne.y
                thetaStart = pi
                thetaEnd = 3*pi/2
                dx = r
                dy = r
              elsif pr.x==cu.x && pr.y>cu.y && cu.x>ne.x && cu.y==ne.y
                thetaStart = 2*pi
                thetaEnd = 3*pi/2
                dx = -r
                dy = r
              elsif pr.x == cu.x && pr.y<cu.y && cu.x>ne.x &&cu.y==ne.y
                thetaStart = 0
                thetaEnd = pi/2
                dx = -r
                dy = -r
              elsif pr.x>cu.x && pr.y==cu.y && cu.x==ne.x && cu.y>ne.y
                thetaStart = pi/2
                thetaEnd = pi
                dx = r
                dy = -r
              elsif pr.x>cu.x && pr.y==cu.y && cu.x==ne.x && cu.y<ne.y
                thetaStart = 3*pi/2
                thetaEnd = pi
                dx = r
                dy = r
              else
                RBA::MessageBox.info("Error","This shouldn't happen",RBA::MessageBox.b_ok)
              end
    
              #make a linearly spaced array from thetaStart to thetaEnd with curveNumPts number of points
              dTheta = (thetaEnd-thetaStart)/(curveNumPts-1)
              theta = []
              for j in 0..curveNumPts-1
                theta[j] = j*dTheta + thetaStart
              end
    
              xPtsCurve = []
              yPtsCurve = []
              theta.each { |angle| # Loop through each new curvy point for a given vertex
                xPtsCurve = (cu.x + dx + r*Math::cos(angle))
                yPtsCurve = (cu.y + dy + r*Math::sin(angle))
    
                newPathPoints[i+1] = RBA::Point.new(xPtsCurve,yPtsCurve) 
                i+=1
              }
            }
    
            newPathPoints[i]=pts[pts.length-1] #write the last vertex
    
          end
    
          # If you want to overwrite the shape do this:
          #shape.path = RBA::Path::new(newPathPoints, shape.path_width)
          # If you want to draw a new path do this:
          curvedStripInner = shape.shapes.insert(RBA::Path::new(newPathPoints, widthInner))
          curvedStripOuter = shape.shapes.insert(RBA::Path::new(newPathPoints, widthOuter))
    
          # Subtract the outer curved one from the inner curved one and put the result on layer 2
          cv = lv.cellview(obj.cell_index)
          out_cell = layout.cell(cv.cell_index)
    
          ep = RBA::EdgeProcessor::new
          outerMinusInner = ep.boolean_p2p([ curvedStripOuter.polygon ], [ curvedStripInner.polygon ], RBA::EdgeProcessor::ModeANotB, false, false)
          outerMinusInner.each do |p|
            out_cell.shapes(out_layer).insert(p)
          end
    
          #delete the paths as they are unneeded now
          shape.shapes.erase(curvedStripInner)
          shape.shapes.erase(curvedStripOuter)
    
        end
    
        p 'Done'
    
        ensure 
        lv.commit
      end
    
    end
    
  • edited August 2013

    Hi David,

    sorry I did not catch the last one about the key binding: of course the wrapper code is responsible for the behavior. The wrapper code is "old style menu binding" which was necessary before macros came into existence.

    Simple ruby modules (".rbm" files) are plain scripts which are executed on startup. Using the application's API these scripts were able to register a piece of code inside a menu handler (an "action") and attach that code to a single or even multiple menu items and/or bind it to a key. That mechanism was implemented through the MenuAction helper class and often appears in sample code.

    "Macros" (".lym" files) however are annotated ruby scripts (more precisely XML files containing the Ruby code plus some attributes). They can be given attributes indicating that they shall appear in the menu and KLayout will add the wrapper code itself in that case. The same way, key bindings are created. These are the attributes you can configure in the macro properties page.

    By placing "old" wrapper code inside the macros plus configuring them for key binding, you basically make KLayout execute the registration code instead of the actual body part - hence the key binding did not have an effect.

    Regarding the second question: The crash is probably because you were using the cell index as the cellview index. That will result in an invalid access (I certainly admit that crashing is unfriendly behavior). Please use "layout.cell(cell_index)".

    But allow me one more remark: you don't need to insert a path into the Shapes container and use Shape#polygon to convert it to a polygon. Path#polygon will simply to the job. So the replacement of the shape should look like this:

      curvedStripInner = RBA::Path::new(newPathPoints, widthInner).polygon
      curvedStripOuter = RBA::Path::new(newPathPoints, widthOuter).polygon
    
      # Subtract the outer curved one from the inner curved one and put the result on layer 2
      out_cell = layout.cell(obj.cell_index)
    
      ep = RBA::EdgeProcessor::new
      outerMinusInner = ep.boolean_p2p([ curvedStripInner ], [ curvedStripOuter ], RBA::EdgeProcessor::ModeANotB, false, false)
      outerMinusInner.each do |p|
        out_cell.shapes(out_layer).insert(p)
      end
    

    Thje two "erase" calls are no longer required.

    That should fix the issue.

    There is one additional pitfall: if you have a selection of multiple shapes, it may happen that you select the same shape of the same child cell twice. That results in the same polygon being created twice in the target cell. Right now there is no good way to avoid that - the easiest solution is to confine the code to single-object selections.

    Regards,

    Matthias

  • edited August 2013

    This works great now, thanks.

    I made the changes you recommended and all works great. I understand the difference between .lym and .rbm now.

    Pasting the final code below in case future readers are interested.

    Thanks

    module Curve90DegBendsRibBool
    
      app = RBA::Application.instance
      mw = app.main_window
    
      lv = mw.current_view
      if lv == nil
        raise "No view selected"
      end
    
      lv.transaction("Curve 90deg bends - rib bool")
      begin
    
        rad = 70.0 # default radius of curvature
        rad = RBA::InputDialog.get_int("Radius", "What radius (um)?",rad)
        r = Float(rad.to_i)*1000
        #r = radius/(obj.trans.mag * layout.dbu) 
    
        lv.each_object_selected do |obj|
    
          shape = obj.shape
          layout = lv.cellview(obj.cv_index).layout
    
          # Get needed parameters
          curveNumPts = 200
          widthInner = 0.37 / (obj.trans.mag * layout.dbu)
          widthOuter = 9.5 / (obj.trans.mag * layout.dbu)
          lnum = 6 # GDS Layer 6
          dtnum = 0
          out_layer = layout.layer_indices.find do |li|
            info = layout.get_info(li)
            info.layer == lnum && info.datatype == dtnum
          end
    
          if shape.is_path?
            pts=[]
            shape.each_point { |p| pts.push(p) }
    
            i = 0
    
            newPathPoints=[]
            newPathPoints[0]=pts[0] # write the first vertex
    
            pts.each_cons(3) { |prevCurrNextPt| # Get a vector that includes the prev pt, the curr pt, and the next pt. Need all three to calculate the curve
              pr=prevCurrNextPt[0]
              cu=prevCurrNextPt[1]
              ne=prevCurrNextPt[2]
    
              pi=Math::PI
              if pr.x<cu.x && pr.y==cu.y && cu.x==ne.x && cu.y<ne.y
                thetaStart = 3*pi/2
                thetaEnd = 2*pi;
                dx = -r
                dy = r
              elsif pr.x<cu.x &&pr.y==cu.y && cu.x==ne.x && cu.y>ne.y
                thetaStart = pi/2
                thetaEnd = 0
                dx = -r
                dy = -r
              elsif pr.x==cu.x && pr.y<cu.y && cu.x<ne.x && cu.y==ne.y
                thetaStart = pi
                thetaEnd = pi/2
                dx = r
                dy = -r
              elsif pr.x==cu.x && pr.y>cu.y && cu.x<ne.x && cu.y==ne.y
                thetaStart = pi
                thetaEnd = 3*pi/2
                dx = r
                dy = r
              elsif pr.x==cu.x && pr.y>cu.y && cu.x>ne.x && cu.y==ne.y
                thetaStart = 2*pi
                thetaEnd = 3*pi/2
                dx = -r
                dy = r
              elsif pr.x == cu.x && pr.y<cu.y && cu.x>ne.x &&cu.y==ne.y
                thetaStart = 0
                thetaEnd = pi/2
                dx = -r
                dy = -r
              elsif pr.x>cu.x && pr.y==cu.y && cu.x==ne.x && cu.y>ne.y
                thetaStart = pi/2
                thetaEnd = pi
                dx = r
                dy = -r
              elsif pr.x>cu.x && pr.y==cu.y && cu.x==ne.x && cu.y<ne.y
                thetaStart = 3*pi/2
                thetaEnd = pi
                dx = r
                dy = r
              else
                RBA::MessageBox.info("Error","This shouldn't happen",RBA::MessageBox.b_ok)
              end
    
              #make a linearly spaced array from thetaStart to thetaEnd with curveNumPts number of points
              dTheta = (thetaEnd-thetaStart)/(curveNumPts-1)
              theta = []
              for j in 0..curveNumPts-1
                theta[j] = j*dTheta + thetaStart
              end
    
              xPtsCurve = []
              yPtsCurve = []
              theta.each { |angle| # Loop through each new curvy point for a given vertex
                xPtsCurve = (cu.x + dx + r*Math::cos(angle))
                yPtsCurve = (cu.y + dy + r*Math::sin(angle))
    
                newPathPoints[i+1] = RBA::Point.new(xPtsCurve,yPtsCurve) 
                i+=1
              }
            }
    
            newPathPoints[i]=pts[pts.length-1] #write the last vertex
    
            # If you want to overwrite the shape do this:
            #shape.path = RBA::Path::new(newPathPoints, shape.path_width)
            # If you want to draw a new path do this:
            curvedStripInner = RBA::Path::new(newPathPoints, widthInner).polygon #shape.shapes.insert(RBA::Path::new(newPathPoints, widthInner))
            curvedStripOuter = RBA::Path::new(newPathPoints, widthOuter).polygon #shape.shapes.insert(RBA::Path::new(newPathPoints, widthOuter))
    
            # Subtract the outer curved one from the inner curved one and put the result on layer 2
            out_cell = layout.cell(obj.cell_index)
    
            ep = RBA::EdgeProcessor::new
            outerMinusInner = ep.boolean_p2p([ curvedStripOuter ], [ curvedStripInner ], RBA::EdgeProcessor::ModeANotB, false, false)
            outerMinusInner.each do |p|
              out_cell.shapes(out_layer).insert(p)
            end   
    
            # Delete the paths as they are unneeded now
            #shape.shapes.erase(curvedStripInner)
            #shape.shapes.erase(curvedStripOuter)
    
          end
    
        end
    
        p 'Done'
    
        ensure 
        lv.commit
      end
    
    end
    
Sign In or Register to comment.