def fit(self, pos, unit):
raise NotImplementedError
+ def draw(self, surf, color):
+ pass
+
class Rect(Shape):
def __init__(self, *args):
self.left, self.top, self.width, self.height = args
def fit(self, pos, unit):
- return pygame.Rect(
+ klass = type(self)
+ if klass not in (Rect, Ellipse):
+ raise NotImplementedError
+ return klass(
(round(pos[0] + self.left * unit[0]), round(pos[1] + self.top * unit[1])),
(round(self.width * unit[0]), round(self.height * unit[1])),
)
- def draw(self, surf, pos, unit, color):
- pygame.draw.rect(surf, color, self.fit(pos, unit))
+ def draw(self, surf, color):
+ pygame.draw.rect(
+ surf, color, pygame.Rect(self.left, self.top, self.width, self.height)
+ )
+
+
+class Ellipse(Rect):
+ def draw(self, surf, color):
+ pygame.draw.ellipse(
+ surf, color, pygame.Rect(self.left, self.top, self.width, self.height)
+ )
class Polygon(Shape):
self.points = list(points)
def fit(self, pos, unit):
- return [
- (round(pos[0] + point[0] * unit[0]), round(pos[1] + point[1] * unit[1]))
- for point in self.points
- ]
+ if type(self) != Polygon:
+ raise NotImplementedError
+ return Polygon(
+ [
+ (round(pos[0] + point[0] * unit[0]), round(pos[1] + point[1] * unit[1]))
+ for point in self.points
+ ]
+ )
- def draw(self, surf, pos, unit, color):
- pygame.draw.polygon(surf, color, self.fit(pos, unit))
+ def draw(self, surf, color):
+ pygame.draw.polygon(surf, color, self.points)
class Circle(Shape):
- def __init__(self, pos, radius):
- self.pos = pos
+ def __init__(self, center, radius):
+ self.center = center
self.radius = radius
def fit(self, pos, unit):
- return pygame.Rect(
+ if type(self) != Circle:
+ raise NotImplementedError
+ if unit[0] == unit[1]:
+ return Circle(
+ (
+ pos[0] + self.center[0] * unit[0],
+ pos[1] + self.center[1] * unit[1],
+ ),
+ self.radius * unit[0],
+ )
+ return Ellipse(
(
- round(pos[0] + (self.pos[0] - self.radius) * unit[0]),
- round(pos[1] + (self.pos[1] - self.radius) * unit[1]),
+ round(pos[0] + (self.center[0] - self.radius) * unit[0]),
+ round(pos[1] + (self.center[1] - self.radius) * unit[1]),
),
(round(self.radius * 2 * unit[0]), round(self.radius * 2 * unit[1])),
)
- def draw(self, surf, pos, unit, color):
- pygame.draw.ellipse(surf, color, self.fit(pos, unit))
+ def draw(self, surf, color):
+ pygame.draw.circle(surf, color, self.center, self.radius)
class StrokeSquareLine(Polygon):
Rotate a rectangle along the direction in which we're drawing.
"""
def __init__(self, p1, p2, width):
- p_rel = (p1[0] - p2[0], p1[1] - p2[1])
+ self.p1 = p1
+ self.p2 = p2
+ self.width = width
+ super().__init__(self.get_points())
+
+ def get_points(self):
+ p_rel = (self.p1[0] - self.p2[0], self.p1[1] - self.p2[1])
twice_distance = 2 * sqrt(p_rel[0] * p_rel[0] + p_rel[1] * p_rel[1])
if twice_distance:
back = (
- p_rel[0] * width / twice_distance, p_rel[1] * width / twice_distance
+ p_rel[0] * self.width / twice_distance,
+ p_rel[1] * self.width / twice_distance,
)
back_sum, back_diff = back[0] + back[1], back[0] - back[1]
else:
- back_sum, back_diff = -width / 2, width / 2
+ back_sum, back_diff = -self.width / 2, self.width / 2
# left is aka: (back[1], -back[0])
# right is aka: (-back[1], back[0])
- super().__init__(
- [
- (p1[0] + back_sum, p1[1] - back_diff),
- (p1[0] + back_diff, p1[1] + back_sum),
- (p2[0] - back_sum, p2[1] + back_diff),
- (p2[0] - back_diff, p2[1] - back_sum),
- ]
+ return [
+ (self.p1[0] + back_sum, self.p1[1] - back_diff),
+ (self.p1[0] + back_diff, self.p1[1] + back_sum),
+ (self.p2[0] - back_sum, self.p2[1] + back_diff),
+ (self.p2[0] - back_diff, self.p2[1] - back_sum),
+ ]
+
+ def fit(self, pos, unit):
+ if type(self) != StrokeSquareLine:
+ raise NotImplementedError
+ return StrokeSquareLine(
+ (pos[0] + self.p1[0] * unit[0], pos[1] + self.p1[1] * unit[1]),
+ (pos[0] + self.p2[0] * unit[0], pos[1] + self.p2[1] * unit[1]),
+ self.width * max(unit),
)
that should be visually indistinguishable with a half circle
"""
def __init__(self, p1, p2, width):
- radius = width / 2
+ self.p1 = p1
+ self.p2 = p2
+ self.width = width
+ super().__init__(self.get_points())
+
+ def get_points(self):
+ radius = self.width / 2
num_vertices = self.corners_needed_to_appear_round(radius)
- delta = (p1[0] - p2[0], p1[1] - p2[1])
+ delta = (self.p1[0] - self.p2[0], self.p1[1] - self.p2[1])
distance = sqrt(delta[0] * delta[0] + delta[1] * delta[1])
points = []
if distance == 0:
back = (delta[0] * radius / distance, delta[1] * radius / distance)
points.extend(
(
- (p2[0] + back[1], p2[1] - back[0]),
- (p1[0] + back[1], p1[1] - back[0]),
+ (self.p2[0] + back[1], self.p2[1] - back[0]),
+ (self.p1[0] + back[1], self.p1[1] - back[0]),
)
)
- p = p1
+ p = self.p1
for i in range(num_vertices):
angle = (i + initial_corner) * tau / num_vertices
points.append((p[0] + cos(angle) * radius, p[1] + sin(angle) * radius))
if i == opposing_corner:
points.extend(
(
- (p1[0] - back[1], p1[1] + back[0]),
- (p2[0] - back[1], p2[1] + back[0]),
+ (self.p1[0] - back[1], self.p1[1] + back[0]),
+ (self.p2[0] - back[1], self.p2[1] + back[0]),
)
)
- p = p2
- super().__init__(points)
+ p = self.p2
+ return points
@staticmethod
def corners_needed_to_appear_round(radius, threshold=.5):
return ceil(pi / acos(1 - threshold / radius))
+ def fit(self, pos, unit):
+ if type(self) != StrokeRoundLine:
+ raise NotImplementedError
+ return StrokeRoundLine(
+ (pos[0] + self.p1[0] * unit[0], pos[1] + self.p1[1] * unit[1]),
+ (pos[0] + self.p2[0] * unit[0], pos[1] + self.p2[1] * unit[1]),
+ self.width * max(unit),
+ )
+
+ def draw(self, surf, pos):
+ super().draw(surf, pos)
+
class Shapes(Shape):
def __init__(self, shapes):
self.shapes = list(shapes)
- def draw(self, surf, pos, unit, color):
+ def fit(self, pos, unit):
+ if type(self) != Shapes:
+ raise NotImplementedError
+ return Shapes([s.fit(pos, unit) for s in self.shapes])
+
+ def draw(self, surf, pos):
for shape in self.shapes:
- shape.draw(surf, pos, unit, color)
+ shape.draw(surf, pos)
class StrokePath:
def __init__(self, points, closed, width):
- self.points = list(*points)
+ self.points = list(points)
self.closed = bool(closed)
self.width = width
yield StrokeRoundLine(old, new, self.width)
old = new
+ def fit(self, pos, unit):
+ if type(self) != StrokePath:
+ raise NotImplementedError
+ return StrokePath(
+ [
+ (pos[0] + point[0] * unit[0], pos[1] + point[1] * unit[1])
+ for point in self.points
+ ],
+ self.closed,
+ self.width,
+ )
+
draw = Shapes.draw
self.center = center
self.radius = radius
super().__init__([], True, width)
+ self.points.extend(self.get_points())
- def get_points(self, unit):
- num_vertices = StrokeRoundLine.corners_needed_to_appear_round(self.radius * max(unit))
+ def get_points(self):
+ num_vertices = StrokeRoundLine.corners_needed_to_appear_round(self.radius)
return [
(
self.center[0] + cos(a) * self.radius,
for a in (tau * i / num_vertices for i in range(num_vertices))
]
- def draw(self, surf, pos, unit, color):
- if not self.points:
- self.points.extend(self.get_points(unit))
- super().draw(surf, pos, unit, color)
+ def fit(self, pos, unit):
+ if type(self) != StrokeCircle:
+ raise NotImplementedError
+ return StrokeCircle(
+ (pos[0] + self.center[0] * unit[0], pos[1] + self.center[1] * unit[1]),
+ self.radius * max(unit),
+ self.width * max(unit),
+ )
+
+ def draw(self, surf, pos):
+ super().draw(surf, pos)
class StrokeCircleSegment(StrokeCircle):
super().__init__(center, radius, width)
self.closed = False
- def get_points(self, unit):
+ def get_points(self):
return [
(self.center[0] + cos(a) * self.radius, self.center[1] + sin(a) * self.radius)
for a in self.get_angle_segments(
self.start_angle,
self.end_angle,
- StrokeRoundLine.corners_needed_to_appear_round(self.radius * max(unit)),
+ StrokeRoundLine.corners_needed_to_appear_round((self.radius + self.width / 2)),
)
]
yield end_angle
break
yield start_angle + a
+
+ def fit(self, pos, unit):
+ if type(self) != StrokeCircleSegment:
+ raise NotImplementedError
+ return StrokeCircleSegment(
+ (pos[0] + self.center[0] * unit[0], pos[1] + self.center[1] * unit[1]),
+ self.radius * max(unit),
+ self.start_angle,
+ self.end_angle,
+ self.width * max(unit),
+ )