Пример #1
0
 def test_graph_degree_code(self):
     g1 = Graph([
         (0, 1),
         (0, 2),
         (0, 3),
         (0, 5),
         (2, 3),
         (3, 4),
         (2, 5),
         (3, 5),
         (4, 6),
         (3, 6),
     ])
     g2 = Graph([
         (0, 1),
         (0, 4),
         (0, 2),
         (0, 6),
         (0, 3),
         (1, 4),
         (2, 6),
         (2, 5),
         (3, 6),
         (2, 3),
     ])
     self.assertTrue(g1.is_isomorphic(g2))
     self.assertEqual(2057732, g1.degree_code())
     self.assertEqual(g1.degree_code(), g2.degree_code())
     for code, edges in DEGREE_CODE_TABLE:
         self.assertEqual(code, Graph(edges).degree_code())
Пример #2
0
 def test_graph_duplicate(self):
     g1 = Graph([(0, 1), (1, 2), (2, 3), (0, 3)])
     g2 = g1.duplicate([2, 3], 1)
     self.assertEqual(set(g2.edges), {
         (0, 1),
         (1, 2),
         (4, 5),
         (1, 4),
         (2, 3),
         (0, 5),
         (0, 3),
     })
Пример #3
0
    def from_profile(self, params: Dict[str, Any]) -> None:
        """Simple load by dict object."""
        # Customize points and multiple joints
        g = Graph(params['graph'])
        expression: str = params['expression']
        pos_list = parse_pos(expression)
        cus: Dict[int, int] = params['cus']
        same: Dict[int, int] = params['same']
        self.cus = cus
        self.same = same
        for node, ref in sorted(self.same.items()):
            pos_list.insert(node, pos_list[ref])
        self.set_graph(g, {i: (x, y) for i, (x, y) in enumerate(pos_list)})

        # Grounded setting
        for row in self.grounded_detect(set(params['placement']), g,
                                        self.same):
            self.set_grounded(row)

        # Driver setting
        input_list: List[Tuple[Tuple[int, int],
                               Tuple[float, float]]] = params['input']
        self.driver.clear()
        self.driver.update(b for (b, _), _ in input_list)

        # Target setting
        target: Dict[int, Sequence[_Coord]] = params['target']
        self.target.clear()
        self.target.update(target)

        self.update()
Пример #4
0
    def __edges2atlas(self) -> None:
        """Turn the text files into a atlas image.

        This operation will load all edges to list widget first.
        """
        file_names = self.input_from_multiple("edges data",
                                              ["Text file (*.txt)"])
        if not file_names:
            return

        read_data = []
        for file_name in file_names:
            with open(file_name, 'r', encoding='utf-8') as f:
                for line in f:
                    read_data.append(line)

        answer = []
        for edges in read_data:
            try:
                g = Graph(eval(edges))
            except (SyntaxError, TypeError):
                QMessageBox.warning(self, "Wrong format",
                                    "Please check text format.")
            else:
                answer.append(g)

        if not answer:
            QMessageBox.information(self, "No data",
                                    "The graph data is empty.")
            return

        self.answer = answer
        self.__set_time_count(0, len(answer))
        self.__reload_atlas()
        self.__save_atlas()
Пример #5
0
    def __from_mechanism(self) -> None:
        """From a generalized mechanism of main canvas."""
        if self.vpoints and self.vlinks:
            graph, _, _, _, _, _ = self.get_graph()
        else:
            graph = Graph([])
        if graph.edges:
            self.edges_text.setText(str(list(graph.edges)))
        else:
            self.edges_text.setText("")
        keep_dof_checked = self.keep_dof.isChecked()
        self.keep_dof.setChecked(False)
        self.nl_input.setValue(len(graph.vertices))
        self.nj_input.setValue(len(graph.edges))
        self.keep_dof.setChecked(keep_dof_checked)

        # Show attributes
        QMessageBox.information(
            self,
            "Generalization",
            f"Link assortment:\n{link_assortment(graph)}\n"
            f"Contracted link assortment:\n{contracted_link_assortment(graph)}"
            if graph.edges else
            "Is a empty graph."
        )
