示例#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)
def generate_frame(i, j, k):
    filename = str(i)+str(j)+str(k)+".svg"
    dwg = Drawing(filename, size=(300, 300))
    dwg.add(dwg.text('i = %d, j = %d, k %d' % (i, j, k), insert=(0.5, 20),
                     fill='red'))
    dwg.add(dwg.line((0, 0), (10, 0), stroke=rgb(10, 10, 16, '%')))
    dwg.save()
    pixel_width = 300
    return convert(pixel_width, filename)
示例#3
0
def draw_multi_transcript_overlay(config,
                                  gene,
                                  vmarkers=None,
                                  window_buffer=0,
                                  plots=None,
                                  log=DEVNULL):
    vmarkers = [] if vmarkers is None else vmarkers
    plots = [] if plots is None else plots

    canvas = Drawing(
        size=(config.width,
              1000))  # just set the height for now and change later
    width = config.width - config.left_margin - config.right_margin - config.overlay_left_label - config.padding
    labels = LabelMapping()  # keep labels consistent within the drawing

    all_exons = set()
    colors = dict()
    for us_tx in gene.transcripts:
        for ex in us_tx.exons:
            all_exons.add(ex)
            colors[
                ex] = config.exon1_color if us_tx.is_best_transcript else config.exon2_color

        for spl_tx in us_tx.transcripts:
            for translation in spl_tx.translations:
                for dom in translation.domains:
                    labels.set_key(dom.name, dom.name)
    genomic_min = min([max([gene.start - window_buffer, 1])] +
                      [m.start
                       for m in vmarkers] + [p.xmin for p in plots if p.xmin])
    genomic_max = max([gene.end + window_buffer] + [m.end for m in vmarkers] +
                      [p.xmax for p in plots if p.xmax])

    mapping = generate_interval_mapping(all_exons,
                                        width,
                                        config.exon_intron_ratio,
                                        config.exon_min_width,
                                        min_inter_width=config.min_width,
                                        start=genomic_min,
                                        end=genomic_max)
    main_group = canvas.g(class_='overlay')

    x = config.overlay_left_label + config.padding
    y = config.marker_top_margin

    for plot in plots:
        if plot.points:
            plot_group = draw_scatter(config, canvas, plot, mapping, log=log)
            main_group.add(plot_group)
            plot_group.translate(x, y)
            y += plot.height + config.padding * 2

    regular_transcripts = sorted(
        [us_tx for us_tx in gene.transcripts if not us_tx.is_best_transcript],
        key=lambda x: x.name)
    for us_tx in regular_transcripts:
        group_element = draw_exon_track(config,
                                        canvas,
                                        us_tx,
                                        mapping,
                                        colors=colors,
                                        genomic_min=genomic_min,
                                        genomic_max=genomic_max)
        main_group.add(group_element)
        group_element.translate(x, y)

        text_element = canvas.text(
            us_tx.name,
            insert=(x - config.padding, y + config.track_height / 2 +
                    config.font_central_shift_ratio * config.label_font_size),
            fill=config.label_color,
            style=config.font_style.format(font_size=config.label_font_size,
                                           text_anchor='end'),
            class_='label')
        main_group.add(text_element)
        y += config.padding + config.track_height

    best_transcripts = sorted(
        [us_tx for us_tx in gene.transcripts if us_tx.is_best_transcript],
        key=lambda x: x.name)
    for us_tx in best_transcripts:
        for spl_tx in us_tx.transcripts:
            labels[us_tx.name] = spl_tx

        group_element = draw_ustranscript(config,
                                          canvas,
                                          us_tx,
                                          mapping=mapping,
                                          colors=colors,
                                          labels=labels)
        main_group.add(group_element)
        group_element.translate(x, y)

        y += config.padding + group_element.height

    y += config.marker_bottom_margin
    # now draw the breakpoints overtop
    for marker in sorted(vmarkers):
        px_itvl = Interval(
            mapping.convert_ratioed_pos(marker.start).start,
            mapping.convert_ratioed_pos(marker.end).end)
        group_element = draw_vmarker(config,
                                     canvas,
                                     marker,
                                     px_itvl.length(),
                                     y,
                                     label=marker.name)
        group_element.translate(x + px_itvl.start, 0)
        main_group.add(group_element)

    main_group.translate(config.left_margin, config.top_margin)
    y += config.bottom_margin
    canvas.add(main_group)
    canvas.attribs['height'] = y
    return canvas
示例#4
0
    def draw_text(self,
                  svg: svgwrite.Drawing,
                  text: str,
                  point,
                  occupied: Occupied,
                  fill: Color,
                  size: float = 10.0,
                  out_fill=Color("white"),
                  out_opacity=0.5,
                  out_fill_2: Optional[Color] = None,
                  out_opacity_2=1.0):
        """
        Drawing text.

          ######     ###  outline 2
         #------#    ---  outline 1
        #| Text |#
         #------#
          ######
        """
        length = len(text) * 6

        if occupied:
            is_occupied: bool = False
            for i in range(-int(length / 2), int(length / 2)):
                if occupied.check((int(point[0] + i), int(point[1] - 4))):
                    is_occupied = True
                    break

            if is_occupied:
                return

            for i in range(-int(length / 2), int(length / 2)):
                for j in range(-12, 5):
                    occupied.register((int(point[0] + i), int(point[1] + j)))
                    # svg.add(svg.rect((point[0] + i, point[1] + j), (1, 1)))

        if out_fill_2:
            svg.add(
                svg.text(text,
                         point,
                         font_size=size,
                         text_anchor="middle",
                         font_family=DEFAULT_FONT,
                         fill=out_fill_2.hex,
                         stroke_linejoin="round",
                         stroke_width=5,
                         stroke=out_fill_2.hex,
                         opacity=out_opacity_2))
        if out_fill:
            svg.add(
                svg.text(text,
                         point,
                         font_size=size,
                         text_anchor="middle",
                         font_family=DEFAULT_FONT,
                         fill=out_fill.hex,
                         stroke_linejoin="round",
                         stroke_width=3,
                         stroke=out_fill.hex,
                         opacity=out_opacity))
        svg.add(
            svg.text(text,
                     point,
                     font_size=size,
                     text_anchor="middle",
                     font_family=DEFAULT_FONT,
                     fill=fill.hex))

        self.y += 11
main_font_size = 24

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']

x = 900
y = 50
delta_x = 400
vertical_locs = []
for loc in locs:
    top_text.add(dwg.text(loc, (x - len(loc)*4, y)))
    vertical_locs.append(x)
    x += delta_x

legend_text = dwg.add(dwg.g(font_size=main_font_size, style="font-family: arial;"))
legend_text.add(dwg.text('Phys', (x-300, y-10), fill=color_phys))
legend_text.add(dwg.text('Scaled', (x-300, y+20), fill=color_scaled))

v_lines = dwg.add(dwg.g(stroke_width=7.0, stroke=color_phys, fill='none'))
v_lines_scaled = dwg.add(dwg.g(stroke_width=7.0, stroke=color_scaled, fill='none'))

for loc in vertical_locs:
    v_lines.add(dwg.line(start=(loc, y+15), end=(loc, 1100)))

