It looks like you're new here. If you want to get involved, click one of these buttons!
I am using a few methods from the demos and help from chatgpt to collect the centroids of several shapes using a macro. The shapes are contained in a layer, and here is the functional code to get close to what I want. (posted below...)
The trouble I am encountering is how to iterate over shapes added to the region, as in the provided "layout.begin_shapes(cell,layer_indexes[idx])" loop. I am looking to get the corners or coordinates of the polygons, but I am unclear--nor able to find--how to get the coordinates such that I can manipulate them for the purpose of getting the centroid or center of mass of a shape.
Here is a snippet of the code in question, included here for verisimilitude.
def button_clicked(self, checked):
""" Event handler: "Calculate area" button clicked """
view = pya.Application.instance().main_window().current_view()
layout = view.cellview(0).layout()
cell = view.cellview(0).cell
#create KLayout region object to use for grabbing shapes
reg = pya.Region()
total_area = 0
com_y = 0
com_x = 0
#1/0, 2/0
layer_input = self.layer_input.selectedItems()
layer_input = [j.text for j in set(layer_input)]
layer_indexes = layout.layer_indexes()
for idx,lyr in enumerate(layout.layer_infos()):
if str(lyr) in layer_input:
#Use the Region insert function on a recursive shape iterator
#that is a function of the layout. This handles all the
#complicated work of finding shapes in the current cell.
reg.insert(layout.begin_shapes(cell,layer_indexes[idx]))
total_area += reg.area()/1e6
_points = self.convert_edges(str(reg.corners()))
centroids = self.centroid_calc(_points)
print(com_x, com_y)
# get the area and place it in the qt dialog
self.area.setText("Total Area: "+str(round(total_area, 2))+", Centroids (x, y): "+str(centroids))
def convert_edges(self, edges):
import re
# Use regular expression to extract tuples
tuple_pattern = r'\(([^)]+)\);'
tuples = re.findall(tuple_pattern, edges)
# Initialize two lists to store the extracted values as tuples
_tuple1 = []
_tuple2 = []
# Iterate through the extracted tuples and split them into values
for tuple_str in tuples:
values = tuple_str.split(';')
x1, y1 = map(float, values[0].split(','))
x2, y2 = map(float, values[1].split(','))
_tuple1.append((x1, y1))
_tuple2.append((x2, y2))
# Print the parsed tuple variables
print(edges)
if not _tuple1: return [((0, 0), (0, 0)),]
return list(zip(_tuple1, _tuple2)) + [(_tuple1[0], _tuple2[-1])]# Wrap around to the first point
def centroid_calc(self, points):
# Initialize variables for the numerator of the centroid equations
sum_x = 0.0
sum_y = 0.0
# Initialize variable for the area of the region
area = 0.0
# Iterate through the points to calculate the centroid
for pair in points:
x1, y1 = pair[0]
x2, y2 = pair[1]
# Calculate the cross product of the two points
cross_product = (x1 * y2) - (x2 * y1)
# Update the area and the centroid numerator values
area += cross_product
sum_x += (x1 + x2) * cross_product
sum_y += (y1 + y2) * cross_product
# Calculate the area and centroid coordinates
area /= 2.0
centroid_x = sum_x / (6 * area)
centroid_y = sum_y / (6 * area)
return round(centroid_x, 2), round(centroid_y, 2)
Comments
Hi @double0darbo,
I do not fully understand what you are trying to achieve. The code is overly complex and does not make much sense to me. E.g. why computing corners and then converting them to edges? Plus that path via strings is extremely inefficient.
Centroids are usually defined for single polygons. The solution in that case was to iterate over the polygons using the recursive shape iterator and computing the centroids from those. The recursive shape iterator delivers polygons plus a transformation that transforms them into the top cell. You can apply this transformation to the centroid after computation. So basic code is this (Caution: not tested):
Matthias
This is exactly the guidance I needed. I can clean-up the code in the previous post so that it is more succinct if preferred, and I will attach the finished code using your snippets once I get it working.
To summarize the previous issue: Since I am new to coding using the Klayout macros, I was unable to figure out how to use the
begin_shapes
method to iterate over multiple shapes contained in a layer to obtain the centroids of each shape in the layer.I am getting the error,
'Polygon' object is not callable
, so I had to correct to not expressedly call the polygon. Thank you for the support, as this is now working brilliantly.Very good. Thanks for sharing the code.
I did not have the time to test the code above, so apologies for the typo. The line was supposed to be "iter.shape().polygon" (without brackets) and I'd advise you to change your code too. Otherwise it will not work if you have labels in your layout for example.
Matthias
Good note. Thank you.
In a similar vein to your labels comment, I am running into an issue with the names of the layers disappearing when I close my
.gds
file. Is that somehow related to the labels you mentioned?@double0darbo No, GDS cannot store layer names. So when you save to GDS, layer names will be gone. Saving to OASIS is an option to prevent that.
By "labels" I meant text objects that may be present in your layout. These do not translate to polygons and the "polygon" property delivers "None". If that case is not handled, you will see an error message in that case.
Matthias