示例#1
1
class Gauge(object):
    config = {
        'id'                    : None,
        'title'                 : 'Title',
        'titleFontColor'        : '#999999',
        'value'                 : 0,
        'valueFontColor'        : '#010101',
        'min'                   : 0,
        'max'                   : 100,
        'showMinMax'            : True,
        'gaugeWidthScale'       : 1.0,
        'gaugeColor'            : '#edebeb',
        'label'                 : "",
        'showInnerShadow'       : True,
        'shadowOpacity'         : 0.2,
        'shadowSize'            : 5,
        'shadowVerticalOffset'  : 3,
        'levelColors'           : ["#a9d70b", "#f9c802", "#ff0000"],
        'levelColorsGradient'   : True,
        'labelFontColor'        : "#b3b3b3",
        'showNeedle'            : False,
        'needleColor'           : "#b3b3b3",
        'canvasWidth'           : 400,
        'canvasHeight'          : 300,
    }

    def __init__(self, *args, **kwargs):
        for param_name, param_value in kwargs.items():
            if self.config.has_key(param_name):
                self.config[param_name] = param_value

        # Overflow values
        if self.config['value'] > self.config['max']:
            self.config['value'] = self.config['max']
        if self.config['value'] < self.config['min']:
            self.config['value'] = self.config['min']
        self.originalValue = self.config['value']

        self.canvas = Drawing(size=(self.config['canvasWidth'],
                                    self.config['canvasHeight']))

        canvasW = self.config['canvasWidth']
        canvasH = self.config['canvasHeight']

        self.canvas.add(self.canvas.rect(insert=(0, 0),
                                         size=(canvasW, canvasH),
                                         stroke="none",
                                         fill="#ffffff"))

        # widget dimensions
        widgetW, widgetH = None, None
        if ((canvasW / canvasH) > 1.25):
            widgetW = 1.25 * canvasH
            widgetH = canvasH
        else:
            widgetW = canvasW
            widgetH = canvasW / 1.25

        # delta 
        dx = (canvasW - widgetW)/2
        dy = (canvasH - widgetH)/2

        # title 
        titleFontSize = ((widgetH / 8) > 10) and (widgetH / 10) or 10
        titleX = dx + widgetW / 2
        titleY = dy + widgetH / 6.5

        # value 
        valueFontSize = ((widgetH / 6.4) > 16) and (widgetH / 6.4) or 16
        valueX = dx + widgetW / 2
        valueY = dy + widgetH / 1.4

        # label 
        labelFontSize = ((widgetH / 16) > 10) and (widgetH / 16) or 10
        labelX = dx + widgetW / 2
        labelY = valueY + valueFontSize / 2 + 6

        # min 
        minFontSize = ((widgetH / 16) > 10) and (widgetH / 16) or 10
        minX = dx + (widgetW / 10) + (widgetW / 6.666666666666667 * self.config['gaugeWidthScale']) / 2
        minY = dy + widgetH / 1.126760563380282

        # max
        maxFontSize = ((widgetH / 16) > 10) and (widgetH / 16) or 10
        maxX = dx + widgetW - (widgetW / 10) - (widgetW / 6.666666666666667 * self.config['gaugeWidthScale']) / 2
        maxY = dy + widgetH / 1.126760563380282

        # parameters
        self.params = {
            'canvasW'         : canvasW,
            'canvasH'         : canvasH,
            'widgetW'         : widgetW,
            'widgetH'         : widgetH,
            'dx'              : dx,
            'dy'              : dy,
            'titleFontSize'   : titleFontSize,
            'titleX'          : titleX,
            'titleY'          : titleY,
            'valueFontSize'   : valueFontSize,
            'valueX'          : valueX,
            'valueY'          : valueY,
            'labelFontSize'   : labelFontSize,
            'labelX'          : labelX,
            'labelY'          : labelY,
            'minFontSize'     : minFontSize,
            'minX'            : minX,
            'minY'            : minY,
            'maxFontSize'     : maxFontSize,
            'maxX'            : maxX,
            'maxY'            : maxY
        }

        # gauge
        self.gauge = self.gauge_path(self.config['max'], self.config['min'], self.config['max'],
                                     self.params['widgetW'], self.params['widgetH'], self.params['dx'],
                                     self.params['dy'], self.config['gaugeWidthScale'],
                                     stroke='none',
                                     fill=self.config['gaugeColor'])

        self.canvas.add(self.gauge)

        # level
        percent_value = (self.config['value'] - self.config['min']) / (self.config['max'] - self.config['min'])
        self.level = self.gauge_path(self.config['value'], self.config['min'], self.config['max'],
                                     self.params['widgetW'], self.params['widgetH'], self.params['dx'],
                                     self.params['dy'], self.config['gaugeWidthScale'],
                                     stroke='none',
                                     fill=self.get_color_for_value(percent_value,
                                                                   self.config['levelColors'],
                                                                   self.config['levelColorsGradient']))
        self.canvas.add(self.level)

        # needle
        if self.config['showNeedle']:
            self.needle = self.needle_path(self.config['value'], self.config['min'], self.config['max'],
                                           self.params['widgetW'], self.params['widgetH'], self.params['dx'],
                                           self.params['dy'], self.config['gaugeWidthScale'],
                                           stroke='none',
                                           fill=self.config['needleColor'])
            self.canvas.add(self.needle)

        # Value
        else:
            text_config = {
                "font-size"     : "%d" % self.params['valueFontSize'],
                "font-weight"   : "bold",
                "font-family"   : "Arial",
                "fill"          : self.config['valueFontColor'],
                "fill-opacity"  : "1",
                "text-anchor"  : 'middle'
            }
            value_text = self.canvas.text('',
                                          insert=('%d' % self.params['valueX'],
                                                  '%d' % self.params['valueY']),
                                          **text_config)
            value_tspan = self.canvas.tspan(self.originalValue,
                                            dy=[8])
            value_text.add(value_tspan)
            self.canvas.add(value_text)

        # Add min & max value
        self.show_minmax()


    def save(self, path):
        svg = self.canvas.tostring()
        svg2png = getattr(cairosvg, 'svg2png')
        png_byte = svg2png(bytestring=svg)
        f = open(path,'w')
        f.write(png_byte)
        f.close()


    def gauge_path(self, value, val_min, val_max, w, h, dx, dy, gws, **extra):
        alpha = (1 - (value - val_min) / (val_max - val_min)) * math.pi
        Ro = w / 2 - w / 10
        Ri = Ro - w / 6.666666666666667 * gws
        Cx = w / 2 + dx
        Cy = h / 1.25 + dy

        Xo = w / 2 + dx + Ro * math.cos(alpha)
        Yo = h - (h - Cy) + dy - Ro * math.sin(alpha)
        Xi = w / 2 + dx + Ri * math.cos(alpha)
        Yi = h - (h - Cy) + dy - Ri * math.sin(alpha)

        path = []
        path.append(u"M%d,%d " % ((Cx - Ri), Cy))
        path.append(u"L%d,%d " % ((Cx - Ro), Cy))
        path.append(u"A%d,%d 0 0,1 %d,%d " % (Ro, Ro, Xo, Yo))
        path.append(u"L%d,%d " % (Xi, Yi))
        path.append(u"A%d,%d 0 0,0 %d,%d " % (Ri, Ri, (Cx - Ri), Cy))
        path.append(u"z ")

        return Path(d=path, **extra)

    def needle_path(self, value, val_min, val_max, w, h, dx, dy, gws, **extra):
        xO = w / 2 + dx
        yO = h / 1.25 + dy

        Rext = w / 2 - w / 10
        Rint = Rext - w / 6.666666666666667 * gws

        x_offset = xO
        y_offset = h - (h - yO) + dy

        val = (value - val_min) / (val_max - val_min)

        angle_b = val<0.5 and val*math.pi or (math.pi - val*math.pi) # Angle de la pointe
        angle_a = math.pi/2 - angle_b
        angle_c = math.pi/2 - angle_b

        rayon_base = 7
        rayon_b = Rint + (Rext-Rint)*10/100

        xA = x_offset + -1 * rayon_base * math.cos(angle_a)
        yA = y_offset - (val<0.5 and -1 or 1) * rayon_base * math.sin(angle_a)

        xC = x_offset + 1 * rayon_base * math.cos(angle_c)
        yC = y_offset - (val<0.5 and 1 or -1) * rayon_base * math.sin(angle_c)

        xB = x_offset + (val<0.5 and -1 or 1) * rayon_b * math.cos(angle_b)
        yB = y_offset - rayon_b * math.sin(angle_b)

        path = []
        path.append(u"M%d,%d " % (xA, yA))
        path.append(u"L%d,%d " % (xB, yB))
        path.append(u"L%d,%d " % (xC, yC))
        path.append(u"A%d,%d 0 1,1 %d,%d " % (rayon_base, rayon_base, xA, yA))
        path.append(u"z ")

        return Path(d=path, **extra)

    def get_color_for_value(self, pct, color, grad):
        no = len(color);
        if no == 1: return color[0]

        HEX = r'[a-fA-F\d]{2}'
        HEX_COLOR = r'#(?P<red>%(hex)s)(?P<green>%(hex)s)(?P<blue>%(hex)s)' % {'hex': HEX}
        inc = grad and (1 / (no - 1)) or (1 / no)
        colors = []
        i = 0
        while i < no:
            percentage = (grad) and (inc * i) or (inc * (i + 1))
            parts = re.match(HEX_COLOR,color[i]).groupdict()

            rval = int(parts['red'], 16)
            gval = int(parts['green'], 16)
            bval = int(parts['blue'], 16)
            colors.append({
                'pct': percentage,
                'color': {
                    'r': rval,
                    'g': gval,
                    'b': bval
                 }
            })
            i+=1

        if pct == 0:
            return 'rgb(%d,%d,%d)' % (colors[0]['color']['r'],
                                      colors[0]['color']['g'],
                                      colors[0]['color']['b'])

        i = 0
        while i < len(colors):
            if pct <= colors[i]['pct']:
                if (grad == True):
                    lower = colors[i-1]
                    upper = colors[i]
                    _range = upper['pct'] - lower['pct']
                    rangePct = (pct - lower['pct']) / _range
                    pctLower = 1 - rangePct
                    pctUpper = rangePct
                    color = {
                      'r': math.floor(lower['color']['r'] * pctLower + upper['color']['r'] * pctUpper),
                      'g': math.floor(lower['color']['g'] * pctLower + upper['color']['g'] * pctUpper),
                      'b': math.floor(lower['color']['b'] * pctLower + upper['color']['b'] * pctUpper)
                    }
                    return 'rgb(%d,%d,%d)' % (color['r'], color['g'], color['b'])
                else:
                    return 'rgb(%d,%d,%d)' % (colors[i]['color']['r'],
                                              colors[i]['color']['g'],
                                              colors[i]['color']['b']) 
            i+=1

    def show_minmax(self):
        # min
        txtMin_config = {
            "font-size"    : '%d' % self.params['minFontSize'],
            "font-weight"  : "normal",
            "font-family"  : "Arial",
            "fill"         : self.config['labelFontColor'],
            "fill-opacity" : self.config['showMinMax'] and "1" or "0",
            "text-anchor"  : 'middle'
        }
        txtMin = self.canvas.text(self.config['min'],
                                  insert=(self.params['minX'],
                                          self.params['minY']),
                                  **txtMin_config)

        self.canvas.add(txtMin)

        # max
        txtMax_config = {
            "font-size"    : '%d' % self.params['maxFontSize'],
            "font-weight"  :"normal",
            "font-family"  :"Arial",
            "fill"         : self.config['labelFontColor'],   
            "fill-opacity" : self.config['showMinMax'] and "1" or "0",
            "text-anchor"  : 'middle'
        }
        txtMax = self.canvas.text(self.config['max'],
                                  insert=(self.params['maxX'],
                                          self.params['maxY']),
                                  **txtMax_config)
        self.canvas.add(txtMax)
