Пример #1
0
    def add_continuity_correction(self, offset_path, blade_path, point):
        """ Adds if the upcoming angle and previous angle are not the same
        we need to correct for that difference by "arcing back" about the
        current blade point with a radius equal to the offset.

        """
        # Current blade position
        cur = blade_path.currentPosition()

        # Determine direction of next move
        sp = QPainterPath()
        sp.moveTo(cur)
        sp.lineTo(point)
        next_angle = sp.angleAtPercent(1)

        # Direction of last move
        angle = blade_path.angleAtPercent(1)

        # If not continuous it needs corrected with an arc
        if isnan(angle) or isnan(next_angle):
            return
        if abs(angle - next_angle) > self.config.cutoff:
            r = self.config.offset
            a = radians(next_angle)
            dx, dy = r*cos(a), -r*sin(a)
            po = QPointF(cur.x()+dx, cur.y()+dy)

            c = offset_path.currentPosition()
            dx, dy = po.x()-cur.x()+c.x()-cur.x(), po.y()-cur.y()+c.y()-cur.y()
            c1 = QPointF(cur.x()+dx, cur.y()+dy)
            offset_path.quadTo(c1, po)
Пример #2
0
    def apply_blade_offset(self, path, job):
        """ Apply blade offset to the given path.

        """
        params = []
        e = None
        cmd = None
        qf = getattr(job.config, 'quality_factor', 1)

        # Holds the blade path
        blade_path = QPainterPath()
        offset_path = QPainterPath()

        for i in range(path.elementCount()):
            e = path.elementAt(i)

            # Finish the previous curve (if there was one)
            if cmd == CurveToElement and e.type != CurveToDataElement:
                n = len(params)
                if n == 2:
                    self.process_quad(offset_path, blade_path, params, qf)
                elif n == 3:
                    self.process_cubic(offset_path, blade_path, params, qf)
                else:
                    raise ValueError("Unexpected curve data length %s" % n)
                params = []

            # Reconstruct the path
            if e.type == MoveToElement:
                cmd = MoveToElement
                self.process_move(offset_path, blade_path, [QPointF(e.x, e.y)])
            elif e.type == LineToElement:
                cmd = LineToElement
                self.process_line(offset_path, blade_path, [QPointF(e.x, e.y)])
            elif e.type == CurveToElement:
                cmd = CurveToElement
                params = [QPointF(e.x, e.y)]
            elif e.type == CurveToDataElement:
                params.append(QPointF(e.x, e.y))

        # Finish the previous curve (if there was one)
        if params and e.type != CurveToDataElement:
            n = len(params)
            if n == 2:
                self.process_quad(offset_path, blade_path, params, qf)
            elif n == 3:
                self.process_cubic(offset_path, blade_path, params, qf)
        return offset_path
Пример #3
0
def split_painter_path(path):
    """ Split a QPainterPath into subpaths. """
    if not isinstance(path, QPainterPath):
        raise TypeError("path must be a QPainterPath, got: {}".format(path))

    # Element types
    MoveToElement = QPainterPath.MoveToElement
    LineToElement = QPainterPath.LineToElement
    CurveToElement = QPainterPath.CurveToElement
    CurveToDataElement = QPainterPath.CurveToDataElement

    subpaths = []
    params = []
    e = None

    def finish_curve(p, params):
        if len(params) == 2:
            p.quadTo(*params)
        elif len(params) == 3:
            p.cubicTo(*params)
        else:
            raise ValueError("Invalid curve parameters: {}".format(params))

    for i in range(path.elementCount()):
        e = path.elementAt(i)

        # Finish the previous curve (if there was one)
        if params and e.type != CurveToDataElement:
            finish_curve(p, params)
            params = []

        # Reconstruct the path
        if e.type == MoveToElement:
            p = QPainterPath()
            p.moveTo(e.x, e.y)
            subpaths.append(p)
        elif e.type == LineToElement:
            p.lineTo(e.x, e.y)
        elif e.type == CurveToElement:
            params = [QPointF(e.x, e.y)]
        elif e.type == CurveToDataElement:
            params.append(QPointF(e.x, e.y))

    # Finish the previous curve (if there was one)
    if params and e and e.type != CurveToDataElement:
        finish_curve(p, params)
    return subpaths
