This paste expires on 2023-06-20 11:09:03.324364. Repaste, or download this paste. . Pasted through web.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.10.7/brython.min.js">
</script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.10.7/brython_stdlib.js">
</script>
<style>
    .texty {
        font-family:monospace;
        font-size:1.5em;
        fill:white;
        stroke:red;
        stroke-width:0.01em;
        text-shadow:
            #0008 0px 0px 2px,
            #0008 0px 0px 2px,
            #0008 0px 0px 2px,
            #0008 0px 0px 2px,
            #0008 0px 0px 2px;
    }
    body {margin:0; padding: 0;}
</style>
</head>
<body onload="brython(1)">
<!-- <pre>1: rotate 2: scale 3: move</pre> -->
<svg width="1200px" height="1200px" xmlns="http://www.w3.org/2000/svg" id="svg_root" style="background-color:#222">
    <g id="the_texts">THETHINGS</g>
    <rect id="BackDrop" fill="none" pointer-events="all"></rect>
    <!-- <text id="diagnose" x ="20" y="20"
        class="texty">THE SOMETHING</text>
        <text id="diagnose1" x ="20" y="40" class="texty">THE SOMETHING</text>
        <text id="diagnose2" x ="20" y="60" class="texty">THE SOMETHING</text> -->
    <g id="intersections"></g>
    <circle id="center_point" r=5 fill="cyan"></circle>
    <circle id="tracking_point" r=5 fill="magenta"></circle>
    <circle id="starting_point" r=5 fill="yellow"></circle>
    <line id="line1" stroke="red"></line>
    <line id="line2" stroke="green"></line>
    <!-- <text id="the_texts">THETHINGS</text> -->
</svg>
<script type="text/python">
"""Much of the drag and drop code is copied from a demo by Doug Schepers
at http://svg-whiz.com/svg/DragAndDrop.svg"""
from browser import document, svg, alert, bind
from math import sin, cos, radians, asin, acos, pi, sqrt
from itertools import combinations as combs
from itertools import groupby
import math, random
class Vec2(object):
    def __init__(self, x, y):
        self._x = float(x)
        self._y = float(y)
    @property
    def x(self):
        return self._x
    @x.setter
    def x(self, new_x):
        self._x = float(new_x)
    @property
    def y(self):
        return self._y
    @y.setter
    def y(self, new_y):
        self._y = float(new_y)
    def __add__(self, other):
        types = (int, float)
        if isinstance(self, types):
            return Vec2(self + other.x, self + other.y)
        elif isinstance(other, types):
            return Vec2(self.x + other, self.y + other)
        else:
            return Vec2(self.x + other.x, self.y + other.y)
    def __div__(self, other):
        types = (int, float)
        if isinstance(self, types):
            self = Vec2(self, self)
        elif isinstance(other, types):
            other = Vec2(other, other)
        x = self.x / other.x
        y = self.y / other.y
        return Vec2(x, y)
    def __mul__(self, other):
        types = (int, float)
        if isinstance(self, types):
            return Vec2(self * other.x, self * other.y)
        elif isinstance(other, types):
            return Vec2(self.x * other, self.y * other)
        else:
            return Vec2(self.x * other.x, self.y * other.y)
    def __neg__(self):
        return Vec2(-self.x, -self.y)
    def __radd__(self, other):
        return Vec2(self.x + other, self.y + other)
    def __rdiv__(self, other):
        return Vec2(other/self.x, other/self.y)
    def __rmul__(self, other):
        return Vec2(other * self.x, other * self.y)
    def __rsub__(self, other):
        return Vec2(other - self.x, other - self.y)
    def __repr__(self):
        return self.__str__()
    def __str__(self):
        return "Vec2: ({0}, {1})".format(self.x, self.y)
    def __sub__(self, other):
        types = (int, float)
        if isinstance(self, types):
            return Vec2(self - other.x, self - other.y)
        elif isinstance(other, types):
            return Vec2(self.x - other, self.y - other)
        else:
            return Vec2(self.x - other.x, self.y - other.y)
    def ceil(self):
        return Vec2(math.ceil(self.x), math.ceil(self.y))
    def floor(self):
        return Vec2(math.floor(self.x), math.floor(self.y))
    def get_data(self):
        return (self.x, self.y)
    def inverse(self):
        return Vec2(1.0/self.x, 1.0/self.y)
    def length(self):
        return math.sqrt(self.square_length())
    def normalize(self):
        length = self.length()
        if length == 0.0:
            return Vec2(0, 0)
        return Vec2(self.x/length, self.y/length)
    def round(self):
        return Vec2(round(self.x), round(self.y))
    def square_length(self):
        return (self.x * self.x) + (self.y * self.y)
    """
    def transform(self, matrix):#mat2, mat2d, mat3, mat4
        pass
    @classmethod
    def cross(cls, a, b):
        z = (a.x * b.y) - (a.y * b.x)
        return Vec3(0, 0, z)
    """
    @classmethod
    def distance(cls, a, b):
        c = b - a
        return c.length()
    @classmethod
    def dot(self, a, b):
        return (a.x * b.x) + (a.y * b.y)
    @classmethod
    def equals(cls, a, b, tolerance=0.0):
        diff = a - b
        dx = math.fabs(diff.x)
        dy = math.fabs(diff.y)
        if dx <= tolerance * max(1, math.fabs(a.x), math.fabs(b.x)) and \
           dy <= tolerance * max(1, math.fabs(a.y), math.fabs(b.y)):
            return True
        return False
    @classmethod
    def max(cls, a, b):
        x = max(a.x, b.x)
        y = max(a.y, b.y)
        return Vec2(x, y)
    @classmethod
    def min(cls, a, b):
        x = min(a.x, b.x)
        y = min(a.y, b.y)
        return Vec2(x, y)
    @classmethod
    def mix(cls, a, b, t):
        return a * t + b * (1-t)
    @classmethod
    def random(cls):
        x = random.random()
        y = random.random()
        return Vec2(x, y)
    @classmethod
    def square_distance(cls, a, b):
        c = b - a
        return c.square_length()