示例#2
0
def profile(id: str, format: str):
    drawing = Drawing()

    w = 320
    h = 320

    drawing["width"] = "%dpx" % w
    drawing["height"] = "%dpx" % h

    prng = Random(id)

    background: Renderable
    if format == "png":
        background = random_background(prng, w, h, local_paths=format == "png")
    else:
        background_data = build_team_background(id, w, h)
        background = EmbeddedBackground(background_data, w, h)

    drawing.add(background.render(drawing))

    distance = w / 2
    profile_scale = 10 * (7 / 80)

    g = generate_profile_image(id, drawing)

    g.translate(distance, prng.uniform(h * 0.25, h * .5))
    g.rotate(prng.gauss(0, 20))
    g.scale(profile_scale)

    drawing.add(g)

    svg_code = drawing.tostring()

    if format == "svg":
        res = Response(drawing.tostring())
        res.headers["Content-Type"] = "image/svg+xml"
        res.headers["Cache-Control"] = "public, immutable, max-age=%d" % (
            86400 * 30)
        return res
    elif format == "png":
        requested_width: int
        try:
            requested_width = int(request.args.get("s"))
        except:
            requested_width = 320

        res = Response(
            svg2png(bytestring=bytearray(svg_code, "utf-8"),
                    scale=requested_width / w))
        res.headers["Content-Type"] = "image/png"
        res.headers["Cache-Control"] = "public, immutable, max-age=%d" % (
            86400 * 30)
        return res

    return None, 406