Пример #6
0
 def test_graph_loop(self):
     g1 = Graph([
         (0, 1), (4, 10), (1, 3), (2, 9), (5, 6), (4, 5), (5, 7), (8, 10),
         (1, 8), (9, 11), (3, 6), (0, 4), (3, 7), (2, 5), (0, 2), (4, 11),
     ])
     pos = external_loop_layout(g1, True)
     self.assertEqual(set(g1.vertices), set(pos))
Пример #7
0
 def test_graph_planar(self):
     g1 = Graph([
         (0, 1), (2, 7), (1, 5), (1, 6), (3, 6), (0, 4), (3, 7), (2, 5),
         (3, 4), (0, 2),
     ])
     self.assertTrue(is_planar(g1))
     self.assertEqual([4, 4], link_assortment(g1))
     self.assertEqual([4, 0, 0, 0], contracted_link_assortment(g1))
 def add_collection(self, edges: Iterable[Tuple[int, int]], *, reload: bool = True) -> None:
     """Add collection by in put edges."""
     error = self.__is_valid_graph(edges)
     if error:
         QMessageBox.warning(self, "Add Collection Error", f"Error: {error}")
         return
     self.collections.append(Graph(edges))
     self.project_no_save()
     if reload:
         self.__reload_atlas()
Пример #9
0
 def clear(self) -> None:
     """Clear the attributes."""
     self.graph = Graph([])
     self.cus.clear()
     self.same.clear()
     self.pos.clear()
     self.status.clear()
     self.grounded = -1
     self.driver.clear()
     self.target.clear()
     self.update()
Пример #10
0
 def __init__(self, parent: QWidget):
     """Input parameters and attributes."""
     super(PreviewCanvas, self).__init__(parent)
     self.graph = Graph([])
     self.cus = {}
     self.same = {}
     self.pos = {}
     self.status = {}
     # Additional attributes
     self.grounded = -1
     self.driver = set()
     self.target = set()
     self.clear()
Пример #11
0
 def __is_valid_graph(self, edges: Iterable[Tuple[int, int]]) -> str:
     """Test graph and return True if it is valid."""
     try:
         g = Graph(edges)
     except (TypeError, ValueError):
         return "wrong format"
     if not g.edges:
         return "is an empty graph"
     if not g.is_connected():
         return "is not a close chain"
     if not is_planar(g):
         return "is not a planar chain"
     if g.has_cut_link():
         return "has cut link"
     try:
         external_loop_layout(g, True)
     except ValueError as error:
         return str(error)
     for h in self.collections:
         if g.is_isomorphic(h):
             return f"is isomorphic with: {h.edges}"
     return ""
 def __from_profile(self) -> None:
     """Show up the dialog to load structure data."""
     dlg = CollectionsDialog(
         self.collections,
         self.get_configure,
         self.project_no_save,
         self.prefer.tick_mark_option,
         self.configure_canvas.monochrome,
         self
     )
     dlg.show()
     if not dlg.exec_():
         dlg.deleteLater()
         return
     # Add customize joints
     params = dlg.params
     graph = Graph(params['graph'])
     expression: str = params['expression']
     pos_list = parse_pos(expression)
     cus: Dict[int, int] = params['cus']
     same: Dict[int, int] = params['same']
     for node, ref in sorted(same.items()):
         pos_list.insert(node, pos_list[ref])
     if not self.set_graph(graph, {i: (x, y) for i, (x, y) in enumerate(pos_list)}):
         dlg.deleteLater()
         return
     self.profile_name.setText(dlg.name)
     dlg.deleteLater()
     del dlg
     self.configure_canvas.cus = cus
     self.configure_canvas.same = same
     # Grounded setting
     for row in PreviewCanvas.grounded_detect(set(params['placement']), graph, same):
         self.__set_grounded(row)
     # Driver, Target
     input_list: List[Tuple[Tuple[int, int], Tuple[float, float]]] = params['input']
     self.driver_list.addItems(f"(P{b}, P{d})" for (b, d), _ in input_list)
     self.configure_canvas.set_driver([d for d, _ in input_list])
     _set_warning(self.driver_label, self.driver_list.count() == 0)
     target_list: Dict[int, Sequence[_Coord]] = params['target']
     self.configure_canvas.set_target(sorted(target_list))
     self.target_list.addItems(f"P{n}" for n in target_list)
     _set_warning(self.target_label, self.target_list.count() == 0)
     # Expression
     self.expr_show.setText(params['expression'])
