Exemple #1
0
    def create_graphical_object(self, context, ruler, size):
        builder = PathBuilder()

        for local, part in self._get_children(context):
            part.add_to_path(local, builder)

        path = builder.build().transform(
            Transformation.make_scale2(Size(1 / size.width, 1 / size.height)))

        fill = self.__fill(context)
        border = self.__border(context)

        return PathObject(path, fill, border)
Exemple #2
0
    def assign_points(self, points):
        p1 = self._compute_position(points, self.__start)
        p2 = self._compute_position(points, self.__end)

        path = PathBuilder()
        path.move_to(p1.position)
        for point in points[p1.id + 1:p2.id + 1]:
            path.line_to(point)
        path.line_to(p2.position)

        self.__path = path.build()
Exemple #3
0
    def __build_path(self):
        path = PathBuilder()

        path.move_to(self.__first_point)

        for point in self.__points:
            path.line_to(point)

        if self.__last_point is not None:
            path.line_to(self.__last_point)

        self.__path = path.build()
Exemple #4
0
 def __build_path(self):
     path = PathBuilder()
     
     for idx, point in enumerate(self.__points):
         if idx == 0:
             path.move_to(point)
         else:
             if idx == self.__index and self.__point is not None:
                 path.line_to(self.__point)
             path.line_to(point)
     
     self.__path = path.build()
Exemple #5
0
 def load(self):
     definitions = {
         "ArrowDefinition": {},
         "CornerDefinition": {},
         "SideDefinition": {},
     }
     
     for child in self.__xmlroot:
         if child.tag == "{{{0}}}ArrowDefinition".format(ADDON_NAMESPACE):
             definition = ArrowDefinition(
                 child.attrib["id"],
                 PathBuilder().from_string(child.attrib["path"]).build(),
                 Point.parse(child.attrib["center"]),
                 self.__parse_rotation(child.attrib["rotation"])
             )
             definitions["ArrowDefinition"][definition.id] = definition
         elif child.tag == "{{{0}}}CornerDefinition".format(ADDON_NAMESPACE):
             ornament = None
             if "ornament" in child.attrib:
                 ornament = PathBuilder().from_string(child.attrib["ornament"]).build()
             
             definition = CornerDefinition(
                 child.attrib["id"],
                 PathBuilder().from_string(child.attrib["path"]).build(),
                 ornament,
                 Point.parse(child.attrib["center"]),
                 child.attrib["corner"]
             )
             definitions["CornerDefinition"][definition.id] = definition
         elif child.tag == "{{{0}}}SideDefinition".format(ADDON_NAMESPACE):
             ornament = None
             if "ornament" in child.attrib:
                 ornament = PathBuilder().from_string(child.attrib["ornament"]).build()
             
             definition = SideDefinition(
                 child.attrib["id"],
                 PathBuilder().from_string(child.attrib["path"]).build(),
                 ornament,
                 Point.parse(child.attrib["center"]),
                 Size(int(child.attrib["width"]), int(child.attrib["height"])),
                 child.attrib["side"]
             )
             definitions["SideDefinition"][definition.id] = definition
     
     return definitions