vec2 = Vec2
def interp(start, end, coef):
    return (end - start)*coef + start
SVGRoot = document["svg_root"]
the_texts = document["the_texts"]
# svg_intersections = SVGRoot['intersection_group']
intersection_group = svg.g()
SVGRoot <= intersection_group
svg_lines = []
svg_points = []
svg_texts = {}
svg_points_by_id = {}
edge_bimap = {}
selected_elem = None
selected_node_id = None
offset = None
selected_points = []
nodes_to_reset = []
edges_by_pt_global = {}
TrueCoords = SVGRoot.createSVGPoint()
GrabPoint = SVGRoot.createSVGPoint()
BackDrop = document["BackDrop"]
last_pos = (20,20)
pos_shift = (0, 20)
# diagnose
line1 = document["line1"]
line2 = document["line2"]
def add_text(key):
    global the_texts
    global svg_texts
    if key in svg_texts:
        return svg_texts[key]
    global last_pos
    global SVGRoot
    (x,y) = last_pos
    svg_texts[key] = svg.text(x=x, y=y)
    svg_texts[key].attrs['class'] = 'texty'
    svg_texts[key].text = '---'
    y+=pos_shift[1]
    last_pos = (x,y)
    # SVGRoot <= svg_texts[key]
    the_texts <= svg_texts[key]
    return svg_texts[key]
# https://www.petercollingridge.co.uk/tutorials/svg/interactive/dragging/
default_color = '#08f'
itsc_color = '#f008'
color_selected = '#f80'
color_sibling = '#0f0'
cell_radius = 10
itsc_radius = 3
check_itsc = False
def circlepoint(angle, radius = 1):
    angle = 2*pi*angle
    # return radius*vec2(cos(angle), sin(angle))
    return (radius*cos(angle), radius*sin(angle))
def signed_angle_glm(A, B):
    Ax, Ay = A
    Bx, By = B
    cross =  Ax * By - Ay * Bx
    dot = Ax*Bx + Ay*By
    lenA = vectlen1(A)
    lenB = vectlen1(B)
    angle = math.acos(dot/(lenA*lenB))
    return angle if cross > 0 else -angle
def vecdiff(A,B):
    return B[0] - A[0], B[1] - A[1]
    0
def vectlen(A, Babibabo):
    Px = Babibabo[0] - A[0]
    Py = Babibabo[1] - A[1]
    return math.sqrt(Px*Px + Py*Py)
def vectlen1(v):
    return math.sqrt(v[0]*v[0] + v[1]*v[1])
