def __init__(self, mechanism: Dict[str, Any], path: Sequence[Sequence[Tuple[float, float]]], parent: QWidget): """Show the information of results, and setup the preview canvas.""" super(PreviewDialog, self).__init__(parent) self.setupUi(self) self.setWindowTitle(f"Preview: {mechanism['Algorithm']} " f"(max {mechanism['last_gen']} generations)") self.setWindowFlags(self.windowFlags() | Qt.WindowMaximizeButtonHint) self.main_splitter.setSizes([400, 150]) self.splitter.setSizes([100, 100, 100]) vpoints = parse_vpoints(mechanism['expression']) vlinks = get_vlinks(vpoints) preview_widget = _DynamicCanvas(mechanism, path, vpoints, vlinks, self) self.left_layout.insertWidget(0, preview_widget) labels = [] for tag, data in chain([('Algorithm', mechanism['Algorithm']), ('time', mechanism['time'])], [(f"P{i}", vpoints[i].c[0]) for i in mechanism['placement']]): if type(data) is tuple: label = f"({data[0]:.02f}, {data[1]:.02f})" elif type(data) is float: label = f"{data:.02f}" else: label = f"{data}" labels.append(f"{tag}: {label}") self.basic_label.setText("\n".join(labels)) # Algorithm information inter = mechanism.get('interrupted', 'N/A') if inter == 'False': inter_icon = "task_completed.png" elif inter == 'N/A': inter_icon = "question.png" else: inter_icon = "interrupted.png" if 'last_fitness' in mechanism: fitness = f"{mechanism['last_fitness']:.06f}" else: fitness = 'N/A' text_list = [ f"Max generation: {mechanism.get('last_gen', 'N/A')}", f"Fitness: {fitness}", f"<img src=\":/icons/{inter_icon}\" width=\"15\"/>" f"Interrupted at: {inter}" ] for k, v in mechanism['settings'].items(): text_list.append(f"{k}: {v}") text = "<br/>".join(text_list) self.algorithm_label.setText( f"<html><head/><body><p>{text}</p></body></html>") # Hardware information self.hardware_label.setText("\n".join([ f"{tag}: {mechanism['hardware_info'][tag]}" for tag in ('os', 'memory', 'cpu') ]))
def slvs_solve( vpoints: Sequence[VPoint], inputs: Dict[Tuple[int, int], float] ) -> Tuple[List[Union[_Coord, Tuple[_Coord, _Coord]]], int]: """Use element module to convert into solvespace expression.""" if not vpoints: return [], 0 vlinks: Dict[str, VLink] = {vlink.name: vlink for vlink in get_vlinks(vpoints)} # Solvespace kernel sys = SolverSystem() sys.set_group(1) wp = sys.create_2d_base() origin_2d = sys.add_point_2d(0., 0., wp) sys.dragged(origin_2d, wp) origin_2d_p = sys.add_point_2d(10., 0., wp) sys.dragged(origin_2d_p, wp) hv = sys.add_line_2d(origin_2d, origin_2d_p, wp) sys.set_group(2) points: List[Entity] = [] sliders: Dict[int, int] = {} slider_bases: List[Entity] = [] slider_slots: List[Entity] = [] for i, vpoint in enumerate(vpoints): if vpoint.no_link(): x, y = vpoint.c[0] # type: float point = sys.add_point_2d(x, y, wp) sys.dragged(point, wp) points.append(point) continue if vpoint.grounded(): x, y = vpoint.c[0] if vpoint.type in {VJoint.P, VJoint.RP}: sliders[i] = len(slider_bases) # Base point (slot) is fixed. point = sys.add_point_2d(x, y, wp) sys.dragged(point, wp) slider_bases.append(point) # Slot point (slot) is movable. x += cos(vpoint.angle) y += sin(vpoint.angle) slider_slots.append(sys.add_point_2d(x, y, wp)) # Pin is movable. x, y = vpoint.c[1] if vpoint.has_offset() and vpoint.true_offset() <= 0.1: if vpoint.offset() > 0: x += 0.1 y += 0.1 else: x -= 0.1 y -= 0.1 points.append(sys.add_point_2d(x, y, wp)) else: point = sys.add_point_2d(x, y, wp) sys.dragged(point, wp) points.append(point) continue x, y = vpoint.c[0] point = sys.add_point_2d(x, y, wp) if vpoint.type in {VJoint.P, VJoint.RP}: sliders[i] = len(slider_bases) # Base point (slot) is movable. slider_bases.append(point) # Slot point (slot) is movable. x += cos(vpoint.angle) y += sin(vpoint.angle) slider_slots.append(sys.add_point_2d(x, y, wp)) if vpoint.pin_grounded(): # Pin is fixed. x, y = vpoint.c[1] point = sys.add_point_2d(x, y, wp) sys.dragged(point, wp) points.append(point) else: # Pin is movable. x, y = vpoint.c[1] if vpoint.has_offset() and vpoint.true_offset() <= 0.1: if vpoint.offset() > 0: x += 0.1 y += 0.1 else: x -= 0.1 y -= 0.1 points.append(sys.add_point_2d(x, y, wp)) continue # Point is movable. points.append(point) for vlink in vlinks.values(): if len(vlink.points) < 2: continue if vlink.name == 'ground': continue a = vlink.points[0] b = vlink.points[1] vp1 = vpoints[a] vp2 = vpoints[b] if vp1.is_slot_link(vlink.name): p1 = slider_bases[sliders[a]] else: p1 = points[a] if vp2.is_slot_link(vlink.name): p2 = slider_bases[sliders[b]] else: p2 = points[b] sys.distance(p1, p2, vp1.distance(vp2), wp) for c in vlink.points[2:]: # type: int for d in (a, b): vp1 = vpoints[c] vp2 = vpoints[d] if vp1.is_slot_link(vlink.name): p1 = slider_bases[sliders[c]] else: p1 = points[c] if vp2.is_slot_link(vlink.name): p2 = slider_bases[sliders[d]] else: p2 = points[d] sys.distance(p1, p2, vp1.distance(vp2), wp) for a, b in sliders.items(): # Base point vp1 = vpoints[a] p1 = points[a] # Base slot slider_slot = sys.add_line_2d(slider_bases[b], slider_slots[b], wp) if vp1.grounded(): # Slot is grounded. sys.angle(hv, slider_slot, vp1.angle, wp) sys.coincident(p1, slider_slot, wp) if vp1.has_offset(): p2 = slider_bases[b] if vp1.offset(): sys.distance(p2, p1, vp1.offset(), wp) else: sys.coincident(p2, p1, wp) else: # Slider between links. for name in vp1.links[:1]: # type: str vlink = vlinks[name] # A base link friend. c = vlink.points[0] if c == a: if len(vlink.points) < 2: # If no any friend. continue c = vlink.points[1] vp2 = vpoints[c] if vp2.is_slot_link(vlink.name): # c is a slider, and it is be connected with slot link. p2 = slider_bases[sliders[c]] else: # c is a R joint or it is not connected with slot link. p2 = points[c] sys.angle( slider_slot, sys.add_line_2d(slider_bases[b], p2, wp), vp1.slope_angle(vp2) - vp1.angle, wp ) sys.coincident(p1, slider_slot, wp) if vp1.has_offset(): p2 = slider_bases[b] if vp1.offset(): sys.distance(p2, p1, vp1.offset(), wp) else: sys.coincident(p2, p1, wp) if vp1.type != VJoint.P: continue for name in vp1.links[1:]: vlink = vlinks[name] # A base link friend. c = vlink.points[0] if c == a: if len(vlink.points) < 2: # If no any friend. continue c = vlink.points[1] vp2 = vpoints[c] if vp2.is_slot_link(vlink.name): # c is a slider, and it is be connected with slot link. p2 = slider_bases[sliders[c]] else: # c is a R joint or it is not connected with slot link. p2 = points[c] sys.angle( slider_slot, sys.add_line_2d(p1, p2, wp), vp1.slope_angle(vp2) - vp1.angle, wp ) for (b, d), angle in inputs.items(): # The constraints of drive shaft. # Simulate the input variables to the mechanism. # The 'base points' are shaft center. if b == d: continue vp1 = vpoints[b] if vp1.type == VJoint.R: p1 = points[b] else: p1 = slider_bases[sliders[b]] angle = radians(angle) p2 = sys.add_point_2d(vp1.cx + cos(angle), vp1.cy + sin(angle), wp) sys.dragged(p2, wp) # The virtual link that dragged by "hand". leader = sys.add_line_2d(p1, p2, wp) # Make another virtual link that should follow "hand". vp2 = vpoints[d] if vp2.type == VJoint.R: p2 = points[d] else: p2 = slider_bases[sliders[d]] link = sys.add_line_2d(p1, p2, wp) sys.angle(link, leader, 0.5, wp) # Solve result_flag = sys.solve() if result_flag == ResultFlag.OKAY: result_list = [] for i, vpoint in enumerate(vpoints): p1 = points[i] x1, y1 = sys.params(p1.params) if vpoint.type == VJoint.R: result_list.append((x1, y1)) else: p2 = slider_bases[sliders[i]] x2, y2 = sys.params(p2.params) result_list.append(((x2, y2), (x1, y1))) return result_list, sys.dof() if result_flag == ResultFlag.INCONSISTENT: error = "inconsistent" elif result_flag == ResultFlag.DIDNT_CONVERGE: error = "didn't converge" else: error = "too many unknowns" error += f": {sys.faileds()}\n{sys.constraints()}" raise ValueError(error)
def __init__( self, mechanism: Dict[str, Any], path: Sequence[Sequence[_Coord]], monochrome: bool, parent: QWidget ): """Show the information of results, and setup the preview canvas.""" super(PreviewDialog, self).__init__(parent) self.setupUi(self) self.setWindowTitle( f"Preview: {mechanism['algorithm']} " f"(max {mechanism['last_gen']} generations)" ) self.setWindowFlags(self.windowFlags() | Qt.WindowMaximizeButtonHint) for splitter in (self.geo_splitter, self.path_cmp_splitter): splitter.setSizes([400, 150]) self.splitter.setSizes([100, 100, 100]) vpoints = parse_vpoints(mechanism['expression']) vlinks = get_vlinks(vpoints) self.canvas1 = _DynamicCanvas(mechanism, path, vpoints, vlinks, self) self.canvas2 = _DynamicCanvas(mechanism, path, parent=self) for c in (self.canvas1, self.canvas2): c.update_pos.connect(self.__set_mouse_pos) c.set_monochrome_mode(monochrome) self.left_layout.insertWidget(0, self.canvas1) self.path_cmp_layout.addWidget(self.canvas2) self.plot_joint.addItems(f"P{i}" for i in self.canvas2.get_target()) labels = [] for tag, data in chain( [(tag, mechanism.get(tag, 'N/A')) for tag in ( 'algorithm', 'time', 'shape_only', 'wavelet_mode')], [(f"P{i}", (vpoints[i].c[0, 0], vpoints[i].c[0, 1])) for i in mechanism['placement']] ): if type(data) is tuple: label = f"({data[0]:.02f}, {data[1]:.02f})" elif type(data) is float: label = f"{data:.02f}" else: label = f"{data}" labels.append(f"{tag}: {label}") self.basic_label.setText("\n".join(labels)) # Algorithm information inter = mechanism.get('interrupted', 'N/A') if inter == 'False': inter_icon = "task_completed.png" elif inter == 'N/A': inter_icon = "question.png" else: inter_icon = "interrupted.png" if 'last_fitness' in mechanism: fitness = f"{mechanism['last_fitness']:.06f}" else: fitness = 'N/A' text_list = [ f"Max generation: {mechanism.get('last_gen', 'N/A')}", f"Fitness: {fitness}", f"<img src=\":/icons/{inter_icon}\" width=\"15\"/>" f"Interrupted at: {inter}" ] for k, v in mechanism['settings'].items(): text_list.append(f"{k}: {v}") text = "<br/>".join(text_list) self.algorithm_label.setText(f"<html><p>{text}</p></html>") # Hardware information self.hardware_label.setText("\n".join([ f"{tag}: {mechanism['hardware_info'][tag]}" for tag in ('os', 'memory', 'cpu') ]))
def _slvs_solve( vpoints: Sequence[VPoint], inputs: Dict[Tuple[int, int], float] ) -> Tuple[List[Union[_Coord, Tuple[_Coord, _Coord]]], int]: """Use element module to convert into Solvespace expression.""" if not vpoints: return [], 0 # VLinks with strings vlinks = {} for vlink in get_vlinks(vpoints): vlinks[vlink.name] = vlink # Solvespace kernel sys = PySolver() sys.set_group(1) wp = sys.create_2d_base() origin_2d = sys.add_point_2d(0., 0., wp) sys.dragged(origin_2d, wp) origin_2d_p = sys.add_point_2d(10., 0., wp) sys.dragged(origin_2d_p, wp) hv = sys.add_line_2d(origin_2d, origin_2d_p, wp) sys.set_group(2) points: List[Entity] = [] sliders: Dict[int, int] = {} slider_bases: List[Entity] = [] slider_slots: List[Entity] = [] for i, vpoint in enumerate(vpoints): if vpoint.no_link(): x, y = vpoint.c[0] point = sys.add_point_2d(x, y, wp) sys.dragged(point, wp) points.append(point) continue if vpoint.grounded(): x, y = vpoint.c[0] if vpoint.type in {VJoint.P, VJoint.RP}: sliders[i] = len(slider_bases) # Base point (slot) is fixed point = sys.add_point_2d(x, y, wp) sys.dragged(point, wp) slider_bases.append(point) # Slot point (slot) is movable x += cos(vpoint.angle) y += sin(vpoint.angle) slider_slots.append(sys.add_point_2d(x, y, wp)) # Pin is movable x, y = _offset(vpoint) points.append(sys.add_point_2d(x, y, wp)) else: point = sys.add_point_2d(x, y, wp) sys.dragged(point, wp) points.append(point) continue x, y = vpoint.c[0] point = sys.add_point_2d(x, y, wp) if vpoint.type in {VJoint.P, VJoint.RP}: sliders[i] = len(slider_bases) # Base point (slot) is movable slider_bases.append(point) # Slot point (slot) is movable x += cos(vpoint.angle) y += sin(vpoint.angle) slider_slots.append(sys.add_point_2d(x, y, wp)) if vpoint.pin_grounded(): # Pin is fixed x, y = vpoint.c[1] point = sys.add_point_2d(x, y, wp) sys.dragged(point, wp) points.append(point) else: # Pin is movable x, y = _offset(vpoint) points.append(sys.add_point_2d(x, y, wp)) continue # Point is movable points.append(point) def pick_slider(i1: int, i2: int) -> Tuple[Entity, Entity, float]: n1 = vpoints[i1] n2 = vpoints[i2] if n1.is_slot_link(vlink.name): e1 = slider_bases[sliders[i1]] else: e1 = points[i1] if n2.is_slot_link(vlink.name): e2 = slider_bases[sliders[i2]] else: e2 = points[i2] return e1, e2, n1.distance(n2) for vlink in vlinks.values(): if len(vlink.points) < 2: continue if vlink.name == VLink.FRAME: continue a = vlink.points[0] b = vlink.points[1] p1, p2, d = pick_slider(a, b) sys.distance(p1, p2, d, wp) for c in vlink.points[2:]: for d in (a, b): p1, p2, x = pick_slider(c, d) sys.distance(p1, p2, x, wp) def get_friend(group: VLink, base: int) -> Optional[Tuple[VPoint, Entity]]: """Get friend for slider.""" # A base link friend index = group.points[0] if index == base: if len(group.points) < 2: # If no any friend return None index = group.points[1] vp = vpoints[index] if vp.is_slot_link(group.name): # c is a slider, and it is be connected with slot link. return vp, slider_bases[sliders[index]] else: # c is a R joint or it is not connected with slot link. return vp, points[index] for a, b in sliders.items(): # Base point vp1 = vpoints[a] p1 = points[a] # Base slot slider_slot = sys.add_line_2d(slider_bases[b], slider_slots[b], wp) if vp1.grounded(): # Slot is grounded sys.angle(hv, slider_slot, vp1.angle, wp) sys.coincident(p1, slider_slot, wp) if vp1.has_offset(): p2 = slider_bases[b] if vp1.offset(): sys.distance(p2, p1, vp1.offset(), wp) else: sys.coincident(p2, p1, wp) else: # Slider between links for name in vp1.links[:1]: vlink = vlinks[name] ret = get_friend(vlink, a) if ret is None: continue vp2, p2 = ret sys.angle(slider_slot, sys.add_line_2d(slider_bases[b], p2, wp), vp1.slope_angle(vp2) - vp1.angle, wp) sys.coincident(p1, slider_slot, wp) if vp1.has_offset(): p2 = slider_bases[b] if vp1.offset(): sys.distance(p2, p1, vp1.offset(), wp) else: sys.coincident(p2, p1, wp) if vp1.type != VJoint.P: continue for name in vp1.links[1:]: vlink = vlinks[name] ret = get_friend(vlink, a) if ret is None: continue vp2, p2 = ret sys.angle(slider_slot, sys.add_line_2d(p1, p2, wp), vp1.slope_angle(vp2) - vp1.angle, wp) for (b, d), angle in inputs.items(): # The constraints of drive shaft # Simulate the input variables to the mechanism # The 'base points' are shaft center if b == d: continue vp1 = vpoints[b] if vp1.type == VJoint.R: p1 = points[b] else: p1 = slider_bases[sliders[b]] angle = radians(angle) p2 = sys.add_point_2d(vp1.cx + cos(angle), vp1.cy + sin(angle), wp) sys.dragged(p2, wp) # The virtual link that dragged by "hand". leader = sys.add_line_2d(p1, p2, wp) # Make another virtual link that should follow "hand". vp2 = vpoints[d] if vp2.type == VJoint.R: p2 = points[d] else: p2 = slider_bases[sliders[d]] link = sys.add_line_2d(p1, p2, wp) sys.angle(link, leader, 0.5, wp) # Solve result_flag = sys.solve() if result_flag == ResultFlag.OKAY: result_list: List[Union[_Coord, Tuple[_Coord, _Coord]]] = [] for i, vpoint in enumerate(vpoints): p1 = points[i] x1, y1 = sys.params(p1.params) if vpoint.type == VJoint.R: result_list.append((x1, y1)) else: p2 = slider_bases[sliders[i]] x2, y2 = sys.params(p2.params) result_list.append(((x2, y2), (x1, y1))) return result_list, sys.dof() if result_flag == ResultFlag.INCONSISTENT: error = "inconsistent" elif result_flag == ResultFlag.DIDNT_CONVERGE: error = "didn't converge" else: error = "too many unknowns" raise ValueError(f"{error}: {sys.failures()}\n{sys.constraints()}")