def __draw_link(self, vlink: VLink) -> None: """Draw a link.""" if vlink.name == VLink.FRAME or not vlink.points: return points = self.__points_pos(vlink) pen = QPen() # Rearrange: Put the nearest point to the next position. qpoints = convex_hull(points, as_qpoint=True) if (self.select_mode == SelectMode.LINK and self.vlinks.index(vlink) in self.selections): pen.setWidth(self.link_width + 6) pen.setColor(Qt.black if self.monochrome else QColor(161, 16, 239)) self.painter.setPen(pen) self.painter.drawPolygon(*qpoints) pen.setWidth(self.link_width) pen.setColor(Qt.black if self.monochrome else QColor(*vlink.color)) self.painter.setPen(pen) brush = QColor(Qt.darkGray) if self.monochrome else LINK_COLOR brush.setAlphaF(self.transparency) self.painter.setBrush(brush) self.painter.drawPolygon(*qpoints) self.painter.setBrush(Qt.NoBrush) if not self.show_point_mark: return pen.setColor(Qt.darkGray) self.painter.setPen(pen) p_count = len(points) cen_x = sum(p[0] for p in points) / p_count cen_y = sum(p[1] for p in points) / p_count self.painter.drawText(QRectF(cen_x - 50, cen_y - 50, 100, 100), Qt.AlignCenter, f'[{vlink.name}]')
def catch(exprs: Sequence[str]) -> bool: """Detection function for solution polygons.""" points, _ = self.solution_polygon( exprs[0], exprs[1:-1], exprs[-1], self.vpoints ) if len(points) < 3: points = convex_hull( [(x + self.sr, y + self.sr) for x, y in points] + [(x - self.sr, y - self.sr) for x, y in points], as_qpoint=True ) else: points = [QPointF(x, y) for x, y in points] polygon = QPolygonF(points) if rect: return polygon.intersects( QPolygonF(self.selector.to_rect(self.zoom))) else: return polygon.containsPoint( QPointF(self.selector.x, -self.selector.y) * self.zoom, Qt.WindingFill )
def __draw_link(self, vlink: VLink) -> None: """Draw a link.""" if vlink.name == VLink.FRAME or not vlink.points: return pen = QPen() # Rearrange: Put the nearest point to the next position. qpoints = convex_hull( [(c.x * self.zoom, c.y * -self.zoom) for c in vlink.points_pos(self.vpoints)], as_qpoint=True) if ( self.select_mode == SelectMode.LINK and self.vlinks.index(vlink) in self.selections ): pen.setWidth(self.link_width + 6) pen.setColor(Qt.black if self.monochrome else QColor(161, 16, 239)) self.painter.setPen(pen) self.painter.drawPolygon(*qpoints) pen.setWidth(self.link_width) pen.setColor(Qt.black if self.monochrome else color_qt(vlink.color)) self.painter.setPen(pen) self.painter.drawPolygon(*qpoints) if not self.show_point_mark: return pen.setColor(Qt.darkGray) self.painter.setPen(pen) p_count = len(qpoints) cen_x = sum(p.x() for p in qpoints) / p_count cen_y = sum(p.y() for p in qpoints) / p_count self.painter.drawText( QRectF(cen_x - 50, cen_y - 50, 100, 100), Qt.AlignCenter, f'[{vlink.name}]' )
def paintEvent(self, event: QPaintEvent) -> None: """Drawing function.""" super(_DynamicCanvas, self).paintEvent(event) pen = QPen() pen.setWidth(self.link_width) brush = color_qt('dark-gray') if self.monochrome else LINK_COLOR self.painter.setBrush(brush) for vlink in self.vlinks: if vlink.name == VLink.FRAME or not vlink.points: continue points = [] for i in vlink.points: vpoint = self.vpoints[i] if (vpoint.type == VJoint.R or not vpoint.is_slot_link(vlink.name)): x, y = self.path.path[i][self.ind] else: x, y = self.path.slider_path[i][self.ind] points.append((x * self.zoom, y * -self.zoom)) qpoints = convex_hull(points, as_qpoint=True) pen.setColor( Qt.black if self.monochrome else color_qt(vlink.color)) self.painter.setPen(pen) self.painter.drawPolygon(*qpoints) self.painter.setBrush(Qt.NoBrush) pen.setWidth(self.path_width) for paths, vel, acc in [ (enumerate(self.path.path), self.vel, self.acc), (self.path.slider_path.items(), self.vel_slider, self.acc_slider), ]: for i, path in paths: vpoint = self.vpoints[i] if self.monochrome: color = color_qt('gray') else: color = color_qt(vpoint.color) pen.setColor(color) self.painter.setPen(pen) self.draw_curve(path) x, y = path[self.ind] zoom = 1. for vec, color in [(vel[i], Qt.blue), (acc[i], Qt.red)]: if self.ind >= len(vec): break vx, vy = vec[self.ind] if isnan(vx) or isnan(vy): break zoom /= self.factor r = hypot(vx, vy) * zoom if isclose(r, 0): break th = atan2(vy, vx) pen.setColor(color) self.painter.setPen(pen) self.draw_arrow(x, y, x + r * cos(th), y + r * sin(th)) self.draw_point(i, x, y, vpoint.grounded(), vpoint.color) self.painter.end()
def dxf_boundary(vpoints: Sequence[VPoint], radius: float, interval: float, version: str, file_name: str): """Create parts sketch in same file.""" vlinks: Dict[str, List[int]] = {} for i, vpoint in enumerate(vpoints): for link in vpoint.links: if link in vlinks: vlinks[link].append(i) else: vlinks[link] = [i] dwg = ezdxf.new(version) msp = dwg.modelspace() # _Interval: Offset with x axis. interval += radius * 2 x_max = -interval # Draw link boundaries for name in sorted(vlinks, key=lambda n: min(vpoints[p].cx for p in vlinks[n])): if name == VLink.FRAME: continue # Draw joint holes x_min = min(vpoints[p].cx for p in vlinks[name]) centers = [(x_max + interval + (vpoints[p].cx - x_min), vpoints[p].cy) for p in vlinks[name]] for coord in centers: msp.add_circle(coord, radius / 2) x_max = max(coord[0] for coord in centers) # Sort the centers centers_ch = convex_hull(centers) boundary = centers_ch.copy() for c in centers: if c not in centers_ch: centers_ch.append(c) centers = centers_ch # Draw boundary edges boundary = boundary_loop(boundary, radius) for c1, c2 in boundary: msp.add_line((c1.x, c1.y), (c2.x, c2.y)) # Draw fillets for i in range(len(boundary)): x, y = centers[i] c1 = boundary[i - 1][1] c2 = boundary[i][0] msp.add_arc(centers[i], radius, degrees(atan2(c1.y - y, c1.x - x)), degrees(atan2(c2.y - y, c2.x - x))) dwg.saveas(file_name)
def catch_l(link: VLink) -> bool: """Detection function for links. + Is polygon: Using Qt polygon geometry. + If just a line: Create a range for mouse detection. """ points = self.__points_pos(link) if len(points) > 2: polygon = QPolygonF(convex_hull(points, as_qpoint=True)) else: polygon = QPolygonF( convex_hull([(x + self.sr, y + self.sr) for x, y in points] + [(x - self.sr, y - self.sr) for x, y in points], as_qpoint=True)) if rect: return polygon.intersects( QPolygonF(self.selector.to_rect(self.zoom))) else: return polygon.containsPoint( QPointF(self.selector.x, -self.selector.y) * self.zoom, Qt.WindingFill)
def slvs2_part(vpoints: List[VPoint], radius: float, file_name: str) -> None: """Generate a linkage sketch by specified radius.""" # Translate min_x = min(vpoint.cx for vpoint in vpoints) min_y = min(vpoint.cy for vpoint in vpoints) centers = [(vpoint.cx - min_x, vpoint.cy - min_y) for vpoint in vpoints] # Synchronous the point coordinates after using convex hull centers_ch = convex_hull(centers) _boundary = centers_ch.copy() for x, y in centers: if (x, y) not in centers_ch: centers_ch.append((x, y)) centers = centers_ch del vpoints, min_x, min_y # Frame (p1, p2, p3) -> ((p1, p2), (p3, p1), (p3, p2)) frame: List[_CoordsPair] = [( Coord(centers[-2][0], centers[-2][1]), Coord(centers[-1][0], centers[-1][1]), )] for x, y in centers[2:]: frame.append((frame[0][0], Coord(x, y))) frame.append((frame[0][1], Coord(x, y))) # Boundary boundary = boundary_loop(_boundary, radius) del _boundary # Writer object writer = SlvsWriter2() writer.script_group.pop() writer.group_normal(0x3, "boundary") # Add "Param" def add_param(edges: Sequence[_CoordsPair]) -> None: """Add param by pair of coordinates.""" for edge in edges: writer.param_num += 0x10 for coord in edge: writer.param_val(writer.param_num, coord.x) writer.param_num += 1 writer.param_val(writer.param_num, coord.y) writer.param_num += 2 writer.param_shift16() def arc_coords( index: int, _cx: float, _cy: float ) -> Iterator[_Coord]: yield from ( (_cx, _cy), (boundary[index - 1][1].x, boundary[index - 1][1].y), (boundary[index][0].x, boundary[index][0].y), ) add_param(frame) add_param(boundary) # Circles for x, y in centers: writer.param_num += 0x10 writer.param_val(writer.param_num, x) writer.param_num += 1 writer.param_val(writer.param_num, y) # Shift to 0x40 writer.param_num += 0x2f writer.param_val(writer.param_num, radius / 2) writer.param_shift16() # Arc for i in range(len(boundary)): cx, cy = centers[i] writer.param_num += 0x10 for x, y in arc_coords(i, cx, cy): writer.param_val(writer.param_num, x) writer.param_num += 1 writer.param_val(writer.param_num, y) writer.param_num += 2 writer.param_shift16() # Group 2: point_count = len(centers) # The number of same points point_num: List[List[int]] = [[] for _ in range(point_count)] # The number of same lines line_num: List[List[int]] = [[] for _ in range(len(frame))] def segment_processing(edges: Sequence[_CoordsPair], *, is_frame: bool = True) -> None: """Add edges to work plane. (No any constraint.)""" # Add "Request" for _ in range(len(edges)): writer.request_line(writer.request_num) writer.request_num += 1 # Add "Entity" p_counter = _by_frame() if is_frame else _by_boundary(len(point_num)) for index, edge in enumerate(edges): writer.entity_line(writer.entity_num) for j, coord in enumerate(edge): writer.entity_num += 1 point_num[next(p_counter)].append(writer.entity_num) writer.entity_point_2d(writer.entity_num, coord.x, coord.y) line_num[index].append(writer.entity_num) writer.entity_shift16() segment_processing(frame, is_frame=True) center_num = [nums[0] for nums in point_num] # Add "Constraint" # Same point constraint for p in point_num: for p_ in p[1:]: writer.constraint_point(writer.constraint_num, p[0], p_) writer.constraint_num += 1 for i, (n1, n2) in enumerate(line_num): p1, p2 = frame[i] writer.constraint_distance(writer.constraint_num, n1, n2, p1.distance(p2)) writer.constraint_num += 1 # Add "Constraint" of position for i, c in enumerate(frame[0]): writer.constraint_grounded(writer.constraint_num, point_num[i][0], c.x, c.y) if i == 1: writer.script_constraint.pop() writer.constraint_num += 1 else: writer.constraint_num += 2 # Group 3: writer.set_group(0x3) # The number of same points point_num = [[] for _ in range(len(boundary))] # The number of same lines line_num = [[] for _ in range(len(boundary))] segment_processing(boundary) # The number of circles circles = [] def add_circle(index: int, _x: float, _y: float) -> None: """Add circle""" # Add "Request" writer.request_circle(writer.request_num) writer.request_num += 1 # Add "Entity" writer.entity_circle(writer.entity_num) circles.append(writer.entity_num) writer.entity_num += 1 writer.entity_point_2d(writer.entity_num, _x, _y) num = writer.entity_num # Shift to 0x20 writer.entity_num += 0x1f writer.entity_normal_2d(writer.entity_num, num) # Shift to 0x40 writer.entity_num += 0x20 writer.entity_distance(writer.entity_num, radius / 2) writer.entity_shift16() # Add "Constraint" for centers writer.constraint_point(writer.constraint_num, num, center_num[index]) writer.constraint_num += 1 # Add "Constraint" for diameter if index == 0: writer.constraint_diameter(writer.constraint_num, circles[-1], radius) else: writer.constraint_equal_radius(writer.constraint_num, circles[-1], circles[0]) writer.constraint_num += 1 def add_arc(index: int, _cx: float, _cy: float) -> None: """Add arc""" # Add "Request" writer.request_arc(writer.request_num) writer.request_num += 1 # Add "Entity" writer.entity_arc(writer.entity_num) circles.append(writer.entity_num) p3 = [] for ax, ay in arc_coords(index, _cx, _cy): writer.entity_num += 1 writer.entity_point_2d(writer.entity_num, ax, ay) p3.append(writer.entity_num) writer.entity_num += 0x3d writer.entity_normal_2d(writer.entity_num, p3[0]) writer.entity_shift16() # Add "Constraint" for three points num1 = point_num[index][0] num2 = point_num[index][1] if num1 % 16 < num2 % 16: num1, num2 = num2, num1 for j, num in enumerate([center_num[index], num1, num2]): writer.constraint_point(writer.constraint_num, p3[j], num) writer.constraint_num += 1 # Add "Constraint" for diameter if index == 0: writer.constraint_diameter(writer.constraint_num, circles[-1], radius * 2) else: writer.constraint_equal_radius(writer.constraint_num, circles[-1], circles[0]) writer.constraint_num += 1 # Add "Constraint" for become tangent line for j, num in enumerate((num1 - num1 % 16, num2 - num2 % 16)): writer.constraint_arc_line_tangent( writer.constraint_num, circles[-1], num, reverse=(j == 1) ) writer.constraint_num += 1 for i, (x, y) in enumerate(centers): add_circle(i, x, y) circles.clear() for i in range(len(boundary)): x, y = centers[i] add_arc(i, x, y) # Write file writer.save(file_name)