示例#3
0
def eye_pop() -> str:
    drawing = Drawing()

    e = PopEye(50, .6, .6, .5 * math.pi)
    g = e.render(drawing)
    g.translate(50, 50)

    drawing.add(g)

    return drawing.tostring()
示例#4
0
def generate_multiple(rows, cols, adj_scale, num_files, path):
    out = []
    previous = []

    for i in range(num_files):
        dwg = Drawing(filename=os.path.join(path, '{}.svg'.format(i)),
                      debug=True)
        previous.extend(generate(dwg, rows, cols, adj_scale, previous))
        #dwg.save()
        out.append(dwg.tostring())
    return out
示例#5
0
def leg_foot() -> str:
    drawing = Drawing()

    r = drawing.rect((-50, -50), (200, 200), stroke="black", fill="white")
    drawing.add(r)

    a = LegWithFoot(leg_length=50, leg_color="#606060", foot_color="#ff0000")
    g = a.render(drawing)
    g.translate(50, 50)

    drawing.add(g)
    return drawing.tostring()
示例#6
0
def arm_hand() -> str:
    drawing = Drawing()

    r = drawing.rect((-50, -50), (200, 200), stroke="black", fill="white")
    drawing.add(r)

    a = ArmWithHand(arm_length=50, arm_color="#606060", hand_color="#ff0000")
    g = a.render(drawing)
    g.translate(50, 50)

    drawing.add(g)
    return drawing.tostring()
    def render(
        self,
        data: DataContainer,
        accepted_media_type: Optional[str] = None,
        renderer_context: Optional[Dict[str, Union[str, int, float]]] = None,
    ) -> bytes:
        """
        :param data: point datum array, or dict that include those array in certain key.
            point datum represent by dict consists with x, y positions and value.
            when dict specified, need to specify corresponding key via view's attribute
            as 'svg_render_map'.
        :param accepted_media_type:
        :param renderer_context:
        :return: SVG formatted heatmap in bytestring.
        """
        renderer_context = renderer_context or {}
        view = renderer_context["view"]

        width = getattr(view, "width", self.default_width)
        height = getattr(view, "height", self.default_height)
        translatorArgs = {
            "colorScale": getattr(view, "color_scale", DEFAULT_COLOR_SCALE),
            "grid": getattr(view, "grid", GRID),
            "opacity": getattr(view, "opacity", OPACITY),
        }

        svg_render_map = getattr(view, "svg_render_map", None)
        bare_data = data if svg_render_map is None else data[svg_render_map]
        datagroup = self.translate(
            bare_data,
            width,
            height,
            x_key=getattr(view, "x_key", "x"),
            y_key=getattr(view, "y_key", "y"),
            v_key=getattr(view, "v_key", "value"),
            is_1d=getattr(view, "is_1d", False),
            **translatorArgs,
        )

        drawSize = (width, height)
        svgargs = getattr(view, "svgargs", {})
        drawing = Drawing(self.filename,
                          size=drawSize,
                          profile=self.svg_profile,
                          **svgargs)
        drawing.add(datagroup)

        return drawing.tostring().encode("ascii")