# WARNING WHEN PASTING SAVE FILE: NO LINE RETURN IS MULTI LINE STRING!
# WARNING WHEN PASTING SAVE FILE: NO LINE RETURN IS MULTI LINE STRING!
def extract_sav():
    sav_data = '''SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
        VERSION :1:1
        GAME    :8:Untangle
        PARAMS  :3:200
        CPARAMS :3:200
        SEED    :15:189466783882172
        DESC    :2642:0-8,0-54,0-123,1-118,1-120,1-141,1-183,2-25,2-74,2-85,2-103,3-4,3-58,3-111,3-125,4-20,4-42,4-125,5-23,5-129,5-178,5-195,6-30,6-94,6-187,6-189,7-45,7-117,7-121,7-127,8-21,8-94,8-189,9-65,9-113,9-156,9-176,10-41,10-54,10-114,10-163,11-56,11-143,11-161,11-171,12-39,12-128,12-143,12-150,13-62,13-63,13-170,13-194,14-19,14-33,14-49,14-148,15-36,15-67,15-91,15-111,16-20,16-53,16-91,16-120,17-31,17-112,18-28,18-50,18-131,19-95,19-153,20-42,20-53,21-102,21-123,21-189,22-108,22-148,22-182,22-186,23-125,23-166,23-178,24-83,24-167,24-180,25-29,25-72,25-85,26-27,26-145,26-167,26-172,27-98,27-99,27-177,28-45,28-50,28-117,29-68,29-105,29-154,30-93,30-94,30-187,31-71,31-82,31-112,32-116,32-142,32-152,33-49,33-108,34-61,34-110,34-166,35-65,35-90,35-97,35-162,36-91,36-100,36-141,37-46,37-73,37-84,38-48,38-135,38-147,38-149,39-150,39-185,39-199,40-50,40-75,40-131,40-195,41-114,41-127,41-157,42-53,42-168,43-80,43-104,43-109,43-190,44-87,44-119,44-139,44-140,45-50,45-117,46-66,46-84,46-133,47-51,47-57,47-64,47-159,48-72,48-105,48-193,49-77,49-95,51-52,51-57,51-89,52-57,52-89,52-146,53-181,54-114,55-78,55-122,55-140,55-144,56-59,56-86,56-171,57-192,58-66,58-67,58-111,59-86,59-171,60-109,60-158,60-182,60-199,61-81,61-179,62-113,62-174,62-175,63-79,63-132,63-194,64-88,64-159,64-168,65-97,65-173,66-102,66-134,67-100,67-107,68-154,68-193,68-197,69-92,69-128,69-199,70-80,70-104,70-147,71-82,71-135,71-191,72-105,72-191,73-84,73-101,73-133,74-101,74-103,74-154,75-129,75-131,75-195,76-109,76-149,76-182,76-197,77-96,77-108,77-197,78-140,78-161,78-184,79-128,79-132,79-184,80-104,80-190,81-168,81-179,81-192,82-112,82-135,83-106,83-146,83-198,84-133,85-137,85-191,86-152,87-90,87-140,87-188,88-115,88-159,88-181,89-106,89-164,90-119,90-162,91-111,92-151,92-186,92-194,93-101,93-137,93-187,94-189,95-107,96-133,96-154,96-197,97-176,98-165,98-172,98-177,99-124,99-145,99-173,100-107,100-153,101-103,102-134,102-187,103-137,104-147,105-193,106-164,106-198,107-153,108-182,109-149,110-166,110-178,110-179,112-147,113-138,113-176,114-157,115-118,115-181,115-183,116-126,116-142,116-190,117-121,118-120,118-183,119-139,119-162,120-181,121-127,122-144,122-188,123-130,123-163,124-169,124-173,124-177,125-166,126-142,126-158,126-185,127-157,128-143,129-131,129-195,130-136,130-160,130-163,132-139,132-184,134-136,135-191,136-160,138-155,138-174,138-196,141-174,141-196,142-152,143-161,144-171,145-173,145-180,146-198,148-151,148-186,149-193,150-152,150-185,151-170,151-175,153-175,155-156,155-165,155-196,156-165,156-169,158-185,158-190,159-164,160-163,161-184,162-176,164-167,165-169,167-180,168-192,169-177,170-175,170-194,172-183,174-196,179-192,186-199
        AUXINFO :4830:e368f1353613f074d0a9c2b6598b16b48ba02654432eebe828afa1e4a75b7b695c41c208565c7e1f1e935e7c52925373dd438d24df2bca01cf30dc17c8018f3e0aa9b01b0c3e93c961a19b61e1a19aba235ce545a4caa68aaee5255769b5c7a57e7b8139ca062ca37b166e6bf03cb3845b09d90f771924ee2f0f91f9f12131501cbcd12663798eaaccf044d2bacbbd7ea53f215eb5ea13ec3d47cf13f26b06497a44357055180f34c433052dc3ef2822b69e7881dd7761370cf237841c5080958ce81dbc0d1cd19910c774b7a5fab3d8e5a21c97d708a4b1bd0c03a53d7d6ae15d674d35450a3efa64847fac1d1b21efe7833c4173835bfb4a425c94fd8cdcf689bf785c09cb6c0fc903e14c2b749fd9244dde8f72eac6ba921ce8c1ec1c0f085f6dd2013c944d55a24c4ece17b264e6e76a73b2c72bae84f259cf02f0961c781c3551e1d08e4ce45615bea6a7f0a53128ef06b4f03fa1774b425975f9136c8c4b999c2718ac8b700203809cfe8bb6a14aab6ed3f9a0209d7e19cce081e04e473df62aaa199f642a4ad293cb75f6d0f0ca50359c5f6d9ff3639d1952eaf6bbd887fc9fc3dc232f5fb5ae2180100f40903ba36c0e7b6e7ddf423e1d5306b259c463a374aeaddaaefa02f3b8f53b6fd414590bcebc75b9df0a7df06dbd730f0332dbb985315528ffa68805b7202a02f20efba6e86904309870610cff9faa3f7329afbe1e1bc8eeb0a4633e7ba2a3ca593e94486f5867d729a8476371a533e7266da2172ba93b3c795d82cdf566d18287dbf94352ccd6fb82078128444779a2ad748e4401388541d608c5d7b49ffa3278dcb990743e79f196a256944f96af7fe20d3756106e4ab9b66c84250628d78af30a0370efca34cc1902e6d11b9ed563c3c79ab391f8cb159f7e1cba184500868747e1d41b7a17f2918f8deed200619a9c958a8e26e5d419bc7dd5148cb5aa0def7203ac25e71b7947a2832fb85498c88acc2f716364940aecfe10e01cc85c6abbb6d681d99dd6d168e166eeadb59892b4c90cabc6adf436a04f41940a49d48997fca25c0cff8ba223b29f295ee51e7419e14d1e125fdfa1054d4dcb49abcdc2cb7bd3d5135c869db4462143b1c43b55f33b03a559d5a5e851ccacfbf88c08d26eedb7e8372e5f8d3b4f6ab8ac6dc663e7deb8d8d0aae9311e02e6791a1b81a470cf785bc4e4cae0b0718be44c54c62edebb7e6c61bd863d130848aab594bc2bfbd99472b22a14b3c6e373113b7581e118c5a0bfa6bd33813b4d919dd723a15cbfe7b97958f27e1461e1223c97a5818a4e3b993dffe5c18bc978569b53b6da1a1c35cbad0c41a0c51d4686148a3c3121f84a37bd7e1f6bd8d167890e6d5ec87e2a01146232f596666954057874f3f4f902a2f6068753b30d68b9a47a3ef293175675b5e34b9e3b99b7502b0ee801c41581a799aeb464d5aaee29bce4baff5c22378d22414fa7bf730089f7accd4b27d8f03d6a3012a6ee7c9a4b3d252776c65fdb1668966bdef00d2c1fdef822d1be512a2c9c3b987af8e9ed2741a5f2e36507c01f881245b36b6cf72fcac99b01b99d463b7f3f5944183eb46352ae09ae0c9db01b26c95abc6265e128f9654a044ae4afc854aac24056c250ef13457848b8d1bf23379e9d655b16485b1998e682ff9baf9d1aadb73c4a2838d9da350fa86edfccc35cd35d576150157f9e14cf0e4e1f7d579586a4bad4d5ebf14dcfaa8573b7e3f58571c7bc41b119123abecbc0cbf7629c4822c58f7f04dc4803143fe3f628a79a3e4e4b985c07eac1e7507c89dbc7feddbe4be6fbe420e74a5138482e9f767f32e3d81065c294fbbf0a3443e353b61a1d64009c4952e2a8ee252dc04b5460762598939a0801801fb12961aa2e29362d9e8091cb2d3d40a50020482a94389f3ea5e76de8685688b69a02dcb338d632bebe3d53800bb22d8fc3ad86db0037b7e60ed4b31a9776907d6eeb16494e2e96fc9f2d37d6bac613ed4db1c969bb902f0b54efdc863a864f5551a6af09c59763a1a01d811369a7f2fcdc96ba0a436c7a89604b4c5a78a09e2dd064b25edddeaf21387d6dbc576139b169a7cf573f1bb7e28d3c2dcd4fc7999b24fc11fde393c9ffcd9ced972cd19385d0debf0b0706a40088c2efac2b78c6023dd166fe00b6dc7f28aba1544faf58a7520d3822ca1b0a69d9b59ba068aa9ceeb4405f3887f1010e2d8a139695e1f77854e3a9e2f4b504880eb5ba5940a39b04d2bcd8181fb3f4164d7837475bc9affbd6fb71603a1a8d7e932dc191921f69b112c30683f74db1c96bceb7a1c47e83385f1a5818a0bde6bc6fd56a1196d83854853952d7c4eee7bc07327b990eeb4aff002acf1b251446e79b9ac92faccdcedcee334526a04fd8342965ebe2e360a58a1ca8154cb477eeda6be683e6057f3e03133eb3bededf45b55d835e590e990f8e15e6dff718ebdb7729ad4b2eafabc333ffe4227a477dccf61eda40ec87b4d8a2b82c8482d97cfb5b37204af961a378583c36d033f36dff42370324aa2bb0329a6707ae266556ce1bdeb44d36330ea7f16427448cb679313022b0b3492ff0b2b0272f74e31eba128bdb9c36a17c0cbede30d6993920413c4f8247f5e8cf6046c75c4f03fd1d340b249d1ed11a6f673a9f7596bb9669738f00a7e97acf615355c60fd42331bb805352a0be44f00704a9c875608300693e675e0ede61c48bea34592c60fffaa67ad3791b67fb1f00e11ecf7610a7e8645c5926f483042b1c08cb0490e30d6cddf37ac1f492b063f4532f3a27c9f2c195eea84ad98378dc7c48a9d262c2bdadc19a2630348d8bab4e4623ac0f2de179783a78cec04a0897bc53b07343bed2e138b27a94d18bab328b9e784b859d6a8e616dd27253e83edaa2a2b42e108fb7e5680e250a9583673ab917bf67a13f83584914354aafc97796c9db57a40da23a41642902ca4034c94a276de305a2962e8bb05dc26d6671816deaebff126711dc9cfc73717708be66cbf2d6f495157ecfb1a4c97c64c09038296515307241309c8c5f663fdd75b693adfbf633a203eec32738c8e6a7a66b080446fb69d5808ea4723d32e53b5a5b2fab52256468f55c569b4d1a7e9dc7f1ac2d939520711387875819360f76a37b4056504147d2ba6f4e664b661fe92b488c6efbba2f1911d07dfb49044083e881a5dbe7a71b44c52b21429ee0b49149307874c75efbc222b8c1acdbd855cb470bdf785a2733492865438a00c78ca94f164e748a04f95606b9f410f4aa6b0ef9631dfa0a403dd966fdb9d0664bacd812a83c6bf77cd886c25d1f0b494512a1730ab5cad6620e91b311cdf251f1fd654ab302ac8eb84f831f6f129e36095574203f2019623c133cbeccb31219d916ad11d9a3b4a5447fb93c7b222ece953b6f
        NSTATES :1:2
        STATEPOS:1:2
        MOVE    :14:P12:999,175/64'''
    lines = sav_data.split('\n')
    graph = lines[6].split(':')[-1]
    # what = lines[7]
    moves = lines[10:]
    count = int(lines[3].split(':')[-1])
    # print(graph[:50])
    # print(what[:50])
    # print(moves[:50])
    # print(count)
    # movements = [a.split(':') for a in moves]
    # movements = [(move, a1, p, coord) for (move, a1, p, coord) in movements]
    # movements = [(p, coord.split('/')[0].split(',')) for (move, a1, p, coord) in movements]
    # movements = [(int(p[1:]), int(coord[0]), int(coord[1])) for (p, coord) in movements]
    movements = [a.split(':') for a in moves]
    movements = [(move, a1, p, coord_slash) for (move, a1, p, coord_slash) in movements]
    movements = [(move, a1, p, coord_slash.split('/')) for (move, a1, p, coord_slash) in movements]
    movements = [(move, a1, p, coord_slashval[0].split(','), coord_slashval[1]) for (move, a1, p, coord_slashval) in movements]
    movements = [
        (
            int(p[1:]),
            # int(int(coord[0])/int(slashval)),
            # int(int(coord[1])/int(slashval))
            int(coord[0])/int(slashval),
            int(coord[1])/int(slashval)
        )
        for (move, a1, p, coord, slashval) in movements]
    position_by_id = {}
    for pid, x, y in movements:
        position_by_id[pid] = (x,y)
    #
    graph1 = [a.split('-') for a in graph.split(',')]
    graph1 = [(int(a), int(b)) for a,b in graph1]
    # print(graph1)
    # import itertools
    # edge_set = {
    #     grk:[i for i in grl]
    #     for grk, grl in itertools.groupby(sorted(graph1), lambda a: a[0])
    #     }
    # duping the edges
    edge_set = {(a,b) for a,b in graph1}|{(b, a) for a,b in graph1}
    edge_set_li = list(edge_set)
    bimap = {
        grk:[i[1] for i in grl]
            for grk, grl in
            # itertools.groupby(sorted(edge_set_li), lambda a: a[0])
            groupby(sorted(edge_set_li), lambda a: a[0])
        }
    return graph1, bimap, position_by_id, count
