示例#1
0
def astro_glorb() -> str:
    drawing = Drawing()
    prng = random.Random()

    #background = drawing.rect((0,0), size=(200,200), fill="black")
    background = StarsBackground(300, 300)
    drawing.add(background.render(drawing))

    #a = GlorbAlien(position=(75, 75), size=60)
    a = random_glorb(prng, size=prng.randint(40, 80))
    #astro = DomeHelmetAstronaut(a, a.size)
    astro = random_domed_astronaut(prng, a)
    g = astro.render(drawing)
    g.translate(prng.randint(100, 200), prng.randint(100, 200))
    g.rotate(prng.gauss(0, 20))

    drawing.add(g)

    return drawing.tostring()
示例#2
0
    def get_svg(self, unit=mm):
        '''
        Generate an SVG Drawing based of the generated gear profile.
        :param unit: None or a unit within the 'svgwrite' module, such as svgwrite.mm, svgwrite.cm
        :return: An svgwrite.Drawing object populated only with the gear path.
        '''

        points = self.get_point_list()
        width, height = np.ptp(points, axis=0)
        left, top = np.min(points, axis=0)
        size = (width * unit, height * unit) if unit is not None else (width,
                                                                       height)
        dwg = Drawing(size=size,
                      viewBox='{} {} {} {}'.format(left, top, width, height))
        p = Path('M')
        p.push(points)
        p.push('Z')
        dwg.add(p)
        return dwg
示例#3
0
 def test_two_exon_transcript(self):
     transcript = fusion.FusionTranscript()
     transcript.position = Interval(1, 555)
     transcript.exons = [
         genomic.Exon(55820038, 55820969, transcript=transcript),
         genomic.Exon(55820971, 55820976, transcript=transcript)
     ]
     transcript.exon_mapping[Interval(55820971, 55820976)] = MockObject(
         transcript=MockObject(exon_number=lambda *x: 2))
     cfg = DiagramSettings(width=1500)
     canvas = Drawing(size=(cfg.width, 1000))
     drawing_width = cfg.width - cfg.label_left_margin - cfg.left_margin - cfg.right_margin
     canvas.add(
         draw_ustranscript(cfg,
                           canvas,
                           transcript,
                           target_width=drawing_width))
     if OUTPUT_SVG:
         canvas.saveas('test_two_exon_transcript.svg')
示例#4
0
 def draw_diagram(self) -> None:
     n_countries: int = self.model.rowCount()
     if n_countries > 0:
         style: SvgStyle = self.svg_style
         delta_angle: float = 2.0*math.pi/n_countries
         max_value: float = max(self.model.values)
         dwg = Drawing(self.temp_svg_file.fileName(), profile='tiny', viewBox='-250 -250 500 500')
         for idx, v in enumerate(self.model.values):
             x: float = style.line_length * v/max_value * math.sin(idx * delta_angle)
             y: float = -style.line_length * v/max_value * math.cos(idx * delta_angle)
             dwg.add(shapes.Line(start=(0, 0), end=(x, y),
                                 stroke=style.line_color, stroke_width=style.line_width))
             radius: float = style.circle_rad
             if style.circle_rad_normalization:
                 radius *= v/max_value
             dwg.add(shapes.Circle(center=(x, y), r=radius,
                                   stroke=style.circle_stroke_color, stroke_width=style.circle_stroke_width,
                                   fill=style.circle_fill_color))
         dwg.save(pretty=True)
         self.load_svg(self.temp_svg_file.fileName())
示例#5
0
文件: draw.py 项目: e76nystrom/pnc
    def open(self, inFile, drawDxf=True, drawSvg=True):
        if drawSvg and self.svg is None:
            svgFile = inFile + ".svg"
            try:
                self.svg = Drawing(svgFile, profile='full', fill='black')
                self.path = Path(stroke_width=.5, stroke='black', fill='none')
            except IOError:
                self.svg = None
                self.path = None
                ePrint("svg file open error %s" % (svgFile))

        if drawDxf and self.d is None:
            dxfFile = inFile + "_ngc.dxf"
            try:
                self.d = dxf.drawing(dxfFile)
                self.layerIndex = 0
                self.d.add_layer('0', color=self.color, lineweight=0)
                self.setupLayers()
            except IOError:
                self.d = None
                ePrint("dxf file open error %s" % (dxfFile))
示例#6
0
    def __init__(self,
                 name,
                 commits,
                 configuration,
                 deltax=50.0,
                 width=700.0,
                 y=40.0,
                 sep=20.0,
                 image_size=40.0):
        self.commits = commits
        self.authors = AuthorList()
        self.initial_last_dates(configuration)
        self.position_function()
        height = y + sep + (self.max_elements + 1) * (image_size + sep)
        self.dwg = Drawing(name, size=(width + 2 * deltax, height))

        self.deltax = deltax
        self.width = width
        self.y = y
        self.sep = sep
        self.image_size = image_size
示例#7
0
    def __init__(self,
                 tmax,
                 grid,
                 darea=DrawArea,
                 symbols=SymbolAssets,
                 m3d=Matrix3D):
        self._off = (2.2, 2.5)
        size = 100
        self._size = (size, size * 1.9)

        self._matrix3d = m3d(tmax[0], self._size, self._off, grid)
        self._area = darea(self._off, self._size, tmax[0], tmax[1],
                           grid).area()
        self.dwg = Drawing('graph.svg', size=self._area, id="graph")

        viewp = self.fixOneLineRoot(grid)
        self.dwg.viewbox(*viewp)

        self._symbols = symbols(self.dwg)

        self.setup()
示例#8
0
    def render(self,
               size: Tuple[int, int] = (512, 512),
               view_box: str = "-0.5 -0.5 1.0 1.0",
               **extra) -> Drawing:
        drawing = Drawing("", size, viewBox=view_box, **extra)

        for view in self.views:
            projection = np.dot(view.camera.view, view.camera.projection)

            clip_path = drawing.defs.add(drawing.clipPath())
            clip_min = view.viewport.minx, view.viewport.miny
            clip_size = view.viewport.width, view.viewport.height
            clip_path.add(drawing.rect(clip_min, clip_size))

            # TODO: Handle Z-index with meshes
            for group in view.scene.groups:
                for g in self._create_group(drawing, projection, view.viewport,
                                            group):
                    g["clip-path"] = clip_path.get_funciri()
                    drawing.add(g)
        return drawing
    def generate_tiling(self):
        dwg = Drawing("{}/tiling2.svg".format(self.output_folder),
                      profile="tiny")

        current_color = 0
        row_spacing = self.pent_height * 2 + self.bottom_length

        for y in range(self.num_down):
            transform = "translate({}, {})".format(0, self.rep_spacing * y)
            dgroup = dwg.add(dwg.g(transform=transform))
            for x in range(self.num_across):
                # if x is odd, point 1 of pent 1 needs to be attached to point 3 of pent 2
                if x % 2 == 1:
                    dx = int(
                        x / 2
                    ) * self.rep_spacing + self.pent_width * 2 + self.column_offset.real
                    transform = "translate({}, {})".format(
                        dx, self.column_offset.imag)
                else:
                    transform = "translate({}, {})".format(
                        int(x / 2) * self.rep_spacing, 0)
                group = dgroup.add(dwg.g(transform=transform))
                for pent in self.cairo_group:
                    group.add(
                        dwg.path(
                            **{
                                'd':
                                pent.d(),
                                'fill':
                                self._colors[current_color %
                                             len(self._colors)],
                                'stroke-width':
                                4,
                                'stroke':
                                rgb(0, 0, 0)
                            }))
                    current_color += 1

        dwg.viewbox(*self.pattern_viewbox)
        dwg.save(pretty=True)
示例#10
0
def export_svg_svgwrite(fn, paths, w, h, line_width=0.1):

    from svgwrite import Drawing
    w_str = "{}pt".format(w)
    h_str = "{}pt".format(h)

    dwg = Drawing(filename=fn,
                  size=(w_str, h_str),
                  viewBox=("0 0 {} {}".format(w, h)))

    for path in paths:
        if (len(path) > 1):
            str_list = []
            str_list.append("M {},{}".format(path[0, 0], path[0, 1]))
            for e in path[1:]:
                str_list.append(" L {},{}".format(e[0], e[1]))
            s = ''.join(str_list)
            dwg.add(
                dwg.path(s).stroke(color="rgb(0%,0%,0%)",
                                   width=line_width).fill("none"))

    dwg.save()