示例#8
0
def write_svg(svgpath, defs, paths, svg_attributes):
    # Create an SVG file
    assert svg_attributes is not None
    dwg = Drawing(filename=svgpath, **svg_attributes)
    doc = parseString(dwg.tostring())

    svg = doc.firstChild
    if defs != '':
        defsnode = parseString(defs).firstChild
        svg.replaceChild(defsnode, svg.firstChild)
    for i, path in enumerate(paths):
        svg.appendChild(path.cloneNode(deep=True))

    xmlstring = doc.toprettyxml()
    doc.unlink()
    with open(svgpath, 'w') as f:
        f.write(xmlstring)       
示例#9
0
def alien_glorb() -> str:
    drawing = Drawing()
    prng = random.Random()

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

    #a = GlorbAlien(position=(75, 75), size=60)
    a = random_glorb(prng, size=prng.randint(40, 80))
    g = a.render(drawing)
    g.translate(prng.randint(50, 150), prng.randint(50, 150))
    g.rotate(prng.gauss(0, 20))

    drawing.add(g)

    return drawing.tostring()
示例#10
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()
示例#11
0
 def __str__(self):
     draw = SVGDrawing(
         size=(self.size[0] * mm, self.size[1] * mm), profile='full',
         viewBox="0 0 {} {}".format(self.size[0], self.size[1]))
     for obj in self.objects:
         drawed = obj._draw()
         # object can consists of several objects
         if hasattr(drawed, '__iter__'):
             for svg_obj in drawed:
                 draw.add(svg_obj)
         else:
             draw.add(drawed)
         # defs section (masks, clips, markers, etc)
         defs = obj._defs()
         if defs:
             if hasattr(defs, '__iter__'):
                 for defs_item in defs:
                     draw.defs.add(defs_item)
             else:
                 draw.defs.add(defs)
     return draw.tostring()
