clone

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

main
diagramzip / renderers / python / blockdiag-family / src / nwdiag / metrics.py
metrics.py 5.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 division1718import math19from collections import namedtuple2021import blockdiag.metrics22from blockdiag.utils import XY, Box2324from nwdiag import elements2526cellsize = blockdiag.metrics.DiagramMetrics.cellsize272829class DiagramMetrics(blockdiag.metrics.DiagramMetrics):30 node_width = cellsize * 1331 span_width = cellsize * 632 span_height = cellsize * 133334 def __init__(self, diagram, **kwargs):35 super(DiagramMetrics, self).__init__(diagram, **kwargs)3637 self.networks = diagram.networks38 self.trunk_diameter = self.cellsize39 self.jump_shift = self.trunk_diameter // 240 self.jump_radius = self.trunk_diameter41 self.page_padding = [self.span_height // 2, 0, 0, self.node_width]4243 for node in diagram.nodes:44 bottom = [n for n in node.networks if n.xy.y > node.xy.y]45 cnwidth = (len(bottom) + 1) * self.cellsize * 246 if self.cell(node).width < cnwidth:47 node.width = cnwidth48 self.spreadsheet.set_node_width(node.xy.x, cnwidth)4950 def node(self, node):51 n = super(DiagramMetrics, self).cell(node)52 return NodeMetrics(node, self, n.x1, n.y1, n.x2, n.y2)5354 def cell(self, node):55 if isinstance(node, elements.Network):56 metrics = super(DiagramMetrics, self).cell(node, use_padding=False)57 elif isinstance(node, elements.NodeGroup):58 n = super(DiagramMetrics, self).cell(node)59 metrics = GroupMetrics(node, self, n.x1, n.y1, n.x2, n.y2)60 else:61 metrics = super(DiagramMetrics, self).cell(node)62 return metrics6364 def network(self, network):65 n = self.cell(network)66 return NetworkMetrics(self, n.x1, n.y1, n.x2, n.y2)676869class NetworkMetrics(blockdiag.metrics.NodeMetrics):70 @property71 def trunkline(self):72 x0 = self.left.x73 x1 = self.right.x74 y = self.top.y7576 return [XY(x0, y), XY(x1, y)]7778 @property79 def top(self):80 pt = self.box.top81 return XY(pt.x, pt.y - self.span_height // 2)8283 @property84 def left(self):85 pt = self.box.left86 return XY(pt.x - self.span_width // 2, pt.y)8788 @property89 def right(self):90 pt = self.box.right91 return XY(pt.x + self.span_width // 2, pt.y)9293 @property94 def textbox(self):95 x = self.left.x96 y = self.top.y9798 width = self.node_width * 3 // 299 height = self.node_height100101 return Box(x - width, y - height // 2, x, y + height // 2)102103104class NodeMetrics(blockdiag.metrics.NodeMetrics):105 def __init__(self, node, metrics, x1, y1, x2, y2):106 super(NodeMetrics, self).__init__(metrics, x1, y1, x2, y2)107 self.node = node108109 @property110 def connectors(self):111 above = [n for n in self.node.networks if n.xy.y <= self.node.xy.y]112 above.sort(key=lambda a: a.xy.y, reverse=True)113114 bottom = [n for n in self.node.networks if n.xy.y > self.node.xy.y]115 bottom.sort(key=lambda a: a.xy.y)116117 Connector = namedtuple('Connector', 'network line textbox')118119 connectors = []120 for networks in [above, bottom]:121 for network in networks:122 if network.hidden:123 span = 0124 else:125 span = self.trunk_diameter // 2126127 if network.xy.y <= self.node.xy.y:128 x, y2 = self.top129 y1 = self.network(network).top.y + span130 else:131 x, y1 = self.bottom132 y2 = self.network(network).top.y - span133134 if len(networks) == 1:135 dx = 0136 else:137 pos = networks.index(network)138 base_x = (len(networks) - 1) / 2.0 - pos139 dx = int(math.floor(base_x * self.cellsize * 2))140141 width = self.node_width + self.span_width142 textbox = Box(x + dx + self.cellsize // 2,143 y2 - self.span_height // 2,144 x + width - self.cellsize // 2,145 y2)146 line = [XY(x + dx, y1), XY(x + dx, y2)]147148 cn = Connector(network, line, textbox)149 connectors.append(cn)150151 return connectors152153154class GroupMetrics(blockdiag.metrics.NodeMetrics):155 def __init__(self, group, metrics, x1, y1, x2, y2):156 super(GroupMetrics, self).__init__(metrics, x1, y1, x2, y2)157158 self.is_root_group = False159 if group.nodes:160 networks = group.nodes[0].networks[:]161 networks.sort(key=lambda a: a.xy.y)162 network = min(networks)163 if self.top.x == metrics.network(network).top.x:164 self.is_root_group = True165166 @property167 def grouplabelbox(self):168 box = super(GroupMetrics, self).grouplabelbox169 span = self.cellsize170 box = Box(box[0], box[1] + span, box[2], box[3] + span)171172 if self.is_root_group:173 width = (self.node_width + self.span_width) // 2174 box = Box(box[0] + width, box[1], box[2] + width, box[3])175176 return box177178 @property179 def marginbox(self):180 box = super(GroupMetrics, self).box181 margin_x = self.span_height // 2 - self.cellsize182 margin_y = self.cellsize183 return Box(box[0] - margin_y, box[1] - margin_x,184 box[2] + margin_y, box[3] + margin_x)