From 0813eaa3c9e990f375b1b337812eedfc4a567d88 Mon Sep 17 00:00:00 2001
From: Dmitry <b4tm4n@mail.ru>
Date: Sat, 23 Mar 2024 22:58:57 +0300
Subject: [PATCH] add anim2

---
 mod_graph/anim2_control.py | 154 +++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)
 create mode 100644 mod_graph/anim2_control.py

diff --git a/mod_graph/anim2_control.py b/mod_graph/anim2_control.py
new file mode 100644
index 0000000..fcc82e0
--- /dev/null
+++ b/mod_graph/anim2_control.py
@@ -0,0 +1,154 @@
+"""
+    https://stepik.org/lesson/468998/step/7?unit=459819
+
+    3: "Квадрат в самом начале стоит в правом нижнем углу и двигается при нажатии 
+    стрелок только вверх или влево"
+    4: "Квадрат двигается при нажатии стрелок но не может выйти за пределы экрана"
+
+    Библиотека graph: https://kpolyakov.spb.ru/download/pygraph.zip
+"""
+
+import graph
+from graph import *
+
+from typing import NamedTuple
+import types
+
+consts = types.SimpleNamespace()
+consts.VK_Q = 81
+
+
+class Point(NamedTuple):
+    """
+    Вспомогательный класс для упрощения работы с координатами (сложение)
+    """
+
+    x: int | float
+    y: int | float
+
+    def __add__(self, other):
+        if isinstance(other, self.__class__):
+            return self.__class__(self.x + other.x, self.y + other.y)
+        if isinstance(other, (int, float)):
+            return self.__class__(self.x + other, self.y + other)
+        return NotImplemented
+
+    def __sub__(self, other):
+        if isinstance(other, self.__class__):
+            return self.__class__(self.x - other.x, self.y - other.y)
+        if isinstance(other, (int, float)):
+            return self.__class__(self.x - other, self.y - other)
+        return NotImplemented
+
+
+data = {"delta": Point(0, 0), "callbacks": []}
+
+
+def make_title(n, name, start, font=("Arial", 15), n_color="indigo"):
+    pos = Point(10, 20) + start
+    n_width = Point(35, 0)
+    l1 = label(f"«{n}»", *pos, font=font, foreground=n_color)
+    l2 = label(f": {name}", *(pos + n_width), font=font)
+    return l1, l2
+
+
+def square_up_left(start_pos=None):
+    start_pos = start_pos or Point(0, 0)
+
+    make_title(3, "Квадрат двигается вверх и влево", start_pos)
+    title_height = Point(0, 60)
+
+    start_pos = start_pos + title_height
+    scene_sz = Point(300, 300)
+    box_sz = Point(40, 40)
+    box_offset = Point(260, 260)
+
+    brushColor("Blue")
+    rectangle(*start_pos, *(start_pos + scene_sz))
+    brushColor("Yellow")
+
+    box_pos = start_pos + box_offset
+    obj = rectangle(*box_pos, *(box_pos + box_sz))
+
+    def update():
+        delta = data["delta"]
+        coord = Point(xCoord(obj), yCoord(obj)) - start_pos
+        if (
+            coord.x + delta.x < 0
+            or coord.x + box_sz.x + delta.x > scene_sz.x
+            or coord.y + delta.y < 0
+            or coord.y + box_sz.y + delta.y > scene_sz.y
+            or delta.x > 0
+            or delta.y > 0
+        ):
+            return
+        moveObjectBy(obj, delta.x, delta.y)
+
+    data.setdefault("callbacks", []).append(update)
+
+
+def square_free_move(start_pos=None):
+    start_pos = start_pos or Point(0, 0)
+
+    make_title(4, "Квадрат двигается свободно", start_pos)
+    title_height = Point(0, 60)
+
+    start_pos = start_pos + title_height
+    scene_sz = Point(300, 300)
+    box_sz = Point(40, 40)
+    box_offset = Point(130, 130)
+
+    brushColor("Blue")
+    rectangle(*start_pos, *(start_pos + scene_sz))
+    brushColor("Yellow")
+
+    box_pos = start_pos + box_offset
+    obj = rectangle(*box_pos, *(box_pos + box_sz))
+
+    def update():
+        delta = data["delta"]
+        coord = Point(xCoord(obj), yCoord(obj)) - start_pos
+        if (
+            coord.x + delta.x < 0
+            or coord.x + box_sz.x + delta.x > scene_sz.x
+            or coord.y + delta.y < 0
+            or coord.y + box_sz.y + delta.y > scene_sz.y
+        ):
+            return
+        moveObjectBy(obj, delta.x, delta.y)
+
+    data.setdefault("callbacks", []).append(update)
+
+
+def keyPressed(event):
+    match event.keycode:
+        case graph.VK_LEFT:
+            data["delta"] = Point(-5, 0)
+        case graph.VK_RIGHT:
+            data["delta"] = Point(5, 0)
+        case graph.VK_UP:
+            data["delta"] = Point(0, -5)
+        case graph.VK_DOWN:
+            data["delta"] = Point(0, 5)
+        case consts.VK_Q | graph.VK_ESCAPE:
+            killTimer(data["update_timer"])
+            close()
+
+
+def update():
+    for callback in data["callbacks"]:
+        callback()
+    data["delta"] = Point(0, 0)
+
+
+def main():
+    canvasSize(460, 800)
+    square_up_left()
+    square_free_move(Point(0, 350))
+
+    data["update_timer"] = onTimer(update, 40)
+    onKey(keyPressed)
+    run()
+
+
+main()