Exemplo n.º 1
0
    def test_group_with_number_of_rects_translated(self):

        group = Group()

        dx, dy = 5, 10

        xmin, ymin = 1000, 1000
        xmax, ymax = -1000, -1000
        rects = []

        for x, y, w, h in [
            (10, 20, 5, 7),
            (30, 40, 5, 7),
        ]:
            rect = Rectangle(width=str(w), height=str(h), x=str(x), y=str(y))
            rects.append(rect)

            xmin = min(xmin, x)
            xmax = max(xmax, x + w)
            ymin = min(ymin, y)
            ymax = max(ymax, y + h)

            group.add(rect)

        group.transform = Transform(translate=(dx, dy))

        self.assert_bounding_box_is_equal(group, (dx + xmin,
                                                  dx + xmax),
                                          (dy + ymin,
                                           dy + ymax))
Exemplo n.º 2
0
    def test_group_with_regular_rect(self):
        group = Group()
        x, y = 10, 20
        w, h = 7, 20

        rect = Rectangle(width=str(w), height=str(h), x=str(x), y=str(y))

        group.add(rect)

        self.assert_bounding_box_is_equal(group, (x, x + w),
                                          (y, y + h))
 def getNumbers( self, ticks: int, fmtstr: str, intan: float, inrad: float, style: Style ):
   "The numbers at the main-ticks. intan: shift perpendicular to axis, inrad: shift in axis."
   
   group = Group()
   if ticks <= 0:
     return group
   
   step = (self.max-self.min) / ticks
   for i in range(ticks+1):
     val = self.min + i * step
     point = self.transform(val)
     group.add( textAt( point.x-intan*self.sin+inrad*self.cos, point.y+intan*self.cos+inrad*self.sin, ("{0:"+fmtstr+"}").format(val), style) )
   return group
Exemplo n.º 4
0
    def effect(self):
        if len(self.svg.selected) != 2:
            errormsg(
                _("Please select exact two objects:\n1. object representing path,\n2. object representing dots."
                  ))
            return

        (iddot, dot) = self.svg.selected.popitem()
        (idpath, path) = self.svg.selected.popitem()

        bb = dot.bounding_box()
        parent = path.find('..')
        group = Group()
        parent.add(group)
        for point in path.path.end_points:
            clone = Use()
            clone.set('xlink:href', '#' + iddot)
            clone.set('x', point.x - bb.center.x)
            clone.set('y', point.y - bb.center.y)
            group.add(clone)
 def getTicks( self, ticks: int, linevon: float, linebis: float, style: Style):
   "The main ticks, linevon and linebis are the start- and end-point perpendicular to the axis in svg-coordinates."
   
   group = Group()
   if ticks <= 0:
     return group
   
   step = (self.max-self.min) / ticks
   for i in range(ticks+1):
     val = self.min + i * step
     point = self.transform(val)
     line = Line(
       x1=str(point.x-linevon*self.sin),
       y1=str(point.y+linevon*self.cos),
       x2=str(point.x-linebis*self.sin),
       y2=str(point.y+linebis*self.cos)
     )
     line.style = style
     group.add(line)
   return group
Exemplo n.º 6
0
    def test_group_nested_transform(self):
        group = Group()

        x, y = 10, 20
        w, h = 7, 20

        scale = 2

        rect = Rectangle(width=str(w), height=str(h), x=str(x), y=str(y))

        rect.transform = Transform(rotate=45, scale=scale)

        group.add(rect)

        group.transform = Transform(rotate=-45)  # rotation is compensated, but scale is not

        a = rect.composed_transform()
        self.assert_bounding_box_is_equal(group, (scale * x,
                                                  scale * (x + w)),
                                          (scale * y,
                                           scale * (y + h)))
    def effect(self):
        if len(self.svg.selected) != 2:
            errormsg(
                _("Please select exact two objects:\n1. object representing path,\n2. object representing dots."
                  ))
            return

        (iddot, dot) = self.svg.selected.popitem()
        (idpath, path) = self.svg.selected.popitem()

        bb = dot.bounding_box()
        parent = path.find('..')
        group = Group()
        parent.add(group)

        end_points = list(path.path.end_points)
        control_points = []
        for cp in path.path.control_points:
            is_endpoint = False
            for ep in end_points:
                if cp.x == ep.x and cp.y == ep.y:
                    is_endpoint = True
                    break
            if not is_endpoint:
                control_points.append(cp)

        pointlist = []
        if self.options.endpoints:
            pointlist += end_points
        if self.options.controlpoints:
            pointlist += control_points

        for point in pointlist:
            clone = Use()
            clone.set('xlink:href', '#' + iddot)
            clone.set('x', point.x - bb.center.x)
            clone.set('y', point.y - bb.center.y)
            group.add(clone)