Пример #4
0
 def splitAtPercent(self, t):
     paths = []
     path = QPainterPath()
     i = 0
     while i < self.elementCount():
         e = self.elementAt(i)
         if e.type == ElementType.MoveToElement:
             if not path.isEmpty():
                 paths.append(path)
             path = QPainterPath(QPointF(e.x, e.y))
         elif e.type == ElementType.LineToElement:
             path.lineTo(QPointF(e.x, e.y))
         elif e.type == ElementType.CurveToElement:
             e1, e2 = self.elementAt(i + 1), self.elementAt(i + 2)
             path.cubicTo(QPointF(e.x, e.y), QPointF(e1.x, e1.y),
                          QPointF(e2.x, e2.y))
             i += 2
         else:
             raise ValueError("Invalid element type %s" % (e.type, ))
         i += 1
     if not path.isEmpty():
         paths.append(path)
     return paths
Пример #5
0
    def process_line(self, offset_path, blade_path, params):
        """ Correct continuity and adjust the end point of the line to end
        at the correct spot.
        """
        r = self.config.offset
        p0 = params[0]
        self.add_continuity_correction(offset_path, blade_path, p0)
        blade_path.lineTo(p0)  # Must be done after continuity correction!
        angle = blade_path.angleAtPercent(1)

        if isnan(angle):
            dx, dy = 0, r
        else:
            a = radians(angle)
            dx, dy = r*cos(a), -r*sin(a)

        po = QPointF(p0.x()+dx, p0.y()+dy)
        offset_path.lineTo(po)
Пример #6
0
    def process_move(self, offset_path, blade_path, params):
        """ Adjust the start point of a move by the blade offset. When just
        starting we don't know the blade angle so no correction is done.

        """
        r = self.config.offset
        p0 = params[0]

        # Get direction of last move
        blade_path.moveTo(p0)
        angle = blade_path.angleAtPercent(1)

        if isnan(angle):
            dx, dy = 0, r
        else:
            a = radians(angle)
            dx, dy = r*cos(a), -r*sin(a)

        po = QPointF(p0.x()+dx, p0.y()+dy)
        offset_path.moveTo(po)
Пример #7
0
 def get_items_in(self, top_left, bottom_right):
     qrect = QRectF(QPointF(top_left.x, top_left.y),
                    QPointF(bottom_right.x, bottom_right.y))
     items = self.scene.items(qrect)
     return [item.ref().declaration for item in items if item.ref()]
Пример #8
0
 def set_points(self, points):
     polygon = QPolygonF([QPointF(*p[:2]) for p in points])
     self.widget.setPolygon(polygon)
Пример #9
0
    def create(self, swap_xy=False, scale=None):
        """ Create a path model that is rotated and scaled

        """
        model = QPainterPath()

        if not self.path:
            return

        path = self._create_copy()

        # Update size
        bbox = path.boundingRect()
        self.size = [bbox.width(), bbox.height()]

        # Create copies
        c = 0
        points = self._copy_positions_iter(path)

        if self.auto_copies:
            self.stack_size = self._compute_stack_sizes(path)
            if self.stack_size[0]:
                copies_left = self.copies % self.stack_size[0]
                if copies_left:  # not a full stack
                    with self.events_suppressed():
                        self.copies = self._desired_copies
                        self.add_stack()

        while c < self.copies:
            x, y = next(points)
            model.addPath(path * QTransform.fromTranslate(x, -y))
            c += 1

        # Create weedline
        if self.plot_weedline:
            self._add_weedline(model, self.plot_weedline_padding)

        # Determine padding
        bbox = model.boundingRect()
        if self.align_center[0]:
            px = (self.material.width() - bbox.width()) / 2.0
        else:
            px = self.material.padding_left

        if self.align_center[1]:
            py = -(self.material.height() - bbox.height()) / 2.0
        else:
            py = -self.material.padding_bottom

        # Scale and rotate
        if scale:
            model *= QTransform.fromScale(*scale)
            px, py = px * abs(scale[0]), py * abs(scale[1])

        if swap_xy:
            t = QTransform()
            t.rotate(90)
            model *= t

        # Move to 0,0
        bbox = model.boundingRect()
        p = bbox.bottomLeft()
        tx, ty = -p.x(), -p.y()

        # If swapped, make sure padding is still correct
        if swap_xy:
            px, py = -py, -px
        tx += px
        ty += py

        model = model * QTransform.fromTranslate(tx, ty)

        end_point = (QPointF(0, -self.feed_after + model.boundingRect().top())
                     if self.feed_to_end else QPointF(0, 0))
        model.moveTo(end_point)

        return model
