def point_alignment(self) -> None: """Alignment function.""" selected_rows = self.entities_point.selected_rows() if not selected_rows: QMessageBox.warning(self, "Points alignment", "No selected points with this operation.") return if self.alignment_mode == 0: axis = "x" elif self.alignment_mode == 1: axis = "y" else: raise ValueError("no such alignment option") value, ok = QInputDialog.getDouble( self, f"Set {axis} axis", f"Align the selected points into {axis} axis:", 0, -9999, 9999, 4) if not ok: return self.command_stack.beginMacro(f"Align points with {axis}") for row in selected_rows: args = self.entities_point.row_data(row) if self.alignment_mode == 0: args.x = value elif self.alignment_mode == 1: args.y = value else: raise ValueError("no such alignment option") self.command_stack.push( EditPointTable(row, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, args)) self.command_stack.endMacro()
def __set_link_length(self) -> None: """Set link length.""" dlg = _LinkLengthDialog(self) dlg.show() if not dlg.exec_(): return data = {(dlg.get_leader(), dlg.get_follower()): dlg.get_length()} dlg.deleteLater() system = SolverSystem( self.vpoint_list, {(b, d): a for b, d, a in self.inputs_widget.input_pairs()}) system.set_data(data) try: result = system.solve() except ValueError: QMessageBox.warning(self, "Solved error", "The condition is not valid.") return self.command_stack.beginMacro(f"Set link length:{set(data)}") for row, c in enumerate(result): args = self.entities_point.row_data(row) if isinstance(c[0], float): args.x, args.y = c else: (args.x, args.y), _ = c self.command_stack.push( EditPointTable(row, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, args)) self.command_stack.endMacro()
def __edit_point(self, row: Union[int, bool] = False) -> None: """Edit point function.""" dlg = EditPointDialog(self.vpoint_list, self.vlink_list, row, self) dlg.show() if not dlg.exec_(): dlg.deleteLater() return row_count = self.entities_point.rowCount() type_str = dlg.type_box.currentText().split()[0] if type_str != 'R': type_str += f":{dlg.angle_box.value() % 360}" args = PointArgs( ','.join( dlg.selected.item(link).text() for link in range(dlg.selected.count())), type_str, dlg.color_box.currentText(), dlg.x_box.value(), dlg.y_box.value()) if row is False: self.command_stack.beginMacro(f"Add {{Point{row_count}}}") self.command_stack.push( AddTable(self.vpoint_list, self.entities_point)) row = row_count else: row = dlg.name_box.currentIndex() self.command_stack.beginMacro(f"Edit {{Point{row}}}") dlg.deleteLater() self.command_stack.push( EditPointTable(row, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, args)) self.command_stack.endMacro()
def delete_point(self, row: Optional[int] = None) -> None: """Push delete point command to stack.""" if row is None: row = self.entities_point.currentRow() if row < 0: return args = self.entities_point.row_data(row) args.links = '' self.command_stack.beginMacro(f"Delete {{Point{row}}}") for i in reversed([ i for i, (b, d, _) in enumerate(self.inputs_widget.input_pairs()) if row in {b, d} ]): self.inputs_widget.remove_var(i) self.command_stack.push( EditPointTable(row, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, args)) for i in range(self.entities_link.rowCount()): self.command_stack.push( FixSequenceNumber(self.vlink_list, self.entities_link, i, row)) self.command_stack.push( DeleteTable(row, self.vpoint_list, self.entities_point, is_rename=True)) self.inputs_widget.variable_excluding(row) self.command_stack.endMacro() if self.prefer.auto_remove_link_option: self.delete_redundant_links()
def lock_points(self) -> None: """Turn a group of points to fixed on ground or not.""" to_fixed = self.action_p_lock.isChecked() selected_rows = self.entities_point.selected_rows() self.cmd_stack.beginMacro( f"{'Grounded' if to_fixed else 'Ungrounded'} " f"{sorted(selected_rows)}" ) for row in selected_rows: new_links = list(self.vpoint_list[row].links) if to_fixed: if VLink.FRAME not in new_links: new_links.append(VLink.FRAME) elif VLink.FRAME in new_links: new_links.remove(VLink.FRAME) args = self.entities_point.row_data(row) args.links = ','.join(s for s in new_links if s) self.cmd_stack.push(EditPointTable( row, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, args )) self.cmd_stack.endMacro()
def add_point( self, x: float, y: float, links: str = "", color: str = 'Green', type_num: Union[int, VJoint] = VJoint.R, angle: float = 0. ) -> int: """Add an ordinary point. Return the row count of new point.""" row_count = self.entities_point.rowCount() self.cmd_stack.beginMacro(f"Add {{Point{row_count}}}") self.cmd_stack.push(AddTable(self.vpoint_list, self.entities_point)) if type_num == VJoint.R: type_str = 'R' elif type_num == VJoint.P: type_str = f'P:{angle}' else: type_str = f'RP:{angle}' self.cmd_stack.push(EditPointTable( row_count, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, PointArgs(links, type_str, color, x, y) )) self.cmd_stack.endMacro() return row_count
def parse_expression(self, expr: str) -> None: """Parse expression.""" try: args_list = parse_params(expr) except LarkError: QMessageBox.warning( self, "Loading failed", f"Your expression is in an incorrect format." ) else: for args in args_list: links = args.links.split(',') link_names = {vlink.name for vlink in self.vlink_list} for link_name in links: # If link name not exist if link_name not in link_names: self.add_link(link_name, 'Blue') row_count = self.entities_point.rowCount() self.command_stack.beginMacro(f"Add {{Point{row_count}}}") self.command_stack.push(AddTable( self.vpoint_list, self.entities_point )) self.command_stack.push(EditPointTable( row_count, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, args )) self.command_stack.endMacro()
def __to_multiple_joint(self, index: int, points: Sequence[int]) -> None: """Merge points into a multiple joint. @index: The index of main joint in the sequence. """ row = points[index] self.command_stack.beginMacro( f"Merge {sorted(points)} as multiple joint {{Point{row}}}" ) links = list(self.vpoint_list[row].links) args = self.entities_point.row_data(row) for point in sorted(points, reverse=True): for link in self.vpoint_list[point].links: if link not in links: links.append(link) self.delete_point(point) args.links = ','.join(links) self.command_stack.push(AddTable(self.vpoint_list, self.entities_point)) self.command_stack.push(EditPointTable( self.entities_point.rowCount() - 1, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, args )) self.command_stack.endMacro()
def clone_point(self) -> None: """Clone a point (with orange color).""" row = self.entities_point.currentRow() args = self.entities_point.row_data(row) args.color = 'Orange' row_count = self.entities_point.rowCount() self.command_stack.beginMacro( f"Clone {{Point{row}}} as {{Point{row_count}}}") self.command_stack.push(AddTable(self.vpoint_list, self.entities_point)) self.command_stack.push( EditPointTable(row_count, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, args)) self.command_stack.endMacro()
def set_free_move(self, args: Sequence[Tuple[int, Tuple[float, float, float]]]): """Free move function.""" points_text = ", ".join(f"Point{c[0]}" for c in args) self.command_stack.beginMacro(f"Moved {{{points_text}}}") for row, (x, y, angle) in args: arg = self.entities_point.row_data(row) arg.x = x arg.y = y if arg.type != 'R': angle_tag = arg.type.split(':')[0] arg.type = f"{angle_tag}:{angle:.02f}" self.command_stack.push( EditPointTable(row, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, arg)) self.command_stack.endMacro()
def __merge_joint(self, index: int, points: Sequence[int]) -> None: """Merge the joints into a specific joint.""" base = points[index] self.cmd_stack.beginMacro( f"Merge {sorted(points)} based on {{Point{base}}}") links = list(self.vpoint_list[base].links) args = self.entities_point.row_data(base) for p in points: if p != base: links.extend(set(self.vpoint_list[p].links) - set(links)) args.links = ','.join(links) self.cmd_stack.push( EditPointTable(base, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, args)) for p in sorted(points, reverse=True): if p != base: self.delete_point(p) self.cmd_stack.endMacro()
def __set_scale(self) -> None: """Scale the mechanism.""" dlg = _ScaleDialog(self) if not dlg.exec_(): dlg.deleteLater() return factor = dlg.factor() dlg.deleteLater() self.command_stack.beginMacro(f"Scale mechanism: {factor}") for row in range(self.entities_point.rowCount()): args = self.entities_point.row_data(row) args.x *= factor args.y *= factor self.command_stack.push( EditPointTable(row, self.vpoint_list, self.vlink_list, self.entities_point, self.entities_link, args)) self.command_stack.endMacro()