Пример #13
0
    def __init__(self, parent: QWidget):
        """Input parameters and attributes.

        + Origin graph
        + Customize points: Dict[str, int]
        + Multiple joints: Dict[int, int]
        + Positions: Dict[int, Tuple[float, float]]
        + Joint status: Dict[int, bool]
        + Name dict: Dict['P0', 'A']
        """
        super(PreviewCanvas, self).__init__(parent)
        self.graph = Graph([])
        self.cus: Dict[int, int] = {}
        self.same: Dict[int, int] = {}
        self.pos: Dict[int, _Coord] = {}
        self.status: Dict[int, bool] = {}

        # Additional attributes.
        self.grounded = -1
        self.driver: Set[int] = set()
        self.target: Set[int] = set()

        self.clear()
Пример #14
0
 def test_graph_degenerate(self):
     g1 = Graph([(0, 1), (0, 2), (2, 1), (0, 3), (3, 4), (4, 5), (5, 1)])
     self.assertTrue(g1.is_degenerate())
Пример #15
0
    def get_graph(self) -> Tuple[
        Graph,
        List[int],
        List[Tuple[int, int]],
        Dict[int, Tuple[float, float]],
        Dict[int, int],
        Dict[int, int]
    ]:
        """Generalization Algorithm

        Return edges data, grounded list, variable list and multiple joints.
        VLinks will become graph vertices.
        """
        link_names = [vlink.name for vlink in self.vlink_list]
        input_pair = set()
        for b, d, _ in self.inputs_widget.input_pairs():
            input_pair.update({b, d})
        # links name for RP joint
        k = len(self.vlink_list)
        graph = Graph([])
        grounded_list = []
        pos = {}
        same = {}
        used_point: Set[int] = set()
        mapping = {}
        # Link names will change to index number
        for i, vlink in enumerate(self.vlink_list):
            for p in vlink.points:
                if p in used_point:
                    continue
                vpoint = self.vpoint_list[p]
                base_num = len(graph.edges)
                mapping[p] = base_num
                pos[base_num] = (vpoint.x, vpoint.y)
                for link_name in vpoint.links:
                    if vlink.name == link_name:
                        continue
                    m = link_names.index(link_name)
                    grounded = VLink.FRAME in {vlink.name, link_name}
                    ref_num = len(graph.edges)
                    if ref_num != base_num:
                        pos[ref_num] = (vpoint.x, vpoint.y)
                    if vpoint.type == VJoint.RP:
                        graph.add_edge(i, k)
                        if grounded:
                            grounded_list.append(len(graph.edges))
                        graph.add_edge(k, m)
                        k += 1
                    else:
                        if ref_num != base_num:
                            same[ref_num] = base_num
                        graph.add_edge(i, m)
                    if grounded and ref_num not in same:
                        grounded_list.append(ref_num)
                used_point.add(p)
        counter = len(graph.edges)
        cus = {}
        for vpoint in self.vpoint_list:
            if len(vpoint.links) == 1:
                cus[counter] = link_names.index(vpoint.links[0])
                counter += 1
        return (
            graph,
            grounded_list,
            [(mapping[b], mapping[d])
             for b, d, _ in self.inputs_widget.input_pairs()],
            pos,
            cus,
            same,
        )
