案例分享:
from si_fab import all as pdk
import ipkiss3.all as i3
from ipcore.properties.restrictions import RestrictTuple
from ipkiss.geometry.shapes.modifiers import __ShapePathBase__
import numpy as np
from math import atan2
class ShapePathTaperExtended(__ShapePathBase__):
""" Tapered path with extended ends based on a shape. """
extension = i3.Tuple2Property(
restriction=RestrictTuple((float)),
doc="extension of the path shape at start and end: (start, end)"
)
end_path_width = i3.PositiveNumberProperty(doc="end width of path")
start_path_width = i3.PositiveNumberProperty(doc="start width of path")
def _default_end_path_width(self):
return self.path_width
def _default_path_width(self):
return self.start_path_width
def _default_extension(self):
return 0., 0.
def __init__(self, original_shape, start_path_width, end_path_width, **kwargs):
super(ShapePathTaperExtended, self).__init__(
original_shape=original_shape,
start_path_width=start_path_width,
end_path_width=end_path_width,
**kwargs)
def define_points(self, pts):
# TODO: include start_face_angle and end_face_angle in the calculations
start_ext, end_ext = self.extension
west_coords = i3.Shape()
east_coords = i3.Shape()
orig_shp = i3.Shape(self.__get_original_shape_without_straight_angles__())
start_angle, end_angle = orig_shp.get_face_angles()
if len(orig_shp) == 0 or np.isclose(orig_shp.length(), 0.):
return pts
# begin
orig_shp[0] = orig_shp[0].move_polar(-start_ext, start_angle)
# end
orig_shp[-1] = orig_shp[-1].move_polar(end_ext, end_angle)
dist = [i3.distance(orig_shp[_], orig_shp[_ + 1]) for _ in range(len(orig_shp) - 1)]
widths = [(self.end_path_width - self.path_width) * np.sum(dist[:_]) / np.sum(dist) + self.path_width for _ in
range(len(orig_shp))]
coords = orig_shp.points
n_points = len(coords)
start_angle, end_angle = orig_shp.get_face_angles()
# middle
for i in range(n_points):
x = coords[i][0]
y = coords[i][1]
if i == 0:
angle1 = i3.DEG2RAD * start_angle
angle2 = atan2(coords[i + 1][1] - y, coords[i + 1][0] - x)
elif i == n_points - 1:
angle1 = atan2(y - coords[i - 1][1], x - coords[i - 1][0])
angle2 = i3.DEG2RAD * end_angle
else:
angle1 = atan2(y - coords[i - 1][1], x - coords[i - 1][0])
angle2 = atan2(coords[i + 1][1] - y, coords[i + 1][0] - x)
angle = angle1 + 0.5 * (angle2 - angle1 + np.pi) % (np.pi) - 0.5 * np.pi
turn = (angle2 - angle1) % (2 * np.pi)
ca = np.cos(angle)
sa = np.sin(angle)
if turn == np.pi and i not in [0, n_points - 1]:
i3.LOG.error("Path to Boundary conversion is not possible with paths that turn 180 degree at a node")
raise SystemExit
w = 0.5 * widths[i] / np.abs(np.cos(0.5 * turn))
c_west = (x - w * sa, y + w * ca)
c_east = (x + w * sa, y - w * ca)
west_coords.append(c_west)
east_coords.append(c_east)
east_coords.reverse()
pts.extend(west_coords)
pts.extend(east_coords)
pts.append(west_coords[0])
return pts
class gradient_arc(i3.PCell):
class Layout(i3.LayoutView):
radius = i3.PositiveNumberProperty(doc="Radius of the central bends", default=100.0)
def _generate_elements(self, elems):
shape_wg1 = [
(0.0, 0.0),
(self.radius, 0),
(self.radius, self.radius),
(self.radius, 2 * self.radius),
(0, 2 * self.radius),
]
shape_wg1_path = i3.ShapeRound(original_shape=shape_wg1, radius=self.radius)
elems += i3.Boundary(layer=i3.TECH.PPLAYER.SI,
shape=ShapePathTaperExtended(
original_shape=shape_wg1_path,
start_path_width=10,
end_path_width=1,
))
return elems
if __name__ == '__main__':
gradient_arc().Layout().visualize()