Exemple #1
0
 def test_equals(self):
     """Segments should be equalitive"""
     self.assertEqual(Move(10, 10), Move(10, 10))
     self.assertEqual(Line(10, 10), Line(10, 10))
     self.assertEqual(line(10, 10), line(10, 10))
     self.assertNotEqual(line(10, 10), Line(10, 10))
     self.assertEqual(Horz(10), Line(10, 0))
     self.assertEqual(Vert(10), Line(0, 10))
     self.assertNotEqual(Vert(10), Horz(10))
Exemple #2
0
    def test_to_curves(self):
        """Segments can become curves"""
        self.assertRaises(ValueError, Move(0, 0).to_curve, None)
        self.assertEqual(
            Line(10, 10).to_curve(Vector2d(10, 5)), (10, 5, 10, 10, 10, 10))
        self.assertEqual(
            Horz(10).to_curve(Vector2d(10, 5)), (10, 5, 10, 5, 10, 5))
        self.assertEqual(
            Vert(10).to_curve(Vector2d(5, 10)), (5, 10, 5, 10, 5, 10))
        self.assertEqual(
            Curve(5, 5, 10, 10, 4, 4).to_curve(Vector2d(0, 0)),
            (5, 5, 10, 10, 4, 4))

        self.assertEqual(
            Smooth(10, 10, 4, 4).to_curve(Vector2d(4, 4), Vector2d(10, 10)),
            (-2, -2, 10, 10, 4, 4),
        )

        self.assertAlmostTuple(
            Quadratic(10, 10, 4, 4).to_curve(Vector2d(0, 0)).args,
            (6.666666666666666, 6.666666666666666, 8, 8, 4, 4),
        )

        self.assertAlmostTuple(
            TepidQuadratic(4, 4).to_curve(Vector2d(14, 19), Vector2d(11,
                                                                     12)).args,
            #            (20.666666666666664, 30, 17.333333333333332, 25, 4, 4),
            (15.999999999999998, 23.666666666666664, 12.666666666666666,
             18.666666666666664, 4, 4),
        )

        curves = list(Arc(50, 50, 0, 0, 1, 85, 85).to_curves(Vector2d(0, 0)))
        self.assertEqual(len(curves), 3)
        self.assertAlmostTuple(
            curves[0].args,
            (19.77590700610636, -5.4865851247611115, 38.18634924829132,
             -10.4196482558544, 55.44095225512604, -5.796291314453416))
        self.assertAlmostTuple(
            curves[1].args,
            (72.69555526196076, -1.172934373052433, 86.17293437305243,
             12.30444473803924, 90.79629131445341, 29.559047744873958))
        self.assertAlmostTuple(
            curves[2].args,
            (95.41964825585441, 46.81365075170867, 90.4865851247611,
             65.22409299389365, 77.85533905932738, 77.85533905932738))

        def apply_to_curve(obj):
            obj.to_curve(Vector2d())

        def apply_to_curves(obj):
            obj.to_curve(Vector2d())

        self.assertRaises(ValueError, apply_to_curve, ZoneClose())
        self.assertRaises(ValueError, apply_to_curves, zoneClose())

        self.assertRaises(ValueError, apply_to_curve, Move(0, 0))
        self.assertRaises(ValueError, apply_to_curves, move(0, 0))
Exemple #3
0
def draw_line(x1, y1, x2, y2, width, name, parent):
    elem = parent.add(inkex.PathElement())
    elem.style = {
        'stroke': '#000000',
        'stroke-width': str(width),
        'fill': 'none'
    }
    elem.set('inkscape:label', name)
    elem.path = [Move(x1, y1), Line(x2, y2)]
