Esempio n. 1
0
    def __from_mechanism(self):
        """Reload button: Auto-combine the mechanism from the workbook."""
        joint_data = self.jointDataFunc()
        link_data = self.linkDataFunc()
        if joint_data and link_data:
            graph = Graph(self.getGraph())
            self.edges_text.setText(str(graph.edges))
        else:
            graph = Graph([])
            self.edges_text.setText("")
        keep_dof_checked = self.keep_dof.isChecked()
        self.keep_dof.setChecked(False)
        self.NL_input.setValue(
            sum(len(vlink.points) > 1 for vlink in link_data) + sum(
                len(vpoint.links) - 2 for vpoint in joint_data
                if (vpoint.type == VPoint.RP) and (len(vpoint.links) > 1)))
        self.NJ_input.setValue(
            sum((len(vpoint.links) - 1 + int(vpoint.type == VPoint.RP))
                for vpoint in joint_data if (len(vpoint.links) > 1)))
        self.keep_dof.setChecked(keep_dof_checked)

        # Auto synthesis.
        if not graph.edges:
            return
        self.l_a_list.setCurrentRow(
            compare_assortment(tuple(l_a(graph)), self.__l_a_synthesis()))
        self.c_l_a_list.setCurrentRow(
            compare_assortment(tuple(c_l_a(graph)), self.__c_l_a_synthesis()))
Esempio n. 2
0
def _reversed_graph(graph: Graph) -> Graph:
    """Edges will become nodes."""
    graph_ = Graph([])
    nodes = dict(edges_view(graph))
    for i, (l1, l2) in nodes.items():
        for j, edge in nodes.items():
            if i == j:
                continue
            if (l1 in edge) or (l2 in edge):
                graph_.add_edge(i, j)
    return graph_
