clone

a browser-first diagram editor with live svg previews, local drafts and encrypted, accountless sharing

main
diagramzip / renderers / python / blockdiag-family / src / blockdiag / noderenderer / base.py
base.py 4.9K
1# -*- coding: utf-8 -*-2# Copyright 2011 Takeshi KOMIYA3#4# Licensed under the Apache License, Version 2.0 (the "License");5# you may not use this file except in compliance with the License.6# You may obtain a copy of the License at7#8# http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing, software11# distributed under the License is distributed on an "AS IS" BASIS,12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13# See the License for the specific language governing permissions and14# limitations under the License.1516from __future__ import division1718from blockdiag.utils import XY, Box, images192021class NodeShape(object):22 def __init__(self, node, metrics):23 self.node = node24 self.metrics = metrics2526 m = self.metrics.cell(self.node)27 self.textalign = 'center'28 self.connectors = [m.top, m.right, m.bottom, m.left]2930 if node.icon is None:31 self.iconbox = None32 self.textbox = m.box33 else:34 image_size = images.get_image_size(node.icon)35 if image_size is None:36 iconsize = (0, 0)37 else:38 boundedbox = [metrics.node_width // 2, metrics.node_height]39 iconsize = images.calc_image_size(image_size, boundedbox)4041 vmargin = (metrics.node_height - iconsize[1]) // 242 self.iconbox = Box(m.topleft.x,43 m.topleft.y + vmargin,44 m.topleft.x + iconsize[0],45 m.topleft.y + vmargin + iconsize[1])4647 self.textbox = Box(self.iconbox[2], m.top.y,48 m.bottomright.x, m.bottomright.y)4950 def render(self, drawer, _format, **kwargs):51 if self.node.stacked and not kwargs.get('stacked'):52 node = self.node.duplicate()53 node.label = ""54 node.background = ""55 for i in range(2, 0, -1):56 # use original_metrics FORCE57 r = self.metrics.original_metrics.cellsize // 2 * i58 metrics = self.metrics.shift(r, r)5960 self.__class__(node, metrics).render(drawer, _format,61 stacked=True, **kwargs)6263 if hasattr(self, 'render_vector_shape') and _format == 'SVG':64 self.render_vector_shape(drawer, _format, **kwargs)65 else:66 self.render_shape(drawer, _format, **kwargs)6768 self.render_icon(drawer, **kwargs)69 self.render_label(drawer, **kwargs)70 self.render_number_badge(drawer, **kwargs)7172 def render_icon(self, drawer, **kwargs):73 if self.node.icon is not None and kwargs.get('shadow') is not True:74 drawer.image(self.iconbox, self.node.icon)7576 def render_shape(self, drawer, _, **kwargs):77 pass7879 def render_label(self, drawer, **kwargs):80 if not kwargs.get('shadow'):81 font = self.metrics.font_for(self.node)82 drawer.textarea(self.textbox, self.node.label, font,83 rotate=self.node.rotate,84 fill=self.node.textcolor, halign=self.textalign,85 line_spacing=self.metrics.line_spacing,86 orientation=self.node.label_orientation)8788 def render_number_badge(self, drawer, **kwargs):89 if self.node.numbered is not None and kwargs.get('shadow') is None:90 badgeFill = kwargs.get('badgeFill')9192 xy = self.metrics.cell(self.node).topleft93 r = self.metrics.cellsize * 3 // 29495 box = Box(xy.x - r, xy.y - r, xy.x + r, xy.y + r)96 font = self.metrics.font_for(self.node)97 drawer.ellipse(box, outline=self.node.linecolor, fill=badgeFill)98 drawer.textarea(box, self.node.numbered, font,99 rotate=self.node.rotate,100 fill=self.node.textcolor)101102 @property103 def top(self):104 return self.connectors[0]105106 @property107 def left(self):108 return self.connectors[3]109110 @property111 def right(self):112 point = self.connectors[1]113 if self.node.stacked:114 point = XY(point.x + self.metrics.cellsize, point.y)115 return point116117 @property118 def bottom(self):119 point = self.connectors[2]120 if self.node.stacked:121 point = XY(point.x, point.y + self.metrics.cellsize)122 return point123124 def shift_shadow(self, value):125 xdiff = self.metrics.shadow_offset.x126 ydiff = self.metrics.shadow_offset.y127128 if isinstance(value, XY):129 ret = XY(value.x + xdiff, value.y + ydiff)130 elif isinstance(value, Box):131 ret = Box(value.x1 + xdiff, value.y1 + ydiff,132 value.x2 + xdiff, value.y2 + ydiff)133 elif isinstance(value, (list, tuple)):134 ret = [self.shift_shadow(x) for x in value]135136 return ret