示例#12
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")
示例#13
0
def disvg(paths=None, colors=None,
          filename=os_path.join(getcwd(), 'disvg_output.svg'),
          stroke_widths=None, nodes=None, node_colors=None, node_radii=None,
          timestamp=False, 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):
  """Takes in a list of paths and creates an SVG file containing said paths.
  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 stored in the current working
    directory and named 'disvg_output.svg').

    :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 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.
  """


  _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


  # append directory to filename (if not included)
  if os_path.dirname(filename) == '':
    filename = os_path.join(getcwd(), filename)

  # append time stamp to filename
  if timestamp:
    fbname, fext = os_path.splitext(filename)
    dirname = os_path.dirname(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 dx > dy:
      szx = str(mindim) + 'px'
      szy = str(int(ceil(mindim * dy / dx))) + 'px'
    else:
      szx = str(int(ceil(mindim * dx / dy))) + 'px'
      szy = str(mindim) + 'px'
    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


  ss = dwg.tostring()
  return ss


  # save svg
  if not os_path.exists(os_path.dirname(filename)):
    makedirs(os_path.dirname(filename))

  dwg.save(pretty=False)
示例#14
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
示例#15
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)
示例#16
0
def _draw_json_circuit(json_circuit,
                       unit: str = 'px',
                       round_index: int = 0,
                       show_clbits: bool = True,
                       bit_order: dict = None) -> Tuple[str, Tuple[int, int]]:
    """Draw a circuit represented as a JSON string.

    Args:
        json_circuit (dict): A quantum circuit in JSON format. This can be obtained with
                             the QISKit object qiskit.unroll.JsonBackend.
        unit         (str) : Unit used to draw the circuit. This parameter is not really
                             tested at the moment and values different from "px" could
                             cause the function to fail.
        round_index  (int) : Number of digits after the decimal point to keep in the SVG.
                             A value different from "0" could cause the function to fail,
                             this parameter need to be tested.
        show_clbits  (bool): True if the function should draw the classical bits, False
                             otherwise.
        bit_order    (dict): A Python dictionnary storing the bit ordering.
    Returns:
        Tuple[str, Tuple[int, int]]: (SVG, (width, height))
            - SVG: string representing the given circuit in SVG format.
            - width: computed width in pixels.
            - height: computed height in pixels.
    """

    # TEMPORARY FIX FOR THE QOBJ STRUCTURE
    # "issue": the json_circuit['header']['clbit_labels'] and
    # json_circuit['header']['qubit_labels'] don't have the same meaning and it
    # seems unintuitive. I fix that here. This part should be removed if the
    # qobj structure change to fix this behaviour.
    new_clbit_labels = list()
    for clbit_label in json_circuit['header']['clbit_labels']:
        for i in range(clbit_label[1]+1):
            new_clbit_labels.append([clbit_label[0], i])
    json_circuit['header']['clbit_labels'] = new_clbit_labels

    # Take the appropriate default value for bit_order if not provided by the
    # user.
    if bit_order is None:
        bit_order = dict()
        for qubit_index, qubit_label in enumerate(json_circuit['header']['qubit_labels']):
            bit_order["".join(map(str, qubit_label))] = qubit_index
        for clbit_index, clbit_label in enumerate(json_circuit['header']['clbit_labels']):
            bit_order["".join(map(str, clbit_label))] = clbit_index

    # Transform the bit_order structure in a more useful one: the bit_order structure
    # associates bit labels to their index, but in the json circuit we don't have bit
    # labels but rather bit indices. So we want to have a dictionnary mapping indices
    # in the json circuit to indices on the drawn circuit.
    bit_mapping = {'clbits': dict(), 'qubits': dict()}
    for qubit_index, qubit_label in enumerate(json_circuit['header']['qubit_labels']):
        bit_mapping['qubits'][qubit_index] = bit_order["".join(map(str, qubit_label))]
    for clbit_index, clbit_label in enumerate(json_circuit['header']['clbit_labels']):
        bit_mapping['clbits'][clbit_index] = bit_order["".join(map(str, clbit_label))]

    # Compute the width and height
    width, height = _helpers.get_dimensions(json_circuit, show_clbits)
    width, height = round(width, round_index), round(height, round_index)
    width_str, height_str = str(width)+unit, str(height)+unit

    # Create the drawing
    drawing = Drawing(size=(width_str, height_str))

    # Create the internal structure used by the drawing functions
    index_last_gate_on_reg = {'clbits': [0] * json_circuit['header'].get('number_of_clbits', 0),
                              'qubits': [0] * json_circuit['header'].get('number_of_qubits', 0)}

    # And draw!
    # First the registers names and lines
    _draw_registers_names_and_lines(drawing, width, json_circuit, show_clbits)
    # And then each gate
    for operation in json_circuit['operations']:
        _draw_gate(drawing, index_last_gate_on_reg, operation, show_clbits, bit_mapping)
    return (drawing.tostring(), (width, height))
