示例#1
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()
示例#2
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)
        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])
        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[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()
示例#3
0
 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.workbook_no_save()
     if reload:
         self.__reload_atlas()
示例#4
0
 def clear(self) -> None:
     """Clear the attributes."""
     self.G = Graph([])
     self.cus.clear()
     self.same.clear()
     self.pos.clear()
     self.status.clear()
     self.grounded = -1
     self.driver.clear()
     self.target.clear()
     self.update()
示例#5
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 ""
示例#6
0
 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'])
示例#7
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.")
示例#8
0
    def __init__(self, parent: QWidget) -> None:
        """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()
示例#9
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:
        if monochrome:
            color = QColor(Qt.darkGray)
        else:
            color = 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))
示例#10
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,
        )