def game500():
    raw = ''
    # MOVE    :14:P65:579,307/22
    movements = [a.split(':') for a in movements.split('\n')]
    movements = [(move, a1, p, coord) for (move, a1, p, coord) in movements]
    movements = [(p, coord.split('/')[0].split(',')) for (move, a1, p, coord) in movements]
    movements = [(int(p[1:]), int(coord[0]), int(coord[1])) for (p, coord) in movements]
    position_by_id = {}
    for pid, x, y in movements:
        position_by_id[pid] = (x,y)
    #
    graph1 = [a.split('-') for a in raw.split(',')]
    graph1 = [(int(a), int(b)) for a,b in graph1]
    # print(graph1)
    # import itertools
    # edge_set = {
    #     grk:[i for i in grl]
    #     for grk, grl in itertools.groupby(sorted(graph1), lambda a: a[0])
    #     }
    # duping the edges
    edge_set = {(a,b) for a,b in graph1}|{(a, b) for a,b in graph1}
    edge_set_li = list(edge_set)
    bimap = {
        grk:[i[1] for i in grl]
            for grk, grl in
            # itertools.groupby(sorted(edge_set_li), lambda a: a[0])
            groupby(sorted(edge_set_li), lambda a: a[0])
        }
    return graph1, bimap, position_by_id
