class ShapeEllipseArc(Shape): """ ellipse arc around a given center """ center = Coord2Property(default=(0.0, 0.0)) box_size = Size2Property(default=(1.0, 1.0)) 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(ShapeEllipseArc, self).__init__(**kwargs) def define_points(self, pts): sa = self.start_angle * DEG2RAD ea = self.end_angle * DEG2RAD h_radius = self.box_size[0] / 2.0 v_radius = self.box_size[1] / 2.0 n_s = float(self.end_angle - self.start_angle) / self.angle_step n_steps = int(math.ceil(abs(n_s))) * numpy.sign(n_s) if n_steps == 0: if sa == ea: pts = numpy.array([[ math.cos(sa) * h_radius + self.center[0], math.sin(sa) * v_radius + self.center[1] ]]) else: pts = numpy.array([[ math.cos(sa) * h_radius + self.center[0], math.sin(sa) * v_radius + self.center[1] ], [ math.cos(ea) * h_radius + self.center[0], math.sin(ea) * v_radius + self.center[1] ]]) return pts angle_step = float(ea - sa) / n_steps if self.clockwise: angle_step = -angle_step sign = -1 else: sign = +1 while sign * sa > sign * ea: ea += sign * 2 * math.pi angles = numpy.arange(sa, ea + 0.5 * angle_step, angle_step) pts = numpy.column_stack( (numpy.cos(angles), numpy.sin(angles))) * numpy.array([ (h_radius, v_radius) ]) + numpy.array([(self.center[0], self.center[1])]) return pts def move(self, position): self.center = (self.center.x + position[0], self.center.y + position[1]) return self def is_empty(self): return self.start_angle == self.end_angle or self.box_size[0] == 0.0 or self.box_size[1] == 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 ShapePathSpike(__ShapePathBase__): """ simple path based on a centerline shape,but with a sharp endpoint with a given angle """ spike_angle = AngleProperty(restriction = RestrictRange(0.0, 180.0, False, True), default = 90.0) def define_points(self, pts): original = self.__get_original_shape_without_straight_angles__() if len(original) <= 1: return a = original.angles_rad() a2 = a * 0.5 a1 = roll(a2, 1) if original.closed: a2[-1] = a2[0] a1[0] = a1[-1] else: a2[-1] = self.end_face_angle * DEG2RAD - a2[-2] a1[0] = self.start_face_angle * DEG2RAD - a1[1] a_plus = a2 + a1 cos_a_min = cos(a2 - a1) offsets = column_stack((-sin(a_plus) / cos_a_min, cos(a_plus) / cos_a_min)) * (0.5 * self.path_width) # spikes if not original.closed and self.spike_angle > 0 and self.spike_angle < 180.0: L = 0.5 * self.path_width / tan(self.spike_angle * constants.DEG2RAD * 0.5) start_spike = array([[original[0][0] - cos(a[0]) * L, original[0][1] - sin(a[0]) * L]]) end_spike = array([[original[-1][0] + cos(a[-2]) * L, original[-1][1] + sin(a[-2]) * L]]) else: start_spike = ndarray((0, 2)) end_spike = ndarray((0, 2)) pts = vstack((start_spike, original.points + offsets, end_spike, flipud(original.points - offsets))) return pts
class Shape(transformable.Transformable, StrongPropertyInitializer, MixinBowl): '''Basic shape''' points = PointsDefinitionProperty(fdef_name="define_points") start_face_angle = AngleProperty( allow_none=True, doc= "Use this to overrule the 'dangling' angle at the start of an open shape" ) end_face_angle = AngleProperty( allow_none=True, doc= "Use this to overrule the 'dangling' angle at the end of an open shape" ) def __init__(self, points=[], closed=None, **kwargs): if not (isinstance(points, Shape) and (closed is None)): kwargs["closed"] = closed else: if (points.closed): kwargs["closed"] = True if isinstance(points, Shape): if not "start_face_angle" in kwargs: kwargs["start_face_angle"] = points.start_face_angle if not "end_face_angle" in kwargs: kwargs["end_face_angle"] = points.end_face_angle if (points is not None): if (isinstance(points, list) or isinstance(points, ndarray) or isinstance(points, Shape) or isinstance(points, tuple)): if (len(points) > 0): kwargs["points"] = points elif (isinstance(points, Coord2)): kwargs["points"] = [points] else: try: from dependencies.shapely_wrapper import CoordinateSequence if (isinstance(points, CoordinateSequence)): pl = [pt for pt in points] kwargs["points"] = pl else: raise Exception() except Exception, e: raise IpkissException( "Unexpected type %s for parameter 'points' in Shape::__init__" % str(type(points))) super(Shape, self).__init__(**kwargs)
class ShapeBend(ShapeArc): """ bend: circular arc but specified by its starting point insetad of center """ start_point = Coord2Property(default=(0.0, 0.0)) center = DefinitionProperty(fdef_name="define_center") start_angle = DefinitionProperty(fdef_name="define_start_angle") end_angle = DefinitionProperty(fdef_name="define_end_angle") input_angle = AngleProperty(default=0.0) output_angle = AngleProperty(default=90.0) def __init__(self, **kwargs): super(ShapeBend, self).__init__(**kwargs) def __get_sign(self): if self.clockwise: sign = -1 else: sign = 1 return sign def define_center(self): sign = self.__get_sign() c = (self.start_point[0] - sign * self.radius * math.sin(self.input_angle * DEG2RAD), self.start_point[1] + sign * self.radius * math.cos(self.input_angle * DEG2RAD)) return c def define_start_angle(self): sign = self.__get_sign() a = self.input_angle - sign * 90.0 return a def define_end_angle(self): sign = self.__get_sign() a = self.output_angle - sign * 90.0 return a def move(self, position): self.start_point = Coord2(self.start_point[0] + position[0], self.start_point[1] + position[1]) return self
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 ShapeRoundAdiabaticSplineGeneric(__ShapeModifier__): """ returns a shape with adiabatic spline corners """ radii = RestrictedProperty( restriction=RestrictList(restriction=RESTRICT_NONNEGATIVE), required=True) adiabatic_angles_list = RestrictedProperty(required=True) angle_step = AngleProperty(default=TECH.METRICS.ANGLE_STEP) def __original_shape_without_straight_angles__(self): S1 = Shape(self.original_shape) S = Shape(S1).remove_straight_angles() straight = (numpy.abs( numpy.abs((S1.turns_rad() + (0.5 * numpy.pi)) % numpy.pi) - 0.5 * numpy.pi) < 0.00001) if not S1.closed: straight[0] = False straight[-1] = False R = numpy.delete(self.radii, straight.nonzero()[0], 0) A = numpy.delete(self.adiabatic_angles_list, straight.nonzero()[0], 0) return (S, R, A) def define_points(self, pts): (s, R, A) = self.__original_shape_without_straight_angles__() if len(R) != len(s): raise AttributeError( "ShapeRoundAdiabaticSplineGeneric: length of radius vector should be identical to that of points in shape" ) if len(A) != len(s): raise AttributeError( "ShapeRoundAdiabaticSplineGeneric: length of adiabatic_angles vector should be identical to that of points in shape" ) if len(s) < 3: self.__points__ = s.points return margin = 0.5 / get_grids_per_unit() S = [] if not self.original_shape.closed: S.append(numpy.array([s.points[0]])) L1 = 0.0 for i in range(1, len(s) - 1): sh = AdiabaticSplineCircleSplineShape(start_point=s[i - 1], turn_point=s[i], end_point=s[i + 1], radius=R[i], adiabatic_angles=A[i], angle_step=self.angle_step) L0 = sh[0].distance(s[i]) if L0 + L1 - margin > s[i - 1].distance(s[i]): LOG.warning( "Insufficient space for spline rounding in (%f, %f)" % (s[i].x, s[i].y)) L1 = sh[-1].distance(s[i]) S.append(sh.points) if self.original_shape.closed: sh = AdiabaticSplineCircleSplineShape(start_point=s[-2], turn_point=s[-1], end_point=s[0], radius=R[-1], adiabatic_angles=A[-1], angle_step=self.angle_step) L0 = sh[0].distance(s[-1]) if L0 + L1 - margin > s[-2].distance(s[-1]): LOG.warning( "Insufficient space for spline rounding in (%f, %f)" % (s[-1].x, s[-1].y)) L1 = sh[-1].distance(s[-1]) S.append(sh.points) sh = AdiabaticSplineCircleSplineShape(start_point=s[-1], turn_point=s[0], end_point=s[1], radius=R[0], adiabatic_angles=A[0], angle_step=self.angle_step) L0 = sh[0].distance(s[0]) if L0 + L1 - margin > s[-1].distance(s[0]): LOG.warning( "Insufficient space for spline rounding in (%f, %f)" % (s[0].x, s[0].y)) L1 = sh[-1].distance(s[0]) S.append(sh.points) self.__closed__ = True else: # open curve S.append(numpy.array([s.points[-1]])) L0 = 0.0 if L0 + L1 - margin > s[-2].distance(s[-1]): LOG.warning( "Insufficient space for spline rounding in (%f, %f)" % (s[-1].x, s[-1].y)) L1 = sh[-1].distance(s[0]) self.__closed__ = False return numpy.vstack(S)
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 ShapeRoundedRectangle(Shape): """ rectangle with rounded corners """ center = Coord2Property(default=(0.0, 0.0)) box_size = Size2Property(default=(1.0, 1.0)) radius = NonNegativeNumberProperty(default=1.0) angle_step = AngleProperty(default=TECH.METRICS.ANGLE_STEP) def __init__(self, **kwargs): kwargs["closed"] = True super(ShapeRoundedRectangle, self).__init__(**kwargs) 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.radius <= 0: pts += [(cx + dx, cy + dy), (cx - dx, cy + dy), (cx - dx, cy - dy), (cx + dx, cy - dy), (cx + dx, cy + dy)] else: if self.radius > min(self.box_size) / 2.0: self.radius = min(self.box_size) / 2.0 if (self.radius == self.box_size[0] / 2.0) and (self.radius == self.box_size[1] / 2.0): pts += ShapeCircle(center=self.center, radius=self.radius, angle_step=self.angle_step) elif self.radius == self.box_size[0] / 2.0: pts += ShapeArc(center=(cx, cy + dy - self.radius), radius=self.radius, start_angle=0.0, end_angle=180.0, angle_step=self.angle_step) pts += ShapeArc(center=(cx, cy - dy + self.radius), radius=self.radius, start_angle=180.0, end_angle=360.0, angle_step=self.angle_step) elif self.radius == self.box_size[1] / 2.0: pts += ShapeArc(center=(cx + dx - self.radius, cy), radius=self.radius, start_angle=270, end_angle=450.0, angle_step=self.angle_step) pts += ShapeArc(center=(cx - dx + self.radius, cy), radius=self.radius, start_angle=90.0, end_angle=270.0, angle_step=self.angle_step) else: pts += ShapeArc(center=(cx + dx - self.radius, cy + dy - self.radius), radius=self.radius, start_angle=0.0, end_angle=90.0, angle_step=self.angle_step) pts += ShapeArc(center=(cx - dx + self.radius, cy + dy - self.radius), radius=self.radius, start_angle=90.0, end_angle=180.0, angle_step=self.angle_step) pts += ShapeArc(center=(cx - dx + self.radius, cy - dy + self.radius), radius=self.radius, start_angle=180.0, end_angle=270.0, angle_step=self.angle_step) pts += ShapeArc(center=(cx + dx - self.radius, cy - dy + self.radius), radius=self.radius, start_angle=270.0, end_angle=360.0, angle_step=self.angle_step) 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.0 or self.box_size[1] == 0.0