Exemplo n.º 8
0
def split_fill_and_stroke(path_node):
    """Split a path into two paths, one filled and one stroked

    Returns a the list [fill, stroke], where each is the XML element of the
    fill or stroke, or None.
    """
    style = dict(inkex.Style.parse_str(path_node.get("style", "")))

    # If there is only stroke or only fill, don't split anything
    if "fill" in style and style["fill"] == "none":
        if "stroke" not in style or style["stroke"] == "none":
            return [None, None]  # Path has neither stroke nor fill
        else:
            return [None, path_node]
    if "stroke" not in style.keys() or style["stroke"] == "none":
        return [path_node, None]


    group = Group()
    fill = group.add(PathElement())
    stroke = group.add(PathElement())

    d = path_node.pop('d')
    if d is None:
        raise AssertionError("Cannot split stroke and fill of non-path element")

    nodetypes = path_node.pop('sodipodi:nodetypes', None)
    path_id = path_node.pop('id', str(id(path_node)))
    transform = path_node.pop('transform', None)
    path_node.pop('style')

    # Pass along all remaining attributes to the group
    for attrib_name, attrib_value in path_node.attrib.items():
        group.set(attrib_name, attrib_value)

    group.set("id", path_id)

    # Next split apart the style attribute
    style_group = {}
    style_fill = {"stroke": "none", "fill": "#000000"}
    style_stroke = {"fill": "none", "stroke": "none"}

    for key in style.keys():
        if key.startswith("fill"):
            style_fill[key] = style[key]
        elif key.startswith("stroke"):
            style_stroke[key] = style[key]
        elif key.startswith("marker"):
            style_stroke[key] = style[key]
        elif key.startswith("filter"):
            style_group[key] = style[key]
        else:
            style_fill[key] = style[key]
            style_stroke[key] = style[key]

    if len(style_group) != 0:
        group.set("style", str(inkex.Style(style_group)))

    fill.set("style", str(inkex.Style(style_fill)))
    stroke.set("style", str(inkex.Style(style_stroke)))

    # Finalize the two paths
    fill.set("d", d)
    stroke.set("d", d)
    if nodetypes is not None:
        fill.set('sodipodi:nodetypes', nodetypes)
        stroke.set('sodipodi:nodetypes', nodetypes)
    fill.set("id", path_id + "-fill")
    stroke.set("id", path_id + "-stroke")
    if transform is not None:
        fill.set("transform", transform)
        stroke.set("transform", transform)

    # Replace the original node with the group
    path_node.getparent().replace(path_node, group)

    return [fill, stroke]
  def effect(self):
  
    # Create some styles first

    linewidth = self.svg.unittouu(str(self.options.stroke_width)+self.options.stroke_width_unit)
    cssstyle = {
      'stroke': '#000000',
      'stroke-width': linewidth,
      'fill': 'none',
      'stroke-linecap': 'butt',
      'stroke-linejoin': 'round'
    }
    pathstyle = Style(cssstyle)
    cssstyle['stroke-linecap'] = 'square'
    borderstyle = Style(cssstyle)
    cssstyle['stroke-width'] = linewidth*0.6
    tickstyle = Style(cssstyle)
    cssstyle['stroke-width'] = linewidth*.3
    subtickstyle = Style(cssstyle)
    cssstyle['stroke'] = '#999'
    cssstyle['stroke-width'] = linewidth*0.5
    gridstyle = Style(cssstyle)
    cssstyle['stroke-width'] = linewidth*0.2
    subgridstyle = Style(cssstyle)

    self.ticksize    = self.svg.unittouu("8px")
    self.subticksize = self.svg.unittouu("4px")
    self.fontsize    = self.svg.unittouu("10pt")

    textstyle = {
      'font-size': self.fontsize,
      'font-family':self.options.font_family,
      'fill-opacity': '1.0',
      'stroke': 'none',
      'text-align': 'end',
      'text-anchor': 'end',
      'font-weight': 'normal',
      'font-style': 'normal',
      'fill': '#000'
    }
    textrechtsbdg = Style(textstyle)
    textstyle['text-align']='center'
    textstyle['text-anchor']='middle'
    textzentriert = Style(textstyle)
    textstyle['font-size']=2*self.fontsize
    texttitle = Style(textstyle)

    # Check on selected object
    if len(self.svg.selected) != 1:
      raise AbortExtension(_("Please select exact one object, a rectangle is preferred."))

    try:
      yidxarray = [ int(x) for x in re.split(r'[^0-9]',self.options.yidxs) if x ]
    except ValueError as e:
      raise AbortExtension(_("Split produces a string, that is not convertable to int."))
    
    # get CSV data
    self.data = getCSVData(self.options.csv_file,
                           self.options.csv_encoding,
                           { 'delimiter': self.options.csv_delimiter.replace('\\t', '\t') },
                           self.options.ignorefirst,
                           self.options.xidx, yidxarray)
    
    if self.data.len() < 2:
      raise AbortExtension(_("Less than 2 pairs of values. Nothing to plot."))
    
    # sort by x-values for a proper line
    self.data.sort()
    
    self.data.calculateMinMax()

    # Get minima and maxima for x- and y-values
    self.xmin = self.data.getXMin() if self.options.xmin_autodetect else self.options.xmin
    self.xmax = self.data.getXMax() if self.options.xmax_autodetect else self.options.xmax
    if self.xmin >= self.xmax:
      raise AbortExtension(_('xmin > xmax'))
    
    self.ymin = self.data.getYMin() if self.options.ymin_autodetect else self.options.ymin
    self.ymax = self.data.getYMax() if self.options.ymax_autodetect else self.options.ymax
    if self.ymin >= self.ymax:
      raise AbortExtension(_('ymin > ymax'))
    
    # Get the parent of the node
    (nodeid,node) = self.svg.selected.popitem()
    self.bb = node.bounding_box()
    parent = node.getparent()
    group = Group()
    parent.add(group)

    # get axis and path
    xAchse = Axis(Vector2d(self.bb.left,self.bb.bottom),Vector2d(self.bb.right,self.bb.bottom),self.xmin,self.xmax)
    yAchse = Axis(Vector2d(self.bb.left,self.bb.bottom),Vector2d(self.bb.left ,self.bb.top)   ,self.ymin,self.ymax)
    
    plot = []
    for i in range(len(yidxarray)):
      plot.append( self.plotPath(pathstyle,i) )

    # evaluate options and add all together
    if self.options.xgrid:
      group.add(xAchse.getTicks(   self.options.xtickn,                         0, -self.bb.height, gridstyle))
      group.add(xAchse.getSubTicks(self.options.xtickn, self.options.xsubtickn, 0, -self.bb.height, subgridstyle))
    if self.options.ygrid:
      group.add(yAchse.getTicks(   self.options.ytickn,                         0, self.bb.width, gridstyle))
      group.add(yAchse.getSubTicks(self.options.ytickn, self.options.ysubtickn, 0, self.bb.width, subgridstyle))
    if self.options.xtickn > 0:
      tlvon = -self.ticksize if self.options.xticksin  else 0
      tlbis =  self.ticksize if self.options.xticksout else 0
      group.add(xAchse.getTicks(   self.options.xtickn,                            tlvon,    tlbis, tickstyle))
      group.add(xAchse.getSubTicks(self.options.xtickn, self.options.xsubtickn, .5*tlvon, .5*tlbis, subtickstyle))
      group.add(xAchse.getNumbers( self.options.xtickn, self.options.xformat, 3*self.ticksize, 0, textzentriert))
    if self.options.ytickn > 0:
      tlvon = -self.ticksize if self.options.yticksout else 0
      tlbis =  self.ticksize if self.options.yticksin  else 0
      group.add(yAchse.getTicks(   self.options.ytickn,                            tlvon,    tlbis, tickstyle))
      group.add(yAchse.getSubTicks(self.options.ytickn, self.options.ysubtickn, .5*tlvon, .5*tlbis, subtickstyle))
      group.add(yAchse.getNumbers( self.options.ytickn, self.options.yformat, -2*self.ticksize, -.3*self.fontsize, textrechtsbdg))
    
    for i in range(len(yidxarray)):
      group.add(plot[i])

    if self.options.border_left:
      group.add( self.createBorderLine('left', borderstyle) )
    if self.options.border_bottom:
      group.add( self.createBorderLine('bottom', borderstyle) )
    if self.options.border_right:
      group.add( self.createBorderLine('right', borderstyle) )
    if self.options.border_top:
      group.add( self.createBorderLine('top', borderstyle) )
    if self.options.label_title:
      group.add( textAt(self.bb.center.x,self.bb.minimum.y-self.fontsize,self.options.label_title, texttitle) )
    if self.options.label_yaxis:
      group.add( textAt(self.bb.minimum.x-4*self.fontsize,self.bb.center.y,self.options.label_yaxis, textzentriert,-90) )
    if self.options.label_xaxis:
      group.add( textAt(self.bb.center.x,self.bb.maximum.y+3.5*self.fontsize,self.options.label_xaxis, textzentriert) )

    if self.options.storedata:
      group.set('data-values',str(self.data))
    
    if self.options.remove:
      parent.remove(node)