示例#17
0
def species_marker(request, genus_name='-', species_name='-'):
    """
    Generate a SVG marker for a given species
    Args:
        request:
        genus_name:
        species_name:

    Returns:

    """
    if species_name == '-':
        color = 'bbbbbb'
        species_name = '?'
    else:
        color = species_to_color(genus_name, species_name)

    marker_width = 60
    marker_height = 100
    marker_border = 5
    stroke_width = 1
    line_color = 'black'
    marker_color = '#' + color
    bezier_length = marker_width / 3

    width = marker_width + marker_border * 2
    height = marker_height + marker_border * 2
    font_size = marker_height / 4

    arc_centre_drop = (marker_height / 3.5
                       )  # Distance from top of marker to rotation centre
    arc_radius_vertical = arc_centre_drop

    image = Drawing(size=('%dpx' % width, '%dpx' % height))
    marker = Path(stroke=line_color,
                  stroke_width=stroke_width,
                  fill=marker_color)

    marker.push(f'M {marker_border} {arc_centre_drop + marker_border} '
                )  # Left arc edge

    marker.push(
        f'C {marker_border} {arc_centre_drop + marker_border + bezier_length} '
        f'{width / 2 - bezier_length / 3} {height - marker_border - bezier_length} '
        f'{width / 2} {height - marker_border}'  # Point
    )

    marker.push(
        f'C {width / 2 + bezier_length / 3} {height - marker_border - bezier_length} '
        f'{width - marker_border} {arc_centre_drop + marker_border + bezier_length} '
        f'{width - marker_border} {arc_centre_drop + marker_border} '  # Right edge
    )  # Right arc edge

    marker.push_arc(target=(marker_border, arc_centre_drop + marker_border),
                    rotation=180,
                    r=(marker_width / 2, arc_radius_vertical),
                    absolute=True,
                    angle_dir='-')

    marker.push('z')
    image.add(marker)
    image.add(
        Text(species_name,
             (width / 2, marker_border + arc_centre_drop + marker_height / 20),
             font_family='Arial',
             font_size=font_size,
             dominant_baseline="middle",
             text_anchor="middle"))

    return HttpResponse(image.tostring(), content_type='image/svg+xml')
