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 resolve(self) -> None: """Resolve: Using three libraries to solve the system. + Pyslvs + Python-Solvespace + Sketch Solve """ for b, d, a in self.inputs_widget.input_pairs(): if b == d: self.vpoint_list[b].set_offset(a) solve_kernel = self.prefer.planar_solver_option try: if solve_kernel == 0: result = expr_solving( self.get_triangle(), {n: f'P{n}' for n in range(len(self.vpoint_list))}, self.vpoint_list, tuple(a for b, d, a in self.inputs_widget.input_pairs() if b != d)) elif solve_kernel == 1: result, _ = _slvs_solve(self.vpoint_list, { (b, d): a for b, d, a in self.inputs_widget.input_pairs() } if not self.free_move_button.isChecked() else {}) elif solve_kernel == 2: result = SolverSystem( self.vpoint_list, {(b, d): a for b, d, a in self.inputs_widget.input_pairs()}).solve() else: raise ValueError("incorrect kernel") except ValueError as error: # Error: Show warning without update data. if self.prefer.console_error_option: logger.warn(format_exc()) error_text = f"Error: {error}" self.conflict.setToolTip(error_text) self.conflict.setStatusTip(error_text) self.conflict.setVisible(True) self.dof_view.setVisible(False) else: self.entities_point.update_current_position(result) for i, c in enumerate(result): if type(c[0]) is float: self.vpoint_list[i].move(cast(_Coord, c)) else: c1, c2 = cast(Tuple[_Coord, _Coord], c) self.vpoint_list[i].move(c1, c2) self.__dof = vpoint_dof(self.vpoint_list) self.dof_view.setText( f"{self.__dof} ({self.inputs_widget.input_count()})") self.conflict.setVisible(False) self.dof_view.setVisible(True) self.reload_canvas()
def resolve(self) -> None: """Resolve: Using three libraries to solve the system. + Pyslvs + Python-Solvespace + Sketch Solve """ for b, d, a in self.inputs_widget.input_pairs(): if b == d: self.vpoint_list[b].set_offset(a) solve_kernel = self.prefer.planar_solver_option input_pair = {(b, d): a for b, d, a in self.inputs_widget.input_pairs()} try: if solve_kernel == Kernel.PYSLVS: result = expr_solving( self.get_triangle(), self.vpoint_list, input_pair ) elif solve_kernel == Kernel.SOLVESPACE: result, _ = _slvs_solve( self.vpoint_list, input_pair if not self.free_move_button.isChecked() else {} ) elif solve_kernel == Kernel.SKETCH_SOLVE: result = SolverSystem(self.vpoint_list, input_pair).solve() else: raise ValueError("incorrect kernel") except ValueError as error: # Error: Show warning without update data. if self.prefer.console_error_option: logger.warn(format_exc()) error_text = f"Error: {error}" self.conflict.setToolTip(error_text) self.conflict.setStatusTip(error_text) self.conflict.setVisible(True) self.dof_view.setVisible(False) else: self.entities_point.update_current_position(result) for i, c in enumerate(result): if isinstance(c[0], float): self.vpoint_list[i].move(c) else: c1, c2 = c self.vpoint_list[i].move(c1, c2) self.__dof = vpoint_dof(self.vpoint_list) self.dof_view.setText( f"{self.__dof} ({self.inputs_widget.input_count()})") self.conflict.setVisible(False) self.dof_view.setVisible(True) self.reload_canvas()
def test_solving_bfgs(self): """Test Sketch Solve kernel.""" expr, _ = example_list("Jansen's linkage (Single)") system = SolverSystem(parse_vpoints(expr), {(0, 1): 0.}) result = system.solve() x, y = result[7] self.assertAlmostEqual(-43.170055, x, 6) self.assertAlmostEqual(-91.753226, y, 6) # Test if angle value changed system.set_inputs({(0, 1): 45.}) result = system.solve() x, y = result[7] self.assertAlmostEqual(-24.406394, x, 6) self.assertAlmostEqual(-91.789596, y, 6) # Test if link length changed system.set_data({(0, 1): 16.}) result = system.solve() x, y = result[7] self.assertAlmostEqual(-24.117994, x, 6) self.assertAlmostEqual(-91.198072, y, 6)
def preview_path(self, auto_preview: List[List[Tuple[float, float]]], slider_auto_preview: Dict[int, List[Tuple[float, float]]], vpoints: Sequence[VPoint]): """Resolve auto preview path.""" if not self.right_input(): auto_preview.clear() slider_auto_preview.clear() return vpoints = tuple(vpoint.copy() for vpoint in vpoints) solve_kernel = self.prefer.path_preview_option if solve_kernel == len(kernel_list): solve_kernel = self.prefer.planar_solver_option interval_o = self.inputs_widget.interval() # path: [[p]: ((x0, y0), (x1, y1), (x2, y2), ...), ...] auto_preview.clear() slider_auto_preview.clear() for i, vpoint in enumerate(vpoints): auto_preview.append([]) if vpoint.type in {VJoint.P, VJoint.RP}: slider_auto_preview[i] = [] bases = [] drivers = [] angles_o = [] for b, d, a in self.inputs_widget.input_pairs(): bases.append(b) drivers.append(d) angles_o.append(a) i_count = self.inputs_widget.input_count() # Cumulative angle angles_cum = [0.] * i_count nan = float('nan') for interval in (interval_o, -interval_o): # Driver pointer dp = 0 angles = angles_o.copy() while dp < i_count: try: if solve_kernel == 0: result = expr_solving( self.get_triangle(vpoints), {n: f'P{n}' for n in range(len(vpoints))}, vpoints, angles) elif solve_kernel == 1: if self.free_move_button.isChecked(): inputs: _Inputs = {} else: inputs = {(bases[i], drivers[i]): angles[i] for i in range(i_count)} result, _ = _slvs_solve(vpoints, inputs) elif solve_kernel == 2: result = SolverSystem( vpoints, {(bases[i], drivers[i]): angles[i] for i in range(i_count)}).solve() else: raise ValueError("incorrect kernel") except ValueError: # Update with error sign for i in range(len(vpoints)): auto_preview[i].append((nan, nan)) # Back to last feasible solution angles[dp] -= interval dp += 1 else: # Update with result for i, vpoint in enumerate(vpoints): if vpoint.type == VJoint.R: auto_preview[i].append(cast(_Coord, result[i])) vpoint.move(cast(_Coord, result[i])) elif vpoint.type in {VJoint.P, VJoint.RP}: slot, pin = cast(Tuple[_Coord, _Coord], result[i]) # Pin path auto_preview[i].append(pin) # Slot path slider_auto_preview[i].append(slot) vpoint.move(slot, pin) angles[dp] += interval angles[dp] %= 360 angles_cum[dp] += abs(interval) if angles_cum[dp] > 360: angles[dp] -= interval dp += 1
def preview_path( self, auto_preview: List[List[Tuple[float, float]]], slider_auto_preview: Dict[int, List[Tuple[float, float]]], vpoints: Sequence[VPoint] ): """Resolve auto preview path.""" auto_preview.clear() slider_auto_preview.clear() if not self.right_input(): return vpoints = tuple(vpoint.copy() for vpoint in vpoints) solve_kernel = self.prefer.path_preview_option if solve_kernel == Kernel.SAME_AS_SOLVING: solve_kernel = self.prefer.planar_solver_option interval = self.inputs_widget.interval() # path: [[p]: ((x0, y0), (x1, y1), (x2, y2), ...), ...] for i, vpoint in enumerate(vpoints): auto_preview.append([]) if vpoint.type in {VJoint.P, VJoint.RP}: slider_auto_preview[i] = [] input_pair = {(b, d): a for b, d, a in self.inputs_widget.input_pairs()} # Cumulative angle angles_cum = dict.fromkeys(input_pair, 0.) nan = float('nan') for dp in input_pair: for interval in (interval, -interval): while 0 <= angles_cum[dp] <= 360: try: if solve_kernel == Kernel.PYSLVS: result = expr_solving( self.get_triangle(vpoints), vpoints, input_pair ) elif solve_kernel == Kernel.SOLVESPACE: result, _ = _slvs_solve( vpoints, {} if self.free_move_button.isChecked() else input_pair ) elif solve_kernel == Kernel.SKETCH_SOLVE: result = SolverSystem(vpoints, input_pair).solve() else: raise ValueError("incorrect kernel") except ValueError: # Update with error sign for i in range(len(vpoints)): auto_preview[i].append((nan, nan)) # Back to last feasible solution input_pair[dp] -= interval break # Update with result for i, vpoint in enumerate(vpoints): if vpoint.type == VJoint.R: auto_preview[i].append(cast(_Coord, result[i])) vpoint.move(cast(_Coord, result[i])) elif vpoint.type in {VJoint.P, VJoint.RP}: slot, pin = cast(Tuple[_Coord, _Coord], result[i]) # Pin path auto_preview[i].append(pin) # Slot path slider_auto_preview[i].append(slot) vpoint.move(slot, pin) angles_cum[dp] += abs(interval) input_pair[dp] += interval input_pair[dp] %= 360 for path in auto_preview: path[:] = path[:-1]