def rotate_point(point, angle, center_point=(0, 0)):
    """Rotates a point around center_point(origin by default)
    Angle is in degrees.
    Rotation is counter-clockwise
    """
    angle_rad = radians(angle % 360)
    # Shift the point so that center_point becomes the origin
    new_point = (point[0] - center_point[0], point[1] - center_point[1])
    new_point = (new_point[0] * cos(angle_rad) - new_point[1] * sin(angle_rad),
                 new_point[0] * sin(angle_rad) + new_point[1] * cos(angle_rad))
    # Reverse the shifting we have done
    new_point = (new_point[0] + center_point[0], new_point[1] + center_point[1])
    return new_point
def rotate(point,center_point,theta): #rotate x,y around xo,yo by theta (rad)
    x,y = point
    xo, yo = center_point
    xr=math.cos(theta)*(x-xo)-math.sin(theta)*(y-yo) + xo
    yr=math.sin(theta)*(x-xo)+math.cos(theta)*(y-yo) + yo
    return (xr,yr)
def rotate_items(angle, selected_points, center_point):
    # unused
    for pt in selected_points:
        (x, y) = (
            float(pt.getAttributeNS(None, 'cx')),
            float(pt.getAttributeNS(None, 'cy')))
        # (new_x, new_y) = rotate_point((x,y), angle, center_point)
        (new_x, new_y) = rotate((x,y), center_point, angle)
        pt.setAttributeNS(None, 'cx', new_x)
        pt.setAttributeNS(None, 'cy', new_y)
def rotate_items_mutate(angle, selected_points, previous_pos, center_point):
    point_dict = {}
    for pt in selected_points:
        ident = pt.attrs['id']
        prev_pos = previous_pos[ident]
        (new_x, new_y) = rotate(prev_pos, center_point, angle)
        pt.setAttributeNS(None, 'cx', new_x)
        pt.setAttributeNS(None, 'cy', new_y)
        set_line_coord(pt, new_x, new_y)
def scale_items(scale, selected_points, previous_pos, center_point):
    for pt in selected_points:
        ident = pt.attrs['id']
        prev_pos = previous_pos[ident]
        (xdiff, ydiff) = vecdiff(prev_pos, center_point)
        # add_text("vecdiff").text = (xdiff, ydiff)
        (new_x, new_y) = (
            center_point[0]-xdiff * scale,
            center_point[1]-ydiff * scale
            )
        pt.setAttributeNS(None, 'cx', new_x)
        pt.setAttributeNS(None, 'cy', new_y)
        set_line_coord(pt, new_x, new_y)
        # (x, y) = (
        #     float(pt.getAttributeNS(None, 'cx')),
        #     float(pt.getAttributeNS(None, 'cy')))
def GetTrueCoords(evt):
    # find the current zoom level and pan setting, and adjust the reported
    # mouse position accordingly
    newScale = SVGRoot.currentScale
    translation = SVGRoot.currentTranslate
    x = (evt.clientX - translation.x) / newScale
    y = (evt.clientY - translation.y) / newScale
    return x,y
def GetRelative(evt):
    # find the current zoom level and pan setting, and adjust the reported
    # mouse position accordingly
    newScale = SVGRoot.currentScale
    translation = SVGRoot.currentTranslate
    x = (evt.movementX) / newScale
    y = (evt.movementY) / newScale
    return x,y
# selection and movement/scaling
class mouse_selector:
    # verts = sf.VertexArray(sf.PrimitiveType.LINES)
    rect = svg.rect(fill="#0000", stroke="white",stroke_width="2", id='selectador')
    dragging = False
    startx, starty = 0,0
    callback = None
    def __init__(self, svgroot):
        svgroot <= self.rect
    def rectminmax(self, x,y):
        diffx = x-self.startx
        diffy = y-self.starty
        if diffx <=0:
            width = -diffx
            rectx = x
        else:
            width = diffx
            rectx = self.startx
        if diffy <=0:
            height = -diffy
            recty = y
        else:
            height = diffy
            recty = self.starty
        # print(rectx, recty, width, height)
        return (rectx, recty, width, height)
    def mouse_moved(self, pos):
        if self.dragging:
            x,y = pos
            (rectx, recty, width, height) = self.rectminmax(x,y)
            # self.rect.setAttributeNS(None, 'width', x-self.startx)
            # self.rect.setAttributeNS(None, 'height', y-self.starty)
            self.rect.setAttributeNS(None, 'x', rectx)
            self.rect.setAttributeNS(None, 'y', recty)
            self.rect.setAttributeNS(None, 'width', width)
            self.rect.setAttributeNS(None, 'height', height)
    def mouse_push(self, pos):
        self.dragging = True
        # print('mouse_push', pos)
        x,y = pos
        self.rect.setAttributeNS(None, 'x', x)
        self.rect.setAttributeNS(None, 'y', y)
        self.rect.setAttributeNS(None, 'width', 0)
        self.rect.setAttributeNS(None, 'height', 0)
        self.rect.setAttributeNS(None, 'visibility', 'visible')
        self.startx = x
        self.starty = y
    def mouse_release(self, pos):
        if self.callback != None:
            x,y = pos
            (rectx, recty, width, height) = self.rectminmax(x,y)
            # self.callback((self.startx, self.starty),pos)
            self.callback((rectx, recty),(rectx+width, recty+height))
        self.dragging = False
        self.rect.setAttributeNS(None, 'visibility', 'hidden')
