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 raster_to_vector(image, path): drawing = Drawing(path, profile='tiny') width, height = image.size pixels = image.load() start_x = start_y = 0 last_pixel = pixels[0, 0] for close_y in range(height): for close_x in range(width): pixel = pixels[close_x, close_y] # If pixel has different color if pixel != last_pixel: color = _RGB.format(*last_pixel) _add_rectangles(drawing, width, color, start_x, start_y, close_x, close_y) # Reset values last_pixel = pixel start_x = close_x start_y = close_y _add_rectangles(drawing, width, color, start_x, start_y, close_x, close_y) # Save constructed SVG try: drawing.save() except FileNotFoundError: makedirs(dirname(path)) drawing.save()
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)
def create_empty_chart(full_file_name): """Creates a chart of the proper dimensions with a white background.""" num_days_in_week = 7 num_weeks = math.ceil(NUM_DAYS_TO_SHOW / 7.0) if date.today().weekday() + 1 < NUM_DAYS_TO_SHOW % 7: # We need to draw NUM_DAYS_TO_SHOW % 7 extra days, but on the last week # we have only space for date.today().weekday() + 1 days. num_weeks += 1 width = 2 * MARGIN + num_weeks * DAY_BOX_SIZE + \ (num_weeks - 1) * DAY_BOX_SEPARATION height = 2 * MARGIN + num_days_in_week * DAY_BOX_SIZE + \ (num_days_in_week - 1) * DAY_BOX_SEPARATION chart = Drawing(full_file_name, size=(width, height)) chart.add(chart.rect(insert=(0, 0), size=(width, height), fill='white')) return chart
def create_svg_image(styles, board_size, hexagons): """ Creates SVG drawing. The drawing contains all given css styles, a board (background rectangle) of given size and all given hexagons. The board can be styled using '.board'. All hexagonal fields can be styled using '.hex-field'. Fields can be also styled using 'hex-field-X', where X is the type of the field. :param styles iterable of css styles (strings) :param board_size tuple representing board size (width, height) :param hexagons iterable of hexagons (tuples in a form of (vertices, type) ) :returns SVG Drawing object """ svg_image = Drawing() for style in styles: svg_image.add(svg_image.style(style)) svg_image.add(svg_image.rect(size=board_size, class_="board")) for hexagon in hexagons: svg_image.add(svg_image.polygon(hexagon.vertices, class_="hex-field hex-field-%d" % hexagon.type)) return svg_image
def test_single_bp_ins_exon(self): transcript = fusion.FusionTranscript() transcript.position = Interval(401258, 408265) transcript.exons = [ genomic.Exon(401258, 401461, transcript=transcript), genomic.Exon(404799, 405254, intact_end_splice=False, transcript=transcript), genomic.Exon(405255, 405255, intact_start_splice=False, intact_end_splice=False, transcript=transcript), genomic.Exon(405256, 408265, intact_start_splice=False, transcript=transcript) ] cfg = DiagramSettings(width=1500) canvas = Drawing(size=(cfg.width, 1000)) drawing_width = cfg.width - cfg.label_left_margin - cfg.left_margin - cfg.right_margin canvas.add( draw_ustranscript(cfg, canvas, transcript, target_width=drawing_width)) if OUTPUT_SVG: canvas.saveas('test_single_bp_ins_exon.svg')
def generate_tiling(self): dwg = Drawing("{}/tiling2.svg".format(self.output_folder), profile="tiny") current_color = 0 row_spacing = self.pent_height * 2 + self.bottom_length for y in range(self.num_down): transform = "translate({}, {})".format(0, self.rep_spacing * y) dgroup = dwg.add(dwg.g(transform=transform)) for x in range(self.num_across): # if x is odd, point 1 of pent 1 needs to be attached to point 3 of pent 2 if x % 2 == 1: dx = int( x / 2 ) * self.rep_spacing + self.pent_width * 2 + self.column_offset.real transform = "translate({}, {})".format( dx, self.column_offset.imag) else: transform = "translate({}, {})".format( int(x / 2) * self.rep_spacing, 0) group = dgroup.add(dwg.g(transform=transform)) for pent in self.cairo_group: group.add( dwg.path( **{ 'd': pent.d(), 'fill': self._colors[current_color % len(self._colors)], 'stroke-width': 4, 'stroke': rgb(0, 0, 0) })) current_color += 1 dwg.viewbox(*self.pattern_viewbox) dwg.save(pretty=True)
def box_with_char( drawing: svgwrite.Drawing, text: str, x=0, y=0, width=BOX_SIZE, height=BOX_SIZE, border_width=BORDER_WIDTH, fill="#fff", border="#888", char_width=4, char_height=12, ): """Render a box with a single character inside of it""" drawing.add( drawing.rect( insert=(x, y), size=(width, height), fill=fill, stroke=border, stroke_width=border_width, )) text_x = x - char_width * len(str(text)) + width // 2 text_y = y + height // 2 + char_height // 2 drawing.add( drawing.text(text, insert=(text_x, text_y), fill=svgwrite.rgb(50, 50, 50)))
def _draw_classical_double_line(drawing: Drawing, x1_coord: int, y1_coord: int, x2_coord: int, y2_coord: int) -> None: """Draw a double line between (x1_coord, y1_coord) and (x2_coord, y2_coord). :param drawing: Drawing that will be used to draw. :param x1_coord: x-coordinate of the first point. :param y1_coord: y-coordinate of the first point. :param x2_coord: x-coordinate of the second point. :param y2_coord: y-coordinate of the second point. :raise NotImplementedError: when x1_coord != x2_coord and y1_coord != y2_coord (i.e. when the line is neither horizontal nor vertical). """ x_increment, y_increment = 0, 0 if x1_coord == x2_coord: x_increment = _constants.DOUBLE_LINES_SEPARATION elif y1_coord == y2_coord: y_increment = _constants.DOUBLE_LINES_SEPARATION else: raise NotImplementedError("The drawn line should be either horizontal " "or vertical.") drawing.add( drawing.line(start=(x1_coord - x_increment, y1_coord - y_increment), end=(x2_coord - x_increment, y2_coord - y_increment), stroke=_constants.GATE_BORDER_COLOR, stroke_width=_constants.STROKE_THICKNESS)) drawing.add( drawing.line(start=(x1_coord + x_increment, y1_coord + y_increment), end=(x2_coord + x_increment, y2_coord + y_increment), stroke=_constants.GATE_BORDER_COLOR, stroke_width=_constants.STROKE_THICKNESS))
def plot(self, x, y, scale, **extra): assert isinstance(scale, (int, float)) assert isinstance(x, (int, float)) assert isinstance(y, (int, float)) svg = Drawing() g = svg.g() p1 = [x, y] p2 = [x + max(self.ticks) * scale, y] g.add(svg.line(start=p1, end=p2, stroke="black", stroke_width=1)) for i, tick in enumerate(self.ticks): p1 = [x + tick * scale, y - 5] p2 = [x + tick * scale, y + 0.5] g.add(svg.line(start=p1, end=p2, stroke="black", stroke_width=1)) g.add( svg.text(text="%s" % self.labels[i], insert=[x + tick * scale, y + 10], font_size=8, font_family="Arial")) return g
def draw(self, start=0, end=None, ext='svg', name=None): if end is None: end = os.path.getsize(self.source_path) if name is None: name = self.source_path.name name = f"{name}.{ext}" if ext == 'svg': from svgwrite import Drawing from svgwrite.shapes import Rect dwg = Drawing(name, profile='tiny') elif ext == 'dxf': from dxfwrite import DXFEngine as dxf dwg = dxf.drawing(name) with open(self.source_path, 'rb') as source_file: for i, bit in enumerate(bits(source_file, start, end)): if bit == self.invert: x = (i // self.height) * self.scale y = (-i % self.height) * self.scale params = { 'insert': (x, y), 'size': (self.scale, self.scale) } if ext == 'dxf': rect = DxfRect(**params).to_face3d() else: rect = Rect(**params) dwg.add(rect) dwg.save()
def test_single_bp_dup(self): transcript = fusion.FusionTranscript() transcript.position = Interval(1, 500) transcript.exons = [ genomic.Exon(1, 7, transcript=transcript, intact_end_splice=False), genomic.Exon(8, 8, transcript=transcript, intact_start_splice=False, intact_end_splice=False), genomic.Exon(9, 100, transcript=transcript, intact_start_splice=False), genomic.Exon(200, 500, transcript=transcript) ] cfg = DiagramSettings(width=1500) canvas = Drawing(size=(cfg.width, 1000)) drawing_width = cfg.width - cfg.label_left_margin - cfg.left_margin - cfg.right_margin canvas.add( draw_ustranscript(cfg, canvas, transcript, target_width=drawing_width)) if OUTPUT_SVG: canvas.saveas('test_single_bp_dup.svg')
def _draw_rings(self, dr: svgwrite.Drawing, center: XY, radius_range: ValueRange): length_range = self.poster.length_range_by_date ring_distance = self._determine_ring_distance() if ring_distance is None: return distance = ring_distance while distance < length_range.upper(): radius = ( radius_range.lower() + radius_range.diameter() * distance / length_range.upper() ) dr.add( dr.circle( center=center.tuple(), r=radius, stroke=self._ring_color, stroke_opacity="0.2", fill="none", stroke_width=0.3, ) ) distance += ring_distance
class Builder(object): def __init__(self, name, commits, configuration, deltax=50.0, width=700.0, y=40.0, sep=20.0, image_size=40.0): self.commits = commits self.authors = AuthorList() self.initial_last_dates(configuration) self.position_function() height = y + sep + (self.max_elements + 1) * (image_size + sep) self.dwg = Drawing(name, size=(width + 2 * deltax, height)) self.deltax = deltax self.width = width self.y = y self.sep = sep self.image_size = image_size def initial_last_dates(self, configuration): self.initial = self.commits[0]["date"] self.last = self.commits[-1]["date"] self.max_elements = 0 for date_tuple, elements in configuration.items(): date = datetime(*date_tuple).replace(tzinfo=timezone.utc) self.last = max(self.last, date) self.initial = min(self.initial, date) self.max_elements = max(self.max_elements, len(elements)) def position_function(self): size = self.last.timestamp() - self.initial.timestamp() def position(date, y, deltax=0, deltay=0): return (self.deltax + (date.timestamp() - self.initial.timestamp()) / size * self.width + deltax, y + deltay) self.position = position def add(self, element): for xml in element.draw(self): self.dwg.add(xml) def save(self): self.dwg.save()
def __init__(self, name, commits, configuration, deltax=50.0, width=700.0, y=40.0, sep=20.0, image_size=40.0): self.commits = commits self.authors = AuthorList() self.initial_last_dates(configuration) self.position_function() height = y + sep + (self.max_elements + 1) * (image_size + sep) self.dwg = Drawing(name, size=(width + 2 * deltax, height)) self.deltax = deltax self.width = width self.y = y self.sep = sep self.image_size = image_size
def slice_file(f=None, resolution=0.1): print("Status: Loading File.") model = STLModel(f) scale = 10 stats = model.stats() sub_vertex = Vector3(stats['extents']['x']['lower'], stats['extents']['y']['lower'], stats['extents']['z']['lower']) add_vertex = Vector3(0.5, 0.5, 0.5) model.xmin = model.xmax = None model.ymin = model.ymax = None model.zmin = model.zmax = None print("Status: Scaling Triangles.") for triangle in model.triangles: triangle.vertices[0] -= sub_vertex triangle.vertices[1] -= sub_vertex triangle.vertices[2] -= sub_vertex # The lines above have no effect on the normal. triangle.vertices[0] = (triangle.vertices[0] * scale) + add_vertex triangle.vertices[1] = (triangle.vertices[1] * scale) + add_vertex triangle.vertices[2] = (triangle.vertices[2] * scale) + add_vertex # Recalculate the triangle normal u = model.triangles[0].vertices[1] - model.triangles[0].vertices[0] v = model.triangles[0].vertices[2] - model.triangles[0].vertices[0] triangle.n = Normal((u.y * v.z) - (u.z * v.y), (u.z * v.x) - (u.x * v.z), (u.x * v.y) - (u.y * v.x)) model.update_extents(triangle) print("Status: Calculating Slices") interval = scale * resolution stats = model.stats() print(stats) for targetz in range(0, int(stats['extents']['z']['upper']), int(interval)): dwg = Drawing('outputs/svg/' + str(targetz) + '.svg', profile='tiny') pairs = model.slice_at_z(targetz) for pair in pairs: dwg.add(dwg.line(pair[0], pair[1], stroke=rgb(0, 0, 0, "%"))) dwg.save() print("Status: Finished Outputting Slices")
def render(self, dwg: Drawing) -> Group: g = dwg.g() eye = dwg.circle(center=(0, 0), r=self.radius, fill="white", stroke_width=0) g.add(eye) pupil_radius = self.radius * self.pupil_radius pupil_rho = min(self.radius * self.pupil_rho, self.radius * (1 - self.pupil_radius)) pupil_coords = (pupil_rho * math.cos(self.pupil_phi), pupil_rho * math.sin(self.pupil_phi)) pupil = dwg.circle(center=pupil_coords, r=pupil_radius, fill="black", stroke_width=0) g.add(pupil) reflection_radius = pupil_radius * .2 reflection_rho = pupil_radius * .6 reflection_phi = 1.75 * math.pi reflection_coords = (pupil_coords[0] + reflection_rho * math.cos(reflection_phi), pupil_coords[1] + reflection_rho * math.sin(reflection_phi)) reflection = dwg.circle(center=reflection_coords, r=reflection_radius, fill="white") g.add(reflection) return g
def draw(self, dr: svgwrite.Drawing, size: XY, offset: XY): """Draw the heatmap based on tracks.""" normal_lines = [] special_lines = [] bbox = self._determine_bbox() for tr in self.poster.tracks: for line in utils.project(bbox, size, offset, tr.polylines): if tr.special: special_lines.append(line) else: normal_lines.append(line) for lines, color in [(normal_lines, self.poster.colors['track']), (special_lines, self.poster.colors['special'])]: for opacity, width in [(0.1, 5.0), (0.2, 2.0), (1.0, 0.3)]: for line in lines: dr.add( dr.polyline(points=line, stroke=color, stroke_opacity=opacity, fill='none', stroke_width=width, stroke_linejoin='round', stroke_linecap='round'))
def test_draw_template(self): # def draw_template(self, canvas, template, target_width, height, labels=None, colors=None): d = DiagramSettings() canvas = Drawing(size=(1000, 50)) t = genomic.Template('1', 1, 100000, bands=[ BioInterval(None, 1, 8000, 'p1'), BioInterval(None, 10000, 15000, 'p2') ]) g = draw_template(d, canvas, t, 1000) canvas.add(g) canvas.attribs['height'] = g.height canvas = Drawing(size=(1000, 50)) g = draw_template(d, canvas, TEMPLATE_METADATA['1'], 1000) self.assertEqual( d.breakpoint_top_margin + d.breakpoint_bottom_margin + d.template_track_height, g.height) canvas.add(g) canvas.attribs['height'] = g.height self.assertEqual(2, len(canvas.elements))
def save_radial_tree_plot(filename, root_list, step_size): # define some params white = "rgb(255, 255, 255)" black = "rgb(0, 0, 0)" # create the drawing surface svg_drawing = Drawing(filename=filename, size=(SVG_SIZE, SVG_SIZE), debug=True) # create defs, in this case, just a single gradient rad_grad = svg_drawing.radialGradient(("50%", "50%"), "100%", ("50%", "50%"), id="rad_grad") rad_grad.add_stop_color("0%", black, 255) rad_grad.add_stop_color("100%", white, 255) svg_drawing.defs.add(rad_grad) tree_plot = svg_drawing.mask( id='treeplot', style= 'stroke: black; stroke-width: 3; fill: none; stroke-linecap: round; stroke-opacity: 0.5;' ) tree_plot.add(svg_drawing.rect((0, 0), (SVG_SIZE, SVG_SIZE)).fill(white)) for root in root_list: draw_radial_tree_node(svg_drawing, tree_plot, root, rad_grad, step_size) base_rect = svg_drawing.rect((0, 0), (SVG_SIZE, SVG_SIZE), mask="url(#treeplot)").fill(black) svg_drawing.add(base_rect) svg_drawing.add(tree_plot) svg_drawing.save()
def make_x(wall: Wall, svg: Drawing): rngx = np.arange(2, 146 - 2, 0.25) waves = Polygon.Polygon() w2 = 0.85 for y in np.arange(80, 100 + 120, 5.0): points = [] for x in rngx: fxy = f(x + wall.x0, y / 1.66) points.append((x + wall.x0, y + fxy + w2)) for x in reversed(rngx): fxy = f(x + wall.x0, y / 1.66) points.append((x + wall.x0, y + fxy - w2)) p = Polygon.Polygon(points) waves += wall.window & p svg.save() waves &= wall.window wall.result = wall.wall - wall.window + waves
def draw(self, svg: svgwrite.Drawing, point: np.array, color: Color, opacity=1.0, tags: Dict[str, Any] = None, outline: bool = False): """ Draw icon shape into SVG file. :param svg: output SVG file :param point: icon position :param color: fill color :param opacity: icon opacity :param tags: tags to be displayed as hint :param outline: draw outline for the icon """ point = np.array(list(map(int, point))) path: svgwrite.path.Path = self.get_path(svg, point) path.update({"fill": color.hex}) if outline: opacity: float = 0.5 path.update({ "fill": color.hex, "stroke": color.hex, "stroke-width": 2.2, "stroke-linejoin": "round" }) if opacity != 1.0: path.update({"opacity": opacity}) if tags: title: str = "\n".join(map(lambda x: x + ": " + tags[x], tags)) path.set_desc(title=title) svg.add(path)
def make_lines2(layer, drawing: svgwrite.Drawing, inpts): tail = inpts while len(tail) > 1: head, *tail = tail minpt = min(tail, key=lambda x: distance(x, head)) if not in_circle(circle_radius, mid_point( head, minpt)) and distance(minpt, head) < 15: layer.add( drawing.line(head, minpt, stroke=svgwrite.rgb(255, 10, 16, '%'), stroke_width=8, stroke_linecap='round'))
def draw_svg(self, drawing: svgwrite.Drawing, g): glane = drawing.g() glane.attribs["class"] = "lane" cp1 = self.control_points[0] cp2 = self.control_points[-1] rel = relative_pose(cp1.as_SE2(), cp2.as_SE2()) _, theta = geo.translation_angle_from_SE2(rel) delta = int(np.sign(theta)) fill = {-1: "#577094", 0: "#709457", 1: "#947057"}[delta] points = self.lane_profile(points_per_segment=10) p = drawing.polygon(points=points, fill=fill, fill_opacity=0.5) glane.add(p) center_points = self.center_line_points(points_per_segment=10) center_points = [ geo.translation_angle_from_SE2(_)[0] for _ in center_points ] p = drawing.polyline( points=center_points, stroke=fill, fill="none", stroke_dasharray="0.02", stroke_width=0.01, ) glane.add(p) g.add(glane) for x in self.control_points: q = x.asmatrix2d().m p1, _ = geo.translation_angle_from_SE2(q) delta_arrow = np.array([self.width / 4, 0]) p2 = SE2_apply_R2(q, delta_arrow) gp = drawing.g() gp.attribs["class"] = "control-point" l = drawing.line( start=p1.tolist(), end=p2.tolist(), stroke="black", stroke_width=self.width / 20.0, ) gp.add(l) c = drawing.circle( center=p1.tolist(), r=self.width / 8, fill="white", stroke="black", stroke_width=self.width / 20.0, ) gp.add(c) g.add(gp)
def make_y(wall: Wall, svg: Drawing): rngy = np.arange(wall.y0 - 2, wall.y1 + 2, 0.25) waves = Polygon.Polygon() w2 = 0.85 for x in np.arange(wall.x0 - 20, wall.x1 + 20, 6.0): points = [] for y in rngy: fxy = f(x, y / 1.66) points.append((x + fxy - w2, y)) for y in reversed(rngy): fxy = f(x, y / 1.66) points.append((x + fxy + w2, y)) p = Polygon.Polygon(points) waves += wall.window & p svg.save() waves &= wall.window wall.result = wall.wall - wall.window + waves
def _draw_circle_segment( self, dr: svgwrite.Drawing, g: svgwrite.container.Group, tracks: typing.List[Track], a1: float, a2: float, rr: ValueRange, center: XY, values: str = "", key_times: str = "", ) -> None: 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) max_length = self.poster.length_range_by_date.upper() if self._max_distance: max_length = self._max_distance.to_base_units() assert max_length is not None r1 = rr.lower() assert r1 is not None r2 = rr.interpolate((length / max_length).magnitude) sin_a1, cos_a1 = math.sin(a1), math.cos(a1) sin_a2, cos_a2 = math.sin(a2), math.cos(a2) path = dr.path( d=("M", center.x + r1 * sin_a1, center.y - r1 * cos_a1), fill=color, stroke="none", ) path.push("l", (r2 - r1) * sin_a1, (r1 - r2) * cos_a1) path.push( f"a{r2},{r2} 0 0,0 {r2 * (sin_a2 - sin_a1)},{r2 * (cos_a1 - cos_a2)}" ) path.push("l", (r1 - r2) * sin_a2, (r2 - r1) * cos_a2) date_title = str(tracks[0].start_time().date()) str_length = utils.format_float(self.poster.m2u(length)) path.set_desc(title=f"{date_title} {str_length} {self.poster.u()}") if self.poster.with_animation: path.add( svgwrite.animate.Animate( "opacity", dur=f"{self.poster.animation_time}s", values=values, keyTimes=key_times, repeatCount="indefinite", )) g.add(path)
def _draw_track(self, dr: svgwrite.Drawing, g: svgwrite.container.Group, tr: Track, size: XY, offset: XY) -> None: color = self.color(self.poster.length_range, tr.length(), tr.special) str_length = utils.format_float(self.poster.m2u(tr.length())) date_title = str(tr.start_time.date()) if tr.start_time else "Unknown Date" for line in utils.project(tr.bbox(), size, offset, tr.polylines): polyline = dr.polyline( points=line, stroke=color, fill="none", stroke_width=0.5, stroke_linejoin="round", stroke_linecap="round", ) polyline.set_desc(title=f"{date_title} {str_length} {self.poster.u()}") g.add(polyline)
def square_in_grid(image: Drawing, row: int, column: int, fill, offsets=(), title=None): x_offset = offsets[0] if len(offsets) else 0 y_offset = offsets[1] if len(offsets) > 1 else 0 left = grid_square_left(column, x_offset) top = grid_square_top(row, y_offset) rect = image.rect(insert=(left, top), size=(GRID_SQUARE, GRID_SQUARE), fill=fill) if title is not None: rect.set_desc(title=title) return rect
def test_translate(): translate = shape.translate(-50, -50) xml = ElementTree.tostring(translate.get_svg().get_xml()).decode() assert xml == ('<path d="M 50.000, 50.000 L 70.000, 90.000 ' 'L 110.000, 50.000 Z" />') drawing_translate = Drawing(dirname.joinpath('translate.svg'), size=(200, 200), stroke='black', stroke_width=1) drawing_translate.add(translate.get_svg()) drawing_translate.save()
def save(self): """Saves the illustration into :attr:`output_filename`.""" drawing = Drawing(self.output_filename, size=self.size, stroke=self.stroke, stroke_width=self.stroke_width, font_size=self.font_size, font_family=self.font_family) for region in self._regions: drawing.add(region.get_svg()) drawing.save(pretty=True)
def test_flip(): flip = shape.flip(90) xml = ElementTree.tostring(flip.get_svg().get_xml()).decode() assert xml == ('<path d="M 80.000, 100.000 L 60.000, 140.000 ' 'L 20.000, 100.000 Z" />') drawing_flip = Drawing(dirname.joinpath('flip.svg'), size=(200, 200), stroke='black', stroke_width=1) drawing_flip.add(flip.get_svg()) drawing_flip.save()
def test_scale(): scale = shape.scale(0.5) xml = ElementTree.tostring(scale.get_svg().get_xml()).decode() assert xml == ('<path d="M 50.000, 50.000 L 60.000, 70.000 ' 'L 80.000, 50.000 Z" />') drawing_scale = Drawing(dirname.joinpath('scale.svg'), size=(200, 200), stroke='black', stroke_width=1) drawing_scale.add(scale.get_svg()) drawing_scale.save()
def _draw_registers_names_and_lines(drawing: Drawing, circuit_width: int, json_circuit: str, show_clbits: bool) -> None: # First we draw the names of each register qubit_labels = json_circuit['header'].get('qubit_labels', []) clbit_labels = json_circuit['header'].get('clbit_labels', []) if show_clbits else [] ## 1. Compute the font size that will be used to keep good dimensions font_size = _constants.REGISTER_NAME_FONT_SIZE for qubit_label in itertools.chain(qubit_labels, clbit_labels): qubit_text_name = "{}[{}]".format(*qubit_label) desired_width = (_constants.REGISTER_NAME_WIDTH - _constants.REGISTER_NAME_LEFT_BORDER - _constants.REGISTER_NAME_RIGHT_BORDER) adapted_font_size = _helpers.adapt_text_font_size(qubit_text_name, desired_width, _constants.MAX_REGISTER_NAME_HEIGHT) font_size = min(font_size, adapted_font_size) ## 2. Draw the bit names y_coord = _constants.VERTICAL_BORDER for bit_label in itertools.chain(qubit_labels, clbit_labels): qubit_text_name = "{}[{}]".format(*bit_label) drawing.add( drawing.text( qubit_text_name, insert=(_constants.REGISTER_NAME_WIDTH - _constants.REGISTER_NAME_RIGHT_BORDER, y_coord + _constants.FONT_SIZE_CENTER_VERTICALLY_MULTIPLIER * font_size), text_anchor="end", font_size=font_size ) ) y_coord += _constants.REGISTER_LINES_VERTICAL_SPACING # Then we draw the register lines y_coord = _constants.VERTICAL_BORDER # Start with quantum registers quantum_register_number = json_circuit['header'].get('number_of_qubits', 0) for _ in range(quantum_register_number): drawing.add(drawing.line(start=(_constants.REGISTER_NAME_WIDTH, y_coord), end=(circuit_width, y_coord), stroke=_constants.GATE_BORDER_COLOR, stroke_width=_constants.STROKE_THICKNESS)) y_coord += _constants.REGISTER_LINES_VERTICAL_SPACING # And see if we want to plot classical registers. if show_clbits: classical_register_number = json_circuit['header'].get('number_of_clbits', 0) for _ in range(classical_register_number): _draw_classical_double_line(drawing, _constants.REGISTER_NAME_WIDTH, y_coord, circuit_width, y_coord) y_coord += _constants.REGISTER_LINES_VERTICAL_SPACING
def convert(self): c = canvas = Drawing() img = self._image mean, std = img[:,:,1].mean(), img[:,:,1].std() for x,y in product(xrange(self.width), xrange(self.height)): hue, lum, sat = self._pixelval(x,y) perimeter = self.peri(lum, mean, std) color = Color.from_hls(hue, lum, sat) if perimeter > self._size_threshold: shape = c.circle( center = ( (self._size_of_pixel + self._d_pixel) * x, (self._size_of_pixel + self._d_pixel) * y), r = self._size_of_pixel * perimeter / 2, fill = rgb(*color.rgb8)) canvas.add(shape) return canvas
def draw_calendar(x: int, y: int, dwg: svgwrite.Drawing, lines: List[str]): shapes = dwg.add(dwg.g()) shapes.add(dwg.rect(insert=(x, y), size=(460 * px, 330 * px), fill='white', stroke='black', stroke_width=1)) offset = 35 for line in lines: date_text = dwg.add(dwg.g(font_size=36, font_family="Helvetica")) date_text.add(dwg.text(line, (10 + x, offset + y))) offset += 40
def draw_diagram(self) -> None: n_countries: int = self.model.rowCount() if n_countries > 0: style: SvgStyle = self.svg_style delta_angle: float = 2.0*math.pi/n_countries max_value: float = max(self.model.values) dwg = Drawing(self.temp_svg_file.fileName(), profile='tiny', viewBox='-250 -250 500 500') for idx, v in enumerate(self.model.values): x: float = style.line_length * v/max_value * math.sin(idx * delta_angle) y: float = -style.line_length * v/max_value * math.cos(idx * delta_angle) dwg.add(shapes.Line(start=(0, 0), end=(x, y), stroke=style.line_color, stroke_width=style.line_width)) radius: float = style.circle_rad if style.circle_rad_normalization: radius *= v/max_value dwg.add(shapes.Circle(center=(x, y), r=radius, stroke=style.circle_stroke_color, stroke_width=style.circle_stroke_width, fill=style.circle_fill_color)) dwg.save(pretty=True) self.load_svg(self.temp_svg_file.fileName())
def export_svg_svgwrite(fn, paths, w, h, line_width=0.1): from svgwrite import Drawing w_str = "{}pt".format(w) h_str = "{}pt".format(h) dwg = Drawing(filename = fn, size = (w_str, h_str), viewBox=("0 0 {} {}".format(w,h))) for path in paths: if(len(path) > 1): str_list = [] str_list.append("M {},{}".format(path[0,0],path[0,1])) for e in path[1:]: str_list.append(" L {},{}".format(e[0],e[1])) s = ''.join(str_list) dwg.add(dwg.path(s).stroke(color="rgb(0%,0%,0%)",width=line_width).fill("none")) dwg.save()
def create_svg(file, surface, controls_sequence = None, sn = None): min_x, max_x = None, None min_y, max_y = None, None for polygon in surface.polygons: for vertex in polygon.vertices: if min_x is None: min_x, max_x = vertex[0], vertex[0] min_y, max_y = vertex[1], vertex[1] else: min_x, max_x = min(vertex[0], min_x), max(vertex[0], max_x) min_y, max_y = min(vertex[1], min_y), max(vertex[1], max_y) a = (min_x, max_x, min_y, max_y) b = (max_x - min_x, max_y - min_y, 10.0) surface_view = Drawing(size = (max_x - min_x, max_y - min_y)) def correct_vertex(vertex, surface_bounds, canvas_bounds): x_coordinate, y_coordinate = vertex left_bound, right_bound, bottom_bound, top_bound = surface_bounds canvas_width, canvas_height, canvas_padding = canvas_bounds x_coordinate_scaling = \ (canvas_width - 2.0 * canvas_padding) \ / (right_bound - left_bound) y_coordinate_scaling = \ (canvas_height - 2.0 * canvas_padding) \ / (top_bound - bottom_bound) x_coordinate = \ x_coordinate_scaling * (x_coordinate - left_bound) \ + canvas_padding y_coordinate = \ y_coordinate_scaling * (y_coordinate - bottom_bound) \ + canvas_padding y_coordinate = canvas_height - y_coordinate return x_coordinate, y_coordinate for polygon in surface.polygons: vertices = \ [correct_vertex((x_coordinate, y_coordinate), a, b) \ for x_coordinate, y_coordinate, z_coordinate \ in polygon.vertices] if surface.maximal_impossibility - surface.minimal_impossibility > 0.0: relative_impossibility = \ (polygon.impossibility - surface.minimal_impossibility) \ / (surface.maximal_impossibility - surface.minimal_impossibility) else: relative_impossibility = 0.0 fill_color_red_component = round(255.0 * relative_impossibility) fill_color_green_component = round(255.0 - 255.0 * relative_impossibility) fill_color_blue_component = 0 fill_color = \ rgb( fill_color_red_component, fill_color_green_component, fill_color_blue_component ) polygon_view = \ Polygon( vertices, stroke_width = 1, stroke = rgb(0, 0, 0), fill = fill_color ) surface_view.add(polygon_view) if sn is not None: for polygon_index, polygon in enumerate(surface.polygons): polygon_index_view = \ Text( str(polygon_index), insert = \ correct_vertex( (polygon.center[0], polygon.center[1]), a, b ) ) surface_view.add(polygon_index_view) # if sn is not None: # from svgwrite.text import Text # for state in sn: # polygon = state.polygon # polygon_number = sn[state] # polygon_center = polygon.center[0], polygon.center[1] # q,w = correct_vertex(polygon_center, a, b) # polygon_center_view = \ # Text( # str(polygon_number), # insert = (q+2,w+2), # style = "font-size: 50%; font-color: #808080" # ) # surface_view.add(polygon_center_view) if controls_sequence is not None: last_state = None last_polygon_center = None smoother = Smoother(surface = surface, smoothing_depth = 1) for state in controls_sequence: polygon = state.polygons_sequence[-1] polygon_center = polygon.center[0], polygon.center[1] polygon_center_view = \ Circle( correct_vertex(polygon_center, a, b), 2, stroke_width = 0, fill = rgb(0, 0, 0), ) surface_view.add(polygon_center_view) if last_state is not None: smoother.push_transfer( last_state.get_transfer(state) ) first_transfer_point, second_transfer_point = \ smoother.transfers_points_sequence[0] first_trek_view = \ Line( correct_vertex(last_polygon_center, a, b), correct_vertex( (first_transfer_point.coordinates[0], first_transfer_point.coordinates[1]), a, b ), stroke_width = 1, stroke = rgb(0, 0, 0), ) first_trek_end_view = \ Circle( correct_vertex( (first_transfer_point.coordinates[0], first_transfer_point.coordinates[1]), a, b ), 2, stroke_width = 0, fill = rgb(0, 0, 0), ) second_trek_view = \ Line( correct_vertex( (second_transfer_point.coordinates[0], second_transfer_point.coordinates[1]), a, b ), correct_vertex(polygon_center, a, b), stroke_width = 1, stroke = rgb(0, 0, 0), ) second_trek_start_view = \ Circle( correct_vertex( (second_transfer_point.coordinates[0], second_transfer_point.coordinates[1]), a, b ), 2, stroke_width = 0, fill = rgb(0, 0, 0), ) surface_view.add(first_trek_view) surface_view.add(first_trek_end_view) surface_view.add(second_trek_view) surface_view.add(second_trek_start_view) last_state = state last_polygon_center = polygon_center surface_view.write(file)
tabpad = " " fontsize = 12 titlefont = 16 w = width - 2*padding h = height - 2*padding - titlefont - fontsize line_width = 200 # styling for svg css = 'text { font-family: "Georgia", Georgia, serif; }' # create svg dwg = Drawing("test.svg", size=(width,height)) dwg.defs.add(dwg.style(css)) data = read_csv(indata) 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())
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)
def export_workflow(workflow, store, size=None): """Construct a SVG description for a workflow. Args: workflow (WorkflowDef) store (dict of uid, def): elements definitions size (int, int): size of drawing in pixels Returns: (str) - SVG description of workflow """ # check that each node has a position for node in workflow['nodes']: if 'x' not in node or 'y' not in node: raise UserWarning("need to position workflow first") if size is None: size = (600, 600) # draw paper = Drawing("workflow.svg", size, id="repr") lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="bg_loaded") lg.add_stop_color(0, color='#8c8cff') lg.add_stop_color(1, color='#c8c8c8') paper.defs.add(lg) lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="bg_failed") lg.add_stop_color(0, color='#ff8cff') lg.add_stop_color(1, color='#c8c8c8') paper.defs.add(lg) lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="in_port") lg.add_stop_color(0, color='#3333ff') lg.add_stop_color(1, color='#2222ff') paper.defs.add(lg) lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="out_port") lg.add_stop_color(0, color='#ffff33') lg.add_stop_color(1, color='#9a9a00') paper.defs.add(lg) for i, link in enumerate(workflow['links']): draw_link(paper, workflow, store, link, i) bbs = [] for i, node in enumerate(workflow['nodes']): bb = draw_node(paper, workflow, store, node, i) bbs.append(bb) # reformat whole drawing to fit screen xmin = min(bb[0] for bb in bbs) - draw_padding xmax = max(bb[2] for bb in bbs) + draw_padding ymin = min(bb[1] for bb in bbs) - draw_padding ymax = max(bb[3] for bb in bbs) + draw_padding w = float(size[0]) h = float(size[1]) xratio = (xmax - xmin) / w yratio = (ymax - ymin) / h if xratio > yratio: xsize = int(xratio * w) ysize = int(xratio * h) ymin -= (ysize - (ymax - ymin)) / 2 else: xsize = int(yratio * w) ysize = int(yratio * h) xmin -= (xsize - (xmax - xmin)) / 2 paper.viewbox(xmin, ymin, xsize, ysize) return paper.tostring(), (xmin, ymin, xsize, ysize)
def draw(): drawing = Drawing(drawing_file_name, drawing_size) first_circle_view = \ Circle( center = first_circle_center, r = first_circle_radius, fill_opacity = 0, stroke = rgb(0, 0, 0), stroke_width = 1 ) first_circle_center_view = \ Circle( center = first_circle_center, r = 3, fill = rgb(0, 0, 0), stroke_width = 0 ) drawing.add(first_circle_view) drawing.add(first_circle_center_view) second_circle_view = \ Circle( center = second_circle_center, r = second_circle_radius, fill_opacity = 0, stroke = rgb(0, 0, 0), stroke_width = 1 ) second_circle_center_view = \ Circle( center = second_circle_center, r = 3, fill = rgb(0, 0, 0), stroke_width = 0 ) drawing.add(second_circle_view) drawing.add(second_circle_center_view) drawing.save()
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
def visualize(self, trajectory, output_file): drawing = Drawing() # Определение параметров образов состояний аппарата machine_view_length, machine_view_width = self.__machine_view_size coordinates_scaling = machine_view_length / self.__machine_length machine_diameter = \ ((machine_view_length * 2.0) ** 2.0 + machine_view_width ** 2.0) \ ** 0.5 machine_radius = machine_diameter / 2.0 # Создание последовательности записываемых состояний аппарата def generate_states_sequence(): spawn_time = 0.0 for trajectory_time, state in trajectory: if trajectory_time >= spawn_time: spawn_time += self.__time_interval yield state states_sequence = generate_states_sequence() # Запись последовательности состояний аппарата is_view_box_initialized = False view_box_minimal_x, view_box_minimal_y = 0.0, 0.0 view_box_maximal_x, view_box_maximal_y = 0.0, 0.0 for state in states_sequence: # Создание образа состояния аппарата state_view_angle = - state.coordinates[2] / math.pi * 180.0 state_view_center = \ state.coordinates[0] * coordinates_scaling, \ - state.coordinates[1] * coordinates_scaling state_view_position = \ state_view_center[0], \ state_view_center[1] - machine_view_width / 2.0 state_view = \ Rect( insert = state_view_position, size = self.__machine_view_size, fill = rgb(255, 255, 255), stroke = rgb(0, 0, 0), stroke_width = 1 ) state_view.rotate( state_view_angle, center = state_view_center ) # Добавление образа состояния аппарата к образу траектории drawing.add(state_view) if is_view_box_initialized: view_box_minimal_x, view_box_minimal_y = \ min(state_view_center[0], view_box_minimal_x), \ min(state_view_center[1], view_box_minimal_y) view_box_maximal_x, view_box_maximal_y = \ max(state_view_center[0], view_box_maximal_x), \ max(state_view_center[1], view_box_maximal_y) else: is_view_box_initialized = True view_box_minimal_x, view_box_minimal_y = \ state_view_center[0], \ state_view_center[1] view_box_maximal_x, view_box_maximal_y = \ state_view_center[0], \ state_view_center[1] # Настройка отображения образа траектории drawing.viewbox( minx = view_box_minimal_x - machine_radius, miny = view_box_minimal_y - machine_radius, width = view_box_maximal_x - view_box_minimal_x + machine_diameter, height = view_box_maximal_y - view_box_minimal_y + machine_diameter ) # Запись образа траектории в файл try: drawing.write(output_file) except: raise Exception() #!!!!! Генерировать хорошие исключения
""" Figure built manually in SVG. Note, these require the svgwrite package (and optionally, the svglib package to convert to pdf). """ from __future__ import print_function import subprocess from svgwrite import Drawing filename = 'scaling_run_model.svg' color_phys = '#85C1E9' color_scaled = '#EC7063' 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
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)
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()