extra_text = dwg.add(dwg.g(font_size=main_font_size - 3, style="font-family: arial;"))
extra_text.add(dwg.text('Unit Conversion', (vertical_locs[0] + 10, 650)))
示例#6
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,
          openinbrowser=True,
          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):
    """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 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 will fix issues with rapidly opening
        multiple SVGs in your browser.

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

    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[0] = svg_attributes.get("width", dimensions[0])
        dimensions[1] = 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))

    # save svg
    if not os_path.exists(os_path.dirname(filename)):
        makedirs(os_path.dirname(filename))
    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)
示例#7
0
    def draw(self, dr: svgwrite.Drawing, size: XY, offset: XY):
        if self.poster.tracks is None:
            raise PosterError("No tracks to draw")
        year_size = 200 * 4.0 / 80.0
        year_style = f"font-size:{year_size}px; font-family:Arial;"
        year_length_style = f"font-size:{110 * 3.0 / 80.0}px; font-family:Arial;"
        month_names_style = f"font-size:2.5px; font-family:Arial"
        total_length_year_dict = self.poster.total_length_year_dict
        for year in range(self.poster.years.to_year, self.poster.years.from_year - 1,-1):
            start_date_weekday, _ = calendar.monthrange(year, 1)
            github_rect_first_day = datetime.date(year, 1, 1)
            # Github profile the first day start from the last Monday of the last year or the first Monday of this year
            # It depands on if the first day of this year is Monday or not.
            github_rect_day = github_rect_first_day + datetime.timedelta(
                -start_date_weekday
            )
            year_length = total_length_year_dict.get(year, 0)
            year_length = format_float(self.poster.m2u(year_length))
            try:
                month_names = [
                    locale.nl_langinfo(day)[:3]  # Get only first three letters
                    for day in [
                        locale.MON_1,
                        locale.MON_2,
                        locale.MON_3,
                        locale.MON_4,
                        locale.MON_5,
                        locale.MON_6,
                        locale.MON_7,
                        locale.MON_8,
                        locale.MON_9,
                        locale.MON_10,
                        locale.MON_11,
                        locale.MON_12,
                    ]
                ]
                # support windows or others doesn't support locale Name, by Hard code
            except Exception as e:
                print(str(e))
                month_names = [
                    "Jan",
                    "Feb",
                    "Mar",
                    "Apr",
                    "May",
                    "Jun",
                    "Jul",
                    "Aug",
                    "Sep",
                    "Oct",
                    "Nov",
                    "Dec",
                ]
            km_or_mi = "mi"
            if self.poster.units == "metric":
                km_or_mi = "km"
            dr.add(
                dr.text(
                    f"{year}",
                    insert=offset.tuple(),
                    fill=self.poster.colors["text"],
                    alignment_baseline="hanging",
                    style=year_style,
                )
            )

            dr.add(
                dr.text(
                    f"{year_length} {km_or_mi}",
                    insert=(offset.tuple()[0] + 165, offset.tuple()[1] + 2),
                    fill=self.poster.colors["text"],
                    alignment_baseline="hanging",
                    style=year_length_style,
                )
            )
            # add month name up to the poster one by one because of svg text auto trim the spaces.
            for num, name in enumerate(month_names):
                dr.add(
                    dr.text(
                        f"{name}",
                        insert=(offset.tuple()[0] + 15.5 * num, offset.tuple()[1] + 14),
                        fill=self.poster.colors["text"],
                        style=month_names_style,
                    )
                )

            rect_x = 10.0
            dom = (2.6, 2.6)
            # add every day of this year for 53 weeks and per week has 7 days
            for i in range(54):
                rect_y = offset.y + year_size + 2
                for j in range(7):
                    if int(github_rect_day.year) > year:
                        break
                    rect_y += 3.5
                    color = "#444444"
                    date_title = str(github_rect_day)
                    if date_title in self.poster.tracks_by_date:
                        tracks = self.poster.tracks_by_date[date_title]
                        length = sum([t.length for t in tracks])
                        distance1 = self.poster.special_distance["special_distance"]
                        distance2 = self.poster.special_distance["special_distance2"]
                        has_special = distance1 < length / 1000 < distance2
                        color = self.color(
                            self.poster.length_range_by_date, length, has_special
                        )
                        if length / 1000 >= distance2:
                            color = self.poster.colors.get(
                                "special2"
                            ) or self.poster.colors.get("special")
                        str_length = format_float(self.poster.m2u(length))
                        date_title = f"{date_title} {str_length} {km_or_mi}"

                    rect = dr.rect((rect_x, rect_y), dom, fill=color)
                    rect.set_desc(title=date_title)
                    dr.add(rect)
                    github_rect_day += datetime.timedelta(1)
                rect_x += 3.5
            offset.y += 3.5 * 9 + year_size + 1.5
示例#8
0
    def _draw(self, dr: svgwrite.Drawing, size: XY, offset: XY, year: int):
        min_size = min(size.x, size.y)
        year_size = min_size * 4.0 / 80.0
        year_style = f"font-size:{year_size}px; font-family:Arial;"
        month_style = f"font-size:{min_size * 3.0 / 80.0}px; font-family:Arial;"
        day_style = f"dominant-baseline: central; font-size:{min_size * 1.0 / 80.0}px; font-family:Arial;"
        day_length_style = f"font-size:{min_size * 1.0 / 80.0}px; font-family:Arial;"

        dr.add(
            dr.text(
                f"{year}",
                insert=offset.tuple(),
                fill=self.poster.colors["text"],
                alignment_baseline="hanging",
                style=year_style,
            )
        )
        offset.y += year_size
        size.y -= year_size
        count_x = 31
        for month in range(1, 13):
            date = datetime.date(year, month, 1)
            (_, last_day) = calendar.monthrange(year, month)
            count_x = max(count_x, date.weekday() + last_day)

        cell_size = min(size.x / count_x, size.y / 36)
        spacing = XY(
            (size.x - cell_size * count_x) / (count_x - 1),
            (size.y - cell_size * 3 * 12) / 11,
        )

        # chinese weekday key number is the third.
        keyword_num = 0
        if locale.getlocale()[0] == "zh_CN":
            keyword_num = 2
        # first character of localized day names, starting with Monday.
        dow = [
            locale.nl_langinfo(day)[keyword_num].upper()
            for day in [
                locale.DAY_2,
                locale.DAY_3,
                locale.DAY_4,
                locale.DAY_5,
                locale.DAY_6,
                locale.DAY_7,
                locale.DAY_1,
            ]
        ]

        for month in range(1, 13):
            date = datetime.date(year, month, 1)
            y = month - 1
            y_pos = offset.y + (y * 3 + 1) * cell_size + y * spacing.y
            dr.add(
                dr.text(
                    date.strftime("%B"),
                    insert=(offset.x, y_pos - 2),
                    fill=self.poster.colors["text"],
                    alignment_baseline="hanging",
                    style=month_style,
                )
            )

            day_offset = date.weekday()
            while date.month == month:
                x = date.day - 1
                x_pos = offset.x + (day_offset + x) * cell_size + x * spacing.x
                pos = (x_pos + 0.05 * cell_size, y_pos + 1.15 * cell_size)
                dim = (cell_size * 0.9, cell_size * 0.9)
                text_date = date.strftime("%Y-%m-%d")
                if text_date in self.poster.tracks_by_date:
                    tracks = self.poster.tracks_by_date[text_date]
                    length = sum([t.length for t in tracks])
                    has_special = len([t for t in tracks if t.special]) > 0
                    color = self.color(
                        self.poster.length_range_by_date, length, has_special
                    )
                    dr.add(dr.rect(pos, dim, fill=color))
                    dr.add(
                        dr.text(
                            utils.format_float(self.poster.m2u(length)),
                            insert=(
                                pos[0] + cell_size / 2,
                                pos[1] + cell_size + cell_size / 2,
                            ),
                            text_anchor="middle",
                            style=day_length_style,
                            fill=self.poster.colors["text"],
                        )
                    )
                else:
                    dr.add(dr.rect(pos, dim, fill="#444444"))

                dr.add(
                    dr.text(
                        dow[date.weekday()],
                        insert=(
                            offset.x + (day_offset + x) * cell_size + cell_size / 2,
                            pos[1] + cell_size / 2,
                        ),
                        text_anchor="middle",
                        alignment_baseline="middle",
                        style=day_style,
                    )
                )
                date += datetime.timedelta(1)
示例#9
0
    def plot(self, x, y, scale, break_width=10, **extra):

        svg = Drawing()

        break_length = 0
        fragments = self.get_fragments()
        g = svg.g()

        for i in range(len(fragments)):

            f = fragments[i]

            if i > 0:
                break_length += f.start - fragments[
                    i - 1].end - break_width / scale

            p1 = [x + (f.start - break_length) * scale, y]
            p2 = [x + (f.end - break_length) * scale, y]

            f.position = p1

            line1 = svg.line(start=p1, end=p2, **extra)

            line2 = svg.line(start=[p1[0], p1[1] - 5],
                             end=[p1[0], p1[1] + 5],
                             **extra)
            text2 = svg.text(text=f.start,
                             insert=(p1[0], p1[1] + 10),
                             font_size=8,
                             font_family="Arial",
                             transform='rotate(%s, %s, %s)' %
                             (90, p1[0], p1[1] + 10))
            line3 = svg.line(start=[p2[0], p1[1] - 5],
                             end=[p2[0], p1[1] + 5],
                             **extra)
            text3 = svg.text(text=f.end,
                             insert=(p2[0], p2[1] + 10),
                             font_size=8,
                             font_family="Arial",
                             transform='rotate(%s, %s, %s)' %
                             (90, p2[0], p1[1] + 10))
            g.add(line1)
            g.add(line2)
            g.add(text2)
            g.add(line3)
            g.add(text3)

        text1 = svg.text(text=self.name,
                         insert=(10, y),
                         font_size=10,
                         font_family="Arial")
        g.add(text1)
        text2 = svg.text(text="%s nt" % self.length,
                         insert=(x / 2.0 - 10, y + 12),
                         font_size=8,
                         font_family="Arial")
        g.add(text2)

        for e in self.features:
            if e.frag_genome:
                patch, text = e.plot(scale=scale)
                g.add(patch)
                g.add(text)

        return g
示例#10
0
ncols = cols.shape[1]
nlines = ncols - 1
colspace = h - nlines * 200
colwidth = colspace / float(ncols)
collocs = (arange(ncols) + 1) * colwidth

# pad
g = dwg.add(dwg.g(transform="translate(%i,%i)" % (padding, padding)))
title = g.add(dwg.g())

for i in range(ncols):
    if i == 0:
        title.add(
            dwg.text(cols.columns[i],
                     insert=(collocs[i], 0),
                     text_anchor='end',
                     font_size='%ipx' % titlefont))
    elif i == ncols - 1:
        title.add(
            dwg.text(cols.columns[i],
                     insert=(collocs[i], 0),
                     font_size='%ipx' % titlefont))

g = g.add(dwg.g(transform="translate(0,%i)" % (titlefont + fontsize)))
# loop over and add labels
vmin = min(cols.values)
vmax = max(cols.values)


def scale(val, src=(vmin, vmax), dst=(0, h)):
    return ((float(val) - src[0]) /
示例#11
0
def parse_shape(shape, i, gstates):
    # see https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf
    output_filename = "page%s.svg" % i
    dwg = Drawing(output_filename, profile='tiny')
    fill = "none"
    stroke = rgb(0, 0, 0)
    stroke_width = 4
    stroke_dasharray = None
    stroke_miterlimit = None
    transform = (1, 0, 0, 1, 0, 0)
    shapes_stack = []
    d = ""
    paths = []
    for line in shape.split("\n"):
        line = line.strip()
        parts = line.split(" ")
        nums = []
        for part in parts:
            try:
                nums.append(float(part))
            except ValueError:
                pass
        if endswith(line, 'BDC'):
            continue
        elif endswith(line, 'Tj'):
            text = ' '.join(parts[:-1])
            text = text.split('Tj')[0]
            group = Group(transform="matrix({})".format(' '.join(
                [str(d) for d in transform])))
            group.add(dwg.text(text))
            shapes_stack.append(group)
        elif endswith(line, 'Tc'):
            pass  # not yet implemented
        elif endswith(line, 'Tm'):
            for i, part in enumerate(parts):
                if part == 'Tm':
                    transform = [float(d) for d in parts[i - 6:i]]
                if part == 'Tw':
                    word_spacing = parts[i - 1]
                if part == 'Tc':
                    char_spacing = parts[i - 1]
        elif endswith(line, 'q'):
            # q - start stack
            continue
        elif endswith(line, 're'):
            # rectangle
            vals = {'insert': (nums[0], nums[1]), 'size': (nums[2], nums[3])}
            if fill:
                vals['fill'] = fill
            if stroke:
                vals['stroke'] = stroke
            shapes_stack.append(dwg.rect(**vals))
        elif endswith(line, 'n'):
            # - clipping path
            continue
        elif endswith(line, 'RG'):
            # set stroke color
            stroke = rgb(*nums[0:3])
        elif endswith(line, 'K'):
            stroke = rgb(*cmyk(*nums[0:4]))
        elif endswith(line, 'J'):
            # not sure how to implement cap styles
            continue
        elif endswith(line, 'cm'):
            # current transformation matrix
            transform = nums[0:6]
        elif endswith(line, 'F') or endswith(line, 'f'):
            # fill
            fill = rgb(*nums[0:3])
        elif endswith(line, 'm'):
            # move to
            d += " M " + format_pointstr(parts[0:2])
        elif endswith(line, ' c'):
            # curve
            d += " C " + format_pointstr(parts[0:6])
        elif endswith(line, 'v'):
            # append to bezier curve
            d += " S " + format_pointstr(parts[0:4])
        elif endswith(line, 'y'):
            d += " C " + format_pointstr(parts[0:4]) + " " + format_pointstr(
                parts[2:4])
        elif endswith(line, 'l'):
            # line to
            d += " L " + format_pointstr(parts[0:2])
        elif endswith(line, 'h'):
            # make sure it's a closed path
            continue
        elif endswith(line, 'S'):
            # stroke to 4-unit width
            continue
        elif endswith(line, 'Q'):
            # end stack (draw)
            # apply transformation...
            for shape in shapes_stack:
                dwg.add(shape)
            if len(d) > 0:
                paths.append(d + " Z")
                d = " ".join(
                    [transform_str(p, transform) for p in d.split(" ")])
                vals = {'d': d}
                vals['stroke-width'] = stroke_width
                if fill:
                    vals['fill'] = fill
                if stroke:
                    vals['stroke'] = stroke
                if stroke_dasharray:
                    vals['stroke-dasharray'] = stroke_dasharray
                if stroke_miterlimit:
                    vals['stroke-miterlimit'] = stroke_miterlimit
                dwg.add(dwg.path(**vals))
            d = ''
            shapes_stack = []
        elif endswith(line, 'gs'):
            key = parts[0]
            if key not in gstates:
                print("could not find state %s in dictionary")
            state = gstates[key]
            # color blending not yet implemented
            pass
        elif endswith(line, 'w'):
            stroke_width = nums[0]
        elif endswith(line, 'd'):
            fullstring = " ".join(parts[:-1])
            parsed = match('([\w\s\d]+)\[([\d\s]+)\](\d)', fullstring)
            if parsed:
                line_props = parsed.group(1)
                if line_props.find('M') >= 0:
                    m_part = line_props.split('M')[0].split(' ')[-2]
                    stroke_miterlimit = float(m_part)
                if line_props.find('w') >= 0:
                    m_part = line_props.split('w')[0].split(' ')[-2]
                    stroke_width = float(m_part)

                stroke_dasharray = parsed.group(2)
                offset = float(
                    parsed.group(3))  # throw away the offset, it doesn't
                # convert nicely to svg
        elif endswith(line, 'M'):
            stroke_miterlimit = nums[0]
        else:
            print("not sure what to do with %s" % line)
    dwg.save()
    return paths
示例#12
0
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']

x = 900
y = 50
delta_x = 400
vertical_locs = []
for loc in locs:
    top_text.add(dwg.text(loc, (x - len(loc) * 4, y)))
    vertical_locs.append(x)
    x += delta_x

legend_text = dwg.add(
    dwg.g(font_size=main_font_size, style="font-family: arial;"))
legend_text.add(dwg.text('Phys', (x - 300, y - 10), fill=color_phys))
legend_text.add(dwg.text('Scaled', (x - 300, y + 20), fill=color_scaled))

v_lines = dwg.add(dwg.g(stroke_width=7.0, stroke=color_phys, fill='none'))
v_lines_scaled = dwg.add(
    dwg.g(stroke_width=7.0, stroke=color_scaled, fill='none'))

for loc in vertical_locs:
    v_lines.add(dwg.line(start=(loc, y + 15), end=(loc, 1200)))
def diode_svg_frame(illuminated, num_across=9, num_down=8, frame=0, single_route=-1):
    filename = "diode{:03d}.svg".format(frame)
    led_symbol = "resources/Symbol_LED.svg"
    image_width = 600
    image_height = 400
    right_margin = 30
    bottom_margin = 30
    dwg = Drawing(filename,
                  size=(image_width+right_margin, image_height+bottom_margin),
                  style="background-color:white")
    # create a white background rectangle
    dwg.add(dwg.rect(size=(image_width+right_margin, image_height+bottom_margin),
                     insert=(0, 0), fill="white"))

    LED_dimensions = [106.0, 71.0]
    LED_points = [[35, 68], [35, 31], [66, 50]]
    LED_entries = [[4, 50], [103, 50]]
    aspect_ratio = LED_dimensions[1]/LED_dimensions[0]
    new_width = image_width/num_across
    new_height = new_width*aspect_ratio
    LED_scale = 0.75
    LED_offsets = [new_width*LED_scale/2, new_height*LED_scale]
    junction_radius = 0.8
    elements = []
    for i in range(0, num_across):
        x_pos = new_width*(num_across-i-1)
        if illuminated[1] >= illuminated[0]:
            incoming_wire = illuminated[1] + 1
        else:
            incoming_wire = illuminated[1]
        if i == incoming_wire:
            connection = "+"
            text_fill = "red"
        elif i == illuminated[0]:
            connection = "-"
            text_fill = "black"
        else:
            connection = "NC"
            text_fill = "gray"
        wire_label = "{} {}".format(i+1, connection)
        # the input wire title
        dwg.add(dwg.text(wire_label, insert=(x_pos+new_width-10, 10),
                             fill=text_fill))
        for j in range(0, num_down):
            y_pos = (image_height/num_down)*j
            position = [x_pos+LED_offsets[0], y_pos+LED_offsets[1]]
            scale = [LED_scale*new_width/LED_dimensions[0],
                     LED_scale*new_height/LED_dimensions[1]]
            # the led svg
            dwg.add(dwg.image(led_symbol, insert=position,
                              size=(new_width*LED_scale, new_height*LED_scale)))
            if i == illuminated[0] and j == illuminated[1] and single_route == -1:
                points = []
                for point in LED_points:
                    points.append(transform_point(point, scale, position))
                # the illuminated svg box
                dwg.add(dwg.polygon(points=points, fill="yellow"))
                line_fill = "green"
                stroke_width = 1
                insert_pos = -1
            else:
                line_fill = "black"
                insert_pos = 0
                stroke_width = 0.5
            # for each LED, we want to generate a line going from the input
            # to its output
            entry_point = transform_point(LED_entries[0], scale, position)
            if i > j:
                incoming_line_points = [[new_width*(num_across-j)-LED_offsets[0], 0],
                                    [new_width*(num_across-j)-LED_offsets[0], y_pos+20],
                                    [entry_point[0], y_pos+20], entry_point]
            elif j > i:
                incoming_line_points = [
                    [new_width * (num_across - j - 1) - LED_offsets[0], 0],
                    [new_width * (num_across - j - 1) - LED_offsets[0],
                     entry_point[1] + LED_offsets[1]],
                    [entry_point[0], entry_point[1]+LED_offsets[1]], entry_point]
            elif i == j:
                incoming_line_points = [
                    [new_width * (num_across - j - 1) - LED_offsets[0], 0],
                    [new_width * (num_across - j - 1) - LED_offsets[0], entry_point[1]], entry_point]
            else:
                incoming_line_points = []
            elements.insert(insert_pos,
                            make_junction_line(dwg, incoming_line_points,
                                               junction_radius, line_fill,
                                               stroke_width))
            # outgoing line
            exit_point = transform_point(LED_entries[1], scale, position)
            outgoing_line_points = [exit_point,
                                    [x_pos+new_width-LED_offsets[0],
                                     exit_point[1]],
                                    [x_pos+new_width-LED_offsets[0], 0]]
            elements.insert(insert_pos,
                            make_junction_line(dwg, outgoing_line_points,
                                               junction_radius, line_fill,
                                               stroke_width))
            route_points = [[new_width * (num_across - j - 1) - LED_offsets[0],
                             0]]
            for point in range(0, single_route+1):
                if point < i:
                    route_points.append([new_width * (num_across - j - 1) - LED_offsets[0], 0])

            # now create the network
            nodes = []
            for i in range(0, num_across):
                for j in range(0, num_down):
                    nodes.append(entry_point)
                    nodes.append(exit_point)
    # flatten the elements structure
    elements = sum(elements, [])
    print(elements)
    # the lines should be drawn last so that they are layered on top of all
    # other elements
    #for element in elements:
    #    dwg.add(element)
    dwg.save()
    return convert(image_width, filename)
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)
    x += delta_x

legend_text = dwg.add(
    dwg.g(font_size=main_font_size, style="font-family: arial;"))
legend_text.add(dwg.text('Phys', (x - 1500, y - 10), fill=color_phys))
legend_text.add(dwg.text('Scaled', (x - 1500, y + 20), fill=color_scaled))

v_lines = dwg.add(dwg.g(stroke_width=7.0, stroke=color_phys, fill='none'))
v_lines_scaled = dwg.add(
    dwg.g(stroke_width=7.0, stroke=color_scaled, fill='none'))

for loc in vertical_locs:
    v_lines.add(dwg.line(start=(loc, y + 15), end=(loc, 1200)))
示例#15
0
key_font = ImageFont.truetype(sys_font, font_main_size)
sec_font = ImageFont.truetype(sys_font, font_sub_size)
key_style = f'font-size: {font_main_size}px; font-family:{web_font}'
sec_style = f'font-size: {font_sub_size}px; font-family:{web_font}'

for y in range(8):
    for x in range(5):
        idx = y * 5 + x
        key = primary[idx]
        alp = alpha[idx]
        bta = beta[idx]
        line_height = 2.5 if idx not in special_keys else 1.5
        key_fill = alp_color if key == str_alp else bta_color if key == str_bta else main_color

        pos = tuple(map(operator.add, padding, (x * 15 * px, y * 15 * py)))
        size = (15 * px, 15 * py)
        button = dwg.add(dwg.g())
        txt_size = key_font.getsize(key)
        alp_size = sec_font.getsize(alp)
        bet_size = sec_font.getsize(bta)
        txt_pos = (pos[0] + (size[0] - txt_size[0]) / 2,
                   pos[1] + size[1] - txt_size[1] / line_height)
        alp_pos = (pos[0] + 5, pos[1] + (size[1] / 3.5) + (alp_size[1] / 10))
        bet_pos = (pos[0] + (size[0] - bet_size[0] - 5), alp_pos[1])
        button.add(dwg.rect(pos, size, fill='white', stroke='black'))
        button.add(dwg.text(key, txt_pos, style=key_style, fill=key_fill))
        button.add(dwg.text(alp, alp_pos, style=sec_style, fill=alp_color))
        button.add(dwg.text(bta, bet_pos, style=sec_style, fill=bta_color))

dwg.save()
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)
    x += delta_x

legend_text = dwg.add(dwg.g(font_size=main_font_size, style="font-family: arial;"))
legend_text.add(dwg.text('Phys', (x-1500, y-10), fill=color_phys))
legend_text.add(dwg.text('Scaled', (x-1500, y+20), fill=color_scaled))

v_lines = dwg.add(dwg.g(stroke_width=7.0, stroke=color_phys, fill='none'))
v_lines_scaled = dwg.add(dwg.g(stroke_width=7.0, stroke=color_scaled, fill='none'))

for loc in vertical_locs:
    v_lines.add(dwg.line(start=(loc, y+15), end=(loc, 1200)))

extra_text = dwg.add(dwg.g(font_size=main_font_size - 3, style="font-family: arial;"))
extra_text.add(dwg.text('fwd', (vertical_locs[5] + 55, 820)))
示例#17
0
    def plot(self, shape="arrow", strand=True, scale=1 / 2000, **extra):

        height = self.height

        if strand:
            y1 = self.frag_genome.position[
                1] - height / 2.0 - self.strand * height
            y2 = y1 + height
        else:
            y1 = self.frag_genome.position[1] - height / 2.0
            y2 = self.frag_genome.position[1] + height / 2.0

        if shape == "rect":
            p1 = [
                self.frag_genome.position[0] +
                (self.start - self.frag_genome.start) * scale, y1
            ]
            p2 = [
                self.frag_genome.position[0] +
                (self.end - self.frag_genome.start) * scale, y1
            ]
            p3 = [
                self.frag_genome.position[0] +
                (self.end - self.frag_genome.start) * scale, (y1 + y2) / 2
            ]
            p4 = [
                self.frag_genome.position[0] +
                (self.end - self.frag_genome.start) * scale, y2
            ]
            p5 = [
                self.frag_genome.position[0] +
                (self.start - self.frag_genome.start) * scale, y2
            ]

        if shape == "arrow":

            if self.strand == 1:

                p1 = [
                    self.frag_genome.position[0] +
                    (self.start - self.frag_genome.start) * scale, y1
                ]
                p2 = [
                    self.frag_genome.position[0] +
                    (self.end - self.frag_genome.start) * scale - height / 3,
                    y1
                ]
                p3 = [
                    self.frag_genome.position[0] +
                    (self.end - self.frag_genome.start) * scale, (y1 + y2) / 2
                ]
                p4 = [
                    self.frag_genome.position[0] +
                    (self.end - self.frag_genome.start) * scale - height / 3,
                    y2
                ]
                p5 = [
                    self.frag_genome.position[0] +
                    (self.start - self.frag_genome.start) * scale, y2
                ]
            else:
                p1 = [
                    self.frag_genome.position[0] +
                    (self.end - self.frag_genome.start) * scale, y1
                ]
                p2 = [
                    self.frag_genome.position[0] +
                    (self.start - self.frag_genome.start) * scale + height / 3,
                    y1
                ]
                p3 = [
                    self.frag_genome.position[0] +
                    (self.start - self.frag_genome.start) * scale,
                    (y1 + y2) / 2
                ]
                p4 = [
                    self.frag_genome.position[0] +
                    (self.start - self.frag_genome.start) * scale + height / 3,
                    y2
                ]
                p5 = [
                    self.frag_genome.position[0] +
                    (self.end - self.frag_genome.start) * scale, y2
                ]

            if self.strand * p2[0] < self.strand * p1[0]:
                p2 = p1
                p4 = p5

        self.position = p1
        if self.strand == -1:
            self.position = p3

        svg = Drawing()

        if self.parent["Homology"]:
            patch = svg.polygon(points=[p1, p2, p3, p4, p5],
                                fill=self.parent["Homology"].color)
        else:
            patch = svg.polygon(points=[p1, p2, p3, p4, p5],
                                fill="white",
                                stroke="black")

        t = [(p1[0] + p3[0]) / 2, p1[1] - 1]
        a = 1

        if strand and self.strand == -1:
            t = [(p1[0] + p3[0]) / 2, p5[1] + 5]
            a = -1

        text = svg.text(text=self.name,
                        insert=t,
                        font_size="8",
                        transform='rotate(%s, %s, %s)' % (-45 * a, t[0], t[1]))

        return patch, text
示例#18
0
class SvgRenderer(StreamRenderer):
    """
    Draws the board like an SVG image (best representation for web)
    """

    __rend_name__ = 'svg'

    DEFAULT_CELL_SIZE_IN_PIXELS = 15
    BOLD_EVERY = 5

    GRID_STROKE_WIDTH = 1
    GRID_BOLD_STROKE_WIDTH = 2

    @property
    def clues_font_size(self):
        """The size of the descriptions text"""
        return self.cell_size * 0.6

    def __init__(self,
                 board=None,
                 stream=stdout,
                 size=DEFAULT_CELL_SIZE_IN_PIXELS):
        super(SvgRenderer, self).__init__(board, stream)

        # decrease startup time when do not need this renderer
        from svgwrite import Drawing

        self.cell_size = size
        self.color_symbols = dict()
        self.drawing = Drawing(size=(self.full_width + self.cell_size,
                                     self.full_height + self.cell_size))
        self._add_definitions()

    def _color_id_by_name(self, color):
        color_id = self.board.color_id_by_name(color)
        if color_id:
            return color_id

        if is_list_like(color):
            return from_two_powers(
                self.board.color_id_by_name(single_color)
                for single_color in color)

        return None

    def _add_symbol(self, id_, color, *parts, **kwargs):
        drawing = self.drawing
        symbol = drawing.symbol(id_=id_, **kwargs)
        for part in parts:
            symbol.add(part)

        if color is not None:
            # SPACE is already an ID
            if color not in (SPACE, SPACE_COLORED):
                if self.is_colored:
                    color = self._color_id_by_name(color)

            self.color_symbols[color] = id_

        drawing.defs.add(symbol)

    def _add_definitions(self):
        drawing = self.drawing

        # dynamic style rules
        drawing.defs.add(
            drawing.style(
                'g.grid-lines line {stroke-width: %i} '
                'g.grid-lines line.bold {stroke-width: %i} '
                'g.header-clues text, g.side-clues text {font-size: %f} ' % (
                    self.GRID_STROKE_WIDTH,
                    self.GRID_BOLD_STROKE_WIDTH,
                    self.clues_font_size,
                )))

        self._add_colors_def()

        self._add_symbol('check',
                         None,
                         drawing.circle(r=40, stroke_width=10,
                                        center=(50, 50)),
                         drawing.polyline(stroke_width=12,
                                          points=[(35, 35), (35, 55),
                                                  (75, 55)],
                                          transform='rotate(-45 50 50)'),
                         stroke='green',
                         fill='none')

        self.check_icon_size = 100

    def _add_colors_def(self):
        drawing = self.drawing
        white_color = Color.white().name

        cell_size = self.cell_size
        rect_size = (cell_size, cell_size)

        upper_triangle_points = ((0, 0), (0, cell_size), (cell_size, 0))
        lower_triangle_points = ((0, cell_size), (cell_size, 0), (cell_size,
                                                                  cell_size))

        # three_colored_flag_rect_size = (cell_size / 3, cell_size)
        # three_colored_flag_insert_points = [(0, 0), (cell_size / 3, 0), (2 * cell_size / 3, 0)]
        three_color_triangle_size = round(cell_size * ((1 / 2)**0.5), 2)
        three_color_triangle_coord = round(
            cell_size - three_color_triangle_size, 2)

        three_colors_upper_points = [(0, 0), (0, three_color_triangle_size),
                                     (three_color_triangle_size, 0)]
        three_colors_lower_points = [
            (cell_size, three_color_triangle_coord),
            (three_color_triangle_coord, cell_size),
            (cell_size, cell_size),
        ]

        # rendering should be predictable
        colors = []
        if self.is_colored:
            for color_name in sorted(self.board.color_map):
                colors.append((color_name, self._color_from_name(color_name)))

            space_color = SPACE_COLORED
        else:
            colors.append((BOX, 'black'))
            space_color = SPACE

        for color_name, fill_color in colors:
            if color_name != white_color:
                self._add_symbol(
                    'color-%s' % color_name, color_name,
                    drawing.rect(
                        size=rect_size,
                        fill=fill_color,
                    ))

        if self.is_colored:
            for (color_name,
                 fill_color), (color_name2,
                               fill_color2) in combinations(colors, 2):
                LOG.info('Transient symbol: %s, %s + %s, %s', color_name,
                         fill_color, color_name2, fill_color2)
                color_tuple = (color_name, color_name2)

                self._add_symbol(
                    'x2-%s' % '-'.join(map(str, color_tuple)),
                    color_tuple,
                    drawing.polygon(
                        points=upper_triangle_points,
                        fill=fill_color,
                    ),
                    drawing.polygon(
                        points=lower_triangle_points,
                        fill=fill_color2,
                    ),
                )

            for (color_name,
                 fill_color), (color_name2,
                               fill_color2), (color_name3,
                                              fill_color3) in combinations(
                                                  colors, 3):
                LOG.info('Transient symbol: %s, %s + %s, %s + %s, %s',
                         color_name, fill_color, color_name2, fill_color2,
                         color_name3, fill_color3)
                color_tuple = (color_name, color_name2, color_name3)

                self._add_symbol(
                    'x3-%s' % '-'.join(map(str, color_tuple)),
                    color_tuple,
                    # drawing.rect(
                    #     insert=three_colored_flag_insert_points[0],
                    #     size=three_colored_flag_rect_size,
                    #     fill=fill_color,
                    # ),
                    # drawing.rect(
                    #     insert=three_colored_flag_insert_points[1],
                    #     size=three_colored_flag_rect_size,
                    #     fill=fill_color2,
                    # ),
                    # drawing.rect(
                    #     insert=three_colored_flag_insert_points[2],
                    #     size=three_colored_flag_rect_size,
                    #     fill=fill_color3,
                    # ),
                    drawing.rect(
                        size=rect_size,
                        fill=fill_color,
                    ),
                    drawing.polygon(
                        points=three_colors_upper_points,
                        fill=fill_color2,
                    ),
                    drawing.polygon(
                        points=three_colors_lower_points,
                        fill=fill_color3,
                    ),
                )

        # it's a little circle
        self._add_symbol('space', space_color,
                         drawing.circle(r=cell_size / 10))

    @property
    def pixel_side_width(self):
        """Horizontal clues side width in pixels"""
        return self.side_width * self.cell_size

    @property
    def pixel_header_height(self):
        """Vertical clues header height in pixels"""
        return self.header_height * self.cell_size

    @property
    def pixel_board_width(self):
        """The width of the main area in pixels"""
        return self.board.width * self.cell_size

    @property
    def pixel_board_height(self):
        """The height of the main area in pixels"""
        return self.board.height * self.cell_size

    @property
    def full_width(self):
        """Full width of the SVG board representation"""
        return self.pixel_side_width + self.pixel_board_width

    @property
    def full_height(self):
        """Full height of the SVG board representation"""
        return self.pixel_header_height + self.pixel_board_height

    def _color_from_name(self, color_name):
        return self.board.rgb_for_color_name(color_name)

    def block_svg(self, value, is_column, clue_number, block_number):
        """
        Return the SVG element for the clue number (colored case included)
        """
        # left to right, bottom to top
        block_number = -block_number

        shift = (0.85, -0.3) if is_column else (-0.3, 0.75)
        i, j = (clue_number, block_number) if is_column else (block_number,
                                                              clue_number)

        if isinstance(value, (list, tuple)):
            # colored board
            value, color_id = value[:2]
        else:
            color_id = None

        block_color = None
        if color_id is not None:
            id_ = self.color_symbols[color_id]

            if is_column:
                color_box = (i, j - 1)
            else:
                color_box = (i - 1, j)

            # drawing.g(class_=id_)
            insert_point = (self.pixel_side_width +
                            (color_box[0] * self.cell_size),
                            self.pixel_header_height +
                            (color_box[1] * self.cell_size))

            block_color = (id_, insert_point)

        extra = dict()
        if color_id == Color.black().id_:
            extra['fill'] = 'white'

        if value == BlottedBlock:
            text_value = ClueCell.BLOTTED_SYMBOL
        else:
            text_value = str(value)

        block_text = self.drawing.text(
            text_value,
            insert=(
                self.pixel_side_width + (i + shift[0]) * self.cell_size,
                self.pixel_header_height + (j + shift[1]) * self.cell_size,
            ),
            **extra)

        return block_color, block_text

    def draw_header(self):
        drawing = self.drawing

        drawing.add(
            drawing.rect(size=(self.pixel_side_width,
                               self.pixel_header_height),
                         class_='nonogram-thumbnail'))
        drawing.add(
            drawing.rect(insert=(self.pixel_side_width, 0),
                         size=(self.pixel_board_width,
                               self.pixel_header_height),
                         class_='nonogram-header'))

        header_group = drawing.g(class_='header-clues')
        for i, col_desc in enumerate(self.board.columns_descriptions):
            if self.board.column_solution_rate(i) == 1:
                x_pos = self.pixel_side_width + (i * self.cell_size)
                header_group.add(
                    drawing.rect(insert=(x_pos, 0),
                                 size=(self.cell_size,
                                       self.pixel_header_height),
                                 class_='solved'))

            for j, desc_item in enumerate(reversed(col_desc)):
                color, text = self.block_svg(desc_item, True, i, j)

                # color first, text next (to write on color)
                if color:
                    id_, insert_point = color
                    icon = drawing.use(
                        href='#' + id_,
                        insert=insert_point,
                    )

                    header_group.add(icon)

                header_group.add(text)

        drawing.add(header_group)

    def draw_side(self):
        drawing = self.drawing

        drawing.add(
            drawing.rect(insert=(0, self.pixel_header_height),
                         size=(self.pixel_side_width, self.pixel_board_height),
                         class_='nonogram-side'))

        side_group = drawing.g(class_='side-clues')
        for j, row_desc in enumerate(self.board.rows_descriptions):
            if self.board.row_solution_rate(j) == 1:
                y_pos = self.pixel_header_height + (j * self.cell_size)
                side_group.add(
                    drawing.rect(insert=(0, y_pos),
                                 size=(self.pixel_side_width, self.cell_size),
                                 class_='solved'))

            for i, desc_item in enumerate(reversed(row_desc)):
                color, text = self.block_svg(desc_item, False, j, i)

                # color first, text next (to write on color)
                if color:
                    id_, insert_point = color
                    icon = drawing.use(
                        href='#' + id_,
                        insert=insert_point,
                    )

                    side_group.add(icon)

                side_group.add(text)

        drawing.add(side_group)

        if self.board.is_solved_full:
            self._insert_solved_symbol()

    def _insert_solved_symbol(self):
        drawing = self.drawing

        check_icon_size = self.check_icon_size
        left_padding = (self.pixel_side_width - check_icon_size) / 2
        top_padding = (self.pixel_header_height - check_icon_size) / 2
        left_padding = max(left_padding, 0)
        top_padding = max(top_padding, 0)

        drawing.add(drawing.use('#check', insert=(left_padding, top_padding)))

    @classmethod
    def _color_code(cls, cell):
        if is_color_cell(cell):
            single_colors = two_powers(cell)
            if len(single_colors) > 3:  # allow two and three colors
                # multiple colors
                return UNKNOWN

        return cell

    def draw_grid(self, cells=None):
        if cells is None:
            cells = self.board.cells

        drawing = self.drawing

        drawing.add(
            drawing.rect(insert=(self.pixel_side_width,
                                 self.pixel_header_height),
                         size=(self.pixel_board_width,
                               self.pixel_board_height),
                         class_='nonogram-grid'))

        cell_groups = dict()
        for cell_value, id_ in iteritems(self.color_symbols):
            cell_groups[cell_value] = drawing.g(class_=id_)

        space_cell = SPACE_COLORED if self.is_colored else SPACE

        for j, row in enumerate(cells):
            for i, cell in enumerate(row):
                cell = self._color_code(cell)

                if cell == UNKNOWN:
                    continue

                if cell == space_cell:
                    insert_point = (self.pixel_side_width +
                                    (i + 0.5) * self.cell_size,
                                    self.pixel_header_height +
                                    (j + 0.5) * self.cell_size)
                else:
                    # for boxes colored and black
                    insert_point = (self.pixel_side_width +
                                    (i * self.cell_size),
                                    self.pixel_header_height +
                                    (j * self.cell_size))

                id_ = self.color_symbols[cell]
                icon = drawing.use(href='#' + id_, insert=insert_point)
                cell_groups[cell].add(icon)

        # to get predictable order
        for cell_value, group in sorted(iteritems(cell_groups),
                                        key=lambda x: x[0]):
            drawing.add(group)

        # write grid on top of the colors
        self._insert_grid_lines()

    def _insert_grid_lines(self):
        drawing = self.drawing

        grid_lines = drawing.g(class_='grid-lines')
        for line in self._get_grid_lines():
            grid_lines.add(line)

        drawing.add(grid_lines)

    def _get_grid_lines(self):
        drawing = self.drawing

        # draw horizontal lines
        for i in range(self.board.height + 1):
            extra = dict()

            if i % self.BOLD_EVERY == 0 or i == self.board.height:
                extra['class'] = 'bold'

            y_pos = self.pixel_header_height + (i * self.cell_size)
            yield drawing.line(start=(0, y_pos),
                               end=(self.full_width, y_pos),
                               **extra)

        # draw vertical lines
        for i in range(self.board.width + 1):
            extra = dict()

            if i % self.BOLD_EVERY == 0 or i == self.board.width:
                extra['class'] = 'bold'

            x_pos = self.pixel_side_width + (i * self.cell_size)
            yield drawing.line(start=(x_pos, 0),
                               end=(x_pos, self.full_height),
                               **extra)

    def render(self):
        self.drawing.write(self.stream)
        # self._print(self.drawing.tostring())

    def draw(self, cells=None):
        self.drawing.elements = []
        self.drawing.add(self.drawing.defs)

        super(SvgRenderer, self).draw(cells=cells)
示例#19
0
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)
    x += delta_x

legend_text = dwg.add(
    dwg.g(font_size=main_font_size, style="font-family: arial;"))
legend_text.add(dwg.text('Phys', (x - 1500, y - 10), fill=color_phys))
legend_text.add(dwg.text('Scaled', (x - 1500, y + 20), fill=color_scaled))

v_lines = dwg.add(dwg.g(stroke_width=7.0, stroke=color_phys, fill='none'))
v_lines_scaled = dwg.add(
    dwg.g(stroke_width=7.0, stroke=color_scaled, fill='none'))

for loc in vertical_locs:
    v_lines.add(dwg.line(start=(loc, y + 15), end=(loc, 1300)))
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)
    x += delta_x

legend_text = dwg.add(dwg.g(font_size=main_font_size, style="font-family: arial;"))
legend_text.add(dwg.text('Phys', (x-1500, y-10), fill=color_phys))
legend_text.add(dwg.text('Scaled', (x-1500, y+20), fill=color_scaled))

v_lines = dwg.add(dwg.g(stroke_width=7.0, stroke=color_phys, fill='none'))
v_lines_scaled = dwg.add(dwg.g(stroke_width=7.0, stroke=color_scaled, fill='none'))

for loc in vertical_locs:
    v_lines.add(dwg.line(start=(loc, y+15), end=(loc, 1300)))

extra_text = dwg.add(dwg.g(font_size=main_font_size - 3, style="font-family: arial;"))
extra_text.add(dwg.text('fwd mode', (150, 50)))
示例#21
0
labels = data.iloc[:,0]
cols = data.iloc[:,1:]

ncols = cols.shape[1]
nlines = ncols - 1
colspace = h - nlines*200
colwidth = colspace / float(ncols)
collocs = (arange(ncols) + 1)*colwidth

# pad
g = dwg.add(dwg.g(transform="translate(%i,%i)" % (padding,padding)))
title = g.add(dwg.g())

for i in range(ncols):
    if i==0:
        title.add(dwg.text(cols.columns[i], insert=(collocs[i],0), text_anchor='end', font_size='%ipx' % titlefont))
    elif i==ncols-1:
        title.add(dwg.text(cols.columns[i], insert=(collocs[i],0), font_size='%ipx' % titlefont))

g = g.add(dwg.g(transform="translate(0,%i)" % (titlefont+fontsize)))
# loop over and add labels
vmin = min(cols.values)
vmax = max(cols.values)

def scale(val, src=(vmin, vmax), dst=(0, h)):
    return ((float(val) - src[0]) / (src[1]-src[0])) * (dst[1]-dst[0]) + dst[0]

def vertplace(j, col):
    val = col.iloc[j]
    if j > 0:
        prev = col.iloc[j-1]
示例#22
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,
          openinbrowser=True, 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 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 will fix issues with rapidly opening
        multiple SVGs in your browser.

        :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
      
    # save svg
    if not os_path.exists(os_path.dirname(filename)):
        makedirs(os_path.dirname(filename))
    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)
示例#23
0
    def draw(self, dr: svgwrite.Drawing, g: svgwrite.container.Group, size: XY,
             offset: XY) -> None:
        if self.poster.tracks is None:
            raise PosterError("No tracks to draw")
        year_size = 200 * 4.0 / 80.0
        year_style = f"font-size:{year_size}px; font-family:Arial;"
        year_length_style = f"font-size:{110 * 3.0 / 80.0}px; font-family:Arial;"
        month_names_style = "font-size:2.5px; font-family:Arial"
        total_length_year_dict = self.poster.total_length_year_dict
        for year in self.poster.years.iter():
            g_year = dr.g(id=f"year{year}")
            g.add(g_year)

            start_date_weekday, _ = calendar.monthrange(year, 1)
            github_rect_first_day = datetime.date(year, 1, 1)
            # Github profile the first day start from the last Monday of the last year or the first Monday of this year
            # It depands on if the first day of this year is Monday or not.
            github_rect_day = github_rect_first_day + datetime.timedelta(
                -start_date_weekday)
            year_length = total_length_year_dict.get(year, 0)
            year_length_str = utils.format_float(self.poster.m2u(year_length))
            month_names = [
                locale.nl_langinfo(day)[:3]  # Get only first three letters
                for day in [
                    locale.MON_1,
                    locale.MON_2,
                    locale.MON_3,
                    locale.MON_4,
                    locale.MON_5,
                    locale.MON_6,
                    locale.MON_7,
                    locale.MON_8,
                    locale.MON_9,
                    locale.MON_10,
                    locale.MON_11,
                    locale.MON_12,
                ]
            ]
            km_or_mi = self.poster.u()
            g_year.add(
                dr.text(
                    f"{year}",
                    insert=offset.tuple(),
                    fill=self.poster.colors["text"],
                    alignment_baseline="hanging",
                    style=year_style,
                ))

            g_year.add(
                dr.text(
                    f"{year_length_str} {km_or_mi}",
                    insert=(offset.tuple()[0] + 165, offset.tuple()[1] + 2),
                    fill=self.poster.colors["text"],
                    alignment_baseline="hanging",
                    style=year_length_style,
                ))
            # add month name up to the poster one by one because of svg text auto trim the spaces.
            for num, name in enumerate(month_names):
                g_year.add(
                    dr.text(
                        f"{name}",
                        insert=(offset.tuple()[0] + 15.5 * num,
                                offset.tuple()[1] + 14),
                        fill=self.poster.colors["text"],
                        style=month_names_style,
                    ))

            rect_x = 10.0
            dom = (2.6, 2.6)
            # add every day of this year for 53 weeks and per week has 7 days
            animate_index = 1
            year_count = self.poster.year_tracks_date_count_dict[year]
            key_times = utils.make_key_times(year_count)
            for _i in range(54):
                rect_y = offset.y + year_size + 2
                for _j in range(7):
                    if int(github_rect_day.year) > year:
                        break
                    rect_y += 3.5
                    color = "#444444"
                    date_title = str(github_rect_day)
                    if date_title in self.poster.tracks_by_date:
                        tracks = self.poster.tracks_by_date[date_title]
                        length = sum([t.length() for t in tracks])
                        distance1 = self.poster.special_distance[
                            "special_distance"]
                        distance2 = self.poster.special_distance[
                            "special_distance2"]
                        has_special = distance1 < length < distance2
                        color = self.color(self.poster.length_range_by_date,
                                           length, has_special)
                        if length >= distance2:
                            special_color = self.poster.colors.get(
                                "special2") or self.poster.colors.get(
                                    "special")
                            if special_color is not None:
                                color = special_color
                        str_length = utils.format_float(
                            self.poster.m2u(length))
                        date_title = f"{date_title} {str_length} {km_or_mi}"
                        # tricky for may cause animate error
                        if animate_index < len(key_times) - 1:
                            animate_index += 1

                    rect = dr.rect((rect_x, rect_y), dom, fill=color)
                    if self.poster.with_animation:
                        values = (";".join(["0"] * animate_index) + ";" +
                                  ";".join(["1"] *
                                           (len(key_times) - animate_index)))
                        rect.add(
                            svgwrite.animate.Animate(
                                "opacity",
                                dur=f"{self.poster.animation_time}s",
                                values=values,
                                keyTimes=";".join(key_times),
                                repeatCount="1",
                            ))
                    rect.set_desc(title=date_title)
                    g_year.add(rect)
                    github_rect_day += datetime.timedelta(1)
                rect_x += 3.5
            offset.y += 3.5 * 9 + year_size + 1.5
示例#24
0
    def _draw_footer(self, d: svgwrite.Drawing) -> None:
        g = d.g(id="footer")
        d.add(g)

        text_color = self.colors["text"]
        header_style = "font-size:4px; font-family:Arial"
        value_style = "font-size:9px; font-family:Arial"
        small_value_style = "font-size:3px; font-family:Arial"

        (
            total_length,
            average_length,
            length_range,
            weeks,
        ) = self._compute_track_statistics()

        g.add(
            d.text(
                self.translate("ATHLETE"),
                insert=(10, self.height - 20),
                fill=text_color,
                style=header_style,
            ))
        g.add(
            d.text(
                self._athlete,
                insert=(10, self.height - 10),
                fill=text_color,
                style=value_style,
            ))
        g.add(
            d.text(
                self.translate("STATISTICS"),
                insert=(120, self.height - 20),
                fill=text_color,
                style=header_style,
            ))
        g.add(
            d.text(
                self.translate("Number") + f": {len(self.tracks)}",
                insert=(120, self.height - 15),
                fill=text_color,
                style=small_value_style,
            ))
        g.add(
            d.text(
                self.translate("Weekly") + ": " +
                format_float(len(self.tracks) / weeks),
                insert=(120, self.height - 10),
                fill=text_color,
                style=small_value_style,
            ))
        g.add(
            d.text(
                self.translate("Total") + ": " +
                self.format_distance(total_length),
                insert=(141, self.height - 15),
                fill=text_color,
                style=small_value_style,
            ))
        g.add(
            d.text(
                self.translate("Avg") + ": " +
                self.format_distance(average_length),
                insert=(141, self.height - 10),
                fill=text_color,
                style=small_value_style,
            ))
        if length_range.is_valid():
            min_length = length_range.lower()
            max_length = length_range.upper()
            assert min_length is not None
            assert max_length is not None
        else:
            min_length = 0.0
            max_length = 0.0
        g.add(
            d.text(
                self.translate("Min") + ": " +
                self.format_distance(min_length),
                insert=(167, self.height - 15),
                fill=text_color,
                style=small_value_style,
            ))
        g.add(
            d.text(
                self.translate("Max") + ": " +
                self.format_distance(max_length),
                insert=(167, self.height - 10),
                fill=text_color,
                style=small_value_style,
            ))
示例#25
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
示例#26
0
    def draw(self, dr: svgwrite.Drawing, size: XY, offset: XY):
        if self.poster.tracks is None:
            raise PosterError("No tracks to draw")
        year_size = 200 * 4.0 / 80.0
        year_style = f"font-size:{year_size}px; font-family:Arial;"
        year_length_style = f"font-size:{110 * 3.0 / 80.0}px; font-family:Arial;"
        month_names_style = f"font-size:2.5px; font-family:Arial"
        total_length_year_dict = self.poster.total_length_year_dict
        for year in self.poster.years:
            start_date_weekday, _ = calendar.monthrange(year, 1)
            github_rect_first_day = datetime.date(year, 1, 1)
            # Github profile the first day start from the last Monday of the last year or the first Monday of this year
            # It depands on if the first day of this year is Monday or not.
            github_rect_day = github_rect_first_day + datetime.timedelta(
                -start_date_weekday)
            year_length = total_length_year_dict.get(year, 0)
            month_names = [
                locale.nl_langinfo(day)[:3]  # Get only first three letters
                for day in [
                    locale.MON_1,
                    locale.MON_2,
                    locale.MON_3,
                    locale.MON_4,
                    locale.MON_5,
                    locale.MON_6,
                    locale.MON_7,
                    locale.MON_8,
                    locale.MON_9,
                    locale.MON_10,
                    locale.MON_11,
                    locale.MON_12,
                ]
            ]
            dr.add(
                dr.text(
                    f"{year}",
                    insert=offset.tuple(),
                    fill=self.poster.colors["text"],
                    alignment_baseline="hanging",
                    style=year_style,
                ))

            dr.add(
                dr.text(
                    f"{year_length} Likes",
                    insert=(offset.tuple()[0] + 160, offset.tuple()[1] + 4),
                    fill=self.poster.colors["text"],
                    alignment_baseline="hanging",
                    style=year_length_style,
                ))
            # add month name up to the poster one by one because of svg text auto trim the spaces.
            for num, name in enumerate(month_names):
                dr.add(
                    dr.text(
                        f"{name}",
                        insert=(offset.tuple()[0] + 15.5 * num,
                                offset.tuple()[1] + 14),
                        fill=self.poster.colors["text"],
                        style=month_names_style,
                    ))

            rect_x = 10.0
            dom = (2.6, 2.6)
            # add every day of this year for 53 weeks and per week has 7 days
            for i in range(54):
                rect_y = offset.y + year_size + 2
                for j in range(7):
                    if int(github_rect_day.year) > year:
                        break
                    rect_y += 3.5
                    color = "#444444"
                    date_title = str(github_rect_day)
                    if date_title in self.poster.tracks_by_date:
                        tracks = self.poster.tracks_by_date[date_title]
                        likes = sum([t["likes_count"] for t in tracks])
                        likes_gap = self.poster.special_likes["special_likes"]
                        likes_gap2 = self.poster.special_likes[
                            "special_likes2"]
                        if year < 2014:
                            likes_gap = likes_gap / 10
                            likes_gap2 = likes_gap2 / 10
                        has_special = likes_gap < likes < likes_gap2
                        color = self.color(self.poster.length_range_by_date,
                                           likes, has_special)
                        if likes >= likes_gap2:
                            color = self.poster.colors.get(
                                "special2") or self.poster.colors.get(
                                    "special")
                        str_likes = str(likes)
                        date_title = f"{date_title} {str_likes} Likes"

                    rect = dr.rect((rect_x, rect_y), dom, fill=color)
                    rect.set_desc(title=date_title)
                    dr.add(rect)
                    github_rect_day += datetime.timedelta(1)
                rect_x += 3.5
            offset.y += 3.5 * 9 + year_size + 1.5
示例#27
0
    def _draw_year(self, dr: svgwrite.Drawing, g: svgwrite.container.Group,
                   size: XY, offset: XY, year: int) -> None:
        min_size = min(size.x, size.y)
        outer_radius = 0.5 * min_size - 6
        radius_range = ValueRange.from_pair(outer_radius / 4, outer_radius)
        center = offset + 0.5 * size

        if self._rings:
            self._draw_rings(dr, g, center, radius_range)

        year_style = f"dominant-baseline: central; font-size:{min_size * 4.0 / 80.0}px; font-family:Arial;"
        month_style = f"font-size:{min_size * 3.0 / 80.0}px; font-family:Arial;"

        g.add(
            dr.text(
                f"{year}",
                insert=center.tuple(),
                fill=self.poster.colors["text"],
                text_anchor="middle",
                alignment_baseline="middle",
                style=year_style,
            ))
        df = 360.0 / (366 if calendar.isleap(year) else 365)
        day = 0
        date = datetime.date(year, 1, 1)
        while date.year == year:
            text_date = date.strftime("%Y-%m-%d")
            a1 = math.radians(day * df)
            a2 = math.radians((day + 1) * df)
            if date.day == 1:
                (_, last_day) = calendar.monthrange(date.year, date.month)
                a3 = math.radians((day + last_day - 1) * df)
                sin_a1, cos_a1 = math.sin(a1), math.cos(a1)
                sin_a3, cos_a3 = math.sin(a3), math.cos(a3)
                r1 = outer_radius + 1
                r2 = outer_radius + 6
                r3 = outer_radius + 2
                g.add(
                    dr.line(
                        start=(center + r1 * XY(sin_a1, -cos_a1)).tuple(),
                        end=(center + r2 * XY(sin_a1, -cos_a1)).tuple(),
                        stroke=self.poster.colors["text"],
                        stroke_width=0.3,
                    ))
                path = dr.path(
                    d=("M", center.x + r3 * sin_a1, center.y - r3 * cos_a1),
                    fill="none",
                    stroke="none",
                )
                path.push(
                    f"a{r3},{r3} 0 0,1 {r3 * (sin_a3 - sin_a1)},{r3 * (cos_a1 - cos_a3)}"
                )
                g.add(path)
                tpath = svgwrite.text.TextPath(
                    path,
                    self.poster.month_name(date.month),
                    startOffset=(0.5 * r3 * (a3 - a1)))
                text = dr.text(
                    "",
                    fill=self.poster.colors["text"],
                    text_anchor="middle",
                    style=month_style,
                )
                text.add(tpath)
                g.add(text)
            if text_date in self.poster.tracks_by_date:
                self._draw_circle_segment(
                    dr,
                    g,
                    self.poster.tracks_by_date[text_date],
                    a1,
                    a2,
                    radius_range,
                    center,
                )

            day += 1
            date += datetime.timedelta(1)
示例#28
0
    def _create_group(self, drawing: Drawing, projection: np.ndarray,
                      viewport: Viewport, group: Group):
        """
        Render all the meshes contained in this group.

        The main consideration here is that we will consider the z-index of
        every object in this group.
        """
        default_style = group.style or {}
        shaders = [
            mesh.shader or (lambda face_index, winding: {})
            for mesh in group.meshes
        ]
        annotators = [
            mesh.annotator or (lambda face_index: None)
            for mesh in group.meshes
        ]

        # A combination of mesh and face indes
        mesh_faces: List[np.ndarray] = []

        for i, mesh in enumerate(group.meshes):
            faces = mesh.faces

            # Extend each point to a vec4, then transform to clip space.
            faces = np.dstack([faces, np.ones(faces.shape[:2])])
            faces = np.dot(faces, projection)

            # Reject trivially clipped polygons.
            xyz, w = faces[:, :, :3], faces[:, :, 3:]
            accepted = np.logical_and(np.greater(xyz, -w), np.less(xyz, +w))
            accepted = np.all(accepted,
                              2)  # vert is accepted if xyz are all inside
            accepted = np.any(accepted,
                              1)  # face is accepted if any vert is inside
            degenerate = np.less_equal(w, 0)[:, :,
                                             0]  # vert is bad if its w <= 0
            degenerate = np.any(degenerate,
                                1)  # face is bad if any of its verts are bad
            accepted = np.logical_and(accepted, np.logical_not(degenerate))
            faces = np.compress(accepted, faces, axis=0)

            # Apply perspective transformation.
            xyz, w = faces[:, :, :3], faces[:, :, 3:]
            faces = xyz / w
            mesh_faces.append(faces)

        # Sort faces from back to front.
        mesh_face_indices = self._sort_back_to_front(mesh_faces)

        # Apply viewport transform to X and Y.
        for faces in mesh_faces:
            faces[:, :, 0:1] = (1.0 + faces[:, :, 0:1]) * viewport.width / 2
            faces[:, :, 1:2] = (1.0 - faces[:, :, 1:2]) * viewport.height / 2
            faces[:, :, 0:1] += viewport.minx
            faces[:, :, 1:2] += viewport.miny

        # Compute the winding direction of each polygon.
        mesh_windings: List[np.ndarray] = []
        for faces in mesh_faces:
            windings = np.zeros(faces.shape[0])
            if faces.shape[1] >= 3:
                p0, p1, p2 = faces[:, 0, :], faces[:, 1, :], faces[:, 2, :]
                normals = np.cross(p2 - p0, p1 - p0)
                np.copyto(windings, normals[:, 2])
            mesh_windings.append(windings)

        group_ = drawing.g(**default_style)
        text_group_ = drawing.g(**default_style)

        # Finally draw the group
        for mesh_index, face_index in mesh_face_indices:
            face = mesh_faces[mesh_index][face_index]
            style = shaders[mesh_index](face_index,
                                        mesh_windings[mesh_index][face_index])
            if style is None:
                continue
            face = np.around(face[:, :2], self.precision)

            if len(face) == 1:
                group_.add(
                    drawing.circle(face[0], style.pop("radius", 0.005),
                                   **style))
            if len(face) == 2:
                group_.add(drawing.line(face[0], face[1], **style))
            else:
                group_.add(drawing.polygon(face, **style))

            annotation = annotators[mesh_index](face_index)
            if annotation is not None:
                centroid = face.mean(axis=0)
                text_group_.add(drawing.text(insert=centroid, **annotation))

        return [group_, text_group_]