Exemple #4
0
 def plot_beam(beam: List[Tuple[Ray, float]],
               node: inkex.ShapeElement) -> None:
     path = inkex.Path()
     if len(beam) > 0:
         path += [Move(beam[0][0].origin[0], beam[0][0].origin[1])]
         for ray, t in beam:
             p1 = ray.origin + t * ray.direction
             path += [Line(p1[0], p1[1])]
     element = node.getparent().add(inkex.PathElement())
     element.style = node.get("style")
     element.path = path.transform(-node.composed_transform())
 def plot_beam(self, beam: List[Ray], node: inkex.ShapeElement) -> None:
     path = inkex.Path()
     if len(beam) > 0:
         path += [Move(beam[0].origin[0], beam[0].origin[1])]
         for ray in beam:
             p1 = ray.origin + ray.travel * ray.direction
             path += [Line(p1[0], p1[1])]
     element = self._beam_layer.add(inkex.PathElement())
     # Need to convert to path to get the correct style for inkex.Use
     element.style = node.to_path_element().style
     element.path = path
Exemple #6
0
 def plot_beam(self, beam: List[Tuple[Ray, float]],
               node: inkex.ShapeElement) -> None:
     path = inkex.Path()
     if len(beam) > 0:
         path += [Move(beam[0][0].origin[0], beam[0][0].origin[1])]
         for ray, t in beam:
             p1 = ray.origin + t * ray.direction
             path += [Line(p1[0], p1[1])]
     svg = self.document.getroot()
     element = svg.add(inkex.PathElement())
     element.style = node.get("style")
     element.path = path
Exemple #7
0
    def effect(self):
        for node in self.svg.selection.filter(inkex.PathElement):
            result = Path()
            prev = Vector2d()
            start = None
            for seg in node.path.to_absolute():
                if start is None:
                    start = seg.end_point(start, prev)
                if isinstance(seg, Curve):
                    result += [
                        Move(seg.x2, seg.y2),
                        Line(prev.x, prev.y),
                        Move(seg.x3, seg.y3),
                        Line(seg.x4, seg.y4),
                    ]
                elif isinstance(seg, Quadratic):
                    result += [
                        Move(seg.x2, seg.y2),
                        Line(prev.x, prev.y),
                        Move(seg.x2, seg.y2),
                        Line(seg.x3, seg.y3)
                    ]
                prev = seg.end_point(start, prev)

            if not result:
                continue

            elem = node.getparent().add(inkex.PathElement())
            elem.path = result.transform(node.transform)
            elem.style = {
                'stroke-linejoin': 'miter',
                'stroke-width': '1.0px',
                'stroke-opacity': '1.0',
                'fill-opacity': '1.0',
                'stroke': '#000000',
                'stroke-linecap': 'butt',
                'fill': 'none'
            }
Exemple #8
0
def draw_poly(pts, face, st, name, parent):
    """Draw polygone"""
    style = {'stroke': '#000000', 'stroke-width': str(st.th), 'stroke-linejoin': st.linejoin,
             'stroke-opacity': st.s_opac, 'fill': st.fill, 'fill-opacity': st.f_opac}
    path = inkex.Path()
    for facet in face:
        if not path:  # for first point
            path.append(Move(pts[facet - 1][0], -pts[facet - 1][1]))
        else:
            path.append(Line(pts[facet - 1][0], -pts[facet - 1][1]))
    path.close()

    poly = parent.add(inkex.PathElement())
    poly.label = name
    poly.style = style
    poly.path = path
Exemple #9
0
    def effect(self):
        for node in self.svg.selection.filter(inkex.PathElement):
            path = node.path.to_absolute()
            result = []
            for cmd_proxy in path.proxy_iterator(
            ):  # type: inkex.Path.PathCommandProxy
                prev = cmd_proxy.previous_end_point
                end = cmd_proxy.end_point
                if cmd_proxy.letter == 'M':
                    result.append(Move(*cmd_proxy.args))
                else:
                    for seg in self.fractalize((prev.x, prev.y, end.x, end.y),
                                               self.options.subdivs,
                                               self.options.smooth):
                        result.append(Line(*seg))
                    result.append(Line(end.x, end.y))

            node.path = result
