Open Rishabhgoyal07 opened 4 months ago
Yes, just look at Cell.references
. For each reference you can go to the corresponding cell with Reference.cell
, and then traverse its references and so on. Note that you might traverse the same cell more than once, if it is referenced multiple times.
@Rishabhgoyal07 here's some code that given a topcell and a child that you want to get, but taking into account any offsets/rotations/repetitions the referencing did. NOTE I only implemented this for polygons and labels in the resulting "transformed cell". I have some comments for how to approach for FlexPath/etc... I think the polygon and label chunks of code should be easy to adapt to any other cell
components.
the code:
def find_leaf_reference_offsets_and_rotations(cell, target_cell_name):
"""
Recursively find the offsets and rotations applied to reach the target cell.
Parameters:
- cell: The current cell being traversed.
- target_cell_name: The name of the target cell to find.
- current_offset: The cumulative offset applied so far.
- current_rotation: The cumulative rotation applied so far.
Returns:
- A list of lists of tuples containing the offset and rotation at each level of the hierarchy.
"""
transformations = []
current_cell_name = cell.name
for ref in cell.references:
if ref.cell.name == target_cell_name:
# Found the target cell, add the current offset and rotation
transformations.append((ref.origin, ref.rotation, ref.x_reflection, ref.magnification, target_cell_name))
else:
# Recursively search in the referenced cell
result = find_leaf_reference_offsets_and_rotations(ref.cell, target_cell_name)
if result:
# If the target cell is found in the recursion, add the current level's offset and rotation
for res in result:
transformations.append(((ref.origin, ref.rotation, ref.x_reflection, ref.magnification, ref.cell.name), res))
return transformations
def rotate_point(point, angle, origin=(0, 0), round_to=3):
"""Rotate a point around a given origin by a given angle."""
ox, oy = origin
px, py = point
qx = ox + math.cos(angle) * (px - ox) - math.sin(angle) * (py - oy)
qy = oy + math.sin(angle) * (px - ox) + math.cos(angle) * (py - oy)
return [round(qx, round_to), round(qy, round_to)]
def reflect_point(point, origin=(0, 0)):
"""Reflect a point across the x-axis around a given origin."""
ox, oy = origin
px, py = point
qx = 2 * ox - px
return [qx, py]
def apply_reps(cell_component_with_repetition, keep_reps_attr=False):
reps_out = []
if str(cell_component_with_repetition.repetition) != 'No repetition':
rep_obj = cell_component_with_repetition.repetition
reps = cell_component_with_repetition.apply_repetition()
# ensure input is not in output, as I experienced once
if isinstance(cell_component_with_repetition, gdstk.Polygon):
polygon = cell_component_with_repetition
reps_out += [rep for rep in reps if not (polygon.points == rep.points).all()]
elif isinstance(cell_component_with_repetition, gdstk.Label):
lbl = cell_component_with_repetition
reps_out += [rep for rep in reps if (
(lbl.origin != rep.origin) or
(lbl.text != rep.text) or
(lbl.texttype == rep.texttype) or
(lbl.anchor == rep.anchor) or
(lbl.rotation == rep.rotation) or
(lbl.magnification == rep.magnification) or
(lbl.x_reflection == rep.x_reflection)
)]
if keep_reps_attr:
cell_component_with_repetition.repetition = rep_obj
return reps_out
def apply_transformations_to_cell(cell, transform_list):
"""Apply a list of transformations to all elements in a cell and return a new cell."""
new_cell = gdstk.Cell(f"{cell.name}_transformed")
# Transform polygons
input_polygons = cell.polygons
while input_polygons:
polygon = input_polygons.pop()
input_polygons.extend(apply_reps(polygon, keep_reps_attr=True))
new_points = [list(point) for point in polygon.points]
origin_for_rotating_around = [0,0]
for origin_transform, rotation_transform, x_reflection, magnification, parent_cell in transform_list:
# Apply translation
translated_points = [[point[0] + origin_transform[0], point[1] + origin_transform[1]] for point in new_points]
origin_for_rotating_around[0] += origin_transform[0]
origin_for_rotating_around[1] += origin_transform[1]
# Apply rotation around the origin
rotated_points = [rotate_point(point, rotation_transform, origin_for_rotating_around) for point in translated_points]
# Apply x reflection
reflected_points = rotated_points
if x_reflection:
#TODO add implementation
reflected_points = [reflect_point(point, origin_for_rotating_around) for point in rotated_points]
new_points = reflected_points
new_polygon = gdstk.Polygon(new_points, datatype=polygon.datatype, layer=polygon.layer)
new_cell.add(new_polygon)
# Transform paths
if cell.paths:
raise NotImplementedError('implement me')
# for path in cell.paths:
# new_points = [list(point) for point in path.points]
#
# for origin_transform, rotation_transform, parent_cell in transform_list:
# # Apply translation
# new_points = [[point[0] + origin_transform[0], point[1] + origin_transform[1]] for point in new_points]
# # Apply rotation around the origin
# new_points = [rotate_point(point, rotation_transform, origin_transform) for point in new_points]
#
# new_path = gdstk.FlexPath(new_points, path.widths, datatype=path.datatype, layer=path.layer)
# new_cell.add(new_path)
# Transform labels
input_labels = cell.labels
while input_labels:
label = input_labels.pop()
input_labels.extend(apply_reps(label, keep_reps_attr=True))
new_position = list(label.origin)
origin_for_rotating_around=[0,0]
for origin_transform, rotation_transform, x_reflection, magnification, parent_cell in transform_list:
# Apply translation
new_position[0] += origin_transform[0]
new_position[1] += origin_transform[1]
# Apply rotation around the origin
origin_for_rotating_around[0] += origin_transform[0]
origin_for_rotating_around[1] += origin_transform[1]
new_position = rotate_point(new_position, rotation_transform, origin_for_rotating_around)
# Apply x reflection
if x_reflection:
new_position = reflect_point(new_position, origin_for_rotating_around)
new_label = gdstk.Label(label.text, new_position, layer=label.layer, texttype=label.texttype,
anchor=label.anchor, rotation=label.rotation, magnification=label.magnification, x_reflection=label.x_reflection)
new_cell.add(new_label)
return new_cell
and some tests:
def create_temp_output_dir(delete_first=True):
temp_output_dir = f'{this_files_dir}/temp_output_data'
if os.path.exists(temp_output_dir) and delete_first:
shutil.rmtree(temp_output_dir)
os.makedirs(temp_output_dir, exist_ok=True)
os.chdir(temp_output_dir)
return os.path.abspath(temp_output_dir)
def create_test_cells():
# Create the library
lib = gdstk.Library()
# Create the leaf cell with a polygon
leaf_cell = gdstk.Cell("LEAF")
polygon = gdstk.rectangle((0, 0), (5, 10), datatype=99, layer=101)
leaf_cell.add(polygon)
lib.add(leaf_cell)
# Create intermediary cells
intermediate_cell_1 = gdstk.Cell("INTERMEDIATE_1")
intermediate_cell_1.add(gdstk.Reference(leaf_cell))
lib.add(intermediate_cell_1)
intermediate_cell_2 = gdstk.Cell("INTERMEDIATE_2")
intermediate_cell_2.add(gdstk.Reference(leaf_cell, origin=(5, 5), rotation=math.pi))
# intermediate_cell_2.add(gdstk.Reference(leaf_cell, origin=(5, 5)))
lib.add(intermediate_cell_2)
# Create the top cell
top_cell = gdstk.Cell("TOP")
top_cell.add(gdstk.Reference(intermediate_cell_1))
top_cell.add(gdstk.Reference(intermediate_cell_2, origin=(10, 10)))
lib.add(top_cell)
tmp_file_path = create_temp_output_dir()+'/temp.oas'
print(tmp_file_path)
lib.write_oas(tmp_file_path)
return lib
def test_find_offsets_and_rotations():
lib = create_test_cells()
top_cell = lib.cells[-1] # Assuming the last cell is the top cell
assert top_cell.name == 'TOP'
# Test case 1: No offset or rotation in intermediary cells
result = find_leaf_reference_offsets_and_rotations(top_cell, "LEAF")
assert len(result) == 2
expected1 = (
((0, 0), 0, False, 1, 'INTERMEDIATE_1'), # Top cell to INTERMEDIATE_1
((0, 0), 0, False, 1, 'LEAF')) # INTERMEDIATE_1 to LEAF
expected2 = (
((10, 10), 0, False, 1, 'INTERMEDIATE_2'), # Top cell to INTERMEDIATE_1
((5, 5), math.pi, False, 1, 'LEAF') # Final offset and rotation
)
# expected3 = (
# ((10, 10), 0, 'INTERMEDIATE_2'), # Top cell to INTERMEDIATE_1
# ((5, 5), 0, 'LEAF') # Final offset and rotation
# )
assert result[0] == expected1, f"Expected {expected1}, but got {result[0]}"
assert result[1] == expected2, f"Expected {expected2}, but got {result[1]}"
# assert result[2] == expected3, f"Expected {expected3}, but got {result[2]}"
flat_cell = top_cell.flatten()
flat_polys = flat_cell.get_polygons(datatype=99, layer=101)
original_poly = [cell for cell in lib.cells if cell.name=='LEAF']
assert len(original_poly) == 1
original_poly = original_poly[0]
assert len(flat_polys) == 2
flat_poly_bbs = []
for poly in flat_polys:
bb = poly.bounding_box()
assert bb in [((0.,0.), (5.,10.)),
# ((15., 15.), (20., 25.)),
((10.,5.), (15.,15.)), ]
flat_poly_bbs.append(bb)
original_poly_transformed = []
op_bb = original_poly.bounding_box()
for transform_list in result:
new_bb = [list(op_bb[0]), list(op_bb[1])]
for origin_transform, rotation_transform, x_reflection, magnification, parent_cell in transform_list:
new_bb[0][0] += origin_transform[0]
new_bb[1][0] += origin_transform[0]
new_bb[0][1] += origin_transform[1]
new_bb[1][1] += origin_transform[1]
# Apply rotation around the origin
ll = rotate_point(new_bb[0], rotation_transform, new_bb[0])
ur = rotate_point(new_bb[1], rotation_transform, new_bb[0])
# Apply rotation around the origin
new_bb[0] = min(ll, ur)
new_bb[1] = max(ll, ur)
original_poly_transformed.append((tuple(new_bb[0]), tuple(new_bb[1])))
assert original_poly_transformed == flat_poly_bbs
# Apply transformations to the LEAF cell and create new transformed cells
leaf_cell = [cell for cell in lib.cells if cell.name == 'LEAF'][0]
transformed_cells = [apply_transformations_to_cell(leaf_cell, transform_list) for transform_list in result]
# Check that the transformed cells match the flattened cell
for transformed_cell in transformed_cells:
transformed_polys = transformed_cell.get_polygons(datatype=99, layer=101)
assert len(transformed_polys) == 1
transformed_poly = transformed_polys[0]
transformed_poly.bounding_box() in flat_poly_bbs
Hii @heitzmann, can we### traverse hierarchy in gdstk and access cell present in that hierarchy. Example, I have 10 hierarchy in my GDS, can I access the bottom and top hierarchy and move from top to bottom or vice versa and access each cell present in that hierarchy.