示例#11
0
def display_svg():
    dwg = Drawing()
    hlines = dwg.add(dwg.g(id="hlines", stroke="green"))
    for y in range(20):
        hlines.add(
            dwg.line(start=(2 * cm, (2 + y) * cm),
                     end=(18 * cm, (2 + y) * cm)))
    vlines = dwg.add(dwg.g(id="vline", stroke="blue"))
    for x in range(17):
        vlines.add(
            dwg.line(start=((2 + x) * cm, 2 * cm),
                     end=((2 + x) * cm, 21 * cm)))
    shapes = dwg.add(dwg.g(id="shapes", fill="red"))

    # set presentation attributes at object creation as SVG-Attributes
    circle = dwg.circle(center=(15 * cm, 8 * cm),
                        r="2.5cm",
                        stroke="blue",
                        stroke_width=3)
    circle["class"] = "class1 class2"
    shapes.add(circle)

    # override the 'fill' attribute of the parent group 'shapes'
    shapes.add(
        dwg.rect(
            insert=(5 * cm, 5 * cm),
            size=(45 * mm, 45 * mm),
            fill="blue",
            stroke="red",
            stroke_width=3,
        ))

    # or set presentation attributes by helper functions of the Presentation-Mixin
    ellipse = shapes.add(
        dwg.ellipse(center=(10 * cm, 15 * cm), r=("5cm", "10mm")))
    ellipse.fill("green", opacity=0.5).stroke("black",
                                              width=5).dasharray([20, 20])

    return Response(dwg.tostring(), mimetype="image/svg+xml")
示例#12
0
def create_gd(cfg=GDConfig()):
    """Main function to load the CSV, processes it, and save the SVG.

    Args:
        cfg: (GDConfig Default=GDConfig()) Graphical Datasheet
            configuration to use.
    """
    infile = None
    outfile_root = None
    if len(argv) in (2, 3) and argv[1].lower().endswith('.csv'):
        infile = argv[1] if len(argv[1]) > 4 else None

    if len(argv) == 3 and argv[2].lower().endswith('.svg'):
        outfile_root = argv[2][0:-4] if len(argv[2]) > 4 else None

    filename_root, lines = read_csv(infile)
    dwg = Drawing(filename=filename_root + '.svg')
    svg_root = outfile_root if outfile_root is not None else filename_root

    embed_style(dwg, filename_root, cfg)
    process_csv_data(dwg, lines, cfg)
    write_svg(dwg, svg_root, cfg)
示例#13
0
def save_pie_chart(filename, root_list, step_size):

    #  create the drawing surface
    svg_drawing = Drawing(filename=filename,
                          size=(SVG_SIZE, SVG_SIZE),
                          debug=True)

    start_x = SVG_SIZE // 2
    start_y = SVG_SIZE // 2
    radius = SVG_SIZE // 2

    all_angles = []
    for node in root_list:
        all_angles += node.pie_angle
    all_angles = sorted(all_angles)

    radians0 = all_angles[-1]
    for i in range(len(all_angles)):
        radians1 = all_angles[i]
        dx0 = radius * (math.sin(radians0))
        dy0 = radius * (math.cos(radians0))
        dx1 = radius * (math.sin(radians1))
        dy1 = radius * (math.cos(radians1))

        m0 = dy0
        n0 = -dx0
        m1 = -dy0 + dy1
        n1 = dx0 - dx1

        w = svg_drawing.path(
            d="M {0},{1} l {2},{3} a {4},{4} 0 0,0 {5},{6} z".format(
                start_x, start_y, m0, n0, radius, m1, n1),
            fill=colors[i],
            stroke="none",
        )
        svg_drawing.add(w)
        radians0 = radians1

    svg_drawing.save()
示例#14
0
    def draw_path_clip(self):
        path_filename = "{}/path_clip_{}.svg".format(
            self.output_folder,
            basename(self.filename).replace(".svg", ""))
        dwg = Drawing(path_filename)
        image_bbox = calc_overall_bbox(self.tile_paths)

        dx = self.pent_x - min(image_bbox[0], image_bbox[1])
        dy = self.pent_y - min(image_bbox[2], image_bbox[3])
        dwg.add(
            dwg.path(
                **{
                    "d": self.new_pentagon().d(),
                    "fill": "none",
                    'stroke-width': 4,
                    'stroke': rgb(0, 0, 0)
                }))
        neg_transform = "translate({}, {})".format(-dx, -dy)
        transform = "translate({}, {})".format(dx, dy)
        clip_path = dwg.defs.add(
            dwg.clipPath(id="pent_path", transform=neg_transform))
        clip_path.add(dwg.path(d=self.new_pentagon().d()))
        group = dwg.add(
            dwg.g(clip_path="url(#pent_path)",
                  transform=transform,
                  id="clippedpath"))
        for i, path in enumerate(self.tile_paths):
            group.add(
                dwg.path(d=path.d(),
                         style=self.tile_attributes[i].get('style'),
                         id=self.tile_attributes[i]['id']))
        dwg.add(dwg.use("#clippedpath", transform="transform(100, 100)"))

        dwg.viewbox(self.pent_x, self.pent_y, self.pent_width,
                    self.pent_height)
        dwg.save()
        xml = xml.dom.minidom.parse(path_filename)
        open(path_filename, "w").write(xml.toprettyxml())
示例#15
0
def create_svg_document(elements,
                        size,
                        viewbox=None,
                        background_color="white",
                        background_opacity=1.0):
    """Create the full SVG document.

    :param viewbox: (minx, miny, width, height)
    """
    dwg = Drawing("ase.svg", profile="tiny", size=size)
    root = Group(id="root")
    dwg.add(root)
    # if Color(background_color).web != "white":
    # apparently the best way, see: https://stackoverflow.com/a/11293812/5033292
    root.add(
        shapes.Rect(size=size,
                    fill=background_color,
                    fill_opacity=background_opacity))
    for element in elements:
        root.add(element)
    if viewbox:
        dwg.viewbox(*viewbox)
    return dwg
示例#16
0
def save_radial_tree_plot(filename, root_list, step_size):

    #  define some params
    white = "rgb(255, 255, 255)"
    black = "rgb(0, 0, 0)"

    #  create the drawing surface
    svg_drawing = Drawing(filename=filename,
                          size=(SVG_SIZE, SVG_SIZE),
                          debug=True)

    #  create defs, in this case, just a single gradient
    rad_grad = svg_drawing.radialGradient(("50%", "50%"),
                                          "100%", ("50%", "50%"),
                                          id="rad_grad")
    rad_grad.add_stop_color("0%", black, 255)
    rad_grad.add_stop_color("100%", white, 255)
    svg_drawing.defs.add(rad_grad)

    tree_plot = svg_drawing.mask(
        id='treeplot',
        style=
        'stroke: black; stroke-width: 3; fill: none; stroke-linecap: round; stroke-opacity: 0.5;'
    )

    tree_plot.add(svg_drawing.rect((0, 0), (SVG_SIZE, SVG_SIZE)).fill(white))

    for root in root_list:
        draw_radial_tree_node(svg_drawing, tree_plot, root, rad_grad,
                              step_size)

    base_rect = svg_drawing.rect((0, 0), (SVG_SIZE, SVG_SIZE),
                                 mask="url(#treeplot)").fill(black)
    svg_drawing.add(base_rect)
    svg_drawing.add(tree_plot)

    svg_drawing.save()
示例#17
0
 def setUp(self):
     self.canvas = Drawing(height=100, width=1000)
示例#18
0
文件: svg.py 项目: openalea/wlformat
def export_workflow(workflow, store, size=None):
    """Construct a SVG description for a workflow.

    Args:
        workflow (WorkflowDef)
        store (dict of uid, def): elements definitions
        size (int, int): size of drawing in pixels

    Returns:
        (str) - SVG description of workflow
    """
    # check that each node has a position
    for node in workflow['nodes']:
        if 'x' not in node or 'y' not in node:
            raise UserWarning("need to position workflow first")

    if size is None:
        size = (600, 600)

    # draw
    paper = Drawing("workflow.svg", size, id="repr")

    lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="bg_loaded")
    lg.add_stop_color(0, color='#8c8cff')
    lg.add_stop_color(1, color='#c8c8c8')
    paper.defs.add(lg)

    lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="bg_failed")
    lg.add_stop_color(0, color='#ff8cff')
    lg.add_stop_color(1, color='#c8c8c8')
    paper.defs.add(lg)

    lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="in_port")
    lg.add_stop_color(0, color='#3333ff')
    lg.add_stop_color(1, color='#2222ff')
    paper.defs.add(lg)

    lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="out_port")
    lg.add_stop_color(0, color='#ffff33')
    lg.add_stop_color(1, color='#9a9a00')
    paper.defs.add(lg)

    for i, link in enumerate(workflow['links']):
        draw_link(paper, workflow, store, link, i)

    bbs = []
    for i, node in enumerate(workflow['nodes']):
        bb = draw_node(paper, workflow, store, node, i)
        bbs.append(bb)

    # reformat whole drawing to fit screen
    xmin = min(bb[0] for bb in bbs) - draw_padding
    xmax = max(bb[2] for bb in bbs) + draw_padding
    ymin = min(bb[1] for bb in bbs) - draw_padding
    ymax = max(bb[3] for bb in bbs) + draw_padding

    w = float(size[0])
    h = float(size[1])
    xratio = (xmax - xmin) / w
    yratio = (ymax - ymin) / h
    if xratio > yratio:
        xsize = int(xratio * w)
        ysize = int(xratio * h)
        ymin -= (ysize - (ymax - ymin)) / 2
    else:
        xsize = int(yratio * w)
        ysize = int(yratio * h)
        xmin -= (xsize - (xmax - xmin)) / 2

    paper.viewbox(xmin, ymin, xsize, ysize)

    return paper.tostring(), (xmin, ymin, xsize, ysize)
