# Rounding Corners through Ruby

edited April 2013
Matthias, I have to thank you for this amazing piece of software. You are the man!

I just discovered PCells, and I'm trying to work my way through my first version to speed my workflow. I would like to be able to apply the 'Round Corners' command from the selection edit menu. So here are a few questions that I have, apologies if the answers exist elsewhere, but I didn't see them in the documentation or the forum:

1) Are the commands accessible in the menu all in the Ruby API somewhere, and is there an easy way to figure out how to access them?
2) If not all commands are in the Ruby API, does the corner rounding command happen to be?

and finally, although this is a lower priority to me:

3) It would be beautiful if the round corners command could be applied on a per-corner basis (both through the GUI and through Ruby). In my designs it is nice to have different parts of a single polygon have different corner radii, which seems difficult to achieve as is.

Thanks for any help that Matthias or the community can lend!

• edited 11:00AM

Hallo,

thank you for that feedback :-)

Not all commands in the menu are accessible as Ruby functions, because the Ruby API is on a lower level than the menu functions. The Ruby API provides methods as classes from the database level while menu functions usually provide functionality on a higher level. I gradually try to make the high-level functionality available but that adds a lot of complexity to the API.

For the corner rounding, however there is a Ruby method available in the API (method "round_corners" of the Polygon class, see http://www.klayout.de/doc/code/class_Polygon.html#method45).

Regarding different rounding radii this method allows to specify two radii for inner and outer corners. In most cases that appears to be sufficient. It is probably possible to extend this to allow specification of a per-corner radius but right now there is no such option available.

Regards,

Matthias

• edited 11:00AM
That's great. I didn't notice the round_corners method!

I think that I'll implement a simple function to round arbitrary corners of a polygon, given a list of points defining the polygon, points to round and their respective radii. If it turns out nice, I'll post it here, along with an example of an output file.

Thanks Matthias!

-Michael
• edited 11:00AM
Hello Matthias and Michael,

I am also looking to make circles. I was wondering if there is a function yet to create real circles, or if I need to make a Ruby Script to do this or round the corners of a polygon.

Have any of you been successful with the code?

Thanks, Natalie
• edited April 2013

Hi Natalie,

there are not circles in GDS, hence there is no support for that kind of shape yet.

One approximation to a circle is a path with round corners and a single point. I don't know however, how portable that solution is. Such an object will be converted to OASIS circles if you save it to OASIS:

pts = [ RBA::Point::new(100, 200) ]   # center
p = RBA::Path::new(pts, 2 * r, r, r, true)


Another way is to create polygons with a sufficient number of points which form a circle, i.e. like this:

n = 200    # number of points
da = 2 * Math::PI / n
pts = n.times.collect { |i| RBA::Point.new(r * cos(i * da), r * sin(i * da)) }
poly = RBA::Polygon::new(pts)


Matthias

• edited 11:00AM

I went and made a promise to post some code, and then once I wrote it I promptly forgot! It may be superseded by now, but I thought that I'd post it here to wrap up my part in this thread.

Here is my silly little code which takes a list of points defining a polygon, two lists which in turn define the points you wish to round, and the radii you wish the curves to have, and finally the number of points per 360 degrees of arc.

Be warned! It's a very amateurish bit of code; the method is simplistic and there is no checking to see if the radii you request result in intersecting curves, leading to awful behavior if the radii is large compared to the distance between unrounded points. It ended up working well enough for me.

def polygon_round_corners(pts,cornerstoround,radii,ptspercircle)

roundpts = []

if cornerstoround == nil
return RBA::Polygon::new(pts)
end

unroundedpts = (pts.length)

for i in 0..unroundedpts

pt = pts[i]

# check to see if this point is to be rounded
rindex = cornerstoround.index(i)

# if we don't want to round this corner, add this unrounded point to the list, and skip to the next point
if rindex == nil
roundpts.push(pt)
next
end

#if we do want to round this corner, we need to start by finding the positions of this point, plus the previous and next points
# first - determine the index of the previous and next points
if i ==0
npt = pts[1]
ppt = pts[unroundedpts-1]
elsif i == unroundedpts -1
npt = pts[0]
ppt = pts[unroundedpts-2]
else
npt = pts[i + 1]
ppt = pts[i - 1]
end

# Next, I'll find the angle between the two line segments
theta1 = Math::atan2(npt.y-pt.y,npt.x-pt.x)
theta2 = Math::atan2(ppt.y-pt.y,ppt.x-pt.x)

phi = (theta1 - theta2).modulo(2*Math::PI)
xbar = Math::cos(theta1) + Math::cos(theta2)
ybar = Math::sin(theta1) + Math::sin(theta2)
theta = Math::atan2(ybar,xbar)

# and then it is easy to find the coordinates of the center of curvature:

dcx = dc*Math::cos(theta)
dcy = dc*Math::sin(theta)

cx = pt.x + dcx
cy = pt.y + dcy

# OK... moving along! We now need to find the starting and ending angles to define the arc.  This should be the angle found above, plus \pi
theta = theta + Math::PI
theta1 = theta - (Math::PI-phi)/2
theta2 = theta + (Math::PI-phi)/2

# check to make sure we don't need to switch theta1 and theta2:

dprevlinex = pt.x - ppt.x
dprevliney = pt.y - ppt.y

dtangentx =  (cx + radius * Math::cos(theta1))-pt.x
dtangenty = (cy + radius * Math:: sin(theta1))-pt.y

if dprevlinex == 0
error = dtangentx
else
error = (dtangenty - dtangentx*dprevliney/dprevlinex)/(dtangenty.abs + dtangentx.abs)
end

if error.abs > 1e-4
temp = theta1
theta1 = theta2
theta2 = temp
end

thetarange = (theta1 - theta2)
numpts  = 2*ptspercircle * (theta1 - theta2).abs / Math::PI
numpts = [numpts,2].max
numpts = numpts.round
dtheta = (-thetarange)/(numpts)

#generate a list of the angles used to generate the points:
angles = (0..numpts).to_a
angles = angles.collect { |n| n * dtheta }
angles = angles.collect{ |n| n + theta1 }

# step over the angles, and add the points to our list

for angle in angles
end

end

# return the polygon composed of all these points!

index = 0
roundpts = roundpts[0...-1]

poly =  RBA::Polygon::new(roundpts)

return poly

end