Esempio n. 3
0
    def __init__(
        self,
        get_solutions: Callable[[], str],
        parent: QWidget
    ):
        """Input parameters and attributes.

        + A function should return a tuple of function expression.
            format: ("PLAP[P1,a0,L0,P2](P3)", "PLLP[P1,a0,L0,P2](P3)", ...)
        + 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.showSolutions = True
        self.get_solutions = get_solutions
        self.G = Graph([])
        self.cus: Dict[str, int] = {}
        self.same: Dict[int, int] = {}
        self.pos: Dict[int, Tuple[float, float]] = {}
        self.status = {}

        # Additional attributes.
        self.grounded = -1
        self.Driver = -1
        self.Target = -1

        self.clear()
Esempio n. 4
0
    def __add_from_files(self):
        """Append atlas by text files."""
        file_names = self.input_from("Edges data", ["Text File (*.txt)"],
                                     multiple=True)
        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)

        collections = []
        for edges in read_data:
            try:
                collections.append(Graph(eval(edges)))
            except (SyntaxError, TypeError):
                QMessageBox.warning(self, "Wrong format",
                                    "Please check the edges text format.")
                return
        if not collections:
            return
        self.collections += collections
        self.__reload_atlas()
Esempio n. 5
0
 def clear(self):
     """Clear the attributes."""
     self.G = Graph([])
     self.cus.clear()
     self.same.clear()
     self.pos.clear()
     self.status.clear()
     self.grounded = -1
     self.Driver = -1
     self.Target = -1
     self.update()
Esempio n. 6
0
    def __from_profile(self):
        """Show up the dialog to load structure data."""
        dlg = CollectionsDialog(self.collections, self.get_configure,
                                self.unsave_func,
                                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])
        pos: Dict[int, _Coord] = dict(enumerate(pos_list))
        if not self.set_graph(graph, pos):
            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
        placement: Set[int] = set(params['Placement'])
        links: List[Set[int]] = [set() for _ in range(len(graph.nodes))]
        for joint, link in edges_view(graph):
            for node in link:
                links[node].add(joint)

        for row, link in enumerate(links):
            if placement == link - set(same):
                self.__set_grounded(row)
                break

        # Driver, Target
        input_list: List[Tuple[int, int]] = params['input']
        self.driver_list.addItems(f"(P{b}, P{d})" for b, d in input_list)
        self.configure_canvas.set_driver(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(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'])
Esempio n. 7
0
 def add_collection(self, edges: Sequence[Tuple[int, int]]):
     """Add collection by in put edges."""
     graph = Graph(edges)
     error = self.__is_valid_graph(graph)
     if error:
         QMessageBox.warning(self, "Add Collection Error",
                             f"Error: {error}")
         return
     self.collections.append(graph)
     self.unsaveFunc()
     self.__reload_atlas()
Esempio n. 8
0
    def __is_valid_graph(self, g: Graph) -> str:
        """Test graph and return True if it is valid."""
        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 ""
Esempio n. 9
0
    def getGraph(self) -> List[Tuple[int, int]]:
        """Return edges data for NetworkX graph class.

        + VLinks will become graph nodes.
        """
        vpoints = self.EntitiesPoint.dataTuple()
        vlinks = self.EntitiesLink.dataTuple()
        graph = Graph([])
        # links name for RP joint.
        k = len(vlinks)
        used_point = set()
        # Link names will change to index number.
        for i, vlink in enumerate(vlinks):
            for p in vlink.points:
                if p in used_point:
                    continue
                for m, vlink_ in enumerate(vlinks):
                    if not ((i != m) and (p in vlink_.points)):
                        continue
                    if vpoints[p].type != VPoint.RP:
                        graph.add_edge(i, m)
                        continue
                    graph.add_edge(i, k)
                    graph.add_edge(k, m)
                    k += 1
                used_point.add(p)
        return [edge for n, edge in edges_view(graph)]
Esempio n. 10
0
def _four_bar_loops(graph: Graph) -> Iterator[Tuple[int, int, int, int]]:
    """A generator to find out the four bar loops."""
    result = set()
    vertexes = {v: k for k, v in edges_view(graph)}

    def loop_set(n: int, n1: int, n2: int,
                 n3: int) -> Tuple[int, int, int, int]:
        """Return a loop set."""
        return (
            vertexes[tuple(sorted((n, n1)))],
            vertexes[tuple(sorted((n1, n2)))],
            vertexes[tuple(sorted((n2, n3)))],
            vertexes[tuple(sorted((n, n3)))],
        )

    for node in graph.nodes:
        if node in result:
            continue
        nb1s = graph.neighbors(node)
        # node not in nb1s
        for nb1 in nb1s:
            if nb1 in result:
                continue
            nb2s = graph.neighbors(nb1)
            # node can not in nb2s
            for nb2 in nb2s:
                if (nb2 == node) or (nb2 in result):
                    continue
                nb3s = graph.neighbors(nb2)
                # node can not in nb3s
                for nb3 in nb3s:
                    if (nb3 in (node, nb1)) or (nb3 in result):
                        continue
                    if node in graph.neighbors(nb3):
                        loop = [node, nb1, nb2, nb3]
                        result.update(loop)
                        yield loop_set(*loop)
Esempio n. 11
0
 def __load_data_base(self):
     """Show up the dialog to load structure data."""
     dlg = CollectionsDialog(self.collections, self.getCollection, self)
     dlg.show()
     if not dlg.exec_():
         return
     self.profile_name = dlg.name()
     params = dlg.params()
     # Add customize joints.
     graph = Graph(params['Graph'])
     self.setGraph(graph, params['pos'])
     self.PreviewWindow.cus = params['cus']
     self.PreviewWindow.same = params['same']
     # Grounded setting.
     drivers = set(params['Driver'])
     followers = set(params['Follower'])
     for row, link in enumerate(graph.nodes):
         points = {f'P{n}' for n, edge in edges_view(graph) if link in edge}
         if (drivers | followers) <= points:
             self.__set_ground(row)
             break
     # Driver, Follower, Target
     for expr in params['Expression'].split(';'):
         if str_before(expr, '[') != 'PLAP':
             continue
         base = str_between(expr, '[', ']').split(',')[0]
         self.__find_follower_to_remove(base)
         rotator = str_between(expr, '(', ')')
         self.driver_list.addItem(f"({base}, {rotator})")
     _set_warning(self.driver_label, not self.driver_list.count())
     self.target_list.addItems(list(params['Target']))
     _set_warning(self.target_label, not self.target_list.count() > 0)
     # Constraints
     self.constraint_list.addItems(
         [", ".join(c) for c in params['constraints']])
     # Expression
     if params['Expression']:
         for expr in params['Expression'].split(';'):
             func = str_before(expr, '[')
             target = str_between(expr, '(', ')')
             params = str_between(expr, '[', ']').split(',')
             params.insert(0, func)
             params.append(target)
             self.__add_solution(*params)
             self.PreviewWindow.setStatus(target, True)
     _set_warning(self.expression_list_label,
                  not self.PreviewWindow.isAllLock())
Esempio n. 12
0
 def test_topologic(self):
     """Testing 'topologic' libraries.
     
     + 'topo' function.
     + 'Graph' class.
     """
     from core.libs import Graph
     G = Graph([(0, 1), (0, 4), (1, 5), (2, 3), (2, 4), (3, 5), (4, 5)])
     H = Graph([(0, 2), (0, 4), (1, 3), (1, 4), (2, 5), (3, 5), (4, 5)])
     I = Graph([(0, 1), (0, 2), (1, 4), (2, 5), (3, 4), (3, 5), (4, 5)])
     self.assertTrue(G.is_isomorphic(H))
     self.assertFalse(G.is_isomorphic(I))
     answer, time = topo([4, 2], degenerate=True)
     self.assertEqual(len(answer), 2)
Esempio n. 13
0
 def from_profile(self, params: Dict[str, Any]):
     """Simple load by dict object."""
     # Add customize joints.
     graph = Graph(params['Graph'])
     self.setGraph(graph, params['pos'])
     self.cus = params['cus']
     self.same = params['same']
     # Grounded setting.
     driver = set(params['Driver'])
     follower = set(params['Follower'])
     for row, link in enumerate(graph.nodes):
         points = set(f'P{n}' for n, edge in edges_view(graph) if link in edge)
         if (driver | follower) <= points:
             self.setGrounded(row)
             break
     # Expression
     if params['Expression']:
         for expr in params['Expression'].split(';'):
             self.setStatus(io.str_between(expr, '(', ')'), True)
     self.update()
Esempio n. 14
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.G = Graph([])
        self.cus: Dict[int, int] = {}
        self.same: Dict[int, int] = {}
        self.pos: Dict[int, _Coord] = {}
        self.status = {}

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

        self.clear()
Esempio n. 15
0
    def __edges2atlas(self):
        """Turn the text files into a atlas image.

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

        for file_name in file_names:
            with open(file_name) 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.__reload_atlas()
        self.save_edges_auto.setChecked(False)
        self.__save_atlas()
        self.save_edges_auto.setChecked(self.save_edges_auto.isChecked())