class transformer:
    '''we use prev_point because there are no transforms available'''
    enabled = False
    started = False
    starting_point = None
    center_point = None
    tracking_point = None
    previous_pos = {}
    axe1 = None
    which = 0
    # def __init__():
    #     pass
    def start(self, which, points):
        # called by keyboard
        # print('points', len(points))
        # we always pick the average point
        avg = ( (sum([float(pt.getAttributeNS(None, 'cx')) for pt in points]),
                 sum([float(pt.getAttributeNS(None, 'cy')) for pt in points])))
        avg_x, avg_y = avg
        self.center_point = (avg_x/len(points), avg_y/len(points))
        self.which = which
        self.enabled = True
        add_text("which").text = self.which
        self.previous_pos = {}
        for pt in points:
            pos = (float(pt.getAttributeNS(None, 'cx')),
                float(pt.getAttributeNS(None, 'cy')))
            ident = pt.attrs['id']
            self.previous_pos[ident] = pos
        center_point_svg = document['center_point']
        center_point_svg.setAttributeNS(None, 'cx', self.center_point[0])
        center_point_svg.setAttributeNS(None, 'cy', self.center_point[1])
        print('enabled transformer, which =', self.which)
    def stop(self):
        self.which = 0
        add_text("which").text = self.which
        self.started = False
        self.enabled = False
        self.tracking_point = False
        self.starting_point = False
        print('disabled transformer, which =', self.which)
    # def transform(self, new_point):
    def transform(self, translation, where):
        global selected_points
        global diagnose
        # diagnose
        if self.started == False:
            self.starting_point = where
            self.tracking_point = where
            self.started = True
            # starting_point_svg = document['starting_point']
            # starting_point_svg.setAttributeNS(None, 'cx', self.starting_point[0])
            # starting_point_svg.setAttributeNS(None, 'cy', self.starting_point[1])
        tpx, tpy = self.tracking_point
        tpx += translation[0]
        tpy += translation[1]
        self.tracking_point = (tpx, tpy)
        tracking_point_svg = document['tracking_point']
        tracking_point_svg.setAttributeNS(None, 'cx', self.tracking_point[0])
        tracking_point_svg.setAttributeNS(None, 'cy', self.tracking_point[1])
        if self.tracking_point == self.starting_point:
            print('self.tracking_point == self.starting_point:')
            return
        if False:
            # for diagnose
            line1.setAttributeNS(None, 'x1', self.center_point[0])
            line1.setAttributeNS(None, 'y1', self.center_point[1])
            line1.setAttributeNS(None, 'x2', self.starting_point[0])
            line1.setAttributeNS(None, 'y2', self.starting_point[1])
            line2.setAttributeNS(None, 'x1', self.center_point[0])
            line2.setAttributeNS(None, 'y1', self.center_point[1])
            line2.setAttributeNS(None, 'x2', self.tracking_point[0])
            line2.setAttributeNS(None, 'y2', self.tracking_point[1])
        self.axe1 = vecdiff(self.starting_point, self.center_point)
        axe2 = vecdiff(self.tracking_point, self.center_point)
        dist_ratio = vectlen1(axe2)/vectlen1(self.axe1)
        if self.which == 1: # rotatate
            angle_rad = signed_angle_glm(self.axe1, axe2)
            # angle_rad =(math.pi*angle_deg/180.)
            angle_deg =(180.*angle_rad/math.pi)
            rotate_items_mutate(
                angle_rad, selected_points, self.previous_pos, self.center_point)
            diagnose.text = f'angle_rad {angle_rad:.2f} angle_deg {angle_deg:.2f}'
        elif self.which == 2: # scale
            starting_len = vectlen(self.center_point, self.starting_point)
            tracking_len = vectlen(self.center_point, self.tracking_point)
            scale_items(tracking_len / starting_len, selected_points, self.previous_pos, self.center_point)
        elif self.which == 3: # move
            (diff_x, diff_y) = vecdiff(self.starting_point,self.tracking_point)
            for pt in selected_points:
                ident = pt.attrs['id']
                (x,y) = self.previous_pos[ident]
                (new_x, new_y) = (x+diff_x, y+diff_y)
                pt.setAttributeNS(None, 'cx', new_x)
                pt.setAttributeNS(None, 'cy', new_y)
                set_line_coord(pt, new_x, new_y)
        self.prev_point = self.tracking_point
mselector = None
transform = None
# events
def start_drag(evt):
    global selected_elem
    global offset
    global svg_texts
    global mselector
    if (evt.target == BackDrop
        or evt.target.tagName != 'circle'
        or 'id' not in evt.target.attrs):
        mselector.mouse_push(GetTrueCoords(evt))
        evt.preventDefault()
    else:
        selected_elem = evt.target
        node_id = selected_elem.attrs['id']
        highlight_start(int(node_id))
        m_x, m_y = GetTrueCoords(evt)
        cx = float(selected_elem.getAttributeNS(None, 'cx'))
        cy = float(selected_elem.getAttributeNS(None, 'cy'))
        offset = (cx-m_x, cy-m_y)
        evt.preventDefault()
    # add_text("selected_elem").text = selected_elem
def end_drag(evt):
    global selected_elem
    global mselector
    if selected_elem:
        selected_elem = None
    else:
        mselector.mouse_release(GetTrueCoords(evt))
    highlight_stop()
    chech_itsc_idx()
def drag2(evt):
    global offset
    global mselector
    global selected_elem
    if selected_elem:
        evt.preventDefault()
        (x,y) = GetTrueCoords(evt)
        x_off, y_off = offset
        # we transform several points
        if transform.enabled:
            # transform.transform((x+x_off,y+y_off))
            transform.transform(GetRelative(evt), (x,y))
            # transform.transform(offset)
            diagnose2.text = f' offset {offset} x {x} y {y}'
        else:
            selected_elem.setAttributeNS(None, 'cx', x+x_off)
            selected_elem.setAttributeNS(None, 'cy', y+y_off)
            set_line_coord(selected_elem, x+x_off,y+y_off)
    else:
        mselector.mouse_moved(GetTrueCoords(evt))
