- This topic has 5 replies, 2 voices, and was last updated 4 years, 5 months ago by jgnedy.
-
AuthorPosts
-
27 April 2020 at 20:51 #6127jgnedyMember
I believe there is a bug in the calculation of the bounding box. Curves that start with a non-90 angle will result in a bbox larger than the element. The minimal way I’ve found to recreate this bug is below
Thanks,
Joe# third-party imports import nazca as nd from nazca import cp, Cell from nazca.interconnects import Interconnect as IC # Interconnect settings XS = 'xs1' nd.add_layer2xsection(xsection=XS, layer=1) IC_1 = IC(width=1, radius=80, xs=XS) # main with Cell('main', autobbox=True) as main: cp.rotate(45) IC_1.bend(angle=45).put() nd.export_gds(topcells=main, filename='bbox_test.gds')
27 April 2020 at 21:53 #6129RonaldKeymasterDear Joe,
Thank you for the example. This may look surprising, but it is expected behaviour.
A bbox of a cell is a rectangular shape in the native orientation of the block. Let’s say you draw a thin diagonal line from (0, 0) to (1, 1), then the bbox is bottom-left (0, 0) to top right (1, 1), but the upper left and bottom right corner of the bbox (square) are empty. If you rotate the bbox by -45 degree, the diagonal is a vertical line and the bbox a diamond shape. If you place this in another cell it will see the diamond left and right tips “pushing” the parent bbox.
The bend example is similar; Interconnects in Nazca by default (by choice) are not instantiated when exported to gds (they are nazca internally cells, actually a stack of cells, each with a bbox), so you do not see that diamond bbox anymore. But you do see it pushing the bbox of cell main. The bbox is visualized in the example below.
Nazca does also have a convex hull option as boundary. Such hull will always make the structure “touch” the parent bbox. But also that touching is optional, because you can still ask a parent cell to either use the hull (if present) or the rectangular bbox of the instance to calculate the bounding box. Calculating a hull costs (much) more time, so it is not used in mask elements or interconnects.
import nazca as nd from nazca import cp, Cell from nazca.interconnects import Interconnect as IC # Interconnect settings XS = 'xs1' nd.add_layer2xsection(xsection=XS, layer=1) IC_1 = IC(width=1, radius=80, xs=XS) with Cell('bend', autobbox=True) as bend: IC_1.bend(angle=45).put() # main with Cell('main', autobbox=True) as main: bend.put(0, 0, 45) nd.export_gds(topcells=main, filename='bbox_test.gds')
28 April 2020 at 00:35 #6130jgnedyMemberI understand a little better. I was hoping to use the bbox (or hull) to extract tight bounding box dimensions of a cell containing many elements, ignoring such rotated bounding boxes of child cells. As you say, the hull of basic elements intentionally matches its bounding box, leading to a similar issue in this case. I thought to try something like “instantiate=False” in the child cell, and/or “hull=True, hull_based_bbox=True”, but no luck. Do you see a way to do this?
Thanks,
Joe
28 April 2020 at 19:52 #6131RonaldKeymasterHey Joe,
There are some pro’s and con’s of a hull vs. bbox. A convex hull is like a rubber band around the shapes. It can not be assigned a top-right etc corner like a rectangular bbox. The bbox is not supposed to be the tightest shape under rotation, it serves another purpose, like placement convenience, vizualization or finding overlaps with other boxes efficiently. Hulls take an order longer to calculate, so default they are off. Note that for your topcell of interest you do want a width and a height, according to a rectangular bbox, if I understand correctly, which depends on the orientation of that topcell.
A way to get the tight bbox of your top cell is to flatten it and get the bbox for that flattened cell. This can be done with the cell_iter(). I also switched the hull on for the flattened cell. The hull has a lot more points than a bbox in this case, due to the outside of the bend being part of the hull.
import nazca as nd from nazca.interconnects import Interconnect as IC # Interconnect settings XS = 'xs1' nd.add_layer2xsection(xsection=XS, layer=1) IC_1 = IC(width=1, radius=80, xs=XS) # main with nd.Cell('main') as main: IC_1.bend(angle=45).put(0, 0, 45) nd.cfg.use_hull = True # switch on hull calculations. lyo = nd.layout() for P in nd.cell_iter(main, flat=True): if P.cell_start: if P.level == 0: lyo.cell_open(params=P) lyo.add_polygons(params=P) if P.cell_close and P.level == 0: lyo.cell_close(params=P) print(lyo.CELL.topcell.bbox) print(lyo.CELL.topcell.hull)
# output (-0.35355339059327373, -0.3535533905932738, 23.931457505076203, 56.56854249492379) [[-3.53553391e-01 3.53553391e-01] [ 3.53553391e-01 -3.53553391e-01] [ 7.07106781e-01 -3.88578059e-16] [ 1.19628836e+00 5.01371256e-01] [ 1.75083025e+00 1.07841601e+00] [ 2.29960800e+00 1.66094527e+00] [ 2.84256736e+00 2.24890147e+00] [ 3.37965467e+00 2.84222649e+00] [ 3.91081685e+00 3.44086168e+00] [ 4.43600139e+00 4.04474789e+00] [ 4.95515640e+00 4.65382541e+00] [ 5.46823055e+00 5.26803407e+00] [ 5.97517314e+00 5.88731314e+00] [ 6.47593406e+00 6.51160141e+00] [ 6.97046382e+00 7.14083720e+00] [ 7.45871354e+00 7.77495830e+00] [ 7.94063497e+00 8.41390203e+00] [ 8.41618046e+00 9.05760525e+00] [ 8.88530302e+00 9.70600434e+00] [ 9.34795629e+00 1.03590352e+01] [ 9.80409452e+00 1.10166333e+01] [ 1.02536727e+01 1.16787337e+01] [ 1.06966462e+01 1.23452708e+01] [ 1.11329715e+01 1.30161789e+01] [ 1.15626053e+01 1.36913915e+01] [ 1.19855052e+01 1.43708421e+01] [ 1.24016294e+01 1.50544633e+01] [ 1.28109367e+01 1.57421877e+01] [ 1.32133867e+01 1.64339473e+01] [ 1.36089397e+01 1.71296737e+01] [ 1.39975565e+01 1.78292981e+01] [ 1.43791987e+01 1.85327515e+01] [ 1.47538286e+01 1.92399642e+01] [ 1.51214092e+01 1.99508663e+01] [ 1.54819041e+01 2.06653877e+01] [ 1.58352777e+01 2.13834576e+01] [ 1.61814951e+01 2.21050052e+01] [ 1.65205221e+01 2.28299590e+01] [ 1.68523251e+01 2.35582475e+01] [ 1.71768714e+01 2.42897986e+01] [ 1.74941289e+01 2.50245401e+01] [ 1.78040662e+01 2.57623993e+01] [ 1.81066526e+01 2.65033033e+01] [ 1.84018584e+01 2.72471789e+01] [ 1.86896543e+01 2.79939526e+01] [ 1.89700119e+01 2.87435505e+01] [ 1.92429034e+01 2.94958985e+01] [ 1.95083019e+01 3.02509224e+01] [ 1.97661811e+01 3.10085474e+01] [ 2.00165156e+01 3.17686987e+01] [ 2.02592806e+01 3.25313011e+01] [ 2.04944522e+01 3.32962794e+01] [ 2.07220070e+01 3.40635578e+01] [ 2.09419226e+01 3.48330605e+01] [ 2.11541773e+01 3.56047115e+01] [ 2.13587500e+01 3.63784346e+01] [ 2.15556206e+01 3.71541532e+01] [ 2.17447696e+01 3.79317906e+01] [ 2.19261783e+01 3.87112701e+01] [ 2.20998288e+01 3.94925145e+01] [ 2.22657039e+01 4.02754467e+01] [ 2.24237872e+01 4.10599893e+01] [ 2.25740631e+01 4.18460647e+01] [ 2.27165167e+01 4.26335952e+01] [ 2.28511340e+01 4.34225030e+01] [ 2.29779017e+01 4.42127101e+01] [ 2.30968072e+01 4.50041385e+01] [ 2.32078387e+01 4.57967098e+01] [ 2.33109853e+01 4.65903459e+01] [ 2.34062369e+01 4.73849681e+01] [ 2.34935839e+01 4.81804980e+01] [ 2.35730178e+01 4.89768570e+01] [ 2.36445307e+01 4.97739663e+01] [ 2.37081155e+01 5.05717472e+01] [ 2.37637660e+01 5.13701208e+01] [ 2.38114766e+01 5.21690081e+01] [ 2.38512427e+01 5.29683304e+01] [ 2.38830602e+01 5.37680084e+01] [ 2.39069262e+01 5.45679633e+01] [ 2.39228381e+01 5.53681159e+01] [ 2.39314575e+01 5.60685425e+01] [ 2.39314575e+01 5.65685425e+01] [ 2.29314575e+01 5.65685425e+01]]
Ronald
28 April 2020 at 20:23 #6132RonaldKeymasteranother way is:
import nazca as nd from nazca.interconnects import Interconnect as IC # Interconnect settings XS = 'xs1' nd.add_layer2xsection(xsection=XS, layer=1) IC_1 = IC(width=1, radius=80, xs=XS) nd.cfg.use_hull = True # switch hull calculation on for all cells nd.cfg.hull_based_bbox = True # use the hull for the next level hull/bbox, not the bbox, for all cells. # main with nd.Cell('main', autobbox=False) as main: IC_1.bend(angle=45).put(0, 0, 45) print(main.bbox) print(main.hull)
Ronald
29 April 2020 at 03:14 #6133jgnedyMemberThanks Ronald. I found your second method to work the best for me. I wrote a helper function in a separate module, then used the with block in my function that returns a cell. In this way, the expensive hull calculation is contained.
@contextmanager def temp_attributes(obj, **kwargs): prev_vals = {k: getattr(obj, k) for k in kwargs} for k, v in kwargs.items(): setattr(obj, k, v) try: yield finally: for k, v in prev_vals.items(): setattr(obj, k, v) --- with temp_attributes(nd.cfg, use_hull=True, hull_based_bbox=True): with Cell('main') as main: ...
-
AuthorPosts
- You must be logged in to reply to this topic.