From: mar77i Date: Thu, 26 Dec 2024 04:29:09 +0000 (+0100) Subject: make fitting move/scale great again X-Git-Url: https://git.mar77i.info/?a=commitdiff_plain;h=3ecc217914343a4e6f7f5eb24e19af7f60efaebe;p=zenbook_conf make fitting move/scale great again --- diff --git a/ui.py b/ui.py old mode 100755 new mode 100644 index 34d88d6..0f0a9d5 --- a/ui.py +++ b/ui.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - from math import asin, nan, pi, sin from time import time from colorsys import hsv_to_rgb @@ -203,33 +201,23 @@ class Button(UIChild): class IconButton(Button): - def __init__(self, parent, shape, shape_size, rect, callback, is_active): + def __init__(self, parent, shape, rect, callback, is_active): super().__init__(parent, rect, None, callback) self.shape = shape - self.shape_size = shape_size self.is_active = is_active def draw(self): - units = (16, 16) - full_size = (units[0] * self.shape_size[0], units[1] * self.shape_size[1]) - center = self.rect.center if self.pushed: pygame.draw.rect(self.parent.surf, "honeydew4", self.rect) - self.shape.draw( - self.parent.surf, - (center[0] - full_size[0] // 2, center[1] - full_size[1] // 2), - units, - "black" if self.pushed else "white", - ) + self.shape.draw(self.parent.surf, "black" if self.pushed else "white") color = "lime" if self.is_active() else ("red" if self.pushed else "gray") pygame.draw.rect(self.parent.surf, color, self.rect, 8) class Icon(UIChild): - def __init__(self, parent, shape, pos): + def __init__(self, parent, shape): super().__init__(parent) self.shape = shape - self.pos = pos def draw(self): - self.shape.draw(self.parent.surf, self.pos, (8, 8), "gray") + self.shape.draw(self.parent.surf, "gray") diff --git a/vector_assets.py b/vector_assets.py index 3a887ba..2f6d416 100755 --- a/vector_assets.py +++ b/vector_assets.py @@ -1,9 +1,20 @@ +#!/usr/bin/env python3 + from math import cos, pi, sin import pygame from vectors import ( - Polygon, Rect, Shapes, StrokeCircleSegment, StrokeRoundLine, StrokeSquareLine + Circle, + Ellipse, + Polygon, + Rect, + Shapes, + StrokeCircle, + StrokeCircleSegment, + StrokePath, + StrokeRoundLine, + StrokeSquareLine, ) tau = 2 * pi @@ -117,31 +128,24 @@ bluetooth = Shapes( def main(): + unit = (16, 16) shapes = [ - { - "shape": laptop_single, - "pos": (100, 100), - }, - { - "shape": laptop_double, - "pos": (600, 100), - }, - { - "shape": laptop_vertical, - "pos": (1100, 100), - }, - { - "shape": touchscreen, - "pos": (100, 600), - }, - { - "shape": stylus, - "pos": (600, 600), - }, - { - "shape": bluetooth, - "pos": (1100, 600), - }, + laptop_single.fit((100, 100), unit), + laptop_double.fit((600, 100), unit), + laptop_vertical.fit((1100, 100), unit), + touchscreen.fit((100, 600), unit), + stylus.fit((600, 600), unit), + bluetooth.fit((1100, 600), unit), + Shapes( + [ + Ellipse((50, 550), (32, 64)).fit((0, 0), (2, 2)), + Circle((150, 582), 32).fit((0, 0), (2, 2)), + StrokePath( + [(250, 550), (275, 550), (325, 600), (300, 600)], True, 4, + ).fit((0, 0), (2, 2)), + StrokeCircle((400, 614), 64, 4).fit((0, 0), (2, 2)), + ], + ), ] surf = pygame.display.set_mode((2000, 1600)) running = True @@ -163,7 +167,7 @@ def main(): elif dirty: surf.fill("black") for item in shapes: - item["shape"].draw(surf, item["pos"], (16, 16), "green") + item.draw(surf, "green") pygame.display.update() dirty = False clock.tick(60) diff --git a/vectors.py b/vectors.py index be4c4fb..55a024c 100644 --- a/vectors.py +++ b/vectors.py @@ -10,6 +10,9 @@ class Shape: def fit(self, pos, unit): raise NotImplementedError + def draw(self, surf, color): + pass + class Rect(Shape): def __init__(self, *args): @@ -18,13 +21,25 @@ class Rect(Shape): 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): @@ -32,31 +47,45 @@ 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): @@ -64,24 +93,38 @@ 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), ) @@ -91,9 +134,15 @@ class StrokeRoundLine(Polygon): 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: @@ -109,41 +158,58 @@ class StrokeRoundLine(Polygon): 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 @@ -163,6 +229,18 @@ class StrokePath: 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 @@ -171,9 +249,10 @@ class StrokeCircle(StrokePath): 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, @@ -182,10 +261,17 @@ class StrokeCircle(StrokePath): 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): @@ -195,13 +281,13 @@ 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)), ) ] @@ -214,3 +300,14 @@ class StrokeCircleSegment(StrokeCircle): 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), + ) diff --git a/zenbook_conf.py b/zenbook_conf.py index d69fab5..8a39983 100755 --- a/zenbook_conf.py +++ b/zenbook_conf.py @@ -75,8 +75,7 @@ class ZenbookConf(FPSMixin, UIParent): ( IconButton( self, - laptop_single, - (22, 22), + laptop_single.fit((100, 100), (16, 16)), pygame.Rect((68, 68), (416, 416)), partial( self.laptop_cb, "single", bt_switch, touch_switch, stylus_switch @@ -85,8 +84,7 @@ class ZenbookConf(FPSMixin, UIParent): ), IconButton( self, - laptop_double, - (22, 22), + laptop_double.fit((600, 100), (16, 16)), pygame.Rect((568, 68), (416, 416)), partial( self.laptop_cb, "double", bt_switch, touch_switch, stylus_switch @@ -95,8 +93,7 @@ class ZenbookConf(FPSMixin, UIParent): ), IconButton( self, - laptop_vertical, - (22, 22), + laptop_vertical.fit((1100, 100), (16, 16)), pygame.Rect((1068, 68), (416, 416)), partial( self.laptop_cb, @@ -110,20 +107,29 @@ class ZenbookConf(FPSMixin, UIParent): bt_switch, Icon( self, - bluetooth, - (bt_switch.rect.right + 68, bt_switch.rect.centery - 88), + bluetooth.fit( + (bt_switch.rect.right + 68, bt_switch.rect.centery - 88), + (8, 8), + ), ), touch_switch, Icon( self, - touchscreen, - (touch_switch.rect.right + 68, touch_switch.rect.centery - 88), + touchscreen.fit( + (touch_switch.rect.right + 68, touch_switch.rect.centery - 88), + (8, 8), + ), ), stylus_switch, Icon( self, - stylus, - (stylus_switch.rect.right + 68, stylus_switch.rect.centery - 88), + stylus.fit( + ( + stylus_switch.rect.right + 68, + stylus_switch.rect.centery - 88, + ), + (8, 8), + ), ), Button( self,