def render_shape(self, drawer, _, **kwargs): fill = kwargs.get('fill') # draw outer circle r = self.radius box = Box(self.center.x - r, self.center.y - r, self.center.x + r, self.center.y + r) if kwargs.get('shadow'): box = self.shift_shadow(box) if kwargs.get('style') == 'blur': drawer.ellipse(box, fill=fill, outline=fill, filter='transp-blur') else: drawer.ellipse(box, fill=fill, outline=fill) else: drawer.ellipse(box, fill='white', outline=self.node.linecolor, style=self.node.style) # draw inner circle box = Box(self.center.x - r / 2, self.center.y - r / 2, self.center.x + r / 2, self.center.y + r / 2) if not kwargs.get('shadow'): if self.node.color == self.node.basecolor: color = self.node.linecolor else: color = self.node.color drawer.ellipse(box, fill=color, outline=self.node.linecolor, style=self.node.style)
def __init__(self, node, metrics): self.node = node self.metrics = metrics m = self.metrics.cell(self.node) self.textalign = 'center' self.connectors = [m.top, m.right, m.bottom, m.left] if node.icon is None: self.iconbox = None self.textbox = m.box else: image_size = images.get_image_size(node.icon) if image_size is None: iconsize = (0, 0) else: boundedbox = [metrics.node_width // 2, metrics.node_height] iconsize = images.calc_image_size(image_size, boundedbox) vmargin = (metrics.node_height - iconsize[1]) // 2 self.iconbox = Box(m.topleft.x, m.topleft.y + vmargin, m.topleft.x + iconsize[0], m.topleft.y + vmargin + iconsize[1]) self.textbox = Box(self.iconbox[2], m.top.y, m.bottomright.x, m.bottomright.y)
def __init__(self, node, metrics=None): super(NationalFlagImage, self).__init__(node, metrics) self.textalign = 'left' self.image_path = image_path box = metrics.cell(node).box bounded = (box[2] - box[0], box[3] - box[1]) size = images.get_image_size(image_path) size = images.calc_image_size(size, bounded) pt = metrics.cell(node).center self.image_box = Box(pt.x - size[0] / 2, pt.y - size[1] / 2, pt.x + size[0] / 2, pt.y + size[1] / 2) width = metrics.node_width / 2 - size[0] / 2 + metrics.cellsize self.textbox = Box(pt.x + size[0] / 2, pt.y - size[1] / 2, pt.x + size[0] / 2 + width, pt.y + size[1] / 2) size = self.metrics.textsize(node.label, self.metrics.font_for(None), self.textbox.width) self.connectors[0] = XY(pt.x, self.image_box[1]) self.connectors[1] = XY( self.image_box.x2 + size.width + self.metrics.node_padding, pt.y) self.connectors[2] = XY(pt.x, self.image_box[3]) self.connectors[3] = XY(self.image_box[0], pt.y)
def frame(self, lanes): dummy = elements.DiagramNode(None) dummy.xy = XY(0, 0) dummy.colwidth = self.colwidth dummy.colheight = self.colheight cell = self.cell(dummy, use_padding=False) headerbox = Box( cell.topleft.x - self.span_width // 2, (cell.topleft.y - self.node_height - self.span_height - 2), cell.topright.x + self.span_width // 2, cell.topright.y - self.span_height // 2) outline = Box(headerbox[0], headerbox[1], headerbox[2], cell.bottom.y + self.span_height // 2) separators = [(XY(headerbox[0], headerbox[3]), XY(headerbox[2], headerbox[3]))] for lane in lanes[:-1]: x = lane.xy.x + lane.colwidth + 1 m = self.cell(lane, use_padding=False) span_width = self.spreadsheet.span_width[x] // 2 x1 = m.right.x + span_width xy = (XY(x1, outline[1]), XY(x1, outline[3])) separators.append(xy) Frame = namedtuple('Frame', 'headerbox outline separators') return Frame(headerbox, outline, separators)
def textarea(self, box, string, font, **kwargs): if 'rotate' in kwargs and kwargs['rotate'] != 0: angle = 360 - int(kwargs['rotate']) % 360 del kwargs['rotate'] if angle in (90, 270): _box = Box(0, 0, box.height, box.width) else: _box = box text = ImageDrawEx(None, parent=self, transparency=True) text.set_canvas_size(_box.size) textbox = Box(0, 0, _box.width, _box.height) text.textarea(textbox, string, font, **kwargs) filler = Image.new('RGB', box.size, kwargs.get('fill')) self.paste(filler, box.topleft, text._image.rotate(angle)) return lines = self.textfolder(box, string, font, **kwargs) if kwargs.get('outline'): outline = kwargs.get('outline') self.rectangle(lines.outlinebox, fill='white', outline=outline) rendered = False for string, xy in lines.lines: self.text(xy, string, font, **kwargs) rendered = True if not rendered and font.size > 0: _font = font.duplicate() _font.size = int(font.size * 0.8) self.textarea(box, string, _font, **kwargs)
def grouplabelbox(self): box = super(GroupMetrics, self).grouplabelbox span = self.cellsize box = Box(box[0], box[1] + span, box[2], box[3] + span) if self.is_root_group: width = (self.node_width + self.span_width) // 2 box = Box(box[0] + width, box[1], box[2] + width, box[3]) return box
def textarea(self, box, string, font, **kwargs): if 'rotate' in kwargs and kwargs['rotate'] != 0: angle = 360 - int(kwargs['rotate']) % 360 self.write("%d rotate", angle) if angle == 90: box = Box(-box.y2, box.x1, -box.y1, box.x1 + box.width) box = box.shift(x=self.size.y, y=self.size.y) elif angle == 180: box = Box(-box.x2, -box.y2, -box.x1, -box.y2 + box.height) box = box.shift(y=self.size.y * 2) elif angle == 270: box = Box(box.y1, -box.x2, box.y2, -box.x1) box = box.shift(x=-self.size.y, y=self.size.y) lines = self.textfolder(box, string, font, **kwargs) if kwargs.get('outline'): outline = kwargs.get('outline') self.rectangle(lines.outlinebox, fill='white', outline=outline) rendered = False for string, xy in lines.lines: self.text(xy, string, font, **kwargs) rendered = True if not rendered and font.size > 0: font.size = int(font.size * 0.8) self.textarea(box, string, font, **kwargs)
def render_shape_background(self, drawer, **kwargs): fill = kwargs.get('fill') m = self.metrics.cell(self.node) pt = m.topleft rx = (self.node.width or self.metrics.node_width) // 12 ry = (self.node.height or self.metrics.node_height) // 5 ellipses = [ Box(pt.x + rx * 2, pt.y + ry, pt.x + rx * 5, pt.y + ry * 3), Box(pt.x + rx * 4, pt.y, pt.x + rx * 9, pt.y + ry * 2), Box(pt.x + rx * 8, pt.y + ry, pt.x + rx * 11, pt.y + ry * 3), Box(pt.x + rx * 9, pt.y + ry * 2, pt.x + rx * 13, pt.y + ry * 4), Box(pt.x + rx * 8, pt.y + ry * 2, pt.x + rx * 11, pt.y + ry * 5), Box(pt.x + rx * 5, pt.y + ry * 2, pt.x + rx * 8, pt.y + ry * 5), Box(pt.x + rx * 2, pt.y + ry * 2, pt.x + rx * 5, pt.y + ry * 5), Box(pt.x + rx * 0, pt.y + ry * 2, pt.x + rx * 4, pt.y + ry * 4) ] for e in ellipses: if kwargs.get('shadow'): e = self.shift_shadow(e) if kwargs.get('style') == 'blur': drawer.ellipse(e, fill=fill, outline=fill, filter='transp-blur') else: drawer.ellipse(e, fill=fill, outline=fill) else: drawer.ellipse(e, fill=self.node.color, outline=self.node.linecolor, style=self.node.style) rects = [ Box(pt.x + rx * 2, pt.y + ry * 2, pt.x + rx * 11, pt.y + ry * 4), Box(pt.x + rx * 4, pt.y + ry, pt.x + rx * 9, pt.y + ry * 2) ] for rect in rects: if kwargs.get('shadow'): rect = self.shift_shadow(rect) if kwargs.get('style') == 'blur': drawer.rectangle(rect, fill=fill, outline=fill, filter='transp-blur') else: drawer.rectangle(rect, fill=fill, outline=fill) else: drawer.rectangle(rect, fill=self.node.color, outline=self.node.color)
def render_shape_background(self, drawer, **kwargs): fill = kwargs.get('fill') m = self.metrics.cell(self.node) r = self.metrics.cellsize * 2 box = m.box ellipses = [ Box(box[0], box[1], box[0] + r * 2, box[3]), Box(box[2] - r * 2, box[1], box[2], box[3]) ] for e in ellipses: if kwargs.get('shadow'): e = self.shift_shadow(e) if kwargs.get('style') == 'blur': drawer.ellipse(e, fill=fill, outline=fill, filter='transp-blur') else: drawer.ellipse(e, fill=fill, outline=fill) else: drawer.ellipse(e, fill=self.node.color, outline=self.node.linecolor, style=self.node.style) rect = Box(box[0] + r, box[1], box[2] - r, box[3]) if kwargs.get('shadow'): rect = self.shift_shadow(rect) if kwargs.get('style') == 'blur': drawer.rectangle(rect, fill=fill, outline=fill, filter='transp-blur') else: drawer.rectangle(rect, fill=fill, outline=fill) else: drawer.rectangle(rect, fill=self.node.color, outline=self.node.color) lines = [(XY(box[0] + r, box[1]), XY(box[2] - r, box[1])), (XY(box[0] + r, box[3]), XY(box[2] - r, box[3]))] for line in lines: if not kwargs.get('shadow'): drawer.line(line, fill=self.node.linecolor, style=self.node.style)
def leftnotebox(self): if not self.edge.leftnote: return Box(0, 0, 0, 0) m = self.metrics cell = m.cell(self.edge.left_node) notesize = self.edge.leftnotesize x = cell.center.x - m.cellsize * 3 - notesize.width y = self.baseheight - notesize.height // 2 if self.edge.failed and self.edge.direction == 'left': x += self.metrics.edge_length // 2 - m.cellsize return Box(x, y, x + notesize.width, y + notesize.height)
def _scale(cls, value, ratio): if ratio == 1: return value klass = value.__class__ if klass == XY: ret = XY(value.x * ratio, value.y * ratio) elif klass == Size: ret = Size(value.width * ratio, value.height * ratio) elif klass == Box: ret = Box(value[0] * ratio, value[1] * ratio, value[2] * ratio, value[3] * ratio) elif klass == tuple: ret = tuple([cls.scale(x, ratio) for x in value]) elif klass == list: ret = [cls.scale(x, ratio) for x in value] elif klass == EdgeLines: ret = EdgeLines() ret.polylines = cls.scale(value.polylines, ratio) elif klass == FontInfo: ret = FontInfo(value.familyname, value.path, value.size * ratio) elif klass == int: ret = value * ratio elif klass == str: ret = value else: ret = cls(value, ratio) return ret
def lane_headerbox(self, lane): headerbox = self.frame([]).headerbox m = self.cell(lane) x1 = m.left.x - self.spreadsheet.span_width[lane.xy.x] // 2 x2 = m.right.x + self.spreadsheet.span_width[lane.xy.x + 1] // 2 return Box(x1, headerbox[1], x2, headerbox[3])
def lane_textbox(self, lane): headerbox = self.frame([]).headerbox m = self.cell(lane, use_padding=False) x1 = m.left.x x2 = m.right.x return Box(x1, headerbox[1], x2, headerbox[3])
def __init__(self, node, metrics=None): super(Actor, self).__init__(node, metrics) m = metrics.cell(node) if node.label: font = metrics.font_for(self.node) textsize = metrics.textsize(node.label, font) shortside = min(m.width, m.height - textsize.height) else: textsize = Size(0, 0) shortside = min(m.width, m.height) r = self.radius = shortside // 8 # radius of actor's head self.center = metrics.cell(node).center self.connectors[0] = XY(self.center.x, self.center.y - r * 9 // 2) self.connectors[1] = XY(self.center.x + r * 4, self.center.y) self.connectors[2] = XY(self.center.x, self.center.y + r * 4 + textsize.height) self.connectors[3] = XY(self.center.x - r * 4, self.center.y) self.textbox = Box(m.left.x, self.center.y + r * 4, m.right.x, self.connectors[2].y)
def rightnotebox(self): if not self.edge.rightnote: return Box(0, 0, 0, 0) m = self.metrics cell = m.cell(self.edge.right_node) if self.edge.direction == 'self': x = self.right + m.cellsize * 2 elif self.edge.failed and self.edge.direction == 'right': x = self.right + m.cellsize * 4 else: x = cell.center.x + m.cellsize * 2 notesize = self.edge.rightnotesize y = self.baseheight - notesize.height // 2 return Box(x, y, x + notesize.width, y + notesize.height)
def get_shape_box(*args): if fn.__name__ == 'polygon': xlist = [pt.x for pt in args[0]] ylist = [pt.y for pt in args[0]] return Box(min(xlist), min(ylist), max(xlist), max(ylist)) else: return args[0]
def __init__(self, node, metrics=None): super(Mail, self).__init__(node, metrics) m = self.metrics.cell(self.node) r = self.metrics.cellsize * 2 self.textbox = Box(m.topleft.x, m.topleft.y + r, m.bottomright.x, m.bottomright.y)
def textsize(self, string, font, maxwidth=None, **kwargs): if maxwidth is None: maxwidth = 65535 box = Box(0, 0, maxwidth, 65535) textbox = textfolder.get(self, box, string, font=None, **kwargs) return textbox.outlinebox.size
def render_shape(self, drawer, _, **kwargs): if kwargs.get('shadow'): return m = self.metrics center = m.cell(self.node).center dots = [center] if self.node.group.orientation == 'landscape': pt = XY(center.x, center.y - m.node_height / 2) dots.append(pt) pt = XY(center.x, center.y + m.node_height / 2) dots.append(pt) else: pt = XY(center.x - m.node_width / 3, center.y) dots.append(pt) pt = XY(center.x + m.node_width / 3, center.y) dots.append(pt) r = m.cellsize / 2 for dot in dots: box = Box(dot.x - r, dot.y - r, dot.x + r, dot.y + r) drawer.ellipse(box, fill=self.node.linecolor, outline=self.node.linecolor)
def __init__(self, node, metrics=None): super(Database, self).__init__(node, metrics) m = self.metrics.cell(self.node) r = self.metrics.cellsize self.textbox = Box(m.topleft.x, m.topleft.y + r * 3 // 2, m.bottomright.x, m.bottomright.y - r // 2)
def activity_shadow(self, node, activity): box = self.activity_box(node, activity) return Box(box[0] + self.shadow_offset.x, box[1] + self.shadow_offset.y, box[2] + self.shadow_offset.x, box[3] + self.shadow_offset.y)
def labelbox(self): span = XY(self.span_width, self.span_height) _dir = self.edge.direction node1 = self.cell(self.edge.node1, use_padding=False) node2 = self.cell(self.edge.node2, use_padding=False) if _dir == 'right': if self.edge.skipped: box = Box(node1.bottomright.x + span.x, node1.bottomright.y, node2.bottomleft.x - span.x, node2.bottomleft.y + span.y // 2) else: box = Box(node1.topright.x, node1.topright.y - span.y // 8, node2.left.x, node2.left.y - span.y // 8) elif _dir == 'right-up': box = Box(node2.left.x - span.x, node2.left.y, node2.bottomleft.x, node2.bottomleft.y) elif _dir == 'right-down': box = Box(node2.topleft.x, node2.topleft.y - span.y // 2, node2.top.x, node2.top.y) elif _dir in ('up', 'left-up', 'left', 'same'): if self.edge.node2.xy.y < self.edge.node1.xy.y: box = Box(node1.topright.x - span.x // 2 + span.x // 4, node1.topright.y - span.y // 2, node1.topright.x + span.x // 2 + span.x // 4, node1.topright.y) else: box = Box(node1.top.x + span.x // 4, node1.top.y - span.y, node1.topright.x + span.x // 4, node1.topright.y - span.y // 2) elif _dir == 'down': box = Box(node2.top.x + span.x // 4, node2.top.y - span.y // 2, node2.topright.x + span.x // 4, node2.topright.y) elif _dir == 'left-down': box = Box(node1.bottomleft.x, node1.bottomleft.y, node1.bottom.x, node1.bottom.y + span.y // 2) # shrink box box = Box(box[0] + span.x // 8, box[1], box[2] - span.x // 8, box[3]) return box
def textbox(self): x = self.left.x y = self.top.y width = self.node_width * 3 // 2 height = self.node_height return Box(x - width, y - height // 2, x, y + height // 2)
def __init__(self, node, metrics=None): super(Cloud, self).__init__(node, metrics) pt = metrics.cell(node).topleft rx = (self.node.width or self.metrics.node_width) // 12 ry = (self.node.height or self.metrics.node_height) // 5 self.textbox = Box(pt.x + rx * 2, pt.y + ry, pt.x + rx * 11, pt.y + ry * 4)
def remap(self, obj): if isinstance(obj, (XY, tuple)) and len(obj) == 2: return XY(obj[0], self.size.y - obj[1]) elif isinstance(obj, (Box, tuple)) and len(obj) == 4: return Box(obj[0], self.size.y - obj[3], obj[2], self.size.y - obj[1]) else: return obj
def __init__(self, node, metrics=None): super(Input, self).__init__(node, metrics) m = self.metrics.cell(self.node) r = self.metrics.cellsize * 3 self.textbox = Box(m.topleft.x + r, m.topleft.y, m.bottomright.x - r, m.bottomright.y)
def __init__(self, node, metrics=None): super(LoopIn, self).__init__(node, metrics) m = self.metrics.cell(self.node) ydiff = self.metrics.node_height // 4 self.textbox = Box(m.topleft.x, m.topleft.y + ydiff, m.bottomright.x, m.bottomright.y)
def textarea(self, box, string, font, **kwargs): if 'rotate' in kwargs and kwargs['rotate'] != 0: angle = 360 - int(kwargs['rotate']) % 360 del kwargs['rotate'] if angle in (90, 270): _box = Box(0, 0, box.height, box.width) else: _box = box text = ImageDrawEx(None, parent=self, transparency=True) text.set_canvas_size(_box.size) textbox = Box(0, 0, _box.width, _box.height) text.textarea(textbox, string, font, **kwargs) filler = Image.new('RGB', box.size, kwargs.get('fill')) mask = text._image.rotate(angle, expand=True) if mask.size != filler.size: # Image.rotate(expand=True) of Pillow earlier than # 3.3.0 (including 2.x) returns image object with # unexpected size: for example, rotating 10x20 by 270 # causes not 20x10 but 21x11. # Therefore, crop rotated image in order to make it # match against size of "filler". mask = mask.crop((0, 0, box.width, box.height)) self.paste(filler, box.topleft, mask) return lines = self.textfolder(box, string, font, **kwargs) if kwargs.get('outline'): outline = kwargs.get('outline') self.rectangle(lines.outlinebox, fill='white', outline=outline) rendered = False for string, xy in lines.lines: self.text(xy, string, font, **kwargs) rendered = True if not rendered and font.size > 0: _font = font.duplicate() _font.size = int(font.size * 0.8) self.textarea(box, string, _font, **kwargs)
def labelbox(self): _dir = self.edge.direction if _dir == 'right': span = XY(self.span_width, self.span_height) cell1 = self.cell(self.edge.node1, use_padding=False) cell2 = self.cell(self.edge.node2, use_padding=False) if self.edge.skipped: box = Box(cell1.bottom.x, cell1.bottom.y, cell1.bottomright.x, cell1.bottomright.y + span.y // 2) else: box = Box(cell1.bottom.x, cell2.left.y - span.y // 2, cell1.bottom.x, cell2.left.y) else: box = super(FlowchartLandscapeEdgeMetrics, self).labelbox return box
def __init__(self, node, metrics=None): super(Square, self).__init__(node, metrics) r = min(metrics.node_width, metrics.node_height) // 2 + \ metrics.cellsize // 2 pt = metrics.cell(node).center self.connectors = [XY(pt.x, pt.y - r), # top XY(pt.x + r, pt.y), # right XY(pt.x, pt.y + r), # bottom XY(pt.x - r, pt.y)] # left self.textbox = Box(pt.x - r, pt.y - r, pt.x + r, pt.y + r)
def rotated_textarea(self, box, string, font, **kwargs): angle = int(kwargs["rotate"]) % 360 del kwargs["rotate"] if angle in (90, 270): _box = Box(box[0], box[1], box[0] + box.height, box[1] + box.width) if angle == 90: _box = _box.shift(x=box.width) elif angle == 270: _box = _box.shift(y=box.height) elif angle == 180: _box = Box(box[2], box[3], box[2] + box.width, box[3] + box.height) else: _box = Box(box[0], box[1], box[0] + box.width, box[1] + box.height) rotate = "rotate(%d,%d,%d)" % (angle, _box[0], _box[1]) group = g(transform="%s" % rotate) self.svg.addElement(group) elem = SVGImageDrawElement(group, self) elem.textarea(_box, string, font, **kwargs)
def textarea(self, box, string, font, **kwargs): self.canvas.saveState() if 'rotate' in kwargs and kwargs['rotate'] != 0: angle = 360 - int(kwargs['rotate']) % 360 self.canvas.rotate(angle) if angle == 90: box = Box(-box.y2, box.x1, -box.y1, box.x1 + box.width) box = box.shift(x=self.size.y, y=self.size.y) elif angle == 180: box = Box(-box.x2, -box.y2, -box.x1, -box.y2 + box.height) box = box.shift(y=self.size.y * 2) elif angle == 270: box = Box(box.y1, -box.x2, box.y2, -box.x1) box = box.shift(x=-self.size.y, y=self.size.y) self.set_font(font) lines = self.textfolder(box, string, font, **kwargs) if kwargs.get('outline'): outline = kwargs.get('outline') self.rectangle(lines.outlinebox, fill='white', outline=outline) rendered = False for string, xy in lines.lines: self.text(xy, string, font, **kwargs) rendered = True self.canvas.restoreState() if not rendered and font.size > 0: font.size = int(font.size * 0.8) self.textarea(box, string, font, **kwargs)