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 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 add_grid(obj: svgwrite.Drawing, s=10): """ function to add grid to SVG drawing, must be called after set_bg_color if used :param obj: svgwrite.Drawing object :param s: grid step :return: None """ pattern_small_grid = obj.pattern(insert=None, size=(s, s), patternUnits='userSpaceOnUse') pattern_small_grid.attribs['id'] = "smallGrid" path_small = obj.path(f'M {s} 0 L 0 0 0 {s}', stroke='gray', fill='none', style=f'stroke-width:{0.5}') pattern_small_grid.add(path_small) pattern_large_grid = obj.pattern(insert=None, size=(s * 10, s * 10), patternUnits='userSpaceOnUse') pattern_large_grid.attribs['id'] = "grid" path_large = obj.path(f'M {s * 10} 0 L 0 0 0 {s * 10}', stroke='gray', fill='none', style=f'stroke-width:{1}') pattern_large_grid.add(path_large) rect_large = obj.rect(fill="url(#smallGrid)", size=(s * 10, s * 10)) pattern_large_grid.add(rect_large) obj.defs.add(pattern_small_grid) obj.defs.add(pattern_large_grid) rect_grid = obj.rect(size=('100%', '100%'), fill="url(#grid)") obj.add(rect_grid)
def _draw_measure_gate(drawing: Drawing, bit_gate_rank: BitRankType, measured_qubit: int, target_clbit: int, show_clbits: bool, bit_mapping: dict) -> None: index_to_draw, _ = _helpers.get_max_index(bit_gate_rank, qubits=[measured_qubit], clbits=[target_clbit]) x_coord = _helpers.get_x_from_index(index_to_draw) yq_coord = _helpers.get_y_from_quantum_register(measured_qubit, bit_mapping) if show_clbits: yc_coord = _helpers.get_y_from_classical_register(target_clbit, len(bit_gate_rank['qubits']), bit_mapping) # Draw the line between the 2 bits _draw_classical_double_line(drawing, x_coord, yq_coord, x_coord, yc_coord) # Draw the little thing that tell where we put the measure. drawing.add(drawing.rect(insert=(x_coord - _constants.MEASURE_GATE_CLBIT_SIZE/2, yc_coord - _constants.MEASURE_GATE_CLBIT_SIZE/2), size=(_constants.MEASURE_GATE_CLBIT_SIZE, _constants.MEASURE_GATE_CLBIT_SIZE), fill=_constants.MEASURE_GATE_CLBIT_FILL_COLOR, stroke=_constants.GATE_BORDER_COLOR, stroke_width=_constants.STROKE_THICKNESS)) # Draw the "measure" gate. _draw_unitary_gate(drawing, bit_gate_rank, measured_qubit, "M", bit_mapping, index_to_draw=index_to_draw) else: # Draw the "measure" gate. _draw_unitary_gate(drawing, bit_gate_rank, measured_qubit, "M" + str(target_clbit), bit_mapping, index_to_draw=index_to_draw)
def draw_legend(self, image: Drawing, legend_title: str, top: int, range_max: float, range_min: int = 0): # Generate up to 5 integer steps step = int(ceil((range_max - range_min) / 5)) image.add( image.text(legend_title, insert=(self.legend_grid['left'], top + 3 * self.legend_grid['cell_height'] / 4), class_='legend_title')) steps = list(range(range_min, int(range_max + 1), step)) if max(steps) != int(range_max): steps.append(int(range_max)) for offset, marker in enumerate(steps): left = self.legend_grid['left'] + offset * self.legend_grid['pitch'] image.add( image.rect( (left, top), (self.legend_grid['cell_width'], self.legend_grid['cell_height']), fill=self.fractional_fill_color( (marker - range_min) / (range_max - range_min)))) image.add( image.text( marker, insert=(left + self.legend_grid['cell_width'] / 2, top + 3 * self.legend_grid['cell_height'] / 4), class_='key', fill='#ffffff' if marker > range_max / 2 else '#000000'))
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_big(x: int, y: int, dwg: svgwrite.Drawing, weather: WeatherForecast, air_quality: AirQuality, icons_location: str): shapes = dwg.add(dwg.g(id='shapes')) shapes.add(dwg.rect(insert=(x, y), size=(360 * px, 300 * px), fill='white', stroke='black', stroke_width=1)) paragraph = dwg.add(dwg.g(font_size=120)) paragraph.add(dwg.text(f"{weather.temperature}°", (x + 240, y + 110), text_anchor="middle")) location = os.path.abspath(f"{icons_location}/{weather.summary.value}.svg") image = dwg.add( dwg.image(href=location, insert=(x + 5, y + 5), size=(120 * px, 120 * px))) feels_like_text = dwg.add(dwg.g(font_size=36, font_family="Helvetica")) feels_like_text.add(dwg.text(f"Air quality: {air_quality}", (x + 10, y + 165))) wind_text = dwg.add(dwg.g(font_size=36, font_family="Helvetica")) wind_text.add(dwg.text(f"{weather.wind_name} {weather.wind_mps} m/s", (x + 10, y + 205), textLength=340)) precipitation_text = dwg.add(dwg.g(font_size=36, font_family="Helvetica")) precipitation_text.add(dwg.text(f"{weather.precip_name} {weather.precip_mm} mm/h", (x + 10, y + 245))) pressure_text = dwg.add(dwg.g(font_size=36, font_family="Helvetica")) pressure_text.add(dwg.text(f"{int(weather.air_pressure)} hPa", (x + 10, y + 285)))
def _draw_gate_rect(drawing: Drawing, x_coord: float, y_coord: float) -> None: anchor = tuple((x_coord - _constants.GATE_SIZE / 2, y_coord - _constants.GATE_SIZE / 2)) drawing.add(drawing.rect(insert=anchor, size=(_constants.GATE_SIZE, _constants.GATE_SIZE), fill=_constants.GATE_FILL_COLOR, stroke=_constants.GATE_BORDER_COLOR, stroke_width=_constants.STROKE_THICKNESS))
def set_bg_color(obj: svgwrite.Drawing, plant_model): """ function to set background color for SVG drawing must be called before function add_grid if it used :param obj: drawing t oset color on :param plant_model: PlantModel proteus object :param ctx: model context :return: None """ bg_color = fetch_color_from_presentation(plant_model.find('Drawing').find('Presentation')) bg_color_rect = obj.rect((0, 0), ('100%', '100%'), fill=bg_color) obj.add(bg_color_rect)
def leg_foot() -> str: drawing = Drawing() r = drawing.rect((-50, -50), (200, 200), stroke="black", fill="white") drawing.add(r) a = LegWithFoot(leg_length=50, leg_color="#606060", foot_color="#ff0000") g = a.render(drawing) g.translate(50, 50) drawing.add(g) return drawing.tostring()
def arm_hand() -> str: drawing = Drawing() r = drawing.rect((-50, -50), (200, 200), stroke="black", fill="white") drawing.add(r) a = ArmWithHand(arm_length=50, arm_color="#606060", hand_color="#ff0000") g = a.render(drawing) g.translate(50, 50) drawing.add(g) return drawing.tostring()
def 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 render(self, dwg: Drawing) -> Group: r = dwg.rect(insert=(0, 0), size=(self.width, self.height), fill=self.color) g = dwg.g() g.add(r) star_count = int(self.width * self.height * .001) for i in range(star_count): s = dwg.circle(center=(self.prng.randint(0, self.width), self.prng.randint(0, self.height)), r=max(2, self.prng.gauss(1, 1)), fill="white") g.add(s) return g
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 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 draw_small(x: int, y: int, dwg: svgwrite.Drawing, time: str, weather: WeatherForecast, icons_location: str): shapes = dwg.add(dwg.g()) shapes.add(dwg.rect(insert=(x, y), size=(220 * px, 145 * px), fill='white', stroke='black', stroke_width=1)) date_text = dwg.add(dwg.g(font_size=35, font_family="Helvetica")) date_text.add(dwg.text(time, (10 + x, 35 + y))) paragraph = dwg.add(dwg.g(font_size=55)) paragraph.add(dwg.text(f"{weather.temperature}°", (x + 135, y + 90), text_anchor="middle")) location = os.path.abspath(f"{icons_location}/{weather.summary.value}.svg") image = dwg.add( dwg.image(href=location, insert=(10 + x, 45 + y), size=(55 * px, 55 * px))) pressure_text = dwg.add(dwg.g(font_size=36, font_family="Helvetica")) pressure_text.add(dwg.text(f"{int(weather.air_pressure)} hPa", (10 + x, 130 + y)))
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 render(self, size: Tuple[int, int] = (512, 512), view_box: str = "-0.5 -0.5 1.0 1.0", **extra) -> Drawing: drawing = Drawing("", size, viewBox=view_box, **extra) for view in self.views: projection = np.dot(view.camera.view, view.camera.projection) clip_path = drawing.defs.add(drawing.clipPath()) clip_min = view.viewport.minx, view.viewport.miny clip_size = view.viewport.width, view.viewport.height clip_path.add(drawing.rect(clip_min, clip_size)) # TODO: Handle Z-index with meshes for group in view.scene.groups: for g in self._create_group(drawing, projection, view.viewport, group): g["clip-path"] = clip_path.get_funciri() drawing.add(g) return drawing
def display_svg(): dwg = Drawing() hlines = dwg.add(dwg.g(id="hlines", stroke="green")) for y in range(20): hlines.add( dwg.line(start=(2 * cm, (2 + y) * cm), end=(18 * cm, (2 + y) * cm))) vlines = dwg.add(dwg.g(id="vline", stroke="blue")) for x in range(17): vlines.add( dwg.line(start=((2 + x) * cm, 2 * cm), end=((2 + x) * cm, 21 * cm))) shapes = dwg.add(dwg.g(id="shapes", fill="red")) # set presentation attributes at object creation as SVG-Attributes circle = dwg.circle(center=(15 * cm, 8 * cm), r="2.5cm", stroke="blue", stroke_width=3) circle["class"] = "class1 class2" shapes.add(circle) # override the 'fill' attribute of the parent group 'shapes' shapes.add( dwg.rect( insert=(5 * cm, 5 * cm), size=(45 * mm, 45 * mm), fill="blue", stroke="red", stroke_width=3, )) # or set presentation attributes by helper functions of the Presentation-Mixin ellipse = shapes.add( dwg.ellipse(center=(10 * cm, 15 * cm), r=("5cm", "10mm"))) ellipse.fill("green", opacity=0.5).stroke("black", width=5).dasharray([20, 20]) return Response(dwg.tostring(), mimetype="image/svg+xml")
def render(self, dwg: Drawing) -> Group: g = dwg.g() head = self.head.render(dwg) socket_relative_width = 1.2 socket_radius = (self.head_size * socket_relative_width, self.head_size*0.3) socket_relative_height = .3 socket_left = (-self.head_size * socket_relative_width, self.head_size * .5) socket_left_bottom = (socket_left[0], socket_left[1] + self.head_size * socket_relative_height) socket_right: Tuple[float, float] = (self.head_size * socket_relative_width, self.head_size * .5) socket_right_bottom: Tuple[float, float] = (socket_right[0], socket_right[1] + self.head_size * socket_relative_height) size_factor = self.head_size / 50.0 arm_length = 50 * size_factor arm_params = { "arm_length": arm_length, "arm_color": self.body_color, "hand_color": helper.colors.lighten_hex(self.body_color, 2), "thickness_shoulder": 30 * size_factor } arm_params.update(self.arm_params) for i in range(self.arm_count): left_arm = ArmWithHand(**arm_params) # type: ignore left_arm_g = left_arm.render(dwg) left_arm_x = socket_right_bottom[0] - left_arm.thickness_shoulder / 2 - (socket_right_bottom[0] - self.head_size * self.body_fatness) / (self.head_size * self.body_height) * i * left_arm.thickness_shoulder * 1.2 left_arm_g.translate(left_arm_x, socket_right_bottom[1] + left_arm.thickness_shoulder / 2 + i * left_arm.thickness_shoulder * .8) left_arm_g.rotate(self.body_left_arm_angle / (math.pi) * 180 + (i * 20)) g.add(left_arm_g) right_arm = ArmWithHand(reverse_shadow=True, **arm_params) # type: ignore right_arm_g = right_arm.render(dwg) right_arm_x = socket_left_bottom[0] + right_arm.thickness_shoulder / 2 + (-self.head_size * self.body_fatness - socket_left_bottom[0]) / (self.head_size * self.body_height) * i * right_arm.thickness_shoulder * 1.2 right_arm_g.translate(right_arm_x, socket_left_bottom[1] + right_arm.thickness_shoulder / 2 + i * right_arm.thickness_shoulder * .8) right_arm_g.rotate(-self.body_right_arm_angle / (math.pi) * 180 - (i * 20)) right_arm_g.scale(-1, 1) g.add(right_arm_g) leg_thickness_thigh = self.body_fatness * self.head_size leg_thickness_foot = leg_thickness_thigh * .7 leg_length = self.head_size * 1 boot_height = leg_length * .5 foot_length = leg_length left_leg = LegWithFoot(leg_length=leg_length, # type: ignore leg_color=self.body_color, thickness_thigh=leg_thickness_thigh, thickness_foot=leg_thickness_foot, foot_color=helper.colors.lighten_hex(self.body_color, 2), boot_height=boot_height, foot_length=foot_length, **self.leg_params) left_leg_g = left_leg.render(dwg) left_leg_g.translate(0, self.head_size * self.body_height) left_leg_g.rotate(-20) g.add(left_leg_g) right_leg = LegWithFoot(leg_length=leg_length, # type: ignore leg_color=self.body_color, thickness_thigh=leg_thickness_thigh, thickness_foot=leg_thickness_foot, foot_color=helper.colors.lighten_hex(self.body_color, 2), boot_height=boot_height, foot_length=foot_length, **self.leg_params) right_leg_g = right_leg.render(dwg) right_leg_g.translate(0, self.head_size * self.body_height) right_leg_g.rotate(20) right_leg_g.scale(-1, 1) g.add(right_leg_g) body = dwg.path(fill=self.body_color) body.push("M %f %f" % (socket_right_bottom[0], socket_right_bottom[1])) body.push("L %f %f" % (self.head_size * self.body_fatness, self.head_size * self.body_height)) body.push("L %f %f" % (self.head_size * (self.body_fatness - .2), self.head_size * (self.body_height + .2))) body.push("L %f %f" % (-self.head_size * (self.body_fatness - .2), self.head_size * (self.body_height + .2))) body.push("L %f %f" % (-self.head_size * self.body_fatness, self.head_size * self.body_height)) body.push("L %f %f" % (socket_left_bottom[0], socket_left_bottom[1])) g.add(body) socket_background_color = helper.colors.darken_hex(self.socket_color) socket_background = dwg.ellipse(fill=socket_background_color, center=(0, self.head_size * .5), r=socket_radius) socket_foreground = dwg.path(fill=self.socket_color) socket_foreground.push("M %f %f" % socket_left) socket_foreground.push("A %f %f 0 0 0 %f %f" % (socket_radius[0], socket_radius[1], socket_right[0], socket_right[1])) socket_foreground.push("l 0 %f" % (self.head_size * .3)) socket_foreground.push("A %f %f 0 0 1 %f %f" % (socket_radius[0], socket_radius[1], - self.head_size * socket_relative_width, self.head_size * .8)) g.add(socket_background) g.add(head) g.add(socket_foreground) dome = dwg.path(fill="white", fill_opacity=.3) dome.push("M %f %f" % socket_left) dome.push("C %f %f %f %f %f %f" % (-self.head_size * (socket_relative_width + 1), -self.head_size * 3, self.head_size * (socket_relative_width + 1), -self.head_size * 3, socket_right[0], socket_right[1])) dome.push("A %f %f 0 0 1 %f %f" % (socket_radius[0], socket_radius[1], socket_left[0], socket_left[1])) refl_mask = dwg.defs.add(dwg.mask((self.head_size * -1.5, self.head_size * -2.5), (self.head_size * 3, self.head_size * 5), id="%s-dome-refl-mask" % self.id)) refl_mask.add(dwg.rect((self.head_size * -1.5, self.head_size * -2.5), (self.head_size * 3, self.head_size * 5), fill="white")) refl_mask.add(dwg.circle((self.head_size * .3, -self.head_size * .25), r=self.head_size * 1.75, fill="black")) dome_reflection = dwg.path(fill="white", fill_opacity=.3, mask="url(#%s-dome-refl-mask)" % self.id) dome_reflection.push("M %f %f" % socket_left) dome_reflection.push("C %f %f %f %f %f %f" % (-self.head_size * (socket_relative_width + 1), -self.head_size * 3, self.head_size * (socket_relative_width + 1), -self.head_size * 3, socket_right[0], socket_right[1])) dome_reflection.push("A %f %f 0 0 1 %f %f" % (socket_radius[0], socket_radius[1], socket_left[0], socket_left[1])) dome_reflection.scale(.9) g.add(dome) g.add(dome_reflection) return 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_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 _draw(self, d: 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 = 'font-size:{}px; font-family:Arial;'.format(year_size) month_style = 'font-size:{}px; font-family:Arial;'.format(min_size * 3.0 / 80.0) day_style = 'dominant-baseline: central; font-size:{}px; font-family:Arial;'.format( min_size * 1.0 / 80.0) day_length_style = 'font-size:{}px; font-family:Arial;'.format( min_size * 1.0 / 80.0) d.add( d.text('{}'.format(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) dow = ["M", "T", "W", "T", "F", "S", "S"] 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 d.add( d.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 + 0.05 * 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]) color = self.color(self.poster.length_range_by_date, length, [t for t in tracks if t.special]) d.add(d.rect(pos, dim, fill=color)) d.add( d.text("{:.1f}".format(self.poster.m2u(length)), insert=(x_pos + cell_size / 2, y_pos + cell_size + cell_size / 2), text_anchor="middle", style=day_length_style, fill=self.poster.colors['text'])) else: d.add(d.rect(pos, dim, fill='#444444')) d.add( d.text(dow[date.weekday()], insert=(offset.x + (day_offset + x) * cell_size + cell_size / 2, y_pos + cell_size / 2), text_anchor="middle", alignment_baseline="middle", style=day_style)) date += datetime.timedelta(1)
dwg = Drawing(join(OUTPUT_DIRECTORY, "next_available_grid.svg"), (num_grid * spacing, num_grid * spacing)) for x in range(num_grid): dwg.add( dwg.line(start=(0, x * spacing), end=(num_grid * spacing, x * spacing), stroke=rgb(10, 10, 16, '%'))) dwg.add( dwg.line(start=(x * spacing, 0), end=(x * spacing, num_grid * spacing), stroke=rgb(10, 10, 16, '%'))) start_point = [4, 4] dwg.add( dwg.rect(insert=(start_point[0] * spacing, start_point[1] * spacing), size=(spacing, spacing), fill=rgb(100, 100, 16, '%'))) count = 0 last_point = start_point for next_available in NextAvailableGrid(*start_point): dwg.add( dwg.line(start=((last_point[0] + 0.5) * spacing, (last_point[1] + 0.5) * spacing), end=((next_available[0] + 0.5) * spacing, (next_available[1] + 0.5) * spacing), stroke=rgb(100, 0, 0, '%'))) last_point = next_available count += 1 if count > 25: break dwg.save()
def export(self): dwg = Drawing(self.name, width=W, height=H) for r in self.rects: dwg.add(dwg.rect(insert=(r.x, r.y), size=(r.w, r.h), fill='black')) dwg.save()
def _draw(self, dr: svgwrite.Drawing, g: svgwrite.container.Group, size: XY, offset: XY, year: int) -> None: 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;" g.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, ) 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 g.add( dr.text( self.poster.month_name(month), 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) g.add(dr.rect(pos, dim, fill=color)) g.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: g.add(dr.rect(pos, dim, fill="#444444")) g.add( dr.text( localized_day_of_week_name(date.weekday(), short=True), 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)
def LQFP(name, offset, width1, width2, padh, padw, spaceing, padcount, bodyw, edge, pinh, pinw): # document sizes doc = (width1 + 2 * offset[0], width1 + 2 * offset[1]) # basic SVG sheet svg = Drawing(filename=name, size=doc) ####################################################################################################### # PCB Pads ####################################################################################################### pads = svg.g(id="pads") # pins 51 to 75 for n in range(0, padcount): x = offset[0] + ((width1 - width2) / 2) + (n * spaceing) y = offset[1] pad = svg.rect(insert=(x, y), size=(padw, padh), stroke_width=0.05, stroke="black", fill="#D4AA00") pads.add(pad) # pins 1 to 25 for n in range(0, padcount): x = offset[0] + ((width1 - width2) / 2) + (n * spaceing) y = offset[1] + (width1 - padh) pad = svg.rect(insert=(x, y), size=(padw, padh), stroke_width=0.05, stroke="black", fill="#D4AA00") pads.add(pad) # pins 76 to 100 for n in range(0, padcount): x = offset[0] y = offset[1] + ((width1 - width2) / 2) + (n * spaceing) pad = svg.rect(insert=(x, y), size=(padh, padw), stroke_width=0.05, stroke="black", fill="#D4AA00") pads.add(pad) # pins 26 to 50 for n in range(0, padcount): x = offset[0] + (width1 - padh) y = offset[1] + ((width1 - width2) / 2) + (n * spaceing) pad = svg.rect(insert=(x, y), size=(padh, padw), stroke_width=0.05, stroke="black", fill="#D4AA00") pads.add(pad) svg.add(pads) ####################################################################################################### # Part ####################################################################################################### corner = (doc[0] / 2) - (bodyw / 2) part = svg.g(id="part") # pins 51 to 75 for n in range(0, padcount): x = offset[0] + ((width1 - width2) / 2) + ( (padw - pinw) / 2) + (n * spaceing) y = corner - pinh pad = svg.rect(insert=(x, y), size=(pinw, pinh), stroke_width=0.05, stroke="black", fill="#999999") part.add(pad) # pins 1 to 25 for n in range(0, padcount): x = offset[0] + ((width1 - width2) / 2) + ( (padw - pinw) / 2) + (n * spaceing) y = corner + bodyw pad = svg.rect(insert=(x, y), size=(pinw, pinh), stroke_width=0.05, stroke="black", fill="#999999") part.add(pad) # pins 76 to 100 for n in range(0, padcount): x = corner - pinh y = offset[0] + ((width1 - width2) / 2) + ( (padw - pinw) / 2) + (n * spaceing) pad = svg.rect(insert=(x, y), size=(pinh, pinw), stroke_width=0.05, stroke="black", fill="#999999") part.add(pad) # pins 26 to 50 for n in range(0, padcount): x = corner + bodyw y = offset[0] + ((width1 - width2) / 2) + ( (padw - pinw) / 2) + (n * spaceing) pad = svg.rect(insert=(x, y), size=(pinh, pinw), stroke_width=0.05, stroke="black", fill="#999999") part.add(pad) # plastic body points = [(corner, corner + edge), (corner + edge, corner), (corner + bodyw - edge, corner), (corner + bodyw, corner + edge), (corner + bodyw, corner + bodyw - edge), (corner + bodyw - edge, corner + bodyw), (corner + edge, corner + bodyw), (corner, corner + bodyw - edge)] body = svg.polygon(points=points, stroke_width=0.05, stroke="black", fill="#333333") part.add(body) # pin 1 indicator ind = svg.circle(center=(corner + 1, corner + bodyw - 1), r=0.5, stroke_width=0.05, stroke="black", fill="#333333") part.add(ind) svg.add(part) svg.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 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 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}" rect = dr.rect((rect_x, rect_y), dom, fill=color) 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
def plot(self, svg: Drawing) -> None: recolor: Optional[str] = None if isinstance(self.color, list): linear_gradient: LinearGradient = svg.linearGradient( self.map_((0, self.max_y)), self.map_((0, 1)), gradientUnits="userSpaceOnUse", ) for index, color in enumerate(self.color): linear_gradient.add_stop_color( index / (len(self.color) - 1), color.hex ) gradient: LinearGradient = svg.defs.add(linear_gradient) recolor = gradient.get_funciri() if isinstance(self.color, Color): recolor = self.color.hex svg.add( svg.rect( insert=(0, 0), size=("100%", "100%"), rx=None, ry=None, fill=self.background_color.hex, ) ) self.draw_grid(svg) last_text_y = 0 for xs, ys, color, title in self.data: if recolor: color = recolor assert len(xs) == len(ys) xs_second: list[float] = [ (x - self.min_x).total_seconds() for x in xs ] points = [] for index, x in enumerate(xs_second): y = ys[index] mapped: np.ndarray = map_array( np.array((x, y)), np.array((self.min_x_second, self.max_y)), np.array((self.max_x_second, self.min_y)), self.canvas.workspace[0], self.canvas.workspace[1], ) points.append(mapped) previous_point: Optional[np.ndarray] = None for point in points: if previous_point is not None: line: Line = svg.line( (previous_point[0], previous_point[1]), (point[0], point[1]), stroke=self.background_color.hex, stroke_width=6, ) svg.add(line) line: Line = svg.line( (previous_point[0], previous_point[1]), (point[0], point[1]), stroke=color, stroke_width=2, ) svg.add(line) previous_point = point for point in points: svg.add( svg.circle( (point[0], point[1]), 5.5, fill=self.background_color.hex, ) ) svg.add(svg.circle((point[0], point[1]), 3.5, fill=color)) title: str text_y = max(last_text_y + 20, point[1] + 6) self.text(svg, (point[0] + 15, text_y), title.upper(), color) last_text_y = text_y with Path(svg.filename).open("w+") as output_file: svg.write(output_file)
Image.fromarray(np.asarray(skeletonized, dtype='uint8') * 255).save("skeletonized.png") g = im_to_graph(skeletonized) lines = list(partition_forest(g)) assert len(lines) == 1 line = lines.pop() smoothed = gaussian_filter1d(np.asarray(line, dtype=float), sigma=DEFAULT_SIGMA, axis=0) length = np.linalg.norm(np.diff(smoothed, axis=0), axis=1).sum() dwg = Drawing("line.svg") dwg.add(dwg.rect(size=drawn_arr.shape[::-1], fill="black")) dwg.add( dwg.polyline([tuple(pair[::-1]) for pair in smoothed], stroke="white", stroke_width=4, fill="none")) dwg.save(pretty=True, indent=2) def pad_dim(vals, pad, integer=True): vmin = vals.min() vmax = vals.max() vpad = (vmax - vmin) * pad if integer: vpad = int(np.ceil(vpad))
def TSSOP(name, offset, width, height, padh, padw, spaceing, padcount, bodyw, bodyh, pinw, pinh, thermalw=0, thermalh=0): # document sizes doc = (width + 2 * offset[0], height + 2 * offset[1]) # basic SVG sheet svg = Drawing(filename=name, size=doc) ####################################################################################################### # PCB Pads ####################################################################################################### pads = svg.g(id="pads") for n in range(0, padcount): x = offset[0] + (n * spaceing) y = offset[1] pad = svg.rect(insert=(x, y), size=(padw, padh), stroke_width=0.05, stroke="black", fill="#D4AA00") pads.add(pad) for n in range(0, padcount): x = offset[0] + (n * spaceing) y = offset[1] + height - padh pad = svg.rect(insert=(x, y), size=(padw, padh), stroke_width=0.05, stroke="black", fill="#D4AA00") pads.add(pad) if thermalw is not 0 and thermalh is not 0: # thermal pad t = svg.rect(insert=((doc[0] / 2) - (thermalw / 2), (doc[1] / 2) - (thermalh / 2)), size=(thermalw, thermalh), stroke_width=0.05, stroke="black", fill="#D4AA00") pads.add(t) svg.add(pads) ####################################################################################################### # Part ####################################################################################################### cornerx = (doc[0] / 2) - (bodyw / 2) cornery = (doc[1] / 2) - (bodyh / 2) part = svg.g(id="part") for n in range(0, padcount): x = offset[0] + (n * spaceing) + ((padw - pinw) / 2) y = offset[1] + (padh - pinh) pin = svg.rect(insert=(x, y), size=(pinw, pinh), stroke_width=0.05, stroke="black", fill="#999999") part.add(pin) for n in range(0, padcount): x = offset[0] + (n * spaceing) + ((padw - pinw) / 2) y = offset[1] + (height - padh) pin = svg.rect(insert=(x, y), size=(pinw, pinh), stroke_width=0.05, stroke="black", fill="#999999") part.add(pin) body = svg.rect(insert=(cornerx, cornery), size=(bodyw, bodyh), stroke_width=0.05, stroke="black", fill="#333333") part.add(body) # pin 1 indicator ind = svg.circle(center=(cornerx + 0.5, cornery + bodyh - 0.5), r=0.2, stroke_width=0.05, stroke="black", fill="#333333") part.add(ind) svg.add(part) svg.save()
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.from_year, self.poster.years.to_year + 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
def add_graphelement_to_svg_drawing(element: GraphElement, drawing: svgwrite.Drawing, filters: Dict[str, Filter]) -> None: args = {} for attr, value in element.attr.items(): if attr.startswith('.svg_tag'): continue if attr.startswith('.svg_'): name = attr[5:] if name == 'filter': args[name] = filters[value].get_funciri() else: args[name] = value if '.svg_tag' in element.attr: tag = element.attr['.svg_tag'] if tag == 'rect': x = float(element.attr['x']) y = -float(element.attr['y']) width = float(element.attr.get('.svg_width', 0.1)) height = float(element.attr.get('.svg_height', 0.1)) x = x - width / 2 y = y - height / 2 drawing.add(drawing.rect((x*mult, y*mult), (width*mult, height*mult), **args)) elif tag == 'path': drawing.add(drawing.path(**args)) elif tag == 'circle': x = float(element.attr['x']) y = -float(element.attr['y']) args.setdefault('r', '1cm') args.setdefault('stroke_width', '0.1mm') args.setdefault('stroke', 'black') args.setdefault('fill', 'none') drawing.add(drawing.circle(center=(x * mult, y * mult), **args)) elif tag == 'image': x = float(element.attr['x']) y = -float(element.attr['y']) width = float(element.attr.pop('.svg_width', 5)) height = float(element.attr.pop('.svg_height', 5)) x = x - width / 2 y = y - height / 2 center = ((x + width / 2), (y + height / 2)) args.setdefault('insert', (x * mult, y * mult)) args.setdefault('size', (width * mult, height * mult)) if '.svgx_rotate' in element.attr: rotation = float(element.attr['.svgx_rotate']) args.setdefault('transform', f'translate({center[0]*mult}, {center[1]*mult}) ' f'rotate({-rotation}) ' f'translate({-center[0]*mult}, {-center[1]*mult})' ) drawing.add(getattr(drawing, element.attr['.svg_tag'])(**args)) elif tag != 'None' and tag is not None: drawing.add(getattr(drawing, element.attr['.svg_tag'])(**args)) elif isinstance(element, Vertex): if '.helper_node' in element.attr and element.attr['.helper_node']: return x = float(element.attr['x']) y = -float(element.attr['y']) args.setdefault('r', '0.4cm') args.setdefault('stroke_width', '1mm') args.setdefault('stroke', 'black') args.setdefault('fill', 'none') drawing.add(drawing.circle(center=(x*mult, y*mult), **args)) elif isinstance(element, Edge): v1 = element.vertex1 v2 = element.vertex2 x1 = float(v1.attr['x']) y1 = -float(v1.attr['y']) x2 = float(v2.attr['x']) y2 = -float(v2.attr['y']) args.setdefault('stroke_width', '1mm') args.setdefault('stroke', 'black') drawing.add(drawing.line(start=(x1*mult, y1*mult), end=(x2*mult, y2*mult), **args)) else: raise ValueError