def highlight_start(node_id):
    global edge_bimap
    global nodes_to_reset
    global selected_node_id
    node_id = int(node_id)
    for n in nodes_to_reset:
        svg_points_by_id[n].setAttributeNS(None, 'fill', default_color)
        # print('highlight_start resetting', n)
    nodes_to_reset = []
    # print('highlight_start', node_id)
    if node_id in edge_bimap:
        selected_node_id = node_id
        add_text('edge_bimap[node_id]').text = edge_bimap[node_id]
        svg_points_by_id[node_id].setAttributeNS(None, 'fill', color_selected)
        # print(edge_bimap[node_id])
        for node_id_friend in edge_bimap[node_id]:
            if node_id_friend in svg_points_by_id:
                # svg_points_by_id[node_id].fill = color_selected
                svg_points_by_id[node_id_friend].setAttributeNS(None, 'fill', color_sibling)
                # nodes_to_reset.append(node_id)
                nodes_to_reset.append(node_id_friend)
                # print('highlight_start sibling', node_id_friend)
    # print(nodes_to_reset)
def highlight_stop():
    # print('highlight_stop')
    global selected_node_id
    global nodes_to_reset
    if selected_node_id:
        node_id = int(selected_node_id)
        if node_id in svg_points_by_id:
            svg_points_by_id[node_id].setAttributeNS(None, 'fill', default_color)
    for n in nodes_to_reset:
        # print('highlight_stop resetting', n)
        svg_points_by_id[n].setAttributeNS(None, 'fill', default_color)
    nodes_to_reset = []
# adjusts line coords
def set_line_coord(target_pt, new_x, new_y):
    global edges_by_pt_global
    global svg_lines
    # line_id = int(target_pt.attrs['class'])
    # print(target_pt.attrs)
    if 'id' not in target_pt.attrs:
        return
    point_id = int(target_pt.attrs['id'])
    line_ids = edges_by_pt_global[point_id]
    for line_id in line_ids:
        if 'id' not in svg_lines[line_id].attrs:
            continue
        a,b = svg_lines[line_id].attrs['id'].split('-')
        a,b = int(a), int(b)
        # print(f'pt_id {point_id} a {a} b {b}')
        svg_line = svg_lines[line_id]
        if a == point_id:
            x1,y1,x2,y2 = (
               float(svg_line.getAttributeNS(None, "x1")),
               float(svg_line.getAttributeNS(None, "y1")),
               float(svg_line.getAttributeNS(None, "x2")),
               float(svg_line.getAttributeNS(None, "y2")))
            # new_x,new_y, x2, y2 = shorten(x1,y1,x2,y2)
            # new_x,new_y, x2, y2 = shorten(new_x,new_y,x2,y2)
            # new_x,new_y, x2, y2 = shorten(x1,y1,new_x,new_y)
            svg_line.setAttributeNS(None, "x1", f"{new_x}")
            svg_line.setAttributeNS(None, "y1", f"{new_y}")
        elif b == point_id:
            x1,y1,x2,y2 = (
               float(svg_line.getAttributeNS(None, "x1")),
               float(svg_line.getAttributeNS(None, "y1")),
               float(svg_line.getAttributeNS(None, "x2")),
               float(svg_line.getAttributeNS(None, "y2")))
            # x1, y1, new_x,new_y = shorten(x1,y1,x2,y2)
            # x1, y1, new_x,new_y = shorten(x1,y1,new_x,new_y)
            # x1, y1, new_x,new_y = shorten(new_x,new_y,x2,y2)
            svg_line.setAttributeNS(None, "x2", f"{new_x}")
            svg_line.setAttributeNS(None, "y2", f"{new_y}")
    # a,b = cl.split('-')
def shorten(Ax, Ay, Bx, By):
    diff = (Ax-Bx, Ay-By)
    (diff_x, diff_y) = diff
    length = math.sqrt(diff_x*diff_x+diff_y*diff_y)
    nm_x, nm_y = (10*diff_x/length, 10*diff_y/length)
    Ax -= nm_x
    Ay -= nm_y
    Bx += nm_x
    By += nm_y
    return (Ax, Ay, Bx, By)
# itsc
def intersects(Ax, Ay, Bx, By, Cx, Cy, Dx, Dy):
    # s1, s2 = vec2(), vec2()
    # s1 = p1 - p0
    # s2 = p3 - p2
    s1x = Bx - Ax
    s1y = By - Ay
    s2x = Dx - Cx
    s2y = Dy - Cy
    det = (-s2x * s1y + s1x * s2y)
    if det == 0:
        return False, None
    # s = (-s1.y * (p0.x - p2.x) + s1.x * (p0.y - p2.y)) / det;
    s = (-s1y * (Ax - Cx) + s1x * (Ay - Cy)) / det;
    # t = (s2.x * (p0.y - p2.y) - s2.y * (p0.x - p2.x)) / det;
    t = (s2x * (Ay - Cy) - s2y * (Ax - Cx)) / det;
    # i = vec2()
    if s >= 0 and s <= 1 and t >= 0 and t <= 1:
        # i = vec2(p0.x + (t * s1.x), p0.y + (t * s1.y))
        i = (Ax + (t * s1x), Ay + (t * s1y))
        return True, i
    return False, None
def chech_itsc_idx():
    global check_itsc
    if not check_itsc:
        return
    print('chech_itsc_idx')
    global intersection_group
    global svg_lines
    global svg_root
    collis_pt_found = []
    # print(edge_set)
    count = 0
    for lineA, lineB in combs(svg_lines,2):
        print(count)
        if count > 1: break
        lineA_x1 = float(lineA.getAttributeNS(None, 'x1'))
        lineA_y1 = float(lineA.getAttributeNS(None, 'y1'))
        lineA_x2 = float(lineA.getAttributeNS(None, 'x2'))
        lineA_y2 = float(lineA.getAttributeNS(None, 'y2'))
        lineB_x1 = float(lineB.getAttributeNS(None, 'x1'))
        lineB_y1 = float(lineB.getAttributeNS(None, 'y1'))
        lineB_x2 = float(lineB.getAttributeNS(None, 'x2'))
        lineB_y2 = float(lineB.getAttributeNS(None, 'y2'))
        does_itsc, itsc_pt = intersects(
            lineA_x1, lineA_y1,
            lineA_x2, lineA_y2,
            lineB_x1, lineB_y1,
            lineB_x2, lineB_y2)
        if (does_itsc == True
            and vectlen((lineA_x1, lineA_y1), itsc_pt) > 1.
            and vectlen((lineA_x2, lineA_y2),itsc_pt) > 1.):
            found_col = True
            collis_pt_found.append(itsc_pt)
            count +=1
    if not count:
        add_text("you won").text = "you won, you little squirel"
    # intersection_group.children = ''
    rm_this = [i for i in range(len(intersection_group.childNodes))]
    for i in rm_this: intersection_group.remove(i)
    intersection_group = svg.g()
    # print(collis_pt_found)
    for it_x, it_y in collis_pt_found:
        # it_x, it_y = GetTrueCoords_xy(it_x, it_y)
        intersection_group <= svg.circle(
            cx=it_x, cy=it_y, r=itsc_radius, fill=itsc_color)
    SVGRoot <= intersection_group
    print('chech_itsc_idx end')