示例#18
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
示例#19
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)
示例#20
0
def write_to_pdf(dwg: Drawing, path: str):
    svg_str = dwg.tostring()
    svg = ET.fromstring(svg_str, etree.XMLParser())
    svgRenderer = SvgRenderer(None)
    drawing = svgRenderer.render(svg)
    renderPDF.drawToFile(drawing, path)
示例#21
0
class DrawTemplateSVG(object):
    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()

    # When you have only one or two items, we need  to fix the grid area
    def fixOneLineRoot(self, grid):
        key_min = min(grid.keys())
        rootSize = len(grid[key_min])
        size = len(grid)

        area = [0, 0, *self._area]

        if key_min <= 1 and rootSize <= 2:
            ajy = self._size[0] * 1.8
            ajx = self._size[0] * 0.5

            area[1] = 0 - ajx
            area[3] = self._area[1] + ajy

            if size <= 1:
                area[0] = 0 - ajx

        return area

    def setup(self):
        self.setup_brightness()
        self.setup_marker()

    def setup_brightness(self):
        self._symbols.brightness()
        self._symbols.brightness("darker", 0.4)

    def setup_marker(self):
        s = self._size[0] / 8

        opts = {'insert': (152, 3), 'size': (s, s)}

        self._symbols.asset_marker('markers.arrow', opts)

    def boundary_box(self, pos, node):
        opts = {'id': node.get('_id')}

        symbol = self._symbols.asset('boundaries_box.front',
                                     'default boundaries', (pos[0], pos[1]),
                                     self._size, opts)
        self.add(symbol)

    def draw_label(self, pos, node):
        text = HelperDrawLabel(self._size, self._matrix3d) \
            .label_by_node(pos, node)

        symbol = self._symbols.text(*text)
        self.add(symbol)

    def draw_app(self, item):
        cad1 = [item[x] for x in range(2)]
        node = item[3]

        pos = self._matrix3d.rotateNodeXY(item)()

        self.draw_root(item)
        self.draw_grid_size(cad1, item[2])
        self.grid_box(pos)
        self.draw_execute(pos, node)
        self.boundary_box(pos, node)
        self.draw_label(pos, node)
        self.draw_tooltips(node)

    def draw_execute(self, pos, node):
        hDrawApp = HelperDrawApplication(self._size)
        hDrawApp.execute(pos, node)

        pSymb = hDrawApp.get_apps()

        for symb in pSymb:
            symbol = self._symbols.asset(*symb)
            self.add(symbol)

    def draw_tooltips(self, node):
        _id = "tool-" + node.get('_id')
        g = self._symbols.create_group(_id)

        hDrawTooltips = HelperDrawTooltips(self._size, self._off)
        hDrawTooltips.execute(node)

        ltxt = hDrawTooltips.get_text()

        symbol = self._symbols.multiline(ltxt, (0, 0))
        g.add(symbol)

    def grid_box(self, pos, opts={'fill-opacity': '0.4'}):
        symbol = self._symbols.asset('grid.base', 'default', pos, self._size,
                                     opts)
        self.add(symbol)

    def draw_grid_size(self, cad1, size):
        cad2 = (cad1[0], cad1[1] + size - 1)

        points = HelperDrawBasePolyline(self._size, self._matrix3d) \
            .create_polyline_by_pos(cad1, cad2)

        symbol = self._symbols.polyline(points, {
            'fill': '#ccc',
            'fill-opacity': 0.2
        })
        self.add(symbol)

    def draw_root(self, item):
        root = item[3].get('root')
        if root:
            nitem = (item[0] - 1, item[1], item[2], item[3])
            pos = self._matrix3d.rotateNodeXY(nitem)()

            symbol = self._symbols.asset('grid.entry', 'default', pos,
                                         self._size)

            self.draw_connect(nitem, item)
            self.add(symbol)

    def add(self, symbol):
        self.dwg.add(symbol)

    def save(self):
        return self.dwg.tostring()

    def draw_connect(self, node1, node2, details=None):
        id = "%s-%s" % (node1[0], node2[0])
        g = self.dwg.g(id=id)

        d = HelperDrawConnector(self._size, self._off,
                                self._matrix3d).connect(node1, node2)

        symbol = self._symbols.conn(d)
        g.add(symbol)

        cls = " conn-%s" % node1[3].get('_id')
        symbol = self._symbols.conn_holder(d, cls)
        g.add(symbol)

        symbol = self._symbols.text(details, (0, 0), {'display': 'none'})
        g.add(symbol)

        self.add(g)
