예제 #1
0
 def set_grounded(self, link: int):
     """Set the grounded link number."""
     self.grounded = link
     for n, edge in edges_view(self.G):
         self.status[n] = self.grounded in edge
     for n, link in self.cus.items():
         self.status[n] = self.grounded == link
     self.update()
예제 #2
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'])
예제 #3
0
 def find_friends(node: int):
     """Find all the nodes that are same link with input node."""
     ev = dict(edges_view(self.configure_canvas.G))
     link = set(ev[node])
     tmp_list = []
     for node_, link_ in ev.items():
         if node_ == node:
             continue
         inter = set(link_) & link
         if inter and inter.pop() != self.configure_canvas.grounded:
             tmp_list.append(f'P{node_}')
     return tmp_list
예제 #4
0
    def get_configure(self) -> Dict[str, Any]:
        """Return collection data.

        + Expression
        + input
        + Graph
        + Placement
        + Target
        + cus
        + same
        """
        for vpoint in self.entities_point.data():
            if vpoint.type in {VJoint.P, VJoint.RP}:
                raise ValueError("not support for prismatic joint yet")

        graph, grounded_list, input_list, pos, cus, same = self.get_graph()

        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)

        placement = set(grounded_list)
        for row, link in enumerate(links):
            if placement == link - set(same):
                grounded = row
                break
        else:
            raise ValueError("no grounded link")

        vpoint_exprs = [
            vpoint.expr()
            for vpoint in graph2vpoints(graph, pos, cus, same, grounded)
        ]

        return {
            'Expression': "M[" + ", ".join(vpoint_exprs) + "]",
            'input': input_list,
            'Graph': graph.edges,
            'Placement': {p: None for p in grounded_list},
            'Target': {p: None for p in cus},
            'cus': cus,
            'same': same,
        }
예제 #5
0
    def set_graph(self, graph: Graph, pos: Dict[int, _Coord]) -> bool:
        """Set the graph to preview canvas, return False if no clear."""
        if not self.__user_clear():
            return False

        self.configure_canvas.set_graph(graph, pos)

        links: List[List[str]] = [[] for _ in range(len(graph.nodes))]
        for joint, link in edges_view(graph):
            for node in link:
                links[node].append(f'P{joint}')

        for link in links:
            self.grounded_list.addItem("(" + ", ".join(link) + ")")

        # Point name as (P1, P2, P3, ...).
        for node in pos:
            self.joint_name.addItem(f'P{node}')

        return True
예제 #6
0
    def add_points_by_graph(self, graph: Graph, pos: Dict[int, Tuple[float,
                                                                     float]],
                            ground_link: Optional[int]):
        """Add points by NetworkX graph and position dict."""
        base_count = self.entities_point.rowCount()
        self.command_stack.beginMacro(
            "Merge mechanism kit from {Number and Type Synthesis}")

        for i in range(len(pos)):
            x, y = pos[i]
            self.add_point(x, y)

        ground: Optional[int] = None
        for link in graph.nodes:
            self.add_link(self.__get_link_serial_number(), 'Blue', [
                base_count + n for n, edge in edges_view(graph) if link in edge
            ])
            if link == ground_link:
                ground = self.entities_link.rowCount() - 1
        self.command_stack.endMacro()
        if ground_link is not None:
            self.constrain_link(ground)
예제 #7
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()
예제 #8
0
    def paintEvent(self, event):
        """Draw the structure."""
        width = self.width()
        height = self.height()
        if self.pos:
            x_right, x_left, y_top, y_bottom = self.__zoom_to_fit_limit()
            x_diff = x_left - x_right
            y_diff = y_top - y_bottom
            x_diff = x_diff if x_diff else 1
            y_diff = y_diff if y_diff else 1
            if width / x_diff < height / y_diff:
                factor = width / x_diff
            else:
                factor = height / y_diff
            self.zoom = factor * 0.75
            self.ox = width / 2 - (x_left + x_right) / 2 * self.zoom
            self.oy = height / 2 + (y_top + y_bottom) / 2 * self.zoom
        else:
            if width <= height:
                self.zoom = width / PreviewCanvas.view_size
            else:
                self.zoom = height / PreviewCanvas.view_size
            self.ox = width / 2
            self.oy = height / 2

        super(PreviewCanvas, self).paintEvent(event)

        pen = QPen()
        pen.setWidth(self.joint_size)
        self.painter.setPen(pen)
        if self.monochrome:
            color = QColor(Qt.darkGray)
        else:
            color = QColor(226, 219, 190)
        color.setAlpha(150)
        self.painter.setBrush(QBrush(color))

        # Links
        for link in self.G.nodes:
            if link == self.grounded:
                continue
            points = []
            # Points that is belong with the link.
            for num, edge in edges_view(self.G):
                if link in edge:
                    if num in self.same:
                        num = self.same[num]
                    x, y = self.pos[num]
                    points.append((x * self.zoom, y * -self.zoom))
            # Customize points.
            for name, link_ in self.cus.items():
                if link == link_:
                    x, y = self.pos[name]
                    points.append((x * self.zoom, y * -self.zoom))
            self.painter.drawPolygon(*convex_hull(points, as_qpoint=True))

        # Nodes
        for node, (x, y) in self.pos.items():
            if node in self.same:
                continue
            x *= self.zoom
            y *= -self.zoom
            if self.monochrome:
                color = Qt.black
            elif node in self.driver:
                color = color_qt('Red')
            elif node in self.target:
                color = color_qt('Orange')
            elif self.get_status(node):
                color = color_qt('Green')
            else:
                color = color_qt('Blue')
            pen.setColor(color)
            self.painter.setPen(pen)
            self.painter.setBrush(QBrush(color))
            self.painter.drawEllipse(QPointF(x, y), self.joint_size,
                                     self.joint_size)
            pen.setColor(Qt.black)
            self.painter.setPen(pen)

        # Text of node.
        pen.setColor(Qt.black)
        self.painter.setPen(pen)
        for node, (x, y) in self.pos.items():
            if node in self.same:
                continue
            x *= self.zoom
            x += 2 * self.joint_size
            y *= -self.zoom
            y -= 2 * self.joint_size
            self.painter.drawText(QPointF(x, y), f'P{node}')

        self.painter.end()
예제 #9
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))