Пример #16
0
def graph2icon(g: Graph,
               width: int,
               node_mode: bool,
               show_label: bool,
               monochrome: bool,
               *,
               except_node: Optional[int] = None,
               engine: str = "",
               pos: Optional[_Pos] = None) -> QIcon:
    """Draw a generalized chain graph."""
    if engine:
        pos = engine_picker(g, engine, node_mode)
    if pos is None:
        raise ValueError("no engine selected")
    if not pos:
        pixmap = QPixmap(width, width)
        pixmap.fill(Qt.transparent)
        return QIcon(pixmap)

    width_bound = -float('inf')
    for x, y in pos.values():
        if abs(x) > width_bound:
            width_bound = x
        if abs(y) > width_bound:
            width_bound = y
    width_bound *= 2.5
    image = QImage(QSize(int(width_bound), int(width_bound)),
                   QImage.Format_ARGB32_Premultiplied)
    image.fill(Qt.transparent)
    painter = QPainter(image)
    painter.translate(image.width() / 2, image.height() / 2)
    pen = QPen()
    r = int(width_bound / 50)
    pen.setWidth(r)
    painter.setPen(pen)
    _font.setPixelSize(r * 6)
    painter.setFont(_font)

    # Draw edges
    if node_mode:
        for l1, l2 in g.edges:
            if except_node in {l1, l2}:
                pen.setColor(Qt.gray)
            else:
                pen.setColor(Qt.black)
            painter.setPen(pen)

            painter.drawLine(QPointF(pos[l1][0], -pos[l1][1]),
                             QPointF(pos[l2][0], -pos[l2][1]))
    else:
        color = color_qt('dark-gray') if monochrome else LINK_COLOR
        color.setAlpha(150)
        painter.setBrush(QBrush(color))
        for link in g.vertices:
            if link == except_node:
                pen.setColor(Qt.gray)
            else:
                pen.setColor(Qt.black)
            painter.setPen(pen)

            painter.drawPolygon(*convex_hull([(pos[n][0], -pos[n][1])
                                              for n, edge in edges_view(g)
                                              if link in edge],
                                             as_qpoint=True))

    # Draw vertices
    for k, (x, y) in pos.items():
        if node_mode:
            color = color_num(len(list(g.neighbors(k))) - 1)
            if k == except_node:
                color.setAlpha(150)
        else:
            if monochrome:
                color = Qt.black
            elif except_node in dict(edges_view(g))[k]:
                color = color_qt('green')
            else:
                color = color_qt('blue')
        pen.setColor(color)
        painter.setPen(pen)
        painter.setBrush(QBrush(color))
        point = QPointF(x, -y)
        painter.drawEllipse(point, r, r)
        if show_label:
            pen.setColor(Qt.darkMagenta)
            painter.setPen(pen)
            painter.drawText(point, str(k))
    painter.end()
    return QIcon(QPixmap.fromImage(image).scaledToWidth(width))
Пример #17
0
 def test_graph_basic(self):
     """Test 'graph' libraries."""
     g1 = Graph([(0, 1), (0, 4), (1, 5), (2, 3), (2, 4), (3, 5), (4, 5)])
     self.assertFalse(g1.is_degenerate())
     self.assertTrue(g1.is_connected())
     self.assertEqual(1, g1.dof())
Пример #18
0
 def test_graph_isomorphic(self):
     g1 = Graph([(0, 1), (0, 4), (1, 5), (2, 3), (2, 4), (3, 5), (4, 5)])
     g2 = Graph([(0, 2), (0, 4), (1, 3), (1, 4), (2, 5), (3, 5), (4, 5)])
     g3 = Graph([(0, 1), (0, 2), (1, 4), (2, 5), (3, 4), (3, 5), (4, 5)])
     self.assertTrue(g1.is_isomorphic(g2))
     self.assertFalse(g1.is_isomorphic(g3))