<commit_before>from functools import singledispatch
from typing import Tuple, Union

from gaphas.aspect.connector import ConnectionSink, Connector
from gaphas.connector import Handle, Port
from gaphas.item import Item
from gaphas.types import Pos
from gaphas.view import GtkView


class ItemHandleInMotion:
    """Move a handle (role is applied to the handle)"""

    GLUE_DISTANCE = 10

    last_x: float
    last_y: float

    def __init__(self, item: Item, handle: Handle, view: GtkView):
        self.item = item
        self.handle = handle
        self.view = view

    def start_move(self, pos: Pos):
        self.last_x, self.last_y = pos
        canvas = self.view.canvas

        cinfo = canvas.connections.get_connection(self.handle)
        if cinfo:
            canvas.solver.remove_constraint(cinfo.constraint)

    def move(self, pos: Pos):
        item = self.item
        view = self.view

        v2i = view.get_matrix_v2i(item)

        x, y = v2i.transform_point(*pos)

        self.handle.pos = (x, y)

        sink = self.glue(pos)

        # do not request matrix update as matrix recalculation will be
        # performed due to item normalization if required
        view.canvas.request_update(item, matrix=False)

        return sink

    def stop_move(self):
        pass

    def glue(self, pos: Pos, distance=GLUE_DISTANCE):
        """Glue to an item near a specific point.

        Returns a ConnectionSink or None.
        """
        item = self.item
        handle = self.handle
        view = self.view

        if not handle.connectable:
            return None

        connectable, port, glue_pos = get_port_at_point(
            view, pos, distance=distance, exclude=(item,)
        )

        # check if item and found item can be connected on closest port
        if connectable and port and glue_pos:
            connections = self.view.canvas.connections
            connector = Connector(self.item, self.handle, connections)
            sink = ConnectionSink(connectable, port)

            if connector.allow(sink):
                # transform coordinates from view space to the item
                # space and update position of item's handle
                v2i = view.get_matrix_v2i(item).transform_point
                handle.pos = v2i(*glue_pos)
                return sink
        return None


HandleInMotion = singledispatch(ItemHandleInMotion)


def get_port_at_point(
    view, vpos, distance=10, exclude=None
) -> Union[Tuple[Item, Port, Tuple[float, float]], Tuple[None, None, None]]:
    """Find item with port closest to specified position.

    List of items to be ignored can be specified with `exclude`
    parameter.

    Tuple is returned

    - found item<commit_msg>Just point_at_port() from now on<commit_after>from functools import singledispatch
from typing import Tuple, Union

from gaphas.aspect.connector import ConnectionSink, Connector
from gaphas.connector import Handle, Port
from gaphas.item import Item
from gaphas.types import Pos
from gaphas.view import GtkView


class ItemHandleInMotion:
    """Move a handle (role is applied to the handle)"""

    GLUE_DISTANCE = 10

    last_x: float
    last_y: float

    def __init__(self, item: Item, handle: Handle, view: GtkView):
        self.item = item
        self.handle = handle
        self.view = view

    def start_move(self, pos: Pos):
        self.last_x, self.last_y = pos
        canvas = self.view.canvas

        cinfo = canvas.connections.get_connection(self.handle)
        if cinfo:
            canvas.solver.remove_constraint(cinfo.constraint)

    def move(self, pos: Pos):
        item = self.item
        view = self.view

        v2i = view.get_matrix_v2i(item)

        x, y = v2i.transform_point(*pos)

        self.handle.pos = (x, y)

        sink = self.glue(pos)

        # do not request matrix update as matrix recalculation will be
        # performed due to item normalization if required
        view.canvas.request_update(item, matrix=False)

        return sink

    def stop_move(self):
        pass

    def glue(self, pos: Pos, distance=GLUE_DISTANCE):
        """Glue to an item near a specific point.

        Returns a ConnectionSink or None.
        """
        item = self.item
        handle = self.handle
        view = self.view

        if not handle.connectable:
            return None

        connectable, port, glue_pos = port_at_point(
            view, pos, distance=distance, exclude=(item,)
        )

        # check if item and found item can be connected on closest port
        if connectable and port and glue_pos:
            connections = self.view.canvas.connections
            connector = Connector(self.item, self.handle, connections)
            sink = ConnectionSink(connectable, port)

            if connector.allow(sink):
                # transform coordinates from view space to the item
                # space and update position of item's handle
                v2i = view.get_matrix_v2i(item).transform_point
                handle.pos = v2i(*glue_pos)
                return sink
        return None


HandleInMotion = singledispatch(ItemHandleInMotion)


def port_at_point(
    view, vpos, distance=10, exclude=None
) -> Union[Tuple[Item, Port, Tuple[float, float]], Tuple[None, None, None]]:
    """Find item with port closest to specified position.

    List of items to be ignored can be specified with `exclude`
    parameter.

    Tuple is returned

    - found item
{'fraction_non_alphanumeric': 0.07629129403175762, 'fraction_numerical': 0.003650301149844862, 'mean_word_length': 2.3353621424223983, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '339643', 'n_tokens_mistral': 1616, 'n_tokens_neox': 1511, 'n_words': 497}