Esempio n. 1
0
 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}]'
     )
Esempio n. 4
0
 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()
Esempio n. 5
0
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)
Esempio n. 6
0
            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)
Esempio n. 7
0
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)