Exemple #10
0
    def test_random_path_1(self):
        import random

        from inkex.paths import Line, Vert, Horz, Curve, Move, Arc, Quadratic, TepidQuadratic, Smooth, ZoneClose

        klasses = (Line, Vert, Horz, Curve, Move, Quadratic)  # , ZoneClose, Arc

        def random_segment(klass):
            args = [random.randint(1, 100) for _ in range(klass.nargs)]
            if klass is Arc:
                args[2] = 0  # random.randint(0, 1)
                args[3] = 0  # random.randint(0, 1)
                args[4] = 0  # random.randint(0, 1)
            return klass(*args)

        random.seed(2128506)
        # random.seed(datetime.now())
        n_trials = 10
        n_elements = 15

        for i in range(n_trials):
            path = Path()
            path.append(Move(0, 0))

            for j in range(n_elements):
                k = random.choice(klasses)
                path.append(random_segment(k))
                if k is Curve:
                    while random.randint(0, 1) == 1:
                        path.append(random_segment(Smooth))
                if k is Quadratic:
                    while random.randint(0, 1) == 1:
                        path.append(random_segment(TepidQuadratic))

            pe = PathElement()
            pe.path = path
            ibb = self.get_inkscape_bounding_box(pe)

            self.assert_bounding_box_is_equal(pe, *ibb, disable_inkscape_check=True)
Exemple #11
0
    def process_segment(cmd_proxy, facegroup, delx, dely):
        """Process each segments"""

        segments = []
        if isinstance(cmd_proxy.command,
                      (Curve, Smooth, TepidQuadratic, Quadratic, Arc)):
            prev = cmd_proxy.previous_end_point
            for curve in cmd_proxy.to_curves():
                bez = [prev] + curve.to_bez()
                prev = curve.end_point(cmd_proxy.first_point, prev)
                tees = [
                    t for t in beziertatslope(bez, (dely, delx)) if 0 < t < 1
                ]
                tees.sort()
                if len(tees) == 1:
                    one, two = beziersplitatt(bez, tees[0])
                    segments.append(Curve(*(one[1] + one[2] + one[3])))
                    segments.append(Curve(*(two[1] + two[2] + two[3])))
                elif len(tees) == 2:
                    one, two = beziersplitatt(bez, tees[0])
                    two, three = beziersplitatt(two, tees[1])
                    segments.append(Curve(*(one[1] + one[2] + one[3])))
                    segments.append(Curve(*(two[1] + two[2] + two[3])))
                    segments.append(Curve(*(three[1] + three[2] + three[3])))
                else:
                    segments.append(curve)
        elif isinstance(cmd_proxy.command, (Line, Curve)):
            segments.append(cmd_proxy.command)
        elif isinstance(cmd_proxy.command, ZoneClose):
            segments.append(
                Line(cmd_proxy.first_point.x, cmd_proxy.first_point.y))
        elif isinstance(cmd_proxy.command, (Vert, Horz)):
            segments.append(cmd_proxy.command.to_line(cmd_proxy.end_point))

        for seg in Path([Move(*cmd_proxy.previous_end_point)] +
                        segments).proxy_iterator():
            if isinstance(seg.command, Move): continue
            Motion.makeface(seg.previous_end_point, seg.command, facegroup,
                            delx, dely)
Exemple #12
0
    def makeface(last, segment, facegroup, delx, dely):
        """translate path segment along vector"""
        elem = facegroup.add(inkex.PathElement())

        npt = segment.translate([delx, dely])

        # reverse direction of path segment
        if isinstance(segment, Curve):
            rev = Curve(npt.x3, npt.y3, npt.x2, npt.y2, last[0] + delx,
                        last[1] + dely)
        elif isinstance(segment, Line):
            rev = Line(last[0] + delx, last[1] + dely)
        else:
            raise RuntimeError("Unexpected segment type {}".format(
                type(segment)))

        elem.path = inkex.Path([
            Move(last[0], last[1]),
            segment,
            npt.to_line(Vector2d()),
            rev,
            ZoneClose(),
        ])
 def test_new_path(self):
     """Test new path element"""
     path = PathElement.new(path=[Move(10, 10), Line(20, 20)])
     self.assertEqual(path.get('d'), 'M 10 10 L 20 20')
