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()
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'])
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
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, }
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
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)
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()
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()
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))