示例#22
0
def team(id: str, format: str) -> Union[Response, Tuple[Any, int]]:
    drawing = Drawing()

    w = 1000
    h = 400

    drawing["width"] = "%dpx" % w
    drawing["height"] = "%dpx" % h

    team_id = id
    emails = request.args.getlist("user")[0:10]
    generate_random = request.args.get("random")

    if generate_random:
        team_id = "%s" % random.random()
        email_count = random.randint(1, 8)
        emails = [random.random() for r in range(email_count)]

    team_prng = Random(team_id)

    background: Renderable
    if format == "png":
        background = random_background(team_prng,
                                       w,
                                       h,
                                       local_paths=format == "png")
    else:
        background_data = build_team_background(team_id, w, h)
        background = EmbeddedBackground(background_data, w, h)

    drawing.add(background.render(drawing))

    distance = w / (len(emails) + 1)

    profile_scale = 1 - (len(emails) - 4) * (7 / 80)

    for i, email in enumerate(emails):
        g = generate_profile_image(email, drawing)

        g.translate((i + 1) * distance, team_prng.randint(100, 200))
        g.rotate(team_prng.gauss(0, 20))
        g.scale(profile_scale)

        drawing.add(g)

    svg_code = drawing.tostring()

    if format == "svg":
        res = Response(drawing.tostring())
        res.headers["Content-Type"] = "image/svg+xml"
        res.headers["Cache-Control"] = "public, immutable, max-age=%d" % (
            86400 * 30)
        return res
    elif format == "png":
        requested_width: int
        try:
            requested_width = int(request.args.get("s"))
        except:
            requested_width = 1000

        res = Response(
            svg2png(bytestring=bytearray(svg_code, "utf-8"),
                    scale=requested_width / w))
        res.headers["Content-Type"] = "image/png"
        res.headers["Cache-Control"] = "public, immutable, max-age=%d" % (
            86400 * 30)
        return res

    return None, 406