Exemple #14
0
    def effect(self):
        path_num = 0
        elems = []
        npaths = []
        sstr = None
        for selem in self.svg.selection.filter(PathElement):
            elems.append(copy.deepcopy(selem))
        if len(elems) == 0:
            raise inkex.AbortExtension("Nothing selected")
        for elem in elems:
            escale = 1.0
            npaths.clear()
            #inkex.utils.debug(elem.attrib)
            if 'style' in elem.attrib:
                sstr = elem.attrib['style']
            if 'transform' in elem.attrib:
                transforms = elem.attrib['transform'].split()
                for tf in transforms:
                    if tf.startswith('scale'):
                        escale = float(tf.split('(')[1].split(')')[0])
                if sstr != None:
                    lsstr = sstr.split(';')
                    for stoken in range(len(lsstr)):
                        if lsstr[stoken].startswith('stroke-width'):
                            swt = lsstr[stoken].split(':')[1]
                            if not swt[2:].isalpha(
                            ):  # is value expressed in units (e.g. px)?
                                swf = str(float(swt) * escale)  # no. scale it
                                lsstr[stoken] = lsstr[stoken].replace(swt, swf)
                        if lsstr[stoken].startswith('stroke-miterlimit'):
                            swt = lsstr[stoken].split(':')[1]
                            if not swt[2:].isalpha(
                            ):  # is value expressed in units (e.g. px)?
                                swf = str(float(swt) * escale)  # no. scale it
                                lsstr[stoken] = lsstr[stoken].replace(swt, swf)
                    sstr = ";".join(lsstr)
                else:
                    sstr = None
                elem.apply_transform()
            xbound, ybound = elem.bounding_box()  # Get bounds of this element
            xmin, xmax = xbound
            ymin, ymax = ybound
            ntotal = len(elem.path)
            nodecnt = 0
            startx = 0
            starty = ymax + 10 * escale
            endx = 0
            endy = starty
            xoffset = 0
            orig_sx = 0
            orig_sy = 0
            orig_ex = 0
            orig_ey = 0
            sx1 = 0
            sy1 = 0
            orig_length = 0
            prev = Vector2d()
            for ptoken in elem.path.to_absolute(
            ):  # For each point in the path
                startx = xmin + xoffset
                if ptoken.letter == 'M':  # Starting a new line
                    orig_sx = ptoken.x
                    orig_sy = ptoken.y
                    cd = Path()
                    cd.append(Move(startx, starty))
                    sx1 = orig_sx
                    sy1 = orig_sy
                else:
                    if last_letter != 'M':
                        orig_sx = orig_ex
                        orig_sy = orig_ey

                    if ptoken.letter == 'L':
                        orig_ex = ptoken.x
                        orig_ey = ptoken.y
                        orig_length = math.sqrt((orig_sx - orig_ex)**2 +
                                                (orig_sy - orig_ey)**2)
                        endx = startx + orig_length
                        cd.append(Line(endx, endy))
                    elif ptoken.letter == 'H':
                        orig_ey = ptoken.to_line(prev).y
                        orig_length = abs(orig_sx - ptoken.x)
                        orig_ex = ptoken.x
                        endx = startx + orig_length
                        cd.append(Line(endx, endy))
                    elif ptoken.letter == 'V':
                        orig_ex = ptoken.to_line(prev).x
                        orig_length = abs(orig_sy - ptoken.y)
                        orig_ey = ptoken.y
                        endx = startx + orig_length
                        cd.append(Line(endx, endy))
                    elif ptoken.letter == 'Z':
                        orig_ex = sx1
                        orig_ey = sy1
                        orig_length = math.sqrt((orig_sx - orig_ex)**2 +
                                                (orig_sy - orig_ey)**2)
                        endx = startx + orig_length
                        cd.append(Line(endx, endy))
                    elif ptoken.letter == 'C':
                        bez = ptoken.to_bez()  # [[x0,y0][x1,y1][x2,y2][x3,y3]]
                        bez.insert(0, [prev.x, prev.y])
                        orig_ex = bez[3][0]  #x3
                        orig_ey = bez[3][1]  #y3
                        orig_length = inkex.bezier.bezierlength(bez)
                        endx = startx + orig_length
                        cd.append(Line(endx, endy))
                    else:
                        raise inkex.AbortExtension(
                            "Unknown letter - {0}".format(ptoken.letter))
                nodecnt = nodecnt + 1
                if ptoken.letter != 'M':
                    if nodecnt == ntotal:
                        self.drawline(str(cd), "hline{0}".format(path_num),
                                      self.svg.get_current_layer(), sstr)
                        path_num = path_num + 1
                    xoffset = xoffset + orig_length
                    prev.x = orig_ex
                    prev.y = orig_ey
                else:
                    prev.x = orig_sx
                    prev.y = orig_sy
                last_letter = ptoken.letter