示例#19
0
文件: svg.py 项目: openalea/wlformat
def export_node(node, store, size=None):
    """Construct a SVG description for a workflow node.

    Args:
        node (NodeDef)
        store (dict of uid, def): elements definitions
        size (int, int): size of drawing in pixels

    Returns:
        (str) - SVG description of workflow node
    """
    pfs = port_font_size

    # node size
    pr = port_radius
    pspace = pr * 9
    nw = compute_node_width(node, node['name'], pspace)
    nh = label_font_size + 2 * pr + 2 * pfs + 2 + (2 * node_padding)

    # draw
    if size is None:
        size = (600, 600)

    paper = Drawing("workflow_node.svg", size, id="repr")

    lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="in_port")
    lg.add_stop_color(0, color='#3333ff')
    lg.add_stop_color(1, color='#2222ff')
    paper.defs.add(lg)

    lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="out_port")
    lg.add_stop_color(0, color='#ffff33')
    lg.add_stop_color(1, color='#9a9a00')
    paper.defs.add(lg)

    # body
    g = paper.add(paper.g())

    # background
    lg = paper.linearGradient((0.5, 0), (0.5, 1.))
    lg.add_stop_color(0, color='#8c8cff')
    lg.add_stop_color(1, color='#c8c8c8')
    paper.defs.add(lg)

    bg = paper.rect((-nw / 2, -nh / 2), (nw, nh),
                    rx=node_padding,
                    ry=node_padding,
                    stroke_width=1)
    bg.stroke('#808080')
    bg.fill(lg)
    g.add(bg)

    # label
    style = ('font-size: %dpx; font-family: %s; '
             'text-anchor: middle' % (label_font_size, label_font))
    frag = paper.tspan(node['name'], dy=[label_font_size // 3])
    label = paper.text("", style=style, fill='#000000')
    label.add(frag)
    g.add(label)

    # ports
    port_style = ('font-size: %dpx; ' % pfs + 'font-family: %s; ' % label_font)
    onstyle = port_style + 'text-anchor: end'
    instyle = port_style + 'text-anchor: start'
    istyle = port_style + 'text-anchor: middle'
    nb = len(node['inputs'])
    py = -nh / 2
    for i, pdef in enumerate(node['inputs']):
        px = i * pspace - pspace * (nb - 1) / 2
        pg = g.add(paper.g())
        pg.translate(px, py)
        idef = store.get(pdef['interface'], None)
        if idef is not None and 'url' in idef:
            link = pg.add(paper.a(href=idef['url'], target='_top'))
        else:
            link = pg

        port = paper.circle((0, 0), pr, stroke='#000000', stroke_width=1)
        port.fill("url(#in_port)")
        link.add(port)
        # port name
        frag = paper.tspan(pdef['name'], dy=[-2 * pr])
        label = paper.text("", style=instyle, fill='#000000')
        label.rotate(-45)
        label.add(frag)
        pg.add(label)
        # port interface
        if idef is None:
            itxt = pdef['interface']
        else:
            itxt = idef['name']
        if len(itxt) > 10:
            itxt = itxt[:7] + "..."
        frag = paper.tspan(itxt, dy=[pr + pfs])
        label = paper.text("", style=istyle, fill='#000000')
        label.add(frag)
        link.add(label)

    nb = len(node['outputs'])
    py = nh / 2
    for i, pdef in enumerate(node['outputs']):
        px = i * pspace - pspace * (nb - 1) / 2
        pg = g.add(paper.g())
        pg.translate(px, py)
        idef = store.get(pdef['interface'], None)
        if idef is not None and 'url' in idef:
            link = pg.add(paper.a(href=idef['url'], target='_top'))
        else:
            link = pg

        port = paper.circle((0, 0), pr, stroke='#000000', stroke_width=1)
        port.fill("url(#out_port)")
        link.add(port)
        # port name
        frag = paper.tspan(pdef['name'], dy=[2 * pr + pfs // 2])
        label = paper.text("", style=onstyle, fill='#000000')
        label.rotate(-45)
        label.add(frag)
        pg.add(label)
        # port interface
        if idef is None:
            itxt = pdef['interface']
        else:
            itxt = idef['name']
        if len(itxt) > 10:
            itxt = itxt[:7] + "..."
        frag = paper.tspan(itxt, dy=[-pr - 2])
        label = paper.text("", style=istyle, fill='#000000')
        label.add(frag)
        link.add(label)

    # reformat whole drawing to fit screen
    xmin = -nw / 2 - draw_padding / 10.
    xmax = +nw / 2 + draw_padding / 10.
    if len(node['inputs']) == 0:
        inames_extend = 0
    else:
        inames = [(len(pdef['name']), pdef['name']) for pdef in node['inputs']]
        inames_extend = string_size(sorted(inames)[-1][1], pfs) * 0.7 + pfs
    ymin = -nh / 2 - pr - inames_extend - draw_padding / 10.
    if len(node['outputs']) == 0:
        onames_extend = 0
    else:
        onames = [(len(pdef['name']), pdef['name'])
                  for pdef in node['outputs']]
        onames_extend = string_size(sorted(onames)[-1][1], pfs) * 0.7 + pfs
    ymax = +nh / 2 + pr + onames_extend + draw_padding / 10.

    w = float(size[0])
    h = float(size[1])
    ratio = max((xmax - xmin) / w, (ymax - ymin) / h)
    xsize = ratio * w
    ysize = ratio * h

    bb = (xmin * xsize / (xmax - xmin), ymin * ysize / (ymax - ymin), xsize,
          ysize)

    paper.viewbox(*bb)
    return paper.tostring(), bb
示例#20
0
                                  start_location - paths[x[0]].point(x[1])))
        path_to_add = paths_to_add.pop(0)
        assert output_paths[-1] is not None
        output_attributes.append(attributes[path_to_add[0]])
        # filter out the reverse path
        paths_to_add = [p for p in paths_to_add if p[0] != path_to_add[0]]
        start_location = paths[path_to_add[0]].start if path_to_add[0] else paths[
            path_to_add[1]].end
'''

if __name__ == "__main__":
    # make a graph of the next available grid
    num_grid = 8
    spacing = 30

    dwg = Drawing(join(OUTPUT_DIRECTORY, "next_available_grid.svg"),
                  (num_grid * spacing, num_grid * spacing))
    for x in range(num_grid):
        dwg.add(
            dwg.line(start=(0, x * spacing),
                     end=(num_grid * spacing, x * spacing),
                     stroke=rgb(10, 10, 16, '%')))
        dwg.add(
            dwg.line(start=(x * spacing, 0),
                     end=(x * spacing, num_grid * spacing),
                     stroke=rgb(10, 10, 16, '%')))
    start_point = [4, 4]

    dwg.add(
        dwg.rect(insert=(start_point[0] * spacing, start_point[1] * spacing),
                 size=(spacing, spacing),
                 fill=rgb(100, 100, 16, '%')))
示例#21
0
"""
Figure built manually in SVG.

Note, these require the svgwrite package (and optionally, the svglib package to convert to pdf).
"""
import subprocess

from svgwrite import Drawing

filename = 'scaling_compute_totals_gmres.svg'
color_phys = '#85C1E9'
color_scaled = '#EC7063'
main_font_size = 20

dwg = Drawing(filename, (2500, 2000), debug=True)

top_text = dwg.add(dwg.g(font_size=main_font_size,
                         style="font-family: arial;"))

locs = [
    'NL Inputs', 'NL Outputs', 'NL Residuals', 'LN Inputs', 'LN Outputs',
    'LN Residuals', 'Jacobian'
]

x = 650
y = 50
delta_x = 180
vertical_locs = []
for loc in locs:
    top_text.add(dwg.text(loc, (x - len(loc) * 4, y)))
    vertical_locs.append(x)
示例#22
0
 def export(self):
     dwg = Drawing(self.name, width=W, height=H)
     for r in self.rects:
         dwg.add(dwg.rect(insert=(r.x, r.y), size=(r.w, r.h), fill='black'))
     dwg.save()
def flatten_scene(pScene):
    lNode = pScene.GetRootNode()

    if not lNode:
        return

    for i in range(lNode.GetChildCount()):

        lChildNode = lNode.GetChild(i)
        if lChildNode.GetNodeAttribute() is None:
            continue
        lAttributeType = (lChildNode.GetNodeAttribute().GetAttributeType())
        if lAttributeType != FbxNodeAttribute.eMesh:
            continue
        lMesh = lChildNode.GetNodeAttribute()
        projected_points = {}
        control_points = lMesh.GetControlPoints()
        start_point = 0
        poly_paths = []
        for polygon_num in range(lMesh.GetPolygonCount()):
            corners = []
            for corner in range(3):
                corners.append(lMesh.GetPolygonVertex(polygon_num, corner))
            # first, check if any of the control points are already projected
            flattened = []
            for j, corner in enumerate(corners):
                if corner in projected_points:
                    flattened.append(projected_points[corner])
                    continue
                target_corner = corners[j - 1]
                current_vec = control_points[corner]
                target_vec = control_points[target_corner]
                angle = acos(
                    current_vec.DotProduct(target_vec) /
                    (current_vec.Length() * target_vec.Length()))
                length = current_vec.Distance(target_vec)
                # find where the last point was. If it doesn't exist, use the start point
                start_corner = projected_points[target_corner] \
                    if target_corner in projected_points else start_point
                flattened_corner = start_corner + length * (cos(angle) +
                                                            1j * sin(angle))
                projected_points[corner] = flattened_corner
                start_point = flattened_corner
                flattened.append(flattened_corner)
            poly_paths.append(
                Path(*[
                    Line(start=flattened[j], end=flattened[j - 1])
                    for j in range(3)
                ]))

        dwg = Drawing("mesh{}.svg".format(i), profile='tiny')
        for poly_path in poly_paths:
            dwg.add(
                dwg.path(
                    **{
                        'd': poly_path.d(),
                        'fill': "none",
                        'stroke-width': 4,
                        'stroke': rgb(0, 0, 0)
                    }))
        bbox = calc_overall_bbox(poly_paths)
        width, height = abs(bbox[1] - bbox[0]), abs(bbox[3] - bbox[2])
        dwg.viewbox(min(bbox[0], bbox[1]), min(bbox[2], bbox[3]), width,
                    height)
        dwg.save()
示例#24
0
	def process(self):
		"""
		Render an SVG histogram/bar chart using a previous frequency analysis
		as input.
		"""
		self.dataset.update_status("Reading source file")
		header = self.parameters.get("header", self.options["header"]["default"])
		max_posts = 0

		# collect post numbers per month
		intervals = {}
		for post in self.iterate_csv_items(self.source_file):
			intervals[post["date"]] = int(post["frequency"])
			max_posts = max(max_posts, int(post["frequency"]))

		if len(intervals) <= 1:
			self.dataset.update_status("Not enough data available for a histogram; need more than one time series.")
			self.dataset.finish(0)
			return

		self.dataset.update_status("Cleaning up data")
		(missing, intervals) = pad_interval(intervals)

		# create histogram
		self.dataset.update_status("Drawing histogram")

		# you may change the following four variables to adjust the graph dimensions
		width = 1024
		height = 786
		y_margin = 75
		x_margin = 50
		x_margin_left = x_margin * 2
		tick_width = 5

		fontsize_normal = int(height / 40)
		fontsize_small = int(height / 75)

		# better don't touch the following
		line_width = round(width / 512)
		y_margin_top = 150 if header else 50
		y_height = height - (y_margin + y_margin_top)
		x_width = width - (x_margin + x_margin_left)
		canvas = Drawing(filename=str(self.dataset.get_results_path()), size=(width, height),
						 style="font-family:monospace;font-size:%ipx" % fontsize_normal)

		# normalize the Y axis to a multiple of a power of 10
		magnitude = pow(10, len(str(max_posts)) - 1)  # ew
		max_neat = math.ceil(max_posts / magnitude) * magnitude
		self.dataset.update_status("Max (normalized): %i (%i) (magnitude: %i)" % (max_posts, max_neat, magnitude))

		# draw border
		canvas.add(Rect(
			insert=(0, 0),
			size=(width, height),
			stroke="#000",
			stroke_width=line_width,
			fill="#FFF"
		))

		# draw header on a black background if needed
		if header:
			if len(header) > 40:
				header = header[:37] + "..."

			header_rect_height = (y_margin_top / 1.5)
			header_fontsize = (width / len(header))

			header_container = SVG(insert=(0, 0), size=(width, header_rect_height))
			header_container.add(Rect(
				insert=(0, 0),
				size=(width, header_rect_height),
				fill="#000"
			))
			header_container.add(Text(
				insert=("50%", "50%"),
				text=header,
				dominant_baseline="middle",
				text_anchor="middle",
				fill="#FFF",
				style="font-size:%i" % header_fontsize
			))
			canvas.add(header_container)

		# horizontal grid lines
		for i in range(0, 10):
			offset = (y_height / 10) * i
			canvas.add(Line(
				start=(x_margin_left, y_margin_top + offset),
				end=(width - x_margin, y_margin_top + offset),
				stroke="#EEE",
				stroke_width=line_width
			))

		# draw bars
		item_width = (width - (x_margin + x_margin_left)) / len(intervals)
		item_height = (height - y_margin - y_margin_top)
		bar_width = item_width * 0.9
		x = x_margin_left + (item_width / 2) - (bar_width / 2)

		if bar_width >= 8:
			arc_adjust = max(8, int(item_width / 5)) / 2
		else:
			arc_adjust = 0

		for interval in intervals:
			posts = int(intervals[interval])
			bar_height = ((posts / max_neat) * item_height)
			self.dataset.update_status("%s: %i posts" % (interval, posts))
			bar_top = height - y_margin - bar_height
			bar_bottom = height - y_margin

			if bar_height == 0:
				x += item_width
				continue

			bar = Path(fill="#000")
			bar.push("M %f %f" % (x, bar_bottom))
			bar.push("L %f %f" % (x, bar_top + (arc_adjust if bar_height > arc_adjust else 0)))
			if bar_height > arc_adjust > 0:
				control = (x, bar_top)
				bar.push("C %f %f %f %f %f %f" % (*control, *control, x + arc_adjust, bar_top))
			bar.push("L %f %f" % (x + bar_width - arc_adjust, height - y_margin - bar_height))
			if bar_height > arc_adjust > 0:
				control = (x + bar_width, bar_top)
				bar.push("C %f %f %f %f %f %f" % (*control, *control, x + bar_width, bar_top + arc_adjust))
			bar.push("L %f %f" % (x + bar_width, height - y_margin))
			bar.push("Z")
			canvas.add(bar)

			x += item_width

		# draw X and Y axis
		canvas.add(Line(
			start=(x_margin_left, height - y_margin),
			end=(width - x_margin, height - y_margin),
			stroke="#000",
			stroke_width=2
		))
		canvas.add(Line(
			start=(x_margin_left, y_margin_top),
			end=(x_margin_left, height - y_margin),
			stroke="#000",
			stroke_width=2
		))

		# draw ticks on Y axis
		for i in range(0, 10):
			offset = (y_height / 10) * i
			canvas.add(Line(
				start=(x_margin_left - tick_width, y_margin_top + offset),
				end=(x_margin_left, y_margin_top + offset),
				stroke="#000",
				stroke_width=line_width
			))

		# draw ticks on X axis
		for i in range(0, len(intervals)):
			offset = (x_width / len(intervals)) * (i + 0.5)
			canvas.add(Line(
				start=(x_margin_left + offset, height - y_margin),
				end=(x_margin_left + offset, height - y_margin + tick_width),
				stroke="#000",
				stroke_width=line_width
			))

		# prettify

		# y labels
		origin = (x_margin_left / 2)
		step = y_height / 10
		for i in range(0, 11):
			label = str(int((max_neat / 10) * i))
			labelsize = (len(label) * fontsize_normal * 1.25, fontsize_normal)
			label_x = origin - (tick_width * 2)
			label_y = height - y_margin - (i * step) - (labelsize[1] / 2)
			label_container = SVG(
				insert=(label_x, label_y),
				size=(x_margin_left / 2, x_margin_left / 5)
			)
			label_container.add(Text(
				insert=("100%", "50%"),
				text=label,
				dominant_baseline="middle",
				text_anchor="end"
			))
			canvas.add(label_container)

		# x labels
		label_width = max(fontsize_small * 6, item_width)
		label_x = x_margin_left
		label_y = height - y_margin + (tick_width * 2)
		next = 0
		for interval in intervals:
			if len(interval) == 7:
				label = month_abbr[int(interval[5:7])] + "\n" + interval[0:4]
			elif len(interval) == 10:
				label = str(int(interval[8:10])) + month_abbr[int(interval[5:7])] + "\n" + interval[0:4]
			else:
				label = interval.replace("-", "\n")

			if label_x > next:
				shift = 0
				for line in label.split("\n"):
					label_container = SVG(
						insert=(label_x + (item_width / 2) - (label_width / 2), label_y + (tick_width * 2)),
						size=(label_width, y_margin), overflow="visible")
					label_container.add(Text(
						insert=("50%", "0%"),
						text=line,
						dominant_baseline="middle",
						text_anchor="middle",
						baseline_shift=-shift
					))
					shift += fontsize_small * 2
					canvas.add(label_container)
					next = label_x + (label_width * 0.9)
			label_x += item_width

		# 4cat logo
		label = "made with 4cat - 4cat.oilab.nl"
		footersize = (fontsize_small * len(label) * 0.7, fontsize_small * 2)
		footer = SVG(insert=(width - footersize[0], height - footersize[1]), size=footersize)
		footer.add(Rect(insert=(0, 0), size=("100%", "100%"), fill="#000"))
		footer.add(Text(
			insert=("50%", "50%"),
			text=label,
			dominant_baseline="middle",
			text_anchor="middle",
			fill="#FFF",
			style="font-size:%i" % fontsize_small
		))
		canvas.add(footer)

		canvas.save(pretty=True)

		self.dataset.update_status("Finished")
		self.dataset.finish(len(intervals))
示例#25
0
def nest(output, files, wbin, hbin, enclosing_rectangle=False):

    packer = newPacker()

    def float2dec(x):
        return _float2dec(x, 4)

    def bbox_paths(paths):
        bbox = None
        for p in paths:
            p_bbox = p.bbox()
            if bbox is None:
                bbox = p_bbox
            else:
                bbox = (min(p_bbox[0], bbox[0]), max(p_bbox[1], bbox[1]),
                        min(p_bbox[2], bbox[2]), max(p_bbox[3], bbox[3]))
        return tuple(float2dec(x) for x in bbox)

    all_paths = {}
    for svg\
            in files:
        paths, attributes = svg2paths(svg)
        bbox = bbox_paths(paths)
        for i in range(files[svg]):
            rid = svg + str(i)
            all_paths[rid] = {'paths': paths, 'bbox': bbox}
            print(rid)
            packer.add_rect(bbox[1] - bbox[0], bbox[3] - bbox[2], rid=rid)

    print('Rectangle packing...')
    while True:
        packer.add_bin(wbin, hbin)
        packer.pack()
        rectangles = {r[5]: r for r in packer.rect_list()}
        if len(rectangles) == len(all_paths):
            break
        else:
            print('not enough space in the bin, adding ')

    combineds = {}

    print('packing into SVGs...')
    for rid, obj in all_paths.items():
        paths = obj['paths']
        bbox = obj['bbox']
        group = Group()

        width, height = (float2dec(bbox[1] - bbox[0]),
                         float2dec(bbox[3] - bbox[2]))
        bin, x, y, w, h, _ = rectangles[rid]
        if bin not in combineds:
            svg_file = output
            if bin != 0:
                splitext = os.path.splitext(svg_file)
                svg_file = splitext[0] + '.%s' % bin + splitext[1]
            dwg = Drawing(svg_file,
                          profile='tiny',
                          size=('%smm' % wbin, '%smm' % hbin),
                          viewBox="0 0 %s %s" % (wbin, hbin))
            combineds[bin] = dwg

        combined = combineds[bin]

        if (width > height and w > h) or \
                (width < height and w < h) or \
                (width == height and w == h):
            rotate = 0
            dx = -bbox[0]
            dy = -bbox[2]
        else:
            rotate = 90
            dx = -bbox[2]
            dy = -bbox[0]

        for p in paths:
            path = Path(d=p.d())
            path.stroke(color='red', width='1')
            path.fill(opacity=0)
            group.add(path)

        group.translate(x + dx, y + dy)
        group.rotate(rotate)
        combined.add(group)

    for combined in combineds.values():
        if enclosing_rectangle:
            r = Rect(size=(wbin, hbin))
            r.fill(opacity=0)
            r.stroke(color='lightgray')
            combined.add(r)

        print('SVG saving...')
        combined.save(pretty=True)
示例#26
0
r = corner_ruler(scale=scale,
                 length=100,
                 major_interval=10,
                 minor_per_major=10)
translate_inch(r, tool_size - diag_shift, diag_shift)
utm_tool.add(r)

# Small map ruler
scale = 8000
diag_shift = 0.85
r = corner_ruler(scale=scale, length=200, major_interval=50, minor_per_major=5)
translate_inch(r, tool_size - diag_shift, diag_shift)
utm_tool.add(r)

#-------------------------------------------------------------------------------
# Repliacte onto printout
#-------------------------------------------------------------------------------
dwg = Drawing(size=(8.5 * inch, 11 * inch))

defs = Defs()
defs.add(utm_tool)
dwg.add(defs)

for xi in range(3):
    for yi in range(4):
        dwg.add(UseInch(
            utm_tool,
            (0.5 + xi * 2.5, 0.5 + yi * 2.5),
        ))
write_to_pdf(dwg, "UTM-roamer-ruler-VT.pdf")
示例#27
0
    def process(self):
        """
		This takes a 4CAT results file as input, and outputs a plain text file
		containing all post bodies as one continuous string, sanitized.
		"""

        link_regex = re.compile(r"https?://[^\s]+")
        delete_regex = re.compile(r"[^a-zA-Z)(.,\n -]")

        # settings
        strip_urls = self.parameters.get("strip-urls",
                                         self.options["strip-urls"]["default"])
        strip_symbols = self.parameters.get(
            "strip-symbols", self.options["strip-symbols"]["default"])
        sides = self.parameters.get("sides", self.options["sides"]["default"])
        self.align = self.parameters.get("align",
                                         self.options["align"]["default"])
        window = convert_to_int(
            self.parameters.get("window", self.options["window"]["default"]),
            5) + 1
        query = self.parameters.get("query", self.options["query"]["default"])
        self.limit = convert_to_int(
            self.parameters.get("limit", self.options["limit"]["default"]),
            100)

        left_branches = []
        right_branches = []

        # do some validation
        if not query.strip() or re.sub(r"\s", "", query) != query:
            self.dataset.update_status(
                "Invalid query for word tree generation. Query cannot be empty or contain whitespace."
            )
            self.dataset.finish(0)
            return

        window = min(window, self.options["window"]["max"] + 1)
        window = max(1, window)

        # find matching posts
        processed = 0
        for post in self.iterate_csv_items(self.source_file):
            processed += 1
            if processed % 500 == 0:
                self.dataset.update_status(
                    "Processing and tokenising post %i" % processed)
            body = post["body"]

            if strip_urls:
                body = link_regex.sub("", body)

            if strip_symbols:
                body = delete_regex.sub("", body)

            body = word_tokenize(body)
            positions = [
                i for i, x in enumerate(body) if x.lower() == query.lower()
            ]

            # get lists of tokens for both the left and right side of the tree
            # on the left side, all lists end with the query, on the right side,
            # they start with the query
            for position in positions:
                right_branches.append(body[position:position + window])
                left_branches.append(body[max(0, position - window):position +
                                          1])

        # Some settings for rendering the tree later
        self.step = self.fontsize * 0.6  # approximately the width of a monospace char
        self.gap = (7 * self.step)  # space for lines between nodes
        width = 1  # will be updated later

        # invert the left side of the tree (because that's the way we want the
        # branching to work for that side)
        # we'll visually invert the nodes in the tree again later
        left_branches = [list(reversed(branch)) for branch in left_branches]

        # first create vertical slices of tokens per level
        self.dataset.update_status("Generating token tree from posts")
        levels_right = [{} for i in range(0, window)]
        levels_left = [{} for i in range(0, window)]
        tokens_left = []
        tokens_right = []

        # for each "level" (each branching point representing a level), turn
        # tokens into nodes, record the max amount of occurences for any
        # token in that level, and keep track of what nodes are in which level.
        # The latter is needed because a token may occur multiple times, at
        # different points in the graph. Do this for both the left and right
        # side of the tree.
        for i in range(0, window):
            for branch in right_branches:
                if i >= len(branch):
                    continue

                token = branch[i].lower()
                if token not in levels_right[i]:
                    parent = levels_right[i - 1][branch[
                        i - 1].lower()] if i > 0 else None
                    levels_right[i][token] = Node(token,
                                                  parent=parent,
                                                  occurrences=1,
                                                  is_top_root=(parent is None))
                    tokens_right.append(levels_right[i][token])
                else:
                    levels_right[i][token].occurrences += 1

                occurrences = levels_right[i][token].occurrences
                self.max_occurrences[i] = max(
                    occurrences, self.max_occurrences[i]
                ) if i in self.max_occurrences else occurrences

            for branch in left_branches:
                if i >= len(branch):
                    continue

                token = branch[i].lower()
                if token not in levels_left[i]:
                    parent = levels_left[i - 1][branch[
                        i - 1].lower()] if i > 0 else None
                    levels_left[i][token] = Node(token,
                                                 parent=parent,
                                                 occurrences=1,
                                                 is_top_root=(parent is None))
                    tokens_left.append(levels_left[i][token])
                else:
                    levels_left[i][token].occurrences += 1

                occurrences = levels_left[i][token].occurrences
                self.max_occurrences[i] = max(
                    occurrences, self.max_occurrences[i]
                ) if i in self.max_occurrences else occurrences

        # nodes that have no siblings can be merged with their parents, else
        # the graph becomes unnecessarily large with lots of single-word nodes
        # connected to single-word nodes. additionally, we want the nodes with
        # the most branches to be sorted to the top, and then only retain the
        # most interesting (i.e. most-occurring) branches
        self.dataset.update_status("Merging and sorting tree nodes")
        for token in tokens_left:
            self.merge_upwards(token)
            self.sort_node(token)
            self.limit_subtree(token)

        for token in tokens_right:
            self.merge_upwards(token)
            self.sort_node(token)
            self.limit_subtree(token)

        # somewhat annoyingly, anytree does not simply delete nodes detached
        # from the tree in the previous steps, but makes them root nodes. We
        # don't need these root nodes (we only need the original root), so the
        # next step is to remove all root nodes that are not the main root.
        # We cannot modify a list in-place, so make a new list with the
        # relevant nodes
        level_sizes = {}
        filtered_tokens_right = []
        for token in tokens_right:
            if token.is_root and not token.is_top_root:
                continue

            filtered_tokens_right.append(token)

        filtered_tokens_left = []
        for token in tokens_left:
            if token.is_root and not token.is_top_root:
                continue

            filtered_tokens_left.append(token)

        # now we know which nodes are left, and can therefore determine how
        # large the canvas needs to be - this is based on the max number of
        # branches found on any level of the tree, in other words, the number
        # of "terminal nodes"
        height_left = self.whitespace * self.fontsize * max([
            self.max_breadth(node)
            for node in filtered_tokens_left if node.is_top_root
        ])
        height_right = self.whitespace * self.fontsize * max([
            self.max_breadth(node)
            for node in filtered_tokens_right if node.is_top_root
        ])
        height = max(height_left, height_right)

        canvas = Drawing(str(self.dataset.get_results_path()),
                         size=(width, height),
                         style="font-family:monospace;font-size:%ipx" %
                         self.fontsize)

        # the nodes on the left side of the graph now have the wrong word order,
        # because we reversed them earlier to generate the correct tree
        # hierarchy - now reverse the node labels so they are proper language
        # again
        for token in tokens_left:
            self.invert_node_labels(token)

        wrapper = SVG(overflow="visible")

        self.dataset.update_status("Rendering tree to SVG file")
        if sides != "right":
            wrapper = self.render(wrapper, [
                token for token in filtered_tokens_left
                if token.is_root and token.children
            ],
                                  height=height,
                                  side=self.SIDE_LEFT)

        if sides != "left":
            wrapper = self.render(wrapper, [
                token for token in filtered_tokens_right
                if token.is_root and token.children
            ],
                                  height=height,
                                  side=self.SIDE_RIGHT)

        # things may have been rendered outside the canvas, in which case we
        # need to readjust the SVG properties
        wrapper.update({"x": 0 if self.x_min >= 0 else self.x_min * -1})
        canvas.update({"width": (self.x_max - self.x_min)})

        canvas.add(wrapper)
        canvas.save(pretty=True)

        self.dataset.update_status("Finished")
        self.dataset.finish(len(tokens_left) + len(tokens_right))
示例#28
0
文件: isoviz.py 项目: pgr-me/4cat
    def process(self):
        graphs = {}
        intervals = []

        smooth = self.parameters.get("smooth",
                                     self.options["smooth"]["default"])
        normalise_values = self.parameters.get(
            "normalise", self.options["normalise"]["default"])
        completeness = convert_to_int(
            self.parameters.get("complete",
                                self.options["complete"]["default"]), 0)
        graph_label = self.parameters.get("label",
                                          self.options["label"]["default"])
        top = convert_to_int(
            self.parameters.get("top", self.options["top"]["default"]), 10)

        # first gather graph data: each distinct item gets its own graph and
        # for each graph we have a sequence of intervals, each interval with
        # its own value
        first_date = "9999-99-99"
        last_date = "0000-00-00"

        with self.source_file.open() as input:
            reader = csv.DictReader(input)

            item_key = "text" if "text" in reader.fieldnames else "item"
            date_key = "time" if "time" in reader.fieldnames else "date"
            value_key = "value" if "value" in reader.fieldnames else "frequency"

        for row in self.iterate_csv_items(self.source_file):
            if row[item_key] not in graphs:
                graphs[row[item_key]] = {}

            # make sure the months and days are zero-padded
            interval = row.get(date_key, "")
            interval = "-".join([
                str(bit).zfill(2 if len(bit) != 4 else 4)
                for bit in interval.split("-")
            ])
            first_date = min(first_date, interval)
            last_date = max(last_date, interval)

            if interval not in intervals:
                intervals.append(interval)

            if interval not in graphs[row[item_key]]:
                graphs[row[item_key]][interval] = 0

            graphs[row[item_key]][interval] += float(row.get(value_key, 0))

        # first make sure we actually have something to render
        intervals = sorted(intervals)
        if len(intervals) <= 1:
            self.dataset.update_status(
                "Not enough data for a side-by-side over-time visualisation.")
            self.dataset.finish(0)
            return

        # only retain most-occurring series - sort by sum of all frequencies
        if len(graphs) > top:
            selected_graphs = {
                graph: graphs[graph]
                for graph in sorted(
                    graphs,
                    key=lambda x: sum(
                        [graphs[x][interval] for interval in graphs[x]]),
                    reverse=True)[0:top]
            }
            graphs = selected_graphs

        # there may be items that do not have values for all intervals
        # this will distort the graph, so the next step is to make sure all
        # graphs consist of the same continuous interval list
        missing = {graph: 0 for graph in graphs}
        for graph in graphs:
            missing[graph], graphs[graph] = pad_interval(
                graphs[graph],
                first_interval=first_date,
                last_interval=last_date)

        # now that's done, make sure the graph datapoints are in order
        intervals = sorted(list(graphs[list(graphs)[0]].keys()))

        # delete graphs that do not have the required amount of intervals
        # this is useful to get rid of outliers and items that only occur
        # very few times over the full interval
        if completeness > 0:
            intervals_required = len(intervals) * (completeness / 100)
            disqualified = []
            for graph in graphs:
                if len(intervals) - missing[graph] < intervals_required:
                    disqualified.append(graph)

            graphs = {
                graph: graphs[graph]
                for graph in graphs if graph not in disqualified
            }

        # determine max value per item, so we can normalize them later
        limits = {}
        max_limit = 0
        for graph in graphs:
            for interval in graphs[graph]:
                limits[graph] = max(limits.get(graph, 0),
                                    abs(graphs[graph][interval]))
                max_limit = max(max_limit, abs(graphs[graph][interval]))

        # order graphs by highest (or lowest) value)
        limits = {
            limit: limits[limit]
            for limit in sorted(limits, key=lambda l: limits[l])
        }
        graphs = {graph: graphs[graph] for graph in limits}

        if not graphs:
            # maybe nothing is actually there to be graphed
            self.dataset.update_status(
                "No items match the selection criteria - nothing to visualise."
            )
            self.dataset.finish(0)
            return None

        # how many vertical grid lines (and labels) are to be included at most
        # 12 is a sensible default because it allows one label per month for a full
        # year's data
        max_gridlines = 12

        # If True, label is put at the lower left bottom of the graph rather than
        # outside it. Automatically set to True if one of the labels is long, as
        # else the label would fall off the screen
        label_in_graph = max([len(item) for item in graphs]) > 30

        # determine how wide each interval should be
        # the graph has a minimum width - but the graph's width will be
        # extended if at this minimum width each item does not have the
        # minimum per-item width
        min_full_width = 600
        min_item_width = 1
        item_width = max(min_item_width, min_full_width / len(intervals))

        # determine how much space each graph should get
        # same trade-off as for the interval width
        min_full_height = 300
        min_item_height = 100
        item_height = max(min_item_height, min_full_height / len(graphs))

        # margin - this should be enough for the text labels to fit in
        margin = 75

        # this determines the "flatness" of the isometric projection and an be
        # tweaked for different looks - basically corresponds to how far the
        # camera is above the horizon
        plane_angle = 120

        # don't change these
        plane_obverse = radians((180 - plane_angle) / 2)
        plane_angle = radians(plane_angle)

        # okay, now determine the full graphic size with these dimensions projected
        # semi-isometrically. We can also use these values later for drawing for
        # drawing grid lines, et cetera. The axis widths and heights here are the
        # dimensions of the bounding box wrapping the isometrically projected axes.
        x_axis_length = (item_width * (len(intervals) - 1))
        y_axis_length = (item_height * len(graphs))

        x_axis_width = (sin(plane_angle / 2) * x_axis_length)
        y_axis_width = (sin(plane_angle / 2) * y_axis_length)
        canvas_width = x_axis_width + y_axis_width

        x_axis_height = (cos(plane_angle / 2) * x_axis_length)
        y_axis_height = (cos(plane_angle / 2) * y_axis_length)
        canvas_height = x_axis_height + y_axis_height

        # now we have the dimensions, the canvas can be instantiated
        canvas = Drawing(str(self.dataset.get_results_path()),
                         size=(canvas_width + margin,
                               canvas_height + (2 * margin)),
                         style="font-family:monospace")

        # draw gridlines - vertical
        gridline_x = y_axis_width
        gridline_y = margin + canvas_height

        step_x_horizontal = sin(plane_angle / 2) * item_width
        step_y_horizontal = cos(plane_angle / 2) * item_width
        step_x_vertical = sin(plane_angle / 2) * item_height
        step_y_vertical = cos(plane_angle / 2) * item_height

        # labels for x axis
        skip = max(1, int(len(intervals) / max_gridlines))
        for i in range(0, len(intervals)):
            if i % skip == 0:
                canvas.add(
                    Line(start=(gridline_x, gridline_y),
                         end=(gridline_x - y_axis_width,
                              gridline_y - y_axis_height),
                         stroke="grey",
                         stroke_width=0.25))

                # to properly position the rotated and skewed text a container
                # element is needed
                label1 = str(intervals[i])[0:4]
                center = (gridline_x, gridline_y)
                container = SVG(x=center[0] - 25,
                                y=center[1],
                                width="50",
                                height="1.5em",
                                overflow="visible")
                container.add(
                    Text(insert=("25%", "100%"),
                         text=label1,
                         transform="rotate(%f) skewX(%f)" %
                         (-degrees(plane_obverse), degrees(plane_obverse)),
                         text_anchor="middle",
                         baseline_shift="-0.75em",
                         style="font-weight:bold;"))

                if re.match(r"^[0-9]{4}-[0-9]{2}", intervals[i]):
                    label2 = month_abbr[int(str(intervals[i])[5:7])]
                    if re.match(r"^[0-9]{4}-[0-9]{2}-[0-9]{2}", intervals[i]):
                        label2 += " %i" % int(intervals[i][8:10])

                    container.add(
                        Text(insert=("25%", "100%"),
                             text=label2,
                             transform="rotate(%f) skewX(%f)" %
                             (-degrees(plane_obverse), degrees(plane_obverse)),
                             text_anchor="middle",
                             baseline_shift="-1.75em"))

                canvas.add(container)

            gridline_x += step_x_horizontal
            gridline_y -= step_y_horizontal

        # draw graphs as filled beziers
        top = step_y_vertical * 1.5
        graph_start_x = y_axis_width
        graph_start_y = margin + canvas_height

        # draw graphs in reverse order, so the bottom one is most in the
        # foreground (in case of overlap)
        for graph in reversed(list(graphs)):
            self.dataset.update_status("Rendering graph for '%s'" % graph)

            # path starting at lower left corner of graph
            area_graph = Path(fill=self.colours[self.colour_index])
            area_graph.push("M %f %f" % (graph_start_x, graph_start_y))
            previous_value = None

            graph_x = graph_start_x
            graph_y = graph_start_y
            for interval in graphs[graph]:
                # normalise value
                value = graphs[graph][interval]
                try:
                    limit = limits[graph] if normalise_values else max_limit
                    value = top * copysign(abs(value) / limit, value)
                except ZeroDivisionError:
                    value = 0

                if previous_value is None:
                    # vertical line upwards to starting value of graph
                    area_graph.push("L %f %f" %
                                    (graph_start_x, graph_start_y - value))
                elif not smooth:
                    area_graph.push("L %f %f" % (graph_x, graph_y - value))
                else:
                    # quadratic bezier from previous value to current value
                    control_left = (graph_x - (step_x_horizontal / 2),
                                    graph_y + step_y_horizontal -
                                    previous_value - (step_y_horizontal / 2))
                    control_right = (graph_x - (step_x_horizontal / 2),
                                     graph_y - value + (step_y_horizontal / 2))
                    area_graph.push("C %f %f %f %f %f %f" %
                                    (*control_left, *control_right, graph_x,
                                     graph_y - value))

                previous_value = value
                graph_x += step_x_horizontal
                graph_y -= step_y_horizontal

            # line to the bottom of the graph at the current Y position
            area_graph.push(
                "L %f %f" %
                (graph_x - step_x_horizontal, graph_y + step_y_horizontal))
            area_graph.push("Z")  # then close the Path
            canvas.add(area_graph)

            # add text labels - skewing is a bit complicated and we need a
            # "center" to translate the origins properly.
            if label_in_graph:
                insert = (graph_start_x + 5, graph_start_y - 10)
            else:
                insert = (graph_x - (step_x_horizontal) + 5,
                          graph_y + step_y_horizontal - 10)

            # we need to take the skewing into account for the translation
            offset_y = tan(plane_obverse) * insert[0]
            canvas.add(
                Text(insert=(0, 0),
                     text=graph,
                     transform="skewY(%f) translate(%f %f)" %
                     (-degrees(plane_obverse), insert[0],
                      insert[1] + offset_y)))

            # cycle colours, back to the beginning if all have been used
            self.colour_index += 1
            if self.colour_index >= len(self.colours):
                self.colour_index = 0

            graph_start_x -= step_x_vertical
            graph_start_y -= step_y_vertical

        # draw gridlines - horizontal
        gridline_x = 0
        gridline_y = margin + canvas_height - y_axis_height
        for graph in graphs:
            gridline_x += step_x_vertical
            gridline_y += step_y_vertical
            canvas.add(
                Line(start=(gridline_x, gridline_y),
                     end=(gridline_x + x_axis_width,
                          gridline_y - x_axis_height),
                     stroke="black",
                     stroke_width=1))

        # x axis
        canvas.add(
            Line(start=(y_axis_width, margin + canvas_height),
                 end=(canvas_width, margin + canvas_height - x_axis_height),
                 stroke="black",
                 stroke_width=2))

        if graph_label:
            canvas.add(
                Text(insert=((margin / 10), (margin / 2)),
                     text=graph_label,
                     style="font-size:2em;",
                     alignment_baseline="hanging"))

        # and finally save the SVG
        canvas.save(pretty=True)
        self.dataset.finish(len(graphs))
示例#29
0
def disvg(paths=None,
          colors=None,
          filename=None,
          stroke_widths=None,
          nodes=None,
          node_colors=None,
          node_radii=None,
          openinbrowser=True,
          timestamp=None,
          margin_size=0.1,
          mindim=600,
          dimensions=None,
          viewbox=None,
          text=None,
          text_path=None,
          font_size=None,
          attributes=None,
          svg_attributes=None,
          svgwrite_debug=False,
          paths2Drawing=False,
          baseunit='px'):
    """Creates (and optionally displays) an SVG file.

    REQUIRED INPUTS:
        :param paths - a list of paths

    OPTIONAL INPUT:
        :param colors - specifies the path stroke color.  By default all paths
        will be black (#000000).  This paramater can be input in a few ways
        1) a list of strings that will be input into the path elements stroke
            attribute (so anything that is understood by the svg viewer).
        2) a string of single character colors -- e.g. setting colors='rrr' is
            equivalent to setting colors=['red', 'red', 'red'] (see the
            'color_dict' dictionary above for a list of possibilities).
        3) a list of rgb 3-tuples -- e.g. colors = [(255, 0, 0), ...].

        :param filename - the desired location/filename of the SVG file
        created (by default the SVG will be named 'disvg_output.svg' or
        'disvg_output_<timestamp>.svg' and stored in the temporary
        directory returned by `tempfile.gettempdir()`.  See `timestamp`
        for information on the timestamp.

        :param stroke_widths - a list of stroke_widths to use for paths
        (default is 0.5% of the SVG's width or length)

        :param nodes - a list of points to draw as filled-in circles

        :param node_colors - a list of colors to use for the nodes (by default
        nodes will be red)

        :param node_radii - a list of radii to use for the nodes (by default
        nodes will be radius will be 1 percent of the svg's width/length)

        :param text - string or list of strings to be displayed

        :param text_path - if text is a list, then this should be a list of
        path (or path segments of the same length.  Note: the path must be
        long enough to display the text or the text will be cropped by the svg
        viewer.

        :param font_size - a single float of list of floats.

        :param openinbrowser -  Set to True to automatically open the created
        SVG in the user's default web browser.

        :param timestamp - if true, then the a timestamp will be
        appended to the output SVG's filename.  This is meant as a
        workaround for issues related to rapidly opening multiple
        SVGs in your browser using `disvg`. This defaults to true if
        `filename is None` and false otherwise.

        :param margin_size - The min margin (empty area framing the collection
        of paths) size used for creating the canvas and background of the SVG.

        :param mindim - The minimum dimension (height or width) of the output
        SVG (default is 600).

        :param dimensions - The (x,y) display dimensions of the output SVG.
        I.e. this specifies the `width` and `height` SVG attributes. Note that 
        these also can be used to specify units other than pixels. Using this 
        will override the `mindim` parameter.

        :param viewbox - This specifies the coordinated system used in the svg.
        The SVG `viewBox` attribute works together with the the `height` and 
        `width` attrinutes.  Using these three attributes allows for shifting 
        and scaling of the SVG canvas without changing the any values other 
        than those in `viewBox`, `height`, and `width`.  `viewbox` should be 
        input as a 4-tuple, (min_x, min_y, width, height), or a string 
        "min_x min_y width height".  Using this will override the `mindim` 
        parameter.

        :param attributes - a list of dictionaries of attributes for the input
        paths.  Note: This will override any other conflicting settings.

        :param svg_attributes - a dictionary of attributes for output svg.
        
        :param svgwrite_debug - This parameter turns on/off `svgwrite`'s 
        debugging mode.  By default svgwrite_debug=False.  This increases 
        speed and also prevents `svgwrite` from raising of an error when not 
        all `svg_attributes` key-value pairs are understood.
        
        :param paths2Drawing - If true, an `svgwrite.Drawing` object is 
        returned and no file is written.  This `Drawing` can later be saved 
        using the `svgwrite.Drawing.save()` method.

    NOTES:
        * The `svg_attributes` parameter will override any other conflicting 
        settings.

        * Any `extra` parameters that `svgwrite.Drawing()` accepts can be 
        controlled by passing them in through `svg_attributes`.

        * The unit of length here is assumed to be pixels in all variables.

        * If this function is used multiple times in quick succession to
        display multiple SVGs (all using the default filename), the
        svgviewer/browser will likely fail to load some of the SVGs in time.
        To fix this, use the timestamp attribute, or give the files unique
        names, or use a pause command (e.g. time.sleep(1)) between uses.

    SEE ALSO:
        * document.py
    """

    _default_relative_node_radius = 5e-3
    _default_relative_stroke_width = 1e-3
    _default_path_color = '#000000'  # black
    _default_node_color = '#ff0000'  # red
    _default_font_size = 12

    if filename is None:
        timestamp = True if timestamp is None else timestamp
        filename = os_path.join(gettempdir(), 'disvg_output.svg')

    dirname = os_path.abspath(os_path.dirname(filename))
    if not os_path.exists(dirname):
        makedirs(dirname)

    # append time stamp to filename
    if timestamp:
        fbname, fext = os_path.splitext(filename)
        tstamp = str(time()).replace('.', '')
        stfilename = os_path.split(fbname)[1] + '_' + tstamp + fext
        filename = os_path.join(dirname, stfilename)

    # check paths and colors are set
    if isinstance(paths, Path) or is_path_segment(paths):
        paths = [paths]
    if paths:
        if not colors:
            colors = [_default_path_color] * len(paths)
        else:
            assert len(colors) == len(paths)
            if isinstance(colors, str):
                colors = str2colorlist(colors,
                                       default_color=_default_path_color)
            elif isinstance(colors, list):
                for idx, c in enumerate(colors):
                    if is3tuple(c):
                        colors[idx] = "rgb" + str(c)

    # check nodes and nodes_colors are set (node_radii are set later)
    if nodes:
        if not node_colors:
            node_colors = [_default_node_color] * len(nodes)
        else:
            assert len(node_colors) == len(nodes)
            if isinstance(node_colors, str):
                node_colors = str2colorlist(node_colors,
                                            default_color=_default_node_color)
            elif isinstance(node_colors, list):
                for idx, c in enumerate(node_colors):
                    if is3tuple(c):
                        node_colors[idx] = "rgb" + str(c)

    # set up the viewBox and display dimensions of the output SVG
    # along the way, set stroke_widths and node_radii if not provided
    assert paths or nodes
    stuff2bound = []
    if viewbox:
        if not isinstance(viewbox, str):
            viewbox = '%s %s %s %s' % viewbox
        if dimensions is None:
            dimensions = viewbox.split(' ')[2:4]
    elif dimensions:
        dimensions = tuple(map(str, dimensions))

        def strip_units(s):
            return re.search(r'\d*\.?\d*', s.strip()).group()

        viewbox = '0 0 %s %s' % tuple(map(strip_units, dimensions))
    else:
        if paths:
            stuff2bound += paths
        if nodes:
            stuff2bound += nodes
        if text_path:
            stuff2bound += text_path
        xmin, xmax, ymin, ymax = big_bounding_box(stuff2bound)
        dx = xmax - xmin
        dy = ymax - ymin

        if dx == 0:
            dx = 1
        if dy == 0:
            dy = 1

        # determine stroke_widths to use (if not provided) and max_stroke_width
        if paths:
            if not stroke_widths:
                sw = max(dx, dy) * _default_relative_stroke_width
                stroke_widths = [sw] * len(paths)
                max_stroke_width = sw
            else:
                assert len(paths) == len(stroke_widths)
                max_stroke_width = max(stroke_widths)
        else:
            max_stroke_width = 0

        # determine node_radii to use (if not provided) and max_node_diameter
        if nodes:
            if not node_radii:
                r = max(dx, dy) * _default_relative_node_radius
                node_radii = [r] * len(nodes)
                max_node_diameter = 2 * r
            else:
                assert len(nodes) == len(node_radii)
                max_node_diameter = 2 * max(node_radii)
        else:
            max_node_diameter = 0

        extra_space_for_style = max(max_stroke_width, max_node_diameter)
        xmin -= margin_size * dx + extra_space_for_style / 2
        ymin -= margin_size * dy + extra_space_for_style / 2
        dx += 2 * margin_size * dx + extra_space_for_style
        dy += 2 * margin_size * dy + extra_space_for_style
        viewbox = "%s %s %s %s" % (xmin, ymin, dx, dy)

        if mindim is None:
            szx = "{}{}".format(dx, baseunit)
            szy = "{}{}".format(dy, baseunit)
        else:
            if dx > dy:
                szx = str(mindim) + baseunit
                szy = str(int(ceil(mindim * dy / dx))) + baseunit
            else:
                szx = str(int(ceil(mindim * dx / dy))) + baseunit
                szy = str(mindim) + baseunit
        dimensions = szx, szy

    # Create an SVG file
    if svg_attributes is not None:
        dimensions = (svg_attributes.get("width", dimensions[0]),
                      svg_attributes.get("height", dimensions[1]))
        debug = svg_attributes.get("debug", svgwrite_debug)
        dwg = Drawing(filename=filename,
                      size=dimensions,
                      debug=debug,
                      **svg_attributes)
    else:
        dwg = Drawing(filename=filename,
                      size=dimensions,
                      debug=svgwrite_debug,
                      viewBox=viewbox)

    # add paths
    if paths:
        for i, p in enumerate(paths):
            if isinstance(p, Path):
                ps = p.d()
            elif is_path_segment(p):
                ps = Path(p).d()
            else:  # assume this path, p, was input as a Path d-string
                ps = p

            if attributes:
                good_attribs = {'d': ps}
                for key in attributes[i]:
                    val = attributes[i][key]
                    if key != 'd':
                        try:
                            dwg.path(ps, **{key: val})
                            good_attribs.update({key: val})
                        except Exception as e:
                            warn(str(e))

                dwg.add(dwg.path(**good_attribs))
            else:
                dwg.add(
                    dwg.path(ps,
                             stroke=colors[i],
                             stroke_width=str(stroke_widths[i]),
                             fill='none'))

    # add nodes (filled in circles)
    if nodes:
        for i_pt, pt in enumerate([(z.real, z.imag) for z in nodes]):
            dwg.add(dwg.circle(pt, node_radii[i_pt], fill=node_colors[i_pt]))

    # add texts
    if text:
        assert isinstance(text, str) or (isinstance(text, list) and isinstance(
            text_path, list) and len(text_path) == len(text))
        if isinstance(text, str):
            text = [text]
            if not font_size:
                font_size = [_default_font_size]
            if not text_path:
                pos = complex(xmin + margin_size * dx, ymin + margin_size * dy)
                text_path = [Line(pos, pos + 1).d()]
        else:
            if font_size:
                if isinstance(font_size, list):
                    assert len(font_size) == len(text)
                else:
                    font_size = [font_size] * len(text)
            else:
                font_size = [_default_font_size] * len(text)
        for idx, s in enumerate(text):
            p = text_path[idx]
            if isinstance(p, Path):
                ps = p.d()
            elif is_path_segment(p):
                ps = Path(p).d()
            else:  # assume this path, p, was input as a Path d-string
                ps = p

            # paragraph = dwg.add(dwg.g(font_size=font_size[idx]))
            # paragraph.add(dwg.textPath(ps, s))
            pathid = 'tp' + str(idx)
            dwg.defs.add(dwg.path(d=ps, id=pathid))
            txter = dwg.add(dwg.text('', font_size=font_size[idx]))
            txter.add(txt.TextPath('#' + pathid, s))

    if paths2Drawing:
        return dwg

    dwg.save()

    # re-open the svg, make the xml pretty, and save it again
    xmlstring = md_xml_parse(filename).toprettyxml()
    with open(filename, 'w') as f:
        f.write(xmlstring)

    # try to open in web browser
    if openinbrowser:
        try:
            open_in_browser(filename)
        except:
            print("Failed to open output SVG in browser.  SVG saved to:")
            print(filename)
示例#30
0
#!/usr/bin/env python3

from svgwrite import Drawing
from xml.etree import ElementTree
from pathlib import Path

from cerebellum_value_map.shape import PathShape
from cerebellum_value_map.path_code import Move, Line

dirname = Path('results')
dirname.mkdir(exist_ok=True)
shape = PathShape(Move([100, 100]), Line([120, 140]), Line([160, 100]))
drawing_orig = Drawing(dirname.joinpath('shape.svg'),
                       size=(200, 200),
                       stroke='black',
                       stroke_width=1)
drawing_orig.add(shape.get_svg())
drawing_orig.save()


def test_flip():
    flip = shape.flip(90)
    xml = ElementTree.tostring(flip.get_svg().get_xml()).decode()
    assert xml == ('<path d="M 80.000, 100.000 L 60.000, 140.000 '
                   'L 20.000, 100.000 Z" />')
    drawing_flip = Drawing(dirname.joinpath('flip.svg'),
                           size=(200, 200),
                           stroke='black',
                           stroke_width=1)
    drawing_flip.add(flip.get_svg())
    drawing_flip.save()