Exemple #6
0
class Selection:
    SELECTION_COLOR = Colors.blue
    SELECTION_SIZE = 2

    SELECTION_POINT_COLOR = Colors.darkgray
    SELECTION_POINT_SIZE = 8

    LABEL_LINE_MINIMAL_DISTANCE = 10

    ICON_COLOR = Colors.darkgray
    ICON_COLOR_BACKGROUND = Colors.white.add_alpha(200)

    CONNECTION_ICON_SHIFT = Vector(8, 0)
    CONNECTION_ICON = PathBuilder()\
        .move_to(Point(0, 5))\
        .line_to(Point(11, 5))\
        .move_to(Point(6, 0))\
        .line_to(Point(11, 5))\
        .line_to((Point(6, 10)))\
        .build()
    CONNECTION_ICON_BOUNDS = Rectangle(0, 0, 11, 10) + CONNECTION_ICON_SHIFT

    def __init__(self, application, diagram):
        self.__selected = set()
        self.__application = application
        self.__diagram = diagram

        application.event_dispatcher.subscribe(ElementHiddenEvent,
                                               self.__something_removed)
        application.event_dispatcher.subscribe(ConnectionHiddenEvent,
                                               self.__something_removed)

    def __something_removed(self, event):
        new_selection = set()
        for selected_item in self.__selected:
            if self.__diagram.contains(selected_item):
                new_selection.add(selected_item)

        self.__selected = new_selection

    @property
    def selected_visuals(self):
        yield from self.__selected

    @property
    def selected_elements(self):
        for visual in self.__selected:
            if isinstance(visual, ElementVisual):
                yield visual

    @property
    def selected_connection(self):
        for visual in self.__selected:
            if isinstance(visual, ConnectionVisual):
                return visual
        return None

    @property
    def selected_diagram(self):
        if self.__selected:
            return None
        else:
            return self.__diagram

    @property
    def diagram(self):
        return self.__diagram

    def select(self, visual):
        self.__selected.clear()
        if visual is not None:
            if isinstance(visual, Iterable):
                self.__selected.update(visual)
            else:
                self.__selected.add(visual)

        self.__application.event_dispatcher.dispatch(
            SelectionChangedEvent(self.__diagram, self))

    def select_all(self):
        self.__selected.clear()
        self.__selected.update(self.__diagram.elements)

        self.__application.event_dispatcher.dispatch(
            SelectionChangedEvent(self.__diagram, self))

    def deselect_all(self):
        self.__selected.clear()

        self.__application.event_dispatcher.dispatch(
            SelectionChangedEvent(self.__diagram, self))

    def add_to_selection(self, visual):
        if isinstance(visual, ConnectionVisual):
            self.__selected.clear()
        elif self.__selected and all(
                isinstance(sel, ConnectionVisual) for sel in self.__selected):
            self.__selected.clear()

        self.__selected.add(visual)

        self.__application.event_dispatcher.dispatch(
            SelectionChangedEvent(self.__diagram, self))

    def remove_from_selection(self, visual):
        if visual in self.__selected:
            self.__selected.remove(visual)

        self.__application.event_dispatcher.dispatch(
            SelectionChangedEvent(self.__diagram, self))

    def toggle_select(self, visual):
        if visual in self.__selected:
            self.remove_from_selection(visual)
        else:
            self.add_to_selection(visual)

    def select_at(self, point):
        object = self.__diagram.get_visual_at(self.__application.ruler, point)

        self.deselect_all()

        if object is not None:
            self.add_to_selection(object)

    def select_in_area(self, area):
        self.__selected.clear()
        for element in self.__diagram.elements:
            if element.get_bounds(
                    self.__application.ruler).is_overlapping(area):
                self.__selected.add(element)

        self.__application.event_dispatcher.dispatch(
            SelectionChangedEvent(self.__diagram, self))

    def draw_for(self, canvas, visual):
        if visual not in self.__selected:
            return

        if isinstance(visual, ElementVisual):
            bounds = visual.get_bounds(canvas.get_ruler())

            if len(self.__selected) == 1:
                for pos_x, pos_y in self.__get_selection_points_positions(
                        visual):
                    self.__draw_selection_point(canvas, bounds, pos_x, pos_y)

                connection_icon_position = bounds.top_right + self.CONNECTION_ICON_SHIFT
                connection_icon = self.CONNECTION_ICON.transform(
                    Transformation.make_translate(connection_icon_position))
                canvas.draw_path(connection_icon,
                                 fg=self.ICON_COLOR,
                                 bg=self.ICON_COLOR_BACKGROUND,
                                 line_width=2)

            canvas.draw_rectangle(bounds,
                                  fg=self.SELECTION_COLOR,
                                  line_width=self.SELECTION_SIZE)
        elif isinstance(visual, ConnectionVisual):
            if len(self.__selected) == 1:
                fg_color = None
                bg_color = self.SELECTION_POINT_COLOR
            else:
                fg_color = self.SELECTION_POINT_COLOR
                bg_color = None

            for point in visual.get_points(canvas.get_ruler()):
                canvas.draw_rectangle(Rectangle.from_point_size(
                    point - Vector(self.SELECTION_POINT_SIZE // 2,
                                   self.SELECTION_POINT_SIZE // 2),
                    Size(self.SELECTION_POINT_SIZE,
                         self.SELECTION_POINT_SIZE)),
                                      fg=fg_color,
                                      bg=bg_color)

            for label in visual.get_labels():
                bounds = label.get_bounds(canvas.get_ruler())
                if bounds.width and bounds.height:
                    canvas.draw_rectangle(bounds,
                                          fg=self.SELECTION_COLOR,
                                          line_width=self.SELECTION_SIZE)
                    line_to_connection = Line.from_point_point(
                        label.get_nearest_point(canvas.get_ruler()),
                        bounds.center)
                    intersect = list(bounds.intersect(line_to_connection))
                    if intersect and (
                            line_to_connection.first - intersect[0]
                    ).length > self.LABEL_LINE_MINIMAL_DISTANCE:
                        canvas.draw_line(line_to_connection.first,
                                         intersect[0],
                                         fg=self.SELECTION_COLOR,
                                         line_width=self.SELECTION_SIZE,
                                         line_style=LineStyle.dot)

    def draw_selected(self, canvas, selection=False, transparent=False):
        if not transparent:
            self.__diagram.draw_background(canvas)

        if self.is_connection_selected:
            self.selected_connection.draw(canvas)
            if selection:
                self.draw_for(canvas, self.selected_connection)
        elif self.is_element_selected:
            elements = [
                element for element in self.__diagram.elements
                if element in self.__selected
            ]

            for element in elements:
                element.draw(canvas)
                if selection:
                    self.draw_for(canvas, element)

            for connection in self.__diagram.connections:
                if connection.source in self.__selected and connection.destination in self.__selected:
                    connection.draw(canvas)
                    if selection:
                        self.draw_for(canvas, connection)
        else:
            raise Exception

    def __get_selection_points_positions(self, visual):
        resizable_x, resizable_y = visual.is_resizable(
            self.__application.ruler)

        if resizable_x and resizable_y:
            yield SelectionPointPosition.first, SelectionPointPosition.first
            yield SelectionPointPosition.first, SelectionPointPosition.last
            yield SelectionPointPosition.last, SelectionPointPosition.first
            yield SelectionPointPosition.last, SelectionPointPosition.last

        if resizable_x:
            yield SelectionPointPosition.first, SelectionPointPosition.center
            yield SelectionPointPosition.last, SelectionPointPosition.center

        if resizable_y:
            yield SelectionPointPosition.center, SelectionPointPosition.first
            yield SelectionPointPosition.center, SelectionPointPosition.last

    def __draw_selection_point(self, canvas, bounds, pos_x, pos_y):
        canvas.draw_rectangle(self.__get_selection_point(bounds, pos_x, pos_y),
                              bg=self.SELECTION_POINT_COLOR)

    def __get_selection_point(self, bounds, pos_x, pos_y):
        x1, x2 = self.__compute_selection_point_position(
            pos_x, bounds.x1, bounds.width)
        y1, y2 = self.__compute_selection_point_position(
            pos_y, bounds.y1, bounds.height)
        return Rectangle(x1, y1, x2 - x1, y2 - y1)

    def __compute_selection_point_position(self, pos, start, size):
        if pos == SelectionPointPosition.first:
            return start, start + self.SELECTION_POINT_SIZE
        elif pos == SelectionPointPosition.center:
            return start + size // 2 - self.SELECTION_POINT_SIZE // 2, start + size // 2 + self.SELECTION_POINT_SIZE // 2
        elif pos == SelectionPointPosition.last:
            return start + size - self.SELECTION_POINT_SIZE, start + size

    def get_action_at(self, position, shift_pressed):
        if len(self.__selected) == 1 and self.is_connection_selected:
            connection, = self.__selected
            action = self.__get_connection_action_at(connection, position,
                                                     shift_pressed)

            if isinstance(action,
                          (MoveConnectionPointAction, AddConnectionPointAction,
                           RemoveConnectionPointAction)):
                return action

        visual = self.__diagram.get_visual_at(self.__application.ruler,
                                              position)

        if len(self.__selected) == 1 and self.is_element_selected:
            element, = self.__selected
            if visual is None or self.__diagram.get_z_order(
                    visual) < self.__diagram.get_z_order(element):
                element_icon_action = self.__get_element_icon_action_at(
                    element, position, shift_pressed)
                if element_icon_action is not None:
                    return element_icon_action

        if visual not in self.__selected:
            return None

        if len(self.__selected) > 1:
            if shift_pressed:
                return None
            else:
                return MoveSelectionAction()

        if isinstance(visual, ElementVisual):
            return self.__get_element_action_at(visual, position,
                                                shift_pressed)
        elif isinstance(visual, ConnectionVisual):
            return self.__get_connection_action_at(visual, position,
                                                   shift_pressed)
        else:
            return None

    def __get_element_action_at(self, element, position, shift_pressed):
        if shift_pressed:
            return None

        bounds = element.get_bounds(self.__application.ruler)
        for pos_x, pos_y in self.__get_selection_points_positions(element):
            if self.__get_selection_point(bounds, pos_x,
                                          pos_y).contains(position):
                return ResizeElementAction(element, pos_x, pos_y)

        return MoveSelectionAction()

    def __get_element_icon_action_at(self, element, position, shift_pressed):
        if shift_pressed:
            return None

        bounds = element.get_bounds(self.__application.ruler)
        connection_icon_bounds = self.CONNECTION_ICON_BOUNDS + bounds.top_right.as_vector(
        )
        if connection_icon_bounds.contains(position):
            return AddUntypedConnectionAction(element)

        return None

    def __get_connection_action_at(self, connection, position, shift_pressed):
        found = None
        for idx, point in enumerate(
                connection.get_points(self.__application.ruler)):
            if idx > 0:  # don't return it for first point
                if (position - point
                    ).length < ConnectionVisual.MAXIMAL_CLICKABLE_DISTANCE:
                    found = idx
                elif found is not None:  # don't return it for last point
                    if shift_pressed:
                        return RemoveConnectionPointAction(connection, found)
                    else:
                        return MoveConnectionPointAction(connection, found)

        for label in connection.get_labels():
            if label.get_bounds(self.__application.ruler).contains(position):
                if shift_pressed:
                    return None
                else:
                    return MoveConnectionLabelAction(connection, label.id)

        if shift_pressed:
            last = None
            for idx, point in enumerate(
                    connection.get_points(self.__application.ruler)):
                if last is not None:
                    distance = Line.from_point_point(
                        last, point).get_distance_to(position)
                    if distance < ConnectionVisual.MAXIMAL_CLICKABLE_DISTANCE:
                        return AddConnectionPointAction(connection, idx)
                last = point

        return None

    def is_selection_at(self, position):
        visual = self.__diagram.get_visual_at(self.__application.ruler,
                                              position)

        return visual in self.__selected

    def is_selected(self, visual):
        return visual in self.__selected

    def get_bounds(self, include_connections=True):
        bounds = [
            visual.get_bounds(self.__application.ruler)
            for visual in self.__selected
        ]

        if include_connections and self.is_element_selected:
            for connection in self.__diagram.connections:
                if connection.source in self.__selected and connection.destination in self.__selected:
                    bounds.append(
                        connection.get_bounds(self.__application.ruler))

        return Rectangle.combine_bounds(bounds)

    @property
    def is_diagram_selected(self):
        return len(self.__selected) == 0

    @property
    def is_connection_selected(self):
        return any(
            isinstance(visual, ConnectionVisual) for visual in self.__selected)

    @property
    def is_element_selected(self):
        return any(
            isinstance(visual, ElementVisual) for visual in self.__selected)

    @property
    def size(self):
        return len(self.__selected)

    def get_lonely_selected_visual(self):
        if len(self.__selected) != 1:
            return None

        for visual in self.__selected:
            return visual
Exemple #7
0
    def assign_bounds(self, bounds):
        corner_and_side_positions = [
            (bounds.top_left, bounds.left_center, bounds.height),
            (bounds.top_right, bounds.top_center, bounds.width),
            (bounds.bottom_right, bounds.right_center, bounds.height),
            (bounds.bottom_left, bounds.bottom_center, bounds.width)
        ]

        path = PathBuilder()
        ornaments = PathBuilder()

        for no, (corner_point, side_point,
                 side_size) in enumerate(corner_and_side_positions):
            rotation = -math.pi * no / 2
            corner = self.__corners[no]
            side = self.__sides[no]
            next_side = self.__sides[(no + 1) % 4]

            if corner is not None:
                transformation = Transformation.make_translate(
                    corner_point) * Transformation.make_rotation(-rotation)
                path.from_path(corner.path.transform(transformation), True)
                if corner.ornament is not None:
                    ornaments.from_path(
                        corner.ornament.transform(transformation))
            elif side is not None:
                transformation = Transformation.make_translate(side_point) * Transformation.make_rotation(-rotation) *\
                    Transformation.make_scale2(Size(1, side_size / side.size.height))
                path.from_path(side.path.transform(transformation), True)
                if side.ornament is not None:
                    ornaments.from_path(
                        side.ornament.transform(transformation))
            elif next_side is None:
                path.move_or_line_to(corner_point)

        path.close()

        self.__path = path.build()
        self.__ornaments_path = ornaments.build()

        if self.__child is not None:
            self.__child.assign_bounds(bounds)
Exemple #8
0
    def assign_bounds(self, bounds):
        path = PathBuilder()
        path.move_to(bounds.left_center)
        path.line_to(bounds.top_center)
        path.line_to(bounds.right_center)
        path.line_to(bounds.bottom_center)
        path.close()

        self.__path = path.build()

        if self.__child is not None:
            self.__child.assign_bounds(bounds)