Exemple #15
0
    def render_stbar(self, keys, values):
        """Draw stacked bar chart"""

        # Iterate over all values to draw the different slices
        color = 0

        # create value sum in order to divide the bars
        try:
            valuesum = sum(values)
        except ValueError:
            valuesum = 0.0

        # Init offset
        offset = 0
        width = self.options.bar_width
        height = self.options.bar_height
        x = self.width / 2
        y = self.height / 2

        if self.blur:
            if self.options.rotate:
                width, height = height, width
                shy = y
            else:
                shy = str(y - self.options.bar_height)

            # Create rectangle element
            shadow = Rectangle(
                x=str(x),
                y=str(shy),
                width=str(width),
                height=str(height),
            )

            # Set shadow blur (connect to filter object in xml path)
            shadow.style = self.blur
            yield shadow

        # Draw Single bars
        for cnt, value in enumerate(values):

            # Calculate the individual heights normalized on 100units
            normedvalue = (self.options.bar_height / valuesum) * float(value)

            # Create rectangle element
            rect = Rectangle()

            # Set chart position to center of document.
            if not self.options.rotate:
                rect.set('x', str(self.width / 2))
                rect.set('y', str(self.height / 2 - offset - normedvalue))
                rect.set("width", str(self.options.bar_width))
                rect.set("height", str(normedvalue))
            else:
                rect.set('x', str(self.width / 2 + offset))
                rect.set('y', str(self.height / 2))
                rect.set("height", str(self.options.bar_width))
                rect.set("width", str(normedvalue))

            rect.set("style", "fill:" + self.get_color())

            # If text is given, draw short paths and add the text
            # TODO: apply overlap workaround for visible gaps in between
            if keys:
                if not self.options.rotate:
                    x1 = (self.width + self.options.bar_width) / 2
                    y1 = y - offset - (normedvalue / 2)
                    x2 = self.options.bar_width / 2 + self.options.text_offset
                    y2 = 0
                    txt = self.width / 2 + self.options.bar_width + self.options.text_offset + 1
                    tyt = y - offset + self.fontoff - (normedvalue / 2)
                else:
                    x1 = x + offset + normedvalue / 2
                    y1 = y + self.options.bar_width / 2
                    x2 = 0
                    y2 = self.options.bar_width / 2 + (self.options.font_size \
                            * cnt) + self.options.text_offset
                    txt = x + offset + normedvalue / 2 - self.fontoff
                    tyt = (y) + self.options.bar_width + (self.options.font_size \
                            * (cnt + 1)) + self.options.text_offset

                elem = inkex.PathElement()
                elem.path = [Move(x1, y1), line(x2, y2)]
                elem.style = {
                    'fill': 'none',
                    'stroke': self.options.font_color,
                    'stroke-width': self.options.stroke_width,
                    'stroke-linecap': 'butt',
                }
                yield elem
                yield self.draw_text(keys[cnt], x=str(txt), y=str(tyt))

            # Increase Offset and Color
            offset = offset + normedvalue
            color = (color + 1) % 8

            # Draw rectangle
            yield rect

        yield self.draw_header(self.width / 2 + offset + normedvalue)
