class Layout(WireWaveguideTemplate.Layout): core_process = ProcessProperty(locked=True, default=TECH.PROCESS.WG) core_purpose = PurposeProperty(locked=True, default=TECH.PURPOSE.CORE) cladding_process = ProcessProperty(locked=True, default=TECH.PROCESS.WG) cladding_purpose = PurposeProperty(locked=True, default=TECH.PURPOSE.CLADDING) core_width = PositiveNumberProperty(default=TECH.WG.CORE_WIDTH) cladding_width = PositiveNumberProperty(default=TECH.WG.CLADDING_WIDTH)
class Layout(wire_wg.WireWaveguideTemplate.Layout): core_width = PositiveNumberProperty( doc="Width of the core of the wire waveguide.") trench_width = PositiveNumberProperty( doc="Lateral material exclusion.") cladding_width = PositiveNumberProperty( doc="Cladding width of the wire waveguide.") core_process = ProcessProperty( locked=True, doc="Process used for the core layer of the MSN wire waveguide.") core_purpose = PurposeProperty( locked=True, doc="Purpose used for the core layer of the MSN wire waveguide.") trench_process = ProcessProperty( locked=True, doc= "Process used for the exclusion layer of the MSN wire waveguide.") trench_purpose = PurposeProperty( locked=True, doc= "Purpose used for the exclusion layer of the MSN wire waveguide.") cladding_process = ProcessProperty( locked=True, doc="Process used for the cladding layer of the MSN wire waveguide." ) cladding_purpose = PurposeProperty( locked=True, doc="Purpose used for the cladding layer of the MSN wire waveguide." ) def _default_core_process(self): return TECH.PROCESS.MSN def _default_core_purpose(self): return TECH.PURPOSE.DRAWING def _default_trench_process(self): return TECH.PROCESS.MSN_TRENCH def _default_trench_purpose(self): return TECH.PURPOSE.DRAWING def _default_cladding_process(self): return TECH.PROCESS.CLADDING def _default_cladding_purpose(self): return TECH.PURPOSE.DRAWING def _default_core_width(self): return TECH.WIREWG.WIRE_WIDTH def _default_trench_width(self): return TECH.WIREWG.TRENCH_WIDTH def _default_cladding_width(self): return TECH.WIREWG.CLADDING_WIDTH
class Layout(rib_wg.RibWaveguideTemplate.Layout): core_process = ProcessProperty( locked=True, doc="Process used for the core layer of the MSN rib_old waveguide." ) core_purpose = PurposeProperty( locked=True, doc="Purpose used for the core layer of the MSN rib_old waveguide." ) exclusion_process = ProcessProperty( locked=True, doc= "Process used for the exclusion layer of the silicon wire waveguide." ) exclusion_purpose = PurposeProperty( locked=True, doc= "Purpose used for the exclusion layer of the silicon wire waveguide." ) cladding_process = ProcessProperty( locked=True, doc= "Process used for the cladding layer of the MSN rib_old waveguide." ) cladding_purpose = PurposeProperty( locked=True, doc= "Purpose used for the cladding layer of the MSN rib_old waveguide." ) core_width = PositiveNumberProperty( doc="Width of the core of the rib_old waveguide.") cladding_width = PositiveNumberProperty( doc="Cladding width of the rib_old waveguide.") def _default_core_process(self): return TECH.PROCESS.MSN def _default_core_purpose(self): return TECH.PURPOSE.DRAWING def _default_exclusion_process(self): return TECH.PROCESS.MSN def _default_exclusion_purpose(self): return TECH.PURPOSE.DRAWING def _default_cladding_process(self): return TECH.PROCESS.SHALLOW def _default_cladding_purpose(self): return TECH.PURPOSE.DRAWING def _default_core_width(self): return TECH.RIBWG.RIB_WIDTH def _default_cladding_width(self): return TECH.WIREWG.CLADDING_WIDTH
class Font(StrongPropertyInitializer): coords = RestrictedProperty(restriction = RestrictType(dict), default = {}) line_width = PositiveNumberProperty(default = 0.1) default_char = RestrictedProperty(restriction = RestrictType(list), default = [[]]) cell_size = Size2Property(default = (0.6, 1.0)) spacing = NonNegativeNumberProperty(default = 0.2) def __init__(self, **kwargs): super(Font, self).__init__( **kwargs) def shapes_character (self, character, letter_height = 1.0, south_west_coord = (0.0,0.0), angle = 0.0): #returns a list of shapes!!! if character in self.coords: shapes = self.coords[character] else: shapes = [self.default_char] ret_shapes = [] #T = Rotation((0.0,0.0), angle) + Magnification((0.0,0,0), letter_height) + Translation(south_west_coord) T = NoDistortTransform(south_west_coord, angle, letter_height/self.letter_height()) for s in shapes: if len(s) < 2: LOG.warning("Shape with too few coordinates in letter " % character) #ret_shapes.append(shape_translate(shape_scale(shape_rotate(s, (0.0,0.0), angle), (letter_height, letter_height)), south_west_coord)) ret_shapes.append(T.apply_to_copy(s)) return ret_shapes def letter_width(self): return self.cell_size[0] def letter_height(self): return self.cell_size[1]
class ShapeRegularPolygon(Shape): """ regular N-sided polygon """ center = Coord2Property(default=(0.0, 0.0)) radius = PositiveNumberProperty(default=1.0) n_o_sides = IntProperty(default=8, restriction=RestrictRange(lower=3)) def __init__(self, **kwargs): kwargs["closed"] = True super(ShapeRegularPolygon, self).__init__(**kwargs) def define_points(self, pts): if self.radius == 0.0: pts.append(self.center) return pts angle_step = 2 * math.pi / self.n_o_sides for i in xrange(0, self.n_o_sides): pts.append( (self.center[0] + self.radius * math.cos((i + 0.5) * angle_step + math.pi / 2), self.center[1] + self.radius * math.sin((i + 0.5) * angle_step + math.pi / 2))) return pts def move(self, position): self.center = Coord2(self.center[0] + position[0], self.center[1] + position[1]) return self def is_empty(self): return (self.radius == 0.0)
class InputBasic(BasicInput): scaling = PositiveNumberProperty(default=1.0) layer_map = DefinitionProperty() prefix = StringProperty(default="") def __init__(self, i_stream=sys.stdin, **kwargs): super(InputBasic, self).__init__(i_stream=i_stream, **kwargs) self.library = None def read(self): return self.parse() def parse(self): return self.parse_library() def parse_library(self): self.library = Library("IMPORT") self.__parse_library__() return self.library def map_layer(self, layer): L = self.layer_map.get(layer, None) if isinstance(L, __Layer__): return L elif L is None: return L else: return Layer(L) def make_structure_name(self, name): return self.prefix + name def define_layer_map(self): return TECH.GDSII.IMPORT_LAYER_MAP #FIXME : using 'default' for the property would be better, but that gives an exception ...
class UnitGridContainer(StrongPropertyInitializer): grids_per_unit = DefinitionProperty(fdef_name="define_grids_per_unit") units_per_grid = DefinitionProperty(fdef_name="define_units_per_grid") unit = PositiveNumberProperty(default=TECH.METRICS.UNIT) grid = PositiveNumberProperty(default=TECH.METRICS.GRID) def define_grids_per_unit(self): return self.unit / self.grid def define_units_per_grid(self): return self.grid / self.unit def validate_properties(self): if self.grid > self.unit: raise Exception("The grid should be smaller than the unit.") return True
class ShapeCross(Shape): """ cross. thickness sets the width of the arms """ center = Coord2Property(default=(0.0, 0.0)) box_size = PositiveNumberProperty(default=20.0) thickness = PositiveNumberProperty(default=5.0) def __init__(self, **kwargs): kwargs["closed"] = True super(ShapeCross, self).__init__(**kwargs) def define_points(self, pts): pts += [(self.center[0] - self.box_size / 2.0, self.center[1] - self.thickness / 2.0), (self.center[0] - self.box_size / 2.0, self.center[1] + self.thickness / 2.0), (self.center[0] - self.thickness / 2.0, self.center[1] + self.thickness / 2.0), (self.center[0] - self.thickness / 2.0, self.center[1] + self.box_size / 2.0), (self.center[0] + self.thickness / 2.0, self.center[1] + self.box_size / 2.0), (self.center[0] + self.thickness / 2.0, self.center[1] + self.thickness / 2.0), (self.center[0] + self.box_size / 2.0, self.center[1] + self.thickness / 2.0), (self.center[0] + self.box_size / 2.0, self.center[1] - self.thickness / 2.0), (self.center[0] + self.thickness / 2.0, self.center[1] - self.thickness / 2.0), (self.center[0] + self.thickness / 2.0, self.center[1] - self.box_size / 2.0), (self.center[0] - self.thickness / 2.0, self.center[1] - self.box_size / 2.0), (self.center[0] - self.thickness / 2.0, self.center[1] - self.thickness / 2.0), (self.center[0] - self.box_size / 2.0, self.center[1] - self.thickness / 2.0)] return pts def move(self, position): self.center = Coord2(self.center[0] + position[0], self.center[1] + position[1]) return self def is_empty(self): return self.box_size == 0.0 or self.thickness == 0.0
class ShapeRoundedRectangleArc(Shape): center = Coord2Property(default=(0.0, 0.0)) box_size = Size2Property(default=(1.0, 1.0)) radius = PositiveNumberProperty(default=0.1) start_angle = AngleProperty(default=0.0) end_angle = AngleProperty(default=90.0) angle_step = AngleProperty(default=TECH.METRICS.ANGLE_STEP) clockwise = BoolProperty(default=False) def __init__(self, **kwargs): super(ShapeRoundedRectangleArc, self).__init__(**kwargs) # restrict radius if self.radius > min(self.box_size) / 2.0: self.radius = min(self.box_size) / 2.0 def define_points(self, pts): cx = self.center[0] cy = self.center[1] dx = 0.5 * self.box_size[0] dy = 0.5 * self.box_size[1] if self.clockwise: as_sign = -1 else: as_sign = 1 # radius = box: circle arc if (self.radius == self.box_size[0] / 2.0) and ( self.radius == self.box_size[1] / 2.0): pts += ShapeArc( self.center, self.radius, self.start_angle, self.end_angle, angle_step=self.angle_step, clockwise=self.clockwise) # radius = zero: part of rectangle elif self.radius <= 0: for a in arange(self.start_angle, self.end_angle + 45.0, as_sign * 90.0): pts += [(cx + sign(math.cos(DEG2RAD * a)) * dx, cy + sign(math.sin(DEG2RAD * a)) * dy)] # arbitrary else: for a in numpy.arange(self.start_angle, self.end_angle + 45.0, as_sign * 90.0): start = max(self.start_angle, a - a % 90.0 + 45.0 - as_sign * 45.0) end = min(self.end_angle, a - a % 90.0 + 45.0 + as_sign * 45.0) pts += ShapeArc( center=(cx + numpy.sign(math.cos(DEG2RAD * a)) * (dx - self.radius), cy + numpy.sign(math.sin(DEG2RAD * a)) * (dy - self.radius)), radius=self.radius, start_angle=start, end_angle=end, angle_step=self.angle_step, clockwise=self.clockwise) return pts
class ShapeSamplePeriodic(ShapeSample): """ creates a shape sampling points on another shape along specified periodic distances from the start point of the shape """ sampling_period = PositiveNumberProperty(required = True) exclude_ends = Size2Property(default = (0.0, 0.0), doc = "Lengths not taken into account on both ends") sampling_distances = LockedProperty() def define_sampling_distances(self): L = self.original_shape.length() return arange(self.exclude_ends[0], L-self.exclude_ends[1]+0.001 * self.sampling_period, self.sampling_period)
class Layout(WindowWaveguideTemplate.Layout): core_process = ProcessProperty(default=TECH.PROCESS.WG, doc="process for the waveguide core") core_purpose = PurposeProperty(default=TECH.PURPOSE.LF.LINE, doc="drawing purpose for the waveguide core") cladding_process = ProcessProperty(doc="process for the waveguide cladding, defaults to the core process") cladding_purpose = PurposeProperty(default=TECH.PURPOSE.LF_AREA, doc="drawing purpose layer for the cladding") # core_width = PositiveNumberProperty(default=TECH.WG.CORE_WIDTH) cladding_width = PositiveNumberProperty(default=TECH.WG.CLADDING_WIDTH, doc="total width of the waveguide with cladding") def _default_cladding_process(self): return self.core_process def _default_cover_layers(self): # Layer for Manhattan rectangles return [PPLayer(self.cladding_process, self.cladding_purpose)] def validate_properties(self): if self.cladding_width < self.core_width: raise PropertyValidationError( "The waveguide cladding should be at least of as wide as the core. core={:f}, cladding={:f}".format( self.core_width, self.cladding_width)) return True def _default_width(self): return self.cladding_width def _default_windows(self): # populate the cladding PPlayers clad_pp_layers_list = [ TECH.PPLAYER.AIM.BESAMFILL, TECH.PPLAYER.AIM.BCAAMFILL, TECH.PPLAYER.AIM.BSEAMFILL, TECH.PPLAYER.AIM.BFNAMFILL, TECH.PPLAYER.AIM.BSNAMFILL, TECH.PPLAYER.AIM.BM1AMFILL, TECH.PPLAYER.AIM.BMLAMFILL, TECH.PPLAYER.AIM.BM2AMFILL, ] windows = [] # add cladding windows for pplayer in clad_pp_layers_list: windows.append(PathTraceWindow(layer=pplayer, start_offset=-0.5 * self.cladding_width, end_offset=+0.5 * self.cladding_width, shape_property_name="cladding_shape")) # add core layer windows.append(PathTraceWindow( layer = TECH.PPLAYER.WG.COR, start_offset = -0.5 * self.core_width, end_offset = +0.5 * self.core_width)) return windows
class ShapeRadialWedge(Shape): """ radial wedge: the coordinates of the start and end point are specified in polar coordinates from a given center """ center = Coord2Property(default=(0.0, 0.0)) inner_radius = PositiveNumberProperty(required=True) outer_radius = PositiveNumberProperty(required=True) inner_width = PositiveNumberProperty(required=True) outer_width = PositiveNumberProperty(required=True) angle = NormalizedAngleProperty(required=True) def __init__(self, **kwargs): kwargs["closed"] = True super(ShapeRadialWedge, self).__init__(**kwargs) def define_points(self, pts): cosangle = math.cos(self.angle * DEG2RAD) sinangle = math.sin(self.angle * DEG2RAD) bc = (self.center[0] + self.inner_radius * math.cos(self.angle * DEG2RAD), self.center[1] + self.inner_radius * math.sin(self.angle * DEG2RAD)) ec = (self.center[0] + self.outer_radius * math.cos(self.angle * DEG2RAD), self.center[1] + self.outer_radius * math.sin(self.angle * DEG2RAD)) pts += [(bc[0] + sinangle * self.inner_width / 2.0, bc[1] - cosangle * self.inner_width / 2.0), (bc[0] - sinangle * self.inner_width / 2.0, bc[1] + cosangle * self.inner_width / 2.0), (ec[0] - sinangle * self.outer_width / 2.0, ec[1] + cosangle * self.outer_width / 2.0), (ec[0] + sinangle * self.outer_width / 2.0, ec[1] - cosangle * self.outer_width / 2.0)] return pts def move(self, position): self.center = (self.center[0] + position[0], self.center[1] + position[1]) return self def is_empty(self): return self.inner_radius == self.outer_radius
class Layout(GenericWaveGuideTemplate.Layout): cladding_width = PositiveNumberProperty(default=2.0) exclusion_width = PositiveNumberProperty(default=20.0) cladding_layer = LayerProperty() exclusion_layer = LayerProperty() def _default_exclusion_layer(self): return TECH.PPLAYER.MSN_TRENCH def _default_core_layer(self): return TECH.PPLAYER.MSN def _default_cladding_layer(self): return TECH.PPLAYER.CLADDING def _default_core_width(self): return 0.45 def _default_windows(self): windows = [ PathTraceWindow(layer=self.core_layer, start_offset=-0.5 * self.core_width, end_offset=0.5 * self.core_width), PathTraceWindow(layer=self.cladding_layer, start_offset=-0.5 * self.cladding_width, end_offset=0.5 * self.cladding_width), PathTraceWindow(layer=self.exclusion_layer, start_offset=-0.5 * self.exclusion_width, end_offset=0.5 * self.exclusion_width) ] windows = [ PathTraceWindow(layer=layer, start_offset=-off / 2.0, end_offset=off / 2.0) for off, layer in zip([1, 2., 3.0, 5.0, 9.0], [ self.core_layer, self.cladding_layer, self.exclusion_layer ]) ] return windows
class ShapeArc(ShapeEllipseArc): """ circular arc """ radius = PositiveNumberProperty(default=1.0) box_size = DefinitionProperty(fdef_name="define_box_size") def __init__(self, **kwargs): ShapeEllipseArc.__init__(self, **kwargs) # super gives error -- why? FIXME def define_box_size(self): bs = Coord2(2 * self.radius, 2 * self.radius) return bs
class ShapeRingSegment(Shape): """ ring segment """ center = Coord2Property(default=(0.0, 0.0)) angle_start = AngleProperty(default=0.0) angle_end = AngleProperty(default=90.0) inner_radius = PositiveNumberProperty(required=True) outer_radius = PositiveNumberProperty(required=True) angle_step = AngleProperty(default=TECH.METRICS.ANGLE_STEP) def __init__(self, **kwargs): kwargs["closed"] = True super(ShapeRingSegment, self).__init__(**kwargs) def define_points(self, pts): arc1 = ShapeArc( center=self.center, radius=self.inner_radius, start_angle=self.angle_start, end_angle=self.angle_end, angle_step=self.angle_step) Shape.reverse(arc1) # do not destroy dynamism arc2 = ShapeArc( center=self.center, radius=self.outer_radius, start_angle=self.angle_start, end_angle=self.angle_end, angle_step=self.angle_step) pts += arc1 pts += arc2 return pts def move(self, position): self.center = (self.center[0] + position[0], self.center[1] + position[1]) return self def is_empty(self): return self.inner_radius == self.outer_radius or self.angle_start == self.angle_end
class __TextElement__(__LayerElement__): text = StringProperty(required=True) coordinate = Coord2Property(default=(0.0, 0.0)) alignment = AlignmentProperty(default=(constants.TEXT_ALIGN_CENTER, constants.TEXT_ALIGN_TOP)) height = PositiveNumberProperty(default=20.0) font = FontProperty(default=TEXT_FONT_DEFAULT) def __init__(self, layer, text, coordinate=(0.0, 0.0), alignment=(constants.TEXT_ALIGN_CENTER, constants.TEXT_ALIGN_TOP), font=TEXT_FONT_DEFAULT, height=20.0, transformation=None, **kwargs): super(__TextElement__, self).__init__(layer=layer, text=text, coordinate=coordinate, alignment=alignment, font=font, height=height, transformation=transformation, **kwargs) def get_h_alignment(self): return self.alignment[0] h_alignment = property(get_h_alignment) def get_v_alignment(self): return self.alignment[1] v_alignment = property(get_v_alignment) def is_empty(self): return (len(string.strip(self.text)) == 0) or (self.height == 0)
class OneEndedNaturalSpline(Shape): radius = PositiveNumberProperty(required=True) angle = AngleProperty(required=True) angle_step = AngleProperty(default=TECH.METRICS.ANGLE_STEP) def define_points(self, pts): alpha = self.angle * DEG2RAD c = math.sin(alpha) if self.angle == 45.0: t = 0.5 else: c2 = c**2 t = math.sin(math.atan(((1.0 - c2) / c2)**0.125))**2 a2 = (1 - t)**2 * (t**4 + (1 - t)**4) L = self.radius * 2 * t * (1 - t) / ( 3 * (t**4 + (1 - t)**4)**1.5 ) # characteristic length of the full natural spline of a right angle # control points of full natural spline (right angle) q0_0 = Coord2(-L, 0) q0_1 = Coord2(0, 0) q0_2 = Coord2(0, 0) q0_3 = Coord2(0, L) # control points of first section of the spline q1_0 = t * q0_0 + (1 - t) * q0_1 q2_0 = t**2 * q0_0 + 2 * t * (1 - t) * q0_1 + (1 - t)**2 * q0_2 q3_0 = t**3 * q0_0 + 3 * t**2 * (1 - t) * q0_1 + 3 * t * ( 1 - t)**2 * q0_2 + (1 - t)**3 * q0_3 S_control = Shape(points=[q0_0, q1_0, q2_0, q3_0]) steps = int(math.ceil(2.0 * self.angle / self.angle_step)) return ShapeBezier(original_shape=S_control, steps=steps).points # add angle step as a parameter
class Layout(GenericWaveGuideTemplate.Layout): cladding_width = PositiveNumberProperty() cladding_layer = LayerProperty() def _default_cladding_width(self): return self.core_width + 4.0 def _default_core_layer(self): return TECH.PPLAYER.SI def _default_cladding_layer(self): return TECH.PPLAYER.SHALLOW def _default_windows(self): windows = [ PathTraceWindow(layer=self.core_layer, start_offset=-0.5 * self.core_width, end_offset=0.5 * self.core_width), PathTraceWindow(layer=self.cladding_layer, start_offset=-0.5 * self.cladding_width, end_offset=0.5 * self.cladding_width) ] return windows
class __ShapePathBase__(__ShapeStartEndAngle__): """ base class for path shapes""" path_width = PositiveNumberProperty(required=True)
class ShapeSerif(__ShapeModifier__): """ puts a bump on the corners of a given shape """ stub_width = PositiveNumberProperty(required=True) stub_height = PositiveNumberProperty(required=True) tip_width = NonNegativeNumberProperty(required=True) only_sharp_angles = BoolProperty(default=False) def __init__(self, **kwargs): super(ShapeSerif, self).__init__(**kwargs) if self.original_shape.closed: self.close() else: self.open() def define_points(self, pts): c = Shape(self.original_shape) if len(c) == 0: return if self.original_shape.is_closed(): if not c[0] == c[-1]: c.append(c[0]) #closed curve c.append(c[1]) else: # open curve pts.append(c[0]) c.remove_identicals() min_sw = self.stub_width for i in range(1, len(c) - 1): angle1 = angle_rad(c[i], c[i - 1]) angle2 = angle_rad(c[i + 1], c[i]) turn = (angle2 - angle1 + pi) % (2 * pi) - pi if turn == 0 or (abs(turn) <= (pi / 2.0) and self.only_sharp_angles): pts.append(c[i]) elif abs(turn == pi): LOG.error("Cannot stub shape with a 180 degree turn") else: d1 = distance(c[i], c[i - 1]) d2 = distance(c[i + 1], c[i]) L = self.stub_width * sin(turn / 2.0) / sin(turn) max_L = max([d1 / 2.0, d2 / 2.0]) if L > max_L: L = max_L sw = L * sin(turn) / sin(turn / 2.0) else: sw = self.stub_width if sw < min_sw: min_sw = sw theta_div2 = (pi - angle1 + angle2) % (2 * pi) / 2.0 s1 = Coord2(c[i][0] - L * cos(angle1), c[i][1] - L * sin(angle1)) s2 = (c[i][0] + L * cos(angle2), c[i][1] + L * sin(angle2)) B = -self.stub_height * sign(turn) delta = 0.5 * (self.stub_width - self.tip_width) s2 = (s1[0] + B * cos(angle1 + theta_div2) + delta * cos(angle1 + theta_div2 - pi / 2.0), s1[1] + B * sin(angle1 + theta_div2) + delta * sin(angle1 + theta_div2 - pi / 2.0)) s4 = Coord2(c[i][0] + L * cos(angle2), c[i][1] + L * sin(angle2)) s3 = (s4[0] + B * cos(angle2 + pi - theta_div2) + delta * cos(angle2 + pi - theta_div2 + pi / 2.0), s4[1] + B * sin(angle2 + pi - theta_div2) + delta * sin(angle2 + pi - theta_div2 + pi / 2.0)) pts.append(s1) pts.append(s2) pts.append(s3) pts.append(s4) if not self.original_shape.closed: # open curve pts.append(c[-1]) if min_sw < self.stub_width: LOG.warning("Stub width is reduced from " + str(self.stub_width) + " to " + str(min_sw) + "to stub shape.") return pts
class AdiabaticSplineCircleSplineShape(Shape): start_point = Coord2Property(required=True) turn_point = Coord2Property(required=True) end_point = Coord2Property(required=True) radius = PositiveNumberProperty(required=True) adiabatic_angles = RestrictedProperty( allow_none=True, doc="tuple of adiabatic transistion angles for input and output") angle_step = AngleProperty(default=TECH.METRICS.ANGLE_STEP) def define_points(self, pts): alpha_in = self.adiabatic_angles[0] alpha_out = self.adiabatic_angles[1] turn_angle = turn_deg(self.start_point, self.turn_point, self.end_point) if (alpha_in + alpha_out) > abs(turn_angle): alpha_in = alpha_out = 0.5 * abs(turn_angle) bend_angle = abs(turn_angle) - alpha_in - alpha_out # first section if alpha_in > 0.0: S5 = OneEndedNaturalSpline(radius=self.radius, angle=alpha_in, angle_step=self.angle_step) else: S5 = Shape((-self.radius, 0)) # middle section if bend_angle > 0.0: S5 += ShapeBendRelative( start_point=S5[-1], input_angle=alpha_in, angle_amount=bend_angle, radius=self.radius, angle_step=self.angle_step, ) # last section if alpha_out > 0.0: S6 = Shape( OneEndedNaturalSpline(radius=self.radius, angle=alpha_out, angle_step=self.angle_step)) else: S6 = Shape((-self.radius, 0)) S6.h_mirror() S6.rotate((0, 0), abs(turn_angle)) S6.reverse() S6.move(S5[-1] - S6[0]) # transform to match the right position S = S5 + S6 if turn_angle < 0: S.v_mirror() L = straight_line_from_point_angle((0.0, 0.0), turn_angle) d = L.distance(S[-1]) ep = S[-1] if abs(turn_angle) == 90.0: d = ep.x else: d = ep.x - ep.y * math.cos(turn_angle * DEG2RAD) / math.sin( turn_angle * DEG2RAD) S.move((-d, 0.0)) S.rotate((0.0, 0.0), angle_deg(self.turn_point, self.start_point)) S.move(self.turn_point) S.remove_identicals() return S.points
class ShapeArcLineArc(Shape): coord_start = Coord2Property(required=True) angle_start = AngleProperty(required=True) radius_start = PositiveNumberProperty(required=True) coord_end = Coord2Property(required=True) angle_end = AngleProperty(required=True) radius_end = PositiveNumberProperty(required=True) angle_step = AngleProperty(default=TECH.METRICS.ANGLE_STEP) def __init__(self, coord_start, angle_start, radius_start, coord_end, angle_end, radius_end, **kwargs): super(ShapeArcLineArc, self).__init__(coord_start=coord_start, coord_end=coord_end, angle_start=angle_start, angle_end=angle_end, radius_start=radius_start, radius_end=radius_end, **kwargs) def define_points(self, pts): sa = self.angle_start * DEG2RAD ea = self.angle_end * DEG2RAD bae = (ea + pi) % (2 * pi) # normalize angles between 0 and 2pi sa = (sa) % (2 * pi) ea = (ea) % (2 * pi) #angle bvetween two points connect_angle = angle_rad(self.coord_end, self.coord_start) ca_start = (connect_angle - sa) % (2 * pi) ca_end = (connect_angle - ea) % (2 * pi) #LOG.debug("ca: %f %f %f" %(, connect_angle , ca_start, ca_end)) #check both positive and negative radii valid = False signs = [(1, 1), (1, -1), (-1, 1), (-1, -1)] for s in signs: radius_start = abs(self.radius_start) * s[0] radius_end = abs(self.radius_end) * s[1] # Centers of circles through the points. c_start = (self.coord_start[0] + radius_start * sin(sa), self.coord_start[1] - radius_start * cos(sa)) c_end = (self.coord_end[0] + radius_end * sin(ea), self.coord_end[1] - radius_end * cos(ea)) #distance between centers dm = distance(c_start, c_end) if abs(radius_start - radius_end) > dm: # no valid solution possible continue # unit vector between circle centers mm = ((c_end[0] - c_start[0]) / dm, (c_end[1] - c_start[1]) / dm) # angle between normal to connector line and circle centers alpha = -acos((radius_start - radius_end) / dm) # unit vector from m to p. mp = (mm[0] * cos(alpha) + mm[1] * sin(alpha), -mm[0] * sin(alpha) + mm[1] * cos(alpha)) # Point at first circle. p0 = (c_start[0] + radius_start * mp[0], c_start[1] + radius_start * mp[1]) # Point at second circle. p1 = (c_end[0] + radius_end * mp[0], c_end[1] + radius_end * mp[1]) #LOG.debug("p0, p1:" %( p0, p1)) forward_angle = angle_rad(p1, p0) % (2 * pi) backward_angle = angle_rad(p0, p1) % (2 * pi) forward_turn = (forward_angle - sa + pi) % (2 * pi) - pi backward_turn = (backward_angle - bae + pi) % (2 * pi) - pi # LOG.debug("F: %f B:%f %f %f" % (s[0], s[1], forward_turn, backward_turn)) if (forward_turn * s[0] <= 0) and (backward_turn * s[1] >= 0): valid = True break if not valid: LOG.error("Can't connect two points with arc_line_arc") raise SystemExit #LOG.debug("angles: %f %f %f %f" %( angle_start, straight_angle*180/pi, angle_end, backward_angle*180/pi)) if forward_turn == 0.0: pts += [self.coord_start] else: pts += ShapeBendRelative(self.coord_start, abs(radius_start), sa * RAD2DEG, forward_turn * RAD2DEG, angle_step=self.angle_step) if backward_turn == 0.0: pts += [self.coord_end] else: bend2 = ShapeBendRelative(self.coord_end, abs(radius_end), bae * RAD2DEG, backward_turn * RAD2DEG, angle_step=self.angle_step) bend2.reverse() pts += bend2 return pts def move(self, position): self.coord_start = (self.coord_start[0] + position[0], self.coord_start[1] + position[1]) self.coord_end = (self.coord_end[0] + position[0], self.coord_end[1] + position[1]) return self
class Layout(AIMWGWireWaveguideTemplate.Layout): core_width = PositiveNumberProperty(default=0.4) cladding_width = PositiveNumberProperty(default=8.0)