Пример #10
0
 def parse(self, e):
     c = QPointF(*map(self.parseUnit, (e.attrib.get('cx', 0),
                                       e.attrib.get('cy', 0))))
     r = self.parseUnit(e.attrib.get('r', 0))
     self.addEllipse(c, r, r)
Пример #11
0
 def parse(self, e):
     c = QPointF(*map(self.parseUnit, (e.attrib.get('cx', 0),
                                       e.attrib.get('cy', 0))))
     rx, ry = map(self.parseUnit,
                  (e.attrib.get('rx', 0), e.attrib.get('ry', 0)))
     self.addEllipse(c, rx, ry)
Пример #12
0
    def request_relayout(self):
        # y = 0.0

        # for child in self.children():
        #     if not isinstance(child, QtContainer):
        #         continue
        #     scene_proxy = self._proxies[child]
        #     width, height = child._layout_manager.best_size()
        #     scene_proxy.setPos(0.0, y)
        #     y += height + 25.0

        for p in self._edge_paths:
            self.scene.removeItem(p)
        self._edge_paths = []

        g = pygraphviz.AGraph(directed=True)
        g.graph_attr['nodesep'] = 100
        g.graph_attr['ranksep'] = 50
        g.node_attr['shape'] = 'rect'

        children_names = {child.declaration.name for child in self.children() if isinstance(child, QtContainer)}

        if any(from_ not in children_names or to not in children_names for (from_, to) in self.declaration.edges):
            # hasn't finished being set up yet
            return

        for child in self.children():
            if not isinstance(child, QtContainer):
                continue
            scene_proxy = self._proxy(child)
            width, height = child._layout_manager.best_size()
            scene_proxy.setGeometry(QRectF(0.0, 0.0, width, height))
            g.add_node(child.declaration.name, width=width, height=height)

        for from_, to in self.declaration.edges:
            g.add_edge(from_, to)

        before = time.time()
        g.layout(prog='dot')
        after = time.time()

        for child in self.children():
            if not isinstance(child, QtContainer):
                continue
            scene_proxy = self._proxies[child]
            node = g.get_node(child.declaration.name)
            center_x, center_y = (-float(v)/72.0 for v in node.attr['pos'].split(','))
            width, height = child._layout_manager.best_size()
            x = center_x - (width / 2.0)
            y = center_y - (height / 2.0)
            scene_proxy.setPos(x, y)

        for from_, to in self.declaration.edges:
            if from_ not in children_names or to not in children_names:
                continue
            edge = g.get_edge(from_, to)
            # TODO: look at below code
            all_points = [tuple(-float(v)/72.0 for v in t.strip('e,').split(',')) for t in edge.attr['pos'].split(' ')]
            arrow = all_points[0]
            start_point = all_points[1]

            painter = QPainterPath(QPointF(*start_point))
            for c1, c2, end in grouper(all_points[2:], 3):
                painter.cubicTo(QPointF(*c1), QPointF(*c2), QPointF(*end))

            self._edge_paths.append(self.scene.addPath(painter))

        self.show_selected()