Exemple #16
0
    def render_pie(self, keys, values, pie_abs=False):
        """Draw pie chart"""
        pie_radius = self.options.pie_radius

        # Iterate all values to draw the different slices
        color = 0
        x = float(self.width) / 2
        y = float(self.height) / 2

        # Create the shadow first (if it should be created):
        if self.blur:
            shadow = Circle(cx=str(x), cy=str(y))
            shadow.set('r', str(pie_radius))
            shadow.style = self.blur + inkex.Style(fill='#000000')
            yield shadow

        # Add a grey background circle with a light stroke
        background = Circle(cx=str(x), cy=str(y))
        background.set("r", str(pie_radius))
        background.set("style", "stroke:#ececec;fill:#f9f9f9")
        yield background

        # create value sum in order to divide the slices
        try:
            valuesum = sum(values)
        except ValueError:
            valuesum = 0

        if pie_abs:
            valuesum = 100

        # Set an offsetangle
        offset = 0

        # Draw single slices
        for cnt, value in enumerate(values):
            # Calculate the PI-angles for start and end
            angle = (2 * 3.141592) / valuesum * float(value)
            start = offset
            end = offset + angle

            # proper overlapping
            if self.options.segment_overlap:
                if cnt != len(values) - 1:
                    end += 0.09  # add a 5° overlap
                if cnt == 0:
                    start -= 0.09  # let the first element overlap into the other direction

            # then add the slice
            pieslice = inkex.PathElement()
            pieslice.set('sodipodi:type', 'arc')
            pieslice.set('sodipodi:cx', x)
            pieslice.set('sodipodi:cy', y)
            pieslice.set('sodipodi:rx', pie_radius)
            pieslice.set('sodipodi:ry', pie_radius)
            pieslice.set('sodipodi:start', start)
            pieslice.set('sodipodi:end', end)
            pieslice.set(
                "style",
                "fill:" + self.get_color() + ";stroke:none;fill-opacity:1")
            ang = angle / 2 + offset

            # If text is given, draw short paths and add the text
            if keys:
                elem = inkex.PathElement()
                elem.path = [
                    Move(
                        (self.width / 2) + pie_radius * math.cos(ang),
                        (self.height / 2) + pie_radius * math.sin(ang),
                    ),
                    line(
                        (self.options.text_offset - 2) * math.cos(ang),
                        (self.options.text_offset - 2) * math.sin(ang),
                    ),
                ]

                elem.style = {
                    'fill': 'none',
                    'stroke': self.options.font_color,
                    'stroke-width': self.options.stroke_width,
                    'stroke-linecap': 'butt',
                }
                yield elem

                label = keys[cnt]
                if self.options.show_values:
                    label += ' ({}{})'.format(str(value), ('', '%')[pie_abs])

                # check if it is right or left of the Pie
                anchor = 'start' if math.cos(ang) > 0 else 'end'
                text = self.draw_text(label, anchor=anchor)

                off = pie_radius + self.options.text_offset
                text.set("x", (self.width / 2) + off * math.cos(ang))
                text.set("y", (self.height / 2) + off * math.sin(ang) +
                         self.fontoff)
                yield text

            # increase the rotation-offset and the colorcycle-position
            offset = offset + angle
            color = (color + 1) % 8

            # append the objects to the extension-layer
            yield pieslice

        yield self.draw_header(self.width / 2 - pie_radius)