def make_svg_circles(points_vec2):
    global svg_points_by_id
    points = []
    for i,(x1,y1) in enumerate(points_vec2):
        circ = svg.circle(
            cx=x1, cy=y1,
            r=cell_radius,
            fill=default_color,
            # stroke_width = 0.5,
            stroke_width = 1.5,
            stroke = '#fff',
            )
        circ.attrs['id'] = str(i)
        points.append(circ)
        svg_points_by_id[i] = circ
    return points
def make_svg_edges(points_vec2, edge_set):
    global edges_by_pt_global
    lines = []
    for line_id, (a, b) in enumerate(edge_set):
        x1, y1 = points_vec2[a]
        x2, y2 = points_vec2[b]
        # x1, y1, x2, y2 = shorten(x1, y1, x2, y2)
        line = svg.line(
            x1=str(x1), y1=str(y1),
            x2=str(x2), y2=str(y2),
            stroke = "white",
            stroke_width = "0.5")
        line.attrs['id'] = f'{a}-{b}'
        if a not in edges_by_pt_global: edges_by_pt_global[a] = []
        if b not in edges_by_pt_global: edges_by_pt_global[b] = []
        edges_by_pt_global[a].append(line_id)
        edges_by_pt_global[b].append(line_id)
        lines.append(line)
    return lines
def selectorationer(start,stop):
    global svg_points
    global color_selected
    global default_color
    startx, starty = start
    stopx, stopy = stop
    global selected_points
    selected_points = []
    print('selectorationer', len(selected_points))
    print('selectorationer', len(svg_points))
    for pt in svg_points:
        pt.setAttributeNS(None, 'fill', default_color)
    for point in svg_points:
        x = float(point.getAttributeNS(None, 'cx'))
        y = float(point.getAttributeNS(None, 'cy'))
        if (startx < x < stopx
            and starty < y < stopy):
            selected_points.append(point)
            point.setAttributeNS(None, 'fill', color_selected)
            i = point.getAttributeNS(None, 'id')
            # print(f'    x {startx} < {x} < {stopx} {startx < x < stopx} {i}')
            # print(f'    y {starty} < {y} < {stopy} {starty < y < stopy} {i}')
    # print('svg_points', len(svg_points))
    # print('selected_points', len(selected_points))
    for pt in selected_points:
        pt.setAttributeNS(None, 'fill', color_selected)
        # print(pt)
def init():
    # import sys
    print('initing')
    global svg_lines
    global svg_points
    global svg_texts
    global mselector
    global transform
    global selected_elem
    global offset
    global selected_points
    global edges_by_pt_global
    global edge_bimap
    svg_lines = []
    svg_points = []
    svg_texts = {}
    selected_elem = None
    offset = None
    selected_points = []
    edges_by_pt_global = {}
    # TrueCoords = SVGRoot.createSVGPoint()
    # GrabPoint = SVGRoot.createSVGPoint()
    mselector = mouse_selector(SVGRoot)
    mselector.callback = selectorationer
    transform = transformer()
    random.seed(3412212)
    # graph, edge_set = THE_GAMES()[8]
    # edge_set, bimap, position_by_id = game500()
    edge_set, bimap, position_by_id, count = extract_sav()
    # edge_set, bimap, position_by_id, count = game700()
    print(len([1 for k,v in bimap.items() if len(v)<3]))
    def centered_point(angle, radius = 400, origin = (400,600)):
        pt = circlepoint(angle, radius)
        x,y = pt
        orx, ory = origin
        return (x+orx, y+ory)
    edge_bimap = bimap
    print(edge_bimap)
    many = max([max(tup) for tup in edge_set])+1
    if position_by_id:
        sqrt_thing = math.sqrt(count)
        # print(position_by_id)
        # return
        points_vec2 = [
            # position_by_id[i] if i in position_by_id
            (
                position_by_id[i][0]*20,
                position_by_id[i][1]*20
            )
            if i in position_by_id
            # else centered_point(i/count)
            else (
                interp(100, 1000, (i%sqrt_thing)/sqrt_thing),
                interp(100, 1000, (i//sqrt_thing)/sqrt_thing)
                )
            for i in range(count)
            ]
    else:
        sqrt_thing = math.sqrt(count)
        points_vec2 = [
            (
                interp(100, 1000, (i%sqrt_thing)/sqrt_thing),
                interp(100, 1000, (i//sqrt_thing)/sqrt_thing)
            )
            for i in range(many)]
    svg_lines = make_svg_edges(points_vec2, edge_set)
    svg_points = make_svg_circles(points_vec2)
    for p in svg_lines:
        SVGRoot <= p
    for p in svg_points:
        SVGRoot <= p
    chech_itsc_idx()
    print('finished initing')
@bind(document, "keydown")
def key_pushed(evt):
    global transforn
    global selected_points
    print(evt.key, evt.code)
    if evt.key in ['1', '2', '3']:
        if transform.enabled:
            transform.stop()
        elif selected_points:
            transform.start(int(evt.key), selected_points)
    elif evt.key == 'q':
        init()
# SVGRoot.bind('keydown', key_pushed)
extract_sav()
# exit()
init()
SVGRoot.bind('mousemove', drag2)
SVGRoot.bind("mousedown", start_drag)
SVGRoot.bind("mouseup", end_drag)
</script>
</body>
</html>
Filename: None. Size: 42kb. View raw, , hex, or download this file.