From d68b6cccaa39e922186b13036a587d1913e90811 Mon Sep 17 00:00:00 2001 From: mar77i Date: Wed, 25 Dec 2024 11:52:45 +0100 Subject: [PATCH] improve vector drawing. split out vector_assets --- vector_assets.py | 180 ++++++++++++++++++++++++++++++ vectors.py | 278 ++++++++--------------------------------------- zenbook_conf.py | 2 +- 3 files changed, 226 insertions(+), 234 deletions(-) create mode 100755 vector_assets.py mode change 100755 => 100644 vectors.py diff --git a/vector_assets.py b/vector_assets.py new file mode 100755 index 0000000..fa4f1dd --- /dev/null +++ b/vector_assets.py @@ -0,0 +1,180 @@ +from math import cos, pi, sin + +import pygame + +from vectors import ( + Polygon, Rect, Shapes, StrokeCircleSegment, StrokeRoundLine, StrokeSquareLine +) + +tau = 2 * pi + + +laptop_single = Shapes( + [ + Rect((2, 3), (16.4, 1)), + Rect((2, 4), (1, 8.1)), + Rect((17.4, 4), (1, 8.1)), + Rect((2, 12.1), (16.4, 1)), + Polygon([(2, 13.1), (18.4, 13.1), (22.4, 18.1), (6, 18.1)]), + ] +) +laptop_double = Shapes( + [ + Rect((2, 3), (16.4, 1)), + Rect((2, 4), (1, 8.1)), + Rect((17.4, 4), (1, 8.1)), + Rect((2, 12.1), (16.4, 1)), + Polygon([(2, 13.1), (3, 13.1), (6.2, 17.1), (5.2, 17.1)]), + Polygon([(17.4, 13.1), (18.4, 13.1), (21.6, 17.1), (20.4, 17.1)]), + Polygon([(5.2, 17.1), (21.6, 17.1), (22.4, 18.1), (6, 18.1)]), + ] +) +laptop_vertical = Shapes( + [ + Rect((0.5, 1), (17.75, 1)), + Rect((0.5, 2), (1, 14.4)), + Rect((9, 2), (1, 14.4)), + Rect((17.25, 2), (1, 14.4)), + Rect((0.5, 16.4), (17.75, 1)), + Polygon([(2, 17.4), (6, 22.4), (20.75, 22.4), (16.75, 17.4)]), + ] +) +FINGER_RADIUS = 1.25 +NUM_VERTICES = 32 +touchscreen = Shapes( + [ + StrokeCircleSegment((12, 16), 5, 0, tau * 3 / 8, 1, NUM_VERTICES), + StrokeRoundLine( + (12 + cos(tau * 3 / 8) * 5, 16 + sin(tau * 3 / 8) * 5), + ( + 4 + cos(tau * 3 / 8) * FINGER_RADIUS, + 13 + sin(tau * 3 / 8) * FINGER_RADIUS, + ), + 1, + NUM_VERTICES, + ), + StrokeCircleSegment( + (4, 13), FINGER_RADIUS, tau * 3 / 8, tau * 7 / 8, 1, NUM_VERTICES + ), + StrokeRoundLine( + ( + 4 + cos(tau * 7 / 8) * FINGER_RADIUS, + 13 + sin(tau * 7 / 8) * FINGER_RADIUS, + ), + (7, 13.5), + 1, + NUM_VERTICES, + ), + StrokeRoundLine((7, 13.5), (7, 6), 1, NUM_VERTICES), + StrokeCircleSegment((8.25, 6), FINGER_RADIUS, pi, tau, 1, NUM_VERTICES), + StrokeRoundLine((9.5, 6), (9.5, 11), 1, NUM_VERTICES), + StrokeCircleSegment( + (11, 11.5), FINGER_RADIUS, tau * 5 / 8, tau * 7 / 8, 1, NUM_VERTICES + ), + StrokeCircleSegment( + (13.25, 12), FINGER_RADIUS, tau * 5 / 8, tau * 7 / 8, 1, NUM_VERTICES + ), + StrokeCircleSegment( + (15.5, 12.5), FINGER_RADIUS, tau * 5 / 8, tau, 1, NUM_VERTICES + ), + StrokeRoundLine((16.75, 12.5), (17, 16), 1, NUM_VERTICES), + StrokeCircleSegment((8.25, 6), 3, pi, tau, 1, NUM_VERTICES), + StrokeCircleSegment((8.25, 6), 5, pi, tau, 1, NUM_VERTICES), + ] +) +stylus = Shapes( + [ + StrokeCircleSegment((3, 3), 1.5, tau * 3 / 8, tau * 7 / 8, 1, NUM_VERTICES), + StrokeRoundLine( + (3 + cos(tau * 3 / 8) * 1.5, 3 + sin(tau * 3 / 8) * 1.5), + (16 + cos(tau * 3 / 8) * 1.5, 16 + sin(tau * 3 / 8) * 1.5), + 1, + NUM_VERTICES, + ), + StrokeRoundLine( + (3 + cos(tau * 7 / 8) * 1.5, 3 + sin(tau * 7 / 8) * 1.5), + (16 + cos(tau * 7 / 8) * 1.5, 16 + sin(tau * 7 / 8) * 1.5), + 1, + NUM_VERTICES, + ), + StrokeRoundLine( + (16 + cos(tau * 3 / 8) * 1.5, 16 + sin(tau * 3 / 8) * 1.5), + (18, 18), + 1, + NUM_VERTICES, + ), + StrokeRoundLine( + (16 + cos(tau * 7 / 8) * 1.5, 16 + sin(tau * 7 / 8) * 1.5), + (18, 18), + 1, + NUM_VERTICES, + ), + StrokeRoundLine((11, 11), (12, 12), 1, NUM_VERTICES), + ] +) +bluetooth = Shapes( + [ + StrokeSquareLine((10, 2), (15, 6), 1.5), + StrokeSquareLine((15, 6), (5, 14), 1.5), + StrokeSquareLine((10, 2.75), (10, 18.25), 1.5), + StrokeSquareLine((10, 19), (15, 15), 1.5), + StrokeSquareLine((15, 15), (5, 7), 1.5), + ] +) + + +def main(): + 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), + }, + ] + surf = pygame.display.set_mode((2000, 1600)) + running = True + dirty = False + clock = pygame.time.Clock() + while True: + for ev in pygame.event.get(): + if ev.type == pygame.QUIT: + running = False + break + elif ev.type == pygame.WINDOWEXPOSED: + dirty = True + elif ev.type == pygame.KEYDOWN: + if ev.key == pygame.K_ESCAPE: + running = False + break + if not running: + break + elif dirty: + surf.fill("black") + for item in shapes: + item["shape"].draw(surf, item["pos"], (16, 16), "green") + pygame.display.update() + dirty = False + clock.tick(60) + + +if __name__ == "__main__": + main() diff --git a/vectors.py b/vectors.py old mode 100755 new mode 100644 index 9b10c52..e71f22d --- a/vectors.py +++ b/vectors.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - from itertools import chain from math import atan2, ceil, cos, floor, pi, sin, sqrt @@ -65,85 +63,68 @@ class StrokeSquareLine(Polygon): """ Rotate a rectangle along the direction in which we're drawing. """ - def __init__(self, start, end, width): - super().__init__(list(self.make_line_shape(start, end, width / 2))) - - def make_line_shape(self, p1, p2, shape_r): + def __init__(self, p1, p2, width): p_rel = (p1[0] - p2[0], p1[1] - p2[1]) - distance = sqrt(p_rel[0] * p_rel[0] + p_rel[1] * p_rel[1]) - if distance: - back = (p_rel[0] * shape_r / distance, p_rel[1] * shape_r / distance) + 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 + ) back_sum, back_diff = back[0] + back[1], back[0] - back[1] else: - back_sum, back_diff = -shape_r, shape_r + back_sum, back_diff = -width / 2, width / 2 # left is aka: (back[1], -back[0]) # right is aka: (-back[1], back[0]) - yield from ( - (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), + 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), + ] ) -class StrokeRoundLine(StrokeSquareLine): +class StrokeRoundLine(Polygon): """ Enclose a rectangle fitted between two points with a regular, non-rotated n-gon that should be visually indistinguishable with a half circle """ - def __init__(self, start, end, width, num_vertices): - self.num_vertices = num_vertices - super().__init__(start, end, width) - - def make_line_shape(self, p1, p2, shape_r): - p_rel = (p1[0] - p2[0], p1[1] - p2[1]) - distance = sqrt(p_rel[0] * p_rel[0] + p_rel[1] * p_rel[1]) + def __init__(self, p1, p2, width, num_vertices): + radius = width / 2 + delta = (p1[0] - p2[0], p1[1] - p2[1]) + distance = sqrt(delta[0] * delta[0] + delta[1] * delta[1]) + points = [] if distance == 0: - yield from ( - (p1[0] + cos(a) * shape_r, p1[1] + sin(a) * shape_r) - for a in ( - (i) * tau / self.num_vertices - for i in range(self.num_vertices) - ) + initial_corner = opposing_corner = num_vertices + back = (0, 0) + else: + initial_corner = ceil( + atan2(-delta[0], delta[1]) * num_vertices / tau ) - return - angle = atan2(p_rel[1], p_rel[0]) - if angle < 0: - angle += tau - # 0 <= angle < tau - initial_corner = ceil( - atan2(-p_rel[0], p_rel[1]) * self.num_vertices / tau - ) % self.num_vertices - opposing_corner = ( - floor(atan2(p_rel[0], -p_rel[1]) * self.num_vertices / tau) - initial_corner - ) % self.num_vertices - shape = [ - (cos(a) * shape_r, sin(a) * shape_r) - for a in ( - (i + initial_corner) * tau / self.num_vertices - for i in range(self.num_vertices) + opposing_corner = ( + floor(atan2(delta[0], -delta[1]) * num_vertices / tau) - initial_corner + ) % num_vertices + 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]), + ) ) - ] - if distance == 0: - for vertex in shape: - yield (p1[0] + vertex[0], p1[1] + vertex[1]) - return - # left: (cos(angle + tau * 3 / 4) * shape_r, sin(angle + tau * 3 / 4) * shape_r) - # right: (cos(angle + tau / 4) * shape_r, sin(angle + tau / 4) * shape_r) - # back = (cos(angle) * shape_r, sin(angle) * shape_r) - # no need for trig here, though, because we only go ±90°: - back = (p_rel[0] * shape_r / distance, p_rel[1] * shape_r / distance) - # left is aka: (back[1], -back[0]) - # right is aka: (-back[1], back[0]) - yield (p2[0] + back[1], p2[1] - back[0]) - yield (p1[0] + back[1], p1[1] - back[0]) p = p1 - for i, vertex in enumerate(shape): - yield (p[0] + vertex[0], p[1] + vertex[1]) - if i == opposing_corner and p == p1: - yield (p[0] - back[1], p[1] + back[0]) + 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]), + ) + ) p = p2 - yield (p[0] - back[1], p[1] + back[0]) + super().__init__(points) class Shapes(Shape): @@ -219,172 +200,3 @@ class StrokeCircleSegment(StrokePath): yield end_angle break yield start_angle + a - - -laptop_single = Shapes( - [ - Rect((2, 3), (16.4, 1)), - Rect((2, 4), (1, 8.1)), - Rect((17.4, 4), (1, 8.1)), - Rect((2, 12.1), (16.4, 1)), - Polygon([(2, 13.1), (18.4, 13.1), (22.4, 18.1), (6, 18.1)]), - ] -) -laptop_double = Shapes( - [ - Rect((2, 3), (16.4, 1)), - Rect((2, 4), (1, 8.1)), - Rect((17.4, 4), (1, 8.1)), - Rect((2, 12.1), (16.4, 1)), - Polygon([(2, 13.1), (3, 13.1), (6.2, 17.1), (5.2, 17.1)]), - Polygon([(17.4, 13.1), (18.4, 13.1), (21.6, 17.1), (20.4, 17.1)]), - Polygon([(5.2, 17.1), (21.6, 17.1), (22.4, 18.1), (6, 18.1)]), - ] -) -laptop_vertical = Shapes( - [ - Rect((0.5, 1), (17.75, 1)), - Rect((0.5, 2), (1, 14.4)), - Rect((9, 2), (1, 14.4)), - Rect((17.25, 2), (1, 14.4)), - Rect((0.5, 16.4), (17.75, 1)), - Polygon([(2, 17.4), (6, 22.4), (20.75, 22.4), (16.75, 17.4)]), - ] -) -FINGER_RADIUS = 1.25 -NUM_VERTICES = 32 -touchscreen = Shapes( - [ - StrokeCircleSegment((12, 16), 5, 0, tau * 3 / 8, 1, NUM_VERTICES), - StrokeRoundLine( - (12 + cos(tau * 3 / 8) * 5, 16 + sin(tau * 3 / 8) * 5), - ( - 4 + cos(tau * 3 / 8) * FINGER_RADIUS, - 13 + sin(tau * 3 / 8) * FINGER_RADIUS, - ), - 1, - NUM_VERTICES, - ), - StrokeCircleSegment((4, 13), FINGER_RADIUS, tau * 3 / 8, tau * 7 / 8, 1, NUM_VERTICES), - StrokeRoundLine( - ( - 4 + cos(tau * 7 / 8) * FINGER_RADIUS, - 13 + sin(tau * 7 / 8) * FINGER_RADIUS, - ), - (7, 13.5), - 1, - NUM_VERTICES, - ), - StrokeRoundLine((7, 13.5), (7, 6), 1, NUM_VERTICES), - StrokeCircleSegment((8.25, 6), FINGER_RADIUS, pi, tau, 1, NUM_VERTICES), - StrokeRoundLine((9.5, 6), (9.5, 11), 1, NUM_VERTICES), - StrokeCircleSegment( - (11, 11.5), FINGER_RADIUS, tau * 5 / 8, tau * 7 / 8, 1, NUM_VERTICES - ), - StrokeCircleSegment( - (13.25, 12), FINGER_RADIUS, tau * 5 / 8, tau * 7 / 8, 1, NUM_VERTICES - ), - StrokeCircleSegment( - (15.5, 12.5), FINGER_RADIUS, tau * 5 / 8, tau, 1, NUM_VERTICES - ), - StrokeRoundLine((16.75, 12.5), (17, 16), 1, NUM_VERTICES), - StrokeCircleSegment((8.25, 6), 3, pi, tau, 1, NUM_VERTICES), - StrokeCircleSegment((8.25, 6), 5, pi, tau, 1, NUM_VERTICES), - ] -) -stylus = Shapes( - [ - StrokeCircleSegment((3, 3), 1.5, tau * 3 / 8, tau * 7 / 8, 1, NUM_VERTICES), - StrokeRoundLine( - (3 + cos(tau * 3 / 8) * 1.5, 3 + sin(tau * 3 / 8) * 1.5), - (16 + cos(tau * 3 / 8) * 1.5, 16 + sin(tau * 3 / 8) * 1.5), - 1, - NUM_VERTICES, - ), - StrokeRoundLine( - (3 + cos(tau * 7 / 8) * 1.5, 3 + sin(tau * 7 / 8) * 1.5), - (16 + cos(tau * 7 / 8) * 1.5, 16 + sin(tau * 7 / 8) * 1.5), - 1, - NUM_VERTICES, - ), - StrokeRoundLine( - (16 + cos(tau * 3 / 8) * 1.5, 16 + sin(tau * 3 / 8) * 1.5), - (18, 18), - 1, - NUM_VERTICES, - ), - StrokeRoundLine( - (16 + cos(tau * 7 / 8) * 1.5, 16 + sin(tau * 7 / 8) * 1.5), - (18, 18), - 1, - NUM_VERTICES, - ), - StrokeRoundLine((11, 11), (12, 12), 1, NUM_VERTICES), - ] -) -bluetooth = Shapes( - [ - StrokeSquareLine((10, 2), (15, 6), 1.5), - StrokeSquareLine((15, 6), (5, 14), 1.5), - StrokeSquareLine((10, 2.75), (10, 18.25), 1.5), - StrokeSquareLine((10, 19), (15, 15), 1.5), - StrokeSquareLine((15, 15), (5, 7), 1.5), - ] -) - - -def main(): - 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), - }, - ] - surf = pygame.display.set_mode((2000, 1600)) - running = True - dirty = False - clock = pygame.time.Clock() - while True: - for ev in pygame.event.get(): - if ev.type == pygame.QUIT: - running = False - break - elif ev.type == pygame.WINDOWEXPOSED: - dirty = True - elif ev.type == pygame.KEYDOWN: - if ev.key == pygame.K_ESCAPE: - running = False - break - if not running: - break - elif dirty: - surf.fill("black") - for item in shapes: - item["shape"].draw(surf, item["pos"], (16, 16), "green") - pygame.display.update() - dirty = False - clock.tick(60) - - -if __name__ == "__main__": - main() diff --git a/zenbook_conf.py b/zenbook_conf.py index 19aece5..d69fab5 100755 --- a/zenbook_conf.py +++ b/zenbook_conf.py @@ -6,7 +6,7 @@ import pygame from bluetooth import BluetoothConf from ui import Button, Icon, IconButton, Switch, UIParent -from vectors import ( +from vector_assets import ( bluetooth, laptop_double, laptop_single, -- 2.47.1