Esempio n. 16
0
    def from_profile(self, params: Dict[str, Any]):
        """Simple load by dict object."""
        # Customize points and multiple joints
        graph = Graph(params['Graph'])
        expression: str = params['Expression']
        pos_list = parse_pos(expression)
        self.cus: Dict[int, int] = params['cus']
        self.same: Dict[int, int] = params['same']
        for node, ref in sorted(self.same.items()):
            pos_list.insert(node, pos_list[ref])
        pos: Dict[int, _Coord] = dict(enumerate(pos_list))
        self.set_graph(graph, pos)

        # Grounded setting
        placement: Set[int] = set(params['Placement'])
        links: List[Set[int]] = [set() for _ in range(len(graph.nodes))]
        for joint, link in edges_view(graph):
            for node in link:
                links[node].add(joint)

        for row, link in enumerate(links):
            if placement == link - set(self.same):
                self.set_grounded(row)
                break

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

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

        self.update()
Esempio n. 17
0
def to_graph(graph: Graph,
             width: int,
             engine: Union[str, Dict[int, Tuple[float, float]]],
             node_mode: bool = False,
             except_node: int = None) -> QIcon:
    """Draw a generalized chain graph."""
    pos: Pos = engine_picker(graph, engine, node_mode)
    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.2
    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 = width_bound / 50
    pen.setWidth(int(r))
    painter.setPen(pen)

    # Draw edges.
    if node_mode:
        for l1, l2 in graph.edges:
            if except_node in {l1, l2}:
                pen.setColor(color_qt('Gray'))
            else:
                pen.setColor(color_qt('Black'))
            painter.setPen(pen)

            painter.drawLine(QPointF(pos[l1][0], -pos[l1][1]),
                             QPointF(pos[l2][0], -pos[l2][1]))
    else:
        painter.setBrush(QBrush(QColor(226, 219, 190, 150)))
        for link in graph.nodes:
            if link == except_node:
                pen.setColor(color_qt('Gray'))
            else:
                pen.setColor(color_qt('Black'))
            painter.setPen(pen)

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

    # Draw nodes.
    for k, (x, y) in pos.items():
        if node_mode:
            color = color_num(len(list(graph.neighbors(k))) - 1)
            if k == except_node:
                color.setAlpha(150)
        else:
            if except_node in dict(edges_view(graph))[k]:
                color = color_qt('Green')
            else:
                color = color_qt('Blue')
        pen.setColor(color)
        painter.setPen(pen)
        painter.setBrush(QBrush(color))
        painter.drawEllipse(QPointF(x, -y), r, r)
    painter.end()
    icon = QIcon(QPixmap.fromImage(image).scaledToWidth(width))
    return icon
Esempio n. 18
0
def to_graph(
    g: Graph,
    width: int,
    engine: Union[str, Pos],
    node_mode: bool,
    show_label: bool,
    monochrome: bool,
    *,
    except_node: Optional[int] = None
) -> QIcon:
    """Draw a generalized chain graph."""
    pos: Pos = engine_picker(g, engine, node_mode)
    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:
        if monochrome:
            color = QColor(Qt.darkGray)
        else:
            color = QColor(226, 219, 190)
        color.setAlpha(150)
        painter.setBrush(QBrush(color))
        for link in g.nodes:
            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 nodes.
    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))
Esempio n. 19
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 nodes.
        """
        vpoints = list(self.entities_point.data())
        vlinks = list(self.entities_link.data())
        link_names = [vlink.name for vlink in vlinks]
        input_pair = set()
        for b, d, _ in self.inputs_widget.input_pairs():
            input_pair.update({b, d})

        # links name for RP joint.
        k = len(vlinks)

        graph = Graph([])
        grounded_list = []
        pos = {}
        same = {}
        used_point = set()
        mapping = {}
        # Link names will change to index number.
        for i, vlink in enumerate(vlinks):
            for p in vlink.points:
                if p in used_point:
                    continue

                vpoint = vpoints[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 = 'ground' 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 vpoints:
            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,
        )