Exemplo n.º 10
0
class PathScatter(pathmodifier.Diffeo):
    def __init__(self):
        super(PathScatter, self).__init__()
        self.arg_parser.add_argument("-n",
                                     "--noffset",
                                     type=float,
                                     dest="noffset",
                                     default=0.0,
                                     help="normal offset")
        self.arg_parser.add_argument("-t",
                                     "--toffset",
                                     type=float,
                                     dest="toffset",
                                     default=0.0,
                                     help="tangential offset")
        self.arg_parser.add_argument(
            "-g",
            "--grouppick",
            type=inkex.Boolean,
            dest="grouppick",
            default=False,
            help="if pattern is a group then randomly pick group members")
        self.arg_parser.add_argument(
            "-m",
            "--pickmode",
            type=str,
            dest="pickmode",
            default="rand",
            help="group pick mode (rand=random seq=sequentially)")
        self.arg_parser.add_argument(
            "-f",
            "--follow",
            type=inkex.Boolean,
            dest="follow",
            default=True,
            help="choose between wave or snake effect")
        self.arg_parser.add_argument(
            "-s",
            "--stretch",
            type=inkex.Boolean,
            dest="stretch",
            default=True,
            help="repeat the path to fit deformer's length")
        self.arg_parser.add_argument("-p",
                                     "--space",
                                     type=float,
                                     dest="space",
                                     default=0.0)
        self.arg_parser.add_argument("-v",
                                     "--vertical",
                                     type=inkex.Boolean,
                                     dest="vertical",
                                     default=False,
                                     help="reference path is vertical")
        self.arg_parser.add_argument(
            "-d",
            "--duplicate",
            type=inkex.Boolean,
            dest="duplicate",
            default=False,
            help="duplicate pattern before deformation")
        self.arg_parser.add_argument(
            "-c",
            "--copymode",
            type=str,
            dest="copymode",
            default="clone",
            help="duplicate pattern before deformation")
        self.arg_parser.add_argument(
            "--tab",
            type=str,
            dest="tab",
            help="The selected UI-tab when OK was pressed")

    def prepareSelectionList(self):
        # first selected->pattern, all but first selected-> skeletons
        pattern_node = self.svg.selected.pop()

        self.gNode = Group()
        pattern_node.getparent().append(self.gNode)

        if self.options.copymode == "copy":
            self.patternNode = pattern_node.duplicate()
        elif self.options.copymode == "clone":
            # TODO: allow 4th option: duplicate the first copy and clone the next ones.
            self.patternNode = self.gNode.add(Use())
            self.patternNode.href = pattern_node
        else:
            self.patternNode = pattern_node

        self.skeletons = self.svg.selected
        self.expand_clones(self.skeletons, True, False)
        self.objects_to_paths(self.skeletons, False)

    def lengthtotime(self, l):
        """
        Receives an arc length l, and returns the index of the segment in self.skelcomp
        containing the corresponding point, to gether with the position of the point on this segment.

        If the deformer is closed, do computations modulo the total length.
        """
        if self.skelcompIsClosed:
            l = l % sum(self.lengths)
        if l <= 0:
            return 0, l / self.lengths[0]
        i = 0
        while (i < len(self.lengths)) and (self.lengths[i] <= l):
            l -= self.lengths[i]
            i += 1
        t = l / self.lengths[min(i, len(self.lengths) - 1)]
        return i, t

    def localTransformAt(self, s, follow=True):
        """
        receives a length, and returns the corresponding point and tangent of self.skelcomp
        if follow is set to false, returns only the translation
        """
        i, t = self.lengthtotime(s)
        if i == len(self.skelcomp) - 1:
            x, y = bezier.between_point(self.skelcomp[i - 1], self.skelcomp[i],
                                        1 + t)
            dx = (self.skelcomp[i][0] -
                  self.skelcomp[i - 1][0]) / self.lengths[-1]
            dy = (self.skelcomp[i][1] -
                  self.skelcomp[i - 1][1]) / self.lengths[-1]
        else:
            x, y = bezier.between_point(self.skelcomp[i], self.skelcomp[i + 1],
                                        t)
            dx = (self.skelcomp[i + 1][0] -
                  self.skelcomp[i][0]) / self.lengths[i]
            dy = (self.skelcomp[i + 1][1] -
                  self.skelcomp[i][1]) / self.lengths[i]
        if follow:
            mat = [[dx, -dy, x], [dy, dx, y]]
        else:
            mat = [[1, 0, x], [0, 1, y]]
        return mat

    def effect(self):

        if len(self.options.ids) < 2:
            inkex.errormsg(_("This extension requires two selected paths."))
            return
        self.prepareSelectionList()

        # center at (0,0)
        bbox = self.patternNode.bounding_box()
        mat = [[1, 0, -bbox.center.x], [0, 1, -bbox.center.y]]
        if self.options.vertical:
            bbox = BoundingBox(-bbox.y, bbox.x)
            mat = (Transform([[0, -1, 0], [1, 0, 0]]) * Transform(mat)).matrix
        mat[1][2] += self.options.noffset
        self.patternNode.transform *= mat

        width = bbox.width
        dx = width + self.options.space

        # check if group and expand it
        patternList = []
        if self.options.grouppick and isinstance(self.patternNode, Group):
            mat = self.patternNode.transform
            for child in self.patternNode:
                child.transform *= mat
                patternList.append(child)
        else:
            patternList.append(self.patternNode)
        # inkex.debug(patternList)

        counter = 0
        for skelnode in self.skeletons.values():
            self.curSekeleton = skelnode.path.to_superpath()
            for comp in self.curSekeleton:
                self.skelcomp, self.lengths = linearize(comp)
                # !!!!>----> TODO: really test if path is closed! end point==start point is not enough!
                self.skelcompIsClosed = (self.skelcomp[0] == self.skelcomp[-1])

                length = sum(self.lengths)
                if self.options.stretch:
                    dx = width + self.options.space
                    n = int(
                        (length - self.options.toffset + self.options.space) /
                        dx)
                    if n > 0:
                        dx = (length - self.options.toffset) / n

                xoffset = self.skelcomp[0][
                    0] - bbox.x.minimum + self.options.toffset
                yoffset = self.skelcomp[0][
                    1] - bbox.y.center - self.options.noffset

                s = self.options.toffset
                while s <= length:
                    mat = self.localTransformAt(s, self.options.follow)
                    if self.options.pickmode == "rand":
                        clone = copy.deepcopy(patternList[random.randint(
                            0,
                            len(patternList) - 1)])

                    if self.options.pickmode == "seq":
                        clone = copy.deepcopy(patternList[counter])
                        counter = (counter + 1) % len(patternList)

                    # !!!--> should it be given an id?
                    # seems to work without this!?!
                    myid = patternList[random.randint(0,
                                                      len(patternList) -
                                                      1)].tag.split('}')[-1]
                    clone.set("id", self.svg.get_unique_id(myid))
                    self.gNode.append(clone)

                    clone.transform *= mat
                    s += dx

        self.patternNode.getparent().remove(self.patternNode)